diff --git a/README.md b/README.md index 569171c..598539a 100644 --- a/README.md +++ b/README.md @@ -24,3 +24,11 @@ Choisir un thème dans le dépôt des thèmes `gen-game-web`, puis copier les fi - SH - Soucis de connexion / déconnexion quand le jeu est terminé et que l'adresse du jeu redirige vers une autre adresse : si on se déconnecte et qu'on revient sur le bo, on est toujours connecté -> MJ - Pour que le cookie soit delete il ne faut pas renvoyer un 302 juste apres le disconnect ``` + + +## Generation des codes + +`artisan queue:work` + +En cas de manipulation pourrie, penser a vider le cache pour supprimer le lock +sur le job de generation des codes. diff --git a/README.txt b/README.txt deleted file mode 100644 index b4a8c3a..0000000 --- a/README.txt +++ /dev/null @@ -1,4 +0,0 @@ -git checkout --orphan master -git add . -git commit -m "Initial commit" -git push \ No newline at end of file diff --git a/app/Console/Commands/ExportCodesUniques.php b/app/Console/Commands/ExportCodesUniques.php index 2aa2e4a..83944d8 100644 --- a/app/Console/Commands/ExportCodesUniques.php +++ b/app/Console/Commands/ExportCodesUniques.php @@ -109,8 +109,8 @@ class ExportCodesUniques extends Command $this->info("L'archive CodesUniques_" . date('dmY') . ".zip a ete creer."); // Envoi de mail à MAIL_ADMIN avec le nom du ZIP "CodesUniques_". date('dmY') .".zip" . - Mail::to(config('mail.MAIL_ADMIN')) - ->send(new MailExportCodesUniques($nbCodeFic, $nbCodeTot, $nbCodeLigne)); + Mail::to(config('mail.admin')) + ->send(new MailExportCodesUniques($nbCodeFic, $nbCodeTot, $nbCodeLigne)); return 0; } @@ -210,6 +210,6 @@ class ExportCodesUniques extends Command Code::whereIn('id', $exportedCodesId)->update(['code_exported' => 1]); } } //end if - }//end foreach + } //end foreach } } diff --git a/app/Console/Commands/TestMail.php b/app/Console/Commands/TestMail.php index ae0936b..176f35a 100644 --- a/app/Console/Commands/TestMail.php +++ b/app/Console/Commands/TestMail.php @@ -12,7 +12,7 @@ class TestMail extends Command * * @var string */ - protected $signature = 'uwil:mail-test'; + protected $signature = 'uwil:mail:test'; /** * The console command description. @@ -24,13 +24,12 @@ class TestMail extends Command /** * Execute the console command. * - * @return integer + * @return void */ public function handle() { Mail::raw(sprintf('UWIL mail-test from %s %s', \config('app.name'), \config('app.url')), function ($msg) { $msg->to('equipe.web@ldc.fr')->subject('Test Email'); }); - return 0; } } diff --git a/app/Console/Commands/UserCreate.php b/app/Console/Commands/UserCreate.php index b14d954..95c8dd2 100644 --- a/app/Console/Commands/UserCreate.php +++ b/app/Console/Commands/UserCreate.php @@ -16,7 +16,7 @@ class UserCreate extends Command * * @var string */ - protected $signature = 'user:create'; + protected $signature = 'uwil:user:create'; /** * The console command description. @@ -26,18 +26,18 @@ class UserCreate extends Command protected $description = 'Create a backend admin user'; /** - * The firstname of the new user. + * The first name of the new user. * * @var string */ - protected $firstname; + protected $first_name; /** - * The lastname of the new user. + * The last name of the new user. * * @var string */ - protected $lastname; + protected $last_name; /** * The email of the new user. @@ -67,11 +67,11 @@ class UserCreate extends Command */ public function handle(): int { - $this->firstname = null; - $this->addFirstname(); + $this->first_name = null; + $this->addFirstName(); - $this->lastname = null; - $this->addLastname(); + $this->last_name = null; + $this->addLastName(); $this->email = null; $this->addEmail(); @@ -83,8 +83,8 @@ class UserCreate extends Command $this->addRole(); $user = new User([ - 'firstname' => $this->firstname, - 'lastname' => $this->lastname, + 'first_name' => $this->first_name, + 'last_name' => $this->last_name, 'email' => $this->email, 'password' => $this->password, 'role' => $this->role, @@ -99,23 +99,23 @@ class UserCreate extends Command } /** - * Add a firstname to the user. + * Add a first name to the user. * * @return void */ - private function addFirstname(): void + private function addFirstName(): void { - while (is_null($this->firstname)) { + while (is_null($this->first_name)) { try { - $tmp = $this->ask('Type the wanted user firstname', 'Admin'); - $validator = Validator::make(['firstname' => $tmp], [ - 'firstname' => 'required|min:3|max:255' + $tmp = $this->ask('Type the wanted user first name', 'Admin'); + $validator = Validator::make(['first_name' => $tmp], [ + 'first_name' => 'required|min:3|max:255' ]); $validator->validate(); - $this->firstname = $tmp; + $this->first_name = $tmp; } catch (ValidationException $e) { $this->error(sprintf( - 'Please provide a valid firstname %s', + 'Please provide a valid first name %s', \implode(',', collect($e->errors())->flatten()->all()) )); continue; @@ -124,23 +124,23 @@ class UserCreate extends Command } /** - * Add a lastname to the user. + * Add a last name to the user. * * @return void */ - private function addLastname(): void + private function addLastName(): void { - while (is_null($this->lastname)) { + while (is_null($this->last_name)) { try { - $tmp = $this->ask('Type the wanted user lastname', 'Saïtama'); - $validator = Validator::make(['lastname' => $tmp], [ - 'lastname' => 'required|min:3|max:255' + $tmp = $this->ask('Type the wanted user last name', 'Saïtama'); + $validator = Validator::make(['last_name' => $tmp], [ + 'last_name' => 'required|min:3|max:255' ]); $validator->validate(); - $this->lastname = $tmp; + $this->last_name = $tmp; } catch (ValidationException $e) { $this->error(sprintf( - 'Please provide a valid lastname %s', + 'Please provide a valid last_name %s', \implode(',', collect($e->errors())->flatten()->all()) )); continue; @@ -200,15 +200,15 @@ class UserCreate extends Command try { $tmp = $this->choice( 'Select his role', - [RoleEnum::conceptor->label(), RoleEnum::admin->label()], - RoleEnum::admin->label(), + [RoleEnum::conceptor->name, RoleEnum::admin->name], + RoleEnum::admin->name, $maxAttempts = null, $allowMultipleSelections = false ); - if ($tmp === RoleEnum::conceptor->label()) { + if ($tmp === RoleEnum::conceptor->name) { $tmp = RoleEnum::conceptor; } - if ($tmp === RoleEnum::admin->label()) { + if ($tmp === RoleEnum::admin->name) { $tmp = RoleEnum::admin; } $validator = Validator::make(['role' => $tmp], [ diff --git a/app/Enums/ActivityLogs/ActivityLogsEventEnum.php b/app/Enums/ActivityLogs/ActivityLogsEventEnum.php new file mode 100644 index 0000000..ebb098c --- /dev/null +++ b/app/Enums/ActivityLogs/ActivityLogsEventEnum.php @@ -0,0 +1,46 @@ +name => 'Création', + self::updated->name => 'Mise à jour', + self::duplicated->name => 'Duplication', + self::deleted->name => 'Suppression', + ]; + + + /** + * Custom added classes definition + */ + private const BOOTSTRAPCLASS = [ + self::created->name => 'success', + self::updated->name => 'warning', + self::duplicated->name => 'secondary', + self::deleted->name => 'danger', + ]; + + /** + * Get Class + * + * @return string + */ + public function bootstrapClass(): string + { + return self::BOOTSTRAPCLASS[$this->name]; + } +} diff --git a/app/Enums/InputType.php b/app/Enums/InputType.php new file mode 100644 index 0000000..0b59d29 --- /dev/null +++ b/app/Enums/InputType.php @@ -0,0 +1,30 @@ +name => 'Contact', + self::legals->name => 'Mentions légales', + self::rules->name => 'Règlement', + self::confidentiality->name => 'Charte de confidentialité', + ]; +} diff --git a/app/Enums/Theme/BootstrapThemeEnum.php b/app/Enums/Theme/BootstrapThemeEnum.php new file mode 100644 index 0000000..f1f3725 --- /dev/null +++ b/app/Enums/Theme/BootstrapThemeEnum.php @@ -0,0 +1,21 @@ +name => 'Light', + self::dark->name => 'Dark', + ]; +} diff --git a/app/Http/Controllers/Bo/ActivityLogController.php b/app/Http/Controllers/Bo/ActivityLogController.php new file mode 100644 index 0000000..7f79987 --- /dev/null +++ b/app/Http/Controllers/Bo/ActivityLogController.php @@ -0,0 +1,71 @@ +authorize('isConceptor'); + /** @var \Illuminate\Database\Eloquent\Builder $query */ + $query = ActivityLog::query()->with('user'); + + /** @var string $search Search field */ + $search = $request->search; + if ($search) { + $this->searchQuery( + $query, + $search, + null, + 'model_class', + ); + } + $searchFields = \implode(', ', [ + trans('validation.custom.target'), + ]); + + /** Sort columns with a query */ + $this->sortQuery($query); + + /** Custom pagination */ + $activitylogModels = $this->paginate($query); + + return view('back::pages.activity_logs.index', compact('activitylogModels', 'search', 'searchFields')); + } + + /** + * Show the form for editing the specified resource. + * + * @param App\Models\ActivityLog $activity_log + * @return \Illuminate\Contracts\View\View + */ + public function show(ActivityLog $activity_log): \Illuminate\Contracts\View\View + { + $this->authorize('isConceptor'); + $targetModelClass = $activity_log->model_class; + $targetModel = $activity_log->model_class::where( + (new $targetModelClass())->getRouteKeyName(), + $activity_log->model_id + )->first(); + + return view('back::pages.activity_logs.show', [ + 'activitylogModel' => $activity_log, + 'targetModel' => $targetModel, + ]); + } +} diff --git a/app/Http/Controllers/Bo/Auth/LoginController.php b/app/Http/Controllers/Bo/Auth/LoginController.php index fa9aff9..314f43e 100644 --- a/app/Http/Controllers/Bo/Auth/LoginController.php +++ b/app/Http/Controllers/Bo/Auth/LoginController.php @@ -6,6 +6,7 @@ use App\Http\Controllers\Controller; use Illuminate\Contracts\Auth\StatefulGuard; use Illuminate\Contracts\View\View; use Illuminate\Foundation\Auth\AuthenticatesUsers; +use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; class LoginController extends Controller @@ -39,7 +40,7 @@ class LoginController extends Controller */ public function showLoginForm(): View { - return view('back::auth.login'); + return view('bo.auth.login'); } /** @@ -51,4 +52,21 @@ class LoginController extends Controller { return response()->redirectToIntended()->getTargetUrl(); } + + /** + * Attempt to log the user into the application. + * + * @param \Illuminate\Http\Request $request + * @return boolean + */ + protected function attemptLogin(Request $request) + { + $credentials = $this->credentials($request); + // * Attempt login only using published users. + $credentials['published'] = true; + return $this->guard()->attempt( + $credentials, + $request->boolean('remember') + ); + } } diff --git a/app/Http/Controllers/Bo/BannedIpController.php b/app/Http/Controllers/Bo/BannedIpController.php index 5d78c15..864dfff 100644 --- a/app/Http/Controllers/Bo/BannedIpController.php +++ b/app/Http/Controllers/Bo/BannedIpController.php @@ -33,13 +33,13 @@ class BannedIpController extends Controller 'ip', ); } - $searchFields = trans('validation.attributes.ip'); + $searchFields = trans('validation.custom.ip'); /** Sort columns with a query */ $this->sortQuery($bannedipModels); /** Custom pagination */ - $bannedipModels = $this->customPaginate($bannedipModels, $request->pagination); + $bannedipModels = $this->paginate($bannedipModels); return view('back::pages.banned-ips.index', compact('bannedipModels', 'search', 'searchFields')); } diff --git a/app/Http/Controllers/Bo/CodesController.php b/app/Http/Controllers/Bo/CodesController.php index 7d715c3..23f7224 100644 --- a/app/Http/Controllers/Bo/CodesController.php +++ b/app/Http/Controllers/Bo/CodesController.php @@ -3,13 +3,14 @@ namespace App\Http\Controllers\Bo; use App\Http\Controllers\Controller; -use App\Http\Requests\CodeSearchRequest; -use App\Http\Requests\ExportCodesRequest; -use App\Http\Requests\GenerateCodesRequest; +use App\Http\Requests\Bo\CodeSearchRequest; +use App\Http\Requests\Bo\ExportCodesRequest; +use App\Http\Requests\Bo\GenerateCodesRequest; use App\Jobs\ProcessExportCodesUniques; use App\Jobs\ProcessUniqueCode; use App\Models\Code; use App\Models\Settings; +use Illuminate\Support\Facades\Queue; use Kwaadpepper\Serial\Exceptions\SerialCasterException; class CodesController extends Controller @@ -21,11 +22,11 @@ class CodesController extends Controller */ public function index(): \Illuminate\Contracts\View\View { - $settings = Settings::first(); - $nbCodeBase = Code::count(); - $nbCodeUsed = Code::where('code_used', 1)->count(); - $nbCodeExported = Code::where('code_exported', 1)->count(); - $nbCodeRemainingToExport = Code::where('code_exported', 0)->count(); + $settings = Settings::query()->firstOrFail(); + $nbCodeBase = Code::query()->count(); + $nbCodeUsed = Code::query()->where('code_used', true)->count(); + $nbCodeExported = Code::query()->where('code_exported', true)->count(); + $nbCodeRemainingToExport = Code::query()->where('code_exported', false)->count(); return view('back::pages.codes.index', compact( 'settings', 'nbCodeBase', @@ -38,55 +39,67 @@ class CodesController extends Controller /** * Run generate codes action * - * @param GenerateCodesRequest $request + * @param \App\Http\Controllers\Bo\GenerateCodesRequest $request * @return \Illuminate\Http\RedirectResponse */ public function generate(GenerateCodesRequest $request): \Illuminate\Http\RedirectResponse { - ProcessUniqueCode::dispatch( - $request->quantity, - $request->code_length + Queue::push( + new ProcessUniqueCode( + intval($request->quantity), + $request->code_length + ) ); return back()->with( 'success', - "La génération à commencée, un mail de notification sera envoyé à l'adresse " . - config('mail.MAIL_ADMIN') . " lorsque le traitement sera terminé." + trans( + // phpcs:ignore Generic.Files.LineLength.TooLong + 'La tache de génération à été ajoutée, un mail de notification sera envoyé à l\'adresse :email lorsque le traitement sera terminé.', + [ + 'email' => config('mail.admin') + ] + ) ); } /** * Run export codes * - * @param ExportCodesRequest $request + * @param \App\Http\Requests\Bo\ExportCodesRequest $request * @return \Illuminate\Http\RedirectResponse */ public function export(ExportCodesRequest $request): \Illuminate\Http\RedirectResponse { - ProcessExportCodesUniques::dispatch( - intval($request->nb_code_fic), - intval($request->nb_code_tot), - intval($request->nb_code_ligne) + Queue::push( + new ProcessExportCodesUniques( + intval($request->nb_code_fic), + intval($request->nb_code_tot), + intval($request->nb_code_ligne) + ) ); return back()->with( 'success', - "L'export des codes à commencé, un mail de notification sera envoyé à l'adresse " . - config('mail.MAIL_ADMIN') . " lorsque le traitement sera terminé." + // phpcs:ignore Generic.Files.LineLength.TooLong + 'L\'export des codes à commencé, un mail de notification sera envoyé à l\'adresse :email lorsque le traitement sera terminé.', + [ + 'email' => config('mail.admin') + ] ); } /** * Search for a specific code. * - * @param CodeSearchRequest $request + * @param \App\Http\Requests\Bo\CodeSearchRequest $request * @return \Illuminate\Contracts\View\View */ public function search(CodeSearchRequest $request): \Illuminate\Contracts\View\View { $code = null; try { - $code = Code::whereId(Code::serialToInt($request->code))->first(); + $code = Code::query()->where('id', Code::serialToInt($request->code))->first(); } catch (\Throwable $e) { if (\get_class($e) === SerialCasterException::class) { $code = false; diff --git a/app/Http/Controllers/Bo/ContentController.php b/app/Http/Controllers/Bo/ContentController.php deleted file mode 100644 index 69f167f..0000000 --- a/app/Http/Controllers/Bo/ContentController.php +++ /dev/null @@ -1,91 +0,0 @@ -search; - if ($search) { - $this->searchQuery( - $contentModels, - $search, - null, - 'key', - 'value' - ); - } - $searchFields = \sprintf( - '%s, %s', - trans('validation.attributes.name'), - trans('validation.attributes.value'), - ); - - /** Sort columns with a query */ - $this->sortQuery($contentModels); - - /** Custom pagination */ - $contentModels = $this->customPaginate($contentModels, $request->pagination); - - return view('back::pages.contents.index', compact('contentModels', 'search', 'searchFields')); - } - - /** - * Show the form for editing the specified resource. - * - * @param \App\Models\Content $content - * @return \Illuminate\Contracts\View\View - */ - public function edit(Content $content): \Illuminate\Contracts\View\View - { - return view('back::pages.contents.edit', [ - 'contentModel' => $content, - ]); - } - - /** - * Update the specified resource in storage. - * - * @param \App\Http\Requests\Bo\Contents\UpdateContentRequest $request - * @param \App\Models\Content $content - * @return \Illuminate\Http\RedirectResponse - */ - public function update(UpdateContentRequest $request, Content $content): \Illuminate\Http\RedirectResponse - { - return DB::transaction(function () use ($request, $content) { - $content->fill($request->validated()); - if ($content->saveOrFail()) { - return redirect() - ->route('bo.contents.edit', $content) - ->with('success', trans( - 'crud.messages.has_been_updated', - ['model' => Str::singular(trans('models.classes.contents'))] - )); - } - return redirect() - ->back() - ->with('error', trans( - 'crud.messages.cannot_be_updated', - ['model' => Str::singular(trans('models.classes.contents'))] - )); - }); - } -} diff --git a/app/Http/Controllers/Bo/FaqQuestionsController.php b/app/Http/Controllers/Bo/FaqQuestionsController.php index caeedd2..bdc01b8 100644 --- a/app/Http/Controllers/Bo/FaqQuestionsController.php +++ b/app/Http/Controllers/Bo/FaqQuestionsController.php @@ -37,7 +37,7 @@ class FaqQuestionsController extends Controller } $searchFields = \sprintf( '%s, %s ...', - Str::ucfirst(__('validation.attributes.question')), + Str::ucfirst(__('validation.custom.question')), __('validation.attributes.answer'), ); @@ -45,7 +45,7 @@ class FaqQuestionsController extends Controller $this->sortQuery($faqQuestionModels); /** Custom pagination */ - $faqQuestionModels = $this->customPaginate($faqQuestionModels, $request->pagination); + $faqQuestionModels = $this->paginate($faqQuestionModels); return view('back::pages.faq-questions.index', compact('faqQuestionModels', 'search', 'searchFields')); } diff --git a/app/Http/Controllers/Bo/FaqThemesController.php b/app/Http/Controllers/Bo/FaqThemesController.php index 1843a0f..74616b3 100644 --- a/app/Http/Controllers/Bo/FaqThemesController.php +++ b/app/Http/Controllers/Bo/FaqThemesController.php @@ -39,7 +39,7 @@ class FaqThemesController extends Controller $this->sortQuery($faqThemeModels); /** Custom pagination */ - $faqThemeModels = $this->customPaginate($faqThemeModels, $request->pagination); + $faqThemeModels = $this->paginate($faqThemeModels); return view('back::pages.faq-themes.index', compact('faqThemeModels', 'search', 'searchFields')); } diff --git a/app/Http/Controllers/Bo/GainViewController.php b/app/Http/Controllers/Bo/GainViewController.php index cdd8e20..95a15c9 100644 --- a/app/Http/Controllers/Bo/GainViewController.php +++ b/app/Http/Controllers/Bo/GainViewController.php @@ -43,7 +43,7 @@ class GainViewController extends Controller $this->sortQuery($gainViewModels); /** Custom pagination */ - $gainViewModels = $this->customPaginate($gainViewModels, $request->pagination); + $gainViewModels = $this->paginate($gainViewModels); return view('back::pages.gain-views.index', compact('gainViewModels', 'search', 'searchFields')); } diff --git a/app/Http/Controllers/Bo/PrizesController.php b/app/Http/Controllers/Bo/PrizesController.php index 4dd1e2c..6d1f719 100644 --- a/app/Http/Controllers/Bo/PrizesController.php +++ b/app/Http/Controllers/Bo/PrizesController.php @@ -45,7 +45,7 @@ class PrizesController extends Controller $this->sortQuery($prizeModels); /** Custom pagination */ - $prizeModels = $this->customPaginate($prizeModels, $request->pagination); + $prizeModels = $this->paginate($prizeModels); return view('back::pages.prizes.index', compact('prizeModels', 'search', 'searchFields')); } diff --git a/app/Http/Controllers/Bo/ProductsController.php b/app/Http/Controllers/Bo/ProductsController.php index 489b130..4d0ea50 100644 --- a/app/Http/Controllers/Bo/ProductsController.php +++ b/app/Http/Controllers/Bo/ProductsController.php @@ -47,7 +47,7 @@ class ProductsController extends Controller $this->sortQuery($productModels); /** Custom pagination */ - $productModels = $this->customPaginate($productModels, $request->pagination); + $productModels = $this->paginate($productModels); return view('back::pages.products.index', compact('productModels', 'search', 'searchFields')); } diff --git a/app/Http/Controllers/Bo/RangeController.php b/app/Http/Controllers/Bo/RangeController.php index 932ad36..1e8b019 100644 --- a/app/Http/Controllers/Bo/RangeController.php +++ b/app/Http/Controllers/Bo/RangeController.php @@ -39,7 +39,7 @@ class RangeController extends Controller $this->sortQuery($rangeModels); /** Custom pagination */ - $rangeModels = $this->customPaginate($rangeModels, $request->pagination); + $rangeModels = $this->paginate($rangeModels); return view('back::pages.ranges.index', compact('rangeModels', 'search', 'searchFields')); } diff --git a/app/Http/Controllers/Bo/StaticPageController.php b/app/Http/Controllers/Bo/StaticPageController.php new file mode 100644 index 0000000..b177201 --- /dev/null +++ b/app/Http/Controllers/Bo/StaticPageController.php @@ -0,0 +1,91 @@ +authorizeResource(StaticPage::class); + } + + /** + * Display a listing of the resource. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Contracts\View\View + */ + public function index(Request $request): \Illuminate\Contracts\View\View + { + /** @var \Illuminate\Database\Eloquent\Builder $query */ + $query = StaticPage::query(); + + /** @var string $search Search field */ + $search = $request->search; + if ($search) { + $this->searchQuery( + $query, + $search, + null, + 'title', + ); + } + $searchFields = \implode(', ', [ + trans('validation.attributes.title'), + ]); + + /** Sort columns with a query */ + $this->sortQuery($query); + + /** Custom pagination */ + $staticPageModelList = $this->paginate($query); + + return view('back::pages.static_pages.index', compact('staticPageModelList', 'search', 'searchFields')); + } + + /** + * Show the form for editing the specified resource. + * + * @param App\Models\StaticPage $static_page + * @return \Illuminate\Contracts\View\View + */ + public function edit(StaticPage $static_page): \Illuminate\Contracts\View\View + { + return view('back::pages.static_pages.edit', ['staticPageModel' => $static_page]); + } + + /** + * Update the specified resource in storage. + * + * @param \App\Http\Requests\Bo\StaticPages\UpdateStaticPageRequest $request + * @param \App\Models\StaticPage $static_page + * @return \Illuminate\Http\RedirectResponse + */ + public function update(UpdateStaticPageRequest $request, StaticPage $static_page): \Illuminate\Http\RedirectResponse + { + return DB::transaction(function () use ($request, $static_page) { + $static_page->fill($request->validated()); + + if ($static_page->saveOrFail()) { + return redirect()->route('bo.static_pages.edit', $static_page) + ->with('success', __('crud.messages.has_been_updated', [ + 'model' => Str::ucfirst(\trans_choice('models.classes.static_page', 1)) + ])); + } + return redirect()->route('bo.static_pages.edit', $static_page) + ->with('error', __('crud.messages.cannot_be_updated', [ + 'model' => Str::ucfirst(\trans_choice('models.classes.static_page', 1)) + ])); + }); + } +} diff --git a/app/Http/Controllers/Bo/UserController.php b/app/Http/Controllers/Bo/UserController.php new file mode 100644 index 0000000..094cd54 --- /dev/null +++ b/app/Http/Controllers/Bo/UserController.php @@ -0,0 +1,169 @@ +authorizeResource(User::class); + } + + /** + * Display a listing of the resource. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Contracts\View\View + */ + public function index(Request $request): \Illuminate\Contracts\View\View + { + /** @var \Illuminate\Database\Eloquent\Builder $userModels */ + $userModels = User::query(); + + /** @var string $search Search field */ + $search = $request->search; + if ($search) { + $this->searchQuery( + $userModels, + $search, + null, + 'first_name', + 'last_name', + 'email', + 'role', + ); + } + $searchFields = \implode(', ', [ + trans('validation.attributes.first_name'), + trans('validation.attributes.last_name'), + trans('validation.attributes.email'), + trans('validation.attributes.role'), + ]); + + /** Sort columns with a query */ + $this->sortQuery($userModels); + + /** Custom pagination */ + $userModels = $this->paginate($userModels); + + return view('back::pages.users.index', compact('userModels', 'search', 'searchFields')); + } + + + /** + * Show the form for creating a new resource. + * + * @param \App\Models\User $user + * @return \Illuminate\Contracts\View\View + */ + public function create(User $user): \Illuminate\Contracts\View\View + { + return view('back::pages.users.create', ['userModel' => $user]); + } + + /** + * Store a newly created resource in storage. + * + * @param \App\Http\Requests\Bo\Users\StoreUserRequest $request + * @return \Illuminate\Http\RedirectResponse + */ + public function store(StoreUserRequest $request): \Illuminate\Http\RedirectResponse + { + return DB::transaction(function () use ($request) { + $user = new User(); + $user->fill($request->validated()); + + if ($user->saveOrFail()) { + return redirect()->route('bo.users.edit', $user) + ->with('success', __('crud.messages.has_been_created', [ + 'model' => Str::ucfirst(__('models.classes.user')) + ])); + } + return redirect()->back() + ->with('error', __('crud.messages.cannot_be_created', [ + 'model' => Str::ucfirst(__('models.classes.user')) + ])); + }); + } + + /** + * Show the form for editing the specified resource. + * + * @param App\Models\User $user + * @return \Illuminate\Contracts\View\View + */ + public function edit(User $user): \Illuminate\Contracts\View\View + { + return view('back::pages.users.edit', ['userModel' => $user]); + } + + /** + * 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): \Illuminate\Http\RedirectResponse + { + return DB::transaction(function () use ($request, $user) { + $user->fill($request->validated()); + + if ($user->saveOrFail()) { + return redirect()->route('bo.users.edit', $user) + ->with('success', __('crud.messages.has_been_updated', [ + 'model' => Str::ucfirst(__('models.classes.user')) + ])); + } + return redirect()->route('bo.users.edit', $user) + ->with('error', __('crud.messages.cannot_be_updated', [ + 'model' => Str::ucfirst(__('models.classes.user')) + ])); + }); + } + + /** + * Remove the specified resource from storage. + * + * @param \App\Models\User $user + * @return \Illuminate\Http\RedirectResponse + */ + public function destroy(User $user): \Illuminate\Http\RedirectResponse + { + if ($user->deleteOrFail()) { + return redirect()->route('bo.users.index') + ->with('success', __('crud.messages.has_been_deleted', [ + 'model' => Str::ucfirst(__('models.classes.user')) + ])); + } + return redirect()->back() + ->with('error', __('crud.messages.cannot_be_deleted', [ + 'model' => Str::ucfirst(__('models.classes.user')) + ])); + } + + /** + * Duplicate the specified resource. + * + * @param \App\Models\User $user + * @return \Illuminate\Contracts\View\View + */ + public function duplicate(User $user): \Illuminate\Contracts\View\View + { + return $this->create($user->replicate()); + } +} diff --git a/app/Http/Controllers/Bo/WinnersListController.php b/app/Http/Controllers/Bo/WinnersListController.php index 3692472..6af2242 100644 --- a/app/Http/Controllers/Bo/WinnersListController.php +++ b/app/Http/Controllers/Bo/WinnersListController.php @@ -24,7 +24,7 @@ class WinnersListController extends Controller $this->sortQuery($participationModels); /** Custom pagination */ - $participationModels = $this->customPaginate($participationModels, $request->pagination); + $participationModels = $this->paginate($participationModels); $settings = Settings::select('use_ean_codes', 'winning_proof')->first(); diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 8449041..768c25f 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -2,20 +2,22 @@ namespace App\Http\Controllers; +use App\Enums\Pagination\ItemsPerPaginationEnum; +use App\Enums\Theme\BootstrapThemeEnum; +use App\Lib\Helpers\ToolboxHelper; use Illuminate\Database\Eloquent\Builder; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; -use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; -use Illuminate\Pagination\LengthAwarePaginator; +use Illuminate\Http\Request; use Illuminate\Routing\Controller as BaseController; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Session; +use Illuminate\Support\Str; class Controller extends BaseController { use AuthorizesRequests; - use DispatchesJobs; use ValidatesRequests; /** @@ -35,7 +37,7 @@ class Controller extends BaseController string $search, callable $searchQuery = null, string ...$fields - ): Builder { + ): \Illuminate\Database\Eloquent\Builder { if (count($fields)) { // Search using words. $words = explode(' ', $search); @@ -64,36 +66,85 @@ class Controller extends BaseController * @param array $fields * @return \Illuminate\Database\Eloquent\Builder */ - private function searchOnFields(Builder $query, string $search, array $fields): Builder - { + private function searchOnFields( + Builder $query, + string $search, + array $fields + ): \Illuminate\Database\Eloquent\Builder { $table = $query->getModel()->getTable(); - foreach ($fields as $f) { - $f = str_replace('?', '\?', $f); - if (is_array($f)) { + foreach ($fields as $field) { + $field = str_replace('?', '\?', $field); + if (is_array($field)) { $query->orWhereRaw( - sprintf("UPPER(CONCAT(%s)) LIKE UPPER(?)", collect($f)->map(function ($q) use ($table) { + sprintf("UPPER(CONCAT(%s)) LIKE UPPER(?)", collect($field)->map(function ($q) use ($table) { return "`$table`.`$q`"; })->implode(', \' \', ')), - ['%' . filter_var($search, FILTER_SANITIZE_STRING) . '%'] - ); - } else { - $query->orWhereRaw( - "UPPER(`$table`.`$f`) LIKE UPPER(?)", - ['%' . filter_var($search, FILTER_SANITIZE_STRING) . '%'] + ['%' . htmlspecialchars($search) . '%'] ); + return $query; } - } + // Get models woth enum's label. + if (is_array($this->getSearchValue($query, $search, $field))) { + foreach ($this->getSearchValue($query, $search, $field) as $array) { + $query->orWhereRaw( + "UPPER(`$table`.`$field`) LIKE UPPER(?)", + ['%' . htmlspecialchars($array->value) . '%'] + ); + } + return $query; + } + // Get models with relation's name/label. + if (Str::endsWith($field, '_id') && $query->getModel()->has(Str::remove('_id', $field))) { + $query->orWhereHas(Str::remove('_id', $field), function ($queryRelation) use ($field, $search) { + if (Schema::hasColumn(Str::remove('_id', $field) . 's', 'name')) { + $queryRelation->where('name', 'like', '%' . $search . '%'); + } elseif (Schema::hasColumn(Str::remove('_id', $field) . 's', 'label')) { + $queryRelation->where('label', 'like', '%' . $search . '%'); + } + }); + return $query; + } + $query->orWhereRaw( + "UPPER(`$table`.`$field`) LIKE UPPER(?)", + ['%' . htmlspecialchars($this->getSearchValue($query, $search, $field)) . '%'] + ); + } //end foreach return $query; } /** - * Sort columns with a query + * Get the search value (on string or Enum). + * + * @param \Illuminate\Database\Eloquent\Builder $query + * @param string $search + * @param string $field + * @return string|array + */ + protected function getSearchValue(Builder $query, string $search, string $field): string|array + { + if ( + count($query->getModel()->getCasts()) and // Check if the model has casts. + isset($query->getModel()->getCasts()[$field]) and // Check if there is cast about specific field. + enum_exists($query->getModel()->getCasts()[$field]) // Check if the cast is an enum. + ) { + $cases = collect($query->getModel()->getCasts()[$field]::toArray()); + $enum = $cases->filter(function ($item) use ($search) { + return preg_match('/' . Str::lower($search) . '/', Str::lower($item->label)); + }); + return $enum->toArray(); + } else { + return $search; + } + } + + /** + * Sort columns with a query. * * @param \Illuminate\Database\Eloquent\Builder $query * @param array $ignore * @return \Illuminate\Database\Eloquent\Builder */ - protected function sortQuery(Builder $query, array $ignore = []): Builder + protected function sortQuery(Builder $query, array $ignore = []): \Illuminate\Database\Eloquent\Builder { $rName = request()->route()->getName(); $table = $query->getModel()->getTable(); @@ -121,6 +172,14 @@ class Controller extends BaseController $query = $query->orderBy('order', 'ASC'); Session::put("$rName.sort_col", 'order'); Session::put("$rName.sort_way", 'asc'); + } elseif (Schema::hasColumn($table, 'updated_at')) { + $query = $query->orderBy('updated_at', 'DESC'); + Session::put("$rName.sort_col", 'updated_at'); + Session::put("$rName.sort_way", 'desc'); + } elseif (Schema::hasColumn($table, 'created_at')) { + $query = $query->orderBy('created_at', 'DESC'); + Session::put("$rName.sort_col", 'created_at'); + Session::put("$rName.sort_way", 'desc'); } return $query; } @@ -128,18 +187,45 @@ class Controller extends BaseController /** * Customize pagination with cache or config (default). * - * @param \Illuminate\Database\Eloquent\Builder $query - * @param integer|null $pagination - * @return \Illuminate\Pagination\LengthAwarePaginator + * @param \Illuminate\Database\Eloquent\Builder $query + * @param \App\Enums\Pagination\ItemsPerPaginationEnum|null $pagination + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator */ - protected function customPaginate(Builder $query, $pagination): LengthAwarePaginator - { - if ($pagination) { - Cache::put('pagination', $pagination); - }; - $query = $query->paginate( - (Cache::get('pagination')) ? intval(Cache::get('pagination')) : config('pagination.default') + protected function paginate( + Builder $query, + ItemsPerPaginationEnum $pagination = null + ): \Illuminate\Contracts\Pagination\LengthAwarePaginator { + $currentRoutePath = Str::slug(request()->route()->getName()); + $cacheKey = "pagination.{$currentRoutePath}"; + $pagination = $pagination ?? ItemsPerPaginationEnum::twelve; + $pagination = ToolboxHelper::getValidatedEnum( + Cache::get($cacheKey, $pagination->value()), + 'pagination', + '\App\Enums\Pagination\ItemsPerPaginationEnum', ); + if (Cache::get($cacheKey) !== $pagination) { + Cache::put($cacheKey, $pagination); + } + $query = $query->paginate($pagination); return $query; } + + /** + * Set the bootstrap theme or light (default). + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\RedirectResponse + */ + protected function setTheme(Request $request): \Illuminate\Http\RedirectResponse + { + $theme = ToolboxHelper::getValidatedEnum( + $request->theme ?: Cache::get('theme', BootstrapThemeEnum::light->value()), + 'theme', + '\App\Http\Controllers\BootstrapThemeEnum', + ); + if (Cache::get("theme") !== $theme) { + Cache::put("theme", $theme); + } + return redirect()->back()->with('success', __('crud.messages.theme_updated')); + } } diff --git a/app/Http/Controllers/Fo/EndPageController.php b/app/Http/Controllers/Fo/EndPageController.php index 28a5ce6..315ee38 100644 --- a/app/Http/Controllers/Fo/EndPageController.php +++ b/app/Http/Controllers/Fo/EndPageController.php @@ -24,6 +24,6 @@ class EndPageController extends Controller ) { return view('theme::fo.end', compact('settings')); } - return redirect()->route('fo.home'); + return redirect()->route('fo.homepage'); } } diff --git a/app/Http/Controllers/Fo/EnterCodeController.php b/app/Http/Controllers/Fo/EnterCodeController.php index e8bf721..f61132e 100644 --- a/app/Http/Controllers/Fo/EnterCodeController.php +++ b/app/Http/Controllers/Fo/EnterCodeController.php @@ -169,7 +169,7 @@ class EnterCodeController extends Controller $arcadiaMail = $this->apiCrm->ArcadiaOutgoingMail($crmId, $fields, 'game'); if ($arcadiaMail->status === 'KO') { - Mail::to(config('mail.MAIL_ADMIN'))->send(new CourrierArcadiaError(auth()->user())); + Mail::to(config('mail.admin'))->send(new CourrierArcadiaError(auth()->user())); } } //end if @@ -260,7 +260,7 @@ class EnterCodeController extends Controller $participation->reduction_id = $reduction->id; $participation->save(); } else { - Mail::to(config('mail.MAIL_ADMIN')) + Mail::to(config('mail.admin')) ->send(new NotifAdmin(\sprintf( 'MESBR : Une erreur est survenue. Code : %s Message : %s', $result["code"], @@ -287,6 +287,6 @@ class EnterCodeController extends Controller if ($participation) { return view('theme::fo.winning-proof', compact('participation')); } - return redirect()->route('fo.home'); + return redirect()->route('fo.homepage'); } } diff --git a/app/Http/Controllers/Fo/FAQController.php b/app/Http/Controllers/Fo/FAQController.php index 35d1e27..4f7e0ba 100644 --- a/app/Http/Controllers/Fo/FAQController.php +++ b/app/Http/Controllers/Fo/FAQController.php @@ -18,7 +18,7 @@ class FAQController extends Controller $questions = FaqQuestion::all(); $themes = FaqTheme::all(); if (count($questions) === 0 && count($themes) === 0) { - return redirect()->route('fo.home'); + return redirect()->route('fo.homepage'); } return view('theme::fo.faq', compact('questions', 'themes')); } diff --git a/app/Http/Controllers/Fo/GainsController.php b/app/Http/Controllers/Fo/GainsController.php index 8b8ddac..e20a2b5 100644 --- a/app/Http/Controllers/Fo/GainsController.php +++ b/app/Http/Controllers/Fo/GainsController.php @@ -16,6 +16,6 @@ class GainsController extends Controller if (view()->exists('theme::fo.gains')) { return view('theme::fo.gains'); } - return redirect()->route('fo.home'); + return redirect()->route('fo.homepage'); } } diff --git a/app/Http/Controllers/Fo/HowToPlayController.php b/app/Http/Controllers/Fo/HowToPlayController.php index 4cb2fe0..9e609f8 100644 --- a/app/Http/Controllers/Fo/HowToPlayController.php +++ b/app/Http/Controllers/Fo/HowToPlayController.php @@ -16,6 +16,6 @@ class HowToPlayController extends Controller if (view()->exists('theme::fo.basic.how-to-play')) { return view('theme::fo.basic.how-to-play'); } - return redirect()->route('fo.home'); + return redirect()->route('fo.homepage'); } } diff --git a/app/Http/Controllers/Fo/LoginController.php b/app/Http/Controllers/Fo/LoginController.php index 37b0f05..e9e49e2 100644 --- a/app/Http/Controllers/Fo/LoginController.php +++ b/app/Http/Controllers/Fo/LoginController.php @@ -88,8 +88,8 @@ class LoginController extends Controller /** @var \App\Models\Customer $userLocal */ $userLocal = Customer::create([ 'crm_id' => $connect->user->crm_id, - 'firstname' => $connect->user->firstname, - 'lastname' => $connect->user->lastname, + 'firstname' => $connect->user->first_name, + 'lastname' => $connect->user->last_name, 'birth_date' => $connect->user->birth_date, 'email' => $email, 'password' => Hash::make($password), @@ -106,8 +106,8 @@ class LoginController extends Controller /** @var \App\Models\Customer $userLocal */ $userLocal = Customer::create([ 'crm_id' => $connect->user->crm_id, - 'firstname' => $connect->user->firstname, - 'lastname' => $connect->user->lastname, + 'firstname' => $connect->user->first_name, + 'lastname' => $connect->user->last_name, 'birth_date' => $connect->user->birth_date, 'email' => $email, 'password' => Hash::make($password), @@ -149,7 +149,7 @@ class LoginController extends Controller public function logout() { Auth::logout(); - return redirect()->route('fo.home'); + return redirect()->route('fo.homepage'); } /** diff --git a/app/Http/Controllers/Fo/TeaserController.php b/app/Http/Controllers/Fo/TeaserController.php index 523cb02..955d465 100644 --- a/app/Http/Controllers/Fo/TeaserController.php +++ b/app/Http/Controllers/Fo/TeaserController.php @@ -19,6 +19,6 @@ class TeaserController extends Controller if ($settings->activated_teaser === 1 && $settings->teaser_end_activity > now()) { return view('theme::fo.teaser', compact('settings')); } - return redirect()->route('fo.home'); + return redirect()->route('fo.homepage'); } } diff --git a/app/Http/Controllers/Fo/ViewContentController.php b/app/Http/Controllers/Fo/ViewContentController.php index b574dfe..cbf73fa 100644 --- a/app/Http/Controllers/Fo/ViewContentController.php +++ b/app/Http/Controllers/Fo/ViewContentController.php @@ -14,7 +14,7 @@ class ViewContentController extends Controller */ public function reglement() { - $content = Content::find(3); + $content = Content::query()->find(3); return view('theme::fo.reglement', compact('content')); } diff --git a/app/Http/Controllers/Fo/WinningProofController.php b/app/Http/Controllers/Fo/WinningProofController.php index 33b099b..92401a7 100644 --- a/app/Http/Controllers/Fo/WinningProofController.php +++ b/app/Http/Controllers/Fo/WinningProofController.php @@ -44,7 +44,7 @@ class WinningProofController extends Controller ['barcode', null] ])->has('instantWin')->first(); if (!$participation) { - return redirect()->route('fo.home'); + return redirect()->route('fo.homepage'); } $request->validate([ @@ -102,7 +102,7 @@ class WinningProofController extends Controller if ($update->status !== 'OK') { Log::warning('Erreur lors de la mise a jour du user (crm_id : ' . $user->crm_id . ') - code : ' . $update->code . ' - message : ' . $update->message); - Mail::to(config('mail.MAIL_ADMIN'))->send(new NotifAdmin( + Mail::to(config('mail.admin'))->send(new NotifAdmin( 'CRM : Erreur lors de la mise a jour du user (crm_id : ' . $user->crm_id . ') - code : ' . $update->code . ' - message : ' . $update->message )); diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index a2ab0d0..1b83325 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -37,7 +37,7 @@ class Kernel extends HttpKernel \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, - \App\Http\Middleware\SecureHeaders::class, + \App\Http\Middleware\SecureHeaders::class ], 'api' => [ @@ -57,16 +57,15 @@ class Kernel extends HttpKernel 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class, - 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, + 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, 'game-date' => \App\Http\Middleware\GameCheckDate::class, 'is-admin' => \App\Http\Middleware\IsAdmin::class, 'is-super-admin' => \App\Http\Middleware\IsSuperAdmin::class, 're7' => \App\Http\Middleware\Re7Middleware::class, - 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, - 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, + 'signed' => \App\Http\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, 'htmlsanitize' => \App\Http\Middleware\HtmlSanitizer::class, diff --git a/app/Http/Middleware/IsAdmin.php b/app/Http/Middleware/IsAdmin.php index 8bece33..7f43034 100644 --- a/app/Http/Middleware/IsAdmin.php +++ b/app/Http/Middleware/IsAdmin.php @@ -2,9 +2,9 @@ namespace App\Http\Middleware; +use App\Enums\Users\RoleEnum; use Closure; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Auth; class IsAdmin { @@ -17,10 +17,18 @@ class IsAdmin */ public function handle(Request $request, Closure $next) { - $role = Auth::user()->role; - if ($role === 1 || $role === 2) { - return $next($request); + /** @var \App\Models\User|null */ + $user = auth('backend')->user(); + + if (!$user) { + abort(403, trans('Vous devez être authentifé')); } - abort(404, "Cette page n'existe pas."); + + if ($user->role->value() > RoleEnum::admin->value()) { + return response()->redirectToRoute('bo.home') + ->with('warning', trans('Vous n\'avez pas les droits nécessaires pour accéder à cet élément')); + } + + return $next($request); } } diff --git a/app/Http/Middleware/IsSuperAdmin.php b/app/Http/Middleware/IsSuperAdmin.php index 1c252b2..1bb4de7 100644 --- a/app/Http/Middleware/IsSuperAdmin.php +++ b/app/Http/Middleware/IsSuperAdmin.php @@ -2,9 +2,9 @@ namespace App\Http\Middleware; +use App\Enums\Users\RoleEnum; use Closure; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Auth; class IsSuperAdmin { @@ -17,10 +17,18 @@ class IsSuperAdmin */ public function handle(Request $request, Closure $next) { - $role = Auth::user()->role; - if ($role === 1) { - return $next($request); + /** @var \App\Models\User|null */ + $user = auth('backend')->user(); + + if (!$user) { + abort(403, trans('Vous devez être authentifé')); } - return redirect()->route('bo.dashboard'); + + if ($user->role->value() > RoleEnum::conceptor->value()) { + return response()->redirectToRoute('bo.home') + ->with('warning', trans('Vous n\'avez pas les droits nécessaires pour accéder à cet élément')); + } + + return $next($request); } } diff --git a/app/Http/Middleware/Re7Middleware.php b/app/Http/Middleware/Re7Middleware.php index 8b67215..137b959 100644 --- a/app/Http/Middleware/Re7Middleware.php +++ b/app/Http/Middleware/Re7Middleware.php @@ -17,7 +17,7 @@ class Re7Middleware public function handle(Request $request, Closure $next) { if (config('app.env') === 'production') { - return redirect()->route('fo.home'); + return redirect()->route('fo.homepage'); } return $next($request); } diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php index 393cabe..2c78eaf 100644 --- a/app/Http/Middleware/RedirectIfAuthenticated.php +++ b/app/Http/Middleware/RedirectIfAuthenticated.php @@ -3,7 +3,6 @@ namespace App\Http\Middleware; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Auth; use Symfony\Component\HttpFoundation\Response; class RedirectIfAuthenticated @@ -24,10 +23,10 @@ class RedirectIfAuthenticated $guards = empty($guards) ? [null] : $guards; foreach ($guards as $guard) { - if (Auth::guard($guard)->check()) { + if (auth($guard)->check()) { switch ($guard) { case 'frontend': - return \redirect()->route('fo.home'); + return \redirect()->route('fo.homepage'); case 'backend': if (!$request->routeIs('bo.*')) { continue; diff --git a/app/Http/Middleware/TrustProxies.php b/app/Http/Middleware/TrustProxies.php index d727add..78de7a0 100644 --- a/app/Http/Middleware/TrustProxies.php +++ b/app/Http/Middleware/TrustProxies.php @@ -20,7 +20,7 @@ class TrustProxies extends Middleware * @var integer */ protected $headers = - Request::HEADER_X_FORWARDED_FOR | + Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO | diff --git a/app/Http/Requests/CodeSearchRequest.php b/app/Http/Requests/Bo/CodeSearchRequest.php similarity index 69% rename from app/Http/Requests/CodeSearchRequest.php rename to app/Http/Requests/Bo/CodeSearchRequest.php index 2ece3fb..06b8b42 100644 --- a/app/Http/Requests/CodeSearchRequest.php +++ b/app/Http/Requests/Bo/CodeSearchRequest.php @@ -1,7 +1,8 @@ check() and - auth()->user()->role === 1; + // ? Is auth and super admin + return auth('backend')->check() and + auth('backend')->user()->role === RoleEnum::conceptor; } /** diff --git a/app/Http/Requests/Bo/Contents/UpdateContentRequest.php b/app/Http/Requests/Bo/Contents/UpdateContentRequest.php deleted file mode 100644 index 23aa3e4..0000000 --- a/app/Http/Requests/Bo/Contents/UpdateContentRequest.php +++ /dev/null @@ -1,42 +0,0 @@ -check(); - } - - /** - * Get the validation rules that apply to the request. - * - * @return array - */ - public function rules() - { - return [ - 'value' => 'required|string|min:15', - ]; - } - - /** - * Get custom attributes for validator errors. - * - * @return array - */ - public function attributes(): array - { - return [ - 'value' => trans('Texte'), - ]; - } -} diff --git a/app/Http/Requests/ExportCodesRequest.php b/app/Http/Requests/Bo/ExportCodesRequest.php similarity index 79% rename from app/Http/Requests/ExportCodesRequest.php rename to app/Http/Requests/Bo/ExportCodesRequest.php index c44d93b..4221f83 100644 --- a/app/Http/Requests/ExportCodesRequest.php +++ b/app/Http/Requests/Bo/ExportCodesRequest.php @@ -1,7 +1,8 @@ where('code_exported', false); + $lastExportedId = Code::query()->where('code_exported', true) ->orderBy('id', 'desc')->first()->id ?? null; if ($lastExportedId) { $codesAvailableToExport = $codesAvailableToExport @@ -26,8 +27,10 @@ class ExportCodesRequest extends FormRequest 'available_export' => $codesAvailableToExport->count() ]); // ? Is auth and super admin, also is json ? - return auth()->check() and - auth()->user()->role === 1 and $this->json(); + return auth('backend')->check() and + // ? Is auth and super admin + auth('backend')->user()->role === RoleEnum::conceptor + and $this->json(); } /** diff --git a/app/Http/Requests/GenerateCodesRequest.php b/app/Http/Requests/Bo/GenerateCodesRequest.php similarity index 76% rename from app/Http/Requests/GenerateCodesRequest.php rename to app/Http/Requests/Bo/GenerateCodesRequest.php index 9677399..1ec2a58 100644 --- a/app/Http/Requests/GenerateCodesRequest.php +++ b/app/Http/Requests/Bo/GenerateCodesRequest.php @@ -1,7 +1,8 @@ check() and auth()->user()->role === 1; + return auth('backend')->check() and + auth('backend')->user()->role === RoleEnum::conceptor; } /** diff --git a/app/Http/Requests/Bo/StaticPages/UpdateStaticPageRequest.php b/app/Http/Requests/Bo/StaticPages/UpdateStaticPageRequest.php new file mode 100644 index 0000000..238721f --- /dev/null +++ b/app/Http/Requests/Bo/StaticPages/UpdateStaticPageRequest.php @@ -0,0 +1,46 @@ +check(); + } + + /** + * Prepare the data for validation. + * + * @return void + */ + public function prepareForValidation(): void + { + $this->merge([ + 'content' => (string)Str::of($this->content) + ]); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array|string> + */ + public function rules(): array + { + return [ + 'seo_title' => 'required|min:45|max:70', + 'seo_description' => 'required|min:50|max:160', + 'title' => 'required|min:3|max:255', + 'content' => 'present', + ]; + } +} diff --git a/app/Http/Requests/Bo/Users/StoreUserRequest.php b/app/Http/Requests/Bo/Users/StoreUserRequest.php new file mode 100644 index 0000000..ffa3d18 --- /dev/null +++ b/app/Http/Requests/Bo/Users/StoreUserRequest.php @@ -0,0 +1,74 @@ +check(); + } + + /** + * Prepare the data for validation. + * + * @return void + */ + protected function prepareForValidation() + { + $this->merge([ + 'published' => $this->published ? true : false + ]); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules(): array + { + return [ + 'first_name' => 'required|string|min:3|max:255', + 'last_name' => 'required|string|min:3|max:255', + 'email' => 'required|email:rfc,strict,dns,spoof,filter|unique:users,email', + 'password' => [ + 'required', 'required_with:password_confirmation', 'confirmed', 'max:255', Password::min(8) + ->letters() + ->mixedCase() + ->numbers() + ->symbols() + ->uncompromised() + ], + 'role' => ['required', new Enum(RoleEnum::class)], + 'published' => 'required|boolean', + ]; + } + + /** + * 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'), + 'password' => trans('validation.attributes.password'), + 'role' => trans('validation.attributes.role'), + 'published' => trans('validation.custom.publication') + ]; + } +} diff --git a/app/Http/Requests/Bo/Users/UpdateUserRequest.php b/app/Http/Requests/Bo/Users/UpdateUserRequest.php new file mode 100644 index 0000000..5dede0b --- /dev/null +++ b/app/Http/Requests/Bo/Users/UpdateUserRequest.php @@ -0,0 +1,48 @@ +merge([ + 'published' => $this->published ? true : false, + 'password' => $this->password ? $this->password : $this->route('user')->password, + 'password_confirmation' => $this->password_confirmation ? + $this->password_confirmation : + $this->route('user')->password + ]); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules(): array + { + return array_merge(parent::rules(), [ + 'email' => [ + 'required', + 'email:rfc,strict,dns,spoof,filter', + 'unique:users,email,' . $this->route('user')->id . ',id', + ], + 'password' => [ + 'required', 'required_with:password_confirmation', 'confirmed', 'max:255', Password::min(8) + ->letters() + ->mixedCase() + ->numbers() + ->symbols() + ->uncompromised() + ], + ]); + } +} diff --git a/app/Traits/Controllers/.gitkeep b/app/Http/Requests/Fo/.gitkeep similarity index 100% rename from app/Traits/Controllers/.gitkeep rename to app/Http/Requests/Fo/.gitkeep diff --git a/app/Jobs/ProcessExportCodesUniques.php b/app/Jobs/ProcessExportCodesUniques.php index 3e07314..fad9472 100644 --- a/app/Jobs/ProcessExportCodesUniques.php +++ b/app/Jobs/ProcessExportCodesUniques.php @@ -7,6 +7,7 @@ use App\Mail\JobFailed; use App\Models\Code; use Exception; use Illuminate\Bus\Queueable; +use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; @@ -14,7 +15,7 @@ use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\Mail; -class ProcessExportCodesUniques implements ShouldQueue +class ProcessExportCodesUniques implements ShouldQueue, ShouldBeUnique { use Dispatchable; use InteractsWithQueue; @@ -30,33 +31,12 @@ class ProcessExportCodesUniques implements ShouldQueue */ public $timeout = 7200; - /** - * Nombre de codes par fichiers. - * - * @var integer - */ - private $nbCodeFic; - - /** - * Nombre total de codes à exporter. - * - * @var integer - */ - private $nbCodeTot; - - /** - * Nombre de codes par ligne. - * - * @var integer - */ - private $nbCodeLigne; - /** * The export file name * * @var string */ - private $exportFileName = null; + protected $exportFileName = null; /** * Create a new job instance. @@ -66,11 +46,11 @@ class ProcessExportCodesUniques implements ShouldQueue * @param integer $nbCodeLigne Nombre de codes par ligne. * @return void */ - public function __construct(int $nbCodeFic, int $nbCodeTot, int $nbCodeLigne) - { - $this->nbCodeFic = $nbCodeFic; - $this->nbCodeTot = $nbCodeTot; - $this->nbCodeLigne = $nbCodeLigne; + public function __construct( + protected int $nbCodeFic, + protected int $nbCodeTot, + protected int $nbCodeLigne + ) { } /** @@ -86,7 +66,8 @@ class ProcessExportCodesUniques implements ShouldQueue date('dmY_His') ); - $lastExportedCodeId = Code::whereCodeExported(true) + $lastExportedCodeId = Code::query() + ->where('code_exported', true) ->orderBy('id', 'desc')->first()->id ?? null; $filesPath = $this->generateCsvFiles($lastExportedCodeId); @@ -115,12 +96,12 @@ class ProcessExportCodesUniques implements ShouldQueue } // * Envoi de mail à MAIL_ADMIN avec le nom du ZIP "CodesUniques_". date('dmY_His') .".zip". - Mail::to(config('mail.MAIL_ADMIN')) - ->send(new ExportCodesUniques( - $this->nbCodeFic, - $this->nbCodeTot, - $this->nbCodeLigne - )); + Mail::to(config('mail.admin')) + ->send(new ExportCodesUniques( + $this->nbCodeFic, + $this->nbCodeTot, + $this->nbCodeLigne + )); } /** @@ -185,7 +166,7 @@ class ProcessExportCodesUniques implements ShouldQueue } // ? Have we reached pagination limit for mass saving ? if (count($exportedCodesId) === $paginate) { - Code::whereIn('id', $exportedCodesId)->update(['code_exported' => 1]); + Code::query()->whereIn('id', $exportedCodesId)->update(['code_exported' => true]); $exportedCodesId = []; } } //end if @@ -193,7 +174,7 @@ class ProcessExportCodesUniques implements ShouldQueue // ? Are there exported codes to save ? if (count($exportedCodesId)) { - Code::whereIn('id', $exportedCodesId)->update(['code_exported' => 1]); + Code::query()->whereIn('id', $exportedCodesId)->update(['code_exported' => true]); } return $outPaths; } @@ -206,10 +187,10 @@ class ProcessExportCodesUniques implements ShouldQueue */ public function failed(Exception $exception) { - Mail::to(config('mail.MAIL_ADMIN')) - ->send(new JobFailed("ProcessExportCodeUnique", [ - 'message' => $exception->getMessage(), - 'trace' => $exception->getTraceAsString() - ])); + Mail::to(config('mail.admin')) + ->send(new JobFailed("ProcessExportCodeUnique", [ + 'message' => $exception->getMessage(), + 'trace' => $exception->getTraceAsString() + ])); } } diff --git a/app/Jobs/ProcessUniqueCode.php b/app/Jobs/ProcessUniqueCode.php index e8c99d0..c0e9a97 100644 --- a/app/Jobs/ProcessUniqueCode.php +++ b/app/Jobs/ProcessUniqueCode.php @@ -6,16 +6,17 @@ use App\Mail\CodeGenerationEnd; use App\Mail\JobFailed; use App\Models\Code; use App\Models\Settings; -use Exception; use Illuminate\Bus\Queueable; +use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Mail; -class ProcessUniqueCode implements ShouldQueue +class ProcessUniqueCode implements ShouldQueue, ShouldBeUnique { use Dispatchable; use InteractsWithQueue; @@ -31,101 +32,104 @@ class ProcessUniqueCode implements ShouldQueue */ public $timeout = 7200; - /** - * Quantity of code to generate - * - * @var integer - */ - private $quantity; - - /** - * Length of code to generate - * - * @var integer - */ - private $length; - /** * Create a new job instance. * - * @param integer $quantity - * @param integer $length + * @param integer $amount Amount of code to generate. + * @param integer $length Length of each code to generate. * @return void */ - public function __construct(int $quantity, int $length) + public function __construct(protected int $amount, protected int $length) { - $this->quantity = $quantity; - $this->length = $length; } /** * Execute the job. - * TODO: Mettre tout ce bordel dans une transaction quand les serveur de leurs morts auront été mis à jour Uu. * * @return void */ public function handle() { - /** @var \App\Models\Settings $settings */ - $settings = app(Settings::class); + DB::transaction(function () { + // ! Le premier code commence à 1 + $last_id = (Code::query()->max('id') ?? 0) + 1; - $maxSQLPlaceholders = 65535; + /** @var \App\Models\Settings $settings */ + $settings = app(Settings::class); - $insertColumns = collect(DB::getSchemaBuilder() - ->getColumnListing((new Code())->getTable())) - ->filter(function ($name) { - // * Ignorer les colonnes avec valeurs par défaut sur la table. - return !in_array($name, ['id', 'code_used', 'code_exported']); - })->count(); + $maxSQLPlaceholders = 65535; - /** - * Nombre d'insertions max sur un seule requète - * - * @var integer $perPage - */ - $perPage = $maxSQLPlaceholders / $insertColumns; + $insertColumns = collect(DB::getSchemaBuilder() + ->getColumnListing((new Code())->getTable())) + ->filter(function ($name) { + // * Ignorer les colonnes avec valeurs par défaut sur la table. + return !in_array($name, ['id', 'code_used', 'code_exported']); + })->count() + 1; - /** - * Nombre de page totale à insérer. - * - * @var integer $totalPages - */ - $totalPages = \ceil($this->quantity / $perPage); + /** + * Nombre d'insertions max sur un seule requète + * + * @var integer $perPage + */ + $perPage = \floor($maxSQLPlaceholders / $insertColumns); - $totalGeneratedInserts = 0; + /** + * Nombre de page totale à insérer. + * + * @var integer $totalPages + */ + $totalPages = \ceil($this->amount / $perPage); - for ($currentPage = 1; $currentPage <= $totalPages; $currentPage++) { - $inserts = []; - $insertToGenerate = min($perPage, $this->quantity - $totalGeneratedInserts); - do { - $inserts[] = [ - 'date_creation' => now()->format('Y-m-d'), - ]; - } while ($totalGeneratedInserts < $this->quantity and --$insertToGenerate); + $totalGeneratedInserts = 0; - // * Mass insert - Code::insert($inserts); + for ($currentPage = 1; $currentPage <= $totalPages; $currentPage++) { + $inserts = []; + $insertToGenerate = min($perPage, $this->amount - $totalGeneratedInserts); + do { + $inserts[] = [ + 'id' => $last_id++, + 'date_creation' => now()->format('Y-m-d'), + ]; + } while (($totalGeneratedInserts < $this->amount) && --$insertToGenerate); - $totalGeneratedInserts += count($inserts); - } + // * Mass insert + Code::query()->insert($inserts); - // * Fin de la génération. - Mail::to(config('mail.MAIL_ADMIN')) - ->send(new CodeGenerationEnd($this->quantity, $settings->name)); + $totalGeneratedInserts += count($inserts); + } + + // * Fin de la génération. + Mail::to(config('mail.admin')) + ->send(new CodeGenerationEnd($this->amount, $settings->name)); + }); } /** * In case of job failure * - * @param Exception $exception + * @param \Exception $exception * @return void */ - public function failed(Exception $exception) + public function failed(\Exception $exception) { - Mail::to(config('mail.MAIL_ADMIN')) - ->send(new JobFailed("ProcessUniqueCode", [ - 'message' => $exception->getMessage(), - 'trace' => $exception->getTraceAsString() - ])); + Mail::to(config('mail.admin'))->send( + new JobFailed('ProcessUniqueCode', [ + 'message' => $exception->getMessage(), + 'trace' => $exception->getTrace() + ]) + ); + } + + /** + * Get the middleware the job should pass through. + * + * @return array + */ + public function middleware(): array + { + return [ + (new WithoutOverlapping('code_generation')) + ->releaseAfter(60) + ]; } } diff --git a/app/Lib/Helpers/CollectionHelper.php b/app/Lib/Helpers/CollectionHelper.php index b9e9ecd..a665a2f 100644 --- a/app/Lib/Helpers/CollectionHelper.php +++ b/app/Lib/Helpers/CollectionHelper.php @@ -1,6 +1,6 @@ getBaseName(); diff --git a/app/Lib/Helpers/ToolboxHelper.php b/app/Lib/Helpers/ToolboxHelper.php index d6b6fff..de1c040 100644 --- a/app/Lib/Helpers/ToolboxHelper.php +++ b/app/Lib/Helpers/ToolboxHelper.php @@ -14,42 +14,13 @@ use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; +use Illuminate\Validation\Rules\Enum; use Illuminate\Validation\ValidationException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\Finder\SplFileInfo; class ToolboxHelper { - /** - * Run a terminal command and gives the output - * - * @param array|string $command The command to execute on terminal. - * @param boolean $throw Wether to throw en esception on failure or not. - * @return string|false Return false on failure only if $throw is false. - * @phpcs:disable Squiz.Commenting.FunctionComment.EmptyThrows - * @phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber - * @throws \Illuminate\Process\Exceptions\ProcessTimedOutException - * @throws \Symfony\Component\Process\Exception\LogicException - * @throws \Symfony\Component\Process\Exception\InvalidArgumentException - * @throws \Symfony\Component\Process\Exception\RuntimeException - * @throws \LogicException - * @throws \RuntimeException If the command fails. - */ - public static function runConsoleCommand(array|string $command, bool $throw = true): string | false - { - // phpcs:enable - $process = \Illuminate\Support\Facades\Process::command($command)->run(); - if ($process->failed()) { - $code = $process->exitCode(); - $ouput = $process->errorOutput(); - if ($throw) { - throw new \RuntimeException("Failed to run command exit code {$code} with output \n{$ouput}"); - } - return false; - } - return $process->output(); - } - /** * @param array $array1 * @param array $array2 @@ -104,7 +75,7 @@ class ToolboxHelper throw new \RuntimeException(); } if (\floatval($response->json('score')) < 0.8) { - $fail("Vous n'avez pas obtenu un score assez élevé lors de la validation du recaptcha"); + $fail(trans('recaptcha.not_enough_score')); } return; } @@ -117,7 +88,7 @@ class ToolboxHelper throw new \RuntimeException(); } } catch (\Exception $e) { - $fail('Erreur lors de la validation du formulaire veuillez réssayer'); + $fail(trans('recaptcha.error')); } //end try } ] @@ -546,4 +517,24 @@ class ToolboxHelper ->contains(fn (string $class_interface) => $class_interface === $interface) ); } + + /** + * Get validated pagination from request. + * + * @param integer $default + * @param string $field + * @param string $enumPath + * @return integer + */ + public static function getValidatedEnum(int $default, string $field, string $enumPath): int + { + try { + return \intval(Validator::make( + [$field => \request()->get($field)], + [$field => new Enum($enumPath)] + )->validated()[$field]); + } catch (ValidationException $e) { + return $default; + } + } } diff --git a/class/MesBR.php b/app/Lib/venvor/MesBR.php similarity index 100% rename from class/MesBR.php rename to app/Lib/venvor/MesBR.php diff --git a/app/Mail/CodeGenerationEnd.php b/app/Mail/CodeGenerationEnd.php index c472db6..f4cd179 100644 --- a/app/Mail/CodeGenerationEnd.php +++ b/app/Mail/CodeGenerationEnd.php @@ -4,6 +4,9 @@ namespace App\Mail; use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; +use Illuminate\Mail\Mailables\Address; +use Illuminate\Mail\Mailables\Content; +use Illuminate\Mail\Mailables\Envelope; use Illuminate\Queue\SerializesModels; class CodeGenerationEnd extends Mailable @@ -12,41 +15,42 @@ class CodeGenerationEnd extends Mailable use SerializesModels; /** - * Number of codes generated + * Create a new job instance. * - * @var integer - */ - private $quantity; - - /** - * The game name - * - * @var string - */ - private $gameName; - - /** - * Create a new message instance. - * - * @param integer $quantity - * @param string $gameName + * @param integer $amount Amount of code to generate. + * @param string $gameName The game name. * @return void */ - public function __construct(int $quantity, string $gameName) + public function __construct(private int $amount, private string $gameName) { - $this->quantity = $quantity; - $this->gameName = $gameName; } /** - * Build the message. + * Get the message envelope. * - * @return $this + * @return \Illuminate\Mail\Mailables\Envelope */ - public function build() + public function envelope(): \Illuminate\Mail\Mailables\Envelope { - return $this->subject('Fin de génération des codes uniques') - ->view('back::mails.generate-end-email') - ->with(['quantity' => $this->quantity, 'gameName' => $this->gameName]); + return new Envelope( + to: [new Address(\config('mail.admin'))], + subject: trans('Fin de génération des codes uniques'), + ); + } + + /** + * Get the message content definition. + * + * @return \Illuminate\Mail\Mailables\Content + */ + public function content(): \Illuminate\Mail\Mailables\Content + { + return new Content( + markdown: 'back::mails.generate-end-email', + with: [ + 'quantity' => $this->amount, + 'gameName' => $this->gameName + ], + ); } } diff --git a/app/Mail/JobFailed.php b/app/Mail/JobFailed.php index c4189c7..c686f0a 100644 --- a/app/Mail/JobFailed.php +++ b/app/Mail/JobFailed.php @@ -11,12 +11,6 @@ class JobFailed extends Mailable use Queueable; use SerializesModels; - /** @var string */ - public $job; - - /** @var array */ - public $exception; - /** * Create a new message instance. * @@ -24,11 +18,10 @@ class JobFailed extends Mailable * @param array $exception * @return void */ - public function __construct(string $job, array $exception) + public function __construct(public string $job, public array $exception) { - $this->job = $job; - $this->exception = $exception; } + /** * Build the message. * diff --git a/app/Models/ActivityLog.php b/app/Models/ActivityLog.php new file mode 100644 index 0000000..1ab7000 --- /dev/null +++ b/app/Models/ActivityLog.php @@ -0,0 +1,129 @@ + + */ + protected $fillable = [ + 'is_anonymous', + 'model_class', + 'model_id', + 'event', + 'data', + 'created_at', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'is_anonymous' => 'bool', + 'event' => ActivityLogsEventEnum::class, + 'data' => 'json', + 'created_at' => 'datetime', + ]; + + // * METHODS + + /** + * Add new activity to the activity logs list. + * + * @param \Illuminate\Database\Eloquent\Model $model + * @param \App\Enums\ActivityLogs\ActivityLogsEventEnum $eventEnum + * @return void + */ + public static function addActivity(Model $model, ActivityLogsEventEnum $eventEnum): void + { + $isAnonymous = true; + /** @var \Illuminate\Database\Eloquent\Model|null */ + $userModel = auth('backend')->user(); + if (is_object($userModel)) { + $userModel = $userModel->getKeyForSelectQuery(); + $isAnonymous = false; + } + $activity = new self(); + $activity->user_id = $userModel; + $activity->is_console = app()->runningInConsole(); + $activity->is_anonymous = $isAnonymous; + $activity->model_class = \get_class($model); + $activity->model_id = $model->getKey(); + $activity->event = $eventEnum; + $activity->data = static::getChangedColumns($activity, $model, $eventEnum); + $activity->created_at = Carbon::now(); + $activity->saveOrFail(); + } + + /** + * Get old, new columns that has changed. + * + * @param \self $activity + * @param \Illuminate\Database\Eloquent\Model $model + * @param \App\Enums\ActivityLogs\ActivityLogsEventEnum $eventEnum + * @return array|null + */ + public static function getChangedColumns(self $activity, Model $model, ActivityLogsEventEnum $eventEnum): array|null + { + $modelClassName = $activity->model_class; + /** Get type of each fields of the target model */ + $targetModel = $activity->model_class::where( + (new $modelClassName())->getRouteKeyName(), + $activity->model_id + )->first(); + if ($targetModel != null) { + // phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed + $targetModelTypes = collect($targetModel->toArray()) + ->map(function ($field, $fieldIndex) use ($targetModel) { + return Schema::getColumnType($targetModel->getTable(), $fieldIndex); + })->toArray(); + // phpcs:enable + } + /** Return old, new and type of values changed */ + return ($eventEnum === ActivityLogsEventEnum::updated) ? + array_intersect_key($targetModelTypes, $model->getChanges()) : null; + } + + // * RELATIONS + + /** + * User BelongsTo relation. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function user() + { + return $this->belongsTo(User::class); + } +} diff --git a/app/Models/Code.php b/app/Models/Code.php index deb2a58..0f646ad 100644 --- a/app/Models/Code.php +++ b/app/Models/Code.php @@ -22,6 +22,13 @@ use Kwaadpepper\Serial\SerialCaster; */ class Code extends Model { + /** + * Indicates if the IDs are auto-incrementing. + * + * @var boolean + */ + public $incrementing = false; + /** * The attributes that are mass assignable. * @@ -33,6 +40,17 @@ class Code extends Model 'date_creation' ]; + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'code_used' => 'bool', + 'code_exported' => 'bool', + 'date_creation' => 'date', + ]; + /** * Indicates if the model should be timestamped. * diff --git a/app/Models/Content.php b/app/Models/Content.php deleted file mode 100644 index 05074e7..0000000 --- a/app/Models/Content.php +++ /dev/null @@ -1,25 +0,0 @@ - + */ + protected $fillable = [ + 'seo_title', + 'seo_description', + 'title', + 'content', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'type' => StaticPageType::class, + ]; + + /** + * Perform any actions required after the model boots. + * + * @return void + */ + protected static function booted(): void + { + static::creating(function (self $model) { + // * ASSERTIONS + static::assertTitleIsUnique($model->title); + + // * CHANGES + $model->slug = Str::slug($model->title); + }); + static::updating(function (self $model) { + // * ASSERTIONS + static::assertTitleIsUnique($model->title, $model->id); + + // * CHANGES + $model->slug = Str::slug($model->title); + }); + } + + // * METHODS + + /** + * Asserts using validation that the title is unique + * + * @param string|null $title + * @param integer|null $id + * @return void + * @throws \Illuminate\Validation\ValidationException If an actu title already exists. + */ + private static function assertTitleIsUnique(string|null $title, ?int $id = null) + { + $table = (new self())->getTable(); + ToolboxHelper::assertFieldIsUnique($table, 'title', $title, $id); + ToolboxHelper::assertFieldIsUnique($table, 'slug', Str::slug($title), $id); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 8684263..91ae6f9 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -3,49 +3,50 @@ namespace App\Models; use App\Enums\Users\RoleEnum; +use App\Traits\Models\ActivityLog; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Illuminate\Support\Facades\Hash; /** - * User. + * @property string $first_name Prénom. + * @property string $last_name Nom. + * @property string $email Email. + * @property string $password Mot de passe. + * @property \App\Enums\Users\RoleEnum $role Rôle. + * @property \Illuminate\Support\Carbon $published État de publication. + * @property \Illuminate\Support\Carbon $published_at Date de mise à jour de l'état de publication. + * @property-read \Illuminate\Support\Carbon $created_at Created date. + * @property-read \Illuminate\Support\Carbon $updated_at Updated date. * - * @property string $firstname Firstname - * @property string $lastname Lastname - * @property string $email Email - * @property-read \Illuminate\Support\Carbon $email_verified_at Date of email verification. - * @property string $password Password - * @property-read string $remember_token Remember token - * @property \App\Enums\Users\RoleEnum $role Role of the user - * @property-read \Illuminate\Support\Carbon $created_at Creation Date. - * @property-read \Illuminate\Support\Carbon $updated_at Last update Date. - * - * @method protected static function booted() The "booted" method of the model. - * @method private static function updatePassword($model) Update the password if needed. + * @property-read \App\Models\ActivityLog $activityLogs Activities logs One-to-many relationship. */ class User extends Authenticatable { + use ActivityLog; use HasFactory; use Notifiable; /** * The attributes that are mass assignable. * - * @var array + * @var array */ protected $fillable = [ - 'firstname', - 'lastname', + 'first_name', + 'last_name', 'email', 'password', 'role', + 'published', + 'published_at', ]; /** * The attributes that should be hidden for serialization. * - * @var array + * @var array */ protected $hidden = [ 'password', @@ -53,13 +54,14 @@ class User extends Authenticatable ]; /** - * Cast the property to an enum. + * The attributes that should be cast. * - * @var array + * @var array */ - protected $enumCasts = [ - 'role' => RoleEnum::class, + protected $casts = [ + 'role' => RoleEnum::class, 'email_verified_at' => 'datetime', + 'published' => 'boolean', ]; /** @@ -67,15 +69,30 @@ class User extends Authenticatable * * @return void */ - protected static function booted() + protected static function booted(): void { - // * EVENT CLOSURES. static::creating(function (self $model) { static::updatePassword($model); - static::setOrder($model); + // * Prevent user elevation privileges + throw_if( + auth('backend')->user() and auth('backend')->user()->role->value() > $model->role->value(), + AuthorizationException::class + ); }); static::updating(function (self $model) { static::updatePassword($model); + if (optional(auth('backend')->user())->getKey() === $model->getKey()) { + \validator( + ['published' => $model->published], + ['published' => 'required|boolean|accepted'], + ['published.accepted' => trans('Vous ne pouvez pas déactiver votre propre compte')], + )->validate(); + } + // * Prevent user elevation privileges + throw_if( + auth('backend')->user() and auth('backend')->user()->role->value() > $model->role->value(), + AuthorizationException::class + ); }); } @@ -93,4 +110,16 @@ class User extends Authenticatable $model->password = Hash::make($model->password); } } + + // * RELATIONS + + /** + * Activities logs One-to-many relationship. + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function activityLogs(): \Illuminate\Database\Eloquent\Relations\HasMany + { + return $this->hasMany(ActivityLog::class); + } } diff --git a/app/Observers/ModelObserver.php b/app/Observers/ModelObserver.php new file mode 100644 index 0000000..71d194b --- /dev/null +++ b/app/Observers/ModelObserver.php @@ -0,0 +1,54 @@ +role->value() <= $minRole->value(); + } +} diff --git a/app/Policies/StaticPagePolicy.php b/app/Policies/StaticPagePolicy.php new file mode 100644 index 0000000..3ab2af8 --- /dev/null +++ b/app/Policies/StaticPagePolicy.php @@ -0,0 +1,36 @@ +role); + } + + /** + * Determine whether the user can create models. + * + * @param \App\Models\User $authUser + * @return boolean + */ + public function create(User $authUser): bool + { + return UserStaticRules::atLeastRole($authUser, RoleEnum::admin); + } + + /** + * Determine whether the user can update the model. + * + * @param \App\Models\User $authUser + * @param \App\Models\User $user + * @return boolean + */ + public function update(User $authUser, User $user): bool + { + return $authUser->getRouteKey() === $user->getRouteKey() or + (UserStaticRules::atLeastRole($authUser, RoleEnum::admin) and + UserStaticRules::atLeastRole($authUser, $user->role)); + } + + /** + * Determine whether the user can delete the model. + * + * @param \App\Models\User $authUser + * @param \App\Models\User $user + * @return boolean + */ + public function delete(User $authUser, User $user): bool + { + return $authUser->getRouteKey() !== $user->getRouteKey() and + UserStaticRules::atLeastRole($authUser, RoleEnum::admin) and + UserStaticRules::atLeastRole($authUser, $user->role); + } + + /** + * Determine whether the user can restore the model. + * + * @param \App\Models\User $authUser + * @param \App\Models\User $user + * @return boolean + */ + public function restore(User $authUser, User $user): bool + { + return $authUser->getRouteKey() !== $user->getRouteKey() and + UserStaticRules::atLeastRole($authUser, RoleEnum::admin) and + UserStaticRules::atLeastRole($authUser, $user->role); + } + + /** + * Determine whether the user can permanently delete the model. + * + * @param \App\Models\User $authUser + * @param \App\Models\User $user + * @return boolean + */ + public function forceDelete(User $authUser, User $user): bool + { + return $authUser->getRouteKey() !== $user->getRouteKey() and + UserStaticRules::atLeastRole($authUser, RoleEnum::admin) and + UserStaticRules::atLeastRole($authUser, $user->role); + } + + /** + * Determine whether the user can duplicate the model. + * + * @param \App\Models\User $authUser + * @param \App\Models\User $user + * @return boolean + */ + public function duplicate(User $authUser, User $user): bool + { + return $authUser->getRouteKey() !== $user->getRouteKey() and + UserStaticRules::atLeastRole($authUser, RoleEnum::admin) and + UserStaticRules::atLeastRole($authUser, $user->role); + } + + /** + * Determine whether the user can publish/unpublish the model. + * + * @param \App\Models\User $authUser + * @param \App\Models\User $user + * @return boolean + */ + public function changePublished(User $authUser, User $user): bool + { + return $authUser->getRouteKey() !== $user->getRouteKey() and + UserStaticRules::atLeastRole($authUser, RoleEnum::admin) and + UserStaticRules::atLeastRole($authUser, $user->role); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 2cced3a..8bfc071 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -3,6 +3,8 @@ namespace App\Providers; use App\Models\Settings; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Carbon; use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\URL; use Illuminate\Support\Pluralizer; @@ -26,6 +28,9 @@ class AppServiceProvider extends ServiceProvider */ public function boot() { + Model::shouldBeStrict(!$this->app->environment('production')); + + // * HTTPS FIX URL::forceScheme('https'); // * HA PROXY FIX. @@ -34,17 +39,17 @@ class AppServiceProvider extends ServiceProvider return request()->getClientIp(); } else { return request()->server('HTTP_X_FORWARDED_FOR') ?? - request()->getClientIp(); + request()->getClientIp(); } }); - // * LOCAL FORCE eg: for Carbon. + // * LOCAL FORCE eg: for Carbon setlocale(LC_ALL, \sprintf( '%s_%s.UTF-8', config('app.locale'), \strtoupper(config('app.locale')) )); - + Carbon::setLocale(config('app.locale')); Pluralizer::useLanguage('french'); app()->singleton(Settings::class, function () { diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index b726da0..1945fce 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -2,14 +2,17 @@ namespace App\Providers; +use App\Enums\Users\RoleEnum; +use App\Models\User; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; +use Illuminate\Support\Facades\Gate; class AuthServiceProvider extends ServiceProvider { /** - * The policy mappings for the application. + * The model to policy mappings for the application. * - * @var array + * @var array */ protected $policies = []; @@ -18,7 +21,10 @@ class AuthServiceProvider extends ServiceProvider * * @return void */ - public function boot() + public function boot(): void { + Gate::define('isConceptor', function (User $user) { + return $user->role === RoleEnum::conceptor; + }); } } diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 13750e8..6e0c396 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -9,9 +9,9 @@ use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvi class EventServiceProvider extends ServiceProvider { /** - * The event listener mappings for the application. + * The event to listener mappings for the application. * - * @var array + * @var array> */ protected $listen = [ Registered::class => [ @@ -19,15 +19,6 @@ class EventServiceProvider extends ServiceProvider ], ]; - /** - * Register any events for your application. - * - * @return void - */ - public function boot(): void - { - } - /** * Determine if events and listeners should be automatically discovered. * diff --git a/app/Providers/TranslationServiceProvider.php b/app/Providers/TranslationServiceProvider.php index a042a23..b3e3f4b 100644 --- a/app/Providers/TranslationServiceProvider.php +++ b/app/Providers/TranslationServiceProvider.php @@ -18,7 +18,7 @@ class TranslationServiceProvider extends ServiceProvider public function boot(): void { foreach ($this->collectLocalesStrings() as $locale) { - // * Supported locales + // * Suported locales Cache::rememberForever(sprintf('translations.%s', $locale), function () use ($locale) { $translations = [ 'php' => $this->phpTranslations($locale), diff --git a/app/Providers/ViewServiceProvider.php b/app/Providers/ViewServiceProvider.php index baf50f0..113b419 100644 --- a/app/Providers/ViewServiceProvider.php +++ b/app/Providers/ViewServiceProvider.php @@ -2,6 +2,7 @@ namespace App\Providers; +use App\Enums\Users\RoleEnum; use App\Models\Prize; use App\Models\Settings; use Illuminate\Pagination\Paginator; @@ -25,16 +26,25 @@ class ViewServiceProvider extends ServiceProvider View::addNamespace('back', resource_path('views')); View::addNamespace('theme', resource_path('theme/mounted/views')); - if (!app()->runningInConsole()) { - $appInfos = Cache::remember('composer', 3600, function () { - return json_decode(File::get(\app_path('../composer.json'))); - }); - View::share('name', $appInfos->name); - View::share('version', $appInfos->version); - View::share('license', $appInfos->license); - // * FORCE BOOTSTRAP PAGINATOR - Paginator::defaultView('back::modules.pagination'); + $appInfos = Cache::remember('composer', 360, function () { + return json_decode(File::get(\app_path('../composer.json'))); + }); + // Share composer.json infos. + if (isset($appInfos->name) and isset($appInfos->version)) { + View::share('cpsr_name', $appInfos->name); + View::share('cpsr_version', $appInfos->version); + } + if (!app()->runningInConsole()) { + // * FORCE BOOTSTRAP PAGINATOR (or custom if in front) + // Early boot, request wont be filled as expected. + if (collect(explode('/', \request()->getPathInfo()))->get(1) === 'bo') { + Paginator::defaultView('modules.pagination'); + } else { + Paginator::useBootstrapFive(); + } + + // FIXME: USe trait cache here // On ramène le nom du jeu et le nom de la société sur toute les pages. $settingsGlobal = Cache::get('settingsGlobal'); if (!$settingsGlobal) { @@ -53,9 +63,15 @@ class ViewServiceProvider extends ServiceProvider } View::share('prizesGlobal', $prizesGlobal); + // * CUSTOM IF STATEMENTS + Blade::if('admin', function () { + return auth('backend')->user() and auth('backend')->user() + ->role->value() <= RoleEnum::admin->value(); + }); Blade::if('superadmin', function () { - return auth()->user() && auth()->user()->role == 1; + return auth('backend')->user() and auth('backend')->user() + ->role->equals(RoleEnum::conceptor); }); } //end if } diff --git a/app/Traits/Controllers/ChangesModelOrder.php b/app/Traits/Controllers/ChangesModelOrder.php new file mode 100644 index 0000000..51af5ba --- /dev/null +++ b/app/Traits/Controllers/ChangesModelOrder.php @@ -0,0 +1,57 @@ +route()->parameterNames); + $className = Str::ucfirst(Str::lower($params->get($params->count() - 2))); + $className = "\\App\\Models\\$className"; + + if (!class_exists($className)) { + throw new \RuntimeException("The $className class doesn't exist."); + } + + /** @var \Illuminate\Database\Eloquent\Model|null */ + $model = $className::where($model->getRouteKeyName(), $model->getRouteKey())->first(); + + if (!$model) { + return back()->with('error', trans('crud.messages.order_not_changed')); + } + + return DB::transaction(function () use ($request, $className, $model) { + /** @var \Illuminate\Database\Eloquent\Model|null */ + $tmp = $className::where('order', $request->direction ? '<' : '>', $model->order) + ->orderBy('order', $request->direction ? 'DESC' : 'ASC')->first(); + + if (!$tmp) { + return back()->with('error', trans('crud.messages.order_not_changed')); + } + + $newOrder = $tmp->order; + $tmp->order = $model->order; + $model->order = $newOrder; + $tmp->saveOrFail(); + $model->saveOrFail(); + Session::flash('success', trans('crud.messages.order_changed')); + + return back(); + }); + } +} diff --git a/app/Traits/Controllers/UpdateModelPublished.php b/app/Traits/Controllers/UpdateModelPublished.php new file mode 100644 index 0000000..4ca47df --- /dev/null +++ b/app/Traits/Controllers/UpdateModelPublished.php @@ -0,0 +1,26 @@ +published = !$model->published; + $model->saveOrFail(); + return redirect()->back()->with('success', trans(__('crud.messages.publish_status_saved'))); + } +} diff --git a/app/Traits/Enums/BaseEnum.php b/app/Traits/Enums/BaseEnum.php index 615c807..559b1ee 100644 --- a/app/Traits/Enums/BaseEnum.php +++ b/app/Traits/Enums/BaseEnum.php @@ -4,14 +4,12 @@ namespace App\Traits\Enums; use Illuminate\Support\Str; -/** - * @author Jérémy Munsch - */ trait BaseEnum { // * GENERIC METHODS + /** - * Get enum value + * Get enum value. * * @return string|integer */ @@ -21,7 +19,7 @@ trait BaseEnum } /** - * Get enum label + * Get enum label. * * @return string */ @@ -31,7 +29,7 @@ trait BaseEnum } /** - * Get enum name + * Get enum name. * * @return string */ @@ -43,7 +41,7 @@ trait BaseEnum // * STATIC FETCH METHODS /** - * Get enum labels + * Get enum labels. * * @return array */ @@ -65,7 +63,7 @@ trait BaseEnum } /** - * Get enum values + * Get enum values. * * @return array */ @@ -79,7 +77,7 @@ trait BaseEnum } /** - * Get enums + * Get enums. * * @return array */ @@ -89,7 +87,7 @@ trait BaseEnum } /** - * Get enums + * Get enums. * * @return array */ @@ -118,30 +116,36 @@ trait BaseEnum } /** - * Fetch enum using value + * Fetch enum using value. * * @param \App\Traits\Enums\BaseEnum|integer|string $value + * @param string $field * @return \App\Traits\Enums\BaseEnum|null */ - public static function make(self|int|string $value): self|null + public static function make(self|int|string $value, string $field = 'value'): self|null { + if ($field === "label") { + $enumLabels = array_change_key_case(array_flip(self::toLabels()), CASE_LOWER); + $value = (isset($enumLabels[strtolower($value)])) ? $enumLabels[strtolower($value)] : false; + $field = 'name'; + } return !\is_object($value) ? - \collect(self::cases())->keyBy('value')->get($value) : $value; + \collect(self::cases())->keyBy($field)->get($value) : $value; } /** - * Test if 2 enum are equals + * Test if 2 enum are equals. * * @param object ...$others * @return boolean - * @throws \Error If provided element are not enums taht extends BaseEnum. + * @throws \Error If provided element are not enums that extends BaseEnum. */ public function equals(object ...$others): bool { foreach ($others as $other) { if (!\in_array(BaseEnum::class, \class_uses_recursive($other))) { throw new \Error(__METHOD__ . - ' requires $enum to use ' . BaseEnum::class); + ' requires $enum to use ' . BaseEnum::class); } if ( get_class($this) === get_class($other) @@ -154,7 +158,7 @@ trait BaseEnum } /** - * Serialize to json + * Serialize to json. * * @return mixed */ @@ -167,7 +171,7 @@ trait BaseEnum // * CACHE /** - * Get cache array + * Get cache array. * * @param string $index * @return array @@ -182,7 +186,7 @@ trait BaseEnum } /** - * Set cache array + * Set cache array. * * @param string $index * @param array $items @@ -198,7 +202,7 @@ trait BaseEnum } /** - * Init cache if needed + * Init cache if needed. * * @return void */ @@ -219,7 +223,7 @@ trait BaseEnum // * PRIVATE /** - * Fetch the LABELS constant if exists + * Fetch the LABELS constant if exists. * * @return array|null */ @@ -235,7 +239,7 @@ trait BaseEnum } /** - * Get custom constants + * Get custom constants. * * @return array */ diff --git a/app/Traits/Models/ActivityLog.php b/app/Traits/Models/ActivityLog.php new file mode 100644 index 0000000..67973e1 --- /dev/null +++ b/app/Traits/Models/ActivityLog.php @@ -0,0 +1,23 @@ +endpoint = $endpoint; - $this->api_key = $api_key; - } - - public function getCustomer($id){ - return $this->callAPI('GET', "/customer/id/$id"); - } - - public function getCustomerByEmail($email){ - return $this->callAPI('GET', "/customer/email/$email"); - } - - public function createCustomer($email, $password, $firstname, $lastname, $optin, $birthdate = null, $phone = null, $address = null, $address2 = null, $city = null, $postcode = null, $country = null){ - $user = [ - 'email' => $email, - 'password' => $password, - 'firstname' => $firstname, - 'lastname' => $lastname, - 'opt_in' => $optin, - 'birth_date' => $birthdate, - 'phone' => $phone, - 'address' => $address, - 'address2' => $address2, - 'city' => $city, - 'postcode' => $postcode, - 'country' => $country - ]; - return $this->callAPI('POST', '/customer/create', $user); - } - - public function updateCustomer($crmId, $firstname = null, $lastname = null, $optin = null, $birthdate = null, $phone = null, $address = null, $address2 = null, $city = null, $postcode = null, $country = null){ - $user = []; - - if ($firstname !== null) - $user['firstname'] = $firstname; - - if ($lastname !== null) - $user['lastname'] = $lastname; - - if ($optin !== null) - $user['opt_in'] = $optin; - - if ($birthdate !== null) - $user['birth_date'] = $birthdate; - - if ($phone !== null) - $user['phone'] = $phone; - - if ($address !== null) - $user['address'] = $address; - - if ($address2 !== null) - $user['address2'] = $address2; - - if ($city !== null) - $user['city'] = $city; - - if ($postcode !== null) - $user['postcode'] = $postcode; - - if ($country !== null) - $user['country'] = $country; - - - return $this->callAPI('PUT', "/customer/$crmId", $user); - } - - public function deleteCustomer($id){ - return $this->callAPI('DELETE', "/customer/$id"); - } - - public function changePwdCustomer($id, $password){ - return $this->callAPI('POST', "/customer/$id/change-password", ['password' => $password]); - } - - public function activateCustomer($id){ - return $this->callAPI('POST', "/customer/$id/activate"); - } - - public function toggleOptInCustomer($id){ - return $this->callAPI('POST', "/customer/$id/toggle-optin"); - } - - public function loginCustomer($email, $password){ - return $this->callAPI('POST', "/customer/login", ['email' => $email, 'password' => $password]); - } - - public function createSingleOptin($email){ - return $this->callAPI('POST', '/single-optin', ['email' => $email]); - } - - public function ArcadiaOutgoingMail($crm_id, $fields, $type){ - return $this->callAPI('POST', '/arcadia/outgoing-mail', ['crm_id' => $crm_id, 'fields' => json_encode($fields), 'type' => $type]); - } - - private function callAPI($method, $url, $data = false){ - - if (strpos($this->endpoint, "dev")){ - if ($data !== false){ - $data = array_merge($data, ['api_token' => $this->api_key]); - }else{ - $data = ['api_token' => $this->api_key]; - } - } - - $url = $this->endpoint . '/api/v1' . $url; - $curl = curl_init(); - switch ($method){ - case "POST": - curl_setopt($curl, CURLOPT_POST, 1); - if ($data) - curl_setopt($curl, CURLOPT_POSTFIELDS, $data); - break; - case "PUT": - curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT"); - if ($data) - curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data)); - break; - case "DELETE": - curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE"); - if ($data) - curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data)); - break; - default: - if ($data) - $url = sprintf("%s?%s", $url, http_build_query($data)); - } - curl_setopt($curl, CURLOPT_URL, $url); - curl_setopt($curl, CURLOPT_COOKIESESSION, TRUE); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); - if (!strpos($this->endpoint, "dev")) { - curl_setopt($curl, CURLOPT_HTTPHEADER, array( - 'Accept: application/json', - 'Authorization: Bearer ' . $this->api_key - )); - } - - $result = curl_exec($curl); - - if (!curl_errno($curl)) { - $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE); - if (!in_array($http_code, [200, 201])) - return (object) ['status' => 'KO', 'code' => 500, 'message' => 'Connection to API failed - Code HTTP : '. $http_code]; - } else { - return (object) ['status' => 'KO', 'code' => 500, 'message' => 'Connection to API failed : '. curl_error($curl)]; - } - - curl_close($curl); - return json_decode($result); - } -} diff --git a/composer.json b/composer.json index db9780e..48c9bbf 100644 --- a/composer.json +++ b/composer.json @@ -1,30 +1,31 @@ { - "name": "laravel/laravel", + "name": "uwil/gen-game-web", "version": "2.0.0", "type": "project", - "description": "The Laravel Framework.", + "description": "Game generator", + "support": { + "email": "equipe.web@ldc.fr" + }, "keywords": [ - "framework", + "game", "laravel" ], "license": "MIT", "require": { - "php": "^8.1", + "php": "^8.2", "diglactic/laravel-breadcrumbs": "^8.0", "guzzlehttp/guzzle": "^7.2", "laravel/framework": "^10.0", "laravel/tinker": "^2.8", "laravel/ui": "^4.2", "mews/purifier": "^3.4", - "sentry/sentry-laravel": "*", - "kwaadpepper/enum": "^2.1", - "kwaadpepper/serial-int-caster": "^1.0" + "sentry/sentry-laravel": "*" }, "require-dev": { - "doctrine/dbal": "^3.6", "fakerphp/faker": "^1.9.1", "kitloong/laravel-migrations-generator": "^6.8", "krlove/eloquent-model-generator": "dev-l10-compatibility", + "laravel-lang/common": "^4.0", "laravel/pint": "^1.0", "laravel/sail": "^1.18", "mockery/mockery": "^1.4.4", @@ -32,7 +33,6 @@ "phpunit/phpunit": "^10.0", "slevomat/coding-standard": "^8.10", "spatie/laravel-ignition": "^2.0", - "squizlabs/php_codesniffer": "^3.6", "thedoctor0/laravel-factory-generator": "^1.2" }, "autoload": { diff --git a/config/app.php b/config/app.php index 06b5bd4..486751d 100644 --- a/config/app.php +++ b/config/app.php @@ -108,7 +108,7 @@ return [ | */ - 'faker_locale' => 'en_US', + 'faker_locale' => 'fr_FR', /* |-------------------------------------------------------------------------- @@ -125,6 +125,23 @@ return [ 'cipher' => 'AES-256-CBC', + /* + |-------------------------------------------------------------------------- + | Maintenance Mode Driver + |-------------------------------------------------------------------------- + | + | These configuration options determine the driver used to determine and + | manage Laravel's "maintenance mode" status. The "cache" driver will + | allow maintenance mode to be controlled across multiple machines. + | + | Supported drivers: "file", "cache" + | + */ + + 'maintenance' => [ + 'driver' => 'file' + ], + /* |-------------------------------------------------------------------------- | Autoloaded Service Providers @@ -179,7 +196,6 @@ return [ App\Providers\RouteServiceProvider::class, App\Providers\TranslationServiceProvider::class, App\Providers\ViewServiceProvider::class, - ], /* diff --git a/config/auth.php b/config/auth.php index 9a8be38..f5385f0 100644 --- a/config/auth.php +++ b/config/auth.php @@ -31,7 +31,7 @@ return [ | users are actually retrieved out of your database or other storage | mechanisms used by this application to persist your user's data. | - | Supported: "session", "token" + | Supported: "session" | */ @@ -43,6 +43,7 @@ return [ 'frontend' => [ 'driver' => 'session', + // ! Remplacer "users" par le model authenticable voulu 'provider' => 'customers', ], ], @@ -67,11 +68,11 @@ return [ 'providers' => [ 'users' => [ 'driver' => 'eloquent', - 'model' => App\Models\User::class, + 'model' => \App\Models\User::class, ], 'customers' => [ 'driver' => 'eloquent', - 'model' => App\Models\Customer::class, + 'model' => \App\Models\Customer::class, ], ], @@ -84,22 +85,26 @@ return [ | than one user table or model in the application and you want to have | separate password reset settings based on the specific user types. | - | The expire time is the number of minutes that the reset token should be + | The expiry time is the number of minutes that each reset token will be | considered valid. This security feature keeps tokens short-lived so | they have less time to be guessed. You may change this as needed. | + | The throttle setting is the number of seconds a user must wait before + | generating more password reset tokens. This prevents the user from + | quickly generating a very large amount of password reset tokens. + | */ 'passwords' => [ 'users' => [ 'provider' => 'users', - 'table' => 'password', + 'table' => 'password_resets', 'expire' => 60, 'throttle' => 60, ], 'customers' => [ 'provider' => 'customers', - 'table' => 'password', + 'table' => 'password_resets', 'expire' => 60, 'throttle' => 60, ], diff --git a/config/breadcrumbs.php b/config/breadcrumbs.php index 0b8d984..6c96342 100644 --- a/config/breadcrumbs.php +++ b/config/breadcrumbs.php @@ -23,7 +23,7 @@ return [ | */ - 'view' => 'breadcrumbs/breadcrumb-layout', + 'view' => 'modules/breadcrumbs/breadcrumb-layout', /* |-------------------------------------------------------------------------- diff --git a/config/broadcasting.php b/config/broadcasting.php index 3e45d82..ae079a6 100644 --- a/config/broadcasting.php +++ b/config/broadcasting.php @@ -11,7 +11,7 @@ return [ | framework when an event needs to be broadcast. You may set this to | any of the connections defined in the "connections" array below. | - | Supported: "pusher", "redis", "log", "null" + | Supported: "pusher", "ably", "redis", "log", "null" | */ diff --git a/config/cache.php b/config/cache.php index 9ac517a..8e0f9c7 100644 --- a/config/cache.php +++ b/config/cache.php @@ -13,9 +13,6 @@ return [ | using this caching library. This connection is used when another is | not explicitly specified when executing a given caching function. | - | Supported: "apc", "array", "database", "file", - | "memcached", "redis", "dynamodb" - | */ 'default' => env('CACHE_DRIVER', 'file'), @@ -29,6 +26,9 @@ return [ | well as their drivers. You may even define multiple stores for the | same cache driver to group types of items stored in your caches. | + | Supported drivers: "apc", "array", "database", "file", + | "memcached", "redis", "dynamodb", "octane", "null" + | */ 'stores' => [ @@ -99,9 +99,9 @@ return [ | Cache Key Prefix |-------------------------------------------------------------------------- | - | When utilizing a RAM based store such as APC or Memcached, there might - | be other applications utilizing the same cache. So, we'll specify a - | value to get prefixed to all our keys so we can avoid collisions. + | When utilizing the APC, database, memcached, Redis, or DynamoDB cache + | stores there might be other applications using the same cache. For + | that reason, you may prefix every cache key to avoid collisions. | */ diff --git a/config/filesystems.php b/config/filesystems.php index e77a2da..89920a5 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -13,20 +13,7 @@ return [ | */ - 'default' => env('FILESYSTEM_DRIVER', 'local'), - - /* - |-------------------------------------------------------------------------- - | Default Cloud Filesystem Disk - |-------------------------------------------------------------------------- - | - | Many applications store files both locally and in the cloud. For this - | reason, you may specify a default "cloud" driver here. This driver - | will be bound as the Cloud disk implementation in the container. - | - */ - - 'cloud' => env('FILESYSTEM_CLOUD', 's3'), + 'default' => env('FILESYSTEM_DISK', 'local'), /* |-------------------------------------------------------------------------- @@ -35,7 +22,7 @@ return [ | | Here you may configure as many filesystem "disks" as you wish, and you | may even configure multiple disks of the same driver. Defaults have - | been setup for each driver as an example of the required options. + | been set up for each driver as an example of the required values. | | Supported Drivers: "local", "ftp", "sftp", "s3" | @@ -78,7 +65,6 @@ return [ ], - /* |-------------------------------------------------------------------------- | Symbolic Links diff --git a/config/hashing.php b/config/hashing.php index db3d9dd..be29b34 100644 --- a/config/hashing.php +++ b/config/hashing.php @@ -29,7 +29,7 @@ return [ */ 'bcrypt' => [ - 'rounds' => env('BCRYPT_ROUNDS', 10), + 'rounds' => env('BCRYPT_ROUNDS', 12), ], /* diff --git a/config/logging.php b/config/logging.php index 00679d6..5906e47 100644 --- a/config/logging.php +++ b/config/logging.php @@ -3,6 +3,7 @@ use Monolog\Handler\NullHandler; use Monolog\Handler\StreamHandler; use Monolog\Handler\SyslogUdpHandler; +use Monolog\Processor\PsrLogMessageProcessor; return [ diff --git a/config/mail.php b/config/mail.php index 3ca5af0..c5d3548 100644 --- a/config/mail.php +++ b/config/mail.php @@ -15,13 +15,6 @@ return [ 'default' => env('MAIL_MAILER', 'smtp'), - // Mail admin pour l'envoi de mail comme la fin de la génération des codes uniques. - 'MAIL_ADMIN' => env('MAIL_ADMIN'), - - 'contactsList' => [ - 'serviceweb' => env('MAIL_ADMIN', 'equipe.web@ldc.fr') - ], - /* |-------------------------------------------------------------------------- | Mailer Configurations @@ -35,8 +28,8 @@ return [ | sending an e-mail. You will specify which one you are using for your | mailers below. You are free to add additional mailers as required. | - | Supported: "smtp", "sendmail", "mailgun", "ses", - | "postmark", "log", "array" + | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", + | "postmark", "log", "array", "failover" | */ @@ -118,11 +111,10 @@ return [ 'theme' => 'default', 'paths' => [ - // * For custom compiled css. - public_path('build/theme/css/mail'), - // * For generic customized blade (moteur seulement) resource_path('views/vendor/mail'), ], ], + 'admin' => env('MAIL_ADMIN') + ]; diff --git a/config/queue.php b/config/queue.php index 91725f8..f5a66b9 100644 --- a/config/queue.php +++ b/config/queue.php @@ -101,7 +101,7 @@ return [ */ 'failed' => [ - 'driver' => env('QUEUE_FAILED_DRIVER', 'database'), + 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), 'database' => env('DB_CONNECTION', 'mysql'), 'table' => 'failed_jobs', ], diff --git a/config/session.php b/config/session.php index 4bf5f2e..d2e3239 100644 --- a/config/session.php +++ b/config/session.php @@ -72,7 +72,7 @@ return [ | */ - 'connection' => env('SESSION_CONNECTION', null), + 'connection' => env('SESSION_CONNECTION'), /* |-------------------------------------------------------------------------- @@ -164,7 +164,7 @@ return [ | | By setting this option to true, session cookies will only be sent back | to the server if the browser has a HTTPS connection. This will keep - | the cookie from being sent to you if it can not be done securely. + | the cookie from being sent to you when it can't be done securely. | */ diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php new file mode 100644 index 0000000..8a8bc99 --- /dev/null +++ b/database/factories/UserFactory.php @@ -0,0 +1,44 @@ + + */ + public function definition() + { + $published = $this->faker->boolean(75); + return [ + 'first_name' => fake()->firstname(), + 'last_name' => fake()->lastname(), + 'email' => fake()->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', + 'remember_token' => Str::random(10), + 'role' => \collect(RoleEnum::toArray())->random()->value, + 'published' => $published, + 'published_at' => ($published) ? now() : null, + ]; + } + + /** + * Indicate that the model's email address should be unverified. + * + * @return static + */ + public function unverified(): static + { + // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass + return $this->state(fn (array $attributes) => [ + 'email_verified_at' => null, + ]); + } +} diff --git a/database/migrations/2014_10_12_000000_create_users_table.php b/database/migrations/2014_10_12_000000_create_users_table.php index e3d921b..612e0a9 100644 --- a/database/migrations/2014_10_12_000000_create_users_table.php +++ b/database/migrations/2014_10_12_000000_create_users_table.php @@ -15,13 +15,15 @@ return new class extends Migration { Schema::create('users', function (Blueprint $table) { $table->id(); - $table->string('firstname')->comment('Firstname of the user.'); - $table->string('lastname')->comment('Lastname of the user.'); + $table->string('first_name')->comment('First name of the user.'); + $table->string('last_name')->comment('Last name of the user.'); $table->string('email')->unique()->comment('Email of the user.'); $table->timestamp('email_verified_at')->nullable(); $table->string('password')->comment('Password of the user account.'); $table->rememberToken(); - $table->boolean('role')->comment('Role of the user who defines the rights/access.'); + $table->unsignedTinyInteger('role')->comment('Role of the user who defines the rights/access.'); + $table->boolean('published')->comment('The user is acivated or not.'); + $table->timestamp('published_at')->nullable()->comment('The date on which the user was acivated.'); $table->timestamps(); }); } diff --git a/database/migrations/2014_10_12_100000_create_password_resets_table.php b/database/migrations/2014_10_12_100000_create_password_resets_table.php index 0ee0a36..fcacb80 100644 --- a/database/migrations/2014_10_12_100000_create_password_resets_table.php +++ b/database/migrations/2014_10_12_100000_create_password_resets_table.php @@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -class CreatePasswordResetsTable extends Migration +return new class extends Migration { /** * Run the migrations. @@ -29,4 +29,4 @@ class CreatePasswordResetsTable extends Migration { Schema::dropIfExists('password_resets'); } -} +}; diff --git a/database/migrations/2019_08_19_000000_create_failed_jobs_table.php b/database/migrations/2019_08_19_000000_create_failed_jobs_table.php index fc1304f..1719198 100644 --- a/database/migrations/2019_08_19_000000_create_failed_jobs_table.php +++ b/database/migrations/2019_08_19_000000_create_failed_jobs_table.php @@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -class CreateFailedJobsTable extends Migration +return new class extends Migration { /** * Run the migrations. @@ -15,7 +15,7 @@ class CreateFailedJobsTable extends Migration { Schema::create('failed_jobs', function (Blueprint $table) { $table->id(); - $table->string('uuid')->nullable()->unique(); + $table->string('uuid')->unique(); $table->text('connection'); $table->text('queue'); $table->longText('payload'); @@ -33,4 +33,4 @@ class CreateFailedJobsTable extends Migration { Schema::dropIfExists('failed_jobs'); } -} +}; diff --git a/database/migrations/2020_10_20_165858_create_codes.php b/database/migrations/2020_10_20_165858_create_codes.php index 4846556..02fa65b 100644 --- a/database/migrations/2020_10_20_165858_create_codes.php +++ b/database/migrations/2020_10_20_165858_create_codes.php @@ -14,7 +14,7 @@ class CreateCodes extends Migration public function up() { Schema::create('codes', function (Blueprint $table) { - $table->id(); + $table->unsignedBigInteger('id')->primary(); $table->boolean('code_used')->default(0); $table->boolean('code_exported')->default(0); $table->date('date_creation'); diff --git a/database/migrations/2020_10_23_153212_create_contents.php b/database/migrations/2020_10_23_153212_create_contents.php deleted file mode 100644 index 6617b31..0000000 --- a/database/migrations/2020_10_23_153212_create_contents.php +++ /dev/null @@ -1,33 +0,0 @@ -id(); - $table->string('key'); - $table->text('value'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::dropIfExists('content'); - } -} diff --git a/database/migrations/2023_09_08_102717_create_actitivy_logs_table.php b/database/migrations/2023_09_08_102717_create_actitivy_logs_table.php new file mode 100644 index 0000000..aeb4d90 --- /dev/null +++ b/database/migrations/2023_09_08_102717_create_actitivy_logs_table.php @@ -0,0 +1,39 @@ +id(); + $table->foreignId('user_id')->nullable()->constrained('users') + ->comment('Id of the user associated, or nullable for a deleted user.'); + $table->boolean('is_anonymous')->comment('If there is no user connected when action was realised.'); + $table->boolean('is_console')->comment('If the action was realised in console.'); + $table->string('model_class')->comment('Target model.'); + $table->integer('model_id')->comment('Id of the target model.'); + $table->integer('event')->comment('Event of this activity (ActivityLogsEventEnum).'); + $table->json('data')->nullable()->comment('List of changes (old and new values).'); + $table->timestamp('created_at')->comment('The date on which the user was published.'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down(): void + { + Schema::dropIfExists('activity_logs'); + } +}; diff --git a/database/migrations/2023_10_19_094711_create_static_pages.php b/database/migrations/2023_10_19_094711_create_static_pages.php new file mode 100644 index 0000000..a28d0c0 --- /dev/null +++ b/database/migrations/2023_10_19_094711_create_static_pages.php @@ -0,0 +1,46 @@ +id(); + + $table->unsignedTinyInteger('type')->unique()->comment('Page type (StaticPageEnum)'); + + $table->string('seo_title')->comment('Seo title about 70 chars as recommended'); + $table->string('seo_description')->comment('Seo description about 160 chars as recommended'); + + $table->string('title')->comment('Title for the page'); + $table->string('slug')->unique()->comment('Unique title slug'); + + $table->text('content')->comment('WYSIWYG text content using ckeditor'); + + $table->timestamps(); + }); + Artisan::call('db:seed', [ + 'class' => 'StaticPageSeeder' + ]); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down(): void + { + Schema::dropIfExists('static_pages'); + } +}; diff --git a/database/seeders/ContentSeeder.php b/database/seeders/ContentSeeder.php deleted file mode 100644 index b58c58a..0000000 --- a/database/seeders/ContentSeeder.php +++ /dev/null @@ -1,33 +0,0 @@ -key = "mentions legales"; - $mentions->value = "..."; - - $charte = new Content(); - $charte->key = "charte"; - $charte->value = "..."; - - $reglement = new Content(); - $reglement->key = "reglement"; - $reglement->value = "..."; - - $mentions->save(); - $charte->save(); - $reglement->save(); - } -} diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 9aa2016..2f99f19 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -14,7 +14,7 @@ class DatabaseSeeder extends Seeder public function run() { $this->call(SettingsSeeder::class); - $this->call(ContentSeeder::class); + $this->call(StaticPageSeeder::class); $this->call(PrizeSeeder::class); } } diff --git a/database/seeders/StaticPageSeeder.php b/database/seeders/StaticPageSeeder.php new file mode 100644 index 0000000..eb60d6c --- /dev/null +++ b/database/seeders/StaticPageSeeder.php @@ -0,0 +1,55 @@ +updateOrCreate( + [ + 'type' => StaticPageType::contact, + ], + [ + 'seo_title' => 'Seo title pour la page contactez nous', + 'seo_description' => 'Seo description pour la page contactez nous', + 'title' => 'Contactez-nous', + 'type' => StaticPageType::contact, + 'content' => \clean(\fake()->randomHtml(6, 7)), + ] + ); + StaticPage::query()->updateOrCreate( + [ + 'type' => StaticPageType::legals, + ], + [ + 'seo_title' => 'Seo title pour la page mentions légales', + 'seo_description' => 'Seo description pour la page mentions légales', + 'title' => 'Mentions légales', + 'type' => StaticPageType::legals, + 'content' => \clean(\fake()->randomHtml(6, 7)), + ] + ); + StaticPage::query()->updateOrCreate( + [ + 'type' => StaticPageType::rules, + ], + [ + 'seo_title' => 'Seo title pour la page charte de confidentialitée', + 'seo_description' => 'Seo description pour la page charte de confidentialitée', + 'title' => 'Charte de confidentialitée', + 'type' => StaticPageType::rules, + 'content' => \clean(\fake()->randomHtml(6, 7)), + ] + ); + } +} diff --git a/database/seeders/UserSeeder.php b/database/seeders/UserSeeder.php new file mode 100644 index 0000000..ba66561 --- /dev/null +++ b/database/seeders/UserSeeder.php @@ -0,0 +1,19 @@ +create(); + } +} diff --git a/public/logo.png b/public/logo.png deleted file mode 100644 index 572f192..0000000 Binary files a/public/logo.png and /dev/null differ diff --git a/resources/lang/en.json b/resources/lang/en.json new file mode 100644 index 0000000..d208ff2 --- /dev/null +++ b/resources/lang/en.json @@ -0,0 +1,54 @@ +{ + "(and :count more error)": "(and :count more error)", + "(and :count more errors)": "(and :count more errors)", + "A fresh verification link has been sent to your email address.": "A fresh verification link has been sent to your email address.", + "All rights reserved.": "All rights reserved.", + "Before proceeding, please check your email for a verification link.": "Before proceeding, please check your email for a verification link.", + "click here to request another": "click here to request another", + "Confirm Password": "Confirm Password", + "Dashboard": "Dashboard", + "Email Address": "Email Address", + "Forbidden": "Forbidden", + "Forgot Your Password?": "Forgot Your Password?", + "Go to page :page": "Go to page :page", + "Hello!": "Hello!", + "If you did not create an account, no further action is required.": "If you did not create an account, no further action is required.", + "If you did not receive the email": "If you did not receive the email", + "If you did not request a password reset, no further action is required.": "If you did not request a password reset, no further action is required.", + "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:", + "Invalid JSON was returned from the route.": "Invalid JSON was returned from the route.", + "Login": "Login", + "Logout": "Logout", + "Name": "Name", + "Not Found": "Not Found", + "of": "of", + "Page Expired": "Page Expired", + "Pagination Navigation": "Pagination Navigation", + "Password": "Password", + "Payment Required": "Payment Required", + "Please click the button below to verify your email address.": "Please click the button below to verify your email address.", + "Please confirm your password before continuing.": "Please confirm your password before continuing.", + "Regards": "Regards", + "Register": "Register", + "Remember Me": "Remember Me", + "Reset Password": "Reset Password", + "Reset Password Notification": "Reset Password Notification", + "results": "results", + "Send Password Reset Link": "Send Password Reset Link", + "Server Error": "Server Error", + "Service Unavailable": "Service Unavailable", + "Showing": "Showing", + "The given data was invalid.": "The given data was invalid.", + "The response is not a streamed response.": "The response is not a streamed response.", + "The response is not a view.": "The response is not a view.", + "This password reset link will expire in :count minutes.": "This password reset link will expire in :count minutes.", + "to": "to", + "Toggle navigation": "Toggle navigation", + "Too Many Requests": "Too Many Requests", + "Unauthorized": "Unauthorized", + "Verify Email Address": "Verify Email Address", + "Verify Your Email Address": "Verify Your Email Address", + "Whoops!": "Whoops!", + "You are logged in!": "You are logged in!", + "You are receiving this email because we received a password reset request for your account.": "You are receiving this email because we received a password reset request for your account." +} \ No newline at end of file diff --git a/resources/lang/en/auth.php b/resources/lang/en/auth.php index e5506df..6db4982 100644 --- a/resources/lang/en/auth.php +++ b/resources/lang/en/auth.php @@ -1,19 +1,9 @@ 'These credentials do not match our records.', + 'failed' => 'These credentials do not match our records.', + 'password' => 'The password is incorrect.', 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', - ]; diff --git a/resources/lang/en/crud.php b/resources/lang/en/crud.php new file mode 100644 index 0000000..b04af04 --- /dev/null +++ b/resources/lang/en/crud.php @@ -0,0 +1,83 @@ + [ + 'list' => 'list', + 'view' => 'view', + 'create' => 'create', + 'duplicate' => 'duplicate', + 'update' => 'update', + 'delete' => 'delete', + 'save' => 'save', + 'edit' => 'edit', + ], + + 'helpers' => [ + 'list' => 'List', + 'list_all' => 'List all :model', + 'create_model' => 'Create :model', + 'view_model' => 'View :model', + 'update_model' => 'Update :model', + 'duplicate_model' => 'Duplicate :model', + 'delete_model' => 'Delete :model', + 'save_model' => 'Save :model', + 'edit_model' => 'Edit :model', + 'no_results' => 'No results for :models', + 'publish' => 'Publish', + 'unpublish' => 'Unpublish', + 'seo_title' => 'Must give a precise description of the page, which is recommended for optimum + positioning in search engines ( tag).', + 'seo_description' => 'Must give a precise description of the page content (<meta> description tag).', + ], + + 'libs' => [ + 'toolbox' => [ + 'assertFieldIsUnique' => 'The :value for the :attribute field is already in use', + 'assertNoInternalBoUrls' => ':attribute : It is forbidden to paste back-office uri into this field', + 'assertNoInternalUrls' => ':attribute : It is forbidden to paste a url of :appname into this field', + ] + ], + + 'messages' => [ + 'has_been_created' => ':model has been created', + 'cannot_be_created' => ':model cannot be created', + 'has_been_updated' => ':model has been updated', + 'cannot_be_updated' => ':model cannot be updated', + 'has_been_deleted' => ':model has been deleted', + 'cannot_be_deleted' => ':model cannot be deleted', + 'order_changed' => 'The order has been changed!', + 'order_not_changed' => 'The order hasn\'t been changed!', + 'publish_status_saved' => 'Change of publication status successful!', + 'right' => 'You don\'t have the rights to access this part !', + 'theme_updated' => 'The theme has been updated !', + ], + + 'minicrud' => [ + 'visibility_label' => 'Visibility', + 'visibility_helper' => 'Check to make it visible.', + 'enum_select' => 'Select :enum from the list.', + 'associated_enum' => 'Associated :enum', + 'dimensions_must_be' => 'Must be :widthpx (width) by :heightpx (height).', + 'picture_title_helper' => 'Useful for users, displayed by moving the mouse over the image', + 'picture_title_label' => 'Title attribute of the image', + 'picture_alt_helper' => 'Must describe your image for search engines and blind people (5 to 6 words)', + 'picture_alt_label' => 'Alt attribute of the image', + 'text_warning' => 'Proofread your content before publishing it', + ], + + 'other' => [ + 'up' => 'Change the order of appearance upwards', + 'down' => 'Change the order of appearance downwards', + 'no_model_found' => 'None :model found', + 'publish' => 'Published', + 'unpublish' => 'Unpublished', + 'search_start' => 'Start search', + 'search_delete' => 'Delete search', + 'search_placeholder' => 'Your keywords here...', + 'sort_ascending' => 'Sort :name in ascending order', + 'sort_descending' => 'Sort :name in descending order', + 'sort_delete' => 'Delete sorting', + 'sort_arrow' => 'Here, the arrows are used to sort, not filter.', + 'right' => 'You don\'t have the rights', + ] +]; diff --git a/resources/lang/en/enums.php b/resources/lang/en/enums.php new file mode 100644 index 0000000..0b67a5f --- /dev/null +++ b/resources/lang/en/enums.php @@ -0,0 +1,3 @@ +<?php + +return []; diff --git a/resources/lang/en/http-statuses.php b/resources/lang/en/http-statuses.php new file mode 100644 index 0000000..3346f78 --- /dev/null +++ b/resources/lang/en/http-statuses.php @@ -0,0 +1,84 @@ +<?php + +declare(strict_types=1); + +return [ + '0' => 'Unknown Error', + '100' => 'Continue', + '101' => 'Switching Protocols', + '102' => 'Processing', + '200' => 'OK', + '201' => 'Created', + '202' => 'Accepted', + '203' => 'Non-Authoritative Information', + '204' => 'No Content', + '205' => 'Reset Content', + '206' => 'Partial Content', + '207' => 'Multi-Status', + '208' => 'Already Reported', + '226' => 'IM Used', + '300' => 'Multiple Choices', + '301' => 'Moved Permanently', + '302' => 'Found', + '303' => 'See Other', + '304' => 'Not Modified', + '305' => 'Use Proxy', + '307' => 'Temporary Redirect', + '308' => 'Permanent Redirect', + '400' => 'Bad Request', + '401' => 'Unauthorized', + '402' => 'Payment Required', + '403' => 'Forbidden', + '404' => 'Not Found', + '405' => 'Method Not Allowed', + '406' => 'Not Acceptable', + '407' => 'Proxy Authentication Required', + '408' => 'Request Timeout', + '409' => 'Conflict', + '410' => 'Gone', + '411' => 'Length Required', + '412' => 'Precondition Failed', + '413' => 'Payload Too Large', + '414' => 'URI Too Long', + '415' => 'Unsupported Media Type', + '416' => 'Range Not Satisfiable', + '417' => 'Expectation Failed', + '418' => 'I\'m a teapot', + '419' => 'Session Has Expired', + '421' => 'Misdirected Request', + '422' => 'Unprocessable Entity', + '423' => 'Locked', + '424' => 'Failed Dependency', + '425' => 'Too Early', + '426' => 'Upgrade Required', + '428' => 'Precondition Required', + '429' => 'Too Many Requests', + '431' => 'Request Header Fields Too Large', + '444' => 'Connection Closed Without Response', + '449' => 'Retry With', + '451' => 'Unavailable For Legal Reasons', + '499' => 'Client Closed Request', + '500' => 'Internal Server Error', + '501' => 'Not Implemented', + '502' => 'Bad Gateway', + '503' => 'Maintenance Mode', + '504' => 'Gateway Timeout', + '505' => 'HTTP Version Not Supported', + '506' => 'Variant Also Negotiates', + '507' => 'Insufficient Storage', + '508' => 'Loop Detected', + '509' => 'Bandwidth Limit Exceeded', + '510' => 'Not Extended', + '511' => 'Network Authentication Required', + '520' => 'Unknown Error', + '521' => 'Web Server is Down', + '522' => 'Connection Timed Out', + '523' => 'Origin Is Unreachable', + '524' => 'A Timeout Occurred', + '525' => 'SSL Handshake Failed', + '526' => 'Invalid SSL Certificate', + '527' => 'Railgun Error', + '598' => 'Network Read Timeout Error', + '599' => 'Network Connect Timeout Error', + 'unknownError' => 'Unknown Error', +]; diff --git a/resources/lang/en/models.php b/resources/lang/en/models.php new file mode 100644 index 0000000..a709346 --- /dev/null +++ b/resources/lang/en/models.php @@ -0,0 +1,9 @@ +<?php + +return [ + 'classes' => [ + 'user' => 'user', + 'activity_log' => 'activity log|activity logs', + 'static_page' => 'static page|static pages', + ] +]; diff --git a/resources/lang/en/pagination.php b/resources/lang/en/pagination.php index d481411..ead9596 100644 --- a/resources/lang/en/pagination.php +++ b/resources/lang/en/pagination.php @@ -1,19 +1,12 @@ <?php +declare(strict_types=1); + return [ - - /* - |-------------------------------------------------------------------------- - | Pagination Language Lines - |-------------------------------------------------------------------------- - | - | The following language lines are used by the paginator library to build - | the simple pagination links. You are free to change them to anything - | you want to customize your views to better match your application. - | - */ - - 'previous' => '« Previous', - 'next' => 'Next »', - + 'next' => 'Next »', + 'previous' => '« Previous', + 'previous_page' => 'Previous page', + 'next_page' => 'Next page', + 'specific_page' => 'Go to page n°:id', + 'paginate_list' => 'Number of elements per page', ]; diff --git a/resources/lang/en/passwords.php b/resources/lang/en/passwords.php index 2345a56..f3b65ba 100644 --- a/resources/lang/en/passwords.php +++ b/resources/lang/en/passwords.php @@ -1,22 +1,11 @@ <?php +declare(strict_types=1); + return [ - - /* - |-------------------------------------------------------------------------- - | Password Reset Language Lines - |-------------------------------------------------------------------------- - | - | The following language lines are the default lines which match reasons - | that are given by the password broker for a password update attempt - | has failed, such as for an invalid token or invalid new password. - | - */ - - 'reset' => 'Your password has been reset!', - 'sent' => 'We have emailed your password reset link!', + 'reset' => 'Your password has been reset.', + 'sent' => 'We have emailed your password reset link.', 'throttled' => 'Please wait before retrying.', - 'token' => 'This password reset token is invalid.', - 'user' => "We can't find a user with that email address.", - + 'token' => 'This password reset token is invalid.', + 'user' => 'We can\'t find a user with that email address.', ]; diff --git a/resources/lang/en/recaptcha.php b/resources/lang/en/recaptcha.php new file mode 100644 index 0000000..8a95f21 --- /dev/null +++ b/resources/lang/en/recaptcha.php @@ -0,0 +1,6 @@ +<?php + +return [ + 'error' => 'An error occurred when validating the form, please try again', + 'not_enough_score' => 'You have not obtained a fairly high score when validation of the reCAPTCHA' +]; diff --git a/resources/lang/en/validation.php b/resources/lang/en/validation.php index a65914f..0307003 100644 --- a/resources/lang/en/validation.php +++ b/resources/lang/en/validation.php @@ -1,151 +1,233 @@ <?php +declare(strict_types=1); + return [ - - /* - |-------------------------------------------------------------------------- - | Validation Language Lines - |-------------------------------------------------------------------------- - | - | The following language lines contain the default error messages used by - | the validator class. Some of these rules have multiple versions such - | as the size rules. Feel free to tweak each of these messages here. - | - */ - - 'accepted' => 'The :attribute must be accepted.', - 'active_url' => 'The :attribute is not a valid URL.', - 'after' => 'The :attribute must be a date after :date.', - 'after_or_equal' => 'The :attribute must be a date after or equal to :date.', - 'alpha' => 'The :attribute may only contain letters.', - 'alpha_dash' => 'The :attribute may only contain letters, numbers, dashes and underscores.', - 'alpha_num' => 'The :attribute may only contain letters and numbers.', - 'array' => 'The :attribute must be an array.', - 'before' => 'The :attribute must be a date before :date.', - 'before_or_equal' => 'The :attribute must be a date before or equal to :date.', - 'between' => [ + 'accepted' => 'The :attribute must be accepted.', + 'accepted_if' => 'The :attribute must be accepted when :other is :value.', + 'active_url' => 'The :attribute is not a valid URL.', + 'after' => 'The :attribute must be a date after :date.', + 'after_or_equal' => 'The :attribute must be a date after or equal to :date.', + 'alpha' => 'The :attribute may only contain letters.', + 'alpha_dash' => 'The :attribute may only contain letters, numbers, and dashes.', + 'alpha_num' => 'The :attribute may only contain letters and numbers.', + 'array' => 'The :attribute must be an array.', + 'ascii' => 'The :attribute field must only contain single-byte alphanumeric characters and symbols.', + 'before' => 'The :attribute must be a date before :date.', + 'before_or_equal' => 'The :attribute must be a date before or equal to :date.', + 'between' => [ + 'array' => 'The :attribute must have between :min and :max items.', + 'file' => 'The :attribute must be between :min and :max kilobytes.', 'numeric' => 'The :attribute must be between :min and :max.', - 'file' => 'The :attribute must be between :min and :max kilobytes.', - 'string' => 'The :attribute must be between :min and :max characters.', - 'array' => 'The :attribute must have between :min and :max items.', + 'string' => 'The :attribute must be between :min and :max characters.', ], - 'boolean' => 'The :attribute field must be true or false.', - 'confirmed' => 'The :attribute confirmation does not match.', - 'date' => 'The :attribute is not a valid date.', - 'date_equals' => 'The :attribute must be a date equal to :date.', - 'date_format' => 'The :attribute does not match the format :format.', - 'different' => 'The :attribute and :other must be different.', - 'digits' => 'The :attribute must be :digits digits.', - 'digits_between' => 'The :attribute must be between :min and :max digits.', - 'dimensions' => 'The :attribute has invalid image dimensions.', - 'distinct' => 'The :attribute field has a duplicate value.', - 'email' => 'The :attribute must be a valid email address.', - 'ends_with' => 'The :attribute must end with one of the following: :values.', - 'exists' => 'The selected :attribute is invalid.', - 'file' => 'The :attribute must be a file.', - 'filled' => 'The :attribute field must have a value.', - 'gt' => [ + 'boolean' => 'The :attribute field must be true or false.', + 'can' => 'The :attribute field contains an unauthorized value.', + 'confirmed' => 'The :attribute confirmation does not match.', + 'current_password' => 'The password is incorrect.', + 'date' => 'The :attribute is not a valid date.', + 'date_equals' => 'The :attribute must be a date equal to :date.', + 'date_format' => 'The :attribute does not match the format :format.', + 'decimal' => 'The :attribute field must have :decimal decimal places.', + 'declined' => 'The :attribute must be declined.', + 'declined_if' => 'The :attribute must be declined when :other is :value.', + 'different' => 'The :attribute and :other must be different.', + 'digits' => 'The :attribute must be :digits digits.', + 'digits_between' => 'The :attribute must be between :min and :max digits.', + 'dimensions' => 'The :attribute has invalid image dimensions.', + 'distinct' => 'The :attribute field has a duplicate value.', + 'doesnt_end_with' => 'The :attribute field must not end with one of the following: :values.', + 'doesnt_start_with' => 'The :attribute field must not start with one of the following: :values.', + 'email' => 'The :attribute must be a valid email address.', + 'ends_with' => 'The :attribute must end with one of the following: :values.', + 'enum' => 'The selected :attribute is invalid.', + 'exists' => 'The selected :attribute is invalid.', + 'file' => 'The :attribute must be a file.', + 'filled' => 'The :attribute field is required.', + 'gt' => [ + 'array' => 'The :attribute must have more than :value items.', + 'file' => 'The :attribute must be greater than :value kilobytes.', 'numeric' => 'The :attribute must be greater than :value.', - 'file' => 'The :attribute must be greater than :value kilobytes.', - 'string' => 'The :attribute must be greater than :value characters.', - 'array' => 'The :attribute must have more than :value items.', + 'string' => 'The :attribute must be greater than :value characters.', ], - 'gte' => [ + 'gte' => [ + 'array' => 'The :attribute must have :value items or more.', + 'file' => 'The :attribute must be greater than or equal :value kilobytes.', 'numeric' => 'The :attribute must be greater than or equal :value.', - 'file' => 'The :attribute must be greater than or equal :value kilobytes.', - 'string' => 'The :attribute must be greater than or equal :value characters.', - 'array' => 'The :attribute must have :value items or more.', + 'string' => 'The :attribute must be greater than or equal :value characters.', ], - 'image' => 'The :attribute must be an image.', - 'in' => 'The selected :attribute is invalid.', - 'in_array' => 'The :attribute field does not exist in :other.', - 'integer' => 'The :attribute must be an integer.', - 'ip' => 'The :attribute must be a valid IP address.', - 'ipv4' => 'The :attribute must be a valid IPv4 address.', - 'ipv6' => 'The :attribute must be a valid IPv6 address.', - 'json' => 'The :attribute must be a valid JSON string.', - 'lt' => [ + 'image' => 'The :attribute must be an image.', + 'in' => 'The selected :attribute is invalid.', + 'in_array' => 'The :attribute field does not exist in :other.', + 'integer' => 'The :attribute must be an integer.', + 'ip' => 'The :attribute must be a valid IP address.', + 'ipv4' => 'The :attribute must be a valid IPv4 address.', + 'ipv6' => 'The :attribute must be a valid IPv6 address.', + 'json' => 'The :attribute must be a valid JSON string.', + 'lowercase' => 'The :attribute field must be lowercase.', + 'lt' => [ + 'array' => 'The :attribute must have less than :value items.', + 'file' => 'The :attribute must be less than :value kilobytes.', 'numeric' => 'The :attribute must be less than :value.', - 'file' => 'The :attribute must be less than :value kilobytes.', - 'string' => 'The :attribute must be less than :value characters.', - 'array' => 'The :attribute must have less than :value items.', + 'string' => 'The :attribute must be less than :value characters.', ], - 'lte' => [ + 'lte' => [ + 'array' => 'The :attribute must not have more than :value items.', + 'file' => 'The :attribute must be less than or equal :value kilobytes.', 'numeric' => 'The :attribute must be less than or equal :value.', - 'file' => 'The :attribute must be less than or equal :value kilobytes.', - 'string' => 'The :attribute must be less than or equal :value characters.', - 'array' => 'The :attribute must not have more than :value items.', + 'string' => 'The :attribute must be less than or equal :value characters.', ], - 'max' => [ + 'mac_address' => 'The :attribute must be a valid MAC address.', + 'max' => [ + 'array' => 'The :attribute may not have more than :max items.', + 'file' => 'The :attribute may not be greater than :max kilobytes.', 'numeric' => 'The :attribute may not be greater than :max.', - 'file' => 'The :attribute may not be greater than :max kilobytes.', - 'string' => 'The :attribute may not be greater than :max characters.', - 'array' => 'The :attribute may not have more than :max items.', + 'string' => 'The :attribute may not be greater than :max characters.', ], - 'mimes' => 'The :attribute must be a file of type: :values.', - 'mimetypes' => 'The :attribute must be a file of type: :values.', - 'min' => [ + 'max_digits' => 'The :attribute field must not have more than :max digits.', + 'mimes' => 'The :attribute must be a file of type: :values.', + 'mimetypes' => 'The :attribute must be a file of type: :values.', + 'min' => [ + 'array' => 'The :attribute must have at least :min items.', + 'file' => 'The :attribute must be at least :min kilobytes.', 'numeric' => 'The :attribute must be at least :min.', - 'file' => 'The :attribute must be at least :min kilobytes.', - 'string' => 'The :attribute must be at least :min characters.', - 'array' => 'The :attribute must have at least :min items.', + 'string' => 'The :attribute must be at least :min characters.', ], - 'not_in' => 'The selected :attribute is invalid.', - 'not_regex' => 'The :attribute format is invalid.', - 'numeric' => 'The :attribute must be a number.', - 'password' => 'The password is incorrect.', - 'present' => 'The :attribute field must be present.', - 'regex' => 'The :attribute format is invalid.', - 'required' => 'The :attribute field is required.', - 'required_if' => 'The :attribute field is required when :other is :value.', - 'required_unless' => 'The :attribute field is required unless :other is in :values.', - 'required_with' => 'The :attribute field is required when :values is present.', - 'required_with_all' => 'The :attribute field is required when :values are present.', - 'required_without' => 'The :attribute field is required when :values is not present.', + 'min_digits' => 'The :attribute field must have at least :min digits.', + 'missing' => 'The :attribute field must be missing.', + 'missing_if' => 'The :attribute field must be missing when :other is :value.', + 'missing_unless' => 'The :attribute field must be missing unless :other is :value.', + 'missing_with' => 'The :attribute field must be missing when :values is present.', + 'missing_with_all' => 'The :attribute field must be missing when :values are present.', + 'multiple_of' => 'The :attribute must be a multiple of :value.', + 'not_in' => 'The selected :attribute is invalid.', + 'not_regex' => 'The :attribute format is invalid.', + 'numeric' => 'The :attribute must be a number.', + 'password' => [ + 'letters' => 'The :attribute field must contain at least one letter.', + 'mixed' => 'The :attribute field must contain at least one uppercase and one lowercase letter.', + 'numbers' => 'The :attribute field must contain at least one number.', + 'symbols' => 'The :attribute field must contain at least one symbol.', + 'uncompromised' => 'The given :attribute has appeared in a data leak. Please choose a different :attribute.', + ], + 'present' => 'The :attribute field must be present.', + 'prohibited' => 'The :attribute field is prohibited.', + 'prohibited_if' => 'The :attribute field is prohibited when :other is :value.', + 'prohibited_unless' => 'The :attribute field is prohibited unless :other is in :values.', + 'prohibits' => 'The :attribute field prohibits :other from being present.', + 'regex' => 'The :attribute format is invalid.', + 'required' => 'The :attribute field is required.', + 'required_array_keys' => 'The :attribute field must contain entries for: :values.', + 'required_if' => 'The :attribute field is required when :other is :value.', + 'required_if_accepted' => 'The :attribute field is required when :other is accepted.', + 'required_unless' => 'The :attribute field is required unless :other is in :values.', + 'required_with' => 'The :attribute field is required when :values is present.', + 'required_with_all' => 'The :attribute field is required when :values is present.', + 'required_without' => 'The :attribute field is required when :values is not present.', 'required_without_all' => 'The :attribute field is required when none of :values are present.', - 'same' => 'The :attribute and :other must match.', - 'size' => [ + 'same' => 'The :attribute and :other must match.', + 'size' => [ + 'array' => 'The :attribute must contain :size items.', + 'file' => 'The :attribute must be :size kilobytes.', 'numeric' => 'The :attribute must be :size.', - 'file' => 'The :attribute must be :size kilobytes.', - 'string' => 'The :attribute must be :size characters.', - 'array' => 'The :attribute must contain :size items.', + 'string' => 'The :attribute must be :size characters.', ], - 'starts_with' => 'The :attribute must start with one of the following: :values.', - 'string' => 'The :attribute must be a string.', - 'timezone' => 'The :attribute must be a valid zone.', - 'unique' => 'The :attribute has already been taken.', - 'uploaded' => 'The :attribute failed to upload.', - 'url' => 'The :attribute format is invalid.', - 'uuid' => 'The :attribute must be a valid UUID.', - - /* - |-------------------------------------------------------------------------- - | Custom Validation Language Lines - |-------------------------------------------------------------------------- - | - | Here you may specify custom validation messages for attributes using the - | convention "attribute.rule" to name the lines. This makes it quick to - | specify a specific custom language line for a given attribute rule. - | - */ - - 'custom' => [ - 'attribute-name' => [ - 'rule-name' => 'custom-message', + 'starts_with' => 'The :attribute must start with one of the following: :values', + 'string' => 'The :attribute must be a string.', + 'timezone' => 'The :attribute must be a valid zone.', + 'ulid' => 'The :attribute field must be a valid ULID.', + 'unique' => 'The :attribute has already been taken.', + 'uploaded' => 'The :attribute failed to upload.', + 'uppercase' => 'The :attribute field must be uppercase.', + 'url' => 'The :attribute format is invalid.', + 'uuid' => 'The :attribute must be a valid UUID.', + 'attributes' => [ + 'address' => 'address', + 'age' => 'age', + 'amount' => 'amount', + 'area' => 'area', + 'available' => 'available', + 'birthday' => 'birthday', + 'body' => 'body', + 'city' => 'city', + 'content' => 'content', + 'country' => 'country', + 'created_at' => 'created at', + 'creator' => 'creator', + 'current_password' => 'current password', + 'date' => 'date', + 'date_of_birth' => 'date of birth', + 'day' => 'day', + 'deleted_at' => 'deleted at', + 'description' => 'description', + 'district' => 'district', + 'duration' => 'duration', + 'email' => 'email', + 'excerpt' => 'excerpt', + 'filter' => 'filter', + 'first_name' => 'first name', + 'gender' => 'gender', + 'group' => 'group', + 'hour' => 'hour', + 'image' => 'image', + 'last_name' => 'last name', + 'lesson' => 'lesson', + 'line_address_1' => 'line address 1', + 'line_address_2' => 'line address 2', + 'message' => 'message', + 'middle_name' => 'middle name', + 'minute' => 'minute', + 'mobile' => 'mobile', + 'month' => 'month', + 'name' => 'name', + 'national_code' => 'national code', + 'number' => 'number', + 'password' => 'password', + 'password_confirmation' => 'password confirmation', + 'phone' => 'phone', + 'photo' => 'photo', + 'postal_code' => 'postal code', + 'price' => 'price', + 'province' => 'province', + 'recaptcha_response_field' => 'recaptcha response field', + 'remember' => 'remember', + 'restored_at' => 'restored at', + 'result_text_under_image' => 'result text under image', + 'role' => 'role', + 'second' => 'second', + 'sex' => 'sex', + 'short_text' => 'short text', + 'size' => 'size', + 'state' => 'state', + 'street' => 'street', + 'student' => 'student', + 'subject' => 'subject', + 'teacher' => 'teacher', + 'terms' => 'terms', + 'test_description' => 'test description', + 'test_locale' => 'test locale', + 'test_name' => 'test name', + 'text' => 'text', + 'time' => 'time', + 'title' => 'title', + 'updated_at' => 'updated at', + 'username' => 'username', + 'year' => 'year', + ], + 'custom' => [ + 'of_the' => [ + 'male' => 'of the ', + 'female' => 'of the ', + 'vowel' => 'of the ' ], + 'event' => 'event', + 'model' => 'target element', + 'order' => 'order', + 'publication' => 'publication', + 'activated' => 'activated', + 'target' => 'target', + 'static_page_type' => 'static page type', + 'seo_title' => 'SEO title', + 'seo_description' => 'SEO description', ], - - /* - |-------------------------------------------------------------------------- - | Custom Validation Attributes - |-------------------------------------------------------------------------- - | - | The following language lines are used to swap our attribute placeholder - | with something more reader friendly such as "E-Mail Address" instead - | of "email". This simply helps us make our message more expressive. - | - */ - - 'attributes' => [], - ]; diff --git a/resources/lang/en/vue.php b/resources/lang/en/vue.php new file mode 100644 index 0000000..b513341 --- /dev/null +++ b/resources/lang/en/vue.php @@ -0,0 +1,38 @@ +<?php + +return [ + 'ImageInputComponent' => [ + 'choose_a_file' => 'Choose a file', + 'change_source_picture' => 'Edit Source Image', + 'view_source_picture' => 'View image', + 'resize_source_picture' => 'Resize image', + 'resized_source_picture' => 'Image resized', + 'preview_picture' => 'Image preview', + 'change_picture_before_import' => 'Image modification before import', + 'close_window' => 'Close the window', + 'close_and_validate' => 'Validate the content and close the window', + 'close_window_without_validation' => 'Close the window without validating the content', + 'zoom_in' => 'Zoom in', + 'zoom_out' => 'Zoom out', + 'move_left' => 'Move left', + 'move_right' => 'Move right', + 'move_up' => 'Move up', + 'move_down' => 'Move down', + 'mirror_horizontal' => 'Horizontal mirror', + 'mirror_vertical' => 'Vertical mirror', + 'rotation' => 'Rotation', + 'rotate_rewind' => 'Counter-clockwise rotation', + 'rotate' => 'Clockwise rotation', + 'degres' => ':deg degrees', + 'validate' => 'Validate', + 'preview' => 'Preview', + ], + 'WordCounterComponent' => [ + 'words' => 'Words', + 'characters' => 'Characters' + ], + 'PasswordVisibility' => [ + 'hide' => 'Hide the password', + 'show' => 'Show the password', + ] +]; diff --git a/resources/lang/fr.json b/resources/lang/fr.json index d5601f8..37cb6d6 100644 --- a/resources/lang/fr.json +++ b/resources/lang/fr.json @@ -1,185 +1,54 @@ { - "A fresh verification link has been sent to your email address.": "Un nouveau lien de vérification a été envoyé à votre adresse email.", - "A new verification link has been sent to the email address you provided during registration.": "Un nouveau lien de vérification a été envoyé à l'adresse email que vous avez indiquée lors de votre inscription.", - "Add a new team member to your team, allowing them to collaborate with you." : "Ajoutez un nouveau membre de l'équipe à votre équipe, permettant de collaborer avec vous.", - "Add additional security to your account using two factor authentication." : "Ajoutez une sécurité supplémentaire à votre compte en utilisant l'authentification à deux facteurs.", - "Add Team Member" : "Ajoutez un membre d'équipe", - "Add" : "Ajoutez", - "Added." : "Ajouté", - "Administrator" : "Administrateur", - "Administrator users can perform any action." : "Les administrateurs peuvent faire n'importe quelle action.", - "All of the people that are part of this team." : "Toutes les personnes qui font partie de cette équipe.", + "(and :count more error)": "(et :count erreur en plus)", + "(and :count more errors)": "(et :count erreurs en plus)", + "A fresh verification link has been sent to your email address.": "Un nouveau lien de vérification a été envoyé à votre adresse e-mail.", "All rights reserved.": "Tous droits réservés.", - "Already registered?": "Déjà inscrit(e) ?", - "API Token Permissions" : "Autorisations de jeton API", - "API Token" : "Jeton API", - "API tokens allow third-party services to authenticate with our application on your behalf." : "Les jetons API permettent à des services tiers de s'authentifier auprès de notre application en votre nom.", - "API Tokens" : "Jeton API", - "Are you sure you want to delete this team? Once a team is deleted, all of its resources and data will be permanently deleted." : "Êtes-vous sûr de vouloir supprimer cette équipe ? Lorsqu'une équipe est supprimée, toutes les ressources et données associées seront supprimées de manière définitive.", - "Are you sure you want to delete your account? Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account." : "Êtes-vous sûr de vouloir supprimer votre compte ? Une fois que votre compte est supprimé, toutes les données associées seront supprimées définitivement. Pour confirmer que vous voulez supprimer définitivement votre compte, renseignez votre mot de passe.", - "Are you sure you would like to delete this API token?" : "Êtes-vous sûr de vouloir supprimer ce jeton API ?", - "Are you sure you would like to leave this team?" : "Êtes-vous sûr de vouloir quitter cette équipe ?", - "Are you sure you would like to remove this person from the team?" : "Êtes-vous sûr de vouloir supprimer cette personne de cette équipe ?", - "Before proceeding, please check your email for a verification link.": "Avant de continuer, veuillez vérifier vos emails, vous devriez avoir reçu un lien de vérification.", - "Browser Sessions" : "Sessions de navigateur", - "click here to request another": "cliquez ici pour faire une nouvelle demande", - "Close" : "Fermer", - "Code" : "Code", - "Confirm Password": "Confirmez votre mot de passe", - "Confirm" : "Confirmer", - "Create a new team to collaborate with others on projects." : "Créer une nouvelle équipe pour collaborer avec d'autres personnes sur des projets.", - "Create API Token" : "Créer un jeton API", - "Create New Team" : "Créer une nouvelle équipe", - "Create Team" : "Créer l'équipe", - "Create" : "Créer", - "Created." : "Créé.", - "Current Password" : "Mot de passe actuel", + "Before proceeding, please check your email for a verification link.": "Avant de continuer, merci de vérifier votre e-mail contenant un lien de vérification.", + "click here to request another": "cliquez ici pour en demander un autre", + "Confirm Password": "Confirmer le mot de passe", "Dashboard": "Tableau de bord", - "Delete Account" : "Supprimer le compte", - "Delete API Token" : "Supprimer le jeton API", - "Delete Team" : "Supprimer l'équipe", - "Delete" : "Supprimer", - "Disable" : "Désactiver", - "Done." : "Terminé.", - "E-Mail Address": "Adresse email", - "Editor" : "Editeur", - "Editor users have the ability to read, create, and update." : "Les éditeurs peuvent lire, créer et mettre à jour", - "Email Password Reset Link": "Lien de réinitialisation du mot de passe", - "Email": "Email", - "Enable" : "Activé", - "Ensure your account is using a long, random password to stay secure." : "Assurez-vous d'utiliser un mot de passe long et aléatoire pour sécuriser votre compte", - "Error": "Erreur", - "For your security, please confirm your password to continue." : "Par mesure de sécurité, veuillez confirmer votre mot de passe pour continuer.", + "Email Address": "Adresse e-mail", "Forbidden": "Interdit", - "Forgot your password? No problem. Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.": "Mot de passe oublié ? Pas de soucis. Veuillez nous indiquer votre adresse email et nous vous enverrons un lien de réinitialisation du mot de passe.", - "Forgot your password?": "Vous avez oublié votre mot de passe ?", - "Forgot Your Password?": "Vous avez oublié votre mot de passe ?", - "Go Home": "Aller à l'accueil", - "Go to page :page" : "Aller à la page :page", + "Forgot Your Password?": "Mot de passe oublié ?", + "Go to page :page": "Aller à la page :page", "Hello!": "Bonjour !", - "hi": "salut", - "If necessary, you may logout of all of your other browser sessions across all of your devices. Some of your recent sessions are listed below; however, this list may not be exhaustive. If you feel your account has been compromised, you should also update your password." : "Si nécessaire, vous pouvez vous déconnecter de toutes vos sessions de navigateur de tous vos appareils. Certaines de vos sessions sont listées plus bas ; pourtant, cette liste peut ne pas être exhaustive. Si vous sentez que votre compte a été compromis, vous pouvez aussi mettre à jour votre mot de passe.", "If you did not create an account, no further action is required.": "Si vous n'avez pas créé de compte, vous pouvez ignorer ce message.", - "If you did not receive the email": "Si vous n'avez pas reçu l'email", + "If you did not receive the email": "Si vous n'avez pas reçu cet e-mail", "If you did not request a password reset, no further action is required.": "Si vous n'avez pas demandé de réinitialisation de mot de passe, vous pouvez ignorer ce message.", - "If you’re having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "Si le bouton \":actionText\" ne fonctionne pas, copiez/collez l'adresse ci-dessous dans votre navigateur :\n", - "Invalid signature.": "Signature invalide", - "Last active" : "Dernier actif", - "Last used" : "Dernière utilisation", - "Leave Team" : "Quitter l'équipe", - "Leave" : "Quitter", + "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "Si vous avez des difficultés à cliquer sur le bouton \":actionText\", copiez et collez l'URL ci-dessous\ndans votre navigateur Web :", + "Invalid JSON was returned from the route.": "Un JSON non valide a été renvoyé par la route.", "Login": "Connexion", - "Logout Other Browser Sessions" : "Déconnecter les sessions ouvertes sur d'autres navigateurs.", "Logout": "Déconnexion", - "Manage Account" : "Gérer le compte", - "Manage and logout your active sessions on other browsers and devices." : "Gérer et déconnecter vos sessions actives sur les autres navigateurs et appareils.", - "Manage API Tokens" : "Gérer les jetons API", - "Manage Role" : "Gérer le rôle", - "Manage Team" : "Gérer l'équipe", "Name": "Nom", - "Nevermind" : "Peu importe", - "New Password" : "Nouveau mot de passe", "Not Found": "Non trouvé", - "of" : "de", - "Oh no": "Oh non", - "Once a team is deleted, all of its resources and data will be permanently deleted. Before deleting this team, please download any data or information regarding this team that you wish to retain." : "Une fois qu'une équipe est supprimée, toutes ses données seront supprimées définitivement. Avant de supprimer cette équipe, veuillez télécharger toutes données ou informations de cette équipe.", - "Once your account is deleted, all of its resources and data will be permanently deleted. Before deleting your account, please download any data or information that you wish to retain." : "Une fois que votre compte est supprimé, toutes vos données sont supprimées définitivement. Avant de supprimer votre compte, veuillez télécharger vos données.", + "of": "de", "Page Expired": "Page expirée", - "Page Not Found": "Page introuvable", "Pagination Navigation": "Pagination", "Password": "Mot de passe", - "Permanently delete this team." : "Supprimer définitivement cette team.", - "Permanently delete your account." : "Supprimer définitivement ce compte.", - "Permissions" : "Permissions", - "Photo" : "Image", - "Please click the button below to verify your email address.": "Veuillez cliquer sur le bouton ci-dessous pour vérifier votre adresse email :", - "Please confirm access to your account by entering one of your emergency recovery codes.": "Veuillez confirmer l'accès à votre compte en entrant l'un des codes de récupération d'urgence.", - "Please confirm access to your account by entering the authentication code provided by your authenticator application.": "Veuillez confirmer l'accès à votre compte en entrant le code d'authentification fourni par votre application d'authentification.", - "Please confirm your password before continuing.": "Veuillez confirmer votre mot de passe avant de continuer", - "Please copy your new API token. For your security, it won't be shown again." : "Veuillez copier votre nouveau token API. Pour votre sécurité, il ne sera pas ré-affiché.", - "Please enter your password to confirm you would like to logout of your other browser sessions across all of your devices." : "Veuillez entrer votre mot de passe pour confirmer que vous voulez déconnecter toutes les autres sessions navigateur sur l'ensemble de vos appareils.", - "Please provide the email address of the person you would like to add to this team. The email address must be associated with an existing account." : "Veuillez renseigner l'adresse email de la personne que vous voulez ajouter à cette équipe. L'adresse email doit être associée à un compte existant.", - "Profile Information" : "Information du profil", - "Profile" : "Profil", - "Recovery Code" : "Code de récupération", + "Payment Required": "Paiement requis", + "Please click the button below to verify your email address.": "Veuillez cliquer sur le bouton ci-dessous pour vérifier votre adresse e-mail :", + "Please confirm your password before continuing.": "Veuillez confirmer votre mot de passe avant de continuer.", "Regards": "Cordialement", - "Regenerate Recovery Codes" : "Régénérer les codes de récupération", "Register": "Inscription", - "Remember me" : "Se souvenir de moi", "Remember Me": "Se souvenir de moi", - "Remove Photo" : "Supprimer l'image", - "Remove Team Member" : "Supprimer le membre d'équipe", - "Remove" : "Supprimer", - "Resend Verification Email": "Renvoyer l'email de vérification", - "Reset Password Notification": "Notification de réinitialisation du mot de passe", "Reset Password": "Réinitialisation du mot de passe", - "results" : "résultats", - "Role" : "Rôle", - "Save" : "Sauvegarder", - "Saved." : "Sauvegardé", - "Select A New Photo" : "Sélectionner une nouvelle image", + "Reset Password Notification": "Notification de réinitialisation du mot de passe", + "results": "résultats", "Send Password Reset Link": "Envoyer le lien de réinitialisation du mot de passe", "Server Error": "Erreur serveur", "Service Unavailable": "Service indisponible", - "Show Recovery Codes" : "Voir les codes de récupération", - "Showing" : "Montrant", - "Sorry, the page you are looking for could not be found.": "Désolé, la page que vous recherchez est introuvable.", - "Sorry, we are doing some maintenance. Please check back soon.": "Désolé, nous sommes en maintenance. Veuillez revenir plus tard.", - "Sorry, you are forbidden from accessing this page.": "Désolé, vous ne pouvez accéder à cette page.", - "Sorry, you are making too many requests to our servers.": "Désolé, vous faites trop de requêtes vers nos serveurs.", - "Sorry, you are not authorized to access this page.": "Désolé, vous n'êtes pas autorisé à accéder à cette page.", - "Sorry, your session has expired. Please refresh and try again.": "Désolé, votre session a expiré. Veuillez actualiser la page et réessayer.", - "Store these recovery codes in a secure password manager. They can be used to recover access to your account if your two factor authentication device is lost.": "Enregistrez ces codes dans un gestionnaire de mot de passe sécurisé. Ils peuvent être réutilisés pour accéder à votre compte si l'authentification à deux facteurs n'abouti pas.", - "Switch Teams" : "Permuter les équipes", - "Team Details" : "Détails de l'équipe", - "Team Members" : "Membres de l'équipe", - "Team Name" : "Nom de l'équipe", - "Team Owner" : "Propriétaire de l'équipe", - "Team Settings" : "Préférences de l'équipe", - "Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we just emailed to you? If you didn't receive the email, we will gladly send you another.":"Merci de vous être inscrit(e) ! Avant de commencer, veuillez vérifier votre adresse email en cliquant sur le lien que nous venons de vous envoyer. Si vous n'avez pas reçu cet email, nous vous en enverrons un nouveau avec plaisir.", - "The :attribute must be a valid role.": "Le :attribute doit être un rôle valide.", - "The :attribute must be at least :length characters and contain at least one number." : "Le champ :attribute doit avoir au moins :length caractères et contenir au moins un chiffre.", - "The :attribute must be at least :length characters and contain at least one special character." : "Le champ :attribute doit avoir au moins :length caractères et contenir au moins un caractère spécial.", - "The :attribute must be at least :length characters and contain at least one uppercase character and one number." : "Le champ :attribute doit avoir au moins :length caractères et contenir au moins une majuscule et un chiffre.", - "The :attribute must be at least :length characters and contain at least one uppercase character and one special character." : "Le champ :attribute doit avoir au moins :length caractères et contenir au moins une majuscule et un caractère spécial.", - "The :attribute must be at least :length characters and contain at least one uppercase character, one number, and one special character." : "Le champ :attribute doit avoir au moins :length caractères, et contenir au moins une majuscule, un chiffre et un caractère spécial.", - "The :attribute must be at least :length characters and contain at least one uppercase character." : "Le champ :attribute doit avoir au moins :length caractères et au moins une majuscule.", - "The :attribute must be at least :length characters." : "Le champ :attribute doit avoir au moins :length caractères.", - "The provided password does not match your current password." : "Le mot de passe indiqué ne correspond pas à votre mot de passe actuel.", - "The provided password was incorrect." : "Le mot de passé indiqué est incorrect.", - "The provided two factor authentication code was invalid." : "Le code d'authentification double facteur fourni est incorrect.", - "The team's name and owner information." : "Les informations concernant l'équipe et son propriétaire.", - "This action is unauthorized.": "Cette action n'est pas autorisée.", - "This device" : "Cet appareil", - "This password does not match our records.": "Ce mot de passe ne correspond pas à nos enregistrements.", + "Showing": "Montrant", + "The given data was invalid.": "La donnée renseignée est incorrecte.", + "The response is not a streamed response.": "La réponse n'est pas une réponse diffusée.", + "The response is not a view.": "La réponse n'est pas une vue.", "This password reset link will expire in :count minutes.": "Ce lien de réinitialisation du mot de passe expirera dans :count minutes.", - "This user already belongs to the team.": "Cet utilisateur appartient déjà à l'équipe.", - "to" : "à", - "Toggle navigation": "Basculer la navigation", - "Token Name" : "Nom du jeton", - "Too Many Attempts.": "Trop de tentatives.", + "to": "à", + "Toggle navigation": "Afficher / masquer le menu de navigation", "Too Many Requests": "Trop de requêtes", - "Two factor authentication is now enabled. Scan the following QR code using your phone's authenticator application.": "L'authentification à deux facteurs est maintenant activée. Enregistrez ce QR code dans votre application d'authentification.", - "Two Factor Authentication" : "Double authentification", "Unauthorized": "Non autorisé", - "Update Password" : "Mettre à jour le mot de passe", - "Update your account's profile information and email address." : "Modifier le profil associé à votre compte ainsi que votre adresse email.", - "Use a recovery code": "Utilisez un code de récupération", - "Use an authentication code": "Utilisez un code d'authentification", - "Verify Email Address": "Vérification de l'adresse email", - "Verify Your Email Address": "Vérifiez votre adresse email", - "We were unable to find a registered user with this email address.": "Nous n'avons pas pu trouver un utilisateur enregistré avec cette adresse e-mail.", - "We won't ask for your password again for a few hours.": "Nous ne vous demanderons plus votre mot de passe pour quelques heures", - "When two factor authentication is enabled, you will be prompted for a secure, random token during authentication. You may retrieve this token from your phone's Google Authenticator application." : "Lorsque l'authentification à deux facteurs est activée, vous serez invité à saisir un jeton aléatoire sécurisé lors de l'authentification. Vous pouvez récupérer ce jeton depuis l'application Google Authenticator de votre téléphone.", - "Whoops! Something went wrong." : "Oups ! Un problème est survenu.", + "Verify Email Address": "Vérifier l'adresse e-mail", + "Verify Your Email Address": "Vérifiez votre adresse e-mail", "Whoops!": "Oups !", - "Whoops, something went wrong on our servers.": "Oups, une erreur s'est produite sur nos serveurs.", - "You are logged in!": "Vous êtes connecté !", - "You are receiving this email because we received a password reset request for your account.": "Vous recevez cet email car nous avons reçu une demande de réinitialisation de mot de passe pour votre compte.", - "You have enabled two factor authentication." : "Vous avez activé la double authentification.", - "You have not enabled two factor authentication." : "Vous n'avez pas activé la double authentification.", - "You may delete any of your existing tokens if they are no longer needed." : "Vous pouvez supprimer n'importe lequel de vos jetons existants s'ils ne sont plus nécessaires.", - "You may not delete your personal team.": "Vous ne pouvez pas supprimer votre équipe personnelle.", - "You may not leave a team that you created.": "Vous ne pouvez pas quitter une équipe que vous avez créée.", - "Your email address is not verified.": "Votre adresse email n'a pas été vérifiée." -} + "You are logged in!": "Vous êtes connecté·e !", + "You are receiving this email because we received a password reset request for your account.": "Vous recevez cet e-mail car nous avons reçu une demande de réinitialisation de mot de passe pour votre compte." +} \ No newline at end of file diff --git a/resources/lang/fr/auth.php b/resources/lang/fr/auth.php index 188cf44..a22cd3f 100644 --- a/resources/lang/fr/auth.php +++ b/resources/lang/fr/auth.php @@ -1,18 +1,9 @@ <?php -return [ - /** - *-------------------------------------------------------------------------- - * Authentication Language Lines - *-------------------------------------------------------------------------- - * - * The following language lines are used during authentication for various - * messages that we need to display to the user. You are free to modify - * these language lines according to your application's requirements. - * - */ +declare(strict_types=1); - 'failed' => 'Ces identifiants ne correspondent pas à nos enregistrements', - 'password' => 'Le mot de passe fourni est incorrect.', +return [ + 'failed' => 'Ces identifiants ne correspondent pas à nos enregistrements.', + 'password' => 'Le mot de passe est incorrect', 'throttle' => 'Tentatives de connexion trop nombreuses. Veuillez essayer de nouveau dans :seconds secondes.', ]; diff --git a/resources/lang/fr/crud.php b/resources/lang/fr/crud.php index f5216b3..75b4dac 100644 --- a/resources/lang/fr/crud.php +++ b/resources/lang/fr/crud.php @@ -2,93 +2,92 @@ return [ 'actions' => [ - 'list' => 'Liste', - 'view' => 'Voir', - 'create' => 'Créer', - 'duplicate' => 'Dupliquer', - 'update' => 'Éditer', - 'delete' => 'Supprimer', - 'save' => 'Sauvegarder', - 'edit' => 'Mise à jour', - 'show' => 'Aperçu', - 'download' => 'Télécharger', - 'historic' => 'Historique', - 'credit' => 'Créditer', - 'stats' => 'Statistiques', + 'list' => 'liste', + 'view' => 'voir', + 'create' => 'créer', + 'duplicate' => 'dupliquer', + 'update' => 'éditer', + 'delete' => 'supprimer', + 'save' => 'sauvegarder', + 'edit' => 'mettre à jour', ], 'helpers' => [ - 'list' => 'Liste', - 'list_all' => 'Lister tous les :model', - 'create_model' => 'Créer :model', - 'view_model' => 'Voir :model', - 'update_model' => 'Éditer :model', + 'list' => 'Liste', + 'list_all' => 'Lister tous les :model', + 'create_model' => 'Créer :model', + 'view_model' => 'Voir :model', + 'update_model' => 'Éditer :model', 'duplicate_model' => 'Dupliquer :model', - 'delete_model' => 'Supprimer :model', - 'save_model' => 'Sauvegarder :model', - 'edit_model' => 'Mettre à jour :model', - 'credit_model' => 'Créditer :model', - 'no_results' => 'Aucun résultat pour :models', - 'publish' => 'Publié', - 'unpublish' => 'Dépublié', + 'delete_model' => 'Supprimer :model', + 'save_model' => 'Sauvegarder :model', + 'edit_model' => 'Mettre à jour :model', + 'no_results' => 'Aucun résultat pour :model', + 'publish' => 'Publié', + 'unpublish' => 'Dépublié', + 'seo_title' => 'Doit décrire précisément la page, recommandation pour un positionnement + optimal dans les moteurs de recherche (balise <title>).', + 'seo_description' => 'Doit décrire précisément le contenu de la page (balise <meta> description).', ], 'libs' => [ 'toolbox' => [ - 'assertFieldIsUnique' => 'La :valeur du champ :attribut est déjà utilisée', + 'assertFieldIsUnique' => 'La :valeur du champ :attribut est déjà utilisée', 'assertNoInternalBoUrls' => ':attribute : Il est interdit de coller l\'uri de back-office dans ce champ', - 'assertNoInternalUrls' => ':attribute : Il est interdit de coller une url de :appname dans ce champ.', + 'assertNoInternalUrls' => ':attribute : Il est interdit de coller une url de :appname dans ce champ.', ] ], 'messages' => [ - 'has_been_created' => ':model a été créé', - 'cannot_be_created' => ':model ne peut être créé', - 'has_been_updated' => ':model a été mis à jour', - 'cannot_be_updated' => ':model ne peut être mis à jour', - 'has_been_deleted' => ':model a été supprimé', - 'cannot_be_deleted' => ':model ne peut être supprimé', - 'cannot_be_deleted_relationship' => ':model ne peut être supprimé. - Veuillez d\'abord supprimer les :relationship associé(e)s', - 'order_changed' => 'L\'ordre a été changé !', - 'order_not_changed' => 'L\'ordre n\'a pas été changé !', + 'has_been_created' => ':model a été créé', + 'cannot_be_created' => ':model ne peut être créé', + 'has_been_updated' => ':model a été mis à jour', + 'cannot_be_updated' => ':model ne peut être mis à jour', + 'has_been_deleted' => ':model a été supprimé', + 'cannot_be_deleted' => ':model ne peut être supprimé', + 'order_changed' => 'L\'ordre a été changé !', + 'order_not_changed' => 'L\'ordre n\'a pas été changé !', 'publish_status_saved' => 'Changement de l\'état de publication réussit !', - 'right' => 'Vous n\'avez pas les droits pour accéder à cette partie !' + 'right' => 'Vous n\'avez pas les droits pour accéder à cette partie !', + 'theme_updated' => 'Le thème a été mis à jour !', ], 'minicrud' => [ - 'visibility_label' => 'Visibilité', - 'visibility_helper' => 'Vérifier qu\'il est visible.', - 'enum_select' => 'Sélectionnez un(e) :enum dans la liste.', - 'select' => 'Sélectionnez un(e) :entity dans la liste.', - 'associated_enum' => ':enum associé(e)', - 'associated' => ':entity associé(e)', - 'dimensions_must_be' => 'Doit être :widthpx (largeur) par :heightpx (hauteur).', + 'visibility_label' => 'Visibilité', + 'visibility_helper' => 'Vérifier qu\'il est visible.', + 'enum_select' => 'Sélectionnez un(e) :enum dans la liste.', + 'select' => 'Sélectionnez un(e) :entity dans la liste.', + 'associated_enum' => ':enum associé(e)', + 'associated' => ':entity associé(e)', + 'dimensions_must_be' => 'Doit être :widthpx (largeur) par :heightpx (hauteur).', 'picture_title_helper' => 'Utile pour les utilisateurs, s\'affiche en passant la souris sur l\'image', - 'picture_title_label' => 'Attribut du titre de l\'image', - 'picture_alt_helper' => 'Doit décrire votre image pour les moteurs de recherche et les aveugles (5 à 6 mots)', - 'picture_alt_label' => 'Attribut Alt de l\'image', - 'text_warning' => 'Relisez votre contenu avant de le publier (minimum :number caractères)', - 'url' => 'Doit être une url valide.', + 'picture_title_label' => 'Attribut du titre de l\'image', + 'picture_alt_helper' => 'Doit décrire votre image pour les moteurs de recherche et les aveugles (5 à 6 mots)', + 'picture_alt_label' => 'Attribut Alt de l\'image', + 'text_warning' => 'Relisez votre contenu avant de le publier (minimum :number caractères)', + 'url' => 'Doit être une url valide.', ], 'pagination' => [ - "previous" => "Page précédente", - "next" => "Page suivante", + "previous" => "Page précédente", + "next" => "Page suivante", "specific_page" => "Accéder à la page n°:id", 'paginate_list' => 'Nombre d\'éléments par page', ], 'other' => [ - 'up' => 'Modifier l\'ordre d\'apparition vers le haut', - 'down' => 'Modifier l\'ordre d\'apparition vers le bas', - 'no_model_found' => 'Aucun :model trouvé', - 'publish' => 'Publié', - 'unpublish' => 'Dépublié', - 'sort_ascending' => 'Trier :name dans l\'ordre croissant', - 'sort_descending' => 'Trier :name dans l\'ordre décroissant', - 'sort_delete' => 'Supprimer le tri', - 'sort_arrow' => 'Ici, les flèches sont utilisées pour trier et non pour filtrer', - 'right' => 'Vous n\'avez pas les droits', + 'up' => 'Modifier l\'ordre d\'apparition vers le haut', + 'down' => 'Modifier l\'ordre d\'apparition vers le bas', + 'no_model_found' => 'Aucun :model trouvé', + 'publish' => 'Publié', + 'unpublish' => 'Dépublié', + 'search_start' => 'Lancer la recherche', + 'search_delete' => 'Supprimer la recherche', + 'search_placeholder' => 'Vos mots clés ici...', + 'sort_ascending' => 'Trier :name dans l\'ordre croissant', + 'sort_descending' => 'Trier :name dans l\'ordre décroissant', + 'sort_delete' => 'Supprimer le tri', + 'sort_arrow' => 'Ici, les flèches sont utilisées pour trier et non pour filtrer', + 'right' => 'Vous n\'avez pas les droits', ] ]; diff --git a/resources/lang/fr/http-statuses.php b/resources/lang/fr/http-statuses.php new file mode 100644 index 0000000..af44920 --- /dev/null +++ b/resources/lang/fr/http-statuses.php @@ -0,0 +1,84 @@ +<?php + +declare(strict_types=1); + +return [ + '0' => 'Erreur inconnue', + '100' => 'Continuer', + '101' => 'Protocoles de commutation', + '102' => 'En traitement', + '200' => 'D\'accord', + '201' => 'Créé', + '202' => 'Accepté', + '203' => 'Informations non autorisées', + '204' => 'Pas content', + '205' => 'Réinitialiser le contenu', + '206' => 'Contenu partiel', + '207' => 'Multi-statut', + '208' => 'Déjà rapporté', + '226' => 'J\'ai l\'habitude', + '300' => 'Choix multiples', + '301' => 'Déplacé de façon permanente', + '302' => 'A trouvé', + '303' => 'Voir autre', + '304' => 'Non modifié', + '305' => 'Utiliser un proxy', + '307' => 'Redirection temporaire', + '308' => 'Redirection permanente', + '400' => 'Mauvaise Demande', + '401' => 'Non autorisé', + '402' => 'Paiement Requis', + '403' => 'Interdit', + '404' => 'Page non trouvée', + '405' => 'Méthode Non Autorisée', + '406' => 'Pas acceptable', + '407' => 'Authentification proxy requise', + '408' => 'Délai d\'expiration de la demande', + '409' => 'Conflit', + '410' => 'Disparu', + '411' => 'Longueur requise', + '412' => 'La précondition a échoué', + '413' => 'Charge utile trop grande', + '414' => 'URI trop long', + '415' => 'Type de support non supporté', + '416' => 'Plage non satisfaisante', + '417' => 'Échec de l\'attente', + '418' => 'Je suis une théière', + '419' => 'La session a expiré', + '421' => 'Demande mal dirigée', + '422' => 'Entité non traitable', + '423' => 'Fermé à clef', + '424' => 'Dépendance échouée', + '425' => 'Trop tôt', + '426' => 'Mise à niveau requise', + '428' => 'Condition préalable requise', + '429' => 'Trop de demandes', + '431' => 'Demander des champs d\'en-tête trop grands', + '444' => 'Connexion fermée sans réponse', + '449' => 'Réessayer avec', + '451' => 'Indisponible pour des raisons légales', + '499' => 'Demande fermée du client', + '500' => 'Erreur Interne du Serveur', + '501' => 'Pas mis en œuvre', + '502' => 'Mauvaise passerelle', + '503' => 'Mode de Maintenance', + '504' => 'Délai d\'attente de la passerelle', + '505' => 'Version HTTP non prise en charge', + '506' => 'La variante négocie également', + '507' => 'Espace insuffisant', + '508' => 'Boucle détectée', + '509' => 'Limite de bande passante dépassée', + '510' => 'Non prolongé', + '511' => 'Authentification réseau requise', + '520' => 'Erreur inconnue', + '521' => 'Le serveur Web est en panne', + '522' => 'La connexion a expiré', + '523' => 'L\'origine est inaccessible', + '524' => 'Un dépassement de délai s\'est produit', + '525' => 'Échec de la prise de contact SSL', + '526' => 'Certificat SSL invalide', + '527' => 'Erreur de canon ferroviaire', + '598' => 'Erreur de délai de lecture réseau', + '599' => 'Erreur de délai de connexion réseau', + 'unknownError' => 'Erreur inconnue', +]; diff --git a/resources/lang/fr/models.php b/resources/lang/fr/models.php index 3d01b35..dfa964b 100644 --- a/resources/lang/fr/models.php +++ b/resources/lang/fr/models.php @@ -2,8 +2,10 @@ return [ 'classes' => [ + 'user' => 'utilisateur', + 'activity_log' => 'journal d\'activité|journaux d\'activité', + 'static_page' => 'page statique|pages statiques', 'bannedips' => 'IP bannies', - 'contents' => 'Contenus', 'faq-questions' => 'Question pour la FAQ|Questions pour la FAQ', 'faq-themes' => 'Thème pour la FAQ|Thèmes pour la FAQ', 'gain-views' => 'Page de gain|Pages de gain', @@ -12,6 +14,5 @@ return [ 'products' => 'Produits', 'winners' => 'Gagnants', 'settings' => 'Réglages', - 'codes' => 'Codes uniques' ] ]; diff --git a/resources/lang/fr/pagination.php b/resources/lang/fr/pagination.php index 8c1080e..ccd0f82 100644 --- a/resources/lang/fr/pagination.php +++ b/resources/lang/fr/pagination.php @@ -1,17 +1,12 @@ <?php -return [ - /* - |-------------------------------------------------------------------------- - | Pagination Language Lines - |-------------------------------------------------------------------------- - | - | The following language lines are used by the paginator library to build - | the simple pagination links. You are free to change them to anything - | you want to customize your views to better match your application. - | - */ +declare(strict_types=1); - 'previous' => '« Précédent', - 'next' => 'Suivant »', +return [ + 'next' => 'Suivant »', + 'previous' => '« Précédent', + 'previous_page' => 'Page précédente', + 'next_page' => 'Page suivante', + 'specific_page' => 'Aller à la page n°:id', + 'paginate_list' => 'Nombre d\'élements par page', ]; diff --git a/resources/lang/fr/passwords.php b/resources/lang/fr/passwords.php index 2f898d3..75ae148 100644 --- a/resources/lang/fr/passwords.php +++ b/resources/lang/fr/passwords.php @@ -1,20 +1,11 @@ <?php -return [ - /* - |-------------------------------------------------------------------------- - | Password Reminder Language Lines - |-------------------------------------------------------------------------- - | - | The following language lines are the default lines which match reasons - | that are given by the password broker for a password update attempt - | has failed, such as for an invalid token or invalid new password. - | - */ +declare(strict_types=1); +return [ 'reset' => 'Votre mot de passe a été réinitialisé !', 'sent' => 'Nous vous avons envoyé par email le lien de réinitialisation du mot de passe !', 'throttled' => 'Veuillez patienter avant de réessayer.', - 'token' => "Ce jeton de réinitialisation du mot de passe n'est pas valide.", - 'user' => "Aucun utilisateur n'a été trouvé avec cette adresse email.", + 'token' => 'Ce jeton de réinitialisation du mot de passe n\'est pas valide.', + 'user' => 'Aucun utilisateur n\'a été trouvé avec cette adresse email.', ]; diff --git a/resources/lang/fr/recaptcha.php b/resources/lang/fr/recaptcha.php new file mode 100644 index 0000000..690105e --- /dev/null +++ b/resources/lang/fr/recaptcha.php @@ -0,0 +1,6 @@ +<?php + +return [ + 'error' => 'Une erreur s\'est produite lors de la validation du formulaire, veuillez réessayer', + 'not_enough_score' => 'Vous n\'avez pas obtenu un score assez élevé lors de la validation du reCAPTCHA' +]; diff --git a/resources/lang/fr/validation.php b/resources/lang/fr/validation.php index 7a9f93a..cb2ebd6 100644 --- a/resources/lang/fr/validation.php +++ b/resources/lang/fr/validation.php @@ -1,19 +1,10 @@ <?php +declare(strict_types=1); + return [ - - /** - *-------------------------------------------------------------------------- - * Validation Language Lines - *-------------------------------------------------------------------------- - * - * The following language lines contain the default error messages used by - * the validator class. Some of these rules have multiple versions such - * as the size rules. Feel free to tweak each of these messages. - * - */ - 'accepted' => 'Le champ :attribute doit être accepté.', + 'accepted_if' => 'Le champ :attribute doit être accepté quand :other a la valeur :value.', 'active_url' => 'Le champ :attribute n\'est pas une URL valide.', 'after' => 'Le champ :attribute doit être une date postérieure au :date.', 'after_or_equal' => 'Le champ :attribute doit être une date postérieure ou égale au :date.', @@ -21,7 +12,7 @@ return [ 'alpha_dash' => 'Le champ :attribute doit contenir uniquement des lettres, des chiffres et des tirets.', 'alpha_num' => 'Le champ :attribute doit contenir uniquement des chiffres et des lettres.', 'array' => 'Le champ :attribute doit être un tableau.', - 'attached' => ':attribute est déjà attaché(e).', + 'ascii' => 'Le champ :attribute ne doit contenir que des caractères alphanumériques et des symboles codés sur un octet.', 'before' => 'Le champ :attribute doit être une date antérieure au :date.', 'before_or_equal' => 'Le champ :attribute doit être une date antérieure ou égale au :date.', 'between' => [ @@ -31,18 +22,25 @@ return [ 'string' => 'Le texte :attribute doit contenir entre :min et :max caractères.', ], 'boolean' => 'Le champ :attribute doit être vrai ou faux.', - 'color' => 'Le champ :attribute doit être au format hexadécimal (ex: #FFFFFF)', + 'can' => 'Le champ :attribute contient une valeur non autorisée.', 'confirmed' => 'Le champ de confirmation :attribute ne correspond pas.', + 'current_password' => 'Le mot de passe est incorrect.', 'date' => 'Le champ :attribute n\'est pas une date valide.', 'date_equals' => 'Le champ :attribute doit être une date égale à :date.', 'date_format' => 'Le champ :attribute ne correspond pas au format :format.', + 'decimal' => 'Le champ :attribute doit comporter :decimal décimales.', + 'declined' => 'Le champ :attribute doit être décliné.', + 'declined_if' => 'Le champ :attribute doit être décliné quand :other a la valeur :value.', 'different' => 'Les champs :attribute et :other doivent être différents.', 'digits' => 'Le champ :attribute doit contenir :digits chiffres.', 'digits_between' => 'Le champ :attribute doit contenir entre :min et :max chiffres.', 'dimensions' => 'La taille de l\'image :attribute n\'est pas conforme.', 'distinct' => 'Le champ :attribute a une valeur en double.', - 'email' => 'Le champ :attribute doit être une adresse email valide.', + 'doesnt_end_with' => 'Le champ :attribute ne doit pas finir avec une des valeurs suivantes : :values.', + 'doesnt_start_with' => 'Le champ :attribute ne doit pas commencer avec une des valeurs suivantes : :values.', + 'email' => 'Le champ :attribute doit être une adresse e-mail valide.', 'ends_with' => 'Le champ :attribute doit se terminer par une des valeurs suivantes : :values', + 'enum' => 'Le champ :attribute sélectionné est invalide.', 'exists' => 'Le champ :attribute sélectionné est invalide.', 'file' => 'Le champ :attribute doit être un fichier.', 'filled' => 'Le champ :attribute doit avoir une valeur.', @@ -66,6 +64,7 @@ return [ 'ipv4' => 'Le champ :attribute doit être une adresse IPv4 valide.', 'ipv6' => 'Le champ :attribute doit être une adresse IPv6 valide.', 'json' => 'Le champ :attribute doit être un document JSON valide.', + 'lowercase' => 'Le champ :attribute doit être en minuscules.', 'lt' => [ 'array' => 'Le tableau :attribute doit contenir moins de :value éléments.', 'file' => 'La taille du fichier de :attribute doit être inférieure à :value kilo-octets.', @@ -78,33 +77,49 @@ return [ 'numeric' => 'La valeur de :attribute doit être inférieure ou égale à :value.', 'string' => 'Le texte :attribute doit contenir au plus :value caractères.', ], + 'mac_address' => 'Le champ :attribute doit être une adresse MAC valide.', 'max' => [ - 'array' => 'Le tableau :attribute ne peut contenir plus de :max éléments.', + 'array' => 'Le tableau :attribute ne peut pas contenir plus que :max éléments.', 'file' => 'La taille du fichier de :attribute ne peut pas dépasser :max kilo-octets.', - 'numeric' => 'La valeur de :attribute ne peut être supérieure à :max.', - 'string' => 'Le texte de :attribute ne peut contenir plus de :max caractères.', + 'numeric' => 'La valeur de :attribute ne peut pas être supérieure à :max.', + 'string' => 'Le texte de :attribute ne peut pas contenir plus de :max caractères.', ], + 'max_digits' => 'Le champ :attribute ne doit pas avoir plus de :max chiffres.', 'mimes' => 'Le champ :attribute doit être un fichier de type : :values.', 'mimetypes' => 'Le champ :attribute doit être un fichier de type : :values.', 'min' => [ 'array' => 'Le tableau :attribute doit contenir au moins :min éléments.', - 'file' => 'La taille du fichier de :attribute doit être supérieure à :min kilo-octets.', + 'file' => 'La taille du fichier de :attribute doit être supérieure ou égale à :min kilo-octets.', 'numeric' => 'La valeur de :attribute doit être supérieure ou égale à :min.', - 'string' => 'Le texte :attribute doit contenir au moins :min caractères.', + 'string' => 'Le texte de :attribute doit contenir au moins :min caractères.', ], + 'min_digits' => 'Le champ :attribute doit avoir au moins :min chiffres.', + 'missing' => 'Le champ :attribute doit être manquant.', + 'missing_if' => 'Le champ :attribute doit être manquant quand :other a la valeur :value.', + 'missing_unless' => 'Le champ :attribute doit être manquant sauf si :other a la valeur :value.', + 'missing_with' => 'Le champ :attribute doit être manquant quand :values est présent.', + 'missing_with_all' => 'Le champ :attribute doit être manquant quand :values sont présents.', 'multiple_of' => 'La valeur de :attribute doit être un multiple de :value', 'not_in' => 'Le champ :attribute sélectionné n\'est pas valide.', 'not_regex' => 'Le format du champ :attribute n\'est pas valide.', 'numeric' => 'Le champ :attribute doit contenir un nombre.', - 'password' => 'Le mot de passe est incorrect', + 'password' => [ + 'letters' => 'Le champ :attribute doit contenir au moins une lettre.', + 'mixed' => 'Le champ :attribute doit contenir au moins une majuscule et une minuscule.', + 'numbers' => 'Le champ :attribute doit contenir au moins un chiffre.', + 'symbols' => 'Le champ :attribute doit contenir au moins un symbole.', + 'uncompromised' => 'La valeur du champ :attribute est apparue dans une fuite de données. Veuillez choisir une valeur différente.', + ], 'present' => 'Le champ :attribute doit être présent.', 'prohibited' => 'Le champ :attribute est interdit.', 'prohibited_if' => 'Le champ :attribute est interdit quand :other a la valeur :value.', 'prohibited_unless' => 'Le champ :attribute est interdit à moins que :other est l\'une des valeurs :values.', + 'prohibits' => 'Le champ :attribute interdit :other d\'être présent.', 'regex' => 'Le format du champ :attribute est invalide.', - 'relatable' => ':attribute n\'est sans doute pas associé(e) avec cette donnée.', 'required' => 'Le champ :attribute est obligatoire.', + 'required_array_keys' => 'Le champ :attribute doit contenir des entrées pour : :values.', 'required_if' => 'Le champ :attribute est obligatoire quand la valeur de :other est :value.', + 'required_if_accepted' => 'Le champ :attribute est obligatoire quand le champ :other a été accepté.', 'required_unless' => 'Le champ :attribute est obligatoire sauf si :other est :values.', 'required_with' => 'Le champ :attribute est obligatoire quand :values est présent.', 'required_with_all' => 'Le champ :attribute est obligatoire quand :values sont présents.', @@ -120,61 +135,100 @@ return [ 'starts_with' => 'Le champ :attribute doit commencer avec une des valeurs suivantes : :values', 'string' => 'Le champ :attribute doit être une chaîne de caractères.', 'timezone' => 'Le champ :attribute doit être un fuseau horaire valide.', + 'ulid' => 'Le champ :attribute doit être un ULID valide.', 'unique' => 'La valeur du champ :attribute est déjà utilisée.', 'uploaded' => 'Le fichier du champ :attribute n\'a pu être téléversé.', + 'uppercase' => 'Le champ :attribute doit être en majuscules.', 'url' => 'Le format de l\'URL de :attribute n\'est pas valide.', 'uuid' => 'Le champ :attribute doit être un UUID valide', - 'ip' => 'Le champ :attribute doit être une adresse IP valide', - 'ean' => 'Le champ :attribute doit être un code EAN valide (7, 8 ou 13 chiffres)', - 'custom' => [ - 'attribute-name' => [ - 'rule-name' => 'custom-message', - ], + 'attributes' => [ + 'address' => 'adresse', + 'age' => 'âge', + 'amount' => 'montant', + 'area' => 'zone', + 'available' => 'disponible', + 'birthday' => 'anniversaire', + 'body' => 'corps', + 'city' => 'ville', + 'content' => 'contenu', + 'country' => 'pays', + 'created_at' => 'créé à', + 'creator' => 'créateur', + 'current_password' => 'mot de passe actuel', + 'date' => 'Date', + 'date_of_birth' => 'date de naissance', + 'day' => 'jour', + 'deleted_at' => 'supprimé à', + 'description' => 'la description', + 'district' => 'quartier', + 'duration' => 'durée', + 'email' => 'adresse e-mail', + 'excerpt' => 'extrait', + 'filter' => 'filtre', + 'first_name' => 'prénom', + 'gender' => 'genre', + 'group' => 'groupe', + 'hour' => 'heure', + 'image' => 'image', + 'last_name' => 'nom', + 'lesson' => 'leçon', + 'line_address_1' => 'ligne d\'adresse 1', + 'line_address_2' => 'ligne d\'adresse 2', + 'message' => 'message', + 'middle_name' => 'deuxième prénom', + 'minute' => 'minute', + 'mobile' => 'portable', + 'month' => 'mois', + 'name' => 'nom', + 'national_code' => 'code national', + 'number' => 'numéro', + 'password' => 'mot de passe', + 'password_confirmation' => 'confirmation du mot de passe', + 'phone' => 'téléphone', + 'photo' => 'photo', + 'postal_code' => 'code postal', + 'price' => 'prix', + 'province' => 'région', + 'recaptcha_response_field' => 'champ de réponse recaptcha', + 'remember' => 'se souvenir', + 'restored_at' => 'restauré à', + 'result_text_under_image' => 'texte de résultat sous l\'image', + 'role' => 'rôle', + 'second' => 'seconde', + 'sex' => 'sexe', + 'short_text' => 'texte court', + 'size' => 'taille', + 'state' => 'état', + 'street' => 'rue', + 'student' => 'étudiant', + 'subject' => 'sujet', + 'teacher' => 'professeur', + 'terms' => 'conditions', + 'test_description' => 'description test', + 'test_locale' => 'localisation test', + 'test_name' => 'nom test', + 'text' => 'texte', + 'time' => 'heure', + 'title' => 'titre', + 'updated_at' => 'mis à jour à', + 'username' => 'nom d\'utilisateur', + 'year' => 'année', ], - - - /** - *-------------------------------------------------------------------------- - * Custom Validation Attributes - *-------------------------------------------------------------------------- - * - * The following language lines are used to swap attribute place-holders - * with something more reader friendly such as E-Mail Address instead - * of "email". This simply helps us make messages a little cleaner. - * - */ - - 'attributes' => [ - 'name' => 'nom', - 'username' => "nom d'utilisateur", - 'email' => 'adresse email', - 'first_name' => 'prénom', - 'last_name' => 'nom', - 'password' => 'mot de passe', - 'password_confirmation' => 'confirmation du mot de passe', - 'city' => 'ville', - 'country' => 'pays', - 'address' => 'adresse', - 'phone' => 'téléphone', - 'mobile' => 'portable', - 'age' => 'âge', - 'sex' => 'sexe', - 'gender' => 'genre', - 'day' => 'jour', - 'month' => 'mois', - 'year' => 'année', - 'hour' => 'heure', - 'minute' => 'minute', - 'second' => 'seconde', - 'title' => 'titre', - 'content' => 'contenu', - 'description' => 'description', - 'excerpt' => 'extrait', - 'date' => 'date', - 'time' => 'heure', - 'available' => 'disponible', - 'size' => 'taille', - 'birth_date' => 'date de naissance', + 'custom' => [ + 'of_the' => [ + 'male' => 'du ', + 'female' => 'de la ', + 'vowel' => 'de l\'' + ], + 'event' => 'évènement', + 'model' => 'élément ciblé', + 'order' => 'ordre', + 'publication' => 'publication', + 'activated' => 'activé', + 'target' => 'cible', + 'static_page_type' => 'type de page', + 'seo_title' => 'titre SEO', + 'seo_description' => 'description SEO', 'ip' => 'IP', 'code' => 'code', diff --git a/resources/lang/fr/vue.php b/resources/lang/fr/vue.php index 4d87ce8..9491a29 100644 --- a/resources/lang/fr/vue.php +++ b/resources/lang/fr/vue.php @@ -2,29 +2,37 @@ return [ 'ImageInputComponent' => [ - 'choose_a_file' => 'Choose a file', - 'change_source_picture' => 'Edit Source Image', - 'view_source_picture' => 'View image', - 'resize_source_picture' => 'Resize image', - 'resized_source_picture' => 'Image resized', - 'preview_picture' => 'Image preview', - 'change_picture_before_import' => 'Image modification before import', - 'close_window' => 'Close the window', - 'close_and_validate' => 'Validate the content and close the window', - 'close_window_without_validation' => 'Close the window without validating the content', - 'zoom_in' => 'Zoom in', - 'zoom_out' => 'Zoom out', - 'move_left' => 'Move left', - 'move_right' => 'Move right', - 'move_up' => 'Move up', - 'move_down' => 'Move down', - 'mirror_horizontal' => 'Horizontal mirror', - 'mirror_vertical' => 'Vertical mirror', - 'rotation' => 'Rotation', - 'rotate_rewind' => 'Counter-clockwise rotation', - 'rotate' => 'Clockwise rotation', - 'degres' => ':deg egrees', - 'validate' => 'Validate', - 'preview' => 'Preview', + 'choose_a_file' => 'Choisir un fichier', + 'change_source_picture' => 'Modifier l\'image source', + 'view_source_picture' => 'Voir l\'image', + 'resize_source_picture' => 'Redimensionner l\'image', + 'resized_source_picture' => 'Image redimensionnée', + 'preview_picture' => 'Aperçu de l\'image', + 'change_picture_before_import' => 'Modification de l\'image avant l\'importation', + 'close_window' => 'Fermer la fenêtre', + 'close_and_validate' => 'Valider le contenu et fermer la fenêtre', + 'close_window_without_validation' => 'Fermer la fenêtre sans valider le contenu', + 'zoom_in' => 'Zoom avant', + 'zoom_out' => 'Zoom arrière', + 'move_left' => 'Déplacer vers la gauche', + 'move_right' => 'Déplacer vers la droite', + 'move_up' => 'Déplacer en vers le haut', + 'move_down' => 'Déplacer en vers le bas', + 'mirror_horizontal' => 'Miroir horizontal', + 'mirror_vertical' => 'Miroir vertical', + 'rotation' => 'Rotation', + 'rotate_rewind' => 'Rotation dans le sens inverse des aiguilles d\'une montre', + 'rotate' => 'Rotation dans le sens des aiguilles d\'une montre', + 'degres' => ':deg degrés', + 'validate' => 'Valider', + 'preview' => 'Prévisualisation', + ], + 'WordCounterComponent' => [ + 'words' => 'Mots', + 'characters' => 'Caractères' + ], + 'PasswordVisibility' => [ + 'hide' => 'Cacher le mot de passe', + 'show' => 'Dévoiler le mot de passe', ] ]; diff --git a/resources/sass/bo/base/_bootstrap-overload.scss b/resources/sass/base/_bootstrap-overload.scss similarity index 100% rename from resources/sass/bo/base/_bootstrap-overload.scss rename to resources/sass/base/_bootstrap-overload.scss diff --git a/resources/sass/bo/base/_common.scss b/resources/sass/base/_common.scss similarity index 100% rename from resources/sass/bo/base/_common.scss rename to resources/sass/base/_common.scss diff --git a/resources/sass/bo/base/_fonts.scss b/resources/sass/base/_fonts.scss similarity index 100% rename from resources/sass/bo/base/_fonts.scss rename to resources/sass/base/_fonts.scss diff --git a/resources/sass/bo/bo.scss b/resources/sass/bo.scss similarity index 100% rename from resources/sass/bo/bo.scss rename to resources/sass/bo.scss diff --git a/resources/sass/bo/_variables.scss b/resources/sass/bo/_variables.scss deleted file mode 100644 index 0f89db0..0000000 --- a/resources/sass/bo/_variables.scss +++ /dev/null @@ -1,19 +0,0 @@ -// Body -$body-bg: #f8fafc; - -// Typography -$font-family-sans-serif: "Nunito", sans-serif; -$font-size-base: 0.9rem; -$line-height-base: 1.6; - -// Colors -$blue: #3490dc; -$indigo: #6574cd; -$purple: #9561e2; -$pink: #f66d9b; -$red: #e3342f; -$orange: #f6993f; -$yellow: #ffed4a; -$green: #38c172; -$teal: #4dc0b5; -$cyan: #6cb2eb; diff --git a/resources/sass/bo/pages/.gitkeep b/resources/sass/pages/.gitkeep similarity index 100% rename from resources/sass/bo/pages/.gitkeep rename to resources/sass/pages/.gitkeep diff --git a/resources/sass/bo/partials/.gitkeep b/resources/sass/partials/.gitkeep similarity index 100% rename from resources/sass/bo/partials/.gitkeep rename to resources/sass/partials/.gitkeep diff --git a/resources/sass/bo/utilities/_bootstrap-custom.scss b/resources/sass/utilities/_bootstrap-custom.scss similarity index 100% rename from resources/sass/bo/utilities/_bootstrap-custom.scss rename to resources/sass/utilities/_bootstrap-custom.scss diff --git a/resources/sass/bo/utilities/_bootstrap-variables.scss b/resources/sass/utilities/_bootstrap-variables.scss similarity index 100% rename from resources/sass/bo/utilities/_bootstrap-variables.scss rename to resources/sass/utilities/_bootstrap-variables.scss diff --git a/resources/sass/bo/utilities/_mixins.scss b/resources/sass/utilities/_mixins.scss similarity index 100% rename from resources/sass/bo/utilities/_mixins.scss rename to resources/sass/utilities/_mixins.scss diff --git a/resources/sass/bo/utilities/_variables.scss b/resources/sass/utilities/_variables.scss similarity index 100% rename from resources/sass/bo/utilities/_variables.scss rename to resources/sass/utilities/_variables.scss diff --git a/resources/sass/bo/vendor/_bootstrap.scss b/resources/sass/vendor/_bootstrap.scss similarity index 100% rename from resources/sass/bo/vendor/_bootstrap.scss rename to resources/sass/vendor/_bootstrap.scss diff --git a/resources/sass/bo/vendor/_ckEditorContent.scss b/resources/sass/vendor/_ckEditorContent.scss similarity index 100% rename from resources/sass/bo/vendor/_ckEditorContent.scss rename to resources/sass/vendor/_ckEditorContent.scss diff --git a/resources/ts/bo/base/common.ts b/resources/ts/base/common.ts similarity index 95% rename from resources/ts/bo/base/common.ts rename to resources/ts/base/common.ts index 89c7b86..1ef2123 100644 --- a/resources/ts/bo/base/common.ts +++ b/resources/ts/base/common.ts @@ -1,5 +1,5 @@ import type { SweetAlertResult } from "sweetalert2"; -import sweetalert from "../../modules/sweetalertConfirm"; +import sweetalert from "../modules/sweetalertConfirm"; window.addEventListener("DOMContentLoaded", () => { /** diff --git a/resources/ts/bo/bo.ts b/resources/ts/bo.ts similarity index 71% rename from resources/ts/bo/bo.ts rename to resources/ts/bo.ts index cd94d30..fa51b5d 100644 --- a/resources/ts/bo/bo.ts +++ b/resources/ts/bo.ts @@ -1,6 +1,6 @@ // ASSETS /// <reference types="vite/client" /> -import.meta.glob(["./../../assets/images/**"]); +import.meta.glob(["./../assets/images/**"]); // UTILITIES import "./utilities/bootstrap"; @@ -8,7 +8,7 @@ import "./utilities/fontawesome"; import "./utilities/vue-bo"; // MODULES -import "./../modules/sweetalert"; +import "./modules/sweetalert"; // CUSTOM import "./base/common"; diff --git a/resources/ts/bo/components/shims-vue.d.ts b/resources/ts/bo/components/shims-vue.d.ts deleted file mode 100644 index d2833d6..0000000 --- a/resources/ts/bo/components/shims-vue.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -declare module "*.vue" { - import type { DefineComponent } from "vue"; - const component: DefineComponent<{}, {}, any>; - // @ts-ignore - export default component; -} diff --git a/resources/ts/bo/utilities/bootstrap.ts b/resources/ts/bo/utilities/bootstrap.ts deleted file mode 100644 index 2419164..0000000 --- a/resources/ts/bo/utilities/bootstrap.ts +++ /dev/null @@ -1,21 +0,0 @@ -import axios from "axios"; -import * as bootstrap from "bootstrap"; -import bsTooltips from "./../../modules/bsTooltips"; - -/** - * We'll load the axios HTTP library which allows us to easily issue requests - * to our Laravel back-end. This library automatically handles sending the - * CSRF token as a header based on the value of the "XSRF" token cookie. - */ -axios.defaults.headers.common = { - Accept: "application/json, text/plain, */*", - "X-Requested-With": "XMLHttpRequest", - "X-CSRF-TOKEN": document - .querySelector("meta[name=\"csrf-token\"]")! - .getAttribute("content"), -}; - -window.axios = axios; -window.bootstrap = bootstrap; - -bsTooltips.methods.initTooltips(); diff --git a/resources/ts/bo/utilities/fontawesome.ts b/resources/ts/bo/utilities/fontawesome.ts deleted file mode 100644 index 58da045..0000000 --- a/resources/ts/bo/utilities/fontawesome.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { config, dom, library } from "@fortawesome/fontawesome-svg-core"; -import { - faArrowAltCircleDown, - faArrowAltCircleUp, - faArrowDown, - faArrowLeft, - faArrowLeftLong, - faArrowRight, - faArrowRightFromBracket, - faArrowUp, - faArrowsAltH, - faArrowsAltV, - faBackspace, - faBan, - faCheck, - faChevronLeft, - faChevronRight, - faCircleCheck, - faCircleExclamation, - faCircleInfo, - faCircleNotch, - faCircleXmark, - faCog, - faCopy, - faCrop, - faEraser, - faEye, - faEyeSlash, - faFileExport, - faFolderOpen, - faListUl, - faPaintRoller, - faPencil, - faPlus, - faQuestionCircle, - faRedoAlt, - faSearch, - faSearchMinus, - faSearchPlus, - faSliders, - faSyncAlt, - faTimes, - faTrash, - faUndoAlt, - faWandMagic -} from "@fortawesome/free-solid-svg-icons"; - -config.autoReplaceSvg = false; -library.add(faEraser); -library.add(faArrowLeftLong); -library.add(faPlus); -library.add(faTrash); -library.add(faEye); -library.add(faEyeSlash); -library.add(faFolderOpen); -library.add(faCrop); -library.add(faQuestionCircle); -library.add(faCheck); -library.add(faUndoAlt); -library.add(faRedoAlt); -library.add(faSyncAlt); -library.add(faSearchPlus); -library.add(faSearchMinus); -library.add(faArrowLeft); -library.add(faArrowRight); -library.add(faArrowUp); -library.add(faArrowDown); -library.add(faArrowsAltH); -library.add(faArrowsAltV); -library.add(faWandMagic); -library.add(faPencil); -library.add(faTimes); -library.add(faBackspace); -library.add(faSearch); -library.add(faArrowRightFromBracket); -library.add(faArrowAltCircleDown); -library.add(faArrowAltCircleUp); -library.add(faCircleCheck); -library.add(faCircleXmark); -library.add(faCopy); -library.add(faListUl); -library.add(faBan); -library.add(faCircleExclamation); -library.add(faCircleInfo); -library.add(faPaintRoller); -library.add(faChevronLeft); -library.add(faChevronRight); -library.add(faCircleXmark); -library.add(faCircleNotch); -library.add(faSliders); -library.add(faCog); -library.add(faFileExport); - -dom.watch(); diff --git a/resources/ts/bo/components/BelongsToManyDropdownComponent.vue b/resources/ts/components/BelongsToManyDropdownComponent.vue similarity index 96% rename from resources/ts/bo/components/BelongsToManyDropdownComponent.vue rename to resources/ts/components/BelongsToManyDropdownComponent.vue index 38bc712..284d9e3 100644 --- a/resources/ts/bo/components/BelongsToManyDropdownComponent.vue +++ b/resources/ts/components/BelongsToManyDropdownComponent.vue @@ -63,15 +63,16 @@ </template> <script lang="ts"> -import { defineComponent } from "vue"; import type VueTagsInput from "@sipec/vue3-tags-input"; import { VueTagsInput as VueTagsInputCls } from "@sipec/vue3-tags-input"; import slugify from "slugify"; -import route from "../../modules/route"; -import trans from "../../modules/trans"; -import parseLaravelErrorMessages from "../../modules/parseLaravelErrorMessages"; +import { defineComponent } from "vue"; +import parseLaravelErrorMessages from "../modules/parseLaravelErrorMessages"; +import route from "../modules/route"; +import trans from "../modules/trans"; export default defineComponent({ + name: "BelongsToManyDropdownComponent", mixins: [route, trans, parseLaravelErrorMessages], components: { VueTagsInputCls, @@ -196,7 +197,9 @@ export default defineComponent({ <style lang="scss"> .belongs-to-many-dropdown { - max-width: 100% !important; + &, .vue-tags-input { + max-width: 100% !important; + } .ti- { &autocomplete { max-height: 10rem !important; diff --git a/resources/ts/bo/components/CkEditorComponent.vue b/resources/ts/components/CkEditorComponent.vue similarity index 81% rename from resources/ts/bo/components/CkEditorComponent.vue rename to resources/ts/components/CkEditorComponent.vue index 363232c..962cfc3 100644 --- a/resources/ts/bo/components/CkEditorComponent.vue +++ b/resources/ts/components/CkEditorComponent.vue @@ -1,5 +1,5 @@ <template> - <div> + <div class="ck-editor-component"> <div ref="wordCounter" /> <div ref="ckeditor" /> <input @@ -15,8 +15,8 @@ <script lang="ts"> import { defineComponent } from "vue"; -import trans from "./../../modules/trans"; -import "./../../../js/vendor/ckeditor"; +import "./../../js/vendor/ckeditor"; +import trans from "./../modules/trans"; // import { CKUploadAdaptater } from "../../bo/CkUploadAdaptater"; export default defineComponent({ @@ -84,7 +84,9 @@ export default defineComponent({ if (this.internalText != this.dbtext) { this.internalText = this.dbtext ? this.dbtext : ""; if (this.editor) { - this.editor.setData(this.internalText); + const text = this.internalText; + Object.freeze(text); + this.editor.setData(text); } } }, @@ -93,15 +95,24 @@ export default defineComponent({ makeSuperBuild() { const self = this, ckeditor = this.$refs.ckeditor as HTMLElement, - wordCounter = this.$refs.wordCounter as HTMLElement; - // * Specific to Calliope - // verticalTopMenuOffset = document.querySelector( - // "body > div> .navbar" - // ) as HTMLElement | null; + wordCounter = this.$refs.wordCounter as HTMLElement, + // * Top Menu + verticalTopMenuOffset = document.querySelector( + "body > #top-navbar" + ) as HTMLElement | null; - // if (!verticalTopMenuOffset) { - // throw new Error("Could not find bo Top menu navbar"); - // } + if (!verticalTopMenuOffset) { + console.warn("Could not find bo #top-navbar"); + } + + const ui = verticalTopMenuOffset ? { + viewportOffset: { + top: verticalTopMenuOffset.getBoundingClientRect().height, + right: 0, + bottom: 0, + left: 0, + }, + } : undefined; // @ts-ignore ClassicEditor.create(ckeditor, { @@ -126,29 +137,21 @@ export default defineComponent({ this.words = stats.words; }, }, - // ui: { - // viewportOffset: { - // top: verticalTopMenuOffset.getBoundingClientRect().height, - // right: 0, - // bottom: 0, - // left: 0, - // }, - // }, + ui: ui, link: { // Automatically add target="_blank" and rel="noopener noreferrer" to all external links. addTargetToExternalLinks: true, // Let the users control the "download" attribute of each link. - decorators: [ - { + decorators: { + openInNewTab: { mode: "manual", label: "Ouvrir dans un nouvel onglet", attributes: { target: "_blank", - rel: "noopener noreferrer", - }, - }, - ], + } + } + } }, typing: { transformations: { @@ -301,7 +304,7 @@ export default defineComponent({ }; // Display all toolbar elements // console.log(Array.from(editor.ui.componentFactory.names())); - self.editor = editor; + self.editor = Object.freeze(editor); editor.setData(this.internalText); editor.model.document.on("change:data", () => { if(timer) { @@ -326,27 +329,38 @@ export default defineComponent({ </script> <style lang="scss"> -@import url("./../../../js/vendor/ckeditor/build/styles.css"); -.ck-rounded-corners { - border-radius: 0 0 0.375rem 0.375rem !important; -} -.ck-word-count { - background-color: #fafafa; - border: 1px solid #c4c4c4; - border-bottom: 0px; - border-top-left-radius: 0.375rem; - border-top-right-radius: 0.375rem; - color: initial; - display: flex; - flex-direction: row; - justify-content: space-around !important; -} -.ck-content { - transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; -} -.ck-focused { - border-color: #86b7fe !important; - box-shadow: 0 0 0 0.25rem rgb(13 110 253 / 25%) !important; - outline: 0 !important; +@import url("./../../js/vendor/ckeditor/build/styles.css"); +.ck-editor-component{ + // 2. Include functions first (so you can manipulate colors, SVGs, calc, etc) + @import "bootstrap/scss/functions"; + + // 3. Include remainder of required Bootstrap stylesheets + @import "bootstrap/scss/variables"; + .ck-rounded-corners { + border-radius: 0 0 0.375rem 0.375rem !important; + } + .ck-word-count { + background-color: #fafafa; + border: 1px solid #c4c4c4; + border-bottom: 0px; + border-top-left-radius: 0.375rem; + border-top-right-radius: 0.375rem; + color: initial; + display: flex; + flex-direction: row; + justify-content: space-around !important; + } + .ck-content { + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + } + .ck-focused { + border-color: #86b7fe !important; + box-shadow: 0 0 0 0.25rem rgb(13 110 253 / 25%) !important; + outline: 0 !important; + } + .ck-editor__main{ + color: $body-color; + } } + </style> diff --git a/resources/ts/bo/components/DashboardChartComponent.vue b/resources/ts/components/DashboardChartComponent.vue similarity index 100% rename from resources/ts/bo/components/DashboardChartComponent.vue rename to resources/ts/components/DashboardChartComponent.vue diff --git a/resources/ts/bo/components/ImageInputComponent.vue b/resources/ts/components/ImageInputComponent.vue similarity index 72% rename from resources/ts/bo/components/ImageInputComponent.vue rename to resources/ts/components/ImageInputComponent.vue index a9398fd..abc6c36 100644 --- a/resources/ts/bo/components/ImageInputComponent.vue +++ b/resources/ts/components/ImageInputComponent.vue @@ -1,10 +1,10 @@ <template> <div class="container image-input-vue p-0"> <div class="row"> - <div :class="`col-12 ${inTpreview ? 'col-md-6' : ''} form-group`"> + <div :class="`col-12 ${intPreview ? 'col-md-6' : ''} form-group`"> <label - v-if="inTpreview && showLabels" - :for="inTId" + v-if="intPreview && showLabels" + :for="intId" class="col-form-label" > <b>{{ __("vue.ImageInputComponent.choose_a_file") }}</b> @@ -20,34 +20,34 @@ <FontAwesomeIcon icon="fa-solid fa-folder-open" /> </button> <input - :id="inTId" + :id="intId" ref="actualImage" @change="changedImage" type="file" class="d-none" - :name="inTname" + :name="intName" accept=".jpg,.jpeg,.gif,.png" - :aria-describedby="`Help${inTId}`" + :aria-describedby="`Help${intId}`" > <input - v-if="inTvalue.indexOf('data:image/png;base64') !== 0" + v-if="intValue.indexOf('data:image/png;base64') !== 0" class="d-none" - :name="inTname" + :name="intName" type="text" :value="submitIntValue" > <input @click.prevent="chooseAFile" type="text" - :value="inTvalueFileName" + :value="intValueFileName" class="form-control right-aligned" data-bs-toggle="tooltip" :title="__('vue.ImageInputComponent.change_source_picture')" - :aria-describedby="`Help${inTId}`" + :aria-describedby="`Help${intId}`" readonly > <button - v-if="!inTrequired && inThasImage" + v-if="!intRequired && intHasImage" @click.prevent="removeFile" class="btn btn-outline-danger" type="button" @@ -55,11 +55,11 @@ <FontAwesomeIcon icon="fa-solid fa-eraser" /> </button> <button - v-if="!inTpreview && inTvalue" + v-if="!intPreview && intValue" class="btn btn-warning" type="button" data-bs-toggle="modal" - :data-bs-target="`#ModalPreview${inTId}`" + :data-bs-target="`#ModalPreview${intId}`" > <span data-bs-toggle="tooltip" @@ -70,11 +70,11 @@ </button> <button @click.prevent="prepareImageEditor($event)" - :disabled="!inThasImage" - :class="`btn ${!inThasModdedImage ? 'btn-primary' : 'btn-success'}`" + :disabled="!intHasImage" + :class="`btn ${!intHasModdedImage ? 'btn-primary' : 'btn-success'}`" type="button" data-bs-toggle="modal" - :data-bs-target="`#Modal${inTId}`" + :data-bs-target="`#Modal${intId}`" > <span data-bs-toggle="tooltip" @@ -84,7 +84,7 @@ </span> </button> <span - v-if="inThasModdedImage" + v-if="intHasModdedImage" class="input-group-text text-success" data-bs-toggle="tooltip" :title="__('vue.ImageInputComponent.resized_source_picture')" @@ -93,41 +93,42 @@ </span> </div> <small - :id="`Help${inTId}`" - class="form-text text-muted" - >{{ - inThelper - }}</small> + :id="`Help${intId}`" + class="form-text text-body-secondary" + > + {{ intHelper }} + </small> </div> <div - v-if="inTpreview" - :class="`col-12 ${inTpreview ? 'col-md-6' : ''} form-group`" + v-if="intPreview" + :class="`col-12 ${intPreview ? 'col-md-6' : ''} form-group d-flex flex-column`" > - <small - v-if="inTvalue && showLabels" - :id="`PreviewHelp${inTId}`" - class="form-text text-muted d-block" - >{{ __("vue.ImageInputComponent.preview_picture") }} - <span - v-if="inToriginalFile" - class="fw-bold" - > - {{ humanFileSize(inToriginalFile.size) }} - {{ `hauteur ${inToriginalFileDimensions.height}px` }} - {{ `largeur ${inToriginalFileDimensions.width}px` }} - </span> - </small> - <img - v-if="inTvalue" - class="img-fluid" - :src="inTvalue" - :alt="__('Image placeholder')" - :aria-describedby="`PreviewHelp${inTId}`" + <p + v-if="intValue && showLabels" + :id="`PreviewHelp${intId}`" + class="col-form-label fw-bold m-0" > + {{ __("vue.ImageInputComponent.preview_picture") }} + </p> + <img + v-if="intValue" + class="img-fluid" + :src="intValue" + :alt="__('Image placeholder')" + :aria-describedby="`PreviewHelp${intId}`" + > + <small + v-if="intOriginalFile" + class="form-text text-body-secondary" + > + {{ humanFileSize(intOriginalFile.size) }} + {{ `hauteur ${intOriginalFileDimensions.height}px` }} + {{ `largeur ${intOriginalFileDimensions.width}px` }} + </small> </div> </div> <div - :id="`Modal${inTId}`" + :id="`Modal${intId}`" class="modal" tabindex="-1" role="dialog" @@ -270,11 +271,12 @@ <label for="range" class="form-label me-4" - >{{ - __("vue.ImageInputComponent.degres", { - deg: String(inTrotationText), - }) - }} + > + {{ + __("vue.ImageInputComponent.degres", { + deg: String(intRotationText), + }) + }} </label> <input ref="rotationSlider" @@ -307,11 +309,11 @@ </div> <div ref="previewBox" - class="col-12 col-md-6 p-0 bg-light preview-img" + class="col-12 col-md-6 p-0 preview-img" > <div - :id="`Preview${inTId}`" - :style="inTpreviewStyle" + :id="`Preview${intId}`" + :style="intPreviewStyle" /> </div> </div> @@ -343,8 +345,8 @@ </div> </div> <div - v-if="!inTpreview && inTvalue" - :id="`ModalPreview${inTId}`" + v-if="!intPreview && intValue" + :id="`ModalPreview${intId}`" class="modal" tabindex="-1" role="dialog" @@ -374,14 +376,14 @@ <div class="row"> <div class="col-12 text-center"> <small - :id="`PreviewHelp${inTId}`" - class="form-text text-muted d-block" + :id="`PreviewHelp${intId}`" + class="form-text text-body-secondary d-block" >{{ __("vue.ImageInputComponent.preview_picture") }}</small> <img class="img-fluid" - :src="inTvalue" + :src="intValue" :alt="__('Image placeholder')" - :aria-describedby="`PreviewHelp${inTId}`" + :aria-describedby="`PreviewHelp${intId}`" > </div> </div> @@ -397,8 +399,8 @@ import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; import Cropper from "cropperjs"; import { defineComponent } from "vue"; -import bsTooltips from "../../modules/bsTooltips"; -import trans from "../../modules/trans"; +import bsTooltips from "../modules/bsTooltips"; +import trans from "../modules/trans"; const UNITS = [ "byte", @@ -411,13 +413,14 @@ const UNITS = [ const BYTES_PER_KB = 1024; export default defineComponent({ + name: "ImageInputComponent", components: { FontAwesomeIcon, }, mixins: [trans, bsTooltips], emits: { - browseFile: (inTId: string) => { - inTId; + browseFile: (intId: string) => { + intId; return true; }, imageFile: (file: File | null) => { @@ -431,42 +434,42 @@ export default defineComponent({ if (json.length) { // * Init uppon Laravel Blade const data = JSON.parse(json); - this.inTId = data.id; - this.inTname = data.name; - this.inThelper = data.helper ?? ""; - this.inTwidth = Number.parseInt(String(data.width)); - this.inTheight = Number.parseInt(String(data.height)); - this.inTvalue = String(data.value); - if (this.inTvalue.length) { - this.inTvalueFileName = this.inTvalue.replace(/^\/|^/, ""); - this.inTvalue = this.inTvalue.replace(/^\/|^/, "/"); + this.intId = data.id; + this.intName = data.name; + this.intHelper = data.helper ?? ""; + this.intWidth = Number.parseInt(String(data.width)); + this.intHeight = Number.parseInt(String(data.height)); + this.intValue = String(data.value); + if (this.intValue.length) { + this.intValueFileName = this.intValue.replace(/^\/|^/, ""); + this.intValue = this.intValue.replace(/^\/|^/, "/"); } if (data.preview !== undefined) { - this.inTpreview = data.preview ? true : false; + this.intPreview = data.preview ? true : false; } if (data.required !== undefined) { - this.inTrequired = data.required ? true : false; + this.intRequired = data.required ? true : false; } if (data.browseEvent !== undefined) { - this.inTbrowseEvent = data.browseEvent ? true : false; + this.intBrowseEvent = data.browseEvent ? true : false; } } else { // * Init Uppon Vue component - this.inTId = this.id; - this.inTname = this.name; - this.inThelper = this.helper; - this.inTwidth = this.width; - this.inTheight = this.height; - this.inTvalue = this.value; - if (this.inTvalue.length) { - this.inTvalueFileName = this.inTvalue.replace(/^\/|^/, ""); - this.inTvalue = this.inTvalue.replace(/^\/|^/, "/"); + this.intId = this.id; + this.intName = this.name; + this.intHelper = this.helper; + this.intWidth = this.width; + this.intHeight = this.height; + this.intValue = this.value; + if (this.intValue.length) { + this.intValueFileName = this.intValue.replace(/^\/|^/, ""); + this.intValue = this.intValue.replace(/^\/|^/, "/"); } - this.inTpreview = this.preview; - this.inTrequired = this.required; - this.inTbrowseEvent = this.browseEvent; + this.intPreview = this.preview; + this.intRequired = this.required; + this.intBrowseEvent = this.browseEvent; } - this.inThasImage = false; + this.intHasImage = false; }, props: { id: { @@ -511,67 +514,67 @@ export default defineComponent({ }, }, data(): { - inTId: string; - inTvalue: string; - inTvalueFileName: string; - inThelper: string; - inTname: string; - inTrequired: boolean; - inTwidth: number; - inTheight: number; - inTpreview: boolean; + intId: string; + intValue: string; + intValueFileName: string; + intHelper: string; + intName: string; + intRequired: boolean; + intWidth: number; + intHeight: number; + intPreview: boolean; - inTbrowseEvent: boolean; + intBrowseEvent: boolean; - inTrotationText: number; - inTrotationValue: number; - inTscaleX: number; - inTscaleY: number; - inThasImage: boolean; - inThasModdedImage: boolean; - inTcropper: Cropper | null; + intRotationText: number; + intRotationValue: number; + intScaleX: number; + intScaleY: number; + intHasImage: boolean; + intHasModdedImage: boolean; + intCropper: Cropper | null; - inToriginalFile: File | null; - inToriginalFileDimensions: { + intOriginalFile: File | null; + intOriginalFileDimensions: { height: number; width: number; }; - inToriginalFileName: string; - inTpreviewStyle: string; + intOriginalFileName: string; + intPreviewStyle: string; } { return { - inTId: "", - inTvalue: "", - inTvalueFileName: this.__("vue.ImageInputComponent.choose_a_file"), - inThelper: "", - inTname: "", - inTrequired: true, - inTwidth: 0, - inTheight: 0, - inTpreview: true, + intId: "", + intValue: "", + intValueFileName: this.__("vue.ImageInputComponent.choose_a_file"), + intHelper: "", + intName: "", + intRequired: true, + intWidth: 0, + intHeight: 0, + intPreview: true, - inTbrowseEvent: false, + intBrowseEvent: false, - inTrotationText: 0, - inTrotationValue: 0, - inTscaleX: 1, - inTscaleY: 1, - inThasImage: false, - inThasModdedImage: false, - inTcropper: null, + intRotationText: 0, + intRotationValue: 0, + intScaleX: 1, + intScaleY: 1, + intHasImage: false, + intHasModdedImage: false, + intCropper: null, - inToriginalFile: null, - inToriginalFileDimensions: { + intOriginalFile: null, + intOriginalFileDimensions: { height: 0, width: 0, }, - inToriginalFileName: "", - inTpreviewStyle: "", + intOriginalFileName: "", + intPreviewStyle: "", }; }, computed: { imageRatio() { - return this.inTwidth / this.inTheight; + return this.intWidth / this.intHeight; }, cropperOption(): Cropper.Options { return { @@ -590,25 +593,25 @@ export default defineComponent({ cropBoxMovable: true, cropBoxResizable: false, toggleDragModeOnDblclick: false, - aspectRatio: this.inTwidth / this.inTheight, - preview: `#Preview${this.inTId}`, + aspectRatio: this.intWidth / this.intHeight, + preview: `#Preview${this.intId}`, }; }, /** Removes prepended slash */ submitIntValue(): string { - return this.inTvalue.replace(/^\/|^/, ""); + return this.intValue.replace(/^\/|^/, ""); }, }, watch: { - inTrotationValue(value) { - if (!this.inTcropper) { + intRotationValue(value) { + if (!this.intCropper) { return; } - this.inTcropper.rotateTo(value); + this.intCropper.rotateTo(value); }, - inTvalue() { - if (this.inTbrowseEvent) { - this.inThasImage = true; + intValue() { + if (this.intBrowseEvent) { + this.intHasImage = true; } }, value: { @@ -620,39 +623,36 @@ export default defineComponent({ return; } if (!value.length) { - this.$nextTick(() => { - this.removeFile(); - }); + this.removeFile(); return; } else { - this.inThasImage = true; + this.intHasImage = true; } // * Using browse event, If user changed imagem recompile blob (File Upload storage strategy) if (this.inTbrowseEvent && this.inThasImage) { - const self = this, - actualImage = this.$refs.actualImage as HTMLInputElement, + const actualImage = this.$refs.actualImage as HTMLInputElement, container = new DataTransfer(); this.$nextTick(() => { - this.inTvalue = value.replace(/^\/|^/, "/"); - this.inTvalueFileName = value.replace(/^\/|^/, ""); - fetch(this.inTvalue) + this.intValue = value.replace(/^\/|^/, "/"); + this.intValueFileName = value.replace(/^\/|^/, ""); + fetch(this.intValue) .then((response) => { - self.inToriginalFileName = response.url.substring( + this.intOriginalFileName = decodeURI(response.url.substring( response.url.lastIndexOf("/") + 1 - ); + )); return response.blob(); }) .then((blob: Blob) => { if (!actualImage.files) { throw new Error("3- missing files list"); } - this.fetchDataUrlImageSizes(self.inTvalue); + this.fetchDataUrlImageSizes(this.inTvalue); const file = new File([blob], this.inTvalueFileName, { type: "image/png", lastModified: new Date().getTime(), }); - this.inThasImage = true; - this.inToriginalFile = file; + this.intHasImage = true; + this.intOriginalFile = file; this.$emit("imageFile", file); container.items.add(file); actualImage.files = container.files; @@ -675,57 +675,57 @@ export default defineComponent({ style = window.getComputedStyle(previewBox), targetWidth = Number.parseFloat(style.width) / 1.2, targetHeight = - (this.inTheight * Number.parseFloat(style.width)) / - this.inTwidth / + (this.intHeight * Number.parseFloat(style.width)) / + this.intWidth / 1.2; - this.inTpreviewStyle = `width: ${targetWidth.toFixed( + this.intPreviewStyle = `width: ${targetWidth.toFixed( 3 )}px;height:${targetHeight.toFixed(3)}px;`; }, reset() { - if (!this.inTcropper) { + if (!this.intCropper) { return; } - this.inTcropper.reset(); + this.intCropper.reset(); }, zoom(v: number) { - if (!this.inTcropper) { + if (!this.intCropper) { return; } - this.inTcropper.zoom(v); + this.intCropper.zoom(v); }, move(x: number, y: number) { - if (!this.inTcropper) { + if (!this.intCropper) { return; } - this.inTcropper.move(x, y); + this.intCropper.move(x, y); }, scale(horizontal: boolean) { - if (!this.inTcropper) { + if (!this.intCropper) { return; } if (horizontal) { - this.inTscaleX = -1 / this.inTscaleX; - this.inTcropper.scaleX(this.inTscaleX); + this.intScaleX = -1 / this.intScaleX; + this.intCropper.scaleX(this.intScaleX); } else { - this.inTscaleY = -1 / this.inTscaleY; - this.inTcropper.scaleY(this.inTscaleY); + this.intScaleY = -1 / this.intScaleY; + this.intCropper.scaleY(this.intScaleY); } }, rotate(value: number) { const rotationSlider = this.$refs.rotationSlider as HTMLInputElement; - let res = this.inTrotationValue + value; + let res = this.intRotationValue + value; res = res < 0 ? 0 : res; res = res > 360 ? 360 : res; - this.inTrotationValue = res; - this.inTrotationText = this.inTrotationValue; - rotationSlider.value = String(this.inTrotationValue); + this.intRotationValue = res; + this.intRotationText = this.intRotationValue; + rotationSlider.value = String(this.intRotationValue); }, rotateTo(value: number) { value = value < 0 ? 0 : value; value = value > 360 ? 360 : value; - this.inTrotationValue = value; - this.inTrotationText = this.inTrotationValue; + this.intRotationValue = value; + this.intRotationText = this.intRotationValue; }, rotationChange(e: Event) { const el = e.target; @@ -736,73 +736,74 @@ export default defineComponent({ }, chooseAFile() { const actualImage = this.$refs.actualImage as HTMLInputElement; - if (this.inTbrowseEvent) { - this.$emit("browseFile", this.inTId); + if (this.intBrowseEvent) { + this.$emit("browseFile", this.intId); return; } actualImage.click(); }, removeFile() { + if (!this.$refs.actualImage) { + return; + } const actualImage = this.$refs.actualImage as HTMLInputElement; actualImage.value = ""; - this.inTvalueFileName = this.__("vue.ImageInputComponent.choose_a_file"); - this.inTvalue = ""; - this.inToriginalFileName = ""; - this.inToriginalFile = null; - this.inThasModdedImage = false; - this.inToriginalFileDimensions = { + this.intValueFileName = this.__("vue.ImageInputComponent.choose_a_file"); + this.intValue = ""; + this.intOriginalFileName = ""; + this.intOriginalFile = null; + this.intHasModdedImage = false; + this.intOriginalFileDimensions = { height: 0, width: 0, }; - // * To overload watcher for inTvalue - this.$nextTick(() => (this.inThasImage = false)); + // * To overload watcher for intValue + this.$nextTick(() => (this.intHasImage = false)); this.$emit("imageFile", null); }, changedImage() { - const self = this, - reader = new FileReader(), + const reader = new FileReader(), actualImage = this.$refs.actualImage as HTMLInputElement; if (!actualImage.files) { throw new Error("missing files list"); } - self.inThasImage = true; + this.intHasImage = true; reader.addEventListener( "load", () => { // On convertit l'image en une chaîne de caractères base64. - self.inTvalue = String(reader.result); + this.intValue = String(reader.result); }, false ); if (actualImage.files[0]) { - self.inToriginalFile = actualImage.files[0]; - self.inToriginalFileName = actualImage.files[0].name; + this.intOriginalFile = actualImage.files[0]; + this.intOriginalFileName = actualImage.files[0].name; reader.readAsDataURL(actualImage.files[0]); reader.onloadend = () => { this.fetchDataUrlImageSizes(String(reader.result)); this.exportToBlob(); }; - this.inTvalueFileName = actualImage.files[0].name; + this.intValueFileName = actualImage.files[0].name; } }, prepareImageEditor(e: Event) { const actualImage = this.$refs.actualImage as HTMLInputElement, imgEditor = this.$refs.imgEditor as HTMLImageElement, - self = this, createCropper = () => { - if (self.inTcropper) { - self.inTcropper.destroy(); + if (this.intCropper) { + this.intCropper.destroy(); } - self.inTcropper = new Cropper(imgEditor, self.cropperOption); + this.intCropper = new Cropper(imgEditor, this.cropperOption); }; if (!this.browseEvent) { if (!actualImage || !actualImage.files) { throw new Error("actualImage is not defined"); } - this.inToriginalFile = actualImage.files[0]; - this.inToriginalFileName = actualImage.files[0].name; + this.intOriginalFile = actualImage.files[0]; + this.intOriginalFileName = actualImage.files[0].name; imgEditor.src = URL.createObjectURL(actualImage.files[0]); this.previewStyleCompute(); @@ -814,7 +815,7 @@ export default defineComponent({ this.$nextTick(() => { fetch(this.value.replace(/^\/|^/, "/")) .then((response) => { - this.inToriginalFileName = response.url.substring( + this.intOriginalFileName = response.url.substring( response.url.lastIndexOf("/") + 1 ); return response.blob(); @@ -845,29 +846,28 @@ export default defineComponent({ return new Blob([u8arr], { type: mime[1] }); }, fetchDataUrlImageSizes(dataUrl: string) { - const img = new Image(), - self = this; + const img = new Image(); try { img.onload = () => { - self.inToriginalFileDimensions.width = img.width; - self.inToriginalFileDimensions.height = img.height; + this.intOriginalFileDimensions.width = img.width; + this.intOriginalFileDimensions.height = img.height; }; img.src = dataUrl; } catch (e) { - self.inToriginalFileDimensions.width = img.width; - self.inToriginalFileDimensions.height = img.height; + this.intOriginalFileDimensions.width = img.width; + this.intOriginalFileDimensions.height = img.height; } }, exportCropperFile() { - this.inThasModdedImage = true; - if (!this.inTcropper) { + this.intHasModdedImage = true; + if (!this.intCropper) { return; } - this.inTvalue = this.inTcropper + this.intValue = this.intCropper .getCroppedCanvas({ fillColor: "rgba(0, 0, 0, 0)", - width: this.inTwidth, - height: this.inTheight, + width: this.intWidth, + height: this.intHeight, imageSmoothingEnabled: true, imageSmoothingQuality: "high", }) @@ -875,22 +875,21 @@ export default defineComponent({ this.exportToBlob(); }, exportToBlob() { - const self = this, - actualImage = this.$refs.actualImage as HTMLInputElement, + const actualImage = this.$refs.actualImage as HTMLInputElement, container = new DataTransfer(); if (!actualImage.files) { throw new Error("2- missing files list"); } - this.fetchDataUrlImageSizes(self.inTvalue); + this.fetchDataUrlImageSizes(this.intValue); const file = new File( - [self.dataURLtoBlob(self.inTvalue)], - self.inToriginalFileName, + [this.dataURLtoBlob(this.intValue)], + this.intOriginalFileName, { type: "image/png", lastModified: new Date().getTime(), } ); - this.inToriginalFile = file; + this.intOriginalFile = file; this.$emit("imageFile", file); container.items.add(file); actualImage.files = container.files; @@ -916,7 +915,7 @@ export default defineComponent({ </script> <style lang="scss" scopped> -@import "~/cropperjs/dist/cropper.css"; +@import "cropperjs/dist/cropper.css"; .image-input-vue { /* Ensure the size of the image fit the container perfectly */ @@ -926,7 +925,7 @@ export default defineComponent({ /* This rule is very important, please don't ignore this */ max-width: 100%; } - .rotateButton { + .rotate-button { display: flex; flex-direction: column; } @@ -953,7 +952,7 @@ export default defineComponent({ direction: ltr !important; overflow: hidden !important; } - input.right-aligned:not(:focus) { + input.right-aligned :not(:focus) { direction: rtl !important; text-align: left !important; text-overflow: ellipsis !important; diff --git a/resources/ts/bo/components/InputTextAutoComponent.vue b/resources/ts/components/InputTextAutoComponent.vue similarity index 98% rename from resources/ts/bo/components/InputTextAutoComponent.vue rename to resources/ts/components/InputTextAutoComponent.vue index 92deebb..213f539 100644 --- a/resources/ts/bo/components/InputTextAutoComponent.vue +++ b/resources/ts/components/InputTextAutoComponent.vue @@ -19,7 +19,7 @@ <script lang="ts"> import { defineComponent, ref } from "vue"; -import trans from "../../modules/trans"; +import trans from "../modules/trans"; import WordCounter from "./WordCounterComponent.vue"; const textarea = ref<HTMLTextAreaElement | null>(null); diff --git a/resources/ts/bo/components/InstantWinComponent.vue b/resources/ts/components/InstantWinComponent.vue similarity index 97% rename from resources/ts/bo/components/InstantWinComponent.vue rename to resources/ts/components/InstantWinComponent.vue index 76fa2ff..abea4d4 100644 --- a/resources/ts/bo/components/InstantWinComponent.vue +++ b/resources/ts/components/InstantWinComponent.vue @@ -70,8 +70,8 @@ <script lang="ts"> import axios from "axios"; import { defineComponent } from "vue"; -import toast from "../../modules/toast"; -import trans from "../../modules/trans"; +import toast from "../modules/toast"; +import trans from "../modules/trans"; export default defineComponent({ mixins: [toast, trans], diff --git a/resources/ts/components/PasswordVisibility.vue b/resources/ts/components/PasswordVisibility.vue new file mode 100644 index 0000000..e965391 --- /dev/null +++ b/resources/ts/components/PasswordVisibility.vue @@ -0,0 +1,124 @@ +<template> + <div :class="`password-visibility-${inputId} h-100`"> + <span + v-if="visible" + class="text-bg-primary reveal d-flex justify-content-center align-items-center h-100" + @click.prevent="toggleVisibility()" + type="button" + :title="__('vue.PasswordVisibility.hide')" + data-bs="tooltip" + > + <FontAwesomeIcon + icon="fas fa-eye-slash" + :aria-hidden="true" + /> + </span> + <span + v-else + class="text-bg-primary reveal d-flex justify-content-center align-items-center h-100" + @click.prevent="toggleVisibility()" + type="button" + :title="__('vue.PasswordVisibility.show')" + data-bs="tooltip" + > + <FontAwesomeIcon + icon="fas fa-eye" + :aria-hidden="true" + /> + </span> + </div> +</template> + +<script lang="ts"> +import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; +import { Tooltip } from "bootstrap"; +import { defineComponent } from "vue"; +import trans from "./../modules/trans"; + +export default defineComponent({ + name: "PasswordVisibility", + mixins: [trans], + components: { FontAwesomeIcon }, + data(): { + inputId: string; + input: HTMLInputElement; + visible: boolean; + tooltipList: HTMLButtonElement[]; + } { + return { + inputId: "", + input: document.createElement("input"), + visible: false, + tooltipList: [], + }; + }, + computed: {}, + mounted() { + const json = String(this.$attrs.json ?? "{}"), + data = JSON.parse(json); + this.inputId = data.inputId; + this.input = document.getElementById(this.inputId) as HTMLInputElement; + if (!this.input) { + throw new Error(`Cannot find input id '${this.inputId}'`); + } + if (this.input.tagName !== "INPUT") { + throw new Error(`id '${this.inputId}' is not an input`); + } + if ((this.input as HTMLInputElement).type !== "password") { + throw new Error(`id '${this.inputId}' is not an input password`); + } + this.input = this.input as HTMLInputElement; + this.$nextTick(() => { + this.updateBootstrapTooltip(); + }); + }, + methods: { + /** + * Update password status (visible, hidden). + */ + toggleVisibility(): void { + if (this.input.type === "password") { + this.input.type = "text"; + this.visible = true; + } else { + this.input.type = "password"; + this.visible = false; + } + this.$nextTick(() => { + this.updateBootstrapTooltip(); + }); + }, + /** + * Update Bootstrap tooltips. + */ + updateBootstrapTooltip() { + let newTooltipList = [].slice.call( + document.querySelectorAll( + ".password-visibility-" + this.inputId + " [data-bs=\"tooltip\"]" + ) + ) as HTMLButtonElement[]; + let tmp = newTooltipList.filter((x) => !this.tooltipList.includes(x)); + tmp.map((tooltip) => { + return new Tooltip(tooltip); + }); + this.closeBootstrapTooltip(); + this.tooltipList = newTooltipList; + }, + /** + * Close all Bootstrap tooltips. + */ + closeBootstrapTooltip() { + this.tooltipList.forEach((tooltip) => { + tooltip.blur(); + Tooltip.getInstance(tooltip)?.hide(); + }); + }, + }, +}); +</script> + +<style lang="scss"> +.reveal { + width: 52px; +} +</style> diff --git a/resources/ts/bo/components/WebCouponsComponent.vue b/resources/ts/components/WebCouponsComponent.vue similarity index 100% rename from resources/ts/bo/components/WebCouponsComponent.vue rename to resources/ts/components/WebCouponsComponent.vue diff --git a/resources/ts/bo/components/WinNotification.vue b/resources/ts/components/WinNotification.vue similarity index 100% rename from resources/ts/bo/components/WinNotification.vue rename to resources/ts/components/WinNotification.vue diff --git a/resources/ts/bo/components/WordCounterComponent.vue b/resources/ts/components/WordCounterComponent.vue similarity index 82% rename from resources/ts/bo/components/WordCounterComponent.vue rename to resources/ts/components/WordCounterComponent.vue index bdbd9ef..0b4182d 100644 --- a/resources/ts/bo/components/WordCounterComponent.vue +++ b/resources/ts/components/WordCounterComponent.vue @@ -1,21 +1,17 @@ <template> - <div class="word-counter-varchar"> - <span class="words"> - <u>{{ __("Mots") }}</u> -  :  - <b>{{ inTwords }}</b> + <div class="word-counter-varchar d-flex flex-row justify-content-around align-items-center w-100"> + <span> + {{ __("vue.WordCounterComponent.words") }} : <b>{{ inTwords }}</b> </span> - <span class="chars"> - <u>{{ __("Caractères") }}</u> -  :  - <b>{{ inTchars }}</b> + <span> + {{ __("vue.WordCounterComponent.characters") }} : <b>{{ inTchars }}</b> </span> </div> </template> <script lang="ts"> import { defineComponent } from "vue"; -import trans from "../../modules/trans"; +import trans from "../modules/trans"; export default defineComponent({ mixins: [trans], @@ -116,24 +112,11 @@ export default defineComponent({ border-radius: 0 0 0.375rem 0.375rem; } .word-counter-varchar { - display: inline-block; - color: initial; - border: 1px solid rgb(196, 196, 196); + color: var(--bs-body-color); + border: var(--bs-border-width) solid var(--bs-border-color); border-bottom: 0; - background-color: #fafafa; - position: relative; - width: 100%; + background-color: rgb(var(--bs-secondary-bg-rgb)); border-top-left-radius: 0.375rem; border-top-right-radius: 0.375rem; - - .words { - position: relative; - left: 2rem; - } - - .chars { - position: absolute; - left: 9rem; - } } </style> diff --git a/resources/ts/utilities/bootstrap.ts b/resources/ts/utilities/bootstrap.ts index 0cdbcf9..3457350 100644 --- a/resources/ts/utilities/bootstrap.ts +++ b/resources/ts/utilities/bootstrap.ts @@ -1,6 +1,6 @@ import axios from "axios"; import * as bootstrap from "bootstrap"; -import bsTooltips from "../modules/bsTooltips"; +import bsTooltips from "./../modules/bsTooltips"; /** * We'll load the axios HTTP library which allows us to easily issue requests diff --git a/resources/ts/utilities/fontawesome.ts b/resources/ts/utilities/fontawesome.ts index 1f33637..83f7ef5 100644 --- a/resources/ts/utilities/fontawesome.ts +++ b/resources/ts/utilities/fontawesome.ts @@ -1,5 +1,4 @@ import { config, dom, library } from "@fortawesome/fontawesome-svg-core"; -import { faCircleXmark, faCopy } from "@fortawesome/free-regular-svg-icons"; import { faArrowAltCircleDown, faArrowAltCircleUp, @@ -12,17 +11,25 @@ import { faArrowsAltH, faArrowsAltV, faBackspace, + faBan, faCheck, faChevronLeft, faChevronRight, + faCircleCheck, + faCircleExclamation, + faCircleInfo, faCircleNotch, + faCircleXmark, faCog, + faCopy, faCrop, faEraser, faEye, + faEyeSlash, faFileExport, faFolderOpen, faListUl, + faPaintRoller, faPencil, faPlus, faQuestionCircle, @@ -35,8 +42,7 @@ import { faTimes, faTrash, faUndoAlt, - faWandMagic, - faXmark, + faWandMagic } from "@fortawesome/free-solid-svg-icons"; config.autoReplaceSvg = false; @@ -45,6 +51,7 @@ library.add(faArrowLeftLong); library.add(faPlus); library.add(faTrash); library.add(faEye); +library.add(faEyeSlash); library.add(faFolderOpen); library.add(faCrop); library.add(faQuestionCircle); @@ -68,17 +75,21 @@ library.add(faSearch); library.add(faArrowRightFromBracket); library.add(faArrowAltCircleDown); library.add(faArrowAltCircleUp); -library.add(faXmark); -library.add(faFileExport); -library.add(faCog); +library.add(faCircleCheck); +library.add(faCircleXmark); library.add(faCopy); - -// Front +library.add(faListUl); +library.add(faBan); +library.add(faCircleExclamation); +library.add(faCircleInfo); +library.add(faPaintRoller); library.add(faChevronLeft); library.add(faChevronRight); library.add(faCircleXmark); library.add(faCircleNotch); library.add(faSliders); library.add(faListUl); +library.add(faCog); +library.add(faFileExport); dom.watch(); diff --git a/resources/ts/bo/utilities/vue-bo.ts b/resources/ts/utilities/vue-bo.ts similarity index 100% rename from resources/ts/bo/utilities/vue-bo.ts rename to resources/ts/utilities/vue-bo.ts diff --git a/resources/views/breadcrumbs/breadcrumb-body.blade.php b/resources/views/breadcrumbs/breadcrumb-body.blade.php deleted file mode 100644 index 2f17974..0000000 --- a/resources/views/breadcrumbs/breadcrumb-body.blade.php +++ /dev/null @@ -1,12 +0,0 @@ -@hasSection('breadcrumb') -@if(Breadcrumbs::exists(app()->view->getSections()['breadcrumb'])) -{{ call_user_func_array('Breadcrumbs::render', array_merge( - [app()->view->getSections()['breadcrumb']], - !empty($brParam) ? (is_array($brParam) ? $brParam : [$brParam]) : [] -)) }} -@endif -@elseif(request()->route() != null) -@if(Breadcrumbs::exists(request()->route()->getName())) -{{ Breadcrumbs::render(request()->route()->getName()) }} -@endif -@endif diff --git a/resources/views/breadcrumbs/breadcrumb-head.blade.php b/resources/views/breadcrumbs/breadcrumb-head.blade.php deleted file mode 100644 index b2d8502..0000000 --- a/resources/views/breadcrumbs/breadcrumb-head.blade.php +++ /dev/null @@ -1,9 +0,0 @@ -@hasSection('breadcrumb') -@if(Breadcrumbs::exists(app()->view->getSections()['breadcrumb'])) -{{ Breadcrumbs::view('breadcrumbs::json-ld', app()->view->getSections()['breadcrumb'], $brParam ?? null) }} -@endif -@elseif(request()->route() != null) -@if (Breadcrumbs::exists(request()->route()->getName())) -{{ Breadcrumbs::view('breadcrumbs::json-ld', request()->route()->getName()) }} -@endif -@endif diff --git a/resources/views/breadcrumbs/breadcrumb-layout.blade.php b/resources/views/breadcrumbs/breadcrumb-layout.blade.php deleted file mode 100644 index e9ff09d..0000000 --- a/resources/views/breadcrumbs/breadcrumb-layout.blade.php +++ /dev/null @@ -1,17 +0,0 @@ -@if (count($breadcrumbs)) -<ol class="breadcrumb flex-nowrap border-0 w-fit p-0 m-0"> - @foreach ($breadcrumbs as $breadcrumb) - @if ($breadcrumb->url && !$loop->last) - <li class="breadcrumb-item d-flex h2 fw-bold m-0"> - <a class="text-decoration-none text-secondary" href="{{ $breadcrumb->url }}"> - {{ $breadcrumb->title }} - </a> - </li> - @else - <li class="breadcrumb-item d-flex align-items-center flex-grow-1 text-dark h2 fw-bold active m-0"> - {{ $breadcrumb->title }} - </li> - @endif - @endforeach -</ol> -@endif diff --git a/resources/views/layout.blade.php b/resources/views/layout.blade.php index 5ed290a..c1b94e4 100644 --- a/resources/views/layout.blade.php +++ b/resources/views/layout.blade.php @@ -5,74 +5,37 @@ @include('back::layouts.head') </head> -<body class="position-relative min-vh-100"> - <nav class="navbar navbar-expand-md bg-light text-bg-light shadow-sm"> - <div class="container"> - <a class="navbar-brand rounded px-2 py-1" href="{{ url('/') }}"> - {{ config('app.name', 'Laravel') }} - </a> - <button class="navbar-toggler" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" type="button" - aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}"> - <span class="navbar-toggler-icon"></span> - </button> +<body + data-bs-theme="{{ (\App\Enums\Theme\BootstrapThemeEnum::make(intval(Cache::get('theme'))) ?? \App\Enums\Theme\BootstrapThemeEnum::light)->name() }}"> + <!-- Navigation --> + @include('back::layouts.header') - <div class="navbar-collapse collapse" id="navbarSupportedContent"> - <!-- Left Side Of Navbar --> - @include('back::layouts.nav-menu') - - <!-- Right Side Of Navbar --> - <ul class="navbar-nav ms-auto"> - <!-- Authentication Links --> - @guest('backend') - @if (Route::has('bo.login')) - <li class="nav-item"> - <a class="nav-link" href="{{ route('bo.login') }}">{{ __('Connexion') }}</a> - </li> - @endif - - @if (Route::has('bo.register')) - <li class="nav-item"> - <a class="nav-link" href="{{ route('bo.register') }}">{{ __('Créer un compte') }}</a> - </li> - @endif - @endguest - @auth('backend') - <li class="nav-item dropdown"> - <a class="nav-link dropdown-toggle" id="navbarDropdown" data-bs-toggle="dropdown" href="#" role="button" - aria-haspopup="true" aria-expanded="false"> - {{ auth('backend')->user()->firstname }} {{ auth('backend')->user()->lastname }} <span - class="caret"></span> - </a> - - <div class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown"> - <form class="text-center" action="{{ route('bo.logout') }}" method="POST"> - @csrf - <button class="btn btn-link text-decoration-none text-muted text-capitalize p-0" - type="submit">{{ __('Déconnexion') }}</button> - </form> - </div> - </li> - @endauth - </ul> + <!-- Main content --> + <main class="container-fluid"> + <div class="row justify-content-center p-3"> + <!-- Show a message when an action is performed --> + <div class="container"> + <div class="row justify-content-center"> + <div class="col-12 col-md-10 col-lg-8 px-2"> + @include('back::modules.flash-messages') + </div> + </div> </div> - </div> - </nav> - <main class="container-fluid @yield('bg-color') pb-5 pt-2"> - <div class="row justify-content-center"> - <div class="col col-md-6"> - @include('modules.flash-messages') - </div> - </div> - <div class="row p-md-3 justify-content-center"> - <div class="col-12 col-md-10 col-lg-8 p-md-0 pt-3"> - @yield('content') + <!-- Main content --> + <div class="row justify-content-center"> + <div class="col-12 col-md-10 col-lg-8 p-md-0 pt-3"> + @yield('content') + </div> </div> </div> </main> + + <!-- Footer --> @include('back::layouts.footer') - @stack('scripts-constants') + + <!-- Other --> @include('modules.script-routes') - @vite(['resources/ts/bo/bo.ts']) + @vite(['resources/ts/bo.ts']) @stack('scripts') </body> diff --git a/resources/views/layouts/footer.blade.php b/resources/views/layouts/footer.blade.php index 34e895d..15d04b5 100644 --- a/resources/views/layouts/footer.blade.php +++ b/resources/views/layouts/footer.blade.php @@ -1,8 +1,10 @@ -<footer id="footer" class="position-absolute bottom-0 w-100 text-center"> - <div class="text-center p-2 bg-light"> - <span class="text-reset">{{ $name }} - {{ $version }}</span> - - - <span class="text-reset fw-bold">UWIL</span> +<footer class="bg-body-secondary px-0 text-center" id="footer"> + <div class="p-2 text-center"> + @if (isset($cpsr_name) && isset($cpsr_version)) + <span class="text-reset">{{ $cpsr_name }} - {{ $cpsr_version }}</span> + <span>-</span> + @endif + <span class="text-reset fw-bold">{{ __('UWIL') }}</span> © {{ date('Y') }} </div> </footer> diff --git a/resources/views/layouts/head.blade.php b/resources/views/layouts/head.blade.php index e146bea..1aa5c70 100644 --- a/resources/views/layouts/head.blade.php +++ b/resources/views/layouts/head.blade.php @@ -9,33 +9,35 @@ <meta name="csrf-token" content="{{ csrf_token() }}"> @hasSection('canonical') -<link rel="canonical" href="@yield('canonical')" /> + <link href="@yield('canonical')" rel="canonical" /> @else -<link rel="canonical" href="{{ url()->current() }}" /> + <link href="{{ url()->current() }}" rel="canonical" /> @endif <!-- META INDEX, FOLLOW --> @hasSection('metaIndex') -<meta name="robots" content="@yield('metaIndex')"> + <meta name="robots" content="@yield('metaIndex')"> @else -<meta name="robots" content="index, follow"> + <meta name="robots" content="index, follow"> @endif <!-- Favicon --> {{-- GENERER favicon avec un outil https://realfavicongenerator.net/ --}} -<link rel="apple-touch-icon" sizes="180x180" href=""> -<link rel="icon" type="image/png" sizes="32x32" href=""> -<link rel="icon" type="image/png" sizes="16x16" href=""> -<link rel="manifest" href=""> -<link rel="mask-icon" href="" color="#5bbad5"> +<link href="" rel="apple-touch-icon" sizes="180x180"> +<link type="image/png" href="" rel="icon" sizes="32x32"> +<link type="image/png" href="" rel="icon" sizes="16x16"> +<link href="" rel="manifest"> +<link href="" rel="mask-icon" color="#5bbad5"> <meta name="msapplication-TileColor" content="#da532c"> <meta name="theme-color" content="#ffffff"> -@include('breadcrumbs.breadcrumb-head') +@include('modules.breadcrumbs.breadcrumb-head') <!-- STYLES --> -@vite(['resources/sass/bo/bo.scss']) +@vite(['resources/sass/bo.scss']) -@if(config('app.debug')) -<script nonce="{{ $nonce }}">window.vueDebug = true;</script> +@if (config('app.debug')) + <script nonce="{{ $nonce }}"> + window.vueDebug = true; + </script> @endif diff --git a/resources/views/layouts/header.blade.php b/resources/views/layouts/header.blade.php new file mode 100644 index 0000000..a300086 --- /dev/null +++ b/resources/views/layouts/header.blade.php @@ -0,0 +1,83 @@ +<nav class="navbar navbar-expand-lg position-fixed fixed-top bg-body-secondary shadow-sm" id="top-navbar"> + <div class="container"> + <a class="navbar-brand" data-bs-toggle="tooltip" data-bs-placement="bottom" href="{{ route('fo.homepage') }}" + title="{{ __('Accéder au front du projet') }}" target="_blank"> + {{ config('app.name', 'Laravel') }} + </a> + @auth + <button class="navbar-toggler" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" type="button" + aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}"> + <span class="navbar-toggler-icon"></span> + </button> + @endauth + + <div class="navbar-collapse collapse" id="navbarSupportedContent"> + <!-- Left Side Of Navbar --> + @include('back::layouts.nav-list') + + <!-- Right Side Of Navbar --> + <ul class="navbar-nav ms-auto"> + <!-- Bootstrap themes --> + <li class="nav-item dropdown @auth border-end @endauth"> + <button class="btn nav-link dropdown-toggle d-flex align-items-center h-100 flex-row border-0 pe-3" + data-bs-toggle="dropdown" type="button" aria-expanded="false"> + <i class="fa-solid fa-paint-roller"></i> + </button> + <form class="dropdown-menu dropdown-menu-end text-center" id="theme-selector" action="{{ route('bo.theme.set') }}" + method="POST"> + @push('scripts') + <script @if (!empty($nonce)) nonce="{{ $nonce }}" @endif> + document.addEventListener('DOMContentLoaded', function() { + const themeSelector = document.getElementById('theme-selector'); + if (themeSelector) { + themeSelector.addEventListener('change', function() { + this.submit(); + }); + } + }); + </script> + @endpush + @csrf + @php $theme = intval(Cache::get('theme')); @endphp + @foreach (\App\Enums\Theme\BootstrapThemeEnum::toArray() as $key => $bootstrapTheme) + <input class="d-none form-check-input" id="theme{{ $key }}" name="theme" type="radio" + value="{{ $bootstrapTheme->value }}"> + <label class="dropdown-item w-100 text-decoration-none @if ($bootstrapTheme->value === intval(Cache::get('theme'))) active @endif p-2" + for="theme{{ $key }}" role="button"> + {{ $bootstrapTheme->label }} + </label> + @endforeach + </form> + </li> + <!-- Authentication Links --> + @auth + <li class="nav-item dropdown"> + <button class="btn nav-link dropdown-toggle d-flex align-items-center h-100 flex-row border-0 ps-3" id="navbarDropdown" + data-bs-toggle="dropdown" type="button"> + <div class="d-flex flex-column align-items-end lh-1"> + <span + class="fw-bold">{{ auth('backend')->user()->first_name }} {{ auth('backend')->user()->last_name }}</span> + <small class="text-secondary-body">{{ auth('backend')->user()->role->label() }}</small> + </div> + </button> + <div class="dropdown-menu dropdown-menu-end m-0 text-center" aria-labelledby="navbarDropdown"> + <a class="dropdown-item w-100 text-decoration-none p-2" data-bs-toggle="tooltip" data-bs-placement="left" + href="{{ route('bo.users.edit',auth('backend')->user()->getKey()) }}" + title="{{ __('Accéder à son profile') }}"> + {{ __('Profile') }} + </a> + <hr class="dropdown-divider m-0"> + <form class="m-0 text-center" id="logout-form" action="{{ route('bo.logout') }}" method="POST"> + @csrf + <button class="dropdown-item w-100 text-decoration-none p-2" data-bs-toggle="tooltip" data-bs-placement="left" + type="submit" title="{{ __('Se déconnecter du back-office') }}"> + {{ __('Déconnexion') }} + </button> + </form> + </div> + </li> + @endauth + </ul> + </div> + </div> +</nav> diff --git a/resources/views/layouts/nav-list.blade.php b/resources/views/layouts/nav-list.blade.php new file mode 100644 index 0000000..7969996 --- /dev/null +++ b/resources/views/layouts/nav-list.blade.php @@ -0,0 +1,139 @@ +@auth('backend') + <ul class="navbar-nav me-auto"> + <li class="nav-item"> + <a class="nav-link @if (request()->routeIs('bo.dashboard')) active @endif" data-bs-toggle="tooltip" href="{{ route('bo.dashboard') }}" + title="{{ __('Revenir à l\'accueil') }}"> + {{ __('Accueil') }} + </a> + </li> + + @superadmin + <li class="nav-item dropdown"> + <a class="nav-link dropdown-toggle" id="navbarDropdownGame" data-bs-toggle="dropdown" href="#" role="button" + aria-haspopup="true" aria-expanded="false"> + {{ __('Gestion du jeu') }} <span class="caret"></span> + </a> + <ul class="dropdown-menu" aria-labelledby="navbarDropdownGame"> + <li> + <a class="dropdown-item" href="{{ route('bo.instant-win') }}">{{ __('Instant gagnants') }}</a> + </li> + <li> + <a class="dropdown-item" href="{{ route('bo.winners-list') }}">{{ __('Liste des gagnants') }}</a> + </li> + <li> + <a class="dropdown-item" href="{{ route('bo.bannedips.index') }}">{{ __('IP Bannies') }}</a> + </li> + <li> + <a class="dropdown-item" href="{{ route('bo.codes') }}">{{ __('Codes uniques') }}</a> + </li> + </ul> + </li> + <li class="nav-item dropdown"> + <a class="nav-link dropdown-toggle" id="navbarDropdownPrizes" data-bs-toggle="dropdown" href="#" role="button" + aria-haspopup="true" aria-expanded="false"> + {{ __('Lots') }} <span class="caret"></span> + </a> + <ul class="dropdown-menu" aria-labelledby="navbarDropdownPrizes"> + <li> + <a class="dropdown-item" href="{{ route('bo.prizes.index') }}">{{ __('Gérer les lots') }}</a> + </li> + <li> + <a class="dropdown-item" href="{{ route('bo.gain-views.index') }}">{{ __('Gérer les vues des lots') }}</a> + </li> + </ul> + </li> + @endsuperadmin + + @admin + <li class="nav-item dropdown"> + <a class="nav-link dropdown-toggle" id="navbarDropdownProducts" data-bs-toggle="dropdown" href="#" role="button" + aria-haspopup="true" aria-expanded="false"> + {{ __('Produits') }} <span class="caret"></span> + </a> + <ul class="dropdown-menu" aria-labelledby="navbarDropdownProducts"> + <li> + <a class="dropdown-item" href="{{ route('bo.ranges.index') }}">{{ __('Gammes') }}</a> + </li> + <li> + <a class="dropdown-item" href="{{ route('bo.products.index') }}">{{ __('Produits porteurs') }}</a> + </li> + </ul> + </li> + @endadmin + @superadmin + <li class="nav-item dropdown"> + <a class="nav-link dropdown-toggle" id="navbarDropdownContents" data-bs-toggle="dropdown" href="#" role="button" + aria-haspopup="true" aria-expanded="false"> + {{ __('Contenus') }} <span class="caret"></span> + </a> + <ul class="dropdown-menu" aria-labelledby="navbarDropdownContents"> + <li> + <a class="dropdown-item" + href="{{ route('bo.static_pages.edit', \App\Enums\StaticPageType::legals) }}">{{ __('Mentions légales') }}</a> + </li> + <li> + <a class="dropdown-item" + href="{{ route('bo.static_pages.edit', \App\Enums\StaticPageType::confidentiality) }}">{{ __('Charte') }}</a> + </li> + <li> + <a class="dropdown-item" + href="{{ route('bo.static_pages.edit', \App\Enums\StaticPageType::rules) }}">{{ __('Règlement') }}</a> + </li> + </ul> + </li> + <li class="nav-item dropdown"> + <a class="nav-link dropdown-toggle" id="navbarDropdownFaq" data-bs-toggle="dropdown" href="#" role="button" + aria-haspopup="true" aria-expanded="false"> + {{ __('FAQ') }} <span class="caret"></span> + </a> + <ul class="dropdown-menu" aria-labelledby="navbarDropdownFaq"> + <li> + <a class="dropdown-item" href="{{ route('bo.faq-themes.index') }}">{{ __('Thèmes') }}</a> + </li> + <li> + <a class="dropdown-item" href="{{ route('bo.faq-questions.index') }}">{{ __('Questions') }}</a> + </li> + </ul> + </li> + @endsuperadmin + + @canAny(['isConceptor', 'viewAny'], \App\Models\User::class) + <li class="nav-item"> + <div class="dropdown"> + <a class="nav-link @if (request()->routeIs('bo.users.*') || request()->routeIs('bo.activity_logs.*')) active @endif dropdown-toggle" data-bs-toggle="dropdown" type="button" + aria-expanded="false"> + {{ __('Avancé') }} + </a> + <ol class="dropdown-menu"> + @can('viewAny', \App\Models\User::class) + <li> + <a class="dropdown-item @if (request()->routeIs('bo.users.*')) active @endif" data-bs-toggle="tooltip" + href="{{ route('bo.users.index') }}" + title="{{ __('crud.helpers.list_all', ['model' => Str::plural(__('models.classes.user'))]) }}"> + {{ Str::of(__('models.classes.user'))->plural()->ucfirst() }} + </a> + </li> + @endcan + + @can('isConceptor') + <li class="nav-item"> + <a class="nav-link @if (request()->routeIs('bo.settings.*')) active @endif" data-bs-toggle="tooltip" + href="{{ route('bo.settings.edit', \App\Models\Settings::first()) }}" + title="{{ __('models.classes.settings') }}"> + {{ __('models.classes.settings') }} + </a> + </li> + <li> + <a class="dropdown-item @if (request()->routeIs('bo.activity_logs.*')) active @endif" data-bs-toggle="tooltip" + href="{{ route('bo.activity_logs.index') }}" + title="{{ __('crud.helpers.list_all', ['model' => trans_choice('models.classes.activity_log', '*')]) }}"> + {{ Str::of(trans_choice('models.classes.activity_log', '*'))->ucfirst() }} + </a> + </li> + @endcan + </ol> + </a> + </li> + @endcan + </ul> +@endauth diff --git a/resources/views/layouts/nav-menu.blade.php b/resources/views/layouts/nav-menu.blade.php deleted file mode 100644 index 6f73fd0..0000000 --- a/resources/views/layouts/nav-menu.blade.php +++ /dev/null @@ -1,103 +0,0 @@ -@auth('backend') - <ul class="navbar-nav me-auto"> - <li class="nav-item"> - <a class="nav-link @if(request()->routeIs('bo.dashboard')) active @endif" - href="{{ route('bo.dashboard') }}" - title="{{ __('Revenir à l\'accueil') }}" - data-bs-toggle="tooltip"> - {{ __('Dashboard') }} - </a> - </li> - @superadmin - <li class="nav-item"> - <a class="nav-link @if(request()->routeIs('bo.settings.*')) active @endif" - href="{{ route('bo.settings.edit', \App\Models\Settings::first()) }}" - title="{{ __('models.classes.settings') }}" - data-bs-toggle="tooltip"> - {{ __('models.classes.settings') }} - </a> - </li> - <li class="nav-item dropdown"> - <a id="navbarDropdownGame" class="nav-link dropdown-toggle" href="#" role="button" - data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> - {{ __('Gestion du jeu') }} <span class="caret"></span> - </a> - <ul class="dropdown-menu" aria-labelledby="navbarDropdownGame"> - <li> - <a class="dropdown-item" href="{{ route('bo.instant-win') }}">{{ __('Instant gagnants') }}</a> - </li> - <li> - <a class="dropdown-item" href="{{ route('bo.winners-list') }}">{{ __('Liste des gagnants') }}</a> - </li> - <li> - <a class="dropdown-item" href="{{ route('bo.bannedips.index') }}">{{ __('IP Bannies') }}</a> - </li> - <li> - <a class="dropdown-item" href="{{ route('bo.codes') }}">{{ __('Codes uniques') }}</a> - </li> - </ul> - </li> - <li class="nav-item dropdown"> - <a id="navbarDropdownPrizes" class="nav-link dropdown-toggle" href="#" role="button" - data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> - {{ __('Lots') }} <span class="caret"></span> - </a> - <ul class="dropdown-menu" aria-labelledby="navbarDropdownPrizes"> - <li> - <a class="dropdown-item" href="{{route('bo.prizes.index')}}">{{ __('Gérer les lots') }}</a> - </li> - <li> - <a class="dropdown-item" href="{{route('bo.gain-views.index')}}">{{ __('Gérer les vues des lots') }}</a> - </li> - </ul> - </li> - @endsuperadmin - <li class="nav-item dropdown"> - <a id="navbarDropdownProducts" class="nav-link dropdown-toggle" href="#" role="button" - data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> - {{ __('Produits') }} <span class="caret"></span> - </a> - <ul class="dropdown-menu" aria-labelledby="navbarDropdownProducts"> - <li> - <a class="dropdown-item" href="{{ route('bo.ranges.index') }}">{{ __('Gammes') }}</a> - </li> - <li> - <a class="dropdown-item" href="{{route('bo.products.index')}}">{{ __('Produits porteurs') }}</a> - </li> - </ul> - </li> - @superadmin - <li class="nav-item dropdown"> - <a id="navbarDropdownContents" class="nav-link dropdown-toggle" href="#" role="button" - data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> - {{ __('Contenus') }} <span class="caret"></span> - </a> - <ul class="dropdown-menu" aria-labelledby="navbarDropdownContents"> - <li> - <a class="dropdown-item" href="{{route('bo.contents.edit', 1)}}">{{ __('Mentions légales') }}</a> - </li> - <li> - <a class="dropdown-item" href="{{route('bo.contents.edit', 2)}}">{{ __('Charte') }}</a> - </li> - <li> - <a class="dropdown-item" href="{{route('bo.contents.edit', 3)}}">{{ __('Règlement') }}</a> - </li> - </ul> - </li> - <li class="nav-item dropdown"> - <a id="navbarDropdownFaq" class="nav-link dropdown-toggle" href="#" role="button" - data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> - {{ __('FAQ') }} <span class="caret"></span> - </a> - <ul class="dropdown-menu" aria-labelledby="navbarDropdownFaq"> - <li> - <a class="dropdown-item" href="{{route('bo.faq-themes.index')}}">{{ __('Thèmes') }}</a> - </li> - <li> - <a class="dropdown-item" href="{{route('bo.faq-questions.index')}}">{{ __('Questions') }}</a> - </li> - </ul> - </li> - @endsuperadmin - </ul> -@endauth diff --git a/resources/views/modules/breadcrumbs/breadcrumb-body.blade.php b/resources/views/modules/breadcrumbs/breadcrumb-body.blade.php new file mode 100644 index 0000000..51b36a8 --- /dev/null +++ b/resources/views/modules/breadcrumbs/breadcrumb-body.blade.php @@ -0,0 +1,7 @@ +@hasSection('breadcrumb') + @if (Breadcrumbs::exists(app()->view->getSections()['breadcrumb'])) + {{ call_user_func_array('Breadcrumbs::render', array_merge([app()->view->getSections()['breadcrumb']], Arr::wrap($brParam))) }} + @endif +@elseif(Breadcrumbs::exists(request()->route()->getName())) + {{ Breadcrumbs::render(request()->route()->getName()) }} +@endif diff --git a/resources/views/modules/breadcrumbs/breadcrumb-head.blade.php b/resources/views/modules/breadcrumbs/breadcrumb-head.blade.php new file mode 100644 index 0000000..ac3e053 --- /dev/null +++ b/resources/views/modules/breadcrumbs/breadcrumb-head.blade.php @@ -0,0 +1,10 @@ +@hasSection('breadcrumb') + @if (Breadcrumbs::exists(app()->view->getSections()['breadcrumb'])) + {{ call_user_func_array( + 'Breadcrumbs::view', + array_merge(['breadcrumbs::json-ld', app()->view->getSections()['breadcrumb']], Arr::wrap($brParam)), + ) }} + @endif +@elseif(Breadcrumbs::exists(request()->route()->getName())) + {{ Breadcrumbs::view('breadcrumbs::json-ld',request()->route()->getName()) }} +@endif diff --git a/resources/views/modules/breadcrumbs/breadcrumb-layout.blade.php b/resources/views/modules/breadcrumbs/breadcrumb-layout.blade.php new file mode 100644 index 0000000..e1b07cd --- /dev/null +++ b/resources/views/modules/breadcrumbs/breadcrumb-layout.blade.php @@ -0,0 +1,17 @@ +@if (count($breadcrumbs)) + <ol class="breadcrumb justify-content-center w-100 m-0"> + @foreach ($breadcrumbs as $breadcrumb) + @if ($breadcrumb->url && !$loop->last) + <li class="breadcrumb-item d-flex"> + <a class="h2 fw-bold m-0 ps-2" href="{{ $breadcrumb->url }}"> + {{ $breadcrumb->title }} + </a> + </li> + @else + <li class="breadcrumb-item btn-games d-flex align-items-center flex-grow-1 active h2 fw-bold m-0"> + {{ $breadcrumb->title }} + </li> + @endif + @endforeach + </ol> +@endif diff --git a/resources/views/modules/change-model-order.blade.php b/resources/views/modules/change-model-order.blade.php new file mode 100644 index 0000000..a76ed7c --- /dev/null +++ b/resources/views/modules/change-model-order.blade.php @@ -0,0 +1,32 @@ +<td class="text-center align-middle"> + @can('changeOrder', $model) + <div class="d-flex justify-content-center align-items-center"> + <form class="@if ($loop->first and $models->onFirstPage()) invisible @endif" + action="{{ route('bo.' . $routeName . '.change-order', [Str::singular($routeName) => $model, 'direction' => 'up']) }}" + method="POST" + > + @csrf + @method('PATCH') + <button class="btn btn-sm btn-outline-secondary @if ($loop->first and $models->onFirstPage()) disabled @endif" data-bs-toggle="tooltip" + type="submit" title="{{ __('crud.other.up') }}"> + <i class="fas fa-arrow-up"></i> + </button> + </form> + <form class="@if ($loop->last and $models->currentPage() === $models->lastPage()) invisible @endif" + action="{{ route('bo.' . $routeName . '.change-order', [Str::singular($routeName) => $model, 'direction' => 'down']) }}" + method="POST" + > + @csrf + @method('PATCH') + <button class="btn btn-sm btn-outline-secondary @if ($loop->last and $models->currentPage() === $models->lastPage()) disabled @endif ms-1" data-bs-toggle="tooltip" + type="submit" title="{{ __('crud.other.down') }}"> + <i class="fas fa-arrow-down"></i> + </button> + </form> + </div> + @else + <span class="text-danger" data-bs-toggle="tooltip" title="{{ __('crud.other.right') }}"> + <i class="fa-solid fa-ban"></i> + </span> + @endcan +</td> diff --git a/resources/views/modules/change-published-status.blade.php b/resources/views/modules/change-published-status.blade.php new file mode 100644 index 0000000..3322e5a --- /dev/null +++ b/resources/views/modules/change-published-status.blade.php @@ -0,0 +1,20 @@ +<td class="text-center align-middle"> + @can('changePublished', $model) + <form action="{{ route('bo.' . $routeName . '.change-published', $model->getRouteKey()) }}" method="POST"> + @csrf + @method('PATCH') + <button class="btn btn-sm @if ($model->published) btn-primary @else btn-danger @endif" data-bs-toggle="tooltip" + type="submit" title="{{ __($model->published ? __('crud.other.unpublish') : __('crud.other.publish')) }}"> + @if ($model->published) + <i class="fa-solid fa-circle-check"></i> + @else + <i class="fa-solid fa-circle-xmark"></i> + @endif + </button> + </form> + @else + <span class="text-danger" data-bs-toggle="tooltip" title="{{ __('crud.other.right') }}"> + <i class="fa-solid fa-ban"></i> + </span> + @endcan +</td> diff --git a/resources/views/modules/pagination.blade.php b/resources/views/modules/pagination.blade.php index 687ca1b..5ce1e6b 100644 --- a/resources/views/modules/pagination.blade.php +++ b/resources/views/modules/pagination.blade.php @@ -1,140 +1,114 @@ -<?php - // Get actual pagination. - $pagination = (Cache::get('pagination')) ? intval(Cache::get('pagination')) : config('pagination.default'); - // Get actual request parameters. - $queryString = ""; - if (!array_key_exists("page", request()->all())) { - foreach (request()->all() as $key => $value) { - if($key != "page" && $key != "pagination" && $key != "search") { - $queryString .= "&" . $key . "=" . $value; - } - } - } -?> -@if($paginator->items()) -<nav class="pagination-custom d-flex flex-column flex-md-row justify-content-between align-items-center"> - {{-- SELECT ITEMS PER PAGE --}} - <div class="dropup-center dropup d-flex justify-content-center align-items-center w-fit input-group mb-3 m-md-0"> - <span class="btn-md input-group-text" - data-bs-toggle="tooltip" - data-bs-placement="top" - title="{{ __('crud.pagination.paginate_list') }}"> - <i class="fa-solid fa-list-ul"></i> - </span> - <button class="btn btn-primary dropdown-toggle" - type="button" - data-bs-toggle="dropdown" - aria-expanded="false"> - {{ $pagination }} - </button> - <ul class="dropdown-menu"> - @foreach (\App\Enums\ItemsPerPaginationEnum::toArray() as $itemsPerPaginationEnum) - <li> - <a class="dropdown-item text-center user-select-none @if($pagination === $itemsPerPaginationEnum->value) active @endif" - href="{{ request()->fullUrlWithQuery(['page' => 1, 'pagination' => $itemsPerPaginationEnum->value]) . $queryString }}" - role="button"> - {{ $itemsPerPaginationEnum->value }} - </a> - </li> - @endforeach - </ul> - </div> - @if ($paginator->hasPages()) - {{-- PAGINATION --}} - <div class="d-flex justify-content-end align-items-center"> - <ul class="pagination m-0"> - {{-- PREVIOUS PAGE --}} - <li class="page-item @if ($paginator->onFirstPage()) disabled @endif"> - <a class="page-link d-flex align-items-center h-100" - data-bs-toggle="tooltip" - data-bs-placement="top" - title="{{ __('crud.pagination.previous') }}" - @if (!$paginator->onFirstPage()) - href="{{ request()->fullUrlWithQuery(['page' => $paginator->currentPage() - 1]) . $queryString }}" - @endif - rel="prev" - aria-label="@lang('crud.pagination.previous')" - @if ($paginator->onFirstPage()) aria-hidden="true" disabled @endif> - <i class="fa-solid fa-chevron-left fa-2xs mt-1"></i> - </a> - </li> - {{-- FIRST PAGE --}} - @if($paginator->currentPage() > 3) - <li class="page-item" - data-bs-toggle="tooltip" - data-bs-placement="top" - title="{{ __('crud.pagination.specific_page', ['id' => 1]) }}"> - <a class="page-link" href="{{ request()->fullUrlWithQuery(['page' => 1]) . $queryString }}">1</a> - </li> - @endif - {{-- HIDE PAGES TOO FAR --}} - @if($paginator->currentPage() > 4) - <li class="page-item disabled"><span class="page-link">...</span></li> - @endif - {{-- SHOW ACTUAL PAGES + N-2/N+2 --}} - @foreach(range(1, $paginator->lastPage()) as $i) - @if($i >= $paginator->currentPage() - 2 && $i <= $paginator->currentPage() + 2) - @if ($i == $paginator->currentPage()) - <li class="page-item"> - <div class="dropup-center dropup"> - <button class="btn btn-primary dropdown-toggle rounded-0" - type="button" - data-bs-toggle="dropdown" - aria-expanded="false"> - {{ $i }} - </button> - <ul class="dropdown-menu"> - @for ($i = 1; $i <= $paginator->lastPage(); $i++) - <li> - <a class="dropdown-item text-center user-select-none @if($paginator->currentPage() === $i) active @endif" - href="{{ request()->fullUrlWithQuery(['page' => $i]) . $queryString }}" - role="button"> - {{ $i }} - </a> +{{-- GET ACTUAL PAGINATION --}} +@php + $pagination = intval( + Cache::get( + 'pagination.' . + \Illuminate\Support\Str::slug( + request() + ->route() + ->getName(), + ), + ), + ); +@endphp +@if ($paginator->items()) + <nav class="pagination-custom d-flex justify-content-between align-items-center"> + {{-- SELECT ITEMS PER PAGE --}} + <div class="dropup-center d-flex justify-content-center align-items-center input-group w-fit"> + <span class="btn-md input-group-text" data-bs-toggle="tooltip" title="{{ __('pagination.paginate_list') }}"> + <i class="fa-solid fa-list-ul"></i> + </span> + <button class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" type="button" aria-expanded="false"> + {{ $pagination }} + </button> + <ul class="dropdown-menu"> + @foreach (\App\Enums\Pagination\ItemsPerPaginationEnum::toArray() as $itemsPerPaginationEnum) + <li> + <a class="dropdown-item user-select-none @if ($pagination === $itemsPerPaginationEnum->value) active @endif text-center" + href="{{ request()->fullUrlWithQuery(['page' => 1, 'pagination' => $itemsPerPaginationEnum->value]) }}" + role="button"> + {{ $itemsPerPaginationEnum->value }} + </a> + </li> + @endforeach + </ul> + </div> + @if ($paginator->hasPages()) + {{-- PAGINATION --}} + <div class="d-flex justify-content-end align-items-center"> + <ul class="pagination m-0"> + {{-- PREVIOUS PAGE --}} + <li class="page-item @if ($paginator->onFirstPage()) disabled @endif"> + <a class="page-link d-flex align-items-center h-100" data-bs-toggle="tooltip" + title="{{ __('pagination.previous_page') }}" aria-label="{{ __('pagination.previous_page') }}" + @if (!$paginator->onFirstPage()) href="{{ request()->fullUrlWithQuery(['page' => $paginator->currentPage() - 1]) }}" @endif + rel="prev" @if ($paginator->onFirstPage()) aria-hidden="true" disabled @endif> + <i class="fa-solid fa-chevron-left fa-2xs mt-1"></i> + </a> + </li> + {{-- FIRST PAGE --}} + @if ($paginator->currentPage() > 3) + <li class="page-item d-none d-sm-block" data-bs-toggle="tooltip" + title="{{ __('pagination.specific_page', ['id' => 1]) }}"> + <a class="page-link" href="{{ request()->fullUrlWithQuery(['page' => 1]) }}">1</a> </li> - @endfor - </ul> - </div> - </li> - @else - <li class="page-item d-none d-sm-block" - data-bs-toggle="tooltip" - data-bs-placement="top" - title="{{ __('crud.pagination.specific_page', ['id' => $i]) }}"> - <a class="page-link" href="{{ request()->fullUrlWithQuery(['page' => $i]) . $queryString }}">{{ $i }}</a> - </li> - @endif - @endif - @endforeach - {{-- HIDE PAGES TOO FAR --}} - @if($paginator->currentPage() < $paginator->lastPage() - 3) - <li class="page-item disabled"><span class="page-link">...</span></li> - @endif - {{-- LAST PAGE --}} - @if($paginator->currentPage() < $paginator->lastPage() - 2) - <li class="page-item" - data-bs-toggle="tooltip" - data-bs-placement="top" - title="{{ __('crud.pagination.specific_page', ['id' => $paginator->lastPage()]) }}"> - <a class="page-link" href="{{ request()->fullUrlWithQuery(['page' => $paginator->lastPage()]) . $queryString }}">{{ $paginator->lastPage() }}</a> - </li> - @endif - {{-- NEXT PAGE --}} - <li class="page-item @if (!$paginator->hasMorePages()) disabled @endif"> - <a class="page-link d-flex align-items-center h-100" - data-bs-toggle="tooltip" - data-bs-placement="top" - title="{{ __('crud.pagination.next') }}" - @if ($paginator->hasMorePages()) - href="{{ request()->fullUrlWithQuery(['page' => $paginator->currentPage() + 1]) . $queryString }}" @endif - rel="next" - aria-label="@lang('crud.pagination.next')" - @if (!$paginator->hasMorePages()) aria-hidden="true" disabled @endif> - <i class="fa-solid fa-chevron-right fa-2xs mt-1"></i></a> - </li> - </ul> - </div> - @endif -</nav> + {{-- HIDE PAGES TOO FAR --}} + @if ($paginator->currentPage() > 4) + <li class="page-item d-none d-sm-block disabled"><span class="page-link">...</span></li> + @endif + {{-- SHOW ACTUAL PAGES + N-2/N+2 --}} + @foreach (range(1, $paginator->lastPage()) as $i) + @if ($i >= $paginator->currentPage() - 2 && $i <= $paginator->currentPage() + 2) + @if ($i == $paginator->currentPage()) + <li class="page-item"> + <div class="dropup-center"> + <button class="btn btn-primary dropdown-toggle rounded-0" data-bs-toggle="dropdown" type="button" + aria-expanded="false"> + {{ $i }} + </button> + <ul class="dropdown-menu"> + @for ($i = 1; $i <= $paginator->lastPage(); $i++) + <li> + <a class="dropdown-item user-select-none @if ($paginator->currentPage() === $i) active @endif text-center" + href="{{ request()->fullUrlWithQuery(['page' => $i]) }}" role="button"> + {{ $i }} + </a> + </li> + @endfor + </ul> + </div> + </li> + @else + <li class="page-item d-none d-md-block" data-bs-toggle="tooltip" + title="{{ __('pagination.specific_page', ['id' => $i]) }}"> + <a class="page-link" href="{{ request()->fullUrlWithQuery(['page' => $i]) }}">{{ $i }}</a> + </li> + @endif + @endif + @endforeach + {{-- HIDE PAGES TOO FAR --}} + @if ($paginator->currentPage() < $paginator->lastPage() - 3) + <li class="page-item d-none d-sm-block disabled"><span class="page-link">...</span></li> + @endif + {{-- LAST PAGE --}} + @if ($paginator->currentPage() < $paginator->lastPage() - 2) + <li class="page-item d-none d-sm-block" data-bs-toggle="tooltip" + title="{{ __('pagination.specific_page', ['id' => $paginator->lastPage()]) }}"> + <a class="page-link" + href="{{ request()->fullUrlWithQuery(['page' => $paginator->lastPage()]) }}">{{ $paginator->lastPage() }}</a> + </li> + @endif + {{-- NEXT PAGE --}} + <li class="page-item @if (!$paginator->hasMorePages()) disabled @endif"> + <a class="page-link d-flex align-items-center h-100" data-bs-toggle="tooltip" + title="{{ __('pagination.next_page') }}" aria-label="{{ __('pagination.next_page') }}" + @if ($paginator->hasMorePages()) href="{{ request()->fullUrlWithQuery(['page' => $paginator->currentPage() + 1]) }}" @endif + rel="next" @if (!$paginator->hasMorePages()) aria-hidden="true" disabled @endif> + <i class="fa-solid fa-chevron-right fa-2xs mt-1"></i></a> + </li> + </ul> + </div> + @endif + </nav> @endif diff --git a/resources/views/modules/search-bar.blade.php b/resources/views/modules/search-bar.blade.php index 8cb21e1..f8f7c62 100644 --- a/resources/views/modules/search-bar.blade.php +++ b/resources/views/modules/search-bar.blade.php @@ -1,44 +1,46 @@ @php $iArgs = []; - if(request()->sort_col) { + if (request()->sort_col) { $iArgs['sort_col'] = request()->sort_col; } - if(request()->search) { + if (request()->search) { $iArgs['search'] = request()->sort_way; } @endphp -<div class="row my-3"> +<div class="row mb-3"> <div class="col-12"> <form id="searchForm" action="/{{ request()->path() }}"> - <div class="input-group"> + <div class="input-group pt-3"> <label class="input-group-text" for="searchField">{{ $searchFields }}</label> - @if(request()->sort_col) - <input class="d-none" type="text" name="sort_col" value="{{ request()->sort_col }}"> + @if (request()->sort_col) + <input class="d-none" name="sort_col" type="text" value="{{ request()->sort_col }}"> @endif - @if(request()->sort_way) - <input class="d-none" type="text" name="sort_way" value="{{ request()->sort_way }}"> + @if (request()->sort_way) + <input class="d-none" name="sort_way" type="text" value="{{ request()->sort_way }}"> @endif - @if(request()->rangefilter) - <input class="d-none" type="text" name="rangefilter" value="{{ request()->rangefilter }}"> + @if (request()->rangefilter) + <input class="d-none" name="rangefilter" type="text" value="{{ request()->rangefilter }}"> @endif - @if(request()->group) - <input class="d-none" type="text" name="group" value="{{ request()->group }}"> + @if (request()->group) + <input class="d-none" name="group" type="text" value="{{ request()->group }}"> @endif - <input type="search" id="searchField" name="search" class="form-control" value="{{ request()->search }}" placeholder="{{ __('Your keywords here...') }}"/> - <button type="submit" class="btn btn-primary" data-bs-toggle="tooltip" title="{{ __('Start search') }}"> + <input class="form-control" id="searchField" name="search" type="search" value="{{ request()->search }}" + placeholder="{{ __('crud.other.search_placeholder') }}" /> + <button class="btn btn-primary" data-bs-toggle="tooltip" type="submit" title="{{ __('crud.other.search_start') }}"> <i class="fas fa-search"></i> </button> - <button id="searchDelete" type="button" class="btn btn-danger" data-bs-toggle="tooltip" title="{{ __('Delete search') }}"> + <button class="btn btn-danger" id="searchDelete" data-bs-toggle="tooltip" type="button" + title="{{ __('crud.other.search_delete') }}"> <i class="fas fa-backspace"></i> </button> @push('scripts') - <script nonce="{{ $nonce }}"> - document.getElementById('searchDelete').addEventListener('click', function (e) { - document.getElementById('searchField').value = ''; - document.getElementById('searchForm').submit(); - }); - </script> + <script nonce="{{ $nonce }}"> + document.getElementById('searchDelete').addEventListener('click', function(e) { + document.getElementById('searchField').value = ''; + document.getElementById('searchForm').submit(); + }); + </script> @endpush </div> </form> diff --git a/resources/views/modules/table-col-sorter.blade.php b/resources/views/modules/table-col-sorter.blade.php index 6bae966..fcf00bb 100644 --- a/resources/views/modules/table-col-sorter.blade.php +++ b/resources/views/modules/table-col-sorter.blade.php @@ -1,73 +1,76 @@ @php -$route = request()->route(); -$routeName = $route->getName(); -$rst = !is_null(request()->rst) || !Session::has("$routeName.sorted"); -$noOrder = !empty($noOrder); -$noOrder = $noOrder || (Session::get("$routeName.sort_col") !== 'order' and (Session::has("$routeName.sort_col") or Session::has("$routeName.sort_way"))); -$noOrder = $noOrder || !empty(request()->search); -$ignore = (isset($ignore) and is_array($ignore)) ? $ignore : []; + $route = request()->route(); + $routeName = $route->getName(); + $rst = !is_null(request()->rst) || !Session::has("$routeName.sorted"); + $noOrder = !empty($noOrder); + $noOrder = $noOrder || (Session::get("$routeName.sort_col") !== 'order' and (Session::has("$routeName.sort_col") or Session::has("$routeName.sort_way"))); + $noOrder = $noOrder || !empty(request()->search); + $ignore = (isset($ignore) and is_array($ignore)) ? $ignore : []; @endphp -<tr class="table-col-sorter"> - @foreach($cols as $col => $colname) - @php - $iArgs = ['sort_col' => $col]; - if(request()->search) { - $iArgs['search'] = request()->search; - } - @endphp - {{-- NO Sort for order column --}} - @if($col === 'order' or in_array($col, $ignore)) - @if($noOrder and !in_array($col, $ignore)) @continue @endif - <th scope="col" class="text-center h-100 @if(isset($mobileHide) and is_array($mobileHide) and in_array($col, $mobileHide)) d-none d-md-table-cell @endif"> - <div class="d-flex justify-content-center align-items-center h-100"> - <span class="text-nowrap">{{ $colname }}</span> - </div> - </th> - @continue - @endif - <th scope="col" class="text-center h-100 @if(isset($mobileHide) and is_array($mobileHide) and in_array($col, $mobileHide)) d-none d-md-table-cell @endif"> - <div class="d-flex justify-content-center align-items-center h-100"> - <span class="text-nowrap"> - {{ $colname }} - </span> - @if(!session("$routeName.sort_way") or session("$routeName.sort_col") !== $col or (session("$routeName.sort_way") === 'desc' and session("$routeName.sort_col") === $col)) - <a class="text-decoration-none ms-1" - href="{{ request()->fullUrlWithQuery(array_merge($iArgs, ['sort_way' => 'asc', 'rst' => null])) }}"> - <button - class="btn btn-sm btn-outline-dark" - title="{{ __('crud.other.sort_descending', ['name' => Str::lower($colname)]) }}" - data-bs-toggle="tooltip"> - <i class="fas fa-arrow-down 2xs"></i> - </button> - </a> +<tr class="table-col-sorter border-start-0 border-end-0 border-top-0 border-dark border border-2"> + @foreach ($cols as $col => $colname) + @php + $iArgs = ['sort_col' => $col]; + if (request()->search) { + $iArgs['search'] = request()->search; + } + @endphp + {{-- NO Sort for order column --}} + @if ($col === 'order' or in_array($col, $ignore)) + @if ($noOrder and !in_array($col, $ignore)) + @continue @endif - @if(!session("$routeName.sort_way") or session("$routeName.sort_col") !== $col or (session("$routeName.sort_way") === 'asc' and session("$routeName.sort_col") === $col)) - <a class="text-decoration-none ms-1" - href="{{ request()->fullUrlWithQuery(array_merge($iArgs, ['sort_way' => 'desc', 'rst' => null])) }}"> - <button - class="btn btn-sm btn-outline-dark" - title="{{ __('crud.other.sort_ascending', ['name' => Str::lower($colname)]) }}" - data-bs-toggle="tooltip"> - <i class="fas fa-arrow-up 2xs"></i> - </button> - </a> - @endif - </div> - </th> + <th class="h-100 @if (isset($mobileHide) and is_array($mobileHide) and in_array($col, $mobileHide)) d-none d-md-table-cell @endif text-center" scope="col"> + <div class="d-flex justify-content-center align-items-center h-100"> + <span class="text-nowrap">{{ $colname }}</span> + </div> + </th> + @continue + @endif + <th class="h-100 @if (isset($mobileHide) and is_array($mobileHide) and in_array($col, $mobileHide)) d-none d-md-table-cell @endif text-center" scope="col"> + <div class="d-flex justify-content-center align-items-center h-100"> + <span class="text-nowrap"> + {{ $colname }} + </span> + @if ( + !session("$routeName.sort_way") or + session("$routeName.sort_col") !== $col or + session("$routeName.sort_way") === 'desc' and session("$routeName.sort_col") === $col) + <a class="text-decoration-none ms-1" + href="{{ request()->fullUrlWithQuery(array_merge($iArgs, ['sort_way' => 'asc', 'rst' => null])) }}"> + <button class="btn btn-sm btn-outline-secondary" data-bs-toggle="tooltip" + title="{{ __('crud.other.sort_descending', ['name' => Str::lower($colname)]) }}"> + <i class="fas fa-arrow-down 2xs"></i> + </button> + </a> + @endif + @if ( + !session("$routeName.sort_way") or + session("$routeName.sort_col") !== $col or + session("$routeName.sort_way") === 'asc' and session("$routeName.sort_col") === $col) + <a class="text-decoration-none ms-1" + href="{{ request()->fullUrlWithQuery(array_merge($iArgs, ['sort_way' => 'desc', 'rst' => null])) }}"> + <button class="btn btn-sm btn-outline-secondary" data-bs-toggle="tooltip" + title="{{ __('crud.other.sort_ascending', ['name' => Str::lower($colname)]) }}"> + <i class="fas fa-arrow-up 2xs"></i> + </button> + </a> + @endif + </div> + </th> @endforeach {{-- Buttons Col --}} - <th scope="col" class="text-end"> - @if(!$rst) - <a class="btn btn-sm btn-danger" - href="{{ request()->fullUrlWithQuery(['sort_col' => null, 'sort_way' => null, 'rst' => true, 'search' => null]) }}" - data-bs-toggle="tooltip" - title="{{ __('crud.other.sort_delete') }}"> - <i class="fa fa-eraser"></i> - </a> + <th class="text-end" scope="col"> + @if (!$rst) + <a class="btn btn-sm btn-danger" data-bs-toggle="tooltip" + href="{{ request()->fullUrlWithQuery(['sort_col' => null, 'sort_way' => null, 'rst' => true, 'search' => null]) }}" + title="{{ __('crud.other.sort_delete') }}"> + <i class="fa fa-eraser"></i> + </a> @else - <span data-bs-toggle="tooltip" data-bs-placement="top" title="{{ __('crud.other.sort_arrow') }}"> - <i class="fas fa-question-circle"></i> - </span> + <span data-bs-toggle="tooltip" title="{{ __('crud.other.sort_arrow') }}"> + <i class="fas fa-question-circle"></i> + </span> @endif </th> </tr> diff --git a/resources/views/pages/activity_logs/index-list.blade.php b/resources/views/pages/activity_logs/index-list.blade.php new file mode 100644 index 0000000..cb1e0a4 --- /dev/null +++ b/resources/views/pages/activity_logs/index-list.blade.php @@ -0,0 +1,72 @@ +<table class="table-hover table-fix-action m-0 table"> + @if (count($activitylogModels) > 0) + <thead> + @include('back::modules.table-col-sorter', [ + 'cols' => [ + 'event' => Str::ucfirst(__('validation.custom.event')), + 'user' => Str::ucfirst(__('models.classes.user')), + 'model' => Str::ucfirst(__('validation.custom.model')), + 'created_at' => Str::ucfirst(__('validation.attributes.created_at')), + ], + ]) + </thead> + <tbody> + @foreach ($activitylogModels as $activitylogModel) + <tr class="border-bottom list-item"> + <td + class="bg-{{ $activitylogModel->event->bootstrapClass() }}-subtle text-{{ $activitylogModel->event->bootstrapClass() }}-emphasis text-center align-middle"> + {{ $activitylogModel->event->label() }} + </td> + <td class="bg-{{ $activitylogModel->event->bootstrapClass() }}-subtle text-center align-middle"> + @if (isset($activitylogModel->user)) + <a class="btn btn-sm btn-primary text-decoration-none" data-bs-toggle="tooltip" + href="{{ route('bo.users.edit', $activitylogModel->user) }}" + title="{{ __('crud.helpers.view_model', ['model' => __('models.classes.user')]) }}"> + {{ $activitylogModel->user->last_name }} {{ $activitylogModel->user->first_name }} + </a> + @elseif($activitylogModel->is_console) + {{ __('Utilisateur dans la console') }} + @elseif($activitylogModel->is_anonymous) + {{ __('Utilisateur anonyme') }} + @else + {{ __('Utilisateur supprimé') }} + @endif + </td> + @php + $targetModel = $activitylogModel->model_class::where((new $activitylogModel->model_class())->getRouteKeyName(), $activitylogModel->model_id)->first(); + $editRouteName = 'bo.' . optional($targetModel)->getTable() . '.edit'; + @endphp + <td class="bg-{{ $activitylogModel->event->bootstrapClass() }}-subtle text-center align-middle"> + @if (!empty($targetModel) and Route::has($editRouteName)) + <a class="btn btn-sm btn-primary text-decoration-none" data-bs-toggle="tooltip" + href="{{ route($editRouteName, $activitylogModel->model_id) }}" + title="{{ __('crud.helpers.view_model', ['model' => __('models.classes.' . Str::singular($targetModel->getTable()))]) }}"> + @endif + {{ $activitylogModel->model_class }} + @if (!empty($targetModel) and Route::has($editRouteName)) + </a> + @endif + </td> + <td class="bg-{{ $activitylogModel->event->bootstrapClass() }}-subtle text-center align-middle"> + <span class="badge bg-secondary"> + {{ $activitylogModel->created_at->isoFormat('LLLL') }} + </span> + </td> + <td class="text-end align-middle"> + @can('isConceptor') + <a class="btn btn-sm btn-warning" data-bs-toggle="tooltip" + href="{{ route('bo.activity_logs.show', ['activity_log' => $activitylogModel]) }}" + title="{{ Str::ucfirst(__('crud.actions.view')) }}"> + <i class="fa-solid fa-eye"></i> + </a> + @endcan + </td> + </tr> + @endforeach + </tbody> + @else + <tr> + <td class="border-0">{{ __('crud.other.no_model_found', ['model' => trans_choice('models.classes.activity_log', 1)]) }}</td> + </tr> + @endif +</table> diff --git a/resources/views/pages/activity_logs/index.blade.php b/resources/views/pages/activity_logs/index.blade.php new file mode 100644 index 0000000..a7eb42a --- /dev/null +++ b/resources/views/pages/activity_logs/index.blade.php @@ -0,0 +1,15 @@ +@extends('back::layout') + +@section('title', Str::ucfirst(trans_choice('models.classes.activity_log', '*'))) +@section('description', __('Liste de tous les :model', ['model' => trans_choice('models.classes.activity_log', '*')])) + +@section('content') + <div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap"> + @include('modules.breadcrumbs.breadcrumb-body') + </div> + @include('back::modules.search-bar') + <div class="table-responsive mb-3"> + @include('back::pages.activity_logs.index-list') + </div> + {{ $activitylogModels->links() }} +@endsection diff --git a/resources/views/pages/activity_logs/show.blade.php b/resources/views/pages/activity_logs/show.blade.php new file mode 100644 index 0000000..27b33b8 --- /dev/null +++ b/resources/views/pages/activity_logs/show.blade.php @@ -0,0 +1,121 @@ +@extends('back::layout', ['brParam' => $activitylogModel]) + +@section('title', __('Édition d\'un :model', ['model' => __('models.classes.activity_log')])) +@section('description', __('Édition d\'un :model déjà existant', ['model' => __('models.classes.activity_log')])) +@section('breadcrumb', + request()->route()->getName()) + +@section('content') + <div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap pb-3"> + <div class="d-flex align-items-start flex-row"> + <a class="btn btn-primary text-decoration-none m-0" data-bs-toggle="tooltip" href="{{ route('bo.activity_logs.index') }}" + title="{{ __('crud.helpers.list_all', ['model' => trans_choice(__('models.classes.activity_log'), '*')]) }}"> + <i class="fa-solid fa-arrow-left"></i> + </a> + @include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $activitylogModel]) + </div> + </div> + <div class="row"> + <div class="col-12 mb-3"> + <fieldset class="h-100 border p-3"> + <legend>{{ __('Informations principales') }}</legend> + <div class="row"> + <div class="col-12"> + <div class="table-responsive mb-3"> + <table class="table-hover m-0 table"> + <tbody> + <tr class="border-bottom"> + <td class="w-50 fw-bold text-center align-middle">{{ __('Auteur') }}</td> + <td class="w-50 text-center align-middle"> + @if (isset($activitylogModel->user)) + <a class="btn btn-sm btn-primary" data-bs-toggle="tooltip" + href="{{ route('bo.users.edit', $activitylogModel->user) }}" + title="{{ __('crud.helpers.view_model', ['model' => __('models.classes.user')]) }}"> + {{ $activitylogModel->user->last_name }} {{ $activitylogModel->user->first_name }} + </a> + @elseif($activitylogModel->is_console) + {{ __('Utilisateur dans la console') }} + @elseif($activitylogModel->is_anonymous) + {{ __('Utilisateur anonyme') }} + @else + {{ __('Utilisateur supprimé') }} + @endif + </td> + </tr> + <tr class="border-bottom"> + <td class="w-50 fw-bold text-center align-middle">{{ __('Évènement') }}</td> + <td class="w-50 text-center align-middle"> + <span + class="text-{{ $activitylogModel->event->bootstrapClass() }}-emphasis bg-{{ $activitylogModel->event->bootstrapClass() }}-subtle border-{{ $activitylogModel->event->bootstrapClass() }}-subtle rounded-3 border p-1"> + {{ $activitylogModel->event->label() }} + </span> + </td> + </tr> + <tr class="border-bottom"> + <td class="w-50 fw-bold text-center align-middle">{{ __('Élément ciblé') }}</td> + <td class="w-50 text-center align-middle"> + @php + $editRouteName = 'bo.' . optional($targetModel)->getTable() . '.edit'; + @endphp + @if (isset($targetModel) and Route::has($editRouteName)) + <a class="btn btn-sm btn-primary" data-bs-toggle="tooltip" + href="{{ route($editRouteName, $activitylogModel->model_id) }}" + title="{{ __('crud.helpers.view_model', ['model' => __('models.classes.' . Str::singular($targetModel->getTable()))]) }}"> + @endif + {{ $activitylogModel->model_class }} + @if (isset($targetModel) and Route::has($editRouteName)) + </a> + @endif + </td> + </tr> + <tr class="border-bottom"> + <td class="w-50 fw-bold text-center align-middle">{{ __('Réalisé le') }}</td> + <td class="w-50 text-center align-middle"> + <span class="badge bg-secondary">{{ $activitylogModel->created_at }}</span> + </td> + </tr> + </tbody> + </table> + </div> + </div> + </div> + </fieldset> + </div> + @if (isset($activitylogModel->data)) + <div class="col-12 mb-3"> + <fieldset class="h-100 border p-3"> + <legend>{{ __('Modifications apportées') }}</legend> + <div class="row"> + <div class="col-12"> + <div class="table-responsive mb-3"> + <table class="table-hover m-0 table"> + <thead> + <tr class="table-col-sorter border-start-0 border-end-0 border-top-0 border-dark border border-2"> + <td class="w-50 fw-bold text-center align-middle">{{ __('Column') }}</td> + <td class="fw-bold text-center align-middle"> + <i class="fa-solid fa-arrow-right"></i> + </td> + <td class="w-50 fw-bold text-center align-middle">{{ __('Type') }}</td> + </tr> + </thead> + <tbody> + @foreach ($activitylogModel->data as $key => $dataType) + <tr class="border-bottom"> + <td class="fw-bold text-center align-middle">{{ $key }}</td> + <td class="fw-bold text-center align-middle"> + <i class="fa-solid fa-arrow-right"></i> + </td> + <td class="w-25 text-center align-middle"> + {{ $dataType }} + </td> + </tr> + @endforeach + </table> + </div> + </div> + </div> + </fieldset> + </div> + @endif + </div> +@endsection diff --git a/resources/views/pages/banned-ips/create.blade.php b/resources/views/pages/banned-ips/create.blade.php index faecdc5..d4c9154 100644 --- a/resources/views/pages/banned-ips/create.blade.php +++ b/resources/views/pages/banned-ips/create.blade.php @@ -12,7 +12,7 @@ href="{{ route('bo.bannedips.index') }}" title="{{ __('crud.helpers.list_all', ['model' => __('models.classes.bannedips')]) }}"> <i class="fa-solid fa-arrow-left"></i> </a> - @include('breadcrumbs.breadcrumb-body', ['brParam' => $bannedipModel]) + @include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $bannedipModel]) </div> <div class="mb-md-0 mb-2"> <button class="btn btn-primary" id="formSubmitClone" data-bs-toggle="tooltip" data-bs-placement="top" type="submit" diff --git a/resources/views/pages/banned-ips/edit.blade.php b/resources/views/pages/banned-ips/edit.blade.php index c515b90..d126441 100644 --- a/resources/views/pages/banned-ips/edit.blade.php +++ b/resources/views/pages/banned-ips/edit.blade.php @@ -12,7 +12,7 @@ href="{{ route('bo.bannedips.index') }}" title="{{ __('crud.helpers.list_all', ['model' => __('models.classes.bannedips')]) }}"> <i class="fa-solid fa-arrow-left"></i> </a> - @include('breadcrumbs.breadcrumb-body', ['brParam' => $bannedipModel]) + @include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $bannedipModel]) </div> <div class="mb-md-0 mb-2"> <form class="confirmDeleteTS" action="{{ route('bo.bannedips.destroy', $bannedipModel->id) }}" method="POST"> diff --git a/resources/views/pages/banned-ips/form-inputs.blade.php b/resources/views/pages/banned-ips/form-inputs.blade.php index b8c6dda..4b9f715 100644 --- a/resources/views/pages/banned-ips/form-inputs.blade.php +++ b/resources/views/pages/banned-ips/form-inputs.blade.php @@ -1,14 +1,14 @@ <div class="row"> {{-- IP --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> - <legend>{{ Str::ucfirst(__('validation.attributes.ip')) }}</legend> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.custom.ip')) }}</legend> <div class="row"> <div class="col-12"> @include('back::partials.inputs.varchar', [ 'id' => 'bannedipIp', 'label' => __(':field à bannir', [ - 'field' => Str::ucfirst(__('validation.attributes.ip')), + 'field' => Str::ucfirst(__('validation.custom.ip')), ]), 'hasWordCounter' => true, 'name' => 'ip', @@ -16,11 +16,11 @@ 'model' => $bannedipModel ?? null, 'helper' => __('validation.ip', [ 'attribute' => __(':field à bannir', [ - 'field' => Str::ucfirst(__('validation.attributes.ip')), + 'field' => Str::ucfirst(__('validation.custom.ip')), ]), ]), 'placeholder' => __(':field à bannir', [ - 'field' => Str::ucfirst(__('validation.attributes.ip')), + 'field' => Str::ucfirst(__('validation.custom.ip')), ]), ]) </div> diff --git a/resources/views/pages/banned-ips/index.blade.php b/resources/views/pages/banned-ips/index.blade.php index 9c80305..30f6ed7 100644 --- a/resources/views/pages/banned-ips/index.blade.php +++ b/resources/views/pages/banned-ips/index.blade.php @@ -5,7 +5,7 @@ @section('content') <div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap"> - @include('breadcrumbs.breadcrumb-body') + @include('modules.breadcrumbs.breadcrumb-body') <a class="btn btn-primary float-right" data-bs-toggle="tooltip" data-bs-placement="top" href="{{ route('bo.bannedips.create') }}" title="{{ __('crud.helpers.create_model', ['model' => Str::singular(__('models.classes.bannedips'))]) }}"> <i class="fa-solid fa-plus"></i> @@ -17,7 +17,7 @@ <thead> @include('back::modules.table-col-sorter', [ 'cols' => [ - 'ip' => Str::ucfirst(__('validation.attributes.ip')), + 'ip' => Str::ucfirst(__('validation.custom.ip')), ], ]) </thead> diff --git a/resources/views/pages/codes/form-inputs.blade.php b/resources/views/pages/codes/form-inputs.blade.php index b3261b4..748385a 100644 --- a/resources/views/pages/codes/form-inputs.blade.php +++ b/resources/views/pages/codes/form-inputs.blade.php @@ -1,21 +1,21 @@ <div class="row"> {{-- QUANTITY --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ __('Nombre de codes à générer') }}</legend> <div class="row"> <div class="col-12"> @include('back::partials.inputs.int-number', [ 'id' => 'codeGenerationQuantity', 'label' => __(':field', [ - 'field' => Str::ucfirst(__('validation.attributes.quantity')), + 'field' => Str::ucfirst(__('validation.custom.quantity')), ]), 'name' => 'quantity', 'required' => true, 'model' => null, 'helper' => __('validation.min.numeric', [ 'attribute' => __(':field', [ - 'field' => Str::ucfirst(__('validation.attributes.quantity')), + 'field' => Str::ucfirst(__('validation.custom.quantity')), ]), 'min' => 1, ]), @@ -27,14 +27,14 @@ </div> {{-- CODE_LENGTH --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ __('Longueur des codes') }}</legend> <div class="row"> <div class="col-12"> @include('back::partials.inputs.int-number', [ 'id' => 'codeGenerationLength', 'label' => __(':field', [ - 'field' => Str::ucfirst(__('validation.attributes.code_length')), + 'field' => Str::ucfirst(__('validation.custom.code_length')), ]), 'name' => 'code_length', 'required' => true, @@ -43,7 +43,7 @@ 'model' => null, 'helper' => __('validation.min.numeric', [ 'attribute' => __(':field', [ - 'field' => Str::ucfirst(__('validation.attributes.code_length')), + 'field' => Str::ucfirst(__('validation.custom.code_length')), ]), 'min' => 1, ]), diff --git a/resources/views/pages/codes/index.blade.php b/resources/views/pages/codes/index.blade.php index de97f41..14615b3 100644 --- a/resources/views/pages/codes/index.blade.php +++ b/resources/views/pages/codes/index.blade.php @@ -3,7 +3,7 @@ @section('content') @section('content') <div class="d-flex justify-content-between flex-md-nowrap align-items-center mb-3 flex-wrap"> - @include('breadcrumbs.breadcrumb-body') + @include('modules.breadcrumbs.breadcrumb-body') <button class="btn btn-success float-right" data-bs-toggle="modal" data-bs-target="#exportModal" type="button"> <i class="fas fa-file-export"></i> {{ __('Export') }} </button> diff --git a/resources/views/pages/codes/search.blade.php b/resources/views/pages/codes/search.blade.php index cefa536..f22951a 100644 --- a/resources/views/pages/codes/search.blade.php +++ b/resources/views/pages/codes/search.blade.php @@ -2,7 +2,7 @@ @section('content') <div class="d-flex justify-content-between flex-md-nowrap align-items-center mb-3 flex-wrap"> - @include('breadcrumbs.breadcrumb-body') + @include('modules.breadcrumbs.breadcrumb-body') </div> @include('back::pages.codes.search-form') <hr> diff --git a/resources/views/pages/contents/edit.blade.php b/resources/views/pages/contents/edit.blade.php deleted file mode 100644 index c39e83b..0000000 --- a/resources/views/pages/contents/edit.blade.php +++ /dev/null @@ -1,41 +0,0 @@ -@extends('back::layout', ['brParam' => $contentModel]) - -@section('title', __('Édition d\'une :model', ['model' => Str::singular(__('models.classes.contents'))])) -@section('description', __('Édition d\'une :model déjà existante', ['model' => Str::singular(__('models.classes.contents'))])) -@section('breadcrumb', - request()->route()->getName()) - -@section('content') - <div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap pb-3"> - <div class="d-flex align-items-start flex-row"> - <a class="btn btn-primary text-decoration-none m-0 me-2" data-bs-toggle="tooltip" data-bs-placement="top" - href="{{ route('bo.contents.index') }}" title="{{ __('crud.helpers.list_all', ['model' => __('models.classes.contents')]) }}"> - <i class="fa-solid fa-arrow-left"></i> - </a> - @include('breadcrumbs.breadcrumb-body', ['brParam' => $contentModel]) - </div> - <div class="mb-md-0 mb-2"> - <form action="{{ route('bo.contents.update', $contentModel->id) }}" method="POST"> - @csrf - @method('PUT') - <button class="btn btn-primary" id="formSubmitClone" data-bs-toggle="tooltip" data-bs-placement="top" type="submit" - title="{{ __('crud.helpers.save_model', ['model' => Str::singular(__('models.classes.contents'))]) }}"> - {{ __('crud.actions.save') }} - </button> - </form> - </div> - </div> - <form action="{{ route('bo.contents.update', $contentModel->id) }}" method="POST" enctype="multipart/form-data"> - @csrf - @method('PUT') - @include('back::pages.contents.form-inputs') - <div class="row"> - <div class="col text-center"> - <button class="btn btn-primary" id="formSubmit" data-bs-toggle="tooltip" data-bs-placement="top" type="submit" - title="{{ __('crud.helpers.save_model', ['model' => Str::singular(__('models.classes.contents'))]) }}"> - {{ __('crud.actions.save') }} - </button> - </div> - </div> - </form> -@endsection diff --git a/resources/views/pages/contents/form-inputs.blade.php b/resources/views/pages/contents/form-inputs.blade.php deleted file mode 100644 index e87e937..0000000 --- a/resources/views/pages/contents/form-inputs.blade.php +++ /dev/null @@ -1,39 +0,0 @@ -<div class="row"> - {{-- VALUE --}} - <div class="col-12 mb-3"> - <fieldset class="border bg-white p-3"> - <legend>{{ __('Texte') }}</legend> - <div class="row"> - <div class="col-12"> - @include('back::partials.inputs.text-html', [ - 'id' => 'contentValue', - 'label' => __(':field du :model', [ - 'field' => Str::ucfirst(__('validation.attributes.value')), - 'model' => Str::singular(__('models.classes.contents')), - ]), - 'name' => 'value', - 'model' => $contentModel ?? null, - 'required' => true, - 'helper' => __('crud.minicrud.text_warning', [ - 'number' => 15, - ]), - ]) - </div> - </div> - </fieldset> - </div> -</div> - -@push('scripts') - <script> - document.addEventListener("DOMContentLoaded", () => { - // Submit button clone. - const submit = document.getElementById('formSubmit') - submitClone = document.getElementById('formSubmitClone'); - submitClone.addEventListener('click', (event) => { - event.preventDefault(); - submit.click(''); - }) - }); - </script> -@endpush diff --git a/resources/views/pages/contents/index.blade.php b/resources/views/pages/contents/index.blade.php deleted file mode 100644 index 7a5d5eb..0000000 --- a/resources/views/pages/contents/index.blade.php +++ /dev/null @@ -1,43 +0,0 @@ -@extends('back::layout') - -@section('title', __('models.classes.contents')) -@section('description', __('Liste de tous les :model', ['model' => __('models.classes.contents')])) - -@section('content') - <div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap"> - @include('breadcrumbs.breadcrumb-body') - </div> - @include('back::modules.search-bar') - <table class="table-hover table"> - @if (count($contentModels) > 0) - <thead> - @include('back::modules.table-col-sorter', [ - 'cols' => [ - 'key' => Str::ucfirst(__('validation.attributes.name')), - 'value' => Str::ucfirst(__('validation.attributes.value')), - ], - ]) - </thead> - <tbody> - @foreach ($contentModels as $contentModel) - <tr class="border-bottom list-item"> - <td class="text-center align-middle">{{ $contentModel->key }}</td> - <td class="text-center align-middle">{!! Str::limit($contentModel->value, 150, '...') !!}</td> - <td class="text-end align-middle"> - <a class="btn btn-sm btn-primary" data-bs-toggle="tooltip" data-bs-placement="top" - href="{{ route('bo.contents.edit', ['content' => $contentModel->id]) }}" title="{{ __('crud.actions.update') }}"> - <i class="fa-solid fa-pencil"></i> - </a> - </td> - </tr> - @endforeach - </tbody> - @else - <tr> - <td class="fw-bold border-0 text-center"> - {{ __('crud.other.no_model_found', ['model' => Str::singular(__('models.classes.contents'))]) }}</td> - </tr> - @endif - </table> - {!! $contentModels->links() !!} -@endsection diff --git a/resources/views/pages/dashboard/index.blade.php b/resources/views/pages/dashboard/index.blade.php index 8af4ebf..9c1d1bc 100644 --- a/resources/views/pages/dashboard/index.blade.php +++ b/resources/views/pages/dashboard/index.blade.php @@ -3,7 +3,7 @@ @section('content') <div class="d-flex justify-content-between"> <h2>{{ __('Dashboard') }}</h2> - <a class="btn btn-primary text-decoration-none m-0 me-2" href="{{ route('fo.home') }}"> + <a class="btn btn-primary text-decoration-none m-0 me-2" href="{{ route('fo.homepage') }}"> {{ __('Accéder au jeu') }} </a> </div> diff --git a/resources/views/pages/faq-questions/create.blade.php b/resources/views/pages/faq-questions/create.blade.php index 32eccc9..af9e027 100644 --- a/resources/views/pages/faq-questions/create.blade.php +++ b/resources/views/pages/faq-questions/create.blade.php @@ -13,7 +13,7 @@ title="{{ __('crud.helpers.list_all', ['model' => trans_choice('models.classes.faq-questions', 2)]) }}"> <i class="fa-solid fa-arrow-left"></i> </a> - @include('breadcrumbs.breadcrumb-body', ['brParam' => $faqQuestionModel]) + @include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $faqQuestionModel]) </div> <div class="mb-md-0 mb-2"> <button class="btn btn-primary" id="formSubmitClone" data-bs-toggle="tooltip" data-bs-placement="top" type="submit" diff --git a/resources/views/pages/faq-questions/edit.blade.php b/resources/views/pages/faq-questions/edit.blade.php index aa87fb2..9f551ef 100644 --- a/resources/views/pages/faq-questions/edit.blade.php +++ b/resources/views/pages/faq-questions/edit.blade.php @@ -13,7 +13,7 @@ title="{{ __('crud.helpers.list_all', ['model' => trans_choice('models.classes.faq-questions', 2)]) }}"> <i class="fa-solid fa-arrow-left"></i> </a> - @include('breadcrumbs.breadcrumb-body', ['brParam' => $faqQuestionModel]) + @include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $faqQuestionModel]) </div> <div class="mb-md-0 mb-2"> <form class="confirmDeleteTS" action="{{ route('bo.faq-questions.destroy', $faqQuestionModel->id) }}" method="POST"> diff --git a/resources/views/pages/faq-questions/form-inputs.blade.php b/resources/views/pages/faq-questions/form-inputs.blade.php index 3d3b68d..c883955 100644 --- a/resources/views/pages/faq-questions/form-inputs.blade.php +++ b/resources/views/pages/faq-questions/form-inputs.blade.php @@ -1,14 +1,14 @@ <div class="row"> {{-- QUESTION --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> - <legend>{{ Str::ucfirst(__('validation.attributes.question')) }}</legend> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.custom.question')) }}</legend> <div class="row"> <div class="col-12"> @include('back::partials.inputs.varchar', [ 'id' => 'faqQuestionQuestion', 'label' => __(':field à intégrer à la faq', [ - 'field' => Str::ucfirst(__('validation.attributes.question')), + 'field' => Str::ucfirst(__('validation.custom.question')), ]), 'hasWordCounter' => true, 'name' => 'question', @@ -16,13 +16,13 @@ 'model' => $faqQuestionModel ?? null, 'helper' => __('validation.between.string', [ 'attribute' => __(':field', [ - 'field' => Str::ucfirst(__('validation.attributes.question')), + 'field' => Str::ucfirst(__('validation.custom.question')), ]), 'min' => 3, 'max' => 255, ]), 'placeholder' => __(':field à intégrer à la faq', [ - 'field' => Str::ucfirst(__('validation.attributes.question')), + 'field' => Str::ucfirst(__('validation.custom.question')), ]), ]) </div> @@ -31,8 +31,8 @@ </div> {{-- ANSWER --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> - <legend>{{ Str::ucfirst(__('validation.attributes.question')) }}</legend> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.custom.question')) }}</legend> <div class="row"> <div class="col-12"> @include('back::partials.inputs.varchar', [ @@ -61,7 +61,7 @@ </div> {{-- THEME_ID --}} <div class="col-12 col-md-6 pb-3"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ __('Thème') }}</legend> <div class="row"> <div class="col-12"> diff --git a/resources/views/pages/faq-questions/index.blade.php b/resources/views/pages/faq-questions/index.blade.php index 7a04579..9633488 100644 --- a/resources/views/pages/faq-questions/index.blade.php +++ b/resources/views/pages/faq-questions/index.blade.php @@ -5,7 +5,7 @@ @section('content') <div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap"> - @include('breadcrumbs.breadcrumb-body') + @include('modules.breadcrumbs.breadcrumb-body') <a class="btn btn-primary float-right" data-bs-toggle="tooltip" data-bs-placement="top" href="{{ route('bo.faq-questions.create') }}" title="{{ __('crud.helpers.create_model', ['model' => trans_choice('models.classes.faq-questions', 1)]) }}"> <i class="fa-solid fa-plus"></i> @@ -17,7 +17,7 @@ <thead> @include('back::modules.table-col-sorter', [ 'cols' => [ - 'question' => Str::ucfirst(__('validation.attributes.question')), + 'question' => Str::ucfirst(__('validation.custom.question')), 'answer' => Str::ucfirst(__('validation.attributes.answer')), 'theme_id' => Str::ucfirst(__('validation.attributes.theme')), ], diff --git a/resources/views/pages/faq-themes/create.blade.php b/resources/views/pages/faq-themes/create.blade.php index 853e8ab..e17cc5e 100644 --- a/resources/views/pages/faq-themes/create.blade.php +++ b/resources/views/pages/faq-themes/create.blade.php @@ -13,7 +13,7 @@ title="{{ __('crud.helpers.list_all', ['model' => trans_choice('models.classes.faq-themes', 2)]) }}"> <i class="fa-solid fa-arrow-left"></i> </a> - @include('breadcrumbs.breadcrumb-body', ['brParam' => $faqThemeModel]) + @include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $faqThemeModel]) </div> <div class="mb-md-0 mb-2"> <button class="btn btn-primary" id="formSubmitClone" data-bs-toggle="tooltip" data-bs-placement="top" type="submit" diff --git a/resources/views/pages/faq-themes/edit.blade.php b/resources/views/pages/faq-themes/edit.blade.php index 9d0cb9d..8d2ac9c 100644 --- a/resources/views/pages/faq-themes/edit.blade.php +++ b/resources/views/pages/faq-themes/edit.blade.php @@ -13,7 +13,7 @@ title="{{ __('crud.helpers.list_all', ['model' => trans_choice('models.classes.faq-themes', 2)]) }}"> <i class="fa-solid fa-arrow-left"></i> </a> - @include('breadcrumbs.breadcrumb-body', ['brParam' => $faqThemeModel]) + @include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $faqThemeModel]) </div> <div class="mb-md-0 mb-2"> <form class="confirmDeleteTS" action="{{ route('bo.faq-themes.destroy', $faqThemeModel->id) }}" method="POST"> diff --git a/resources/views/pages/faq-themes/form-inputs.blade.php b/resources/views/pages/faq-themes/form-inputs.blade.php index bccd087..6b48b7d 100644 --- a/resources/views/pages/faq-themes/form-inputs.blade.php +++ b/resources/views/pages/faq-themes/form-inputs.blade.php @@ -1,7 +1,7 @@ <div class="row"> {{-- NAME --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ Str::ucfirst(__('validation.attributes.name')) }}</legend> <div class="row"> <div class="col-12"> @@ -17,7 +17,7 @@ 'model' => $faqThemeModel ?? null, 'helper' => __('validation.between.string', [ 'attribute' => __(':field', [ - 'field' => Str::ucfirst(__('validation.attributes.question')), + 'field' => Str::ucfirst(__('validation.custom.question')), ]), 'min' => 3, 'max' => 255, diff --git a/resources/views/pages/faq-themes/index.blade.php b/resources/views/pages/faq-themes/index.blade.php index 484deff..9ea841b 100644 --- a/resources/views/pages/faq-themes/index.blade.php +++ b/resources/views/pages/faq-themes/index.blade.php @@ -5,7 +5,7 @@ @section('content') <div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap"> - @include('breadcrumbs.breadcrumb-body') + @include('modules.breadcrumbs.breadcrumb-body') <a class="btn btn-primary float-right" data-bs-toggle="tooltip" data-bs-placement="top" href="{{ route('bo.faq-themes.create') }}" title="{{ __('crud.helpers.create_model', ['model' => trans_choice('models.classes.faq-themes', 1)]) }}"> <i class="fa-solid fa-plus"></i> diff --git a/resources/views/pages/gain-views/create.blade.php b/resources/views/pages/gain-views/create.blade.php index c720f73..6db163f 100644 --- a/resources/views/pages/gain-views/create.blade.php +++ b/resources/views/pages/gain-views/create.blade.php @@ -13,7 +13,7 @@ title="{{ __('crud.helpers.list_all', ['model' => trans_choice('models.classes.gain-views', 2)]) }}"> <i class="fa-solid fa-arrow-left"></i> </a> - @include('breadcrumbs.breadcrumb-body', ['brParam' => $gainViewModel]) + @include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $gainViewModel]) </div> <div class="mb-md-0 mb-2"> <button class="btn btn-primary" id="formSubmitClone" data-bs-toggle="tooltip" data-bs-placement="top" type="submit" diff --git a/resources/views/pages/gain-views/edit.blade.php b/resources/views/pages/gain-views/edit.blade.php index dbe7d5b..2eaa7c3 100644 --- a/resources/views/pages/gain-views/edit.blade.php +++ b/resources/views/pages/gain-views/edit.blade.php @@ -13,7 +13,7 @@ title="{{ __('crud.helpers.list_all', ['model' => trans_choice('models.classes.gain-views', 2)]) }}"> <i class="fa-solid fa-arrow-left"></i> </a> - @include('breadcrumbs.breadcrumb-body', ['brParam' => $gainViewModel]) + @include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $gainViewModel]) </div> <div class="mb-md-0 mb-2"> <form class="confirmDeleteTS" action="{{ route('bo.gain-views.destroy', $gainViewModel->id) }}" method="POST"> diff --git a/resources/views/pages/gain-views/form-inputs.blade.php b/resources/views/pages/gain-views/form-inputs.blade.php index f2d6e7a..1e250c8 100644 --- a/resources/views/pages/gain-views/form-inputs.blade.php +++ b/resources/views/pages/gain-views/form-inputs.blade.php @@ -1,7 +1,7 @@ <div class="row"> {{-- NAME --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ Str::ucfirst(__('validation.attributes.name')) }}</legend> <div class="row"> <div class="col-12"> @@ -31,7 +31,7 @@ </div> {{-- FILE_NAME --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ Str::ucfirst(__('validation.attributes.file_name')) }}</legend> <div class="row"> <div class="col-12"> diff --git a/resources/views/pages/gain-views/index.blade.php b/resources/views/pages/gain-views/index.blade.php index 8094ebd..d9cb312 100644 --- a/resources/views/pages/gain-views/index.blade.php +++ b/resources/views/pages/gain-views/index.blade.php @@ -5,7 +5,7 @@ @section('content') <div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap"> - @include('breadcrumbs.breadcrumb-body') + @include('modules.breadcrumbs.breadcrumb-body') <a class="btn btn-primary float-right" data-bs-toggle="tooltip" data-bs-placement="top" href="{{ route('bo.gain-views.create') }}" title="{{ __('crud.helpers.create_model', ['model' => trans_choice('models.classes.gain-views', 1)]) }}"> <i class="fa-solid fa-plus"></i> diff --git a/resources/views/pages/prizes/create.blade.php b/resources/views/pages/prizes/create.blade.php index 1f44914..5534d33 100644 --- a/resources/views/pages/prizes/create.blade.php +++ b/resources/views/pages/prizes/create.blade.php @@ -12,7 +12,7 @@ href="{{ route('bo.prizes.index') }}" title="{{ __('crud.helpers.list_all', ['model' => __('models.classes.prizes')]) }}"> <i class="fa-solid fa-arrow-left"></i> </a> - @include('breadcrumbs.breadcrumb-body', ['brParam' => $prizeModel]) + @include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $prizeModel]) </div> <div class="mb-md-0 mb-2"> <button class="btn btn-primary" id="formSubmitClone" data-bs-toggle="tooltip" data-bs-placement="top" type="submit" diff --git a/resources/views/pages/prizes/edit.blade.php b/resources/views/pages/prizes/edit.blade.php index 4aa4ad5..d42ac58 100644 --- a/resources/views/pages/prizes/edit.blade.php +++ b/resources/views/pages/prizes/edit.blade.php @@ -12,7 +12,7 @@ href="{{ route('bo.prizes.index') }}" title="{{ __('crud.helpers.list_all', ['model' => __('models.classes.prizes')]) }}"> <i class="fa-solid fa-arrow-left"></i> </a> - @include('breadcrumbs.breadcrumb-body', ['brParam' => $prizeModel]) + @include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $prizeModel]) </div> <div class="mb-md-0 mb-2"> <form class="confirmDeleteTS" action="{{ route('bo.prizes.destroy', $prizeModel->id) }}" method="POST"> diff --git a/resources/views/pages/prizes/form-inputs.blade.php b/resources/views/pages/prizes/form-inputs.blade.php index 3656e69..924a58d 100644 --- a/resources/views/pages/prizes/form-inputs.blade.php +++ b/resources/views/pages/prizes/form-inputs.blade.php @@ -1,7 +1,7 @@ <div class="row"> {{-- LABEL_ONE --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ Str::ucfirst(__('validation.attributes.label_one')) }}</legend> <div class="row"> <div class="col-12"> @@ -32,7 +32,7 @@ </div> {{-- LABEL_MANY --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ Str::ucfirst(__('validation.attributes.label_many')) }}</legend> <div class="row"> <div class="col-12"> @@ -63,7 +63,7 @@ </div> {{-- ARG --}} <div class="col-12 mb-3"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ Str::ucfirst(__('validation.attributes.arg')) }}</legend> <div class="row"> {{-- ARG_TITLE --}} @@ -134,21 +134,21 @@ </div> {{-- QUANTITY --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> - <legend>{{ Str::ucfirst(__('validation.attributes.quantity')) }}</legend> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.custom.quantity')) }}</legend> <div class="row"> <div class="col-12"> @include('back::partials.inputs.int-number', [ 'id' => 'prizeQuantity', 'label' => __(':field', [ - 'field' => Str::ucfirst(__('validation.attributes.quantity')), + 'field' => Str::ucfirst(__('validation.custom.quantity')), ]), 'name' => 'quantity', 'required' => true, 'model' => $prizeModel ?? null, 'helper' => __('validation.min.numeric', [ 'attribute' => __(':field', [ - 'field' => Str::ucfirst(__('validation.attributes.quantity')), + 'field' => Str::ucfirst(__('validation.custom.quantity')), ]), 'min' => 1, ]), @@ -160,7 +160,7 @@ </div> {{-- PDF_WINNING_PROOF --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ Str::ucfirst(__('validation.attributes.pdf_winning_proof')) }}</legend> <div class="row"> <div class="col-12"> @@ -186,7 +186,7 @@ </div> {{-- NEED_PROOF --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ Str::ucfirst(__('validation.attributes.need_proof')) }}</legend> <div class="row"> <div class="col-12"> @@ -210,7 +210,7 @@ </div> {{-- GAIN_VIEW_ID --}} <div class="col-12 col-md-6 pb-3"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ trans_choice('models.classes.gain-views', 1) }}</legend> <div class="row"> <div class="col-12"> diff --git a/resources/views/pages/prizes/index.blade.php b/resources/views/pages/prizes/index.blade.php index 8b914aa..8d633ea 100644 --- a/resources/views/pages/prizes/index.blade.php +++ b/resources/views/pages/prizes/index.blade.php @@ -5,7 +5,7 @@ @section('content') <div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap"> - @include('breadcrumbs.breadcrumb-body') + @include('modules.breadcrumbs.breadcrumb-body') <a class="btn btn-primary float-right" data-bs-toggle="tooltip" data-bs-placement="top" href="{{ route('bo.prizes.create') }}" title="{{ __('crud.helpers.create_model', ['model' => Str::singular(__('models.classes.prizes'))]) }}"> <i class="fa-solid fa-plus"></i> @@ -18,7 +18,7 @@ @include('back::modules.table-col-sorter', [ 'cols' => [ 'label_one' => Str::ucfirst(__('validation.attributes.label_one')), - 'quantity' => Str::ucfirst(__('validation.attributes.quantity')), + 'quantity' => Str::ucfirst(__('validation.custom.quantity')), 'need_proof' => Str::ucfirst(__('validation.attributes.need_proof')), 'gain_view_id' => Str::ucfirst(trans_choice('models.classes.gain-views', 1)), ], diff --git a/resources/views/pages/products/create.blade.php b/resources/views/pages/products/create.blade.php index 9ed636e..b2f0c67 100644 --- a/resources/views/pages/products/create.blade.php +++ b/resources/views/pages/products/create.blade.php @@ -12,7 +12,7 @@ href="{{ route('bo.products.index') }}" title="{{ __('crud.helpers.list_all', ['model' => __('models.classes.products')]) }}"> <i class="fa-solid fa-arrow-left"></i> </a> - @include('breadcrumbs.breadcrumb-body', ['brParam' => $productModel]) + @include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $productModel]) </div> <div class="mb-md-0 mb-2"> <button class="btn btn-primary" id="formSubmitClone" data-bs-toggle="tooltip" data-bs-placement="top" type="submit" diff --git a/resources/views/pages/products/edit.blade.php b/resources/views/pages/products/edit.blade.php index 0aff1d8..e4d68c4 100644 --- a/resources/views/pages/products/edit.blade.php +++ b/resources/views/pages/products/edit.blade.php @@ -12,7 +12,7 @@ href="{{ route('bo.products.index') }}" title="{{ __('crud.helpers.list_all', ['model' => __('models.classes.products')]) }}"> <i class="fa-solid fa-arrow-left"></i> </a> - @include('breadcrumbs.breadcrumb-body', ['brParam' => $productModel]) + @include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $productModel]) </div> <div class="mb-md-0 mb-2"> <form class="confirmDeleteTS" action="{{ route('bo.products.destroy', $productModel->id) }}" method="POST"> diff --git a/resources/views/pages/products/form-inputs.blade.php b/resources/views/pages/products/form-inputs.blade.php index c2cc30c..7911569 100644 --- a/resources/views/pages/products/form-inputs.blade.php +++ b/resources/views/pages/products/form-inputs.blade.php @@ -1,7 +1,7 @@ <div class="row"> {{-- NAME --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ Str::ucfirst(__('validation.attributes.name')) }}</legend> <div class="row"> <div class="col-12"> @@ -32,7 +32,7 @@ </div> {{-- NO_ART --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ Str::ucfirst(__('validation.attributes.no_art')) }}</legend> <div class="row"> <div class="col-12"> @@ -58,7 +58,7 @@ </div> {{-- PICTURE --}} <div class="col-12 mb-3"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ Str::ucfirst(__('validation.attributes.picture')) }}</legend> <div class="row"> <div class="col-12"> @@ -85,7 +85,7 @@ </div> {{-- CODE_EAN --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ Str::ucfirst(__('validation.attributes.ean_code')) }}</legend> <div class="row"> <div class="col-12"> @@ -112,7 +112,7 @@ </div> {{-- RANGE_ID --}} <div class="col-12 col-md-6 pb-3"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ trans_choice('models.classes.gain-views', 1) }}</legend> <div class="row"> <div class="col-12"> diff --git a/resources/views/pages/products/index.blade.php b/resources/views/pages/products/index.blade.php index fa58852..74bfb3d 100644 --- a/resources/views/pages/products/index.blade.php +++ b/resources/views/pages/products/index.blade.php @@ -5,7 +5,7 @@ @section('content') <div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap"> - @include('breadcrumbs.breadcrumb-body') + @include('modules.breadcrumbs.breadcrumb-body') <a class="btn btn-primary float-right" data-bs-toggle="tooltip" data-bs-placement="top" href="{{ route('bo.products.create') }}" title="{{ __('crud.helpers.create_model', ['model' => Str::singular(__('models.classes.products'))]) }}"> <i class="fa-solid fa-plus"></i> diff --git a/resources/views/pages/ranges/create.blade.php b/resources/views/pages/ranges/create.blade.php index 976f3a6..ad4eb93 100644 --- a/resources/views/pages/ranges/create.blade.php +++ b/resources/views/pages/ranges/create.blade.php @@ -12,7 +12,7 @@ href="{{ route('bo.ranges.index') }}" title="{{ __('crud.helpers.list_all', ['model' => __('models.classes.ranges')]) }}"> <i class="fa-solid fa-arrow-left"></i> </a> - @include('breadcrumbs.breadcrumb-body', ['brParam' => $rangeModel]) + @include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $rangeModel]) </div> <div class="mb-md-0 mb-2"> <button class="btn btn-primary" id="formSubmitClone" data-bs-toggle="tooltip" data-bs-placement="top" type="submit" diff --git a/resources/views/pages/ranges/edit.blade.php b/resources/views/pages/ranges/edit.blade.php index 0516ce3..76d76e2 100644 --- a/resources/views/pages/ranges/edit.blade.php +++ b/resources/views/pages/ranges/edit.blade.php @@ -12,7 +12,7 @@ href="{{ route('bo.ranges.index') }}" title="{{ __('crud.helpers.list_all', ['model' => __('models.classes.ranges')]) }}"> <i class="fa-solid fa-arrow-left"></i> </a> - @include('breadcrumbs.breadcrumb-body', ['brParam' => $rangeModel]) + @include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $rangeModel]) </div> <div class="mb-md-0 mb-2"> <form class="confirmDeleteTS" action="{{ route('bo.ranges.destroy', $rangeModel->id) }}" method="POST"> diff --git a/resources/views/pages/ranges/form-inputs.blade.php b/resources/views/pages/ranges/form-inputs.blade.php index 2b89184..d73193c 100644 --- a/resources/views/pages/ranges/form-inputs.blade.php +++ b/resources/views/pages/ranges/form-inputs.blade.php @@ -1,7 +1,7 @@ <div class="row"> {{-- NAME --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ Str::ucfirst(__('validation.attributes.name')) }}</legend> <div class="row"> <div class="col-12"> @@ -32,7 +32,7 @@ </div> {{-- PICTURE --}} <div class="col-12"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ Str::ucfirst(__('validation.attributes.picture')) }}</legend> <div class="row"> <div class="col-12"> diff --git a/resources/views/pages/ranges/index.blade.php b/resources/views/pages/ranges/index.blade.php index 52f0a34..ebcbab6 100644 --- a/resources/views/pages/ranges/index.blade.php +++ b/resources/views/pages/ranges/index.blade.php @@ -5,7 +5,7 @@ @section('content') <div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap"> - @include('breadcrumbs.breadcrumb-body') + @include('modules.breadcrumbs.breadcrumb-body') <a class="btn btn-primary float-right" data-bs-toggle="tooltip" data-bs-placement="top" href="{{ route('bo.ranges.create') }}" title="{{ __('crud.helpers.create_model', ['model' => Str::singular(__('models.classes.ranges'))]) }}"> <i class="fa-solid fa-plus"></i> diff --git a/resources/views/pages/settings/edit.blade.php b/resources/views/pages/settings/edit.blade.php index 7e7af3d..6dab752 100644 --- a/resources/views/pages/settings/edit.blade.php +++ b/resources/views/pages/settings/edit.blade.php @@ -8,7 +8,7 @@ @section('content') <div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap pb-3"> <div class="d-flex align-items-start flex-row"> - @include('breadcrumbs.breadcrumb-body', ['brParam' => $settingsModel]) + @include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $settingsModel]) </div> <div class="mb-md-0 mb-2"> <form class="confirmDeleteTS" action="{{ route('bo.settings.update', $settingsModel->id) }}" method="POST"> diff --git a/resources/views/pages/settings/form-inputs.blade.php b/resources/views/pages/settings/form-inputs.blade.php index 61aeb47..1f562ad 100644 --- a/resources/views/pages/settings/form-inputs.blade.php +++ b/resources/views/pages/settings/form-inputs.blade.php @@ -6,7 +6,7 @@ <div class="row"> {{-- NAME --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ Str::ucfirst(__('validation.attributes.name')) }}</legend> <div class="row"> <div class="col-12"> @@ -34,13 +34,13 @@ </div> {{-- START_DATE --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> - <legend>{{ Str::ucfirst(__('validation.attributes.start_date')) }}</legend> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.custom.start_date')) }}</legend> <div class="row"> <div class="col-12"> @include('back::partials.inputs.timestamp-datetime', [ 'id' => 'settingsStartDate', - 'label' => Str::ucfirst(__('validation.attributes.start_date')), + 'label' => Str::ucfirst(__('validation.custom.start_date')), 'name' => 'start_date', 'defaultValue' => true, 'required' => true, @@ -53,13 +53,13 @@ </div> {{-- END_DATE --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> - <legend>{{ Str::ucfirst(__('validation.attributes.end_date')) }}</legend> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.custom.end_date')) }}</legend> <div class="row"> <div class="col-12"> @include('back::partials.inputs.timestamp-datetime', [ 'id' => 'settingsEndDate', - 'label' => Str::ucfirst(__('validation.attributes.end_date')), + 'label' => Str::ucfirst(__('validation.custom.end_date')), 'name' => 'end_date', 'defaultValue' => true, 'required' => true, @@ -72,18 +72,18 @@ </div> {{-- NB_PARTICIPATION_PLAYER --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> - <legend>{{ Str::ucfirst(__('validation.attributes.nb_participation_player')) }}</legend> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.custom.nb_participation_player')) }}</legend> <div class="row"> <div class="col-12"> @include('back::partials.inputs.int-number', [ 'id' => 'settingsNbParticipationPlayer', - 'label' => Str::ucfirst(__('validation.attributes.nb_participation_player')), + 'label' => Str::ucfirst(__('validation.custom.nb_participation_player')), 'name' => 'nb_participation_player', 'required' => true, 'model' => $settingsModel ?? null, 'helper' => __('validation.min.numeric', [ - 'attribute' => Str::ucfirst(__('validation.attributes.nb_participation_player')), + 'attribute' => Str::ucfirst(__('validation.custom.nb_participation_player')), 'min' => 0, ]), 'min' => 0, @@ -94,18 +94,18 @@ </div> {{-- NB_PARTICIPATION_IP --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> - <legend>{{ Str::ucfirst(__('validation.attributes.nb_participation_ip')) }}</legend> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.custom.nb_participation_ip')) }}</legend> <div class="row"> <div class="col-12"> @include('back::partials.inputs.int-number', [ 'id' => 'settingsNbParticipationIp', - 'label' => Str::ucfirst(__('validation.attributes.nb_participation_ip')), + 'label' => Str::ucfirst(__('validation.custom.nb_participation_ip')), 'name' => 'nb_participation_ip', 'required' => true, 'model' => $settingsModel ?? null, 'helper' => __('validation.min.numeric', [ - 'attribute' => Str::ucfirst(__('validation.attributes.nb_participation_ip')), + 'attribute' => Str::ucfirst(__('validation.custom.nb_participation_ip')), 'min' => 0, ]), 'min' => 0, @@ -116,18 +116,18 @@ </div> {{-- NB_UNIQUE_CODE_USE --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> - <legend>{{ Str::ucfirst(__('validation.attributes.nb_unique_code_use')) }}</legend> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.custom.nb_unique_code_use')) }}</legend> <div class="row"> <div class="col-12"> @include('back::partials.inputs.int-number', [ 'id' => 'settingsNbUniqueCodeUse', - 'label' => Str::ucfirst(__('validation.attributes.nb_unique_code_use')), + 'label' => Str::ucfirst(__('validation.custom.nb_unique_code_use')), 'name' => 'nb_unique_code_use', 'required' => true, 'model' => $settingsModel ?? null, 'helper' => __('validation.min.numeric', [ - 'attribute' => Str::ucfirst(__('validation.attributes.nb_unique_code_use')), + 'attribute' => Str::ucfirst(__('validation.custom.nb_unique_code_use')), 'min' => 0, ]), 'min' => 0, @@ -138,13 +138,13 @@ </div> {{-- CODE_LENGTH --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> - <legend>{{ Str::ucfirst(__('validation.attributes.code_length')) }}</legend> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.custom.code_length')) }}</legend> <div class="row"> <div class="col-12"> @include('back::partials.inputs.int-number', [ 'id' => 'settingsCodeLength', - 'label' => Str::ucfirst(__('validation.attributes.code_length')), + 'label' => Str::ucfirst(__('validation.custom.code_length')), 'name' => 'code_length', 'required' => false, 'disabled' => true, @@ -158,18 +158,18 @@ </div> {{-- USE_EAN_CODES --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> - <legend>{{ Str::ucfirst(__('validation.attributes.use_ean_codes')) }}</legend> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.custom.use_ean_codes')) }}</legend> <div class="row"> <div class="col-12"> @include('back::partials.inputs.tinyint-boolean', [ 'id' => 'settingsUseEANCodes', - 'label' => Str::ucfirst(__('validation.attributes.use_ean_codes')), + 'label' => Str::ucfirst(__('validation.custom.use_ean_codes')), 'name' => 'use_ean_codes', 'required' => false, 'model' => $settingsModel ?? null, 'helper' => __('validation.boolean', [ - 'attribute' => Str::ucfirst(__('validation.attributes.use_ean_codes')), + 'attribute' => Str::ucfirst(__('validation.custom.use_ean_codes')), ]), ]) </div> @@ -178,18 +178,18 @@ </div> {{-- ACTIVATED_TEASER --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> - <legend>{{ Str::ucfirst(__('validation.attributes.activated_teaser')) }}</legend> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.custom.activated_teaser')) }}</legend> <div class="row"> <div class="col-12"> @include('back::partials.inputs.tinyint-boolean', [ 'id' => 'settingsActivatedTeaser', - 'label' => Str::ucfirst(__('validation.attributes.activated_teaser')), + 'label' => Str::ucfirst(__('validation.custom.activated_teaser')), 'name' => 'activated_teaser', 'required' => false, 'model' => $settingsModel ?? null, 'helper' => __('validation.boolean', [ - 'attribute' => Str::ucfirst(__('validation.attributes.activated_teaser')), + 'attribute' => Str::ucfirst(__('validation.custom.activated_teaser')), ]), ]) </div> @@ -198,7 +198,7 @@ </div> {{-- TEASER_END_ACTIVITY --}} <div class="col-12 col-md-6 mb-3" id="teaser_end_activity"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ Str::ucfirst(__('validation.attributes.teaser_end_activity')) }}</legend> <div class="row"> <div class="col-12"> @@ -217,18 +217,18 @@ </div> {{-- ACTIVATED_CLOSE_PAGE --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> - <legend>{{ Str::ucfirst(__('validation.attributes.activated_close_page')) }}</legend> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.custom.activated_close_page')) }}</legend> <div class="row"> <div class="col-12"> @include('back::partials.inputs.tinyint-boolean', [ 'id' => 'settingsActivatedClosePage', - 'label' => Str::ucfirst(__('validation.attributes.activated_close_page')), + 'label' => Str::ucfirst(__('validation.custom.activated_close_page')), 'name' => 'activated_close_page', 'required' => false, 'model' => $settingsModel ?? null, 'helper' => __('validation.boolean', [ - 'attribute' => Str::ucfirst(__('validation.attributes.activated_close_page')), + 'attribute' => Str::ucfirst(__('validation.custom.activated_close_page')), ]), ]) </div> @@ -237,7 +237,7 @@ </div> {{-- CLOSE_PAGE_ACTIVE_DATE --}} <div class="col-12 col-md-6 mb-3" id="close_page_active_date"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ Str::ucfirst(__('validation.attributes.close_page_active_date')) }}</legend> <div class="row"> <div class="col-12"> @@ -256,8 +256,8 @@ </div> {{-- REDIRECT_CLOSE --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> - <legend>{{ Str::ucfirst(__('validation.attributes.redirect_close')) }}</legend> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.custom.redirect_close')) }}</legend> <div class="row"> <div class="col-12"> @include('back::partials.inputs.varchar', [ @@ -276,18 +276,18 @@ </div> {{-- WINNING_PROOF --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> - <legend>{{ Str::ucfirst(__('validation.attributes.winning_proof')) }}</legend> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.custom.winning_proof')) }}</legend> <div class="row"> <div class="col-12"> @include('back::partials.inputs.tinyint-boolean', [ 'id' => 'settingsReductionsActivated', - 'label' => Str::ucfirst(__('validation.attributes.winning_proof')), + 'label' => Str::ucfirst(__('validation.custom.winning_proof')), 'name' => 'winning_proof', 'required' => false, 'model' => $settingsModel ?? null, 'helper' => __('validation.boolean', [ - 'attribute' => Str::ucfirst(__('validation.attributes.winning_proof')), + 'attribute' => Str::ucfirst(__('validation.custom.winning_proof')), ]), ]) </div> @@ -296,14 +296,14 @@ </div> {{-- COMPANY --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> - <legend>{{ Str::ucfirst(__('validation.attributes.company')) }}</legend> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.custom.company')) }}</legend> <div class="row"> <div class="col-12"> @include('back::partials.inputs.varchar', [ 'id' => 'settingsCompany', 'label' => __('Nom de la :field', [ - 'field' => Str::ucfirst(__('validation.attributes.company')), + 'field' => Str::ucfirst(__('validation.custom.company')), ]), 'hasWordCounter' => true, 'name' => 'company', @@ -311,13 +311,13 @@ 'model' => $settingsModel ?? null, 'helper' => __('validation.between.string', [ 'attribute' => __('Nom de la :field', [ - 'field' => Str::ucfirst(__('validation.attributes.company')), + 'field' => Str::ucfirst(__('validation.custom.company')), ]), 'min' => 3, 'max' => 255, ]), 'placeholder' => __(':field', [ - 'field' => Str::ucfirst(__('validation.attributes.company')), + 'field' => Str::ucfirst(__('validation.custom.company')), ]), ]) </div> @@ -335,18 +335,18 @@ <div class="row"> {{-- REDUCTIONS_ACTIVATED --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> - <legend>{{ Str::ucfirst(__('validation.attributes.reductions_activated')) }}</legend> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.custom.reductions_activated')) }}</legend> <div class="row"> <div class="col-12"> @include('back::partials.inputs.tinyint-boolean', [ 'id' => 'settingsReductionsActivated', - 'label' => Str::ucfirst(__('validation.attributes.reductions_activated')), + 'label' => Str::ucfirst(__('validation.custom.reductions_activated')), 'name' => 'reductions_activated', 'required' => false, 'model' => $settingsModel ?? null, 'helper' => __('validation.boolean', [ - 'attribute' => Str::ucfirst(__('validation.attributes.reductions_activated')), + 'attribute' => Str::ucfirst(__('validation.custom.reductions_activated')), ]), ]) </div> @@ -355,7 +355,7 @@ </div> {{-- REDUCTIONS_SITE --}} <div class="col-12 col-md-6 mb-3" id="reductions_site"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ Str::ucfirst(__('validation.attributes.reductions_site')) }}</legend> <div class="row"> <div class="col-12"> @@ -379,7 +379,7 @@ </div> {{-- REDUCTIONS_CAMPAIGN --}} <div class="col-12 col-md-6 mb-3" id="reductions_campaign"> - <fieldset class="h-100 border bg-white p-3"> + <fieldset class="h-100 border p-3"> <legend>{{ Str::ucfirst(__('validation.attributes.reductions_campaign')) }}</legend> <div class="row"> <div class="col-12"> @@ -437,8 +437,8 @@ <div class="row"> {{-- ANALYTICS --}} <div class="col-12 col-md-6 mb-3"> - <fieldset class="h-100 border bg-white p-3"> - <legend>{{ Str::ucfirst(__('validation.attributes.analytics')) }}</legend> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.custom.analytics')) }}</legend> <div class="row"> <div class="col-12"> @include('back::partials.inputs.int-number', [ diff --git a/resources/views/pages/static_pages/edit.blade.php b/resources/views/pages/static_pages/edit.blade.php new file mode 100644 index 0000000..f2133de --- /dev/null +++ b/resources/views/pages/static_pages/edit.blade.php @@ -0,0 +1,39 @@ +@extends('back::layout', ['brParam' => $staticPageModel]) + +@section('title', __('Édition d\'un :model', ['model' => __('models.classes.static_page')])) +@section('description', __('Édition d\'un :model déjà existant', ['model' => __('models.classes.static_page')])) +@section('breadcrumb', + request()->route()->getName()) + +@section('content') + <div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap pb-3"> + <div class="d-flex align-items-start flex-row"> + <a class="btn btn-primary text-decoration-none m-0" data-bs-toggle="tooltip" href="{{ route('bo.static_pages.index') }}" + title="{{ __('crud.helpers.list_all', ['model' => Str::plural(__('models.classes.static_page'))]) }}"> + <i class="fa-solid fa-arrow-left"></i> + </a> + @include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $staticPageModel]) + </div> + <div class="mb-md-0 mb-2"> + <div class="btn-group"> + <button class="btn btn-primary" id="formSubmitClone" data-bs-toggle="tooltip" type="submit" + title="{{ __('crud.helpers.save_model', ['model' => __('models.classes.static_page')]) }}"> + {{ Str::ucfirst(__('crud.actions.save')) }} + </button> + </div> + </div> + </div> + <form action="{{ route('bo.static_pages.update', $staticPageModel) }}" method="POST" enctype="multipart/form-data"> + @csrf + @method('PUT') + @include('back::pages.static_pages.form') + <div class="row"> + <div class="col text-center"> + <button class="btn btn-primary" id="formSubmit" data-bs-toggle="tooltip" type="submit" + title="{{ __('crud.helpers.save_model', ['model' => __('models.classes.static_page')]) }}"> + {{ Str::ucfirst(__('crud.actions.save')) }} + </button> + </div> + </div> + </form> +@endsection diff --git a/resources/views/pages/static_pages/form.blade.php b/resources/views/pages/static_pages/form.blade.php new file mode 100644 index 0000000..33abf29 --- /dev/null +++ b/resources/views/pages/static_pages/form.blade.php @@ -0,0 +1,171 @@ +<div class="row"> + {{-- ROLE --}} + <div class="col-12 col-md-6 mb-3"> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.custom.static_page_type')) }}</legend> + <div class="row"> + <div class="col-12"> + @include('back::partials.inputs.tinyint-enum', [ + 'id' => 'userRole', + 'label' => __('Associer un rôle'), + 'name' => 'role', + 'model' => $staticPageModel ?? null, + 'required' => false, + 'disabled' => true, + 'associatedModels' => \App\Enums\StaticPageType::toArray(), + 'helper' => '', + ]) + </div> + </div> + </fieldset> + </div> + {{-- TITLE --}} + <div class="col-12 col-md-6 mb-3"> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.attributes.title')) }}</legend> + <div class="row"> + <div class="col-12"> + @include('back::partials.inputs.varchar', [ + 'id' => 'userFirstname', + 'label' => __('Titre de la :model', ['model' => trans_choice('models.classes.static_page', 1)]), + 'hasWordCounter' => true, + 'name' => 'title', + 'required' => true, + 'model' => $staticPageModel ?? null, + 'helper' => __('validation.between.string', [ + 'attribute' => __('titre de la :model', ['model' => trans_choice('models.classes.static_page', 1)]), + 'min' => 3, + 'max' => 255, + ]), + 'placeholder' => __('Un titre clair et concis'), + ]) + </div> + </div> + </fieldset> + </div> +</div> + +<div class="row"> + {{-- SEO_TITLE --}} + <div class="col-12 col-md-6 mb-3"> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.custom.seo_title')) }}</legend> + <div class="row"> + <div class="col-12 mb-3"> + @include('back::partials.inputs.varchar', [ + 'id' => 'staticPagesSeoTitle', + 'label' => __(':field :inter:model', [ + 'field' => Str::ucfirst(__('validation.custom.seo_title')), + 'inter' => __('validation.custom.of_the.female'), + 'model' => trans_choice('models.classes.static_page', 1), + ]), + 'hasWordCounter' => true, + 'name' => 'seo_title', + 'required' => true, + 'model' => $staticPageModel ?? null, + 'helper' => sprintf( + '%s %s', + __('crud.helpers.seo_title'), + __('validation.between.string', [ + 'attribute' => __(':field :inter:model', [ + 'field' => __('validation.custom.seo_title'), + 'inter' => __('validation.custom.of_the.female'), + 'model' => trans_choice('models.classes.static_page', 1), + ]), + 'min' => 45, + 'max' => 70, + ])), + 'placeholder' => __(':field :inter:model', [ + 'field' => Str::ucfirst(__('validation.custom.seo_title')), + 'inter' => __('validation.custom.of_the.female'), + 'model' => trans_choice('models.classes.static_page', 1), + ]), + ]) + </div> + </div> + </fieldset> + </div> + {{-- SEO_DESCRIPTION --}} + <div class="col-12 col-md-6 mb-3"> + <fieldset class="h-100 border p-3"> + <legend>{{ Str::ucfirst(__('validation.custom.seo_description')) }}</legend> + <div class="row"> + <div class="col-12"> + @include('back::partials.inputs.varchar-multiline', [ + 'id' => 'staticPagesSeoDescription', + 'label' => __(':field :inter:model', [ + 'field' => Str::ucfirst(__('validation.custom.seo_description')), + 'inter' => __('validation.custom.of_the.female'), + 'model' => trans_choice('models.classes.static_page', 1), + ]), + 'name' => 'seo_description', + 'model' => $staticPageModel ?? null, + 'required' => true, + 'helper' => sprintf( + '%s %s', + __('crud.helpers.seo_description'), + __('validation.between.string', [ + 'attribute' => __(':field :inter:model', [ + 'field' => __('validation.custom.seo_description'), + 'inter' => __('validation.custom.of_the.female'), + 'model' => trans_choice('models.classes.static_page', 1), + ]), + 'min' => 50, + 'max' => 160, + ])), + 'placeholder' => __(':field :inter:model', [ + 'field' => Str::ucfirst(__('validation.custom.seo_description')), + 'inter' => __('validation.custom.of_the.female'), + 'model' => trans_choice('models.classes.static_page', 1), + ]), + ]) + </div> + </div> + </fieldset> + </div> +</div> + +<div class="row"> + {{-- CONTENT --}} + <div class="col-12 mb-3"> + <fieldset class="border p-3"> + <legend>{{ Str::ucfirst(__('validation.attributes.content')) }}</legend> + <div class="row mb-3"> + <div class="col-12"> + @include('back::partials.inputs.text-html', [ + 'id' => 'articleContent', + 'label' => __(':field :inter:model', [ + 'field' => Str::ucfirst(__('validation.attributes.content')), + 'inter' => __('validation.custom.of_the.female'), + 'model' => trans_choice('models.classes.static_page', 1), + ]), + 'name' => 'content', + 'model' => $staticPageModel ?? null, + 'required' => true, + 'helper' => __('crud.minicrud.text_warning'), + ]) + </div> + </div> + </fieldset> + </div> +</div> + +<div class="row"> + <div class="col text-center"> + <p><b>{{ __('* Champs obligatoires') }}</b></p> + </div> +</div> + +@push('scripts') + <script nonce="{{ $nonce }}"> + document.addEventListener("DOMContentLoaded", () => { + // Submit button clone. + const submit = document.getElementById('formSubmit') + submitClone = document.getElementById('formSubmitClone'); + submitClone.addEventListener('click', (event) => { + event.preventDefault(); + submit.click(''); + }) + }); + </script> +@endpush diff --git a/resources/views/pages/static_pages/index-list.blade.php b/resources/views/pages/static_pages/index-list.blade.php new file mode 100644 index 0000000..2a9a6bf --- /dev/null +++ b/resources/views/pages/static_pages/index-list.blade.php @@ -0,0 +1,45 @@ +<table class="table-hover table-fix-action m-0 table"> + @if (count($staticPageModelList) > 0) + <thead> + @include('back::modules.table-col-sorter', [ + 'cols' => [ + 'type' => Str::ucfirst(__('validation.custom.static_page_type')), + 'title' => Str::ucfirst(__('validation.attributes.title')), + 'updated_at' => Str::ucfirst(__('validation.attributes.updated_at')), + ], + ]) + </thead> + <tbody> + @foreach ($staticPageModelList as $staticPageModel) + <tr class="border-bottom list-item"> + <td class="text-center align-middle">{{ $staticPageModel->type->label() }}</td> + <td class="text-center align-middle">{{ $staticPageModel->title }}</td> + <td class="text-center align-middle"> + <span class="badge bg-secondary">{{ $staticPageModel->updated_at->isoFormat('LLLL') }}</span> + </td> + <td class="text-end align-middle"> + @canAny(['update'], $staticPageModel) + <div class="btn-group"> + @can('update', $staticPageModel) + <a class="btn btn-sm btn-primary" data-bs-toggle="tooltip" + href="{{ route('bo.static_pages.edit', ['static_page' => $staticPageModel]) }}" + title="{{ Str::ucfirst(__('crud.actions.update')) }}"> + <i class="fa-solid fa-pencil"></i> + </a> + @endcan + </div> + @else + <span class="text-danger" data-bs-toggle="tooltip" title="{{ __('list.right') }}"> + <i class="fa-solid fa-ban"></i> + </span> + @endcan + </td> + </tr> + @endforeach + </tbody> + @else + <tr> + <td class="border-0">{{ __('crud.other.no_model_found', ['model' => __('models.classes.static_page')]) }}</td> + </tr> + @endif +</table> diff --git a/resources/views/pages/static_pages/index.blade.php b/resources/views/pages/static_pages/index.blade.php new file mode 100644 index 0000000..0652d35 --- /dev/null +++ b/resources/views/pages/static_pages/index.blade.php @@ -0,0 +1,15 @@ +@extends('back::layout') + +@section('title', Str::ucfirst(__('models.classes.static_page'))) +@section('description', __('Liste de tous les :model', ['model' => Str::plural(__('models.classes.static_page'))])) + +@section('content') + <div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap"> + @include('modules.breadcrumbs.breadcrumb-body') + </div> + @include('back::modules.search-bar') + <div class="table-responsive mb-3"> + @include('back::pages.static_pages.index-list') + </div> + {{ $staticPageModelList->links() }} +@endsection diff --git a/resources/views/pages/users/create.blade.php b/resources/views/pages/users/create.blade.php new file mode 100644 index 0000000..a7a860c --- /dev/null +++ b/resources/views/pages/users/create.blade.php @@ -0,0 +1,36 @@ +@extends('back::layout', ['brParam' => $userModel]) + +@section('title', __('Création d\'un :model', ['model' => __('models.classes.user')])) +@section('description', __('Création d\'un tout nouveau :model', ['model' => __('models.classes.user')])) +@section('breadcrumb', + request()->route()->getName()) + +@section('content') + <div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap pb-3"> + <div class="d-flex align-items-start flex-row"> + <a class="btn btn-primary text-decoration-none m-0" data-bs-toggle="tooltip" href="{{ route('bo.users.index') }}" + title="{{ __('crud.helpers.list_all', ['model' => __('models.classes.user')]) }}"> + <i class="fa-solid fa-arrow-left"></i> + </a> + @include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $userModel]) + </div> + <div class="mb-md-0 mb-2"> + <button class="btn btn-primary" id="formSubmitClone" data-bs-toggle="tooltip" type="submit" + title="{{ __('crud.helpers.save_model', ['model' => __('models.classes.user')]) }}"> + {{ Str::ucfirst(__('crud.actions.save')) }} + </button> + </div> + </div> + <form action="{{ route('bo.users.store') }}" method="POST" enctype="multipart/form-data"> + @csrf + @include('back::pages.users.form') + <div class="row mt-3"> + <div class="col text-center"> + <button class="btn btn-primary" id="formSubmit" data-bs-toggle="tooltip" type="submit" + title="{{ __('crud.helpers.save_model', ['model' => __('models.classes.user')]) }}"> + {{ Str::ucfirst(__('crud.actions.save')) }} + </button> + </div> + </div> + </form> +@endsection diff --git a/resources/views/pages/users/edit.blade.php b/resources/views/pages/users/edit.blade.php new file mode 100644 index 0000000..bdb3ff5 --- /dev/null +++ b/resources/views/pages/users/edit.blade.php @@ -0,0 +1,52 @@ +@extends('back::layout', ['brParam' => $userModel]) + +@section('title', __('Édition d\'un :model', ['model' => __('models.classes.user')])) +@section('description', __('Édition d\'un :model déjà existant', ['model' => __('models.classes.user')])) +@section('breadcrumb', + request()->route()->getName()) + +@section('content') + <div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap pb-3"> + <div class="d-flex align-items-start flex-row"> + <a class="btn btn-primary text-decoration-none m-0" data-bs-toggle="tooltip" href="{{ route('bo.users.index') }}" + title="{{ __('crud.helpers.list_all', ['model' => Str::plural(__('models.classes.user'))]) }}"> + <i class="fa-solid fa-arrow-left"></i> + </a> + @include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $userModel]) + </div> + <div class="mb-md-0 mb-2"> + <form class="btn-group confirmDeleteTS" action="{{ route('bo.users.destroy', $userModel) }}" method="POST" novalidate> + @can('duplicate', $userModel) + <a class="btn btn-secondary" data-bs-toggle="tooltip" href="{{ route('bo.users.duplicate', ['user' => $userModel]) }}" + title="{{ Str::ucfirst(__('crud.actions.duplicate')) }}"> + <i class="fa-solid fa-copy"></i> + </a> + @endcan + <button class="btn btn-primary" id="formSubmitClone" data-bs-toggle="tooltip" type="submit" + title="{{ __('crud.helpers.save_model', ['model' => __('models.classes.user')]) }}"> + {{ Str::ucfirst(__('crud.actions.save')) }} + </button> + @can('delete', $userModel) + @csrf + @method('DELETE') + <button class="btn btn-danger" data-bs-toggle="tooltip" type="submit" title="{{ Str::ucfirst(__('crud.actions.delete')) }}"> + <i class="fa-solid fa-trash"></i> + </button> + @endcan + </form> + </div> + </div> + <form action="{{ route('bo.users.update', $userModel) }}" method="POST" enctype="multipart/form-data"> + @csrf + @method('PUT') + @include('back::pages.users.form') + <div class="row"> + <div class="col text-center"> + <button class="btn btn-primary" id="formSubmit" data-bs-toggle="tooltip" type="submit" + title="{{ __('crud.helpers.save_model', ['model' => __('models.classes.user')]) }}"> + {{ Str::ucfirst(__('crud.actions.save')) }} + </button> + </div> + </div> + </form> +@endsection diff --git a/resources/views/pages/users/form.blade.php b/resources/views/pages/users/form.blade.php new file mode 100644 index 0000000..15f2b1d --- /dev/null +++ b/resources/views/pages/users/form.blade.php @@ -0,0 +1,173 @@ +<div class="row"> + {{-- FIRSTNAME --}} + <div class="col-12 col-md-6 mb-3"> + <fieldset class="h-100 border p-3"> + <legend>{{ __('Prénom') }}</legend> + <div class="row"> + <div class="col-12"> + @include('back::partials.inputs.varchar', [ + 'id' => 'userFirstname', + 'label' => __('Prénom de l\':model', ['model' => __('models.classes.user')]), + 'hasWordCounter' => true, + 'name' => 'first_name', + 'required' => true, + 'model' => $userModel ?? null, + 'helper' => __('validation.between.string', [ + 'attribute' => __('prénom de l\':model', ['model' => __('models.classes.user')]), + 'min' => 3, + 'max' => 255, + ]), + 'placeholder' => __('Un beau prénom'), + ]) + </div> + </div> + </fieldset> + </div> + {{-- LASTNAME --}} + <div class="col-12 col-md-6 mb-3"> + <fieldset class="h-100 border p-3"> + <legend>{{ __('Nom') }}</legend> + <div class="row"> + <div class="col-12"> + @include('back::partials.inputs.varchar', [ + 'id' => 'userLastname', + 'label' => __('Nom de l\':model', ['model' => __('models.classes.user')]), + 'hasWordCounter' => true, + 'name' => 'last_name', + 'required' => true, + 'model' => $userModel ?? null, + 'helper' => __('validation.between.string', [ + 'attribute' => __('nom de l\':model', ['model' => __('models.classes.user')]), + 'min' => 3, + 'max' => 255, + ]), + 'placeholder' => __('Un beau nom'), + ]) + </div> + </div> + </fieldset> + </div> + {{-- EMAIL --}} + <div class="col-12 col-md-6 mb-3"> + <fieldset class="h-100 border p-3"> + <legend>{{ __('E-mail') }}</legend> + <div class="row"> + <div class="col-12"> + @include('back::partials.inputs.varchar-email', [ + 'id' => 'userEmail', + 'label' => __('E-mail de l\':model', ['model' => __('models.classes.user')]), + 'name' => 'email', + 'required' => true, + 'model' => $userModel ?? null, + 'helper' => __('validation.email', [ + 'attribute' => __('E-mail de l\':model', ['model' => __('models.classes.user')]), + ]), + 'placeholder' => __('aaaa.bbb@gmail.com'), + ]) + </div> + </div> + </fieldset> + </div> + {{-- ROLE --}} + <div class="col-12 col-md-6 mb-3"> + <fieldset class="h-100 border p-3"> + <legend>{{ __('Rôle') }}</legend> + <div class="row"> + <div class="col-12"> + @include('back::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.'), + ]) + </div> + </div> + </fieldset> + </div> + {{-- PASSWORD --}} + <div class="col-12 mb-3"> + <fieldset class="h-100 border p-3"> + <legend>{{ __('Mot de passe') }}</legend> + <div class="row"> + <div class="col-12 col-md-6 mb-3"> + @include('back::partials.inputs.varchar-password', [ + 'id' => 'userPassword', + 'label' => __('Mot de passe de l\':model', ['model' => __('models.classes.user')]), + 'name' => 'password', + 'hasWordCounter' => true, + 'required' => !$userModel->exists, + 'model' => null, + 'helper' => __( + 'Doît respecter certaines contraintes (8 caractères minimum, lettres, chiffres, caractères spéciaux).'), + ]) + </div> + <div class="col-12 col-md-6 mb-3"> + @include('back::partials.inputs.varchar-password', [ + 'id' => 'userPassword_confirmation', + 'label' => __('Confirmation du mot de passe de l\':model', [ + 'model' => __('models.classes.user'), + ]), + 'name' => 'password_confirmation', + 'hasWordCounter' => true, + 'required' => !$userModel->exists, + 'model' => null, + 'helper' => __('Doît correspondre au mot de passe saisi précédemment.'), + ]) + </div> + </div> + </fieldset> + </div> + {{-- STATUS --}} + <div class="col-12 col-md-6 mb-3"> + <fieldset class="h-100 border p-3"> + <legend>{{ __('Visibilité') }}</legend> + <div class="row"> + <div class="col-12"> + @include('back::partials.inputs.tinyint-boolean', [ + 'id' => 'userPublished', + 'name' => 'published', + 'label' => __('État de publication de l\':model', ['model' => __('models.classes.user')]), + 'helper' => __('Deux réponses possibles : oui ou non.'), + 'model' => $userModel ?? null, + ]) + </div> + </div> + </fieldset> + </div> + <div class="row"> + <div class="col text-center"> + <p><b>{{ __('* Champs obligatoires') }}</b></p> + </div> + </div> +</div> + +@push('scripts') + <script nonce="{{ $nonce }}"> + document.addEventListener("DOMContentLoaded", () => { + // Submit button clone. + const submit = document.getElementById('formSubmit') + submitClone = document.getElementById('formSubmitClone'); + submitClone.addEventListener('click', (event) => { + event.preventDefault(); + submit.click(''); + }) + + // If we edit the password, the confirmation is required. + const passwordInput = document.getElementById('userPassword'); + const confirmPasswordInput = document.getElementById('userPassword_confirmation'); + passwordInput.addEventListener('input', () => { + if (passwordInput.value !== '') { + confirmPasswordInput.required = true; + } else { + confirmPasswordInput.required = false; + } + }) + }); + </script> +@endpush diff --git a/resources/views/pages/users/index-list.blade.php b/resources/views/pages/users/index-list.blade.php new file mode 100644 index 0000000..5ecbd72 --- /dev/null +++ b/resources/views/pages/users/index-list.blade.php @@ -0,0 +1,70 @@ +<table class="table-hover table-fix-action m-0 table"> + @if (count($userModels) > 0) + <thead> + @include('back::modules.table-col-sorter', [ + 'cols' => [ + 'role' => Str::ucfirst(__('validation.attributes.role')), + 'first_name' => Str::ucfirst(__('validation.attributes.first_name')), + 'last_name' => Str::ucfirst(__('validation.attributes.last_name')), + 'email' => Str::ucfirst(__('validation.attributes.email')), + 'published' => Str::ucfirst(__('validation.custom.activated')), + 'updated_at' => Str::ucfirst(__('validation.attributes.updated_at')), + ], + ]) + </thead> + <tbody> + @foreach ($userModels as $userModel) + <tr class="border-bottom list-item"> + <td class="text-center align-middle">{{ $userModel->role->label() }}</td> + <td class="text-center align-middle">{{ $userModel->first_name }}</td> + <td class="text-center align-middle">{{ $userModel->last_name }}</td> + <td class="text-center align-middle">{{ $userModel->email }}</td> + @include('back::modules.change-published-status', [ + 'routeName' => 'users', + 'model' => $userModel, + ]) + <td class="text-center align-middle"> + <span class="badge bg-secondary">{{ $userModel->updated_at->isoFormat('LLLL') }}</span> + </td> + <td class="text-end align-middle"> + @canAny(['delete', 'duplicate', 'update'], $userModel) + <form class="btn-group confirmDeleteTS" action="{{ route('bo.users.destroy', $userModel) }}" method="POST" + novalidate> + @can('duplicate', $userModel) + <a class="btn btn-sm btn-secondary" data-bs-toggle="tooltip" + href="{{ route('bo.users.duplicate', ['user' => $userModel]) }}" + title="{{ Str::ucfirst(__('crud.actions.duplicate')) }}"> + <i class="fa-solid fa-copy"></i> + </a> + @endcan + @can('update', $userModel) + <a class="btn btn-sm btn-primary" data-bs-toggle="tooltip" + href="{{ route('bo.users.edit', ['user' => $userModel]) }}" + title="{{ Str::ucfirst(__('crud.actions.update')) }}"> + <i class="fa-solid fa-pencil"></i> + </a> + @endcan + @can('delete', $userModel) + @csrf + @method('DELETE') + <button class="btn btn-sm btn-danger" data-bs-toggle="tooltip" type="submit" + title="{{ Str::ucfirst(__('crud.actions.delete')) }}"> + <i class="fa-solid fa-trash"></i> + </button> + @endcan + </form> + @else + <span class="text-danger" data-bs-toggle="tooltip" title="{{ __('list.right') }}"> + <i class="fa-solid fa-ban"></i> + </span> + @endcan + </td> + </tr> + @endforeach + </tbody> + @else + <tr> + <td class="border-0">{{ __('crud.other.no_model_found', ['model' => __('models.classes.user')]) }}</td> + </tr> + @endif +</table> diff --git a/resources/views/pages/users/index.blade.php b/resources/views/pages/users/index.blade.php new file mode 100644 index 0000000..e0e7ab4 --- /dev/null +++ b/resources/views/pages/users/index.blade.php @@ -0,0 +1,21 @@ +@extends('back::layout') + +@section('title', Str::ucfirst(__('models.classes.user'))) +@section('description', __('Liste de tous les :model', ['model' => Str::plural(__('models.classes.user'))])) + +@section('content') + <div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap"> + @include('modules.breadcrumbs.breadcrumb-body') + @can('create', \App\Models\User::class) + <a class="btn btn-primary float-right" data-bs-toggle="tooltip" href="{{ route('bo.users.create') }}" + title="{{ __('crud.helpers.create_model', ['model' => __('models.classes.user')]) }}"> + <i class="fa-solid fa-plus"></i> + </a> + @endcan + </div> + @include('back::modules.search-bar') + <div class="table-responsive mb-3"> + @include('back::pages.users.index-list') + </div> + {{ $userModels->links() }} +@endsection diff --git a/resources/views/pages/winners-list/index.blade.php b/resources/views/pages/winners-list/index.blade.php index 5b65a7e..425a463 100644 --- a/resources/views/pages/winners-list/index.blade.php +++ b/resources/views/pages/winners-list/index.blade.php @@ -5,7 +5,7 @@ @section('content') <div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap"> - @include('breadcrumbs.breadcrumb-body') + @include('modules.breadcrumbs.breadcrumb-body') </div> <table class="table-hover table"> @if (count($participationModels) > 0) diff --git a/resources/views/partials/inputs/varchar-email.blade.php b/resources/views/partials/inputs/varchar-email.blade.php new file mode 100644 index 0000000..4a2c2e1 --- /dev/null +++ b/resources/views/partials/inputs/varchar-email.blade.php @@ -0,0 +1,33 @@ +{{-- + VARIABLE || COMMENT || TYPE || REQUIRED || DEFAULT + + $id || The id of the input tag. || string || REQUIRED || + $name || The name of the input tag. || string || REQUIRED || + $helper || The helper of the input. || string || REQUIRED || + $label || The text displayed as a label. || string || OPTIONNAL || null + $required || Is the field required ? || boolean || OPTIONNAL || false + $placeholder || The placeholder of the input. || string || OPTIONNAL || __('aaa.bbb@example.com') + $model || The model we're creating/editing || Model class || OPTIONNAL || null +--}} + +@if(!empty($label)) +<label for="{{ $id }}" class="col-form-label"> + <b>{{ $label }} {{ $required ?? false ? '*' : '' }}</b> +</label> +@endif +<input + name="{{ $name }}" + type="email" + value="{{ old($name, $model->{$name} ?? '') }}" + class="form-control @if($errors->has($name)) is-invalid @endif" + id="{{ $id }}" + aria-describedby="{{ $id }}Help" + placeholder="{{ $placeholder ?? __('aaa.bbb@example.com') }}" + @if(!empty($required)) + required + @endif> +@include('modules.input-error', [ + 'inputName' => $name, + 'helperId' => '{{ $id }}Help', + 'helper' => $helper +]) diff --git a/resources/views/partials/inputs/varchar-password.blade.php b/resources/views/partials/inputs/varchar-password.blade.php new file mode 100644 index 0000000..c779b0e --- /dev/null +++ b/resources/views/partials/inputs/varchar-password.blade.php @@ -0,0 +1,44 @@ +{{-- + VARIABLE || COMMENT || TYPE || REQUIRED || DEFAULT + + $id || The id of the input tag. || string || REQUIRED || + $name || The name of the input tag. || string || REQUIRED || + $helper || The helper of the input. || string || REQUIRED || + $label || The text displayed as a label. || string || OPTIONNAL || null + $required || Is the field required ? || boolean || OPTIONNAL || false + $placeholder || The placeholder of the input. || string || OPTIONNAL || __('Un beau mot de passe') + $hasWordCounter || Is the WordCounter needed ? || boolean || OPTIONNAL || false + $model || The model we're creating/editing || Model class || OPTIONNAL || null +--}} + +@if(!empty($label)) +<label for="{{ $id }}" class="col-form-label"> + <b>{{ $label }} {{ $required ?? false ? '*' : '' }}</b> +</label> +@endif +@if(!empty($hasWordCounter ?? false)) +<div class="word-counter" data-json='@json(['id'=> "$id"])'></div> +@endif +<div class="col-12 input-group d-flex w-100 justify-content-center"> + <input id="{{ $id }}" + name="{{ $name }}" + type="password" + value="{{ old($name, $model->{$name} ?? '') }}" + class="form-control @if($errors->has($name)) is-invalid @endif @if(!empty($hasWordCounter ?? false)) need-word-counter-password @endif" + id="{{ $id }}" + aria-describedby="{{ $id }}Help" + placeholder="{{ $placeholder ?? __('Un beau mot de passe') }}" + autocomplete="current-password" + @if(!empty($required)) + required + @endif> + @php $dataPassword = ['inputId' => $id]; @endphp + <span class="btn btn-primary password-visibility w-fit d-flex justify-content-center align-items-center p-0 @if(!empty($hasWordCounter ?? false)) need-word-counter-password-btn @endif" + data-json='@json($dataPassword)'> + </span> +</div> +@include('modules.input-error', [ + 'inputName' => $name, + 'helperId' => $id . 'Help', + 'helper' => $helper +]) diff --git a/resources/views/vendor/errors/illustrated-layout.blade.php b/resources/views/vendor/errors/illustrated-layout.blade.php index a277a37..75db5ed 100644 --- a/resources/views/vendor/errors/illustrated-layout.blade.php +++ b/resources/views/vendor/errors/illustrated-layout.blade.php @@ -7,7 +7,7 @@ <title>@yield('title') - @vite(['resources/sass/bo/bo.scss']) + @vite(['resources/sass/bo.scss']) @stack('scripts') diff --git a/resources/views/vendor/errors/minimal.blade.php b/resources/views/vendor/errors/minimal.blade.php index f20ce4d..6d5121b 100644 --- a/resources/views/vendor/errors/minimal.blade.php +++ b/resources/views/vendor/errors/minimal.blade.php @@ -13,7 +13,7 @@ } - @vite(['resources/sass/bo/bo.scss']) + @vite(['resources/sass/bo.scss']) @stack('scripts') diff --git a/routes/breadcrumbs-bo.php b/routes/breadcrumbs-bo.php index d183029..4327025 100644 --- a/routes/breadcrumbs-bo.php +++ b/routes/breadcrumbs-bo.php @@ -1,15 +1,16 @@ push(__('models.classes.contents'), route('bo.contents.index')); +Breadcrumbs::for('bo.static_pages.index', function (Generator $trail) { + $trail->push(Str::ucfirst(trans_choice('models.classes.static_page', '*')), route('bo.static_pages.index')); }); -Breadcrumbs::for('bo.contents.edit', function (Generator $trail, Content $content) { - $trail->parent('bo.contents.index'); - $trail->push(__('crud.actions.edit'), route('bo.contents.edit', $content->id)); +Breadcrumbs::for('bo.static_pages.edit', function (Generator $trail, StaticPage $page) { + $trail->parent('bo.static_pages.index'); + $trail->push(__('crud.actions.edit'), route('bo.static_pages.edit', $page)); }); // * FAQ - QUESTIONS @@ -123,7 +124,7 @@ Breadcrumbs::for('bo.winners-list', function (Generator $trail) { // * UNIQUES CODES Breadcrumbs::for('bo.codes', function (Generator $trail) { - $trail->push(__('models.classes.codes'), route('bo.codes')); + $trail->push(trans('codes uniques'), route('bo.codes')); }); Breadcrumbs::for('bo.codes.search', function (Generator $trail) { $trail->parent('bo.codes'); diff --git a/routes/web-bo.php b/routes/web-bo.php index 5270490..52ac13a 100644 --- a/routes/web-bo.php +++ b/routes/web-bo.php @@ -1,8 +1,8 @@ name('bo.')->group(function () { + // * Authenticable routes Route::namespace('\App\Http\Controllers\Bo')->group(function () { Auth::routes( [ @@ -30,47 +33,70 @@ Route::prefix('bo')->name('bo.')->group(function () { ] ); }); - Route::middleware(['auth:backend', 'is-admin', 'is-super-admin'])->group(function () { - Route::get('/', [DashboardController::class, 'index']) - ->name('dashboard')->withoutMiddleware('is-super-admin'); - // * BANNED IPS - Route::resource('/bannedips', BannedIpController::class)->except('show'); - // * CONTENTS - Route::resource('/contents', ContentController::class) - ->only(['index', 'edit', 'update']) - ->withoutMiddleware('htmlsanitize') - ->middleware(['htmlsanitize:value']); - // * FAQ - THEMES - Route::resource('/faq-themes', FaqThemesController::class)->except('show'); - // * FAQ - QUESTIONS - Route::resource('/faq-questions', FaqQuestionsController::class)->except('show'); - // * GAIN VIEWS - Route::resource('/gain-views', GainViewController::class)->except('show'); - // * PRIZES - Route::resource('/prizes', PrizesController::class) - ->except('show') - ->withoutMiddleware('htmlsanitize') - ->middleware(['htmlsanitize:arg_content']); - // * RANGES - Route::resource('ranges', RangeController::class) - ->except('show') - ->withoutMiddleware('is-super-admin'); - // * PRODUCTS - Route::resource('products', ProductsController::class) - ->except('show') - ->withoutMiddleware('is-super-admin'); - // * SETTINGS. - Route::resource('/settings', SettingsController::class) - ->only(['edit', 'update']); - // * WINNERS LIST - Route::get('/winners-list', [WinnersListController::class, 'index'])->name('winners-list'); - // * INSTANT WIN - Route::get('/instant-win', [InstantWinController::class, 'index'])->name('instant-win'); - Route::get('/ajax/instant-win-generate', [InstantWinController::class, 'instantWinGenerate']); - // * UNIQUES CODES. - Route::get('/codes', [CodesController::class, 'index'])->name('codes'); - Route::post('/codes', [CodesController::class, 'generate'])->name('generate-codes'); - Route::get('/codes/search', [CodesController::class, 'search'])->name('codes.search'); - Route::post('/codes/export', [CodesController::class, 'export'])->name('export-codes'); + Route::middleware('auth:backend')->group(function () { + Route::get('/', [DashboardController::class, 'index'])->name('dashboard'); + + // * USERS. + Route::resource('users', UserController::class)->except('show'); + Route::patch('/users/{user}/change-published', [ + UserController::class, 'changePublished' + ])->name('users.change-published'); + Route::get('/users/{user}/duplicate', [ + UserController::class, 'duplicate' + ])->name('users.duplicate'); + + // * StaticPages + Route::resource('pages', StaticPageController::class) + ->names([ + 'index' => 'static_pages.index', + 'edit' => 'static_pages.edit', + 'update' => 'static_pages.update', + ])->parameters(['pages' => 'static_page']) + ->only(['index', 'edit', 'update']); + + Route::middleware('can:isConceptor')->group(function () { + // * ACTIVITY LOGS. + Route::resource('activity_logs', ActivityLogController::class)->only(['index', 'show']); + }); + + Route::middleware(['is-super-admin'])->group(function () { + // * FAQ - THEMES + Route::resource('/faq-themes', FaqThemesController::class)->except('show'); + // * FAQ - QUESTIONS + Route::resource('/faq-questions', FaqQuestionsController::class)->except('show'); + // * GAIN VIEWS + Route::resource('/gain-views', GainViewController::class)->except('show'); + // * PRIZES + Route::resource('/prizes', PrizesController::class) + ->except('show') + ->withoutMiddleware('htmlsanitize') + ->middleware(['htmlsanitize:arg_content']); + // * RANGES + Route::resource('ranges', RangeController::class) + ->except('show') + ->withoutMiddleware('is-super-admin'); + // * PRODUCTS + Route::resource('products', ProductsController::class) + ->except('show') + ->withoutMiddleware('is-super-admin'); + // * SETTINGS. + Route::resource('/settings', SettingsController::class) + ->only(['edit', 'update']); + // * WINNERS LIST + Route::get('/winners-list', [WinnersListController::class, 'index'])->name('winners-list'); + // * INSTANT WIN + Route::get('/instant-win', [InstantWinController::class, 'index'])->name('instant-win'); + Route::get('/ajax/instant-win-generate', [InstantWinController::class, 'instantWinGenerate']); + // * UNIQUES CODES. + Route::get('/codes', [CodesController::class, 'index'])->name('codes'); + Route::post('/codes', [CodesController::class, 'generate'])->name('generate-codes'); + Route::get('/codes/search', [CodesController::class, 'search'])->name('codes.search'); + Route::post('/codes/export', [CodesController::class, 'export'])->name('export-codes'); + + // * BANNED IPS + Route::resource('/bannedips', BannedIpController::class)->except('show'); + }); }); + // * Others routes + Route::post('theme/set', [Controller::class, 'setTheme'])->name('theme.set'); }); diff --git a/routes/web-fo.php b/routes/web-fo.php index c0fb325..7947f2b 100644 --- a/routes/web-fo.php +++ b/routes/web-fo.php @@ -27,7 +27,7 @@ Route::middleware(['throttle:credentials'])->name('fo.')->group(function () { Route::put('/rejouer', [WinningProofController::class, 'sendWinningProof']) ->name('send-winning-proof'); }); - Route::get('/', [HomeController::class, 'home'])->name('home'); + Route::get('/', [HomeController::class, 'home'])->name('homepage'); Route::get('/reglement', [ViewContentController::class, 'reglement'])->name('reglement'); Route::get('/charte', [ViewContentController::class, 'charte'])->name('charte'); diff --git a/vite.config.ts b/vite.config.ts index fed29d1..ab0995f 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -71,8 +71,8 @@ export default defineConfig({ }), laravel({ input: [ - "resources/sass/bo/bo.scss", - "resources/ts/bo/bo.ts", + "resources/sass/bo.scss", + "resources/ts/bo.ts", "resources/theme/mounted/sass/fo.scss", "resources/theme/mounted/ts/fo.ts", ], @@ -82,8 +82,6 @@ export default defineConfig({ targets: [ { src: [ - // * Logo for Bo error pages - path.normalize("resources/assets/images/engine/logo.png"), // * Engine copy (Default favicon) path.normalize("resources/favicon/android-chrome-192x192.png"), path.normalize("resources/favicon/android-chrome-512x512.png"),