Merge branch 'trunk'

This commit is contained in:
Vincent BRIDIER 2023-07-05 17:05:03 +02:00
commit 1974969ccc
No known key found for this signature in database
GPG key ID: 8571BFA20420D512
403 changed files with 24883 additions and 12117 deletions

5
.browserslistrc Normal file
View file

@ -0,0 +1,5 @@
# Browsers that we support
last 3 version
> 0.3%
not dead # no browsers without security updates

2
.dockerignore Normal file
View file

@ -0,0 +1,2 @@
.git
*.md

View file

@ -13,3 +13,9 @@ trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
[*.vue]
indent_size = 2
[docker-compose.yml]
indent_size = 4

41
.eslintrc.json Normal file
View file

@ -0,0 +1,41 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:vue/vue3-strongly-recommended",
"@vue/eslint-config-typescript"
],
"overrides": [
{
"files": ["*.ts", "*.vue"],
"rules": {
"no-undef": "off"
}
},
{
"files": ["*.vue"],
"rules": {
"indent": "off"
}
}
],
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser",
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": ["vue", "@typescript-eslint"],
"ignorePatterns": ["webpack.mix.js", "**/vendor/*.js", "**/vendor/**/*.js"],
"rules": {
"indent": ["error", 4, { "SwitchCase": 1 }],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "double"],
"semi": ["error", "always"],
"vue/script-indent": ["error", 2, { "baseIndent": 0, "switchCase": 1 }],
"vue/one-component-per-file": "off"
}
}

6
.gitignore vendored
View file

@ -1,6 +1,11 @@
/node_modules
/public/hot
/public/storage
/public/build
/public/medias
/public/uploads
/public/cache
/public/sitemap.xml
/storage/*.key
/vendor
.vscode/
@ -18,3 +23,4 @@ MIG/errors_sql.txt
public/js
public/css
public/assets
auth.json

19
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,19 @@
include:
- project: 'ldc-web/shared-ci'
ref: 'main'
file: 'lint/phpcs7.3.yml'
- project: 'ldc-web/shared-ci'
ref: 'main'
file: 'security/composer7.3.yml'
- project: 'ldc-web/shared-ci'
ref: 'main'
file: 'build/composer7.3.yml'
- project: 'ldc-web/shared-ci'
ref: 'main'
file: 'test/test.yml'
stages:
- lint
- security
- build
- test

4
.gitmodules vendored
View file

@ -1,3 +1,3 @@
[submodule "clients/V2"]
path = clients/V2
url = https://voight001.lesidc.com/crm-client
path = clients/V2
url = git@lab.s-web.fr:ldc-web/projets/crm-client.git

43
.stylelintrc.json Normal file
View file

@ -0,0 +1,43 @@
{
"extends": [
"stylelint-config-prettier",
"stylelint-config-standard-scss",
"stylelint-config-sass-guidelines",
"stylelint-config-idiomatic-order"
],
"plugins": ["stylelint-scss"],
"files": ["**/*.css", "**/*.scss"],
"ignoreFiles": [
"**/vendor/*.css",
"**/vendor/**/*.css",
"**/vendor/*.scss",
"**/vendor/**/*.scss",
"**/vendor/*.sass",
"**/vendor/**/*.sass",
"**/vendor/*.less",
"**/vendor/**/*.less"
],
"customSyntax": "postcss-scss",
"rules": {
"indentation": 4,
"string-quotes": "double",
"selector-max-id": null,
"color-no-invalid-hex": true,
"order/properties-alphabetical-order": null,
"max-nesting-depth": 5,
"selector-max-compound-selectors": 5,
"declaration-colon-space-after": "always-single-line",
"function-parentheses-space-inside": "never-single-line",
"declaration-colon-newline-after": null,
"scss/double-slash-comment-empty-line-before": [
"always",
{
"ignore": [
"between-comments",
"stylelint-commands",
"inside-block"
]
}
]
}
}

26
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,26 @@
{
"phpcs.executablePath": "vendor/bin/phpcs",
"workbench.colorCustomizations": {
"activityBar.activeBackground": "#8dc952",
"activityBar.activeBorder": "#3d7fc2",
"activityBar.background": "#8dc952",
"activityBar.foreground": "#15202b",
"activityBar.inactiveForeground": "#15202b99",
"activityBarBadge.background": "#3d7fc2",
"activityBarBadge.foreground": "#e7e7e7",
"sash.hoverBorder": "#8dc952",
"statusBar.background": "#73b137",
"statusBar.foreground": "#15202b",
"statusBarItem.hoverBackground": "#5a8a2b",
"statusBarItem.remoteBackground": "#73b137",
"statusBarItem.remoteForeground": "#15202b",
"titleBar.activeBackground": "#73b137",
"titleBar.activeForeground": "#15202b",
"titleBar.inactiveBackground": "#73b13799",
"titleBar.inactiveForeground": "#15202b99",
"commandCenter.border": "#15202b99"
},
"peacock.remoteColor": "#73b137",
"intelephense.environment.phpVersion": "7.4"
}

7
Dockerfile Normal file
View file

@ -0,0 +1,7 @@
FROM registry.s-web.fr/ldc-web/projets/ldc-web-images-docker/ldcweb-production-7.3:latest
COPY --chown=www-data:www-data . /app/srcs
COPY --chown=www-data:www-data ./public /app/srcs/public
COPY --chown=www-data:www-data ./vendor /app/srcs/vendor
USER root
RUN chmod -R 755 /app/srcs/storage
USER www-data

View file

@ -0,0 +1,102 @@
<?php
namespace App\Console\Commands;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
/**
* Creates a new user
*/
class CreateAdmin extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'user:create';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a backend admin user';
/**
* Execute the console command.
*
* @return integer
*/
public function handle()
{
$email = null;
while (is_null($email)) {
try {
$tmp = $this->ask('Type the wanted user email', 'equipe.web@ldc.fr');
$validator = Validator::make(['email' => $tmp], [
'email' => 'required|unique:users,email|email:rfc,dns'
]);
$validator->validate();
$email = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid email %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
}
}
$firstname = null;
while (is_null($firstname)) {
try {
$tmp = $this->ask('Type the wanted user firstname', 'Admin');
$validator = Validator::make(['firstname' => $tmp], [
'firstname' => 'required|min:5,max:255'
]);
$validator->validate();
$firstname = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid firstname %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
}
}
$lastname = null;
while (is_null($lastname)) {
try {
$tmp = $this->ask('Type the wanted user lastname', 'Saïtama');
$validator = Validator::make(['lastname' => $tmp], [
'lastname' => 'required|min:5,max:255'
]);
$validator->validate();
$lastname = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid lastname %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
}
}
$password = $this->secret('Type the wanted user password');
$admin = new User([
'lastname' => $lastname,
'firstname' => $firstname,
'email' => $email,
'password' => $password
]);
$admin->saveOrFail();
$this->info('User created.');
return 0;
}
}

View file

@ -1,50 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Hash;
/**
* @codeCoverageIgnore
*/
class UserCreate extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'user:create';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*
* @return integer
*/
public function handle()
{
$firstname = $this->ask('What is the firstname?');
$lastname = $this->ask('What is the lastname?');
$email = $this->ask('What is the email?');
$pwd = $this->secret('What is the password?');
$user = new User();
$user->firstname = $firstname;
$user->lastname = $lastname;
$user->email = $email;
$user->password = Hash::make($pwd);
$user->save();
$this->info('User created.');
return 0;
}
}

View file

@ -16,7 +16,6 @@ use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Kwaadpepper\ExceptionHandler\Exceptions\ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Throwable;
class Handler extends ExceptionHandler
{
@ -56,7 +55,7 @@ class Handler extends ExceptionHandler
* @param \Throwable $e
* @return \Symfony\Component\HttpFoundation\Response
*/
public function render($request, Throwable $e)
public function render($request, \Throwable $e)
{
if (Helper::isRequestForApiV2($request) and $request->isJson()) {
return $this->handleApiV2Exception($request, $e);
@ -142,7 +141,7 @@ class Handler extends ExceptionHandler
// phpcs:ignore Squiz.PHP.DiscouragedFunctions.Found
print_r((string)$e);
}
return $this->getReponse($request, 500, $e->getCode(), $e->getMessage());
return $this->getReponse($request, 500, \intval($e->getCode()), $e->getMessage());
// @codeCoverageIgnoreEnd
}

View file

@ -3,72 +3,160 @@
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Http\Requests\Answers\StoreAnswerRequest;
use App\Http\Requests\Answers\UpdateAnswerRequest;
use App\Models\Answer;
use Illuminate\Database\Eloquent\Builder;
use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
use Kwaadpepper\CrudPolicies\Traits\CrudController;
use App\Models\Question;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
/**
* @codeCoverageIgnore
*/
class AnswerController extends Controller
{
use CrudController {
index as crudIndex;
}
/** @var Log */
public static $modelClass = Answer::class;
/**
* The Bo app layout
* Display a listing of the resource.
*
* @var string
*/
public static $layout = 'layouts.app';
/**
* Event index crud-policies
*
* @param Builder $query
* @return Builder
*/
public function indexQuery(Builder $query): Builder
{
$parent = request()->question;
if ($parent) {
$query->whereHas('question', function ($q) use ($parent) {
return $q->whereId($parent->id);
});
}
return $query;
}
/**
* Overload crud-policies index
*
* @param CrudIndexRequest $request
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function index(CrudIndexRequest $request)
public function index(Request $request)
{
/** @var \Illuminate\View\View */
$view = $this->crudIndex($request);
if ($request->ajax()) {
return $view;
/** @var \App\Models\Question $questionModel */
$questionModel = $request->question;
/** @var \Illuminate\Database\Eloquent\Builder $answers */
$answers = Answer::query()->where('question_id', $questionModel->id);
if ($request->search) {
$this->searchQuery(
$answers,
$request->search,
null,
'answer',
);
}
return view('answer.index', $view->getData());
$this->sortQuery($answers);
$searchFields = trans('Réponse');
$answerModels = $answers->paginate(12);
return view('answers.index', compact('answerModels', 'questionModel', 'searchFields'));
}
/**
* Before store model middleware
* Show the form for creating a new resource.
*
* @param Answer $answer
* @return void
* @param \Illuminate\Http\Request $request
* @param \App\Models\Answer $answer
* @return \Illuminate\Http\Response
*/
public function storeModel(Answer &$answer)
public function create(Request $request, Answer $answer)
{
// * Save relation on create
$answer->question()->associate(request()->question);
/** @var \App\Models\Question $questionModel */
$questionModel = $request->question;
return view('answers.create', [
'answerModel' => $answer,
'questionModel' => $questionModel
]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Answers\StoreAnswerRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreAnswerRequest $request)
{
return DB::transaction(function () use ($request) {
$answer = new Answer();
$answer->fill($request->validated());
if ($answer->saveOrFail()) {
return redirect()
->route('answers.edit', $answer)
->with('success', trans(
':model a été créé.',
['model' => Str::singular(trans('models.classes.answers'))]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été créé.',
['model' => Str::singular(trans('models.classes.answers'))]
));
});
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Question $question
* @param \App\Models\Answer $answers
* @return \Illuminate\Http\Response
*/
public function edit(Question $question, Answer $answers)
{
return view('answers.edit', [
'answerModel' => $answers,
'questionModel' => $question
]);
}
/**
* Update a spacific resource in storage.
*
* @param \App\Http\Requests\Answers\UpdateAnswerRequest $request
* @param \App\Models\Question $question
* @param \App\Models\Answer $answer
* @return \Illuminate\Http\Response
*/
public function update(UpdateAnswerRequest $request, Question $question, Answer $answer)
{
return DB::transaction(function () use ($request, $question, $answer) {
$answer->fill($request->validated());
if ($answer->saveOrFail()) {
return redirect()
->route('questions.answers.edit', ['question' => $question, 'answer' => $answer])
->with('success', trans(
':model a été mis à jour.',
['model' => Str::singular(trans('models.classes.answers'))]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été mis à jour.',
['model' => Str::singular(trans('models.classes.answers'))]
));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Question $question
* @param \App\Models\Answer $answer
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Question $question, Answer $answer): \Illuminate\Http\RedirectResponse
{
if ($answer->deleteOrFail()) {
return redirect()->route('questions.answers.index', $question)
->with('success', trans(
':model a été supprimé.',
['model' => Str::singular(trans('models.classes.answers'))]
));
}
return redirect()->back()
->with('error', trans(
':model n\'a pas pu être supprimé.',
['model' => Str::singular(trans('models.classes.answers'))]
));
}
}

View file

@ -19,8 +19,8 @@ use Illuminate\Support\Facades\Validator;
class ArcadiaController extends Controller
{
/**
* @param Request $request
* @return StatusResource
* @param \Illuminate\Http\Request $request
* @return \App\Http\Resources\StatusResource
*/
public function outgoingMail(Request $request)
{

View file

@ -23,8 +23,8 @@ class CustomerController extends Controller
{
/**
* Store a newly created resource in storage
* @param Request $request
* @return CustomerResource|StatusResource
* @param \Illuminate\Http\Request $request
* @return \App\Http\Resources\CustomerResource|\App\Http\Resources\StatusResource
*/
// phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh, Squiz.Commenting.FunctionComment.Missing
public function store(Request $request)
@ -60,41 +60,45 @@ class CustomerController extends Controller
$customer = new Customer();
$customer->email = $email;
$customer->save();
$singleOptin = SingleOptin::where([['email', $email], ['brand_id', $brandId]])->first();
$singleOptin = SingleOptin::query()->where([
['email', $email], ['brand_id', $brandId]
])->first();
if ($singleOptin) {
$singleOptin->delete();
}
} else {
// Récupère les infos s'ils existent déjà.
$CustomerInfo = CustomerInfo::where([['customer_id', $customer->id], ['brand_id', $brandId]])->first();
$CustomerInfo = CustomerInfo::query()->where([
['customer_id', $customer->id], ['brand_id', $brandId]
])->first();
if ($CustomerInfo) {
return new StatusResource([
'status' => 'KO', 'code' => 103,
'message' => 'User already registered on this site.'
]);
}
}
}//end if
$customerInfos = new CustomerInfo();
$customerInfos->firstname = ucfirst(strtolower($firstname));
$customerInfos->lastname = ucfirst(strtolower($lastname));
$customerInfos->birth_date = !empty($request->birth_date) ?
filter_var($request->birth_date, FILTER_SANITIZE_STRING) : null;
filter_var($request->birth_date, FILTER_SANITIZE_STRING) : null;
$customerInfos->address = !empty($request->address) ?
filter_var($request->address, FILTER_SANITIZE_STRING) : null;
filter_var($request->address, FILTER_SANITIZE_STRING) : null;
$customerInfos->address2 = !empty($request->address2) ?
filter_var($request->address2, FILTER_SANITIZE_STRING) : null;
filter_var($request->address2, FILTER_SANITIZE_STRING) : null;
$customerInfos->phone = !empty($request->phone) ?
filter_var($request->phone, FILTER_SANITIZE_STRING) : null;
filter_var($request->phone, FILTER_SANITIZE_STRING) : null;
try {
$customerInfos->country = Country::make(filter_var($request->country, FILTER_SANITIZE_STRING));
} catch (\Exception $e) {
$customerInfos->country = Country::france();
}
$customerInfos->city = !empty($request->city) ?
filter_var($request->city, FILTER_SANITIZE_STRING) : null;
filter_var($request->city, FILTER_SANITIZE_STRING) : null;
$customerInfos->postcode = !empty($request->postcode) ?
filter_var($request->postcode, FILTER_SANITIZE_STRING) : null;
filter_var($request->postcode, FILTER_SANITIZE_STRING) : null;
$customerInfos->opt_in = $optIn;
$customerInfos->hashcode = $hashcode;
$customerInfos->brand_id = $brandId;
@ -125,10 +129,10 @@ class CustomerController extends Controller
/**
* Display the specified resource.
*
* @param Illuminate\Http\Request $request
* @param mixed $type
* @param mixed $value
* @return CustomerResource | StatusResource
* @param \Illuminate\Http\Request $request
* @param mixed $type
* @param mixed $value
* @return \App\Http\Resources\CustomerResource|\App\Http\Resources\StatusResource
*/
public function show(Request $request, $type, $value)
{
@ -161,8 +165,8 @@ class CustomerController extends Controller
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return CustomerResource|StatusResource
* @param integer $id
* @return \App\Http\Resources\CustomerResource|\App\Http\Resources\StatusResource
*/
// phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded, Squiz.Commenting.FunctionComment.Missing
public function update(Request $request, $id)
@ -276,8 +280,8 @@ class CustomerController extends Controller
/**
* Authenticate the user
* @param Request $request
* @return CustomerResource|StatusResource
* @param \Illuminate\Http\Request $request
* @return \App\Http\Resources\CustomerResource|\App\Http\Resources\StatusResource
*/
public function authenticate(Request $request)
{
@ -327,29 +331,29 @@ class CustomerController extends Controller
]
]
);
}//end if
} //end if
$login->last_connection = now();
$customer->logins()->save($login);
return new CustomerResource($customer, $brandId);
}//end if
} //end if
// "Mot de passe incorrect"
return new StatusResource(['status' => 'KO', 'code' => 117, 'message' => 'Authentication failed.']);
} else {
// "Ce compte n'est pas enregistré pour cette marque"
return new StatusResource(['status' => 'KO', 'code' => 118, 'message' => 'User not found.']);
}//end if
} //end if
} else {
// "Ce compte n'existe pas dans la CRM"
return new StatusResource(['status' => 'KO', 'code' => 119, 'message' => 'User not found.']);
}//end if
} //end if
}
/**
* Change the password of the user for the brand
* @param Request $request
* @param mixed $id
* @return StatusResource
* @param \Illuminate\Http\Request $request
* @param mixed $id
* @return \App\Http\Resources\StatusResource
*/
public function changePassword(Request $request, $id)
{
@ -391,9 +395,9 @@ class CustomerController extends Controller
/**
* Activate the account of the user
* @param Request $request
* @param mixed $id
* @return StatusResource
* @param \Illuminate\Http\Request $request
* @param mixed $id
* @return \App\Http\Resources\StatusResource
*/
public function activateAccount(Request $request, $id)
{
@ -424,8 +428,8 @@ class CustomerController extends Controller
/**
* Delete the user account for the brand
* @param Request $request
* @param mixed $id
* @param \Illuminate\Http\Request $request
* @param mixed $id
*/
// phpcs:disable Squiz.PHP.NonExecutableCode.Unreachable, Squiz.Commenting.FunctionComment.Missing
public function delete(Request $request, $id)
@ -456,9 +460,9 @@ class CustomerController extends Controller
/**
* Change the status of the opt-in for the customer on this brand
* @param Request $request
* @param mixed $id
* @return StatusResource
* @param \Illuminate\Http\Request $request
* @param mixed $id
* @return \App\Http\Resources\StatusResource
*/
public function toggleOptin(Request $request, $id)
{
@ -488,14 +492,14 @@ class CustomerController extends Controller
} else {
// "Cet utilisateur n'existe pas dans la CRM"
return new StatusResource(['status' => 'KO', 'code' => 114, 'message' => 'User not found.']);
}//end if
} //end if
}
/**
* Remove opt-in for the customer on this brand
* @param Request $request
* @param mixed $hashcode
* @return StatusResource
* @param \Illuminate\Http\Request $request
* @param mixed $hashcode
* @return \App\Http\Resources\StatusResource
*/
public function removeOptin(Request $request, $hashcode)
{
@ -527,7 +531,7 @@ class CustomerController extends Controller
}
/**
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return void
*/
// phpcs:ignore Squiz.PHP.NonExecutableCode.Unreachable, Squiz.Commenting.FunctionComment.Missing
@ -549,7 +553,7 @@ class CustomerController extends Controller
// Check si le customer ou l'optin simple existe avant de l'ajouter en optin simple.
if (
!SingleOptin::where([['email', $email], ['brand_id', $brandId]])->exists() &&
!SingleOptin::query()->where([['email', $email], ['brand_id', $brandId]])->exists() &&
!CustomerInfo::join("customers", "customer_infos.customer_id", "=", "customers.id")->where([
['brand_id', $brandId], ['email', $email]
])->exists()

View file

@ -14,10 +14,10 @@ class AnswerController extends Controller
/**
* Store the customer answers
*
* @param StoreCustomerAnswers $request
* @param Service $service
* @param Customer $customer
* @return JsonResponse
* @param \App\Http\Requests\Api\V2\StoreCustomerAnswers $request
* @param \App\Models\Service $service
* @param \App\Models\Customer $customer
* @return \Illuminate\Http\JsonResponse
*/
public function storeCustomerAnswers(
StoreCustomerAnswers $request,

View file

@ -10,7 +10,7 @@ class ApiController extends Controller
/**
* Just returns HTTP 200 response
*
* @return JsonResponse
* @return \Illuminate\Http\JsonResponse
*/
public function ping(): JsonResponse
{

View file

@ -31,8 +31,8 @@ class ArcadiaController extends Controller
/**
* Send arcadia default mail (reclamations)
*
* @param SendArchadiaDefaultMailRequest $request
* @return JsonResponse
* @param \App\Http\Requests\Api\V2\SendArchadiaDefaultMailRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function outgoingMailDefault(SendArchadiaDefaultMailRequest $request): JsonResponse
{
@ -42,8 +42,8 @@ class ArcadiaController extends Controller
/**
* Send arcadia game mail (games)
*
* @param SendArchadiaGameMailRequest $request
* @return JsonResponse
* @param \App\Http\Requests\Api\V2\SendArchadiaGameMailRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function outgoingMailGame(SendArchadiaGameMailRequest $request): JsonResponse
{
@ -54,9 +54,9 @@ class ArcadiaController extends Controller
* Form xml message and save it in the apropriate storage location
* to be picked up by the arcadia job
*
* @param BaseApiRequest $request
* @param ArcadiaMailType $type
* @return JsonResponse
* @param \App\Http\Requests\Api\V2\BaseApiRequest $request
* @param \App\Enums\ArcadiaMailType $type
* @return \Illuminate\Http\JsonResponse
* @throws \Exception If an error occurs while generating and saving the xml file.
*/
private function handleArcadiaMail(BaseApiRequest $request, ArcadiaMailType $type): JsonResponse
@ -115,10 +115,10 @@ class ArcadiaController extends Controller
/**
* render Arcadia xml for order
*
* @param BaseApiRequest $request
* @param ArcadiaMailType $type
* @param ArcadiaOrderType $orderType
* @param array $customerInfo
* @param \App\Http\Requests\Api\V2\BaseApiRequest $request
* @param \App\Enums\ArcadiaMailType $type
* @param \App\Enums\ArcadiaOrderType $orderType
* @param array $customerInfo
* @return string
*/
private function makeXml(

View file

@ -14,6 +14,7 @@ use App\Jobs\UpdateCustomerLastConnection;
use App\Models\Customer;
use App\Models\Service;
use App\Models\SingleOptin;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Resources\Json\JsonResource;
@ -24,11 +25,12 @@ class CustomerController extends Controller
/**
* Creates a new customer for the specified service.
*
* @param StoreCustomerRequest $request
* @param Service $service
* @return JsonResource
* @throws ApiErrorException With ApiError::customerExists if the customer with this email already exists.
* @throws CrmException If saving the customer to DB failed.
* @param \App\Http\Requests\Api\V2\StoreCustomerRequest $request
* @param \App\Models\Service $service
* @return \Illuminate\Http\Resources\Json\JsonResource
* @throws \App\Exceptions\Api\V2\ApiErrorException With ApiError::customerExists if
* the customer with this email already exists.
* @throws \App\Exceptions\Api\V2\CrmException If saving the customer to DB failed.
*/
public function store(StoreCustomerRequest $request, Service $service): JsonResource
{
@ -46,8 +48,10 @@ class CustomerController extends Controller
// * Delete Single optin if exists.
/** @var \App\Models\SingleOptin|null $singleOptin */
$singleOptin = SingleOptin::where('email', $data->email)
->where('brand_id', $service->brand->id)->first();
$singleOptin = SingleOptin::query()->where('email', $data->email)
->whereHas('brand', function (Builder $q) use ($service) {
$q->where('id', $service->brand->id);
})->first();
if ($singleOptin) {
$saved = $singleOptin->delete() && $saved;
}
@ -82,9 +86,9 @@ class CustomerController extends Controller
/**
* Renders the specific customer infos
*
* @param Service $service
* @param Customer $customer
* @return JsonResource
* @param \App\Models\Service $service
* @param \App\Models\Customer $customer
* @return \Illuminate\Http\Resources\Json\JsonResource
*/
public function show(Service $service, Customer $customer): JsonResource
{
@ -97,11 +101,11 @@ class CustomerController extends Controller
/**
* Updates information for a specific customer
*
* @param UpdateCustomerRequest $request
* @param Service $service
* @param Customer $customer
* @return JsonResource
* @throws CrmException If saving the customer to DB failed.
* @param \App\Http\Requests\Api\V2\UpdateCustomerRequest $request
* @param \App\Models\Service $service
* @param \App\Models\Customer $customer
* @return \Illuminate\Http\Resources\Json\JsonResource
* @throws \App\Exceptions\Api\V2\CrmException If saving the customer to DB failed.
*/
public function update(UpdateCustomerRequest $request, Service $service, Customer $customer): JsonResource
{
@ -139,10 +143,10 @@ class CustomerController extends Controller
/**
* Deletes customer information for a specific service
*
* @param Service $service
* @param Customer $customer
* @return JsonResponse
* @throws CrmException If removing the customer off the DB failed.
* @param \App\Models\Service $service
* @param \App\Models\Customer $customer
* @return \Illuminate\Http\JsonResponse
* @throws \App\Exceptions\Api\V2\CrmException If removing the customer off the DB failed.
*/
public function destroy(Service $service, Customer $customer): JsonResponse
{
@ -190,11 +194,14 @@ class CustomerController extends Controller
/**
* Authenticate the user
* @param AuthenticateRequest $request
* @param Service $service
* @return JsonResource
* @throws ApiErrorException With ApiError::authFailed() in case of wrong credentials.
* @throws ApiErrorException With ApiError::customerNotActivated() if customer account is not activated.
*
* @param \App\Http\Requests\Api\V2\AuthenticateRequest $request
* @param \App\Models\Service $service
* @return \Illuminate\Http\Resources\Json\JsonResource
* @throws \App\Exceptions\Api\V2\ApiErrorException With ApiError::authFailed() in case
* of wrong credentials.
* @throws \App\Exceptions\Api\V2\ApiErrorException With ApiError::customerNotActivated() if
* customer account is not activated.
*/
public function authenticate(AuthenticateRequest $request, Service $service): JsonResource
{
@ -218,16 +225,18 @@ class CustomerController extends Controller
return new CustomerResource($service->brand, $customer);
} catch (ModelNotFoundException $e) {
throw new ApiErrorException(ApiError::authFailed());
}//end try
} //end try
}
/**
* Activate the account of the user
* @param Customer $customer
* @param Service $service
*
* @param \App\Models\Customer $customer
* @param \App\Models\Service $service
* @return \Illuminate\Http\JsonResponse
* @throws ApiErrorException With ApiError::customerAlreadyActive if customer account is already active.
* @throws CrmException If saving the customer to DB failed.
* @throws \App\Exceptions\Api\V2\ApiErrorException With ApiError::customerAlreadyActive
* if customer account is already active.
* @throws \App\Exceptions\Api\V2\CrmException If saving the customer to DB failed.
*/
public function activateAccount(Customer $customer, Service $service)
{

View file

@ -6,9 +6,12 @@ use App\Enums\ApiError;
use App\Exceptions\Api\V2\ApiErrorException;
use App\Exceptions\Api\V2\CrmException;
use App\Http\Controllers\Controller;
use App\Http\Requests\Api\V2\BaseApiRequest;
use App\Http\Requests\Api\V2\SingleOptinRequest;
use App\Http\Requests\Api\V2\SingleOptinRemoveWithHashcodeRequest;
use App\Models\CustomerInfo;
use App\Models\Service;
use Illuminate\Http\JsonResponse;
use App\Models\SingleOptin;
class OptinController extends Controller
@ -16,33 +19,26 @@ class OptinController extends Controller
/**
* Creates a new Single optin or destroys it if exists
*
* @param SingleOptinRequest $request
* @param Service $service
* @return JsonResponse
* @throws ApiErrorException With ApiError::singleOptinCustomerExists if this email already has a full account.
* @throws CrmException If the single optin could not be added or removed.
* @param \App\Http\Requests\Api\V2\SingleOptinRequest $request
* @param \App\Models\Service $service
* @return \Illuminate\Http\JsonResponse
* @throws \App\Exceptions\Api\V2\ApiErrorException With ApiError::singleOptinCustomerExists
* if this email already has a full account.
* @throws \App\Exceptions\Api\V2\CrmException If the single optin could not be added or removed.
*/
public function optin(SingleOptinRequest $request, Service $service)
public function optin(SingleOptinRequest $request, Service $service): JsonResponse
{
if (CustomerInfo::getFromEmail($service->brand, $request->email)->exists()) {
throw new ApiErrorException(ApiError::singleOptinCustomerExists());
}
/** @var \App\Models\SingleOptin */
$singleOptin = SingleOptin::where('email', $request->email)
->whereHas('brand', function ($q) use ($service) {
$q->whereId($service->brand->id);
})->firstOrNew();
$singleOptin = SingleOptin::query()->where('email', $request->email)
->whereHas('brand', function ($q) use ($service) {
$q->where('id', $service->brand->id);
})->firstOrNew();
if ($request->opt_in) {
$singleOptin->fill($request->validated());
$singleOptin->brand()->associate($service->brand);
$success = $singleOptin->save();
} else {
$success = $singleOptin->delete() !== false;
}
if (!$success) {
if (!$this->changeOptin($service, $request, \boolval($request->opt_in), $singleOptin)) {
// @codeCoverageIgnoreStart
throw new CrmException('Api failed to update single optin.');
// @codeCoverageIgnoreEnd
@ -50,4 +46,48 @@ class OptinController extends Controller
return response()->json();
}
/**
* Creates a new Single optin or destroys it if exists using hashcode
*
* @param \App\Http\Requests\Api\V2\SingleOptinRemoveWithHashcodeRequest $request
* @param \App\Models\Service $service
* @return \Illuminate\Http\JsonResponse
* @throws \App\Exceptions\Api\V2\CrmException If the single optin could not be removed.
*/
public function optoutWithHashcode(SingleOptinRemoveWithHashcodeRequest $request, Service $service)
{
/** @var \App\Models\SingleOptin */
$singleOptin = SingleOptin::query()->where('hashcode', $request->hashcode)
->whereHas('brand', function ($q) use ($service) {
$q->whereId($service->brand->id);
})->firstOrFail();
if ($singleOptin and !$this->changeOptin($service, $request, \boolval($request->opt_in), $singleOptin)) {
// @codeCoverageIgnoreStart
throw new CrmException('Api failed to update single optin.');
// @codeCoverageIgnoreEnd
}
return response()->json();
}
/**
* Changes the optin state
*
* @param \App\Models\Service $service
* @param \App\Http\Requests\Api\V2\BaseApiRequest $request
* @param boolean $optin
* @param \App\Models\SingleOptin $singleOptin
* @return boolean Returns false in case of failure.
*/
private function changeOptin(Service $service, BaseApiRequest $request, bool $optin, SingleOptin $singleOptin): bool
{
if ($optin) {
$singleOptin->fill($request->validated());
$singleOptin->brand()->associate($service->brand);
return $singleOptin->save();
}
return $singleOptin->delete() !== false;
}
}

View file

@ -16,8 +16,8 @@ class QuestionController extends Controller
/**
* Returns all service questions.
*
* @param Service $service
* @return JsonResource
* @param \App\Models\Service $service
* @return \Illuminate\Http\Resources\Json\ResourceCollection
*/
public function index(Service $service): ResourceCollection
{
@ -27,9 +27,9 @@ class QuestionController extends Controller
/**
* Returns all service questions with user answers for all of them.
*
* @param Service $service
* @param Customer $customer
* @return JsonResource
* @param \App\Models\Service $service
* @param \App\Models\Customer $customer
* @return \Illuminate\Http\Resources\Json\ResourceCollection
*/
public function indexWithResponse(Service $service, Customer $customer): ResourceCollection
{
@ -43,9 +43,9 @@ class QuestionController extends Controller
/**
* Returns all service questions that was not answered by a user.
*
* @param Service $service
* @param Customer $customer
* @return JsonResource
* @param \App\Models\Service $service
* @param \App\Models\Customer $customer
* @return \Illuminate\Http\Resources\Json\JsonResource
*/
public function unAnsweredBy(Service $service, Customer $customer): JsonResource
{

View file

@ -20,9 +20,9 @@ class StatsController extends Controller
/**
* Get specific brand statistics
*
* @param GetBrandStatsRequest $request
* @param Service $service
* @return ResourceCollection
* @param \App\Http\Requests\Api\V2\GetBrandStatsRequest $request
* @param \App\Models\Service $service
* @return \Illuminate\Http\Resources\Json\ResourceCollection
*/
public function brandStats(GetBrandStatsRequest $request, Service $service): ResourceCollection
{
@ -35,9 +35,9 @@ class StatsController extends Controller
/**
* Get statistics for a specific question
*
* @param GetQuestionStatsRequest $request
* @param Question $question
* @return QuestionStatsResource
* @param \App\Http\Requests\Api\V2\GetQuestionStatsRequest $request
* @param \App\Models\Question $question
* @return \App\Http\Resources\Api\V2\QuestionStatsResource
*/
public function questionStats(GetQuestionStatsRequest $request, Question $question): QuestionStatsResource
{
@ -57,10 +57,10 @@ class StatsController extends Controller
/**
* Total réponses par dates
*
* @param Collection $answers
* @param Carbon $start
* @param Carbon $end
* @return Collection
* @param \Illuminate\Support\Collection $answers
* @param \Illuminate\Support\Carbon $start
* @param \Illuminate\Support\Carbon $end
* @return \Illuminate\Support\Collection
*/
private function answersByDays(Collection $answers, Carbon $start, Carbon $end): Collection
{
@ -71,7 +71,7 @@ class StatsController extends Controller
->get()->map(function ($custAnswer) {
return [
'answer_id' => $custAnswer->answer_id,
'created_at' => $custAnswer->created_at->format('Y-m-d'),
'created_at' => $custAnswer->created_at->isoFormat('YYYY-MM-DD'),
];
})->groupBy(['created_at', 'answer_id'])->map(function ($day) {
return $day->map(function ($custAnswers) {
@ -80,7 +80,7 @@ class StatsController extends Controller
});
do {
$currentDate = $start->format('Y-m-d');
$currentDate = $start->isoFormat('YYYY-MM-DD');
/** @var \Illuminate\Support\Collection $answersByDays */
if (!$answersByDays->has($currentDate)) {
$answersByDays->put(

View file

@ -3,42 +3,144 @@
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Http\Requests\Brands\StoreBrandRequest;
use App\Http\Requests\Brands\UpdateBrandRequest;
use App\Models\Brand;
use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
use Kwaadpepper\CrudPolicies\Traits\CrudController;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
/**
* @codeCoverageIgnore
*/
class BrandController extends Controller
{
use CrudController {
index as crudIndex;
}
/** @var Brand */
public static $modelClass = Brand::class;
/**
* The Bo app layout
* Display a listing of the resource.
*
* @var string
*/
public static $layout = 'layouts.app';
/**
* Overload crud-policies index
*
* @param CrudIndexRequest $request
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function index(CrudIndexRequest $request)
public function index(Request $request)
{
/** @var \Illuminate\View\View */
$view = $this->crudIndex($request);
if ($request->ajax()) {
return $view;
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = Brand::query();
if ($request->search) {
$this->searchQuery(
$query,
$request->search,
null,
'name',
);
}
return view('brand.index', $view->getData());
$this->sortQuery($query);
$searchFields = trans('Nom');
$query->withCount(['customerInfos', 'customerInfos as customer_infos_optin_count' => function (Builder $q) {
$q->where('opt_in', true);
}]);
$brandModels = $query->paginate(12);
return view('brands.index', compact('brandModels', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\Brand $brands
* @return \Illuminate\Http\Response
*/
public function create(Brand $brands)
{
return view('brands.create', [
'brandModel' => $brands
]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Brands\StoreBrandRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreBrandRequest $request)
{
return DB::transaction(function () use ($request) {
$brand = new Brand();
$brand->fill($request->validated());
if ($brand->saveOrFail()) {
return redirect()
->route('brands.edit', $brand)
->with('success', trans(
':model a été créé.',
['model' => Str::singular(trans('models.classes.brands'))]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été créé.',
['model' => Str::singular(trans('models.classes.brands'))]
));
});
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Brand $brands
* @return \Illuminate\Http\Response
*/
public function edit(Brand $brands)
{
return view('brands.edit', [
'brandModel' => $brands
]);
}
/**
* Update a spacific resource in storage.
*
* @param \App\Http\Requests\Brands\UpdateBrandRequest $request
* @param \App\Models\Brand $brand
* @return \Illuminate\Http\Response
*/
public function update(UpdateBrandRequest $request, Brand $brand)
{
return DB::transaction(function () use ($request, $brand) {
$brand->fill($request->validated());
if ($brand->saveOrFail()) {
return redirect()
->route('brands.edit', $brand)
->with('success', trans(
':model a été mis à jour.',
['model' => Str::singular(trans('models.classes.brands'))]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été mis à jour.',
['model' => Str::singular(trans('models.classes.brands'))]
));
});
}
/**
* Store a newly created resource in storage.
*
* @return \Illuminate\Http\Response
*/
public function jsonPaginate()
{
return DB::transaction(function () {
/** @var \App\Models\Tag */
$brandModels = Brand::paginate(12);
return \response()->json($brandModels ?? 500);
});
}
}

View file

@ -2,10 +2,13 @@
namespace App\Http\Controllers;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Session;
/**
* @codeCoverageIgnore
@ -15,4 +18,111 @@ class Controller extends BaseController
use AuthorizesRequests;
use DispatchesJobs;
use ValidatesRequests;
/**
* Build Search query on specified fields
* splitting search into words or using whole sentence
*
* @param \Illuminate\Database\Eloquent\Builder $query The eloquent query builder.
* @param string $search The query string.
* @param callable $searchQuery The query string.
* @param string ...$fields Either a simple array or a string
* in case of a string the two fields
* would be concatenate to make the search.
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function searchQuery(
Builder $query,
string $search,
callable $searchQuery = null,
string ...$fields
): Builder {
if (count($fields)) {
// Search using words.
$words = explode(' ', $search);
$query->where(function ($query) use ($searchQuery, $words, $search, $fields) {
foreach ($words as $word) {
if (!strlen($word)) {
continue;
}
$this->searchOnFields($query, $word, $fields);
} //end foreach
// Search whole using whole sentence.
$this->searchOnFields($query, $search, $fields);
if ($searchQuery) {
$searchQuery($query);
}
});
} //end if
return $query;
}
/**
* Build query search on all fields
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $search
* @param array $fields
* @return \Illuminate\Database\Eloquent\Builder
*/
private function searchOnFields(Builder $query, string $search, array $fields): Builder
{
$table = $query->getModel()->getTable();
foreach ($fields as $f) {
$f = str_replace('?', '\?', $f);
if (is_array($f)) {
$query->orWhereRaw(
sprintf("UPPER(CONCAT(%s)) LIKE UPPER(?)", collect($f)->map(function ($q) use ($table) {
return "`$table`.`$q`";
})->implode(', \' \', ')),
['%' . filter_var($search, FILTER_SANITIZE_STRING) . '%']
);
} else {
$query->orWhereRaw(
"UPPER(`$table`.`$f`) LIKE UPPER(?)",
['%' . filter_var($search, FILTER_SANITIZE_STRING) . '%']
);
}
}
return $query;
}
/**
* Sort columns with a query
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param array $ignore
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function sortQuery(Builder $query, array $ignore = []): Builder
{
$rName = request()->route()->getName();
$table = $query->getModel()->getTable();
if (request()->sort_col) {
Session::put("$rName.sort_col", request()->sort_col);
Session::put("$rName.sorted", true);
}
if (request()->sort_way) {
Session::put("$rName.sort_way", request()->sort_way);
Session::put("$rName.sorted", true);
}
if (request()->rst) {
Session::remove("$rName.sorted");
Session::remove("$rName.sort_col");
Session::remove("$rName.sort_way");
}
if (request()->sort_col and request()->sort_way and Schema::hasColumn($table, request()->sort_col)) {
if (
!isset($ignore) or
(isset($ignore) and !in_array(request()->sort_col, $ignore))
) {
$query = $query->orderBy(\sprintf("$table.%s", request()->sort_col), request()->sort_way);
}
} elseif (Schema::hasColumn($table, 'order')) {
$query = $query->orderBy('order', 'ASC');
Session::put("$rName.sort_col", 'order');
Session::put("$rName.sort_way", 'asc');
}
return $query;
}
}

View file

@ -9,13 +9,13 @@ use App\Models\Customer;
use App\Models\CustomerInfo;
use App\Models\CustomerLogin;
use App\Models\SingleOptin;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Str;
use League\Csv\Writer;
use Symfony\Component\HttpFoundation\StreamedResponse;
@ -25,21 +25,10 @@ use Symfony\Component\HttpFoundation\StreamedResponse;
*/
class CustomerController extends Controller
{
/**
* View share crud policies modelClass
*/
public function __construct()
{
$this->middleware(function ($request, $next) {
View::share('modelClass', Customer::class);
return $next($request);
});
}
/**
* View all customers
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
@ -69,7 +58,6 @@ class CustomerController extends Controller
Session::remove("crud.$modelTable.sort_col");
}
if (request()->brand or Session::has("crud.$modelTable.brand")) {
$brand = $request->brand ?? Session::get("crud.$modelTable.brand");
Session::put("crud.$modelTable.brand", $brand);
@ -117,22 +105,25 @@ class CustomerController extends Controller
);
}
/** @var \Illuminate\Pagination\AbstractPaginator */
$customers = $query->paginate(config('crud.paginate', 50));
$searchFields = trans('Email, Prénom, Nom');
return view('customer.index', compact(
/** @var \Illuminate\Pagination\AbstractPaginator */
$customerModels = $query->paginate(50);
return view('customers.index', compact(
'modelClass',
'modelTable',
'customers',
'brands'
'customerModels',
'brands',
'searchFields'
));
}
/**
* Show a specific custmer
*
* @param Customer $customer
* @param Brand $brand
* @param \App\Models\Customer $customer
* @param \App\Models\Brand $brand
* @return \Illuminate\Http\Response
*/
public function show(Customer $customer, Brand $brand)
@ -142,7 +133,7 @@ class CustomerController extends Controller
$customerLogin = CustomerLogin::where('customer_id', $customer->id)
->where('brand_id', $brand->id)->first();
return view('customer.show', compact(
return view('customers.show', compact(
'customerInfos',
'customerLogin'
));
@ -151,8 +142,8 @@ class CustomerController extends Controller
/**
* Delete a specific customer info
*
* @param Customer $customer
* @param Brand $brand
* @param \App\Models\Customer $customer
* @param \App\Models\Brand $brand
* @return \Illuminate\Http\RedirectResponse
*/
public function delete(Customer $customer, Brand $brand)
@ -194,7 +185,7 @@ class CustomerController extends Controller
});
} catch (\ErrorException $e) {
return redirect()->route('customers.index')
->with('error', trans('Le compte n\'as pas pu être supprimé.'));
->with('error', trans('Le compte n\'a pas pu être supprimé.'));
} //end try
return redirect()->route('customers.index')->with('warning', trans('Compte supprimé.'));
@ -208,7 +199,7 @@ class CustomerController extends Controller
public function export()
{
$brands = Brand::all();
return view('customer.export', compact(
return view('customers.export', compact(
'brands'
));
}
@ -216,7 +207,7 @@ class CustomerController extends Controller
/**
* Exports customers following the requested export type
*
* @param CustomerExport $request
* @param \App\Http\Requests\CustomerExport $request
* @return \Symfony\Component\HttpFoundation\StreamedResponse
* @throws \Exception If the request export type in not handled.
*/
@ -241,7 +232,7 @@ class CustomerController extends Controller
'Content-Disposition',
sprintf(
'attachment; filename="Export pour newsletter %s %s.csv"',
Carbon::now()->format('d-m-Y_H-i'),
Carbon::now()->isoFormat('DD-MM-YYYY_HH-mm'),
Str::slug($exportType)
)
);
@ -281,15 +272,16 @@ class CustomerController extends Controller
}
// ! Continue
case CustomerExportType::singleOptin():
SingleOptin::where('brand_id', $request->brand)->get()
->map(function (SingleOptin $singleOptin) use ($w) {
$w->insertOne([
'lastname' => '',
'firstname' => '',
'email' => $singleOptin->email,
'hashcode' => ''
]);
});
SingleOptin::query()->whereHas('brand', function (Builder $q) use ($request) {
$q->where('id', $request->brand);
})->get()->map(function (SingleOptin $singleOptin) use ($w) {
$w->insertOne([
'lastname' => '',
'firstname' => '',
'email' => $singleOptin->email,
'hashcode' => $singleOptin->hashcode
]);
});
break;
default:
throw new \Exception(sprintf('Unhandled CustomerExportType %s', $exportType->label));

View file

@ -23,7 +23,7 @@ class LoginController extends Controller
/**
* Undocumented function
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function authenticate(Request $request)

View file

@ -4,14 +4,14 @@ namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Http\Requests\QuestionExport;
use App\Http\Requests\Questions\StoreQuestionRequest;
use App\Http\Requests\Questions\UpdateQuestionRequest;
use App\Models\Brand;
use App\Models\Question;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
use Kwaadpepper\CrudPolicies\Traits\CrudController;
use League\Csv\Writer;
use Symfony\Component\HttpFoundation\StreamedResponse;
@ -20,48 +20,159 @@ use Symfony\Component\HttpFoundation\StreamedResponse;
*/
class QuestionController extends Controller
{
use CrudController {
index as crudIndex;
}
/** @var Question */
public static $modelClass = Question::class;
/**
* The Bo app layout
* Display a listing of the resource.
*
* @var string
*/
public static $layout = 'layouts.app';
/**
* Event index crud-policies
*
* @param Builder $query
* @return Builder
*/
public function indexQuery(Builder $query): Builder
{
if (request()->brand) {
$query = $query->where('brand_id', '=', request()->brand);
}
return $query;
}
/**
* Overload crud-policies index
*
* @param CrudIndexRequest $request
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function index(CrudIndexRequest $request)
public function index(Request $request)
{
/** @var \Illuminate\View\View */
$view = $this->crudIndex($request);
if ($request->ajax()) {
return $view;
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = Question::query();
if ($request->brand) {
$query = $query->where('brand_id', '=', request()->brand);
}
return view('question.index', $view->getData());
if ($request->search) {
$this->searchQuery(
$query,
$request->search,
null,
'question',
);
}
$this->sortQuery($query);
$searchFields = trans('Question');
/** @var \Illuminate\Pagination\AbstractPaginator */
$questionModels = $query->paginate(12);
return view('questions.index', compact('questionModels', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\Question $question
* @return \Illuminate\Http\Response
*/
public function create(Question $question)
{
$brandModels = Brand::get();
return view('questions.create', [
'questionModel' => $question,
'brandModels' => $brandModels
]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Questions\StoreQuestionRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreQuestionRequest $request)
{
return DB::transaction(function () use ($request) {
$question = new Question();
$question->fill($request->validated());
if ($question->saveOrFail()) {
return redirect()
->route('questions.edit', $question)
->with('success', trans(
':model a été créé.',
['model' => Str::singular(trans('models.classes.questions'))]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été créé.',
['model' => Str::singular(trans('models.classes.questions'))]
));
});
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Question $question
* @return \Illuminate\Http\Response
*/
public function edit(Question $question)
{
$brandModels = Brand::get();
return view('questions.edit', [
'questionModel' => $question,
'brandModels' => $brandModels
]);
}
/**
* Update a spacific resource in storage.
*
* @param \App\Http\Requests\Questions\UpdateQuestionRequest $request
* @param \App\Models\Question $question
* @return \Illuminate\Http\Response
*/
public function update(UpdateQuestionRequest $request, Question $question)
{
return DB::transaction(function () use ($request, $question) {
$question->fill($request->validated());
if ($question->saveOrFail()) {
return redirect()
->route('questions.edit', $question)
->with('success', trans(
':model a été mis à jour.',
['model' => Str::singular(trans('models.classes.questions'))]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été mis à jour.',
['model' => Str::singular(trans('models.classes.questions'))]
));
});
}
/**
* Show the specified resource.
*
* @param \App\Models\Question $question
* @return \Illuminate\Http\Response
*/
public function show(Question $question)
{
return view('questions.show', [
'questionModel' => $question
]);
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Question $question
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Question $question): \Illuminate\Http\RedirectResponse
{
if ($question->deleteOrFail()) {
return redirect()->route('questions.index')
->with('success', trans(
':model a été supprimé.',
['model' => Str::singular(trans('models.classes.questions'))]
));
}
return redirect()->back()
->with('error', trans(
':model n\'a pas pu être supprimé.',
['model' => Str::singular(trans('models.classes.questions'))]
));
}
/**
@ -72,13 +183,13 @@ class QuestionController extends Controller
public function export()
{
$brands = Brand::all();
return view('question.export', compact('brands'));
return view('questions.export', compact('brands'));
}
/**
* Exports questions as csv the requested export type
*
* @param QuestionExport $request
* @param \App\Http\Requests\QuestionExport $request
* @return \Symfony\Component\HttpFoundation\StreamedResponse
*/
public function exportSubmit(QuestionExport $request)
@ -122,7 +233,7 @@ class QuestionController extends Controller
'Content-Disposition',
sprintf(
'attachment; filename="Export pour newsletter %s %s.csv"',
Carbon::now()->format('d-m-Y_H-i'),
Carbon::now()->isoFormat('DD-MM-YYYY_HH-mm'),
Str::slug($brand->name)
)
);
@ -137,6 +248,13 @@ class QuestionController extends Controller
$w->insertOne([]);
}
// * Push empty line if nothing to export
if (empty($results)) {
$w->insertOne([
'Aucune question pour cette marque'
]);
}
/*
We create a callable to output the CSV in chunk
with Symfony StreamResponse you can flush the body content if necessary

View file

@ -3,57 +3,187 @@
namespace App\Http\Controllers;
use App\Http\Requests\GenerateServiceToken;
use App\Http\Requests\Services\StoreServiceRequest;
use App\Http\Requests\Services\UpdateServiceRequest;
use App\Mail\ServiceCredentialRenewed;
use App\Models\Brand;
use App\Models\Service;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
use Kwaadpepper\CrudPolicies\Traits\CrudController;
use Illuminate\Support\Str;
/**
* @codeCoverageIgnore
*/
class ServiceController extends Controller
{
use CrudController {
index as crudIndex;
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = Service::query();
if ($request->brand) {
$query = $query->where('brand_id', '=', request()->brand);
}
if ($request->search) {
$this->searchQuery(
$query,
$request->search,
null,
'name',
);
}
$this->sortQuery($query);
$searchFields = trans('Nom');
$serviceModels = $query->paginate(12);
return view('services.index', compact('serviceModels', 'searchFields'));
}
/** @var Service */
public static $modelClass = Service::class;
/**
* The Bo app layout
* Show the form for creating a new resource.
*
* @var string
* @param \App\Models\Service $service
* @return \Illuminate\Http\Response
*/
public static $layout = 'layouts.app';
/**
* Overload crud-policies index
*
* @param CrudIndexRequest $request
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function index(CrudIndexRequest $request)
public function create(Service $service)
{
/** @var \Illuminate\View\View */
$view = $this->crudIndex($request);
if ($request->ajax()) {
return $view;
$brandModels = Brand::get();
return view('services.create', [
'serviceModel' => $service,
'brandModels' => $brandModels
]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Services\StoreServiceRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreServiceRequest $request)
{
return DB::transaction(function () use ($request) {
$service = new Service();
$service->fill($request->validated());
$service->generateBearerToken();
$this->generateServicePassword($service);
if ($service->saveOrFail()) {
return redirect()
->route('services.edit', $service)
->with('success', trans(
':model a été créé.',
['model' => Str::singular(trans('models.classes.services'))]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été créé.',
['model' => Str::singular(trans('models.classes.services'))]
));
});
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Service $service
* @return \Illuminate\Http\Response
*/
public function edit(Service $service)
{
$brandModels = Brand::get();
return view('services.edit', [
'serviceModel' => $service,
'brandModels' => $brandModels
]);
}
/**
* Update a spacific resource in storage.
*
* @param \App\Http\Requests\Services\UpdateServiceRequest $request
* @param \App\Models\Service $service
* @return \Illuminate\Http\Response
*/
public function update(UpdateServiceRequest $request, Service $service)
{
return DB::transaction(function () use ($request, $service) {
$service->fill($request->validated());
if ($service->saveOrFail()) {
return redirect()
->route('services.edit', $service)
->with('success', trans(
':model a été mis à jour.',
['model' => Str::singular(trans('models.classes.services'))]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été mis à jour.',
['model' => Str::singular(trans('models.classes.services'))]
));
});
}
/**
* Show the specified resource.
*
* @param \App\Models\Service $service
* @return \Illuminate\Http\Response
*/
public function show(Service $service)
{
return view('services.show', [
'serviceModel' => $service
]);
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Service $service
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Service $service): \Illuminate\Http\RedirectResponse
{
if ($service->deleteOrFail()) {
return redirect()->route('services.index')
->with('success', trans(
':model a été supprimé.',
['model' => Str::singular(trans('models.classes.services'))]
));
}
return view('service.index', $view->getData());
return redirect()->back()
->with('error', trans(
':model n\'a pas pu être supprimé.',
['model' => Str::singular(trans('models.classes.services'))]
));
}
/**
* Regen service credentials
*
* @param GenerateServiceToken $request
* @param Service $service
* @param \App\Http\Requests\GenerateServiceToken $request
* @param \App\Models\Service $service
* @return \Illuminate\Http\RedirectResponse
*/
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed, Squiz.Commenting.FunctionComment.Missing
public function generateServicePassword(GenerateServiceToken $request, Service $service)
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed, Squiz.Commenting.FunctionComment.Missing
public function generateServicePassword(Service $service, GenerateServiceToken $request = null)
{
/** @var \App\Models\User $user */
$user = auth()->user();

View file

@ -4,55 +4,67 @@ namespace App\Http\Controllers;
use App\Models\SingleOptin;
use Illuminate\Database\Eloquent\Builder;
use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
use Kwaadpepper\CrudPolicies\Traits\CrudController;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
/**
* @codeCoverageIgnore
*/
class SingleOptinController extends Controller
{
use CrudController {
index as crudIndex;
}
/** @var SingleOptin */
public static $modelClass = SingleOptin::class;
/**
* The Bo app layout
* Display a listing of the resource.
*
* @var string
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public static $layout = 'layouts.app';
/**
* Event index crud-policies
*
* @param Builder $query
* @return Builder
*/
public function indexQuery(Builder $query): Builder
public function index(Request $request)
{
if (request()->brand) {
$query = $query->where('brand_id', request()->brand);
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = SingleOptin::query();
if ($request->brand) {
$query = $query->whereHas('brand', function (Builder $q) {
$q->where('id', request()->brand);
});
}
return $query;
if ($request->search) {
$this->searchQuery(
$query,
$request->search,
null,
'email',
);
}
$this->sortQuery($query);
$searchFields = trans('E-mail');
/** @var \Illuminate\Pagination\AbstractPaginator */
$singleoptinModels = $query->paginate(12);
return view('singleoptins.index', compact('singleoptinModels', 'searchFields'));
}
/**
* Overload crud-policies index
* Remove the specified resource from storage.
*
* @param CrudIndexRequest $request
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
* @param \App\Models\SingleOptin $singleoptin
* @return \Illuminate\Http\RedirectResponse
*/
public function index(CrudIndexRequest $request)
public function destroy(SingleOptin $singleoptin): \Illuminate\Http\RedirectResponse
{
/** @var \Illuminate\View\View */
$view = $this->crudIndex($request);
if ($request->ajax()) {
return $view;
if ($singleoptin->deleteOrFail()) {
return redirect()->route('single_optins.index')
->with('success', trans(
':model a été supprimé.',
['model' => Str::singular(trans('models.classes.singleoptins'))]
));
}
return view('single_optin.index', $view->getData());
return redirect()->back()
->with('error', trans(
':model n\'a pas pu être supprimé.',
['model' => Str::singular(trans('models.classes.singleoptins'))]
));
}
}

View file

@ -21,7 +21,7 @@ class StatsController extends Controller
/**
* Shows stats by brand
*
* @param StatsByBrandRequest $request
* @param \App\Http\Requests\StatsByBrandRequest $request
* @return \Illuminate\Http\Response
*/
public function viewStatsByBrand(StatsByBrandRequest $request)
@ -38,7 +38,7 @@ class StatsController extends Controller
/**
* Exports stats by brand as a csv file
*
* @param StatsByBrandRequest $request
* @param \App\Http\Requests\StatsByBrandRequest $request
* @return \Illuminate\Http\Response
*/
public function exportByBrandCsv(StatsByBrandRequest $request)
@ -62,7 +62,7 @@ class StatsController extends Controller
foreach ($stats as $stat) {
$cols = [
$stat->created_at->format('Y-m-d'),
$stat->created_at->isoFormat('YYYY-MM-DD'),
$stat->brand->name
];
foreach ($columns as $colname) {
@ -82,7 +82,7 @@ class StatsController extends Controller
/**
* Views stats for a specific question
*
* @param StatsByQuestionRequest $request
* @param \App\Http\Requests\StatsByQuestionRequest $request
* @return \Illuminate\Http\Response
*/
public function viewByQuestion(StatsByQuestionRequest $request)
@ -105,7 +105,7 @@ class StatsController extends Controller
/**
* Exports stats for a specific question as csv
*
* @param StatsByQuestionRequest $request
* @param \App\Http\Requests\StatsByQuestionRequest $request
* @return \Illuminate\Http\Response
*/
public function exportByQuestionCsv(StatsByQuestionRequest $request)
@ -146,10 +146,11 @@ class StatsController extends Controller
/**
* Makes a collection of total users answers by day
*
* @param Collection $answers A collection of Answer.
* @param Carbon $start The wanted start period.
* @param Carbon $end The wanted end period.
* @return Collection A collection with date as Y_m_d index with array of answers and their total.
* @param \Illuminate\Support\Collection $answers A collection of Answer.
* @param \Illuminate\Support\Carbon $start The wanted start period.
* @param \Illuminate\Support\Carbon $end The wanted end period.
* @return \Illuminate\Support\Collection A collection with date as Y_m_d index
* with array of answers and their total.
*/
private function answersByDays(Collection $answers, Carbon $start, Carbon $end): Collection
{
@ -160,7 +161,7 @@ class StatsController extends Controller
->get()->map(function ($custAnswer) {
return [
'answer_id' => $custAnswer->answer_id,
'created_at' => $custAnswer->created_at->format('Y-m-d'),
'created_at' => $custAnswer->created_at->isoFormat('YYYY-MM-DD'),
];
})->groupBy(['created_at', 'answer_id'])->map(function ($day) {
return $day->map(function ($custAnswers) {
@ -169,7 +170,7 @@ class StatsController extends Controller
});
do {
$currentDate = $start->format('Y-m-d');
$currentDate = $start->isoFormat('YYYY-MM-DD');
/** @var \Illuminate\Support\Collection $answersByDays */
if (!$answersByDays->has($currentDate)) {
$answersByDays->put(
@ -185,7 +186,7 @@ class StatsController extends Controller
$answers->mapWithKeys(function ($answer) use ($curentAnswers) {
return [$answer->answer =>
$curentAnswers->has($answer->id) ?
$curentAnswers->get($answer->id) : 0];
$curentAnswers->get($answer->id) : 0];
})
);
}

View file

@ -36,6 +36,7 @@ class Kernel extends HttpKernel
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\SecureHeaders::class
],
'api' => [

View file

@ -18,10 +18,12 @@ class ApiCheckTimeStamps
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
* @throws ApiErrorException With ApiError::wrongTimeStamp if timestamp has an incorrect format or is timed out.
* @throws \App\Exceptions\Api\V2\ApiErrorException With ApiError::wrongTimeStamp
* if timestamp has an incorrect
* format or is timed out.
*/
public function handle(Request $request, \Closure $next)
{

View file

@ -15,9 +15,9 @@ class Authenticate extends Middleware
/**
* Handle an incoming request.
*
* @param mixed $request
* @param \Closure $next
* @param mixed ...$guards
* @param mixed $request
* @param \Closure $next
* @param mixed ...$guards
* @return mixed
*
* @throws \Illuminate\Auth\AuthenticationException If authentification fails.

View file

@ -16,8 +16,8 @@ class CheckBrand
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)

View file

@ -0,0 +1,79 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\View;
use Symfony\Component\HttpFoundation\Response;
class SecureHeaders
{
/**
* Enumerate headers which you do not want in your application's responses.
* Great starting point would be to go check out @Scott_Helme's:
* https://securityheaders.com/
*
* @var array
*/
private $unwantedHeaderList = [
'X-Powered-By',
'Server',
];
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
* @return \Symfony\Component\HttpFoundation\Response
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
*/
public function handle(Request $request, Closure $next): Response
{
// phpcs:enable
View::share('nonce', $nonce = \uniqid());
// * For VueJS dev tool to execute
$jsDev = config('app.debug') ? "'unsafe-eval' 'unsafe-inline'" : "'nonce-{$nonce}' 'strict-dynamic'";
$reportUri = Route::has('cspReportUri') ?
\sprintf(' report-uri %s ;', \parse_url(route('cspReportUri'), \PHP_URL_PATH)) : '';
$this->removeUnwantedHeaders($this->unwantedHeaderList);
$response = $next($request);
$response->headers->set('Referrer-Policy', 'no-referrer-when-downgrade');
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('X-XSS-Protection', '1; mode=block');
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains;');
$cspPolicies = [
"manifest-src 'self';",
"connect-src 'self' blob:;",
"img-src * data: blob:;",
"font-src 'self' data: https://fonts.gstatic.com;",
"style-src 'self' 'unsafe-inline';",
"script-src 'self' {$jsDev} " . \implode(' ', [
'https: blob:',
]) . ';'
];
$response->headers->set(
'Content-Security-Policy',
implode(' ', $cspPolicies) . $reportUri
);
$response->headers->set('Feature-Policy', 'fullscreen \'self\';');
return $response;
}
/**
* Remove unwanted headers
*
* @param array $headerList
* @return void
*/
private function removeUnwantedHeaders(array $headerList)
{
foreach ($headerList as $header) {
header_remove($header);
}
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace App\Http\Requests\Answers;
use Illuminate\Foundation\Http\FormRequest;
class StoreAnswerRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'answer' => 'required|string|min:1|max:255',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'answer' => trans('Réponse à une question'),
];
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Http\Requests\Answers;
class UpdateAnswerRequest extends StoreAnswerRequest
{
}

View file

@ -12,13 +12,14 @@ class GetBrandStatsRequest extends BaseApiRequest
* Get the validation rules that apply to the request.
*
* @return array
* @throws ValidationException If given dates are unparsable of period is bigger than 3 months.
* @throws \Illuminate\Validation\ValidationException If given dates are unparsable of
* period is bigger than 3 months.
* @throws \Exception If carbon date processing failed.
*/
public function rules(): array
{
try {
if (Carbon::parse($this->end)->diffInMonths(Carbon::parse($this->start)->addDay()) >= 3) {
if (Carbon::parse($this->end)->floatDiffInMonths(Carbon::parse($this->start)) > 3) {
throw ValidationException::withMessages([
'Vous devez sélectionner une période de maximum 3 mois'
]);

View file

@ -13,13 +13,14 @@ class GetQuestionStatsRequest extends BaseApiRequest
* Get the validation rules that apply to the request.
*
* @return array
* @throws ValidationException If given dates are unparsable of period is bigger than 3 months.
* @throws \Illuminate\Validation\ValidationException If given dates are unparsable of
* period is bigger than 3 months.
* @throws \Exception If carbon date processing failed.
*/
public function rules(): array
{
try {
if (Carbon::parse($this->end)->diffInMonths(Carbon::parse($this->start)->addDay()) >= 3) {
if (Carbon::parse($this->end)->floatDiffInMonths(Carbon::parse($this->start)) > 3) {
throw ValidationException::withMessages([
'Vous devez sélectionner une période de maximum 3 mois'
]);

View file

@ -0,0 +1,18 @@
<?php
namespace App\Http\Requests\Api\V2;
class SingleOptinRemoveWithHashcodeRequest extends BaseApiRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'hashcode' => 'required|string|size:36'
];
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace App\Http\Requests\Brands;
use Illuminate\Foundation\Http\FormRequest;
class StoreBrandRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'name' => 'required|string|min:5|max:255',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'name' => trans('Nom de la marque'),
];
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Http\Requests\Brands;
class UpdateBrandRequest extends StoreBrandRequest
{
}

View file

@ -0,0 +1,48 @@
<?php
namespace App\Http\Requests\Questions;
use App\Enums\QuestionType;
use Illuminate\Foundation\Http\FormRequest;
use Kwaadpepper\Enum\Rules\EnumIsValidRule;
class StoreQuestionRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'question' => 'required|string|min:10|max:255',
'type' => ['required', new EnumIsValidRule(QuestionType::class)],
'brand_id' => 'required|integer|min:0|exists:brands,id',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'question' => trans('Texte de la question'),
'type' => trans('Type de question'),
'brand_id' => trans('Id de la marque'),
];
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Http\Requests\Questions;
class UpdateQuestionRequest extends StoreQuestionRequest
{
}

View file

@ -0,0 +1,44 @@
<?php
namespace App\Http\Requests\Services;
use Illuminate\Foundation\Http\FormRequest;
class StoreServiceRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'name' => 'required|string|min:5|max:255',
'brand_id' => 'required|integer|min:0|exists:brands,id',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'name' => trans('Nom du service'),
'brand_id' => trans('Id de la marque'),
];
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Http\Requests\Services;
class UpdateServiceRequest extends StoreServiceRequest
{
}

View file

@ -26,7 +26,7 @@ class StatsByBrandRequest extends FormRequest
* Get the validation rules that apply to the request.
*
* @return array
* @throws ValidationException If given dates are unparsable of period is bigger than 1 year.
* @throws \Illuminate\Validation\ValidationException If given dates are unparsable of period is bigger than 1 year.
* @throws \Exception If carbon date processing failed.
*/
public function rules()
@ -51,7 +51,7 @@ class StatsByBrandRequest extends FormRequest
'start' => 'required|date|date_format:Y-m-d',
'end' => 'required|date|date_format:Y-m-d|after_or_equal:start',
];
}//end if
} //end if
return [];
}
}

View file

@ -26,7 +26,8 @@ class StatsByQuestionRequest extends FormRequest
* Get the validation rules that apply to the request.
*
* @return array
* @throws ValidationException If given dates are unparsable of period is bigger than 1 year.
* @throws \Illuminate\Validation\ValidationException If given dates are unparsable of period
* is bigger than 1 year.
* @throws \Exception If carbon date processing failed.
*/
public function rules()
@ -51,7 +52,7 @@ class StatsByQuestionRequest extends FormRequest
'start' => 'required|date|date_format:Y-m-d',
'end' => 'required|date|date_format:Y-m-d|after_or_equal:start',
];
}//end if
} //end if
return [];
}
}

View file

@ -8,15 +8,15 @@ use stdClass;
class ApiResponse extends JsonResource
{
/** @var stdClass */
/** @var \stdClass */
protected $data;
/** @var \App\Enums\ApiStatus */
protected $status;
/**
* @param stdClass $data
* @param ApiStatus $status
* @param \stdClass $data
* @param \App\Enums\ApiStatus $status
*/
public function __construct(stdClass $data = null, ApiStatus $status)
{

View file

@ -19,7 +19,7 @@ class BrandStatsResource extends JsonResourceCollection
return [
'stats' => $this->collection->map(function (BrandStat $brandStat) {
return [
'date' => $brandStat->created_at->format('Y-m-d'),
'date' => $brandStat->created_at->isoFormat('YYYY-MM-DD'),
'active' => $brandStat->active,
'inactive' => $brandStat->inactive,
'old_accounts' => $brandStat->old_accounts,

View file

@ -38,8 +38,8 @@ class CustomerResource extends JsonResource
/**
* CustomerResource
*
* @param Brand $brand
* @param Customer $customer
* @param \App\Models\Brand $brand
* @param \App\Models\Customer $customer
*/
public function __construct(Brand $brand, Customer $customer)
{

View file

@ -29,7 +29,7 @@ class SetServiceStats implements ShouldQueue
/**
* Create a new job instance.
*
* @param Service $service
* @param \App\Models\Service $service
* @return void
*/
public function __construct(Service $service)

View file

@ -9,6 +9,7 @@ use App\Models\CustomerLogin;
use App\Models\SingleOptin;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping;
@ -41,7 +42,7 @@ class StatJob implements ShouldQueue
/**
* Create a new job instance.
*
* @param Brand $brand
* @param \App\Models\Brand $brand
* @return void
*/
public function __construct(Brand $brand)
@ -57,17 +58,29 @@ class StatJob implements ShouldQueue
public function handle()
{
$stats = new BrandStat([
'active' => CustomerLogin::whereBrandId($this->brand->id)
->whereDate('last_connection', '<', Carbon::now()->subMonths(6))->count(),
'inactive' => CustomerLogin::whereBrandId($this->brand->id)
'active' => CustomerLogin::query()->whereHas('brand', function (Builder $q) {
$q->where('id', $this->brand->id);
})->whereDate('last_connection', '<', Carbon::now()->subMonths(6))->count(),
'inactive' => CustomerLogin::query()->whereHas('brand', function (Builder $q) {
$q->where('id', $this->brand->id);
})
->whereDate('last_connection', '>', Carbon::now()->subMonths(6))->count(),
'new_accounts' => CustomerLogin::whereBrandId($this->brand->id)
'new_accounts' => CustomerLogin::query()->whereHas('brand', function (Builder $q) {
$q->where('id', $this->brand->id);
})
->whereDate('created_at', '<', Carbon::now()->subMonths(6))->count(),
'old_accounts' => CustomerLogin::whereBrandId($this->brand->id)
'old_accounts' => CustomerLogin::query()->whereHas('brand', function (Builder $q) {
$q->where('id', $this->brand->id);
})
->whereDate('created_at', '>', Carbon::now()->subMonths(6))->count(),
'total_accounts' => CustomerLogin::whereBrandId($this->brand->id)->count(),
'total_opt_in' => CustomerInfo::whereBrandId($this->brand->id)
->whereOptIn(true)->count() + SingleOptin::whereBrandId($this->brand->id)->count()
'total_accounts' => CustomerLogin::query()->whereHas('brand', function (Builder $q) {
$q->where('id', $this->brand->id);
})->count(),
'total_opt_in' => CustomerInfo::query()->whereHas('brand', function (Builder $q) {
$q->where('id', $this->brand->id);
})->whereOptIn(true)->count() + SingleOptin::query()->whereHas('brand', function (Builder $q) {
$q->where('id', $this->brand->id);
})->count()
]);
$stats->brand()->associate($this->brand);
$stats->saveOrFail();

View file

@ -33,9 +33,9 @@ class UpdateCustomerLastConnection implements ShouldQueue
/**
* Create a new job instance.
*
* @param Customer $customer
* @param CustomerLogin $customerLogin
* @param Carbon $date
* @param \App\Models\Customer $customer
* @param \App\Models\CustomerLogin $customerLogin
* @param \Illuminate\Support\Carbon $date
* @return void
*/
public function __construct(

View file

@ -24,7 +24,7 @@ class Helper
/**
* Tells if the request is within apiV2 routes
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return boolean
*/
public static function isRequestForApiV2(Request $request)
@ -38,7 +38,7 @@ class Helper
/**
* Tells if the request is within apiV1 routes
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return boolean
*/
public static function isRequestForApiV1(Request $request): bool
@ -52,7 +52,7 @@ class Helper
/**
* Try to get current route name
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return string|null
*/
public static function getRouteName(Request $request)
@ -63,7 +63,7 @@ class Helper
/**
* Get the bearer token from request
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return string|null
*/
public static function getBearerTokenFromRequest(Request $request): ?string
@ -84,7 +84,7 @@ class Helper
/**
* Try to get current route prefix
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return string|null
*/
public static function getRoutePrefix(Request $request)
@ -96,7 +96,7 @@ class Helper
/**
* Try to get route from request
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Routing\Route|null
*/
public static function getRoute(Request $request)
@ -125,7 +125,7 @@ class Helper
* @param string $method
* @param array $parameters
* @return string request signature
* @throws CrmException If an error occurs.
* @throws \App\Exceptions\Api\V2\CrmException If an error occurs.
*/
public static function signRequest(string $secret, string $uri, string $method, array $parameters): string
{

View file

@ -10,6 +10,7 @@ use App\Models\CustomerLogin;
use App\Models\SingleOptin;
use Carbon\Exceptions\InvalidFormatException;
use Database\migrations\LeGaulois2016Migration;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Hash;
@ -21,14 +22,17 @@ use Illuminate\Support\Str;
*/
class MigrateOldData extends LeGaulois2016Migration
{
/** @var Brand */
/** @var \App\Models\Brand */
private $brand;
/** @var string */
private $table;
/**
* runMigration
*
* @param Brand $brand
* @param string $table
* @param \App\Models\Brand $brand
* @param string $table
* @return void
*/
public function runMigration(Brand $brand, string $table)
@ -82,8 +86,10 @@ class MigrateOldData extends LeGaulois2016Migration
// * Just save Single optin.
if (empty($first_name) && empty($last_name)) {
$sglOptin = SingleOptin::where('email', strtolower($email))
->where('brand_id', $this->brand->id)->firstOrNew();
$sglOptin = SingleOptin::query()->where('email', strtolower($email))
->whereHas('brand', function (Builder $q) {
$q->where('id', $this->brand->id);
})->firstOrNew();
$sglOptin->email = strtolower($email);
$sglOptin->brand_id = $this->brand->id;
$sglOptin->save();

View file

@ -28,8 +28,8 @@ class ServiceCredentialRenewed extends Mailable
/**
* Create a new message instance.
*
* @param Service $service
* @param User $user
* @param \App\Models\Service $service
* @param \App\Models\User $user
* @return void
*/
public function __construct(Service $service, User $user)

View file

@ -4,21 +4,10 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Kwaadpepper\CrudPolicies\Enums\CrudAction;
use Kwaadpepper\CrudPolicies\Enums\CrudType;
use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
class Answer extends Model
{
use HasFactory;
use IsCrudModel;
/** @var string */
protected $crudLabelColumn = 'answer';
/** @var string */
protected $crudValueColumn = 'id';
/**
* The attributes that are mass assignable.
@ -29,32 +18,6 @@ class Answer extends Model
'answer'
];
/**
* Answer
*
* @param array $attributes
*/
public function __construct(array $attributes = [])
{
self::$editableProperties = [
'answer' => [
'label' => Str::ucfirst(trans('models.common.answer')),
'type' => CrudType::string(),
'validate' => [
'min:1',
'max:255'
],
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
]
];
parent::__construct($attributes);
}
// * RELATIONS
/**

View file

@ -4,24 +4,13 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Kwaadpepper\CrudPolicies\Enums\CrudAction;
use Kwaadpepper\CrudPolicies\Enums\CrudType;
use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
use Rennokki\QueryCache\Traits\QueryCacheable;
class Brand extends Model
{
use IsCrudModel;
use HasFactory;
use QueryCacheable;
/** @var string */
protected $crudLabelColumn = 'name';
/** @var string */
protected $crudValueColumn = 'id';
/**
* The timeout cache for this model
*
@ -47,22 +36,6 @@ class Brand extends Model
{
$this->cacheFor = config('api.servicesDBCache') * 60;
self::$editableProperties = [
'name' => [
'label' => Str::ucfirst(trans('models.common.name')),
'type' => CrudType::string(),
'validate' => [
'min:5',
'max:255'
],
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
]
];
parent::__construct($attributes);
}

View file

@ -3,7 +3,6 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
class BrandStat extends Model
{
@ -41,9 +40,9 @@ class BrandStat extends Model
*/
protected static function booted()
{
static::creating(function (BrandStat $stat) {
if (!$stat->created_at) {
$stat->created_at = Carbon::now();
static::creating(function (self $model) {
if (!$model->created_at) {
$model->created_at = now();
}
});
}

View file

@ -60,7 +60,7 @@ class Customer extends Model
/**
* Customer Brand belongsToMany relation
*
* @return \Illuminate\Database\Eloquent\Relations\belongsToMany
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function brands()
{
@ -84,8 +84,8 @@ class Customer extends Model
* or create a New one if $firstOrNewOrFail is 1
* throw model not found exception if firstOrNewOrFail = 2
*
* @param Brand $brand
* @param integer $firstOrNewOrFail
* @param \App\Models\Brand $brand
* @param integer $firstOrNewOrFail
* @return \Illuminate\Database\Eloquent\Builder
*/
public function info(Brand $brand, int $firstOrNewOrFail = self::FIRST_OR_NULL): ?CustomerInfo
@ -98,10 +98,10 @@ class Customer extends Model
}
}
}
$query = CustomerInfo::whereHas('brand', function (Builder $query) use ($brand) {
$query->where('id', $brand->id);
})->whereHas('customer', function (Builder $query) {
$query->where('id', $this->id);
$query = CustomerInfo::query()->whereHas('brand', function (Builder $q) use ($brand) {
$q->where('id', $brand->id);
})->whereHas('customer', function (Builder $q) {
$q->where('id', $this->id);
});
if ($firstOrNewOrFail === self::FIRST_OR_NEW) {
return $query->firstOrNew();
@ -117,8 +117,8 @@ class Customer extends Model
* or create a New one if $firstOrNewOrFail is 1
* throw model not found exception if firstOrNewOrFail = 2
*
* @param Brand $brand
* @param integer $firstOrNewOrFail
* @param \App\Models\Brand $brand
* @param integer $firstOrNewOrFail
* @return \Illuminate\Database\Eloquent\Builder
*/
public function login(Brand $brand, int $firstOrNewOrFail = self::FIRST_OR_NULL): ?CustomerLogin
@ -131,10 +131,10 @@ class Customer extends Model
}
}
}
$query = CustomerLogin::whereHas('brand', function (Builder $query) use ($brand) {
$query->where('id', $brand->id);
})->whereHas('customer', function (Builder $query) use ($brand) {
$query->where('id', $this->id);
$query = CustomerLogin::query()->whereHas('brand', function (Builder $q) use ($brand) {
$q->where('id', $brand->id);
})->whereHas('customer', function (Builder $q) {
$q->where('id', $this->id);
});
if ($firstOrNewOrFail === self::FIRST_OR_NEW) {
return $query->firstOrNew();
@ -148,8 +148,8 @@ class Customer extends Model
/**
* Load login and info for this customer and this specific brand.
*
* @param Brand $brand
* @param boolean $orFail
* @param \App\Models\Brand $brand
* @param boolean $orFail
* @return self
* @throws ModelNotFoundException If no login or info was found for the brand.
*/
@ -175,8 +175,8 @@ class Customer extends Model
/**
* Set this customer as optin
*
* @param Brand $brand
* @param boolean $optIn
* @param \App\Models\Brand $brand
* @param boolean $optIn
* @return void
* @throws \Throwable If customer history update failed.
*/
@ -196,16 +196,16 @@ class Customer extends Model
* Get All answers given by this customer
* for a specific service
*
* @param Service $service
* @return Collection
* @param \App\Models\Service $service
* @return \Illuminate\Database\Eloquent\Collection
*/
public function getAnswers(Service $service): Collection
{
return Answer::whereHas('customers', function ($q) {
$q->whereId($this->id);
})->whereHas('question', function ($q) use ($service) {
$q->whereHas('brand', function ($q) use ($service) {
$q->whereId($service->brand->id);
return Answer::query()->whereHas('customers', function (Builder $q) {
$q->where('id', $this->id);
})->whereHas('question', function (Builder $q) use ($service) {
$q->whereHas('brand', function (Builder $q) use ($service) {
$q->where('id', $service->brand->id);
});
})->get();
}

View file

@ -3,6 +3,7 @@
namespace App\Models;
use App\Enums\Country;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@ -56,14 +57,14 @@ class CustomerInfo extends Model
*/
protected static function booted()
{
static::creating(function (CustomerInfo $customerInfo) {
$customerInfo->hashcode = Str::uuid();
static::creating(function (self $model) {
$model->hashcode = Str::uuid();
});
static::deleting(function (CustomerInfo $customerInfo) {
CustomerOptInHistory::whereHas('customer', function ($q) use ($customerInfo) {
$q->whereId($customerInfo->customer->id);
})->whereHas('brand', function ($q) use ($customerInfo) {
$q->whereId($customerInfo->brand->id);
static::deleting(function (self $model) {
CustomerOptInHistory::query()->whereHas('customer', function (Builder $q) use ($model) {
$q->where('id', $model->customer->id);
})->whereHas('brand', function (Builder $q) use ($model) {
$q->where('id', $model->brand->id);
})->delete();
});
}
@ -105,16 +106,30 @@ class CustomerInfo extends Model
/**
* Get customer info from email
*
* @param Brand $brand
* @param string $email
* @param \App\Models\Brand $brand
* @param string $email
* @return \Illuminate\Database\Eloquent\Builder
*/
public static function getFromEmail(Brand $brand, string $email)
{
return CustomerInfo::whereHas('brand', function ($q) use ($brand) {
return CustomerInfo::query()->whereHas('brand', function (Builder $q) use ($brand) {
$q->where('id', $brand->id);
})->whereHas('customer', function ($q) use ($email) {
})->whereHas('customer', function (Builder $q) use ($email) {
$q->where('email', $email);
});
}
/**
* Get customer info from hashcode
*
* @param \App\Models\Brand $brand
* @param string $hashcode
* @return \Illuminate\Database\Eloquent\Builder
*/
public static function getFromHashcode(Brand $brand, string $hashcode)
{
return CustomerInfo::query()->whereHas('brand', function (Builder $q) use ($brand) {
$q->where('id', $brand->id);
})->where('hashcode', $hashcode);
}
}

View file

@ -44,14 +44,14 @@ class CustomerLogin extends Model
*/
protected static function booted()
{
static::creating(function ($customerLogin) {
if (Hash::needsRehash($customerLogin->password)) {
$customerLogin->password = Hash::make(trim($customerLogin->password));
static::creating(function (self $model) {
if (Hash::needsRehash($model->password)) {
$model->password = Hash::make(trim($model->password));
}
});
static::updating(function ($customerLogin) {
if (Hash::needsRehash($customerLogin->password)) {
$customerLogin->password = Hash::make(trim($customerLogin->password));
static::updating(function (self $model) {
if (Hash::needsRehash($model->password)) {
$model->password = Hash::make(trim($model->password));
}
});
}

View file

@ -5,23 +5,12 @@ namespace App\Models;
use App\Enums\QuestionType;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Kwaadpepper\CrudPolicies\Enums\CrudAction;
use Kwaadpepper\CrudPolicies\Enums\CrudType;
use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
use Kwaadpepper\Enum\Traits\CastsEnums;
class Question extends Model
{
use CastsEnums;
use HasFactory;
use IsCrudModel;
/** @var string */
protected $crudLabelColumn = 'question';
/** @var string */
protected $crudValueColumn = 'id';
/**
* Map attribute names to enum classes.
@ -39,69 +28,10 @@ class Question extends Model
*/
protected $fillable = [
'question',
'type'
'type',
'brand_id'
];
/**
* Question
*
* @param array $attributes
*/
public function __construct(array $attributes = [])
{
self::$editableProperties = [
'question' => [
'label' => Str::ucfirst(trans('models.common.question')),
'type' => CrudType::string(),
'validate' => [
'min:10',
'max:255'
],
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'type' => [
'label' => Str::ucfirst(trans('models.common.type')),
'type' => CrudType::enum(),
'enum' => QuestionType::class,
'actions' => [
CrudAction::view(),
CrudAction::create(),
CrudAction::update(),
]
],
'brand_id' => [
'label' => Str::ucfirst(trans('models.classes.brand')),
'type' => CrudType::belongsTo(),
'belongsTo' => Brand::class,
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update(),
]
],
'answers' => [
'label' => Str::ucfirst(Str::plural(trans('models.classes.answers'))),
'type' => CrudType::hasMany(),
'hasMany' => Answer::class,
'hasManyLabel' => 'answer',
'readonly' => true,
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update(),
]
]
];
parent::__construct($attributes);
}
// * RELATIONS
/**

View file

@ -11,24 +11,14 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Kwaadpepper\CrudPolicies\Enums\CrudAction;
use Kwaadpepper\CrudPolicies\Enums\CrudType;
use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
use Rennokki\QueryCache\Traits\QueryCacheable;
use RuntimeException;
class Service extends Model
{
use IsCrudModel;
use HasFactory;
use QueryCacheable;
/** @var string */
protected $crudLabelColumn = 'name';
/** @var string */
protected $crudValueColumn = 'id';
/**
* The timeout cache for this model
*
@ -52,36 +42,8 @@ class Service extends Model
*/
public function __construct(array $attributes = [])
{
$this->cacheFor = config('api.servicesDBCache') * 60;
self::$editableProperties = [
'name' => [
'label' => Str::ucfirst(trans('models.common.name')),
'type' => CrudType::string(),
'validate' => [
'min:5',
'max:255'
],
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'brand_id' => [
'label' => Str::ucfirst(trans('models.classes.brand')),
'type' => CrudType::belongsTo(),
'belongsTo' => Brand::class,
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
];
parent::__construct($attributes);
$this->cacheFor = config('api.servicesDBCache') * 60;
}
/**
@ -106,9 +68,9 @@ class Service extends Model
*/
protected static function booted()
{
static::creating(function (self $service) {
$service->generateBearerToken();
$service->generateBearerPassword();
static::creating(function (self $model) {
$model->generateBearerToken();
$model->generateBearerPassword();
});
static::created(function () {
// * Flush Eloquent cache.

View file

@ -5,20 +5,31 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Kwaadpepper\CrudPolicies\Enums\CrudAction;
use Kwaadpepper\CrudPolicies\Enums\CrudType;
use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
class SingleOptin extends Model
{
use HasFactory;
use IsCrudModel;
/** @var string */
protected $crudLabelColumn = 'email';
/**
* The primary key for the model.
*
* @var string
*/
protected $primaryKey = 'hashcode';
/** @var string */
protected $crudValueColumn = 'id';
/**
* The "type" of the primary key ID.
*
* @var string
*/
protected $keyType = 'uuid';
/**
* Indicates if the IDs are auto-incrementing.
*
* @var boolean
*/
public $incrementing = false;
/**
* The attributes that are mass assignable.
@ -30,60 +41,15 @@ class SingleOptin extends Model
];
/**
* SingleOptin
* Perform any actions required after the model boots.
*
* @param array $attributes
* @return void
*/
public function __construct(array $attributes = [])
protected static function booted()
{
self::$editableProperties = [
'email' => [
'label' => Str::ucfirst(trans('models.common.email')),
'type' => CrudType::email(),
'validate' => [
'required',
'email:rfc,dns',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
$q = static::query();
if (request()->user) {
$q = $q->where('id', '!=', $this->id);
}
if ($q->where('email', $value)->exists()) {
$fail(__(':attribute est déjà utilisé', [
'attribute' => $attribute
]));
}
}
],
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'brand_id' => [
'label' => Str::ucfirst(trans('models.classes.brand')),
'type' => CrudType::belongsTo(),
'belongsTo' => Brand::class,
'actions' => [
CrudAction::viewAny(),
CrudAction::view()
]
],
'created_at' => [
'label' => Str::ucfirst(trans('models.common.optinDate')),
'type' => CrudType::string(),
'readonly' => true,
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::update()
]
],
];
parent::__construct($attributes);
static::creating(function (self $model) {
$model->hashcode = Str::uuid();
});
}
// * RELATIONS

View file

@ -5,6 +5,8 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
class User extends Authenticatable
{
@ -17,7 +19,8 @@ class User extends Authenticatable
* @var array
*/
protected $fillable = [
'name',
'lastname',
'firstname',
'email',
'password',
];
@ -40,4 +43,26 @@ class User extends Authenticatable
protected $casts = [
'email_verified_at' => 'datetime',
];
/**
* Perform any actions required after the model boots.
*
* @return void
*/
protected static function booted()
{
// * EVENT CLOSURES.
static::creating(function (self $model) {
if (Hash::needsRehash($model->password)) {
$model->password = Hash::make($model->password);
}
$model->remember_token = Str::random(10);
});
static::updating(function (self $model) {
if (Hash::needsRehash($model->password)) {
$model->password = Hash::make($model->password);
}
$model->remember_token = Str::random(10);
});
}
}

View file

@ -3,31 +3,58 @@
namespace App\Policies;
use App\Policies\Rules\UserStaticRules as USR;
use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
/**
* @codeCoverageIgnore
*/
class AnswerPolicy extends RootPolicy
class AnswerPolicy
{
/**
* Init policies on call
* View an Answer.
*
* @return void
* @return boolean
*/
public function setPolicies()
public function view(): bool
{
/**
* This first rule to return true in a policy action authorises the action
*/
$this->policies = [
'answer' => [
'viewAny' => USR::make('everyOne'),
'view' => USR::make('noOne'),
'update' => USR::make('everyOne'),
'create' => USR::make('everyOne'),
'delete' => USR::make('everyOne')
]
];
return USR::noOne();
}
/**
* viewAny an Answer.
* @return boolean
*/
public function viewAny(): bool
{
return USR::everyOne();
}
/**
* Create an Answer.
*
* @return boolean
*/
public function create(): bool
{
return USR::everyOne();
}
/**
* Update an Answer.
*
* @return boolean
*/
public function update(): bool
{
return USR::everyOne();
}
/**
* Delete an Answer.
*
* @return boolean
*/
public function delete(): bool
{
return USR::everyOne();
}
}

View file

@ -3,31 +3,58 @@
namespace App\Policies;
use App\Policies\Rules\UserStaticRules as USR;
use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
/**
* @codeCoverageIgnore
*/
class BrandPolicy extends RootPolicy
class BrandPolicy
{
/**
* Init policies on call
* View a Brand.
*
* @return void
* @return boolean
*/
public function setPolicies()
public function view(): bool
{
/**
* This first rule to return true in a policy action authorises the action
*/
$this->policies = [
'brand' => [
'viewAny' => USR::make('everyOne'),
'view' => USR::make('noOne'),
'update' => USR::make('everyOne'),
'create' => USR::make('everyOne'),
'delete' => USR::make('noOne')
]
];
return USR::noOne();
}
/**
* viewAny a Brand.
* @return boolean
*/
public function viewAny(): bool
{
return USR::everyOne();
}
/**
* Create a Brand.
*
* @return boolean
*/
public function create(): bool
{
return USR::everyOne();
}
/**
* Update a Brand.
*
* @return boolean
*/
public function update(): bool
{
return USR::everyOne();
}
/**
* Delete a Brand.
*
* @return boolean
*/
public function delete(): bool
{
return USR::noOne();
}
}

View file

@ -4,43 +4,71 @@ namespace App\Policies;
use App\Models\Customer;
use App\Models\Service;
use App\Policies\Rules\UserStaticRules as USR;
use Illuminate\Auth\Access\HandlesAuthorization;
use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
use App\Policies\Rules\UserStaticRules as USR;
/**
* @codeCoverageIgnore
*/
class CustomerPolicy extends RootPolicy
class CustomerPolicy
{
use HandlesAuthorization;
/**
* Init policies on call
* View a Customer.
*
* @return void
* @return boolean
*/
public function setPolicies()
public function view(): bool
{
/**
* This first rule to return true in a policy action authorises the action
*/
$this->policies = [
'customer' => [
'viewAny' => USR::make('everyOne'),
'view' => USR::make('everyOne'),
'create' => USR::make('noOne'),
'update' => USR::make('noOne'),
'delete' => USR::make('everyOne')
]
];
return USR::everyOne();
}
/**
* View any a Customer.
*
* @return boolean
*/
public function viewAny(): bool
{
return USR::everyOne();
}
/**
* Create a Customer.
*
* @return boolean
*/
public function create(): bool
{
return USR::noOne();
}
/**
* Update a Customer.
*
* @return boolean
*/
public function update(): bool
{
return USR::noOne();
}
/**
* Delete a Customer.
*
* @return boolean
*/
public function delete(): bool
{
return USR::everyOne();
}
/**
* Check if the customer belongs to the service
*
* @param Service $service
* @param Customer $customer
* @param \App\Models\Service $service
* @param \App\Models\Customer $customer
* @return boolean
*/
public function customerBelongsToService(Service $service, Customer $customer)

View file

@ -5,39 +5,66 @@ namespace App\Policies;
use App\Models\Question;
use App\Models\Service;
use App\Policies\Rules\UserStaticRules as USR;
use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
/**
* @codeCoverageIgnore
*/
class QuestionPolicy extends RootPolicy
class QuestionPolicy
{
/**
* Init policies on call
* View a Question.
*
* @return void
* @return boolean
*/
public function setPolicies()
public function view(): bool
{
/**
* This first rule to return true in a policy action authorises the action
*/
$this->policies = [
'question' => [
'viewAny' => USR::make('everyOne'),
'view' => USR::make('everyOne'),
'update' => USR::make('everyOne'),
'create' => USR::make('everyOne'),
'delete' => USR::make('everyOne')
]
];
return USR::everyOne();
}
/**
* viewAny a Question.
* @return boolean
*/
public function viewAny(): bool
{
return USR::everyOne();
}
/**
* Create a Question.
*
* @return boolean
*/
public function create(): bool
{
return USR::everyOne();
}
/**
* Update a Question.
*
* @return boolean
*/
public function update(): bool
{
return USR::everyOne();
}
/**
* Delete a Question.
*
* @return boolean
*/
public function delete(): bool
{
return USR::everyOne();
}
/**
* Check if the question belongs to the service
*
* @param Service $service
* @param Question $question
* @param \App\Models\Service $service
* @param \App\Models\Question $question
* @return boolean
*/
public function questionBelongsToService(Service $service, Question $question)

View file

@ -2,21 +2,19 @@
namespace App\Policies\Rules;
use Kwaadpepper\CrudPolicies\Policies\Rules\StaticRules;
/**
* Userlevel Policiy Static Rules
* ! Rules has to return boolean value
*
* @codeCoverageIgnore
*/
abstract class UserStaticRules extends StaticRules
abstract class UserStaticRules
{
/**
* Allow everyone
*
* @return boolean
*/
protected static function everyOne(): bool
public static function everyOne(): bool
{
return true;
}
@ -25,7 +23,7 @@ abstract class UserStaticRules extends StaticRules
*
* @return boolean
*/
protected static function noOne(): bool
public static function noOne(): bool
{
return false;
}

View file

@ -3,31 +3,59 @@
namespace App\Policies;
use App\Policies\Rules\UserStaticRules as USR;
use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
/**
* @codeCoverageIgnore
*/
class ServicePolicy extends RootPolicy
class ServicePolicy
{
/**
* Init policies on call
* View an Answer.
*
* @return void
* @return boolean
*/
public function setPolicies()
public function view(): bool
{
/**
* This first rule to return true in a policy action authorises the action
*/
$this->policies = [
'service' => [
'viewAny' => USR::make('everyOne'),
'view' => USR::make('everyOne'),
'update' => USR::make('everyOne'),
'create' => USR::make('everyOne'),
'delete' => USR::make('noOne')
]
];
return USR::everyOne();
}
/**
* viewAny an Service.
*
* @return boolean
*/
public function viewAny(): bool
{
return USR::everyOne();
}
/**
* Create an Service.
*
* @return boolean
*/
public function create(): bool
{
return USR::everyOne();
}
/**
* Update an Service.
*
* @return boolean
*/
public function update(): bool
{
return USR::everyOne();
}
/**
* Delete an Service.
*
* @return boolean
*/
public function delete(): bool
{
return USR::noOne();
}
}

View file

@ -3,31 +3,58 @@
namespace App\Policies;
use App\Policies\Rules\UserStaticRules as USR;
use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
/**
* @codeCoverageIgnore
*/
class SingleOptinPolicy extends RootPolicy
class SingleOptinPolicy
{
/**
* Init policies on call
* View an Answer.
*
* @return void
* @return boolean
*/
public function setPolicies()
public function view(): bool
{
/**
* This first rule to return true in a policy action authorises the action
*/
$this->policies = [
'singleoptin' => [
'viewAny' => USR::make('everyOne'),
'view' => USR::make('noOne'),
'create' => USR::make('noOne'),
'update' => USR::make('noOne'),
'delete' => USR::make('everyOne')
]
];
return USR::noOne();
}
/**
* viewAny an Answer.
* @return boolean
*/
public function viewAny(): bool
{
return USR::everyOne();
}
/**
* Create an Answer.
*
* @return boolean
*/
public function create(): bool
{
return USR::noOne();
}
/**
* Update an Answer.
*
* @return boolean
*/
public function update(): bool
{
return USR::noOne();
}
/**
* Delete an Answer.
*
* @return boolean
*/
public function delete(): bool
{
return USR::everyOne();
}
}

View file

@ -4,14 +4,10 @@ namespace App\Providers;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
/**
@ -36,14 +32,11 @@ class AppServiceProvider extends ServiceProvider
JsonResource::withoutWrapping();
// * LOCAL HTTPS FIX.
if (!in_array(config('app.env'), ['local', 'testing'])) {
URL::forceScheme('https');
}
URL::forceScheme('https');
// * HA PROXY FIX.
App::singleton('clientIp', function () {
if (
config('app.env') === 'local'
) {
if (config('app.env') === 'local') {
return request()->getClientIp();
} else {
return request()->server('HTTP_X_FORWARDED_FOR') ??
@ -57,16 +50,5 @@ class AppServiceProvider extends ServiceProvider
\strtoupper(config('app.locale'))
));
Carbon::setLocale(config('app.locale'));
// * FORCE BOOTSTRAP PAGINATOR.
if (!app()->runningInConsole()) {
Paginator::useBootstrap();
}
$appInfos = Cache::remember('composer', 360, function () {
return json_decode(File::get(\app_path('../composer.json')));
});
View::share('name', $appInfos->name);
View::share('version', $appInfos->version);
View::share('license', $appInfos->license);
}
}

View file

@ -25,6 +25,7 @@ class AuthServiceProvider extends ServiceProvider
*/
public function boot()
{
// Register policies.
$this->registerPolicies();
Auth::viaRequest('api-hmac', function (Request $request) {

View file

@ -0,0 +1,77 @@
<?php
namespace App\Providers;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\ServiceProvider;
class TranslationServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
foreach ($this->collectLocalesStrings() as $locale) {
// * Suported locales
Cache::rememberForever(sprintf('translations.%s', $locale), function () use ($locale) {
$translations = [
'php' => $this->phpTranslations($locale),
'json' => $this->jsonTranslations($locale),
];
return $translations;
});
}
}
/**
* Gather provided langs checking from files
*
* @return \Illuminate\Support\Collection
*/
private function collectLocalesStrings(): Collection
{
return collect(File::allFiles(resource_path('lang/')))->flatMap(function ($file) {
if ($file->getRelativePath()) {
return [$file->getRelativePath() => ''];
}
})->keys();
}
/**
* Gather all php translations
*
* @param string $locale
* @return array
*/
private function phpTranslations(string $locale): array
{
$path = resource_path("lang/$locale");
return collect(File::allFiles($path))->flatMap(function ($file) use ($locale) {
$translation = $file->getBasename('.php');
return [$translation => trans($translation, [], $locale)];
})->all();
}
/**
* Gather json translations
*
* @param string $locale
* @return array
*/
private function jsonTranslations(string $locale): array
{
$path = resource_path("lang/$locale.json");
if (is_string($path) && is_readable($path)) {
return json_decode(file_get_contents($path), true) ?? [];
}
return [];
}
}

View file

@ -0,0 +1,32 @@
<?php
namespace App\Providers;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class ViewServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot(): void
{
// * Set default view of the paginator.
Paginator::defaultView('modules.pagination');
// * Shares this data with all views.
// Get data from composer.json.
$appInfos = Cache::remember('composer', 360, function () {
return json_decode(File::get(\app_path('../composer.json')));
});
View::share('name', $appInfos->name);
View::share('version', $appInfos->version);
View::share('license', $appInfos->license);
}
}

@ -1 +1 @@
Subproject commit 41e2fff7f76107859805cd4a138b80a575f9f72c
Subproject commit b15e9bcb59f4680a736e34dabdb059682fc7b8bc

View file

@ -40,9 +40,9 @@ use Exception;
*/
class WPCrm extends ApiV1
{
/** @var LDCCRM\WPCrm $sessionKey */
private static $sessionKey = 'LDC_CRM_USER';
/** @var DCCRM\WPCrm $sessionDestroyed */
private static $sessionDestroyed = false;
/**
@ -63,8 +63,8 @@ class WPCrm extends ApiV1
* Try to login with CRM API
* return api response code if an error occurs
*
* @param string $email The customer email
* @param string $password The customer password
* @param string $email The customer email.
* @param string $password The customer password.
*
* @return \LDCCRM\WPCrmUser|int
*/

View file

@ -29,7 +29,6 @@ use JsonSerializable;
*/
class WPCrmUser implements JsonSerializable
{
/**
* The CRM ID
*
@ -61,9 +60,9 @@ class WPCrmUser implements JsonSerializable
/**
* WPCrmUser construct
*
* @param int $crm_id The crm id
* @param string $hashcode The customer hashcode (UUID)
* @param string $email The customer email
* @param integer $crm_id The crm id.
* @param string $hashcode The customer hashcode (UUID).
* @param string $email The customer email.
*/
public function __construct($crm_id, $hashcode, $email)
{
@ -75,9 +74,9 @@ class WPCrmUser implements JsonSerializable
/**
* Magic getter
*
* @param string $prop The prop to get
* @param string $prop The prop to get.
*
* @return int|string
* @return integer|string
*/
public function __get($prop)
{

View file

@ -1,24 +1,8 @@
{
"name": "ldc/ldccrmv2",
"type": "project",
"version": "3.0.5",
"version": "3.0.6",
"description": "LDC CRM V2 - revamped",
"authors": [
{
"name": "Romain Bonhomme",
"email": "romain.bonhomme@ldc.fr"
},
{
"name": "Vincent Bridier",
"email": "vicent.bridier@ldc.fr",
"homepage": "https://bridier.me"
},
{
"name": "Jérémy Munsch",
"email": "jeremy.munsch@ldc.fr",
"homepage": "https://jeremydev.ovh"
}
],
"license": "MIT",
"require": {
"php": "^7",
@ -27,11 +11,11 @@
"ext-json": "*",
"arondeparon/laravel-request-sanitizer": "^3.1",
"axlon/laravel-postal-code-validation": "^3.2",
"diglactic/laravel-breadcrumbs": "^8.1",
"doctrine/dbal": "^3.0",
"fideloper/proxy": "^4.4",
"fruitcake/laravel-cors": "^2.0",
"guzzlehttp/guzzle": "^7.0.1",
"kwaadpepper/crud-policies": "^0.3.5",
"kwaadpepper/enum": "^2.1",
"kwaadpepper/exception-handler": "^0.0.6",
"laravel-lang/lang": "~7.0",
@ -105,7 +89,7 @@
"runtestmigration": "@php artisan migrate:fresh --env=testing --seed",
"runtest": "@php artisan test --no-coverage --testsuite CRM,Client",
"runtestWithCoverage": "@php artisan test --testsuite CRM,Client",
"runserve": "@php artisan serve --host=127.0.0.1 --port=8888 --env=testing&",
"runserve": "@php artisan serve -q --host=127.0.0.1 --port=8888 --env=testing&",
"killserve": "kill $(ps -f | grep '[1]27.0.0.1:8888' | awk '{print $2}')",
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",

406
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "754ae14d2b3a505a0f58f377d93fc1b9",
"content-hash": "ceff538e9083e5fcfd91d183dcd2677b",
"packages": [
{
"name": "arondeparon/laravel-request-sanitizer",
@ -253,6 +253,77 @@
],
"time": "2021-08-15T20:50:18+00:00"
},
{
"name": "diglactic/laravel-breadcrumbs",
"version": "v8.1.1",
"source": {
"type": "git",
"url": "https://github.com/diglactic/laravel-breadcrumbs.git",
"reference": "f72a78eb3e26aea507d7888a65f15e5790864e21"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/diglactic/laravel-breadcrumbs/zipball/f72a78eb3e26aea507d7888a65f15e5790864e21",
"reference": "f72a78eb3e26aea507d7888a65f15e5790864e21",
"shasum": ""
},
"require": {
"facade/ignition-contracts": "^1.0",
"laravel/framework": "^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0",
"php": "^7.2 || ^8.0"
},
"conflict": {
"davejamesmiller/laravel-breadcrumbs": "*"
},
"require-dev": {
"orchestra/testbench": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0",
"php-coveralls/php-coveralls": "^2.4",
"phpunit/phpunit": "^8.5 || ^9.5",
"spatie/phpunit-snapshot-assertions": "^2.2 || ^4.2"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Diglactic\\Breadcrumbs\\ServiceProvider"
],
"aliases": {
"Breadcrumbs": "Diglactic\\Breadcrumbs\\Breadcrumbs"
}
}
},
"autoload": {
"psr-4": {
"Diglactic\\Breadcrumbs\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Sheng Slogar",
"email": "sheng@diglactic.com",
"role": "Maintainer"
},
{
"name": "Dave James Miller",
"email": "dave@davejamesmiller.com",
"role": "Original Creator"
}
],
"description": "A simple Laravel-style way to create breadcrumbs.",
"homepage": "https://github.com/diglactic/laravel-breadcrumbs",
"keywords": [
"laravel"
],
"support": {
"issues": "https://github.com/diglactic/laravel-breadcrumbs/issues",
"source": "https://github.com/diglactic/laravel-breadcrumbs/tree/v8.1.1"
},
"time": "2023-04-17T23:24:15+00:00"
},
{
"name": "doctrine/cache",
"version": "2.2.0",
@ -887,125 +958,6 @@
],
"time": "2020-12-29T14:50:06+00:00"
},
{
"name": "ezyang/htmlpurifier",
"version": "v4.13.0",
"source": {
"type": "git",
"url": "https://github.com/ezyang/htmlpurifier.git",
"reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/08e27c97e4c6ed02f37c5b2b20488046c8d90d75",
"reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75",
"shasum": ""
},
"require": {
"php": ">=5.2"
},
"require-dev": {
"simpletest/simpletest": "dev-master#72de02a7b80c6bb8864ef9bf66d41d2f58f826bd"
},
"type": "library",
"autoload": {
"files": [
"library/HTMLPurifier.composer.php"
],
"psr-0": {
"HTMLPurifier": "library/"
},
"exclude-from-classmap": [
"/library/HTMLPurifier/Language/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1-or-later"
],
"authors": [
{
"name": "Edward Z. Yang",
"email": "admin@htmlpurifier.org",
"homepage": "http://ezyang.com"
}
],
"description": "Standards compliant HTML filter written in PHP",
"homepage": "http://htmlpurifier.org/",
"keywords": [
"html"
],
"support": {
"issues": "https://github.com/ezyang/htmlpurifier/issues",
"source": "https://github.com/ezyang/htmlpurifier/tree/master"
},
"time": "2020-06-29T00:56:53+00:00"
},
{
"name": "facade/flare-client-php",
"version": "1.10.0",
"source": {
"type": "git",
"url": "https://github.com/facade/flare-client-php.git",
"reference": "213fa2c69e120bca4c51ba3e82ed1834ef3f41b8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/facade/flare-client-php/zipball/213fa2c69e120bca4c51ba3e82ed1834ef3f41b8",
"reference": "213fa2c69e120bca4c51ba3e82ed1834ef3f41b8",
"shasum": ""
},
"require": {
"facade/ignition-contracts": "~1.0",
"illuminate/pipeline": "^5.5|^6.0|^7.0|^8.0",
"php": "^7.1|^8.0",
"symfony/http-foundation": "^3.3|^4.1|^5.0",
"symfony/mime": "^3.4|^4.0|^5.1",
"symfony/var-dumper": "^3.4|^4.0|^5.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.14",
"phpunit/phpunit": "^7.5",
"spatie/phpunit-snapshot-assertions": "^2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"files": [
"src/helpers.php"
],
"psr-4": {
"Facade\\FlareClient\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Send PHP errors to Flare",
"homepage": "https://github.com/facade/flare-client-php",
"keywords": [
"exception",
"facade",
"flare",
"reporting"
],
"support": {
"issues": "https://github.com/facade/flare-client-php/issues",
"source": "https://github.com/facade/flare-client-php/tree/1.10.0"
},
"funding": [
{
"url": "https://github.com/spatie",
"type": "github"
}
],
"time": "2022-08-09T11:23:57+00:00"
},
{
"name": "facade/ignition-contracts",
"version": "1.0.2",
@ -1708,72 +1660,6 @@
],
"time": "2022-06-20T21:43:11+00:00"
},
{
"name": "kwaadpepper/crud-policies",
"version": "0.3.6",
"source": {
"type": "git",
"url": "https://github.com/Kwaadpepper/crud-policies.git",
"reference": "205b8a16c99c362fc78422eeb31c0ba618a3d351"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Kwaadpepper/crud-policies/zipball/205b8a16c99c362fc78422eeb31c0ba618a3d351",
"reference": "205b8a16c99c362fc78422eeb31c0ba618a3d351",
"shasum": ""
},
"require": {
"doctrine/dbal": "^3.1",
"facade/flare-client-php": "^1.8",
"illuminate/support": "~8",
"kwaadpepper/enum": "^2.0",
"laravel/framework": "~8",
"mews/purifier": "^3.3"
},
"require-dev": {
"orchestra/testbench": "^6.13",
"phpunit/phpunit": "~9.0",
"slevomat/coding-standard": "^7.0",
"squizlabs/php_codesniffer": "^3.5"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Kwaadpepper\\CrudPolicies\\Providers\\CrudPoliciesServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Kwaadpepper\\CrudPolicies\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jérémy Munsch",
"email": "github@jeremydev.ovh",
"homepage": "https://jeremydev.ovh"
}
],
"description": "Makes CRUD easy, make policies easy and everyone happy",
"homepage": "https://github.com/kwaadpepper/crud-policies",
"keywords": [
"Scafolding",
"crud",
"laravel",
"policies"
],
"support": {
"issues": "https://github.com/Kwaadpepper/crud-policies/issues",
"source": "https://github.com/Kwaadpepper/crud-policies/tree/0.3.6"
},
"time": "2022-05-04T13:19:25+00:00"
},
{
"name": "kwaadpepper/enum",
"version": "2.1.4",
@ -2624,87 +2510,6 @@
],
"time": "2022-04-17T13:12:02+00:00"
},
{
"name": "mews/purifier",
"version": "3.3.8",
"source": {
"type": "git",
"url": "https://github.com/mewebstudio/Purifier.git",
"reference": "164b420c4a80d2d0a89c1023f0aa9b3c0e3d6dc5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mewebstudio/Purifier/zipball/164b420c4a80d2d0a89c1023f0aa9b3c0e3d6dc5",
"reference": "164b420c4a80d2d0a89c1023f0aa9b3c0e3d6dc5",
"shasum": ""
},
"require": {
"ezyang/htmlpurifier": "4.13.*",
"illuminate/config": "^5.8|^6.0|^7.0|^8.0|^9.0",
"illuminate/filesystem": "^5.8|^6.0|^7.0|^8.0|^9.0",
"illuminate/support": "^5.8|^6.0|^7.0|^8.0|^9.0",
"php": "^7.2|^8.0"
},
"require-dev": {
"graham-campbell/testbench": "^3.2|^5.5.1",
"mockery/mockery": "^1.3.3",
"phpunit/phpunit": "^8.0|^9.0"
},
"suggest": {
"laravel/framework": "To test the Laravel bindings",
"laravel/lumen-framework": "To test the Lumen bindings"
},
"type": "package",
"extra": {
"laravel": {
"providers": [
"Mews\\Purifier\\PurifierServiceProvider"
],
"aliases": {
"Purifier": "Mews\\Purifier\\Facades\\Purifier"
}
}
},
"autoload": {
"files": [
"src/helpers.php"
],
"psr-4": {
"Mews\\Purifier\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Muharrem ERİN",
"email": "me@mewebstudio.com",
"homepage": "https://github.com/mewebstudio",
"role": "Developer"
}
],
"description": "Laravel 5/6/7 HtmlPurifier Package",
"homepage": "https://github.com/mewebstudio/purifier",
"keywords": [
"Purifier",
"htmlpurifier",
"laravel5 HtmlPurifier",
"laravel5 Purifier",
"laravel5 Security",
"laravel6 HtmlPurifier",
"laravel6 Purifier",
"laravel6 Security",
"security",
"xss"
],
"support": {
"issues": "https://github.com/mewebstudio/Purifier/issues",
"source": "https://github.com/mewebstudio/Purifier/tree/3.3.8"
},
"time": "2022-07-20T12:14:06+00:00"
},
{
"name": "monolog/monolog",
"version": "2.8.0",
@ -6792,6 +6597,71 @@
],
"time": "2022-03-03T08:28:38+00:00"
},
{
"name": "facade/flare-client-php",
"version": "1.10.0",
"source": {
"type": "git",
"url": "https://github.com/facade/flare-client-php.git",
"reference": "213fa2c69e120bca4c51ba3e82ed1834ef3f41b8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/facade/flare-client-php/zipball/213fa2c69e120bca4c51ba3e82ed1834ef3f41b8",
"reference": "213fa2c69e120bca4c51ba3e82ed1834ef3f41b8",
"shasum": ""
},
"require": {
"facade/ignition-contracts": "~1.0",
"illuminate/pipeline": "^5.5|^6.0|^7.0|^8.0",
"php": "^7.1|^8.0",
"symfony/http-foundation": "^3.3|^4.1|^5.0",
"symfony/mime": "^3.4|^4.0|^5.1",
"symfony/var-dumper": "^3.4|^4.0|^5.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.14",
"phpunit/phpunit": "^7.5",
"spatie/phpunit-snapshot-assertions": "^2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"files": [
"src/helpers.php"
],
"psr-4": {
"Facade\\FlareClient\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Send PHP errors to Flare",
"homepage": "https://github.com/facade/flare-client-php",
"keywords": [
"exception",
"facade",
"flare",
"reporting"
],
"support": {
"issues": "https://github.com/facade/flare-client-php/issues",
"source": "https://github.com/facade/flare-client-php/tree/1.10.0"
},
"funding": [
{
"url": "https://github.com/spatie",
"type": "github"
}
],
"time": "2022-08-09T11:23:57+00:00"
},
{
"name": "facade/ignition",
"version": "2.17.6",
@ -9179,5 +9049,5 @@
"platform-overrides": {
"php": "7.3.26"
},
"plugin-api-version": "2.2.0"
"plugin-api-version": "2.3.0"
}

View file

@ -175,7 +175,8 @@ return [
App\Providers\AuthServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
App\Providers\TranslationServiceProvider::class,
App\Providers\ViewServiceProvider::class,
],
/*
@ -228,14 +229,5 @@ return [
'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class,
'Carbon' => Illuminate\Support\Carbon::class,
'ArcadiaMailType' => App\Enums\ArcadiaMailType::class,
'ArcadiaOrderType' => App\Enums\ArcadiaOrderType::class,
'CustomerExportType' => App\Enums\CustomerExportType::class,
'Brand' => App\Models\Brand::class,
'Customer' => App\Models\Customer::class,
'Question' => App\Models\Question::class,
'Answer' => App\Models\Answer::class,
'BrandController' => App\Http\Controllers\BrandController::class,
],
];

77
config/breadcrumbs.php Normal file
View file

@ -0,0 +1,77 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| View Name
|--------------------------------------------------------------------------
|
| Choose a view to display when Breadcrumbs::render() is called.
| Built in templates are:
|
| - 'breadcrumbs::bootstrap5' - Bootstrap 5
| - 'breadcrumbs::bootstrap4' - Bootstrap 4
| - 'breadcrumbs::bulma' - Bulma
| - 'breadcrumbs::foundation6' - Foundation 6
| - 'breadcrumbs::json-ld' - JSON-LD Structured Data
| - 'breadcrumbs::materialize' - Materialize
| - 'breadcrumbs::tailwind' - Tailwind CSS
| - 'breadcrumbs::uikit' - UIkit
|
| Or a custom view, e.g. '_partials/breadcrumbs'.
|
*/
'view' => 'breadcrumbs/layout',
/*
|--------------------------------------------------------------------------
| Breadcrumbs File(s)
|--------------------------------------------------------------------------
|
| The file(s) where breadcrumbs are defined. e.g.
|
| - base_path('routes/breadcrumbs.php')
| - glob(base_path('breadcrumbs/*.php'))
|
*/
'files' => [
base_path('routes/breadcrumbs.php'),
],
/*
|--------------------------------------------------------------------------
| Exceptions
|--------------------------------------------------------------------------
|
| Determine when to throw an exception.
|
*/
// * When route-bound breadcrumbs are used but the current route doesn't have a name (UnnamedRouteException)
'unnamed-route-exception' => true,
// * When route-bound breadcrumbs are used and the matching breadcrumb doesn't exist (InvalidBreadcrumbException)
'missing-route-bound-breadcrumb-exception' => true,
// * When a named breadcrumb is used but doesn't exist (InvalidBreadcrumbException)
'invalid-named-breadcrumb-exception' => true,
/*
|--------------------------------------------------------------------------
| Classes
|--------------------------------------------------------------------------
|
| Subclass the default classes for more advanced customisations.
|
*/
// * Manager
'manager-class' => Diglactic\Breadcrumbs\Manager::class,
// * Generator
'generator-class' => Diglactic\Breadcrumbs\Generator::class,
];

View file

@ -31,7 +31,7 @@ class RepairStatsJobs extends Migration
$ref->setAccessible(true);
$service = $ref->getValue(unserialize($payload->data->command));
$date = Carbon::parse($job->created_at)->format('Y-m-d');
$date = Carbon::parse($job->created_at)->isoFormat('YYYY-MM-DD');
$serviceStat = ServiceStat::whereDate('date', $date)
->where('service_id', $service->id)->where('brand_id', $service->brand->id)

View file

@ -20,9 +20,7 @@ class ParseCountryOnCustomerInfos extends Migration
$perPage = 10000;
$currentPage = 0;
$totalPages = ceil(CustomerInfo::query()->count() / $perPage);
dump("items per page $perPage");
for ($currentPage; $currentPage <= $totalPages; $currentPage++) {
dump("page $currentPage");
DB::transaction(function () use ($currentPage, $perPage) {
collect(CustomerInfo::query()
->simplePaginate($perPage, ['*'], 'page', $currentPage)->items())

View file

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CascadeDeleteQuestionAnswers extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
// * Add cascade delete
Schema::table('answers', function (Blueprint $table) {
$table->dropForeign(['question_id']);
$table->foreign('question_id')->references('id')->on('questions')->cascadeOnDelete();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
// * Undo cascade delete (restrict as default)
Schema::table('answers', function (Blueprint $table) {
$table->dropForeign(['question_id']);
$table->foreign('question_id')->references('id')->on('questions');
});
}
}

View file

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CascadeDeleteCustomerAnswer extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
// * Add cascade delete
Schema::table('customer_answer', function (Blueprint $table) {
$table->dropForeign(['answer_id']);
$table->foreign('answer_id')->references('id')->on('answers')->cascadeOnDelete();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
// * Undo cascade delete (restrict as default)
Schema::table('customer_answer', function (Blueprint $table) {
$table->dropForeign(['answer_id']);
$table->foreign('answer_id')->references('id')->on('answers');
});
}
}

View file

@ -0,0 +1,49 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
class AddChangeSingleOptinToUseHashcode extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('single_optins', function (Blueprint $table) {
$table->uuid('hashcode')->comment('Single optin hashcode')->after('id');
});
DB::transaction(function () {
DB::table('single_optins')->get()->each(function (object $single_optin) {
DB::table('single_optins')->where('id', $single_optin->id)->update([
'hashcode' => Str::uuid()
]);
});
});
Schema::table('single_optins', function (Blueprint $table) {
$table->uuid('hashcode')->comment('Single optin hashcode')->after('id')
->unique()->change();
$table->dropColumn('id');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('single_optins', function (Blueprint $table) {
$table->id()->after('hashcode');
});
Schema::table('single_optins', function (Blueprint $table) {
$table->dropColumn('hashcode');
});
}
}

View file

@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class EnhanceCustomerInfosHashcode extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('customer_infos', function (Blueprint $table) {
$table->uuid('hashcode')
->comment('Customer hashcode relative for a specific brand')->change();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
// * No need to undo that.
}
}

12841
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -7,23 +7,59 @@
"watch-poll": "mix watch -- --watch-options-poll=1000",
"hot": "mix watch --hot",
"prod": "npm run production",
"production": "mix --production"
"production": "mix --production",
"eslint": "node ./node_modules/.bin/eslint -c .eslintrc.json ./resources",
"eslint-ci": "node ./node_modules/.bin/eslint -c .eslintrc.json ./resources -f junit",
"stylelint": "npx stylelint '**/*.scss'",
"stylelint-ci": "npx stylelint '**/*.scss' -f json"
},
"devDependencies": {
"@fortawesome/fontawesome-svg-core": "^6.4.0",
"@fortawesome/free-brands-svg-icons": "^6.4.0",
"@fortawesome/free-regular-svg-icons": "^6.4.0",
"@fortawesome/free-solid-svg-icons": "^6.4.0",
"@rushstack/eslint-patch": "^1.3.1",
"@types/bootstrap": "^5.2.6",
"@types/vue-select": "^3.16.2",
"@types/webpack-env": "^1.18.1",
"@typescript-eslint/eslint-plugin": "^5.59.9",
"@typescript-eslint/parser": "^5.59.9",
"@vue/eslint-config-typescript": "^11.0.3",
"axios": "^0.21",
"bootstrap": "^5.3.0",
"chart.js": "^3.6.0",
"clean-webpack-plugin": "^4.0.0",
"eslint": "^8.42.0",
"eslint-plugin-vue": "^9.14.1",
"eslint-webpack-plugin": "^4.0.1",
"laravel-mix": "^6.0.6",
"laravel-mix-clean": "^0.1.0",
"laravel-mix-eslint": "^0.2.0",
"laravel-mix-polyfill": "^3.0.0",
"laravel-mix-stylelint": "^1.0.0",
"moment": "^2.29.1",
"postcss": "^8.1.14",
"postcss-discard-comments": "^4.0.2",
"prettier": "^2.8.8",
"print-js": "^1.6.0",
"resolve-url-loader": "^3.1.3",
"sass": "^1.32.11",
"sass-loader": "^11.0.1",
"slugify": "^1.6.6",
"stylelint": "^14.11.0",
"stylelint-config-idiomatic-order": "^8.1.0",
"stylelint-config-prettier": "^9.0.5",
"stylelint-config-sass-guidelines": "^9.0.1",
"stylelint-config-standard-scss": "^6.1.0",
"stylelint-webpack-plugin": "^4.1.1",
"sweetalert2": "^11.7.12",
"swiper": "^6.6.1",
"vue": "^2.6.12",
"vue-loader": "^15.9.6",
"vue-template-compiler": "^2.6.12"
"ts-loader": "^9.4.3",
"typescript": "^4.8.2",
"typescript-eslint": "^0.0.1-alpha.0",
"vue": "^3.2.38",
"vue-eslint-parser": "^9.1.0",
"vue-loader": "^16.8.3",
"vue-select": "^4.0.0-beta.3"
}
}

View file

@ -1,4 +0,0 @@
{
"/js/preload-back.js": "/js/preload-back.js",
"/css/preload-back.css": "/css/preload-back.css"
}

View file

@ -1,6 +0,0 @@
require('./bootstrap');
require('./pages/customer.delete')
require('./pages/service.index')
require('./pages/single_optin.index')
require('./pages/statsExport')

View file

@ -1,28 +0,0 @@
window._ = require('lodash');
/**
* We'll load the axios HTTP library which allows us to easily issue requests
* to our Laravel back-end. This library automatically handles sending the
* CSRF token as a header based on the value of the "XSRF" token cookie.
*/
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
/**
* Echo exposes an expressive API for subscribing to channels and listening
* for events that are broadcast by Laravel. Echo and event broadcasting
* allows your team to easily build robust real-time web applications.
*/
// import Echo from 'laravel-echo';
// window.Pusher = require('pusher-js');
// window.Echo = new Echo({
// broadcaster: 'pusher',
// key: process.env.MIX_PUSHER_APP_KEY,
// cluster: process.env.MIX_PUSHER_APP_CLUSTER,
// forceTLS: true
// });

View file

@ -1,34 +0,0 @@
let customerDelete = document.getElementById('customerDelete');
if (customerDelete) {
customerDelete.addEventListener('submit', (e) => {
let self = e.target
e.preventDefault()
Swal.fire({
title: 'Supprimer ce client pour cette marque ?',
text: 'Attention cette action est irréversible, si le client n\'appartient plus à aucune marque il sera complètement retiré des bases',
icon: 'warning',
confirmButtonText: 'Je comprends le danger et suis conscient de ce que je fait',
showCancelButton: true,
cancelButtonText: 'Annuler',
confirmButtonColor: '#dc3545'
}).then((result) => {
if (result.isConfirmed) {
Swal.fire({
title: 'Attention au conséquences',
html: 'La supression de ce compte dans la crm n\'entrainera pas ça supression dans MESBR ni dans le front cache DB (pour les sites WPCRM). <br><h3><b>Une fois le compte supprimé je ne pourrai plus récupérer le hashcode nécessaire pour effectuer ces opérations.</b></h3>',
icon: 'info',
confirmButtonText: 'Je comprends le danger et suis conscient que celà aura des conséquences',
showCancelButton: true,
cancelButtonText: 'Annuler',
confirmButtonColor: '#dc3545'
}).then((result) => {
if (result.isConfirmed) {
self.submit()
}
})
}
})
})
}

View file

@ -1,36 +0,0 @@
let genForms = document.getElementsByClassName('genToken');
if (genForms.length) {
for (let form of genForms) {
form.addEventListener('submit', (e) => {
let self = e.target
e.preventDefault()
Swal.fire({
title: 'Regénérer un token ?',
text: 'Attention la regeneration empêchera le service de fonctionner tant que son token n\'aura pas été mis à jour',
icon: 'warning',
confirmButtonText: 'Je comprends le danger et suis conscient de ce que je fait',
showCancelButton: true,
cancelButtonText: 'Annuler',
confirmButtonColor: '#dc3545'
}).then((result) => {
if (result.isConfirmed) {
Swal.fire({
title: 'Vous allez régénérer un token de service en production',
html: '<h3><b>Je suis conscient que celà va avoir des implications et qu\'il me faudra mettre à jour le bearer token du service concerné dans son envirronement actuel</b></h3>',
icon: 'info',
confirmButtonText: 'Oui, feu patate !!',
showCancelButton: true,
cancelButtonText: 'Annuler',
confirmButtonColor: '#dc3545'
}).then((result) => {
if (result.isConfirmed) {
self.submit()
}
})
}
})
})
}
}

View file

@ -1,11 +0,0 @@
let brandSelect = document.getElementById('brandSelect');
if (brandSelect) {
brandSelect.addEventListener('change', function (e) {
if (brandSelect.value !== '0') {
const url = new URL(location.href);
url.searchParams.set('brand', brandSelect.value);
url.searchParams.delete('rst');
location.assign(url.search);
}
});
}

Some files were not shown because too many files have changed in this diff Show more