Finish the coupon base.

This commit is contained in:
Ferks-FK 2023-05-18 19:51:10 +00:00 committed by IceToast
parent 490e11572d
commit 640468acbe
13 changed files with 626 additions and 81 deletions

View file

@ -0,0 +1,42 @@
<?php
namespace App\Console\Commands;
use App\Settings\CouponSettings;
use App\Models\Coupon;
use Carbon\Carbon;
use Illuminate\Console\Command;
class DeleteExpiredCoupons extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'coupons:delete';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Delete expired coupons from DB.';
/**
* Execute the console command.
*
* @return int
*/
public function handle(CouponSettings $couponSettings)
{
if ($couponSettings->delete_coupon_on_expires) {
$expired_coupons = Coupon::where('expires_at', '<=', Carbon::now(config('app.timezone')))->get();
foreach ($expired_coupons as $expired_coupon) {
$expired_coupon->delete();
}
}
}
}

View file

@ -16,6 +16,7 @@ class Kernel extends ConsoleKernel
protected $commands = [ protected $commands = [
Commands\ChargeCreditsCommand::class, Commands\ChargeCreditsCommand::class,
Commands\ChargeServers::class, Commands\ChargeServers::class,
Commands\DeleteExpiredCoupons::class,
]; ];
/** /**
@ -29,6 +30,7 @@ class Kernel extends ConsoleKernel
$schedule->command('servers:charge')->everyMinute(); $schedule->command('servers:charge')->everyMinute();
$schedule->command('cp:versioncheck:get')->daily(); $schedule->command('cp:versioncheck:get')->daily();
$schedule->command('payments:open:clear')->daily(); $schedule->command('payments:open:clear')->daily();
$schedule->command('coupons:delete')->daily();
//log cronjob activity //log cronjob activity
$schedule->call(function () { $schedule->call(function () {

View file

@ -0,0 +1,25 @@
<?php
namespace App\Events;
use App\Models\Coupon;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class CouponUsedEvent
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public Coupon $coupon;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Coupon $coupon)
{
$this->coupon = $coupon;
}
}

View file

@ -2,6 +2,7 @@
namespace App\Extensions\PaymentGateways\PayPal; namespace App\Extensions\PaymentGateways\PayPal;
use App\Events\CouponUsedEvent;
use App\Helpers\AbstractExtension; use App\Helpers\AbstractExtension;
use App\Events\PaymentEvent; use App\Events\PaymentEvent;
use App\Events\UserUpdateCreditsEvent; use App\Events\UserUpdateCreditsEvent;
@ -56,6 +57,7 @@ class PayPalExtension extends AbstractExtension
// Partner Discount. // Partner Discount.
$price = $price - ($price * $discount / 100); $price = $price - ($price * $discount / 100);
$price = number_format($price, 2);
// create a new payment // create a new payment
$payment = Payment::create([ $payment = Payment::create([
@ -82,12 +84,12 @@ class PayPalExtension extends AbstractExtension
"reference_id" => uniqid(), "reference_id" => uniqid(),
"description" => $shopProduct->display . ($discount ? (" (" . __('Discount') . " " . $discount . '%)') : ""), "description" => $shopProduct->display . ($discount ? (" (" . __('Discount') . " " . $discount . '%)') : ""),
"amount" => [ "amount" => [
"value" => $shopProduct->getTotalPrice(), "value" => $price,
'currency_code' => strtoupper($shopProduct->currency_code), 'currency_code' => strtoupper($shopProduct->currency_code),
'breakdown' => [ 'breakdown' => [
'item_total' => [ 'item_total' => [
'currency_code' => strtoupper($shopProduct->currency_code), 'currency_code' => strtoupper($shopProduct->currency_code),
'value' => number_format($price, 2), 'value' => $price
], ],
'tax_total' => [ 'tax_total' => [
'currency_code' => strtoupper($shopProduct->currency_code), 'currency_code' => strtoupper($shopProduct->currency_code),
@ -158,6 +160,8 @@ class PayPalExtension extends AbstractExtension
if ($coupon_code) { if ($coupon_code) {
$coupon = new Coupon; $coupon = new Coupon;
$coupon->incrementUses($coupon_code); $coupon->incrementUses($coupon_code);
event(new CouponUsedEvent($coupon));
} }
event(new UserUpdateCreditsEvent($user)); event(new UserUpdateCreditsEvent($user));

View file

@ -4,6 +4,7 @@ namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\Coupon; use App\Models\Coupon;
use App\Settings\LocaleSettings;
use App\Traits\Coupon as CouponTrait; use App\Traits\Coupon as CouponTrait;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Carbon\Carbon; use Carbon\Carbon;
@ -20,11 +21,13 @@ class CouponController extends Controller
* *
* @return \Illuminate\Http\Response * @return \Illuminate\Http\Response
*/ */
public function index() public function index(LocaleSettings $localeSettings)
{ {
$this->checkPermission(self::READ_PERMISSION); $this->checkPermission(self::READ_PERMISSION);
return view('admin.coupons.index'); return view('admin.coupons.index', [
'locale_datatables' => $localeSettings->datatables
]);
} }
/** /**
@ -47,21 +50,12 @@ class CouponController extends Controller
*/ */
public function store(Request $request) public function store(Request $request)
{ {
$coupon_code = $request->input('coupon_code'); $coupon_code = $request->input('code');
$coupon_type = $request->input('coupon_type');
$coupon_value = $request->input('coupon_value');
$coupon_max_uses = $request->input('coupon_uses');
$coupon_datepicker = $request->input('datepicker');
$random_codes_amount = $request->input('range_codes'); $random_codes_amount = $request->input('range_codes');
$rules = [ $rules = $this->requestRules($request);
"coupon_type" => "required|string|in:percentage,amount",
"coupon_uses" => "required|integer|digits_between:1,100",
"coupon_value" => "required|numeric|between:0,100",
"datepicker" => "required|date|after:" . Carbon::now()->format(Coupon::formatDate())
];
// If for some reason you pass both fields at once. // If for some reason you pass both fields at once.
if ($coupon_code && $random_codes_amount) { if ($coupon_code && $random_codes_amount) {
return redirect()->back()->with('error', __('Only one of the two code inputs must be provided.'))->withInput($request->all()); return redirect()->back()->with('error', __('Only one of the two code inputs must be provided.'))->withInput($request->all());
} }
@ -69,12 +63,6 @@ class CouponController extends Controller
return redirect()->back()->with('error', __('At least one of the two code inputs must be provided.'))->withInput($request->all()); return redirect()->back()->with('error', __('At least one of the two code inputs must be provided.'))->withInput($request->all());
} }
if ($coupon_code) {
$rules['coupon_code'] = 'required|string|min:4';
} elseif ($random_codes_amount) {
$rules['range_codes'] = 'required|integer|digits_between:1,100';
}
$request->validate($rules); $request->validate($rules);
if (array_key_exists('range_codes', $rules)) { if (array_key_exists('range_codes', $rules)) {
@ -85,23 +73,17 @@ class CouponController extends Controller
foreach ($coupons as $coupon) { foreach ($coupons as $coupon) {
$data[] = [ $data[] = [
'code' => $coupon, 'code' => $coupon,
'type' => $coupon_type, 'type' => $request->input('type'),
'value' => $coupon_value, 'value' => $request->input('value'),
'max_uses' => $coupon_max_uses, 'max_uses' => $request->input('max_uses'),
'expires_at' => $coupon_datepicker, 'expires_at' => $request->input('expires_at'),
'created_at' => Carbon::now(), // Does not fill in by itself when using the 'insert' method. 'created_at' => Carbon::now(), // Does not fill in by itself when using the 'insert' method.
'updated_at' => Carbon::now() 'updated_at' => Carbon::now()
]; ];
} }
Coupon::insert($data); Coupon::insert($data);
} else { } else {
Coupon::create([ Coupon::create($request->except('_token'));
'code' => $coupon_code,
'type' => $coupon_type,
'value' => $coupon_value,
'max_uses' => $coupon_max_uses,
'expires_at' => $coupon_datepicker,
]);
} }
return redirect()->route('admin.coupons.index')->with('success', __("The coupon's was registered successfully.")); return redirect()->route('admin.coupons.index')->with('success', __("The coupon's was registered successfully."));
@ -126,7 +108,12 @@ class CouponController extends Controller
*/ */
public function edit(Coupon $coupon) public function edit(Coupon $coupon)
{ {
// $this->checkPermission(self::WRITE_PERMISSION);
return view('admin.coupons.edit', [
'coupon' => $coupon,
'expired_at' => $coupon->expires_at ? Carbon::createFromTimestamp($coupon->expires_at) : null
]);
} }
/** /**
@ -138,7 +125,23 @@ class CouponController extends Controller
*/ */
public function update(Request $request, Coupon $coupon) public function update(Request $request, Coupon $coupon)
{ {
// $coupon_code = $request->input('code');
$random_codes_amount = $request->input('range_codes');
$rules = $this->requestRules($request);
// If for some reason you pass both fields at once.
if ($coupon_code && $random_codes_amount) {
return redirect()->back()->with('error', __('Only one of the two code inputs must be provided.'))->withInput($request->all());
}
if (!$coupon_code && !$random_codes_amount) {
return redirect()->back()->with('error', __('At least one of the two code inputs must be provided.'))->withInput($request->all());
}
$request->validate($rules);
$coupon->update($request->except('_token'));
return redirect()->route('admin.coupons.index')->with('success', __('coupon has been updated!'));
} }
/** /**
@ -149,11 +152,87 @@ class CouponController extends Controller
*/ */
public function destroy(Coupon $coupon) public function destroy(Coupon $coupon)
{ {
// $this->checkPermission(self::WRITE_PERMISSION);
$coupon->delete();
return redirect()->back()->with('success', __('coupon has been removed!'));
}
private function requestRules(Request $request)
{
$coupon_code = $request->input('code');
$random_codes_amount = $request->input('range_codes');
$rules = [
"type" => "required|string|in:percentage,amount",
"max_uses" => "required|integer|digits_between:1,100",
"value" => "required|numeric|between:0,100",
"expires_at" => "nullable|date|after:" . Carbon::now()->format(Coupon::formatDate())
];
if ($coupon_code) {
$rules['code'] = "required|string|min:4";
} elseif ($random_codes_amount) {
$rules['range_codes'] = 'required|integer|digits_between:1,100';
}
return $rules;
} }
public function redeem(Request $request) public function redeem(Request $request)
{ {
return $this->validateCoupon($request->user(), $request->input('couponCode'), $request->input('productId')); return $this->validateCoupon($request->user(), $request->input('couponCode'), $request->input('productId'));
} }
public function dataTable()
{
$query = Coupon::query();
return datatables($query)
->addColumn('actions', function(Coupon $coupon) {
return '
<a data-content="'.__('Edit').'" data-toggle="popover" data-trigger="hover" data-placement="top" href="'.route('admin.coupons.edit', $coupon->id).'" class="btn btn-sm btn-info mr-1"><i class="fas fa-pen"></i></a>
<form class="d-inline" onsubmit="return submitResult();" method="post" action="'.route('admin.coupons.destroy', $coupon->id).'">
'.csrf_field().'
'.method_field('DELETE').'
<button data-content="'.__('Delete').'" data-toggle="popover" data-trigger="hover" data-placement="top" class="btn btn-sm btn-danger mr-1"><i class="fas fa-trash"></i></button>
</form>
';
})
->addColumn('status', function(Coupon $coupon) {
$color = 'success';
$status = $coupon->getStatus();
if ($status != __('VALID')) {
$color = 'danger';
}
return '<span class="badge badge-'.$color.'">'.str_replace('_', ' ', $status).'</span>';
})
->editColumn('uses', function (Coupon $coupon) {
return "{$coupon->uses} / {$coupon->max_uses}";
})
->editColumn('value', function (Coupon $coupon) {
if ($coupon->type === 'percentage') {
return $coupon->value . "%";
}
return number_format($coupon->value, 2, '.', '');
})
->editColumn('expires_at', function (Coupon $coupon) {
if (!$coupon->expires_at) {
return __('Never');
}
return Carbon::createFromTimestamp($coupon->expires_at);
})
->editColumn('created_at', function(Coupon $coupon) {
return Carbon::createFromTimeString($coupon->created_at);
})
->editColumn('code', function (Coupon $coupon) {
return "<code>{$coupon->code}</code>";
})
->rawColumns(['actions', 'code', 'status'])
->make();
}
} }

View file

@ -0,0 +1,47 @@
<?php
namespace App\Listeners;
use App\Events\CouponUsedEvent;
use App\Settings\CouponSettings;
use Carbon\Carbon;
class CouponUsed
{
private $delete_coupon_on_expires;
private $delete_coupon_on_uses_reached;
/**
* Create the event listener.
*
* @return void
*/
public function __construct(CouponSettings $couponSettings)
{
$this->delete_coupon_on_expires = $couponSettings->delete_coupon_on_expires;
$this->delete_coupon_on_uses_reached = $couponSettings->delete_coupon_on_uses_reached;
}
/**
* Handle the event.
*
* @param \App\Events\CouponUsedEvent $event
* @return void
*/
public function handle(CouponUsedEvent $event)
{
if ($this->delete_coupon_on_expires) {
if (!is_null($event->coupon->expired_at)) {
if ($event->coupon->expires_at <= Carbon::now()->timestamp) {
$event->coupon->delete();
}
}
}
if ($this->delete_coupon_on_uses_reached) {
if ($event->coupon->uses >= $event->coupon->max_uses) {
$event->coupon->delete();
}
}
}
}

View file

@ -4,6 +4,8 @@ namespace App\Providers;
use App\Events\PaymentEvent; use App\Events\PaymentEvent;
use App\Events\UserUpdateCreditsEvent; use App\Events\UserUpdateCreditsEvent;
use App\Events\CouponUsedEvent;
use App\Listeners\CouponUsed;
use App\Listeners\CreateInvoice; use App\Listeners\CreateInvoice;
use App\Listeners\UnsuspendServers; use App\Listeners\UnsuspendServers;
use App\Listeners\UserPayment; use App\Listeners\UserPayment;
@ -31,6 +33,9 @@ class EventServiceProvider extends ServiceProvider
CreateInvoice::class, CreateInvoice::class,
UserPayment::class, UserPayment::class,
], ],
CouponUsedEvent::class => [
CouponUsed::class
],
SocialiteWasCalled::class => [ SocialiteWasCalled::class => [
// ... other providers // ... other providers
'SocialiteProviders\\Discord\\DiscordExtendSocialite@handle', 'SocialiteProviders\\Discord\\DiscordExtendSocialite@handle',

View file

@ -7,6 +7,8 @@ use Spatie\LaravelSettings\Settings;
class CouponSettings extends Settings class CouponSettings extends Settings
{ {
public ?int $max_uses_per_user; public ?int $max_uses_per_user;
public ?bool $delete_coupon_on_expires;
public ?bool $delete_coupon_on_uses_reached;
public static function group(): string public static function group(): string
{ {
@ -20,7 +22,9 @@ class CouponSettings extends Settings
public static function getValidations() public static function getValidations()
{ {
return [ return [
'max_uses_per_user' => 'required|integer' 'max_uses_per_user' => 'required|integer',
'delete_coupon_on_expires' => 'required|boolean',
'delete_coupon_on_uses_reached' => 'required|boolean',
]; ];
} }
@ -36,7 +40,17 @@ class CouponSettings extends Settings
'max_uses_per_user' => [ 'max_uses_per_user' => [
'label' => 'Max Uses Per User', 'label' => 'Max Uses Per User',
'type' => 'number', 'type' => 'number',
'description' => 'Maximum number of uses that a user can make of the same coupon.', 'description' => 'Maximum number of uses that a user can make of the same coupon.'
],
'delete_coupon_on_expires' => [
'label' => 'Delete Coupon On Expires',
'type' => 'boolean',
'description' => 'Automatically deletes the coupon if it expires.'
],
'delete_coupon_on_uses_reached' => [
'label' => 'Delete Coupon When Max Uses Reached',
'type' => 'boolean',
'description' => 'Delete a coupon as soon as its maximum usage is reached.'
] ]
]; ];
} }

View file

@ -7,10 +7,14 @@ return new class extends SettingsMigration
public function up(): void public function up(): void
{ {
$this->migrator->add('coupon.max_uses_per_user', 1); $this->migrator->add('coupon.max_uses_per_user', 1);
$this->migrator->add('coupon.delete_coupon_on_expires', false);
$this->migrator->add('coupon.delete_coupon_on_uses_reached', false);
} }
public function down(): void public function down(): void
{ {
$this->migrator->delete('coupon.max_uses_per_user'); $this->migrator->delete('coupon.max_uses_per_user');
$this->migrator->delete('coupon.delete_coupon_on_expires');
$this->migrator->delete('coupon.delete_coupon_on_uses_reached');
} }
}; };

View file

@ -201,6 +201,7 @@ Route::middleware(['auth', 'checkSuspended'])->group(function () {
Route::resource('partners', PartnerController::class); Route::resource('partners', PartnerController::class);
//coupons //coupons
Route::get('coupons/datatable', [CouponController::class, 'dataTable'])->name('coupons.datatable');
Route::post('coupons/redeem', [CouponController::class, 'redeem'])->name('coupon.redeem'); Route::post('coupons/redeem', [CouponController::class, 'redeem'])->name('coupon.redeem');
Route::resource('coupons', CouponController::class); Route::resource('coupons', CouponController::class);

View file

@ -84,7 +84,7 @@
@enderror @enderror
</div> </div>
<div id="coupon_code_element" class="form-group"> <div id="coupon_code_element" class="form-group">
<label for="coupon_code"> <label for="code">
{{ __('Coupon Code') }} {{ __('Coupon Code') }}
<i <i
data-toggle="popover" data-toggle="popover"
@ -95,13 +95,13 @@
</label> </label>
<input <input
type="text" type="text"
id="coupon_code" id="code"
name="coupon_code" name="code"
placeholder="SUMMER" placeholder="SUMMER"
class="form-control @error('coupon_code') is-invalid @enderror" class="form-control @error('code') is-invalid @enderror"
value="{{ old('coupon_code') }}" value="{{ old('code') }}"
> >
@error('coupon_code') @error('code')
<div class="text-danger"> <div class="text-danger">
{{ $message }} {{ $message }}
</div> </div>
@ -109,7 +109,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="custom-control mb-3 p-0"> <div class="custom-control mb-3 p-0">
<label for="coupon_type"> <label for="type">
{{ __('Coupon Type') }} {{ __('Coupon Type') }}
<i <i
data-toggle="popover" data-toggle="popover"
@ -119,17 +119,17 @@
</i> </i>
</label> </label>
<select <select
name="coupon_type" name="type"
id="coupon_type" id="type"
class="custom-select @error('coupon_type') is_invalid @enderror" class="custom-select @error('type') is_invalid @enderror"
style="width: 100%; cursor: pointer;" style="width: 100%; cursor: pointer;"
autocomplete="off" autocomplete="off"
required required
> >
<option value="percentage" @if(old('coupon_type') == 'percentage') selected @endif>{{ __('Percentage') }}</option> <option value="percentage" @if(old('type') == 'percentage') selected @endif>{{ __('Percentage') }}</option>
<option value="amount" @if(old('coupon_type') == 'amount') selected @endif>{{ __('Amount') }}</option> <option value="amount" @if(old('type') == 'amount') selected @endif>{{ __('Amount') }}</option>
</select> </select>
@error('coupon_type') @error('type')
<div class="text-danger"> <div class="text-danger">
{{ $message }} {{ $message }}
</div> </div>
@ -138,7 +138,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="input-group d-flex flex-column"> <div class="input-group d-flex flex-column">
<label for="coupon_value"> <label for="value">
{{ __('Coupon Value') }} {{ __('Coupon Value') }}
<i <i
data-toggle="popover" data-toggle="popover"
@ -149,18 +149,18 @@
</label> </label>
<div class="d-flex"> <div class="d-flex">
<input <input
name="coupon_value" name="value"
id="coupon_value" id="value"
type="number" type="number"
step="any" step="any"
min="1" min="1"
max="100" max="100"
class="form-control @error('coupon_value') is-invalid @enderror" class="form-control @error('value') is-invalid @enderror"
value="{{ old('coupon_value') }}" value="{{ old('value') }}"
> >
<span id="input_percentage" class="input-group-text">%</span> <span id="input_percentage" class="input-group-text">%</span>
</div> </div>
@error('coupon_value') @error('value')
<div class="text-danger"> <div class="text-danger">
{{ $message }} {{ $message }}
</div> </div>
@ -168,7 +168,7 @@
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="coupon_uses"> <label for="max_uses">
{{ __('Max uses') }} {{ __('Max uses') }}
<i <i
data-toggle="popover" data-toggle="popover"
@ -178,43 +178,43 @@
</i> </i>
</label> </label>
<input <input
name="coupon_uses" name="max_uses"
id="coupon_uses" id="max_uses"
type="number" type="number"
step="any" step="any"
min="1" min="1"
max="100" max="100"
class="form-control @error('coupon_uses') is-invalid @enderror" class="form-control @error('max_uses') is-invalid @enderror"
value="{{ old('coupon_uses') }}" value="{{ old('max_uses') }}"
> >
@error('coupon_uses') @error('max_uses')
<div class="text-danger"> <div class="text-danger">
{{ $message }} {{ $message }}
</div> </div>
@enderror @enderror
</div> </div>
<div class="d-flex flex-column input-group form-group date" id="datepicker" data-target-input="nearest"> <div class="d-flex flex-column input-group form-group date" id="expires_at" data-target-input="nearest">
<label for="datepicker"> <label for="expires_at">
{{ __('Expires at') }} {{ __('Expires at') }}
<i <i
data-toggle="popover" data-toggle="popover"
data-trigger="hover" data-trigger="hover"
data-content="{{__('The date when the coupon will expire.')}}" data-content="{{__('The date when the coupon will expire (If no date is provided, the coupon never expires).')}}"
class="fas fa-info-circle"> class="fas fa-info-circle">
</i> </i>
</label> </label>
<div class="d-flex"> <div class="d-flex">
<input <input
value="{{old('datepicker')}}" value="{{ old('expires_at') }}"
name="datepicker" name="expires_at"
placeholder="yyyy-mm-dd hh:mm:ss" placeholder="yyyy-mm-dd hh:mm:ss"
type="text" type="text"
class="form-control @error('datepicker') is-invalid @enderror datetimepicker-input" class="form-control @error('expires_at') is-invalid @enderror datetimepicker-input"
data-target="#datepicker" data-target="#expires_at"
/> />
<div <div
class="input-group-append" class="input-group-append"
data-target="#datepicker" data-target="#expires_at"
data-toggle="datetimepicker" data-toggle="datetimepicker"
> >
<div class="input-group-text"> <div class="input-group-text">
@ -222,7 +222,7 @@
</div> </div>
</div> </div>
</div> </div>
@error('datepicker') @error('expires_at')
<div class="text-danger"> <div class="text-danger">
{{ $message }} {{ $message }}
</div> </div>
@ -244,7 +244,7 @@
<script> <script>
$(document).ready(function() { $(document).ready(function() {
$('#datepicker').datetimepicker({ $('#expires_at').datetimepicker({
format: 'Y-MM-DD HH:mm:ss', format: 'Y-MM-DD HH:mm:ss',
icons: { icons: {
time: 'far fa-clock', time: 'far fa-clock',
@ -263,8 +263,8 @@
$('#coupon_code_element').prop('disabled', true).hide() $('#coupon_code_element').prop('disabled', true).hide()
$('#range_codes_element').prop('disabled', false).show() $('#range_codes_element').prop('disabled', false).show()
if ($('#coupon_code').val()) { if ($('#code').val()) {
$('#coupon_code').prop('value', null) $('#code').prop('value', null)
} }
} else { } else {
@ -277,7 +277,7 @@
} }
}) })
$('#coupon_type').change(function() { $('#type').change(function() {
if ($(this).val() == 'percentage') { if ($(this).val() == 'percentage') {
$('#input_percentage').prop('disabled', false).show() $('#input_percentage').prop('disabled', false).show()
} else { } else {

View file

@ -0,0 +1,291 @@
@extends('layouts.main')
@section('content')
<!-- CONTENT HEADER -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1>{{__('Coupon')}}</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{route('home')}}">{{__('Dashboard')}}</a></li>
<li class="breadcrumb-item"><a href="{{route('admin.coupons.index')}}">{{__('Coupon')}}</a>
</li>
<li class="breadcrumb-item"><a class="text-muted"
href="{{route('admin.coupons.edit' , $coupon->id)}}">{{__('Edit')}}</a>
</li>
</ol>
</div>
</div>
</div>
</section>
<!-- END CONTENT HEADER -->
<!-- MAIN CONTENT -->
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="col-lg-6">
<div class="card">
<div class="card-header">
<h5 class="card-title">
<i class="fas fa-money-check-alt mr-2"></i>{{__('Coupon details')}}
</h5>
</div>
<div class="card-body">
<form action="{{ route('admin.coupons.update', $coupon->id) }}" method="POST">
@csrf
@method('PATCH')
<div class="d-flex flex-row-reverse">
<div class="custom-control custom-switch">
<input
type="checkbox"
id="random_codes"
name="random_codes"
class="custom-control-input"
>
<label for="random_codes" class="custom-control-label">
{{ __('Random Codes') }}
<i
data-toggle="popover"
data-trigger="hover"
data-content="{{__('Replace the creation of a single code with several at once with a custom field.')}}"
class="fas fa-info-circle">
</i>
</label>
</div>
</div>
<div id="range_codes_element" style="display: none;" class="form-group">
<label for="range_codes">
{{ __('Range Codes') }}
<i
data-toggle="popover"
data-trigger="hover"
data-content="{{__('Generate a number of random codes.')}}"
class="fas fa-info-circle">
</i>
</label>
<input
type="number"
id="range_codes"
name="range_codes"
step="any"
min="1"
max="100"
class="form-control @error('range_codes') is-invalid @enderror"
>
@error('range_codes')
<div class="text-danger">
{{ $message }}
</div>
@enderror
</div>
<div id="coupon_code_element" class="form-group">
<label for="code">
{{ __('Coupon Code') }}
<i
data-toggle="popover"
data-trigger="hover"
data-content="{{__('The coupon code to be registered.')}}"
class="fas fa-info-circle">
</i>
</label>
<input
type="text"
id="code"
name="code"
placeholder="SUMMER"
class="form-control @error('code') is-invalid @enderror"
value="{{ $coupon->code }}"
>
@error('code')
<div class="text-danger">
{{ $message }}
</div>
@enderror
</div>
<div class="form-group">
<div class="custom-control mb-3 p-0">
<label for="type">
{{ __('Coupon Type') }}
<i
data-toggle="popover"
data-trigger="hover"
data-content="{{__('The way the coupon should discount.')}}"
class="fas fa-info-circle">
</i>
</label>
<select
name="type"
id="type"
class="custom-select @error('type') is_invalid @enderror"
style="width: 100%; cursor: pointer;"
autocomplete="off"
required
>
<option value="percentage" @if($coupon->type == 'percentage') selected @endif>{{ __('Percentage') }}</option>
<option value="amount" @if($coupon->type == 'amount') selected @endif>{{ __('Amount') }}</option>
</select>
@error('type')
<div class="text-danger">
{{ $message }}
</div>
@enderror
</div>
</div>
<div class="form-group">
<div class="input-group d-flex flex-column">
<label for="value">
{{ __('Coupon Value') }}
<i
data-toggle="popover"
data-trigger="hover"
data-content="{{__('The value that the coupon will represent.')}}"
class="fas fa-info-circle">
</i>
</label>
<div class="d-flex">
<input
name="value"
id="value"
type="number"
step="any"
min="1"
max="100"
class="form-control @error('value') is-invalid @enderror"
value="{{ $coupon->value }}"
>
<span id="input_percentage" class="input-group-text">%</span>
</div>
@error('value')
<div class="text-danger">
{{ $message }}
</div>
@enderror
</div>
</div>
<div class="form-group">
<label for="max_uses">
{{ __('Max uses') }}
<i
data-toggle="popover"
data-trigger="hover"
data-content="{{__('The maximum number of times the coupon can be used.')}}"
class="fas fa-info-circle">
</i>
</label>
<input
name="max_uses"
id="max_uses"
type="number"
step="any"
min="1"
max="100"
class="form-control @error('max_uses') is-invalid @enderror"
value="{{ $coupon->max_uses }}"
>
@error('max_uses')
<div class="text-danger">
{{ $message }}
</div>
@enderror
</div>
<div class="d-flex flex-column input-group form-group date" id="expires_at" data-target-input="nearest">
<label for="expires_at">
{{ __('Expires at') }}
<i
data-toggle="popover"
data-trigger="hover"
data-content="{{__('The date when the coupon will expire (If no date is provided, the coupon never expires).')}}"
class="fas fa-info-circle">
</i>
</label>
<div class="d-flex">
<input
value="{{ $expired_at ?? '' }}"
name="expires_at"
placeholder="yyyy-mm-dd hh:mm:ss"
type="text"
class="form-control @error('expires_at') is-invalid @enderror datetimepicker-input"
data-target="#expires_at"
/>
<div
class="input-group-append"
data-target="#expires_at"
data-toggle="datetimepicker"
>
<div class="input-group-text">
<i class="fa fa-calendar"></i>
</div>
</div>
</div>
@error('expires_at')
<div class="text-danger">
{{ $message }}
</div>
@enderror
</div>
<div class="form-group text-right mb-0">
<button type="submit" class="btn btn-primary">
{{__('Submit')}}
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- END CONTENT -->
<script>
$(document).ready(function() {
$('#expires_at').datetimepicker({
format: 'Y-MM-DD HH:mm:ss',
icons: {
time: 'far fa-clock',
date: 'far fa-calendar',
up: 'fas fa-arrow-up',
down: 'fas fa-arrow-down',
previous: 'fas fa-chevron-left',
next: 'fas fa-chevron-right',
today: 'fas fa-calendar-check',
clear: 'far fa-trash-alt',
close: 'far fa-times-circle'
}
});
$('#random_codes').change(function() {
if ($(this).is(':checked')) {
$('#coupon_code_element').prop('disabled', true).hide()
$('#range_codes_element').prop('disabled', false).show()
if ($('#code').val()) {
$('#code').prop('value', null)
}
} else {
$('#coupon_code_element').prop('disabled', false).show()
$('#range_codes_element').prop('disabled', true).hide()
if ($('#range_codes').val()) {
$('#range_codes').prop('value', null)
}
}
})
$('#type').change(function() {
if ($(this).val() == 'percentage') {
$('#input_percentage').prop('disabled', false).show()
} else {
$('#input_percentage').prop('disabled', true).hide()
}
})
})
</script>
@endsection

View file

@ -42,10 +42,12 @@
<table id="datatable" class="table table-striped"> <table id="datatable" class="table table-striped">
<thead> <thead>
<tr> <tr>
<th>{{__('Partner discount')}}</th> <th>{{__('Status')}}</th>
<th>{{__('Registered user discount')}}</th> <th>{{__('Code')}}</th>
<th>{{__('Referral system commission')}}</th> <th>{{__('Value')}}</th>
<th>{{__('Created')}}</th> <th>{{__('Used / Max Uses')}}</th>
<th>{{__('Expires')}}</th>
<th>{{__('Created At')}}</th>
<th>{{__('Actions')}}</th> <th>{{__('Actions')}}</th>
</tr> </tr>
</thead> </thead>
@ -62,4 +64,33 @@
</section> </section>
<!-- END CONTENT --> <!-- END CONTENT -->
<script>
function submitResult() {
return confirm("{{__('Are you sure you wish to delete?')}}") !== false;
}
$(document).ready(function() {
$('#datatable').DataTable({
language: {
url: '//cdn.datatables.net/plug-ins/1.11.3/i18n/{{ $locale_datatables }}.json'
},
processing: true,
serverSide: true,
stateSave: true,
ajax: "{{route('admin.coupons.datatable')}}",
columns: [
{data: 'status'},
{data: 'code'},
{data: 'value'},
{data: 'uses'},
{data: 'expires_at'},
{data: 'created_at'},
{data: 'actions', sortable: false},
],
fnDrawCallback: function( oSettings ) {
$('[data-toggle="popover"]').popover();
}
});
})
</script>
@endsection @endsection