{% extends 'backfront.html.twig' %}
{% block title %}SIP-ACADEMY - Mon Panier{% endblock %}
{% block meta_description %}
Consultez votre panier d'achat SIP Academy. Gérez vos formations et packs, et finalisez votre commande en toute sécurité.
{% endblock %}
{% block body %}
<style>
/* ===== COULEURS ET VARIABLES ===== */
:root {
--gold: #FFAE27;
--gold-dark: #e09500;
--gold-light: #fff8e6;
--navy: #1F3971;
--navy-dark: #162d5a;
--navy-light: #e8edf8;
--text-dark: #1a2a3a;
--text-muted: #6c757d;
--border-light: #eef2f6;
--success: #28a745;
--danger: #dc3545;
}
/* ===== BREADCRUMB ===== */
.breadcrumb-area {
background: linear-gradient(135deg, var(--navy) 0%, var(--navy-dark) 100%);
padding: 80px 0 60px;
position: relative;
}
.breadcrumb-title h1 {
color: #fff;
font-size: 42px;
font-weight: 700;
margin-bottom: 10px;
}
.breadcrumb-list {
list-style: none;
padding: 0;
margin: 0;
display: flex;
justify-content: center;
gap: 15px;
}
.breadcrumb-list li, .breadcrumb-list li a {
color: rgba(255,255,255,0.7);
text-decoration: none;
}
.breadcrumb-list li.active {
color: var(--gold);
}
/* ===== PANIER ===== */
.cart-section {
padding: 60px 0;
background: #f8fafc;
}
/* Titre principal */
.cart-header {
margin-bottom: 30px;
}
.cart-title {
font-size: 28px;
font-weight: 700;
color: var(--text-dark);
margin-bottom: 8px;
}
.cart-subtitle {
color: var(--text-muted);
font-size: 14px;
}
/* Tableau */
.cart-table {
background: #fff;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 5px 25px rgba(0,0,0,0.05);
border: 1px solid var(--border-light);
}
.cart-table thead th {
background: var(--navy-light);
padding: 15px 20px;
font-weight: 600;
color: var(--navy);
border-bottom: 2px solid var(--border-light);
}
.cart-table tbody td {
padding: 20px;
vertical-align: middle;
border-bottom: 1px solid var(--border-light);
}
/* Carte produit */
.product-card {
display: flex;
align-items: center;
gap: 15px;
}
.product-image {
width: 80px;
height: 80px;
border-radius: 12px;
overflow: hidden;
background: linear-gradient(135deg, #f8faff 0%, #fff 100%);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.product-image img {
width: auto;
height: auto;
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
.product-info {
flex: 1;
}
.product-title {
font-size: 16px;
font-weight: 700;
color: var(--text-dark);
margin-bottom: 4px;
}
.product-title a {
color: inherit;
text-decoration: none;
transition: color 0.2s;
}
.product-title a:hover {
color: var(--gold);
}
.product-meta {
font-size: 12px;
color: var(--text-muted);
}
/* Badges */
.badge-custom {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 14px;
border-radius: 30px;
font-size: 12px;
font-weight: 600;
}
.badge-formation {
background: linear-gradient(135deg, #0d6efd, #0a58ca);
color: white;
}
.badge-pack {
background: linear-gradient(135deg, #28a745, #1e7e34);
color: white;
}
/* Prix */
.price-current {
font-size: 18px;
font-weight: 700;
color: var(--gold-dark);
}
.price-old {
font-size: 13px;
color: var(--text-muted);
text-decoration: line-through;
margin-left: 8px;
}
/* Bouton supprimer */
.btn-remove {
width: 36px;
height: 36px;
border-radius: 50%;
background: #f8f9fa;
border: 1px solid var(--border-light);
color: var(--danger);
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
cursor: pointer;
}
.btn-remove:hover {
background: var(--danger);
color: white;
transform: scale(1.05);
}
/* Panier vide */
/* Panier vide - centré parfaitement */
.empty-cart {
text-align: center;
padding: 60px 40px;
background: #fff;
border-radius: 24px;
border: 1px solid var(--border-light);
box-shadow: 0 10px 30px rgba(0,0,0,0.05);
max-width: 550px;
margin: 0 auto; /* Centrage horizontal */
width: 100%;
}
.empty-cart-icon {
font-size: 80px;
color: var(--gold);
margin-bottom: 20px;
}
.empty-cart h3 {
font-size: 24px;
font-weight: 700;
color: var(--text-dark);
margin-bottom: 12px;
}
.empty-cart p {
color: var(--text-muted);
margin-bottom: 30px;
font-size: 15px;
}
.btn-continue {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 10px;
background: linear-gradient(135deg, var(--navy), var(--navy-dark));
color: #fff;
padding: 12px 32px;
border-radius: 40px;
text-decoration: none;
font-weight: 600;
transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.btn-continue:hover {
background: linear-gradient(135deg, var(--gold), var(--gold-dark));
color: var(--navy);
transform: translateY(-3px);
box-shadow: 0 10px 25px rgba(255,174,39,0.3);
}
.btn-continue i {
transition: transform 0.3s;
}
.btn-continue:hover i {
transform: translateX(-5px);
}
/* Résumé commande */
.summary-card {
background: #fff;
border-radius: 20px;
padding: 25px;
box-shadow: 0 5px 25px rgba(0,0,0,0.05);
border: 1px solid var(--border-light);
position: sticky;
top: 20px;
}
.summary-title {
font-size: 20px;
font-weight: 700;
color: var(--text-dark);
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 2px solid var(--border-light);
}
.summary-row {
display: flex;
justify-content: space-between;
padding: 10px 0;
font-size: 14px;
}
.summary-row.total {
border-top: 2px solid var(--border-light);
margin-top: 10px;
padding-top: 15px;
font-size: 18px;
font-weight: 700;
color: var(--text-dark);
}
.summary-row.total .summary-value {
color: var(--gold-dark);
font-size: 22px;
}
.summary-divider {
height: 1px;
background: var(--border-light);
margin: 15px 0;
}
.btn-checkout {
width: 100%;
background: linear-gradient(135deg, var(--navy), var(--navy-dark));
color: #fff;
border: none;
padding: 14px;
border-radius: 40px;
font-weight: 700;
font-size: 16px;
transition: all 0.3s ease;
margin-top: 20px;
cursor: pointer;
}
.btn-checkout:hover {
background: linear-gradient(135deg, var(--gold), var(--gold-dark));
color: var(--navy);
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(255,174,39,0.3);
}
.payment-icons {
display: flex;
justify-content: center;
gap: 15px;
margin-top: 20px;
padding-top: 15px;
border-top: 1px solid var(--border-light);
}
.payment-icons i {
font-size: 24px;
color: #adb5bd;
transition: color 0.3s;
}
.payment-icons i:hover {
color: var(--gold);
}
/* Responsive */
@media (max-width: 768px) {
.cart-table thead {
display: none;
}
.cart-table tbody tr {
display: block;
margin-bottom: 15px;
border: 1px solid var(--border-light);
border-radius: 16px;
}
.cart-table tbody td {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 15px;
border-bottom: 1px solid var(--border-light);
}
.cart-table tbody td:last-child {
border-bottom: none;
}
.cart-table tbody td::before {
content: attr(data-label);
font-weight: 600;
color: var(--navy);
}
.product-card {
flex-direction: column;
text-align: center;
}
.summary-card {
margin-top: 20px;
position: static;
}
}
</style>
<!-- BREADCRUMB -->
<div class="breadcrumb-area">
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="breadcrumb-title text-center">
<h1>Mon Panier</h1>
<ul class="breadcrumb-list">
<li><a href="{{ path('app_home') }}">Accueil</a></li>
<li class="active">Panier</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<!-- SECTION PANIER -->
<!-- SECTION PANIER -->
<div class="cart-section">
<div class="container">
{% if items|length > 0 %}
<!-- Affichage quand le panier n'est pas vide -->
<div class="row">
<div class="col-lg-8">
<div class="cart-header">
<h2 class="cart-title">Mon Panier</h2>
<p class="cart-subtitle">{{ items|length }} élément(s) dans votre panier</p>
</div>
<div class="cart-table">
<table class="table mb-0">
<thead>
<tr>
<th>Produit</th>
<th>Type</th>
<th class="text-end">Prix</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
{% set totalPanier = 0 %}
{% for item in items %}
{% set quantite = 1 %}
{% set prix = item.prix %}
{% set sousTotal = prix * quantite %}
{% set totalPanier = totalPanier + sousTotal %}
<tr>
<td data-label="Produit">
<div class="product-card">
<div class="product-image">
{% if item.type == 'formation' %}
<img src="{{ asset('uploads/formation/' ~ item.item.affiche) }}" alt="{{ item.item.titre }}">
{% else %}
<img src="{{ asset('uploads/formation/' ~ item.item.image) }}" alt="{{ item.item.libelle }}">
{% endif %}
</div>
<div class="product-info">
<h5 class="product-title">
<a href="{% if item.type == 'formation' %}{{ path('app_formation_detailsfront', {'slug': item.item.slug}) }}{% else %}#{% endif %}">
{% if item.type == 'formation' %}
{{ item.item.titre }}
{% else %}
{{ item.item.libelle }}
{% endif %}
</a>
</h5>
<div class="product-meta">
{% if item.type == 'formation' and item.item.formateur %}
Par {{ item.item.formateur.prenom }} {{ item.item.formateur.nom }}
{% elseif item.type == 'pack' %}
Pack de formations
{% endif %}
</div>
</div>
</div>
</td>
<td data-label="Type">
{% if item.type == 'formation' %}
<span class="badge-custom badge-formation">
<i class="fa fa-graduation-cap"></i> Formation
</span>
{% else %}
<span class="badge-custom badge-pack">
<i class="fa fa-cubes"></i> Pack
</span>
{% endif %}
</td>
<td data-label="Prix" class="text-end">
<span class="price-current">{{ sousTotal }} DT</span>
{% if item.type == 'formation' and item.item.isPromo %}
<div class="price-old"><del>{{ item.item.prixPromo }} DT</del></div>
{% endif %}
</td>
<td data-label="Action" class="text-center">
<button class="btn-remove"
data-id="{{ item.item.id }}"
data-type="{{ item.type }}">
<i class="fa fa-trash-o"></i>
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-lg-4">
<div class="summary-card">
<h4 class="summary-title">Résumé de la commande</h4>
<div class="summary-row">
<span class="summary-label">Sous-total</span>
<span class="summary-value">{{ totalPanier|number_format(2, '.', ',') }} DT</span>
</div>
{% set tva = totalPanier * 0.19 %}
<div class="summary-row">
<span class="summary-label">TVA (19%)</span>
<span class="summary-value">{{ tva|number_format(2, '.', ',') }} DT</span>
</div>
<div class="summary-row">
<span class="summary-label">Timbre fiscal</span>
<span class="summary-value">1.00 DT</span>
</div>
{% set totalAvecTvaEtTimbre = totalPanier + tva + 1 %}
<div class="summary-row total">
<span class="summary-label">Total à payer</span>
<span class="summary-value">{{ totalAvecTvaEtTimbre|number_format(2, '.', ',') }} DT</span>
</div>
<button id="btn-payer" class="btn-checkout">
<i class="fa fa-lock"></i>
Valider la commande
</button>
</div>
</div>
</div>
{% else %}
<!-- Panier vide - centré -->
<div class="row">
<div class="col-12">
<div class="empty-cart">
<div class="empty-cart-icon">
<i class="fa fa-shopping-cart"></i>
</div>
<h3>Votre panier est vide</h3>
<p>Découvrez nos formations et packs pour commencer votre apprentissage</p>
<a href="{{ path('app_formation_elearning_front') }}" class="btn-continue">
<i class="fa fa-arrow-left"></i>
Découvrir nos formations
</a>
</div>
</div>
</div>
{% endif %}
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
// Gestion de la suppression
document.querySelectorAll('.btn-remove').forEach(btn => {
btn.addEventListener('click', async (e) => {
e.preventDefault();
const id = btn.dataset.id;
const type = btn.dataset.type;
const result = await Swal.fire({
title: 'Confirmation',
text: 'Voulez-vous vraiment supprimer cet élément du panier ?',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#dc3545',
cancelButtonColor: '#6c757d',
confirmButtonText: 'Oui, supprimer',
cancelButtonText: 'Annuler'
});
if (result.isConfirmed) {
try {
const response = await fetch(`/panier/supprimer/${id}?type=${type}`, {
method: 'POST',
headers: { 'X-Requested-With': 'XMLHttpRequest' }
});
if (response.ok) {
Swal.fire({
icon: 'success',
title: 'Supprimé !',
text: 'L\'élément a été supprimé de votre panier.',
timer: 1500,
showConfirmButton: false
}).then(() => location.reload());
}
} catch (err) {
console.error(err);
}
}
});
});
// Gestion du paiement
const btnPayer = document.getElementById('btn-payer');
const isAuthenticated = {{ isAuthenticated ? 'true' : 'false' }};
if (btnPayer) {
btnPayer.addEventListener('click', (event) => {
event.preventDefault();
if (!isAuthenticated) {
Swal.fire({
icon: 'info',
title: 'Connexion requise',
text: 'Vous devez être connecté pour finaliser votre commande.',
showCancelButton: true,
confirmButtonText: 'Se connecter',
cancelButtonText: 'Annuler',
confirmButtonColor: '#1F3971'
}).then((result) => {
if (result.isConfirmed) {
// Rediriger vers login avec l'URL de retour vers le panier
window.location.href = '{{ path('app_login') }}?redirect={{ path('panier_voir')|url_encode }}';
}
});
}
else {
Swal.fire({
icon: 'question',
title: 'Confirmation',
html: 'Voulez-vous confirmer cette commande ?<br><small>Le paiement s\'effectuera à la livraison</small>',
showCancelButton: true,
confirmButtonText: 'Oui, confirmer',
cancelButtonText: 'Annuler',
confirmButtonColor: '#28a745'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = '{{ path('panier_confirmer') }}';
}
});
}
});
}
});
</script>
{% endblock %}