mirror of
https://gitlab.com/CIEFWorldwideSdnBhd/exchange-2.0.git
synced 2026-08-19 04:23:55 +00:00
update affiliate program
This commit is contained in:
@@ -2,29 +2,54 @@
|
||||
|
||||
namespace App\Classes\Modules\Affiliate\ControllersLogic;
|
||||
|
||||
use App\Classes\General\Abstracts\AbstractControllerLogic;
|
||||
use App\Classes\Modules\Affiliate\Services\CreatesAffiliate;
|
||||
use App\Classes\Modules\Affiliate\DataTransferObjects\AffiliateObject;
|
||||
use App\Classes\Modules\Affiliate\Standards\Rules\CanCreateAffiliate;
|
||||
use App\Http\Resources\AffiliateResource;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class CreateAffiliateLogic
|
||||
class CreateAffiliateLogic extends AbstractControllerLogic
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function notification(): array
|
||||
{
|
||||
return [
|
||||
'title' => 'Created Affiliate',
|
||||
'message' => 'You have successfully created a new Affiliate Code'
|
||||
];
|
||||
}
|
||||
|
||||
/** @var CanCreateAffiliate */
|
||||
private $canCreateAffiliate;
|
||||
|
||||
/** @var CreatesAffiliate */
|
||||
private $createsAffiliate;
|
||||
|
||||
/**
|
||||
* CreateAffiliateLogic constructor.
|
||||
* @param CanCreateAffiliate $canCreateAffiliate
|
||||
* @param CreatesAffiliate $createsAffiliate
|
||||
*/
|
||||
public function __construct(CreatesAffiliate $createsAffiliate)
|
||||
public function __construct(
|
||||
CanCreateAffiliate $canCreateAffiliate,
|
||||
CreatesAffiliate $createsAffiliate
|
||||
)
|
||||
{
|
||||
$this->canCreateAffiliate = $canCreateAffiliate;
|
||||
$this->createsAffiliate = $createsAffiliate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
* @throws \App\Classes\Exceptions\AccessForbiddenException
|
||||
* @throws \App\Classes\Exceptions\RequestValidationException
|
||||
*/
|
||||
public function execute(Request $request): JsonResponse
|
||||
public function logic(Request $request): JsonResponse
|
||||
{
|
||||
$code = $request->input('code');
|
||||
$code = $code !== null ? (string) $code : '';
|
||||
@@ -43,12 +68,13 @@ class CreateAffiliateLogic
|
||||
$isActive
|
||||
);
|
||||
|
||||
// Validate the affiliate object
|
||||
$this->canCreateAffiliate->passes($object);
|
||||
|
||||
// Create the affiliate
|
||||
$affiliate = $this->createsAffiliate->execute($object);
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Affiliate code created successfully',
|
||||
'data' => $affiliate
|
||||
]);
|
||||
return $this->resourceResponse(new AffiliateResource($affiliate));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,14 +23,8 @@ class CreatesAffiliate extends AbstractUpdateRecord
|
||||
if (empty($code)) {
|
||||
$code = $this->generateUniqueCode();
|
||||
} else {
|
||||
// Validate uniqueness of user-provided code (including soft-deleted records)
|
||||
$existingAffiliate = Affiliate::where('code', $code)
|
||||
->whereNull('deleted_at')
|
||||
->first();
|
||||
|
||||
if ($existingAffiliate) {
|
||||
throw new \App\Classes\Exceptions\MalformedRequestException('Affiliate code already exists. Please choose a different code.');
|
||||
}
|
||||
// Uppercase the code for consistency
|
||||
$code = strtoupper($code);
|
||||
}
|
||||
|
||||
$model = new Affiliate();
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Affiliate\Standards\Rules;
|
||||
|
||||
use App\Classes\General\Abstracts\AbstractRule;
|
||||
use App\Classes\Modules\Affiliate\DataTransferObjects\AffiliateObject;
|
||||
use App\Classes\Modules\Affiliate\Standards\Validators\AffiliateCreateValidation;
|
||||
|
||||
class CanCreateAffiliate extends AbstractRule
|
||||
{
|
||||
/** @var AffiliateCreateValidation */
|
||||
private $affiliateCreateValidation;
|
||||
|
||||
/**
|
||||
* CanCreateAffiliate constructor.
|
||||
* @param AffiliateCreateValidation $affiliateCreateValidation
|
||||
*/
|
||||
public function __construct(AffiliateCreateValidation $affiliateCreateValidation)
|
||||
{
|
||||
$this->affiliateCreateValidation = $affiliateCreateValidation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AffiliateObject $object
|
||||
* @return bool
|
||||
*/
|
||||
protected function authorized($object): bool
|
||||
{
|
||||
// Authorization is handled by policy in controller
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AffiliateObject $object
|
||||
* @return bool
|
||||
* @throws \App\Classes\Exceptions\RequestValidationException
|
||||
*/
|
||||
protected function validators($object): bool
|
||||
{
|
||||
return $this->affiliateCreateValidation->validate($object);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AffiliateObject $object
|
||||
* @return bool
|
||||
*/
|
||||
protected function criteria($object): bool
|
||||
{
|
||||
// No additional criteria needed for affiliate creation
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Affiliate\Standards\Validators;
|
||||
|
||||
use App\Classes\General\Abstracts\AbstractValidation;
|
||||
use App\Classes\Modules\Affiliate\DataTransferObjects\AffiliateObject;
|
||||
use App\Models\Affiliate;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class AffiliateCreateValidation extends AbstractValidation
|
||||
{
|
||||
/**
|
||||
* @param AffiliateObject $object
|
||||
* @return array
|
||||
*/
|
||||
protected function data($object): array
|
||||
{
|
||||
return [
|
||||
'code' => $object->getCode(),
|
||||
'campaign_name' => $object->getCampaignName(),
|
||||
'campaign_description' => $object->getCampaignDescription(),
|
||||
'is_active' => $object->getIsActive(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function rules(): array
|
||||
{
|
||||
return [
|
||||
'code' => [
|
||||
'nullable',
|
||||
'string',
|
||||
'min:3',
|
||||
'max:255',
|
||||
'regex:/^[a-zA-Z0-9_-]+$/',
|
||||
function ($attribute, $value, $fail) {
|
||||
if (!empty($value)) {
|
||||
$uppercaseCode = strtoupper($value);
|
||||
$exists = Affiliate::whereRaw('UPPER(code) = ?', [$uppercaseCode])
|
||||
->whereNull('deleted_at')
|
||||
->exists();
|
||||
|
||||
if ($exists) {
|
||||
$fail('Affiliate code already exists. Please choose a different code.');
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
'campaign_name' => 'required|string|max:255',
|
||||
'campaign_description' => 'nullable|string|max:65535',
|
||||
'is_active' => 'required|boolean',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function messages(): array
|
||||
{
|
||||
return [
|
||||
'code.regex' => 'The affiliate code may only contain letters, numbers, hyphens, and underscores.',
|
||||
'code.min' => 'The affiliate code must be at least 3 characters.',
|
||||
'campaign_name.required' => 'A campaign name is required.',
|
||||
'campaign_name.max' => 'The campaign name cannot exceed 255 characters.',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -3,35 +3,21 @@
|
||||
namespace App\Http\Controllers\Affiliate;
|
||||
|
||||
use App\Classes\Modules\Affiliate\ControllersLogic\CreateAffiliateLogic;
|
||||
use App\Http\Requests\CreateAffiliateRequest;
|
||||
use App\Models\Affiliate;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class CreateAffiliateController
|
||||
{
|
||||
/**
|
||||
* @param CreateAffiliateRequest $request
|
||||
* @param Request $request
|
||||
* @param CreateAffiliateLogic $logic
|
||||
* @return JsonResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function create(CreateAffiliateRequest $request, CreateAffiliateLogic $logic): JsonResponse {
|
||||
$this->authorize('create', Affiliate::class);
|
||||
return $logic->execute($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorize a given action for the current user.
|
||||
*
|
||||
* @param mixed $ability
|
||||
* @param mixed|array $arguments
|
||||
* @return \Illuminate\Auth\Access\Response
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function authorize($ability, $arguments = [])
|
||||
public function create(Request $request, CreateAffiliateLogic $logic): JsonResponse
|
||||
{
|
||||
return app(\Illuminate\Contracts\Auth\Access\Gate::class)->authorize($ability, $arguments);
|
||||
return $logic->execute($request);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ class CreateAffiliateRequest extends FormRequest
|
||||
'min:3',
|
||||
'max:255',
|
||||
'regex:/^[a-zA-Z0-9_-]+$/',
|
||||
'unique:affiliates,code,NULL,id,deleted_at,NULL'
|
||||
],
|
||||
'campaign_name' => 'required|string|max:255',
|
||||
'campaign_description' => 'nullable|string|max:65535',
|
||||
|
||||
@@ -42,6 +42,28 @@
|
||||
</modal-form-component>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row m-b-15">
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<div class="col p-r-0">
|
||||
<div class="form-group no-margin form-group-default b-rad-none">
|
||||
<label class="text-primary">Search</label>
|
||||
<input type="text" class="form-control" v-model="searchTerm" @keyup.enter="search" placeholder="Search by campaign name, code, or description"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto p-l-0 p-r-0">
|
||||
<div class="btn btn-primary b-rad-none" @click="search">
|
||||
<i class="fa fa-search lh-40"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto p-l-0">
|
||||
<div class="btn btn-secondary b-rad-none" @click="reset">
|
||||
<i class="fa fa-remove lh-40"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row m-b-10 b-b p-b-5" style="border-color: #e0e0e0 !important;">
|
||||
<div class="col-2">
|
||||
<div class="font-heading all-caps fs-9 muted">Campaign</div>
|
||||
@@ -71,7 +93,7 @@
|
||||
</div>
|
||||
<div class="row m-b-20">
|
||||
<div class="col">
|
||||
<list-component section="affiliatesSection" :endpoint="route('api.affiliate.list')" :key="refreshKey">
|
||||
<list-component section="affiliatesSection" :endpoint="route('api.affiliate.list')" :key="refreshKey" :options="options">
|
||||
<template slot="list" slot-scope="{data}">
|
||||
<affiliate-single-item-component section="affiliatesSection" :data="data"></affiliate-single-item-component>
|
||||
</template>
|
||||
@@ -89,11 +111,26 @@
|
||||
data() {
|
||||
return {
|
||||
refreshKey: 1,
|
||||
searchTerm: '',
|
||||
options: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
refreshAffiliateCodes() {
|
||||
this.refreshKey++;
|
||||
},
|
||||
search() {
|
||||
if (this.searchTerm && this.searchTerm.trim()) {
|
||||
this.options['affiliate_search'] = this.searchTerm.trim();
|
||||
} else {
|
||||
delete this.options['affiliate_search'];
|
||||
}
|
||||
this.refreshKey++;
|
||||
},
|
||||
reset() {
|
||||
this.searchTerm = '';
|
||||
delete this.options['affiliate_search'];
|
||||
this.refreshKey++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +195,21 @@
|
||||
errorHandler(error) {
|
||||
// Don't reload the list on error - just handle the error message
|
||||
this.isLoading = false;
|
||||
this.errorMessageHandler(error);
|
||||
|
||||
// Parse error message properly
|
||||
let errorMessage = error.message || error;
|
||||
|
||||
// If error.message is a JSON string, parse it
|
||||
if (typeof errorMessage === 'string') {
|
||||
try {
|
||||
const parsed = JSON.parse(errorMessage);
|
||||
errorMessage = parsed.message || errorMessage;
|
||||
} catch (e) {
|
||||
// If parsing fails, use the message as-is
|
||||
}
|
||||
}
|
||||
|
||||
this.errorMessageHandler(errorMessage);
|
||||
},
|
||||
resetForm() {
|
||||
if (this.$v) {
|
||||
|
||||
Reference in New Issue
Block a user