feat: ✨ add claim mail templates crud

This commit is contained in:
Alexis Gousseau 2025-08-04 11:09:03 +02:00
parent 7df0fe4634
commit ed7ed6f55e
No known key found for this signature in database
GPG key ID: 4A614CB3DBBA2406
14 changed files with 813 additions and 94 deletions

View file

@ -0,0 +1,174 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\ClaimMailTemplates\ClaimMailTemplateRequest;
use App\Models\Brand;
use App\Models\ClaimMailTemplate;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class ClaimMailTemplateController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(ClaimMailTemplate::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = ClaimMailTemplate::query()->when(
$request->filled('brand'),
fn($q) => $q->whereHas('brand', fn($q) => $q->where('id', $request->brand))
);
if ($request->search) {
$this->searchQuery(
$query,
$request->search,
null,
'label',
);
}
$this->sortQuery($query);
$searchFields = trans('validation.custom.label');
$claimMailTemplateModels = $this->paginate($query);
return view('bo.pages.claim_mail_templates.index', compact('claimMailTemplateModels', 'searchFields'));
}
/**
* Show the specified resource.
*
* @param \App\Models\ClaimMailTemplate $claim_mail_template
* @return \Illuminate\Contracts\View\View
*/
public function show(ClaimMailTemplate $claim_mail_template): \Illuminate\Contracts\View\View
{
return view('bo.pages.claim_mail_templates.show', [
'claimMailTemplateModel' => $claim_mail_template,
]);
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\ClaimMailTemplate $claim_mail_template
* @return \Illuminate\Contracts\View\View
*/
public function create(ClaimMailTemplate $claim_mail_template): \Illuminate\Contracts\View\View
{
$brandModels = Brand::query()->orderBy('name', 'ASC')->paginate($this->jsonItemPerPagination);
return view('bo.pages.claim_mail_templates.create', [
'claimMailTemplateModel' => $claim_mail_template,
'brandModels' => $brandModels,
]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\ClaimMailTemplates\ClaimMailTemplateRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(ClaimMailTemplateRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$claimMailTemplateModel = new ClaimMailTemplate();
$claimMailTemplateModel->fill($request->validated());
if ($claimMailTemplateModel->saveOrFail()) {
return redirect()
->route('bo.claim_mail_templates.edit', $claimMailTemplateModel)
->with('success', trans(
':model a été créé.',
['model' => Str::singular(trans('models.classes.claimmailtemplates'))]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été créé.',
['model' => Str::singular(trans('models.classes.claimmailtemplates'))]
));
});
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\ClaimMailTemplate $claim_mail_template
* @return \Illuminate\Contracts\View\View
*/
public function edit(ClaimMailTemplate $claim_mail_template): \Illuminate\Contracts\View\View
{
$brandModels = Brand::query()->orderBy('name', 'ASC')->paginate($this->jsonItemPerPagination);
return view('bo.pages.claim_mail_templates.edit', [
'claimMailTemplateModel' => $claim_mail_template,
'brandModels' => $brandModels,
]);
}
/**
* Update a spacific resource in storage.
*
* @param \App\Http\Requests\Bo\ClaimSubjects\ClaimMailTemplateRequest $request
* @param \App\Models\ClaimMailTemplate $claim_mail_template
* @return \Illuminate\Http\RedirectResponse
*/
public function update(
ClaimMailTemplateRequest $request,
ClaimMailTemplate $claim_mail_template
): \Illuminate\Http\RedirectResponse {
return DB::transaction(function () use ($request, $claim_mail_template) {
$claim_mail_template->fill($request->validated());
if ($claim_mail_template->saveOrFail()) {
return redirect()
->route('bo.claim_mail_templates.edit', $claim_mail_template)
->with('success', trans(
':model a été mis à jour.',
['model' => Str::singular(trans('models.classes.claimmailtemplates'))]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été mis à jour.',
['model' => Str::singular(trans('models.classes.claimmailtemplates'))]
));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\ClaimMailTemplate $claim_mail_template
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(ClaimMailTemplate $claim_mail_template): \Illuminate\Http\RedirectResponse
{
if ($claim_mail_template->deleteOrFail()) {
return redirect()->route('bo.claim_mail_templates.index')
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::of(__('models.classes.claimmailtemplates'))->singular()
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::of(__('models.classes.claimmailtemplates'))->singular()
]));
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace App\Http\Requests\Bo\ClaimMailTemplates;
use Illuminate\Foundation\Http\FormRequest;
class ClaimMailTemplateRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'brand_id' => 'required|exists:brands,id',
'label' => 'required|string|min:3|max:255',
'content' => 'required|string|min:3|max:500',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'brand_id' => trans('models.classes.brands'),
'label' => trans('validation.custom.label'),
'content' => trans('validation.attributes.content'),
];
}
}

View file

@ -0,0 +1,109 @@
<?php
namespace App\Policies;
use App\Enums\Users\RoleEnum;
use App\Models\ClaimMailTemplate;
use App\Models\User;
use App\Policies\Rules\UserStaticRules;
class ClaimMailTemplatePolicy
{
/**
* ViewAny a claim subject.
*
* @param \App\Models\User $authUser
* @return boolean
*/
public function viewAny(User $authUser): bool
{
return UserStaticRules::atLeastRole($authUser, RoleEnum::marketing);
}
/**
* View a claim subject.
*
* @param \App\Models\User $authUser
* @param \App\Models\ClaimMailTemplate $claim_mail_template
* @return boolean
*/
public function view(User $authUser, ClaimMailTemplate $claim_mail_template): bool
{
return $this->checkOnlyMinAdminOrInUserBrands($authUser, $claim_mail_template);
}
/**
* Create a claim subject.
*
* @param \App\Models\User $authUser
* @return boolean
*/
public function create(User $authUser): bool
{
return UserStaticRules::atLeastRole($authUser, RoleEnum::marketing);
}
/**
* Store a claim subject.
*
* @param \App\Models\User $authUser
* @return boolean
*/
public function store(User $authUser): bool
{
return UserStaticRules::atLeastRole($authUser, RoleEnum::marketing);
}
/**
* Edit a claim subject.
*
* @param \App\Models\User $authUser
* @param \App\Models\ClaimMailTemplate $claim_mail_template
* @return boolean
*/
public function edit(User $authUser, ClaimMailTemplate $claim_mail_template): bool
{
return $this->checkOnlyMinAdminOrInUserBrands($authUser, $claim_mail_template);
}
/**
* Update a claim subject.
*
* @param \App\Models\User $authUser
* @param \App\Models\ClaimMailTemplate $claim_mail_template
* @return boolean
*/
public function update(User $authUser, ClaimMailTemplate $claim_mail_template): bool
{
return $this->checkOnlyMinAdminOrInUserBrands($authUser, $claim_mail_template);
}
/**
* Delete a claim subject.
*
* @param \App\Models\User $authUser
* @param \App\Models\ClaimMailTemplate $claim_mail_template
* @return boolean
*/
public function delete(User $authUser, ClaimMailTemplate $claim_mail_template): bool
{
return $this->checkOnlyMinAdminOrInUserBrands($authUser, $claim_mail_template);
}
/**
* Check if the user an access a specific resource.
*
* @param \App\Models\User $authUser
* @param \App\Models\ClaimMailTemplate $claim_mail_template
* @return boolean
*/
private function checkOnlyMinAdminOrInUserBrands(User $authUser, ClaimMailTemplate $claim_mail_template): bool
{
$userBrandModelKeys = $authUser->brands->pluck('id')->toArray();
$claimMailTemplateModelKey = $claim_mail_template->brand->getKey();
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin) || (
UserStaticRules::atLeastRole($authUser, RoleEnum::marketing)
&& \in_array($claimMailTemplateModelKey, $userBrandModelKeys)
);
}
}

View file

@ -1,109 +1,102 @@
<template>
<WordCounter
:id="intId"
:id="id"
class="ck-word-count"
/>
<textarea
ref="textarea"
:id="intId"
:class="`input-text-auto-component ${intClass}`"
:name="intName"
:aria-describedby="intAriaDescribedby"
:placeholder="intPlaceholder"
:required="intRequired"
@input="onInput"
:id="id"
:class="{'form-control': true, [classPath]: !!classPath, 'is-invalid': errors.methods.parseValidationInput('content', allErrors)}"
:name="name"
:aria-describedby="ariaDescribedby"
:placeholder="placeholder"
:required="required"
@input="styleElement(checkInput())"
@blur="onBlur"
v-model="intValue"
v-model="value"
rows="5"
/>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import trans from "../../modules/trans";
<script lang="ts" setup>
import { computed, nextTick, onBeforeMount, onMounted, ref, useAttrs } from "vue";
import WordCounter from "./WordCounterComponent.vue";
import errors from "./../../modules/errors";
const textarea = ref<HTMLTextAreaElement | null>(null);
export default defineComponent({
name: "InputTextAutoComponent",
inheritAttrs: false,
mixins: [trans],
components: {
WordCounter,
},
setup() {
return { textarea };
},
beforeMount() {
if (!this.$attrs.json) {
throw new Error("this component requires json");
}
},
mounted() {
const json = String(this.$attrs.json ?? "{}"),
data = JSON.parse(json);
this.intId = String(data.id);
this.intClass = String(data.class);
this.intName = String(data.name);
this.intPlaceholder = String(data.placeholder);
this.intAriaDescribedby = String(data.ariaDescribedby);
this.intValue = String(data.value);
if (data.required) {
this.intRequired = true;
}
if (this.textarea) {
// Default input init
this.textarea.setAttribute("style", "height: 1rem;overflow-y: hidden;");
}
// * Init input
this.$nextTick(this.onInput);
},
data(): {
intId: string;
intClass: string;
intName: string;
intPlaceholder: string;
intAriaDescribedby: string;
intRequired: boolean;
intValue: string;
} {
return {
intId: "",
intClass: "",
intName: "",
intPlaceholder: "",
intAriaDescribedby: "",
intRequired: false,
intValue: "",
};
},
methods: {
onBlur() {
const el = textarea.value;
if (!el) {
throw new Error("undefined textarea element");
}
// * Remove every new line char
el.value = el.value.replace(/[\n\r]/g, "");
this.$nextTick(() => {
el.style.height = "auto";
el.style.height = `${el.scrollHeight}px`;
});
},
onInput() {
const el = textarea.value;
if (!el) {
throw new Error("undefined textarea element");
}
el.style.height = "auto";
el.style.height = `${el.scrollHeight}px`;
},
},
defineOptions({
name: "InputTextAutoComponent"
});
</script>
<style lang="scss">
.input-text-auto-component {
resize: none;
// * ATTRIBUTES
const attrs = useAttrs();
// * REFS
const textarea = ref<HTMLTextAreaElement|null>(null);
// * DATA
/** Hack to set the id for wordCounterComponent. */
const id = ref<string>(JSON.parse(String(attrs.json ?? "{}")).id);
const classPath = ref<string>("");
const name = ref<string>("");
const placeholder = ref<string>("");
const ariaDescribedby = ref<string>("");
const value = ref<string>("");
const required = ref<boolean>(true);
const allErrors = ref<LaravelValidationErrors>({});
// * COMPUTED
const isUsedWithProps = computed<boolean>(() => attrs.json === undefined);
onBeforeMount(() => {
if (isUsedWithProps.value) {
throw new Error("this component requires json");
}
});
// * LIFECYCLE
onMounted(() => {
const json = String(attrs.json ?? "{}"),
data = JSON.parse(json);
if (!isUsedWithProps.value) {
id.value = String(data.id);
classPath.value = String(data.class);
name.value = String(data.name);
placeholder.value = String(data.placeholder);
ariaDescribedby.value = String(data.ariaDescribedby);
value.value = String(data.value);
required.value = data.required;
textarea.value?.setAttribute("style", "height:1rem;overflow-y:hidden;");
allErrors.value = data.errors;
}
nextTick(() => {
styleElement(checkInput());
});
});
// * METHODS
/** When the textarea get the focus. */
function onBlur() {
const el = checkInput();
/** Remove every new line char */
// el.value = el.value.replace(/[\n\r]/g, "");
nextTick(() => {
styleElement(el);
});
}
</style>
/** Check if the element is an HTMLTextAreaElement. */
function checkInput(): HTMLTextAreaElement {
const el = textarea.value;
if (!el) {
throw new Error("undefined textarea element");
}
return el;
}
/** Style the textarea element. */
function styleElement(el: HTMLTextAreaElement): void {
el.style.height = "auto";
el.style.height = `${el.scrollHeight}px`;
}
</script>

View file

@ -11,6 +11,7 @@
\App\Models\SingleOptin::class,
\App\Models\Claim::class,
\App\Models\ClaimSubject::class,
\App\Models\ClaimMailTemplate::class,
];
@endphp
@foreach ($modelsClasses as $crudModel)

View file

@ -0,0 +1,46 @@
@extends('bo.layout')
@section('title', sprintf('%s - %s', __('models.classes.claimmailtemplates'), str(__('création'))->ucfirst()))
@section('description', __('Création d\'une :model', ['model' => __('models.classes.claimmailtemplates')]))
@section('metaIndex', 'noindex,nofollow')
@section('content')
<div class="card border-0">
<div class="card-header">
<div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap">
<div class="d-flex align-items-start flex-row">
@can('viewAny', \App\Models\ClaimMailTemplate::class)
<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.claim_mail_templates.index') }}"
title="{{ __('Retourner sur la liste des :model', ['model' => __('models.classes.claimmailtemplates')]) }}">
<i class="fa-solid fa-arrow-left"></i>
</a>
@endcan
@include('modules.breadcrumbs.breadcrumb-body')
</div>
@can('create', $claimMailTemplateModel)
<div class="col-12 col-md-4 d-flex justify-content-center justify-content-md-end align-items-center">
<button class="btn btn-primary" id="formSubmitClone" data-bs-tooltip="tooltip" type="button"
title="{{ __('Sauvegarder cette :model', ['model' => Str::singular(__('models.classes.claimmailtemplates'))]) }}">
{{ __('Sauvegarder') }}
</button>
</div>
@endcan
</div>
</div>
<div class="card-body">
<form action="{{ route('bo.claim_mail_templates.store') }}" method="POST">
@csrf
@include('bo.pages.claim_mail_templates.form')
<div class="row">
<div class="col text-center">
@can('create', $claimMailTemplateModel)
<button class="btn btn-primary" id="formSubmit" data-bs-tooltip="tooltip" type="submit"
title="{{ __('Sauvegarder cette :model', ['model' => Str::singular(__('models.classes.claimmailtemplates'))]) }}">{{ __('Sauvegarder') }}</button>
@endcan
</div>
</div>
</form>
</div>
</div>
@endsection

View file

@ -0,0 +1,65 @@
@extends('bo.layout')
@section('title', sprintf('%s - %s', __('models.classes.claimmailtemplates'), str(__('édition'))->ucfirst()))
@section('description', __('Édition d\'une :model', ['model' => __('models.classes.claimmailtemplates')]))
@section('metaIndex', 'noindex,nofollow')
@section('content')
<div class="card border-0">
<div class="card-header">
<div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap">
<div class="d-flex align-items-start flex-row">
@can('viewAny', \App\Models\ClaimMailTemplate::class)
<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.claim_mail_templates.index') }}"
title="{{ __('Retourner sur la liste des :model', ['model' => __('models.classes.claimmailtemplates')]) }}">
<i class="fa-solid fa-arrow-left"></i>
</a>
@endcan
@include('modules.breadcrumbs.breadcrumb-body')
</div>
<form class="btn-group" id="dataDelete"
action="{{ route('bo.claim_mail_templates.destroy', $claimMailTemplateModel) }}" method="POST">
@can('view', $claimMailTemplateModel)
<a class="btn btn-warning" data-bs-tooltip="tooltip"
href="{{ route('bo.claim_mail_templates.show', $claimMailTemplateModel->getKey()) }}"
title="{{ __('Voir ce :model', ['model' => str(__('models.classes.claimmailtemplates'))->singular()]) }}">
<i class="fa-solid fa-eye"></i>
</a>
@endcan
@can('update', $claimMailTemplateModel)
<button class="btn btn-primary" id="formSubmitClone" data-bs-tooltip="tooltip" type="button"
title="{{ __('Sauvegarder ce :model', ['model' => str(__('models.classes.claimmailtemplates'))->singular()]) }}">
{{ __('Sauvegarder') }}
</button>
@endcan
@can('delete', $claimMailTemplateModel)
@csrf
@method('DELETE')
<button class="btn btn-danger" data-bs-tooltip="tooltip"
title="{{ __('Supprimer ce :model', ['model' => str(__('models.classes.claimmailtemplates'))->singular()]) }}">
<i class="fa-solid fa-trash"></i>
</button>
@endcan
</form>
</div>
</div>
<div class="card-body">
<form action="{{ route('bo.claim_mail_templates.update', $claimMailTemplateModel->getKey()) }}" method="POST">
@csrf
@method('PUT')
@include('bo.pages.claim_mail_templates.form')
<div class="row">
<div class="col text-center">
@can('update', $claimMailTemplateModel)
<button class="btn btn-primary" id="formSubmit" data-bs-tooltip="tooltip" type="submit"
title="{{ __('Sauvegarder ce :model', ['model' => str(__('models.classes.claimmailtemplates'))->singular()]) }}">
{{ __('Sauvegarder') }}
</button>
@endcan
</div>
</div>
</form>
</div>
</div>
@endsection

View file

@ -0,0 +1,93 @@
<div class="row">
{{-- BRAND --}}
<div class="col-12 col-md-6 pb-3">
<fieldset class="h-100 border p-3">
<legend>{{ __('Marque') }}</legend>
<div class="row">
@include('bo.partials.inputs.search-belongs-to-dropdown', [
'id' => 'claimMailTemplateBrand',
'label' => __('Modifier la :model associée', [
'model' => str(__('models.classes.brands'))->singular(),
]),
'name' => 'brand_id',
'relation' => 'brand',
'model' => $claimMailTemplateModel,
'targetClass' => 'App\Models\Brand',
'modelListPaginate' => $brandModels ?? [],
'constructedRoute' => route('bo.brands.json-paginate'),
'fieldName' => 'name',
'multiple' => false,
'required' => true,
'disabled' => false,
'placeholder' => __('Rechercher une :model', [
'model' => str(__('models.classes.brands'))->singular(),
]),
'helper' => __('Sélectionner une :model qui sera associée parmis la liste.', [
'model' => str(__('models.classes.brands'))->singular(),
]),
])
</div>
</fieldset>
</div>
{{-- LABEL --}}
<div class="col-12 col-md-6 mb-3">
<fieldset class="h-100 border p-3">
<legend>{{ str(__('validation.custom.label'))->ucfirst() }}</legend>
<div class="row">
<div class="col-12 mb-3">
@include('bo.partials.inputs.varchar', [
'id' => 'claimMailTemplateLabel',
'label' => __('Nom du :model', [
'model' => str(__('models.classes.claimsubjects'))->singular(),
]),
'hasWordCounter' => true,
'name' => 'label',
'required' => true,
'model' => $claimMailTemplateModel ?? null,
'helper' => __('Doit décrire précisément le produit (3 caractères minimum, 255 maximum).'),
])
</div>
</div>
</fieldset>
</div>
<div class="col-12 mb-3">
<fieldset class="h-100 border p-3">
<legend>{{ str(__('validation.attributes.content'))->ucfirst() }}</legend>
<div class="row">
<div class="col-12 mb-3">
@include('bo.partials.inputs.varchar-multiline', [
'id' => 'claimMailTemplateContent',
'label' => __('Texte du :model', [
'model' => str(__('models.classes.claimsubjects'))->singular(),
]),
'name' => 'content',
'model' => $claimMailTemplateModel ?? null,
'required' => true,
'helper' => __('validation.between.string', [
'attribute' => __(':model', [
'model' => str(__('models.classes.claimsubjects'))->singular(),
]),
'min' => 5,
'max' => 160,
]),
'placeholder' => __('Votre texte ici.'),
])
</div>
</div>
</fieldset>
</div>
</div>
@push('scripts')
<script @if (isset($nonce)) nonce="{{ $nonce }}" @endif>
document.addEventListener("DOMContentLoaded", () => {
// Submit button clone.
const submit = document.getElementById('formSubmit')
submitClone = document.getElementById('formSubmitClone');
submitClone.addEventListener('click', (event) => {
event.preventDefault();
submit.click();
})
});
</script>
@endpush

View file

@ -0,0 +1,64 @@
<table class="table table-hover table-fix-action m-0">
<thead class="border-top-0 border-start-0 border-end-0 border border-2">
@include('bo.modules.table-col-sorter', [
'cols' => [
'label' => str(__('validation.custom.label'))->ucfirst(),
'content' => str(__('validation.attributes.content'))->ucfirst(),
'brand_id' => str(__('models.classes.brands'))->singular()->ucfirst(),
'updated_at' => str(__('validation.attributes.updated_at'))->ucfirst(),
],
])
</thead>
<tbody>
@if ($claimMailTemplateModels->count())
@foreach ($claimMailTemplateModels as $claimMailTemplateModel)
<tr>
<td class="text-center align-middle">{{ $claimMailTemplateModel->label }}</td>
<td class="text-center align-middle">{{ str($claimMailTemplateModel->content)->limit(50) }}</td>
<td class="text-center align-middle">{{ $claimMailTemplateModel->brand->name }}</td>
<td class="text-center align-middle">
<span class="badge bg-warning text-bg-warning text-wrap">
{{ $claimMailTemplateModel->updated_at }}
</span>
</td>
<td class="text-end align-middle">
<form class="btn-group" id="dataDelete"
action="{{ route('bo.claim_mail_templates.destroy', $claimMailTemplateModel) }}"
method="POST">
@can('view', $claimMailTemplateModel)
<a class="btn btn-sm btn-warning" data-bs-tooltip="tooltip"
href="{{ route('bo.claim_mail_templates.show', $claimMailTemplateModel->getKey()) }}"
title="{{ __('Voir ce :model', ['model' => Str::singular(__('models.classes.claimmailtemplates'))]) }}">
<i class="fa-solid fa-eye"></i>
</a>
@endcan
@can('update', $claimMailTemplateModel)
<a class="btn btn-sm btn-primary" data-bs-tooltip="tooltip"
href="{{ route('bo.claim_mail_templates.edit', $claimMailTemplateModel->getKey()) }}"
title="{{ __('Modifier ce :model', ['model' => Str::singular(__('models.classes.claimmailtemplates'))]) }}">
<i class="fa-solid fa-pencil"></i>
</a>
@endcan
@can('delete', $claimMailTemplateModel)
@csrf
@method('DELETE')
<button class="btn btn-sm btn-danger" data-bs-tooltip="tooltip"
title="{{ __('Supprimer ce :model', ['model' => Str::singular(__('models.classes.claimmailtemplates'))]) }}">
<i class="fa-solid fa-trash"></i>
</button>
@endcan
</form>
</td>
</tr>
@endforeach
@else
<tr>
<td class="text-center" colspan="5">
<span class="h3">
{{ __('Aucun résultat pour les :models', ['models' => __('models.classes.claimmailtemplates')]) }}
</span>
</td>
</tr>
@endif
</tbody>
</table>

View file

@ -0,0 +1,41 @@
@extends('bo.layout')
@section('title', sprintf('%s - %s', __('models.classes.claimmailtemplates'), str(__('liste'))->ucfirst()))
@section('description', __('Listage de toutes les :model', ['model' => __('models.classes.claimmailtemplates')]))
@section('metaIndex', 'noindex,nofollow')
@section('content')
<div class="row w-100 mx-auto">
<div class="col p-0">
<div class="card rounded-0 border-0">
<div class="card-header">
<div class="row w-100 mx-auto">
<div class="col p-0">
@include('modules.breadcrumbs.breadcrumb-body')
</div>
@can('create', \App\Models\ClaimMailTemplate::class)
<div class="col-md-3 d-flex align-items-center p-0">
<div class="input-group justify-content-end">
<a class="btn btn-primary" data-bs-tooltip="tooltip"
href="{{ route('bo.claim_mail_templates.create') }}"
title="{{ __('Ajouter une :model', ['model' => str(__('models.classes.claimmailtemplates'))->singular()]) }}">
<i class="fa-solid fa-plus"></i>
</a>
</div>
</div>
@endcan
</div>
</div>
<div class="card-body">
@include('bo.modules.search-bar')
<div class="row w-100 mx-auto mb-2">
<div class="col table-responsive p-0">
@include('bo.pages.claim_mail_templates.index-list')
</div>
</div>
{!! $claimMailTemplateModels->links() !!}
</div>
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,65 @@
@extends('bo.layout', ['brParam' => $claimMailTemplateModel])
@section('title', sprintf('%s - %s', __('models.classes.claimmailtemplates'), str(__('aperçu'))->ucfirst()))
@section('description', __('Aperçu d\'un :model', ['model' => __('models.classes.claimmailtemplates')]))
@section('metaIndex', 'noindex,nofollow')
@section('breadcrumb', request()->route()->getName())
@section('content')
<div class="card border-0">
<div class="card-header">
<div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap">
<div class="d-flex align-items-start flex-row">
@can('viewAny', \App\Models\ClaimMailTemplate::class)
<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.claim_mail_templates.index') }}"
title="{{ __('Retourner sur la liste des :model', ['model' => __('models.classes.claimmailtemplates')]) }}">
<i class="fa-solid fa-arrow-left"></i>
</a>
@endcan
@include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $claimMailTemplateModel])
</div>
<form class="btn-group" id="dataDelete"
action="{{ route('bo.claim_mail_templates.destroy', $claimMailTemplateModel) }}" method="POST">
@can('update', $claimMailTemplateModel)
<a class="btn btn-primary" data-bs-tooltip="tooltip"
href="{{ route('bo.claim_mail_templates.edit', $claimMailTemplateModel->getKey()) }}"
title="{{ __('Modifier ce :model', ['model' => Str::singular(__('models.classes.claimmailtemplates'))]) }}">
<i class="fa-solid fa-pencil"></i>
</a>
@endcan
@can('delete', $claimMailTemplateModel)
@csrf
@method('DELETE')
<button class="btn btn-danger" data-bs-tooltip="tooltip"
title="{{ __('Supprimer ce :model', ['model' => Str::singular(__('models.classes.claimmailtemplates'))]) }}">
<i class="fa-solid fa-trash"></i>
</button>
@endcan
</form>
</div>
</div>
<div class="card-body">
<table class="table-sm table-bordered table-striped table">
<tbody>
<tr>
<td class="w-50"><b>{{ str(__('validation.custom.label'))->ucfirst() }}</b></td>
<td class="w-50">{{ $claimMailTemplateModel->label }}</td>
</tr>
<tr>
<td class="w-50"><b>{{ str(__('validation.attributes.content'))->ucfirst() }}</b></td>
<td class="w-50">
<p class="m-0">
{!! clean(nl2br($claimMailTemplateModel->content)) !!}
</p>
</td>
</tr>
<tr>
<td class="w-50"><b>{{ __('Marque') }}</b></td>
<td class="w-50">{{ $claimMailTemplateModel->brand->name }}</td>
</tr>
</tbody>
</table>
</div>
</div>
@endsection

View file

@ -23,6 +23,7 @@
'required' => $required,
'value' => old($name, $model->{$name} ?? ''),
'class' => 'w-100',
'errors' => $errors->getBag('default')->getMessages() ?? [],
];
@endphp
<div class="input-text-auto" data-json='@json($data)'></div>

View file

@ -160,3 +160,20 @@ Breadcrumbs::for('bo.claim_subjects.show', function (Generator $trail) {
$trail->parent('bo.claim_subjects.index');
$trail->push(trans('Aperçu'));
});
// * CLAIM MAIL TEMPLATES
Breadcrumbs::for('bo.claim_mail_templates.index', function (Generator $trail) {
$trail->push(trans('models.classes.claimsubjects'), route('bo.claim_mail_templates.index'));
});
Breadcrumbs::for('bo.claim_mail_templates.create', function (Generator $trail) {
$trail->parent('bo.claim_mail_templates.index');
$trail->push(trans('Création'), route('bo.claim_mail_templates.create'));
});
Breadcrumbs::for('bo.claim_mail_templates.edit', function (Generator $trail) {
$trail->parent('bo.claim_mail_templates.index');
$trail->push(trans('Édition'));
});
Breadcrumbs::for('bo.claim_mail_templates.show', function (Generator $trail) {
$trail->parent('bo.claim_mail_templates.index');
$trail->push(trans('Aperçu'));
});

View file

@ -4,6 +4,7 @@ use App\Http\Controllers\Bo\ActivityLogsController;
use App\Http\Controllers\Bo\AnswerController;
use App\Http\Controllers\Bo\BrandController;
use App\Http\Controllers\Bo\ClaimController;
use App\Http\Controllers\Bo\ClaimMailTemplateController;
use App\Http\Controllers\Bo\ClaimMessageController;
use App\Http\Controllers\Bo\ClaimSubjectController;
use App\Http\Controllers\Bo\CustomerController;
@ -107,6 +108,9 @@ Route::middleware(['throttle:web', 'admin-secure'])->prefix('bo')->name('bo.')->
// * CLAIM SUBJECTS.
Route::resource('claim_subjects', ClaimSubjectController::class);
// * CLAIM MAIL TEMPLATES.
Route::resource('claim_mail_templates', ClaimMailTemplateController::class);
// * OTHERS.
Route::post('theme/set', [Controller::class, 'setTheme'])->name('theme.set');
});