add contact page

This commit is contained in:
Alexis Gousseau 2023-10-26 13:17:31 +02:00
parent 58111ca5dd
commit 0f4fe5eaac
26 changed files with 940 additions and 130 deletions

View file

@ -23,7 +23,6 @@ enum ActivityLogsEventEnum: int
self::deleted->name => 'Suppression',
];
/**
* Custom added classes definition
*/

View file

@ -0,0 +1,21 @@
<?php
namespace App\Enums\Contact;
use App\Traits\Enums\BaseEnum;
enum CivilityEnum: int
{
use BaseEnum;
case man = 1;
case woman = 2;
/**
* Optionnal labels definition
*/
private const LABELS = [
self::man->name => 'Homme',
self::woman->name => 'Femme',
];
}

View file

@ -0,0 +1,25 @@
<?php
namespace App\Enums\Contact;
use App\Traits\Enums\BaseEnum;
enum ObjectEnum: int
{
use BaseEnum;
case customerService = 1;
case ideasSuggestions = 2;
case informationRequest = 3;
case other = 4;
/**
* Optionnal labels definition
*/
private const LABELS = [
self::customerService->name => 'Service consommateurs',
self::ideasSuggestions->name => 'Idées & suggestions',
self::informationRequest->name => 'Demande d’informations',
self::other->name => 'Divers',
];
}

View file

@ -4,11 +4,14 @@ namespace App\Http\Controllers\Fo;
use App\Enums\StaticPageType;
use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\ContactRequest;
use App\Mail\SendContactForm;
use App\Models\Homepage;
use App\Models\News;
use App\Models\Range;
use App\Models\SeasonalSection;
use App\Models\StaticPage;
use Illuminate\Support\Facades\Mail;
class StaticPageController extends Controller
{
@ -68,4 +71,34 @@ class StaticPageController extends Controller
'rangeModels',
));
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Contracts\View\View
*/
public function contact(): \Illuminate\Contracts\View\View
{
/** @var \App\Models\StaticPage $staticPageModel */
$contactModel = StaticPage::query()->where('type', StaticPageType::contact)->firstOrFail();
return view('fo.pages.contact', compact('contactModel'));
}
/**
* Display a listing of the resource.
*
* @param \App\Http\Requests\Fo\ContactRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function contactForm(ContactRequest $request): \Illuminate\Http\RedirectResponse
{
Mail::to(\config('mail.from.address'))->send(new SendContactForm($request->validated()));
return redirect()->route('fo.contact.show')
->with(
'success',
'Bonjour, votre message a bien été transmis. nous vous en remercions. Cordialement, l’équipe Agis.'
);
}
}

View file

@ -0,0 +1,184 @@
<?php
namespace App\Http\Requests\Fo;
use App\Enums\Contact\CivilityEnum;
use App\Enums\Contact\ObjectEnum;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Enum;
class ContactRequest extends FormRequest
{
use HasPicture;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return true;
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'accept_rules' => $this->accept_rules ? true : false,
'send_data' => $this->send_data ? true : false,
]);
$this->mergePicture('file');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$rules = [
'civility' => ['sometimes', 'nullable', new Enum(CivilityEnum::class)],
'last_name' => 'sometimes|nullable|string|between:3,255',
'first_name' => 'sometimes|nullable|string|between:3,255',
'society' => 'sometimes|nullable|string|between:3,255',
'email' => 'required|email:rfc,strict,dns,spoof,filter|max:255',
'phone' => 'sometimes|nullable|numeric|min:0',
'address' => 'sometimes|nullable|string|between:3,255',
'postal_code' => 'sometimes|nullable|numeric|min:0',
'city' => 'sometimes|nullable|string|between:3,255',
'object' => ['sometimes', 'nullable', new Enum(ObjectEnum::class)],
'product_name' => [
'nullable', 'string', 'between:3,255',
// @phpcs:ignore Generic.Files.LineLength.TooLong
'required_with:product_date_limit,product_barcode,product_lot,product_date_consumption,product_location',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_name'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'product_date_limit' => [
'sometimes', 'nullable', 'string', 'between:3,255',
// @phpcs:ignore Generic.Files.LineLength.TooLong
'required_with:product_name,product_barcode,product_lot,product_date_consumption,product_location',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_date_limit'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'product_barcode' => [
'sometimes', 'nullable', 'numeric', 'min:0',
// @phpcs:ignore Generic.Files.LineLength.TooLong
'required_with:product_name,product_date_limit,product_lot,product_date_consumption,product_location',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_barcode'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'product_lot' => [
'sometimes', 'nullable', 'numeric', 'min:0',
// @phpcs:ignore Generic.Files.LineLength.TooLong
'required_with:product_name,product_date_limit,product_barcode,product_date_consumption,product_location',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_lot'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'product_date_consumption' => [
'sometimes', 'nullable', 'string', 'between:3,255',
// @phpcs:ignore Generic.Files.LineLength.TooLong
'required_with:product_name,product_date_limit,product_barcode,product_lot,product_location',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_date_consumption'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'product_location' => [
'sometimes', 'nullable', 'string', 'between:3,255',
// @phpcs:ignore Generic.Files.LineLength.TooLong
'required_with:product_name,product_date_limit,product_barcode,product_lot,product_date_consumption',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_location'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'message' => 'sometimes|nullable|string|between:3,255',
'accept_rules' => 'required|boolean',
'send_data' => 'required|boolean',
];
return \array_merge($rules, $this->pictureRules('file', 0, 0, 1920, 1080, true));
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'civility' => trans('validation.custom.civility'),
'first_name' => trans('validation.attributes.first_name'),
'last_name' => trans('validation.attributes.last_name'),
'society' => trans('validation.custom.society'),
'email' => trans('validation.attributes.email'),
'phone' => trans('validation.attributes.phone'),
'address' => trans('validation.attributes.address'),
'postal_code' => trans('validation.attributes.postal_code'),
'city' => trans('validation.attributes.city'),
'object' => trans('validation.custom.object'),
'product_name' => trans('validation.custom.product_name'),
'product_date_limit' => trans('validation.custom.product_date_limit'),
'product_barcode' => trans('validation.custom.product_barcode'),
'product_lot' => trans('validation.custom.product_lot'),
'product_date_consumption' => trans('validation.custom.product_date_consumption'),
'product_location' => trans('validation.custom.product_location'),
'message' => trans('validation.attributes.message'),
'accept_rules' => trans('validation.custom.accept_rules'),
'send_data' => trans('validation.custom.send_data'),
];
}
}

View file

@ -0,0 +1,208 @@
<?php
namespace App\Mail;
use App\Enums\Contact\CivilityEnum;
use App\Enums\Contact\ObjectEnum;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class SendContactForm extends Mailable
{
use Queueable;
use SerializesModels;
/**
* The civility.
*
* @var \App\Enums\Contact\CivilityEnum|null
*/
private $civility;
/**
* The first name.
*
* @var string|null
*/
private $first_name;
/**
* The last name.
*
* @var string|null
*/
private $last_name;
/**
* The society.
*
* @var string|null
*/
private $society;
/**
* The email.
*
* @var string
*/
private $email;
/**
* The phone.
*
* @var string|null
*/
private $phone;
/**
* The address.
*
* @var string|null
*/
private $address;
/**
* The postal code.
*
* @var string|null
*/
private $postal_code;
/**
* The city.
*
* @var string|null
*/
private $city;
/**
* The object.
*
* @var \App\Enums\Contact\ObjectEnum|null
*/
private $object;
/**
* The product name.
*
* @var string
*/
private $product_name;
/**
* The product date limit.
*
* @var string
*/
private $product_date_limit;
/**
* The product barcode.
*
* @var string
*/
private $product_barcode;
/**
* The product lot.
*
* @var string
*/
private $product_lot;
/**
* The product date consumption.
*
* @var string
*/
private $product_date_consumption;
/**
* The product_location.
*
* @var string
*/
private $product_location;
/**
* The message.
*
* @var string|null
*/
private $message;
/**
* The file.
*
* @var string|null
*/
private $file;
/**
* Create a new message instance.
*
* @param array $edition
* @return void
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
*/
public function __construct(array $edition)
{
// phpcs:enable
$this->civility = (isset($edition['civility'])) ? CivilityEnum::make(intval($edition['civility']))->label() : null;
$this->first_name = $edition['first_name'] ?? null;
$this->last_name = $edition['last_name'] ?? null;
$this->society = $edition['society'] ?? null;
$this->email = $edition['email'];
$this->phone = $edition['phone'] ?? null;
$this->address = $edition['address'] ?? null;
$this->postal_code = $edition['postal_code'] ?? null;
$this->city = $edition['city'] ?? null;
$this->object = (isset($edition['object'])) ? ObjectEnum::make(intval($edition['object']))->label() : null;
$this->product_name = $edition['product_name'];
$this->product_date_limit = $edition['product_date_limit'];
$this->product_barcode = $edition['product_barcode'];
$this->product_lot = $edition['product_lot'];
$this->product_date_consumption = $edition['product_date_consumption'];
$this->product_location = $edition['product_location'];
$this->message = $edition['message'] ?? null;
$this->file = $edition['file'] ?? null;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
$mail = $this->subject(\config('app.name') . ' - contact formulaire')
->markdown('emails.send-contact-form')
->with([
'civility' => $this->civility,
'first_name' => $this->first_name,
'last_name' => $this->last_name,
'society' => $this->society,
'email' => $this->email,
'phone' => $this->phone,
'address' => $this->address,
'postal_code' => $this->postal_code,
'city' => $this->city,
'object' => $this->object,
'product_name' => $this->product_name,
'product_date_limit' => $this->product_date_limit,
'product_barcode' => $this->product_barcode,
'product_lot' => $this->product_lot,
'product_date_consumption' => $this->product_date_consumption,
'product_location' => $this->product_location,
'message' => $this->message,
]);
if (isset($this->file)) {
$mail = $mail->attach($this->file->getRealPath(), [
'as' => $this->file->getClientOriginalName(),
'mime' => $this->file->getClientMimeType(),
]);
}
return $mail;
}
}

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -221,53 +221,65 @@ return [
'female' => 'of the ',
'vowel' => 'of the '
],
'event' => 'event',
'model' => 'target element',
'order' => 'order',
'publication' => 'publication',
'activated' => 'activated',
'target' => 'target',
'static_page_type' => 'static page type',
'seo' => 'SEO',
'seo_title' => 'SEO title',
'seo_description' => 'SEO description',
'description' => 'description',
'subtitle' => 'subtitle',
'tagline' => 'tagline',
'attribute_alt' => 'alt attribute',
'attribute_title' => 'title attribute',
'picture_productsheet' => 'productsheet picture',
'picture_thumbnail' => 'thumbnail',
'category' => 'category',
'publish_at' => 'publication date',
'unpublish_at' => 'end of publication date',
'background' => 'background',
'picture' => 'picture',
'picture_1' => 'picture one',
'picture_2' => 'picture two',
'picture_3' => 'picture three',
'presentation_title' => 'presentation title',
'title_part1' => 'title part one',
'title_part2' => 'title part two',
'title_part3' => 'title part three',
'category' => 'category',
'publish_at' => 'publication date',
'unpublish_at' => 'end of publication date',
'picture_ambiance' => 'ambiance picture',
'picture_packshot' => 'packshot picture',
'cooking_advice' => 'cooking advice',
'ingredients' => 'ingredients',
'nutritionnal_values' => 'nutritionnal values',
'key_feed' => 'key number for "feed well"',
'key_earth_1' => 'key number for "respect the earth" 1',
'key_earth_2' => 'key number for "respect the earth" 2',
'key_together_1' => 'key number for "live together" 1',
'key_together_2' => 'key number for "live together" 2',
'key_together_3' => 'key number for "live together" 3',
'key_together_4' => 'key number for "live together" 4',
'key_territory' => 'key number for "integrate its territory"',
'video_url' => 'URL of the video',
'job' => 'job',
'statut' => 'statut',
'event' => 'event',
'model' => 'target element',
'order' => 'order',
'publication' => 'publication',
'activated' => 'activated',
'target' => 'target',
'static_page_type' => 'static page type',
'seo' => 'SEO',
'seo_title' => 'SEO title',
'seo_description' => 'SEO description',
'description' => 'description',
'subtitle' => 'subtitle',
'tagline' => 'tagline',
'attribute_alt' => 'alt attribute',
'attribute_title' => 'title attribute',
'picture_productsheet' => 'productsheet picture',
'picture_thumbnail' => 'thumbnail',
'category' => 'category',
'publish_at' => 'publication date',
'unpublish_at' => 'end of publication date',
'background' => 'background',
'picture' => 'picture',
'picture_1' => 'picture one',
'picture_2' => 'picture two',
'picture_3' => 'picture three',
'presentation_title' => 'presentation title',
'title_part1' => 'title part one',
'title_part2' => 'title part two',
'title_part3' => 'title part three',
'category' => 'category',
'publish_at' => 'publication date',
'unpublish_at' => 'end of publication date',
'picture_ambiance' => 'ambiance picture',
'picture_packshot' => 'packshot picture',
'cooking_advice' => 'cooking advice',
'ingredients' => 'ingredients',
'nutritionnal_values' => 'nutritionnal values',
'key_feed' => 'key number for "feed well"',
'key_earth_1' => 'key number for "respect the earth" 1',
'key_earth_2' => 'key number for "respect the earth" 2',
'key_together_1' => 'key number for "live together" 1',
'key_together_2' => 'key number for "live together" 2',
'key_together_3' => 'key number for "live together" 3',
'key_together_4' => 'key number for "live together" 4',
'key_territory' => 'key number for "integrate its territory"',
'video_url' => 'URL of the video',
'job' => 'job',
'statut' => 'statut',
'society' => 'society',
'civility' => 'civility',
'object' => 'object',
'product_name' => 'product name',
'product_date_consumption' => 'product date consumption',
'product_date_limit' => 'product date limit',
'product_barcode' => 'product barcode',
'product_lot' => 'product lot',
'product_location' => 'product location',
'accept_rules' => 'acceptation of rules',
'send_data' => 'autorization to send data',
'file' => 'file',
],
];

View file

@ -223,53 +223,65 @@ return [
'female' => 'de la ',
'vowel' => 'de l\''
],
'event' => 'évènement',
'model' => 'élément ciblé',
'order' => 'ordre',
'publication' => 'publication',
'activated' => 'activé',
'target' => 'cible',
'static_page_type' => 'type de page',
'seo' => 'référencement SEO',
'seo_title' => 'titre SEO',
'seo_description' => 'description SEO',
'description' => 'description',
'subtitle' => 'sous-titre',
'tagline' => 'phrase d\'accroche',
'attribute_alt' => 'attribut alt',
'attribute_title' => 'attribut title',
'picture_productsheet' => 'image de la fiche produit',
'picture_thumbnail' => 'vignette',
'category' => 'catégorie',
'publish_at' => 'date de publication',
'unpublish_at' => 'date de fin de publication',
'background' => 'fond',
'picture' => 'image',
'picture_1' => 'image une',
'picture_2' => 'image deux',
'picture_3' => 'image trois',
'presentation_title' => 'titre de présentation',
'title_part1' => 'première partie du titre',
'title_part2' => 'seconde partie du titre',
'title_part3' => 'dernière partie du titre',
'category' => 'catégorie',
'publish_at' => 'date de publication',
'unpublish_at' => 'date de fin de publication',
'picture_ambiance' => 'image d\'ambiance',
'picture_packshot' => 'image du packshot',
'cooking_advice' => 'conseil de réchauffage',
'ingredients' => 'ingrédients',
'nutritionnal_values' => 'valeurs nutritionnelles',
'key_feed' => 'chiffre clé "bien nourrir"',
'key_earth_1' => 'Chiffre clé "respecter la terre" 1',
'key_earth_2' => 'Chiffre clé "respecter la terre" 2',
'key_together_1' => 'Chiffre clé "vivre ensemble" 1',
'key_together_2' => 'Chiffre clé "vivre ensemble" 2',
'key_together_3' => 'Chiffre clé "vivre ensemble" 3',
'key_together_4' => 'Chiffre clé "vivre ensemble" 4',
'key_territory' => 'Chiffre clé "intégrer son territoire"',
'video_url' => 'URL de la vidéo',
'job' => 'métier',
'statut' => 'statut',
'event' => 'évènement',
'model' => 'élément ciblé',
'order' => 'ordre',
'publication' => 'publication',
'activated' => 'activé',
'target' => 'cible',
'static_page_type' => 'type de page',
'seo' => 'référencement SEO',
'seo_title' => 'titre SEO',
'seo_description' => 'description SEO',
'description' => 'description',
'subtitle' => 'sous-titre',
'tagline' => 'phrase d\'accroche',
'attribute_alt' => 'attribut alt',
'attribute_title' => 'attribut title',
'picture_productsheet' => 'image de la fiche produit',
'picture_thumbnail' => 'vignette',
'category' => 'catégorie',
'publish_at' => 'date de publication',
'unpublish_at' => 'date de fin de publication',
'background' => 'fond',
'picture' => 'image',
'picture_1' => 'image une',
'picture_2' => 'image deux',
'picture_3' => 'image trois',
'presentation_title' => 'titre de présentation',
'title_part1' => 'première partie du titre',
'title_part2' => 'seconde partie du titre',
'title_part3' => 'dernière partie du titre',
'category' => 'catégorie',
'publish_at' => 'date de publication',
'unpublish_at' => 'date de fin de publication',
'picture_ambiance' => 'image d\'ambiance',
'picture_packshot' => 'image du packshot',
'cooking_advice' => 'conseil de réchauffage',
'ingredients' => 'ingrédients',
'nutritionnal_values' => 'valeurs nutritionnelles',
'key_feed' => 'chiffre clé "bien nourrir"',
'key_earth_1' => 'Chiffre clé "respecter la terre" 1',
'key_earth_2' => 'Chiffre clé "respecter la terre" 2',
'key_together_1' => 'Chiffre clé "vivre ensemble" 1',
'key_together_2' => 'Chiffre clé "vivre ensemble" 2',
'key_together_3' => 'Chiffre clé "vivre ensemble" 3',
'key_together_4' => 'Chiffre clé "vivre ensemble" 4',
'key_territory' => 'Chiffre clé "intégrer son territoire"',
'video_url' => 'URL de la vidéo',
'job' => 'métier',
'statut' => 'statut',
'society' => 'société',
'civility' => 'civilité',
'object' => 'objet',
'product_name' => 'nom du produit',
'product_date_consumption' => 'date de consommation',
'product_date_limit' => 'date de limite de consommation',
'product_barcode' => 'le code barre du produit',
'product_lot' => 'numéro du lot figurant à côté de la date',
'product_location' => 'le lieu de l\'achat',
'accept_rules' => 'acceptation des règles',
'send_data' => 'autorisation d\'enregistrer des données',
'file' => 'fichier',
],
];

View file

@ -12,3 +12,15 @@ img {
background-repeat: no-repeat;
background-size: 100% 100%;
}
.divide {
height: 4px;
background-image: url('@/images/homepage/h-bar.svg');
background-position: center bottom;
background-repeat: no-repeat;
background-size: 100% .2rem;
}
.highlight.black {
@include button-shape(#000000);
}

View file

@ -17,3 +17,4 @@
// * PAGES.
@import './pages/homepage';
@import './pages/contact';

View file

@ -0,0 +1,32 @@
.contact {
.select-form {
&::after {
position: absolute;
right: 1rem;
bottom: 0.3rem;
display: block;
width: 2rem;
height: 2rem;
background: url("@/images/global/btn-arrow.png");
background-position: center;
background-repeat: no-repeat;
background-size: 100% 100%;
content: "";
pointer-events: none;
transform: rotate(180deg);
transition: 0.1s ease-in-out;
}
&.active::after {
transform: rotate(270deg);
}
}
&-products-input {
display: none;
&.active {
display: flex;
}
}
}

View file

@ -15,14 +15,6 @@
.f-rufina {
font-size: 1.2rem;
}
.divide {
height: 4px;
background-image: url('@/images/homepage/h-bar.svg');
background-position: center bottom;
background-repeat: no-repeat;
background-size: 100% .2rem;
}
}
.seasonal-sections {

View file

@ -10,6 +10,9 @@ import "./utilities/vue-fo";
// CUSTOM
import "./base/common";
// PAGES
import "./pages/contact";
// COOKIES
import CookieNotice from "@ldc-web/cookie-notice";
window.CookieNotice = CookieNotice;

View file

@ -0,0 +1,35 @@
document.addEventListener("DOMContentLoaded", function () {
// Select after animation
const selects = document.querySelectorAll(".contact .select-form") as NodeListOf<HTMLFieldSetElement>;
document.body.addEventListener("click", function (event) {
const target = event.target as Node | null;
selects.forEach(select => {
if(select.contains(target)){
selectToggleArrowClass(select);
} else {
selectRemoveArrowClass(select);
}
});
});
function selectToggleArrowClass(select: HTMLFieldSetElement) {
select.classList.toggle("active");
}
function selectRemoveArrowClass(select: HTMLFieldSetElement) {
select.classList.remove("active");
}
// Show more input on specific option selected
const selectObject = document.getElementsByName("object")[0] as unknown as HTMLSelectElement;
selectObject.addEventListener("click", function () {
const contactProductsInput = document.querySelector(".contact-products-input") as HTMLDivElement;
if (Number(selectObject.value) === 1) {
contactProductsInput.classList.add("active");
}
else {
contactProductsInput.classList.remove("active");
}
});
});

View file

@ -0,0 +1,28 @@
@component('mail::message')
# Bonjour,
Voici un message depuis le formulaire de contact du site web {{ config('app.name') }} :
Information sur l'utilisateur:
{{ __('validation.custom.civility') }}: {{ $civility }}
{{ __('validation.attributes.first_name') }}: {{ $first_name }}
{{ __('validation.attributes.last_name') }}: {{ $last_name }}
{{ __('validation.custom.society') }}: {{ $society }}
{{ __('validation.attributes.email') }}: {{ $email }}
{{ __('validation.attributes.password') }}: {{ $phone }}
{{ __('validation.attributes.address') }}: {{ $address }}
{{ __('validation.attributes.postal_code') }}: {{ $postal_code }}
{{ __('validation.attributes.city') }}: {{ $city }}
{{ __('validation.custom.object') }}: {{ $object }}
{{ __('validation.attributes.message') }}: {{ $message }}
Information sur le produit:
{{ __('validation.custom.product_name') }}: {{ $product_name }}
{{ __('validation.custom.product_date_limit') }}: {{ $product_date_limit }}
{{ __('validation.custom.product_barcode') }}: {{ $product_barcode }}
{{ __('validation.custom.product_lot') }}: {{ $product_lot }}
{{ __('validation.custom.product_date_consumption') }}: {{ $product_date_consumption }}
{{ __('validation.custom.product_location') }}: {{ $product_location }}
{{ config('app.name') }}.
@endcomponent

View file

@ -8,8 +8,8 @@
<body>
@include('fo.layouts.header')
<main>
<div class="row justify-content-center">
<div class="col col-md-6">
<div class="row container mx-auto">
<div class="col-12 p-0">
@include('modules.flash-messages')
</div>
</div>

View file

@ -83,8 +83,8 @@
</a>
</li>
<li class="nav-item">
<a class="nav-link text-uppercase @if (request()->routeIs('CHANGEME')) active @endif" data-bs-toggle="tooltip"
href="#" title="{{ __('Contact') }}">
<a class="nav-link text-uppercase @if (request()->routeIs('fo.contact.show')) active @endif" data-bs-toggle="tooltip"
href="{{ route('fo.contact.show') }}" title="{{ __('Contact') }}">
{{ __('Contact') }}
</a>
</li>

View file

@ -25,7 +25,7 @@
{{ __('Rejoignez-nous') }}
</a>
<a class="nav-link display-5 fw-bold text-uppercase @if (request()->routeIs('CHANGEME')) active @endif" data-bs-toggle="tooltip"
href="#" title="{{ __('Contact') }}">
href="{{ route('fo.contact.show') }}" title="{{ __('Contact') }}">
{{ __('Contact') }}
</a>
</li>

View file

@ -1,12 +0,0 @@
@auth('backend')
<ul class="navbar-nav me-auto">
<li class="nav-item">
<a class="nav-link @if(request()->routeIs('fo.homepage')) active @endif"
href="{{ route('fo.homepage') }}"
title="{{ __('Revenir à l\'accueil') }}"
data-bs-toggle="tooltip">
{{ __('Accueil') }}
</a>
</li>
</ul>
@endauth

View file

@ -0,0 +1,225 @@
@extends('fo.layout')
@section('title', $contactModel->seo_title ?? __('Contact'))
@section('description', $contactModel->seo_description ?? __('Contactez-nous !'))
@section('breadcrumb',
request()->route()->getName())
@section('content')
<section class="contact">
<div class="container">
<div class="row">
<div class="col-12">
<span class="divide d-block w-100 mt-xl-3 my-2"></span>
<h1 class="f-sheldon text-uppercase my-3 text-center">{{ $contactModel->title }}</h1>
<span class="divide d-block w-100 mt-xl-3 my-2"></span>
</div>
<div class="col-12">
<p class="text-capitalize m-0 text-center">{{ __('siège social agis') }}</p>
<p class="text-capitalize m-0 text-center">{{ __('802 rue sainte-geneviève') }}</p>
<p class="text-capitalize m-0 text-center">{{ __('84000 avignon') }}</p>
<p class="text-capitalize m-0 text-center">{{ __('t. +33 (0)1 4 90 80 99') }}</p>
</div>
</div>
</div>
<form method="POST" action="{{ route('fo.contact.store') }}" enctype="multipart/form-data">
@method('POST')
@csrf
<div class="bg-agis-yellow mb-md-5 pb-md-5 m-0 p-0">
<div class="container">
<div class="position-relative px-md-0 mt-5 px-2 py-5">
<p
class="position-absolute start-50 translate-middle f-readex-pro highlight black top-0 m-0 w-fit px-5 text-center text-white">
{{ __('Formulaire de contact') }}</p>
<div class="row w-100 mx-auto">
<div class="col-12 col-md-6 p-0">
<div class="h-100 me-md-5 position-relative">
<fieldset class="select-form d-flex flex-column justify-content-end align-items-start h-100">
<select class="form-control text-capitalize" name="civility" role="button">
<option class="text-capitalize" disabled selected>{{ __('validation.custom.civility') }}</option>
@foreach (\App\Enums\Contact\CivilityEnum::toArray() as $key => $enumObject)
<option class="text-capitalize" value="{{ $enumObject->value }}"
@if (old('civility') == $key) selected @endif>
{{ $enumObject->label }}
</option>
@endforeach
</select>
</fieldset>
</div>
</div>
<div class="col-12 col-md-6 p-0">
<fieldset class="d-flex flex-column justify-content-center align-items-start ps-md-5">
<label class="mt-1">{{ Str::of(__('validation.attributes.last_name'))->ucFirst() }}&nbsp;:</label>
<input class="form-control" name="last_name" value="{{ old('last_name') }}">
</fieldset>
</div>
<div class="col-12 col-md-6 p-0">
<fieldset class="d-flex flex-column justify-content-center align-items-start pe-md-5">
<label class="mt-1">{{ Str::of(__('validation.attributes.first_name'))->ucFirst() }}&nbsp;:</label>
<input class="form-control" name="first_name" value="{{ old('first_name') }}">
</fieldset>
</div>
<div class="col-12 col-md-6 p-0">
<fieldset class="d-flex flex-column justify-content-center align-items-start ps-md-5">
<label class="mt-1">{{ Str::of(__('validation.custom.society'))->ucFirst() }}&nbsp;:</label>
<input class="form-control" name="society" value="{{ old('society') }}">
</fieldset>
</div>
<div class="col-12 col-md-6 p-0">
<fieldset class="d-flex flex-column justify-content-center align-items-start pe-md-5">
<label class="mt-1">{{ Str::of(__('validation.attributes.email'))->ucFirst() }}&nbsp;:</label>
<input class="form-control" name="email" value="{{ old('email') }}" required>
</fieldset>
</div>
<div class="col-12 col-md-6 p-0">
<fieldset class="d-flex flex-column justify-content-center align-items-start ps-md-5">
<label class="mt-1">{{ Str::of(__('validation.attributes.phone'))->ucFirst() }}&nbsp;:</label>
<input class="form-control" name="phone" type="number" value="{{ old('phone') }}">
</fieldset>
</div>
<div class="col-12 col-md-6 p-0">
<fieldset class="d-flex flex-column justify-content-center align-items-start pe-md-5">
<label class="mt-1">{{ Str::of(__('validation.attributes.address'))->ucFirst() }}&nbsp;:</label>
<input class="form-control" name="address" value="{{ old('address') }}">
</fieldset>
</div>
<div class="col-12 col-md-6 p-0">
<fieldset class="d-flex flex-column justify-content-center align-items-start ps-md-5">
<label class="mt-1">{{ Str::of(__('validation.attributes.postal_code'))->ucFirst() }}&nbsp;:</label>
<input class="form-control" name="postal_code" type="number" value="{{ old('postal_code') }}">
</fieldset>
</div>
<div class="col-12 col-md-6 p-0">
<fieldset class="d-flex flex-column justify-content-center align-items-start pe-md-5">
<label class="mt-1">{{ Str::of(__('validation.attributes.city'))->ucFirst()->value() }}&nbsp;:</label>
<input class="form-control" name="city" value="{{ old('city') }}">
</fieldset>
</div>
<div class="col-12 col-md-6 p-0">
<div class="ms-md-5 position-relative pt-md-0 h-100 pt-4">
<fieldset class="select-form d-flex flex-column justify-content-end align-items-start h-100">
<select class="form-control text-capitalize" name="object" role="button">
<option class="text-capitalize" disabled selected>{{ __('validation.custom.object') }}</option>
@foreach (\App\Enums\Contact\ObjectEnum::toArray() as $key => $enumObject)
<option class="text-capitalize" value="{{ $enumObject->value }}"
@if (old('object') === $key) selected @endif>
{{ $enumObject->label }}
</option>
@endforeach
</select>
</fieldset>
</div>
</div>
<div class="contact-products-input row mx-auto p-0">
<p class="highlight black m-0 mx-auto my-5 w-fit px-5 text-center text-white">
{{ Str::of(__('pour pouvoir vous répondre nous avons besoin de quelques informations :'))->ucFirst()->value() }}
</p>
<div class="col-12 col-md-6 p-0">
<fieldset class="d-flex flex-column justify-content-center align-items-start pe-md-5">
<label class="mt-1" for="product_name">
{{ Str::of(__('validation.custom.product_name'))->ucFirst()->value() }}*&nbsp;:
</label>
<input class="form-control" id="product_name" name="product_name" value="{{ old('product_name') }}">
</fieldset>
</div>
<div class="col-12 col-md-6 p-0">
<fieldset class="d-flex flex-column justify-content-center align-items-start ps-md-5">
<label class="mt-1" for="product_date_limit">
{{ Str::of(__('validation.custom.product_date_limit'))->ucFirst()->value() }}*&nbsp;:
</label>
<input class="form-control" id="product_date_limit" name="product_date_limit"
value="{{ old('product_date_limit') }}">
</fieldset>
</div>
<div class="col-12 col-md-6 p-0">
<fieldset class="d-flex flex-column justify-content-center align-items-start pe-md-5">
<label class="mt-1" for="product_barcode">
{{ Str::of(__('validation.custom.product_barcode'))->ucFirst()->value() }}*&nbsp;:
</label>
<input class="form-control" id="product_barcode" name="product_barcode" type="number"
value="{{ old('product_barcode') }}">
</fieldset>
</div>
<div class="col-12 col-md-6 p-0">
<fieldset class="d-flex flex-column justify-content-center align-items-start ps-md-5">
<label class="mt-1" for="product_lot">
{{ Str::of(__('validation.custom.product_lot'))->ucFirst()->value() }}*&nbsp;:
</label>
<input class="form-control" id="product_lot" name="product_lot" type="number"
value="{{ old('product_lot') }}">
</fieldset>
</div>
<div class="col-12 col-md-6 p-0">
<fieldset class="d-flex flex-column justify-content-center align-items-start pe-md-5">
<label class="mt-1" for="product_date_consumption">
{{ Str::of(__('validation.custom.product_date_consumption'))->ucFirst()->value() }}*&nbsp;:
<span class="text-body-secondary">{{ __('Ex : 25/08/2028') }}</span>
</label>
<input class="form-control" id="product_date_consumption" name="product_date_consumption"
value="{{ old('product_date_consumption') }}">
</fieldset>
</div>
<div class="col-12 col-md-6 p-0">
<fieldset class="d-flex flex-column justify-content-center align-items-start ps-md-5">
<label class="mt-1" for="product_location">
{{ Str::of(__('validation.custom.product_location'))->ucFirst()->value() }}*&nbsp;:
</label>
<input class="form-control" id="product_location" name="product_location"
value="{{ old('product_location') }}">
</fieldset>
</div>
<div class="col-12 my-5 p-0">
<fieldset class="row">
<div class="col-12 col-md-6 pe-md-5">
<label class="mt-1" for="file">
{{ Str::of(__('souhaitez-vous joindre une photo ?'))->ucFirst()->value() }}
</label>
<span
class="text-body-secondary">{{ Str::of(__('formats acceptés : jpg, png'))->ucFirst()->value() }}</span>
</div>
<div class="col-12 col-md-6 ps-md-5">
<input class="form-control" id ="file" name="file" type="file"
value="{{ old('file') }}" role="button" accept="image/*">
</div>
</fieldset>
</div>
</div>
<div class="col-12 mb-5 p-0">
<fieldset class="d-flex flex-column justify-content-center align-items-start">
<label class="mt-1" for="message">
{{ Str::of(__('message'))->ucFirst()->value() }}&nbsp;:
</label>
<textarea class="form-control" id="message" name="message" rows="6">{{ old('message') }}</textarea>
</fieldset>
</div>
<div class="col-12 mb-1 p-0">
<fieldset class="d-flex justify-content-start align-items-start align-items-md-center flex-row">
<input class="form-check-input rounded-0 mt-2 p-3" id="accept_rules" name="accept_rules" type="checkbox"
value="{{ old('accept_rules') }}" role="button" required>
<label class="ms-2 mt-1" for="accept_rules" role="button">
{{ Str::of(__('j\'ai lu et j\'accepte la politique de confidentialité de ce site.'))->ucFirst()->value() }}&nbsp;:
<a href="{{ route('fo.legals.show') }}" target="_blank">{{ __('Voir nos mentions légales') }}</a>
</label>
</fieldset>
</div>
<div class="col-12 mb-1 p-0">
<fieldset class="d-flex justify-content-start align-items-start align-items-md-center flex-row">
<input class="form-check-input rounded-0 mt-2 p-3" id="send_data" name="send_data" type="checkbox"
value="{{ old('send_data') }}" role="button" required>
<label class="ms-2 mt-1" for="send_data" role="button">
{{ Str::of(__('j\'autorise Agis à enregistrer mes données à fin de traiter ma demande'))->ucFirst()->value() }}&nbsp;:
</label>
</fieldset>
</div>
<p>{{ __('*Champ obligatoire') }}</p>
</div>
</div>
<button class="btn btn-primary d-none d-md-block w-100 text-capitalize" type="submit">{{ __('envoyer') }}</button>
</div>
</div>
<div class="px-3">
<button class="btn btn-primary d-block d-md-none w-100 mb-5 mt-3" type="submit">{{ __('envoyer') }}</button>
</div>
</form>
</section>
@endsection

View file

@ -109,11 +109,11 @@
</div>
<div class="d-flex justify-content-evenly align-items-center d-xl-none mb-2 mt-4 flex-row">
<div class="swiper-button-prev rounded-circle" role="button">
<img src="{{ Vite::asset('resources/assets/images/homepage/swiper-nav.png') }}"
<img src="{{ Vite::asset('resources/assets/images/global/btn-arrow.png') }}"
title="{{ __('Navigation du slider (gauche)') }}" alt="{{ __('flèche vers la gauche') }}">
</div>
<div class="swiper-button-next rounded-circle" role="button">
<img src="{{ Vite::asset('resources/assets/images/homepage/swiper-nav.png') }}"
<img src="{{ Vite::asset('resources/assets/images/global/btn-arrow.png') }}"
title="{{ __('Navigation du slider (droite)') }}" alt="{{ __('flèche vers la droite') }}">
</div>
</div>
@ -155,11 +155,11 @@
<div
class="navigation d-flex justify-content-evenly justify-content-xl-between align-items-center p-xl-4 mb-2 mt-3 flex-row p-0">
<div class="swiper-button-prev rounded-circle" role="button">
<img src="{{ Vite::asset('resources/assets/images/homepage/swiper-nav.png') }}"
<img src="{{ Vite::asset('resources/assets/images/global/btn-arrow.png') }}"
title="{{ __('Navigation du slider (gauche)') }}" alt="{{ __('flèche vers la gauche') }}">
</div>
<div class="swiper-button-next rounded-circle" role="button">
<img src="{{ Vite::asset('resources/assets/images/homepage/swiper-nav.png') }}"
<img src="{{ Vite::asset('resources/assets/images/global/btn-arrow.png') }}"
title="{{ __('Navigation du slider (droite)') }}" alt="{{ __('flèche vers la droite') }}">
</div>
</div>

View file

@ -8,5 +8,5 @@
@section('button', true)
@section('image')
<img class="logo img-fluid" src="{{ Vite::asset('resources/assets/global/logo.png') }}" alt="{{ config('app.name') }}">
<img class="logo img-fluid" src="{{ Vite::asset('resources/assets/images/global/logo.png') }}" alt="{{ config('app.name') }}">
@endsection

View file

@ -4,9 +4,7 @@
@if (trim($slot) === 'Laravel')
<img class="logo" src="https://laravel.com/img/notification-logo.png" alt="Laravel Logo">
@else
{{-- FIXME: Personaliser ca --}}
<img class="logo img-fluid" src="{{ Vite::asset('resources/assets/global/logo.png') }}" alt="{{ config('app.name') }}">
{{ $slot }}
<img class="logo img-fluid" src="{{ Vite::asset('resources/assets/images/global/logo.png') }}" alt="{{ config('app.name') }}">
@endif
</a>
</td>

View file

@ -9,6 +9,8 @@ Route::middleware(['throttle:credentials'])
Route::get('/', [StaticPageController::class, 'homepage'])->name('homepage');
Route::view('/bs-demo', 'fo.pages.bs-demo')->name('bs-demo');
Route::get('/contact', [StaticPageController::class, 'contact'])->name('contact.show');
Route::post('/contact-form', [StaticPageController::class, 'contactForm'])->name('contact.store');
Route::get('/mentions-legales', [StaticPageController::class, 'legals'])->name('legals.show');
Route::get('/charte-de-confidentialite', [StaticPageController::class, 'policies'])->name('policies.show');
});