Skip to content
This repository was archived by the owner on Jul 20, 2026. It is now read-only.

Commit c2fc2a4

Browse files
committed
cart and checkout
1 parent 2912d9c commit c2fc2a4

11 files changed

Lines changed: 1351 additions & 230 deletions

frontend/src/app/app.routes.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ import { Routes } from '@angular/router';
22
import { AccountComponent } from './account.component';
33
import { HomeComponent } from './home.component';
44
import { ShopComponent } from './shop.component';
5+
import { CartComponent } from './cart.component';
56

67
export const routes: Routes = [
78
{ path: '', component: HomeComponent},
89
{ path: 'account', component: AccountComponent},
9-
{ path: 'shop', component: ShopComponent }
10+
{ path: 'shop', component: ShopComponent },
11+
{ path: 'cart', component: CartComponent }
1012
];
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
<div class="cart-page">
2+
3+
@if (step() === 'cart') {
4+
<div class="cart-view">
5+
<h2 class="cart-title">
6+
Mon panier
7+
@if (itemCount() > 0) {
8+
<span class="cart-badge">{{ itemCount() }}</span>
9+
}
10+
</h2>
11+
12+
@if (items().length === 0) {
13+
<div class="cart-empty">
14+
<p>Votre panier est vide.</p>
15+
</div>
16+
}
17+
18+
@if (items().length > 0) {
19+
<ul class="cart-list">
20+
@for (item of items(); track item.cartItemId) {
21+
<li class="cart-item">
22+
23+
<div class="item-header">
24+
<div class="item-meta">
25+
<span class="item-badge" [class.badge-menu]="item.type === 'menu'">
26+
{{ item.type === 'menu' ? 'Menu' : 'Produit' }}
27+
</span>
28+
<strong class="item-name">
29+
{{ item.type === 'product' ? item.product!.name : item.menu!.name }}
30+
</strong>
31+
</div>
32+
<div class="item-right">
33+
<div class="qty-control">
34+
<button class="qty-btn" (click)="cartService.decrementItem(item.cartItemId)"></button>
35+
<span class="qty-val">{{ item.quantity }}</span>
36+
<button class="qty-btn" (click)="cartService.incrementItem(item.cartItemId)">+</button>
37+
</div>
38+
<span class="item-price">
39+
{{ ((item.type === 'product' ? item.product!.price : item.menu!.price) * item.quantity).toFixed(2) }}€
40+
</span>
41+
<button class="remove-btn" (click)="cartService.removeItem(item.cartItemId)"></button>
42+
</div>
43+
</div>
44+
45+
@if (item.type === 'product') {
46+
<div class="item-summary">
47+
@if (summaryIngredients(item.product!.customization.ingredients)) {
48+
<span class="summary-tag removed">{{ summaryIngredients(item.product!.customization.ingredients) }}</span>
49+
}
50+
@if (summaryExtras(item.product!.customization.extras)) {
51+
<span class="summary-tag">{{ summaryExtras(item.product!.customization.extras) }}</span>
52+
}
53+
</div>
54+
}
55+
56+
@if (item.type === 'menu') {
57+
<div class="item-summary">
58+
@for (cust of item.menu!.customizations; track cust.productId) {
59+
<div class="menu-product-summary">
60+
<span class="menu-product-label">{{ cust.productName }}</span>
61+
@if (summaryIngredients(cust.ingredients)) {
62+
<span class="summary-tag removed">{{ summaryIngredients(cust.ingredients) }}</span>
63+
}
64+
@if (summaryExtras(cust.extras)) {
65+
<span class="summary-tag">{{ summaryExtras(cust.extras) }}</span>
66+
}
67+
</div>
68+
}
69+
</div>
70+
}
71+
72+
</li>
73+
}
74+
</ul>
75+
76+
<div class="cart-footer">
77+
<div class="cart-total">
78+
<span>Total</span>
79+
<strong>{{ total().toFixed(2) }}€</strong>
80+
</div>
81+
<button class="btn-fire" (click)="goToCheckout()">
82+
Commander — {{ total().toFixed(2) }}€
83+
</button>
84+
<button class="btn-ghost" (click)="cartService.clearCart()">Vider le panier</button>
85+
</div>
86+
}
87+
</div>
88+
}
89+
90+
@if (step() === 'checkout') {
91+
<div class="checkout-view">
92+
<button class="back-btn" (click)="backToCart()">← Retour au panier</button>
93+
<h2 class="cart-title">Finaliser la commande</h2>
94+
95+
<form class="checkout-form" [formGroup]="checkoutForm" (ngSubmit)="submitOrder()">
96+
97+
<fieldset class="form-section">
98+
<legend class="section-legend">Coordonnées</legend>
99+
100+
<div class="form-row">
101+
<div class="form-field" [class.error]="fieldInvalid('firstName')">
102+
<label>Prénom</label>
103+
<input type="text" formControlName="firstName" placeholder="Jean" />
104+
@if (fieldInvalid('firstName')) { <span class="error-msg">Requis</span> }
105+
</div>
106+
<div class="form-field" [class.error]="fieldInvalid('lastName')">
107+
<label>Nom</label>
108+
<input type="text" formControlName="lastName" placeholder="Dupont" />
109+
@if (fieldInvalid('lastName')) { <span class="error-msg">Requis</span> }
110+
</div>
111+
</div>
112+
113+
<div class="form-field" [class.error]="fieldInvalid('email')">
114+
<label>Email</label>
115+
<input type="email" formControlName="email" placeholder="jean@example.com" />
116+
@if (fieldInvalid('email')) { <span class="error-msg">Email invalide</span> }
117+
</div>
118+
119+
<div class="form-field" [class.error]="fieldInvalid('address')">
120+
<label>Adresse</label>
121+
<input type="text" formControlName="address" placeholder="12 rue de la Paix" />
122+
@if (fieldInvalid('address')) { <span class="error-msg">Requis</span> }
123+
</div>
124+
125+
<div class="form-row">
126+
<div class="form-field" [class.error]="fieldInvalid('city')">
127+
<label>Ville</label>
128+
<input type="text" formControlName="city" placeholder="Belfort" />
129+
@if (fieldInvalid('city')) { <span class="error-msg">Requis</span> }
130+
</div>
131+
<div class="form-field" [class.error]="fieldInvalid('zip')">
132+
<label>Code postal</label>
133+
<input type="text" formControlName="zip" placeholder="90000" />
134+
@if (fieldInvalid('zip')) { <span class="error-msg">Requis</span> }
135+
</div>
136+
</div>
137+
</fieldset>
138+
139+
<fieldset class="form-section">
140+
<legend class="section-legend">Paiement</legend>
141+
142+
<div class="form-field" [class.error]="fieldInvalid('cardNumber')">
143+
<label>Numéro de carte</label>
144+
<input
145+
type="text"
146+
formControlName="cardNumber"
147+
placeholder="1234567812345678"
148+
maxlength="16"
149+
(input)="formatCardNumber($event)"
150+
/>
151+
@if (fieldInvalid('cardNumber')) { <span class="error-msg">16 chiffres requis</span> }
152+
</div>
153+
154+
<div class="form-row">
155+
<div class="form-field" [class.error]="fieldInvalid('cardExpiry')">
156+
<label>Expiration</label>
157+
<input
158+
type="text"
159+
formControlName="cardExpiry"
160+
placeholder="MM/AA"
161+
maxlength="5"
162+
(input)="formatExpiry($event)"
163+
/>
164+
@if (fieldInvalid('cardExpiry')) { <span class="error-msg">Format MM/AA</span> }
165+
</div>
166+
<div class="form-field" [class.error]="fieldInvalid('cardCvc')">
167+
<label>CVC</label>
168+
<input type="text" formControlName="cardCvc" placeholder="123" maxlength="4" />
169+
@if (fieldInvalid('cardCvc')) { <span class="error-msg">3-4 chiffres</span> }
170+
</div>
171+
</div>
172+
</fieldset>
173+
174+
<div class="order-summary">
175+
<span>Total à payer</span>
176+
<strong>{{ total().toFixed(2) }}€</strong>
177+
</div>
178+
179+
<button type="submit" class="btn-fire">
180+
Confirmer et payer — {{ total().toFixed(2) }}€
181+
</button>
182+
183+
</form>
184+
</div>
185+
}
186+
187+
@if (step() === 'confirm') {
188+
<div class="confirm-view">
189+
<div class="confirm-icon"></div>
190+
<h2 class="confirm-title">Commande confirmée !</h2>
191+
<p class="confirm-sub">Votre commande a été enregistrée. Merci !</p>
192+
<button class="btn-fire" (click)="restartShopping()">Retour à la carte</button>
193+
</div>
194+
}
195+
196+
</div>

0 commit comments

Comments
 (0)