diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index be055ac..363401a 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -2,6 +2,7 @@ namespace App\Console; +use App\Jobs\CouponCanBeUsedUpdate; use Illuminate\Console\Scheduling\Schedule; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; @@ -15,6 +16,7 @@ class Kernel extends ConsoleKernel */ protected function schedule(Schedule $schedule) { + $schedule->job(CouponCanBeUsedUpdate::class)->everyMinute(); } /** diff --git a/app/Enums/CampaignTypes.php b/app/Enums/CampaignTypes.php index e4da7dd..0238b68 100644 --- a/app/Enums/CampaignTypes.php +++ b/app/Enums/CampaignTypes.php @@ -18,4 +18,4 @@ enum CampaignTypes: int self::WALL->name => "Mur", self::BASKET->name => "Panier", ]; -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Bo/BrandController.php b/app/Http/Controllers/Bo/BrandController.php index 8568d21..98cf714 100644 --- a/app/Http/Controllers/Bo/BrandController.php +++ b/app/Http/Controllers/Bo/BrandController.php @@ -10,7 +10,6 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; - class BrandController extends Controller { /** diff --git a/app/Http/Controllers/Bo/CouponController.php b/app/Http/Controllers/Bo/CouponController.php new file mode 100644 index 0000000..4ca0cef --- /dev/null +++ b/app/Http/Controllers/Bo/CouponController.php @@ -0,0 +1,156 @@ +search) { + $this->searchQuery( + $coupon, + $request->search, + null, + 'name', + ); + } + + $this->sortQuery($coupon); + $searchFields = trans('Name'); + + $couponModels = $coupon->paginate(10); + + return view('bo.pages.coupons.index', compact('couponModels', 'searchFields')); + } + + /** + * Show the form for creating a new resource. + * + * @return \Illuminate\Http\Response + */ + public function create() + { + $brandModels = Brand::orderBy('name', 'ASC')->get(); + + return view('bo.pages.coupons.create', compact('brandModels')); + } + + + /** + * Store a newly created resource in storage. + * + * @param \App\Http\Requests\Bo\Coupons\StoreCouponRequest $request + * @return \Illuminate\Http\Response + */ + public function store(StoreCouponRequest $request) + { + return DB::transaction(function () use ($request) { + $coupon = new Coupon(); + $coupon->fill($request->validated()); + + if ($coupon->saveOrFail()) { + return redirect() + ->route('bo.coupons.edit', $coupon) + ->with('success', trans( + 'crud.messages.has_been_created', + ['model' => Str::singular(trans('models.classes.coupons'))] + )); + } + return redirect() + ->back() + ->with('error', trans( + 'crud.messages.cannot_be_created', + ['model' => Str::singular(trans('models.classes.coupons'))] + )); + }); + } + + /** + * Display a listing of the resource. + * + * @param \App\Models\Coupon $coupon + * @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory + */ + public function edit(Coupon $coupon) + { + $brandModels = Brand::orderBy('name', 'ASC')->get(); + + return view('bo.pages.coupons.edit', [ + 'couponModel' => $coupon, + 'brandModels' => $brandModels + ]); + } + + /** + * Update the specified resource in storage. + * + * @param \App\Http\Requests\Bo\Coupons\UpdateCouponRequest $request + * @param \App\Models\Coupon $coupon + * @return \Illuminate\Http\Response + */ + public function update(UpdateCouponRequest $request, Coupon $coupon) + { + return DB::transaction(function () use ($request, $coupon) { + $coupon->fill($request->validated()); + if ($coupon->saveOrFail()) { + return redirect() + ->route('bo.coupons.edit', $coupon) + ->with('success', trans( + 'crud.messages.has_been_updated', + ['model' => Str::singular(trans('models.classes.coupons'))] + )); + } //end if + return redirect() + ->back() + ->with('error', trans( + 'crud.messages.cannot_be_updated', + ['model' => Str::singular(trans('models.classes.coupons'))] + )); + }); + } + + /** + * Remove the specified resource from storage. + * + * @param \App\Models\Coupon $coupon + * @return \Illuminate\Http\Response + */ + public function destroy(Coupon $coupon) + { + if ($coupon->deleteOrFail()) { + return redirect() + ->route('bo.coupons.index') + ->with('success', trans( + 'crud.messages.has_been_deleted', + ['model' => Str::singular(trans('models.classes.coupons'))] + )); + } + return redirect() + ->back() + ->with('error', trans( + ':crud.messages.cannot_be_deleted', + ['model' => Str::singular(trans('models.classes.coupons'))] + )); + } +} diff --git a/app/Http/Requests/Bo/Coupons/StoreCouponRequest.php b/app/Http/Requests/Bo/Coupons/StoreCouponRequest.php new file mode 100644 index 0000000..5953c4e --- /dev/null +++ b/app/Http/Requests/Bo/Coupons/StoreCouponRequest.php @@ -0,0 +1,67 @@ +check(); + } + + /** + * Prepare the data for validation. + * + * @return void + */ + protected function prepareForValidation() + { + $this->mergePicture('decoration'); + $this->mergePicture('gs1'); + $this->mergePicture('website_picture'); + $this->mergePicture('website_picture_mobile'); + $this->merge([ + 'status' => $this->status ? true : false + ]); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + $rules = [ + 'name' => 'required|string|min:3|max:255', + 'status' => 'required|boolean', + 'brand_id' => 'required|exists:brands,id', + 'process_code' => 'required|string|min:3|max:255', + 'amount' => 'required|numeric|min:0.1|max:3', + 'valid_on' => 'required|string', + 'validity_period' => 'required|numeric|min:1|max:31', + 'legal_notices' => 'required|string', + 'limit_qty' => 'required|numeric|digits_between:1,3', + 'limit_period' => 'required|numeric|digits_between:1,3', + 'start_diffusion_date' => 'nullable|date|before:end_diffusion_date', + 'end_diffusion_date' => 'nullable|date|after:start_diffusion_date' + ]; + return \array_merge( + $rules, + $this->pictureRules('decoration', 280, 160, 280, 160, false), + $this->pictureRules('gs1', 628, 360, 628, 360, true), + $this->pictureRules('website_picture', 580, 190, 580, 1900, true), + $this->pictureRules('website_picture_mobile', 570, 560, 570, 560, true), + ); + } +} diff --git a/app/Http/Requests/Bo/Coupons/UpdateCouponRequest.php b/app/Http/Requests/Bo/Coupons/UpdateCouponRequest.php new file mode 100644 index 0000000..e071bb3 --- /dev/null +++ b/app/Http/Requests/Bo/Coupons/UpdateCouponRequest.php @@ -0,0 +1,7 @@ +can_be_used = $coupon->checkIfCanBeUsed(); + $coupon->save(); + } + } +} diff --git a/app/Models/Basket.php b/app/Models/Basket.php index 50ec75a..b67aab2 100644 --- a/app/Models/Basket.php +++ b/app/Models/Basket.php @@ -17,7 +17,7 @@ class Basket extends Model 'campaign_id' ]; - public function content() : HasMany + public function content(): HasMany { return $this->hasMany(BasketsContent::class); } diff --git a/app/Models/BasketsContent.php b/app/Models/BasketsContent.php index 400ff5c..68b3518 100644 --- a/app/Models/BasketsContent.php +++ b/app/Models/BasketsContent.php @@ -10,12 +10,12 @@ class BasketsContent extends Model { use HasFactory; - public function basket() : BelongsTo + public function basket(): BelongsTo { return $this->belongsTo(Basket::class); } - public function coupon() : BelongsTo + public function coupon(): BelongsTo { return $this->BelongsTo(Coupon::class); } diff --git a/app/Models/Coupon.php b/app/Models/Coupon.php index 7daaa0e..05cd8b7 100644 --- a/app/Models/Coupon.php +++ b/app/Models/Coupon.php @@ -2,17 +2,87 @@ namespace App\Models; +use App\Lib\Helpers\FileStorageHelper; use Carbon\Carbon; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; +/** + * @property integer $id Model id. + * @property integer $brand_id ID de la marque sur laquelle le coupon s'applique. + * @property string $name Nom du coupon. + * @property string $process_code Code de traitement. + * @property double $amount Montant de la réduction - Max 3 euros. + * @property string $valid_on Texte réduction valable sur. + * @property integer $validity_period Période de validité en jours - default 30 j - max 1 mois. + * @property string $legal_notices Mentions légales. + * @property string $decoration Path de l'image de décoration + * - 280x160px + * - JPG. + * @property string|null $gs1 Path de l'image GS1 pour coupon Benelux + * - 628x360px + * - JPG + * @property string|null $website_picture Path de l'image a destination des sites internet + * - 580x190px + * - JPG/PNG + * @property string|null $website_picture_mobile Path de l'image a destination des sites internet mobile + * - 570x560px + * - JPG/PNG + * @property integer $limit_qty Limite de quantité d'impression / customer. + * @property integer $limit_period Limite d'impression dans une periode (en jours) / customer. + * @property integer $status Statut du coupon, Publié / Non Publié. + * @property integer $can_be_used Statut d'utilisation du coupon, Utilisable / Non utilisable, + * Modifié par calcul dans un Job en fonction des dates. + * @property \Carbon\Carbon|null $start_diffusion_date Date de début de diffusion - diffusion immédiate si null. + * @property \Carbon\Carbon|null $end_diffusion_date Date de fin de diffusion - diffusion sans fin si null. + * @property \Carbon\Carbon|null $end_validity_date Date de fin de validité + * - diffusion immédiate si null + * - required si end_diffusion_date est set et date + * au minimum egale à end_diffusion_date + validity_period jours. + * @package App\Models + */ class Coupon extends Model { - // FIXME : Ajouter les mouvement de quantité, mettre juste une colonne et se servir des logs avec un type pour mouvement ? - // FIXME / Ajouter logs impressions use HasFactory; + // FIXME : Ajouter les mouvement de quantité, mettre juste une colonne et se servir des logs avec un type pour mouvement ? + // FIXME / Ajouter logs impressions. + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'name', + 'status', + 'brand_id', + 'process_code', + 'amount', + 'valid_on', + 'validity_period', + 'legal_notices', + 'decoration', + 'gs1', + 'website_picture', + 'website_picture_mobile', + 'limit_qty', + 'limit_period', + 'start_diffusion_date', + 'end_diffusion_date', + ]; + + /** + * The attributes that aren't mass assignable. + * + * @var array + */ + protected $guarded = [ + 'can_be_used', + 'end_validity_date', + ]; + /** * The attributes that should be cast. * @@ -22,38 +92,96 @@ class Coupon extends Model 'start_diffusion_date' => 'date:Y-m-d', 'end_diffusion_date' => 'date:Y-m-d', 'end_validity_date' => 'date:Y-m-d', + 'status' => 'bool' ]; - public function campaigns() : BelongsToMany + /** + * Perform any actions required after the model boots. + * + * @return void + */ + protected static function booted(): void + { + static::creating(function (self $model) { + self::manageFields($model); + }); + static::updating(function (self $model) { + self::manageFields($model); + }); + static::updated(function (self $model) { + FileStorageHelper::removeOldFile($model, 'decoration'); + FileStorageHelper::removeOldFile($model, 'gs1'); + FileStorageHelper::removeOldFile($model, 'website_picture'); + FileStorageHelper::removeOldFile($model, 'website_picture_mobile'); + }); + static::deleted(function (self $model) { + FileStorageHelper::removeFile($model, 'decoration'); + FileStorageHelper::removeFile($model, 'gs1'); + FileStorageHelper::removeFile($model, 'website_picture'); + FileStorageHelper::removeFile($model, 'website_picture_mobile'); + }); + } + + // * RELATIONSHIPS + + /** + * The campaigns that belong to the Coupon + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ + public function campaigns(): BelongsToMany { return $this->belongsToMany(Campaign::class); } + // * METHODS + /** - * The "booted" method of the model. + * Manage fields + * + * @param self $model + * @return void */ - protected static function booted(): void + private static function manageFields(self $model) { - static::creating(function (Coupon $coupon) { - $coupon->can_be_used = $this->checkIfCanBeUsed(); - }); - static::updating(function (Coupon $coupon) { - $coupon->can_be_used = $this->checkIfCanBeUsed(); - }); + $model->decoration = FileStorageHelper::storeFile($model, $model->decoration, true); + $model->gs1 = FileStorageHelper::storeFile($model, $model->gs1, true); + $model->website_picture = FileStorageHelper::storeFile($model, $model->website_picture, true); + $model->website_picture_mobile = FileStorageHelper::storeFile($model, $model->website_picture_mobile, true); + + if (is_null($model->start_diffusion_date)) { + $model->start_diffusion_date = Carbon::now(); + } + if (isset($model->end_diffusion_date)) { + $model->end_validity_date = $model->end_diffusion_date->addDays($model->validity_period); + } else { + $model->end_validity_date = Carbon::now()->addDays($model->validity_period); + } + // Last action is to test if model can be used. + $model->can_be_used = $model->checkIfCanBeUsed(); } + /** + * Get can be used + * + * @return boolean + */ public function checkIfCanBeUsed() { if ( - (is_null($this->start_diffusion_date) or $this->start_diffusion_date->lessThanOrEqualTo(Carbon::now())) and - (is_null($this->end_diffusion_date) or $this->end_diffusion_date->greaterThanOrEqualTo(Carbon::now())) and - (is_null($this->end_validity_date) or $this->end_validity_date->greaterThanOrEqualTo(Carbon::now()->addDays($this->validity_period))) - ){ + (!is_null($this->start_diffusion_date) and + $this->start_diffusion_date->lessThanOrEqualTo(Carbon::now()) + ) and + (!is_null($this->end_diffusion_date) and + $this->end_diffusion_date->greaterThanOrEqualTo(Carbon::now()) + ) and + (!is_null($this->end_validity_date) and + $this->end_validity_date->greaterThanOrEqualTo(Carbon::now()->addDays($this->validity_period))) + ) { return true; } // FIXME : Ajouter calendrier de diffusion et le prendre en compte dans la condition - return false; } } diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index f426039..a3d2fca 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -3,6 +3,7 @@ namespace App\Providers; use App\Models\Campaign; +use App\Models\Coupon; use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; use Illuminate\Http\Request; @@ -42,6 +43,7 @@ class RouteServiceProvider extends ServiceProvider }); Route::model('campaign', Campaign::class); + Route::model('coupon', Coupon::class); } /** diff --git a/database/factories/CouponFactory.php b/database/factories/CouponFactory.php new file mode 100644 index 0000000..873907a --- /dev/null +++ b/database/factories/CouponFactory.php @@ -0,0 +1,60 @@ + + */ +class CouponFactory extends Factory +{ + /** + * The name of the factory's corresponding model. + * + * @var string + */ + protected $model = Coupon::class; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition() + { + $coupon = new Coupon(); + + // Create a carbon instance date. + $start_diffusion_date = Carbon::parse(fake()->date()); + + // Add random days to start diffusion date. + $end_diffusion_date = Carbon::parse($start_diffusion_date)->addDays(fake()->numberBetween(30, 365)); + + // Add random days to end diddusion date. + $end_validity_date = Carbon::parse($end_diffusion_date)->addDays(fake()->numberBetween(1, 30)); + + // Get a brand in DB to have is name and id. + $random_brand = fake()->randomElement(DB::table('brands')->get()); + + return [ + 'name' => fake()->unique()->word(), + 'process_code' => fake()->unique()->bothify('?????-#####'), + 'amount' => fake()->randomFloat(1, 0.1, 3), + 'valid_on' => $random_brand->name, + 'validity_period' => fake()->numberBetween(1, 30), + 'legal_notices' => fake()->text(100), + 'limit_qty' => fake()->numberBetween(0, 2), + 'limit_period' => fake()->numberBetween(0, 30), + 'status' => fake()->boolean(75), + 'can_be_used' => $coupon->checkIfCanBeUsed(), + 'start_diffusion_date' => fake()->boolean(75) ? $start_diffusion_date : null, + 'end_diffusion_date' => fake()->boolean(75) ? $end_diffusion_date : null, + 'end_validity_date' => fake()->boolean(75) ? $end_validity_date : null, + 'brand_id' => $random_brand->id + ]; + } +} diff --git a/database/migrations/2023_08_20_090058_create_coupons_table.php b/database/migrations/2023_08_20_090058_create_coupons_table.php index 441ae0c..627d792 100644 --- a/database/migrations/2023_08_20_090058_create_coupons_table.php +++ b/database/migrations/2023_08_20_090058_create_coupons_table.php @@ -17,7 +17,7 @@ return new class extends Migration $table->string('process_code')->unique()->comment("Code de traitement"); $table->float('amount')->comment("Montant de la réduction - Max 3 euros"); $table->text('valid_on')->comment("Texte réduction valable sur ..."); - $table->unsignedInteger('validity_period')->default(30)->comment("Période de validité en jours - default 30 j - max 1 mois"); + $table->unsignedSmallInteger('validity_period')->default(30)->comment("Période de validité en jours - default 30 j - max 1 mois"); $table->text('legal_notices')->comment("Mentions légales"); $table->string('decoration')->comment("Path de l'image de décoration - 280x160px - JPG"); $table->string('gs1')->nullable()->comment("Path de l'image GS1 pour coupon Benelux - 628x360px - JPG"); diff --git a/database/migrations/2023_09_18_110818_add_foreign_keys_to_coupons_table.php b/database/migrations/2023_09_18_110818_add_foreign_keys_to_coupons_table.php new file mode 100644 index 0000000..25ab156 --- /dev/null +++ b/database/migrations/2023_09_18_110818_add_foreign_keys_to_coupons_table.php @@ -0,0 +1,29 @@ +foreignId('brand_id')->after('id')->comment('ID de la marque sur laquelle le coupon s\'applique')->constrained(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('coupons', function (Blueprint $table) { + // + $table->dropForeign(['brand_id']); + }); + } +}; diff --git a/database/seeders/CouponSeeder.php b/database/seeders/CouponSeeder.php new file mode 100644 index 0000000..01e7ee6 --- /dev/null +++ b/database/seeders/CouponSeeder.php @@ -0,0 +1,34 @@ +make()->each(function (Coupon $coupon) { + $coupon->decoration = FileStorageHelper::storeFile($coupon, new \SplFileInfo(\resource_path( + '../resources/assets/images/coupon/decoration.jpg' + ))); + $coupon->gs1 = FileStorageHelper::storeFile($coupon, new \SplFileInfo(\resource_path( + '../resources/assets/images/coupon/gs1.jpg' + ))); + $coupon->website_picture = FileStorageHelper::storeFile($coupon, new \SplFileInfo(\resource_path( + '../resources/assets/images/coupon/website_picture.jpg' + ))); + $coupon->website_picture_mobile = FileStorageHelper::storeFile($coupon, new \SplFileInfo(\resource_path( + '../resources/assets/images/coupon/website_picture_mobile.jpg' + ))); + $coupon->saveOrFail(); + }); + } +} diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 794e057..2bf3bd3 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -15,7 +15,8 @@ class DatabaseSeeder extends Seeder { $this->call([ BrandSeeder::class, - CampaignSeeder::class + CampaignSeeder::class, + CouponSeeder::class ]); } } diff --git a/resources/assets/images/coupon/decoration.jpg b/resources/assets/images/coupon/decoration.jpg new file mode 100644 index 0000000..a40f55d Binary files /dev/null and b/resources/assets/images/coupon/decoration.jpg differ diff --git a/resources/assets/images/coupon/gs1.jpg b/resources/assets/images/coupon/gs1.jpg new file mode 100644 index 0000000..ae2a120 Binary files /dev/null and b/resources/assets/images/coupon/gs1.jpg differ diff --git a/resources/assets/images/coupon/website_picture.jpg b/resources/assets/images/coupon/website_picture.jpg new file mode 100644 index 0000000..b69766c Binary files /dev/null and b/resources/assets/images/coupon/website_picture.jpg differ diff --git a/resources/assets/images/coupon/website_picture_mobile.jpg b/resources/assets/images/coupon/website_picture_mobile.jpg new file mode 100644 index 0000000..3ce5314 Binary files /dev/null and b/resources/assets/images/coupon/website_picture_mobile.jpg differ diff --git a/resources/lang/en/models.php b/resources/lang/en/models.php index 0a80140..5207b30 100644 --- a/resources/lang/en/models.php +++ b/resources/lang/en/models.php @@ -13,6 +13,7 @@ return [ 'storeregions' => 'Store Regions', 'storetypes' => 'Store Types', 'brands' => 'Brands', - 'campaigns' => 'Campaigns' + 'campaigns' => 'Campaigns', + 'coupons' => 'Coupons' ] ]; diff --git a/resources/lang/en/validation.php b/resources/lang/en/validation.php index 54b913f..d30abc5 100644 --- a/resources/lang/en/validation.php +++ b/resources/lang/en/validation.php @@ -119,15 +119,30 @@ return [ "uuid" => "The :attribute must be a valid UUID.", "custom" => [], 'attributes' => [ - 'name' => 'Name', - 'contact_mail' => 'E-mail', - 'RCS' => 'RCS', - 'logo' => 'Logo', - 'crm_token' => 'CRM Token', - 'crm_password' => 'CRM Password', - 'type' => 'Type', - 'status' => 'Status', - 'brand_id' => 'Associated brand', - 'updated_at' => 'Updated at' + 'name' => 'Name', + 'contact_mail' => 'E-mail', + 'RCS' => 'RCS', + 'logo' => 'Logo', + 'crm_token' => 'CRM Token', + 'crm_password' => 'CRM Password', + 'type' => 'Type', + 'status' => 'Status', + 'brand_id' => 'Brand id', + 'updated_at' => 'Updated at', + 'process_code' => 'Process code', + 'amount' => 'Amount', + 'valid_on' => 'Valid on', + 'validity_period' => 'Validity period', + 'legal_notices' => 'Legal notices', + 'decoration' => 'Decoration', + 'gs1' => 'gs1', + 'website_picture' => 'Website picture', + 'website_picture_mobile' => 'Website picture mobile', + 'limit_qty' => 'Limit quantity', + 'limit_period' => 'Limit period', + 'can_be_used' => 'Can be used', + 'start_diffusion_date' => 'Start diffusion date', + 'end_diffusion_date' => 'End diffusion date', + 'end_validity_date' => 'End validity date' ], ]; diff --git a/resources/lang/fr/models.php b/resources/lang/fr/models.php index a0265d2..6bb3911 100644 --- a/resources/lang/fr/models.php +++ b/resources/lang/fr/models.php @@ -4,6 +4,7 @@ return [ 'classes' => [ 'users' => 'Utilisateurs', 'brands' => 'Marques', - 'campaigns' => 'Campagnes' + 'campaigns' => 'Campagnes', + 'coupons' => 'Coupons' ] ]; diff --git a/resources/lang/fr/validation.php b/resources/lang/fr/validation.php index 1468a8e..e5e9c3a 100644 --- a/resources/lang/fr/validation.php +++ b/resources/lang/fr/validation.php @@ -119,15 +119,30 @@ return [ 'uuid' => 'Le champ :attribute doit être un UUID valide', 'custom' => [], 'attributes' => [ - 'name' => 'Nom', - 'contact_mail' => 'E-mail', - 'rcs' => 'RCS', - 'logo' => 'Logo', - 'crm_token' => 'CRM Token', - 'crm_password' => 'Mot de passe CRM', - 'type' => 'Type', - 'status' => 'Status', - 'brand_id' => 'Marque associée', - 'updated_at' => 'Date de modification' + 'name' => 'Nom', + 'contact_mail' => 'E-mail', + 'rcs' => 'RCS', + 'logo' => 'Logo', + 'crm_token' => 'CRM Token', + 'crm_password' => 'Mot de passe CRM', + 'type' => 'Type', + 'status' => 'Status', + 'brand_id' => 'ID de la marque', + 'updated_at' => 'Date de modification', + 'process_code' => 'Code de processus', + 'amount' => 'Montant', + 'valid_on' => 'Valable sur', + 'validity_period' => 'Période de validité', + 'legal_notices' => 'Mentions légales', + 'decoration' => 'Décoration', + 'gs1' => 'gs1', + 'website_picture' => 'Photo pour site web', + 'website_picture_mobile' => 'Photo pour le site web mobile', + 'limit_qty' => 'Quantité limite', + 'limit_period' => 'Période limite', + 'can_be_used' => 'Peut être utilisé', + 'start_diffusion_date' => 'Date de début de diffusion', + 'end_diffusion_date' => 'Date de fin de diffusion', + 'end_validity_date' => 'Date de fin de validité' ], ]; diff --git a/resources/views/bo/layouts/nav-menu.blade.php b/resources/views/bo/layouts/nav-menu.blade.php index b03479b..caa5e81 100644 --- a/resources/views/bo/layouts/nav-menu.blade.php +++ b/resources/views/bo/layouts/nav-menu.blade.php @@ -1,22 +1,24 @@ @auth('backend') - -@endauth + +@endauth \ No newline at end of file diff --git a/resources/views/bo/pages/coupons/create.blade.php b/resources/views/bo/pages/coupons/create.blade.php new file mode 100644 index 0000000..4654295 --- /dev/null +++ b/resources/views/bo/pages/coupons/create.blade.php @@ -0,0 +1,41 @@ +@extends('bo.layout') + +@section('title', sprintf('%s - %s', Str::singular(__('models.classes.coupons')), Str::ucfirst(__('crud.actions.create')))) +@section('description', __('Creation d\'un nouveau coupon.')) + +@section('breadcrumb', + request()->route()->getName()) + +@section('content') +
+
+
+
+

+ + + + {{ Str::singular(__('models.classes.coupons')) }} {{ __('create') }} +

+
+
+
+
+
+ @csrf + @include('bo.pages.coupons.form-inputs') +
+
+ +
+
+
+
+
+@endsection diff --git a/resources/views/bo/pages/coupons/edit.blade.php b/resources/views/bo/pages/coupons/edit.blade.php new file mode 100644 index 0000000..708bdd1 --- /dev/null +++ b/resources/views/bo/pages/coupons/edit.blade.php @@ -0,0 +1,43 @@ +@extends('bo.layout', ['brParam' => $couponModel]) + +@section('title', sprintf('%s - %s %s', Str::singular(__('models.classes.coupons')), Str::ucfirst(__('crud.actions.edit')), + $couponModel->name)) +@section('description', $couponModel->valid_on) + +@section('breadcrumb', + request()->route()->getName()) + +@section('content') +
+
+
+
+

+ + + + {{ Str::singular(__('models.classes.coupons')) }} {{ __('crud.actions.edit') }} +

+
+
+
+
+
+ @csrf + @method('PUT') + @include('bo.pages.coupons.form-inputs', ['model' => $couponModel]) +
+
+ +
+
+
+
+
+@endsection diff --git a/resources/views/bo/pages/coupons/form-inputs.blade.php b/resources/views/bo/pages/coupons/form-inputs.blade.php new file mode 100644 index 0000000..c54cd1f --- /dev/null +++ b/resources/views/bo/pages/coupons/form-inputs.blade.php @@ -0,0 +1,395 @@ +
+ {{-- NAME --}} +
+
+ {{ __('validation.attributes.name') }} +
+
+ @include('bo.partials.inputs.varchar', [ + 'id' => 'couponName', + 'label' => __('Nom du :model', ['model' => Str::singular(__('models.classes.coupons'))]), + 'hasWordCounter' => true, + 'name' => 'name', + 'required' => true, + 'model' => $couponModel ?? null, + 'helper' => __('validation.between.string', [ + 'attribute' => __('Nom du :model', ['model' => Str::singular(__('models.classes.coupons'))]), + 'min' => 3, + 'max' => 255, + ]), + 'placeholder' => __('Nom du coupon'), + ]) +
+
+
+
+ {{-- BRAND_ID --}} +
+
+ {{ __('validation.attributes.brand_id') }} +
+
+ @include('bo.partials.inputs.bigint-foreign-id', [ + 'id' => 'couponBrandId', + 'label' => __(':model associée', ['model' => Str::singular(trans('models.classes.brands'))]), + 'name' => 'brand_id', + 'size' => 5, + 'model' => $couponModel ?? null, + 'associatedModels' => $brandModels, + 'foreignLabel' => 'name', + 'helper' => __('Chose one :model among this list.', [ + 'model' => Str::singular(trans('models.classes.brands')), + ]), + ]) +
+
+
+
+ {{-- PROCESS CODE --}} +
+
+ {{ __('validation.attributes.process_code') }} +
+
+ @include('bo.partials.inputs.varchar', [ + 'id' => 'couponProcessCode', + 'label' => __('Code processus du :model', ['model' => Str::singular(__('models.classes.coupons'))]), + 'hasWordCounter' => true, + 'name' => 'process_code', + 'required' => true, + 'model' => $couponModel ?? null, + 'helper' => __('validation.between.string', [ + 'attribute' => __('Code processus du :model', ['model' => Str::singular(__('models.classes.coupons'))]), + 'min' => 3, + 'max' => 255, + ]), + ]) +
+
+
+
+ {{-- AMOUNT --}} +
+
+ {{ __('validation.attributes.amount') }} +
+
+ @include('bo.partials.inputs.int-number', [ + 'id' => 'couponAmount', + 'label' => __('Montant du :model', ['model' => Str::singular(__('models.classes.coupons'))]), + 'name' => 'amount', + 'step' => 0.1, + 'required' => true, + 'model' => $couponModel ?? null, + 'helper' => __('validation.between.numeric', [ + 'attribute' => __('Montant du :model', ['model' => Str::singular(__('models.classes.coupons'))]), + 'min' => 0.1, + 'max' => 3, + ]), + 'placeholder' => __('Montant du coupon'), + ]) +
+
+
+
+ {{-- VALID ON --}} +
+
+ {{ __('validation.attributes.valid_on') }} +
+
+ @include('bo.partials.inputs.text-html', [ + 'id' => 'couponValidOn', + 'label' => __(':model valable sur', ['model' => Str::singular(__('models.classes.coupons'))]), + 'name' => 'valid_on', + 'required' => true, + 'model' => $couponModel ?? null, + 'helper' => __('validation.gt.string', [ + 'attribute' => __(':model valable sur', ['model' => Str::singular(__('models.classes.coupons'))]), + 'value' => 15, + ]), + ]) +
+
+
+
+ {{-- VALIDITY PERIOD --}} +
+
+ {{ __('validation.attributes.validity_period') }} +
+
+ @include('bo.partials.inputs.smallint-number', [ + 'id' => 'couponValidityPeriod', + 'label' => __('Période de validité du :model', ['model' => Str::singular(__('models.classes.coupons'))]), + 'name' => 'validity_period', + 'required' => true, + 'model' => $couponModel ?? null, + 'helper' => __('validation.between.numeric', [ + 'attribute' => __('Période de validité du :model', [ + 'model' => Str::singular(__('models.classes.coupons')), + ]), + 'min' => 1, + 'max' => 30, + ]), + 'placeholder' => __('Période de validité du coupon'), + ]) +
+
+
+
+ {{-- LEGAL NOTICES --}} +
+
+ {{ __('validation.attributes.legal_notices') }} +
+
+ @include('bo.partials.inputs.text-html', [ + 'id' => 'couponLegalNotices', + 'label' => __('Mentions légales du :model', ['model' => Str::singular(__('models.classes.coupons'))]), + 'name' => 'legal_notices', + 'required' => true, + 'model' => $couponModel ?? null, + 'helper' => __('validation.gt.string', [ + 'attribute' => __('Mentions légales du :model', ['model' => Str::singular(__('models.classes.coupons'))]), + 'value' => 15, + ]), + ]) +
+
+
+
+ {{-- DECORATION --}} +
+
+ {{ __('validation.attributes.decoration') }} +
+
+ @include('bo.partials.inputs.varchar-picture', [ + 'id' => 'couponDecoration', + 'name' => 'decoration', + 'width' => 280, + 'height' => 160, + 'required' => true, + 'model' => $couponModel ?? null, + 'helper' => __('crud.minicrud.dimensions_must_be', [ + 'widthpx' => '280px', + 'heightpx' => '160px', + ]), + 'preview' => true, + ]) +
+
+
+
+ {{-- GS1 --}} +
+
+ {{ __('validation.attributes.gs1') }} +
+
+ @include('bo.partials.inputs.varchar-picture', [ + 'id' => 'couponGs1', + 'name' => 'gs1', + 'width' => 628, + 'height' => 360, + 'required' => true, + 'model' => $couponModel ?? null, + 'helper' => __('crud.minicrud.dimensions_must_be', [ + 'widthpx' => '628px', + 'heightpx' => '360px', + ]), + 'required' => false, + 'preview' => true, + ]) +
+
+
+
+ {{-- WEBSITE PICTURE --}} +
+
+ {{ __('validation.attributes.website_picture') }} +
+
+ @include('bo.partials.inputs.varchar-picture', [ + 'id' => 'couponWebsitePicture', + 'name' => 'website_picture', + 'width' => 580, + 'height' => 190, + 'model' => $couponModel ?? null, + 'helper' => __('crud.minicrud.dimensions_must_be', [ + 'widthpx' => '580px', + 'heightpx' => '190px', + ]), + 'required' => false, + 'preview' => true, + ]) +
+
+
+
+ {{-- WEBSITE PICTURE MOBILE --}} +
+
+ {{ __('validation.attributes.website_picture_mobile') }} +
+
+ @include('bo.partials.inputs.varchar-picture', [ + 'id' => 'couponWebsitePictureMobile', + 'name' => 'website_picture_mobile', + 'width' => 570, + 'height' => 560, + 'model' => $couponModel ?? null, + 'helper' => __('crud.minicrud.dimensions_must_be', [ + 'widthpx' => '570px', + 'heightpx' => '560px', + ]), + 'required' => false, + 'preview' => true, + ]) +
+
+
+
+ {{-- LIMITY QTY --}} +
+
+ {{ __('validation.attributes.limit_qty') }} +
+
+ @include('bo.partials.inputs.smallint-number', [ + 'id' => 'couponLimitQty', + 'label' => __('Limite de quantité d\'impression du :model par clients', [ + 'model' => Str::singular(__('models.classes.coupons')), + ]), + 'name' => 'limit_qty', + 'required' => true, + 'model' => $couponModel ?? null, + 'helper' => __('validation.between.numeric', [ + 'attribute' => __('Limite de quantité d\'impression du :model par clients', [ + 'model' => Str::singular(__('models.classes.coupons')), + ]), + 'min' => 1, + 'max' => 30, + ]), + 'placeholder' => __('Limite de quantité d\'impression du coupon par clients'), + ]) +
+
+
+
+ {{-- LIMITY PERIOD --}} +
+
+ {{ __('validation.attributes.limit_period') }} +
+
+ @include('bo.partials.inputs.smallint-number', [ + 'id' => 'couponLimitPeriod', + 'label' => __('Limite d\'impression du :model dans une période (en jours)', [ + 'model' => Str::singular(__('models.classes.coupons')), + ]), + 'name' => 'limit_period', + 'required' => true, + 'model' => $couponModel ?? null, + 'helper' => __('validation.between.numeric', [ + 'attribute' => __('Limite d\'impression du :model dans une période (en jours)', [ + 'model' => Str::singular(__('models.classes.coupons')), + ]), + 'min' => 1, + 'max' => 30, + ]), + 'placeholder' => __('Limite d\'impression du coupon dans une période (en jours)'), + ]) +
+
+
+
+ {{-- START DIFFUSION DATE --}} +
+
+ {{ __('validation.attributes.start_diffusion_date') }} +
+
+ @include('bo.partials.inputs.timestamp-date', [ + 'id' => 'couponStartDiffusionDate', + 'label' => __('Date de début de diffusion du :model', [ + 'model' => Str::singular(__('models.classes.coupons')), + ]), + 'name' => 'start_diffusion_date', + 'required' => false, + 'model' => $couponModel ?? null, + 'helper' => __('validation.date', [ + 'attribute' => __('Date de début de diffusion du :model', [ + 'model' => Str::singular(__('models.classes.coupons')), + ]), + ]), + ]) +
+
+
+
+
+{{-- END DIFFUSION DATE --}} +
+
+ {{ __('validation.attributes.end_diffusion_date') }} +
+
+ @include('bo.partials.inputs.timestamp-date', [ + 'id' => 'couponEndDiffusionDate', + 'label' => __('Date de fin de diffusion du :model', ['model' => Str::singular(__('models.classes.coupons'))]), + 'name' => 'end_diffusion_date', + 'required' => false, + 'model' => $couponModel ?? null, + 'helper' => __('validation.date', [ + 'attribute' => __('Date de fin de diffusion du :model', [ + 'model' => Str::singular(__('models.classes.coupons')), + ]), + ]), + ]) +
+
+
+
+{{-- END VALIDITY DATE --}} +
+
+ {{ __('validation.attributes.end_validity_date') }} +
+
+ @include('bo.partials.inputs.timestamp-date', [ + 'id' => 'couponEndDiffusionDate', + 'label' => __('Date de fin de validité du :model', ['model' => Str::singular(__('models.classes.coupons'))]), + 'name' => 'end_validity_date', + 'required' => false, + 'disabled' => true, + 'model' => $couponModel ?? null, + 'helper' => __('validation.date', [ + 'attribute' => __('Date de fin de validité du :model', [ + 'model' => Str::singular(__('models.classes.coupons')), + ]), + ]), + ]) +
+
+
+
+{{-- STATUS --}} +
+
+ {{ __('validation.attributes.status') }} +
+
+ @include('bo.partials.inputs.tinyint-boolean', [ + 'id' => 'couponStatus', + 'name' => 'status', + 'label' => __('crud.minicrud.visibility_label'), + 'helper' => __('crud.minicrud.visibility_helper'), + 'model' => $couponModel ?? null, + ]) +
+
+
+
diff --git a/resources/views/bo/pages/coupons/index-list.blade.php b/resources/views/bo/pages/coupons/index-list.blade.php new file mode 100644 index 0000000..0afb8c3 --- /dev/null +++ b/resources/views/bo/pages/coupons/index-list.blade.php @@ -0,0 +1,87 @@ +
+ + + @php + $cols = [ + 'name' => __('validation.attributes.name'), + 'brand_id' => __('validation.attributes.brand_id'), + 'status' => __('validation.attributes.status'), + 'process_code' => __('validation.attributes.process_code'), + 'amount' => __('validation.attributes.amount'), + 'valid_on' => __('validation.attributes.valid_on'), + 'validity_period' => __('validation.attributes.validity_period'), + 'legal_notices' => __('validation.attributes.legal_notices'), + 'decoration' => __('validation.attributes.decoration'), + 'gs1' => __('validation.attributes.gs1'), + 'website_picture' => __('validation.attributes.website_picture'), + 'website_picture_mobile' => __('validation.attributes.website_picture_mobile'), + 'limit_qty' => __('validation.attributes.limit_qty'), + 'limit_period' => __('validation.attributes.limit_period'), + 'can_be_used' => __('validation.attributes.can_be_used'), + 'start_diffusion_date' => __('validation.attributes.start_diffusion_date'), + 'end_diffusion_date' => __('validation.attributes.end_diffusion_date'), + 'end_validity_date' => __('validation.attributes.end_validity_date'), + 'updated_at' => __('validation.attributes.updated_at'), + ]; + @endphp + @include('bo.modules.table-col-sorter', ['cols' => $cols, 'mobileHide' => ['status']]) + + + @if ($couponModels->count()) + @foreach ($couponModels as $couponModel) + + + + @include('bo.modules.change-status', [ + 'routeName' => 'coupons', + 'model' => $couponModel, + ]) + + + + + + + + + + + + + + + + + + + @endforeach + @else + + + + @endif + +
{{ $couponModel->name }}{{ $couponModel->brand_id }}{{ $couponModel->process_code }}{{ $couponModel->amount }}€{{ $couponModel->valid_on }}{{ $couponModel->validity_period }} {{ __('jours') }}{{ $couponModel->legal_notices }}{{ $couponModel->decoration }}{{ $couponModel->gs1 }}{{ $couponModel->website_picture }}{{ $couponModel->website_picture_mobile }}{{ $couponModel->limit_qty }}{{ $couponModel->limit_period }} {{ __('jours') }}{{ $couponModel->can_be_used }}{{ $couponModel->start_diffusion_date }}{{ $couponModel->end_diffusion_date }}{{ $couponModel->end_validity_date }} + {{ $couponModel->updated_at->formatLocalized('%d/%m/%Y à %H:%M') }} + +
+ + + + @method('DELETE') + @csrf +
+ +
+
+
+ {{ __('crud.helpers.no_results', ['model' => __('models.classes.coupons')]) }} +
+
diff --git a/resources/views/bo/pages/coupons/index.blade.php b/resources/views/bo/pages/coupons/index.blade.php new file mode 100644 index 0000000..3bcfb41 --- /dev/null +++ b/resources/views/bo/pages/coupons/index.blade.php @@ -0,0 +1,48 @@ +@extends('bo.layout') + +@section('title', __('crud.helpers.list_all', ['model' => __('models.classes.coupons')])) +@section('description', __('crud.helpers.list_all', ['model' => __('models.classes.coupons')])) +@section('breadcrumb', request()->route()->getName()) + +@section('content') +
+
+
+
+
+
+
+

{{ __('models.classes.coupons') }} {{ Str::ucfirst(__('crud.helpers.list')) }}  

+
+
+
+ + + +
+
+
+
+
+ @include('bo.modules.search-bar') +
+
+ {{ $couponModels->withQueryString()->onEachSide(1)->links() }} +
+
+
+
+ @include('bo.pages.coupons.index-list') +
+
+
+
+ {{ $couponModels->withQueryString()->onEachSide(1)->links() }} +
+
+
+
+
+
+
+@endsection diff --git a/resources/views/bo/partials/inputs/timestamp-date.blade.php b/resources/views/bo/partials/inputs/timestamp-date.blade.php new file mode 100644 index 0000000..aabdbde --- /dev/null +++ b/resources/views/bo/partials/inputs/timestamp-date.blade.php @@ -0,0 +1,19 @@ + +{{-- blade-formatter-disable --}} + +{{-- blade-formatter-enable --}} +@include('modules.input-error', [ + 'inputName' => $name, + 'helperId' => "{$id}Help", + 'helper' => $helper, +]) diff --git a/routes/web-bo.php b/routes/web-bo.php index 499638e..96f5a57 100644 --- a/routes/web-bo.php +++ b/routes/web-bo.php @@ -2,6 +2,7 @@ use App\Http\Controllers\Bo\BrandController; use App\Http\Controllers\Bo\CampaignController; +use App\Http\Controllers\Bo\CouponController; use App\Http\Controllers\Bo\DashboardController; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Route; @@ -25,9 +26,16 @@ Route::prefix('bo')->name('bo.')->group( Route::middleware('auth:backend')->group(function () { Route::get('/', [DashboardController::class, 'index'])->name('dashboard'); + + // * BRANDS Route::resource('brands', BrandController::class)->except('show'); - //* CAMPAIGNS + // * COUPONS + Route::resource('coupons', CouponController::class)->except('show'); + Route::post('/coupons/{coupon}/change-published', [CouponController::class, 'changePublished']) + ->name('coupons.change-published'); + + // * CAMPAIGNS Route::resource('campaigns', CampaignController::class)->except('show'); Route::post('/campaigns/{campaign}/change-published', [CampaignController::class, 'changePublished']) ->name('campaigns.change-published');