Added site.show page + some fixes on home and unveil pages

This commit is contained in:
Noah Lancien 2024-03-01 18:21:20 +01:00
parent b16eb6ceb7
commit b23524f292
14 changed files with 501 additions and 43 deletions

View file

@ -0,0 +1,33 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Enums\Slides\SliderTypeEnum;
use App\Enums\StaticPageType;
use App\Http\Controllers\Controller;
use App\Models\News;
use App\Models\Section;
use App\Models\Site;
use App\Models\Slide;
use App\Models\StaticPage;
use App\Models\Strip;
class SiteController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Contracts\View\View
*/
public function show(Site $site): \Illuminate\Contracts\View\View
{
$siteModel = $site;
if ($siteModel->order === Site::query()->max('order')) {
$nextSiteModel = Site::query()->orderBy('order')->first();
} else {
$nextSiteModel = Site::query()->orderBy('order')->where('order', '>', $siteModel->order)->first();
}
return view('fo.pages.site', compact('siteModel', 'nextSiteModel'));
}
}

View file

@ -33,19 +33,19 @@ class SiteSeeder extends Seeder
$site->picture_1 = FileStorageHelper::storeFile(
new Site(),
new \SplFileInfo(\resource_path(
'../database/factories/assets/sites/default-picture-100x100.png'
'../database/factories/assets/sites/default-picture-813x450.jpg'
))
);
$site->picture_2 = FileStorageHelper::storeFile(
new Site(),
new \SplFileInfo(\resource_path(
'../database/factories/assets/sites/default-picture-100x100.png'
'../database/factories/assets/sites/default-picture-813x450.jpg'
))
);
$site->picture_3 = FileStorageHelper::storeFile(
new Site(),
new \SplFileInfo(\resource_path(
'../database/factories/assets/sites/default-picture-100x100.png'
'../database/factories/assets/sites/default-picture-813x450.jpg'
))
);
$site->map_picture = FileStorageHelper::storeFile(

View file

@ -0,0 +1,3 @@
<svg width="44" height="45" viewBox="0 0 44 45" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.2" d="M17.6693 0.238281V21.9795C17.6693 27.9036 16.0814 32.7638 12.9055 36.5599C9.72966 40.4134 5.42782 42.9729 0 44.2383V34.5755C1.67454 34.0579 3.0315 33.4252 4.07087 32.6775C5.11024 31.9298 5.91864 31.0383 6.49606 30.003C7.07349 28.9677 7.44882 27.7598 7.62205 26.3795C7.79528 25.0566 7.88189 23.5899 7.88189 21.9795V19.65H0V0.238281H17.6693ZM44 0.238281V21.9795C44 27.9036 42.4121 32.7638 39.2362 36.5599C36.0604 40.4134 31.7585 42.9729 26.3307 44.2383V34.5755C28.0052 34.0579 29.3622 33.4252 30.4016 32.6775C31.4409 31.9298 32.2493 31.0383 32.8268 30.003C33.3465 28.9677 33.7218 27.7598 33.9528 26.3795C34.126 25.0566 34.2126 23.5899 34.2126 21.9795V19.65H26.3307V0.238281H44Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 818 B

View file

@ -1,3 +1,7 @@
@use "sass:color";
$color-secondary-light: color.adjust($color-secondary, $lightness: 46%);
.video-frame {
.video-thumbnail {
height: 100%;
@ -12,6 +16,24 @@
}
}
// Mask to add a transparent effect on the right side.
.mask-white {
mask: linear-gradient(-90deg, #fff6, #fff 30%);
}
// Rotate arrow svg (base is to the right)
.arrow-left {
transform: rotate(180deg);
}
.arrow-bottom {
transform: rotate(90deg);
}
.arrow-top {
transform: rotate(-90deg);
}
// * Top curve for primary sections
.border-top-curved-primary {
padding-top: 6rem;
@ -22,7 +44,7 @@
border-image-width: 6rem 0 0 0;
}
// * Top curve for white sections
// * Top curve for white sections when the upper section is primary
.border-top-curved-white {
padding-top: 6rem;
border-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 390 99' fill='#{urlencodecolor($color-primary)}'%3E%3Crect width='100%25' height='100%25'/%3E%3Cpath d='M0.5 98.5L0.499996 0.678555C124.136 38.9283 266.621 33.2224 389.5 0.650116L389.5 98.5L0.5 298.5Z' fill='#{urlencodecolor($color-white)}' stroke='#{urlencodecolor($color-white)}'/%3E%3C/svg%3E"); border-image-outset: 1;
@ -31,7 +53,26 @@
border-image-width: 6rem 0 0 0;
}
// Map on all colors
// * Top curve for secondary-light sections
.border-top-curved-secondary-light {
padding-top: 6rem;
border-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 390 99' fill='none'%3E%3Cpath d='M0.5 98.5L0.499996 0.678555C124.136 38.9283 266.621 33.2224 389.5 0.650116L389.5 98.5L0.5 298.5Z' fill='#{urlencodecolor($color-secondary-light)}' stroke='#{urlencodecolor($color-secondary-light)}'/%3E%3C/svg%3E");
border-image-outset: 1;
border-image-repeat: inherit;
border-image-slice: 85 0;
border-image-width: 6rem 0 0 0;
}
// * Top curve for white sections when the upper section is secondary-light
.border-top-curved-white-upper-secondary-light {
padding-top: 6rem;
border-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 390 99' fill='#{urlencodecolor($color-secondary-light)}'%3E%3Crect width='100%25' height='100%25'/%3E%3Cpath d='M0.5 98.5L0.499996 0.678555C124.136 38.9283 266.621 33.2224 389.5 0.650116L389.5 98.5L0.5 298.5Z' fill='#{urlencodecolor($color-white)}' stroke='#{urlencodecolor($color-white)}'/%3E%3C/svg%3E"); border-image-outset: 1;
border-image-repeat: inherit;
border-image-slice: 85 0;
border-image-width: 6rem 0 0 0;
}
// Map on all colors
@each $idx, $color in $theme-colors {
// Tilde svg
.tilde-#{$idx} {
@ -71,7 +112,7 @@
border-radius: 5rem;
background-color: $color;
/** stylelint-disable-next-line */
/* stylelint-disable-next-line declaration-property-value-no-unknown */
clip-path: xywh(0 -5% 65% 75% round 15% 0);
color: #fff;
font-size: 2.5rem;
@ -79,6 +120,12 @@
}
}
// * BACKGROUNDS
// Background color which are not exactly the defined colors in _variables.scss
.bg-secondary-light {
background-color: $color-secondary-light;
}
// LLLLLLLLLLL GGGGGGGGGGGGG
// L:::::::::L GGG::::::::::::G
@ -106,5 +153,15 @@
.border-top-curved-white {
border-image-slice: 100% 0;
}
// * Top curve for secondary-light sections
.border-top-curved-secondary-light {
border-image-slice: 100% 0;
}
// * Top curve for white sections when the upper section is secondary-light
.border-top-curved-white-upper-secondary-light {
border-image-slice: 100% 0;
}
}

View file

@ -19,4 +19,5 @@
// * PAGES
@import "./pages/homepage";
@import "./pages/unveil";
@import "./pages/site";

View file

@ -168,37 +168,6 @@
}
}
.life {
.mask-white {
mask: linear-gradient(-90deg, #fff6, #fff 30%);
}
.swiper-button-prev-custom {
top: 50%;
left: 1rem;
transform: rotate(180deg);
}
.swiper-button-next-custom {
top: calc(50% + 19px);
right: 1rem;
}
.swiper-button-prev-custom, .swiper-button-next-custom {
position: absolute;
z-index: 20;
padding: 0.5rem;
border: 1px solid;
border-color: $primary;
border-radius: 2rem;
background-color: $color-white;
}
.swiper-button-disabled {
display: none;
}
}
.swiper-news {
clip-path: inset(-2rem -50vw 0 0);
}

View file

@ -0,0 +1,20 @@
.site {
.words {
.swiper-sites-words {
.quote {
right: 2rem;
}
}
}
.brands {
.swiper-sites-brands {
clip-path: inset(-2rem -50vw 0 0);
.separator {
border-right: 1px solid;
border-right-color: #ddd;
}
}
}
}

View file

@ -1 +1,27 @@
@import '~/swiper/swiper.min.css'
@import '~/swiper/swiper.min.css';
// * CUSTOM STYLE
.swiper-button-prev-custom {
top: 50%;
left: 1rem;
transform: rotate(180deg);
}
.swiper-button-next-custom {
top: calc(50% + 19px);
right: 1rem;
}
.swiper-button-prev-custom, .swiper-button-next-custom {
position: absolute;
z-index: 20;
padding: 0.5rem;
border: 1px solid;
border-color: $primary;
border-radius: 2rem;
background-color: $color-white;
}
.swiper-button-disabled {
display: none !important;
}

View file

@ -69,8 +69,8 @@ document.addEventListener("DOMContentLoaded", function () {
grabCursor: true,
slidesPerView: 1.1,
navigation: {
nextEl: ".swiper-button-next-custom",
prevEl: ".swiper-button-prev-custom",
nextEl: ".swiper-news-button-next",
prevEl: ".swiper-news-button-prev",
},
keyboard: {
enabled: true,
@ -110,6 +110,67 @@ document.addEventListener("DOMContentLoaded", function () {
},
},
},
"swiper-sites-pictures": {
modules: [Navigation, Keyboard],
grabCursor: true,
slidesPerView: 1.1,
spaceBetween: 20,
navigation: {
nextEl: ".swiper-sites-pictures-button-next",
prevEl: ".swiper-sites-pictures-button-prev",
},
keyboard: {
enabled: true,
},
breakpoints: {
576: {
slidesPerView: 1.1,
},
768: {
slidesPerView: 1.5,
},
1200: {
slidesPerView: 1.3,
},
},
},
"swiper-sites-words": {
modules: [Navigation, Keyboard],
grabCursor: true,
slidesPerView: 1.1,
navigation: {
nextEl: ".swiper-sites-words-button-next",
prevEl: ".swiper-sites-words-button-prev",
},
keyboard: {
enabled: true,
},
breakpoints: {
992: {
slidesPerView: 2,
},
},
},
"swiper-sites-brands": {
modules: [Navigation, Keyboard],
grabCursor: true,
slidesPerView: "auto",
keyboard: {
enabled: true,
},
breakpoints: {
576: {
slidesPerView: 1.1,
spaceBetween: 0,
},
768: {
slidesPerView: 1.5,
},
1200: {
slidesPerView: 1.3,
},
},
},
};
type SwiperSlider = {

View file

@ -6,8 +6,12 @@ import {
faCircleInfo,
faCirclePlay,
faCircleXmark,
faLocationArrow,
faLocationDot,
faMagnifyingGlass,
faQuestionCircle
faPaw,
faQuestionCircle,
faUserGroup
} from "@fortawesome/free-solid-svg-icons";
config.autoReplaceSvg = false;
@ -19,5 +23,9 @@ library.add(faCirclePlay);
library.add(faQuestionCircle);
library.add(faMagnifyingGlass);
library.add(faArrowRightLong);
library.add(faLocationDot);
library.add(faLocationArrow);
library.add(faUserGroup);
library.add(faPaw);
dom.watch();

View file

@ -262,7 +262,7 @@
@endforeach
</div>
</div>
<div class="swiper-button swiper-button-prev-custom d-none d-lg-block mx-3">
<div class="swiper-button swiper-news-button-prev swiper-button-prev-custom d-none d-lg-block mx-3">
<img src="{{ Vite::asset('resources/assets/images/misc/blue-arrow.svg') }}"
title="{{ __('Navigation du slider (gauche)') }}" alt="{{ __('flèche vers la gauche') }}"
role="button" tabindex="0">
@ -279,7 +279,7 @@
</div>
</div>
</div>
<div class="swiper-button swiper-button-next-custom d-none d-lg-block mx-3">
<div class="swiper-button swiper-news-button-next swiper-button-next-custom d-none d-lg-block mx-3">
<img src="{{ Vite::asset('resources/assets/images/misc/blue-arrow.svg') }}"
title="{{ __('Navigation du slider (droite)') }}" alt="{{ __('flèche vers la droite') }}" role="button"
tabindex="0">

View file

@ -0,0 +1,254 @@
@extends('fo.layout')
@section('title', $siteModel->seo_title)
@section('description', $siteModel->seo_description)
@section('breadcrumb', request()->route()->getName())
@section('content')
<section class="site">
<section class="header">
<div class="container">
<div class="row justify-content-center">
<div class="col-4 col-lg-3">
<img class="w-100" src="{{ asset($siteModel->logo) }}" title="{{ $siteModel->logo_title }}"
alt="{{ $siteModel->logo_alt }}">
</div>
</div>
<div class="row justify-content-between">
<div class="col">
<a href="#CHANGEME" title="{{ __('Revenir à la page #CHANGEME') }}">
<img class="arrow-left" src="{{ Vite::asset('resources/assets/images/misc/blue-arrow.svg') }}">
{{ __('Retour') }}
</a>
</div>
<div class="col d-flex justify-content-end">
@if ($nextSiteModel)
<a href="{{ route('fo.sites.show', $nextSiteModel) }}"
title="{{ __('Voir le site suivant : :name', ['name' => $nextSiteModel->name]) }}">
{{ __('Site suivant') }}
<img class="arrow-right" src="{{ Vite::asset('resources/assets/images/misc/blue-arrow.svg') }}">
</a>
@endif
</div>
</div>
</div>
</section>
<section class="pictures position-relative mt-3">
<div class="pictures-content mask-white overflow-hidden">
<div class="container">
<div class="row align-items-center">
<div class="col-12 position-relative">
{{-- Swiper pictures --}}
<div class="swiper swiper-sites-pictures h-fit overflow-visible" id="swiper-sites-pictures">
<div class="swiper-wrapper h-fit">
<div class="swiper-slide h-auto">
<img class="w-100 rounded-3" src="{{ asset($siteModel->picture_1) }}"
title="{{ $siteModel->picture_1_title }}" alt="{{ $siteModel->picture_1_alt }}">
</div>
<div class="swiper-slide h-auto">
<img class="w-100 rounded-3" src="{{ asset($siteModel->picture_2) }}"
title="{{ $siteModel->picture_2_title }}" alt="{{ $siteModel->picture_2_alt }}">
</div>
<div class="swiper-slide h-auto">
<img class="w-100 rounded-3" src="{{ asset($siteModel->picture_3) }}"
title="{{ $siteModel->picture_3_title }}" alt="{{ $siteModel->picture_3_alt }}">
</div>
</div>
</div>
<div class="swiper-button swiper-sites-pictures-button-prev swiper-button-prev-custom d-none d-lg-block mx-3">
<img src="{{ Vite::asset('resources/assets/images/misc/blue-arrow.svg') }}"
title="{{ __('Navigation du slider (gauche)') }}" alt="{{ __('flèche vers la gauche') }}" role="button"
tabindex="0">
</div>
</div>
</div>
</div>
</div>
<div class="swiper-button swiper-sites-pictures-button-next swiper-button-next-custom d-none d-lg-block mx-3">
<img src="{{ Vite::asset('resources/assets/images/misc/blue-arrow.svg') }}" title="{{ __('Navigation du slider (droite)') }}"
alt="{{ __('flèche vers la droite') }}" role="button" tabindex="0">
</div>
</section>
<section class="infos mt-5 text-center">
<div class="container">
<div class="row">
<div class="col-12">
<h1 class="h1 text-primary fw-bold">{{ $siteModel->name }}</h1>
<p class="text-primary mt-4">{{ $siteModel->description }}</p>
<p class="text-tertiary mt-4">{{ $siteModel->description_2 }}</p>
</div>
</div>
<div class="row align-items-center mt-4">
<div class="col-6 d-flex justify-content-lg-end">
{{-- TODO: Formatter après avoir monté la version de Laravel, après avoir débloqué les accès de Nicolas sur git --}}
<p class="text-primary"><i class="fa-solid fa-user-group"></i> {{ $siteModel->employees }} {{ __('personnes') }}</p>
</div>
<div class="col-6 d-flex justify-content-lg-start">
{{-- FIXME: fournir pictos pour SpeciesEnum. --}}
<i class="fa-solid fa-paw"></i>
<p class="text-primary d-flex align-items-center ms-1">
@php
$pictoLabels = [];
foreach ($siteModel->pictos as $picto) {
$pictoLabels[] = \App\Enums\Sites\SpeciesEnum::make($picto)->label();
}
@endphp
{{ implode(', ', $pictoLabels) }}
</p>
</div>
</div>
</div>
</section>
<section class="location mt-3">
<div class="container">
<div class="row align-items-center">
<div class="col-12 col-lg-6">
<img class="w-100 rounded-4 mb-lg-0 mb-5" src="{{ asset($siteModel->map_picture) }}"
title="{{ $siteModel->map_picture_title }}" alt="{{ $siteModel->map_picture_alt }}">
</div>
<div class="col-12 col-lg-6">
<div class="card bg-secondary-light rounded-4 p-lg-5 border-0">
<div class="card-body text-tertiary">
<div class="row align-items-center mb-3">
<div class="col-2 d-flex justify-content-center pe-0">
<i class="fa-solid fa-2xl fa-location-dot"></i>
</div>
<div class="col-10">
<h5 class="card-title fw-bold">{{ $siteModel->name }}</h5>
{{-- FIXME: EVO BO : Eventuellement splitter en BO l'adresse et le postcode --}}
<p class="card-text fw-semibold mb-0">{{ $siteModel->address }}</p>
<p class="card-text fw-semibold mb-0">{{ __('Téléphone') }} : {{ $siteModel->phone_number }}</p>
</div>
</div>
<hr class="divider d-none d-lg-block bg-primary w-25 mx-auto border-2 opacity-100">
<div class="row align-items-center">
<div class="col-2 d-flex justify-content-center pe-0">
<i class="fa-solid fa-2xl fa-location-arrow"></i>
</div>
<div class="col-10">
<h5 class="card-title fw-bold">{{ __('Accès') }} :</h5>
@foreach ($siteModel->access_line as $access_line)
<p class="card-text fw-semibold mb-0">{{ $access_line }}</p>
@endforeach
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<section class="words mt-5">
<div class="container">
<div class="row">
<div class="col-12">
<h2 class="h2 fw-bold text-primary text-lg-start text-center">{{ __('Le mot de...') }}</h2>
</div>
</div>
</div>
{{-- Swiper words --}}
<div class="words-content mask-white overflow-hidden">
<div class="container">
<div class="row align-items-center">
<div class="col-12 position-relative">
{{-- FIXME: A l'origine, ces parties ne contiennent que le nom, la fonction et un texte. Une image est demandée en plus dans la créa. --}}
<div class="swiper swiper-sites-words mt-lg-0 mt-5 h-fit overflow-visible" id="swiper-sites-words">
<div class="swiper-wrapper h-fit">
<div class="swiper-slide h-auto">
@include('fo.partials.testimonials.card', [
'job' => __('Directeur du site'),
'name' => $siteModel->director_name,
'speach' => $siteModel->director_speach,
'bgClass' => 'bg-primary',
// 'jobClass' => 'text-tertiary', La demande du client, mais niveau accessibilité c'est pas top du tout...
'jobClass' => 'text-white',
'nameClass' => 'text-white',
'speachClass' => 'text-white',
])
</div>
<div class="swiper-slide h-auto">
@include('fo.partials.testimonials.card', [
'job' => $siteModel->employee_function,
'name' => $siteModel->employee_name,
'speach' => $siteModel->employee_speach,
'bgClass' => 'bg-tertiary',
// 'jobClass' => 'text-primary', La demande du client, mais niveau accessibilité c'est pas top du tout...
'jobClass' => 'text-white',
'nameClass' => 'text-white',
'speachClass' => 'text-white',
])
</div>
</div>
</div>
<div
class="swiper-button swiper-button-prev-custom swiper-sites-words-button-prev swiper-button-prev-custom d-none d-lg-block mx-3">
<img src="{{ Vite::asset('resources/assets/images/misc/blue-arrow.svg') }}"
title="{{ __('Navigation du slider (gauche)') }}" alt="{{ __('flèche vers la gauche') }}"
role="button" tabindex="0">
</div>
</div>
</div>
</div>
</div>
<div
class="swiper-button swiper-button-next-custom swiper-sites-words-button-next swiper-button-next-custom d-none d-lg-block mx-3">
<img src="{{ Vite::asset('resources/assets/images/misc/blue-arrow.svg') }}"
title="{{ __('Navigation du slider (droite)') }}" alt="{{ __('flèche vers la droite') }}" role="button"
tabindex="0">
</div>
</section>
<section class="highlights border-top-curved-secondary-light mt-5">
<div class="bg-secondary-light">
<div class="container py-4">
<div class="row justify-content-center">
<div class="col-8">
<h2 class="h2 fw-bold text-primary text-center">{{ __('Nos points forts et nos engagements') }}</h2>
</div>
</div>
<div class="row">
<div class="col-12">
{{-- FIXME: Ici était prévu à la base un WYSIWYG (cf doc. Périmètre du site - Fonctionnalités). La créa ne correspond pas et prévoit un slider de différentes icones. --}}
<div class="ck-content text-primary">{!! clean($siteModel->highlights) !!}</div>
</div>
</div>
</div>
</div>
</section>
@if ($siteModel->logos)
<section class="brands border-top-curved-white-upper-secondary-light overflow-hidden">
<div class="container my-5">
<div class="row align-items-center">
<div class="col-8 col-lg-3 offset-2 offset-lg-0">
<p class="text-primary text-lg-start fw-bold fs-3 text-center">{{ __('Les marques fabriquées ici') }}</p>
</div>
<div class="col-12 col-lg-6">
{{-- FIXME: en mobile, les logos défilent ? --}}
{{-- Swiper brands --}}
<div class="swiper swiper-sites-brands h-fit overflow-visible" id="swiper-sites-brands">
<div class="swiper-wrapper h-fit">
@foreach ($siteModel->logos as $logo)
<div class="swiper-slide h-auto">
<div class="d-flex">
<div class="row align-items-center flex-grow-1">
<div class="col-6 col-lg-12">
<img class="w-100" src="{{ asset($logo->picture) }}"
title="{{ $logo->picture_title }}" alt="{{ $logo->picture_alt }}">
</div>
<div class="col-6 d-lg-none">
<p class="w-100 text-center">{{ $logo->label }}</p>
</div>
</div>
<div class="separator @if ($loop->last) border-0 @endif mx-3"></div>
</div>
</div>
@endforeach
</div>
</div>
</div>
</div>
</div>
</section>
@endif
</section>
@endsection

View file

@ -0,0 +1,24 @@
<div @class([
'card testimonial-card rounded-4 h-100 position-relative me-3 p-3',
$bgClass => !empty($bgClass),
])>
<img class="quote position-absolute" src="{{ Vite::asset('resources/assets/images/misc/quotes.svg') }}">
<div class="card-body">
<div class="d-flex">
@if (!empty($picture))
<img src="{{ asset($picture['path']) }}" title="{{ $picture['title'] ?? '' }}" alt="{{ $picture['alt'] ?? '' }}">
@endif
<div class="person-infos">
<p @class([
'text-uppercase fw-bold small mb-1',
$jobClass => !empty($jobClass),
])>{{ $job }}</p>
<p @class([$nameClass => !empty($nameClass)])>{{ $name }}</p>
</div>
</div>
<div @class(['card-text', $speachClass => !empty($speachClass)])>
{{-- FIXME: Mettre une limite de caractères pour éviter d'avoir une carte immense par rapport à l'autre et d'avoir un rendu horrible --}}
{!! clean($speach) !!}
</div>
</div>
</div>

View file

@ -1,5 +1,6 @@
<?php
use App\Http\Controllers\Fo\SiteController;
use App\Http\Controllers\Fo\HomeController;
use App\Http\Controllers\Fo\StaticPageController;
use Illuminate\Support\Facades\Route;
@ -12,6 +13,7 @@ Route::middleware(['throttle:credentials'])->name('fo.')->group(function () {
Route::get('/', [HomeController::class, 'index'])->name('homepage');
Route::get('/on-se-devoile', [StaticPageController::class, 'unveil'])->name('unveil');
Route::get('/sites/{site:slug}', [SiteController::class, 'show'])->name('sites.show');
// * Iframe cookies
Route::get('/frame/{frame}', function (string $frame) {