Maintenant que la partie client est pratiquement terminée nous allons nous consacrer à l'administration du site. L'administrateur a pour tâche de modérer les annonces et certains messages, d'autre part il doit gérer les annonces obsolètes.
Pour vous simplifier la vie vous pouvez télécharger le dossier complet pour le code de cet article.sb-admin-2
Pour ce projet j'ai opté pour sb-admin-2 comme template : Il est basé sur Bootstrap 4 qu'on utilise déjà et je le trouve plutôt esthétique.Vous pouvez télécharger les fichiers à partir du site. Ensuite vous copiez le contenu du dossier scss dans un dossier sb-admin-2 de notre projet :
De la même manière on copie le fichier Javascript sb-admin-2.js dans le projet : Au début de ce ficheir on va ajouter l'appel à Popper, JQuery et Bootstrap :window.Popper = require('popper.js').default;
window.$ = window.jQuery = require('jquery');
require('bootstrap');
...
Enfin pour compiler tout ça on complète le fichier webpack.mix.js :
mix.js('resources/js/sb-admin-2.js', 'public/js')
.sass('resources/sass/sb-admin-2/sb-admin-2.scss', 'public/css');
Et on compile !
Nos assets sont maintenant prêts...
Routes et contrôleur
Pour l'administration on va créer un contrôleur :php artisan make:controller AdminController
On va déjà créer la méthode index et déclarer les deux repositories qu'on a déjà créés :
<?php
namespace App\Http\Controllers;
use Carbon\Carbon;
use Illuminate\Http\Request;
use App\Repositories\ { AdRepository, MessageRepository };
class AdminController extends Controller
{
protected $adRepository;
protected $messagerepository;
public function __construct(AdRepository $adRepository, Messagerepository $messagerepository)
{
$this->adRepository = $adRepository;
$this->messagerepository = $messagerepository;
}
public function index()
{
return view('admin.index');
}
}
On ajoute aussi une route en préparant un groupe pour l’administration :
Route::prefix('admin')->middleware('admin')->group(function () {
Route::get('/', 'AdminController@index')->name('admin.index');
});
On crée le middleware admin :
php artisan make:middleware Admin
Avec ce code :
public function handle($request, Closure $next)
{
$user = $request->user();
if ($user && $user->admin) {
return $next($request);
}
return redirect()->route('home');
}
Et on le déclare dans le Kernel :
protected $routeMiddleware = [
...
'admin' => \App\Http\Middleware\Admin::class,
];
On va se contenter de ça pour le moment.
L'accès à l'administration
On va prévoir un accès à l'administration à partir de la page d'accueil dans la barre de menu. Dans le provider AppServiceProvider on va ajouter une instruction pour Blade pour sélectionner les administrateurs. J'en profite pour ajouter la francisation de la ressource que j'ai oubliée dans un précédent article :use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Route;
...
public function boot()
{
Blade::if ('admin', function () {
return auth()->check() && auth()->user()->admin;
});
Route::resourceVerbs([
'create' => 'creer',
'edit' => 'modifier',
]);
}
Dans le layout app on ajoute le lien de l'administration en le conditionnant avec l'instruction qu'on vient de créer :
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<!-- Right Side Of Navbar -->
<ul class="navbar-nav ml-auto">
<!-- Authentication Links -->
@guest
<li class="nav-item">
<a class="nav-link" href="{{ route('login') }}">Connexion</a>
</li>
@else
@admin
<li class="nav-item">
<a class="nav-link" href="{{ route('admin.index') }}">Administration</a>
</li>
@endadmin
<li class="nav-item">
<a class="nav-link" href="{{ route('logout') }}"
onclick="event.preventDefault(); document.getElementById('logout-form').submit();">
Déconnexion
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
@csrf
</form>
</li>
@endguest
</ul>
</div>
Le layout de l'administration
On crée un layout (admin) pour les vues de l'administration accompagné de deux vues pour le haut de la page (back-head) et le base de la page (back-footer) :
Cette organisation est due au fait qu'on utilisera aussi les deux vues annexes pour le profil utilisateur.Vue back-head
Voici le code de l'entête :<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>Annonces</title>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css">
<link href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i" rel="stylesheet">
<link href="{{ asset('css/sb-admin-2.css') }}" rel="stylesheet">
</head>
Vue back-footer
Voici le code du bas de page :<script src="{{ asset('js/sb-admin-2.js') }}"></script>
<script>
$(function () {
$('[data-toggle="tooltip"]').tooltip();
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
})
</script>
@yield('script')
</body>
</html>
Vue admin
Et enfin le layout dont le code est extrait de celui de sb-admin-2 :@include('layouts.back-head')
<body id="page-top">
<!-- Page Wrapper -->
<div id="wrapper">
<!-- Sidebar -->
<ul class="navbar-nav bg-gradient-primary sidebar sidebar-dark accordion" id="accordionSidebar">
<!-- Sidebar - Brand -->
<a class="sidebar-brand d-flex align-items-center justify-content-center" href="{{ url('/') }}">
<div class="sidebar-brand-icon rotate-n-15">
<i class="fab fa-earlybirds fa-2x"></i>
</div>
<div class="sidebar-brand-text mx-3">Annonces</div>
</a>
<!-- Divider -->
<hr class="sidebar-divider my-0">
<!-- Nav Item - Dashboard -->
<li class="nav-item @if(request()->route()->getName() == 'admin.index') active @endif">
<a class="nav-link" href="{{ route('admin.index') }}">
<i class="fas fa-fw fa-tachometer-alt"></i>
<span>Panneau</span></a>
</li>
<!-- Divider -->
<hr class="sidebar-divider">
<!-- Heading -->
<div class="sidebar-heading">
Annonces
</div>
<li class="nav-item @if(request()->route()->getName() == 'admin.ads') active @endif">
<a class="nav-link" href="">
<i class="fas fa-fw fa-question"></i>
<span>A modérer</span></a>
</li>
<li class="nav-item @if(request()->route()->getName() == 'admin.obsoletes') active @endif">
<a class="nav-link" href="">
<i class="fas fa-fw fa-hourglass-end"></i>
<span>Obsolètes</span></a>
</li>
<!-- Divider -->
<hr class="sidebar-divider">
<!-- Heading -->
<div class="sidebar-heading">
Messages
</div>
<li class="nav-item @if(request()->route()->getName() == 'admin.messages') active @endif">
<a class="nav-link" href="">
<i class="fas fa-fw fa-question"></i>
<span>A modérer</span></a>
</li>
<!-- Divider -->
<hr class="sidebar-divider d-none d-md-block">
<!-- Sidebar Toggler (Sidebar) -->
<div class="text-center d-none d-md-inline">
<button class="rounded-circle border-0" id="sidebarToggle"></button>
</div>
</ul>
<!-- End of Sidebar -->
<!-- Content Wrapper -->
<div id="content-wrapper" class="d-flex flex-column">
<!-- Main Content -->
<div id="content">
<!-- Topbar -->
<nav class="navbar navbar-expand navbar-light bg-white topbar mb-4 static-top shadow">
<!-- Sidebar Toggle (Topbar) -->
<button id="sidebarToggleTop" class="btn btn-link d-md-none rounded-circle mr-3">
<i class="fa fa-bars"></i>
</button>
<!-- Topbar Navbar -->
<ul class="navbar-nav ml-auto">
<!-- Nav Item - User Information -->
<li class="nav-item dropdown no-arrow">
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="d-lg-inline text-gray-600 small">{{ auth()->user()->name }}</span>
</a>
<!-- Dropdown - User Information -->
<div class="dropdown-menu dropdown-menu-right shadow animated--grow-in" aria-labelledby="userDropdown">
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="{{ route('logout') }}"
onclick="event.preventDefault(); document.getElementById('logout-form').submit();">
<i class="fas fa-sign-out-alt fa-sm fa-fw mr-2 text-gray-400"></i>
Déconnexion
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
@csrf
</form>
</div>
</li>
</ul>
</nav>
<!-- End of Topbar -->
<!-- Begin Page Content -->
<div class="container-fluid">
@yield('content')
</div>
<!-- /.container-fluid -->
</div>
<!-- End of Main Content -->
<!-- Footer -->
<footer class="sticky-footer bg-white">
<div class="container my-auto">
<div class="copyright text-center my-auto">
<span>Copyright © Annonces 2019</span>
</div>
</div>
</footer>
<!-- End of Footer -->
</div>
<!-- End of Content Wrapper -->
</div>
<!-- End of Page Wrapper -->
@include('layouts.back-footer')
Vue index
Enfin on crée un dossier admin et la vue index qui est l'accueil de l'administration :@extends('layouts.admin')
@section('content')
<!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">Tableau de bord</h1>
</div>
<!-- Content Row -->
<div class="row">
<div class="col-xl-4 col-md-6 mb-4">
<div class="card border-left-primary shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">Annonces à modérer</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"></div>
</div>
<div class="col-auto">
<i class="fas fa-cog fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-4 col-md-6 mb-4">
<div class="card border-left-primary shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">Messages à modérer</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"></div>
</div>
<div class="col-auto">
<i class="fas fa-cog fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-4 col-md-6 mb-4">
<div class="card border-left-warning shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1">Annonces obsolètes</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"></div>
</div>
<div class="col-auto">
<i class="fas fa-hourglass-end fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
@endsection
L'accueil
Maintenant lorsqu'un administrateur de connecte il a le lien vers l'adminstration sur la page d'accueil du site :
Et si on clique on arrive enfin sur la page de l'administration : On a maintenant notre squelette en place, il ne reste plus qu'à coder tout ça ! On va commencer par renseigner les cartes avec le nombre d'éléments de chaque catégorie.AdRepository
Dans ce repository on va ajouter ces deux méthodes pour le comptage des annonces non actives et obsolètes :public function noActiveCount($ads = null)
{
if($ads) {
return $ads->where('active', false)->count();
}
return Ad::where('active', false)->count();
}
public function obsoleteCount($ads = null)
{
if($ads) {
return $ads->where('active', true)->where('limit', '<', Carbon::now())->count();
}
return Ad::where('limit', '<', Carbon::now())->count();
}
MessageRepository
De la même manière on crée cette méthode dans ce repository pour compter les message à modérer :public function count()
{
return Message::count();
}
AdminController
On met à jour la méthode index du contrôleur :public function index()
{
$adModerationCount = $this->adRepository->noActiveCount();
$adPerimesCount = $this->adRepository->obsoleteCount();
$messageModerationCount = $this->messagerepository->count();
return view('admin.index', compact('adModerationCount', 'messageModerationCount', 'adPerimesCount'));
}
Vue index
Il ne nous reste plus qu'à ajouter les variables dans la vue index :...
<div class="h5 mb-0 font-weight-bold text-gray-800">{{ $adModerationCount }}</div>
...
<div class="h5 mb-0 font-weight-bold text-gray-800">{{ $messageModerationCount }}</div>
...
<div class="h5 mb-0 font-weight-bold text-gray-800">{{ $adPerimesCount }}</div>
...
Maintenant on obtient le nombre pour chaque catégorie :
Annonces à modérer
Les routes
On doit ajouter des routes pour la modération des annonces :- accès à la liste
- approbation
- refus
Route::prefix('admin')->middleware('admin')->group(function () {
...
Route::prefix('annonces')->group(function () {
Route::get('/', 'AdminController@ads')->name('admin.ads');
Route::middleware('ajax')->group(function () {
Route::post('approve/{ad}', 'AdminController@approve')->name('admin.approve');
Route::post('refuse', 'AdminController@refuse')->name('admin.refuse');
});
});
});
On avait déjà le groupe admin, on y inclus un groupe annonces. On voit que l'approbation et le refus se feront en ajax, on utilise le middleware qu'on a déjà créé.
AdRespository
Dans le repository on ajoute une méthode pour récupérer les annonces non actives paginées classées par date :public function noActive($nbr)
{
return Ad::whereActive(false)->latest()->paginate($nbr);
}
AdminController
Dans le contrôleur on utilise le repository pour envoyer les données dans la vue :public function ads()
{
$adModeration = $this->adRepository->noActive(5);
return view('admin.ads', compact('adModeration'));
}
Vue admin.ads
On crée la vue ads : Avec ce code :@extends('layouts.admin')
@section('content')
@include('partials.message', ['url' => route('admin.refuse')])
@include('partials.alerts', ['title' => 'Annonces à modérer'])
<div class="table-responsive">
<table class="table table-hover">
<thead class="thead-light">
<tr>
<th scope="col">Titre</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
@foreach ($adModeration as $ad)
<tr id="{{ $ad->id }}">
<td>{{ $ad->title }}</td>
<td class="float-right">
<a class="btn btn-primary btn-sm" href="{{ route('annonces.show', $ad->id) }}" target="_blank" role="button" data-toggle="tooltip" title="Voir l'annonce"><i class="fas fa-eye"></i></a>
<a class="btn btn-success btn-sm" href="{{ route('admin.approve', $ad->id) }}" role="button" data-toggle="tooltip" title="Approuver l'annonce"><i class="fas fa-thumbs-up"></i></a>
<i class="fas fa-spinner fa-pulse fa-lg" style="display: none"></i>
<a class="btn btn-danger btn-sm" href="#" role="button" data-id="{{ $ad->id }}" data-toggle="tooltip" title="Refuser l'annonce"><i class="fas fa-thumbs-down"></i></a>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div class="d-flex">
<div class="mx-auto">
{{ $adModeration->links() }}
</div>
</div>
@endsection
@section('script')
@include('partials.script')
@endsection
On fait appel à quelques vues partielles. On a déjà celle pour les messages, on va créer les deux autres :
Vue partielle alerts
Cette vue est destinée à afficher les alertes :<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">{{ $title }}</h1>
</div>
@if(session()->has('status'))
<div class="alert alert-success alert-dismissible fade show" role="alert">
{{ session('status') }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
@endif
<div class="alert alert-warning alert-dismissible fade d-none" role="alert">
Le serveur est inaccessible, veuillez retenter dans quelques minutes.
<button type="button" class="close" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
Vue partielle script
Dans cette vue on a le Javascript pour gérer les requêtes HTML, les alertes :<script>
$(() => {
const toggleButtons = () => {
$('#icon').toggle();
$('#buttons').toggle();
}
$('.alert-success button').click(() => {
$('.alert-success').addClass('d-none').removeClass('show');
});
$('.alert-warning button').click(() => {
$('.alert-warning').addClass('d-none').removeClass('show');
});
$('.btn-success').click((e) => {
e.preventDefault();
let that = $(e.currentTarget);
that.hide();
that.closest('td').find('i.fa-spinner').show();
$.post(that.attr('href'))
.done((data) => {
document.location.reload(true);
})
.fail(() => {
$('.alert-warning').removeClass('d-none').addClass('show');
that.show();
that.closest('td').find('i.fa-spinner').hide();
});
});
$('.btn-danger').click((e) => {
e.preventDefault();
$('#id').val($(e.currentTarget).attr('data-id'));
$('#messageModal').modal();
});
$('#messageForm').submit((e) => {
let that = e.currentTarget;
e.preventDefault();
$('#message').removeClass('is-invalid');
$('.invalid-feedback').html('');
toggleButtons();
$.ajax({
method: $(that).attr('method'),
url: $(that).attr('action'),
data: $(that).serialize()
})
.done((data) => {
document.location.reload(true);
})
.fail((data) => {
toggleButtons();
if(data.status == 422) {
$.each(data.responseJSON.errors, function (i, error) {
$(document)
.find('[name="' + i + '"]')
.addClass('is-invalid')
.next()
.append(error[0]);
});
} else {
$('#messageModal').modal('hide');
$('.alert-warning').removeClass('d-none').addClass('show');
}
});
});
})
</script>
Layout admin
Dans notre layout admin il faut ajouter le lien pour accéder à la vue des annonces à modérer (on n'avait pas renseigné le href) :
<li class="nav-item @if(request()->route()->getName() == 'admin.ads') active @endif">
<a class="nav-link" href="{{ route('admin.ads') }}">
<i class="fas fa-fw fa-question"></i>
<span>A modérer</span></a>
</li>
Le lien devient alors actif et en cliquant on arrive sur le page des annonces à modérer :
Pour chaque annonce on dispose de 3 icônes de commande pour :
- voir l'annonce
- approuver l'annonce
- refuser l'annonce
Approuver une annonce
On va coder maintenant côté serveur pour l'approbation d'une annonce.AdRepository
Approuver une annonce signifie mettre true dans le champ active. On crée donc une méthode dans le repository pour le faire :public function approve($ad)
{
$ad->active = true;
$ad->save();
}
Notification AdApprove
On va prévoir aussi une notification pour le rédacteur pour le prévenir que son annonce a été approuvée :php artisan make:notification AdApprove
Avec ce code :
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use App\Models\Ad;
class AdApprove extends Notification
{
use Queueable;
protected $ad;
public function __construct(Ad $ad)
{
$this->ad = $ad;
}
public function via($notifiable)
{
return ['mail'];
}
public function toMail($notifiable)
{
return (new MailMessage)
->line('Nous avons approuvé une annonce que vous avez déposée :')
->action('Voir votre annonce', route('annonces.show', $this->ad->id))
->line("Merci d'utiliser notre site pour vos annonces !");
}
public function toArray($notifiable)
{
return [
//
];
}
}
AdminController
Il ne nous reste plus qu'à coder le contrôleur :use App\Models\Ad;
use App\Notifications\AdApprove;
...
public function approve(Request $request, Ad $ad)
{
$this->adRepository->approve($ad);
$request->session()->flash('status', "L'annonce a bien été approuvée et le rédacteur va être notifié.");
$ad->notify(new AdApprove($ad));
return response()->json(['id' => $ad->id]);
}
Fonctionnement
Voyons maintenant le fonctionnement pour l'approbation d'une annonce... Dans la vue ads pour le lien d'approbation on a ce code :<a class="btn btn-success btn-sm" href="{{ route('admin.approve', $ad->id) }}" role="button" data-toggle="tooltip" title="Approuver l'annonce"><i class="fas fa-thumbs-up"></i></a>
L'url générée est de la forme admin/annonces/approve/{annonce_id}. Comme on est en Ajax c'est le Javascript qui gère la requête :
$('.btn-success').click((e) => {
e.preventDefault();
let that = $(e.currentTarget);
that.hide();
that.closest('td').find('i.fa-spinner').show();
$.post(that.attr('href'))
.done((data) => {
document.location.reload(true);
})
.fail(() => {
$('.alert-warning').removeClass('d-none').addClass('show');
that.show();
that.closest('td').find('i.fa-spinner').hide();
});
});
Pendant la durée de la communication avec le serveur l'icône d'approbation est remplacée par une icône d'attente animée :
Dans le contrôleur on traite la requête avec ce code :public function approve(Request $request, Ad $ad)
{
$this->adRepository->approve($ad);
$request->session()->flash('status', "L'annonce a bien été approuvée et le rédacteur va être notifié.");
$ad->notify(new AdApprove($ad));
return response()->json(['id' => $ad->id]);
}
On approuve l'annonce, on prépare un message en flash session, on envoie la notification au rédacteur, on renvoie l'identifiant de l'annonce au client.
Si le serveur ne renvoie pas d'erreur la page est rechargée, ce qui actualise la liste des annonces, et l'alerte s'affiche : Avec ce message : Si le serveur retourne une erreur on affiche cette autre alerte :Refuser une annonce
On va coder maintenant côté serveur pour le refus d'une annonce.AdRepository
Refuser une annonce signifie la supprimer définitivement. On crée donc une méthode dans le repository pour le faire :public function delete($ad)
{
$ad->delete();
}
Notification AdRefuse
On va prévoir aussi une notification pour le rédacteur pour le prévenir que son annonce a été refusée :php artisan make:notification AdRefuse
Avec ce code :
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class AdRefuse extends Notification
{
use Queueable;
protected $message;
public function __construct($message)
{
$this->message = $message;
}
public function via($notifiable)
{
return ['mail'];
}
public function toMail($notifiable)
{
return (new MailMessage)
->line('Nous avons refusé une annonce que vous avez déposée pour la raison suivante :')
->line($this->message)
->line("Merci d'utiliser notre site pour vos annonces !");
}
public function toArray($notifiable)
{
return [
//
];
}
}
MessageRefuseRequest
Comme on va devoir envoyer un message à partir d'un formulaire on a besoin d'une Form Request :php artisan make:request MessageRefuse
Qu'on code ainsi :
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class MessageRefuse extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'message' => ['required', 'string', 'max:500'],
];
}
}
AdminController
Il ne nous reste plus qu'à coder le contrôleur :use App\Notifications\ { AdApprove, AdRefuse };
use App\Http\Requests\MessageRefuse as MessageRefuseRequest;
...
public function refuse(MessageRefuseRequest $request)
{
$ad = $this->adRepository->getById($request->id);
$ad->notify(new AdRefuse($request->message));
$this->adRepository->delete($ad);
$request->session()->flash('status', "L'annonce a bien été refusée et le rédacteur va être notifié.");
return response()->json(['id' => $ad->id]);
}
Fonctionnement
Voyons maintenant le fonctionnement pour l'approbation d'une annonce... Dans la vue ads pour le lien de refus on a ce code :<a class="btn btn-danger btn-sm" href="#" role="button" data-id="{{ $ad->id }}" data-toggle="tooltip" title="Refuser l'annonce"><i class="fas fa-thumbs-down"></i></a>
L'action va ouvrir une page modale :
$('.btn-danger').click((e) => {
e.preventDefault();
$('#id').val($(e.currentTarget).attr('data-id'));
$('#messageModal').modal();
});
En effet il faut expliquer au rédacteur pourquoi on refuse l'annonce.
La soumission est assurée par Javascript :
$('#messageForm').submit((e) => {
let that = e.currentTarget;
e.preventDefault();
$('#message').removeClass('is-invalid');
$('.invalid-feedback').html('');
toggleButtons();
$.ajax({
method: $(that).attr('method'),
url: $(that).attr('action'),
data: $(that).serialize()
})
.done((data) => {
document.location.reload(true);
})
.fail((data) => {
toggleButtons();
if(data.status == 422) {
$.each(data.responseJSON.errors, function (i, error) {
$(document)
.find('[name="' + i + '"]')
.addClass('is-invalid')
.next()
.append(error[0]);
});
} else {
$('#messageModal').modal('hide');
$('.alert-warning').removeClass('d-none').addClass('show');
}
});
});
Le traitement de l'échec (fail) distingue deux cas :
- erreur de validation (code 422), dans ce cas on affiche l'erreur
- autre erreur du serveur, dans ce cas on efface la page modale et on affiche une alerte
La requête arrive dans la méthode refuse du contrôleur. La Form request assure la validation et on renvoie une erreur 422 avec le message de l'erreur au besoin :
Comme pour l'approbation de l'annonce on a des alertes de réussite ou d'échec. Avec la notification :Conclusion
Dans cette première partie de construciton de l'administration on a mis en place la structure de base et la page d'accueil. Nous avons aussi traité le cas de l'approbation et du refus des annonces. Dans le prochain article on terminera avec la gestion des annonces obsolètes et la modération des messages.
Par bestmomo
Nombre de commentaires : 32