Migrate in K8S cluster

This commit is contained in:
noah.lancien 2025-04-02 15:14:41 +00:00 • committed by Vincent BRIDIER
parent 30df92ab01
commit c3899725ad
98 changed files with 21599 additions and 24719 deletions

14
.dockerignore Normal file
View file

@ -0,0 +1,14 @@
.git
*.md
node_modules/
npm-debug.log
.npmrc
tests/
Dockerfile*
.dockerignore
.gitignore
LICENSE
.vscode
vendor/
public/build/
auth.json

View file

@ -25,5 +25,11 @@ MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=
MAIL_FROM_NAME=
# CRM
CRM_ENDPOINT=
# Sentry
SENTRY_LARAVEL_DSN=

31
.gitignore vendored
View file

@ -1,24 +1,29 @@
.env
.env.backup
.env.testing
.env.production
.phpunit.result.cache
.stylelintcache
/.fleet
/.idea
/.phpunit.cache
/.vscode
/node_modules
/bootstrap
/public/build
/public/cache
/public/hot
/public/sitemap.xml
/public/storage
/public/landings/*
!/public/landings/.gitkeep
/public/generation/*
!/public/generation/.gitkeep
/public/export/*
!/public/export/.gitkeep
/public/coupons/*
!/public/coupons/.gitkeep
/public/images/logos/*
!/public/images/logos/.gitkeep
/storage/*.key
/vendor
/.idea
/.vscode
/.vagrant
auth.json
docker-compose.override.yml
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
.env
.phpunit.result.cache
.npmrc
/tests/coverage

23
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,23 @@
include:
- project: "ldc-web/shared-ci"
ref: "main"
file: "lint/lint7.3.yml"
- project: "ldc-web/shared-ci"
ref: "main"
file: "security/security7.3.yml"
- project: "ldc-web/shared-ci"
ref: "main"
file: "build/build7.3.yml"
- project: "ldc-web/shared-ci"
ref: "main"
file: "test/test.yml"
- project: "ldc-web/shared-ci"
ref: "main"
file: "deploy/deploy.yml"
stages:
- lint
- security
- build
- test
- deploy

5
Dockerfile Normal file
View file

@ -0,0 +1,5 @@
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
USER root
RUN chmod -R 755 /app/srcs/storage
USER www-data

View file

@ -3,45 +3,49 @@
namespace App\Exceptions;
use App\Http\Resources\StatusResource;
use Exception;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Kwaadpepper\ExceptionHandler\Exceptions\ExceptionHandler;
use Illuminate\Http\JsonResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;
class Handler extends ExceptionHandler
{
/**
* A list of exception types with their corresponding custom log levels.
*
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
*/
protected $levels = [];
/**
* A list of the exception types that are not reported.
*
* @var array
* @var array<int, class-string<\Throwable>>
*/
protected $dontReport = [
//
];
protected $dontReport = [];
/**
* A list of the inputs that are never flashed for validation exceptions.
* A list of the inputs that are never flashed to the session on validation exceptions.
*
* @var array
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Report or log an exception.
* Register the exception handling callbacks for the application.
*
* @param \Throwable $exception
* @return void
*/
public function report(Throwable $exception)
public function register()
{
parent::report($exception);
$this->reportable(function (Throwable $e) {
if (app()->bound('sentry')) {
app('sentry')->captureException($e);
}
});
}
/**
@ -56,12 +60,7 @@ class Handler extends ExceptionHandler
if ($request->wantsJson() && $exception instanceof ModelNotFoundException) {
return response()->json(new StatusResource(['status' => 'KO', 'code' => 404, 'message' => 'Resource not found.']), 404);
}
// if ($exception instanceof \ErrorException) {
// $response = response()->view('errors.500', [], 500);
// return $this->toIlluminateResponse($response,$exception);
// }
return parent::render($request, $exception);
}
}

View file

@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use App\Coupon;
use App\Site;
use App\Campagne;
@ -54,7 +55,7 @@ class CouponController extends Controller
public function create()
{
$campagnes = Campagne::where('camp_siteId', session()->get('site')->site_id)->get()
->pluck('camp_name', 'camp_id');
->pluck('camp_name', 'camp_id');
$site = Site::where(['id' => session()->get('site')->id])->first();
return view('coupon.create')->with('campagnes', $campagnes)->with('site', $site);
@ -85,8 +86,8 @@ class CouponController extends Controller
if ($v->fails()) {
return redirect('bo/coupon/' . $coupon->coupon_id . '/edit')
->withErrors($v)
->withInput();
->withErrors($v)
->withInput();
}
@ -235,8 +236,8 @@ class CouponController extends Controller
if ($v->fails()) {
return redirect('bo/coupon/' . $coupon->coupon_id . '/edit')
->withErrors($v)
->withInput();
->withErrors($v)
->withInput();
}
if (!isset($request->coupon_enablelimitquantity)) {
@ -318,8 +319,9 @@ class CouponController extends Controller
foreach ($datas as $key => $item) {
if ($key == 'coupon_image' || $key == 'coupon_gs1code' || $key == 'coupon_decoration_image' || $key == 'coupon_logo_image' || (substr($key, 7, 9) == 'thumbnail' && strlen($key) < 18)) {
if (isset($item)) {
$path = $item->store('coupons');
$path = str_replace('public/', '', $path);
/** @var \Illuminate\Http\UploadedFile $item */
$path = $item->store('modelfiles/coupons', 'public');
$path = Str::start($path, 'storage/');
$datas->put($key, $path);
} else {
$datas->put($key, $request->input($key . '_hidden'));

View file

@ -0,0 +1,190 @@
<?php
namespace App\Lib\Helpers;
class ExceptionsHelper
{
/**
* Handle sentry event
*
* @param \Sentry\Event $event
* @return null|\Sentry\Event
*/
public static function sentryEventHandler(\Sentry\Event $event): ?\Sentry\Event
{
// Remove unwanted reports.
$event->setModules([]);
$event->setOsContext(null);
/** @var \Sentry\ExceptionDataBag[] */
$exceptionBag = [];
foreach ($event->getExceptions() as $exceptionDataBag) {
$exceptionBag[] = self::getAnonymizedSentryException($exceptionDataBag);
}
$event->setExceptions($exceptionBag);
return $event;
}
/**
* Anonimize Sentry Exception bag
*
* @param \Sentry\ExceptionDataBag $exceptionDataBag
* @return \Sentry\ExceptionDataBag
*/
private static function getAnonymizedSentryException(
\Sentry\ExceptionDataBag $exceptionDataBag
): \Sentry\ExceptionDataBag {
$exceptionDataBag->setValue(self::getAnonymizedMessage($exceptionDataBag->getValue()));
if ($stackTrace = $exceptionDataBag->getStacktrace()) {
$exceptionDataBag->setStacktrace(self::getAnonymizedSentryStackTrace($stackTrace));
}
return $exceptionDataBag;
}
/**
* Anonymise sentry stacktrace
*
* @param \Sentry\Stacktrace $stackTrace
* @return \Sentry\Stacktrace
*/
private static function getAnonymizedSentryStackTrace(\Sentry\Stacktrace $stackTrace): \Sentry\Stacktrace
{
/** @var \Sentry\Frame[] */
$frames = \array_reverse($stackTrace->getFrames(), true);
$i = \count($frames) - 1;
// * Remove all but keep first (index.php).
while ($i-- > 0) {
$stackTrace->removeFrame(count($stackTrace->getFrames()) - 1);
}
foreach ($frames as $index => $frame) {
$frame->setPostContext([]);
$frame->setPreContext([]);
$frame->setContextLine('');
$frame->setVars(self::getAnonymizedSentryParams($frame->getVars()));
$stackTrace->addFrame($frame);
if ($index === 0) {
$stackTrace->removeFrame(count($stackTrace->getFrames()) - 1);
}
}
return $stackTrace;
}
/**
* Get anonymized sentry params
*
* @param mixed[] $params
* @return string[]
*/
private static function getAnonymizedSentryParams(array $params): array
{
foreach ($params as &$param) {
$type = \strtolower(\gettype($param));
if (\is_string($param)) {
$type = \explode(' ', \trim(strtolower($param)))[0] ?? null;
}
switch (true) {
case $type === 'lambda':
case $type === 'object':
continue 2;
case $type === 'array' and \is_array($param):
$param = sprintf('Array(%d)', \count($param));
continue 2;
default:
if (\is_numeric($param)) {
$param = \ucfirst($type);
continue 2;
}
if (\is_string($param)) {
$param = sprintf('String(%d)', \strlen($param));
continue 2;
}
} //end switch
} //end foreach
return $params;
}
/**
* Anonymize at least Sql queries
*
* @param string $message
* @return string
*/
public static function getAnonymizedMessage(string $message): string
{
$message = \preg_replace('/^(.*)( \(SQL:.*\))$/', '$1', $message) ?? $message;
if ($sqlPos = \strpos($message, 'to use near')) {
$message = \mb_strcut($message, 0, $sqlPos);
}
return $message;
}
/**
* Anonymize stack trace
*
* @param \Throwable $e
* @return string
*/
public static function getAnonymizedStackTrace(\Throwable $e): string
{
$projectPath = \realpath(\sprintf('%s/../', \app_path()));
$traceStack = $e->getTrace();
$traceStr = '';
$maxStackStage = 0;
foreach ($traceStack as $stackStage => $stack) {
$maxStackStage = $stackStage;
$traceStr .= \sprintf(
'#%s %s(%d): ',
$stackStage,
str_replace($projectPath, '', $stack['file'] ?? ''),
$stack['line'] ?? ''
);
if (isset($stack['class'])) {
$traceStr .= "{$stack['class']}->{$stack['function']}(";
if (isset($stack['args'])) {
$traceStr .= self::handleArgs($stack);
}
$traceStr .= ")\n";
} else {
$traceStr .= "{$stack['function']}(";
if (isset($stack['args'])) {
$traceStr .= self::handleArgs($stack);
}
$traceStr .= ")\n";
}
} //end foreach
$maxStackStage++;
$traceStr .= "#$maxStackStage {main}";
return $traceStr;
}
/**
* Handle Args
*
* @param array $stack
* @return string
*/
public static function handleArgs(array $stack): string
{
$traceArgs = '';
$argsCount = count($stack['args']);
for ($i = 0; $i < $argsCount; $i++) {
$type = \gettype($stack['args'][$i]);
switch ($type) {
case 'object':
$traceArgs .= sprintf('Object(%s)', \get_class($stack['args'][$i]));
break;
case 'array':
$traceArgs .= sprintf('Array(%d)', \count($stack['args'][$i]));
break;
case 'string':
$traceArgs .= sprintf('String(%d)', \strlen($stack['args'][$i]));
break;
default:
$traceArgs .= \gettype($stack['args'][$i]);
}
$traceArgs .= ($i !== ($argsCount - 1) ? ',' : '');
}
return $traceArgs;
}
}

View file

@ -15,10 +15,8 @@ class AppServiceProvider extends ServiceProvider
*/
public function boot()
{
if (config('app.env') !== 'local') {
URL::forceScheme('https');
}
$this->loadViewsFrom(public_path('/landings'), 'landings');
URL::forceScheme('https');
$this->loadViewsFrom(storage_path('app/public/landings'), 'landings');
JsonResource::withoutWrapping();
}

View file

@ -15,11 +15,11 @@ trait GenerateBR
public function generateBase(Request $request, Coupon $coupon)
{
putenv('GDFONTPATH=' . public_path() . '/generation/src/');
$arial = public_path() . '/generation/src/arial.ttf';
$arialbd = public_path() . '/generation/src/arialbd.ttf';
$impact = public_path() . '/generation/src/impact.ttf';
$hand_picture = 'generation/src/hand.png';
$mask_img = 'generation/src/mask.png';
$arial = public_path() . '/build/assets/fonts/coupons/arial.ttf';
$arialbd = public_path() . '/build/assets/fonts/coupons/arialbd.ttf';
$impact = public_path() . '/build/assets/fonts/coupons/impact.ttf';
$hand_picture = 'build/assets/images/coupons/hand.png';
$mask_img = 'build/assets/images/coupons/mask.png';
if (null !== $request->input('site_id')) {
$site = Site::where(['id' => $request->input('site_id')])->first();
@ -197,14 +197,14 @@ trait GenerateBR
// ---- Créer le fichier final
imagepng($mask, public_path('generation') . "/" . $coupon->coupon_code . ".png", 9);
imagepng($mask, storage_path('app/public/modelfiles/coupons') . "/" . $coupon->coupon_code . ".png", 9);
// ---- Libère la mémoire associée aux images manipulées
imagedestroy($logo);
imagedestroy($pdt);
imagedestroy($mask);
return "generation/" . $coupon->coupon_code . ".png";
return "storage/modelfiles/coupons/" . $coupon->coupon_code . ".png";
}
function imagettftextSp($image, $size, $angle, $x, $y, $color, $font, $text, $spacing = 0)
@ -229,30 +229,29 @@ trait GenerateBR
function imagettftext_contour($image, $fontsize, $angle, $x, $y, $color, $font, $text, $color_contour, $contour_haut = 1, $contour_bas = 1, $contour_gauche = 1, $contour_droite = 1)
{
$coords_x = array();
$coords_x = array();
$coords_y = array();
for ($i_y = -$contour_haut; $i_y <= $contour_bas; $i_y++) {
$y_temp = @array_fill(0, $contour_gauche + $contour_droite + 1, $i_y);
$x_temp = array();
$y_temp = @array_fill(0, $contour_gauche + $contour_droite + 1, $i_y);
$x_temp = array();
for ($i_x = -$contour_gauche; $i_x <= $contour_droite; $i_x++) {
$x_temp[] = $i_x;
$x_temp[] = $i_x;
}
$coords_x = array_merge($coords_x, $x_temp);
$coords_y = array_merge($coords_y, $y_temp);
$coords_x = array_merge($coords_x, $x_temp);
$coords_y = array_merge($coords_y, $y_temp);
}
$c = count($coords_x);
$c = count($coords_x);
for ($i = 0; $i < $c; $i++) {
imagettftext($image, $fontsize, $angle, $x + $coords_x[$i], $y + $coords_y[$i], $color_contour, $font, $text);
imagettftext($image, $fontsize, $angle, $x + $coords_x[$i], $y + $coords_y[$i], $color_contour, $font, $text);
}
imagettftext($image, $fontsize, $angle, $x, $y, $color, $font, $text);
imagettftext($image, $fontsize, $angle, $x, $y, $color, $font, $text);
}
public function printCp($coupon, $contact)
{
$arial = public_path() . '/generation/src/arial.ttf';
$arialbd = public_path() . '/generation/src/arialbd.ttf';
$impact = public_path() . '/generation/src/impact.ttf';
$arial = public_path() . '/build/assets/fonts/coupons/arial.ttf';
$arialbd = public_path() . '/build/assets/fonts/coupons/arialbd.ttf';
// ---- Masque dans lequel l'image finale sera placée
$mask = imagecreatefrompng($coupon->coupon_image);
@ -399,9 +398,8 @@ trait GenerateBR
// VB 22/04/2021
public function printCoupon(Coupon $coupon, Customers $customer, $crmInfos)
{
$arial = public_path() . '/generation/src/arial.ttf';
$arialbd = public_path() . '/generation/src/arialbd.ttf';
$impact = public_path() . '/generation/src/impact.ttf';
$arial = public_path() . '/build/assets/fonts/coupons/arial.ttf';
$arialbd = public_path() . '/build/assets/fonts/coupons/arialbd.ttf';
// ---- Masque dans lequel l'image finale sera placée
$mask = imagecreatefrompng($coupon->coupon_image);

View file

@ -1,7 +1,10 @@
{
"name": "laravel/laravel",
"description": "The Laravel Framework.",
"keywords": ["framework", "laravel"],
"keywords": [
"framework",
"laravel"
],
"license": "MIT",
"type": "project",
"require": {
@ -11,13 +14,13 @@
"barryvdh/laravel-dompdf": "^0.9.0",
"doctrine/dbal": "^2.8",
"fideloper/proxy": "^4.0",
"kwaadpepper/exception-handler": "^0.0.8",
"laravel/framework": "^8.0",
"laravel/tinker": "^2.0",
"laravel/ui": "^3.0",
"laravelcollective/html": "6.*",
"ldc/ldccrmv2client": "^3.0",
"picqer/php-barcode-generator": "^0.3.0",
"sentry/sentry-laravel": "^4.13",
"szymach/c-pchart": "^3.0",
"uxweb/sweet-alert": "^2.0"
},
@ -35,9 +38,8 @@
},
"repositories": [
{
"name": "ldc/ldccrmv2client",
"type": "vcs",
"url": "https://voight001.lesidc.com/crm-client"
"type": "composer",
"url": "https://lab.s-web.fr/api/v4/group/23/-/packages/composer/packages.json"
}
],
"autoload": {
@ -56,8 +58,7 @@
},
"extra": {
"laravel": {
"dont-discover": [
]
"dont-discover": []
}
},
"scripts": {
@ -75,7 +76,13 @@
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true
"optimize-autoloader": true,
"gitlab-domains": [
"lab.s-web.fr"
],
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
}
},
"minimum-stability": "dev",
"prefer-stable": true

2991
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,17 +0,0 @@
<?php
return [
/**
* Send mail when app debug is turn off
* so you can be warned if an unhandled exception occurs
*/
'enableMaiLog' => !config('app.debug'),
/**
* Put a developper secured mailbox in here
* so you can receive email if things go mad
*/
'contactsList' => [
'equipe.web@ldc.fr'
]
];

View file

@ -87,6 +87,22 @@ return [
'driver' => 'errorlog',
'level' => 'debug',
],
'stdout' => [
'driver' => 'monolog',
'level' => env('LOG_LEVEL', 'debug'),
'handler' => StreamHandler::class,
'formatter' => env('LOG_STDOUT_FORMATTER'),
'with' => [
'stream' => 'php://stdout',
],
],
'k8s' => [
'driver' => 'stack',
'channels' => ['daily', 'stdout'],
'ignore_exceptions' => false,
],
],
];

61
config/sentry.php Normal file
View file

@ -0,0 +1,61 @@
<?php
return [
'dsn' => env('SENTRY_LARAVEL_DSN', env('SENTRY_DSN')),
// The release version of your application.
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')).
'release' => env('SENTRY_RELEASE'),
// When left empty or `null` the Laravel environment will be used.
'environment' => env('SENTRY_ENVIRONMENT'),
'breadcrumbs' => [
// Capture Laravel logs in breadcrumbs.
'logs' => true,
// Capture SQL queries in breadcrumbs.
'sql_queries' => true,
// Capture bindings on SQL queries logged in breadcrumbs.
'sql_bindings' => false,
// Capture queue job information in breadcrumbs.
'queue_info' => true,
// Capture command information in breadcrumbs.
'command_info' => true,
],
'tracing' => [
// Trace queue jobs as their own transactions.
'queue_job_transactions' => env('SENTRY_TRACE_QUEUE_ENABLED', false),
// Capture queue jobs as spans when executed on the sync driver.
'queue_jobs' => true,
// Capture SQL queries as spans.
'sql_queries' => true,
// Try to find out where the SQL query originated from and add it to the query spans.
'sql_origin' => true,
// Capture views as spans.
'views' => true,
// Indicates if the tracing integrations supplied by Sentry should be loaded.
'default_integrations' => true,
// Indicates that requests without a matching route should be traced.
'missing_routes' => false,
],
// @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#send-default-pii.
'send_default_pii' => env('SENTRY_SEND_DEFAULT_PII', false),
// @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#traces-sample-rate.
'traces_sample_rate' => env('SENTRY_TRACES_SAMPLE_RATE') === null ? null : (float)env('SENTRY_TRACES_SAMPLE_RATE'),
'before_send' => [\App\Lib\Helpers\ExceptionsHelper::class, 'sentryEventHandler'],
];

View file

@ -0,0 +1,85 @@
<?php
use App\Coupon;
use App\Site;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Str;
class MoveUploadedFilesToBuildFolder extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$couponModels = Coupon::query()->withTrashed()->get();
foreach ($couponModels as $couponModel) {
$couponModel->coupon_gs1code = self::getUpdatedFilePath($couponModel->coupon_gs1code ?? '');
$couponModel->coupon_thumbnail1 = self::getUpdatedFilePath($couponModel->coupon_thumbnail1 ?? '');
$couponModel->coupon_thumbnail2 = self::getUpdatedFilePath($couponModel->coupon_thumbnail2 ?? '');
$couponModel->coupon_thumbnail3 = self::getUpdatedFilePath($couponModel->coupon_thumbnail3 ?? '');
$couponModel->coupon_thumbnail4 = self::getUpdatedFilePath($couponModel->coupon_thumbnail4 ?? '');
$couponModel->coupon_image = self::getUpdatedFilePath($couponModel->coupon_image ?? '');
$couponModel->coupon_decoration_image = self::getUpdatedFilePath($couponModel->coupon_decoration_image ?? '');
$couponModel->coupon_logo_image = self::getUpdatedFilePath($couponModel->coupon_logo_image ?? '');
$couponModel->save();
}
$siteModels = Site::query()->get();
foreach ($siteModels as $siteModel) {
$siteModel->site_logo = self::getUpdatedFilePath($siteModel->site_logo ?? '',);
$siteModel->save();
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
$couponModels = Coupon::query()->withTrashed()->get();
foreach ($couponModels as $couponModel) {
$couponModel->coupon_gs1code = self::getOldFilePath($couponModel->coupon_gs1code ?? '');
$couponModel->coupon_thumbnail1 = self::getOldFilePath($couponModel->coupon_thumbnail1 ?? '');
$couponModel->coupon_thumbnail2 = self::getOldFilePath($couponModel->coupon_thumbnail2 ?? '');
$couponModel->coupon_thumbnail3 = self::getOldFilePath($couponModel->coupon_thumbnail3 ?? '');
$couponModel->coupon_thumbnail4 = self::getOldFilePath($couponModel->coupon_thumbnail4 ?? '');
$couponModel->coupon_image = self::getOldFilePath($couponModel->coupon_image ?? '');
$couponModel->coupon_decoration_image = self::getOldFilePath($couponModel->coupon_decoration_image ?? '');
$couponModel->coupon_logo_image = self::getOldFilePath($couponModel->coupon_logo_image ?? '');
$couponModel->save();
}
$siteModels = Site::query()->get();
foreach ($siteModels as $siteModel) {
$siteModel->site_logo = self::getOldFilePath($siteModel->site_logo ?? '',);
$siteModel->save();
}
}
/**
* Get the updated file path (with 'storage/modelfiles' instead of folder in public)
* @param string $path
*
* @return string
*/
private static function getUpdatedFilePath(string $path): string
{
return !empty($path) ? Str::start($path, 'storage/modelfiles/') : '';
}
/**
* Get the old file path (with 'uploads/images' instead of 'storage/modelfiles')
* @param string $path
*
* @return string
*/
private static function getOldFilePath(string $path): string
{
return Str::replaceFirst('storage/modelfiles/', '', $path);
}
}

30
k8s-queues.sh Normal file
View file

@ -0,0 +1,30 @@
#!/usr/bin/env bash
# From https://stackoverflow.com/a/76488746/17320077
work=true
function stopQueues() {
echo "Stopping queues..."
work=false
php artisan queue:restart
sleep 45s
exit 0
}
# handle graceful shutdown
trap "" SIGPIPE
trap stopQueues EXIT
echo "Starting PHP queue workers..."
while $work; do
php artisan queue:work database --queue=default,database --sleep=3 --tries=3 --max-time=3600 --no-interaction
sleep 1
done
# Si besoin de plusieurs threads puis terminer par un done sans &
#(while $work; do
# php artisan queue:work --queue="$(grep -oP 'SQS_QUEUE=\K.*' .env)" --no-interaction
# sleep 1
#done &)

177
manifest.yaml Normal file
View file

@ -0,0 +1,177 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: ${CI_PROJECT_PATH_SLUG}-config-map
data:
APP_NAME: "${APP_APP_NAME}"
APP_ENV: "${APP_APP_ENV}"
APP_KEY: "${APP_APP_KEY}"
APP_DEBUG: "${APP_APP_DEBUG}"
APP_URL: "https://${REDIRECT}${APP_DOMAIN}"
LOG_CHANNEL: "k8s"
DB_HOST: "mariadb.common-services"
DB_DATABASE: "${APP_DB_DATABASE}"
DB_USERNAME: "${APP_DB_USERNAME}"
DB_PASSWORD: "${APP_DB_PASSWORD}"
MAIL_HOST: "${APP_MAIL_HOST}"
MAIL_PORT: "${APP_MAIL_PORT}"
MAIL_USERNAME: "${APP_MAIL_USERNAME}"
MAIL_PASSWORD: "${APP_MAIL_PASSWORD}"
MAIL_ENCRYPTION: "${APP_MAIL_ENCRYPTION}"
MAIL_FROM_ADDRESS: "${APP_MAIL_FROM_ADDRESS}"
MAIL_FROM_NAME: "${APP_APP_NAME}"
CRM_ENDPOINT: "${APP_CRM_ENDPOINT}"
SENTRY_LARAVEL_DSN: "${APP_SENTRY_LARAVEL_DSN}"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ${CI_PROJECT_PATH_SLUG}-pvc-storage
spec:
storageClassName: "csi-cinder-classic"
accessModes:
- ReadWriteOnce
resources:
requests:
storage: $APP_PVC_SIZE
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${CI_PROJECT_PATH_SLUG}-deployment
spec:
replicas: 1
selector:
matchLabels:
app: ${CI_PROJECT_PATH_SLUG}-app
template:
metadata:
labels:
app: ${CI_PROJECT_PATH_SLUG}-app
spec:
containers:
- name: ${CI_PROJECT_PATH_SLUG}-container
image: $IMAGE_TAG
envFrom:
- configMapRef:
name: ${CI_PROJECT_PATH_SLUG}-config-map
volumeMounts:
- name: storage
mountPath: /app/srcs/storage
- name: ${CI_PROJECT_PATH_SLUG}-queue-worker
image: $IMAGE_TAG
lifecycle:
preStop:
exec:
command:
[
"/bin/sh",
"-c",
"kill -s TERM 1; while test $(ps ax | grep -v grep | grep bash | wc -l) -gt 0; do sleep 2s; done",
]
resources:
limits:
memory: 512Mi
cpu: "1"
command:
- "bash"
args:
- "k8s-queues.sh"
envFrom:
- configMapRef:
name: ${CI_PROJECT_PATH_SLUG}-config-map
volumeMounts:
- name: storage
mountPath: /app/srcs/storage
imagePullSecrets:
- name: docker-registry-credentials
securityContext:
runAsUser: 33
runAsGroup: 33
fsGroup: 33
fsGroupChangePolicy: "OnRootMismatch"
volumes:
- name: storage
persistentVolumeClaim:
claimName: ${CI_PROJECT_PATH_SLUG}-pvc-storage
---
apiVersion: v1
kind: Service
metadata:
name: ${CI_PROJECT_PATH_SLUG}-service
spec:
selector:
app: ${CI_PROJECT_PATH_SLUG}-app
ports:
- port: 80
targetPort: 443
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: ${CI_PROJECT_PATH_SLUG}-ingress-route
spec:
entryPoints:
- web
- websecure
routes:
- match: Host(`${APP_DOMAIN}`) || Host(`www.${APP_DOMAIN}`)
kind: Rule
services:
- name: ${CI_PROJECT_PATH_SLUG}-service
port: 80
middlewares:
- name: redirect-no-www-to-www
tls:
secretName: ${CRT_TLS_NAME}
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: ${CI_PROJECT_PATH_SLUG}-cronjob
spec:
schedule: "* * * * *"
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- ${CI_PROJECT_PATH_SLUG}-app
topologyKey: "kubernetes.io/hostname"
namespaces: [$KUBE_NAMESPACE]
containers:
- name: ${CI_PROJECT_PATH_SLUG}-scheduled
image: $IMAGE_TAG
imagePullPolicy: IfNotPresent
command:
- "php"
args:
- "artisan"
- "schedule:run"
envFrom:
- configMapRef:
name: ${CI_PROJECT_PATH_SLUG}-config-map
volumeMounts:
- name: storage
mountPath: /app/srcs/storage
imagePullSecrets:
- name: docker-registry-credentials
volumes:
- name: storage
persistentVolumeClaim:
claimName: ${CI_PROJECT_PATH_SLUG}-pvc-storage
securityContext:
runAsUser: 33
runAsGroup: 33
fsGroup: 33
fsGroupChangePolicy: "OnRootMismatch"
restartPolicy: OnFailure

27276
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -2,24 +2,38 @@
"private": true,
"scripts": {
"dev": "npm run development",
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "npm run development -- --watch",
"watch-poll": "npm run watch -- --watch-poll",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
"development": "mix",
"watch": "mix watch",
"watch-poll": "mix watch -- --watch-options-poll=1000",
"hot": "mix watch --hot",
"prod": "npm run production",
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
"production": "mix --production"
},
"devDependencies": {
"axios": "^0.18",
"axios": "^1.8.4",
"bootstrap": "^4.0.0",
"browser-sync": "^2.24.7",
"browser-sync-webpack-plugin": "2.0.1",
"cross-env": "^5.1",
"clean-webpack-plugin": "^4.0.0",
"eslint": "^9.23.0",
"eslint-webpack-plugin": "^5.0.0",
"jquery": "^3.2",
"laravel-mix": "^2.0",
"laravel-mix": "^6.0",
"laravel-mix-clean": "^0.1.0",
"laravel-mix-eslint": "^0.2.0",
"laravel-mix-polyfill": "^3.0.1",
"laravel-mix-stylelint": "^1.0.0",
"lodash": "^4.17.5",
"popper.js": "^1.12",
"resolve-url-loader": "^5.0.0",
"sass": "^1.86.0",
"sass-loader": "^12.6.0",
"stylelint": "^16.16.0",
"stylelint-config-idiomatic-order": "^10.0.0",
"stylelint-config-sass-guidelines": "^12.1.0",
"stylelint-config-standard-scss": "^14.0.0",
"stylelint-scss": "^6.11.1",
"stylelint-webpack-plugin": "^5.0.1",
"sweetalert": "^2.0.8",
"vue": "^2.5.7"
"vue": "^2.5.7",
"webpack-cli": "^6.0.1"
}
}

12
production-resources.yaml Normal file
View file

@ -0,0 +1,12 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: $APP_DOMAIN
spec:
dnsNames:
- $APP_DOMAIN
- www.${APP_DOMAIN}
secretName: $APP_DOMAIN
issuerRef:
name: $APP_CERT_ISSUER
kind: ClusterIssuer

7410
public/css/app.css vendored

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5 KiB

83
public/js/Chart.js vendored

File diff suppressed because one or more lines are too long

83
public/js/ajax.js vendored

File diff suppressed because one or more lines are too long

94
public/js/app.js vendored

File diff suppressed because one or more lines are too long

View file

@ -1,6 +0,0 @@
{
"/js/app.js": "/js/app.js",
"/css/app.css": "/css/app.css",
"/js/ajax.js": "/js/ajax.js",
"/js/Chart.js": "/js/Chart.js"
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.5 KiB

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1024 1024"><defs><linearGradient id="a" x1="50.31%" x2="50%" y1="74.74%" y2="0%"><stop offset="0%" stop-color="#FFE98A"/><stop offset="67.7%" stop-color="#B63E59"/><stop offset="100%" stop-color="#68126F"/></linearGradient><circle id="c" cx="603" cy="682" r="93"/><filter id="b" width="203.2%" height="203.2%" x="-51.6%" y="-51.6%" filterUnits="objectBoundingBox"><feOffset in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur in="shadowOffsetOuter1" result="shadowBlurOuter1" stdDeviation="32"/><feColorMatrix in="shadowBlurOuter1" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/></filter><linearGradient id="d" x1="49.48%" x2="49.87%" y1="11.66%" y2="77.75%"><stop offset="0%" stop-color="#F7EAB9"/><stop offset="100%" stop-color="#E5765E"/></linearGradient><linearGradient id="e" x1="91.59%" x2="66.97%" y1="5.89%" y2="100%"><stop offset="0%" stop-color="#A22A50"/><stop offset="100%" stop-color="#EE7566"/></linearGradient><linearGradient id="f" x1="49.48%" x2="49.61%" y1="11.66%" y2="98.34%"><stop offset="0%" stop-color="#F7EAB9"/><stop offset="100%" stop-color="#E5765E"/></linearGradient><linearGradient id="g" x1="78.5%" x2="36.4%" y1="106.76%" y2="26.41%"><stop offset="0%" stop-color="#A22A50"/><stop offset="100%" stop-color="#EE7566"/></linearGradient></defs><g fill="none" fill-rule="evenodd"><rect width="1024" height="1024" fill="url(#a)"/><use fill="black" filter="url(#b)" xlink:href="#c"/><use fill="#FFF6CB" xlink:href="#c"/><g fill="#FFFFFF" opacity=".3" transform="translate(14 23)"><circle cx="203" cy="255" r="3" fill-opacity=".4"/><circle cx="82" cy="234" r="2"/><circle cx="22" cy="264" r="2" opacity=".4"/><circle cx="113" cy="65" r="3"/><circle cx="202" cy="2" r="2"/><circle cx="2" cy="114" r="2"/><circle cx="152" cy="144" r="2"/><circle cx="362" cy="224" r="2"/><circle cx="453" cy="65" r="3" opacity=".4"/><circle cx="513" cy="255" r="3"/><circle cx="593" cy="115" r="3"/><circle cx="803" cy="5" r="3" opacity=".4"/><circle cx="502" cy="134" r="2"/><circle cx="832" cy="204" r="2"/><circle cx="752" cy="114" r="2"/><circle cx="933" cy="255" r="3" opacity=".4"/><circle cx="703" cy="225" r="3"/><circle cx="903" cy="55" r="3"/><circle cx="982" cy="144" r="2"/><circle cx="632" cy="14" r="2"/></g><g transform="translate(0 550)"><path fill="#8E2C15" d="M259 5.47c0 5.33 3.33 9.5 10 12.5s9.67 9.16 9 18.5h1c.67-6.31 1-11.8 1-16.47 8.67 0 13.33-1.33 14-4 .67 4.98 1.67 8.3 3 9.97 1.33 1.66 2 5.16 2 10.5h1c0-5.65.33-9.64 1-11.97 1-3.5 4-10.03-1-14.53S295 7 290 3c-5-4-10-3-13 2s-5 7-9 7-5-3.53-5-5.53c0-2 2-5-1.5-5s-7.5 0-7.5 2c0 1.33 1.67 2 5 2z"/><path fill="url(#d)" d="M1024 390H0V105.08C77.3 71.4 155.26 35 297.4 35c250 0 250.76 125.25 500 125 84.03-.08 160.02-18.2 226.6-40.93V390z"/><path fill="url(#d)" d="M1024 442H0V271.82c137.51-15.4 203.1-50.49 356.67-60.1C555.24 199.3 606.71 86.59 856.74 86.59c72.78 0 124.44 10.62 167.26 25.68V442z"/><path fill="url(#e)" d="M1024 112.21V412H856.91c99.31-86.5 112.63-140.75 39.97-162.78C710.24 192.64 795.12 86.58 856.9 86.58c72.7 0 124.3 10.6 167.09 25.63z"/><path fill="url(#e)" d="M1024 285.32V412H857c99.31-86.6 112.63-140.94 39.97-163L1024 285.32z"/><path fill="url(#f)" d="M0 474V223.93C67.12 190.69 129.55 155 263 155c250 0 331.46 162.6 530 175 107.42 6.71 163-26.77 231-58.92V474H0z"/><path fill="url(#e)" d="M353.02 474H0V223.93C67.12 190.69 129.55 155 263 155c71.14 0 151.5 12.76 151.5 70.5 0 54.5-45.5 79.72-112.5 109-82.26 35.95-54.57 111.68 51.02 139.5z"/><path fill="url(#g)" d="M353.02 474H0v-14.8l302-124.7c-82.26 35.95-54.57 111.68 51.02 139.5z"/></g><g fill="#FFFFFF" opacity=".2" transform="translate(288 523)"><circle cx="250" cy="110" r="110"/><circle cx="420" cy="78" r="60"/><circle cx="70" cy="220" r="70"/></g><g fill="#FFFFFF" fill-rule="nonzero" opacity=".08" transform="translate(135 316)"><path d="M10 80.22a14.2 14.2 0 0 1 20 0 14.2 14.2 0 0 0 20 0l20-19.86a42.58 42.58 0 0 1 60 0l15 14.9a21.3 21.3 0 0 0 30 0 21.3 21.3 0 0 1 30 0l.9.9A47.69 47.69 0 0 1 220 110H0v-5.76c0-9.02 3.6-17.67 10-24.02zm559.1-66.11l5.9-5.86c11.07-11 28.93-11 40 0l10 9.94a14.19 14.19 0 0 0 20 0 14.19 14.19 0 0 1 20 0 16.36 16.36 0 0 0 21.3 1.5l8.7-6.47a33.47 33.47 0 0 1 40 0l4.06 3.03A39.6 39.6 0 0 1 755 48H555a47.77 47.77 0 0 1 14.1-33.89z"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 4.2 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

Before

Width:  |  Height:  |  Size: 633 KiB

After

Width:  |  Height:  |  Size: 633 KiB

View file

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 139 KiB

View file

Before

Width:  |  Height:  |  Size: 665 KiB

After

Width:  |  Height:  |  Size: 665 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

View file

View file

@ -1,4 +1,3 @@
// Body
$body-bg: #f8fafc;
@ -49,17 +48,17 @@ $media-expressions: (
// Variables
// --------------------------
$fa-font-path: "../webfonts" !default;
$fa-font-size-base: 16px !default;
$fa-css-prefix: fa !default;
$fa-version: "5.3.1" !default;
$fa-border-color: #eee !default;
$fa-inverse: #fff !default;
$fa-li-width: 2em !default;
$fa-font-path: "~@/fonts" !default;
$fa-font-size-base: 16px !default;
$fa-css-prefix: fa !default;
$fa-version: "5.3.1" !default;
$fa-border-color: #eee !default;
$fa-inverse: #fff !default;
$fa-li-width: 2em !default;
// Convenience function used to set content property
@function fa-content($fa-var) {
@return unquote("\"#{ $fa-var }\"");
@return unquote("\"#{ $fa-var }\"");
}
$fa-var-500px: \f26e;

View file

@ -1,5 +1,6 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@ -9,15 +10,16 @@
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
<link href="{{ asset('css/jquery.fancybox.min.css') }}" rel="stylesheet">
<link href="{{ mix('css/app.css', 'build') }}" rel="stylesheet">
<link href="{{ mix('css/jquery.fancybox.min.css', 'build') }}" rel="stylesheet">
</head>
<body class="code404">
<div>
<h2>Erreur 404</h2>
<p>La page que vous essayez d'atteindre n'existe pas ou a été déplacée</p>
</div>
</body>
</html>
</html>

View file

@ -1,5 +1,6 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@ -9,15 +10,16 @@
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
<link href="{{ asset('css/jquery.fancybox.min.css') }}" rel="stylesheet">
<link href="{{ mix('css/app.css', 'build') }}" rel="stylesheet">
<link href="{{ mix('css/jquery.fancybox.min.css', 'build') }}" rel="stylesheet">
</head>
<body class="code404" style="background-color:transparent;">
<div>
<h2>Erreur</h2>
<p>Une erreur est survenue, Connectez vous depuis un autre navigateur (Google Chrome, Firefox).</p>
</div>
</body>
</html>
</html>

View file

@ -1,5 +1,6 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@ -9,15 +10,16 @@
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
<link href="{{ asset('css/jquery.fancybox.min.css') }}" rel="stylesheet">
<link href="{{ mix('css/app.css', 'build') }}" rel="stylesheet">
<link href="{{ mix('css/jquery.fancybox.min.css', 'build') }}" rel="stylesheet">
</head>
<body class="code404">
<div>
<h2>Erreur 500</h2>
<p>Une erreur est survenue, Merci de réessayer ultérieurement.</p>
</div>
</body>
</html>
</html>

View file

@ -1,5 +1,6 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@ -10,71 +11,22 @@
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Scripts -->
<script src="{{ asset('js/app.js') }}" defer></script>
<script src="{{ asset('js/ajax.js') }}" defer></script>
<script src="{{ mix('js/app.js', 'build') }}" defer></script>
<script src="{{ mix('js/ajax.js', 'build') }}" defer></script>
<!-- Fonts -->
<link rel="dns-prefetch" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet" type="text/css">
<link href="https://fonts.gstatic.com" rel="dns-prefetch">
<link type="text/css" href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
<link href="{{ mix('css/app.css', 'build') }}" rel="stylesheet">
</head>
<body>
<div id="app">
<!-- <nav class="navbar navbar-expand-md navbar-light navbar-laravel">
<div class="container">
<a class="navbar-brand" href="{{ url('/') }}">
{{ config('app.name', 'Laravel') }}
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
</ul>
<ul class="navbar-nav ml-auto">
@guest
<li class="nav-item">
<a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
</li>
<li class="nav-item">
@if (Route::has('register'))
<a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
@endif
</li>
@else
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
{{ Auth::user()->name }} <span class="caret"></span>
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
{{ __('Logout') }}
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
@csrf
</form>
</div>
</li>
@endguest
</ul>
</div>
</div>
</nav> -->
<main class="py-4">
@yield('content')
</main>
</div>
</body>
</html>

View file

@ -1,5 +1,6 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@ -9,11 +10,11 @@
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
<link href="{{ asset('css/jquery.fancybox.min.css') }}" rel="stylesheet">
<link href="{{ mix('css/app.css', 'build') }}" rel="stylesheet">
<link href="{{ mix('css/jquery.fancybox.min.css', 'build') }}" rel="stylesheet">
</head>
<body>
<div class="wrapper">
@auth
@ -32,8 +33,9 @@
</div>
</body>
<!-- Scripts -->
<script src="{{ asset('js/app.js') }}" ></script>
<script src="{{ asset('js/ajax.js') }}" ></script>
<script src="{{ asset('js/jquery.fancybox.min.js') }}"></script>
<script src="{{ mix('js/app.js', 'build') }}"></script>
<script src="{{ mix('js/ajax.js', 'build') }}"></script>
<script src="{{ mix('js/jquery.fancybox.min.js', 'build') }}"></script>
@yield('scripts')
</html>

View file

@ -2,94 +2,103 @@
@section('content')
<h2><i class="fas fa-cogs"></i> Maintenance </h2>
<h2><i class="fas fa-cogs"></i> Maintenance </h2>
<div class="user-app">
<div class="heading">
<h5><i class="fas fa-users-cog"></i> Gestion des utilisateurs</h5>
<a href="{{ route('maintenance.user.create') }}" class="add-button"><i class="fas fa-plus-square"></i> Ajouter</a>
</div>
@if(isset($users) && count($users)>0)
<table>
<thead>
<th>Nom</th>
<th>Email</th>
<th>Rôle</th>
<th>Site liés</th>
<th></th>
</thead>
@foreach ($users as $user)
<tr>
<td><a href="{{ route('maintenance.user.edit', $user) }}">{{ $user->name }}</a></td>
<td>{{ $user->email }}</td>
<td>{{ $user->roles }}</td>
<td class="sites">{!! $user->linkedSites !!}</td>
@if (Auth::user()->id == $user->id)
<td></td>
@else
<td><a href="{{ route('maintenance.user.destroy', $user) }}"><i class="fas fa-times delete-icon"></i></a></td>
@endif
</tr>
@endforeach
<tfoot>
<div class="user-app">
<div class="heading">
<h5><i class="fas fa-users-cog"></i> Gestion des utilisateurs</h5>
<a class="add-button" href="{{ route('maintenance.user.create') }}"><i class="fas fa-plus-square"></i> Ajouter</a>
</div>
@if (isset($users) && count($users) > 0)
<table>
<thead>
<th>Nom</th>
<th>Email</th>
<th>Rôle</th>
<th>Site liés</th>
<th></th>
</thead>
@foreach ($users as $user)
<tr>
<td><a href="{{ route('maintenance.user.edit', $user) }}">{{ $user->name }}</a></td>
<td>{{ $user->email }}</td>
<td>{{ $user->roles }}</td>
<td class="sites">{!! $user->linkedSites !!}</td>
@if (Auth::user()->id == $user->id)
<td></td>
@else
<td><a href="{{ route('maintenance.user.destroy', $user) }}"><i class="fas fa-times delete-icon"></i></a></td>
@endif
</tr>
@endforeach
<tfoot>
<tr>
<td colspan="5">
</td>
</tr>
</tfoot>
</table>
{!! Form::open(['id' => 'delete-hidden']) !!}
</table>
{!! Form::open(['id' => 'delete-hidden']) !!}
@method('DELETE')
{!! Form::close() !!}
@endif
</div>
<div class="clean-app">
<h5><i class="fas fa-database"></i> Nettoyage de la base</h5>
<div class="clean-line">
<p>Nettoyer les murs <span>(datant de plus d'un an)</span></p>
<div class="btn-clean">
<div class="loader-mini">
<div class="lds-ripple"><div></div><div></div></div>
</div>
<a href="#" class="add-button clean-walls"><i class="fas fa-broom"></i> Nettoyer</a>
</div>
{!! Form::close() !!}
@endif
</div>
<div class="clean-line">
<p>Nettoyer les paniers <span>(qui ont été imprimés et datant de plus de deux mois)</span></p>
<div class="btn-clean">
<div class="loader-mini">
<div class="lds-ripple"><div></div><div></div></div>
<div class="clean-app">
<h5><i class="fas fa-database"></i> Nettoyage de la base</h5>
<div class="clean-line">
<p>Nettoyer les murs <span>(datant de plus d'un an)</span></p>
<div class="btn-clean">
<div class="loader-mini">
<div class="lds-ripple">
<div></div>
<div></div>
</div>
</div>
<a class="add-button clean-walls" href="#"><i class="fas fa-broom"></i> Nettoyer</a>
</div>
<a href="#" class="add-button clean-baskets"><i class="fas fa-broom"></i> Nettoyer</a>
</div>
</div>
<div class="clean-line">
<p>Nettoyer les contacts <span>(qui n'ont pas de paniers ni de murs liés et datant de plus d'un mois)</span></p>
<div class="btn-clean">
<div class="loader-mini">
<div class="lds-ripple"><div></div><div></div></div>
<div class="clean-line">
<p>Nettoyer les paniers <span>(qui ont été imprimés et datant de plus de deux mois)</span></p>
<div class="btn-clean">
<div class="loader-mini">
<div class="lds-ripple">
<div></div>
<div></div>
</div>
</div>
<a class="add-button clean-baskets" href="#"><i class="fas fa-broom"></i> Nettoyer</a>
</div>
<a href="#" class="add-button clean-contacts"><i class="fas fa-broom"></i> Nettoyer</a>
</div>
<div class="clean-line">
<p>Nettoyer les contacts <span>(qui n'ont pas de paniers ni de murs liés et datant de plus d'un mois)</span></p>
<div class="btn-clean">
<div class="loader-mini">
<div class="lds-ripple">
<div></div>
<div></div>
</div>
</div>
<a class="add-button clean-contacts" href="#"><i class="fas fa-broom"></i> Nettoyer</a>
</div>
</div>
<input id="csrf" name="csrf" type="hidden" value="<?php echo csrf_token(); ?>">
</div>
<div class="error-log">
<h5><i class="fas fa-file-alt"></i> Erreur d'appels :</h5>
<canvas id="myChart" height="300" width="200"></canvas>
</div>
<input type="hidden" name="csrf" id="csrf" value="<?php echo csrf_token(); ?>">
</div>
<div class="error-log">
<h5><i class="fas fa-file-alt"></i> Erreur d'appels :</h5>
<canvas id="myChart" height="300" width="200"></canvas>
</div>
@endsection
@section('scripts')
<script src="{{ asset('js/Chart.js') }}" charset="utf-8"></script>
<script src="{{ mix('js/Chart.js', 'build') }}" charset="utf-8"></script>
<script>
var truc = {!! json_encode($logs, JSON_UNESCAPED_SLASHES) !!};
var labels = [];
var errorCount = [];
$.each(truc, function(index, value){
$.each(truc, function(index, value) {
labels.push(value[0]);
errorCount.push(value[1]);
})
@ -116,7 +125,7 @@
scales: {
yAxes: [{
ticks: {
beginAtZero:true,
beginAtZero: true,
stepSize: 1,
}
}]
@ -133,62 +142,65 @@
});
$('.clean-app').on('click', '.clean-contacts', function(event){
$('.clean-app').on('click', '.clean-contacts', function(event) {
event.preventDefault();
let loader = $(this).parent().find('.loader-mini');
loader.show();
$.fancyConfirm({
title : "<i class='fas fa-broom'></i> Nettoyage",
message : "Êtes-vous sûr de vouloir nettoyer la base des contacts qui n'ont pas de paniers ni de murs liés et datant de plus d'un mois ?",
okButton : 'Supprimer',
noButton : 'Annuler',
callback : function (value) {
title: "<i class='fas fa-broom'></i> Nettoyage",
message: "Êtes-vous sûr de vouloir nettoyer la base des contacts qui n'ont pas de paniers ni de murs liés et datant de plus d'un mois ?",
okButton: 'Supprimer',
noButton: 'Annuler',
callback: function(value) {
if (value) {
$.ajax({
url: '/displayCleanContacts',
type: 'POST',
data: {},
dataType: 'JSON',
success: function (response) {
success: function(response) {
$.fancyConfirm({
title : "<i class='fas fa-broom'></i> Nettoyage",
message : "Vous allez supprimer "+response.contact_count+" contacts qui ont plus d'un mois et qui n'ont pas de panier ni de murs en cours, Êtes-vous sûr ?",
okButton : 'Supprimer',
noButton : "Annuler",
callback : function (value) {
title: "<i class='fas fa-broom'></i> Nettoyage",
message: "Vous allez supprimer " + response.contact_count +
" contacts qui ont plus d'un mois et qui n'ont pas de panier ni de murs en cours, Êtes-vous sûr ?",
okButton: 'Supprimer',
noButton: "Annuler",
callback: function(value) {
if (value) {
$.ajax({
url: '/cleanContacts',
type: 'POST',
data: {},
dataType: 'JSON',
success: function (response) {
success: function(response) {
$.fancyInfo({
title : "<i class='fas fa-info-circle'></i> Succès",
message : "Le nettoyage des contacts a bien été effectué.",
okButton : 'Valider',
noButton : "",
callback : function (value) {
if (value) {
}else {
loader.hide();
title: "<i class='fas fa-info-circle'></i> Succès",
message: "Le nettoyage des contacts a bien été effectué.",
okButton: 'Valider',
noButton: "",
callback: function(
value) {
if (
value) {} else {
loader
.hide();
}
}
});
loader.hide();
},
error: function (e) {
error: function(e) {
console.log(e.responseText);
}
});
}else {
} else {
loader.hide();
}
}
});
loader.hide();
},
error: function (e) {
error: function(e) {
console.log(e.responseText);
}
});
@ -197,38 +209,38 @@
}
}
});
})
$('.clean-app').on('click', '.clean-walls', function(event){
$('.clean-app').on('click', '.clean-walls', function(event) {
event.preventDefault();
let loader = $(this).parent().find('.loader-mini');
loader.show();
$.fancyConfirm({
title : "<i class='fas fa-broom'></i> Nettoyage",
message : "Êtes-vous sûr de vouloir nettoyer la base des mur imprimés ?",
okButton : 'Supprimer',
noButton : 'Annuler',
callback : function (value) {
title: "<i class='fas fa-broom'></i> Nettoyage",
message: "Êtes-vous sûr de vouloir nettoyer la base des mur imprimés ?",
okButton: 'Supprimer',
noButton: 'Annuler',
callback: function(value) {
if (value) {
$.ajax({
url: '/cleanWalls',
type: 'POST',
data: {},
dataType: 'JSON',
success: function (response) {
success: function(response) {
$.fancyInfo({
title : "<i class='fas fa-info-circle'></i> Succès",
message : "Le nettoyage des murs a bien été effectué.",
okButton : 'Valider',
noButton : "",
callback : function (value) {
title: "<i class='fas fa-info-circle'></i> Succès",
message: "Le nettoyage des murs a bien été effectué.",
okButton: 'Valider',
noButton: "",
callback: function(value) {
}
});
loader.hide();
},
error: function (e) {
error: function(e) {
console.log(e.responseText);
}
});
@ -237,38 +249,38 @@
}
}
});
})
$('.clean-app').on('click', '.clean-baskets', function(event){
$('.clean-app').on('click', '.clean-baskets', function(event) {
event.preventDefault();
let loader = $(this).parent().find('.loader-mini');
loader.show();
$.fancyConfirm({
title : "<i class='fas fa-broom'></i> Nettoyage",
message : "Êtes-vous sûr de vouloir nettoyer la base des panier imprimés ?",
okButton : 'Supprimer',
noButton : 'Annuler',
callback : function (value) {
title: "<i class='fas fa-broom'></i> Nettoyage",
message: "Êtes-vous sûr de vouloir nettoyer la base des panier imprimés ?",
okButton: 'Supprimer',
noButton: 'Annuler',
callback: function(value) {
if (value) {
$.ajax({
url: '/cleanBaskets',
type: 'POST',
data: {},
dataType: 'JSON',
success: function (response) {
success: function(response) {
$.fancyInfo({
title : "<i class='fas fa-info-circle'></i> Succès",
message : "Le nettoyage des paniers a bien été effectué.",
okButton : 'Valider',
noButton : "",
callback : function (value) {
title: "<i class='fas fa-info-circle'></i> Succès",
message: "Le nettoyage des paniers a bien été effectué.",
okButton: 'Valider',
noButton: "",
callback: function(value) {
}
});
loader.hide();
},
error: function (e) {
error: function(e) {
console.log(e.responseText);
}
});
@ -277,7 +289,7 @@
}
}
});
})
</script>
@endsection
@endsection

View file

@ -2,16 +2,17 @@
@section('content')
<div class="title-stat">
<h2><i class="fas fa-chart-line"></i>Statistiques de la campagne</h2>
<h2><i class="fas fa-chart-line"></i>Statistiques de la campagne</h2>
<div class="selector-stat">
<h4>Période :</h4>
<input type="date" name="startdate" id="startdate" value="{{ $startdate }}">
<input type="date" name="enddate" id="enddate" value="{{ $enddate }}">
<input type="hidden" name="csrf" id="csrf" value="<?php echo csrf_token(); ?>">
<a href="#" class="add-button reload-stats" id="reloader" data-campid="{{ $campagne->camp_id }}"><i class="fas fa-chart-line"></i> Charger</a>
<input id="startdate" name="startdate" type="date" value="{{ $startdate }}">
<input id="enddate" name="enddate" type="date" value="{{ $enddate }}">
<input id="csrf" name="csrf" type="hidden" value="<?php echo csrf_token(); ?>">
<a class="add-button reload-stats" id="reloader" data-campid="{{ $campagne->camp_id }}" href="#"><i
class="fas fa-chart-line"></i> Charger</a>
</div>
</div>
<div class="statistiquesGlobales">
<table class="campstat">
<thead>
@ -32,17 +33,17 @@
<tr>
<td>Impression limité</td>
<td>
@if ( $campagne->camp_limitedPrinting === 1)
@if ($campagne->camp_limitedPrinting === 1)
Oui
@else
Non
@endif
@endif
</td>
</tr>
<tr>
<td>Date de création</td>
<td>
{{ date('d/m/Y', strtotime($campagne->camp_createdate)) }}
{{ date('d/m/Y', strtotime($campagne->camp_createdate)) }}
</td>
</tr>
<tr>
@ -50,11 +51,11 @@
Publiée ?
</td>
<td>
@if ( $campagne->camp_published === 1)
@if ($campagne->camp_published === 1)
<i class="fas fa-check-circle green"></i>
@else
<i class="fas fa-times-circle red"></i>
@endif
@endif
</td>
</tr>
</table>
@ -68,50 +69,55 @@
<div class="title">
<h3>Les Coupons</h3>
<div class="btns">
<a href="#" class="add-button open-all-stats"><i class="fas fa-plus-square"></i> Tout Ouvrir</a>
<a href="#" class="add-button close-all-stats"><i class="fas fa-minus-square"></i> Tout Fermer</a>
<a class="add-button open-all-stats" href="#"><i class="fas fa-plus-square"></i> Tout Ouvrir</a>
<a class="add-button close-all-stats" href="#"><i class="fas fa-minus-square"></i> Tout Fermer</a>
</div>
</div>
@foreach ($campagne->coupons as $coupon)
<div class="couponStatsDetail">
<h4><span class="expand">{{ $coupon->coupon_name }}</span> <span style="background-color:{{$coupon->color}}" class="couponLegend" data-label="{{ $coupon->coupon_name }}"></span></h4>
<h4><span class="expand">{{ $coupon->coupon_name }}</span> <span class="couponLegend"
data-label="{{ $coupon->coupon_name }}" style="background-color:{{ $coupon->color }}"></span></h4>
<div class="content">
<figure>
<a href="{{ asset($coupon->coupon_image) }}" data-fancybox="images" data-caption="{{ $coupon->coupon_name }}"><img src="{{ asset($coupon->coupon_image) }}" alt=""></a>
<a data-fancybox="images" data-caption="{{ $coupon->coupon_name }}" href="{{ asset($coupon->coupon_image) }}"><img
src="{{ asset($coupon->coupon_image) }}" alt=""></a>
</figure>
<p><span>Code de traitement :</span> <span>{{ $coupon->coupon_code }}</span></p>
<p><span>Valeur :</span> <span>{{ str_replace('.', ',', number_format($coupon->coupon_value, 2))}} €</span></p>
<p><span>Quantité Totale :</span> <span>{{$coupon->quantity}}</span></p>
<p><span>Quantité Restante :</span> <span>{{$coupon->remain}}</span></p>
<p><span>Quantité Diffusé :</span> <span>{{$coupon->diffused}}</span></p>
<p><span>Quantité Imprimé :</span> <span>{{$coupon->quantity - $coupon->remain}}</span></p>
@if($coupon->coupon_mindatediffusion != null)
<p><span>Date de début de diffusion :</span> <span>{{ date('d/m/Y', strtotime($coupon->coupon_mindatediffusion)) }}</span></p>
<p><span>Valeur :</span> <span>{{ str_replace('.', ',', number_format($coupon->coupon_value, 2)) }} €</span></p>
<p><span>Quantité Totale :</span> <span>{{ $coupon->quantity }}</span></p>
<p><span>Quantité Restante :</span> <span>{{ $coupon->remain }}</span></p>
<p><span>Quantité Diffusé :</span> <span>{{ $coupon->diffused }}</span></p>
<p><span>Quantité Imprimé :</span> <span>{{ $coupon->quantity - $coupon->remain }}</span></p>
@if ($coupon->coupon_mindatediffusion != null)
<p><span>Date de début de diffusion :</span>
<span>{{ date('d/m/Y', strtotime($coupon->coupon_mindatediffusion)) }}</span></p>
@else
<p><span>Date de début de diffusion :</span> <span>n/c</span></p>
@endif
@if($coupon->coupon_maxdatediffusion != null)
<p><span>Date de fin de diffusion :</span> <span>{{ date('d/m/Y', strtotime($coupon->coupon_maxdatediffusion)) }}</span></p>
@if ($coupon->coupon_maxdatediffusion != null)
<p><span>Date de fin de diffusion :</span>
<span>{{ date('d/m/Y', strtotime($coupon->coupon_maxdatediffusion)) }}</span></p>
@else
<p><span>Date de fin de diffusion :</span> <span>n/c</span></p>
@endif
@if ( $coupon->coupon_published == 1)
@if ($coupon->coupon_published == 1)
<p><span>Publié ?</span><span><i class="fas fa-check-circle green"></i></span></p>
@else
<p><span>Publié ?</span><span><i class="fas fa-times-circle red"></i></span></p>
<p><span>Publié ?</span><span><i class="fas fa-times-circle red"></i></span></p>
@endif
@if ( $coupon->isPrintable == 1)
@if ($coupon->isPrintable == 1)
<p><span>Imprimable ?</span><span><i class="fas fa-check-circle green"></i></span></p>
@else
@if ( $coupon->isPaused == 1)
@if ($coupon->isPaused == 1)
<p><span>Imprimable ?</span><span><i class="fas fa-pause-circle orange"></i></span></p>
@else
<p><span>Imprimable ?</span><span><i class="fas fa-times-circle red"></i></span></p>
@endif
@endif
<input type="hidden" class="couponStatData" data-color="{{ $coupon->color }}" data-name="{{ $coupon->coupon_name }}" id="{{ $coupon->coupon_code }}" >
<input class="couponStatData" id="{{ $coupon->coupon_code }}" data-color="{{ $coupon->color }}"
data-name="{{ $coupon->coupon_name }}" type="hidden">
</div>
</div>
@endforeach
</div>
@ -123,198 +129,195 @@
@endsection
@section('scripts')
<script src="{{ asset('js/Chart.js') }}" charset="utf-8"></script>
<script>
var myLine;
var myLineGlobal;
function load_graphs(campid, startdate, enddate){
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('#csrf').val()
}
});
$.ajax({
url: '/loadStat',
type: 'POST',
data: {
campid: campid,
startdate : startdate,
enddate : enddate,
},
dataType: 'JSON',
success: function (response) {
//console.log("label", response.labels.reverse());
//Mise en place du graphique globa
var configGlobal = {
type: 'line',
data: {
labels: response.labels,
datasets: [{
label : "Coupons",
data: response.dataGlobal,
<script src="{{ mix('js/Chart.js', 'build') }}" charset="utf-8"></script>
<script>
var myLine;
var myLineGlobal;
function load_graphs(campid, startdate, enddate) {
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('#csrf').val()
}
});
$.ajax({
url: '/loadStat',
type: 'POST',
data: {
campid: campid,
startdate: startdate,
enddate: enddate,
},
dataType: 'JSON',
success: function(response) {
//console.log("label", response.labels.reverse());
//Mise en place du graphique globa
var configGlobal = {
type: 'line',
data: {
labels: response.labels,
datasets: [{
label: "Coupons",
data: response.dataGlobal,
fill: false,
borderColor: '#de9700',
backgroundColor: '#de9700',
borderWidth: 1,
}]
},
options: {
maintainAspectRatio: true,
responsive: true,
legend: {
display: false,
},
title: {
display: false,
},
scales: {
yAxes: [{
ticks: {
fontColor: '#adb5bd'
},
}],
xAxes: [{
ticks: {
fontColor: '#adb5bd'
},
}]
}
}
}
var ctxGlobal = document.getElementById("myChartGlobal").getContext('2d');
myLineGlobal = new Chart(ctxGlobal, configGlobal);
//Mise en place du graphique spec
var dataSpecCp = [];
$.each(response.dataSpec, function(coupon_code, data) {
var dataset = {
label: $('#' + coupon_code).attr('data-name'),
borderColor: $('#' + coupon_code).attr('data-color'),
borderWidth: 1,
backgroundColor: $('#' + coupon_code).attr('data-color'),
fill: false,
borderColor: '#de9700',
backgroundColor: '#de9700',
borderWidth :1,
}]
},
options: {
maintainAspectRatio: true,
responsive: true,
legend: {
display:false,
data: data,
}
dataSpecCp.push(dataset);
})
var config = {
type: 'line',
data: {
labels: response.labels,
datasets: dataSpecCp,
},
title: {
display:false,
},
scales: {
yAxes: [{
ticks: {
fontColor: '#adb5bd'
},
}],
xAxes: [{
options: {
maintainAspectRatio: true,
responsive: true,
legend: {
display: false,
position: 'bottom',
},
title: {
display: false,
text: 'Consommation de BR par jour'
},
scales: {
yAxes: [{
ticks: {
fontColor: '#adb5bd'
},
}],
xAxes: [{
ticks: {
fontColor: '#adb5bd'
},
}]
}
}
}
}
var ctxGlobal = document.getElementById("myChartGlobal").getContext('2d');
myLineGlobal = new Chart(ctxGlobal, configGlobal);
//Mise en place du graphique spec
var dataSpecCp = [];
$.each(response.dataSpec, function(coupon_code, data){
var dataset = {
label : $('#'+coupon_code).attr('data-name'),
borderColor: $('#'+coupon_code).attr('data-color'),
borderWidth :1,
backgroundColor: $('#'+coupon_code).attr('data-color'),
fill: false,
data: data,
}
dataSpecCp.push(dataset);
})
var config = {
type: 'line',
data: {
labels: response.labels,
datasets: dataSpecCp,
},
options: {
maintainAspectRatio: true,
responsive: true,
legend: {
display:false,
position:'bottom',
},
title: {
display:false,
text: 'Consommation de BR par jour'
},
scales: {
yAxes: [{
ticks: {
fontColor: '#adb5bd'
},
}],
xAxes: [{
ticks: {
fontColor: '#adb5bd'
},
}]
}
}
}
var ctx = document.getElementById("myChart").getContext('2d');
myLine = new Chart(ctx, config);
},
error: function (e) {
console.log(e.responseText);
}
});
}
window.onload = function() {
load_graphs($('#reloader').attr('data-campid'), $('#startdate').val(), $('#enddate').val());
// ---- SHOW ALL
$('.fa-eye').on('click', function(){
$('.couponLegend').each(function(){
$(this).find('.line').remove();
})
myLine.data.datasets.forEach(function(ds) {
ds.hidden = false;
});
myLine.update();
})
// ---- HIDE ALL
$('.fa-eye-slash').on('click', function(){
$('.couponLegend').each(function(){
$(this).append('<div class="line"></div>');
})
myLine.data.datasets.forEach(function(ds) {
ds.hidden = true;
});
myLine.update();
})
$('.couponStats').on('click', '.couponLegend', function(e){
if($(this).find('.line').length){
$(this).find('.line').remove();
} else {
$(this).append('<div class="line"></div>');
}
var label = $(this).attr('data-label');
myLine.data.datasets.forEach(function(ds) {
if(ds.label == label){
ds.hidden = !ds.hidden;
}
});
myLine.update();
})
}
$('.reload-stats').on('click', function(event){
var campid = $(this).attr('data-campid');
var startdate = $('#startdate').val();
var enddate = $('#enddate').val();
event.preventDefault();
//console.log(enddate);
if(startdate != "" && enddate != ""){
teststartdate = new Date(startdate);
testenddate = new Date(enddate);
if(teststartdate < testenddate){
myLine.destroy();
myLineGlobal.destroy();
load_graphs(campid, startdate, enddate);
} else {
$.fancyConfirm({
title : "<i class='fas fa-times-circle red'></i> Erreur",
message : "La date de fin doit être supérieure à la date de début.",
okButton : 'Valider',
noButton : "",
callback : function (value) {
}
});
}
} else {
$.fancyConfirm({
title : "<i class='fas fa-times-circle red'></i> Erreur",
message : "Veuillez remplir les deux dates pour voir les données associées.",
okButton : 'Valider',
noButton : "",
callback : function (value) {
var ctx = document.getElementById("myChart").getContext('2d');
myLine = new Chart(ctx, config);
},
error: function(e) {
console.log(e.responseText);
}
});
}
})
</script>
@endsection
window.onload = function() {
load_graphs($('#reloader').attr('data-campid'), $('#startdate').val(), $('#enddate').val());
// ---- SHOW ALL
$('.fa-eye').on('click', function() {
$('.couponLegend').each(function() {
$(this).find('.line').remove();
})
myLine.data.datasets.forEach(function(ds) {
ds.hidden = false;
});
myLine.update();
})
// ---- HIDE ALL
$('.fa-eye-slash').on('click', function() {
$('.couponLegend').each(function() {
$(this).append('<div class="line"></div>');
})
myLine.data.datasets.forEach(function(ds) {
ds.hidden = true;
});
myLine.update();
})
$('.couponStats').on('click', '.couponLegend', function(e) {
if ($(this).find('.line').length) {
$(this).find('.line').remove();
} else {
$(this).append('<div class="line"></div>');
}
var label = $(this).attr('data-label');
myLine.data.datasets.forEach(function(ds) {
if (ds.label == label) {
ds.hidden = !ds.hidden;
}
});
myLine.update();
})
}
$('.reload-stats').on('click', function(event) {
var campid = $(this).attr('data-campid');
var startdate = $('#startdate').val();
var enddate = $('#enddate').val();
event.preventDefault();
//console.log(enddate);
if (startdate != "" && enddate != "") {
teststartdate = new Date(startdate);
testenddate = new Date(enddate);
if (teststartdate < testenddate) {
myLine.destroy();
myLineGlobal.destroy();
load_graphs(campid, startdate, enddate);
} else {
$.fancyConfirm({
title: "<i class='fas fa-times-circle red'></i> Erreur",
message: "La date de fin doit être supérieure à la date de début.",
okButton: 'Valider',
noButton: "",
callback: function(value) {}
});
}
} else {
$.fancyConfirm({
title: "<i class='fas fa-times-circle red'></i> Erreur",
message: "Veuillez remplir les deux dates pour voir les données associées.",
okButton: 'Valider',
noButton: "",
callback: function(value) {}
});
}
})
</script>
@endsection

View file

@ -4,56 +4,56 @@
<title>Vos bons de réduction</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<link rel="stylesheet" type="text/css" href="{{'/landings/'.$ref}}/css/style.css">
<link rel="stylesheet" type="text/css" href="{{'/landings/'.$ref}}/css/print.css" media="print">
<link rel="stylesheet" type="text/css" href="{{'/storage/landings/'.$ref}}/css/style.css">
<link rel="stylesheet" type="text/css" href="{{'/storage/landings/'.$ref}}/css/print.css" media="print">
<script type="text/javascript" src="https://code.jquery.com/jquery-latest.js"></script>
</HEAD>
</HEAD>
<BODY>
<div id="nepasimprimer">
<div class="global">
<h2>Impression de vos bons de réduction</h2>
<h3><!--#4DTEXT s_Affiche_Titre--></h3>
<p class="precautions">
1 - Vérifiez que votre imprimante est allumée et reliée à l'ordinateur<br/>
2 - Vérifiez qu'elle contient du papier blanc et de l'encre<br/>
3 - Cliquez ci-dessous pour lancer l'impression
</p>
<center><input type="button" class="print" id="print_br" value="IMPRIMER MES COUPONS" onclick="impressionstart();" /></center>
<center><img id="loader" src="{{'/landings/'.$ref}}/images/loader.gif" alt="Chargement..." /></center>
<center><img id="loader" src="{{'/storage/landings/'.$ref}}/images/loader.gif" alt="Chargement..." /></center>
<input type="hidden" name="csrf" id="csrf" value="<?php echo csrf_token(); ?>">
</div>
</div>
<div id="aimprimer"></div>
<script type="text/javascript">
function impressionexe() {
var total = document.getElementsByTagName("img").length;
var compteur = 0, i;
compteur = 0;
for(i=0; i<total; i++){
if(document.getElementsByTagName("img")[i].complete){
compteur++;
compteur++;
}
}
if(compteur < total){
setTimeout("impressionexe()",2500);
}
else {
else {
window.print();
document.location.href="/merci";
}
}
function impressioncharge() {
$.ajaxSetup({
headers: {
@ -64,25 +64,25 @@
type:"POST",
url:"/ajx_impression",
data:"",
success:function(reponse){
if(reponse != ""){
var codehtmlimpression = reponse;
var codehtmlimpression = reponse;
document.getElementById("aimprimer").innerHTML = codehtmlimpression;
setTimeout("impressionexe()",2500);
}
else {
else {
alert("pas de réponse du serveur");
}
}
});
}
function impressionstart() {
document.getElementById('print_br').style.display='none';
document.getElementById('loader').style.display='block';
impressioncharge();
}
}
</script>
</BODY>
</HTML>
</HTML>

View file

@ -4,17 +4,17 @@
<title>Découvrez nos bons de réduction</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<link rel="stylesheet" type="text/css" href="{{'/landings/'.$ref}}/css/style.css">
<link rel="stylesheet" type="text/css" href="{{'/storage/landings/'.$ref}}/css/style.css">
<script type="text/javascript" src="https://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript">
function setupLabel() {
if ($('.input_check input').length) {
$('.input_check').each(function(){
$('.input_check').each(function(){
$(this).removeClass('check_on');
});
$('.input_check input:checked').each(function(){
$('.input_check input:checked').each(function(){
$(this).parent('label').addClass('check_on');
});
});
};
}
@ -22,7 +22,7 @@
var tabCheckbox = $("form input:checkbox");
var nbCheckbox = tabCheckbox.length;
var nbChoix = 0;
for(i=0;i<nbCheckbox;i++){
if (eval(tabCheckbox[i].checked) == true){
var regex = /^coupons/;
@ -33,7 +33,7 @@
}
}
if(nbChoix != 0){
document.coupons.submit();
document.coupons.submit();
}
}
@ -42,7 +42,7 @@
$('.input_check').click(function(){
setupLabel();
});
setupLabel();
setupLabel();
});
</script>
@ -55,7 +55,7 @@
<form name="coupons" action="/imprimer" method="post" enctype="multipart/form-data" style="margin-top:20px;">
@csrf
<input type="hidden" name="sessionuuid" value="">
<div style="padding-bottom: 10px;">
<div style="float: left;height: 40px;">
<h3>Vous avez {{ $nb }} coupon disponible</h3>
@ -65,8 +65,8 @@
</div>
<div style="clear:both;"></div>
</div>
<div class="clear"></div>
<div id="choixcoupons">
@foreach ($coupons as $coupon)
@ -83,8 +83,8 @@
@endforeach
</div>
<center><input type="button" style="display: @if($nb == 0) none;@else block; @endif" class="print" value="IMPRIMER MES COUPONS" onclick="envoyer();"></center>
<br/>
<br/>
</form>
</div>
</BODY>
</HTML>
</HTML>

View file

@ -4,9 +4,9 @@
<title>Vos bons de réduction</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<link rel="stylesheet" type="text/css" href="{{'/landings/'.$ref}}/css/style.css">
<link rel="stylesheet" type="text/css" href="{{'/storage/landings/'.$ref}}/css/style.css">
<script type="text/javascript" src="https://code.jquery.com/jquery-latest.js"></script>
</HEAD>
</HEAD>
<BODY>
<div class="global">
@ -16,4 +16,4 @@
<center><input type="button" class="print" value="RETOUR AUX OFFRES" onclick='location.href="/index";' /></center>
</div>
</BODY>
</HTML>
</HTML>

View file

@ -3,16 +3,16 @@
<HEAD>
<title>Votre bon de réduction Réghalal</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link href="{{'/landings/'.$ref}}/css/style.css" rel="stylesheet" type="text/css"/>
<link href="{{'/landings/'.$ref}}/css/print.css" rel="stylesheet" type="text/css" media="print"/>
<script type="text/javascript" src="{{'/landings/'.$ref}}/js/jquery191.min.js"></script>
</HEAD>
<link href="{{'/storage/landings/'.$ref}}/css/style.css" rel="stylesheet" type="text/css"/>
<link href="{{'/storage/landings/'.$ref}}/css/print.css" rel="stylesheet" type="text/css" media="print"/>
<script type="text/javascript" src="{{'/storage/landings/'.$ref}}/js/jquery191.min.js"></script>
</HEAD>
<BODY>
<div id="nepasimprimer">
<div class="content">
<h1>Votre bon de réduction</h1>
<div style="display: @if($nb == 0) none;@else block; @endif">
<p style="text-align:center;">{{$contact->contact_prenom}},</p>
<p style="text-align:center;">LDC a le plaisir de vous offrir un bon de réduction</p>
@ -20,17 +20,17 @@
1 - Vérifiez que votre imprimante est allumée et reliée à l'ordinateur.<br/>
2 - Vérifiez qu'elle contient du papier blanc et de l'encre.<br/>
3 - Cliquez ci-dessous pour lancer l'impression.</p>
<a id="trigger" href="#" onclick="impressionstart();">Imprimer mon bon</a>
<center><img id="loader" src="{{'/landings/'.$ref}}/images/loader.gif" alt="Chargement..." /></center>
</div>
<center><img id="loader" src="{{'/storage/landings/'.$ref}}/images/loader.gif" alt="Chargement..." /></center>
</div>
<div style="display: @if($nb == 0) block;@else none; @endif">
<p style="text-align:center;">{{$contact->contact_prenom}},</p>
<p style="text-align:center;">Vous n'avez plus de bon à imprimer.</p>
</div>
</div>
<input type="hidden" name="csrf" id="csrf" value="<?php echo csrf_token(); ?>">
</div>
</div>
</div>
<div id="aimprimer"></div>
@ -40,22 +40,22 @@ function impressionexe() {
var total = document.getElementsByTagName("img").length;
var compteur = 0, i;
compteur = 0;
for(i=0; i<total; i++){
if(document.getElementsByTagName("img")[i].complete){
compteur++;
compteur++;
}
}
if(compteur < total){
setTimeout("impressionexe()",2500);
}
else {
}
else {
window.print();
document.location.href="/merci";
}
}
function impressioncharge() {
$.ajaxSetup({
headers: {
@ -68,23 +68,23 @@ function impressioncharge() {
data:"",
success:function(reponse){
if(reponse != ""){
var codehtmlimpression = reponse;
var codehtmlimpression = reponse;
document.getElementById("aimprimer").innerHTML = codehtmlimpression;
setTimeout("impressionexe()",2500);
}
else {
}
else {
alert("pas de réponse du serveur");
}
}
});
}
function impressionstart() {
document.getElementById('trigger').style.display='none';
document.getElementById('loader').style.display='block';
impressioncharge();
}
}
</script>
</BODY>
</HTML>
</HTML>

View file

@ -3,10 +3,10 @@
<HEAD>
<title>Votre bon de réduction Reghalal</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link href="{{'/landings/'.$ref}}/css/style.css" rel="stylesheet" type="text/css" media="screen"/>
<script type="text/javascript" src="{{'/landings/'.$ref}}/js/jquery191.min.js"></script>
</HEAD>
<link href="{{'/storage/landings/'.$ref}}/css/style.css" rel="stylesheet" type="text/css" media="screen"/>
<script type="text/javascript" src="{{'/storage/landings/'.$ref}}/js/jquery191.min.js"></script>
</HEAD>
<BODY>
<div class="content">
<h1>Votre bon de réduction</h1>
@ -15,4 +15,4 @@
<a id="back" style="text-transform:none;" href="/index" title="Retour aux offres">Retour aux offres</a>
</div>
</BODY>
</HTML>
</HTML>

View file

@ -4,10 +4,10 @@
<title>Votre bon de réduction </title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="text/javascript" src="{{'/landings/'.$ref}}/js/jquery191.min.js"></script>
<script type="text/javascript" src="{{'/landings/'.$ref}}/js/detect.js"></script>
<link href="{{'/landings/'.$ref}}/css/style.css" rel="stylesheet" type="text/css"/>
<link href="{{'/landings/'.$ref}}/css/print.css" rel="stylesheet" type="text/css" media="print"/>
<script type="text/javascript" src="{{'/storage/landings/'.$ref}}/js/jquery191.min.js"></script>
<script type="text/javascript" src="{{'/storage/landings/'.$ref}}/js/detect.js"></script>
<link href="{{'/storage/landings/'.$ref}}/css/style.css" rel="stylesheet" type="text/css"/>
<link href="{{'/storage/landings/'.$ref}}/css/print.css" rel="stylesheet" type="text/css" media="print"/>
<script type="text/javascript">
@ -83,7 +83,7 @@
<div style="display: @if($nb == 0) none;@else block; @endif">
<div class="desk">
@ -95,7 +95,7 @@
<center><div id='loader' class='sk-fading-circle'><div class='sk-circle1 sk-circle'></div><div class='sk-circle2 sk-circle'></div><div class='sk-circle3 sk-circle'></div><div class='sk-circle4 sk-circle'></div><div class='sk-circle5 sk-circle'></div><div class='sk-circle6 sk-circle'></div><div class='sk-circle7 sk-circle'></div><div class='sk-circle8 sk-circle'></div><div class='sk-circle9 sk-circle'></div><div class='sk-circle10 sk-circle'></div><div class='sk-circle11 sk-circle'></div><div class='sk-circle12 sk-circle'></div></div></center>
</div>
<div class="mob">
<div id="precautions" style="font-weight: bold; padding:0px 20px; padding-top:30px;">

View file

@ -4,9 +4,9 @@
<title>Vos bons de réduction</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<link rel="stylesheet" type="text/css" href="{{'/landings/'.$ref}}/css/style.css">
<link rel="stylesheet" type="text/css" href="{{'/storage/landings/'.$ref}}/css/style.css">
<script type="text/javascript" src="https://code.jquery.com/jquery-latest.js"></script>
</HEAD>
</HEAD>
<BODY>
<div class="global">
@ -16,4 +16,4 @@
<center><input type="button" class="print" value="RETOUR AUX OFFRES" onclick='location.href="/index";' /></center>
</div>
</BODY>
</HTML>
</HTML>

View file

@ -4,55 +4,55 @@
<title>Vos bons de réduction</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<link rel="stylesheet" type="text/css" href="{{'/landings/'.$ref}}/css/style.css">
<link rel="stylesheet" type="text/css" href="{{'/landings/'.$ref}}/css/print.css" media="print">
<link rel="stylesheet" type="text/css" href="{{'/storage/landings/'.$ref}}/css/style.css">
<link rel="stylesheet" type="text/css" href="{{'/storage/landings/'.$ref}}/css/print.css" media="print">
<script type="text/javascript" src="https://code.jquery.com/jquery-latest.js"></script>
</HEAD>
</HEAD>
<BODY>
<div id="nepasimprimer">
<div class="global">
<h2>Impression de vos bons de réduction</h2>
<p class="precautions">
1 - Vérifiez que votre imprimante est allumée et reliée à l'ordinateur<br/>
2 - Vérifiez qu'elle contient du papier blanc et de l'encre<br/>
3 - Cliquez ci-dessous pour lancer l'impression
</p>
<center><input type="button" class="print" id="print_br" value="IMPRIMER MES COUPONS" onclick="impressionstart();" /></center>
<center><img id="loader" src="{{'/landings/'.$ref}}/images/loader.gif" alt="Chargement..." /></center>
<center><img id="loader" src="{{'/storage/landings/'.$ref}}/images/loader.gif" alt="Chargement..." /></center>
<input type="hidden" name="csrf" id="csrf" value="<?php echo csrf_token(); ?>">
</div>
</div>
<div id="aimprimer"></div>
<script type="text/javascript">
function impressionexe() {
var total = document.getElementsByTagName("img").length;
var compteur = 0, i;
compteur = 0;
for(i=0; i<total; i++){
if(document.getElementsByTagName("img")[i].complete){
compteur++;
compteur++;
}
}
if(compteur < total){
setTimeout("impressionexe()",2500);
}
else {
else {
window.print();
document.location.href="/merci";
}
}
function impressioncharge() {
$.ajaxSetup({
headers: {
@ -63,25 +63,25 @@
type:"POST",
url:"/ajx_impression",
data:"",
success:function(reponse){
if(reponse != ""){
var codehtmlimpression = reponse;
var codehtmlimpression = reponse;
document.getElementById("aimprimer").innerHTML = codehtmlimpression;
setTimeout("impressionexe()",2500);
}
else {
else {
alert("pas de réponse du serveur");
}
}
});
}
function impressionstart() {
document.getElementById('print_br').style.display='none';
document.getElementById('loader').style.display='block';
impressioncharge();
}
}
</script>
</BODY>
</HTML>
</HTML>

View file

@ -4,17 +4,17 @@
<title>Découvrez nos bons de réduction</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<link rel="stylesheet" type="text/css" href="{{'/landings/'.$ref}}/css/style.css">
<link rel="stylesheet" type="text/css" href="{{'/storage/landings/'.$ref}}/css/style.css">
<script type="text/javascript" src="https://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript">
function setupLabel() {
if ($('.input_check input').length) {
$('.input_check').each(function(){
$('.input_check').each(function(){
$(this).removeClass('check_on');
});
$('.input_check input:checked').each(function(){
$('.input_check input:checked').each(function(){
$(this).parent('label').addClass('check_on');
});
});
};
}
@ -22,7 +22,7 @@
var tabCheckbox = $("form input:checkbox");
var nbCheckbox = tabCheckbox.length;
var nbChoix = 0;
for(i=0;i<nbCheckbox;i++){
if (eval(tabCheckbox[i].checked) == true){
var regex = /^coupons/;
@ -33,7 +33,7 @@
}
}
if(nbChoix != 0){
document.coupons.submit();
document.coupons.submit();
}
}
@ -42,7 +42,7 @@
$('.input_check').click(function(){
setupLabel();
});
setupLabel();
setupLabel();
});
</script>
@ -55,7 +55,7 @@
<form name="coupons" action="/imprime" method="post" enctype="multipart/form-data" style="margin-top:20px;">
@csrf
<input type="hidden" name="sessionuuid" value="">
<div style="padding-bottom: 10px;">
<div style="float: left;height: 40px;">
<h3>Vous avez {{ $nb }} coupon disponible</h3>
@ -65,8 +65,8 @@
</div>
<div style="clear:both;"></div>
</div>
<div class="clear"></div>
<div id="choixcoupons">
@foreach ($coupons as $coupon)
@ -77,8 +77,8 @@
@endforeach
</div>
<center><input type="button" style="display: @if($nb == 0) none;@else block; @endif" class="print" value="IMPRIMER MES COUPONS" onclick="envoyer();"></center>
<br/>
<br/>
</form>
</div>
</BODY>
</HTML>
</HTML>

View file

@ -4,9 +4,9 @@
<title>Vos bons de réduction</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<link rel="stylesheet" type="text/css" href="{{'/landings/'.$ref}}/css/style.css">
<link rel="stylesheet" type="text/css" href="{{'/storage/landings/'.$ref}}/css/style.css">
<script type="text/javascript" src="https://code.jquery.com/jquery-latest.js"></script>
</HEAD>
</HEAD>
<BODY>
<div class="global">
@ -16,4 +16,4 @@
<center><input type="button" class="print" value="RETOUR AUX OFFRES" onclick='location.href="/index";' /></center>
</div>
</BODY>
</HTML>
</HTML>

69
webpack.mix.js vendored
View file

@ -1,4 +1,52 @@
const mix = require('laravel-mix');
const path = require("path");
const mix = require("laravel-mix");
mix.setPublicPath("public/build");
mix.setResourceRoot("/build");
require("laravel-mix-polyfill");
require("laravel-mix-clean");
require("laravel-mix-eslint");
require("laravel-mix-stylelint");
const assetDir = "assets";
const polyfill = {
enabled: true,
bugfixes: true,
useBuiltIns: "usage",
targets: "last 3 version, not dead, >0.3%",
};
mix.webpackConfig({
resolve: {
extensions: ["*", ".js"],
alias: {
"@": path.resolve("resources/assets"),
},
},
});
mix.options({
fileLoaderDirs: {
fonts: `${assetDir}/fonts`,
images: `${assetDir}/images`,
},
postCss: [
require("postcss-discard-comments")({
removeAll: true,
}),
],
});
mix.clean({
cleanOnceBeforeBuildPatterns: [
"css/*",
"js/*",
"images/*",
"fonts/*",
"assets/*",
],
});
/*
|--------------------------------------------------------------------------
@ -11,12 +59,13 @@ const mix = require('laravel-mix');
|
*/
mix.js('resources/js/app.js', 'public/js')
.js('resources/js/ajax.js', 'public/js')
.js('resources/js/Chart.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css')
.options({
processCssUrls: false
})
.browserSync('mesbr.test')
.sourceMaps();
mix.js("resources/js/app.js", "js")
.js("resources/js/ajax.js", "js")
.js("resources/js/Chart.js", "js")
.js("resources/js/jquery.fancybox.min.js", "js")
.css("resources/css/jquery.fancybox.min.css", "css")
.sass("resources/sass/app.scss", "css")
.copyDirectory("resources/assets", "public/build/assets")
.polyfill(polyfill)
.sourceMaps(false, "inline-source-map")
.version();

6423
yarn.lock

File diff suppressed because it is too large Load diff