Laravel

Un framework qui rend heureux

Voir cette catégorie
Vers le bas
Voir cette série
Shop : la page d'accueil
Dimanche 12 janvier 2025 14:13

Nous allons à présent nous concentrer sur la mise en place du frontend en commençant par la page d'accueil. Nous allons utiliser MaryUI et Volt, comme je l'ai déjà fait précédemment pour le projet CMS. Il faudra afficher les produits sur cette page, mais aussi les barres de navigations nécessaires en prévoyant la possibilité de naviguer sur un écran étroit.

Vous pouvez trouver le code dans ce dépôt Github.

Installation de MaryUI avec Livewire et Volt

Nous allons commencer par installer MaryUI :

composer require robsontenorio/mary
php artisan mary:install

Là, vous allez tomber sur deux questions :

Also install `livewire/volt` ?

Vous répondez oui, donc la valeur 0.

La seconde question concerne l'outil à utiliser, choisissez npm. Il va s'installer de nombreuses dépendances, en particulier DaisyUI et Tailwind. Il ne reste plus qu'à générer avec Vite! :

npm run dev

Votre page d'accueil devrait maintenant avoir cet aspect :
Vous avez à présent quelque chose de fonctionnel qui vous permet de comprendre comme fonction MaryUI, il y a un tuto complet ici. Libre à vous d'explorer tout ça, mais on va l'ignorer pour la suite de notre projet.

Avec Volt, on utilise des composants et avec l'installation initiale de maryUI on a eu la création automatique d'un composant :

Si vous allez voir le code, vous trouverez deux parties :

  • une classe PHP pour la gestion
  • une partie HTML pour la vue

C'est le principe de base de Volt qui concentre dans le même fichier ce qui est habituellement séparé avec Livewire.

En ce qui concerne le layout, il se trouve par défaut ici :

On dispose par défaut d'une page avec une barre de navigation rétractable sur la gauche. On va devoir modifier tout ça pour notre boutique.

Au niveau de la route qui a été créée pour afficher le composant, ça se passe évidemment dans route/web.php :

use Livewire\Volt\Volt;

Volt::route('/', 'index')->name('home');

On utilise la façade Volt pour créer les routes qui pointent sur un composant de Volt. Remarquez la syntaxe avec le point pour désigner l'emplacement du composant.

On va faire un peu de ménage en supprimant le dossier users et son contenu, ainsi que la vue welcome :

Évidemment à présent plus rien ne fonctionne...

Les langues

On veut une boutique multilingue, vous devez donc installer un package de langue :

composer require --dev laravel-lang/common

Dans le fichier .env, on précise qu'on veut le site en français (j'en profite pour ajouter d'autres précisions, adaptez selon votre cas) :

APP_NAME=Shop

APP_URL=http://shop.oo

APP_LOCALE=fr

On génère les fichiers :

php artisan lang:update

Le composant pour la page d'accueil

La page d'accueil va être gérée par un composant Volt, commençons par le créer :

php artisan make:volt index --class 

On change aussi la route pour pointer ce composant :

Volt::route('/', 'index');

Vous obtenez quelque chose comme ça :

Le composant prend par défaut le layout components/layouts/app.blade.php qui a été créé par MaryUI. On va devoir le modifier pour qu'il corresponde à nos besoins.

La barre de navigation

On crée un composant pour la barre de navigation :

php artisan make:volt navigation/navbar --class

On ajoute ce code :

<?php

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\{Auth, Session};
use Livewire\Volt\Component;
use Livewire\Attributes\On;
use Darryldecode\Cart\CartCollection;
use Mary\Traits\Toast;

new class extends Component {
    use Toast;

	public function logout(): void
	{
		Auth::guard('web')->logout();
		Session::invalidate();
		Session::regenerateToken();
		$this->redirect('/');
	}
};
?>

<x-nav sticky full-width>

    <x-slot:brand>
        <label for="main-drawer" class="mr-3 lg:hidden">
            <x-icon name="o-bars-3" class="cursor-pointer" />
        </label>

        <a href="/" wire:navigate>
            <x-app-brand />
        </a>
    </x-slot:brand>

    <x-slot:actions>
        <span class="hidden lg:block">
            @if ($user = auth()->user())
                <x-dropdown>
                    <x-slot:trigger>
                        <x-button label="{{ $user->name }} {{ $user->firstname }}" class="btn-ghost" />
                    </x-slot:trigger>
                    <span class="text-black">
                        <x-menu-item title="{{ __('My profile') }}" link="" />
                        <x-menu-item title="{{ __('My addresses') }}" link="" />
                        <x-menu-item title="{{ __('My orders') }}" link="" />
                        <x-menu-item title="{{ __('RGPD') }}" link="" />
                        <x-menu-item title="{{ __('Logout') }}" wire:click="logout" />  
                    </span>                  
                </x-dropdown>
            @else
                <x-button label="{{ __('Login') }}" link="/login" class="btn-ghost" />
            @endif
        </span>
    </x-slot:actions>
</x-nav>

On ajoute les traductions dans le fichier fr.json :

"My profile": "Mon profil",
"My addresses": "Mes adresses",
"My orders": "Mes commandes",
"RGPD": "RGPD",

Dans le layout, vous retirez ce code qui concerne la navigation :

{{-- NAVBAR mobile only --}}
<x-nav sticky class="lg:hidden">
    <x-slot:brand>
        <x-app-brand />
    </x-slot:brand>
    <x-slot:actions>
        <label for="main-drawer" class="lg:hidden me-3">
            <x-icon name="o-bars-3" class="cursor-pointer" />
        </label>
    </x-slot:actions>
</x-nav>

Et vous insérez notre nouveau composant à la place :

{{-- NAVBAR --}}
<livewire:navigation.navbar />

Lors de son installation, MaryUI a créé aussi ce composant :

Il est destiné à afficher le logo de la boutique dans la partie gauche de la barre de navigation. On va adapter cette partie du code :

public function render(): View|Closure|string
{
    return <<<'HTML'
            <a href="/" wire:navigate>
                <!-- Hidden when collapsed -->
                <div {{ $attributes->class(["hidden-when-collapsed"]) }}>
                    <div class="">
                        <img src="{{ asset('storage/photos/logo1.png') }}" alt="" class="h-auto w-26 sm:w-32 md:w-40 lg:w-48 xl:w-56">
                    </div>
                </div>

                <!-- Display when collapsed -->
                <div class="display-when-collapsed hidden mx-5 mt-4 lg:mb-6 h-[28px]">
                    <x-icon name="s-square-3-stack-3d" class="-mb-1 w-6 text-purple-500" />
                </div>
            </a>
        HTML;
}

L'image logo1.png est sur le dépôt Github. À présent, on a notre barre de navigation avec le logo à gauche et le menu à droite :

La barre latérale

Sur petits écrans, on doit gérer la barre de navigation latérale. On crée aussi un composant pour celle-ci :

php artisan make:volt navigation/sidebar --class

On va évidemment trouver un code très proche de la barre qu'on a vue ci-dessus :

<?php

use Illuminate\Support\Facades\{Auth, Session};
use Livewire\Volt\Component;

new class() extends Component {

	public function logout(): void
	{
		Auth::guard('web')->logout();
		Session::invalidate();
		Session::regenerateToken();
		$this->redirect('/');
	}
};
?>

<div>
    <x-menu activate-by-route>
        {{-- Utilisateur --}}
        @if($user = auth()->user())
            <x-menu-separator />
                <x-list-item :item="$user" value="name" sub-value="email" no-separator no-hover class="-mx-2 !-my-2 rounded">
                    <x-slot:actions>
                        <x-button icon="o-power" wire:click="logout" class="btn-circle btn-ghost btn-xs" tooltip-left="{{ __('Logout') }}" no-wire-navigate />
                    </x-slot:actions>
                </x-list-item>
                <x-menu-item title="{{ __('My profile') }}" icon="o-user" link="" />
				<x-menu-item title="{{ __('My addresses') }}" icon="o-map-pin" link="" />
				<x-menu-item title="{{ __('My orders') }}" icon="o-shopping-cart" link="" />
				<x-menu-item title="{{ __('RGPD') }}" icon="o-lock-closed" link="" />
            <x-menu-separator />
        @else
            <x-menu-item title="{{ __('Login') }}" link="/login" />
        @endif

    </x-menu>
</div>

Dans le layout, remplacez tout le x-main par celui-ci :

{{-- MAIN --}}
<x-main full-width>

    {{-- SIDEBAR --}}
    <x-slot:sidebar drawer="main-drawer" collapsible class="bg-base-100 lg:bg-inherit lg:hidden">
        <livewire:navigation.sidebar />
    </x-slot:sidebar>

    {{-- SLOT --}}
    <x-slot:content>
        {{ $slot }}
    </x-slot:content>

</x-main>

La barre latérale devrait à présent fonctionner.

La page d'accueil

Pour le moment, notre composant index pour la page d'accueil est vide. On veut y présenter les produits de la boutique. Mais dans un premier temps, comme on va avoir besoin des données de base de la boutique dans pratiquement toutes les vues, on va partager ces données dans AppServiceProvider :

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\View;
use App\Models\Shop;

class AppServiceProvider extends ServiceProvider
{

    ...

    public function boot(): void
    {
        if(app()->runningInConsole()) {
            return;
        }
        
        View::share('shop', Shop::firstOrFail());
    }
}

Au passage, on prend une précaution pour éviter une erreur si on régénère la base de données.

Voilà le code du composant index :

<?php

use Livewire\Volt\Component;
use App\Models\Product;

new class extends Component 
{
    public function with(): array
    {
        return [
            'products' => Product::whereActive(true)->get(),
        ];
    }
    
}; ?>

<div class="container mx-auto">
    <x-card class="w-full shadow-md shadow-gray-500" shadow separator >
        {!! $shop->home !!}
    </x-card>
    <br>
    <div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
        @foreach ($products as $product)
            <x-card
                class="shadow-md transition duration-500 ease-in-out shadow-gray-500 hover:shadow-xl hover:shadow-gray-500"
                title="{{ number_format($product->price, 2, ',', ' ') . ' € TTC' }}" >
                {!! $product->name !!} 
                @unless($product->quantity)
                    <br><span class="text-red-500">@lang('Product out of stock')</span>
                @endunless
                @if ($product->image)
                    <x-slot:figure>
                        @if($product->quantity)
                            <a href="">
                        @endif
                            <img src="{{ asset('storage/photos/' . $product->image) }}" alt="{!! $product->name !!}" />
                        @if($product->quantity) </a> @endif
                    </x-slot:figure>
                @endif
            </x-card>
        @endforeach
    </div>
    <br>
    <x-card class="w-full shadow-md shadow-gray-500" shadow separator >
        <x-accordion wire:model="group" class="shadow-md shadow-gray-500">
            <x-collapse name="group1">
                <x-slot:heading>{{ __('General informations') }}</x-slot:heading>
                <x-slot:content>{!! $shop->home_infos !!}</x-slot:content>
            </x-collapse>
            <x-collapse name="group2">
                <x-slot:heading>{{ __('Shipping charges') }}</x-slot:heading>
                <x-slot:content>{!! $shop->home_shipping !!}</x-slot:content>
            </x-collapse>
        </x-accordion>
    </x-card>
</div>

Je n'ai pas prévu de pagination en partant du principe qu'on a une petite boutique avec peu de produits. Mais ça serait facile à ajouter.

On ajoute quelques traductions

"General informations": "Informations générales",
"Shipping charges": "Frais de port",

On obtient l'affichage :

Je ne montre pas l'aspect responsive, sur écran moyen, on a deux colonnes et une seule sur petit écran.

Le thème

On peut changer le thème de couleurs pour apporter une touche plus personnalisée. Par exemple, dans tailwind.config.js ajoutez ce code :

daisyui: {
    themes: ["light", "dark", "corporate"],
},

Et dans le layout app :

<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" data-theme="corporate" > 

Vous pouvez tester d'autres thèmes selon vos goûts. Pour accentuer la barre de navigation, on peut aussi la colorier :

<x-nav sticky full-width :class="'bg-cyan-700 text-white'">

Vous pouvez vous amuser avec tout ça...

La navigation du bas de page

Pour être complet, on va ajouter le bas de page avec sa navigation. On crée le composant :

php artisan make:volt navigation/footer --class

Avec ce code :

<?php

use App\Models\Footer;
use Livewire\Volt\Component;

new class() extends Component {

};
?>

<footer class="p-10 text-white bg-cyan-700 footer">
    <nav>
        <a href="" class="link link-hover">@lang('Shipping')</a>
        <a href="" class="link link-hover">@lang('Legal informations')</a>
        <a href="" class="link link-hover">@lang('Terms and conditions of sale')</a>
    </nav>
    <nav>
        <a href="" class="link link-hover">@lang('Privacy policy')
        <a href="" class="link link-hover">@lang('Environmental protection')</a>
        <a href="" class="link link-hover">@lang('Administrative mandate')</a>
    </nav>
    <nav>
      <h6 class="footer-title">@lang('Social medias')</h6>
      <div class="grid grid-flow-col gap-4">
        <a href="{{ $shop->facebook }}" target="_blank">
          <svg
            xmlns="http://www.w3.org/2000/svg"
            width="24"
            height="24"
            viewBox="0 0 24 24"
            class="fill-current">
            <path
              d="M9 8h-3v4h3v12h5v-12h3.642l.358-4h-4v-1.667c0-.955.192-1.333 1.115-1.333h2.885v-5h-3.808c-3.596 0-5.192 1.583-5.192 4.615v3.385z"></path>
          </svg>
        </a>
      </div>
    </nav>
</footer>

On ajoute le code dans le layout app pour charger le footer :

{{-- FOOTER --}}
<hr><br>
<livewire:navigation.footer />
<br>

Quelques traductions :

"Shipping": "Livraisons",
"Legal informations": "Mentions légales",
"Terms and conditions of sale": "Conditions générales de vente",
"Privacy policy": "Politique de confidentialité",
"Environmental protection": "Respect de l'environnement",
"Administrative mandate": "Mandat administratif",
"Social medias": "Médias sociaux",

Et on a notre bas de page :

Les pages

Maintenant, on va s'occuper des pages statiques. On crée un nouveau composant :

php artisan make:volt page --class

Avec ce simple code :

<?php

use Livewire\Volt\Component;
use App\Models\Page;

new class extends Component {

    public Page $page;
    
    public function mount(Page $page): void
    {
        $this->page = $page;
    }

}; ?>

<div class="container mx-auto">
    <x-card title="{!! $this->page->title !!}" class="w-full shadow-md shadow-gray-500" shadow separator >
        {!! $this->page->text !!}
    </x-card>
</div>

On ajoute la route :

Volt::route('/pages/{page:slug}', 'page')->name('pages');

Dans le footer, on renseigne les routes :

<nav>
    <a href="{{ route('pages', ['page' => 'livraisons']) }}" class="link link-hover">@lang('Shipping')</a>
    <a href="{{ route('pages', ['page' => 'mentions-legales']) }}" class="link link-hover">@lang('Legal informations')</a>
    <a href="{{ route('pages', ['page' => 'conditions-generales-de-vente']) }}" class="link link-hover">@lang('Terms and conditions of sale')</a>
</nav>
<nav>
    <a href="{{ route('pages', ['page' => 'politique-de-confidentialite']) }}" class="link link-hover">@lang('Privacy policy')
    <a href="{{ route('pages', ['page' => 'respect-environnement']) }}" class="link link-hover">@lang('Environmental protection')</a>
    <a href="{{ route('pages', ['page' => 'mandat-administratif']) }}" class="link link-hover">@lang('Administrative mandate')</a>
</nav>

Les liens du footer doivent désormais fonctionner.

Conclusion

Dans ce chapitre, on a mis en place notre page d'accueil avec la navigation. On dispose d'une vignette pour chaque produit avec son image, son prix et sa description. Pour le moment les liens ne fonctionnent pas.



    Par bestmomo

    Nombre de commentaires : 4

    Article précédent : Shop : les données 2/2
    Article suivant : Shop : l'authentification