feat: remove export mail and replaced with .csv and .pdf downloads

This commit is contained in:
Noah Lancien 2025-11-25 14:57:34 +01:00
parent 8fbaa234b4
commit b7b4013897
19 changed files with 585 additions and 275 deletions

View file

@ -0,0 +1,39 @@
<?php
namespace App\Enums\Claims;
use Uwil\LaravelCommon\Traits\Enums\BaseEnum;
enum ClaimExportFormat: int
{
use BaseEnum;
case csv = 1;
case pdf = 2;
/**
* Optionnal labels definition
*/
private const LABELS = [
self::csv->name => 'CSV',
self::pdf->name => 'PDF',
];
public function class()
{
return match ($this) {
self::csv => 'success',
self::pdf => 'danger',
default => 'primary',
};
}
public function picto()
{
return match ($this) {
self::csv => 'fa-solid fa-file-csv',
self::pdf => 'fa-solid fa-file-pdf',
default => 'fa-solid fa-file',
};
}
}

View file

@ -2,6 +2,7 @@
namespace App\Http\Controllers\Bo;
use App\Enums\Claims\ClaimExportFormat;
use App\Enums\Claims\ClaimResponseType;
use App\Enums\Claims\ClaimStatus;
use App\Enums\Users\RoleEnum;
@ -11,7 +12,6 @@ use App\Http\Requests\Bo\Claims\ExportClaimRequest;
use App\Http\Requests\Bo\Claims\SendClaimAnswerRequest;
use App\Http\Requests\Bo\Claims\UpdateClaimRequest;
use App\Http\Requests\Bo\PaginateModelsRequest;
use App\Jobs\ExportClaimsAsCsv;
use App\Mail\ClaimAnswerMail;
use App\Models\Brand;
use App\Models\Claim;
@ -20,9 +20,12 @@ use App\Models\ClaimMessage;
use App\Models\ClaimSubject;
use App\Models\CustomerInfo;
use App\Traits\Controllers\UpdateModelPublished;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Uwil\LaravelCommon\Helpers\CollectionHelper;
class ClaimController extends Controller
@ -67,8 +70,6 @@ class ClaimController extends Controller
fn($q) => $q->whereNotIn('status', [ClaimStatus::treated, ClaimStatus::refused])
);
// dd($query->toRawSql());
/** @var string $search Search field */
$search = $request->search;
if ($search) {
@ -95,7 +96,13 @@ class ClaimController extends Controller
/** Custom pagination */
$claimModels = $query->simplePaginate($this->getPagination());
$claimSubjectModels = ClaimSubject::all();
$claimSubjectModels = ClaimSubject::query()
->when(
$request->filled('brand'),
fn($q) => $q->whereHas('brand', fn($q) => $q->where('id', $request->brand)),
fn($q) => $q->whereHas('brand', fn($q) => $q->whereIn('id', $userModel->brands()->pluck('id')))
)
->get();
return view('bo.pages.claims.index', compact('claimModels', 'search', 'searchFields', 'claimSubjectModels'));
}
@ -220,28 +227,89 @@ class ClaimController extends Controller
return \response()->json($models);
}
// * EXPORT
/**
* CSV File headers
*
* @var array
*/
private $header = [
"Prénom NOM",
"Email",
"Adresse",
"Date de création",
"Sujet",
"Statut",
"Message",
"Pièce jointe",
];
public function export(ExportClaimRequest $request)
{
$claims = Claim::query()->whereIn('id', $request->validated('claimList'))->get();
$brandNames = Brand::query()
->whereIn(
'id',
\collect($claims)
->pluck('brand_id')
->unique()
)->pluck('name')
->toArray();
$format = ClaimExportFormat::from($request->validated('format'));
// dd($brandNames);
\dispatch(new ExportClaimsAsCsv($request->validated('email'), $brandNames, $claims));
if ($format->equals(ClaimExportFormat::csv)) {
$csvlines[] = $this->header;
foreach ($claims as $claim) {
$csvlines[] = $this->fillLine($claim);
}
return \redirect()
->back()
->with('success', trans("Le traitement de l'export partiel a été lancé. Si vous n'avez pas recu de mail
cela signifie qu'une erreur est survenue et nous sommes au courant."));
return new StreamedResponse(function() use ($csvlines) {
$this->array2csv($csvlines, ';');
}, 200, [
'Content-Type' => 'text/csv',
'Content-Disposition' => 'attachment; filename="export.csv"',
]);
} elseif ($format->equals(ClaimExportFormat::pdf)) {
$pdf = Pdf::loadView('bo.pages.claims.pdf', [
'claimModels' => $claims
]);
return $pdf->download('export.pdf');
};
return back()->with('error', __('Une erreur est survenue lors de l\'export. Veuillez réessayer en rafraîchissant la page.'));
}
private function array2csv(
array $lines,
string $delimiter = ',',
string $enclosure = '"',
string $escape_char = "\\"
){
$f = fopen('php://output', 'w');
// UTF-8 BOM.
$BOM = "\xEF\xBB\xBF";
fwrite($f, $BOM);
foreach ($lines as $item) {
fputcsv($f, $item, $delimiter, $enclosure, $escape_char);
}
fclose($f);
}
/**
* fill one line for csv
* @param \App\Models\Claim $claim
*
* @return array
*/
private function fillLine(Claim $claim)
{
$oneline = [
sprintf('%s %s', $claim->first_name, Str::upper($claim->last_name)),
$claim->email,
sprintf('%s, %s, %s', $claim->address, $claim->postcode, $claim->city),
$claim->created_at->toDateTimeString(),
$claim->claimSubject->label,
$claim->status->label(),
$claim->message,
asset($claim->file),
];
return $oneline;
}
}

View file

@ -2,11 +2,9 @@
namespace App\Http\Requests\Bo\Claims;
use App\Enums\Claims\ClaimResponseType;
use App\Enums\Claims\ClaimStatus;
use App\Enums\Claims\ClaimType;
use App\Enums\Claims\ClaimExportFormat;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Enum;
use Illuminate\Validation\Rule;
class ExportClaimRequest extends FormRequest
{
@ -35,7 +33,10 @@ class ExportClaimRequest extends FormRequest
public function rules(): array
{
return [
'email' => 'required|email',
'format' => [
'required',
Rule::enum(ClaimExportFormat::class)
],
'claimList' => 'required|array',
'claimList.*' => 'required|numeric|exists:claims,id',
];

View file

@ -1,134 +0,0 @@
<?php
namespace App\Jobs;
use App\Mail\ExportClaims;
use App\Models\Claim;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class ExportClaimsAsCsv implements ShouldQueue
{
use Queueable;
use Dispatchable;
use InteractsWithQueue;
use SerializesModels;
/**
* CSV File headers
*
* @var array
*/
private $header = [
"Prénom NOM",
"Email",
"Adresse",
"Date de création",
"Sujet",
"Statut",
"Message",
"Pièce jointe",
];
/**
* Create a new job instance.
*/
public function __construct(private string $recipient, private array $brandNames, private $claims)
{
//
}
/**
* Execute the job.
*/
public function handle(): void
{
/** @var \Illuminate\Filesystem\FilesystemAdapter */
$disk = Storage::disk('export');
$disk->deleteDirectory('export_claims');
$disk->makeDirectory('export_claims');
// Insertion de la ligne d'en-têtes.
$csvlines[] = $this->header;
foreach ($this->claims as $inscription) {
$csvlines[] = $this->fillLine($inscription);
}
$data = $this->array2csv($csvlines, ";");
$uuid = Str::uuid();
$fileName = date('d-m-Y') . '-' . $uuid . '.csv';
$disk->put("export_claims/{$fileName}", $data);
if (!$disk->exists("export_claims/{$fileName}")) {
throw new \RuntimeException(
"The CSV file 'export_claims/{$fileName}' has not been created."
);
}
$brandNamesString = implode(', ', $this->brandNames);
Mail::to($this->recipient)->send(new ExportClaims($fileName, $brandNamesString));
}
/**
* fill one line for csv
* @param \App\Models\Claim $claim
*
* @return array
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh, Generic.Metrics.CyclomaticComplexity.MaxExceeded
*/
private function fillLine(Claim $claim)
{
// @phpcs:enable
$oneline = [
sprintf('%s %s', $claim->first_name, Str::upper($claim->last_name)),
$claim->email,
$claim->address . "," . $claim->postcode . "," . $claim->city,
$claim->created_at->toDateTimeString(),
$claim->claimSubject->label,
$claim->status->label(),
$claim->message,
asset($claim->file),
];
return $oneline;
}
/**
* converts an array to a csv stream
*
* @param array $data
* @param string $delimiter
* @param string $enclosure
* @param string $escape_char
*
* @return string
*/
private function array2csv(
array $data,
string $delimiter = ',',
string $enclosure = '"',
string $escape_char = "\\"
) {
$f = fopen('php://memory', 'r+');
// UTF-8 BOM.
$BOM = "\xEF\xBB\xBF";
fwrite($f, $BOM);
foreach ($data as $item) {
fputcsv($f, $item, $delimiter, $enclosure, $escape_char);
}
rewind($f);
return stream_get_contents($f);
}
}

View file

@ -1,62 +0,0 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Storage;
class ExportClaims extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*/
public function __construct(private string $fileName, private string $brandName)
{
//
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: sprintf('Export des réclamations - %s', $this->brandName),
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
markdown: 'mail.export-claims',
text: 'mail.export-claims-plain'
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
Attachment::fromStorageDisk('export', "export_claims/{$this->fileName}")
->as(sprintf(
'export-reclas-%s.csv',
now()->format('Y-m-d')
))
];
}
}

View file

@ -106,6 +106,18 @@ class ClaimPolicy
return $this->checkOnlyMinAdminOrInUserBrands($authUser, $claim);
}
/**
* Export a Claim.
*
* @param \App\Models\User $authUser
* @param \App\Models\Claim $claim
* @return boolean
*/
public function export(User $authUser, Claim $claim): bool
{
return $this->checkOnlyMinAdminOrInUserBrands($authUser, $claim);
}
/**
* Check if the user an access a specific resource.
*

View file

@ -28,6 +28,7 @@
"24slides/laravel-saml2": "^2.4",
"arondeparon/laravel-request-sanitizer": "^5.0",
"axlon/laravel-postal-code-validation": "^3.2",
"barryvdh/laravel-dompdf": "^3.1",
"diglactic/laravel-breadcrumbs": "^9.0",
"doctrine/dbal": "^3.0",
"doctrine/inflector": "^2.0",

367
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": "c078e9a537be5c78427890f22fc24612",
"content-hash": "f04361d9a00c60112340a92b68293a68",
"packages": [
{
"name": "24slides/laravel-saml2",
@ -210,6 +210,83 @@
},
"time": "2024-09-10T15:23:59+00:00"
},
{
"name": "barryvdh/laravel-dompdf",
"version": "v3.1.1",
"source": {
"type": "git",
"url": "https://github.com/barryvdh/laravel-dompdf.git",
"reference": "8e71b99fc53bb8eb77f316c3c452dd74ab7cb25d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/8e71b99fc53bb8eb77f316c3c452dd74ab7cb25d",
"reference": "8e71b99fc53bb8eb77f316c3c452dd74ab7cb25d",
"shasum": ""
},
"require": {
"dompdf/dompdf": "^3.0",
"illuminate/support": "^9|^10|^11|^12",
"php": "^8.1"
},
"require-dev": {
"larastan/larastan": "^2.7|^3.0",
"orchestra/testbench": "^7|^8|^9|^10",
"phpro/grumphp": "^2.5",
"squizlabs/php_codesniffer": "^3.5"
},
"type": "library",
"extra": {
"laravel": {
"aliases": {
"PDF": "Barryvdh\\DomPDF\\Facade\\Pdf",
"Pdf": "Barryvdh\\DomPDF\\Facade\\Pdf"
},
"providers": [
"Barryvdh\\DomPDF\\ServiceProvider"
]
},
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Barryvdh\\DomPDF\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Barry vd. Heuvel",
"email": "barryvdh@gmail.com"
}
],
"description": "A DOMPDF Wrapper for Laravel",
"keywords": [
"dompdf",
"laravel",
"pdf"
],
"support": {
"issues": "https://github.com/barryvdh/laravel-dompdf/issues",
"source": "https://github.com/barryvdh/laravel-dompdf/tree/v3.1.1"
},
"funding": [
{
"url": "https://fruitcake.nl",
"type": "custom"
},
{
"url": "https://github.com/barryvdh",
"type": "github"
}
],
"time": "2025-02-13T15:07:54+00:00"
},
{
"name": "brick/math",
"version": "0.12.1",
@ -995,6 +1072,161 @@
],
"time": "2024-02-05T11:56:58+00:00"
},
{
"name": "dompdf/dompdf",
"version": "v3.1.4",
"source": {
"type": "git",
"url": "https://github.com/dompdf/dompdf.git",
"reference": "db712c90c5b9868df3600e64e68da62e78a34623"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/db712c90c5b9868df3600e64e68da62e78a34623",
"reference": "db712c90c5b9868df3600e64e68da62e78a34623",
"shasum": ""
},
"require": {
"dompdf/php-font-lib": "^1.0.0",
"dompdf/php-svg-lib": "^1.0.0",
"ext-dom": "*",
"ext-mbstring": "*",
"masterminds/html5": "^2.0",
"php": "^7.1 || ^8.0"
},
"require-dev": {
"ext-gd": "*",
"ext-json": "*",
"ext-zip": "*",
"mockery/mockery": "^1.3",
"phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11",
"squizlabs/php_codesniffer": "^3.5",
"symfony/process": "^4.4 || ^5.4 || ^6.2 || ^7.0"
},
"suggest": {
"ext-gd": "Needed to process images",
"ext-gmagick": "Improves image processing performance",
"ext-imagick": "Improves image processing performance",
"ext-zlib": "Needed for pdf stream compression"
},
"type": "library",
"autoload": {
"psr-4": {
"Dompdf\\": "src/"
},
"classmap": [
"lib/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1"
],
"authors": [
{
"name": "The Dompdf Community",
"homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md"
}
],
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
"homepage": "https://github.com/dompdf/dompdf",
"support": {
"issues": "https://github.com/dompdf/dompdf/issues",
"source": "https://github.com/dompdf/dompdf/tree/v3.1.4"
},
"time": "2025-10-29T12:43:30+00:00"
},
{
"name": "dompdf/php-font-lib",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/dompdf/php-font-lib.git",
"reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d",
"reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": "^7.1 || ^8.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^3 || ^4 || ^5 || ^6"
},
"type": "library",
"autoload": {
"psr-4": {
"FontLib\\": "src/FontLib"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1-or-later"
],
"authors": [
{
"name": "The FontLib Community",
"homepage": "https://github.com/dompdf/php-font-lib/blob/master/AUTHORS.md"
}
],
"description": "A library to read, parse, export and make subsets of different types of font files.",
"homepage": "https://github.com/dompdf/php-font-lib",
"support": {
"issues": "https://github.com/dompdf/php-font-lib/issues",
"source": "https://github.com/dompdf/php-font-lib/tree/1.0.1"
},
"time": "2024-12-02T14:37:59+00:00"
},
{
"name": "dompdf/php-svg-lib",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/dompdf/php-svg-lib.git",
"reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/eb045e518185298eb6ff8d80d0d0c6b17aecd9af",
"reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": "^7.1 || ^8.0",
"sabberworm/php-css-parser": "^8.4"
},
"require-dev": {
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Svg\\": "src/Svg"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0-or-later"
],
"authors": [
{
"name": "The SvgLib Community",
"homepage": "https://github.com/dompdf/php-svg-lib/blob/master/AUTHORS.md"
}
],
"description": "A library to read, parse and export to PDF SVG files.",
"homepage": "https://github.com/dompdf/php-svg-lib",
"support": {
"issues": "https://github.com/dompdf/php-svg-lib/issues",
"source": "https://github.com/dompdf/php-svg-lib/tree/1.0.0"
},
"time": "2024-04-29T13:26:35+00:00"
},
{
"name": "dragonmantank/cron-expression",
"version": "v3.4.0",
@ -3330,6 +3562,73 @@
],
"time": "2024-12-08T08:18:47+00:00"
},
{
"name": "masterminds/html5",
"version": "2.10.0",
"source": {
"type": "git",
"url": "https://github.com/Masterminds/html5-php.git",
"reference": "fcf91eb64359852f00d921887b219479b4f21251"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251",
"reference": "fcf91eb64359852f00d921887b219479b4f21251",
"shasum": ""
},
"require": {
"ext-dom": "*",
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.7-dev"
}
},
"autoload": {
"psr-4": {
"Masterminds\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Matt Butcher",
"email": "technosophos@gmail.com"
},
{
"name": "Matt Farina",
"email": "matt@mattfarina.com"
},
{
"name": "Asmir Mustafic",
"email": "goetas@gmail.com"
}
],
"description": "An HTML5 parser and serializer.",
"homepage": "http://masterminds.github.io/html5-php",
"keywords": [
"HTML5",
"dom",
"html",
"parser",
"querypath",
"serializer",
"xml"
],
"support": {
"issues": "https://github.com/Masterminds/html5-php/issues",
"source": "https://github.com/Masterminds/html5-php/tree/2.10.0"
},
"time": "2025-07-25T09:04:22+00:00"
},
{
"name": "mews/purifier",
"version": "3.4.2",
@ -5115,6 +5414,72 @@
},
"time": "2024-11-20T21:13:56+00:00"
},
{
"name": "sabberworm/php-css-parser",
"version": "v8.9.0",
"source": {
"type": "git",
"url": "https://github.com/MyIntervals/PHP-CSS-Parser.git",
"reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/d8e916507b88e389e26d4ab03c904a082aa66bb9",
"reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9",
"shasum": ""
},
"require": {
"ext-iconv": "*",
"php": "^5.6.20 || ^7.0.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"require-dev": {
"phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.41",
"rawr/cross-data-providers": "^2.0.0"
},
"suggest": {
"ext-mbstring": "for parsing UTF-8 CSS"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "9.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Sabberworm\\CSS\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Raphael Schweikert"
},
{
"name": "Oliver Klee",
"email": "github@oliverklee.de"
},
{
"name": "Jake Hotson",
"email": "jake.github@qzdesign.co.uk"
}
],
"description": "Parser for CSS Files written in PHP",
"homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser",
"keywords": [
"css",
"parser",
"stylesheet"
],
"support": {
"issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues",
"source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.9.0"
},
"time": "2025-07-11T13:20:48+00:00"
},
{
"name": "sentry/sentry",
"version": "4.10.0",

View file

@ -67,14 +67,6 @@ return [
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
'throw' => false,
],
'export' => [
'driver' => 'local',
'root' => storage_path('app/export'),
'visibility' => 'private',
'throw' => true,
],
],
/*

View file

@ -10,6 +10,7 @@ return [
'delete' => 'delete',
'save' => 'save',
'edit' => 'edit',
'export' => 'export',
],
'helpers' => [

View file

@ -10,6 +10,7 @@ return [
'delete' => 'supprimer',
'save' => 'sauvegarder',
'edit' => 'mettre à jour',
'export' => 'exporter',
],
'helpers' => [

View file

@ -1,17 +1,24 @@
// Type for 'bootstrap modal show' event. Needed for 'relatedTarget'.
interface BootstrapModalShowEvent extends CustomEvent {
relatedTarget: HTMLElement;
}
document.addEventListener("DOMContentLoaded", function () {
const exportForm = document.getElementById("claimExportForm");
const exportFormBtn = document.getElementById("claimExportFormBtn");
const exportFormBtnInside = document.getElementById("claimExportFormBtnInside");
const exportFormModal = document.getElementById("exportModal");
const claimListInputs = document.getElementsByClassName("claim-list-input");
if (!exportForm || !exportFormBtn || !exportFormBtnInside || !(exportForm instanceof HTMLFormElement)) {
if (!exportFormModal || !claimListInputs) {
return;
}
exportFormBtn.addEventListener("click", function (e) {
e.preventDefault();
exportFormModal.addEventListener("show.bs.modal", (event) => {
const customEvent = event as BootstrapModalShowEvent;
const button = customEvent.relatedTarget as HTMLElement;
const datas = button.getAttribute("data-bs-claim-list");
exportFormBtnInside.click();
for (const claimListInput of claimListInputs) {
(claimListInput as HTMLInputElement).value = String(datas);
}
});
});

View file

@ -30,6 +30,9 @@ import {
faEye,
faEyeSlash,
faFile,
faFileCsv,
faFileExport,
faFilePdf,
faFolderOpen,
faListUl,
faPaintRoller,
@ -59,6 +62,9 @@ library.add(
faEye,
faEyeSlash,
faFile,
faFileCsv,
faFileExport,
faFilePdf,
faFolderOpen,
faCrop,
faQuestionCircle,

View file

@ -2,14 +2,14 @@
$selectedSubject = request()->subject ?? (isset($modelTable) ? Session::get("crud.$modelTable.subject") : null);
@endphp
<div class="row align-items-center justify-content-between mb-3">
<div class="col-3">
<button class="btn btn-success d-flex flex-column" id="export-claim" data-bs-toggle="modal" data-bs-target="#exportModal">
<i class="fa-solid fa-file"></i>
{{ __('Export')}}
<div class="row align-items-center mb-3">
<div class="col-1">
<button class="btn btn-primary d-flex flex-column" id="export-btn" data-bs-toggle="modal" data-bs-target="#exportModal" data-bs-claim-list="@json($claimModels->pluck('id')->toArray())">
<i class="fa-solid fa-file-export"></i>
{{ __('Exporter')}}
</button>
</div>
<div class="col-3">
<div class="col-3 offset-8">
<form action="{{ request()->fullUrlWithQuery(['rstFltrs' => null]) }}" class="row gx-2 mb-2">
{{-- SUBJECT SELECT --}}
<select class="form-select mb-1 mb-lg-0"
@ -34,27 +34,20 @@
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form action="{{ route('bo.claims.export') }}" class="row gx-2 mb-2" method="POST" id="claimExportForm">
@csrf
{{-- EMAIL INPUT --}}
<label for="exportEmail" class="form-label">{{ __('Destinataire de l\'export') }}&nbsp;</label>
<input
type="email"
class="form-control"
name="email"
id="exportEmail"
required
pattern="^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
title="Veuillez saisir une adresse email valide">
<div id="emailHelp" class="form-text">{{ __('L\'export sera envoyé en .csv à cette adresse mail.')}}</div>
<button type="submit" class="d-none" id="claimExportFormBtnInside"></button>
<input hidden name="claimList" value="@json($claimModels->pluck('id')->toArray())">
</form>
@foreach(\App\Enums\Claims\ClaimExportFormat::cases() as $supportedFormat)
<form action="{{ route('bo.claims.export', ['format' => $supportedFormat->name]) }}" class="row gx-2 mb-2" method="POST">
@csrf
<input hidden name="format" value="{{ $supportedFormat }}">
<input hidden class="claim-list-input" name="claimList" value="">
<button class="btn btn-{{ $supportedFormat->class()}} d-flex flex-column" id="export-{{ $supportedFormat->name }}" type="submit">
<i class="{{ $supportedFormat->picto() }}"></i>
{{ __('Exporter en .:format', ['format' => $supportedFormat->name])}}
</button>
</form>
@endforeach
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ __('Fermer')}}</button>
<button type="button" class="btn btn-primary" type="submit" id="claimExportFormBtn">{{ __('Exporter')}}</button>
</div>
</div>
</div>

View file

@ -60,6 +60,16 @@
<i class="fa-solid fa-comments"></i>
</a>
@endcan
@can('export', $claimModel)
<a class="btn btn-secondary d-flex flex-column"
data-bs-tooltip="tooltip"
title="{{ str(__('crud.actions.export'))->ucfirst() }}"
data-bs-toggle="modal"
data-bs-target="#exportModal"
data-bs-claim-list="@json([$claimModel->id])">
<i class="fa-solid fa-file-export"></i>
</a>
@endcan
</div>
@else
<span class="text-danger" data-bs-tooltip="tooltip" title="{{ __('list.right') }}">

View file

@ -0,0 +1,24 @@
<style>
.page-break {
page-break-after: always;
}
</style>
@foreach($claimModels as $claimModel)
<p>{{ __('Référence') }}&nbsp;: {{ $claimModel->reference }} </p>
<p>{{ __('Date de la demande') }}&nbsp;: {{ $claimModel->created_at->toDateString() }} </p>
<p>{{ __('Prénom NOM') }}&nbsp;: {{ $claimModel->first_name }} {{ str($claimModel->last_name)->upper() }}</p>
<p>{{ __('Email') }}&nbsp;: {{ $claimModel->email }} </p>
<p>{{ __('Adresse') }}&nbsp;: {{ sprintf('%s, %s, %s', $claimModel->address, $claimModel->postcode, $claimModel->city) }} </p>
<p>{{ __('Statut de la réclamation') }}&nbsp;: {{ $claimModel->status->label() }} </p>
<p>{{ __('Sujet de la réclamation') }}&nbsp;: {{ $claimModel->claimSubject->label }} </p>
<p>{{ __('Type de demande') }}&nbsp;: {{ $claimModel->type->label() }} </p>
<p>{{ __('Marque concernée') }}&nbsp;: {{ $claimModel->brand->name }} </p>
<p>{{ __('Message') }}&nbsp;: {{ $claimModel->message }} </p>
<p>{{ __('Voir la pièce jointe') }}&nbsp;: <a target="blank" href="{{ asset($claimModel->file) }}">{{ __('Cliquez ici') }}</a> </p>
@if (!$loop->last)
<div class="page-break"></div>
@endif
@endforeach

View file

@ -1,5 +0,0 @@
Bonjour,
Vous trouverez ci-joint l'export des réclamations du {{ now()->isoFormat('DD\/MM\/YYYY à HH:mm') }}
Ceci est un message automatique, ne pas répondre.

View file

@ -1,10 +0,0 @@
{{-- blade-formatter-disable --}}
@component('mail::layout')
# {{ __('Bonjour,') }}
{{ __('Vous trouverez ci-joint l\'export des réclamations du') }} {{ now()->isoFormat('DD\/MM\/YYYY à HH:mm') }}
{{ __('Ceci est un message automatique, ne pas répondre.') }}
@endcomponent

View file

@ -93,7 +93,8 @@ Route::middleware(['throttle:web', 'admin-secure'])->prefix('bo')->name('bo.')->
// * CLAIMS.
Route::resource('claims', ClaimController::class)->only(['index', 'edit', 'update']);
Route::post('claims/{claim}/answer', [ClaimController::class, 'answer'])->name('claims.answer');
Route::post('claims/export', [ClaimController::class, 'export'])->name('claims.export');
Route::post('claims/export', [ClaimController::class, 'export'])
->name('claims.export');
// * CLAIM SUBJECTS.
Route::get('/{brand}/claim_subjects/json/paginate', [ClaimController::class, 'jsonClaimPaginateModels'])