Merge branch '49-update-recla' into 'develop'
feat: many ui improvements for the claim part See merge request ldc-web/projets/crm!46
This commit is contained in:
commit
b6fe43cdcf
29 changed files with 1447 additions and 13 deletions
|
|
@ -12,6 +12,7 @@ enum ClaimStatus: int
|
|||
case sentDigital = 2;
|
||||
case sentMarketing = 3;
|
||||
case treated = 4;
|
||||
case refused = 5;
|
||||
|
||||
/**
|
||||
* Optionnal labels definition
|
||||
|
|
@ -21,6 +22,7 @@ enum ClaimStatus: int
|
|||
self::sentDigital->name => 'Transférée au service Digital',
|
||||
self::sentMarketing->name => 'Retournée au Marketing',
|
||||
self::treated->name => 'Traitée',
|
||||
self::refused->name => 'Refusé de répondre',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -31,6 +33,7 @@ enum ClaimStatus: int
|
|||
self::sentDigital->name => '#0033c0',
|
||||
self::sentMarketing->name => '#69077c',
|
||||
self::treated->name => '#188618',
|
||||
self::refused->name => '#dc143c',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
85
app/Enums/ExportFormat.php
Normal file
85
app/Enums/ExportFormat.php
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use Uwil\LaravelCommon\Traits\Enums\BaseEnum;
|
||||
|
||||
enum ExportFormat: int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case csv = 1;
|
||||
case csvWithoutAttachment = 2;
|
||||
case pdf = 3;
|
||||
|
||||
/**
|
||||
* Optionnal labels definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::csv->name => 'csv',
|
||||
self::csvWithoutAttachment->name => 'csv sans pièces jointes',
|
||||
self::pdf->name => 'pdf',
|
||||
];
|
||||
|
||||
/**
|
||||
* Bootstrap color class to use for the export buttons.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function class(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::csv,
|
||||
self::csvWithoutAttachment => 'success',
|
||||
self::pdf => 'danger',
|
||||
default => 'primary',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Font Awesome picto to use for the export buttons.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function picto(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::csv,
|
||||
self::csvWithoutAttachment => 'fa-solid fa-file-csv',
|
||||
self::pdf => 'fa-solid fa-file-pdf',
|
||||
default => 'fa-solid fa-file',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* File headers to use when the exported file is return with StreamedResponse.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fileHeaders(): array
|
||||
{
|
||||
return match ($this) {
|
||||
self::csv,
|
||||
self::csvWithoutAttachment => [
|
||||
'Content-Type' => 'text/csv',
|
||||
'Content-Disposition' => 'attachment; filename="export.csv"',
|
||||
],
|
||||
default => [],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Export file name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function fileName(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::csv,
|
||||
self::csvWithoutAttachment => 'export.csv',
|
||||
self::pdf => 'export.pdf',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ class ClaimController extends Controller
|
|||
{
|
||||
$claimModel = (new Claim())->fill(Arr::except($request->validated(), 'file'));
|
||||
if ($request->filled('file')) {
|
||||
$claimModel->file = FileHelper::storeBase64File('claim', $request->file);
|
||||
$claimModel->file = FileHelper::storeBase64File('claim', $request->file, private: true);
|
||||
}
|
||||
|
||||
if (!$claimModel->saveOrFail()) {
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@ namespace App\Http\Controllers\Bo;
|
|||
|
||||
use App\Enums\Claims\ClaimResponseType;
|
||||
use App\Enums\Claims\ClaimStatus;
|
||||
use App\Enums\ExportFormat;
|
||||
use App\Enums\Users\RoleEnum;
|
||||
use App\Events\ClaimModelChanged;
|
||||
use App\Http\Controllers\Controller;
|
||||
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;
|
||||
|
|
@ -17,15 +19,18 @@ use App\Models\ClaimMailTemplate;
|
|||
use App\Models\ClaimMessage;
|
||||
use App\Models\ClaimSubject;
|
||||
use App\Models\CustomerInfo;
|
||||
use App\Traits\Controllers\ExportDatas;
|
||||
use App\Traits\Controllers\UpdateModelPublished;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Uwil\LaravelCommon\Helpers\CollectionHelper;
|
||||
|
||||
class ClaimController extends Controller
|
||||
{
|
||||
use UpdateModelPublished;
|
||||
use ExportDatas;
|
||||
|
||||
/**
|
||||
* Create the controller instance.
|
||||
|
|
@ -54,6 +59,15 @@ class ClaimController extends Controller
|
|||
)->when(
|
||||
$userModel->role->equals(RoleEnum::marketing),
|
||||
fn($q) => $q->whereHas('brand', fn($q) => $q->whereIn('id', $userModel->brands->modelKeys()))
|
||||
)
|
||||
->when(
|
||||
$request->filled('subject'),
|
||||
fn($q) => $q->whereHas('claimSubject', fn($q) => $q->where('id', $request->subject))
|
||||
)
|
||||
->when(
|
||||
$request->filled('status'),
|
||||
fn($q) => $q->whereIn('status', $request->status),
|
||||
fn($q) => $q->whereNotIn('status', [ClaimStatus::treated, ClaimStatus::refused])
|
||||
);
|
||||
|
||||
/** @var string $search Search field */
|
||||
|
|
@ -82,7 +96,15 @@ class ClaimController extends Controller
|
|||
/** Custom pagination */
|
||||
$claimModels = $query->simplePaginate($this->getPagination());
|
||||
|
||||
return view('bo.pages.claims.index', compact('claimModels', 'search', 'searchFields'));
|
||||
$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'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -204,4 +226,39 @@ class ClaimController extends Controller
|
|||
);
|
||||
return \response()->json($models);
|
||||
}
|
||||
|
||||
|
||||
// * EXPORT
|
||||
|
||||
/**
|
||||
* Export claim datas to a given format
|
||||
*
|
||||
* @param ExportClaimRequest $request
|
||||
* @return \Illuminate\Http\Response|Symfony\Component\HttpFoundation\StreamedResponse|\Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function export(ExportClaimRequest $request)
|
||||
{
|
||||
/** @var \Illuminate\Database\Eloquent\Collection<int, \App\Models\Claim> $claims*/
|
||||
$claims = Claim::query()->whereIn('id', $request->validated('claimList'))->get();
|
||||
$format = ExportFormat::from($request->validated('format'));
|
||||
|
||||
// * CSV
|
||||
if ($format->equals(ExportFormat::csvWithoutAttachment, ExportFormat::csv)) {
|
||||
$csv = $this->generateCsv($claims);
|
||||
if (!$this->needsZip($format, $claims)) {
|
||||
return new StreamedResponse(function () use ($format, $csv) {
|
||||
echo $csv;
|
||||
}, 200, $this->getFileHeaders($format));
|
||||
}
|
||||
return $this->returnExportZip($format, $claims, $csv);
|
||||
// * PDF
|
||||
} elseif ($format->equals(ExportFormat::pdf)) {
|
||||
$pdf = $this->generatePdf($claims);
|
||||
|
||||
if (!$this->needsZip(ExportFormat::pdf, $claims)) {
|
||||
return $pdf->download('export.pdf');
|
||||
}
|
||||
return $this->returnExportZip($format, $claims, $pdf->output());
|
||||
}; //end if
|
||||
}
|
||||
}
|
||||
|
|
|
|||
65
app/Http/Requests/Bo/Claims/ExportClaimRequest.php
Normal file
65
app/Http/Requests/Bo/Claims/ExportClaimRequest.php
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo\Claims;
|
||||
|
||||
use App\Enums\ExportFormat;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class ExportClaimRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return auth('backend')->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function prepareForValidation()
|
||||
{
|
||||
$this->merge([
|
||||
'claimList' => json_decode($this->input('claimList'), true)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'format' => [
|
||||
'required',
|
||||
Rule::enum(ExportFormat::class)
|
||||
],
|
||||
'claimList' => 'required|array',
|
||||
'claimList.*' => 'required|numeric|exists:claims,id',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*
|
||||
* @return array<string,\Illuminate\Contracts\Translation\Translator|string|array<mixed>|null>
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'brand_id' => trans('models.classes.brands'),
|
||||
'subject_id' => trans('models.classes.claimsubjects'),
|
||||
'type' => trans('validation.custom.type'),
|
||||
'status' => trans('validation.custom.status'),
|
||||
'response_type' => trans('validation.custom.response_type'),
|
||||
];
|
||||
}
|
||||
}
|
||||
34
app/Interfaces/IsExportable.php
Normal file
34
app/Interfaces/IsExportable.php
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace App\Interfaces;
|
||||
|
||||
interface IsExportable
|
||||
{
|
||||
/**
|
||||
* Get the formatted csv line for the current model.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFormattedCsvLine(): array;
|
||||
|
||||
/**
|
||||
* Get the csv headers for this model.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getCsvHeaders(): array;
|
||||
|
||||
/**
|
||||
* 'file_extension' virtual property.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFileExtensionAttribute(): string;
|
||||
|
||||
/**
|
||||
* 'export_file_name' virtual property.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getExportFileNameAttribute(): string;
|
||||
}
|
||||
17
app/Lib/Helpers/FileHelper.php
Normal file
17
app/Lib/Helpers/FileHelper.php
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\Lib\Helpers;
|
||||
|
||||
class FileHelper
|
||||
{
|
||||
/**
|
||||
* Get file path in private storage from stored path.
|
||||
*
|
||||
* @param string $basePath
|
||||
* @return string
|
||||
*/
|
||||
public static function getPrivateFilePath(string $basePath): string
|
||||
{
|
||||
return str($basePath)->replaceFirst('storage/app/private', '');
|
||||
}
|
||||
}
|
||||
|
|
@ -6,8 +6,10 @@ use App\Enums\Claims\ClaimResponseType;
|
|||
use App\Enums\Claims\ClaimStatus;
|
||||
use App\Enums\Claims\ClaimType;
|
||||
use App\Events\ClaimModelChanged;
|
||||
use App\Interfaces\IsExportable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
|
|
@ -29,8 +31,11 @@ use Illuminate\Support\Str;
|
|||
* @property string|null $hashcode Claim associated Customer hashcode.
|
||||
* @property-read \Illuminate\Support\Carbon $created_at Claim created date.
|
||||
* @property-read \Illuminate\Support\Carbon $updated_at Claim updated date.
|
||||
*
|
||||
* @property-read string $file_extension Extension of the file.
|
||||
* @property-read string $export_file_name Name of the exported file.
|
||||
*/
|
||||
class Claim extends Model
|
||||
class Claim extends Model implements IsExportable
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
|
|
@ -199,4 +204,76 @@ class Claim extends Model
|
|||
trim(implode(' ', array_filter([$this->postcode, $this->city])))
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the formmated csv line for the current model.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFormattedCsvLine(): array
|
||||
{
|
||||
return [
|
||||
$this->reference,
|
||||
$this->created_at->toDateTimeString(),
|
||||
sprintf('%s %s', $this->first_name, Str::upper($this->last_name)),
|
||||
$this->email,
|
||||
sprintf('%s, %s, %s', $this->address, $this->postcode, $this->city),
|
||||
$this->status->label(),
|
||||
$this->claimSubject->label,
|
||||
$this->type->label(),
|
||||
$this->brand->name,
|
||||
$this->message,
|
||||
$this->export_file_name,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the csv headers for this model.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getCsvHeaders(): array
|
||||
{
|
||||
return [
|
||||
'Référence',
|
||||
'Date de création',
|
||||
'Prénom NOM',
|
||||
'Email',
|
||||
'Adresse',
|
||||
'Statut',
|
||||
'Sujet',
|
||||
'Type',
|
||||
'Marque',
|
||||
'Message',
|
||||
'Pièce jointe',
|
||||
];
|
||||
}
|
||||
|
||||
// * VIRTUAL ATTRIBUTES
|
||||
|
||||
/**
|
||||
* 'file_extension' virtual property.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFileExtensionAttribute(): string
|
||||
{
|
||||
return File::extension($this->file);
|
||||
}
|
||||
|
||||
/**
|
||||
* 'export_file_name' virtual property.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getExportFileNameAttribute(): string
|
||||
{
|
||||
return $this->file ?
|
||||
sprintf(
|
||||
'%s.%s',
|
||||
$this->reference,
|
||||
$this->file_extension,
|
||||
) :
|
||||
__('Aucune pièce jointe');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
229
app/Traits/Controllers/ExportDatas.php
Normal file
229
app/Traits/Controllers/ExportDatas.php
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits\Controllers;
|
||||
|
||||
use App\Enums\ExportFormat;
|
||||
use App\Lib\Helpers\FileHelper;
|
||||
use Barryvdh\DomPDF\Facade\Pdf;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use ZipArchive;
|
||||
|
||||
trait ExportDatas
|
||||
{
|
||||
/**
|
||||
* Check if the export needs to be in a .zip file.
|
||||
* .zip is needed for :
|
||||
* - csv with any files
|
||||
* - pdf with files other than pictures
|
||||
*
|
||||
* @param ExportFormat $format Requested format.
|
||||
* @param \Illuminate\Database\Eloquent\Collection $models Collection of models to export.
|
||||
* @param string $fileFieldName Name of the field containing the file.
|
||||
* @return boolean
|
||||
*/
|
||||
public function needsZip(
|
||||
ExportFormat $format,
|
||||
\Illuminate\Database\Eloquent\Collection $models,
|
||||
string $fileFieldName = 'file'
|
||||
): bool {
|
||||
if ($format->equals(ExportFormat::csv)) {
|
||||
return $models->whereNotNull($fileFieldName)->pluck($fileFieldName)->isNotEmpty();
|
||||
} elseif ($format->equals(ExportFormat::pdf)) {
|
||||
return $models
|
||||
->whereNotNull($fileFieldName)
|
||||
->whereNotIn('file_extension', ['jpg', 'jpeg', 'png'])
|
||||
->isNotEmpty();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a StreamedResponse with a zip file.
|
||||
*
|
||||
* @param ExportFormat $format
|
||||
* @param \Illuminate\Database\Eloquent\Collection $models
|
||||
* @param string $file
|
||||
* @return \Symfony\Component\HttpFoundation\StreamedResponse
|
||||
*/
|
||||
public function returnExportZip(
|
||||
ExportFormat $format,
|
||||
\Illuminate\Database\Eloquent\Collection $models,
|
||||
string $file
|
||||
): \Symfony\Component\HttpFoundation\StreamedResponse {
|
||||
return new StreamedResponse(function () use ($file, $models, $format) {
|
||||
// Create zip.
|
||||
$zip = new ZipArchive();
|
||||
$zipName = $this->createExportZip($zip, $file, $format);
|
||||
|
||||
// Add attachments.
|
||||
$this->addAttachmentsToZip($zip, $models, $format);
|
||||
|
||||
$zip->close();
|
||||
readfile($zipName);
|
||||
unlink($zipName);
|
||||
}, 200, $this->getFileHeaders($format, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the zip folder for the export with the main file inside.
|
||||
*
|
||||
* @param ZipArchive $zip
|
||||
* @param string $file
|
||||
* @param ExportFormat $format
|
||||
* @return string|false
|
||||
*
|
||||
* @throws \Exception If the zip opening failed.
|
||||
*/
|
||||
private function createExportZip(ZipArchive &$zip, string $file, ExportFormat $format): string|false
|
||||
{
|
||||
$zipName = tempnam(Storage::disk('private')->path('export'), 'zip');
|
||||
if (!$zip->open($zipName, ZipArchive::OVERWRITE)) {
|
||||
throw new \Exception(__('Couldn\'t open zip file.'));
|
||||
};
|
||||
$zip->addFromString($format->fileName(), $file);
|
||||
|
||||
return $zipName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add attachments to zip
|
||||
*
|
||||
* @param ZipArchive $zip
|
||||
* @param \Illuminate\Database\Eloquent\Collection $models
|
||||
* @param ExportFormat $format
|
||||
* @param string $fileFieldName Name of the field containing the file.
|
||||
* @return void
|
||||
*/
|
||||
private function addAttachmentsToZip(
|
||||
ZipArchive &$zip,
|
||||
\Illuminate\Database\Eloquent\Collection $models,
|
||||
ExportFormat $format,
|
||||
string $fileFieldName = 'file'
|
||||
) {
|
||||
$modelsWithFilesToZip = $models->whereNotNull($fileFieldName)
|
||||
->when($format->equals(ExportFormat::pdf), function ($models) {
|
||||
return $models->whereNotIn('file_extension', ['jpg', 'jpeg', 'png']);
|
||||
});
|
||||
foreach ($modelsWithFilesToZip as $model) {
|
||||
$filePath = FileHelper::getPrivateFilePath($model->{$fileFieldName});
|
||||
if (Storage::disk('private')->exists($filePath)) {
|
||||
$zip->addFile(
|
||||
Storage::disk('private')->path($filePath),
|
||||
$model->export_file_name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file headers to use with StreamedResponse.
|
||||
*
|
||||
* @param ExportFormat $format
|
||||
* @param boolean $isZip
|
||||
* @return array
|
||||
*/
|
||||
public function getFileHeaders(ExportFormat $format, bool $isZip = false): array
|
||||
{
|
||||
if ($isZip) {
|
||||
return [
|
||||
'Content-Type' => 'application/zip',
|
||||
'Content-Disposition' => 'attachment; filename="export.zip"',
|
||||
];
|
||||
};
|
||||
return $format->fileHeaders();
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform Array into CSV.
|
||||
*
|
||||
* @param array $lines
|
||||
* @param string $delimiter
|
||||
* @param string $enclosure
|
||||
* @param string $escape_char
|
||||
* @return string|false
|
||||
*/
|
||||
private function array2csv(
|
||||
array $lines,
|
||||
string $delimiter = ',',
|
||||
string $enclosure = '"',
|
||||
string $escape_char = "\\"
|
||||
) {
|
||||
$f = fopen('php://temp', 'r+');
|
||||
|
||||
// UTF-8 BOM.
|
||||
$BOM = "\xEF\xBB\xBF";
|
||||
fwrite($f, $BOM);
|
||||
|
||||
foreach ($lines as $item) {
|
||||
fputcsv($f, $item, $delimiter, $enclosure, $escape_char);
|
||||
}
|
||||
|
||||
rewind($f);
|
||||
$csvContent = stream_get_contents($f);
|
||||
fclose($f);
|
||||
|
||||
return $csvContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate CSV with given models.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Collection $models
|
||||
* @return string|false
|
||||
*/
|
||||
private function generateCsv(\Illuminate\Database\Eloquent\Collection $models)
|
||||
{
|
||||
// CSV Header.
|
||||
$class = get_class($models->first());
|
||||
$csvlines[] = $class::getCsvHeaders();
|
||||
|
||||
foreach ($models as $model) {
|
||||
$csvlines[] = $model->getFormattedCsvLine();
|
||||
}
|
||||
|
||||
return $this->array2csv($csvlines, ';');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate PDF with models datas.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Collection $models
|
||||
* @param string $fileFieldName
|
||||
* @return \Barryvdh\DomPDF\PDF
|
||||
*/
|
||||
private function generatePdf(
|
||||
\Illuminate\Database\Eloquent\Collection $models,
|
||||
string $fileFieldName = 'file'
|
||||
): \Barryvdh\DomPDF\PDF {
|
||||
// Convert pictures to base64.
|
||||
foreach ($models->whereIn('file_extension', ['jpg', 'jpeg', 'png']) as $model) {
|
||||
$filePath = FileHelper::getPrivateFilePath($model->{$fileFieldName});
|
||||
$model->fileBase64 = sprintf(
|
||||
'data:%s;base64,%s',
|
||||
File::mimeType(Storage::disk('private')->path($filePath)),
|
||||
base64_encode(Storage::disk('private')->get($filePath))
|
||||
);
|
||||
}
|
||||
|
||||
// Create temp folder if it doesn't exists. Needed to allow base64 pictures to be displayed in pdf.
|
||||
if (!Storage::disk('private')->exists('temp')) {
|
||||
Storage::disk('private')->makeDirectory('temp');
|
||||
}
|
||||
|
||||
$modelClassName = class_basename($models->first());
|
||||
return Pdf::setOptions(['tempDir' => Storage::disk('private')->path('temp')])
|
||||
->loadView(
|
||||
sprintf(
|
||||
'export.pdf',
|
||||
str($modelClassName)->lower()->plural()
|
||||
),
|
||||
[
|
||||
'models' => $models,
|
||||
'modelName' => str($modelClassName)->lower()
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
367
composer.lock
generated
|
|
@ -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",
|
||||
|
|
|
|||
304
config/dompdf.php
Normal file
304
config/dompdf.php
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Settings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Set some default values. It is possible to add all defines that can be set
|
||||
| in dompdf_config.inc.php. You can also override the entire config file.
|
||||
|
|
||||
*/
|
||||
|
||||
// Throw an Exception on warnings from dompdf.
|
||||
'show_warnings' => true,
|
||||
|
||||
// Override the public path if needed.
|
||||
'public_path' => null,
|
||||
|
||||
// Dejavu Sans font is missing glyphs for converted entities, turn it off if you need to show € and £.
|
||||
'convert_entities' => true,
|
||||
|
||||
'options' => [
|
||||
/**
|
||||
* The location of the DOMPDF font directory
|
||||
*
|
||||
* The location of the directory where DOMPDF will store fonts and font metrics
|
||||
* Note: This directory must exist and be writable by the webserver process.
|
||||
* *Please note the trailing slash.*
|
||||
*
|
||||
* Notes regarding fonts:
|
||||
* Additional .afm font metrics can be added by executing load_font.php from command line.
|
||||
*
|
||||
* Only the original "Base 14 fonts" are present on all pdf viewers. Additional fonts must
|
||||
* be embedded in the pdf file or the PDF may not display correctly. This can significantly
|
||||
* increase file size unless font subsetting is enabled. Before embedding a font please
|
||||
* review your rights under the font license.
|
||||
*
|
||||
* Any font specification in the source HTML is translated to the closest font available
|
||||
* in the font directory.
|
||||
*
|
||||
* The pdf standard "Base 14 fonts" are:
|
||||
* Courier, Courier-Bold, Courier-BoldOblique, Courier-Oblique,
|
||||
* Helvetica, Helvetica-Bold, Helvetica-BoldOblique, Helvetica-Oblique,
|
||||
* Times-Roman, Times-Bold, Times-BoldItalic, Times-Italic,
|
||||
* Symbol, ZapfDingbats.
|
||||
*/
|
||||
|
||||
// Advised by dompdf (https://github.com/dompdf/dompdf/pull/782).
|
||||
'font_dir' => storage_path('fonts'),
|
||||
|
||||
/**
|
||||
* The location of the DOMPDF font cache directory
|
||||
*
|
||||
* This directory contains the cached font metrics for the fonts used by DOMPDF.
|
||||
* This directory can be the same as DOMPDF_FONT_DIR
|
||||
*
|
||||
* Note: This directory must exist and be writable by the webserver process.
|
||||
*/
|
||||
'font_cache' => storage_path('fonts'),
|
||||
|
||||
/**
|
||||
* The location of a temporary directory.
|
||||
*
|
||||
* The directory specified must be writeable by the webserver process.
|
||||
* The temporary directory is required to download remote images and when
|
||||
* using the PDFLib back end.
|
||||
*/
|
||||
'temp_dir' => ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir(),
|
||||
|
||||
/**
|
||||
* ==== IMPORTANT ====
|
||||
*
|
||||
* dompdf's "chroot": Prevents dompdf from accessing system files or other
|
||||
* files on the webserver. All local files opened by dompdf must be in a
|
||||
* subdirectory of this directory. DO NOT set it to '/' since this could
|
||||
* allow an attacker to use dompdf to read any files on the server. This
|
||||
* should be an absolute path.
|
||||
* This is only checked on command line call by dompdf.php, but not by
|
||||
* direct class use like:
|
||||
* $dompdf = new DOMPDF(); $dompdf->load_html($htmldata); $dompdf->render(); $pdfdata = $dompdf->output();
|
||||
*/
|
||||
'chroot' => realpath(base_path()),
|
||||
|
||||
/**
|
||||
* Protocol whitelist
|
||||
*
|
||||
* Protocols and PHP wrappers allowed in URIs, and the validation rules
|
||||
* that determine if a resouce may be loaded. Full support is not guaranteed
|
||||
* for the protocols/wrappers specified
|
||||
* by this array.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
'allowed_protocols' => [
|
||||
'data://' => ['rules' => []],
|
||||
'file://' => ['rules' => []],
|
||||
'http://' => ['rules' => []],
|
||||
'https://' => ['rules' => []],
|
||||
],
|
||||
|
||||
/**
|
||||
* Operational artifact (log files, temporary files) path validation
|
||||
*/
|
||||
'artifactPathValidation' => null,
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
'log_output_file' => null,
|
||||
|
||||
/**
|
||||
* Whether to enable font subsetting or not.
|
||||
*/
|
||||
'enable_font_subsetting' => false,
|
||||
|
||||
/**
|
||||
* The PDF rendering backend to use
|
||||
*
|
||||
* Valid settings are 'PDFLib', 'CPDF' (the bundled R&OS PDF class), 'GD' and
|
||||
* 'auto'. 'auto' will look for PDFLib and use it if found, or if not it will
|
||||
* fall back on CPDF. 'GD' renders PDFs to graphic files.
|
||||
* {@link * Canvas_Factory} ultimately determines which rendering class to
|
||||
* instantiate based on this setting.
|
||||
*
|
||||
* Both PDFLib & CPDF rendering backends provide sufficient rendering
|
||||
* capabilities for dompdf, however additional features (e.g. object,
|
||||
* image and font support, etc.) differ between backends. Please see
|
||||
* {@link PDFLib_Adapter} for more information on the PDFLib backend
|
||||
* and {@link CPDF_Adapter} and lib/class.pdf.php for more information
|
||||
* on CPDF. Also see the documentation for each backend at the links
|
||||
* below.
|
||||
*
|
||||
* The GD rendering backend is a little different than PDFLib and
|
||||
* CPDF. Several features of CPDF and PDFLib are not supported or do
|
||||
* not make any sense when creating image files. For example,
|
||||
* multiple pages are not supported, nor are PDF 'objects'. Have a
|
||||
* look at {@link GD_Adapter} for more information. GD support is
|
||||
* experimental, so use it at your own risk.
|
||||
*
|
||||
* @link http://www.pdflib.com
|
||||
* @link http://www.ros.co.nz/pdf
|
||||
* @link http://www.php.net/image
|
||||
*/
|
||||
'pdf_backend' => 'CPDF',
|
||||
|
||||
/**
|
||||
* html target media view which should be rendered into pdf.
|
||||
* List of types and parsing rules for future extensions:
|
||||
* http://www.w3.org/TR/REC-html40/types.html
|
||||
* screen, tty, tv, projection, handheld, print, braille, aural, all
|
||||
* Note: aural is deprecated in CSS 2.1 because it is replaced by speech in CSS 3.
|
||||
* Note, even though the generated pdf file is intended for print output,
|
||||
* the desired content might be different (e.g. screen or projection view of html file).
|
||||
* Therefore allow specification of content here.
|
||||
*/
|
||||
'default_media_type' => 'screen',
|
||||
|
||||
/**
|
||||
* The default paper size.
|
||||
*
|
||||
* North America standard is "letter"; other countries generally "a4"
|
||||
*
|
||||
* @see CPDF_Adapter::PAPER_SIZES for valid sizes ('letter', 'legal', 'A4', etc.)
|
||||
*/
|
||||
'default_paper_size' => 'a4',
|
||||
|
||||
/**
|
||||
* The default paper orientation.
|
||||
*
|
||||
* The orientation of the page (portrait or landscape).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
'default_paper_orientation' => 'portrait',
|
||||
|
||||
/**
|
||||
* The default font family
|
||||
*
|
||||
* Used if no suitable fonts can be found. This must exist in the font folder.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
'default_font' => 'serif',
|
||||
|
||||
/**
|
||||
* Image DPI setting
|
||||
*
|
||||
* This setting determines the default DPI setting for images and fonts. The
|
||||
* DPI may be overridden for inline images by explictly setting the
|
||||
* image's width & height style attributes (i.e. if the image's native
|
||||
* width is 600 pixels and you specify the image's width as 72 points,
|
||||
* the image will have a DPI of 600 in the rendered PDF. The DPI of
|
||||
* background images can not be overridden and is controlled entirely
|
||||
* via this parameter.
|
||||
*
|
||||
* For the purposes of DOMPDF, pixels per inch (PPI) = dots per inch (DPI).
|
||||
* If a size in html is given as px (or without unit as image size),
|
||||
* this tells the corresponding size in pt.
|
||||
* This adjusts the relative sizes to be similar to the rendering of the
|
||||
* html page in a reference browser.
|
||||
*
|
||||
* In pdf, always 1 pt = 1/72 inch
|
||||
*
|
||||
* Rendering resolution of various browsers in px per inch:
|
||||
* Windows Firefox and Internet Explorer:
|
||||
* SystemControl->Display properties->FontResolution: Default:96, largefonts:120, custom:?
|
||||
* Linux Firefox:
|
||||
* about:config *resolution: Default:96
|
||||
* (xorg screen dimension in mm and Desktop font dpi settings are ignored)
|
||||
*
|
||||
* Take care about extra font/image zoom factor of browser.
|
||||
*
|
||||
* In images, <img> size in pixel attribute, img css style, are overriding
|
||||
* the real image dimension in px for rendering.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
'dpi' => 96,
|
||||
|
||||
/**
|
||||
* Enable embedded PHP
|
||||
*
|
||||
* If this setting is set to true then DOMPDF will automatically evaluate embedded PHP contained
|
||||
* within <script type="text/php"> ... </script> tags.
|
||||
*
|
||||
* ==== IMPORTANT ==== Enabling this for documents you do not trust (e.g. arbitrary remote html pages)
|
||||
* is a security risk.
|
||||
* Embedded scripts are run with the same level of system access available to dompdf.
|
||||
* Set this option to false (recommended) if you wish to process untrusted documents.
|
||||
* This setting may increase the risk of system exploit.
|
||||
* Do not change this settings without understanding the consequences.
|
||||
* Additional documentation is available on the dompdf wiki at:
|
||||
* https://github.com/dompdf/dompdf/wiki
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
'enable_php' => false,
|
||||
|
||||
/**
|
||||
* Rnable inline JavaScript
|
||||
*
|
||||
* If this setting is set to true then DOMPDF will automatically insert JavaScript code contained
|
||||
* within <script type="text/javascript"> ... </script> tags as written into the PDF.
|
||||
* NOTE: This is PDF-based JavaScript to be executed by the PDF viewer,
|
||||
* not browser-based JavaScript executed by Dompdf.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
'enable_javascript' => true,
|
||||
|
||||
/**
|
||||
* Enable remote file access
|
||||
*
|
||||
* If this setting is set to true, DOMPDF will access remote sites for
|
||||
* images and CSS files as required.
|
||||
*
|
||||
* ==== IMPORTANT ====
|
||||
* This can be a security risk, in particular in combination with isPhpEnabled and
|
||||
* allowing remote html code to be passed to $dompdf = new DOMPDF(); $dompdf->load_html(...);
|
||||
* This allows anonymous users to download legally doubtful internet content which on
|
||||
* tracing back appears to being downloaded by your server, or allows malicious php code
|
||||
* in remote html pages to be executed by your server with your account privileges.
|
||||
*
|
||||
* This setting may increase the risk of system exploit. Do not change
|
||||
* this settings without understanding the consequences. Additional
|
||||
* documentation is available on the dompdf wiki at:
|
||||
* https://github.com/dompdf/dompdf/wiki
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
'enable_remote' => false,
|
||||
|
||||
/**
|
||||
* List of allowed remote hosts
|
||||
*
|
||||
* Each value of the array must be a valid hostname.
|
||||
*
|
||||
* This will be used to filter which resources can be loaded in combination with
|
||||
* isRemoteEnabled. If enable_remote is FALSE, then this will have no effect.
|
||||
*
|
||||
* Leave to NULL to allow any remote host.
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
'allowed_remote_hosts' => null,
|
||||
|
||||
/**
|
||||
* A ratio applied to the fonts height to be more like browsers' line height
|
||||
*/
|
||||
'font_height_ratio' => 1.1,
|
||||
|
||||
/**
|
||||
* Use the HTML5 Lib parser
|
||||
*
|
||||
* @deprecated This feature is now always on in dompdf 2.x
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
'enable_html5_parser' => true,
|
||||
],
|
||||
|
||||
];
|
||||
|
|
@ -67,7 +67,6 @@ return [
|
|||
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
|
||||
'throw' => false,
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ return [
|
|||
'delete' => 'delete',
|
||||
'save' => 'save',
|
||||
'edit' => 'edit',
|
||||
'export' => 'export',
|
||||
],
|
||||
|
||||
'helpers' => [
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ return [
|
|||
'delete' => 'supprimer',
|
||||
'save' => 'sauvegarder',
|
||||
'edit' => 'mettre à jour',
|
||||
'export' => 'exporter',
|
||||
],
|
||||
|
||||
'helpers' => [
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import "./../modules/sweetalert";
|
|||
import "./base/common";
|
||||
|
||||
// CUSTOM
|
||||
import "./pages/claims";
|
||||
import "./pages/customer.delete";
|
||||
import "./pages/data.delete";
|
||||
import "./pages/search-bar";
|
||||
|
|
|
|||
24
resources/ts/bo/pages/claims.ts
Normal file
24
resources/ts/bo/pages/claims.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// Type for 'bootstrap modal show' event. Needed for 'relatedTarget'.
|
||||
interface BootstrapModalShowEvent extends CustomEvent {
|
||||
relatedTarget: HTMLElement;
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const exportFormModal = document.getElementById("exportModal");
|
||||
const claimListInputs = document.getElementsByClassName("claim-list-input");
|
||||
|
||||
if (!exportFormModal || !claimListInputs) {
|
||||
return;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
for (const claimListInput of claimListInputs) {
|
||||
(claimListInput as HTMLInputElement).value = String(datas);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -2,6 +2,7 @@ enum FilterKey {
|
|||
Brand = "brand",
|
||||
Question = "question",
|
||||
Answer = "answer",
|
||||
Subject = "subject",
|
||||
}
|
||||
|
||||
function setupFilterListener(selectId: string, paramKey: FilterKey): void {
|
||||
|
|
@ -25,6 +26,7 @@ const filterMap: Record<string, FilterKey> = {
|
|||
brandSelect: FilterKey.Brand,
|
||||
questionSelect: FilterKey.Question,
|
||||
answerSelect: FilterKey.Answer,
|
||||
subjectSelect: FilterKey.Subject,
|
||||
};
|
||||
|
||||
Object.entries(filterMap).forEach(([selectId, paramKey]) => {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,10 @@ import {
|
|||
faEraser,
|
||||
faEye,
|
||||
faEyeSlash,
|
||||
faFile,
|
||||
faFileCsv,
|
||||
faFileExport,
|
||||
faFilePdf,
|
||||
faFolderOpen,
|
||||
faListUl,
|
||||
faPaintRoller,
|
||||
|
|
@ -57,6 +61,10 @@ library.add(
|
|||
faTrash,
|
||||
faEye,
|
||||
faEyeSlash,
|
||||
faFile,
|
||||
faFileCsv,
|
||||
faFileExport,
|
||||
faFilePdf,
|
||||
faFolderOpen,
|
||||
faCrop,
|
||||
faQuestionCircle,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,20 @@
|
|||
\App\Models\ClaimSubject::class,
|
||||
\App\Models\ClaimMailTemplate::class,
|
||||
];
|
||||
$defaultFilters = [
|
||||
'App\Models\Customer' => [],
|
||||
'App\Models\Brand' => [],
|
||||
'App\Models\Question' => [],
|
||||
'App\Models\Service' => [],
|
||||
'App\Models\SingleOptin' => [],
|
||||
'App\Models\Claim' => [
|
||||
'sort_col' => 'created_at',
|
||||
'sort_way' => 'desc'
|
||||
],
|
||||
'App\Models\ClaimSubject' => [],
|
||||
'App\Models\ClaimMailTemplate' => [],
|
||||
|
||||
]
|
||||
@endphp
|
||||
@foreach ($modelsClasses as $crudModel)
|
||||
@can('viewAny', $crudModel)
|
||||
|
|
@ -23,7 +37,10 @@
|
|||
<ul class="nav flex-column mb-2">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link w-fit" data-bs-tooltip="tooltip" data-bs-placement="right"
|
||||
href="{{ route(sprintf('%s%s.index', $prefix ?? '', $crudModel->getTable())) }}"
|
||||
href="{{ route(
|
||||
sprintf('%s%s.index', $prefix ?? '', $crudModel->getTable()),
|
||||
$defaultFilters[get_class($crudModel)]
|
||||
) }}"
|
||||
title="{{ __('Afficher la liste') }}">
|
||||
{{ __('Liste') }}
|
||||
</a>
|
||||
|
|
@ -37,6 +54,18 @@
|
|||
</a>
|
||||
</li>
|
||||
@endcan
|
||||
@if ($crudModel instanceof \App\Models\Claim)
|
||||
<li class="nav-item">
|
||||
<a class="nav-link w-fit" data-bs-tooltip="tooltip" data-bs-placement="right"
|
||||
href="{{ route(
|
||||
sprintf('%s%s.index', $prefix ?? '', $crudModel->getTable()),
|
||||
['status' => [\App\Enums\Claims\ClaimStatus::treated, \App\Enums\Claims\ClaimStatus::refused]]
|
||||
) }}"
|
||||
title="{{ __('Afficher l\'historique') }}">
|
||||
{{ __('Historique') }}
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
@if (!$loop->last)
|
||||
<hr>
|
||||
|
|
|
|||
54
resources/views/bo/modules/claim-tools.blade.php
Normal file
54
resources/views/bo/modules/claim-tools.blade.php
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
@php
|
||||
$selectedSubject = request()->subject ?? (isset($modelTable) ? Session::get("crud.$modelTable.subject") : null);
|
||||
@endphp
|
||||
|
||||
<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 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"
|
||||
id="subjectSelect" role="button" name="subject">
|
||||
<option value="0" @selected(!$selectedSubject)>{{ __('Sélectionnez un sujet...') }}</option>
|
||||
@foreach ($claimSubjectModels as $claimSubjectModel)
|
||||
<option value="{{ $claimSubjectModel->getKey() }}" @selected($selectedSubject == $claimSubjectModel->getKey())>
|
||||
{{ $claimSubjectModel->label }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- EXPORT MODAL --}}
|
||||
<div class="modal fade" id="exportModal" tabindex="-1" aria-labelledby="exportModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title fs-5" id="exportModalLabel">{{ __('Exporter les réclamations')}}</h2>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
@foreach(\App\Enums\ExportFormat::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->label()])}}
|
||||
</button>
|
||||
</form>
|
||||
@endforeach
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ __('Fermer')}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -67,7 +67,7 @@
|
|||
@php
|
||||
$parameters = ['rstFltrs' => 'true', 'search' => null, 'page' => null];
|
||||
if ($routes) {
|
||||
$parameters = array_merge($parameters, ['brand' => null, 'question' => null, 'answer' => null]);
|
||||
$parameters = array_merge($parameters, ['brand' => null, 'question' => null, 'answer' => null, 'subject' => null]);
|
||||
}
|
||||
@endphp
|
||||
<a class="btn btn-danger ms-2" data-bs-tooltip="tooltip"
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<div class="d-flex align-items-start flex-row">
|
||||
@can('viewAny', $claimModel)
|
||||
<a class="btn btn-primary text-decoration-none me-2" data-bs-tooltip="tooltip" data-bs-placement="top"
|
||||
data-bs-tooltip="tooltip" href="{{ route('bo.claims.index') }}"
|
||||
data-bs-tooltip="tooltip" href="{{ route('bo.claims.index', ['sort_col' => 'created_at', 'sort_way' => 'desc']) }}"
|
||||
title="{{ __('crud.helpers.list_all', ['model' => __('models.classes.claims')]) }}">
|
||||
<i class="fa-solid fa-arrow-left"></i>
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@
|
|||
<thead class="border-top-0 border-start-0 border-end-0 border border-2">
|
||||
@include('bo.modules.table-col-sorter', [
|
||||
'cols' => [
|
||||
'reference' => str(__('validation.custom.reference'))->ucfirst(),
|
||||
'email' => str(__('models.classes.customers'))->ucfirst(),
|
||||
'brand_id' => str(__('models.classes.brands'))->singular()->ucfirst(),
|
||||
'type' => str(__('validation.custom.type'))->ucfirst(),
|
||||
'status' => str(__('validation.custom.status'))->ucfirst(),
|
||||
'reference' => str(__('validation.custom.reference'))->ucfirst(),
|
||||
'created_at' => str(__('validation.attributes.created_at'))->ucfirst(),
|
||||
'email' => str(__('models.classes.customers'))->ucfirst(),
|
||||
'message' => str(__('validation.attributes.message'))->ucfirst(),
|
||||
'brand_id' => str(__('models.classes.brands'))->singular()->ucfirst(),
|
||||
'type' => str(__('validation.custom.type'))->ucfirst(),
|
||||
'status' => str(__('validation.custom.status'))->ucfirst(),
|
||||
'updated_at' => str(__('validation.attributes.updated_at'))->ucfirst(),
|
||||
],
|
||||
])
|
||||
|
|
@ -16,12 +18,18 @@
|
|||
@foreach ($claimModels as $claimModel)
|
||||
<tr class="border-bottom list-item">
|
||||
<td class="text-center align-middle">{{ $claimModel->reference }}</td>
|
||||
<td class="text-center align-middle">
|
||||
<span class="badge bg-secondary text-bg-secondary">
|
||||
{{ $claimModel->created_at->isoFormat('LLLL') }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-center align-middle">
|
||||
<div class="d-flex flex-column justify-content-center align-items-center">
|
||||
<p class="m-0">{{ sprintf('%s %s', $claimModel->first_name, $claimModel->last_name) }}</p>
|
||||
<p class="text-muted m-0">{{ e($claimModel->email) }}</p>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-center align-middle">{{ $claimModel->message }}</td>
|
||||
<td class="text-center align-middle">{{ $claimModel->brand->name }}</td>
|
||||
<td class="text-center align-middle">
|
||||
<x-bo.claims.enum-badge :claimModel="$claimModel" enum="type" />
|
||||
|
|
@ -52,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') }}">
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@include('bo.modules.claim-tools')
|
||||
@include('bo.modules.search-bar')
|
||||
<div class="row w-100 mx-auto mb-2">
|
||||
<div class="col table-responsive p-0">
|
||||
|
|
|
|||
21
resources/views/export/claim_item_pdf.blade.php
Normal file
21
resources/views/export/claim_item_pdf.blade.php
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<p>{{ __('Référence') }} : {{ $model->reference }} </p>
|
||||
<p>{{ __('Date de la demande') }} : {{ $model->created_at->toDateString() }} </p>
|
||||
<p>{{ __('Prénom NOM') }} : {{ $model->first_name }} {{ str($model->last_name)->upper() }}</p>
|
||||
<p>{{ __('Email') }} : {{ $model->email }} </p>
|
||||
<p>{{ __('Adresse') }} : {{ sprintf('%s, %s, %s', $model->address, $model->postcode, $model->city) }} </p>
|
||||
<p>{{ __('Statut de la réclamation') }} : {{ $model->status->label() }} </p>
|
||||
<p>{{ __('Sujet de la réclamation') }} : {{ $model->claimSubject->label }} </p>
|
||||
<p>{{ __('Type de demande') }} : {{ $model->type->label() }} </p>
|
||||
<p>{{ __('Marque concernée') }} : {{ $model->brand->name }} </p>
|
||||
<p>{{ __('Message') }} : {{ $model->message }} </p>
|
||||
{{-- ATTACHMENT --}}
|
||||
@if ($model->file_extension)
|
||||
@if (in_array($model->file_extension, ['jpg', 'jpeg', 'png']))
|
||||
<p>{{ __('Image') }} :</p>
|
||||
<img src="{{ $model->fileBase64 }}">
|
||||
@else
|
||||
<p>{{ __('Nom de la pièce jointe') }} : {{ $model->export_file_name }}</p>
|
||||
@endif
|
||||
@else
|
||||
<p>{{ __('Aucune pièce jointe.') }}</p>
|
||||
@endif
|
||||
24
resources/views/export/pdf.blade.php
Normal file
24
resources/views/export/pdf.blade.php
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<!doctype html>
|
||||
<html dir="ltr" lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
<style>
|
||||
.page-break {
|
||||
page-break-after: always;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@foreach($models as $model)
|
||||
@include('export.' . $modelName . '_item_pdf', ['model' => $model])
|
||||
|
||||
{{-- Page break between every claim. --}}
|
||||
@if (!$loop->last)
|
||||
<div class="page-break"></div>
|
||||
@endif
|
||||
@endforeach
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -93,6 +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');
|
||||
|
||||
// * CLAIM SUBJECTS.
|
||||
Route::get('/{brand}/claim_subjects/json/paginate', [ClaimController::class, 'jsonClaimPaginateModels'])
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue