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))--}}
+
+
+
+
+
+
+
+
+
+ @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
+
+
+
+
+
+
+
+ @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'),
+ ],
+ ]
+ ]
+];