diff --git a/src/Contracts/Requests/CreatePromotion.php b/src/Contracts/Requests/CreatePromotion.php new file mode 100644 index 0000000..8642549 --- /dev/null +++ b/src/Contracts/Requests/CreatePromotion.php @@ -0,0 +1,11 @@ + collect()]); + return view('vanilo::promotion.index', [ + 'promotions' => PromotionProxy::paginate(100), + ]); + } + + public function create() + { + return view('vanilo::promotion.create', [ + 'promotion' => app(Promotion::class), + ]); + } + + public function store(CreatePromotion $request) + { + try { + $promotion = PromotionProxy::create($request->validated()); + + flash()->success(__(':name has been created', ['name' => $promotion->name])); + } catch (\Exception $e) { + flash()->error(__('Error: :msg', ['msg' => $e->getMessage()])); + + return redirect()->back()->withInput(); + } + + return redirect(route('vanilo.admin.promotion.index')); + } + + public function show(Promotion $promotion) + { + return view('vanilo::promotion.show', [ + 'promotion' => $promotion, + ]); + } + + public function edit(Promotion $promotion) + { + return view('vanilo::promotion.edit', [ + 'promotion' => $promotion, + ]); + } + + public function update(Promotion $promotion, UpdatePromotion $request) + { + try { + $promotion->update($request->validated()); + + flash()->success(__(':name has been updated', ['name' => $promotion->name])); + } catch (\Exception $e) { + flash()->error(__('Error: :msg', ['msg' => $e->getMessage()])); + + return redirect()->back()->withInput(); + } + + return redirect(route('vanilo.admin.promotion.index')); + } + + public function destroy(Promotion $promotion) + { + try { + $name = $promotion->name; + $promotion->delete(); + + flash()->warning(__(':name has been deleted', ['name' => $name])); + } catch (\Exception $e) { + flash()->error(__('Error: :msg', ['msg' => $e->getMessage()])); + + return redirect()->back(); + } + + return redirect(route('vanilo.admin.promotion.index')); } } diff --git a/src/Http/Requests/CreatePromotion.php b/src/Http/Requests/CreatePromotion.php new file mode 100644 index 0000000..6afdf22 --- /dev/null +++ b/src/Http/Requests/CreatePromotion.php @@ -0,0 +1,33 @@ + 'required|string|min:1|max:255', + 'description' => 'nullable|string|min:1|max:65000', + 'priority' => 'required|integer|min:0', + 'is_exclusive' => 'required|boolean', + 'usage_limit' => 'nullable|integer|min:0', + 'is_coupon_based' => 'required|boolean', + 'starts_at' => 'required|date|before:ends_at', + 'ends_at' => 'required|date|after:starts_at', + 'applies_to_discounted' => 'required|boolean', + ]; + } + + public function authorize() + { + return true; + } +} diff --git a/src/Http/Requests/UpdatePromotion.php b/src/Http/Requests/UpdatePromotion.php new file mode 100644 index 0000000..9ecd98e --- /dev/null +++ b/src/Http/Requests/UpdatePromotion.php @@ -0,0 +1,32 @@ + 'required|string|min:1|max:255', + 'description' => 'nullable|string|min:1|max:65000', + 'priority' => 'required|integer|min:0', + 'is_exclusive' => 'required|boolean', + 'usage_limit' => 'nullable|integer|min:0', + 'is_coupon_based' => 'required|boolean', + 'starts_at' => 'required|date|before:ends_at', + 'ends_at' => 'required|date|after:starts_at', + 'applies_to_discounted' => 'required|boolean', + ]; + } + + public function authorize() + { + return true; + } +} diff --git a/src/Providers/ModuleServiceProvider.php b/src/Providers/ModuleServiceProvider.php index f78f040..c719d44 100644 --- a/src/Providers/ModuleServiceProvider.php +++ b/src/Providers/ModuleServiceProvider.php @@ -33,6 +33,7 @@ use Vanilo\Admin\Http\Requests\CreateMedia; use Vanilo\Admin\Http\Requests\CreatePaymentMethod; use Vanilo\Admin\Http\Requests\CreateProduct; +use Vanilo\Admin\Http\Requests\CreatePromotion; use Vanilo\Admin\Http\Requests\CreateProperty; use Vanilo\Admin\Http\Requests\CreatePropertyValue; use Vanilo\Admin\Http\Requests\CreatePropertyValueForm; @@ -53,6 +54,7 @@ use Vanilo\Admin\Http\Requests\UpdateOrder; use Vanilo\Admin\Http\Requests\UpdatePaymentMethod; use Vanilo\Admin\Http\Requests\UpdateProduct; +use Vanilo\Admin\Http\Requests\UpdatePromotion; use Vanilo\Admin\Http\Requests\UpdateProperty; use Vanilo\Admin\Http\Requests\UpdatePropertyValue; use Vanilo\Admin\Http\Requests\UpdateShippingMethod; @@ -112,6 +114,8 @@ class ModuleServiceProvider extends BaseBoxServiceProvider UpdateTaxRate::class, CreateLinkForm::class, CreateLink::class, + CreatePromotion::class, + UpdatePromotion::class, ]; public function register(): void diff --git a/src/resources/routes/breadcrumbs.php b/src/resources/routes/breadcrumbs.php index da1d120..ced1ce3 100644 --- a/src/resources/routes/breadcrumbs.php +++ b/src/resources/routes/breadcrumbs.php @@ -256,3 +256,23 @@ $breadcrumbs->parent('vanilo.admin.tax-rate.show', $taxRate); $breadcrumbs->push(__('Edit'), route('vanilo.admin.tax-rate.edit', $taxRate)); }); + +Breadcrumbs::for('vanilo.admin.promotion.index', function ($breadcrumbs) { + $breadcrumbs->parent('home'); + $breadcrumbs->push(__('Promotions'), route('vanilo.admin.promotion.index')); +}); + +Breadcrumbs::for('vanilo.admin.promotion.create', function ($breadcrumbs) { + $breadcrumbs->parent('vanilo.admin.promotion.index'); + $breadcrumbs->push(__('Create')); +}); + +Breadcrumbs::for('vanilo.admin.promotion.show', function ($breadcrumbs, $promotion) { + $breadcrumbs->parent('vanilo.admin.promotion.index'); + $breadcrumbs->push($promotion->name, route('vanilo.admin.promotion.show', $promotion)); +}); + +Breadcrumbs::for('vanilo.admin.promotion.edit', function ($breadcrumbs, $promotion) { + $breadcrumbs->parent('vanilo.admin.promotion.show', $promotion); + $breadcrumbs->push(__('Edit'), route('vanilo.admin.promotion.edit', $promotion)); +}); diff --git a/src/resources/views/promotion/_form.blade.php b/src/resources/views/promotion/_form.blade.php new file mode 100644 index 0000000..d7f07c9 --- /dev/null +++ b/src/resources/views/promotion/_form.blade.php @@ -0,0 +1,193 @@ +{{--@php(dd($errors))--}} + +
+
+ + {!! icon('promotion') !!} + + + {{ Form::text('name', null, [ + 'class' => 'form-control form-control-lg' . ($errors->has('name') ? ' is-invalid' : ''), + 'placeholder' => __('Name of the promotion') + ]) + }} + + @if ($errors->has('name')) +
{{ $errors->first('name') }}
+ @endif +
+
+ +
+
+ + + + + @if ($errors->has('starts_at')) + +
{{ $errors->first('starts_at') }}
+ @endif +
+ +
+ + + + + @if ($errors->has('ends_at')) + +
{{ $errors->first('ends_at') }}
+ @endif +
+
+ +
+
+ +
+ + {!! icon('stock') !!} + + {{ Form::number('priority', null, [ + 'class' => 'form-control' . ($errors->has('priority') ? ' is-invalid' : ''), + 'placeholder' => __('Priority'), + ]) + }} +
+ @if ($errors->has('priority')) + +
{{ $errors->first('priority') }}
+ @endif +
+ +
+ + +
+ + {!! icon('stock') !!} + + {{ Form::number('usage_limit', null, [ + 'class' => 'form-control' . ($errors->has('usage_limit') ? ' is-invalid' : ''), + 'placeholder' => __('Usage Limit'), + 'min' => 0, + 'x-model.number' => 'usageLimit', + 'x-bind:readonly' => 'isUsageIsUnlimited', + 'x-bind:class' => 'isUsageIsUnlimited ? "bg-light text-secondary text-opacity-25" : ""' + ]) + }} +
+ + +
+
+ + @if ($errors->has('usage_limit')) + +
{{ $errors->first('usage_limit') }}
+ @endif +
+
+ +
+
+ + {{ Form::hidden('is_exclusive', 0) }} + +
+ {{ Form::checkbox('is_exclusive', 1, null, ['class' => 'form-check-input', 'id' => 'is-exclusive', 'role' => 'switch']) }} +
+ + @if ($errors->has('is_exclusive')) +
{{ $errors->first('is_exclusive') }}
+ @endif +
+ +
+ + {{ Form::hidden('is_coupon_based', 0) }} + +
+ {{ Form::checkbox('is_coupon_based', 1, null, ['class' => 'form-check-input', 'id' => 'is-coupon-based', 'role' => 'switch']) }} +
+ + @if ($errors->has('is_coupon_based')) +
{{ $errors->first('is_coupon_based') }}
+ @endif +
+ +
+ + {{ Form::hidden('applies_to_discounted', 0) }} + +
+ {{ Form::checkbox('applies_to_discounted', 1, null, ['class' => 'form-check-input', 'id' => 'applies-to-discounted', 'role' => 'switch']) }} +
+ + @if ($errors->has('applies_to_discounted')) +
{{ $errors->first('applies_to_discounted') }}
+ @endif +
+
+ +
+ +
+ + + {{ + Form::textarea( + 'description', + null, + [ + 'class' => 'form-control' . ($errors->has('description') ? ' is-invalid' : ''), + 'placeholder' => __('Type the promotion description here') + ] + ) + }} + + @if ($errors->has('description')) +
{{ $errors->first('description') }}
+ @endif +
+ +@push('scripts') + +@endpush diff --git a/src/resources/views/promotion/create.blade.php b/src/resources/views/promotion/create.blade.php new file mode 100644 index 0000000..8469bdb --- /dev/null +++ b/src/resources/views/promotion/create.blade.php @@ -0,0 +1,23 @@ +@extends('appshell::layouts.private') + +@section('title') + {{ __('Create Promotion') }} +@stop + +@section('content') + {!! Form::model($promotion, ['route' => 'vanilo.admin.promotion.store', 'autocomplete' => 'off']) !!} + + + {{ __('Promotion Details') }} + + @include('vanilo::promotion._form') + + + + + + + + + {!! Form::close() !!} +@stop diff --git a/src/resources/views/promotion/edit.blade.php b/src/resources/views/promotion/edit.blade.php new file mode 100644 index 0000000..a8f35b2 --- /dev/null +++ b/src/resources/views/promotion/edit.blade.php @@ -0,0 +1,28 @@ +@extends('appshell::layouts.private') + +@section('title') + {{ __('Editing') }} {{ $promotion->name }} +@stop + +@section('content') + {!! + Form::model($promotion, [ + 'route' => ['vanilo.admin.promotion.update', $promotion], + 'method' => 'PUT' + ]) + !!} + + + {{ __('Promotion Details') }} + + @include('vanilo::promotion._form') + + + + + + + + + {!! Form::close() !!} +@stop diff --git a/src/resources/views/promotion/index.blade.php b/src/resources/views/promotion/index.blade.php index e3be0cc..528e8fa 100644 --- a/src/resources/views/promotion/index.blade.php +++ b/src/resources/views/promotion/index.blade.php @@ -5,20 +5,20 @@ @stop @push('page-actions') - + @endpush @section('content') @yield('title') -{{-- {!! widget('vanilo::promotion.table')->render($promotions) !!}--}} + {!! widget('vanilo::promotion.table')->render($promotions) !!} -{{-- @if($promotions->hasPages())--}} -{{--
--}} -{{-- --}} -{{-- @endif--}} + @if($promotions->hasPages()) +
+ + @endif @stop diff --git a/src/resources/views/promotion/show.blade.php b/src/resources/views/promotion/show.blade.php new file mode 100644 index 0000000..c55bd84 --- /dev/null +++ b/src/resources/views/promotion/show.blade.php @@ -0,0 +1,59 @@ +@extends('appshell::layouts.private') + +@section('title') + {{ $promotion->name }} +@stop + +@push('page-actions') + +@endpush + +@section('content') +
+
+ @php + $now = now(); + $hasStarted = $now->greaterThanOrEqualTo($promotion->starts_at); + $hasEnded = $now->greaterThanOrEqualTo($promotion->ends_at); + @endphp + + + {{ $promotion->name }} + + + @if (!$hasStarted) + {{ __('Not Started') }} + @elseif ($hasEnded) + {{ __('Ended') }} + @else + {{ __('Active') }} + @endif + + +
+ +
+ + {{ __('Promotion Duration') }} + + + {{ __('Starts at') }} + {{ show_datetime($promotion->starts_at) }} + | + {{ __('Ends at') }} + {{ show_datetime($promotion->ends_at) }} + + +
+ +
+ + {{ __('Usage Count: ') }} {{ $promotion->usage_count }} + + + {{ __('Usage Limit: ') }} {{ $promotion->usage_limit ?? __('Unlimited') }} + + +
+
+@stop diff --git a/src/resources/widgets/promotion/table.widget.php b/src/resources/widgets/promotion/table.widget.php new file mode 100644 index 0000000..0dddb0e --- /dev/null +++ b/src/resources/widgets/promotion/table.widget.php @@ -0,0 +1,81 @@ + AppShellWidgets::TABLE, + 'options' => [ + 'hover' => true, + 'columns' => [ + 'name' => [ + 'widget' => [ + 'type' => 'link', + 'text' => [ + 'bold' => true, + 'text' => '$model.name', + ], + 'url' => [ + 'route' => 'vanilo.admin.promotion.show', + 'parameters' => ['$model'] + ], + 'onlyIfCan' => 'view promotions', + ], + 'title' => __('Name') + ], + 'priority' => [ + 'title' => __('Priority'), + 'widget' => [ + 'type' => 'badge', + 'color' => 'primary', + 'text' => '$model.priority', + ], + ], + 'is_exclusive' => [ + 'title' => __('Exclusive'), + 'valign' => 'middle', + 'widget' => [ + 'type' => 'badge', + 'color' => ['bool' => ['success', 'secondary']], + 'text' => '$model.is_exclusive', + 'modifier' => sprintf('bool2text:%s,%s', __('yes'), __('no')) + ] + ], + 'is_coupon_based' => [ + 'title' => __('Coupon Based'), + 'valign' => 'middle', + 'widget' => [ + 'type' => 'badge', + 'color' => ['bool' => ['success', 'secondary']], + 'text' => '$model.is_coupon_based', + 'modifier' => sprintf('bool2text:%s,%s', __('yes'), __('no')) + ] + ], + 'applies_to_discounted' => [ + 'title' => __('Applies to Discounted'), + 'valign' => 'middle', + 'widget' => [ + 'type' => 'badge', + 'color' => ['bool' => ['success', 'secondary']], + 'text' => '$model.applies_to_discounted', + 'modifier' => sprintf('bool2text:%s,%s', __('yes'), __('no')) + ] + ], + 'starts_at' => [ + 'widget' => [ + 'type' => 'show_datetime', + 'text' => '$model.starts_at' + ], + 'title' => __('Starts at'), + ], + 'ends_at' => [ + 'widget' => [ + 'type' => 'show_datetime', + 'text' => '$model.ends_at' + ], + 'title' => __('Ends at'), + ], + ] + ] +];