diff --git a/.gitignore b/.gitignore index 7f08db7..cc0913e 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ yarn-error.log /.idea /.vscode auth.json +.npmrc diff --git a/app/Enums/Users/RoleEnum.php b/app/Enums/Users/RoleEnum.php index 3471d6d..8c4b23f 100644 --- a/app/Enums/Users/RoleEnum.php +++ b/app/Enums/Users/RoleEnum.php @@ -10,6 +10,7 @@ enum RoleEnum: int case conceptor = 1; case admin = 2; + case marketing = 3; case visitor = 99; /** @@ -20,6 +21,7 @@ enum RoleEnum: int private const LABELS = [ self::conceptor->name => 'enums.user-role.conceptor', self::admin->name => 'enums.user-role.admin', + self::marketing->name => 'enums.user-role.marketing', self::visitor->name => 'enums.user-role.visitor', ]; } diff --git a/app/Http/Controllers/Bo/StatsController.php b/app/Http/Controllers/Bo/StatsController.php index 43d7d55..80f1c43 100644 --- a/app/Http/Controllers/Bo/StatsController.php +++ b/app/Http/Controllers/Bo/StatsController.php @@ -29,7 +29,9 @@ class StatsController extends Controller */ public function index(StatsRequest $request): \Illuminate\Contracts\View\View { - $brandModels = Brand::all(); + /** @var \App\Models\User $user */ + $user = auth('backend')->user(); + $brandModels = $user->getRelatedBrands(); // Check if it's a post request or not to get data. if ($request->post()) { diff --git a/app/Http/Controllers/Bo/UrlController.php b/app/Http/Controllers/Bo/UrlController.php index 12f4473..f6d3a76 100644 --- a/app/Http/Controllers/Bo/UrlController.php +++ b/app/Http/Controllers/Bo/UrlController.php @@ -2,10 +2,10 @@ namespace App\Http\Controllers\Bo; +use App\Enums\Users\RoleEnum; use App\Http\Controllers\Controller; use App\Http\Requests\Bo\Urls\StoreUrlRequest; use App\Http\Requests\Bo\Urls\UpdateUrlRequest; -use App\Models\Brand; use App\Models\Url; use App\Models\UrlHistory; use Illuminate\Http\Request; @@ -24,8 +24,13 @@ class UrlController extends Controller */ public function index(Request $request) { + /** @var \App\Models\User $user */ + $user = auth('backend')->user(); + /** @var \Illuminate\Database\Eloquent\Builder $urls */ - $urls = Url::query(); + $urls = $user->role->equals(RoleEnum::conceptor) ? + Url::query() : + Url::query()->whereIn('brand_id', $user->brands->pluck('id')); if ($request->search) { $this->searchQuery( @@ -55,8 +60,12 @@ class UrlController extends Controller */ public function create(Url $url) { + /** @var \App\Models\User $user */ + $user = auth('backend')->user(); + $brandModels = $user->getRelatedBrands(); + return view('bo.pages.urls.create', [ - 'brands' => Brand::all(), + 'brands' => $brandModels, 'urlModel' => $url ]); } @@ -118,9 +127,13 @@ class UrlController extends Controller */ public function edit(Url $url) { + /** @var \App\Models\User $user */ + $user = auth('backend')->user(); + $brandModels = $user->getRelatedBrands(); + return view('bo.pages.urls.edit', [ 'urlModel' => $url, - 'brands' => Brand::all() + 'brands' => $brandModels ]); } diff --git a/app/Http/Controllers/Bo/UserController.php b/app/Http/Controllers/Bo/UserController.php new file mode 100644 index 0000000..b7bb28c --- /dev/null +++ b/app/Http/Controllers/Bo/UserController.php @@ -0,0 +1,164 @@ +search) { + $this->searchQuery( + $users, + $request->search, + null, + 'firstname', + 'lastname', + 'email' + ); + } + + $this->sortQuery($users); + $searchFields = \implode(', ', [ + trans('validation.custom.firstname'), + trans('validation.custom.lastname'), + trans('validation.custom.email'), + ]); + + + $users = $this->paginate($users); + + return view('bo.pages.users.index', compact('users', 'searchFields')); + } + + + + /** + * Show the form for creating a new resource. + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View + */ + public function create() + { + $brandModels = Brand::query()->get(); + return view('bo.pages.users.create', compact('brandModels')); + } + + /** + * Store a newly created resource in storage. + * + * @param \App\Http\Requests\Bo\Users\StoreUserRequest $request + * @return \Illuminate\Http\RedirectResponse + */ + public function store(StoreUserRequest $request) + { + return DB::transaction(function () use ($request) { + $user = new User(); + $user->fill($request->validated()); + + if ($user->saveOrFail()) { + $user->brands()->sync(collect($request->brands)->pluck('id')); + return redirect() + ->route('bo.users.edit', $user) + ->with('success', trans( + ':model a été créé', + ['model' => trans('models.classes.user')] + )); + } + + return redirect() + ->back() + ->with('error', trans( + ':model n\'a pas été créé', + ['model' => trans('models.classes.user')] + )); + }); + } + + + /** + * Show the form for editing the specified resource. + * + * @param \App\Models\User $user + * @return \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View + */ + public function edit(User $user) + { + $brandModels = Brand::query()->get(); + return view('bo.pages.users.edit', [ + 'userModel' => $user, + 'brandModels' => $brandModels + ]); + } + + + /** + * Update the specified resource in storage. + * + * @param \App\Http\Requests\Bo\Users\UpdateUserRequest $request + * @param \App\Models\User $user + * @return \Illuminate\Http\RedirectResponse + */ + public function update(UpdateUserRequest $request, User $user) + { + return DB::transaction(function () use ($request, $user) { + $user->fill($request->validated()); + $user->brands()->sync(collect($request->brands)->pluck('id')); + if ($user->saveOrFail()) { + return redirect() + ->route('bo.users.edit', $user) + ->with('success', trans( + ':model a été mis à jour', + ['model' => trans('models.classes.user')] + )); + } + return redirect() + ->back() + ->with('error', trans( + ':model n\'a pas été mis à jour', + ['model' => trans('models.classes.user')] + )); + }); + } + + /** + * Remove the specified resource from storage. + * + * @param \App\Models\User $user + * @return \Illuminate\Http\RedirectResponse + */ + public function destroy(User $user) + { + if ($user->deleteOrFail()) { + return redirect() + ->route('bo.users.index') + ->with('warning', trans( + ':model a bien été supprimé', + ['model' => trans('models.classes.user')] + )); + } + + return redirect() + ->back() + ->with('error', trans( + ':model n\'a pas été supprimé', + ['model' => trans('models.classes.user')] + )); + } +} diff --git a/app/Http/Requests/Bo/Users/StoreUserRequest.php b/app/Http/Requests/Bo/Users/StoreUserRequest.php new file mode 100644 index 0000000..d8a29b5 --- /dev/null +++ b/app/Http/Requests/Bo/Users/StoreUserRequest.php @@ -0,0 +1,55 @@ + 'required|string|min:3|max:255', + 'lastname' => 'required|string|min:3|max:255', + 'email' => 'required|email:rfc,strict,dns,spoof,filter|unique:users,email', + 'role' => ['required', new Enum(RoleEnum::class)], + 'brands' => 'nullable|array|min:1', + 'brands.*' => 'nullable|array', + 'brands.*.id' => 'nullable|exists:\App\Models\Brand,id', + ]; + } + + /** + * Get custom attributes for validator errors. + * + * @return array + */ + public function attributes(): array + { + return [ + 'first_name' => trans('validation.attributes.first_name'), + 'last_name' => trans('validation.attributes.last_name'), + 'email' => trans('validation.attributes.email'), + 'role' => trans('validation.attributes.role'), + ]; + } +} diff --git a/app/Http/Requests/Bo/Users/UpdateUserRequest.php b/app/Http/Requests/Bo/Users/UpdateUserRequest.php new file mode 100644 index 0000000..bd4fce9 --- /dev/null +++ b/app/Http/Requests/Bo/Users/UpdateUserRequest.php @@ -0,0 +1,52 @@ +route('user')); + } + + /** + * Prepare the data for validation. + * + * @return void + */ + protected function prepareForValidation(): void + { + /** @var \App\Models\User $user */ + $user = $this->route('user'); + if (!$user->role->equals(RoleEnum::conceptor)) { + $this->merge(['email' => $user->email]); + } + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + /** @var \App\Models\User $user */ + $user = $this->route('user'); + return array_merge(parent::rules(), [ + 'email' => [ + 'required', + 'email:rfc,strict,dns,spoof,filter', + Rule::unique('users', 'email')->ignore($user->getRouteKey()), + ] + ]); + } +} diff --git a/app/Models/Brand.php b/app/Models/Brand.php index 958bca9..100fe3b 100644 --- a/app/Models/Brand.php +++ b/app/Models/Brand.php @@ -4,6 +4,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; class Brand extends Model { @@ -39,4 +40,16 @@ class Brand extends Model { return $this->hasMany(Service::class); } + + /** + * The users that belong to the brand. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany<\App\Models\User, + * $this, + * \Illuminate\Database\Eloquent\Relations\Pivot> + */ + public function users(): BelongsToMany + { + return $this->belongsToMany(User::class); + } } diff --git a/app/Models/Url.php b/app/Models/Url.php index b0df032..e815411 100644 --- a/app/Models/Url.php +++ b/app/Models/Url.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Traits\Models\ActivityLog; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Uwil\LaravelCommon\Helpers\FileHelper; @@ -9,6 +10,7 @@ use Uwil\LaravelCommon\Helpers\FileHelper; class Url extends Model { use HasFactory; + use ActivityLog; /** * The attributes that are mass assignable. diff --git a/app/Models/User.php b/app/Models/User.php index 55fc62a..cf5e660 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -5,6 +5,7 @@ namespace App\Models; use App\Enums\Users\RoleEnum; use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; @@ -72,4 +73,33 @@ class User extends Authenticatable AuthorizationException::class ); } + + /** + * Get related brands or all the brands if the user is consceptor. + * + * @return \Illuminate\Database\Eloquent\Collection| + * \Illuminate\Database\Eloquent\Relations\BelongsToMany<\App\Models\Brand, + * $this, + * \Illuminate\Database\Eloquent\Relations\Pivot> + */ + public function getRelatedBrands() + { + return $this->role->equals(RoleEnum::conceptor) ? + Brand::all() : + $this->brands; + } + + // * RELATIONSHIPS + + /** + * The brands that belong to the user. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany<\App\Models\Brand, + * $this, + * \Illuminate\Database\Eloquent\Relations\Pivot> + */ + public function brands(): BelongsToMany + { + return $this->belongsToMany(Brand::class); + } } diff --git a/app/Policies/BrandPolicy.php b/app/Policies/BrandPolicy.php new file mode 100644 index 0000000..189fd5e --- /dev/null +++ b/app/Policies/BrandPolicy.php @@ -0,0 +1,65 @@ +role) && $authModel->role->value() <= $minRole->value(); + } +} diff --git a/app/Policies/ServicePolicy.php b/app/Policies/ServicePolicy.php new file mode 100644 index 0000000..cbbfbcf --- /dev/null +++ b/app/Policies/ServicePolicy.php @@ -0,0 +1,65 @@ +brands->pluck('id'))->contains($url->brand->id)) || + UserStaticRules::atLeastRole($authUser, RoleEnum::conceptor); + } + + /** + * Determine whether the user can create a new model. + * + * @param \App\Models\User $authUser + * @return boolean + */ + public function create(User $authUser): bool + { + return UserStaticRules::atLeastRole($authUser, RoleEnum::conceptor); + } + + /** + * Determine whether the user can update the model. + * + * @param \App\Models\User $authUser + * @param \App\Models\Url $url + * @return boolean + */ + public function update(User $authUser, Url $url): bool + { + return (UserStaticRules::atLeastRole($authUser, RoleEnum::marketing) && + \collect($authUser->brands->pluck('id'))->contains($url->brand->id)) || + UserStaticRules::atLeastRole($authUser, RoleEnum::conceptor); + } + + /** + * Determine whether the user can delete the model. + * + * @param \App\Models\User $authUser + * @return boolean + */ + public function delete(User $authUser): bool + { + return UserStaticRules::atLeastRole($authUser, RoleEnum::conceptor); + } +} diff --git a/app/Policies/UserPolicy.php b/app/Policies/UserPolicy.php new file mode 100644 index 0000000..130d755 --- /dev/null +++ b/app/Policies/UserPolicy.php @@ -0,0 +1,65 @@ +registerPolicies(); + Gate::define('isConceptor', function (Model $authModel) { + return isset($authModel->role) && $authModel->role === RoleEnum::conceptor; + }); + Auth::viaRequest('api-hmac', function (Request $request) { $service = null; diff --git a/database/migrations/2026_04_07_100919_create_brand_user_table.php b/database/migrations/2026_04_07_100919_create_brand_user_table.php new file mode 100644 index 0000000..43d8963 --- /dev/null +++ b/database/migrations/2026_04_07_100919_create_brand_user_table.php @@ -0,0 +1,27 @@ +foreignId('brand_id')->constrained(); + $table->foreignId('user_id')->constrained(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('brand_user'); + } +}; diff --git a/resources/lang/en/enums.php b/resources/lang/en/enums.php index 44ad242..df833a2 100644 --- a/resources/lang/en/enums.php +++ b/resources/lang/en/enums.php @@ -4,6 +4,7 @@ return [ 'user-role' => [ "conceptor" => "conceptor", "admin" => "admin", + "marketing" => "marketing", "visitor" => "visitor", ], 'draft' => 'Draft', diff --git a/resources/lang/en/models.php b/resources/lang/en/models.php index 26e5b00..f06c2c9 100644 --- a/resources/lang/en/models.php +++ b/resources/lang/en/models.php @@ -19,6 +19,8 @@ return [ 'timeslots' => 'Collection time slots', 'orders' => 'Orders', 'contacts' => 'Contact messages', + 'user' => 'user', + 'brand' => 'brand', 'activity_log' => 'activity log|activity logs' ] ]; diff --git a/resources/lang/fr/enums.php b/resources/lang/fr/enums.php index 1275727..d272bd7 100644 --- a/resources/lang/fr/enums.php +++ b/resources/lang/fr/enums.php @@ -4,6 +4,7 @@ return [ 'user-role' => [ "conceptor" => "concepteur", "admin" => "administrateur", + "marketing" => "marketing", "visitor" => "visiteur", ], 'draft' => 'Brouillon', diff --git a/resources/lang/fr/models.php b/resources/lang/fr/models.php index 470cd00..caa6aaa 100644 --- a/resources/lang/fr/models.php +++ b/resources/lang/fr/models.php @@ -22,6 +22,8 @@ return [ 'brands' => 'Marques', 'urls' => 'URLs', 'services' => 'Services', + 'user' => 'utilisateur', + 'brand' => 'marque', 'activity_log' => 'journal d\'activité|journaux d\'activité' ] ]; diff --git a/resources/lang/fr/validation.php b/resources/lang/fr/validation.php index f8cbe75..d8a86b6 100644 --- a/resources/lang/fr/validation.php +++ b/resources/lang/fr/validation.php @@ -117,7 +117,11 @@ return [ 'uploaded' => 'Le fichier du champ :attribute n\'a pu être téléversé.', 'url' => 'Le format de l\'URL de :attribute n\'est pas valide.', 'uuid' => 'Le champ :attribute doit être un UUID valide', - 'custom' => [], + 'custom' => [ + 'firstname' => 'Prénom', + 'lastname' => 'Nom', + 'email' => 'Adresse email', + ], 'phone' => 'Le champ :attribute n\'est pas un numéro de téléphone valide.', 'color' => ':attribute doit être un code couleur valide (hexadecimal, RGB, RGBA ou un nom de couleur CSS)', 'color_hex' => ':attribute doit être un code couleur hexadecimal valide', diff --git a/resources/views/bo/layouts/nav-menu.blade.php b/resources/views/bo/layouts/nav-menu.blade.php index 4d0bd35..3a923ed 100644 --- a/resources/views/bo/layouts/nav-menu.blade.php +++ b/resources/views/bo/layouts/nav-menu.blade.php @@ -6,29 +6,55 @@ {{ __('Dashboard') }} + @can('isConceptor') + + @endcan - - - + @canAny(['viewAny', 'create', 'update', 'delete'], \App\Models\Brand::class) + + @endcan + @canAny(['viewAny', 'create', 'update', 'delete'], \App\Models\Brand::class) + + @endcan + @canAny(['viewAny', 'create', 'update', 'delete'], \App\Models\Service::class) + + @endcan + @canAny(['viewAny', 'create', 'update', 'delete'], \App\Models\User::class) + + @endcan @endauth diff --git a/resources/views/bo/pages/users/create.blade.php b/resources/views/bo/pages/users/create.blade.php new file mode 100644 index 0000000..85f76bd --- /dev/null +++ b/resources/views/bo/pages/users/create.blade.php @@ -0,0 +1,31 @@ +@extends('bo.layout') + +@section('title', sprintf('%s - %s', str(__('models.classes.user'))->plural()->ucfirst(), Str::ucfirst(__('création')))) + +@section('breadcrumb', request()->route()->getName()) + +@section('content') +
+
+
+

+ +  {{ str(__('models.classes.user'))->plural()->ucfirst() }} {{ __('création') }} +

+
+
+
+
+ @csrf + @include('bo.pages.users.form') +
+
+ +
+
+
+
+
+@endsection diff --git a/resources/views/bo/pages/users/edit.blade.php b/resources/views/bo/pages/users/edit.blade.php new file mode 100644 index 0000000..027b6bc --- /dev/null +++ b/resources/views/bo/pages/users/edit.blade.php @@ -0,0 +1,58 @@ +@extends('bo.layout', ['brParam' => $userModel]) + +@section('title', sprintf('%s - %s %s', str(__('models.classes.user'))->plural()->ucfirst(), Str::ucfirst(__('édition')), $userModel->lastname)) + +@section('breadcrumb', request()->route()->getName()) + +@section('content') +
+
+
+
+

+ + + +  {{ str(__('models.classes.user'))->plural()->ucfirst() }} {{ __('édition') }} +

+
+
+
+ @method('DELETE') + @csrf + +
+
+
+
+
+
+ @csrf + @method('PUT') + @include('bo.pages.users.form', compact('userModel')) + +
+
+ * {{ __('champs obligatoires') }} +
+
+
+
+ +
+
+
+
+
+@endsection diff --git a/resources/views/bo/pages/users/form.blade.php b/resources/views/bo/pages/users/form.blade.php new file mode 100644 index 0000000..33db203 --- /dev/null +++ b/resources/views/bo/pages/users/form.blade.php @@ -0,0 +1,108 @@ +
+ {{-- LASTNAME --}} +
+
+ {{ __('validation.custom.lastname') }}  +
+
+ @include('bo.partials.inputs.varchar', [ + 'id' => 'userLastname', + 'hasWordCounter' => true, + 'name' => 'lastname', + 'required' => true, + 'model' => $userModel ?? null, + 'helper' => __('Nom (3 caractères minimum, 255 maximum).'), + 'placeholder' => "Dupont" + ]) +
+
+
+
+ {{-- FIRSTNAME --}} +
+
+ {{ __('validation.custom.firstname') }}  +
+
+ @include('bo.partials.inputs.varchar', [ + 'id' => 'userFirstname', + 'hasWordCounter' => true, + 'name' => 'firstname', + 'required' => true, + 'model' => $userModel ?? null, + 'helper' => __('Prénom (3 caractères minimum, 255 maximum).'), + 'placeholder' => "Jean" + ]) +
+
+
+
+ {{-- EMAIL --}} +
+
+ {{ __('validation.custom.email') }}  +
+
+ @include('bo.partials.inputs.varchar-email', [ + 'id' => 'userEmail', + 'name' => 'email', + 'required' => true, + 'model' => $userModel ?? null, + 'helper' => __('Adresse email valide.'), + 'placeholder' => "jean.dupont@ldc.fr" + ]) +
+
+
+
+ {{-- ROLE --}} +
+
+ {{ __('Rôle') }} +
+
+ @include('bo.partials.inputs.tinyint-enum', [ + 'id' => 'userRole', + 'label' => __('Associer un rôle'), + 'name' => 'role', + 'model' => $userModel ?? null, + 'required' => true, + 'associatedModels' => Arr::where( + \App\Enums\Users\RoleEnum::toArray(), + fn(object $role) => $role->value >= auth('backend')->user()->role->value()), + 'helper' => __('Sélectionnez un rôle dans la liste déroulante.'), + ]) +
+
+
+
+ {{-- BRANDS --}} +
+
+ {{ str(__('models.classes.brand'))->plural()->ucFirst() }} +
+
+ @include('bo.partials.inputs.pivot-belongsToMany', [ + 'id' => 'productRecipes', + 'label' => __(':field associées au :model', [ + 'field' => str(__('models.classes.brand'))->plural()->ucFirst(), + 'model' => __('models.classes.user'), + ]), + 'name' => 'brands', + 'model' => $userModel ?? null, + 'itemsList' => $brandModels, + 'itemLimit' => [0, 8], + 'placeholder' => __('Rechercher une :model', [ + 'model' => __('models.classes.brand') + ]), + 'helper' => __('Séléctionnez les :field associer à cet :model', [ + 'field' => str(__('models.classes.brand'))->plural(), + 'model' => __('models.classes.user'), + ]), + 'required' => false, + ]) +
+
+
+
+
diff --git a/resources/views/bo/pages/users/index.blade.php b/resources/views/bo/pages/users/index.blade.php new file mode 100644 index 0000000..39f8cd2 --- /dev/null +++ b/resources/views/bo/pages/users/index.blade.php @@ -0,0 +1,37 @@ +@extends('bo.layout') + +@section('title', sprintf('%s - %s', str(__('models.classes.user'))->plural()->ucfirst(), Str::ucfirst(__('liste')))) + +@section('breadcrumb', request()->route()->getName()) + +@section('content') +
+
+
+
+

{{ str(__('models.classes.user'))->plural()->ucfirst() }} {{ Str::ucfirst(__('liste')) }}

+
+
+
+ +
+
+
+
+
+ @include('modules.searchBar') +
+
+ @include('bo.pages.users.indexList') +
+
+
+
+ {{ $users->links() }} +
+
+
+
+@endsection diff --git a/resources/views/bo/pages/users/indexList.blade.php b/resources/views/bo/pages/users/indexList.blade.php new file mode 100644 index 0000000..cb0ef4d --- /dev/null +++ b/resources/views/bo/pages/users/indexList.blade.php @@ -0,0 +1,52 @@ +
+ + + @php + $cols = [ + 'firstname' => __('validation.custom.firstname'), + 'lastname' => __('validation.custom.lastname'), + 'email' => __('validation.custom.email'), + ]; + $routeName = request()->route()->getName(); + $rst = !is_null(request()->rst) || !Session::has("$routeName.sorted"); + $noOrder = Session::get("$routeName.sort_col") !== 'order' and (Session::has("$routeName.sort_col") or Session::has("$routeName.sort_way")); + $noOrder = Session::get("$routeName.sort_col") !== '' and (Session::has("$routeName.sort_col") or Session::has("$routeName.sort_way")); + @endphp + @include('modules.tableColSorter', [ + 'cols' => $cols, + 'mobileHide' => [], + ]) + + + @if ($users->count()) + @foreach ($users as $usersModel) + + + + + + + @endforeach + @else + + + + @endif + +
+ {{ $usersModel->firstname }} + + {{ $usersModel->lastname }} + + {{ $usersModel->email }} + +
+ +
+
+ {{ __('Aucun résultat pour les :models', ['models' => str(__('models.classes.user'))->plural()->ucfirst()]) }} + +
+
diff --git a/resources/views/bo/partials/inputs/tinyint-enum.blade.php b/resources/views/bo/partials/inputs/tinyint-enum.blade.php index e6ad0fb..56c0338 100644 --- a/resources/views/bo/partials/inputs/tinyint-enum.blade.php +++ b/resources/views/bo/partials/inputs/tinyint-enum.blade.php @@ -1,18 +1,42 @@ - - $errors->has($name) + ]) + name="{{ $name }}" + aria-describedby="{{ $id }}Help" + @disabled(!empty($disabled)) + @required(!empty($required)) + role="button"> + @foreach ($associatedModels as $associatedModel) + @endforeach @include('modules.input-error', [ -'inputName' => $name, -'helperId' => "{$id}Help", -'helper' => $helper, -]) \ No newline at end of file + 'inputName' => $name, + 'helperId' => "{$id}Help", + 'helper' => $helper +]) diff --git a/routes/web-bo.php b/routes/web-bo.php index b4333a0..fbe30e4 100644 --- a/routes/web-bo.php +++ b/routes/web-bo.php @@ -6,6 +6,7 @@ use App\Http\Controllers\Bo\DashboardController; use App\Http\Controllers\Bo\ServiceController; use App\Http\Controllers\Bo\StatsController; use App\Http\Controllers\Bo\UrlController; +use App\Http\Controllers\Bo\UserController; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Route; @@ -51,6 +52,9 @@ Route::prefix('bo') Route::post('/services/{service}/token/generate', [ServiceController::class, 'generateServicePassword']) ->name('generate-token-service'); + // * USERS + Route::resource('users', UserController::class); + Route::middleware('can:isConceptor')->group(function () { // * ACTIVITY LOGS. Route::resource('activity_logs', ActivityLogController::class)->only(['index', 'show']);