add: full multi-tenancy control

This commit is contained in:
Cauê Faleiros
2026-02-02 15:31:15 -03:00
commit c6ec92802b
1711 changed files with 258106 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
{
"name": "krayin/laravel-activity",
"license": "MIT",
"authors": [
{
"name": "Jitendra Singh",
"email": "jitendra@webkul.com"
}
],
"require": {
"krayin/laravel-core": "^1.0",
"krayin/laravel-user": "^1.0"
},
"autoload": {
"psr-4": {
"Webkul\\Activity\\": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"Webkul\\Activity\\Providers\\ActivityServiceProvider"
],
"aliases": {}
}
},
"minimum-stability": "dev"
}

View File

@@ -0,0 +1,5 @@
<?php
namespace Webkul\Activity\Contracts;
interface Activity {}

View File

@@ -0,0 +1,5 @@
<?php
namespace Webkul\Activity\Contracts;
interface File {}

View File

@@ -0,0 +1,5 @@
<?php
namespace Webkul\Activity\Contracts;
interface Participant {}

View File

@@ -0,0 +1,41 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('activities', function (Blueprint $table) {
$table->increments('id');
$table->string('title')->nullable();
$table->string('type');
$table->text('comment')->nullable();
$table->json('additional')->nullable();
$table->datetime('schedule_from')->nullable();
$table->datetime('schedule_to')->nullable();
$table->boolean('is_done')->default(0);
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('activities');
}
};

View File

@@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('activity_files', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('path');
$table->integer('activity_id')->unsigned();
$table->foreign('activity_id')->references('id')->on('activities')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('activity_files');
}
};

View File

@@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('activity_participants', function (Blueprint $table) {
$table->increments('id');
$table->integer('activity_id')->unsigned();
$table->foreign('activity_id')->references('id')->on('activities')->onDelete('cascade');
$table->integer('user_id')->nullable()->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->integer('person_id')->nullable()->unsigned();
$table->foreign('person_id')->references('id')->on('persons')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('activity_participants');
}
};

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('activities', function (Blueprint $table) {
$table->string('location')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('activities', function (Blueprint $table) {
$table->dropColumn('location');
});
}
};

View File

@@ -0,0 +1,52 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('activities', function (Blueprint $table) {
$table->dropForeign(['user_id']);
$table->unsignedInteger('user_id')->nullable()->change();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('activities', function (Blueprint $table) {
$tablePrefix = DB::getTablePrefix();
// Disable foreign key checks temporarily.
DB::statement('SET FOREIGN_KEY_CHECKS=0');
// Drop the foreign key constraint using raw SQL.
DB::statement('ALTER TABLE '.$tablePrefix.'activities DROP FOREIGN KEY activities_user_id_foreign');
// Drop the index.
DB::statement('ALTER TABLE '.$tablePrefix.'activities DROP INDEX activities_user_id_foreign');
// Change the column to be non-nullable.
$table->unsignedInteger('user_id')->nullable(false)->change();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
// Re-enable foreign key checks.
DB::statement('SET FOREIGN_KEY_CHECKS=1');
});
}
};

View File

@@ -0,0 +1,111 @@
<?php
namespace Webkul\Activity\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Activity\Contracts\Activity as ActivityContract;
use Webkul\Contact\Models\PersonProxy;
use Webkul\Lead\Models\LeadProxy;
use Webkul\Product\Models\ProductProxy;
use Webkul\User\Models\UserProxy;
use Webkul\Warehouse\Models\WarehouseProxy;
class Activity extends Model implements ActivityContract
{
/**
* Define table name of property
*
* @var string
*/
protected $table = 'activities';
/**
* Define relationships that should be touched on save
*
* @var array
*/
protected $with = ['user'];
/**
* Cast attributes to date time
*
* @var array
*/
protected $casts = [
'schedule_from' => 'datetime',
'schedule_to' => 'datetime',
];
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'title',
'type',
'location',
'comment',
'additional',
'schedule_from',
'schedule_to',
'is_done',
'user_id',
];
/**
* Get the user that owns the activity.
*/
public function user()
{
return $this->belongsTo(UserProxy::modelClass());
}
/**
* The participants that belong to the activity.
*/
public function participants()
{
return $this->hasMany(ParticipantProxy::modelClass());
}
/**
* Get the file associated with the activity.
*/
public function files()
{
return $this->hasMany(FileProxy::modelClass(), 'activity_id');
}
/**
* The leads that belong to the activity.
*/
public function leads()
{
return $this->belongsToMany(LeadProxy::modelClass(), 'lead_activities');
}
/**
* The Person that belong to the activity.
*/
public function persons()
{
return $this->belongsToMany(PersonProxy::modelClass(), 'person_activities');
}
/**
* The leads that belong to the activity.
*/
public function products()
{
return $this->belongsToMany(ProductProxy::modelClass(), 'product_activities');
}
/**
* The Warehouse that belong to the activity.
*/
public function warehouses()
{
return $this->belongsToMany(WarehouseProxy::modelClass(), 'warehouse_activities');
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Webkul\Activity\Models;
use Konekt\Concord\Proxies\ModelProxy;
class ActivityProxy extends ModelProxy {}

View File

@@ -0,0 +1,71 @@
<?php
namespace Webkul\Activity\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
use Webkul\Activity\Contracts\File as FileContract;
class File extends Model implements FileContract
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'activity_files';
/**
* The attributes that should be appended to the model.
*
* @var array
*/
protected $appends = ['url'];
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name',
'path',
'activity_id',
];
/**
* Get image url for the product image.
*/
public function url()
{
return Storage::url($this->path);
}
/**
* Get image url for the product image.
*/
public function getUrlAttribute()
{
return $this->url();
}
/**
* Get the activity that owns the file.
*/
public function activity()
{
return $this->belongsTo(ActivityProxy::modelClass());
}
/**
* @return array
*/
public function toArray()
{
$array = parent::toArray();
$array['url'] = $this->url;
return $array;
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Webkul\Activity\Models;
use Konekt\Concord\Proxies\ModelProxy;
class FileProxy extends ModelProxy {}

View File

@@ -0,0 +1,52 @@
<?php
namespace Webkul\Activity\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Activity\Contracts\Participant as ParticipantContract;
use Webkul\Contact\Models\PersonProxy;
use Webkul\User\Models\UserProxy;
class Participant extends Model implements ParticipantContract
{
public $timestamps = false;
protected $table = 'activity_participants';
protected $with = ['user', 'person'];
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'activity_id',
'user_id',
'person_id',
];
/**
* Get the activity that owns the participant.
*/
public function activity()
{
return $this->belongsTo(ActivityProxy::modelClass());
}
/**
* Get the user that owns the participant.
*/
public function user()
{
return $this->belongsTo(UserProxy::modelClass());
}
/**
* Get the person that owns the participant.
*/
public function person()
{
return $this->belongsTo(PersonProxy::modelClass());
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Webkul\Activity\Models;
use Konekt\Concord\Proxies\ModelProxy;
class ParticipantProxy extends ModelProxy {}

View File

@@ -0,0 +1,19 @@
<?php
namespace Webkul\Activity\Providers;
use Illuminate\Routing\Router;
use Illuminate\Support\ServiceProvider;
class ActivityServiceProvider extends ServiceProvider
{
/**
* Bootstrap services.
*
* @return void
*/
public function boot(Router $router)
{
$this->loadMigrationsFrom(__DIR__.'/../Database/Migrations');
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Webkul\Activity\Providers;
use Webkul\Core\Providers\BaseModuleServiceProvider;
class ModuleServiceProvider extends BaseModuleServiceProvider
{
protected $models = [
\Webkul\Activity\Models\Activity::class,
\Webkul\Activity\Models\File::class,
\Webkul\Activity\Models\Participant::class,
];
}

View File

@@ -0,0 +1,187 @@
<?php
namespace Webkul\Activity\Repositories;
use Illuminate\Container\Container;
use Webkul\Core\Eloquent\Repository;
class ActivityRepository extends Repository
{
/**
* Create a new repository instance.
*
* @return void
*/
public function __construct(
protected FileRepository $fileRepository,
Container $container
) {
parent::__construct($container);
}
/**
* Specify Model class name
*
* @return mixed
*/
public function model()
{
return 'Webkul\Activity\Contracts\Activity';
}
/**
* Create pipeline.
*
* @return \Webkul\Activity\Contracts\Activity
*/
public function create(array $data)
{
$activity = parent::create($data);
if (isset($data['file'])) {
$this->fileRepository->create([
'name' => $data['name'] ?? $data['file']->getClientOriginalName(),
'path' => $data['file']->store('activities/'.$activity->id),
'activity_id' => $activity->id,
]);
}
if (! isset($data['participants'])) {
return $activity;
}
foreach ($data['participants']['users'] ?? [] as $userId) {
$activity->participants()->create([
'user_id' => $userId,
]);
}
foreach ($data['participants']['persons'] ?? [] as $personId) {
$activity->participants()->create([
'person_id' => $personId,
]);
}
return $activity;
}
/**
* Update pipeline.
*
* @param int $id
* @param string $attribute
* @return \Webkul\Activity\Contracts\Activity
*/
public function update(array $data, $id, $attribute = 'id')
{
$activity = parent::update($data, $id);
if (isset($data['participants'])) {
$activity->participants()->delete();
foreach ($data['participants']['users'] ?? [] as $userId) {
/**
* In some cases, the component exists in an HTML form, and traditional HTML does not send an empty array.
* Therefore, we need to check for an empty string.
* This scenario occurs only when all participants are removed.
*/
if (empty($userId)) {
break;
}
$activity->participants()->create([
'user_id' => $userId,
]);
}
foreach ($data['participants']['persons'] ?? [] as $personId) {
/**
* In some cases, the component exists in an HTML form, and traditional HTML does not send an empty array.
* Therefore, we need to check for an empty string.
* This scenario occurs only when all participants are removed.
*/
if (empty($personId)) {
break;
}
$activity->participants()->create([
'person_id' => $personId,
]);
}
}
return $activity;
}
/**
* @param string $dateRange
* @return mixed
*/
public function getActivities($dateRange)
{
$tablePrefix = \DB::getTablePrefix();
return $this->select(
'activities.id',
'activities.created_at',
'activities.title',
'activities.schedule_from as start',
'activities.schedule_to as end',
'users.name as user_name',
)
->addSelect(\DB::raw('IF('.$tablePrefix.'activities.is_done, "done", "") as class'))
->leftJoin('activity_participants', 'activities.id', '=', 'activity_participants.activity_id')
->leftJoin('users', 'activities.user_id', '=', 'users.id')
->whereIn('type', ['call', 'meeting', 'lunch'])
->whereBetween('activities.schedule_from', $dateRange)
->where(function ($query) {
if ($userIds = bouncer()->getAuthorizedUserIds()) {
$query->whereIn('activities.user_id', $userIds)
->orWhereIn('activity_participants.user_id', $userIds);
}
})
->distinct()
->get();
}
/**
* @param string $startFrom
* @param string $endFrom
* @param array $participants
* @param int $id
* @return bool
*/
public function isDurationOverlapping($startFrom, $endFrom, $participants, $id)
{
$queryBuilder = $this->leftJoin('activity_participants', 'activities.id', '=', 'activity_participants.activity_id')
->where(function ($query) use ($startFrom, $endFrom) {
$query->where([
['activities.schedule_from', '<=', $startFrom],
['activities.schedule_to', '>=', $startFrom],
])->orWhere([
['activities.schedule_from', '>=', $startFrom],
['activities.schedule_from', '<=', $endFrom],
]);
})
->where(function ($query) use ($participants) {
if (is_null($participants)) {
return;
}
if (isset($participants['users'])) {
$query->orWhereIn('activity_participants.user_id', $participants['users']);
}
if (isset($participants['persons'])) {
$query->orWhereIn('activity_participants.person_id', $participants['persons']);
}
})
->groupBy('activities.id');
if (! is_null($id)) {
$queryBuilder->where('activities.id', '!=', $id);
}
return $queryBuilder->count() ? true : false;
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Webkul\Activity\Repositories;
use Webkul\Core\Eloquent\Repository;
class FileRepository extends Repository
{
/**
* Specify model class name.
*
* @return mixed
*/
public function model()
{
return \Webkul\Activity\Contracts\File::class;
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Webkul\Activity\Repositories;
use Webkul\Core\Eloquent\Repository;
class ParticipantRepository extends Repository
{
/**
* Specify Model class name
*
* @return mixed
*/
public function model()
{
return 'Webkul\Activity\Contracts\Participant';
}
}

View File

@@ -0,0 +1,183 @@
<?php
namespace Webkul\Activity\Traits;
use Webkul\Activity\Repositories\ActivityRepository;
use Webkul\Attribute\Contracts\AttributeValue;
use Webkul\Attribute\Repositories\AttributeValueRepository;
trait LogsActivity
{
/**
* The "booted" method of the model.
*/
protected static function booted(): void
{
static::created(function ($model) {
if (! method_exists($model->entity ?? $model, 'activities')) {
return;
}
if (! $model instanceof AttributeValue) {
$activity = app(ActivityRepository::class)->create([
'type' => 'system',
'title' => trans('admin::app.activities.created'),
'is_done' => 1,
'user_id' => auth()->check()
? auth()->id()
: null,
]);
$model->activities()->attach($activity->id);
return;
}
static::logActivity($model);
});
static::updated(function ($model) {
if (! method_exists($model->entity ?? $model, 'activities')) {
return;
}
static::logActivity($model);
});
static::deleting(function ($model) {
if (! method_exists($model->entity ?? $model, 'activities')) {
return;
}
$model->activities()->delete();
});
}
/**
* Create activity.
*/
protected static function logActivity($model)
{
$customAttributes = [];
if (method_exists($model, 'getCustomAttributes')) {
$customAttributes = $model->getCustomAttributes()->pluck('code')->toArray();
}
$updatedAttributes = static::getUpdatedAttributes($model);
foreach ($updatedAttributes as $attributeCode => $attributeData) {
if (in_array($attributeCode, $customAttributes)) {
continue;
}
$attributeCode = $model->attribute?->name ?: $attributeCode;
$activity = app(ActivityRepository::class)->create([
'type' => 'system',
'title' => trans('admin::app.activities.updated', ['attribute' => $attributeCode]),
'is_done' => 1,
'additional' => json_encode([
'attribute' => $attributeCode,
'new' => [
'value' => $attributeData['new'],
'label' => static::getAttributeLabel($attributeData['new'], $model->attribute),
],
'old' => [
'value' => $attributeData['old'],
'label' => static::getAttributeLabel($attributeData['old'], $model->attribute),
],
]),
'user_id' => auth()->id(),
]);
if ($model instanceof AttributeValue) {
$model->entity->activities()->attach($activity->id);
} else {
$model->activities()->attach($activity->id);
}
}
}
/**
* Get attribute label.
*/
protected static function getAttributeLabel($value, $attribute)
{
return app(AttributeValueRepository::class)->getAttributeLabel($value, $attribute);
}
/**
* Create activity.
*/
protected static function getUpdatedAttributes($model)
{
$updatedAttributes = [];
foreach ($model->getDirty() as $key => $value) {
if (in_array($key, [
'id',
'attribute_id',
'entity_id',
'entity_type',
'updated_at',
])) {
continue;
}
$newValue = static::decodeValueIfJson($value);
$oldValue = static::decodeValueIfJson($model->getOriginal($key));
if ($newValue != $oldValue) {
$updatedAttributes[$key] = [
'new' => $newValue,
'old' => $oldValue,
];
}
}
return $updatedAttributes;
}
/**
* Convert value if json.
*/
protected static function decodeValueIfJson($value)
{
if (
! is_array($value)
&& json_decode($value, true)
) {
$value = json_decode($value, true);
}
if (! is_array($value)) {
return $value;
}
static::ksortRecursive($value);
return $value;
}
/**
* Sort array recursively.
*/
protected static function ksortRecursive(&$array)
{
if (! is_array($array)) {
return;
}
ksort($array);
foreach ($array as &$value) {
if (! is_array($value)) {
continue;
}
static::ksortRecursive($value);
}
}
}

5
packages/Webkul/Admin/.gitignore vendored Executable file
View File

@@ -0,0 +1,5 @@
/node_modules
/package-lock.json
npm-debug.log
/playwright-report
/test-results

View File

@@ -0,0 +1,35 @@
{
"name": "krayin/laravel-admin",
"license": "MIT",
"authors": [
{
"name": "Jitendra Singh",
"email": "jitendra@webkul.com"
}
],
"require": {
"krayin/laravel-attribute": "^1.0",
"krayin/laravel-contact": "^1.0",
"krayin/laravel-core": "^1.0",
"krayin/laravel-email": "^1.0",
"krayin/laravel-lead": "^1.0",
"krayin/laravel-product": "^1.0",
"krayin/laravel-tag": "^1.0",
"krayin/laravel-ui": "^1.0",
"krayin/laravel-user": "^1.0"
},
"autoload": {
"psr-4": {
"Webkul\\Admin\\": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"Webkul\\Admin\\Providers\\AdminServiceProvider"
],
"aliases": {}
}
},
"minimum-stability": "dev"
}

View File

@@ -0,0 +1,35 @@
{
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"devDependencies": {
"@playwright/test": "^1.50.1",
"@types/node": "^22.7.8",
"autoprefixer": "^10.4.16",
"axios": "^1.7.4",
"laravel-vite-plugin": "^1.0",
"postcss": "^8.4.23",
"tailwindcss": "^3.3.2",
"vite": "^5.4.12",
"vue": "^3.4.21"
},
"dependencies": {
"@vee-validate/i18n": "^4.9.1",
"@vee-validate/rules": "^4.9.1",
"@vitejs/plugin-vue": "^4.2.3",
"chartjs-chart-funnel": "^4.2.1",
"dompurify": "^3.1.7",
"dotenv": "^16.4.7",
"flatpickr": "^4.6.13",
"mitt": "^3.0.1",
"playwright": "^1.48.1",
"readline-sync": "^1.4.10",
"vee-validate": "^4.9.1",
"vue-cal": "^4.9.0",
"vue-flatpickr": "^2.3.0",
"vuedraggable": "^4.1.0"
}
}

View File

@@ -0,0 +1,3 @@
module.exports = ({ env }) => ({
plugins: [require("tailwindcss")(), require("autoprefixer")()],
});

View File

@@ -0,0 +1,60 @@
<?php
namespace Webkul\Admin;
use Webkul\User\Repositories\UserRepository;
class Bouncer
{
/**
* Checks if user allowed or not for certain action
*
* @param string $permission
* @return void
*/
public function hasPermission($permission)
{
if (auth()->guard('user')->check() && auth()->guard('user')->user()->role->permission_type == 'all') {
return true;
} else {
if (! auth()->guard('user')->check() || ! auth()->guard('user')->user()->hasPermission($permission)) {
return false;
}
}
return true;
}
/**
* Checks if user allowed or not for certain action
*
* @param string $permission
* @return void
*/
public static function allow($permission)
{
if (! auth()->guard('user')->check() || ! auth()->guard('user')->user()->hasPermission($permission)) {
abort(401, 'This action is unauthorized');
}
}
/**
* This function will return user ids of current user's groups
*
* @return array|null
*/
public function getAuthorizedUserIds()
{
$user = auth()->guard('user')->user();
if ($user->view_permission == 'global') {
return null;
}
if ($user->view_permission == 'group') {
return app(UserRepository::class)->getCurrentUserGroupsUserIds();
} else {
return [$user->id];
}
}
}

View File

@@ -0,0 +1,527 @@
<?php
return [
[
'key' => 'dashboard',
'name' => 'admin::app.layouts.dashboard',
'route' => 'admin.dashboard.index',
'sort' => 1,
], [
'key' => 'leads',
'name' => 'admin::app.acl.leads',
'route' => 'admin.leads.index',
'sort' => 2,
], [
'key' => 'leads.create',
'name' => 'admin::app.acl.create',
'route' => ['admin.leads.create', 'admin.leads.store'],
'sort' => 1,
], [
'key' => 'leads.view',
'name' => 'admin::app.acl.view',
'route' => 'admin.leads.view',
'sort' => 2,
], [
'key' => 'leads.edit',
'name' => 'admin::app.acl.edit',
'route' => ['admin.leads.edit', 'admin.leads.update', 'admin.leads.mass_update'],
'sort' => 3,
], [
'key' => 'leads.delete',
'name' => 'admin::app.acl.delete',
'route' => ['admin.leads.delete', 'admin.leads.mass_delete'],
'sort' => 4,
], [
'key' => 'quotes',
'name' => 'admin::app.acl.quotes',
'route' => 'admin.quotes.index',
'sort' => 3,
], [
'key' => 'quotes.create',
'name' => 'admin::app.acl.create',
'route' => ['admin.quotes.create', 'admin.quotes.store'],
'sort' => 1,
], [
'key' => 'quotes.edit',
'name' => 'admin::app.acl.edit',
'route' => ['admin.quotes.edit', 'admin.quotes.update'],
'sort' => 2,
], [
'key' => 'quotes.print',
'name' => 'admin::app.acl.print',
'route' => 'admin.quotes.print',
'sort' => 3,
], [
'key' => 'quotes.delete',
'name' => 'admin::app.acl.delete',
'route' => ['admin.quotes.delete', 'admin.quotes.mass_delete'],
'sort' => 4,
], [
'key' => 'mail',
'name' => 'admin::app.acl.mail',
'route' => 'admin.mail.index',
'sort' => 4,
], [
'key' => 'mail.inbox',
'name' => 'admin::app.acl.inbox',
'route' => 'admin.mail.index',
'sort' => 1,
], [
'key' => 'mail.draft',
'name' => 'admin::app.acl.draft',
'route' => 'admin.mail.index',
'sort' => 2,
], [
'key' => 'mail.outbox',
'name' => 'admin::app.acl.outbox',
'route' => 'admin.mail.index',
'sort' => 3,
], [
'key' => 'mail.sent',
'name' => 'admin::app.acl.sent',
'route' => 'admin.mail.index',
'sort' => 4,
], [
'key' => 'mail.trash',
'name' => 'admin::app.acl.trash',
'route' => 'admin.mail.index',
'sort' => 5,
], [
'key' => 'mail.compose',
'name' => 'admin::app.acl.create',
'route' => ['admin.mail.store'],
'sort' => 6,
], [
'key' => 'mail.view',
'name' => 'admin::app.acl.view',
'route' => 'admin.mail.view',
'sort' => 7,
], [
'key' => 'mail.edit',
'name' => 'admin::app.acl.edit',
'route' => 'admin.mail.update',
'sort' => 8,
], [
'key' => 'mail.delete',
'name' => 'admin::app.acl.delete',
'route' => ['admin.mail.delete', 'admin.mail.mass_delete'],
'sort' => 9,
], [
'key' => 'activities',
'name' => 'admin::app.acl.activities',
'route' => 'admin.activities.index',
'sort' => 5,
], [
'key' => 'activities.create',
'name' => 'admin::app.acl.create',
'route' => ['admin.activities.create', 'admin.activities.store'],
'sort' => 1,
], [
'key' => 'activities.edit',
'name' => 'admin::app.acl.edit',
'route' => ['admin.activities.edit', 'admin.activities.update', 'admin.activities.mass_update'],
'sort' => 2,
], [
'key' => 'activities.delete',
'name' => 'admin::app.acl.delete',
'route' => ['admin.activities.delete', 'admin.activities.mass_delete'],
'sort' => 3,
], [
'key' => 'contacts',
'name' => 'admin::app.acl.contacts',
'route' => 'admin.contacts.users.index',
'sort' => 6,
], [
'key' => 'contacts.persons',
'name' => 'admin::app.acl.persons',
'route' => 'admin.contacts.persons.index',
'sort' => 1,
], [
'key' => 'contacts.persons.create',
'name' => 'admin::app.acl.create',
'route' => ['admin.contacts.persons.create', 'admin.contacts.persons.store'],
'sort' => 2,
], [
'key' => 'contacts.persons.edit',
'name' => 'admin::app.acl.edit',
'route' => ['admin.contacts.persons.edit', 'admin.contacts.persons.update'],
'sort' => 3,
], [
'key' => 'contacts.persons.delete',
'name' => 'admin::app.acl.delete',
'route' => ['admin.contacts.persons.delete', 'admin.contacts.persons.mass_delete'],
'sort' => 4,
], [
'key' => 'contacts.persons.view',
'name' => 'admin::app.acl.view',
'route' => 'admin.contacts.persons.view',
'sort' => 5,
], [
'key' => 'contacts.organizations',
'name' => 'admin::app.acl.organizations',
'route' => 'admin.contacts.organizations.index',
'sort' => 2,
], [
'key' => 'contacts.organizations.create',
'name' => 'admin::app.acl.create',
'route' => ['admin.contacts.organizations.create', 'admin.contacts.organizations.store'],
'sort' => 1,
], [
'key' => 'contacts.organizations.edit',
'name' => 'admin::app.acl.edit',
'route' => ['admin.contacts.organizations.edit', 'admin.contacts.organizations.update'],
'sort' => 2,
], [
'key' => 'contacts.organizations.delete',
'name' => 'admin::app.acl.delete',
'route' => ['admin.contacts.organizations.delete', 'admin.contacts.organizations.mass_delete'],
'sort' => 3,
], [
'key' => 'products',
'name' => 'admin::app.acl.products',
'route' => 'admin.products.index',
'sort' => 7,
], [
'key' => 'products.create',
'name' => 'admin::app.acl.create',
'route' => ['admin.products.create', 'admin.products.store'],
'sort' => 1,
], [
'key' => 'products.edit',
'name' => 'admin::app.acl.edit',
'route' => ['admin.products.edit', 'admin.products.update'],
'sort' => 2,
], [
'key' => 'products.delete',
'name' => 'admin::app.acl.delete',
'route' => ['admin.products.delete', 'admin.products.mass_delete'],
'sort' => 3,
], [
'key' => 'products.view',
'name' => 'admin::app.acl.view',
'route' => 'admin.products.view',
'sort' => 3,
], [
'key' => 'settings',
'name' => 'admin::app.acl.settings',
'route' => 'admin.settings.index',
'sort' => 8,
], [
'key' => 'settings.user',
'name' => 'admin::app.acl.user',
'route' => ['admin.settings.groups.index', 'admin.settings.roles.index', 'admin.settings.users.index'],
'sort' => 1,
], [
'key' => 'settings.user.groups',
'name' => 'admin::app.acl.groups',
'route' => 'admin.settings.groups.index',
'sort' => 1,
], [
'key' => 'settings.user.groups.create',
'name' => 'admin::app.acl.create',
'route' => ['admin.settings.groups.create', 'admin.settings.groups.store'],
'sort' => 1,
], [
'key' => 'settings.user.groups.edit',
'name' => 'admin::app.acl.edit',
'route' => ['admin.settings.groups.edit', 'admin.settings.groups.update'],
'sort' => 2,
], [
'key' => 'settings.user.groups.delete',
'name' => 'admin::app.acl.delete',
'route' => 'admin.settings.groups.delete',
'sort' => 3,
], [
'key' => 'settings.user.roles',
'name' => 'admin::app.acl.roles',
'route' => 'admin.settings.roles.index',
'sort' => 2,
], [
'key' => 'settings.user.roles.create',
'name' => 'admin::app.acl.create',
'route' => ['admin.settings.roles.create', 'admin.settings.roles.store'],
'sort' => 1,
], [
'key' => 'settings.user.roles.edit',
'name' => 'admin::app.acl.edit',
'route' => ['admin.settings.roles.edit', 'admin.settings.roles.update'],
'sort' => 2,
], [
'key' => 'settings.user.roles.delete',
'name' => 'admin::app.acl.delete',
'route' => 'admin.settings.roles.delete',
'sort' => 3,
], [
'key' => 'settings.user.users',
'name' => 'admin::app.acl.users',
'route' => 'admin.settings.users.index',
'sort' => 3,
], [
'key' => 'settings.user.users.create',
'name' => 'admin::app.acl.create',
'route' => ['admin.settings.users.create', 'admin.settings.users.store'],
'sort' => 1,
], [
'key' => 'settings.user.users.edit',
'name' => 'admin::app.acl.edit',
'route' => ['admin.settings.users.edit', 'admin.settings.users.update', 'admin.settings.users.mass_update'],
'sort' => 2,
], [
'key' => 'settings.user.users.delete',
'name' => 'admin::app.acl.delete',
'route' => ['admin.settings.users.delete', 'admin.settings.users.mass_delete'],
'sort' => 3,
], [
'key' => 'settings.lead',
'name' => 'admin::app.acl.lead',
'route' => ['admin.settings.pipelines.index', 'admin.settings.sources.index', 'admin.settings.types.index'],
'sort' => 2,
], [
'key' => 'settings.lead.pipelines',
'name' => 'admin::app.acl.pipelines',
'route' => 'admin.settings.pipelines.index',
'sort' => 1,
], [
'key' => 'settings.lead.pipelines.create',
'name' => 'admin::app.acl.create',
'route' => ['admin.settings.pipelines.create', 'admin.settings.pipelines.store'],
'sort' => 1,
], [
'key' => 'settings.lead.pipelines.edit',
'name' => 'admin::app.acl.edit',
'route' => ['admin.settings.pipelines.edit', 'admin.settings.pipelines.update'],
'sort' => 2,
], [
'key' => 'settings.lead.pipelines.delete',
'name' => 'admin::app.acl.delete',
'route' => 'admin.settings.pipelines.delete',
'sort' => 3,
], [
'key' => 'settings.lead.sources',
'name' => 'admin::app.acl.sources',
'route' => 'admin.settings.sources.index',
'sort' => 2,
], [
'key' => 'settings.lead.sources.create',
'name' => 'admin::app.acl.create',
'route' => ['admin.settings.sources.store'],
'sort' => 1,
], [
'key' => 'settings.lead.sources.edit',
'name' => 'admin::app.acl.edit',
'route' => ['admin.settings.sources.edit', 'admin.settings.sources.update'],
'sort' => 2,
], [
'key' => 'settings.lead.sources.delete',
'name' => 'admin::app.acl.delete',
'route' => 'admin.settings.sources.delete',
'sort' => 3,
], [
'key' => 'settings.lead.types',
'name' => 'admin::app.acl.types',
'route' => 'admin.settings.types.index',
'sort' => 3,
], [
'key' => 'settings.lead.types.create',
'name' => 'admin::app.acl.create',
'route' => ['admin.settings.types.store'],
'sort' => 1,
], [
'key' => 'settings.lead.types.edit',
'name' => 'admin::app.acl.edit',
'route' => ['admin.settings.types.edit', 'admin.settings.types.update'],
'sort' => 2,
], [
'key' => 'settings.lead.types.delete',
'name' => 'admin::app.acl.delete',
'route' => 'admin.settings.types.delete',
'sort' => 3,
], [
'key' => 'settings.automation',
'name' => 'admin::app.acl.automation',
'route' => ['admin.settings.attributes.index', 'admin.settings.email_templates.index', 'admin.settings.workflows.index'],
'sort' => 3,
], [
'key' => 'settings.automation.attributes',
'name' => 'admin::app.acl.attributes',
'route' => 'admin.settings.attributes.index',
'sort' => 1,
], [
'key' => 'settings.automation.attributes.create',
'name' => 'admin::app.acl.create',
'route' => ['admin.settings.attributes.create', 'admin.settings.attributes.store'],
'sort' => 1,
], [
'key' => 'settings.automation.attributes.edit',
'name' => 'admin::app.acl.edit',
'route' => ['admin.settings.attributes.edit', 'admin.settings.attributes.update', 'admin.settings.attributes.mass_update'],
'sort' => 2,
], [
'key' => 'settings.automation.attributes.delete',
'name' => 'admin::app.acl.delete',
'route' => 'admin.settings.attributes.delete',
'sort' => 3,
], [
'key' => 'settings.automation.email_templates',
'name' => 'admin::app.acl.email-templates',
'route' => 'admin.settings.email_templates.index',
'sort' => 7,
], [
'key' => 'settings.automation.email_templates.create',
'name' => 'admin::app.acl.create',
'route' => ['admin.settings.email_templates.create', 'admin.settings.email_templates.store'],
'sort' => 1,
], [
'key' => 'settings.automation.email_templates.edit',
'name' => 'admin::app.acl.edit',
'route' => ['admin.settings.email_templates.edit', 'admin.settings.email_templates.update'],
'sort' => 2,
], [
'key' => 'settings.automation.email_templates.delete',
'name' => 'admin::app.acl.delete',
'route' => 'admin.settings.email_templates.delete',
'sort' => 3,
], [
'key' => 'settings.automation.workflows',
'name' => 'admin::app.acl.workflows',
'route' => 'admin.settings.workflows.index',
'sort' => 2,
], [
'key' => 'settings.automation.workflows.create',
'name' => 'admin::app.acl.create',
'route' => ['admin.settings.workflows.create', 'admin.settings.workflows.store'],
'sort' => 1,
], [
'key' => 'settings.automation.workflows.edit',
'name' => 'admin::app.acl.edit',
'route' => ['admin.settings.workflows.edit', 'admin.settings.workflows.update'],
'sort' => 2,
], [
'key' => 'settings.automation.workflows.delete',
'name' => 'admin::app.acl.delete',
'route' => 'admin.settings.workflows.delete',
'sort' => 3,
], [
'key' => 'settings.automation.events',
'name' => 'admin::app.acl.event',
'route' => 'admin.settings.marketing.events.index',
'sort' => 2,
], [
'key' => 'settings.automation.events.create',
'name' => 'admin::app.acl.create',
'route' => ['admin.settings.marketing.events.create', 'admin.settings.marketing.events.store'],
'sort' => 1,
], [
'key' => 'settings.automation.events.edit',
'name' => 'admin::app.acl.edit',
'route' => ['admin.settings.marketing.events.edit', 'admin.settings.marketing.events.update'],
'sort' => 2,
], [
'key' => 'settings.automation.events.delete',
'name' => 'admin::app.acl.delete',
'route' => ['admin.settings.marketing.events.delete', 'admin.settings.marketing.events.mass_delete'],
'sort' => 3,
], [
'key' => 'settings.automation.campaigns',
'name' => 'admin::app.acl.campaigns',
'route' => 'admin.settings.marketing.campaigns.index',
'sort' => 2,
], [
'key' => 'settings.automation.campaigns.create',
'name' => 'admin::app.acl.create',
'route' => ['admin.settings.marketing.campaigns.create', 'admin.settings.marketing.campaigns.store'],
'sort' => 1,
], [
'key' => 'settings.automation.campaigns.edit',
'name' => 'admin::app.acl.edit',
'route' => ['admin.settings.marketing.campaigns.edit', 'admin.settings.marketing.campaigns.update'],
'sort' => 2,
], [
'key' => 'settings.automation.campaigns.delete',
'name' => 'admin::app.acl.delete',
'route' => ['admin.settings.marketing.campaigns.delete', 'admin.settings.marketing.campaigns.mass_delete'],
'sort' => 3,
], [
'key' => 'settings.automation.webhooks',
'name' => 'admin::app.acl.webhook',
'route' => 'admin.settings.webhooks.index',
'sort' => 1,
], [
'key' => 'settings.automation.webhooks.create',
'name' => 'admin::app.acl.create',
'route' => ['admin.settings.webhooks.create', 'admin.settings.webhooks.store'],
'sort' => 1,
], [
'key' => 'settings.automation.webhooks.edit',
'name' => 'admin::app.acl.edit',
'route' => ['admin.settings.webhooks.edit', 'admin.settings.webhooks.update'],
'sort' => 2,
], [
'key' => 'settings.automation.webhooks.delete',
'name' => 'admin::app.acl.delete',
'route' => 'admin.settings.webhooks.delete',
'sort' => 3,
], [
'key' => 'settings.other_settings',
'name' => 'admin::app.acl.other-settings',
'route' => 'admin.settings.tags.index',
'sort' => 4,
], [
'key' => 'settings.other_settings.tags',
'name' => 'admin::app.acl.tags',
'route' => 'admin.settings.tags.index',
'sort' => 1,
], [
'key' => 'settings.other_settings.tags.create',
'name' => 'admin::app.acl.create',
'route' => ['admin.settings.tags.create', 'admin.settings.tags.store', 'admin.leads.tags.attach'],
'sort' => 1,
], [
'key' => 'settings.other_settings.tags.edit',
'name' => 'admin::app.acl.edit',
'route' => ['admin.settings.tags.edit', 'admin.settings.tags.update'],
'sort' => 1,
], [
'key' => 'settings.other_settings.tags.delete',
'name' => 'admin::app.acl.delete',
'route' => ['admin.settings.tags.delete', 'admin.settings.tags.mass_delete', 'admin.leads.tags.detach'],
'sort' => 2,
],
[
'key' => 'settings.data_transfer',
'name' => 'admin::app.acl.data-transfer',
'route' => 'admin.settings.data_transfer.imports.index',
'sort' => 10,
], [
'key' => 'settings.data_transfer.imports',
'name' => 'admin::app.acl.imports',
'route' => 'admin.settings.data_transfer.imports.index',
'sort' => 1,
], [
'key' => 'settings.data_transfer.imports.create',
'name' => 'admin::app.acl.create',
'route' => 'admin.settings.data_transfer.imports.create',
'sort' => 1,
], [
'key' => 'settings.data_transfer.imports.edit',
'name' => 'admin::app.acl.edit',
'route' => 'admin.settings.data_transfer.imports.edit',
'sort' => 2,
], [
'key' => 'settings.data_transfer.imports.delete',
'name' => 'admin::app.acl.delete',
'route' => 'admin.settings.data_transfer.imports.delete',
'sort' => 3,
], [
'key' => 'settings.data_transfer.imports.import',
'name' => 'admin::app.acl.import',
'route' => 'admin.settings.data_transfer.imports.imports',
'sort' => 4,
],
[
'key' => 'configuration',
'name' => 'admin::app.acl.configuration',
'route' => 'admin.configuration.index',
'sort' => 9,
],
];

View File

@@ -0,0 +1,33 @@
<?php
return [
'leads' => [
'name' => 'admin::app.leads.index.title',
'repository' => 'Webkul\Lead\Repositories\LeadRepository',
],
'persons' => [
'name' => 'admin::app.contacts.persons.index.title',
'repository' => 'Webkul\Contact\Repositories\PersonRepository',
],
'organizations' => [
'name' => 'admin::app.contacts.organizations.index.title',
'repository' => 'Webkul\Contact\Repositories\OrganizationRepository',
],
'products' => [
'name' => 'admin::app.products.index.title',
'repository' => 'Webkul\Product\Repositories\ProductRepository',
],
'quotes' => [
'name' => 'admin::app.quotes.index.title',
'repository' => 'Webkul\Quote\Repositories\QuoteRepository',
],
'warehouses' => [
'name' => 'admin::app.settings.warehouses.index.title',
'repository' => 'Webkul\Warehouse\Repositories\WarehouseRepository',
],
];

View File

@@ -0,0 +1,54 @@
<?php
return [
'leads' => [
'name' => 'Leads',
'repository' => 'Webkul\Lead\Repositories\LeadRepository',
'label_column' => 'title',
],
'lead_sources' => [
'name' => 'Lead Sources',
'repository' => 'Webkul\Lead\Repositories\SourceRepository',
],
'lead_types' => [
'name' => 'Lead Types',
'repository' => 'Webkul\Lead\Repositories\TypeRepository',
],
'lead_pipelines' => [
'name' => 'Lead Pipelines',
'repository' => 'Webkul\Lead\Repositories\PipelineRepository',
],
'lead_pipeline_stages' => [
'name' => 'Lead Pipeline Stages',
'repository' => 'Webkul\Lead\Repositories\StageRepository',
],
'users' => [
'name' => 'Sales Owners',
'repository' => 'Webkul\User\Repositories\UserRepository',
],
'organizations' => [
'name' => 'Organizations',
'repository' => 'Webkul\Contact\Repositories\OrganizationRepository',
],
'persons' => [
'name' => 'Persons',
'repository' => 'Webkul\Contact\Repositories\PersonRepository',
],
'warehouses' => [
'name' => 'Warehouses',
'repository' => 'Webkul\Warehouse\Repositories\WarehouseRepository',
],
'locations' => [
'name' => 'Locations',
'repository' => 'Webkul\Warehouse\Repositories\LocationRepository',
],
];

View File

@@ -0,0 +1,314 @@
<?php
return [
/**
* General.
*/
[
'key' => 'general',
'name' => 'admin::app.configuration.index.general.title',
'info' => 'admin::app.configuration.index.general.info',
'sort' => 1,
], [
'key' => 'general.general',
'name' => 'admin::app.configuration.index.general.general.title',
'info' => 'admin::app.configuration.index.general.general.info',
'icon' => 'icon-setting',
'sort' => 1,
], [
'key' => 'general.general.locale_settings',
'name' => 'admin::app.configuration.index.general.general.locale-settings.title',
'info' => 'admin::app.configuration.index.general.general.locale-settings.title-info',
'sort' => 1,
'fields' => [
[
'name' => 'locale',
'title' => 'admin::app.configuration.index.general.general.locale-settings.title',
'type' => 'select',
'default' => 'en',
'options' => 'Webkul\Core\Core@locales',
],
],
], [
'key' => 'general.general.admin_logo',
'name' => 'admin::app.configuration.index.general.general.admin-logo.title',
'info' => 'admin::app.configuration.index.general.general.admin-logo.title-info',
'sort' => 2,
'fields' => [
[
'name' => 'logo_image',
'title' => 'admin::app.configuration.index.general.general.admin-logo.logo-image',
'type' => 'image',
'validation' => 'mimes:bmp,jpeg,jpg,png,webp,svg',
],
],
], [
'key' => 'general.settings',
'name' => 'admin::app.configuration.index.general.settings.title',
'info' => 'admin::app.configuration.index.general.settings.info',
'icon' => 'icon-configuration',
'sort' => 2,
], [
'key' => 'general.settings.footer',
'name' => 'admin::app.configuration.index.general.settings.footer.title',
'info' => 'admin::app.configuration.index.general.settings.footer.info',
'sort' => 1,
'fields' => [
[
'name' => 'label',
'title' => 'admin::app.configuration.index.general.settings.footer.powered-by',
'type' => 'editor',
'default' => 'Powered by <span style="color: rgb(14, 144, 217);"><a href="http://www.krayincrm.com" target="_blank">Krayin</a></span>, an open-source project by <span style="color: rgb(14, 144, 217);"><a href="https://webkul.com" target="_blank">Webkul</a></span>.',
'tinymce' => true,
],
],
], [
'key' => 'general.settings.menu',
'name' => 'admin::app.configuration.index.general.settings.menu.title',
'info' => 'admin::app.configuration.index.general.settings.menu.info',
'sort' => 2,
'fields' => [
[
'name' => 'dashboard',
'title' => 'admin::app.configuration.index.general.settings.menu.dashboard',
'type' => 'text',
'default' => 'Dashboard',
'validation' => 'max:20',
], [
'name' => 'leads',
'title' => 'admin::app.configuration.index.general.settings.menu.leads',
'type' => 'text',
'default' => 'Leads',
'validation' => 'max:20',
], [
'name' => 'quotes',
'title' => 'admin::app.configuration.index.general.settings.menu.quotes',
'type' => 'text',
'default' => 'Quotes',
'validation' => 'max:20',
], [
'name' => 'mail.mail',
'title' => 'admin::app.configuration.index.general.settings.menu.mail',
'type' => 'text',
'default' => 'Mail',
'validation' => 'max:20',
], [
'name' => 'mail.inbox',
'title' => 'admin::app.configuration.index.general.settings.menu.inbox',
'type' => 'text',
'default' => 'Inbox',
'validation' => 'max:20',
], [
'name' => 'mail.draft',
'title' => 'admin::app.configuration.index.general.settings.menu.draft',
'type' => 'text',
'default' => 'Draft',
'validation' => 'max:20',
], [
'name' => 'mail.outbox',
'title' => 'admin::app.configuration.index.general.settings.menu.outbox',
'type' => 'text',
'default' => 'Outbox',
'validation' => 'max:20',
], [
'name' => 'mail.sent',
'title' => 'admin::app.configuration.index.general.settings.menu.sent',
'type' => 'text',
'default' => 'Sent',
'validation' => 'max:20',
], [
'name' => 'mail.trash',
'title' => 'admin::app.configuration.index.general.settings.menu.trash',
'type' => 'text',
'default' => 'Trash',
'validation' => 'max:20',
], [
'name' => 'activities',
'title' => 'admin::app.configuration.index.general.settings.menu.activities',
'type' => 'text',
'default' => 'Activities',
'validation' => 'max:20',
], [
'name' => 'contacts.contacts',
'title' => 'admin::app.configuration.index.general.settings.menu.contacts',
'type' => 'text',
'default' => 'Contacts',
'validation' => 'max:20',
], [
'name' => 'contacts.persons',
'title' => 'admin::app.configuration.index.general.settings.menu.persons',
'type' => 'text',
'default' => 'Persons',
'validation' => 'max:20',
], [
'name' => 'contacts.organizations',
'title' => 'admin::app.configuration.index.general.settings.menu.organizations',
'type' => 'text',
'default' => 'Organizations',
'validation' => 'max:20',
], [
'name' => 'products',
'title' => 'admin::app.configuration.index.general.settings.menu.products',
'type' => 'text',
'default' => 'Products',
'validation' => 'max:20',
], [
'name' => 'settings',
'title' => 'admin::app.configuration.index.general.settings.menu.settings',
'type' => 'text',
'default' => 'Settings',
'validation' => 'max:20',
], [
'name' => 'configuration',
'title' => 'admin::app.configuration.index.general.settings.menu.configuration',
'type' => 'text',
'default' => 'Configuration',
'validation' => 'max:20',
],
],
], [
'key' => 'general.settings.menu_color',
'name' => 'admin::app.configuration.index.general.settings.menu-color.title',
'info' => 'admin::app.configuration.index.general.settings.menu-color.info',
'sort' => 3,
'fields' => [
[
'name' => 'brand_color',
'title' => 'admin::app.configuration.index.general.settings.menu-color.brand-color',
'type' => 'color',
'default' => '#0E90D9',
],
],
], [
'key' => 'general.magic_ai',
'name' => 'admin::app.configuration.index.magic-ai.title',
'info' => 'admin::app.configuration.index.magic-ai.info',
'icon' => 'icon-setting',
'sort' => 3,
], [
'key' => 'general.magic_ai.settings',
'name' => 'admin::app.configuration.index.magic-ai.settings.title',
'info' => 'admin::app.configuration.index.magic-ai.settings.info',
'sort' => 1,
'fields' => [
[
'name' => 'enable',
'title' => 'admin::app.configuration.index.magic-ai.settings.enable',
'type' => 'boolean',
'channel_based' => true,
], [
'name' => 'api_key',
'title' => 'admin::app.configuration.index.magic-ai.settings.api-key',
'type' => 'password',
'depends' => 'enable:1',
'validation' => 'required_if:enable,1',
'info' => 'admin::app.configuration.index.magic-ai.settings.api-key-info',
], [
'name' => 'model',
'title' => 'admin::app.configuration.index.magic-ai.settings.models.title',
'type' => 'select',
'channel_based' => true,
'depends' => 'enable:1',
'options' => [
[
'title' => 'admin::app.configuration.index.magic-ai.settings.models.gpt-4o',
'value' => 'openai/chatgpt-4o-latest',
], [
'title' => 'admin::app.configuration.index.magic-ai.settings.models.gpt-4o-mini',
'value' => 'openai/gpt-4o-mini',
], [
'title' => 'admin::app.configuration.index.magic-ai.settings.models.gemini-2-0-flash-001',
'value' => 'google/gemini-2.0-flash-001',
], [
'title' => 'admin::app.configuration.index.magic-ai.settings.models.deepseek-r1',
'value' => 'deepseek/deepseek-r1-distill-llama-8b',
], [
'title' => 'admin::app.configuration.index.magic-ai.settings.models.llama-3-2-3b-instruct',
'value' => 'meta-llama/llama-3.2-3b-instruct',
], [
'title' => 'admin::app.configuration.index.magic-ai.settings.models.grok-2-1212',
'value' => 'x-ai/grok-2-1212',
],
],
], [
'name' => 'other_model',
'title' => 'admin::app.configuration.index.magic-ai.settings.other',
'type' => 'text',
'info' => 'admin::app.configuration.index.magic-ai.settings.other-model',
'default' => null,
'depends' => 'enable:1',
],
],
], [
'key' => 'general.magic_ai.doc_generation',
'name' => 'admin::app.configuration.index.magic-ai.settings.doc-generation',
'info' => 'admin::app.configuration.index.magic-ai.settings.doc-generation-info',
'sort' => 2,
'fields' => [
[
'name' => 'enabled',
'title' => 'admin::app.configuration.index.magic-ai.settings.enable',
'type' => 'boolean',
],
],
],
/**
* Email.
*/
[
'key' => 'email',
'name' => 'admin::app.configuration.index.email.title',
'info' => 'admin::app.configuration.index.email.info',
'sort' => 2,
], [
'key' => 'email.imap',
'name' => 'admin::app.configuration.index.email.imap.title',
'info' => 'admin::app.configuration.index.email.imap.info',
'icon' => 'icon-setting',
'sort' => 1,
], [
'key' => 'email.imap.account',
'name' => 'admin::app.configuration.index.email.imap.account.title',
'info' => 'admin::app.configuration.index.email.imap.account.title-info',
'sort' => 1,
'fields' => [
[
'name' => 'host',
'title' => 'admin::app.configuration.index.email.imap.account.host',
'type' => 'text',
'default' => config('imap.accounts.default.host'),
],
[
'name' => 'port',
'title' => 'admin::app.configuration.index.email.imap.account.port',
'type' => 'text',
'default' => config('imap.accounts.default.port'),
],
[
'name' => 'encryption',
'title' => 'admin::app.configuration.index.email.imap.account.encryption',
'type' => 'text',
'default' => config('imap.accounts.default.encryption'),
],
[
'name' => 'validate_cert',
'title' => 'admin::app.configuration.index.email.imap.account.validate-cert',
'type' => 'boolean',
'default' => config('imap.accounts.default.validate_cert'),
],
[
'name' => 'username',
'title' => 'admin::app.configuration.index.email.imap.account.username',
'type' => 'text',
'default' => config('imap.accounts.default.username'),
],
[
'name' => 'password',
'title' => 'admin::app.configuration.index.email.imap.account.password',
'type' => 'password',
'default' => config('imap.accounts.default.password'),
],
],
],
];

View File

@@ -0,0 +1,299 @@
<?php
return [
/**
* Dashboard.
*/
[
'key' => 'dashboard',
'name' => 'admin::app.layouts.dashboard',
'route' => 'admin.dashboard.index',
'sort' => 1,
'icon-class' => 'icon-dashboard',
],
/**
* Leads.
*/
[
'key' => 'leads',
'name' => 'admin::app.layouts.leads',
'route' => 'admin.leads.index',
'sort' => 2,
'icon-class' => 'icon-leads',
],
/**
* Quotes.
*/
[
'key' => 'quotes',
'name' => 'admin::app.layouts.quotes',
'route' => 'admin.quotes.index',
'sort' => 3,
'icon-class' => 'icon-quote',
],
/**
* Emails.
*/
[
'key' => 'mail',
'name' => 'admin::app.layouts.mail.title',
'route' => 'admin.mail.index',
'params' => ['route' => 'inbox'],
'sort' => 4,
'icon-class' => 'icon-mail',
], [
'key' => 'mail.inbox',
'name' => 'admin::app.layouts.mail.inbox',
'route' => 'admin.mail.index',
'params' => ['route' => 'inbox'],
'sort' => 2,
'icon-class' => '',
], [
'key' => 'mail.draft',
'name' => 'admin::app.layouts.mail.draft',
'route' => 'admin.mail.index',
'params' => ['route' => 'draft'],
'sort' => 3,
'icon-class' => '',
], [
'key' => 'mail.outbox',
'name' => 'admin::app.layouts.mail.outbox',
'route' => 'admin.mail.index',
'params' => ['route' => 'outbox'],
'sort' => 4,
'icon-class' => '',
], [
'key' => 'mail.sent',
'name' => 'admin::app.layouts.mail.sent',
'route' => 'admin.mail.index',
'params' => ['route' => 'sent'],
'sort' => 4,
'icon-class' => '',
], [
'key' => 'mail.trash',
'name' => 'admin::app.layouts.mail.trash',
'route' => 'admin.mail.index',
'params' => ['route' => 'trash'],
'sort' => 5,
'icon-class' => '',
],
// , [
// 'key' => 'mail.setting',
// 'name' => 'admin::app.layouts.mail.setting',
// 'route' => 'admin.mail.index',
// 'params' => ['route' => 'setting'],
// 'sort' => 5,
// ]
/**
* Activities.
*/
[
'key' => 'activities',
'name' => 'admin::app.layouts.activities',
'route' => 'admin.activities.index',
'sort' => 5,
'icon-class' => 'icon-activity',
],
/**
* Contacts.
*/
[
'key' => 'contacts',
'name' => 'admin::app.layouts.contacts',
'route' => 'admin.contacts.persons.index',
'sort' => 6,
'icon-class' => 'icon-contact',
], [
'key' => 'contacts.persons',
'name' => 'admin::app.layouts.persons',
'route' => 'admin.contacts.persons.index',
'sort' => 1,
'icon-class' => '',
], [
'key' => 'contacts.organizations',
'name' => 'admin::app.layouts.organizations',
'route' => 'admin.contacts.organizations.index',
'sort' => 2,
'icon-class' => '',
],
/**
* Products.
*/
[
'key' => 'products',
'name' => 'admin::app.layouts.products',
'route' => 'admin.products.index',
'sort' => 7,
'icon-class' => 'icon-product',
],
/**
* Settings.
*/
[
'key' => 'settings',
'name' => 'admin::app.layouts.settings',
'route' => 'admin.settings.index',
'sort' => 8,
'icon-class' => 'icon-setting',
], [
'key' => 'settings.user',
'name' => 'admin::app.layouts.user',
'route' => 'admin.settings.groups.index',
'info' => 'admin::app.layouts.user-info',
'sort' => 1,
'icon-class' => 'icon-settings-group',
], [
'key' => 'settings.user.groups',
'name' => 'admin::app.layouts.groups',
'info' => 'admin::app.layouts.groups-info',
'route' => 'admin.settings.groups.index',
'sort' => 1,
'icon-class' => 'icon-settings-group',
], [
'key' => 'settings.user.roles',
'name' => 'admin::app.layouts.roles',
'info' => 'admin::app.layouts.roles-info',
'route' => 'admin.settings.roles.index',
'sort' => 2,
'icon-class' => 'icon-role',
], [
'key' => 'settings.user.users',
'name' => 'admin::app.layouts.users',
'info' => 'admin::app.layouts.users-info',
'route' => 'admin.settings.users.index',
'sort' => 3,
'icon-class' => 'icon-user',
], [
'key' => 'settings.lead',
'name' => 'admin::app.layouts.lead',
'info' => 'admin::app.layouts.lead-info',
'route' => 'admin.settings.pipelines.index',
'sort' => 2,
'icon-class' => '',
], [
'key' => 'settings.lead.pipelines',
'name' => 'admin::app.layouts.pipelines',
'info' => 'admin::app.layouts.pipelines-info',
'route' => 'admin.settings.pipelines.index',
'sort' => 1,
'icon-class' => 'icon-settings-pipeline',
], [
'key' => 'settings.lead.sources',
'name' => 'admin::app.layouts.sources',
'info' => 'admin::app.layouts.sources-info',
'route' => 'admin.settings.sources.index',
'sort' => 2,
'icon-class' => 'icon-settings-sources',
], [
'key' => 'settings.lead.types',
'name' => 'admin::app.layouts.types',
'info' => 'admin::app.layouts.types-info',
'route' => 'admin.settings.types.index',
'sort' => 3,
'icon-class' => 'icon-settings-type',
], [
'key' => 'settings.warehouse',
'name' => 'admin::app.layouts.warehouse',
'info' => 'admin::app.layouts.warehouses-info',
'route' => 'admin.settings.pipelines.index',
'icon-class' => '',
'sort' => 2,
], [
'key' => 'settings.warehouse.warehouses',
'name' => 'admin::app.layouts.warehouses',
'info' => 'admin::app.layouts.warehouses-info',
'route' => 'admin.settings.warehouses.index',
'sort' => 1,
'icon-class' => 'icon-settings-warehouse',
], [
'key' => 'settings.automation',
'name' => 'admin::app.layouts.automation',
'info' => 'admin::app.layouts.automation-info',
'route' => 'admin.settings.attributes.index',
'sort' => 3,
'icon-class' => '',
], [
'key' => 'settings.automation.attributes',
'name' => 'admin::app.layouts.attributes',
'info' => 'admin::app.layouts.attributes-info',
'route' => 'admin.settings.attributes.index',
'sort' => 1,
'icon-class' => 'icon-attribute',
], [
'key' => 'settings.automation.email_templates',
'name' => 'admin::app.layouts.email-templates',
'info' => 'admin::app.layouts.email-templates-info',
'route' => 'admin.settings.email_templates.index',
'sort' => 2,
'icon-class' => 'icon-settings-mail',
], [
'key' => 'settings.automation.events',
'name' => 'admin::app.layouts.events',
'info' => 'admin::app.layouts.events-info',
'route' => 'admin.settings.marketing.events.index',
'sort' => 2,
'icon-class' => 'icon-calendar',
], [
'key' => 'settings.automation.campaigns',
'name' => 'admin::app.layouts.campaigns',
'info' => 'admin::app.layouts.campaigns-info',
'route' => 'admin.settings.marketing.campaigns.index',
'sort' => 2,
'icon-class' => 'icon-note',
], [
'key' => 'settings.automation.webhooks',
'name' => 'admin::app.layouts.webhooks',
'info' => 'admin::app.layouts.webhooks-info',
'route' => 'admin.settings.webhooks.index',
'sort' => 2,
'icon-class' => 'icon-settings-webhooks',
], [
'key' => 'settings.automation.workflows',
'name' => 'admin::app.layouts.workflows',
'info' => 'admin::app.layouts.workflows-info',
'route' => 'admin.settings.workflows.index',
'sort' => 3,
'icon-class' => 'icon-settings-flow',
],
[
'key' => 'settings.automation.data_transfer',
'name' => 'admin::app.layouts.data_transfer',
'info' => 'admin::app.layouts.data_transfer_info',
'route' => 'admin.settings.data_transfer.imports.index',
'sort' => 4,
'icon-class' => 'icon-download',
], [
'key' => 'settings.other_settings',
'name' => 'admin::app.layouts.other-settings',
'info' => 'admin::app.layouts.other-settings-info',
'route' => 'admin.settings.tags.index',
'sort' => 4,
'icon-class' => 'icon-settings',
], [
'key' => 'settings.other_settings.tags',
'name' => 'admin::app.layouts.tags',
'info' => 'admin::app.layouts.tags-info',
'route' => 'admin.settings.tags.index',
'sort' => 1,
'icon-class' => 'icon-settings-tag',
],
/**
* Configuration.
*/
[
'key' => 'configuration',
'name' => 'admin::app.layouts.configuration',
'route' => 'admin.configuration.index',
'sort' => 9,
'icon-class' => 'icon-configuration',
],
];

View File

@@ -0,0 +1,235 @@
<?php
namespace Webkul\Admin\DataGrids\Activity;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Webkul\Admin\Traits\ProvideDropdownOptions;
use Webkul\DataGrid\DataGrid;
use Webkul\Lead\Repositories\LeadRepository;
use Webkul\User\Repositories\UserRepository;
class ActivityDataGrid extends DataGrid
{
use ProvideDropdownOptions;
/**
* Prepare query builder.
*/
public function prepareQueryBuilder(): Builder
{
$queryBuilder = DB::table('activities')
->distinct()
->select(
'activities.*',
'leads.id as lead_id',
'leads.title as lead_title',
'leads.lead_pipeline_id',
'users.id as created_by_id',
'users.name as created_by',
)
->leftJoin('activity_participants', 'activities.id', '=', 'activity_participants.activity_id')
->leftJoin('lead_activities', 'activities.id', '=', 'lead_activities.activity_id')
->leftJoin('leads', 'lead_activities.lead_id', '=', 'leads.id')
->leftJoin('users', 'activities.user_id', '=', 'users.id')
->whereIn('type', ['call', 'meeting', 'lunch'])
->where(function ($query) {
if ($userIds = bouncer()->getAuthorizedUserIds()) {
$query->whereIn('activities.user_id', $userIds)
->orWhereIn('activity_participants.user_id', $userIds);
}
})->groupBy('activities.id', 'leads.id', 'users.id');
$this->addFilter('id', 'activities.id');
$this->addFilter('title', 'activities.title');
$this->addFilter('schedule_from', 'activities.schedule_from');
$this->addFilter('created_by', 'users.name');
$this->addFilter('created_by_id', 'users.name');
$this->addFilter('created_at', 'activities.created_at');
$this->addFilter('lead_title', 'leads.title');
return $queryBuilder;
}
/**
* Prepare columns.
*/
public function prepareColumns(): void
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.activities.index.datagrid.id'),
'type' => 'integer',
'searchable' => true,
'filterable' => true,
'sortable' => true,
]);
$this->addColumn([
'index' => 'is_done',
'label' => trans('admin::app.activities.index.datagrid.is_done'),
'type' => 'string',
'dropdown_options' => $this->getBooleanDropdownOptions('yes_no'),
'searchable' => false,
'closure' => fn ($row) => view('admin::activities.datagrid.is-done', compact('row'))->render(),
]);
$this->addColumn([
'index' => 'title',
'label' => trans('admin::app.activities.index.datagrid.title'),
'type' => 'string',
'searchable' => true,
'filterable' => true,
'sortable' => true,
]);
$this->addColumn([
'index' => 'created_by_id',
'label' => trans('admin::app.activities.index.datagrid.created_by'),
'type' => 'string',
'searchable' => false,
'sortable' => true,
'filterable' => true,
'filterable_type' => 'searchable_dropdown',
'filterable_options' => [
'repository' => UserRepository::class,
'column' => [
'label' => 'name',
'value' => 'name',
],
],
'closure' => function ($row) {
$route = urldecode(route('admin.settings.users.index', ['id[eq]' => $row->created_by_id]));
return "<a class='text-brandColor hover:underline' href='".$route."'>".$row->created_by.'</a>';
},
]);
$this->addColumn([
'index' => 'comment',
'label' => trans('admin::app.activities.index.datagrid.comment'),
'type' => 'string',
]);
$this->addColumn([
'index' => 'lead_title',
'label' => trans('admin::app.activities.index.datagrid.lead'),
'type' => 'string',
'searchable' => true,
'sortable' => true,
'filterable' => true,
'filterable_type' => 'searchable_dropdown',
'filterable_options' => [
'repository' => LeadRepository::class,
'column' => [
'label' => 'title',
'value' => 'title',
],
],
'closure' => function ($row) {
if ($row->lead_title == null) {
return "<span class='text-gray-800 dark:text-gray-300'>N/A</span>";
}
$route = urldecode(route('admin.leads.view', $row->lead_id));
return "<a class='text-brandColor hover:underline' target='_blank' href='".$route."'>".$row->lead_title.'</a>';
},
]);
$this->addColumn([
'index' => 'type',
'label' => trans('admin::app.activities.index.datagrid.type'),
'type' => 'string',
'searchable' => false,
'filterable' => false,
'sortable' => true,
'closure' => fn ($row) => trans('admin::app.activities.index.datagrid.'.$row->type),
]);
$this->addColumn([
'index' => 'schedule_from',
'label' => trans('admin::app.activities.index.datagrid.schedule_from'),
'type' => 'date',
'sortable' => true,
'searchable' => true,
'filterable' => true,
'closure' => fn ($row) => core()->formatDate($row->schedule_from),
]);
$this->addColumn([
'index' => 'schedule_to',
'label' => trans('admin::app.activities.index.datagrid.schedule_to'),
'type' => 'date',
'sortable' => true,
'searchable' => true,
'filterable' => true,
'closure' => fn ($row) => core()->formatDate($row->schedule_to),
]);
$this->addColumn([
'index' => 'created_at',
'label' => trans('admin::app.activities.index.datagrid.created_at'),
'type' => 'date',
'sortable' => true,
'searchable' => true,
'filterable' => true,
'closure' => fn ($row) => core()->formatDate($row->created_at),
]);
}
/**
* Prepare actions.
*/
public function prepareActions(): void
{
if (bouncer()->hasPermission('activities.edit')) {
$this->addAction([
'index' => 'edit',
'icon' => 'icon-edit',
'title' => trans('admin::app.activities.index.datagrid.edit'),
'method' => 'GET',
'url' => fn ($row) => route('admin.activities.edit', $row->id),
]);
}
if (bouncer()->hasPermission('activities.delete')) {
$this->addAction([
'index' => 'delete',
'icon' => 'icon-delete',
'title' => trans('admin::app.activities.index.datagrid.update'),
'method' => 'DELETE',
'url' => fn ($row) => route('admin.activities.delete', $row->id),
]);
}
}
/**
* Prepare mass actions.
*/
public function prepareMassActions(): void
{
$this->addMassAction([
'icon' => 'icon-delete',
'title' => trans('admin::app.activities.index.datagrid.mass-delete'),
'method' => 'POST',
'url' => route('admin.activities.mass_delete'),
]);
$this->addMassAction([
'title' => trans('admin::app.activities.index.datagrid.mass-update'),
'url' => route('admin.activities.mass_update'),
'method' => 'POST',
'options' => [
[
'label' => trans('admin::app.activities.index.datagrid.done'),
'value' => 1,
], [
'label' => trans('admin::app.activities.index.datagrid.not-done'),
'value' => 0,
],
],
]);
}
}

View File

@@ -0,0 +1,124 @@
<?php
namespace Webkul\Admin\DataGrids\Contact;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Webkul\Contact\Repositories\PersonRepository;
use Webkul\DataGrid\DataGrid;
class OrganizationDataGrid extends DataGrid
{
/**
* Create datagrid instance.
*
* @return void
*/
public function __construct(protected PersonRepository $personRepository) {}
/**
* Prepare query builder.
*/
public function prepareQueryBuilder(): Builder
{
return DB::table('organizations')
->addSelect(
'organizations.id',
'organizations.name',
'organizations.address',
'organizations.created_at'
);
if ($userIds = bouncer()->getAuthorizedUserIds()) {
$queryBuilder->whereIn('organizations.user_id', $userIds);
}
$this->addFilter('id', 'organizations.id');
$this->addFilter('organization', 'organizations.name');
}
/**
* Add columns.
*/
public function prepareColumns(): void
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.contacts.organizations.index.datagrid.id'),
'type' => 'integer',
'filterable' => true,
'sortable' => true,
]);
$this->addColumn([
'index' => 'name',
'label' => trans('admin::app.contacts.organizations.index.datagrid.name'),
'type' => 'string',
'sortable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'persons_count',
'label' => trans('admin::app.contacts.organizations.index.datagrid.persons-count'),
'type' => 'string',
'searchable' => false,
'sortable' => false,
'filterable' => false,
'closure' => function ($row) {
$personsCount = $this->personRepository->findWhere(['organization_id' => $row->id])->count();
return $personsCount;
},
]);
$this->addColumn([
'index' => 'created_at',
'label' => trans('admin::app.settings.tags.index.datagrid.created-at'),
'type' => 'date',
'searchable' => true,
'filterable' => true,
'filterable_type' => 'date_range',
'sortable' => true,
'closure' => fn ($row) => core()->formatDate($row->created_at),
]);
}
/**
* Prepare actions.
*/
public function prepareActions(): void
{
if (bouncer()->hasPermission('contacts.organizations.edit')) {
$this->addAction([
'icon' => 'icon-edit',
'title' => trans('admin::app.contacts.organizations.index.datagrid.edit'),
'method' => 'GET',
'url' => fn ($row) => route('admin.contacts.organizations.edit', $row->id),
]);
}
if (bouncer()->hasPermission('contacts.organizations.delete')) {
$this->addAction([
'icon' => 'icon-delete',
'title' => trans('admin::app.contacts.organizations.index.datagrid.delete'),
'method' => 'DELETE',
'url' => fn ($row) => route('admin.contacts.organizations.delete', $row->id),
]);
}
}
/**
* Prepare mass actions.
*/
public function prepareMassActions(): void
{
$this->addMassAction([
'icon' => 'icon-delete',
'title' => trans('admin::app.contacts.organizations.index.datagrid.delete'),
'method' => 'PUT',
'url' => route('admin.contacts.organizations.mass_delete'),
]);
}
}

View File

@@ -0,0 +1,160 @@
<?php
namespace Webkul\Admin\DataGrids\Contact;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Webkul\Contact\Repositories\OrganizationRepository;
use Webkul\DataGrid\DataGrid;
class PersonDataGrid extends DataGrid
{
/**
* Create a new class instance.
*
* @return void
*/
public function __construct(protected OrganizationRepository $organizationRepository) {}
/**
* Prepare query builder.
*/
public function prepareQueryBuilder(): Builder
{
$queryBuilder = DB::table('persons')
->addSelect(
'persons.id',
'persons.name as person_name',
'persons.emails',
'persons.contact_numbers',
'organizations.name as organization',
'organizations.id as organization_id'
)
->leftJoin('organizations', 'persons.organization_id', '=', 'organizations.id');
if ($userIds = bouncer()->getAuthorizedUserIds()) {
$queryBuilder->whereIn('persons.user_id', $userIds);
}
$this->addFilter('id', 'persons.id');
$this->addFilter('person_name', 'persons.name');
$this->addFilter('organization', 'organizations.name');
return $queryBuilder;
}
/**
* Add columns.
*/
public function prepareColumns(): void
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.contacts.persons.index.datagrid.id'),
'type' => 'integer',
'filterable' => true,
'sortable' => true,
'searchable' => true,
]);
$this->addColumn([
'index' => 'person_name',
'label' => trans('admin::app.contacts.persons.index.datagrid.name'),
'type' => 'string',
'sortable' => true,
'filterable' => true,
'searchable' => true,
]);
$this->addColumn([
'index' => 'emails',
'label' => trans('admin::app.contacts.persons.index.datagrid.emails'),
'type' => 'string',
'sortable' => false,
'filterable' => true,
'searchable' => true,
'closure' => fn ($row) => collect(json_decode($row->emails, true) ?? [])->pluck('value')->join(', '),
]);
$this->addColumn([
'index' => 'contact_numbers',
'label' => trans('admin::app.contacts.persons.index.datagrid.contact-numbers'),
'type' => 'string',
'sortable' => true,
'filterable' => true,
'searchable' => true,
'closure' => fn ($row) => collect(json_decode($row->contact_numbers, true) ?? [])->pluck('value')->join(', '),
]);
$this->addColumn([
'index' => 'organization',
'label' => trans('admin::app.contacts.persons.index.datagrid.organization-name'),
'type' => 'string',
'searchable' => true,
'filterable' => true,
'sortable' => true,
'filterable_type' => 'searchable_dropdown',
'filterable_options' => [
'repository' => OrganizationRepository::class,
'column' => [
'label' => 'name',
'value' => 'name',
],
],
]);
}
/**
* Prepare actions.
*/
public function prepareActions(): void
{
if (bouncer()->hasPermission('contacts.persons.view')) {
$this->addAction([
'icon' => 'icon-eye',
'title' => trans('admin::app.contacts.persons.index.datagrid.view'),
'method' => 'GET',
'url' => function ($row) {
return route('admin.contacts.persons.view', $row->id);
},
]);
}
if (bouncer()->hasPermission('contacts.persons.edit')) {
$this->addAction([
'icon' => 'icon-edit',
'title' => trans('admin::app.contacts.persons.index.datagrid.edit'),
'method' => 'GET',
'url' => function ($row) {
return route('admin.contacts.persons.edit', $row->id);
},
]);
}
if (bouncer()->hasPermission('contacts.persons.delete')) {
$this->addAction([
'icon' => 'icon-delete',
'title' => trans('admin::app.contacts.persons.index.datagrid.delete'),
'method' => 'DELETE',
'url' => function ($row) {
return route('admin.contacts.persons.delete', $row->id);
},
]);
}
}
/**
* Prepare mass actions.
*/
public function prepareMassActions(): void
{
if (bouncer()->hasPermission('contacts.persons.delete')) {
$this->addMassAction([
'icon' => 'icon-delete',
'title' => trans('admin::app.contacts.persons.index.datagrid.delete'),
'method' => 'POST',
'url' => route('admin.contacts.persons.mass_delete'),
]);
}
}
}

View File

@@ -0,0 +1,326 @@
<?php
namespace Webkul\Admin\DataGrids\Lead;
use Illuminate\Contracts\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Webkul\DataGrid\DataGrid;
use Webkul\Lead\Repositories\PipelineRepository;
use Webkul\Lead\Repositories\SourceRepository;
use Webkul\Lead\Repositories\StageRepository;
use Webkul\Lead\Repositories\TypeRepository;
use Webkul\Tag\Repositories\TagRepository;
use Webkul\User\Repositories\UserRepository;
class LeadDataGrid extends DataGrid
{
/**
* Pipeline instance.
*
* @var \Webkul\Contract\Repositories\Pipeline
*/
protected $pipeline;
/**
* Create data grid instance.
*
* @return void
*/
public function __construct(
protected PipelineRepository $pipelineRepository,
protected StageRepository $stageRepository,
protected SourceRepository $sourceRepository,
protected TypeRepository $typeRepository,
protected UserRepository $userRepository,
protected TagRepository $tagRepository,
) {
if (request('pipeline_id')) {
$this->pipeline = $this->pipelineRepository->find(request('pipeline_id'));
} else {
$this->pipeline = $this->pipelineRepository->getDefaultPipeline();
}
}
/**
* Prepare query builder.
*/
public function prepareQueryBuilder(): Builder
{
$tablePrefix = DB::getTablePrefix();
$queryBuilder = DB::table('leads')
->addSelect(
'leads.id',
'leads.title',
'leads.status',
'leads.lead_value',
'leads.expected_close_date',
'lead_sources.name as lead_source_name',
'lead_types.name as lead_type_name',
'leads.created_at',
'lead_pipeline_stages.name as stage',
'lead_tags.tag_id as tag_id',
'users.id as user_id',
'users.name as sales_person',
'persons.id as person_id',
'persons.name as person_name',
'tags.name as tag_name',
'lead_pipelines.rotten_days as pipeline_rotten_days',
'lead_pipeline_stages.code as stage_code',
DB::raw('CASE WHEN DATEDIFF(NOW(),'.$tablePrefix.'leads.created_at) >='.$tablePrefix.'lead_pipelines.rotten_days THEN 1 ELSE 0 END as rotten_lead'),
)
->leftJoin('users', 'leads.user_id', '=', 'users.id')
->leftJoin('persons', 'leads.person_id', '=', 'persons.id')
->leftJoin('lead_types', 'leads.lead_type_id', '=', 'lead_types.id')
->leftJoin('lead_pipeline_stages', 'leads.lead_pipeline_stage_id', '=', 'lead_pipeline_stages.id')
->leftJoin('lead_sources', 'leads.lead_source_id', '=', 'lead_sources.id')
->leftJoin('lead_pipelines', 'leads.lead_pipeline_id', '=', 'lead_pipelines.id')
->leftJoin('lead_tags', 'leads.id', '=', 'lead_tags.lead_id')
->leftJoin('tags', 'tags.id', '=', 'lead_tags.tag_id')
->groupBy('leads.id')
->where('leads.lead_pipeline_id', $this->pipeline->id);
if ($userIds = bouncer()->getAuthorizedUserIds()) {
$queryBuilder->whereIn('leads.user_id', $userIds);
}
if (! is_null(request()->input('rotten_lead.in'))) {
$queryBuilder->havingRaw($tablePrefix.'rotten_lead = '.request()->input('rotten_lead.in'));
}
$this->addFilter('id', 'leads.id');
$this->addFilter('user', 'leads.user_id');
$this->addFilter('sales_person', 'users.name');
$this->addFilter('lead_source_name', 'lead_sources.id');
$this->addFilter('lead_type_name', 'lead_types.id');
$this->addFilter('person_name', 'persons.name');
$this->addFilter('type', 'lead_pipeline_stages.code');
$this->addFilter('stage', 'lead_pipeline_stages.id');
$this->addFilter('tag_name', 'tags.name');
$this->addFilter('expected_close_date', 'leads.expected_close_date');
$this->addFilter('created_at', 'leads.created_at');
$this->addFilter('rotten_lead', DB::raw('DATEDIFF(NOW(), '.$tablePrefix.'leads.created_at) >= '.$tablePrefix.'lead_pipelines.rotten_days'));
return $queryBuilder;
}
/**
* Prepare columns.
*/
public function prepareColumns(): void
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.leads.index.datagrid.id'),
'type' => 'integer',
'sortable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'sales_person',
'label' => trans('admin::app.leads.index.datagrid.sales-person'),
'type' => 'string',
'searchable' => false,
'sortable' => true,
'filterable' => true,
'filterable_type' => 'searchable_dropdown',
'filterable_options' => [
'repository' => UserRepository::class,
'column' => [
'label' => 'name',
'value' => 'name',
],
],
]);
$this->addColumn([
'index' => 'title',
'label' => trans('admin::app.leads.index.datagrid.subject'),
'type' => 'string',
'searchable' => true,
'sortable' => true,
]);
$this->addColumn([
'index' => 'lead_source_name',
'label' => trans('admin::app.leads.index.datagrid.source'),
'type' => 'string',
'searchable' => false,
'sortable' => true,
'filterable' => true,
'filterable_type' => 'dropdown',
'filterable_options' => $this->sourceRepository->all(['name as label', 'id as value'])->toArray(),
]);
$this->addColumn([
'index' => 'lead_value',
'label' => trans('admin::app.leads.index.datagrid.lead-value'),
'type' => 'string',
'sortable' => true,
'searchable' => false,
'filterable' => true,
'closure' => fn ($row) => core()->formatBasePrice($row->lead_value, 2),
]);
$this->addColumn([
'index' => 'lead_type_name',
'label' => trans('admin::app.leads.index.datagrid.lead-type'),
'type' => 'string',
'searchable' => false,
'sortable' => true,
'filterable' => true,
'filterable_type' => 'dropdown',
'filterable_options' => $this->typeRepository->all(['name as label', 'id as value'])->toArray(),
]);
$this->addColumn([
'index' => 'tag_name',
'label' => trans('admin::app.leads.index.datagrid.tag-name'),
'type' => 'string',
'searchable' => false,
'sortable' => true,
'filterable' => true,
'filterable_type' => 'searchable_dropdown',
'closure' => fn ($row) => $row->tag_name ?? '--',
'filterable_options' => [
'repository' => TagRepository::class,
'column' => [
'label' => 'name',
'value' => 'name',
],
],
]);
$this->addColumn([
'index' => 'person_name',
'label' => trans('admin::app.leads.index.datagrid.contact-person'),
'type' => 'string',
'searchable' => false,
'sortable' => true,
'filterable' => true,
'filterable_type' => 'searchable_dropdown',
'filterable_options' => [
'repository' => \Webkul\Contact\Repositories\PersonRepository::class,
'column' => [
'label' => 'name',
'value' => 'name',
],
],
'closure' => function ($row) {
$route = route('admin.contacts.persons.view', $row->person_id);
return "<a class=\"text-brandColor transition-all hover:underline\" href='".$route."'>".$row->person_name.'</a>';
},
]);
$this->addColumn([
'index' => 'stage',
'label' => trans('admin::app.leads.index.datagrid.stage'),
'type' => 'string',
'searchable' => false,
'sortable' => true,
'filterable' => true,
'filterable_type' => 'dropdown',
'filterable_options' => $this->pipeline->stages->pluck('name', 'id')
->map(function ($name, $id) {
return ['value' => $id, 'label' => $name];
})
->values()
->all(),
]);
$this->addColumn([
'index' => 'rotten_lead',
'label' => trans('admin::app.leads.index.datagrid.rotten-lead'),
'type' => 'string',
'sortable' => true,
'searchable' => false,
'closure' => function ($row) {
if (! $row->rotten_lead) {
return trans('admin::app.leads.index.datagrid.no');
}
if (in_array($row->stage_code, ['won', 'lost'])) {
return trans('admin::app.leads.index.datagrid.no');
}
return trans('admin::app.leads.index.datagrid.yes');
},
]);
$this->addColumn([
'index' => 'expected_close_date',
'label' => trans('admin::app.leads.index.datagrid.date-to'),
'type' => 'date',
'searchable' => false,
'sortable' => true,
'filterable' => true,
'filterable_type' => 'date_range',
'closure' => function ($row) {
if (! $row->expected_close_date) {
return '--';
}
return $row->expected_close_date;
},
]);
$this->addColumn([
'index' => 'created_at',
'label' => trans('admin::app.leads.index.datagrid.created-at'),
'type' => 'date',
'searchable' => false,
'sortable' => true,
'filterable' => true,
'filterable_type' => 'date_range',
]);
}
/**
* Prepare actions.
*/
public function prepareActions(): void
{
if (bouncer()->hasPermission('leads.view')) {
$this->addAction([
'icon' => 'icon-eye',
'title' => trans('admin::app.leads.index.datagrid.view'),
'method' => 'GET',
'url' => fn ($row) => route('admin.leads.view', $row->id),
]);
}
if (bouncer()->hasPermission('leads.delete')) {
$this->addAction([
'icon' => 'icon-delete',
'title' => trans('admin::app.leads.index.datagrid.delete'),
'method' => 'delete',
'url' => fn ($row) => route('admin.leads.delete', $row->id),
]);
}
}
/**
* Prepare mass actions.
*/
public function prepareMassActions(): void
{
$this->addMassAction([
'icon' => 'icon-delete',
'title' => trans('admin::app.leads.index.datagrid.mass-delete'),
'method' => 'POST',
'url' => route('admin.leads.mass_delete'),
]);
$this->addMassAction([
'title' => trans('admin::app.leads.index.datagrid.mass-update'),
'url' => route('admin.leads.mass_update'),
'method' => 'POST',
'options' => $this->pipeline->stages->map(fn ($stage) => [
'label' => $stage->name,
'value' => $stage->id,
])->toArray(),
]);
}
}

View File

@@ -0,0 +1,212 @@
<?php
namespace Webkul\Admin\DataGrids\Mail;
use Carbon\Carbon;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Webkul\DataGrid\DataGrid;
use Webkul\Email\Repositories\EmailRepository;
use Webkul\Tag\Repositories\TagRepository;
class EmailDataGrid extends DataGrid
{
/**
* Default sort column of datagrid.
*
* @var ?string
*/
protected $sortColumn = 'created_at';
/**
* Prepare query builder.
*/
public function prepareQueryBuilder(): Builder
{
$queryBuilder = DB::table('emails')
->select(
'emails.id',
'emails.name',
'emails.from',
'emails.subject',
'emails.reply',
'emails.is_read',
'emails.created_at',
'tags.name as tags',
DB::raw('COUNT(DISTINCT '.DB::getTablePrefix().'email_attachments.id) as attachments')
)
->leftJoin('email_attachments', 'emails.id', '=', 'email_attachments.email_id')
->leftJoin('email_tags', 'emails.id', '=', 'email_tags.email_id')
->leftJoin('tags', 'tags.id', '=', 'email_tags.tag_id')
->groupBy('emails.id')
->where('folders', 'like', '%"'.request('route').'"%')
->whereNull('parent_id');
$this->addFilter('id', 'emails.id');
$this->addFilter('name', 'emails.name');
$this->addFilter('tags', 'tags.name');
$this->addFilter('created_at', 'emails.created_at');
return $queryBuilder;
}
/**
* Prepare Columns.
*/
public function prepareColumns(): void
{
$this->addColumn([
'index' => 'attachments',
'label' => trans('admin::app.mail.index.datagrid.attachments'),
'type' => 'string',
'searchable' => false,
'filterable' => false,
'sortable' => false,
'closure' => fn ($row) => $row->attachments ? '<i class="icon-attachment text-2xl"></i>' : '',
]);
$this->addColumn([
'index' => 'name',
'label' => trans('admin::app.mail.index.datagrid.from'),
'type' => 'string',
'sortable' => true,
'searchable' => true,
'filterable' => true,
'closure' => function ($row) {
return $row->name
? trim($row->name, '"')
: trim($row->from, '"');
},
]);
$this->addColumn([
'index' => 'subject',
'label' => trans('admin::app.mail.index.datagrid.subject'),
'type' => 'string',
'sortable' => true,
'searchable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'reply',
'label' => trans('admin::app.mail.index.datagrid.content'),
'type' => 'string',
'sortable' => true,
'searchable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'tags',
'label' => trans('admin::app.mail.index.datagrid.tags'),
'type' => 'string',
'searchable' => false,
'sortable' => true,
'filterable' => true,
'filterable_type' => 'searchable_dropdown',
'closure' => function ($row) {
if ($email = app(EmailRepository::class)->find($row->id)) {
return $email->tags;
}
return '--';
},
'filterable_options' => [
'repository' => TagRepository::class,
'column' => [
'label' => 'name',
'value' => 'name',
],
],
]);
$this->addColumn([
'index' => 'created_at',
'label' => trans('admin::app.mail.index.datagrid.date'),
'type' => 'date',
'searchable' => true,
'filterable' => true,
'filterable_type' => 'date_range',
'sortable' => true,
'closure' => function ($row) {
return Carbon::parse($row->created_at)->isToday()
? Carbon::parse($row->created_at)->format('h:i A')
: Carbon::parse($row->created_at)->format('M d');
},
]);
}
/**
* Prepare actions.
*/
public function prepareActions(): void
{
if (bouncer()->hasPermission('mail.view')) {
$this->addAction([
'index' => 'edit',
'icon' => request('route') == 'draft'
? 'icon-edit'
: 'icon-eye',
'title' => request('route') == 'draft'
? trans('admin::app.mail.index.datagrid.edit')
: trans('admin::app.mail.index.datagrid.view'),
'method' => 'GET',
'params' => [
'type' => request('route') == 'trash'
? 'delete'
: 'trash',
],
'url' => fn ($row) => route('admin.mail.view', [request('route'), $row->id]),
]);
}
if (bouncer()->hasPermission('mail.delete')) {
$this->addAction([
'index' => 'delete',
'icon' => 'icon-delete',
'title' => trans('admin::app.mail.index.datagrid.delete'),
'method' => 'DELETE',
'params' => [
'type' => request('route') == 'trash'
? 'delete'
: 'trash',
],
'url' => fn ($row) => route('admin.mail.delete', $row->id),
]);
}
}
/**
* Prepare mass actions.
*/
public function prepareMassActions(): void
{
if (request('route') == 'trash') {
$this->addMassAction([
'title' => trans('admin::app.mail.index.datagrid.move-to-inbox'),
'method' => 'POST',
'url' => route('admin.mail.mass_update', ['folders' => ['inbox']]),
'options' => [
[
'value' => 'trash',
'label' => trans('admin::app.mail.index.datagrid.move-to-inbox'),
],
],
]);
}
$this->addMassAction([
'icon' => 'icon-delete',
'title' => request('route') == 'trash'
? trans('admin::app.mail.index.datagrid.delete')
: trans('admin::app.mail.index.datagrid.move-to-trash'),
'method' => 'POST',
'url' => route('admin.mail.mass_delete', [
'type' => request('route') == 'trash'
? 'delete'
: 'trash',
]),
]);
}
}

View File

@@ -0,0 +1,169 @@
<?php
namespace Webkul\Admin\DataGrids\Product;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Webkul\DataGrid\DataGrid;
use Webkul\Tag\Repositories\TagRepository;
class ProductDataGrid extends DataGrid
{
/**
* Prepare query builder.
*/
public function prepareQueryBuilder(): Builder
{
$tablePrefix = DB::getTablePrefix();
$queryBuilder = DB::table('products')
->leftJoin('product_inventories', 'products.id', '=', 'product_inventories.product_id')
->leftJoin('product_tags', 'products.id', '=', 'product_tags.product_id')
->leftJoin('tags', 'tags.id', '=', 'product_tags.tag_id')
->select(
'products.id',
'products.sku',
'products.name',
'products.price',
'tags.name as tag_name',
)
->addSelect(DB::raw('SUM('.$tablePrefix.'product_inventories.in_stock) as total_in_stock'))
->addSelect(DB::raw('SUM('.$tablePrefix.'product_inventories.allocated) as total_allocated'))
->addSelect(DB::raw('SUM('.$tablePrefix.'product_inventories.in_stock - '.$tablePrefix.'product_inventories.allocated) as total_on_hand'))
->groupBy('products.id');
if (request()->route('id')) {
$queryBuilder->where('product_inventories.warehouse_id', request()->route('id'));
}
$this->addFilter('id', 'products.id');
$this->addFilter('total_in_stock', DB::raw('SUM('.$tablePrefix.'product_inventories.in_stock'));
$this->addFilter('total_allocated', DB::raw('SUM('.$tablePrefix.'product_inventories.allocated'));
$this->addFilter('total_on_hand', DB::raw('SUM('.$tablePrefix.'product_inventories.in_stock - '.$tablePrefix.'product_inventories.allocated'));
$this->addFilter('tag_name', 'tags.name');
return $queryBuilder;
}
/**
* Add columns.
*/
public function prepareColumns(): void
{
$this->addColumn([
'index' => 'sku',
'label' => trans('admin::app.products.index.datagrid.sku'),
'type' => 'string',
'sortable' => true,
'searchable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'name',
'label' => trans('admin::app.products.index.datagrid.name'),
'type' => 'string',
'sortable' => true,
'searchable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'price',
'label' => trans('admin::app.products.index.datagrid.price'),
'type' => 'string',
'sortable' => true,
'searchable' => true,
'filterable' => true,
'closure' => fn ($row) => round($row->price, 2),
]);
$this->addColumn([
'index' => 'total_in_stock',
'label' => trans('admin::app.products.index.datagrid.in-stock'),
'type' => 'string',
'sortable' => true,
]);
$this->addColumn([
'index' => 'total_allocated',
'label' => trans('admin::app.products.index.datagrid.allocated'),
'type' => 'string',
'sortable' => true,
]);
$this->addColumn([
'index' => 'total_on_hand',
'label' => trans('admin::app.products.index.datagrid.on-hand'),
'type' => 'string',
'sortable' => true,
]);
$this->addColumn([
'index' => 'tag_name',
'label' => trans('admin::app.products.index.datagrid.tag-name'),
'type' => 'string',
'searchable' => false,
'sortable' => true,
'filterable' => true,
'filterable_type' => 'searchable_dropdown',
'closure' => fn ($row) => $row->tag_name ?? '--',
'filterable_options' => [
'repository' => TagRepository::class,
'column' => [
'label' => 'name',
'value' => 'name',
],
],
]);
}
/**
* Prepare actions.
*/
public function prepareActions(): void
{
if (bouncer()->hasPermission('products.view')) {
$this->addAction([
'index' => 'view',
'icon' => 'icon-eye',
'title' => trans('admin::app.products.index.datagrid.view'),
'method' => 'GET',
'url' => fn ($row) => route('admin.products.view', $row->id),
]);
}
if (bouncer()->hasPermission('products.edit')) {
$this->addAction([
'index' => 'edit',
'icon' => 'icon-edit',
'title' => trans('admin::app.products.index.datagrid.edit'),
'method' => 'GET',
'url' => fn ($row) => route('admin.products.edit', $row->id),
]);
}
if (bouncer()->hasPermission('products.delete')) {
$this->addAction([
'index' => 'delete',
'icon' => 'icon-delete',
'title' => trans('admin::app.products.index.datagrid.delete'),
'method' => 'DELETE',
'url' => fn ($row) => route('admin.products.delete', $row->id),
]);
}
}
/**
* Prepare mass actions.
*/
public function prepareMassActions(): void
{
$this->addMassAction([
'icon' => 'icon-delete',
'title' => trans('admin::app.products.index.datagrid.delete'),
'method' => 'POST',
'url' => route('admin.products.mass_delete'),
]);
}
}

View File

@@ -0,0 +1,229 @@
<?php
namespace Webkul\Admin\DataGrids\Quote;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Webkul\DataGrid\DataGrid;
class QuoteDataGrid extends DataGrid
{
/**
* Prepare query builder.
*/
public function prepareQueryBuilder(): Builder
{
$tablePrefix = DB::getTablePrefix();
$queryBuilder = DB::table('quotes')
->addSelect(
'quotes.id',
'quotes.subject',
'quotes.expired_at',
'quotes.sub_total',
'quotes.discount_amount',
'quotes.tax_amount',
'quotes.adjustment_amount',
'quotes.grand_total',
'quotes.created_at',
'users.id as user_id',
'users.name as sales_person',
'persons.id as person_id',
'persons.name as person_name',
'quotes.expired_at as expired_quotes'
)
->leftJoin('users', 'quotes.user_id', '=', 'users.id')
->leftJoin('persons', 'quotes.person_id', '=', 'persons.id');
if ($userIds = bouncer()->getAuthorizedUserIds()) {
$queryBuilder->whereIn('quotes.user_id', $userIds);
}
$this->addFilter('id', 'quotes.id');
$this->addFilter('user', 'quotes.user_id');
$this->addFilter('sales_person', 'users.name');
$this->addFilter('person_name', 'persons.name');
$this->addFilter('expired_at', 'quotes.expired_at');
$this->addFilter('created_at', 'quotes.created_at');
if (request()->input('expired_quotes.in') == 1) {
$this->addFilter('expired_quotes', DB::raw('DATEDIFF(NOW(), '.$tablePrefix.'quotes.expired_at) >= '.$tablePrefix.'NOW()'));
} else {
$this->addFilter('expired_quotes', DB::raw('DATEDIFF(NOW(), '.$tablePrefix.'quotes.expired_at) < '.$tablePrefix.'NOW()'));
}
return $queryBuilder;
}
/**
* Prepare columns.
*/
public function prepareColumns(): void
{
$this->addColumn([
'index' => 'subject',
'label' => trans('admin::app.quotes.index.datagrid.subject'),
'type' => 'string',
'filterable' => true,
'sortable' => true,
]);
$this->addColumn([
'index' => 'sales_person',
'label' => trans('admin::app.quotes.index.datagrid.sales-person'),
'type' => 'string',
'sortable' => true,
'filterable' => true,
'filterable_type' => 'searchable_dropdown',
'filterable_options' => [
'repository' => \Webkul\User\Repositories\UserRepository::class,
'column' => [
'label' => 'name',
'value' => 'name',
],
],
]);
$this->addColumn([
'index' => 'person_name',
'label' => trans('admin::app.quotes.index.datagrid.person'),
'type' => 'string',
'sortable' => true,
'filterable' => true,
'filterable_type' => 'searchable_dropdown',
'filterable_options' => [
'repository' => \Webkul\Contact\Repositories\PersonRepository::class,
'column' => [
'label' => 'name',
'value' => 'name',
],
],
'closure' => function ($row) {
$route = route('admin.contacts.persons.view', $row->person_id);
return "<a class=\"text-brandColor transition-all hover:underline\" href='".$route."'>".$row->person_name.'</a>';
},
]);
$this->addColumn([
'index' => 'sub_total',
'label' => trans('admin::app.quotes.index.datagrid.subtotal'),
'type' => 'string',
'sortable' => true,
'filterable' => true,
'closure' => fn ($row) => core()->formatBasePrice($row->sub_total, 2),
]);
$this->addColumn([
'index' => 'discount_amount',
'label' => trans('admin::app.quotes.index.datagrid.discount'),
'type' => 'string',
'sortable' => true,
'filterable' => true,
'closure' => fn ($row) => core()->formatBasePrice($row->discount_amount, 2),
]);
$this->addColumn([
'index' => 'tax_amount',
'label' => trans('admin::app.quotes.index.datagrid.tax'),
'type' => 'string',
'filterable' => true,
'sortable' => true,
'closure' => fn ($row) => core()->formatBasePrice($row->tax_amount, 2),
]);
$this->addColumn([
'index' => 'adjustment_amount',
'label' => trans('admin::app.quotes.index.datagrid.adjustment'),
'type' => 'string',
'sortable' => true,
'filterable' => false,
'closure' => fn ($row) => core()->formatBasePrice($row->adjustment_amount, 2),
]);
$this->addColumn([
'index' => 'grand_total',
'label' => trans('admin::app.quotes.index.datagrid.grand-total'),
'type' => 'string',
'sortable' => true,
'filterable' => true,
'closure' => fn ($row) => core()->formatBasePrice($row->grand_total, 2),
]);
$this->addColumn([
'index' => 'expired_at',
'label' => trans('admin::app.quotes.index.datagrid.expired-at'),
'type' => 'date',
'searchable' => false,
'sortable' => true,
'filterable' => true,
'closure' => fn ($row) => core()->formatDate($row->expired_at, 'd M Y'),
]);
$this->addColumn([
'index' => 'created_at',
'label' => trans('admin::app.quotes.index.datagrid.created-at'),
'type' => 'date',
'searchable' => false,
'sortable' => true,
'filterable' => true,
'closure' => fn ($row) => core()->formatDate($row->created_at),
]);
}
/**
* Prepare actions.
*/
public function prepareActions(): void
{
if (bouncer()->hasPermission('quotes.edit')) {
$this->addAction([
'index' => 'edit',
'icon' => 'icon-edit',
'title' => trans('admin::app.quotes.index.datagrid.edit'),
'method' => 'GET',
'url' => fn ($row) => route('admin.quotes.edit', $row->id),
]);
}
if (bouncer()->hasPermission('quotes.print')) {
$this->addAction([
'index' => 'print',
'icon' => 'icon-print',
'title' => trans('admin::app.quotes.index.datagrid.print'),
'method' => 'GET',
'url' => fn ($row) => route('admin.quotes.print', $row->id),
]);
}
if (bouncer()->hasPermission('quotes.delete')) {
$this->addAction([
'index' => 'delete',
'icon' => 'icon-delete',
'title' => trans('admin::app.quotes.index.datagrid.delete'),
'method' => 'DELETE',
'url' => fn ($row) => route('admin.quotes.delete', $row->id),
]);
}
}
/**
* Prepare mass actions.
*/
public function prepareMassActions(): void
{
$this->addMassAction([
'icon' => 'icon-delete',
'title' => trans('admin::app.quotes.index.datagrid.delete'),
'method' => 'POST',
'url' => route('admin.quotes.mass_delete'),
]);
$this->addMassAction([
'icon' => 'icon-delete',
'title' => trans('admin::app.quotes.index.datagrid.delete'),
'method' => 'POST',
'url' => route('admin.quotes.mass_delete'),
]);
}
}

View File

@@ -0,0 +1,153 @@
<?php
namespace Webkul\Admin\DataGrids\Settings;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\DataGrid\DataGrid;
class AttributeDataGrid extends DataGrid
{
/**
* Prepare query builder.
*/
public function prepareQueryBuilder(): Builder
{
$queryBuilder = DB::table('attributes')
->select(
'attributes.id',
'attributes.code',
'attributes.name',
'attributes.type',
'attributes.entity_type',
'attributes.is_user_defined as attribute_type'
)
->where('entity_type', '<>', 'locations');
$this->addFilter('id', 'attributes.id');
$this->addFilter('type', 'attributes.type');
$this->addFilter('attribute_type', 'attributes.is_user_defined');
return $queryBuilder;
}
/**
* Prepare columns.
*/
public function prepareColumns(): void
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.settings.attributes.index.datagrid.id'),
'type' => 'string',
'filterable' => true,
'sortable' => true,
]);
$this->addColumn([
'index' => 'code',
'label' => trans('admin::app.settings.attributes.index.datagrid.code'),
'type' => 'string',
'sortable' => true,
'searchable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'name',
'label' => trans('admin::app.settings.attributes.index.datagrid.name'),
'type' => 'string',
'sortable' => true,
'searchable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'entity_type',
'label' => trans('admin::app.settings.attributes.index.datagrid.entity-type'),
'type' => 'string',
'sortable' => true,
'searchable' => false,
'filterable' => true,
'filterable_type' => 'dropdown',
'filterable_options' => app(AttributeRepository::class)
->select('entity_type as label', 'entity_type as value')
->distinct()
->get()
->map(function ($item) {
$item->label = trans('admin::app.settings.attributes.index.datagrid.entity-types.'.$item->label);
return $item;
})
->toArray(),
'closure' => fn ($row) => ucfirst($row->entity_type),
]);
$this->addColumn([
'index' => 'type',
'label' => trans('admin::app.settings.attributes.index.datagrid.type'),
'type' => 'string',
'sortable' => true,
'filterable' => true,
'filterable_type' => 'dropdown',
'filterable_options' => app(AttributeRepository::class)
->select('type as label', 'type as value')
->distinct()
->get()
->map(function ($item) {
$item->label = trans('admin::app.settings.attributes.index.datagrid.types.'.$item->label);
return $item;
})
->toArray(),
]);
$this->addColumn([
'index' => 'attribute_type',
'label' => trans('admin::app.settings.attributes.index.datagrid.is-default'),
'type' => 'boolean',
'searchable' => true,
'filterable' => false,
'sortable' => true,
'closure' => fn ($value) => trans('admin::app.settings.attributes.index.datagrid.'.($value->attribute_type ? 'no' : 'yes')),
]);
}
/**
* Prepare actions.
*/
public function prepareActions(): void
{
if (bouncer()->hasPermission('settings.automation.attributes.edit')) {
$this->addAction([
'icon' => 'icon-edit',
'title' => trans('admin::app.settings.attributes.index.datagrid.edit'),
'method' => 'GET',
'url' => fn ($row) => route('admin.settings.attributes.edit', $row->id),
]);
}
if (bouncer()->hasPermission('settings.automation.attributes.delete')) {
$this->addAction([
'icon' => 'icon-delete',
'title' => trans('admin::app.settings.attributes.index.datagrid.delete'),
'method' => 'DELETE',
'url' => fn ($row) => route('admin.settings.attributes.delete', $row->id),
]);
}
}
/**
* Prepare mass actions.
*/
public function prepareMassActions(): void
{
$this->addMassAction([
'icon' => 'icon-delete',
'title' => trans('admin::app.settings.attributes.index.datagrid.delete'),
'method' => 'POST',
'url' => route('admin.settings.attributes.mass_delete'),
]);
}
}

View File

@@ -0,0 +1,161 @@
<?php
namespace Webkul\Admin\DataGrids\Settings\DataTransfer;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Webkul\DataGrid\DataGrid;
class ImportDataGrid extends DataGrid
{
/**
* Prepare query builder.
*/
public function prepareQueryBuilder(): Builder
{
return DB::table('imports')
->select(
'id',
'state',
'file_path',
'error_file_path',
'started_at',
'completed_at',
'type',
'summary',
);
}
/**
* Prepare Columns.
*/
public function prepareColumns(): void
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.settings.data-transfer.imports.index.datagrid.id'),
'type' => 'integer',
'filterable' => true,
'sortable' => true,
]);
$this->addColumn([
'index' => 'type',
'label' => trans('admin::app.settings.data-transfer.imports.index.datagrid.type'),
'type' => 'string',
'filterable' => true,
'sortable' => true,
]);
$this->addColumn([
'index' => 'state',
'label' => trans('admin::app.settings.data-transfer.imports.index.datagrid.state'),
'type' => 'string',
'filterable' => true,
'sortable' => true,
]);
$this->addColumn([
'index' => 'file_path',
'label' => trans('admin::app.settings.data-transfer.imports.index.datagrid.uploaded-file'),
'type' => 'string',
'closure' => function ($row) {
return '<a href="'.route('admin.settings.data_transfer.imports.download', $row->id).'" class="cursor-pointer text-blue-600 hover:underline">'.$row->file_path.'<a>';
},
]);
$this->addColumn([
'index' => 'error_file_path',
'label' => trans('admin::app.settings.data-transfer.imports.index.datagrid.error-file'),
'type' => 'string',
'closure' => function ($row) {
if (empty($row->error_file_path)) {
return '';
}
return '<a href="'.route('admin.settings.data_transfer.imports.download_error_report', $row->id).'" class="cursor-pointer text-blue-600 hover:underline">'.$row->error_file_path.'<a>';
},
]);
$this->addColumn([
'index' => 'started_at',
'label' => trans('admin::app.settings.data-transfer.imports.index.datagrid.started-at'),
'type' => 'date',
'filterable' => true,
'filterable_type' => 'date_range',
'sortable' => true,
]);
$this->addColumn([
'index' => 'completed_at',
'label' => trans('admin::app.settings.data-transfer.imports.index.datagrid.completed-at'),
'type' => 'date',
'filterable' => true,
'filterable_type' => 'date_range',
'sortable' => true,
]);
$this->addColumn([
'index' => 'summary',
'label' => trans('admin::app.settings.data-transfer.imports.index.datagrid.summary'),
'type' => 'string',
'closure' => function ($row) {
if (empty($row->summary)) {
return '';
}
$summary = json_decode($row->summary, true);
$stats = [];
foreach ($summary as $type => $value) {
$stats[] = trans('admin::app.settings.data-transfer.imports.index.datagrid.'.$type).': '.$summary[$type];
}
return implode(', ', $stats);
},
]);
}
/**
* Prepare actions.
*/
public function prepareActions(): void
{
if (bouncer()->hasPermission('settings.data_transfer.imports.import')) {
$this->addAction([
'index' => 'import',
'icon' => 'icon-import',
'title' => trans('admin::app.settings.data-transfer.imports.index.datagrid.import'),
'method' => 'GET',
'url' => function ($row) {
return route('admin.settings.data_transfer.imports.import', $row->id);
},
]);
}
if (bouncer()->hasPermission('settings.data_transfer.imports.edit')) {
$this->addAction([
'index' => 'edit',
'icon' => 'icon-edit',
'title' => trans('admin::app.settings.data-transfer.imports.index.datagrid.edit'),
'method' => 'GET',
'url' => function ($row) {
return route('admin.settings.data_transfer.imports.edit', $row->id);
},
]);
}
if (bouncer()->hasPermission('settings.data_transfer.imports.delete')) {
$this->addAction([
'index' => 'delete',
'icon' => 'icon-delete',
'title' => trans('admin::app.settings.data-transfer.imports.index.datagrid.delete'),
'method' => 'DELETE',
'url' => function ($row) {
return route('admin.settings.data_transfer.imports.delete', $row->id);
},
]);
}
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace Webkul\Admin\DataGrids\Settings;
use Illuminate\Support\Facades\DB;
use Webkul\DataGrid\DataGrid;
class EmailTemplateDataGrid extends DataGrid
{
/**
* Prepare query builder.
*/
public function prepareQueryBuilder()
{
$queryBuilder = DB::table('email_templates')
->addSelect(
'email_templates.id',
'email_templates.name',
'email_templates.subject',
);
$this->addFilter('id', 'email_templates.id');
return $queryBuilder;
}
/**
* Add columns.
*
* @return void
*/
public function prepareColumns()
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.settings.email-template.index.datagrid.id'),
'type' => 'string',
'sortable' => true,
'searchable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'name',
'label' => trans('admin::app.settings.email-template.index.datagrid.name'),
'type' => 'string',
'sortable' => true,
'searchable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'subject',
'label' => trans('admin::app.settings.email-template.index.datagrid.subject'),
'type' => 'string',
'sortable' => true,
]);
}
/**
* Prepare actions.
*
* @return void
*/
public function prepareActions()
{
if (bouncer()->hasPermission('settings.automation.email_templates.edit')) {
$this->addAction([
'index' => 'delete',
'icon' => 'icon-edit',
'title' => trans('admin::app.settings.email-template.index.datagrid.edit'),
'method' => 'GET',
'url' => fn ($row) => route('admin.settings.email_templates.edit', $row->id),
]);
}
if (bouncer()->hasPermission('settings.automation.email_templates.delete')) {
$this->addAction([
'index' => 'delete',
'icon' => 'icon-delete',
'title' => trans('admin::app.settings.email-template.index.datagrid.delete'),
'method' => 'DELETE',
'url' => fn ($row) => route('admin.settings.email_templates.delete', $row->id),
]);
}
}
}

View File

@@ -0,0 +1,84 @@
<?php
namespace Webkul\Admin\DataGrids\Settings;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Webkul\DataGrid\DataGrid;
class GroupDataGrid extends DataGrid
{
/**
* Prepare query builder.
*/
public function prepareQueryBuilder(): Builder
{
$queryBuilder = DB::table('groups')
->addSelect(
'groups.id',
'groups.name',
'groups.description'
);
$this->addFilter('id', 'groups.id');
return $queryBuilder;
}
/**
* Prepare columns.
*/
public function prepareColumns(): void
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.settings.groups.index.datagrid.id'),
'type' => 'string',
'searchable' => true,
'filterable' => true,
'sortable' => true,
]);
$this->addColumn([
'index' => 'name',
'type' => 'string',
'label' => trans('admin::app.settings.groups.index.datagrid.name'),
'searchable' => true,
'filterable' => true,
'sortable' => true,
]);
$this->addColumn([
'index' => 'description',
'label' => trans('admin::app.settings.groups.index.datagrid.description'),
'type' => 'string',
'sortable' => false,
]);
}
/**
* Prepare actions.
*/
public function prepareActions(): void
{
if (bouncer()->hasPermission('settings.user.groups.edit')) {
$this->addAction([
'index' => 'edit',
'icon' => 'icon-edit',
'title' => trans('admin::app.settings.groups.index.datagrid.edit'),
'method' => 'GET',
'url' => fn ($row) => route('admin.settings.groups.edit', $row->id),
]);
}
if (bouncer()->hasPermission('settings.user.groups.delete')) {
$this->addAction([
'index' => 'delete',
'icon' => 'icon-delete',
'title' => trans('admin::app.settings.groups.index.datagrid.delete'),
'method' => 'DELETE',
'url' => fn ($row) => route('admin.settings.groups.delete', $row->id),
]);
}
}
}

View File

@@ -0,0 +1,110 @@
<?php
namespace Webkul\Admin\DataGrids\Settings\Marketing;
use Illuminate\Support\Facades\DB;
use Webkul\DataGrid\DataGrid;
class CampaignDatagrid extends DataGrid
{
/**
* Prepare query builder.
*/
public function prepareQueryBuilder()
{
$queryBuilder = DB::table('marketing_campaigns')
->addSelect(
'marketing_campaigns.id',
'marketing_campaigns.name',
'marketing_campaigns.subject',
'marketing_campaigns.status',
);
$this->addFilter('id', 'marketing_campaigns.id');
return $queryBuilder;
}
/**
* Add columns.
*
* @return void
*/
public function prepareColumns()
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.settings.marketing.campaigns.index.datagrid.id'),
'type' => 'string',
'sortable' => true,
'searchable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'name',
'label' => trans('admin::app.settings.marketing.campaigns.index.datagrid.name'),
'type' => 'string',
'sortable' => true,
'searchable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'subject',
'label' => trans('admin::app.settings.marketing.campaigns.index.datagrid.subject'),
'type' => 'string',
'sortable' => true,
]);
$this->addColumn([
'index' => 'status',
'label' => trans('admin::app.settings.marketing.campaigns.index.datagrid.status'),
'type' => 'string',
'sortable' => true,
]);
}
/**
* Prepare actions.
*
* @return void
*/
public function prepareActions()
{
if (bouncer()->hasPermission('settings.automation.campaigns.edit')) {
$this->addAction([
'index' => 'edit',
'icon' => 'icon-edit',
'title' => trans('admin::app.settings.marketing.campaigns.index.datagrid.edit'),
'method' => 'GET',
'url' => fn ($row) => route('admin.settings.marketing.campaigns.edit', $row->id),
]);
}
if (bouncer()->hasPermission('settings.automation.campaigns.delete')) {
$this->addAction([
'index' => 'delete',
'icon' => 'icon-delete',
'title' => trans('admin::app.settings.marketing.campaigns.index.datagrid.delete'),
'method' => 'DELETE',
'url' => fn ($row) => route('admin.settings.marketing.campaigns.delete', $row->id),
]);
}
}
/**
* Prepare mass actions.
*/
public function prepareMassActions(): void
{
if (bouncer()->hasPermission('settings.automation.campaigns.mass_delete')) {
$this->addMassAction([
'icon' => 'icon-delete',
'title' => trans('admin::app.settings.marketing.campaigns.index.datagrid.delete'),
'method' => 'POST',
'url' => route('admin.settings.marketing.campaigns.mass_delete'),
]);
}
}
}

View File

@@ -0,0 +1,110 @@
<?php
namespace Webkul\Admin\DataGrids\Settings\Marketing;
use Illuminate\Support\Facades\DB;
use Webkul\DataGrid\DataGrid;
class EventDataGrid extends DataGrid
{
/**
* Prepare query builder.
*/
public function prepareQueryBuilder()
{
$queryBuilder = DB::table('marketing_events')
->addSelect(
'marketing_events.id',
'marketing_events.name',
'marketing_events.description',
'marketing_events.date',
);
$this->addFilter('id', 'marketing_events.id');
return $queryBuilder;
}
/**
* Add columns.
*
* @return void
*/
public function prepareColumns()
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.settings.marketing.events.index.datagrid.id'),
'type' => 'string',
'sortable' => true,
'searchable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'name',
'label' => trans('admin::app.settings.marketing.events.index.datagrid.name'),
'type' => 'string',
'sortable' => true,
'searchable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'description',
'label' => trans('admin::app.settings.marketing.events.index.datagrid.description'),
'type' => 'string',
'sortable' => true,
]);
$this->addColumn([
'index' => 'date',
'label' => trans('admin::app.settings.marketing.events.index.datagrid.date'),
'type' => 'string',
'sortable' => true,
]);
}
/**
* Prepare actions.
*
* @return void
*/
public function prepareActions()
{
if (bouncer()->hasPermission('settings.automation.events.edit')) {
$this->addAction([
'index' => 'edit',
'icon' => 'icon-edit',
'title' => trans('admin::app.settings.marketing.events.index.datagrid.edit'),
'method' => 'GET',
'url' => fn ($row) => route('admin.settings.marketing.events.edit', $row->id),
]);
}
if (bouncer()->hasPermission('settings.automation.events.delete')) {
$this->addAction([
'index' => 'delete',
'icon' => 'icon-delete',
'title' => trans('admin::app.settings.marketing.events.index.datagrid.delete'),
'method' => 'DELETE',
'url' => fn ($row) => route('admin.settings.marketing.events.delete', $row->id),
]);
}
}
/**
* Prepare mass actions.
*/
public function prepareMassActions(): void
{
if (bouncer()->hasPermission('settings.automation.events.delete')) {
$this->addMassAction([
'icon' => 'icon-delete',
'title' => trans('admin::app.settings.marketing.events.index.datagrid.delete'),
'method' => 'POST',
'url' => route('admin.settings.marketing.events.mass_delete'),
]);
}
}
}

View File

@@ -0,0 +1,93 @@
<?php
namespace Webkul\Admin\DataGrids\Settings;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Webkul\DataGrid\DataGrid;
class PipelineDataGrid extends DataGrid
{
/**
* Prepare query builder.
*/
public function prepareQueryBuilder(): Builder
{
$queryBuilder = DB::table('lead_pipelines')
->addSelect(
'lead_pipelines.id',
'lead_pipelines.name',
'lead_pipelines.rotten_days',
'lead_pipelines.is_default',
);
$this->addFilter('id', 'lead_pipelines.id');
return $queryBuilder;
}
/**
* Prepare columns.
*/
public function prepareColumns(): void
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.settings.pipelines.index.datagrid.id'),
'type' => 'string',
'sortable' => true,
]);
$this->addColumn([
'index' => 'name',
'label' => trans('admin::app.settings.pipelines.index.datagrid.name'),
'type' => 'string',
'searchable' => true,
'filterable' => true,
'sortable' => true,
]);
$this->addColumn([
'index' => 'rotten_days',
'label' => trans('admin::app.settings.pipelines.index.datagrid.rotten-days'),
'type' => 'string',
'sortable' => true,
]);
$this->addColumn([
'index' => 'is_default',
'label' => trans('admin::app.settings.pipelines.index.datagrid.is-default'),
'type' => 'boolean',
'searchable' => true,
'filterable' => true,
'sortable' => true,
'closure' => fn ($value) => trans('admin::app.settings.pipelines.index.datagrid.'.($value->is_default ? 'yes' : 'no')),
]);
}
/**
* Prepare actions.
*/
public function prepareActions(): void
{
if (bouncer()->hasPermission('settings.lead.pipelines.edit')) {
$this->addAction([
'index' => 'edit',
'icon' => 'icon-edit',
'title' => trans('admin::app.settings.pipelines.index.datagrid.edit'),
'method' => 'GET',
'url' => fn ($row) => route('admin.settings.pipelines.edit', $row->id),
]);
}
if (bouncer()->hasPermission('settings.lead.pipelines.delete')) {
$this->addAction([
'index' => 'delete',
'icon' => 'icon-delete',
'title' => trans('admin::app.settings.pipelines.index.datagrid.delete'),
'method' => 'DELETE',
'url' => fn ($row) => route('admin.settings.pipelines.delete', $row->id),
]);
}
}
}

View File

@@ -0,0 +1,102 @@
<?php
namespace Webkul\Admin\DataGrids\Settings;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Webkul\DataGrid\DataGrid;
class RoleDataGrid extends DataGrid
{
/**
* Prepare query builder.
*/
public function prepareQueryBuilder(): Builder
{
$queryBuilder = DB::table('roles')
->addSelect(
'roles.id',
'roles.name',
'roles.description',
'roles.permission_type'
);
$this->addFilter('id', 'roles.id');
$this->addFilter('name', 'roles.name');
return $queryBuilder;
}
/**
* Prepare Columns.
*/
public function prepareColumns(): void
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.settings.roles.index.datagrid.id'),
'type' => 'string',
'filterable' => true,
'sortable' => true,
]);
$this->addColumn([
'index' => 'name',
'label' => trans('admin::app.settings.roles.index.datagrid.name'),
'type' => 'string',
'filterable' => true,
'sortable' => true,
]);
$this->addColumn([
'index' => 'description',
'label' => trans('admin::app.settings.roles.index.datagrid.description'),
'type' => 'string',
'sortable' => false,
]);
$this->addColumn([
'index' => 'permission_type',
'label' => trans('admin::app.settings.roles.index.datagrid.permission-type'),
'type' => 'string',
'searchable' => true,
'filterable' => true,
'filterable_type' => 'dropdown',
'filterable_options' => [
[
'label' => trans('admin::app.settings.roles.index.datagrid.custom'),
'value' => 'custom',
],
[
'label' => trans('admin::app.settings.roles.index.datagrid.all'),
'value' => 'all',
],
],
'sortable' => true,
]);
}
/**
* Prepare actions.
*/
public function prepareActions(): void
{
if (bouncer()->hasPermission('settings.user.roles.edit')) {
$this->addAction([
'icon' => 'icon-edit',
'title' => trans('admin::app.settings.roles.index.datagrid.edit'),
'method' => 'GET',
'url' => fn ($row) => route('admin.settings.roles.edit', $row->id),
]);
}
if (bouncer()->hasPermission('settings.user.roles.delete')) {
$this->addAction([
'icon' => 'icon-delete',
'title' => trans('admin::app.settings.roles.index.datagrid.delete'),
'method' => 'DELETE',
'url' => fn ($row) => route('admin.settings.roles.delete', $row->id),
]);
}
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace Webkul\Admin\DataGrids\Settings;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Webkul\DataGrid\DataGrid;
class SourceDataGrid extends DataGrid
{
/**
* Prepare query builder.
*/
public function prepareQueryBuilder(): Builder
{
$queryBuilder = DB::table('lead_sources')
->addSelect(
'lead_sources.id',
'lead_sources.name'
);
$this->addFilter('id', 'lead_sources.id');
return $queryBuilder;
}
/**
* Prepare Columns.
*/
public function prepareColumns(): void
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.settings.sources.index.datagrid.id'),
'type' => 'string',
'sortable' => true,
]);
$this->addColumn([
'index' => 'name',
'label' => trans('admin::app.settings.sources.index.datagrid.name'),
'type' => 'string',
'searchable' => true,
'filterable' => true,
'sortable' => true,
]);
}
/**
* Prepare actions.
*/
public function prepareActions(): void
{
if (bouncer()->hasPermission('settings.lead.sources.edit')) {
$this->addAction([
'index' => 'edit',
'icon' => 'icon-edit',
'title' => trans('admin::app.settings.sources.index.datagrid.edit'),
'method' => 'GET',
'url' => fn ($row) => route('admin.settings.sources.edit', $row->id),
]);
}
if (bouncer()->hasPermission('settings.lead.sources.delete')) {
$this->addAction([
'index' => 'delete',
'icon' => 'icon-delete',
'title' => trans('admin::app.settings.sources.index.datagrid.delete'),
'method' => 'DELETE',
'url' => fn ($row) => route('admin.settings.sources.delete', $row->id),
]);
}
}
}

View File

@@ -0,0 +1,124 @@
<?php
namespace Webkul\Admin\DataGrids\Settings;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Webkul\DataGrid\DataGrid;
class TagDataGrid extends DataGrid
{
/**
* Prepare query builder.
*/
public function prepareQueryBuilder(): Builder
{
$queryBuilder = DB::table('tags')
->addSelect(
'tags.id',
'tags.name',
'tags.color',
'tags.created_at',
'users.name as user_name',
)
->leftJoin('users', 'tags.user_id', '=', 'users.id');
if ($userIds = bouncer()->getAuthorizedUserIds()) {
$queryBuilder->whereIn('tags.user_id', $userIds);
}
$this->addFilter('id', 'tags.id');
$this->addFilter('name', 'tags.name');
$this->addFilter('created_at', 'tags.created_at');
$this->addFilter('user_name', 'users.id');
return $queryBuilder;
}
/**
* Prepare Columns.
*/
public function prepareColumns(): void
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.settings.tags.index.datagrid.id'),
'type' => 'string',
'searchable' => true,
'sortable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'name',
'label' => trans('admin::app.settings.tags.index.datagrid.name'),
'type' => 'string',
'searchable' => true,
'sortable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'user_name',
'label' => trans('admin::app.settings.tags.index.datagrid.users'),
'type' => 'string',
'searchable' => true,
'sortable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'created_at',
'label' => trans('admin::app.settings.tags.index.datagrid.created-at'),
'type' => 'date',
'searchable' => true,
'filterable' => true,
'sortable' => true,
'filterable_type' => 'date_range',
'closure' => fn ($row) => core()->formatDate($row->created_at),
]);
}
/**
* Prepare actions.
*/
public function prepareActions(): void
{
if (bouncer()->hasPermission('settings.other_settings.tags.edit')) {
$this->addAction([
'index' => 'edit',
'icon' => 'icon-edit',
'title' => trans('admin::app.settings.tags.index.datagrid.edit'),
'method' => 'GET',
'url' => function ($row) {
return route('admin.settings.tags.edit', $row->id);
},
]);
}
if (bouncer()->hasPermission('settings.other_settings.tags.delete')) {
$this->addAction([
'index' => 'delete',
'icon' => 'icon-delete',
'title' => trans('admin::app.settings.tags.index.datagrid.delete'),
'method' => 'DELETE',
'url' => function ($row) {
return route('admin.settings.tags.delete', $row->id);
},
]);
}
}
/**
* Prepare mass actions.
*/
public function prepareMassActions(): void
{
$this->addMassAction([
'icon' => 'icon-delete',
'title' => trans('admin::app.settings.tags.index.datagrid.delete'),
'method' => 'POST',
'url' => route('admin.settings.tags.mass_delete'),
]);
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace Webkul\Admin\DataGrids\Settings;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Webkul\DataGrid\DataGrid;
class TypeDataGrid extends DataGrid
{
/**
* Prepare query builder.
*/
public function prepareQueryBuilder(): Builder
{
$queryBuilder = DB::table('lead_types')
->addSelect(
'lead_types.id',
'lead_types.name'
);
$this->addFilter('id', 'lead_types.id');
return $queryBuilder;
}
/**
* Prepare Columns.
*/
public function prepareColumns(): void
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.settings.types.index.datagrid.id'),
'type' => 'string',
'filterable' => true,
'sortable' => true,
]);
$this->addColumn([
'index' => 'name',
'label' => trans('admin::app.settings.types.index.datagrid.name'),
'type' => 'string',
'filterable' => true,
'sortable' => true,
]);
}
/**
* Prepare Actions.
*/
public function prepareActions(): void
{
if (bouncer()->hasPermission('settings.lead.types.edit')) {
$this->addAction([
'index' => 'edit',
'icon' => 'icon-edit',
'title' => trans('admin::app.settings.roles.index.datagrid.edit'),
'method' => 'GET',
'url' => fn ($row) => route('admin.settings.types.update', $row->id),
]);
}
if (bouncer()->hasPermission('settings.lead.types.delete')) {
$this->addAction([
'index' => 'delete',
'icon' => 'icon-delete',
'title' => trans('admin::app.settings.roles.index.datagrid.delete'),
'method' => 'DELETE',
'url' => fn ($row) => route('admin.settings.types.delete', $row->id),
]);
}
}
}

View File

@@ -0,0 +1,146 @@
<?php
namespace Webkul\Admin\DataGrids\Settings;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Webkul\DataGrid\DataGrid;
class UserDataGrid extends DataGrid
{
/**
* Prepare query builder.
*/
public function prepareQueryBuilder(): Builder
{
$queryBuilder = DB::table('users')
->distinct()
->addSelect(
'id',
'name',
'email',
'image',
'status',
'created_at'
)
->leftJoin('user_groups', 'id', '=', 'user_groups.user_id');
if ($userIds = bouncer()->getAuthorizedUserIds()) {
$queryBuilder->whereIn('id', $userIds);
}
return $queryBuilder;
}
/**
* Add columns.
*/
public function prepareColumns(): void
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.settings.users.index.datagrid.id'),
'type' => 'string',
'sortable' => true,
]);
$this->addColumn([
'index' => 'name',
'label' => trans('admin::app.settings.users.index.datagrid.name'),
'type' => 'string',
'sortable' => true,
'searchable' => true,
'filterable' => true,
'closure' => function ($row) {
return [
'image' => $row->image ? Storage::url($row->image) : null,
'name' => $row->name,
];
},
]);
$this->addColumn([
'index' => 'email',
'label' => trans('admin::app.settings.users.index.datagrid.email'),
'type' => 'string',
'sortable' => true,
'searchable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'status',
'label' => trans('admin::app.settings.users.index.datagrid.status'),
'type' => 'boolean',
'filterable' => true,
'sortable' => true,
'searchable' => true,
]);
$this->addColumn([
'index' => 'created_at',
'label' => trans('admin::app.settings.users.index.datagrid.created-at'),
'type' => 'date',
'sortable' => true,
'searchable' => true,
'filterable_type' => 'date_range',
'filterable' => true,
]);
}
/**
* Prepare actions.
*/
public function prepareActions(): void
{
if (bouncer()->hasPermission('settings.user.users.edit')) {
$this->addAction([
'index' => 'edit',
'icon' => 'icon-edit',
'title' => trans('admin::app.settings.users.index.datagrid.edit'),
'method' => 'GET',
'url' => fn ($row) => route('admin.settings.users.edit', $row->id),
]);
}
if (bouncer()->hasPermission('settings.user.users.delete')) {
$this->addAction([
'index' => 'delete',
'icon' => 'icon-delete',
'title' => trans('admin::app.settings.users.index.datagrid.delete'),
'method' => 'DELETE',
'url' => fn ($row) => route('admin.settings.users.delete', $row->id),
]);
}
}
/**
* Prepare mass actions.
*/
public function prepareMassActions(): void
{
$this->addMassAction([
'icon' => 'icon-delete',
'title' => trans('admin::app.settings.users.index.datagrid.delete'),
'method' => 'POST',
'url' => route('admin.settings.users.mass_delete'),
]);
$this->addMassAction([
'title' => trans('admin::app.settings.users.index.datagrid.update-status'),
'method' => 'POST',
'url' => route('admin.settings.users.mass_update'),
'options' => [
[
'label' => trans('admin::app.settings.users.index.datagrid.active'),
'value' => 1,
],
[
'label' => trans('admin::app.settings.users.index.datagrid.inactive'),
'value' => 0,
],
],
]);
}
}

View File

@@ -0,0 +1,153 @@
<?php
namespace Webkul\Admin\DataGrids\Settings;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Webkul\DataGrid\DataGrid;
class WarehouseDataGrid extends DataGrid
{
/**
* Prepare query builder.
*
* @return void
*/
public function prepareQueryBuilder(): Builder
{
$queryBuilder = DB::table('warehouses')
->leftJoin('product_inventories', 'warehouses.id', '=', 'product_inventories.warehouse_id')
->select(
'warehouses.id',
'warehouses.name',
'warehouses.contact_name',
'warehouses.contact_emails',
'warehouses.contact_numbers',
'warehouses.created_at',
)
->addSelect(DB::raw('count(DISTINCT '.DB::getTablePrefix().'product_inventories.product_id) as products'))
->groupBy('warehouses.id');
$this->addFilter('id', 'warehouses.id');
$this->addFilter('created_at', 'warehouses.created_at');
return $queryBuilder;
}
/**
* Add columns.
*/
public function prepareColumns(): void
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.settings.warehouses.index.datagrid.id'),
'type' => 'string',
'sortable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'name',
'label' => trans('admin::app.settings.warehouses.index.datagrid.name'),
'type' => 'string',
'sortable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'contact_name',
'label' => trans('admin::app.settings.warehouses.index.datagrid.contact-name'),
'type' => 'string',
'searchable' => true,
'filterable' => true,
'sortable' => true,
]);
$this->addColumn([
'index' => 'contact_emails',
'label' => trans('admin::app.settings.warehouses.index.datagrid.contact-emails'),
'type' => 'string',
'searchable' => true,
'filterable' => true,
'sortable' => true,
'closure' => function ($row) {
$emails = json_decode($row->contact_emails, true);
if ($emails) {
return collect($emails)->pluck('value')->join(', ');
}
},
]);
$this->addColumn([
'index' => 'contact_numbers',
'label' => trans('admin::app.settings.warehouses.index.datagrid.contact-numbers'),
'type' => 'string',
'sortable' => false,
'closure' => function ($row) {
$numbers = json_decode($row->contact_numbers, true);
if ($numbers) {
return collect($numbers)->pluck('value')->join(', ');
}
},
]);
$this->addColumn([
'index' => 'products',
'label' => trans('admin::app.settings.warehouses.index.datagrid.products'),
'type' => 'string',
'sortable' => true,
'filterable' => false,
]);
$this->addColumn([
'index' => 'created_at',
'label' => trans('admin::app.settings.warehouses.index.datagrid.created-at'),
'type' => 'date',
'searchable' => true,
'filterable' => true,
'filterable_type' => 'date_range',
'sortable' => true,
'closure' => function ($row) {
return core()->formatDate($row->created_at);
},
]);
}
/**
* Prepare actions.
*
* @return void
*/
public function prepareActions()
{
$this->addAction([
'icon' => 'icon-eye',
'title' => trans('admin::app.settings.warehouses.index.datagrid.view'),
'method' => 'GET',
'url' => function ($row) {
return route('admin.settings.warehouses.view', $row->id);
},
]);
$this->addAction([
'icon' => 'icon-edit',
'title' => trans('admin::app.settings.warehouses.index.datagrid.edit'),
'method' => 'GET',
'url' => function ($row) {
return route('admin.settings.warehouses.edit', $row->id);
},
]);
$this->addAction([
'icon' => 'icon-delete',
'title' => trans('admin::app.settings.warehouses.index.datagrid.delete'),
'method' => 'DELETE',
'url' => function ($row) {
return route('admin.settings.warehouses.delete', $row->id);
},
]);
}
}

View File

@@ -0,0 +1,96 @@
<?php
namespace Webkul\Admin\DataGrids\Settings;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Webkul\DataGrid\DataGrid;
class WebhookDataGrid extends DataGrid
{
/**
* Prepare query builder.
*/
public function prepareQueryBuilder(): Builder
{
$queryBuilder = DB::table('webhooks')
->addSelect(
'webhooks.id',
'webhooks.name',
'webhooks.entity_type',
'webhooks.end_point',
);
$this->addFilter('id', 'webhooks.id');
return $queryBuilder;
}
/**
* Prepare Columns.
*/
public function prepareColumns(): void
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.settings.webhooks.index.datagrid.id'),
'type' => 'string',
'searchable' => true,
'filterable' => true,
'sortable' => true,
]);
$this->addColumn([
'index' => 'name',
'label' => trans('admin::app.settings.webhooks.index.datagrid.name'),
'type' => 'string',
'searchable' => true,
'filterable' => true,
'sortable' => true,
]);
$this->addColumn([
'index' => 'entity_type',
'label' => trans('admin::app.settings.webhooks.index.datagrid.entity-type'),
'type' => 'string',
'searchable' => true,
'filterable' => true,
'sortable' => true,
]);
$this->addColumn([
'index' => 'end_point',
'label' => trans('admin::app.settings.webhooks.index.datagrid.end-point'),
'type' => 'string',
'searchable' => true,
'filterable' => true,
'sortable' => true,
]);
}
/**
* Prepare actions.
*/
public function prepareActions(): void
{
if (bouncer()->hasPermission('settings.automation.webhooks.edit')) {
$this->addAction([
'index' => 'edit',
'icon' => 'icon-edit',
'title' => trans('admin::app.settings.webhooks.index.datagrid.edit'),
'method' => 'GET',
'url' => fn ($row) => route('admin.settings.webhooks.edit', $row->id),
]);
}
if (bouncer()->hasPermission('settings.automation.webhooks.delete')) {
$this->addAction([
'index' => 'delete',
'icon' => 'icon-delete',
'title' => trans('admin::app.settings.webhooks.index.datagrid.delete'),
'method' => 'DELETE',
'url' => fn ($row) => route('admin.settings.webhooks.delete', $row->id),
]);
}
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace Webkul\Admin\DataGrids\Settings;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Webkul\DataGrid\DataGrid;
class WorkflowDataGrid extends DataGrid
{
/**
* Prepare query builder.
*/
public function prepareQueryBuilder(): Builder
{
$queryBuilder = DB::table('workflows')
->addSelect(
'workflows.id',
'workflows.name'
);
$this->addFilter('id', 'workflows.id');
return $queryBuilder;
}
/**
* Prepare Columns.
*/
public function prepareColumns(): void
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.settings.workflows.index.datagrid.id'),
'type' => 'string',
'searchable' => true,
'filterable' => true,
'sortable' => true,
]);
$this->addColumn([
'index' => 'name',
'label' => trans('admin::app.settings.workflows.index.datagrid.name'),
'type' => 'string',
'searchable' => true,
'filterable' => true,
'sortable' => true,
]);
}
/**
* Prepare actions.
*/
public function prepareActions(): void
{
if (bouncer()->hasPermission('settings.automation.workflows.edit')) {
$this->addAction([
'index' => 'edit',
'icon' => 'icon-edit',
'title' => trans('admin::app.settings.workflows.index.datagrid.edit'),
'method' => 'GET',
'url' => fn ($row) => route('admin.settings.workflows.edit', $row->id),
]);
}
if (bouncer()->hasPermission('settings.automation.workflows.delete')) {
$this->addAction([
'index' => 'delete',
'icon' => 'icon-delete',
'title' => trans('admin::app.settings.workflows.index.datagrid.delete'),
'method' => 'DELETE',
'url' => fn ($row) => route('admin.settings.workflows.delete', $row->id),
]);
}
}
}

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('view_permission')->after('status')->default('global')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('view_permission');
});
}
};

View File

@@ -0,0 +1,42 @@
<?php
use Carbon\Carbon;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
DB::table('attributes')->insert([
[
'id' => '7',
'code' => 'expected_close_date',
'name' => 'Expected Close Date',
'type' => 'date',
'entity_type' => 'leads',
'lookup_type' => null,
'validation' => null,
'sort_order' => '8',
'is_required' => '0',
'is_unique' => '0',
'quick_add' => '1',
'is_user_defined' => '0',
'created_at' => Carbon::now(),
'updated_at' => Carbon::now(),
],
]);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down() {}
};

View File

@@ -0,0 +1,117 @@
<?php
namespace Webkul\Admin\Exceptions;
use App\Exceptions\Handler as AppExceptionHandler;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Container\Container;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Validation\ValidationException;
use PDOException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Throwable;
class Handler extends AppExceptionHandler
{
/**
* Json error messages.
*
* @var array
*/
protected $jsonErrorMessages = [];
/**
* Create handler instance.
*
* @return void
*/
public function __construct(Container $container)
{
parent::__construct($container);
$this->jsonErrorMessages = [
'404' => trans('admin::app.common.resource-not-found'),
'403' => trans('admin::app.common.forbidden-error'),
'401' => trans('admin::app.common.unauthenticated'),
'500' => trans('admin::app.common.internal-server-error'),
];
}
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function render($request, Throwable $exception)
{
if (! config('app.debug')) {
return $this->renderCustomResponse($exception);
}
return parent::render($request, $exception);
}
/**
* Convert an authentication exception into a response.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson()) {
return response()->json(['message' => $this->jsonErrorMessages[401]], 401);
}
return redirect()->guest(route('customer.session.index'));
}
/**
* Render custom HTTP response.
*
* @return \Illuminate\Http\Response|null
*/
private function renderCustomResponse(Throwable $exception)
{
if ($exception instanceof HttpException) {
$statusCode = in_array($exception->getStatusCode(), [401, 403, 404, 503])
? $exception->getStatusCode()
: 500;
return $this->response($statusCode);
}
if ($exception instanceof ValidationException) {
return parent::render(request(), $exception);
}
if ($exception instanceof ModelNotFoundException) {
return $this->response(404);
} elseif ($exception instanceof PDOException || $exception instanceof \ParseError) {
return $this->response(500);
} else {
return $this->response(500);
}
}
/**
* Return custom response.
*
* @param string $path
* @param string $errorCode
* @return mixed
*/
private function response($errorCode)
{
if (request()->expectsJson()) {
return response()->json([
'message' => isset($this->jsonErrorMessages[$errorCode])
? $this->jsonErrorMessages[$errorCode]
: trans('admin::app.common.something-went-wrong'),
], $errorCode);
}
return response()->view('admin::errors.index', compact('errorCode'));
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Webkul\Admin\Facades;
use Illuminate\Support\Facades\Facade;
class Bouncer extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'bouncer';
}
}

View File

@@ -0,0 +1,142 @@
<?php
namespace Webkul\Admin\Helpers;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Webkul\Admin\Helpers\Reporting\Activity;
use Webkul\Admin\Helpers\Reporting\Lead;
use Webkul\Admin\Helpers\Reporting\Organization;
use Webkul\Admin\Helpers\Reporting\Person;
use Webkul\Admin\Helpers\Reporting\Product;
use Webkul\Admin\Helpers\Reporting\Quote;
class Dashboard
{
/**
* Create a controller instance.
*
* @return void
*/
public function __construct(
protected Lead $leadReporting,
protected Activity $activityReporting,
protected Product $productReporting,
protected Person $personReporting,
protected Organization $organizationReporting,
protected Quote $quoteReporting,
) {}
/**
* Returns the overall revenue statistics.
*/
public function getRevenueStats(): array
{
return [
'total_won_revenue' => $this->leadReporting->getTotalWonLeadValueProgress(),
'total_lost_revenue' => $this->leadReporting->getTotalLostLeadValueProgress(),
];
}
/**
* Returns the overall statistics.
*/
public function getOverAllStats(): array
{
return [
'total_leads' => $this->leadReporting->getTotalLeadsProgress(),
'average_lead_value' => $this->leadReporting->getAverageLeadValueProgress(),
'average_leads_per_day' => $this->leadReporting->getAverageLeadsPerDayProgress(),
'total_quotations' => $this->quoteReporting->getTotalQuotesProgress(),
'total_persons' => $this->personReporting->getTotalPersonsProgress(),
'total_organizations' => $this->organizationReporting->getTotalOrganizationsProgress(),
];
}
/**
* Returns leads statistics.
*/
public function getTotalLeadsStats(): array
{
return [
'all' => [
'over_time' => $this->leadReporting->getTotalLeadsOverTime(),
],
'won' => [
'over_time' => $this->leadReporting->getTotalWonLeadsOverTime(),
],
'lost' => [
'over_time' => $this->leadReporting->getTotalLostLeadsOverTime(),
],
];
}
/**
* Returns leads revenue statistics by sources.
*/
public function getLeadsStatsBySources(): mixed
{
return $this->leadReporting->getTotalWonLeadValueBySources();
}
/**
* Returns leads revenue statistics by types.
*/
public function getLeadsStatsByTypes(): mixed
{
return $this->leadReporting->getTotalWonLeadValueByTypes();
}
/**
* Returns open leads statistics by states.
*/
public function getOpenLeadsByStates(): mixed
{
return $this->leadReporting->getOpenLeadsByStates();
}
/**
* Returns top selling products statistics.
*/
public function getTopSellingProducts(): Collection
{
return $this->productReporting->getTopSellingProductsByRevenue(5);
}
/**
* Returns top selling products statistics.
*/
public function getTopPersons(): Collection
{
return $this->personReporting->getTopCustomersByRevenue(5);
}
/**
* Get the start date.
*
* @return \Carbon\Carbon
*/
public function getStartDate(): Carbon
{
return $this->leadReporting->getStartDate();
}
/**
* Get the end date.
*
* @return \Carbon\Carbon
*/
public function getEndDate(): Carbon
{
return $this->leadReporting->getEndDate();
}
/**
* Returns date range
*/
public function getDateRange(): string
{
return $this->getStartDate()->format('d M').' - '.$this->getEndDate()->format('d M');
}
}

View File

@@ -0,0 +1,343 @@
<?php
namespace Webkul\Admin\Helpers\Reporting;
use Carbon\CarbonPeriod;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
abstract class AbstractReporting
{
/**
* The starting date for a given period.
*/
protected Carbon $startDate;
/**
* The ending date for a given period.
*/
protected Carbon $endDate;
/**
* The starting date for the previous period.
*/
protected Carbon $lastStartDate;
/**
* The ending date for the previous period.
*/
protected Carbon $lastEndDate;
/**
* Create a helper instance.
*
* @return void
*/
public function __construct()
{
$this->setStartDate(request()->date('start'));
$this->setEndDate(request()->date('end'));
}
/**
* Set the start date or default to 30 days ago if not provided.
*
* @param \Carbon\Carbon|null $startDate
* @return void
*/
public function setStartDate(?Carbon $startDate = null): self
{
$this->startDate = $startDate ? $startDate->startOfDay() : now()->subDays(30)->startOfDay();
$this->setLastStartDate();
return $this;
}
/**
* Sets the end date to the provided date's end of day, or to the current
* date if not provided or if the provided date is in the future.
*
* @param \Carbon\Carbon|null $endDate
* @return void
*/
public function setEndDate(?Carbon $endDate = null): self
{
$this->endDate = ($endDate && $endDate->endOfDay() <= now()) ? $endDate->endOfDay() : now();
$this->setLastEndDate();
return $this;
}
/**
* Get the start date.
*
* @return \Carbon\Carbon
*/
public function getStartDate(): Carbon
{
return $this->startDate;
}
/**
* Get the end date.
*
* @return \Carbon\Carbon
*/
public function getEndDate(): Carbon
{
return $this->endDate;
}
/**
* Sets the start date for the last period.
*/
private function setLastStartDate(): void
{
if (! isset($this->startDate)) {
$this->setStartDate(request()->date('start'));
}
if (! isset($this->endDate)) {
$this->setEndDate(request()->date('end'));
}
$this->lastStartDate = $this->startDate->clone()->subDays($this->startDate->diffInDays($this->endDate));
}
/**
* Sets the end date for the last period.
*/
private function setLastEndDate(): void
{
$this->lastEndDate = $this->startDate->clone();
}
/**
* Get the last start date.
*
* @return \Carbon\Carbon
*/
public function getLastStartDate(): Carbon
{
return $this->lastStartDate;
}
/**
* Get the last end date.
*
* @return \Carbon\Carbon
*/
public function getLastEndDate(): Carbon
{
return $this->lastEndDate;
}
/**
* Calculate the percentage change between previous and current values.
*
* @param float|int $previous
* @param float|int $current
*/
public function getPercentageChange($previous, $current): float|int
{
if (! $previous) {
return $current ? 100 : 0;
}
return ($current - $previous) / $previous * 100;
}
/**
* Returns time intervals.
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
* @param string $period
* @return array
*/
public function getTimeInterval($startDate, $endDate, $dateColumn, $period)
{
if ($period == 'auto') {
$totalMonths = $startDate->diffInMonths($endDate) + 1;
/**
* If the difference between the start and end date is more than 5 months
*/
$intervals = $this->getMonthsInterval($startDate, $endDate);
if (! empty($intervals)) {
return [
'group_column' => "MONTH($dateColumn)",
'intervals' => $intervals,
];
}
/**
* If the difference between the start and end date is more than 6 weeks
*/
$intervals = $this->getWeeksInterval($startDate, $endDate);
if (! empty($intervals)) {
return [
'group_column' => "WEEK($dateColumn)",
'intervals' => $intervals,
];
}
/**
* If the difference between the start and end date is less than 6 weeks
*/
return [
'group_column' => "DAYOFYEAR($dateColumn)",
'intervals' => $this->getDaysInterval($startDate, $endDate),
];
} else {
$datePeriod = CarbonPeriod::create($this->startDate, "1 $period", $this->endDate);
if ($period == 'year') {
$formatter = '?';
} elseif ($period == 'month') {
$formatter = '?-?';
} else {
$formatter = '?-?-?';
}
$groupColumn = 'DATE_FORMAT('.$dateColumn.', "'.Str::replaceArray('?', ['%Y', '%m', '%d'], $formatter).'")';
$intervals = [];
foreach ($datePeriod as $date) {
$formattedDate = $date->format(Str::replaceArray('?', ['Y', 'm', 'd'], $formatter));
$intervals[] = [
'filter' => $formattedDate,
'start' => $formattedDate,
];
}
return [
'group_column' => $groupColumn,
'intervals' => $intervals,
];
}
}
/**
* Returns time intervals.
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
* @return array
*/
public function getMonthsInterval($startDate, $endDate)
{
$intervals = [];
$totalMonths = $startDate->diffInMonths($endDate) + 1;
/**
* If the difference between the start and end date is less than 5 months
*/
if ($totalMonths <= 5) {
return $intervals;
}
for ($i = 0; $i < $totalMonths; $i++) {
$intervalStartDate = clone $startDate;
$intervalStartDate->addMonths($i);
$start = $intervalStartDate->startOfDay();
$end = ($totalMonths - 1 == $i)
? $endDate
: $intervalStartDate->addMonth()->subDay()->endOfDay();
$intervals[] = [
'filter' => $start->month,
'start' => $start->format('d M'),
'end' => $end->format('d M'),
];
}
return $intervals;
}
/**
* Returns time intervals.
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
* @return array
*/
public function getWeeksInterval($startDate, $endDate)
{
$intervals = [];
$startWeekDay = Carbon::createFromTimeString(core()->xWeekRange($startDate, 0).' 00:00:01');
$endWeekDay = Carbon::createFromTimeString(core()->xWeekRange($endDate, 1).' 23:59:59');
$totalWeeks = $startWeekDay->diffInWeeks($endWeekDay);
/**
* If the difference between the start and end date is less than 6 weeks
*/
if ($totalWeeks <= 6) {
return $intervals;
}
for ($i = 0; $i < $totalWeeks; $i++) {
$intervalStartDate = clone $startDate;
$intervalStartDate->addWeeks($i);
$start = $i == 0
? $startDate
: Carbon::createFromTimeString(core()->xWeekRange($intervalStartDate, 0).' 00:00:01');
$end = ($totalWeeks - 1 == $i)
? $endDate
: Carbon::createFromTimeString(core()->xWeekRange($intervalStartDate->subDay(), 1).' 23:59:59');
$intervals[] = [
'filter' => $start->week,
'start' => $start->format('d M'),
'end' => $end->format('d M'),
];
}
return $intervals;
}
/**
* Returns time intervals.
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
* @return array
*/
public function getDaysInterval($startDate, $endDate)
{
$intervals = [];
$totalDays = $startDate->diffInDays($endDate) + 1;
for ($i = 0; $i < $totalDays; $i++) {
$intervalStartDate = clone $startDate;
$intervalStartDate->addDays($i);
$intervals[] = [
'filter' => $intervalStartDate->dayOfYear,
'start' => $intervalStartDate->startOfDay()->format('d M'),
'end' => $intervalStartDate->endOfDay()->format('d M'),
];
}
return $intervals;
}
}

View File

@@ -0,0 +1,5 @@
<?php
namespace Webkul\Admin\Helpers\Reporting;
class Activity extends AbstractReporting {}

View File

@@ -0,0 +1,483 @@
<?php
namespace Webkul\Admin\Helpers\Reporting;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Webkul\Lead\Repositories\LeadRepository;
use Webkul\Lead\Repositories\StageRepository;
class Lead extends AbstractReporting
{
/**
* The channel ids.
*/
protected array $stageIds;
/**
* The all stage ids.
*/
protected array $allStageIds;
/**
* The won stage ids.
*/
protected array $wonStageIds;
/**
* The lost stage ids.
*/
protected array $lostStageIds;
/**
* Create a helper instance.
*
* @return void
*/
public function __construct(
protected LeadRepository $leadRepository,
protected StageRepository $stageRepository
) {
$this->allStageIds = $this->stageRepository->pluck('id')->toArray();
$this->wonStageIds = $this->stageRepository->where('code', 'won')->pluck('id')->toArray();
$this->lostStageIds = $this->stageRepository->where('code', 'lost')->pluck('id')->toArray();
parent::__construct();
}
/**
* Returns current customers over time
*
* @param string $period
*/
public function getTotalLeadsOverTime($period = 'auto'): array
{
$this->stageIds = $this->allStageIds;
$period = $this->determinePeriod($period);
return $this->getOverTimeStats($this->startDate, $this->endDate, 'leads.id', 'created_at', $period);
}
/**
* Returns current customers over time
*
* @param string $period
*/
public function getTotalWonLeadsOverTime($period = 'auto'): array
{
$this->stageIds = $this->wonStageIds;
$period = $this->determinePeriod($period);
return $this->getOverTimeStats($this->startDate, $this->endDate, 'leads.id', 'closed_at', $period);
}
/**
* Returns current customers over time
*
* @param string $period
*/
public function getTotalLostLeadsOverTime($period = 'auto'): array
{
$this->stageIds = $this->lostStageIds;
$period = $this->determinePeriod($period);
return $this->getOverTimeStats($this->startDate, $this->endDate, 'leads.id', 'closed_at', $period);
}
/**
* Determine the appropriate period based on date range
*
* @param string $period
*/
protected function determinePeriod($period = 'auto'): string
{
if ($period !== 'auto') {
return $period;
}
$diffInDays = $this->startDate->diffInDays($this->endDate);
$diffInMonths = $this->startDate->diffInMonths($this->endDate);
$diffInYears = $this->startDate->diffInYears($this->endDate);
if ($diffInYears > 3) {
return 'year';
} elseif ($diffInMonths > 6) {
return 'month';
} elseif ($diffInDays > 60) {
return 'week';
} else {
return 'day';
}
}
/**
* Retrieves total leads and their progress.
*/
public function getTotalLeadsProgress(): array
{
return [
'previous' => $previous = $this->getTotalLeads($this->lastStartDate, $this->lastEndDate),
'current' => $current = $this->getTotalLeads($this->startDate, $this->endDate),
'progress' => $this->getPercentageChange($previous, $current),
];
}
/**
* Retrieves total leads by date
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
*/
public function getTotalLeads($startDate, $endDate): int
{
return $this->leadRepository
->resetModel()
->whereBetween('created_at', [$startDate, $endDate])
->count();
}
/**
* Retrieves average leads per day and their progress.
*/
public function getAverageLeadsPerDayProgress(): array
{
return [
'previous' => $previous = $this->getAverageLeadsPerDay($this->lastStartDate, $this->lastEndDate),
'current' => $current = $this->getAverageLeadsPerDay($this->startDate, $this->endDate),
'progress' => $this->getPercentageChange($previous, $current),
];
}
/**
* Retrieves average leads per day
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
*/
public function getAverageLeadsPerDay($startDate, $endDate): float
{
$days = $startDate->diffInDays($endDate);
if ($days == 0) {
return 0;
}
return $this->getTotalLeads($startDate, $endDate) / $days;
}
/**
* Retrieves total lead value and their progress.
*/
public function getTotalLeadValueProgress(): array
{
return [
'previous' => $previous = $this->getTotalLeadValue($this->lastStartDate, $this->lastEndDate),
'current' => $current = $this->getTotalLeadValue($this->startDate, $this->endDate),
'formatted_total' => core()->formatBasePrice($current),
'progress' => $this->getPercentageChange($previous, $current),
];
}
/**
* Retrieves total lead value
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
*/
public function getTotalLeadValue($startDate, $endDate): float
{
return $this->leadRepository
->resetModel()
->whereBetween('created_at', [$startDate, $endDate])
->sum('lead_value');
}
/**
* Retrieves average lead value and their progress.
*/
public function getAverageLeadValueProgress(): array
{
return [
'previous' => $previous = $this->getAverageLeadValue($this->lastStartDate, $this->lastEndDate),
'current' => $current = $this->getAverageLeadValue($this->startDate, $this->endDate),
'formatted_total' => core()->formatBasePrice($current),
'progress' => $this->getPercentageChange($previous, $current),
];
}
/**
* Retrieves average lead value
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
*/
public function getAverageLeadValue($startDate, $endDate): float
{
return $this->leadRepository
->resetModel()
->whereBetween('created_at', [$startDate, $endDate])
->avg('lead_value') ?? 0;
}
/**
* Retrieves total won lead value and their progress.
*/
public function getTotalWonLeadValueProgress(): array
{
return [
'previous' => $previous = $this->getTotalWonLeadValue($this->lastStartDate, $this->lastEndDate),
'current' => $current = $this->getTotalWonLeadValue($this->startDate, $this->endDate),
'formatted_total' => core()->formatBasePrice($current),
'progress' => $this->getPercentageChange($previous, $current),
];
}
/**
* Retrieves average won lead value
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
* @return array
*/
public function getTotalWonLeadValue($startDate, $endDate): ?float
{
return $this->leadRepository
->resetModel()
->whereIn('lead_pipeline_stage_id', $this->wonStageIds)
->whereBetween('created_at', [$startDate, $endDate])
->sum('lead_value');
}
/**
* Retrieves average lost lead value and their progress.
*/
public function getTotalLostLeadValueProgress(): array
{
return [
'previous' => $previous = $this->getTotalLostLeadValue($this->lastStartDate, $this->lastEndDate),
'current' => $current = $this->getTotalLostLeadValue($this->startDate, $this->endDate),
'formatted_total' => core()->formatBasePrice($current),
'progress' => $this->getPercentageChange($previous, $current),
];
}
/**
* Retrieves average lost lead value
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
* @return array
*/
public function getTotalLostLeadValue($startDate, $endDate): ?float
{
return $this->leadRepository
->resetModel()
->whereIn('lead_pipeline_stage_id', $this->lostStageIds)
->whereBetween('created_at', [$startDate, $endDate])
->sum('lead_value');
}
/**
* Retrieves total lead value by sources.
*/
public function getTotalWonLeadValueBySources()
{
return $this->leadRepository
->resetModel()
->select(
'lead_sources.name',
DB::raw('SUM(lead_value) as total')
)
->leftJoin('lead_sources', 'leads.lead_source_id', '=', 'lead_sources.id')
->whereIn('lead_pipeline_stage_id', $this->wonStageIds)
->whereBetween('leads.created_at', [$this->startDate, $this->endDate])
->groupBy('lead_source_id')
->get();
}
/**
* Retrieves total lead value by types.
*/
public function getTotalWonLeadValueByTypes()
{
return $this->leadRepository
->resetModel()
->select(
'lead_types.name',
DB::raw('SUM(lead_value) as total')
)
->leftJoin('lead_types', 'leads.lead_type_id', '=', 'lead_types.id')
->whereIn('lead_pipeline_stage_id', $this->wonStageIds)
->whereBetween('leads.created_at', [$this->startDate, $this->endDate])
->groupBy('lead_type_id')
->get();
}
/**
* Retrieves open leads by states.
*/
public function getOpenLeadsByStates()
{
return $this->leadRepository
->resetModel()
->select(
'lead_pipeline_stages.name',
DB::raw('COUNT(lead_value) as total')
)
->leftJoin('lead_pipeline_stages', 'leads.lead_pipeline_stage_id', '=', 'lead_pipeline_stages.id')
->whereNotIn('lead_pipeline_stage_id', $this->wonStageIds)
->whereNotIn('lead_pipeline_stage_id', $this->lostStageIds)
->whereBetween('leads.created_at', [$this->startDate, $this->endDate])
->groupBy('lead_pipeline_stage_id')
->orderByDesc('total')
->get();
}
/**
* Returns over time stats.
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
* @param string $valueColumn
* @param string $dateColumn
* @param string $period
*/
public function getOverTimeStats($startDate, $endDate, $valueColumn, $dateColumn = 'created_at', $period = 'auto'): array
{
$period = $this->determinePeriod($period);
$intervals = $this->generateTimeIntervals($startDate, $endDate, $period);
$groupColumn = $this->getGroupColumn($dateColumn, $period);
$query = $this->leadRepository
->resetModel()
->select(
DB::raw("$groupColumn AS date"),
DB::raw('COUNT(DISTINCT id) AS count'),
DB::raw('SUM('.\DB::getTablePrefix()."$valueColumn) AS total")
)
->whereIn('lead_pipeline_stage_id', $this->stageIds)
->whereBetween($dateColumn, [$startDate, $endDate])
->groupBy(DB::raw($groupColumn))
->orderBy(DB::raw($groupColumn));
$results = $query->get();
$resultLookup = $results->keyBy('date');
$stats = [];
foreach ($intervals as $interval) {
$result = $resultLookup->get($interval['key']);
$stats[] = [
'label' => $interval['label'],
'count' => $result ? (int) $result->count : 0,
'total' => $result ? (float) $result->total : 0,
];
}
return $stats;
}
/**
* Generate time intervals based on period
*/
protected function generateTimeIntervals(Carbon $startDate, Carbon $endDate, string $period): array
{
$intervals = [];
$current = $startDate->copy();
while ($current <= $endDate) {
$interval = [
'key' => $this->formatDateForGrouping($current, $period),
'label' => $this->formatDateForLabel($current, $period),
];
$intervals[] = $interval;
switch ($period) {
case 'day':
$current->addDay();
break;
case 'week':
$current->addWeek();
break;
case 'month':
$current->addMonth();
break;
case 'year':
$current->addYear();
break;
}
}
return $intervals;
}
/**
* Get the SQL group column based on period
*/
protected function getGroupColumn(string $dateColumn, string $period): string
{
switch ($period) {
case 'day':
return "DATE($dateColumn)";
case 'week':
return "DATE_FORMAT($dateColumn, '%Y-%u')";
case 'month':
return "DATE_FORMAT($dateColumn, '%Y-%m')";
case 'year':
return "YEAR($dateColumn)";
default:
return "DATE($dateColumn)";
}
}
/**
* Format date for grouping key
*/
protected function formatDateForGrouping(Carbon $date, string $period): string
{
switch ($period) {
case 'day':
return $date->format('Y-m-d');
case 'week':
return $date->format('Y-W');
case 'month':
return $date->format('Y-m');
case 'year':
return $date->format('Y');
default:
return $date->format('Y-m-d');
}
}
/**
* Format date for display label
*/
protected function formatDateForLabel(Carbon $date, string $period): string
{
switch ($period) {
case 'day':
return $date->format('M d');
case 'week':
return 'Week '.$date->format('W, Y');
case 'month':
return $date->format('M Y');
case 'year':
return $date->format('Y');
default:
return $date->format('M d');
}
}
}

View File

@@ -0,0 +1,80 @@
<?php
namespace Webkul\Admin\Helpers\Reporting;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Webkul\Contact\Repositories\OrganizationRepository;
class Organization extends AbstractReporting
{
/**
* Create a helper instance.
*
* @return void
*/
public function __construct(protected OrganizationRepository $organizationRepository)
{
parent::__construct();
}
/**
* Retrieves total organizations and their progress.
*/
public function getTotalOrganizationsProgress(): array
{
return [
'previous' => $previous = $this->getTotalOrganizations($this->lastStartDate, $this->lastEndDate),
'current' => $current = $this->getTotalOrganizations($this->startDate, $this->endDate),
'progress' => $this->getPercentageChange($previous, $current),
];
}
/**
* Retrieves total organizations by date
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
*/
public function getTotalOrganizations($startDate, $endDate): int
{
return $this->organizationRepository
->resetModel()
->whereBetween('created_at', [$startDate, $endDate])
->count();
}
/**
* Gets top customers by revenue.
*
* @param int $limit
*/
public function getTopOrganizationsByRevenue($limit = null): Collection
{
$tablePrefix = DB::getTablePrefix();
$items = $this->organizationRepository
->resetModel()
->leftJoin('persons', 'organizations.id', '=', 'persons.organization_id')
->leftJoin('leads', 'persons.id', '=', 'leads.person_id')
->select('*', 'persons.id as id')
->addSelect(DB::raw('SUM('.$tablePrefix.'leads.lead_value) as revenue'))
->whereBetween('leads.closed_at', [$this->startDate, $this->endDate])
->having(DB::raw('SUM('.$tablePrefix.'leads.lead_value)'), '>', 0)
->groupBy('organization_id')
->orderBy('revenue', 'DESC')
->limit($limit)
->get();
$items = $items->map(function ($item) {
return [
'id' => $item->id,
'name' => $item->name,
'revenue' => $item->revenue,
'formatted_revenue' => core()->formatBasePrice($item->revenue),
];
});
return $items;
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace Webkul\Admin\Helpers\Reporting;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Webkul\Contact\Repositories\PersonRepository;
class Person extends AbstractReporting
{
/**
* Create a helper instance.
*
* @return void
*/
public function __construct(protected PersonRepository $personRepository)
{
parent::__construct();
}
/**
* Retrieves total persons and their progress.
*/
public function getTotalPersonsProgress(): array
{
return [
'previous' => $previous = $this->getTotalPersons($this->lastStartDate, $this->lastEndDate),
'current' => $current = $this->getTotalPersons($this->startDate, $this->endDate),
'progress' => $this->getPercentageChange($previous, $current),
];
}
/**
* Retrieves total persons by date
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
*/
public function getTotalPersons($startDate, $endDate): int
{
return $this->personRepository
->resetModel()
->whereBetween('created_at', [$startDate, $endDate])
->count();
}
/**
* Gets top customers by revenue.
*
* @param int $limit
*/
public function getTopCustomersByRevenue($limit = null): Collection
{
$tablePrefix = DB::getTablePrefix();
$items = $this->personRepository
->resetModel()
->leftJoin('leads', 'persons.id', '=', 'leads.person_id')
->select('*', 'persons.id as id')
->addSelect(DB::raw('SUM('.$tablePrefix.'leads.lead_value) as revenue'))
->whereBetween('leads.closed_at', [$this->startDate, $this->endDate])
->having(DB::raw('SUM('.$tablePrefix.'leads.lead_value)'), '>', 0)
->groupBy('person_id')
->orderBy('revenue', 'DESC')
->limit($limit)
->get();
$items = $items->map(function ($item) {
return [
'id' => $item->id,
'name' => $item->name,
'emails' => $item->emails,
'contact_numbers' => $item->contact_numbers,
'revenue' => $item->revenue,
'formatted_revenue' => core()->formatBasePrice($item->revenue),
];
});
return $items;
}
}

View File

@@ -0,0 +1,94 @@
<?php
namespace Webkul\Admin\Helpers\Reporting;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Webkul\Lead\Repositories\ProductRepository;
class Product extends AbstractReporting
{
/**
* Create a helper instance.
*
* @return void
*/
public function __construct(
protected ProductRepository $productRepository
) {
parent::__construct();
}
/**
* Gets top-selling products by revenue.
*
* @param int $limit
*/
public function getTopSellingProductsByRevenue($limit = null): Collection
{
$tablePrefix = DB::getTablePrefix();
$items = $this->productRepository
->resetModel()
->with('product')
->leftJoin('leads', 'lead_products.lead_id', '=', 'leads.id')
->leftJoin('products', 'lead_products.product_id', '=', 'products.id')
->select('*')
->addSelect(DB::raw('SUM('.$tablePrefix.'lead_products.amount) as revenue'))
->whereBetween('leads.closed_at', [$this->startDate, $this->endDate])
->having(DB::raw('SUM('.$tablePrefix.'lead_products.amount)'), '>', 0)
->groupBy('product_id')
->orderBy('revenue', 'DESC')
->limit($limit)
->get();
$items = $items->map(function ($item) {
return [
'id' => $item->product_id,
'name' => $item->name,
'price' => $item->product?->price,
'formatted_price' => core()->formatBasePrice($item->price),
'revenue' => $item->revenue,
'formatted_revenue' => core()->formatBasePrice($item->revenue),
];
});
return $items;
}
/**
* Gets top-selling products by quantity.
*
* @param int $limit
*/
public function getTopSellingProductsByQuantity($limit = null): Collection
{
$tablePrefix = DB::getTablePrefix();
$items = $this->productRepository
->resetModel()
->with('product')
->leftJoin('leads', 'lead_products.lead_id', '=', 'leads.id')
->leftJoin('products', 'lead_products.product_id', '=', 'products.id')
->select('*')
->addSelect(DB::raw('SUM('.$tablePrefix.'lead_products.quantity) as total_qty_ordered'))
->whereBetween('leads.closed_at', [$this->startDate, $this->endDate])
->having(DB::raw('SUM('.$tablePrefix.'lead_products.quantity)'), '>', 0)
->groupBy('product_id')
->orderBy('total_qty_ordered', 'DESC')
->limit($limit)
->get();
$items = $items->map(function ($item) {
return [
'id' => $item->product_id,
'name' => $item->name,
'price' => $item->product?->price,
'formatted_price' => core()->formatBasePrice($item->price),
'total_qty_ordered' => $item->total_qty_ordered,
];
});
return $items;
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace Webkul\Admin\Helpers\Reporting;
use Webkul\Quote\Repositories\QuoteRepository;
class Quote extends AbstractReporting
{
/**
* Create a helper instance.
*
* @return void
*/
public function __construct(protected QuoteRepository $quoteRepository)
{
parent::__construct();
}
/**
* Retrieves total quotes and their progress.
*/
public function getTotalQuotesProgress(): array
{
return [
'previous' => $previous = $this->getTotalQuotes($this->lastStartDate, $this->lastEndDate),
'current' => $current = $this->getTotalQuotes($this->startDate, $this->endDate),
'progress' => $this->getPercentageChange($previous, $current),
];
}
/**
* Retrieves total quotes by date
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
*/
public function getTotalQuotes($startDate, $endDate): int
{
return $this->quoteRepository
->resetModel()
->whereBetween('created_at', [$startDate, $endDate])
->count();
}
}

View File

@@ -0,0 +1,261 @@
<?php
namespace Webkul\Admin\Http\Controllers\Activity;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Webkul\Activity\Repositories\ActivityRepository;
use Webkul\Activity\Repositories\FileRepository;
use Webkul\Admin\DataGrids\Activity\ActivityDataGrid;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Admin\Http\Requests\MassDestroyRequest;
use Webkul\Admin\Http\Requests\MassUpdateRequest;
use Webkul\Admin\Http\Resources\ActivityResource;
use Webkul\Attribute\Repositories\AttributeRepository;
class ActivityController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(
protected ActivityRepository $activityRepository,
protected FileRepository $fileRepository,
protected AttributeRepository $attributeRepository,
) {}
/**
* Display a listing of the resource.
*/
public function index(): View
{
return view('admin::activities.index');
}
/**
* Returns a listing of the resource.
*/
public function get(): JsonResponse
{
if (! request()->has('view_type')) {
return datagrid(ActivityDataGrid::class)->process();
}
$startDate = request()->get('startDate')
? Carbon::createFromTimeString(request()->get('startDate').' 00:00:01')
: Carbon::now()->startOfWeek()->format('Y-m-d H:i:s');
$endDate = request()->get('endDate')
? Carbon::createFromTimeString(request()->get('endDate').' 23:59:59')
: Carbon::now()->endOfWeek()->format('Y-m-d H:i:s');
$activities = $this->activityRepository->getActivities([$startDate, $endDate])->toArray();
return response()->json([
'activities' => $activities,
]);
}
/**
* Store a newly created resource in storage.
*/
public function store(): RedirectResponse|JsonResponse
{
$this->validate(request(), [
'type' => 'required',
'comment' => 'required_if:type,note',
'schedule_from' => 'required_unless:type,note,file',
'schedule_to' => 'required_unless:type,note,file',
'file' => 'required_if:type,file',
]);
if (request('type') === 'meeting') {
/**
* Check if meeting is overlapping with other meetings.
*/
$isOverlapping = $this->activityRepository->isDurationOverlapping(
request()->input('schedule_from'),
request()->input('schedule_to'),
request()->input('participants'),
request()->input('id')
);
if ($isOverlapping) {
if (request()->ajax()) {
return response()->json([
'message' => trans('admin::app.activities.overlapping-error'),
], 400);
}
session()->flash('success', trans('admin::app.activities.overlapping-error'));
return redirect()->back();
}
}
Event::dispatch('activity.create.before');
$activity = $this->activityRepository->create(array_merge(request()->all(), [
'is_done' => request('type') == 'note' ? 1 : 0,
'user_id' => auth()->guard('user')->user()->id,
]));
Event::dispatch('activity.create.after', $activity);
if (request()->ajax()) {
return response()->json([
'data' => new ActivityResource($activity),
'message' => trans('admin::app.activities.create-success'),
]);
}
session()->flash('success', trans('admin::app.activities.create-success'));
return redirect()->back();
}
/**
* Show the form for editing the specified resource.
*/
public function edit(int $id): View
{
$activity = $this->activityRepository->findOrFail($id);
$leadId = old('lead_id') ?? optional($activity->leads()->first())->id;
$lookUpEntityData = $this->attributeRepository->getLookUpEntity('leads', $leadId);
return view('admin::activities.edit', compact('activity', 'lookUpEntityData'));
}
/**
* Update the specified resource in storage.
*/
public function update($id): RedirectResponse|JsonResponse
{
Event::dispatch('activity.update.before', $id);
$data = request()->all();
$activity = $this->activityRepository->update($data, $id);
/**
* We will not use `empty` directly here because `lead_id` can be a blank string
* from the activity form. However, on the activity view page, we are only updating the
* `is_done` field, so `lead_id` will not be present in that case.
*/
if (isset($data['lead_id'])) {
$activity->leads()->sync(
! empty($data['lead_id'])
? [$data['lead_id']]
: []
);
}
Event::dispatch('activity.update.after', $activity);
if (request()->ajax()) {
return response()->json([
'data' => new ActivityResource($activity),
'message' => trans('admin::app.activities.update-success'),
]);
}
session()->flash('success', trans('admin::app.activities.update-success'));
return redirect()->route('admin.activities.index');
}
/**
* Mass Update the specified resources.
*/
public function massUpdate(MassUpdateRequest $massUpdateRequest): JsonResponse
{
$activities = $this->activityRepository->findWhereIn('id', $massUpdateRequest->input('indices'));
foreach ($activities as $activity) {
Event::dispatch('activity.update.before', $activity->id);
$activity = $this->activityRepository->update([
'is_done' => $massUpdateRequest->input('value'),
], $activity->id);
Event::dispatch('activity.update.after', $activity);
}
return response()->json([
'message' => trans('admin::app.activities.mass-update-success'),
]);
}
/**
* Download file from storage.
*/
public function download(int $id): StreamedResponse
{
try {
$file = $this->fileRepository->findOrFail($id);
return Storage::download($file->path);
} catch (\Exception $exception) {
abort(404);
}
}
/*
* Remove the specified resource from storage.
*/
public function destroy(int $id): JsonResponse
{
$activity = $this->activityRepository->findOrFail($id);
try {
Event::dispatch('activity.delete.before', $id);
$activity?->delete($id);
Event::dispatch('activity.delete.after', $id);
return response()->json([
'message' => trans('admin::app.activities.destroy-success'),
], 200);
} catch (\Exception $exception) {
return response()->json([
'message' => trans('admin::app.activities.destroy-failed'),
], 400);
}
}
/**
* Mass Delete the specified resources.
*/
public function massDestroy(MassDestroyRequest $massDestroyRequest): JsonResponse
{
$activities = $this->activityRepository->findWhereIn('id', $massDestroyRequest->input('indices'));
try {
foreach ($activities as $activity) {
Event::dispatch('activity.delete.before', $activity->id);
$this->activityRepository->delete($activity->id);
Event::dispatch('activity.delete.after', $activity->id);
}
return response()->json([
'message' => trans('admin::app.activities.mass-destroy-success'),
]);
} catch (\Exception $exception) {
return response()->json([
'message' => trans('admin::app.activities.mass-delete-failed'),
], 400);
}
}
}

View File

@@ -0,0 +1,84 @@
<?php
namespace Webkul\Admin\Http\Controllers\Configuration;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Admin\Http\Requests\ConfigurationForm;
use Webkul\Core\Repositories\CoreConfigRepository as ConfigurationRepository;
class ConfigurationController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(protected ConfigurationRepository $configurationRepository) {}
/**
* Display a listing of the resource.
*/
public function index(): View
{
if (
request()->route('slug')
&& request()->route('slug2')
) {
return view('admin::configuration.edit');
}
return view('admin::configuration.index');
}
/**
* Store a newly created resource in storage.
*/
public function store(ConfigurationForm $request): RedirectResponse
{
Event::dispatch('core.configuration.save.before');
$this->configurationRepository->create($request->all());
Event::dispatch('core.configuration.save.after');
session()->flash('success', trans('admin::app.configuration.index.save-success'));
return redirect()->back();
}
/**
* download the file for the specified resource.
*
* @return \Illuminate\Http\Response
*/
public function download()
{
$path = request()->route()->parameters()['path'];
$fileName = 'configuration/'.$path;
$config = $this->configurationRepository->findOneByField('value', $fileName);
return Storage::download($config['value']);
}
/**
* Search for configurations.
*/
public function search(): JsonResponse
{
$results = $this->configurationRepository->search(
system_config()->getItems(),
request()->query('query')
);
return new JsonResponse([
'data' => $results,
]);
}
}

View File

@@ -0,0 +1,130 @@
<?php
namespace Webkul\Admin\Http\Controllers\Contact;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Event;
use Illuminate\View\View;
use Webkul\Admin\DataGrids\Contact\OrganizationDataGrid;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Admin\Http\Requests\AttributeForm;
use Webkul\Admin\Http\Requests\MassDestroyRequest;
use Webkul\Contact\Repositories\OrganizationRepository;
class OrganizationController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(protected OrganizationRepository $organizationRepository)
{
request()->request->add(['entity_type' => 'organizations']);
}
/**
* Display a listing of the resource.
*/
public function index(): View|JsonResponse
{
if (request()->ajax()) {
return datagrid(OrganizationDataGrid::class)->process();
}
return view('admin::contacts.organizations.index');
}
/**
* Show the form for creating a new resource.
*/
public function create(): View
{
return view('admin::contacts.organizations.create');
}
/**
* Store a newly created resource in storage.
*/
public function store(AttributeForm $request): RedirectResponse
{
Event::dispatch('contacts.organization.create.before');
$organization = $this->organizationRepository->create(request()->all());
Event::dispatch('contacts.organization.create.after', $organization);
session()->flash('success', trans('admin::app.contacts.organizations.index.create-success'));
return redirect()->route('admin.contacts.organizations.index');
}
/**
* Show the form for editing the specified resource.
*/
public function edit(int $id): View
{
$organization = $this->organizationRepository->findOrFail($id);
return view('admin::contacts.organizations.edit', compact('organization'));
}
/**
* Update the specified resource in storage.
*/
public function update(AttributeForm $request, int $id): RedirectResponse
{
Event::dispatch('contacts.organization.update.before', $id);
$organization = $this->organizationRepository->update(request()->all(), $id);
Event::dispatch('contacts.organization.update.after', $organization);
session()->flash('success', trans('admin::app.contacts.organizations.index.update-success'));
return redirect()->route('admin.contacts.organizations.index');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(int $id): JsonResponse
{
try {
Event::dispatch('contact.organization.delete.before', $id);
$this->organizationRepository->delete($id);
Event::dispatch('contact.organization.delete.after', $id);
return response()->json([
'message' => trans('admin::app.contacts.organizations.index.delete-success'),
], 200);
} catch (\Exception $exception) {
return response()->json([
'message' => trans('admin::app.contacts.organizations.index.delete-failed'),
], 400);
}
}
/**
* Mass Delete the specified resources.
*/
public function massDestroy(MassDestroyRequest $massDestroyRequest): JsonResponse
{
$organizations = $this->organizationRepository->findWhereIn('id', $massDestroyRequest->input('indices'));
foreach ($organizations as $organization) {
Event::dispatch('contact.organization.delete.before', $organization);
$this->organizationRepository->delete($organization->id);
Event::dispatch('contact.organization.delete.after', $organization);
}
return response()->json([
'message' => trans('admin::app.contacts.organizations.index.delete-success'),
]);
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace Webkul\Admin\Http\Controllers\Contact\Persons;
use Illuminate\Support\Facades\DB;
use Webkul\Activity\Repositories\ActivityRepository;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Admin\Http\Resources\ActivityResource;
use Webkul\Email\Repositories\AttachmentRepository;
use Webkul\Email\Repositories\EmailRepository;
class ActivityController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(
protected ActivityRepository $activityRepository,
protected EmailRepository $emailRepository,
protected AttachmentRepository $attachmentRepository
) {}
/**
* Display a listing of the resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function index($id)
{
$activities = $this->activityRepository
->leftJoin('person_activities', 'activities.id', '=', 'person_activities.activity_id')
->where('person_activities.person_id', $id)
->get();
return ActivityResource::collection($this->concatEmailAsActivities($id, $activities));
}
/**
* Store a newly created resource in storage.
*/
public function concatEmailAsActivities($personId, $activities)
{
$emails = DB::table('emails as child')
->select('child.*')
->join('emails as parent', 'child.parent_id', '=', 'parent.id')
->where('parent.person_id', $personId)
->union(DB::table('emails as parent')->where('parent.person_id', $personId))
->get();
return $activities->concat($emails->map(function ($email) {
return (object) [
'id' => $email->id,
'parent_id' => $email->parent_id,
'title' => $email->subject,
'type' => 'email',
'is_done' => 1,
'comment' => $email->reply,
'schedule_from' => null,
'schedule_to' => null,
'user' => auth()->guard('user')->user(),
'participants' => [],
'location' => null,
'additional' => [
'folders' => json_decode($email->folders),
'from' => json_decode($email->from),
'to' => json_decode($email->reply_to),
'cc' => json_decode($email->cc),
'bcc' => json_decode($email->bcc),
],
'files' => $this->attachmentRepository->findWhere(['email_id' => $email->id])->map(function ($attachment) {
return (object) [
'id' => $attachment->id,
'name' => $attachment->name,
'path' => $attachment->path,
'url' => $attachment->url,
'created_at' => $attachment->created_at,
'updated_at' => $attachment->updated_at,
];
}),
'created_at' => $email->created_at,
'updated_at' => $email->updated_at,
];
}))->sortByDesc('id')->sortByDesc('created_at');
}
}

View File

@@ -0,0 +1,235 @@
<?php
namespace Webkul\Admin\Http\Controllers\Contact\Persons;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Facades\Event;
use Illuminate\View\View;
use Prettus\Repository\Criteria\RequestCriteria;
use Webkul\Admin\DataGrids\Contact\PersonDataGrid;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Admin\Http\Requests\AttributeForm;
use Webkul\Admin\Http\Requests\MassDestroyRequest;
use Webkul\Admin\Http\Resources\PersonResource;
use Webkul\Contact\Repositories\PersonRepository;
class PersonController extends Controller
{
/**
* Create a new class instance.
*
* @return void
*/
public function __construct(protected PersonRepository $personRepository)
{
request()->request->add(['entity_type' => 'persons']);
}
/**
* Display a listing of the resource.
*/
public function index()
{
if (request()->ajax()) {
return datagrid(PersonDataGrid::class)->process();
}
return view('admin::contacts.persons.index');
}
/**
* Show the form for creating a new resource.
*/
public function create(): View
{
return view('admin::contacts.persons.create');
}
/**
* Store a newly created resource in storage.
*/
public function store(AttributeForm $request): RedirectResponse|JsonResponse
{
Event::dispatch('contacts.person.create.before');
$person = $this->personRepository->create($request->all());
Event::dispatch('contacts.person.create.after', $person);
if (request()->ajax()) {
return response()->json([
'data' => $person,
'message' => trans('admin::app.contacts.persons.index.create-success'),
]);
}
session()->flash('success', trans('admin::app.contacts.persons.index.create-success'));
return redirect()->route('admin.contacts.persons.index');
}
/**
* Display the specified resource.
*/
public function show(int $id): View
{
$person = $this->personRepository->findOrFail($id);
return view('admin::contacts.persons.view', compact('person'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(int $id): View
{
$person = $this->personRepository->findOrFail($id);
return view('admin::contacts.persons.edit', compact('person'));
}
/**
* Update the specified resource in storage.
*/
public function update(AttributeForm $request, int $id): RedirectResponse|JsonResponse
{
Event::dispatch('contacts.person.update.before', $id);
$person = $this->personRepository->update($request->all(), $id);
Event::dispatch('contacts.person.update.after', $person);
if (request()->ajax()) {
return response()->json([
'data' => $person,
'message' => trans('admin::app.contacts.persons.index.update-success'),
], 200);
}
session()->flash('success', trans('admin::app.contacts.persons.index.update-success'));
return redirect()->route('admin.contacts.persons.index');
}
/**
* Search person results.
*/
public function search(): JsonResource
{
if ($userIds = bouncer()->getAuthorizedUserIds()) {
$persons = $this->personRepository
->pushCriteria(app(RequestCriteria::class))
->findWhereIn('user_id', $userIds);
} else {
$persons = $this->personRepository
->pushCriteria(app(RequestCriteria::class))
->all();
}
return PersonResource::collection($persons);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(int $id): JsonResponse
{
$person = $this->personRepository->findOrFail($id);
if (
$person->leads
&& $person->leads->count() > 0
) {
return response()->json([
'message' => trans('admin::app.contacts.persons.index.delete-failed'),
], 400);
}
try {
Event::dispatch('contacts.person.delete.before', $person);
$person->delete();
Event::dispatch('contacts.person.delete.after', $person);
return response()->json([
'message' => trans('admin::app.contacts.persons.index.delete-success'),
], 200);
} catch (Exception $exception) {
return response()->json([
'message' => trans('admin::app.contacts.persons.index.delete-failed'),
], 400);
}
}
/**
* Mass destroy the specified resources from storage.
*/
public function massDestroy(MassDestroyRequest $request): JsonResponse
{
try {
$persons = $this->personRepository->findWhereIn('id', $request->input('indices', []));
$deletedCount = 0;
$blockedCount = 0;
foreach ($persons as $person) {
if (
$person->leads
&& $person->leads->count() > 0
) {
$blockedCount++;
continue;
}
Event::dispatch('contact.person.delete.before', $person);
$this->personRepository->delete($person->id);
Event::dispatch('contact.person.delete.after', $person);
$deletedCount++;
}
$statusCode = 200;
switch (true) {
case $deletedCount > 0 && $blockedCount === 0:
$message = trans('admin::app.contacts.persons.index.all-delete-success');
break;
case $deletedCount > 0 && $blockedCount > 0:
$message = trans('admin::app.contacts.persons.index.partial-delete-warning');
break;
case $deletedCount === 0 && $blockedCount > 0:
$message = trans('admin::app.contacts.persons.index.none-delete-warning');
$statusCode = 400;
break;
default:
$message = trans('admin::app.contacts.persons.index.no-selection');
$statusCode = 400;
break;
}
return response()->json(['message' => $message], $statusCode);
} catch (Exception $exception) {
return response()->json([
'message' => trans('admin::app.contacts.persons.index.delete-failed'),
], 400);
}
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace Webkul\Admin\Http\Controllers\Contact\Persons;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Event;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Contact\Repositories\PersonRepository;
class TagController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(protected PersonRepository $personRepository) {}
/**
* Store a newly created resource in storage.
*/
public function attach(int $id): JsonResponse
{
Event::dispatch('persons.tag.create.before', $id);
$person = $this->personRepository->find($id);
if (! $person->tags->contains(request()->input('tag_id'))) {
$person->tags()->attach(request()->input('tag_id'));
}
Event::dispatch('persons.tag.create.after', $person);
return response()->json([
'message' => trans('admin::app.contacts.persons.view.tags.create-success'),
]);
}
/**
* Remove the specified resource from storage.
*/
public function detach(int $personId): JsonResponse
{
Event::dispatch('persons.tag.delete.before', $personId);
$person = $this->personRepository->find($personId);
$person->tags()->detach(request()->input('tag_id'));
Event::dispatch('persons.tag.delete.after', $person);
return response()->json([
'message' => trans('admin::app.contacts.persons.view.tags.destroy-success'),
]);
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Webkul\Admin\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function redirectToLogin()
{
return redirect()->route('admin.session.create');
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace Webkul\Admin\Http\Controllers;
use Webkul\Admin\Helpers\Dashboard;
class DashboardController extends Controller
{
/**
* Request param functions
*
* @var array
*/
protected $typeFunctions = [
'over-all' => 'getOverAllStats',
'revenue-stats' => 'getRevenueStats',
'total-leads' => 'getTotalLeadsStats',
'revenue-by-sources' => 'getLeadsStatsBySources',
'revenue-by-types' => 'getLeadsStatsByTypes',
'top-selling-products' => 'getTopSellingProducts',
'top-persons' => 'getTopPersons',
'open-leads-by-states' => 'getOpenLeadsByStates',
];
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(protected Dashboard $dashboardHelper) {}
/**
* Display a listing of the resource.
*
* @return \Illuminate\View\View
*/
public function index()
{
return view('admin::dashboard.index')->with([
'startDate' => $this->dashboardHelper->getStartDate(),
'endDate' => $this->dashboardHelper->getEndDate(),
]);
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\JsonResponse
*/
public function stats()
{
$stats = $this->dashboardHelper->{$this->typeFunctions[request()->query('type')]}();
return response()->json([
'statistics' => $stats,
'date_range' => $this->dashboardHelper->getDateRange(),
]);
}
}

View File

@@ -0,0 +1,117 @@
<?php
namespace Webkul\Admin\Http\Controllers\DataGrid;
use Illuminate\Support\Facades\Event;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\DataGrid\Repositories\SavedFilterRepository;
class SavedFilterController extends Controller
{
/**
* Create a new controller instance.
*/
public function __construct(protected SavedFilterRepository $savedFilterRepository) {}
/**
* Save filters to the database.
*/
public function store()
{
$userId = auth()->guard()->user()->id;
$this->validate(request(), [
'name' => 'required|unique:datagrid_saved_filters,name,NULL,id,src,'.request('src').',user_id,'.$userId,
]);
Event::dispatch('datagrid.saved_filter.create.before');
$savedFilter = $this->savedFilterRepository->create([
'user_id' => $userId,
'name' => request('name'),
'src' => request('src'),
'applied' => request('applied'),
]);
Event::dispatch('datagrid.saved_filter.create.after', $savedFilter);
return response()->json([
'data' => $savedFilter,
'message' => trans('admin::app.components.datagrid.toolbar.filter.saved-success'),
]);
}
/**
* Retrieves the saved filters.
*/
public function get()
{
$savedFilters = $this->savedFilterRepository->findWhere([
'src' => request()->get('src'),
'user_id' => auth()->guard()->user()->id,
]);
return response()->json(['data' => $savedFilters]);
}
/**
* Update the saved filter.
*/
public function update(int $id)
{
$userId = auth()->guard()->user()->id;
$this->validate(request(), [
'name' => 'required|unique:datagrid_saved_filters,name,'.$id.',id,src,'.request('src').',user_id,'.$userId,
]);
$savedFilter = $this->savedFilterRepository->findOneWhere([
'id' => $id,
'user_id' => auth()->guard()->user()->id,
]);
if (! $savedFilter) {
return response()->json([], 404);
}
Event::dispatch('datagrid.saved_filter.update.before', $id);
$updatedFilter = $this->savedFilterRepository->update(request()->only([
'name',
'src',
'applied',
]), $id);
Event::dispatch('datagrid.saved_filter.update.after', $updatedFilter);
return response()->json([
'data' => $updatedFilter,
'message' => trans('admin::app.components.datagrid.toolbar.filter.updated-success'),
]);
}
/**
* Delete the saved filter.
*/
public function destroy(int $id)
{
Event::dispatch('datagrid.saved_filter.delete.before', $id);
$success = $this->savedFilterRepository->deleteWhere([
'id' => $id,
'user_id' => auth()->guard()->user()->id,
]);
Event::dispatch('datagrid.saved_filter.delete.after', $id);
if (! $success) {
return response()->json([
'message' => trans('admin::app.components.datagrid.toolbar.filter.delete-error'),
]);
}
return response()->json([
'message' => trans('admin::app.components.datagrid.toolbar.filter.delete-success'),
]);
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Webkul\Admin\Http\Controllers;
use Illuminate\Support\Facades\Crypt;
class DataGridController extends Controller
{
/**
* Look up.
*/
public function lookUp()
{
/**
* Validation for parameters.
*/
$params = $this->validate(request(), [
'datagrid_id' => ['required'],
'column' => ['required'],
'search' => ['required', 'min:2'],
]);
/**
* Preparing the datagrid instance and only columns.
*/
$datagrid = app(Crypt::decryptString($params['datagrid_id']));
$datagrid->prepareColumns();
/**
* Finding the first column from the collection.
*/
$column = collect($datagrid->getColumns())->map(fn ($column) => $column->toArray())->where('index', $params['column'])->firstOrFail();
/**
* Fetching on the basis of column options.
*/
return app($column['filterable_options']['repository'])
->select([$column['filterable_options']['column']['label'].' as label', $column['filterable_options']['column']['value'].' as value'])
->where($column['filterable_options']['column']['label'], 'LIKE', '%'.$params['search'].'%')
->get();
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace Webkul\Admin\Http\Controllers\Lead;
use Illuminate\Support\Facades\DB;
use Webkul\Activity\Repositories\ActivityRepository;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Admin\Http\Resources\ActivityResource;
use Webkul\Email\Repositories\AttachmentRepository;
use Webkul\Email\Repositories\EmailRepository;
class ActivityController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(
protected ActivityRepository $activityRepository,
protected EmailRepository $emailRepository,
protected AttachmentRepository $attachmentRepository
) {}
/**
* Display a listing of the resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function index($id)
{
$activities = $this->activityRepository
->leftJoin('lead_activities', 'activities.id', '=', 'lead_activities.activity_id')
->where('lead_activities.lead_id', $id)
->get();
return ActivityResource::collection($this->concatEmailAsActivities($id, $activities));
}
/**
* Store a newly created resource in storage.
*/
public function concatEmailAsActivities($leadId, $activities)
{
$emails = DB::table('emails as child')
->select('child.*')
->join('emails as parent', 'child.parent_id', '=', 'parent.id')
->where('parent.lead_id', $leadId)
->union(DB::table('emails as parent')->where('parent.lead_id', $leadId))
->get();
return $activities->concat($emails->map(function ($email) {
return (object) [
'id' => $email->id,
'parent_id' => $email->parent_id,
'title' => $email->subject,
'type' => 'email',
'is_done' => 1,
'comment' => $email->reply,
'schedule_from' => null,
'schedule_to' => null,
'user' => auth()->guard('user')->user(),
'participants' => [],
'location' => null,
'additional' => [
'folders' => json_decode($email->folders),
'from' => json_decode($email->from),
'to' => json_decode($email->reply_to),
'cc' => json_decode($email->cc),
'bcc' => json_decode($email->bcc),
],
'files' => $this->attachmentRepository->findWhere(['email_id' => $email->id])->map(function ($attachment) {
return (object) [
'id' => $attachment->id,
'name' => $attachment->name,
'path' => $attachment->path,
'url' => $attachment->url,
'created_at' => $attachment->created_at,
'updated_at' => $attachment->updated_at,
];
}),
'created_at' => $email->created_at,
'updated_at' => $email->updated_at,
];
}))->sortByDesc('id')->sortByDesc('created_at');
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace Webkul\Admin\Http\Controllers\Lead;
use Illuminate\Support\Facades\Event;
use Webkul\Admin\Http\Controllers\Mail\EmailController as BaseEmailController;
use Webkul\Admin\Http\Resources\ActivityResource;
class EmailController extends BaseEmailController
{
/**
* Store a newly created resource in storage.
*
* @return \Illuminate\Http\Response
*/
public function store()
{
$response = json_decode(parent::store()->getContent(), true);
return response()->json([
'data' => $this->transformToActivity($response['data']),
'message' => $response['message'],
]);
return $response;
}
/**
* Store a newly created resource in storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function detach($id)
{
Event::dispatch('email.update.before', request()->input('email_id'));
$email = $this->emailRepository->update([
'lead_id' => null,
], request()->input('email_id'));
Event::dispatch('email.update.after', $email);
return response()->json([
'message' => trans('admin::app.mail.update-success'),
]);
}
/**
* Transform the email data to activity resource.
*
* @param array $data
* @return \Webkul\Admin\Http\Resources\ActivityResource
*/
public function transformToActivity($data)
{
return new ActivityResource((object) [
'id' => $data['id'],
'parent_id' => $data['parent_id'],
'title' => $data['subject'],
'type' => 'email',
'is_done' => 1,
'comment' => $data['reply'],
'schedule_from' => null,
'schedule_to' => null,
'user' => auth()->guard('user')->user(),
'participants' => [],
'location' => null,
'additional' => json_encode([
'folders' => $data['folders'],
'from' => $data['from'],
'to' => $data['reply_to'],
'cc' => $data['cc'],
'bcc' => $data['bcc'],
]),
'files' => array_map(function ($attachment) {
return (object) $attachment;
}, $data['attachments']),
'created_at' => $data['created_at'],
'updated_at' => $data['updated_at'],
]);
}
}

View File

@@ -0,0 +1,734 @@
<?php
namespace Webkul\Admin\Http\Controllers\Lead;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Validator;
use Illuminate\View\View;
use Prettus\Repository\Criteria\RequestCriteria;
use Webkul\Admin\DataGrids\Lead\LeadDataGrid;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Admin\Http\Requests\LeadForm;
use Webkul\Admin\Http\Requests\MassDestroyRequest;
use Webkul\Admin\Http\Requests\MassUpdateRequest;
use Webkul\Admin\Http\Resources\LeadResource;
use Webkul\Admin\Http\Resources\StageResource;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\Contact\Repositories\PersonRepository;
use Webkul\Lead\Helpers\MagicAI;
use Webkul\Lead\Repositories\LeadRepository;
use Webkul\Lead\Repositories\PipelineRepository;
use Webkul\Lead\Repositories\ProductRepository;
use Webkul\Lead\Repositories\SourceRepository;
use Webkul\Lead\Repositories\StageRepository;
use Webkul\Lead\Repositories\TypeRepository;
use Webkul\Lead\Services\MagicAIService;
use Webkul\Tag\Repositories\TagRepository;
use Webkul\User\Repositories\UserRepository;
class LeadController extends Controller
{
/**
* Const variable for supported types.
*/
const SUPPORTED_TYPES = 'pdf,bmp,jpeg,jpg,png,webp';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(
protected UserRepository $userRepository,
protected AttributeRepository $attributeRepository,
protected SourceRepository $sourceRepository,
protected TypeRepository $typeRepository,
protected PipelineRepository $pipelineRepository,
protected StageRepository $stageRepository,
protected LeadRepository $leadRepository,
protected ProductRepository $productRepository,
protected PersonRepository $personRepository
) {
request()->request->add(['entity_type' => 'leads']);
}
/**
* Display a listing of the resource.
*/
public function index()
{
if (request()->ajax()) {
return datagrid(LeadDataGrid::class)->process();
}
if (request('pipeline_id')) {
$pipeline = $this->pipelineRepository->find(request('pipeline_id'));
} else {
$pipeline = $this->pipelineRepository->getDefaultPipeline();
}
return view('admin::leads.index', [
'pipeline' => $pipeline,
'columns' => $this->getKanbanColumns(),
]);
}
/**
* Returns a listing of the resource.
*/
public function get(): JsonResponse
{
if (request()->query('pipeline_id')) {
$pipeline = $this->pipelineRepository->find(request()->query('pipeline_id'));
} else {
$pipeline = $this->pipelineRepository->getDefaultPipeline();
}
if ($stageId = request()->query('pipeline_stage_id')) {
$stages = $pipeline->stages->where('id', request()->query('pipeline_stage_id'));
} else {
$stages = $pipeline->stages;
}
foreach ($stages as $stage) {
/**
* We have to create a new instance of the lead repository every time, which is
* why we're not using the injected one.
*/
$query = app(LeadRepository::class)
->pushCriteria(app(RequestCriteria::class))
->where([
'lead_pipeline_id' => $pipeline->id,
'lead_pipeline_stage_id' => $stage->id,
]);
if ($userIds = bouncer()->getAuthorizedUserIds()) {
$query->whereIn('leads.user_id', $userIds);
}
$stage->lead_value = (clone $query)->sum('lead_value');
$data[$stage->sort_order] = (new StageResource($stage))->jsonSerialize();
$data[$stage->sort_order]['leads'] = [
'data' => LeadResource::collection($paginator = $query->with([
'tags',
'type',
'source',
'user',
'person',
'person.organization',
'pipeline',
'pipeline.stages',
'stage',
'attribute_values',
])->paginate(10)),
'meta' => [
'current_page' => $paginator->currentPage(),
'from' => $paginator->firstItem(),
'last_page' => $paginator->lastPage(),
'per_page' => $paginator->perPage(),
'to' => $paginator->lastItem(),
'total' => $paginator->total(),
],
];
}
return response()->json($data);
}
/**
* Show the form for creating a new resource.
*/
public function create(): View
{
return view('admin::leads.create');
}
/**
* Store a newly created resource in storage.
*/
public function store(LeadForm $request): RedirectResponse|JsonResponse
{
Event::dispatch('lead.create.before');
$data = request()->all();
$data['status'] = 1;
if (! empty($data['lead_pipeline_stage_id'])) {
$stage = $this->stageRepository->findOrFail($data['lead_pipeline_stage_id']);
$data['lead_pipeline_id'] = $stage->lead_pipeline_id;
} else {
if (empty($data['lead_pipeline_id'])) {
$pipeline = $this->pipelineRepository->getDefaultPipeline();
$data['lead_pipeline_id'] = $pipeline->id;
} else {
$pipeline = $this->pipelineRepository->findOrFail($data['lead_pipeline_id']);
}
$stage = $pipeline->stages()->first();
$data['lead_pipeline_stage_id'] = $stage->id;
}
if (in_array($stage->code, ['won', 'lost'])) {
$data['closed_at'] = Carbon::now();
}
$lead = $this->leadRepository->create($data);
if (request()->ajax()) {
return response()->json([
'message' => trans('admin::app.leads.create-success'),
'data' => new LeadResource($lead),
]);
}
Event::dispatch('lead.create.after', $lead);
session()->flash('success', trans('admin::app.leads.create-success'));
if (! empty($data['lead_pipeline_id'])) {
$params['pipeline_id'] = $data['lead_pipeline_id'];
}
return redirect()->route('admin.leads.index', $params ?? []);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(int $id): View
{
$lead = $this->leadRepository->findOrFail($id);
return view('admin::leads.edit', compact('lead'));
}
/**
* Display a resource.
*/
public function view(int $id)
{
$lead = $this->leadRepository->findOrFail($id);
$userIds = bouncer()->getAuthorizedUserIds();
if (
$userIds
&& ! in_array($lead->user_id, $userIds)
) {
return redirect()->route('admin.leads.index');
}
return view('admin::leads.view', compact('lead'));
}
/**
* Update the specified resource in storage.
*/
public function update(LeadForm $request, int $id): RedirectResponse|JsonResponse
{
Event::dispatch('lead.update.before', $id);
$data = $request->all();
if (isset($data['lead_pipeline_stage_id'])) {
$stage = $this->stageRepository->findOrFail($data['lead_pipeline_stage_id']);
$data['lead_pipeline_id'] = $stage->lead_pipeline_id;
} else {
$pipeline = $this->pipelineRepository->getDefaultPipeline();
$stage = $pipeline->stages()->first();
$data['lead_pipeline_id'] = $pipeline->id;
$data['lead_pipeline_stage_id'] = $stage->id;
}
$lead = $this->leadRepository->update($data, $id);
Event::dispatch('lead.update.after', $lead);
if (request()->ajax()) {
return response()->json([
'message' => trans('admin::app.leads.update-success'),
]);
}
session()->flash('success', trans('admin::app.leads.update-success'));
if (request()->has('closed_at')) {
return redirect()->back();
} else {
return redirect()->route('admin.leads.index', $data['lead_pipeline_id']);
}
}
/**
* Update the lead attributes.
*/
public function updateAttributes(int $id)
{
$data = request()->all();
$attributes = $this->attributeRepository->findWhere([
'entity_type' => 'leads',
['code', 'NOTIN', ['title', 'description']],
]);
Event::dispatch('lead.update.before', $id);
$lead = $this->leadRepository->update($data, $id, $attributes);
Event::dispatch('lead.update.after', $lead);
return response()->json([
'message' => trans('admin::app.leads.update-success'),
]);
}
/**
* Update the lead stage.
*/
public function updateStage(int $id)
{
$this->validate(request(), [
'lead_pipeline_stage_id' => 'required',
]);
$lead = $this->leadRepository->findOrFail($id);
$stage = $lead->pipeline->stages()
->where('id', request()->input('lead_pipeline_stage_id'))
->firstOrFail();
Event::dispatch('lead.update.before', $id);
$payload = request()->merge([
'entity_type' => 'leads',
'lead_pipeline_stage_id' => $stage->id,
])->only([
'closed_at',
'lost_reason',
'lead_pipeline_stage_id',
'entity_type',
]);
$lead = $this->leadRepository->update($payload, $id, ['lead_pipeline_stage_id']);
Event::dispatch('lead.update.after', $lead);
return response()->json([
'message' => trans('admin::app.leads.update-success'),
]);
}
/**
* Search person results.
*/
public function search(): AnonymousResourceCollection
{
if ($userIds = bouncer()->getAuthorizedUserIds()) {
$results = $this->leadRepository
->pushCriteria(app(RequestCriteria::class))
->findWhereIn('user_id', $userIds);
} else {
$results = $this->leadRepository
->pushCriteria(app(RequestCriteria::class))
->all();
}
return LeadResource::collection($results);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(int $id): JsonResponse
{
$this->leadRepository->findOrFail($id);
try {
Event::dispatch('lead.delete.before', $id);
$this->leadRepository->delete($id);
Event::dispatch('lead.delete.after', $id);
return response()->json([
'message' => trans('admin::app.leads.destroy-success'),
]);
} catch (\Exception $exception) {
return response()->json([
'message' => trans('admin::app.leads.destroy-failed'),
], 400);
}
}
/**
* Mass update the specified resources.
*/
public function massUpdate(MassUpdateRequest $massUpdateRequest): JsonResponse
{
$leads = $this->leadRepository->findWhereIn('id', $massUpdateRequest->input('indices'));
try {
foreach ($leads as $lead) {
Event::dispatch('lead.update.before', $lead->id);
$lead = $this->leadRepository->find($lead->id);
$lead?->update(['lead_pipeline_stage_id' => $massUpdateRequest->input('value')]);
Event::dispatch('lead.update.before', $lead->id);
}
return response()->json([
'message' => trans('admin::app.leads.update-success'),
]);
} catch (\Exception $th) {
return response()->json([
'message' => trans('admin::app.leads.update-failed'),
], 400);
}
}
/**
* Mass delete the specified resources.
*/
public function massDestroy(MassDestroyRequest $massDestroyRequest): JsonResponse
{
$leads = $this->leadRepository->findWhereIn('id', $massDestroyRequest->input('indices'));
try {
foreach ($leads as $lead) {
Event::dispatch('lead.delete.before', $lead->id);
$this->leadRepository->delete($lead->id);
Event::dispatch('lead.delete.after', $lead->id);
}
return response()->json([
'message' => trans('admin::app.leads.destroy-success'),
]);
} catch (\Exception $exception) {
return response()->json([
'message' => trans('admin::app.leads.destroy-failed'),
]);
}
}
/**
* Attach product to lead.
*/
public function addProduct(int $leadId): JsonResponse
{
$product = $this->productRepository->updateOrCreate(
[
'lead_id' => $leadId,
'product_id' => request()->input('product_id'),
],
array_merge(
request()->all(),
[
'lead_id' => $leadId,
'amount' => request()->input('price') * request()->input('quantity'),
],
)
);
return response()->json([
'data' => $product,
'message' => trans('admin::app.leads.update-success'),
]);
}
/**
* Remove product attached to lead.
*/
public function removeProduct(int $id): JsonResponse
{
try {
Event::dispatch('lead.product.delete.before', $id);
$this->productRepository->deleteWhere([
'lead_id' => $id,
'product_id' => request()->input('product_id'),
]);
Event::dispatch('lead.product.delete.after', $id);
return response()->json([
'message' => trans('admin::app.leads.destroy-success'),
]);
} catch (\Exception $exception) {
return response()->json([
'message' => trans('admin::app.leads.destroy-failed'),
]);
}
}
/**
* Kanban lookup.
*/
public function kanbanLookup()
{
$params = $this->validate(request(), [
'column' => ['required'],
'search' => ['required', 'min:2'],
]);
/**
* Finding the first column from the collection.
*/
$column = collect($this->getKanbanColumns())->where('index', $params['column'])->firstOrFail();
/**
* Fetching on the basis of column options.
*/
return app($column['filterable_options']['repository'])
->select([$column['filterable_options']['column']['label'].' as label', $column['filterable_options']['column']['value'].' as value'])
->where($column['filterable_options']['column']['label'], 'LIKE', '%'.$params['search'].'%')
->get()
->map
->only('label', 'value');
}
/**
* Get columns for the kanban view.
*/
private function getKanbanColumns(): array
{
return [
[
'index' => 'id',
'label' => trans('admin::app.leads.index.kanban.columns.id'),
'type' => 'integer',
'searchable' => false,
'search_field' => 'in',
'filterable' => true,
'filterable_type' => null,
'filterable_options' => [],
'allow_multiple_values' => true,
'sortable' => true,
'visibility' => true,
],
[
'index' => 'lead_value',
'label' => trans('admin::app.leads.index.kanban.columns.lead-value'),
'type' => 'string',
'searchable' => false,
'search_field' => 'in',
'filterable' => true,
'filterable_type' => null,
'filterable_options' => [],
'allow_multiple_values' => true,
'sortable' => true,
'visibility' => true,
],
[
'index' => 'user_id',
'label' => trans('admin::app.leads.index.kanban.columns.sales-person'),
'type' => 'string',
'searchable' => false,
'search_field' => 'in',
'filterable' => true,
'filterable_type' => 'searchable_dropdown',
'filterable_options' => [
'repository' => UserRepository::class,
'column' => [
'label' => 'name',
'value' => 'id',
],
],
'allow_multiple_values' => true,
'sortable' => true,
'visibility' => true,
],
[
'index' => 'person.id',
'label' => trans('admin::app.leads.index.kanban.columns.contact-person'),
'type' => 'string',
'searchable' => false,
'search_field' => 'in',
'filterable' => true,
'filterable_options' => [],
'allow_multiple_values' => true,
'sortable' => true,
'visibility' => true,
'filterable_type' => 'searchable_dropdown',
'filterable_options' => [
'repository' => PersonRepository::class,
'column' => [
'label' => 'name',
'value' => 'id',
],
],
],
[
'index' => 'lead_type_id',
'label' => trans('admin::app.leads.index.kanban.columns.lead-type'),
'type' => 'string',
'searchable' => false,
'search_field' => 'in',
'filterable' => true,
'filterable_type' => 'dropdown',
'filterable_options' => $this->typeRepository->all(['name as label', 'id as value'])->toArray(),
'allow_multiple_values' => true,
'sortable' => true,
'visibility' => true,
],
[
'index' => 'lead_source_id',
'label' => trans('admin::app.leads.index.kanban.columns.source'),
'type' => 'string',
'searchable' => false,
'search_field' => 'in',
'filterable' => true,
'filterable_type' => 'dropdown',
'filterable_options' => $this->sourceRepository->all(['name as label', 'id as value'])->toArray(),
'allow_multiple_values' => true,
'sortable' => true,
'visibility' => true,
],
[
'index' => 'tags.name',
'label' => trans('admin::app.leads.index.kanban.columns.tags'),
'type' => 'string',
'searchable' => false,
'search_field' => 'in',
'filterable' => true,
'filterable_options' => [],
'allow_multiple_values' => true,
'sortable' => true,
'visibility' => true,
'filterable_type' => 'searchable_dropdown',
'filterable_options' => [
'repository' => TagRepository::class,
'column' => [
'label' => 'name',
'value' => 'name',
],
],
],
];
}
/**
* Create lead with specified AI.
*/
public function createByAI()
{
$leadData = [];
$errorMessages = [];
foreach (request()->file('files') as $file) {
$lead = $this->processFile($file);
if (
isset($lead['status'])
&& $lead['status'] === 'error'
) {
$errorMessages[] = $lead['message'];
} else {
$leadData[] = $lead;
}
}
if (isset($errorMessages[0]['code'])) {
return response()->json(MagicAI::errorHandler($errorMessages[0]['message']));
}
if (
empty($leadData)
&& ! empty($errorMessages)
) {
return response()->json(MagicAI::errorHandler(implode(', ', $errorMessages)), 400);
}
if (empty($leadData)) {
return response()->json(MagicAI::errorHandler(trans('admin::app.leads.no-valid-files')), 400);
}
return response()->json([
'message' => trans('admin::app.leads.create-success'),
'leads' => $this->createLeads($leadData),
]);
}
/**
* Process file.
*
* @param mixed $file
*/
private function processFile($file)
{
$validator = Validator::make(
['file' => $file],
['file' => 'required|extensions:'.str_replace(' ', '', self::SUPPORTED_TYPES)]
);
if ($validator->fails()) {
return MagicAI::errorHandler($validator->errors()->first());
}
$base64Pdf = base64_encode(file_get_contents($file->getRealPath()));
$extractedData = MagicAIService::extractDataFromFile($base64Pdf);
$lead = MagicAI::mapAIDataToLead($extractedData);
return $lead;
}
/**
* Create multiple leads.
*/
private function createLeads($rawLeads): array
{
$leads = [];
foreach ($rawLeads as $rawLead) {
Event::dispatch('lead.create.before');
foreach ($rawLead['person']['emails'] as $email) {
$person = $this->personRepository
->whereJsonContains('emails', [['value' => $email['value']]])
->first();
if ($person) {
$rawLead['person']['id'] = $person->id;
break;
}
}
$pipeline = $this->pipelineRepository->getDefaultPipeline();
$stage = $pipeline->stages()->first();
$lead = $this->leadRepository->create(array_merge($rawLead, [
'lead_pipeline_id' => $pipeline->id,
'lead_pipeline_stage_id' => $stage->id,
]));
Event::dispatch('lead.create.after', $lead);
$leads[] = $lead;
}
return $leads;
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace Webkul\Admin\Http\Controllers\Lead;
use Illuminate\Support\Facades\Event;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Lead\Repositories\LeadRepository;
use Webkul\Quote\Repositories\QuoteRepository;
class QuoteController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(
protected LeadRepository $leadRepository,
protected QuoteRepository $quoteRepository
) {}
/**
* Store a newly created resource in storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function store($id)
{
Event::dispatch('leads.quote.create.before');
$lead = $this->leadRepository->find($id);
if (! $lead->quotes->contains(request('id'))) {
$lead->quotes()->attach(request('id'));
}
Event::dispatch('leads.quote.create.after', $lead);
return response()->json([
'message' => trans('admin::app.leads.quote-create-success'),
], 200);
}
/**
* Remove the specified resource from storage.
*
* @param int $leadId
* @param int $tagId
* @return \Illuminate\Http\Response
*/
public function delete($leadId)
{
Event::dispatch('leads.quote.delete.before', $leadId);
$lead = $this->leadRepository->find($leadId);
$lead->quotes()->detach(request('quote_id'));
Event::dispatch('leads.quote.delete.after', $lead);
return response()->json([
'message' => trans('admin::app.leads.view.quotes.destroy-success'),
], 200);
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace Webkul\Admin\Http\Controllers\Lead;
use Illuminate\Support\Facades\Event;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Lead\Repositories\LeadRepository;
class TagController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(protected LeadRepository $leadRepository) {}
/**
* Store a newly created resource in storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function attach($id)
{
Event::dispatch('leads.tag.create.before', $id);
$lead = $this->leadRepository->find($id);
if (! $lead->tags->contains(request()->input('tag_id'))) {
$lead->tags()->attach(request()->input('tag_id'));
}
Event::dispatch('leads.tag.create.after', $lead);
return response()->json([
'message' => trans('admin::app.leads.view.tags.create-success'),
]);
}
/**
* Remove the specified resource from storage.
*
* @param int $leadId
* @return \Illuminate\Http\Response
*/
public function detach($leadId)
{
Event::dispatch('leads.tag.delete.before', $leadId);
$lead = $this->leadRepository->find($leadId);
$lead->tags()->detach(request()->input('tag_id'));
Event::dispatch('leads.tag.delete.after', $lead);
return response()->json([
'message' => trans('admin::app.leads.view.tags.destroy-success'),
]);
}
}

View File

@@ -0,0 +1,336 @@
<?php
namespace Webkul\Admin\Http\Controllers\Mail;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;
use Webkul\Admin\DataGrids\Mail\EmailDataGrid;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Admin\Http\Requests\MassDestroyRequest;
use Webkul\Admin\Http\Requests\MassUpdateRequest;
use Webkul\Admin\Http\Resources\EmailResource;
use Webkul\Email\InboundEmailProcessor\Contracts\InboundEmailProcessor;
use Webkul\Email\Mails\Email;
use Webkul\Email\Repositories\AttachmentRepository;
use Webkul\Email\Repositories\EmailRepository;
use Webkul\Lead\Repositories\LeadRepository;
class EmailController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(
protected LeadRepository $leadRepository,
protected EmailRepository $emailRepository,
protected AttachmentRepository $attachmentRepository
) {}
/**
* Display a listing of the resource.
*/
public function index(): View|JsonResponse|RedirectResponse
{
if (! request('route')) {
return redirect()->route('admin.mail.index', ['route' => 'inbox']);
}
if (! bouncer()->hasPermission('mail.'.request('route'))) {
abort(401, 'This action is unauthorized');
}
switch (request('route')) {
case 'compose':
return view('admin::mail.compose');
default:
if (request()->ajax()) {
return datagrid(EmailDataGrid::class)->process();
}
return view('admin::mail.index');
}
}
/**
* Display a resource.
*
* @return \Illuminate\View\View
*/
public function view()
{
$email = $this->emailRepository
->with(['emails', 'attachments', 'emails.attachments', 'lead', 'lead.person', 'lead.tags', 'lead.source', 'lead.type', 'person'])
->findOrFail(request('id'));
if ($userIds = bouncer()->getAuthorizedUserIds()) {
$results = $this->leadRepository->findWhere([
['id', '=', $email->lead_id],
['user_id', 'IN', $userIds],
]);
} else {
$results = $this->leadRepository->findWhere([
['id', '=', $email->lead_id],
]);
}
if (empty($results->toArray())) {
unset($email->lead_id);
}
if (request('route') == 'draft') {
return response()->json([
'data' => new EmailResource($email),
]);
}
return view('admin::mail.view', compact('email'));
}
/**
* Store a newly created resource in storage.
*
* @return \Illuminate\Http\Response
*/
public function store()
{
$this->validate(request(), [
'reply_to' => 'required|array|min:1',
'reply_to.*' => 'email',
'reply' => 'required',
]);
Event::dispatch('email.create.before');
$email = $this->emailRepository->create(request()->all());
if (! request('is_draft')) {
try {
Mail::send(new Email($email));
$this->emailRepository->update([
'folders' => ['sent'],
], $email->id);
} catch (\Exception $e) {
}
}
Event::dispatch('email.create.after', $email);
if (request()->ajax()) {
return response()->json([
'data' => new EmailResource($email),
'message' => trans('admin::app.mail.create-success'),
]);
}
if (request('is_draft')) {
session()->flash('success', trans('admin::app.mail.saved-to-draft'));
return redirect()->route('admin.mail.index', ['route' => 'draft']);
}
session()->flash('success', trans('admin::app.mail.create-success'));
return redirect()->route('admin.mail.index', ['route' => 'sent']);
}
/**
* Update the specified resource in storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update($id)
{
Event::dispatch('email.update.before', $id);
$data = request()->all();
if (! is_null(request('is_draft'))) {
$data['folders'] = request('is_draft') ? ['draft'] : ['outbox'];
}
$email = $this->emailRepository->update($data, request('id') ?? $id);
Event::dispatch('email.update.after', $email);
if (! is_null(request('is_draft')) && ! request('is_draft')) {
try {
Mail::send(new Email($email));
$this->emailRepository->update([
'folders' => ['inbox', 'sent'],
], $email->id);
} catch (\Exception $e) {
}
}
if (! is_null(request('is_draft'))) {
if (request('is_draft')) {
session()->flash('success', trans('admin::app.mail.saved-to-draft'));
return redirect()->route('admin.mail.index', ['route' => 'draft']);
} else {
session()->flash('success', trans('admin::app.mail.create-success'));
return redirect()->route('admin.mail.index', ['route' => 'inbox']);
}
}
if (request()->ajax()) {
return response()->json([
'data' => new EmailResource($email->refresh()),
'message' => trans('admin::app.mail.update-success'),
]);
}
session()->flash('success', trans('admin::app.mail.update-success'));
return redirect()->back();
}
/**
* Run process inbound parse email.
*
* @return \Illuminate\Http\Response
*/
public function inboundParse(InboundEmailProcessor $inboundEmailProcessor)
{
$inboundEmailProcessor->processMessage(request('email'));
return response()->json([], 200);
}
/**
* Download file from storage
*
* @param int $id
* @return \Illuminate\View\View
*/
public function download($id)
{
$attachment = $this->attachmentRepository->findOrFail($id);
try {
return Storage::download($attachment->path);
} catch (\Exception $e) {
session()->flash('error', $e->getMessage());
return redirect()->back();
}
}
/**
* Mass Update the specified resources.
*/
public function massUpdate(MassUpdateRequest $massUpdateRequest): JsonResponse
{
$emails = $this->emailRepository->findWhereIn('id', $massUpdateRequest->input('indices'));
try {
foreach ($emails as $email) {
Event::dispatch('email.update.before', $email->id);
$this->emailRepository->update([
'folders' => request('folders'),
], $email->id);
Event::dispatch('email.update.after', $email->id);
}
return response()->json([
'message' => trans('admin::app.mail.mass-update-success'),
]);
} catch (Exception) {
return response()->json([
'message' => trans('admin::app.mail.mass-update-success'),
], 400);
}
}
/**
* Remove the specified resource from storage.
*/
public function destroy(int $id): JsonResponse|RedirectResponse
{
$email = $this->emailRepository->findOrFail($id);
try {
Event::dispatch('email.'.request('type').'.before', $id);
$parentId = $email->parent_id;
if (request('type') == 'trash') {
$this->emailRepository->update([
'folders' => ['trash'],
], $id);
} else {
$this->emailRepository->delete($id);
}
Event::dispatch('email.'.request('type').'.after', $id);
if (request()->ajax()) {
return response()->json([
'message' => trans('admin::app.mail.delete-success'),
], 200);
}
session()->flash('success', trans('admin::app.mail.delete-success'));
if ($parentId) {
return redirect()->back();
}
return redirect()->route('admin.mail.index', ['route' => 'inbox']);
} catch (\Exception $exception) {
if (request()->ajax()) {
return response()->json([
'message' => trans('admin::app.mail.delete-failed'),
], 400);
}
session()->flash('error', trans('admin::app.mail.delete-failed'));
return redirect()->back();
}
}
/**
* Mass Delete the specified resources.
*/
public function massDestroy(MassDestroyRequest $massDestroyRequest): JsonResponse
{
$mails = $this->emailRepository->findWhereIn('id', $massDestroyRequest->input('indices'));
try {
foreach ($mails as $email) {
Event::dispatch('email.'.$massDestroyRequest->input('type').'.before', $email->id);
if ($massDestroyRequest->input('type') == 'trash') {
$this->emailRepository->update(['folders' => ['trash']], $email->id);
} else {
$this->emailRepository->delete($email->id);
}
Event::dispatch('email.'.$massDestroyRequest->input('type').'.after', $email->id);
}
return response()->json([
'message' => trans('admin::app.mail.delete-success'),
]);
} catch (\Exception $e) {
return response()->json([
'message' => trans('admin::app.mail.delete-success'),
]);
}
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace Webkul\Admin\Http\Controllers\Mail;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Event;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Email\Repositories\EmailRepository;
class TagController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(protected EmailRepository $emailRepository) {}
/**
* Store a newly created resource in storage.
*/
public function attach(int $id): JsonResponse
{
Event::dispatch('mails.tag.create.before', $id);
$mail = $this->emailRepository->find($id);
if (! $mail->tags->contains(request()->input('tag_id'))) {
$mail->tags()->attach(request()->input('tag_id'));
}
Event::dispatch('mails.tag.create.after', $mail);
return response()->json([
'message' => trans('admin::app.mail.view.tags.create-success'),
]);
}
/**
* Remove the specified resource from storage.
*/
public function detach(int $mailId): JsonResponse
{
Event::dispatch('mails.tag.delete.before', $mailId);
$mail = $this->emailRepository->find($mailId);
$mail->tags()->detach(request()->input('tag_id'));
Event::dispatch('mails.tag.delete.after', $mail);
return response()->json([
'message' => trans('admin::app.mail.view.tags.destroy-success'),
]);
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace Webkul\Admin\Http\Controllers\Products;
use Webkul\Activity\Repositories\ActivityRepository;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Admin\Http\Resources\ActivityResource;
use Webkul\Email\Repositories\EmailRepository;
class ActivityController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(
protected ActivityRepository $activityRepository,
protected EmailRepository $emailRepository
) {}
/**
* Display a listing of the resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function index($id)
{
$activities = $this->activityRepository
->leftJoin('product_activities', 'activities.id', '=', 'product_activities.activity_id')
->where('product_activities.product_id', $id)
->get();
return ActivityResource::collection($this->concatEmail($activities));
}
/**
* Store a newly created resource in storage.
*/
public function concatEmail($activities)
{
return $activities->sortByDesc('id')->sortByDesc('created_at');
}
}

View File

@@ -0,0 +1,216 @@
<?php
namespace Webkul\Admin\Http\Controllers\Products;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Facades\Event;
use Illuminate\View\View;
use Prettus\Repository\Criteria\RequestCriteria;
use Webkul\Admin\DataGrids\Product\ProductDataGrid;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Admin\Http\Requests\AttributeForm;
use Webkul\Admin\Http\Requests\MassDestroyRequest;
use Webkul\Admin\Http\Resources\ProductResource;
use Webkul\Product\Repositories\ProductRepository;
class ProductController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(protected ProductRepository $productRepository)
{
request()->request->add(['entity_type' => 'products']);
}
/**
* Display a listing of the resource.
*/
public function index(): View|JsonResponse
{
if (request()->ajax()) {
return datagrid(ProductDataGrid::class)->process();
}
return view('admin::products.index');
}
/**
* Show the form for creating a new resource.
*/
public function create(): View
{
return view('admin::products.create');
}
/**
* Store a newly created resource in storage.
*
* @return \Illuminate\Http\Response
*/
public function store(AttributeForm $request)
{
Event::dispatch('product.create.before');
$product = $this->productRepository->create($request->all());
Event::dispatch('product.create.after', $product);
session()->flash('success', trans('admin::app.products.index.create-success'));
return redirect()->route('admin.products.index');
}
/**
* Show the form for viewing the specified resource.
*/
public function view(int $id): View
{
$product = $this->productRepository->findOrFail($id);
return view('admin::products.view', compact('product'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(int $id): View|JsonResponse
{
$product = $this->productRepository->findOrFail($id);
$inventories = $product->inventories()
->with('location')
->get()
->map(function ($inventory) {
return [
'id' => $inventory->id,
'name' => $inventory->location->name,
'warehouse_id' => $inventory->warehouse_id,
'warehouse_location_id' => $inventory->warehouse_location_id,
'in_stock' => $inventory->in_stock,
'allocated' => $inventory->allocated,
];
});
return view('admin::products.edit', compact('product', 'inventories'));
}
/**
* Update the specified resource in storage.
*/
public function update(AttributeForm $request, int $id)
{
Event::dispatch('product.update.before', $id);
$product = $this->productRepository->update($request->all(), $id);
Event::dispatch('product.update.after', $product);
if (request()->ajax()) {
return response()->json([
'message' => trans('admin::app.products.index.update-success'),
]);
}
session()->flash('success', trans('admin::app.products.index.update-success'));
return redirect()->route('admin.products.index');
}
/**
* Store a newly created resource in storage.
*/
public function storeInventories(int $id, ?int $warehouseId = null): JsonResponse
{
$this->validate(request(), [
'inventories' => 'array',
'inventories.*.warehouse_location_id' => 'required',
'inventories.*.warehouse_id' => 'required',
'inventories.*.in_stock' => 'required|integer|min:0',
'inventories.*.allocated' => 'required|integer|min:0',
]);
$product = $this->productRepository->findOrFail($id);
Event::dispatch('product.update.before', $id);
$this->productRepository->saveInventories(request()->all(), $id, $warehouseId);
Event::dispatch('product.update.after', $product);
return new JsonResponse([
'message' => trans('admin::app.products.index.update-success'),
], 200);
}
/**
* Search product results
*/
public function search(): JsonResource
{
$products = $this->productRepository
->pushCriteria(app(RequestCriteria::class))
->orderBy('created_at', 'desc')
->take(5)
->get();
return ProductResource::collection($products);
}
/**
* Returns product inventories grouped by warehouse.
*/
public function warehouses(int $id): JsonResponse
{
$warehouses = $this->productRepository->getInventoriesGroupedByWarehouse($id);
return response()->json(array_values($warehouses));
}
/**
* Remove the specified resource from storage.
*/
public function destroy(int $id): JsonResponse
{
$product = $this->productRepository->findOrFail($id);
try {
Event::dispatch('settings.products.delete.before', $id);
$product->delete($id);
Event::dispatch('settings.products.delete.after', $id);
return new JsonResponse([
'message' => trans('admin::app.products.index.delete-success'),
], 200);
} catch (\Exception $exception) {
return new JsonResponse([
'message' => trans('admin::app.products.index.delete-failed'),
], 400);
}
}
/**
* Mass Delete the specified resources.
*/
public function massDestroy(MassDestroyRequest $massDestroyRequest): JsonResponse
{
$indices = $massDestroyRequest->input('indices');
foreach ($indices as $index) {
Event::dispatch('product.delete.before', $index);
$this->productRepository->delete($index);
Event::dispatch('product.delete.after', $index);
}
return new JsonResponse([
'message' => trans('admin::app.products.index.delete-success'),
]);
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace Webkul\Admin\Http\Controllers\Products;
use Illuminate\Support\Facades\Event;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Product\Repositories\ProductRepository;
class TagController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(protected ProductRepository $productRepository) {}
/**
* Store a newly created resource in storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function attach($id)
{
Event::dispatch('products.tag.create.before', $id);
$product = $this->productRepository->findOrFail($id);
if (! $product->tags->contains(request()->input('tag_id'))) {
$product->tags()->attach(request()->input('tag_id'));
}
Event::dispatch('products.tag.create.after', $product);
return response()->json([
'message' => trans('admin::app.leads.view.tags.create-success'),
]);
}
/**
* Remove the specified resource from storage.
*
* @param int $productId
* @return \Illuminate\Http\Response
*/
public function detach($productId)
{
Event::dispatch('products.tag.delete.before', $productId);
$product = $this->productRepository->find($productId);
$product->tags()->detach(request()->input('tag_id'));
Event::dispatch('products.tag.delete.after', $product);
return response()->json([
'message' => trans('admin::app.leads.view.tags.destroy-success'),
]);
}
}

View File

@@ -0,0 +1,198 @@
<?php
namespace Webkul\Admin\Http\Controllers\Quote;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Event;
use Illuminate\View\View;
use Prettus\Repository\Criteria\RequestCriteria;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Webkul\Admin\DataGrids\Quote\QuoteDataGrid;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Admin\Http\Requests\AttributeForm;
use Webkul\Admin\Http\Requests\MassDestroyRequest;
use Webkul\Admin\Http\Resources\QuoteResource;
use Webkul\Core\Traits\PDFHandler;
use Webkul\Lead\Repositories\LeadRepository;
use Webkul\Quote\Repositories\QuoteRepository;
class QuoteController extends Controller
{
use PDFHandler;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(
protected QuoteRepository $quoteRepository,
protected LeadRepository $leadRepository
) {
request()->request->add(['entity_type' => 'quotes']);
}
/**
* Display a listing of the resource.
*/
public function index(): View|JsonResponse
{
if (request()->ajax()) {
return datagrid(QuoteDataGrid::class)->process();
}
return view('admin::quotes.index');
}
/**
* Show the form for creating a new resource.
*/
public function create(): View
{
$lead = $this->leadRepository->find(request('id'));
return view('admin::quotes.create', compact('lead'));
}
/**
* Store a newly created resource in storage.
*/
public function store(AttributeForm $request): RedirectResponse
{
Event::dispatch('quote.create.before');
$quote = $this->quoteRepository->create($request->all());
$leadId = request('lead_id');
if ($leadId) {
$lead = $this->leadRepository->find($leadId);
$lead->quotes()->attach($quote->id);
}
Event::dispatch('quote.create.after', $quote);
session()->flash('success', trans('admin::app.quotes.index.create-success'));
return request()->query('from') === 'lead' && $leadId
? redirect()->route('admin.leads.view', ['id' => $leadId, 'from' => 'quotes'])
: redirect()->route('admin.quotes.index');
}
/**
* Show the form for editing the specified resource.
*/
public function edit(int $id): View
{
$quote = $this->quoteRepository->findOrFail($id);
return view('admin::quotes.edit', compact('quote'));
}
/**
* Update the specified resource in storage.
*/
public function update(AttributeForm $request, int $id): RedirectResponse
{
Event::dispatch('quote.update.before', $id);
$quote = $this->quoteRepository->update($request->all(), $id);
$quote->leads()->detach();
$leadId = request('lead_id');
if ($leadId) {
$lead = $this->leadRepository->find($leadId);
$lead->quotes()->attach($quote->id);
}
Event::dispatch('quote.update.after', $quote);
session()->flash('success', trans('admin::app.quotes.index.update-success'));
return request()->query('from') === 'lead' && $leadId
? redirect()->route('admin.leads.view', ['id' => $leadId, 'from' => 'quotes'])
: redirect()->route('admin.quotes.index');
}
/**
* Search the quotes.
*/
public function search(): AnonymousResourceCollection
{
$quotes = $this->quoteRepository
->pushCriteria(app(RequestCriteria::class))
->all();
return QuoteResource::collection($quotes);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(int $id): JsonResponse
{
$this->quoteRepository->findOrFail($id);
try {
Event::dispatch('quote.delete.before', $id);
$this->quoteRepository->delete($id);
Event::dispatch('quote.delete.after', $id);
return response()->json([
'message' => trans('admin::app.quotes.index.delete-success'),
], 200);
} catch (\Exception $exception) {
return response()->json([
'message' => trans('admin::app.quotes.index.delete-failed'),
], 400);
}
}
/**
* Mass Delete the specified resources.
*/
public function massDestroy(MassDestroyRequest $massDestroyRequest): JsonResponse
{
$quotes = $this->quoteRepository->findWhereIn('id', $massDestroyRequest->input('indices'));
try {
foreach ($quotes as $quotes) {
Event::dispatch('quote.delete.before', $quotes->id);
$this->quoteRepository->delete($quotes->id);
Event::dispatch('quote.delete.after', $quotes->id);
}
return response()->json([
'message' => trans('admin::app.quotes.index.delete-success'),
]);
} catch (\Exception $exception) {
return response()->json([
'message' => trans('admin::app.quotes.index.delete-failed'),
], 400);
}
}
/**
* Print and download the for the specified resource.
*/
public function print($id): Response|StreamedResponse
{
$quote = $this->quoteRepository->findOrFail($id);
return $this->downloadPDF(
view('admin::quotes.pdf', compact('quote'))->render(),
'Quote_'.$quote->subject.'_'.$quote->created_at->format('d-m-Y')
);
}
}

View File

@@ -0,0 +1,236 @@
<?php
namespace Webkul\Admin\Http\Controllers\Settings;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;
use Webkul\Admin\DataGrids\Settings\AttributeDataGrid;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Admin\Http\Requests\MassDestroyRequest;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\Attribute\Repositories\AttributeValueRepository;
use Webkul\Core\Contracts\Validations\Code;
class AttributeController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(
protected AttributeRepository $attributeRepository,
protected AttributeValueRepository $attributeValueRepository
) {}
/**
* Display a listing of the resource.
*/
public function index(): View|JsonResponse
{
if (request()->ajax()) {
return datagrid(AttributeDataGrid::class)->process();
}
return view('admin::settings.attributes.index');
}
/**
* Show the form for creating a new resource.
*/
public function create(): View
{
return view('admin::settings.attributes.create');
}
/**
* Store a newly created resource in storage.
*/
public function store(): RedirectResponse
{
$this->validate(request(), [
'code' => ['required', 'unique:attributes,code,NULL,NULL,entity_type,'.request('entity_type'), new Code],
'name' => 'required',
'type' => 'required',
]);
Event::dispatch('settings.attribute.create.before');
request()->request->add(['quick_add' => 1]);
$attribute = $this->attributeRepository->create(request()->all());
Event::dispatch('settings.attribute.create.after', $attribute);
session()->flash('success', trans('admin::app.settings.attributes.index.create-success'));
return redirect()->route('admin.settings.attributes.index');
}
/**
* Show the form for editing the specified resource.
*/
public function edit(int $id): View
{
$attribute = $this->attributeRepository->findOrFail($id);
return view('admin::settings.attributes.edit', compact('attribute'));
}
/**
* Update the specified resource in storage.
*/
public function update($id): RedirectResponse
{
$this->validate(request(), [
'code' => ['required', 'unique:attributes,code,NULL,NULL,entity_type,'.$id, new Code],
'name' => 'required',
'type' => 'required',
]);
Event::dispatch('settings.attribute.update.before', $id);
$attribute = $this->attributeRepository->update(request()->all(), $id);
Event::dispatch('settings.attribute.update.after', $attribute);
session()->flash('success', trans('admin::app.settings.attributes.index.update-success'));
return redirect()->route('admin.settings.attributes.index');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(int $id): JsonResponse
{
$attribute = $this->attributeRepository->findOrFail($id);
if (! $attribute->is_user_defined) {
return response()->json([
'message' => trans('admin::app.settings.attributes.index.user-define-error'),
], 400);
}
try {
Event::dispatch('settings.attribute.delete.before', $id);
$this->attributeRepository->delete($id);
Event::dispatch('settings.attribute.delete.after', $id);
return response()->json([
'status' => true,
'message' => trans('admin::app.settings.attributes.index.delete-success'),
], 200);
} catch (\Exception $exception) {
return response()->json([
'message' => trans('admin::app.settings.attributes.index.delete-failed'),
], 400);
}
}
/**
* Check unique validation.
*
* @return void
*/
public function checkUniqueValidation()
{
$attribute = $this->attributeRepository->findOneWhere([
'code' => request('attribute_code'),
]);
return response()->json([
'validated' => $this->attributeValueRepository->isValueUnique(
request('entity_id'),
request('entity_type'),
$attribute,
request('attribute_value'),
),
]);
}
/**
* Search attribute lookup results
*/
public function lookup($lookup): JsonResponse
{
$results = $this->attributeRepository->getLookUpOptions($lookup, request()->input('query'));
return response()->json($results);
}
/**
* Search attribute lookup results
*/
public function lookupEntity(string $lookup): JsonResponse
{
$result = $this->attributeRepository->getLookUpEntity($lookup, request()->input('query'));
return response()->json($result);
}
/**
* Mass Delete the specified resources.
*/
public function massDestroy(MassDestroyRequest $massDestroyRequest): JsonResponse
{
$count = 0;
$attributes = $this->attributeRepository->findWhereIn('id', $massDestroyRequest->input('indices'));
foreach ($attributes as $attribute) {
$attribute = $this->attributeRepository->find($attribute->id);
if (! $attribute->is_user_defined) {
continue;
}
Event::dispatch('settings.attribute.delete.before', $attribute->id);
$this->attributeRepository->delete($attribute->id);
Event::dispatch('settings.attribute.delete.after', $attribute->id);
$count++;
}
if (! $count) {
return response()->json([
'message' => trans('admin::app.settings.attributes.index.mass-delete-failed'),
], 400);
}
return response()->json([
'message' => trans('admin::app.settings.attributes.index.delete-success'),
]);
}
/**
* Get attribute options associated with attribute.
*
* @return \Illuminate\View\View
*/
public function getAttributeOptions(int $id)
{
$attribute = $this->attributeRepository->findOrFail($id);
return $attribute->options()->orderBy('sort_order')->get();
}
/**
* Download image or file
*/
public function download()
{
if (! request('path')) {
return false;
}
return Storage::download(request('path'));
}
}

View File

@@ -0,0 +1,491 @@
<?php
namespace Webkul\Admin\Http\Controllers\Settings\DataTransfer;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;
use Webkul\Admin\DataGrids\Settings\DataTransfer\ImportDataGrid;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\DataTransfer\Helpers\Import;
use Webkul\DataTransfer\Repositories\ImportRepository;
class ImportController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(
protected ImportRepository $importRepository,
protected Import $importHelper
) {}
/**
* Display a listing of the resource.
*/
public function index(): View|JsonResponse
{
if (request()->ajax()) {
return datagrid(ImportDataGrid::class)->process();
}
return view('admin::settings.data-transfer.imports.index');
}
/**
* Show the form for creating a new resource.
*/
public function create(): View
{
return view('admin::settings.data-transfer.imports.create');
}
/**
* Store a newly created resource in storage.
*/
public function store(): RedirectResponse
{
$importers = array_keys(config('importers'));
$this->validate(request(), [
'type' => 'required|in:'.implode(',', $importers),
'action' => 'required:in:append,delete',
'validation_strategy' => 'required:in:stop-on-errors,skip-errors',
'allowed_errors' => 'required|integer|min:0',
'field_separator' => 'required',
'file' => 'required|mimes:csv,xls,xlsx,txt',
]);
Event::dispatch('data_transfer.imports.create.before');
$data = request()->only([
'type',
'action',
'process_in_queue',
'validation_strategy',
'validation_strategy',
'allowed_errors',
'field_separator',
]);
if (! isset($data['process_in_queue'])) {
$data['process_in_queue'] = false;
} else {
$data['process_in_queue'] = true;
}
$import = $this->importRepository->create(
array_merge(
[
'file_path' => request()->file('file')->storeAs(
'imports',
time().'-'.request()->file('file')->getClientOriginalName(),
'public'
),
],
$data
)
);
Event::dispatch('data_transfer.imports.create.after', $import);
session()->flash('success', trans('admin::app.settings.data-transfer.imports.create-success'));
return redirect()->route('admin.settings.data_transfer.imports.import', $import->id);
}
/**
* Show the form for editing a new resource.
*/
public function edit(int $id): View
{
$import = $this->importRepository->findOrFail($id);
return view('admin::settings.data-transfer.imports.edit', compact('import'));
}
/**
* Update a resource in storage.
*/
public function update(int $id): RedirectResponse
{
$importers = array_keys(config('importers'));
$import = $this->importRepository->findOrFail($id);
$this->validate(request(), [
'type' => 'required|in:'.implode(',', $importers),
'action' => 'required:in:append,delete',
'validation_strategy' => 'required:in:stop-on-errors,skip-errors',
'allowed_errors' => 'required|integer|min:0',
'field_separator' => 'required',
'file' => 'mimes:csv,xls,xlsx,txt',
]);
Event::dispatch('data_transfer.imports.update.before');
$data = array_merge(
request()->only([
'type',
'action',
'process_in_queue',
'validation_strategy',
'validation_strategy',
'allowed_errors',
'field_separator',
]),
[
'state' => 'pending',
'processed_rows_count' => 0,
'invalid_rows_count' => 0,
'errors_count' => 0,
'errors' => null,
'error_file_path' => null,
'started_at' => null,
'completed_at' => null,
'summary' => null,
]
);
Storage::disk('public')->delete($import->error_file_path ?? '');
if (request()->file('file') && request()->file('file')->isValid()) {
Storage::disk('public')->delete($import->file_path);
$data['file_path'] = request()->file('file')->storeAs(
'imports',
time().'-'.request()->file('file')->getClientOriginalName(),
'public'
);
}
if (! isset($data['process_in_queue'])) {
$data['process_in_queue'] = false;
}
$import = $this->importRepository->update($data, $import->id);
Event::dispatch('data_transfer.imports.update.after', $import);
session()->flash('success', trans('admin::app.settings.data-transfer.imports.update-success'));
return redirect()->route('admin.settings.data_transfer.imports.import', $import->id);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(int $id): JsonResponse
{
$import = $this->importRepository->findOrFail($id);
try {
Storage::disk('public')->delete($import->file_path);
Storage::disk('public')->delete($import->error_file_path ?? '');
$this->importRepository->delete($id);
return response()->json([
'message' => trans('admin::app.settings.data-transfer.imports.delete-success'),
]);
} catch (\Exception $e) {
}
return response()->json([
'message' => trans('admin::app.settings.data-transfer.imports.delete-failed'),
], 500);
}
/**
* Show the form for creating a new resource.
*/
public function import(int $id): View
{
$import = $this->importRepository->findOrFail($id);
$isValid = $this->importHelper
->setImport($import)
->isValid();
if ($import->state == Import::STATE_LINKING) {
if ($this->importHelper->isIndexingRequired()) {
$state = Import::STATE_INDEXING;
} else {
$state = Import::STATE_COMPLETED;
}
} elseif ($import->state == Import::STATE_INDEXING) {
$state = Import::STATE_COMPLETED;
} else {
$state = Import::STATE_COMPLETED;
}
$stats = $this->importHelper->stats($state);
$import->unsetRelations();
return view('admin::settings.data-transfer.imports.import', compact('import', 'isValid', 'stats'));
}
/**
* Store a newly created resource in storage.
*/
public function validateImport(int $id): JsonResponse
{
$import = $this->importRepository->findOrFail($id);
$isValid = $this->importHelper
->setImport($import)
->validate();
return new JsonResponse([
'is_valid' => $isValid,
'import' => $this->importHelper->getImport()->unsetRelations(),
]);
}
/**
* Store a newly created resource in storage.
*/
public function start(int $id): JsonResponse
{
$import = $this->importRepository->findOrFail($id);
if (! $import->processed_rows_count) {
return new JsonResponse([
'message' => trans('admin::app.settings.data-transfer.imports.nothing-to-import'),
], 400);
}
$this->importHelper->setImport($import);
if (! $this->importHelper->isValid()) {
return new JsonResponse([
'message' => trans('admin::app.settings.data-transfer.imports.not-valid'),
], 400);
}
if (
$import->process_in_queue
&& config('queue.default') == 'sync'
) {
return new JsonResponse([
'message' => trans('admin::app.settings.data-transfer.imports.setup-queue-error'),
], 400);
}
/**
* Set the import state to processing
*/
if ($import->state == Import::STATE_VALIDATED) {
$this->importHelper->started();
}
/**
* Get the first pending batch to import
*/
$importBatch = $import->batches->where('state', Import::STATE_PENDING)->first();
if ($importBatch) {
/**
* Start the import process
*/
try {
if ($import->process_in_queue) {
$this->importHelper->start();
} else {
$this->importHelper->start($importBatch);
}
} catch (\Exception $e) {
return new JsonResponse([
'message' => $e->getMessage(),
], 400);
}
} else {
if ($this->importHelper->isLinkingRequired()) {
$this->importHelper->linking();
} elseif ($this->importHelper->isIndexingRequired()) {
$this->importHelper->indexing();
} else {
$this->importHelper->completed();
}
}
return new JsonResponse([
'stats' => $this->importHelper->stats(Import::STATE_PROCESSED),
'import' => $this->importHelper->getImport()->unsetRelations(),
]);
}
/**
* Store a newly created resource in storage.
*/
public function link(int $id): JsonResponse
{
$import = $this->importRepository->findOrFail($id);
if (! $import->processed_rows_count) {
return new JsonResponse([
'message' => trans('admin::app.settings.data-transfer.imports.nothing-to-import'),
], 400);
}
$this->importHelper->setImport($import);
if (! $this->importHelper->isValid()) {
return new JsonResponse([
'message' => trans('admin::app.settings.data-transfer.imports.not-valid'),
], 400);
}
/**
* Set the import state to linking
*/
if ($import->state == Import::STATE_PROCESSED) {
$this->importHelper->linking();
}
/**
* Get the first processing batch to link
*/
$importBatch = $import->batches->where('state', Import::STATE_PROCESSED)->first();
/**
* Set the import state to linking/completed
*/
if ($importBatch) {
/**
* Start the resource linking process
*/
try {
$this->importHelper->link($importBatch);
} catch (\Exception $e) {
return new JsonResponse([
'message' => $e->getMessage(),
], 400);
}
} else {
if ($this->importHelper->isIndexingRequired()) {
$this->importHelper->indexing();
} else {
$this->importHelper->completed();
}
}
return new JsonResponse([
'stats' => $this->importHelper->stats(Import::STATE_LINKED),
'import' => $this->importHelper->getImport()->unsetRelations(),
]);
}
/**
* Store a newly created resource in storage.
*/
public function indexData(int $id): JsonResponse
{
$import = $this->importRepository->findOrFail($id);
if (! $import->processed_rows_count) {
return new JsonResponse([
'message' => trans('admin::app.settings.data-transfer.imports.nothing-to-import'),
], 400);
}
$this->importHelper->setImport($import);
if (! $this->importHelper->isValid()) {
return new JsonResponse([
'message' => trans('admin::app.settings.data-transfer.imports.not-valid'),
], 400);
}
/**
* Set the import state to linking
*/
if ($import->state == Import::STATE_LINKED) {
$this->importHelper->indexing();
}
/**
* Get the first processing batch to link
*/
$importBatch = $import->batches->where('state', Import::STATE_LINKED)->first();
/**
* Set the import state to linking/completed
*/
if ($importBatch) {
/**
* Start the resource linking process
*/
try {
$this->importHelper->index($importBatch);
} catch (\Exception $e) {
return new JsonResponse([
'message' => $e->getMessage(),
], 400);
}
} else {
/**
* Set the import state to completed
*/
$this->importHelper->completed();
}
return new JsonResponse([
'stats' => $this->importHelper->stats(Import::STATE_INDEXED),
'import' => $this->importHelper->getImport()->unsetRelations(),
]);
}
/**
* Returns import stats
*/
public function stats(int $id, string $state = Import::STATE_PROCESSED): JsonResponse
{
$import = $this->importRepository->findOrFail($id);
$stats = $this->importHelper
->setImport($import)
->stats($state);
return new JsonResponse([
'stats' => $stats,
'import' => $this->importHelper->getImport()->unsetRelations(),
]);
}
/**
* Download import error report
*/
public function downloadSample(string $type)
{
$importer = config('importers.'.$type);
return Storage::download($importer['sample_path']);
}
/**
* Download import error report
*/
public function download(int $id)
{
$import = $this->importRepository->findOrFail($id);
return Storage::disk('public')->download($import->file_path);
}
/**
* Download import error report
*/
public function downloadErrorReport(int $id)
{
$import = $this->importRepository->findOrFail($id);
return Storage::disk('public')->download($import->file_path);
}
}

View File

@@ -0,0 +1,133 @@
<?php
namespace Webkul\Admin\Http\Controllers\Settings;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Event;
use Illuminate\View\View;
use Webkul\Admin\DataGrids\Settings\EmailTemplateDataGrid;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Automation\Helpers\Entity;
use Webkul\EmailTemplate\Repositories\EmailTemplateRepository;
class EmailTemplateController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(
protected EmailTemplateRepository $emailTemplateRepository,
protected Entity $workflowEntityHelper
) {}
/**
* Display a listing of the email template.
*/
public function index(): View|JsonResponse
{
if (request()->ajax()) {
return datagrid(EmailTemplateDataGrid::class)->process();
}
return view('admin::settings.email-templates.index');
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\View\View
*/
public function create()
{
$placeholders = $this->workflowEntityHelper->getEmailTemplatePlaceholders();
return view('admin::settings.email-templates.create', compact('placeholders'));
}
/**
* Store a newly created email templates in storage.
*/
public function store(): RedirectResponse
{
$this->validate(request(), [
'name' => 'required|unique:email_templates,name',
'subject' => 'required',
'content' => 'required',
]);
Event::dispatch('settings.email_templates.create.before');
$emailTemplate = $this->emailTemplateRepository->create(request()->all());
Event::dispatch('settings.email_templates.create.after', $emailTemplate);
session()->flash('success', trans('admin::app.settings.email-template.index.create-success'));
return redirect()->route('admin.settings.email_templates.index');
}
/**
* Show the form for editing the specified email template.
*/
public function edit(int $id): View
{
$emailTemplate = $this->emailTemplateRepository->findOrFail($id);
$placeholders = $this->workflowEntityHelper->getEmailTemplatePlaceholders();
return view('admin::settings.email-templates.edit', compact('emailTemplate', 'placeholders'));
}
/**
* Update the specified email template in storage.
*/
public function update(int $id): RedirectResponse
{
$this->validate(request(), [
'name' => 'required|unique:email_templates,name,'.$id,
'subject' => 'required',
'content' => 'required',
]);
Event::dispatch('settings.email_templates.update.before', $id);
$emailTemplate = $this->emailTemplateRepository->update(request()->all(), $id);
Event::dispatch('settings.email_templates.update.after', $emailTemplate);
session()->flash('success', trans('admin::app.settings.email-template.index.update-success'));
return redirect()->route('admin.settings.email_templates.index');
}
/**
* Remove the specified email template from storage.
*/
public function destroy(int $id): JsonResponse
{
$emailTemplate = $this->emailTemplateRepository->findOrFail($id);
try {
Event::dispatch('settings.email_templates.delete.before', $id);
$emailTemplate->delete($id);
Event::dispatch('settings.email_templates.delete.after', $id);
return response()->json([
'message' => trans('admin::app.settings.email-template.index.delete-success'),
], 200);
} catch (\Exception $exception) {
return response()->json([
'message' => trans('admin::app.settings.email-template.index.delete-failed'),
], 400);
}
return response()->json([
'message' => trans('admin::app.settings.email-template.index.delete-failed'),
], 400);
}
}

View File

@@ -0,0 +1,127 @@
<?php
namespace Webkul\Admin\Http\Controllers\Settings;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Facades\Event;
use Illuminate\View\View;
use Webkul\Admin\DataGrids\Settings\GroupDataGrid;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\User\Repositories\GroupRepository;
class GroupController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(protected GroupRepository $groupRepository) {}
/**
* Display a listing of the resource.
*/
public function index(): View|JsonResponse
{
if (request()->ajax()) {
return datagrid(GroupDataGrid::class)->process();
}
return view('admin::settings.groups.index');
}
/**
* Store a newly created resource in storage.
*/
public function store(): JsonResponse
{
$this->validate(request(), [
'name' => 'required|unique:groups,name|max:50',
'description' => 'required|max:250',
]);
Event::dispatch('settings.group.create.before');
$group = $this->groupRepository->create(request()->only([
'name',
'description',
]));
Event::dispatch('settings.group.create.after', $group);
return new JsonResponse([
'data' => $group,
'message' => trans('admin::app.settings.groups.index.create-success'),
]);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(int $id): JsonResource
{
$group = $this->groupRepository->findOrFail($id);
return new JsonResource([
'data' => $group,
]);
}
/**
* Update the specified resource in storage.
*/
public function update(int $id): JsonResponse
{
$this->validate(request(), [
'name' => 'required|max:50|unique:groups,name,'.$id,
'description' => 'required|max:250',
]);
Event::dispatch('settings.group.update.before', $id);
$group = $this->groupRepository->update(request()->only([
'name',
'description',
]), $id);
Event::dispatch('settings.group.update.after', $group);
return new JsonResponse([
'data' => $group,
'message' => trans('admin::app.settings.groups.index.update-success'),
]);
}
/**
* Remove the specified resource from storage.
*
* @return \Illuminate\Http\Response
*/
public function destroy(int $id): JsonResponse
{
$group = $this->groupRepository->findOrFail($id);
if ($group->users()->exists()) {
return response()->json([
'message' => trans('admin::app.settings.groups.index.delete-failed-associated-users'),
], 400);
}
try {
Event::dispatch('settings.group.delete.before', $id);
$group->delete($id);
Event::dispatch('settings.group.delete.after', $id);
return new JsonResponse([
'message' => trans('admin::app.settings.groups.index.destroy-success'),
], 200);
} catch (\Exception $exception) {
return new JsonResponse([
'message' => trans('admin::app.settings.groups.index.delete-failed'),
], 400);
}
}
}

View File

@@ -0,0 +1,79 @@
<?php
namespace Webkul\Admin\Http\Controllers\Settings;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Event;
use Prettus\Repository\Criteria\RequestCriteria;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Admin\Http\Requests\AttributeForm;
use Webkul\Warehouse\Repositories\LocationRepository;
class LocationController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(protected LocationRepository $locationRepository) {}
/**
* Search location results
*
* @return \Illuminate\Http\Response
*/
public function search()
{
$results = $this->locationRepository
->pushCriteria(app(RequestCriteria::class))
->all();
return response()->json([
'data' => $results,
]);
}
/**
* Store a newly created resource in storage.
*/
public function store(AttributeForm $request): JsonResponse
{
Event::dispatch('settings.location.create.before');
$location = $this->locationRepository->create(request()->all());
Event::dispatch('settings.location.create.after', $location);
return new JsonResponse([
'data' => $location,
'message' => trans('admin::app.settings.warehouses.view.locations.create-success'),
]);
}
/**
* Remove the specified resource from storage.
*
* @return \Illuminate\Http\Response
*/
public function destroy(int $id): JsonResponse
{
$this->locationRepository->findOrFail($id);
try {
Event::dispatch('settings.location.delete.before', $id);
$this->locationRepository->delete($id);
Event::dispatch('settings.location.delete.after', $id);
return new JsonResponse([
'message' => trans('admin::app.settings.warehouses.view.locations.delete-success'),
], 200);
} catch (\Exception $exception) {
return new JsonResponse([
'message' => trans('admin::app.settings.warehouses.view.locations.delete-failed'),
], 400);
}
}
}

View File

@@ -0,0 +1,157 @@
<?php
namespace Webkul\Admin\Http\Controllers\Settings\Marketing;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Event;
use Illuminate\View\View;
use Webkul\Admin\DataGrids\Settings\Marketing\CampaignDatagrid;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Admin\Http\Requests\MassDestroyRequest;
use Webkul\EmailTemplate\Repositories\EmailTemplateRepository;
use Webkul\Marketing\Repositories\CampaignRepository;
use Webkul\Marketing\Repositories\EventRepository;
class CampaignsController extends Controller
{
/**
* Create new a controller instance.
*/
public function __construct(
protected CampaignRepository $campaignRepository,
protected EventRepository $eventRepository,
protected EmailTemplateRepository $emailTemplateRepository,
) {}
/**
* Display a listing of the marketing campaigns.
*/
public function index(): View|JsonResponse
{
if (request()->isXmlHttpRequest()) {
return datagrid(CampaignDatagrid::class)->process();
}
return view('admin::settings.marketing.campaigns.index');
}
/**
* Get marketing events.
*/
public function getEvents(): JsonResponse
{
$events = $this->eventRepository->get(['id', 'name']);
return response()->json([
'data' => $events,
]);
}
/**
* Get Email Templates.
*/
public function getEmailTemplates(): JsonResponse
{
$emailTemplates = $this->emailTemplateRepository->get(['id', 'name']);
return response()->json([
'data' => $emailTemplates,
]);
}
/**
* Store a newly created marketing campaign in storage.
*/
public function store(): JsonResponse
{
$validatedData = $this->validate(request(), [
'name' => 'required|string|max:255',
'subject' => 'required|string|max:255',
'marketing_template_id' => 'required|exists:email_templates,id',
'marketing_event_id' => 'required|exists:marketing_events,id',
'status' => 'sometimes|required|in:0,1',
]);
Event::dispatch('settings.marketing.campaigns.create.before');
$marketingCampaign = $this->campaignRepository->create($validatedData);
Event::dispatch('settings.marketing.campaigns.create.after', $marketingCampaign);
return response()->json([
'message' => trans('admin::app.settings.marketing.campaigns.index.create-success'),
]);
}
/**
* Show the specified Resource.
*/
public function show(int $id): JsonResponse
{
$campaign = $this->campaignRepository->findOrFail($id);
return response()->json([
'data' => $campaign,
]);
}
/**
* Update the specified marketing campaign in storage.
*/
public function update(int $id): JsonResponse
{
$validatedData = $this->validate(request(), [
'name' => 'required|string|max:255',
'subject' => 'required|string|max:255',
'marketing_template_id' => 'required|exists:email_templates,id',
'marketing_event_id' => 'required|exists:marketing_events,id',
'status' => 'sometimes|required|in:0,1',
]);
Event::dispatch('settings.marketing.campaigns.update.before', $id);
$marketingCampaign = $this->campaignRepository->update($validatedData, $id);
Event::dispatch('settings.marketing.campaigns.update.after', $marketingCampaign);
return response()->json([
'message' => trans('admin::app.settings.marketing.campaigns.index.update-success'),
]);
}
/**
* Remove the specified marketing campaign from storage.
*/
public function destroy(int $id): JsonResponse
{
Event::dispatch('settings.marketing.campaigns.delete.before', $id);
$this->campaignRepository->delete($id);
Event::dispatch('settings.marketing.campaigns.delete.after', $id);
return response()->json([
'message' => trans('admin::app.settings.marketing.campaigns.index.delete-success'),
]);
}
/**
* Remove the specified marketing campaigns from storage.
*/
public function massDestroy(MassDestroyRequest $massDestroyRequest): JsonResponse
{
$campaigns = $this->campaignRepository->findWhereIn('id', $massDestroyRequest->input('indices'));
foreach ($campaigns as $campaign) {
Event::dispatch('settings.marketing.campaigns.delete.before', $campaign);
$this->campaignRepository->delete($campaign->id);
Event::dispatch('settings.marketing.campaigns.delete.after', $campaign);
}
return response()->json([
'message' => trans('admin::app.settings.marketing.campaigns.index.mass-delete-success'),
]);
}
}

View File

@@ -0,0 +1,169 @@
<?php
namespace Webkul\Admin\Http\Controllers\Settings\Marketing;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Event;
use Illuminate\View\View;
use Webkul\Admin\DataGrids\Settings\Marketing\EventDataGrid;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Admin\Http\Requests\MassDestroyRequest;
use Webkul\Marketing\Repositories\EventRepository;
class EventController extends Controller
{
/**
* Create a new controller instance.
*/
public function __construct(protected EventRepository $eventRepository) {}
/**
* Display a listing of the marketing events.
*/
public function index(): View|JsonResponse
{
if (request()->ajax()) {
return datagrid(EventDataGrid::class)->process();
}
return view('admin::settings.marketing.events.index');
}
/**
* Store a newly created marketing event in storage.
*/
public function store(): JsonResponse
{
$validatedData = $this->validate(request(), [
'name' => 'required|max:60',
'description' => 'required',
'date' => 'required|date|after_or_equal:today',
]);
Event::dispatch('settings.marketing.events.create.before');
$marketingEvent = $this->eventRepository->create($validatedData);
Event::dispatch('settings.marketing.events.create.after', $marketingEvent);
return response()->json([
'message' => trans('admin::app.settings.marketing.events.index.create-success'),
'data' => $marketingEvent,
]);
}
/**
* Update the specified marketing event in storage.
*/
public function update(int $id): JsonResponse
{
$validatedData = $this->validate(request(), [
'name' => 'required|max:60',
'description' => 'required',
'date' => 'required|date|after_or_equal:today',
]);
Event::dispatch('settings.marketing.events.update.before', $id);
$marketingEvent = $this->eventRepository->update($validatedData, $id);
Event::dispatch('settings.marketing.events.update.after', $marketingEvent);
return response()->json([
'message' => trans('admin::app.settings.marketing.events.index.update-success'),
'data' => $marketingEvent,
]);
}
/**
* Remove the specified marketing event from storage.
*/
public function destroy(int $id): JsonResponse
{
$event = $this->eventRepository->findOrFail($id);
if ($event->campaigns->isNotEmpty()) {
return response()->json([
'message' => trans('admin::app.settings.marketing.events.index.delete-failed-associated-campaigns'),
], 422);
}
Event::dispatch('settings.marketing.events.delete.before', $event);
$this->eventRepository->delete($event->id);
Event::dispatch('settings.marketing.events.delete.after', $event);
return response()->json([
'message' => trans('admin::app.settings.marketing.events.index.delete-success'),
]);
}
/**
* Remove the specified marketing events from storage.
*/
public function massDestroy(MassDestroyRequest $request): JsonResponse
{
try {
$events = $this->eventRepository->findWhereIn('id', $request->input('indices', []));
$deletedCount = 0;
$blockedCount = 0;
foreach ($events as $event) {
if (
$event->campaigns
&& $event->campaigns->isNotEmpty()
) {
$blockedCount++;
continue;
}
Event::dispatch('settings.marketing.events.delete.before', $event);
$this->eventRepository->delete($event->id);
Event::dispatch('settings.marketing.events.delete.after', $event);
$deletedCount++;
}
$statusCode = 200;
switch (true) {
case $deletedCount > 0 && $blockedCount === 0:
$message = trans('admin::app.settings.marketing.events.index.mass-delete-success');
break;
case $deletedCount > 0 && $blockedCount > 0:
$message = trans('admin::app.settings.marketing.events.index.partial-delete-warning');
break;
case $deletedCount === 0 && $blockedCount > 0:
$message = trans('admin::app.settings.marketing.events.index.none-delete-warning');
$statusCode = 400;
break;
default:
$message = trans('admin::app.settings.marketing.events.index.no-selection');
$statusCode = 400;
break;
}
return response()->json(['message' => $message], $statusCode);
} catch (Exception $e) {
return response()->json([
'message' => trans('admin::app.settings.marketing.events.index.mass-delete-failed'),
], 400);
}
}
}

View File

@@ -0,0 +1,152 @@
<?php
namespace Webkul\Admin\Http\Controllers\Settings;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Event;
use Illuminate\View\View;
use Webkul\Admin\DataGrids\Settings\PipelineDataGrid;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Admin\Http\Requests\PipelineForm;
use Webkul\Lead\Repositories\PipelineRepository;
class PipelineController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(protected PipelineRepository $pipelineRepository) {}
/**
* Display a listing of the resource.
*/
public function index(): View|JsonResponse
{
if (request()->ajax()) {
return datagrid(PipelineDataGrid::class)->process();
}
return view('admin::settings.pipelines.index');
}
/**
* Show the form for creating a new resource.
*/
public function create(): View
{
return view('admin::settings.pipelines.create');
}
/**
* Store a newly created resource in storage.
*/
public function store(PipelineForm $request): RedirectResponse
{
$request->validated();
$request->merge([
'is_default' => request()->has('is_default') ? 1 : 0,
]);
Event::dispatch('settings.pipeline.create.before');
$pipeline = $this->pipelineRepository->create($request->all());
Event::dispatch('settings.pipeline.create.after', $pipeline);
session()->flash('success', trans('admin::app.settings.pipelines.index.create-success'));
return redirect()->route('admin.settings.pipelines.index');
}
/**
* Show the form for editing the specified resource.
*/
public function edit(int $id): View
{
$pipeline = $this->pipelineRepository->findOrFail($id);
return view('admin::settings.pipelines.edit', compact('pipeline'));
}
/**
* Update the specified resource in storage.
*/
public function update(PipelineForm $request, int $id): RedirectResponse
{
$request->validated();
$isDefault = request()->has('is_default') ? 1 : 0;
if (! $isDefault) {
$defaultCount = $this->pipelineRepository->findWhere(['is_default' => 1])->count();
$pipeline = $this->pipelineRepository->find($id);
if (
$defaultCount === 1
&& $pipeline->is_default
) {
session()->flash('error', trans('admin::app.settings.pipelines.index.default-required'));
return redirect()->back();
}
}
$request->merge(['is_default' => $isDefault]);
Event::dispatch('settings.pipeline.update.before', $id);
$pipeline = $this->pipelineRepository->update($request->all(), $id);
Event::dispatch('settings.pipeline.update.after', $pipeline);
session()->flash('success', trans('admin::app.settings.pipelines.index.update-success'));
return redirect()->route('admin.settings.pipelines.index');
}
/**
* Remove the specified resource from storage.
*/
public function destroy($id): JsonResponse
{
$pipeline = $this->pipelineRepository->findOrFail($id);
if ($pipeline->is_default) {
return response()->json([
'message' => trans('admin::app.settings.pipelines.index.default-delete-error'),
], 400);
} else {
$defaultPipeline = $this->pipelineRepository->getDefaultPipeline();
$pipeline->leads()->update([
'lead_pipeline_id' => $defaultPipeline->id,
'lead_pipeline_stage_id' => $defaultPipeline->stages()->first()->id,
]);
}
try {
Event::dispatch('settings.pipeline.delete.before', $id);
$this->pipelineRepository->delete($id);
Event::dispatch('settings.pipeline.delete.after', $id);
return response()->json([
'message' => trans('admin::app.settings.pipelines.index.delete-success'),
], 200);
} catch (\Exception $exception) {
return response()->json([
'message' => trans('admin::app.settings.pipelines.index.delete-failed'),
], 400);
}
return response()->json([
'message' => trans('admin::app.settings.pipelines.index.delete-failed'),
], 400);
}
}

View File

@@ -0,0 +1,168 @@
<?php
namespace Webkul\Admin\Http\Controllers\Settings;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Event;
use Illuminate\View\View;
use Webkul\Admin\DataGrids\Settings\RoleDataGrid;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\User\Repositories\RoleRepository;
class RoleController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(protected RoleRepository $roleRepository) {}
/**
* Display a listing of the resource.
*/
public function index(): View|JsonResponse
{
if (request()->ajax()) {
return datagrid(RoleDataGrid::class)->process();
}
return view('admin::settings.roles.index');
}
/**
* Show the form for creating a new resource.
*/
public function create(): View
{
return view('admin::settings.roles.create');
}
/**
* Store a newly created resource in storage.
*/
public function store(): RedirectResponse
{
$this->validate(request(), [
'name' => 'required',
'permission_type' => 'required|in:all,custom',
'description' => 'required',
]);
if (request('permission_type') == 'custom') {
$this->validate(request(), [
'permissions' => 'required',
]);
}
Event::dispatch('settings.role.create.before');
$data = request()->only([
'name',
'description',
'permission_type',
'permissions',
]);
$role = $this->roleRepository->create($data);
Event::dispatch('settings.role.create.after', $role);
session()->flash('success', trans('admin::app.settings.roles.index.create-success'));
return redirect()->route('admin.settings.roles.index');
}
/**
* Show the form for editing the specified resource.
*/
public function edit(int $id): View
{
$role = $this->roleRepository->findOrFail($id);
return view('admin::settings.roles.edit', compact('role'));
}
/**
* Update the specified resource in storage.
*/
public function update(int $id): RedirectResponse
{
$this->validate(request(), [
'name' => 'required',
'permission_type' => 'required|in:all,custom',
'description' => 'required',
'permissions' => 'required_if:permission_type,custom',
]);
Event::dispatch('settings.role.update.before', $id);
$data = array_merge(request()->only([
'name',
'description',
'permission_type',
]), [
'permissions' => request()->has('permissions') ? request('permissions') : [],
]);
$role = $this->roleRepository->update($data, $id);
Event::dispatch('settings.role.update.after', $role);
session()->flash('success', trans('admin::app.settings.roles.index.update-success'));
return redirect()->back();
}
/**
* Remove the specified resource from storage.
*/
public function destroy(int $id): JsonResponse
{
$response = [
'responseCode' => 400,
];
$role = $this->roleRepository->findOrFail($id);
if ($role->users && $role->users->count() >= 1) {
$response['message'] = trans('admin::app.settings.roles.index.being-used');
session()->flash('error', $response['message']);
} elseif ($this->roleRepository->count() == 1) {
$response['message'] = trans('admin::app.settings.roles.index.last-delete-error');
session()->flash('error', $response['message']);
} else {
try {
Event::dispatch('settings.role.delete.before', $id);
if (auth()->guard('user')->user()->role_id == $id) {
$response['message'] = trans('admin::app.settings.roles.index.current-role-delete-error');
} else {
$this->roleRepository->delete($id);
Event::dispatch('settings.role.delete.after', $id);
$message = trans('admin::app.settings.roles.index.delete-success');
$response = [
'responseCode' => 200,
'message' => $message,
];
session()->flash('success', $message);
}
} catch (\Exception $exception) {
$message = $exception->getMessage();
$response['message'] = $message;
session()->flash('error', $message);
}
}
return response()->json($response, $response['responseCode']);
}
}

View File

@@ -0,0 +1,97 @@
<?php
namespace Webkul\Admin\Http\Controllers\Settings;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Collection;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Core\Menu\MenuItem;
class SettingController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\View\View
*/
public function index()
{
return view('admin::settings.index');
}
/**
* Search for settings.
*/
public function search(): ?JsonResponse
{
$query = strtolower(request()->query('query'));
if (empty($query)) {
return response()->json(['data' => []]);
}
$results = $this->searchMenuItems($this->getSettingsConfig(), $query);
return response()->json([
'data' => $results->values(),
]);
}
/**
* Recursively search through menu items and children.
*
* @param Collection<int, MenuItem> $menuItems
* @return Collection<int, array<string, mixed>>
*/
protected function searchMenuItems(Collection $menuItems, string $query): Collection
{
$results = collect();
foreach ($menuItems as $item) {
if ($this->matchesQuery($item, $query)) {
$results->push([
'name' => $item->getName(),
'url' => $item->getUrl(),
'icon' => $item->getIcon(),
'key' => $item->getKey(),
]);
}
if ($item->haveChildren()) {
$childResults = $this->searchMenuItems($item->getChildren(), $query);
$results = $results->merge($childResults);
}
}
return $results;
}
/**
* Determine if the menu item matches the query.
*/
protected function matchesQuery(MenuItem $item, string $query): bool
{
$query = strtolower($query);
$url = strtolower($item->getUrl());
if (
! $url
|| ! str_contains($url, $query)
) {
return false;
}
return true;
}
/**
* Get the settings configuration.
*/
protected function getSettingsConfig(): Collection
{
return menu()
->getItems('admin')
->filter(fn (MenuItem $item) => $item->getKey() === 'settings');
}
}

View File

@@ -0,0 +1,117 @@
<?php
namespace Webkul\Admin\Http\Controllers\Settings;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Event;
use Illuminate\View\View;
use Webkul\Admin\DataGrids\Settings\SourceDataGrid;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Lead\Repositories\SourceRepository;
class SourceController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(protected SourceRepository $sourceRepository) {}
/**
* Display a listing of the resource.
*/
public function index(): View|JsonResponse
{
if (request()->ajax()) {
return datagrid(SourceDataGrid::class)->process();
}
return view('admin::settings.sources.index');
}
/**
* Store a newly created resource in storage.
*/
public function store(): JsonResponse
{
$this->validate(request(), [
'name' => ['required', 'unique:lead_sources,name'],
]);
Event::dispatch('settings.source.create.before');
$source = $this->sourceRepository->create(request()->only(['name']));
Event::dispatch('settings.source.create.after', $source);
return new JsonResponse([
'data' => $source,
'message' => trans('admin::app.settings.sources.index.create-success'),
]);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(int $id): View|JsonResponse
{
$source = $this->sourceRepository->findOrFail($id);
return new JsonResponse([
'data' => $source,
]);
}
/**
* Update the specified resource in storage.
*/
public function update(int $id): JsonResponse
{
$this->validate(request(), [
'name' => 'required|unique:lead_sources,name,'.$id,
]);
Event::dispatch('settings.source.update.before', $id);
$source = $this->sourceRepository->update(request()->only(['name']), $id);
Event::dispatch('settings.source.update.after', $source);
return new JsonResponse([
'data' => $source,
'message' => trans('admin::app.settings.sources.index.update-success'),
]);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(int $id): JsonResponse
{
$source = $this->sourceRepository->findOrFail($id);
if ($source->leads()->count() > 0) {
return new JsonResponse([
'message' => trans('admin::app.settings.sources.index.delete-failed-associated-leads'),
], 400);
}
try {
Event::dispatch('settings.source.delete.before', $id);
$source->delete();
Event::dispatch('settings.source.delete.after', $id);
return new JsonResponse([
'message' => trans('admin::app.settings.sources.index.delete-success'),
], 200);
} catch (Exception $exception) {
return new JsonResponse([
'message' => trans('admin::app.settings.sources.index.delete-failed'),
], 400);
}
}
}

View File

@@ -0,0 +1,161 @@
<?php
namespace Webkul\Admin\Http\Controllers\Settings;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Event;
use Illuminate\View\View;
use Prettus\Repository\Criteria\RequestCriteria;
use Webkul\Admin\DataGrids\Settings\TagDataGrid;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Admin\Http\Requests\MassDestroyRequest;
use Webkul\Admin\Http\Resources\TagResource;
use Webkul\Tag\Repositories\TagRepository;
class TagController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(protected TagRepository $tagRepository) {}
/**
* Display a listing of the resource.
*/
public function index(): View|JsonResponse
{
if (request()->ajax()) {
return datagrid(TagDataGrid::class)->process();
}
return view('admin::settings.tags.index');
}
/**
* Store a newly created resource in storage.
*/
public function store(): JsonResponse
{
$this->validate(request(), [
'name' => ['required', 'unique:tags,name', 'max:50'],
]);
Event::dispatch('settings.tag.create.before');
$tag = $this->tagRepository->create(array_merge(request()->only([
'name',
'color',
]), [
'user_id' => auth()->guard('user')->user()->id,
]));
Event::dispatch('settings.tag.create.after', $tag);
return new JsonResponse([
'data' => new TagResource($tag),
'message' => trans('admin::app.settings.tags.index.create-success'),
]);
}
/**
* Show the form for editing the specified tag.
*/
public function edit(int $id): View|JsonResponse
{
$tag = $this->tagRepository->findOrFail($id);
return new JsonResponse([
'data' => $tag,
]);
}
/**
* Update the specified tag in storage.
*/
public function update(int $id): JsonResponse
{
$this->validate(request(), [
'name' => 'required|max:50|unique:tags,name,'.$id,
]);
Event::dispatch('settings.tag.update.before', $id);
$tag = $this->tagRepository->update(request()->only([
'name',
'color',
]), $id);
Event::dispatch('settings.tag.update.after', $tag);
return new JsonResponse([
'data' => new TagResource($tag),
'message' => trans('admin::app.settings.tags.index.update-success'),
]);
}
/**
* Remove the specified type from storage.
*/
public function destroy(int $id): JsonResponse
{
$tag = $this->tagRepository->findOrFail($id);
try {
Event::dispatch('settings.tag.delete.before', $id);
$tag->delete($id);
Event::dispatch('settings.tag.delete.after', $id);
return new JsonResponse([
'message' => trans('admin::app.settings.tags.index.delete-success'),
], 200);
} catch (\Exception $exception) {
return new JsonResponse([
'message' => trans('admin::app.settings.tags.index.delete-failed'),
], 400);
}
}
/**
* Search tag results
*
* @return \Illuminate\Http\Response
*/
public function search()
{
$tags = $this->tagRepository
->pushCriteria(app(RequestCriteria::class))
->all();
return TagResource::collection($tags);
}
/**
* Mass Delete the specified resources.
*/
public function massDestroy(MassDestroyRequest $massDestroyRequest): JsonResponse
{
$indices = $massDestroyRequest->input('indices');
try {
foreach ($indices as $index) {
Event::dispatch('settings.tag.delete.before', $index);
$this->tagRepository->delete($index);
Event::dispatch('settings.tag.delete.after', $index);
}
return new JsonResponse([
'message' => trans('admin::app.customers.reviews.index.datagrid.mass-delete-success'),
], 200);
} catch (\Exception $e) {
return new JsonResponse([
'message' => $e->getMessage(),
], 500);
}
}
}

View File

@@ -0,0 +1,110 @@
<?php
namespace Webkul\Admin\Http\Controllers\Settings;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Event;
use Illuminate\View\View;
use Webkul\Admin\DataGrids\Settings\TypeDataGrid;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Lead\Repositories\TypeRepository;
class TypeController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(protected TypeRepository $typeRepository) {}
/**
* Display a listing of the type.
*/
public function index(): View|JsonResponse
{
if (request()->ajax()) {
return datagrid(TypeDataGrid::class)->process();
}
return view('admin::settings.types.index');
}
/**
* Store a newly created type in storage.
*/
public function store(): JsonResponse
{
$this->validate(request(), [
'name' => ['required', 'unique:lead_types,name'],
]);
Event::dispatch('settings.type.create.before');
$type = $this->typeRepository->create(request()->only(['name']));
Event::dispatch('settings.type.create.after', $type);
return new JsonResponse([
'data' => $type,
'message' => trans('admin::app.settings.types.index.create-success'),
]);
}
/**
* Show the form for editing the specified type.
*/
public function edit(int $id): View|JsonResponse
{
$type = $this->typeRepository->findOrFail($id);
return new JsonResponse([
'data' => $type,
]);
}
/**
* Update the specified type in storage.
*/
public function update(int $id): JsonResponse
{
$this->validate(request(), [
'name' => 'required|unique:lead_types,name,'.$id,
]);
Event::dispatch('settings.type.update.before', $id);
$type = $this->typeRepository->update(request()->only(['name']), $id);
Event::dispatch('settings.type.update.after', $type);
return new JsonResponse([
'data' => $type,
'message' => trans('admin::app.settings.types.index.update-success'),
]);
}
/**
* Remove the specified type from storage.
*/
public function destroy(int $id): JsonResponse
{
$type = $this->typeRepository->findOrFail($id);
try {
Event::dispatch('settings.type.delete.before', $id);
$type->delete($id);
Event::dispatch('settings.type.delete.after', $id);
return new JsonResponse([
'message' => trans('admin::app.settings.types.index.delete-success'),
], 200);
} catch (\Exception $exception) {
return new JsonResponse([
'message' => trans('admin::app.settings.types.index.delete-failed'),
], 400);
}
}
}

View File

@@ -0,0 +1,260 @@
<?php
namespace Webkul\Admin\Http\Controllers\Settings;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Mail;
use Illuminate\View\View;
use Prettus\Repository\Criteria\RequestCriteria;
use Webkul\Admin\DataGrids\Settings\UserDataGrid;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Admin\Http\Requests\MassDestroyRequest;
use Webkul\Admin\Http\Requests\MassUpdateRequest;
use Webkul\Admin\Http\Resources\UserResource;
use Webkul\Admin\Notifications\User\Create as UserCreatedNotification;
use Webkul\User\Repositories\GroupRepository;
use Webkul\User\Repositories\RoleRepository;
use Webkul\User\Repositories\UserRepository;
class UserController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(
protected UserRepository $userRepository,
protected GroupRepository $groupRepository,
protected RoleRepository $roleRepository
) {}
/**
* Display a listing of the resource.
*/
public function index(): View|JsonResponse
{
if (request()->ajax()) {
return datagrid(UserDataGrid::class)->process();
}
$roles = $this->roleRepository->all();
$groups = $this->groupRepository->all();
return view('admin::settings.users.index', compact('roles', 'groups'));
}
/**
* Store a newly created resource in storage.
*/
public function store(): View|JsonResponse
{
$this->validate(request(), [
'email' => 'required|email|unique:users,email',
'name' => 'required',
'password' => 'nullable',
'confirm_password' => 'nullable|required_with:password|same:password',
'role_id' => 'required',
'status' => 'boolean|in:0,1',
'view_permission' => 'string|in:global,group,individual',
]);
$data = request()->all();
if (
isset($data['password'])
&& $data['password']
) {
$data['password'] = bcrypt($data['password']);
}
Event::dispatch('settings.user.create.before');
$admin = $this->userRepository->create($data);
$admin->groups()->sync($data['groups'] ?? []);
try {
Mail::queue(new UserCreatedNotification($admin));
} catch (\Exception $e) {
report($e);
}
Event::dispatch('settings.user.create.after', $admin);
return new JsonResponse([
'data' => $admin,
'message' => trans('admin::app.settings.users.index.create-success'),
]);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(int $id): View|JsonResponse
{
$admin = $this->userRepository->with(['role', 'groups'])->findOrFail($id);
return new JsonResponse([
'data' => $admin,
]);
}
/**
* Update the specified resource in storage.
*/
public function update(int $id): JsonResponse
{
$this->validate(request(), [
'email' => 'required|email|unique:users,email,'.$id,
'name' => 'required|string',
'password' => 'nullable|string|min:6',
'confirm_password' => 'nullable|required_with:password|same:password',
'role_id' => 'required|integer|exists:roles,id',
'status' => 'nullable|boolean|in:0,1',
'view_permission' => 'required|string|in:global,group,individual',
]);
$data = request()->all();
if (empty($data['password'])) {
$data = Arr::except($data, ['password', 'confirm_password']);
} else {
$data['password'] = bcrypt($data['password']);
}
$authUser = auth()->guard('user')->user();
if ($authUser->id == $id) {
$data['status'] = true;
}
Event::dispatch('settings.user.update.before', $id);
$admin = $this->userRepository->update($data, $id);
$admin->groups()->sync($data['groups'] ?? []);
Event::dispatch('settings.user.update.after', $admin);
return new JsonResponse([
'data' => $admin,
'message' => trans('admin::app.settings.users.index.update-success'),
]);
}
/**
* Search user results.
*/
public function search(): JsonResource
{
$users = $this->userRepository
->pushCriteria(app(RequestCriteria::class))
->all();
return UserResource::collection($users);
}
/**
* Destroy specified user.
*/
public function destroy(int $id): JsonResponse
{
if ($this->userRepository->count() == 1) {
return new JsonResponse([
'message' => trans('admin::app.settings.users.index.last-delete-error'),
], 400);
}
try {
Event::dispatch('user.admin.delete.before', $id);
$this->userRepository->delete($id);
Event::dispatch('user.admin.delete.after', $id);
return new JsonResponse([
'message' => trans('admin::app.settings.users.index.delete-success'),
], 200);
} catch (\Exception $e) {
}
return new JsonResponse([
'message' => trans('admin::app.settings.users.index.delete-failed'),
], 500);
}
/**
* Mass Update the specified resources.
*/
public function massUpdate(MassUpdateRequest $massDestroyRequest): JsonResponse
{
$count = 0;
$users = $this->userRepository->findWhereIn('id', $massDestroyRequest->input('indices'));
foreach ($users as $users) {
if (auth()->guard('user')->user()->id == $users->id) {
continue;
}
Event::dispatch('settings.user.update.before', $users->id);
$this->userRepository->update([
'status' => $massDestroyRequest->input('value'),
], $users->id);
Event::dispatch('settings.user.update.after', $users->id);
$count++;
}
if (! $count) {
return response()->json([
'message' => trans('admin::app.settings.users.index.mass-update-failed'),
], 400);
}
return response()->json([
'message' => trans('admin::app.settings.users.index.mass-update-success'),
]);
}
/**
* Mass Delete the specified resources.
*/
public function massDestroy(MassDestroyRequest $massDestroyRequest): JsonResponse
{
$count = 0;
$users = $this->userRepository->findWhereIn('id', $massDestroyRequest->input('indices'));
foreach ($users as $user) {
if (auth()->guard('user')->user()->id == $user->id) {
continue;
}
Event::dispatch('settings.user.delete.before', $user->id);
$this->userRepository->delete($user->id);
Event::dispatch('settings.user.delete.after', $user->id);
$count++;
}
if (! $count) {
return response()->json([
'message' => trans('admin::app.settings.users.index.mass-delete-failed'),
], 400);
}
return response()->json([
'message' => trans('admin::app.settings.users.index.mass-delete-success'),
]);
}
}

Some files were not shown because too many files have changed in this diff Show More