feat: Implement multi-step order flow with product configuration, user data input, and order summary page.

This commit is contained in:
2025-11-27 13:07:05 +01:00
parent 0a03f9be60
commit 292ed397ab
10 changed files with 223 additions and 43 deletions

View File

@@ -1,8 +1,12 @@
import { Routes } from '@angular/router'; import { Routes } from '@angular/router';
import { Productspage } from './productspage/productspage'; import { Productspage } from './productspage/productspage';
import { Productconfigpage } from './productconfigpage/productconfigpage'; import { Productconfigpage } from './productconfigpage/productconfigpage';
import { Userdatapage } from './userdatapage/userdatapage';
import { Ordersummarypage } from './ordersummarypage/ordersummarypage';
export const routes: Routes = [ export const routes: Routes = [
{ path: '', component: Productspage }, { path: '', component: Productspage },
{ path: 'products/:id', component: Productconfigpage }, { path: 'products/:id', component: Productconfigpage },
{ path: 'user-data', component: Userdatapage },
{ path: 'order-success', component: Ordersummarypage },
]; ];

View File

@@ -28,10 +28,13 @@ export interface IProductConfig {
} }
export interface IUserData { export interface IUserData {
fullName: string; firstName: string;
lastName: string;
email: string;
phone: string;
city: string;
address: string; address: string;
zipCode: string; zipCode: string;
city: string;
} }
export interface IPaymentMethod { export interface IPaymentMethod {

View File

@@ -1 +1,29 @@
<p>ordersummarypage works!</p> <div class="summary">
<sdx-card layout="notification" notification-type="confirmation" icon-name="icon-check-mark-circle-filled"
label="Order confirmation" label-aria-level="4">
Your order has been confirmed.
Shipping number: {{ order.shippingNumber }}
</sdx-card>
<!-- show shipping number and summary of whole order -->
<sdx-card layout="notification" notification-type="confirmation" icon-name="icon-check-mark-circle-filled"
label="Order summary" label-aria-level="4">
{{ order.product?.name }}
<br>
Gesamtpreis: {{ totalPrice }} CHF
<h5>Added ingredients</h5>
@if(order.config?.extraBacon){
<sdx-input-item type="checkbox" checked disabled>
Extra Bacon
</sdx-input-item>
}
@else {
<sdx-input-item type="checkbox" disabled>
Extra Bacon
</sdx-input-item>
}
<p>Salad amount: {{ order.config?.saladAmount || 'normal' }}</p>
<h3>Shipping Number:</h3>
<h4>{{ order.shippingNumber }}</h4>
</sdx-card>
</div>

View File

@@ -0,0 +1,13 @@
.summary {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 1rem;
height: 100vh;
}
.summary sdx-card {
width: 50%;
}

View File

@@ -1,11 +1,39 @@
import { Component } from '@angular/core'; import { Component , inject, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { OrderService } from '../services/order.service';
@Component({ @Component({
selector: 'app-ordersummarypage', selector: 'app-ordersummarypage',
imports: [], imports: [],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
templateUrl: './ordersummarypage.html', templateUrl: './ordersummarypage.html',
styleUrl: './ordersummarypage.scss', styleUrl: './ordersummarypage.scss',
}) })
export class Ordersummarypage { export class Ordersummarypage {
orderService = inject(OrderService);
get totalPrice() {
const order = this.orderService.getOrder();
return (order.product?.basePrice ?? 0) + (order.config?.priceDelta ?? 0);
}
get saladAmount(): number {
const order = this.orderService.getOrder();
switch (order.config?.saladAmount) {
case 'less':
return 1;
case 'normal':
return 2;
case 'much':
return 3;
}
return 2;
}
get order() {
const order = this.orderService.getOrder();
const basePrice = order.product?.basePrice ?? 0;
return order;
}
} }

View File

@@ -76,5 +76,7 @@ export class Productconfigpage {
}, },
totalPrice: this.totalPrice, totalPrice: this.totalPrice,
}); });
window.location.href = '/user-data';
} }
} }

View File

@@ -16,7 +16,7 @@ export class OrderService {
this.currentOrder = { ...this.currentOrder, ...partialOrder }; this.currentOrder = { ...this.currentOrder, ...partialOrder };
} }
public placeOrder(): string { public placeOrder(): IOrder {
// Logic to send order // Logic to send order
const finalOrder = this.getOrder(); const finalOrder = this.getOrder();
@@ -26,6 +26,6 @@ export class OrderService {
console.log('Order placed:', this.getOrder()); console.log('Order placed:', this.getOrder());
return shippingID; return this.getOrder();
} }
} }

View File

@@ -1,38 +1,84 @@
<sdx-dialog <div class="userdatapage">
label="Call back now" <sdx-progress-full id="progress" #progress theme="horizontal"
type="closable-modal" sr-hint="Your current progress in completing your purchase" value="2">
id="contact-modal" icon-name="icon-call-centre" <sdx-progress-full-step label="Your order" [summary]="productName">
display-change-callback=" <a href="/products/{{productId}}">Change quantity, add or remove ingredients</a>
/* Fügt Fokus-Logik hinzu, wenn nötig */ <sdx-button-group>
if (arguments[0] === 'open') document.querySelector('#contact-input').doFocus(); <sdx-button label="Next" (click)="progress.nextStep()"></sdx-button>
if (arguments[0] === 'closing') document.querySelector('#contact-opener').doFocus();"> </sdx-button-group>
</sdx-progress-full-step>
<sdx-dialog-toggle>
<sdx-button id="contact-opener" label="Rückruf anfordern"></sdx-button>
</sdx-dialog-toggle>
<sdx-dialog-content> <sdx-progress-full-step label="Personal details" summary="First Name,
<p>We will be happy to put you through to one of our employees now.</p> Last Name,
E-Mail,
<p class="margin-bottom-4"> Phone">
<sdx-input <div class="row row--gutters">
id="contact-input" <div class="col-md-6">
label="How can we contact you by phone?" <sdx-input-group type="radio" name="salutation" inline="inline" label="Salutation" required>
#phoneNumber> <sdx-input-item value="misses" id="misses">Misses</sdx-input-item>
</sdx-input> <sdx-input-item value="mister" id="mister">Mister</sdx-input-item>
</p> </sdx-input-group>
</div>
</div>
<sdx-button-group> <div class="row row--gutters">
<sdx-button <div class="col-md-6">
label="Call back now" <sdx-input id="firstName" label="First name" name="firstName" required></sdx-input>
(click)="saveContactData(phoneNumber.value)"> </div>
</sdx-button>
<div class="col-md-6">
<sdx-button <sdx-input id="lastName" label="Last name" name="lastName" required></sdx-input>
label="Cancel" </div>
theme="secondary" </div>
onclick="document.getElementById('contact-modal').close()">
</sdx-button> <div class="row row--gutters">
</sdx-button-group> <div class="col-md-6">
</sdx-dialog-content> <sdx-input id="email" label="E-Mail" name="email" required></sdx-input>
</sdx-dialog> </div>
<div class="col-md-6">
<sdx-input id="phone" label="Phone" name="phone"></sdx-input>
</div>
</div>
<div class="row row--gutters">
<div class="col-md-6">
<sdx-input id="city" label="City" name="city"></sdx-input>
</div>
<div class="col-md-6">
<sdx-input id="address" label="Address" name="address"></sdx-input>
</div>
</div>
<div class="row row--gutters">
<div class="col-md-6">
<sdx-input id="zipCode" label="Zip Code" name="zipCode"></sdx-input>
</div>
</div>
<div class="row row--gutters">
<div class="col-xs-12">
<sdx-button-group>
<sdx-button label="Next" (click)="progress.nextStep(); saveData()"></sdx-button>
<sdx-button label="Back" (click)="progress.previousStep()" theme="secondary"></sdx-button>
</sdx-button-group>
</div>
</div>
</sdx-progress-full-step>
<sdx-progress-full-step label="Payment method">
<sdx-option-picker #payments options='[
{ "name": "Twint", "value": "twint", "checked": true, "srHint": "Twint" },
{ "name": "Visa", "value": "visa", "srHint": "Visa" },
{ "name": "Mastercard", "value": "mastercard", "srHint": "Mastercard" }
]' (change)="setPaymentMethod(payments.value)"></sdx-option-picker>
<sdx-button-group>
<sdx-button label="Finish Order" (click)="progress.nextStep(); saveData(); finishOrder();"></sdx-button>
<sdx-button label="Back" (click)="progress.previousStep()" theme="secondary"></sdx-button>
</sdx-button-group>
</sdx-progress-full-step>
</sdx-progress-full>
</div>

View File

@@ -0,0 +1,6 @@
.userdatapage {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}

View File

@@ -1,5 +1,8 @@
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { Component, CUSTOM_ELEMENTS_SCHEMA, inject } from '@angular/core';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';
import { IPaymentMethod, IProduct, IUserData } from '../models/order.interface';
import { OrderService } from '../services/order.service';
@Component({ @Component({
selector: 'app-userdatapage', selector: 'app-userdatapage',
@@ -10,4 +13,51 @@ import { FormsModule } from '@angular/forms';
}) })
export class Userdatapage { export class Userdatapage {
orderService = inject(OrderService);
router = inject(Router);
paymentMethod: 'twint' | 'visa' | 'mastercard' = 'twint';
get productName() {
const product: IProduct = JSON.parse(localStorage.getItem('cart') || '{}').product;
return product?.name;
}
get productId() {
const product: IProduct = JSON.parse(localStorage.getItem('cart') || '{}').product;
return product?.id;
}
public saveData() {
const contactData: IUserData = {
firstName: (document.getElementById('firstName') as HTMLInputElement)?.value,
lastName: (document.getElementById('lastName') as HTMLInputElement)?.value,
email: (document.getElementById('email') as HTMLInputElement)?.value,
phone: (document.getElementById('phone') as HTMLInputElement)?.value,
city: (document.getElementById('city') as HTMLInputElement)?.value,
address: (document.getElementById('address') as HTMLInputElement)?.value,
zipCode: (document.getElementById('zipCode') as HTMLInputElement)?.value,
};
const cart = JSON.parse(localStorage.getItem('cart') || '{}');
this.orderService.updateOrder({
userData: contactData,
paymentMethod: { type: this.paymentMethod },
product: cart.product,
config: cart.config
});
console.log(contactData, this.paymentMethod);
}
public finishOrder() {
this.orderService.placeOrder();
this.router.navigate(['/order-success']);
}
public setPaymentMethod(values: any) {
if (Array.isArray(values) && values.length > 0) {
this.paymentMethod = values[0] as 'twint' | 'visa' | 'mastercard';
}
}
} }