Fix Mathu Coding und Payment fix

This commit is contained in:
FlorianSpeicher
2025-06-11 13:30:36 +02:00
parent 4b3d280789
commit ab72054820
7 changed files with 199 additions and 138 deletions

View File

@@ -59,7 +59,7 @@
"other": "Sonstiges", "other": "Sonstiges",
"outOfStock": "Ausverkauft", "outOfStock": "Ausverkauft",
"payment": "Bezahlung", "payment": "Bezahlung",
"paymentNotAvailable": "Keine Zahlungsmöglichkeit hinterlegt.", "paymentNotAvailable": "Keine Zahlungsmethoden verfügbar.",
"pageDoesNotExist": "Ups! Diese Seite existiert nicht.", "pageDoesNotExist": "Ups! Diese Seite existiert nicht.",
"phone": "Telefon", "phone": "Telefon",
"placeOrder": "Zahlungspflichtig bestellen", "placeOrder": "Zahlungspflichtig bestellen",
@@ -91,5 +91,7 @@
"noRatingsYet": "Noch keine Bewertungen vorhanden.", "noRatingsYet": "Noch keine Bewertungen vorhanden.",
"accounts": "Konten", "accounts": "Konten",
"statistics": "Statistiken", "statistics": "Statistiken",
"thanksForRating": "Vielen Dank für die Bewertung!" "thanksForRating": "Vielen Dank für die Bewertung!",
"telephone": "Telefon",
"basketEmpty": "Der Warenkorb ist leer."
} }

View File

@@ -91,5 +91,7 @@
"noRatingsYet": "No ratings yet", "noRatingsYet": "No ratings yet",
"accounts": "Accounts", "accounts": "Accounts",
"statistics": "Statistics", "statistics": "Statistics",
"thanksForRating": "Thank you for rating!" "thanksForRating": "Thank you for rating!",
"telephone": "Telephone",
"basketEmpty": "The shopping cart is empty."
} }

View File

@@ -1,13 +1,14 @@
import React, { createContext, useContext, useState } from 'react'; import React, { createContext, useContext, useState } from 'react';
import Item from '../components/Item';
interface BasketItem { interface BasketItem {
itemId: string; item: Item;
quantity: number; quantity: number;
} }
interface BasketContextType { interface BasketContextType {
basket: BasketItem[]; basket: BasketItem[];
addToBasket: (itemId: string, quantity: number) => void; addToBasket: (item: Item, quantity: number) => void;
clearBasket: () => void; clearBasket: () => void;
} }
@@ -16,19 +17,19 @@ const BasketContext = createContext<BasketContextType | undefined>(undefined);
export const BasketProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { export const BasketProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [basket, setBasket] = useState<BasketItem[]>([]); const [basket, setBasket] = useState<BasketItem[]>([]);
const addToBasket = (itemId: string, quantity: number) => { const addToBasket = (item: Item, quantity: number) => {
setBasket((prevBasket) => { setBasket((prevBasket) => {
const existingItem = prevBasket.find((item) => item.itemId === itemId); const existingItem = prevBasket.find((basketItem) => basketItem.item.uuid === item.uuid);
if (existingItem) { if (existingItem) {
// Update quantity if item already exists // Update quantity if item already exists
return prevBasket.map((item) => return prevBasket.map((basketItem) =>
item.itemId === itemId basketItem.item.uuid === item.uuid
? { ...item, quantity: item.quantity + quantity } ? { ...basketItem, quantity: basketItem.quantity + quantity }
: item : basketItem
); );
} }
// Add new item to basket // Add new item to basket
return [...prevBasket, { itemId, quantity }]; return [...prevBasket, { item, quantity }];
}); });
}; };

View File

@@ -14,7 +14,7 @@ export default function ItemCard({ item }: { item: Item }) {
const { addToBasket } = useBasket(); const { addToBasket } = useBasket();
const handleAddToCart = () => { const handleAddToCart = () => {
addToBasket(item.id, 1); addToBasket(item, 1);
console.log(`Added ${1} of ${item.name} to basket`); console.log(`Added ${1} of ${item.name} to basket`);
}; };

View File

@@ -0,0 +1,18 @@
export default function LoginPopUp() {
return (
<div className="login-popup">
<h2>Login</h2>
<form>
<div className="form-group">
<label htmlFor="username">Username</label>
<input type="text" id="username" name="username" required />
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input type="password" id="password" name="password" required />
</div>
<button type="submit">Login</button>
</form>
</div>
);
}

View File

@@ -41,7 +41,7 @@ export default function ProductInfo({ item }: { item: Item }) {
const handleAddToCart = () => { const handleAddToCart = () => {
addToBasket(item.id, quantity); addToBasket(item, quantity);
setOpen(true); setOpen(true);
console.log(`Added {quantity} of €{item.name} to basket`); console.log(`Added {quantity} of €{item.name} to basket`);
}; };

View File

@@ -1,32 +1,33 @@
import React, { useState } from 'react';
import { import {
Box, Box,
Button, Button,
Container, Container,
Step, Divider,
StepLabel,
Stepper,
Typography,
TextField,
Grid, Grid,
Paper,
List, List,
ListItem, ListItem,
ListItemText, ListItemText,
Divider, Paper,
styled, Step,
StepLabel,
Stepper,
TextField,
Typography
} from '@mui/material'; } from '@mui/material';
import { useBasket } from '../helper/BasketProvider'; import React, { useState } from 'react';
import { useTranslation } from "react-i18next";
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import {useTranslation} from "react-i18next"; import { useBasket } from '../helper/BasketProvider';
const Item = styled(Paper)(({ theme }) => ({ type ShippingDetails = {
backgroundColor: theme.palette.background.paper, firstName: string;
color: theme.palette.text.primary, lastName: string;
...theme.typography.body2, telefon: string;
padding: theme.spacing(1), address: string;
textAlign: 'center', postalCode: string;
})); city: string;
country: string;
};
export default function Payment() { export default function Payment() {
@@ -35,13 +36,14 @@ export default function Payment() {
const navigator = useNavigate(); const navigator = useNavigate();
const { basket, clearBasket } = useBasket(); const { basket, clearBasket } = useBasket();
const [activeStep, setActiveStep] = useState(0); const [activeStep, setActiveStep] = useState(0);
const [shippingDetails, setShippingDetails] = useState({ const [shippingDetails, setShippingDetails] = useState<ShippingDetails>({
firstName: '', firstName: '',
lastName: '', lastName: '',
telefon: '',
address: '', address: '',
postalCode: '', postalCode: '',
city: '', city: '',
country: '' country: 'Deutschland',
}); });
const [orderNumber, setOrderNumber] = useState<string | null>(null); const [orderNumber, setOrderNumber] = useState<string | null>(null);
const steps = [t('reviewCart'), t('shippingDetails'), t('payment'), t('orderSummary')]; const steps = [t('reviewCart'), t('shippingDetails'), t('payment'), t('orderSummary')];
@@ -71,6 +73,18 @@ export default function Payment() {
clearBasket(); clearBasket();
}; };
// Hilfsfunktion prüfen, ob alle Pflichtfelder ausgefüllt sind
const isShippingDetailsValid = () => {
return (
shippingDetails.firstName.trim() !== '' &&
shippingDetails.lastName.trim() !== '' &&
shippingDetails.address.trim() !== '' &&
shippingDetails.postalCode.trim() !== '' &&
shippingDetails.city.trim() !== '' &&
shippingDetails.country.trim() !== ''
);
};
const renderStepContent = (step: number) => { const renderStepContent = (step: number) => {
switch (step) { switch (step) {
case 0: case 0:
@@ -79,18 +93,24 @@ export default function Payment() {
<Typography variant="h6" gutterBottom> <Typography variant="h6" gutterBottom>
{t('reviewCart')} {t('reviewCart')}
</Typography> </Typography>
<List> {basket.length === 0 ? (
{basket.map((item) => ( <Typography color="error" sx={{ my: 2 }}>
<ListItem key={item.itemId}> {t('basketEmpty')}
<ListItemText </Typography>
primary={`Item ID: ${item.itemId}`} ) : (
secondary={`Quantity: ${item.quantity}`} <List>
/> {basket.map((item) => (
</ListItem> <ListItem key={item.item.uuid}>
))} <ListItemText
</List> primary={`${item.item.name} (${item.item.price100 / 100} €)`}
secondary={`Item ID: ${item.item.uuid} \n Quantity: ${item.quantity} \n Price: ${(item.item.price100 * item.quantity) / 100}`}
/>
</ListItem>
))}
</List>
)}
<Divider sx={{ my: 2 }} /> <Divider sx={{ my: 2 }} />
<Button variant="outlined" color="error" onClick={handleClearBasket}> <Button variant="outlined" color="error" onClick={handleClearBasket} disabled={basket.length === 0}>
{t('clearCart')} {t('clearCart')}
</Button> </Button>
</Box> </Box>
@@ -102,60 +122,70 @@ export default function Payment() {
{t('shippingDetails')} {t('shippingDetails')}
</Typography> </Typography>
<Grid container spacing={2}> <Grid container spacing={2}>
<Item>
<TextField <TextField
fullWidth fullWidth
label={t('firstName')} label={t('firstName')}
name="firstName" name="firstName"
value={shippingDetails.firstName} value={shippingDetails.firstName}
onChange={handleInputChange} onChange={handleInputChange}
/> required
</Item> />
<Item>
<TextField <TextField
fullWidth fullWidth
label={t('lastName')} label={t('lastName')}
name="lastName" name="lastName"
value={shippingDetails.lastName} value={shippingDetails.lastName}
onChange={handleInputChange} onChange={handleInputChange}
/> required
</Item> />
<Item>
<TextField <TextField
fullWidth fullWidth
label={t('address')} label={t('telephone')}
name="address" name="telefon"
value={shippingDetails.address} value={shippingDetails.telefon}
onChange={handleInputChange} onChange={handleInputChange}
/> type='number'
</Item> />
<Item>
<TextField <TextField
fullWidth fullWidth
label={t('postalCode')} label={t('address')}
name="postalCode" name="address"
value={shippingDetails.postalCode} value={shippingDetails.address}
onChange={handleInputChange} onChange={handleInputChange}
/> required
</Item> />
<Item>
<TextField <TextField
fullWidth fullWidth
label={t('city')} label={t('postalCode')}
name="city" name="postalCode"
value={shippingDetails.city} value={shippingDetails.postalCode}
onChange={handleInputChange} onChange={handleInputChange}
/> required
</Item> type='number'
<Item> />
<TextField
fullWidth <TextField
label={t('country')} fullWidth
name="country" label={t('city')}
value={shippingDetails.country} name="city"
onChange={handleInputChange} value={shippingDetails.city}
/> onChange={handleInputChange}
</Item> required
/>
<TextField
fullWidth
label={t('country')}
name="country"
value={shippingDetails.country}
onChange={handleInputChange}
required
/>
</Grid> </Grid>
</Box> </Box>
); );
@@ -194,10 +224,10 @@ export default function Payment() {
<Typography variant="h6">{t('orderedItems')}:</Typography> <Typography variant="h6">{t('orderedItems')}:</Typography>
<List> <List>
{basket.map((item) => ( {basket.map((item) => (
<ListItem key={item.itemId}> <ListItem key={item.item.uuid}>
<ListItemText <ListItemText
primary={`Item ID: ${item.itemId}`} primary={`${item.item.name} (${item.item.price100 / 100} €)`}
secondary={`Quantity: ${item.quantity}`} secondary={`Item ID: ${item.item.uuid} \n Quantity: ${item.quantity} \n Price: ${(item.item.price100 * item.quantity) / 100}`}
/> />
</ListItem> </ListItem>
))} ))}
@@ -211,45 +241,53 @@ export default function Payment() {
return ( return (
<div className="page-background"> <div className="page-background">
<Container maxWidth="md" sx={{ py: 4 }}> <Container maxWidth="md" sx={{ py: 4 }}>
<Paper elevation={3} sx={{ p: 4 }}> <Paper elevation={3} sx={{ p: 4 }}>
<Typography variant="h4" align="center" gutterBottom> <Typography variant="h4" align="center" gutterBottom>
{t('completeYourOrder')} {t('completeYourOrder')}
</Typography> </Typography>
<Stepper activeStep={activeStep} alternativeLabel> <Stepper activeStep={activeStep} alternativeLabel>
{steps.map((label) => ( {steps.map((label) => (
<Step key={label}> <Step key={label}>
<StepLabel>{label}</StepLabel> <StepLabel>{label}</StepLabel>
</Step> </Step>
))} ))}
</Stepper> </Stepper>
<Box sx={{ mt: 4 }}>{renderStepContent(activeStep)}</Box> <Box sx={{ mt: 4 }}>{renderStepContent(activeStep)}</Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', mt: 4 }}> <Box sx={{ display: 'flex', justifyContent: 'space-between', mt: 4 }}>
<Button
disabled={activeStep === 0}
onClick={handleBack}
variant="outlined"
>
{t('back')}
</Button>
{activeStep === steps.length - 1 ? (
<Button <Button
variant="contained" disabled={activeStep === 0}
color="primary" onClick={handleBack}
onClick={() => { variant="outlined"
navigator('/');
}}
> >
{t('finish')} {t('back')}
</Button> </Button>
) : ( {activeStep === steps.length - 1 ? (
<Button variant="contained" color="primary" onClick={handleNext}> <Button
{activeStep === steps.length - 2 ? t('placeOrder') : t('next')} variant="contained"
</Button> color="primary"
)} onClick={() => {
</Box> navigator('/');
</Paper> }}
</Container> >
{t('finish')}
</Button>
) : (
<Button
variant="contained"
color="primary"
onClick={handleNext}
disabled={
basket.length === 0 ||
(activeStep === 1 && !isShippingDetailsValid())
}
>
{activeStep === steps.length - 2 ? t('placeOrder') : t('next')}
</Button>
)}
</Box>
</Paper>
</Container>
</div> </div>
); );
} }