more translations
This commit is contained in:
@@ -722,7 +722,7 @@ export class LoginComponent extends Component {
|
||||
{tabValue === 1 && (
|
||||
<TextField
|
||||
margin="dense"
|
||||
label="Passwort bestätigen"
|
||||
label={this.props.t ? this.props.t('auth.confirmPassword') : 'Passwort bestätigen'}
|
||||
type="password"
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React from "react";
|
||||
import { Box, TextField, Typography } from "@mui/material";
|
||||
import { withI18n } from "../../i18n/withTranslation.js";
|
||||
|
||||
const AddressForm = ({ title, address, onChange, errors, namePrefix }) => {
|
||||
const AddressForm = ({ title, address, onChange, errors, namePrefix, t }) => {
|
||||
// Helper function to determine if a required field should show error styling
|
||||
const getRequiredFieldError = (fieldName, value) => {
|
||||
const isEmpty = !value || value.trim() === "";
|
||||
@@ -36,7 +37,7 @@ const AddressForm = ({ title, address, onChange, errors, namePrefix }) => {
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
label="Vorname"
|
||||
label={t ? t('checkout.addressFields.firstName') : 'Vorname'}
|
||||
name="firstName"
|
||||
value={address.firstName}
|
||||
onChange={onChange}
|
||||
@@ -49,7 +50,7 @@ const AddressForm = ({ title, address, onChange, errors, namePrefix }) => {
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
label="Nachname"
|
||||
label={t ? t('checkout.addressFields.lastName') : 'Nachname'}
|
||||
name="lastName"
|
||||
value={address.lastName}
|
||||
onChange={onChange}
|
||||
@@ -62,7 +63,7 @@ const AddressForm = ({ title, address, onChange, errors, namePrefix }) => {
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
label="Adresszusatz"
|
||||
label={t ? t('checkout.addressFields.addressSupplement') : 'Adresszusatz'}
|
||||
name="addressAddition"
|
||||
value={address.addressAddition || ""}
|
||||
onChange={onChange}
|
||||
@@ -70,7 +71,7 @@ const AddressForm = ({ title, address, onChange, errors, namePrefix }) => {
|
||||
InputLabelProps={{ shrink: true }}
|
||||
/>
|
||||
<TextField
|
||||
label="Straße"
|
||||
label={t ? t('checkout.addressFields.street') : 'Straße'}
|
||||
name="street"
|
||||
value={address.street}
|
||||
onChange={onChange}
|
||||
@@ -83,7 +84,7 @@ const AddressForm = ({ title, address, onChange, errors, namePrefix }) => {
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
label="Hausnummer"
|
||||
label={t ? t('checkout.addressFields.houseNumber') : 'Hausnummer'}
|
||||
name="houseNumber"
|
||||
value={address.houseNumber}
|
||||
onChange={onChange}
|
||||
@@ -96,7 +97,7 @@ const AddressForm = ({ title, address, onChange, errors, namePrefix }) => {
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
label="PLZ"
|
||||
label={t ? t('checkout.addressFields.postalCode') : 'PLZ'}
|
||||
name="postalCode"
|
||||
value={address.postalCode}
|
||||
onChange={onChange}
|
||||
@@ -109,7 +110,7 @@ const AddressForm = ({ title, address, onChange, errors, namePrefix }) => {
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
label="Stadt"
|
||||
label={t ? t('checkout.addressFields.city') : 'Stadt'}
|
||||
name="city"
|
||||
value={address.city}
|
||||
onChange={onChange}
|
||||
@@ -122,7 +123,7 @@ const AddressForm = ({ title, address, onChange, errors, namePrefix }) => {
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
label="Land"
|
||||
label={t ? t('checkout.addressFields.country') : 'Land'}
|
||||
name="country"
|
||||
value={address.country}
|
||||
onChange={onChange}
|
||||
@@ -135,4 +136,4 @@ const AddressForm = ({ title, address, onChange, errors, namePrefix }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default AddressForm;
|
||||
export default withI18n()(AddressForm);
|
||||
|
||||
@@ -293,7 +293,7 @@ class CartTab extends Component {
|
||||
};
|
||||
|
||||
validateAddressForm = () => {
|
||||
const errors = CheckoutValidation.validateAddressForm(this.state);
|
||||
const errors = CheckoutValidation.validateAddressForm(this.state, this.props.t);
|
||||
this.setState({ addressFormErrors: errors });
|
||||
return Object.keys(errors).length === 0;
|
||||
};
|
||||
@@ -323,7 +323,7 @@ class CartTab extends Component {
|
||||
handleCompleteOrder = () => {
|
||||
this.setState({ completionError: null }); // Clear previous errors
|
||||
|
||||
const validationError = CheckoutValidation.getValidationErrorMessage(this.state);
|
||||
const validationError = CheckoutValidation.getValidationErrorMessage(this.state, false, this.props.t);
|
||||
if (validationError) {
|
||||
this.setState({ completionError: validationError });
|
||||
this.validateAddressForm(); // To show field-specific errors
|
||||
@@ -440,7 +440,7 @@ class CartTab extends Component {
|
||||
const deliveryCost = this.orderService.getDeliveryCost();
|
||||
const { isPickupOnly, hasStecklinge } = CheckoutValidation.getCartItemFlags(cartItems);
|
||||
|
||||
const preSubmitError = CheckoutValidation.getValidationErrorMessage(this.state);
|
||||
const preSubmitError = CheckoutValidation.getValidationErrorMessage(this.state, false, this.props.t);
|
||||
const displayError = completionError || preSubmitError;
|
||||
|
||||
return (
|
||||
@@ -480,7 +480,7 @@ class CartTab extends Component {
|
||||
{isLoadingStripe ? (
|
||||
<Box sx={{ textAlign: 'center', py: 4 }}>
|
||||
<Typography variant="body1">
|
||||
Zahlungskomponente wird geladen...
|
||||
{this.props.t ? this.props.t('payment.loadingPaymentComponent') : 'Zahlungskomponente wird geladen...'}
|
||||
</Typography>
|
||||
</Box>
|
||||
) : showStripePayment && StripeComponent ? (
|
||||
|
||||
@@ -4,6 +4,7 @@ import AddressForm from "./AddressForm.js";
|
||||
import DeliveryMethodSelector from "./DeliveryMethodSelector.js";
|
||||
import PaymentMethodSelector from "./PaymentMethodSelector.js";
|
||||
import OrderSummary from "./OrderSummary.js";
|
||||
import { withI18n } from "../../i18n/withTranslation.js";
|
||||
|
||||
class CheckoutForm extends Component {
|
||||
render() {
|
||||
@@ -40,7 +41,7 @@ class CheckoutForm extends Component {
|
||||
{paymentMethod !== "cash" && (
|
||||
<>
|
||||
<AddressForm
|
||||
title="Rechnungsadresse"
|
||||
title={this.props.t ? this.props.t('checkout.invoiceAddress') : 'Rechnungsadresse'}
|
||||
address={invoiceAddress}
|
||||
onChange={onInvoiceAddressChange}
|
||||
errors={addressFormErrors}
|
||||
@@ -57,7 +58,7 @@ class CheckoutForm extends Component {
|
||||
}
|
||||
label={
|
||||
<Typography variant="body2">
|
||||
Für zukünftige Bestellungen speichern
|
||||
{this.props.t ? this.props.t('checkout.saveForFuture') : 'Für zukünftige Bestellungen speichern'}
|
||||
</Typography>
|
||||
}
|
||||
sx={{ mb: 2 }}
|
||||
@@ -70,13 +71,12 @@ class CheckoutForm extends Component {
|
||||
variant="body1"
|
||||
sx={{ mb: 2, fontWeight: "bold", color: "#2e7d32" }}
|
||||
>
|
||||
Für welchen Termin ist die Abholung der Stecklinge
|
||||
gewünscht?
|
||||
{this.props.t ? this.props.t('checkout.pickupDate') : 'Für welchen Termin ist die Abholung der Stecklinge gewünscht?'}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
<TextField
|
||||
label="Anmerkung"
|
||||
label={this.props.t ? this.props.t('checkout.note') : 'Anmerkung'}
|
||||
name="note"
|
||||
value={note}
|
||||
onChange={onNoteChange}
|
||||
@@ -108,7 +108,7 @@ class CheckoutForm extends Component {
|
||||
}
|
||||
label={
|
||||
<Typography variant="body1">
|
||||
Lieferadresse ist identisch mit Rechnungsadresse
|
||||
{this.props.t ? this.props.t('checkout.sameAddress') : 'Lieferadresse ist identisch mit Rechnungsadresse'}
|
||||
</Typography>
|
||||
}
|
||||
sx={{ mb: 2 }}
|
||||
@@ -116,7 +116,7 @@ class CheckoutForm extends Component {
|
||||
|
||||
{!useSameAddress && (
|
||||
<AddressForm
|
||||
title="Lieferadresse"
|
||||
title={this.props.t ? this.props.t('checkout.deliveryAddress') : 'Lieferadresse'}
|
||||
address={deliveryAddress}
|
||||
onChange={onDeliveryAddressChange}
|
||||
errors={addressFormErrors}
|
||||
@@ -151,8 +151,7 @@ class CheckoutForm extends Component {
|
||||
}
|
||||
label={
|
||||
<Typography variant="body2">
|
||||
Ich habe die AGBs, die Datenschutzerklärung und die
|
||||
Bestimmungen zum Widerrufsrecht gelesen
|
||||
{this.props.t ? this.props.t('checkout.termsAccept') : 'Ich habe die AGBs, die Datenschutzerklärung und die Bestimmungen zum Widerrufsrecht gelesen'}
|
||||
</Typography>
|
||||
}
|
||||
sx={{ mb: 3, mt: 2 }}
|
||||
@@ -175,12 +174,12 @@ class CheckoutForm extends Component {
|
||||
disabled={isCompletingOrder || !!preSubmitError}
|
||||
>
|
||||
{isCompletingOrder
|
||||
? "Bestellung wird verarbeitet..."
|
||||
: "Bestellung abschließen"}
|
||||
? (this.props.t ? this.props.t('checkout.processingOrder') : 'Bestellung wird verarbeitet...')
|
||||
: (this.props.t ? this.props.t('checkout.completeOrder') : 'Bestellung abschließen')}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default CheckoutForm;
|
||||
export default withI18n()(CheckoutForm);
|
||||
@@ -1,5 +1,5 @@
|
||||
class CheckoutValidation {
|
||||
static validateAddressForm(state) {
|
||||
static validateAddressForm(state, t = null) {
|
||||
const {
|
||||
invoiceAddress,
|
||||
deliveryAddress,
|
||||
@@ -12,15 +12,15 @@ class CheckoutValidation {
|
||||
// Validate invoice address (skip if payment method is "cash")
|
||||
if (paymentMethod !== "cash") {
|
||||
if (!invoiceAddress.firstName)
|
||||
errors.invoiceFirstName = "Vorname erforderlich";
|
||||
errors.invoiceFirstName = t ? t('checkout.validationErrors.firstNameRequired') : "Vorname erforderlich";
|
||||
if (!invoiceAddress.lastName)
|
||||
errors.invoiceLastName = "Nachname erforderlich";
|
||||
if (!invoiceAddress.street) errors.invoiceStreet = "Straße erforderlich";
|
||||
errors.invoiceLastName = t ? t('checkout.validationErrors.lastNameRequired') : "Nachname erforderlich";
|
||||
if (!invoiceAddress.street) errors.invoiceStreet = t ? t('checkout.validationErrors.streetRequired') : "Straße erforderlich";
|
||||
if (!invoiceAddress.houseNumber)
|
||||
errors.invoiceHouseNumber = "Hausnummer erforderlich";
|
||||
errors.invoiceHouseNumber = t ? t('checkout.validationErrors.houseNumberRequired') : "Hausnummer erforderlich";
|
||||
if (!invoiceAddress.postalCode)
|
||||
errors.invoicePostalCode = "PLZ erforderlich";
|
||||
if (!invoiceAddress.city) errors.invoiceCity = "Stadt erforderlich";
|
||||
errors.invoicePostalCode = t ? t('checkout.validationErrors.postalCodeRequired') : "PLZ erforderlich";
|
||||
if (!invoiceAddress.city) errors.invoiceCity = t ? t('checkout.validationErrors.cityRequired') : "Stadt erforderlich";
|
||||
}
|
||||
|
||||
// Validate delivery address for shipping methods that require it
|
||||
@@ -29,37 +29,37 @@ class CheckoutValidation {
|
||||
(deliveryMethod === "DHL" || deliveryMethod === "DPD")
|
||||
) {
|
||||
if (!deliveryAddress.firstName)
|
||||
errors.deliveryFirstName = "Vorname erforderlich";
|
||||
errors.deliveryFirstName = t ? t('checkout.validationErrors.firstNameRequired') : "Vorname erforderlich";
|
||||
if (!deliveryAddress.lastName)
|
||||
errors.deliveryLastName = "Nachname erforderlich";
|
||||
errors.deliveryLastName = t ? t('checkout.validationErrors.lastNameRequired') : "Nachname erforderlich";
|
||||
if (!deliveryAddress.street)
|
||||
errors.deliveryStreet = "Straße erforderlich";
|
||||
errors.deliveryStreet = t ? t('checkout.validationErrors.streetRequired') : "Straße erforderlich";
|
||||
if (!deliveryAddress.houseNumber)
|
||||
errors.deliveryHouseNumber = "Hausnummer erforderlich";
|
||||
errors.deliveryHouseNumber = t ? t('checkout.validationErrors.houseNumberRequired') : "Hausnummer erforderlich";
|
||||
if (!deliveryAddress.postalCode)
|
||||
errors.deliveryPostalCode = "PLZ erforderlich";
|
||||
if (!deliveryAddress.city) errors.deliveryCity = "Stadt erforderlich";
|
||||
errors.deliveryPostalCode = t ? t('checkout.validationErrors.postalCodeRequired') : "PLZ erforderlich";
|
||||
if (!deliveryAddress.city) errors.deliveryCity = t ? t('checkout.validationErrors.cityRequired') : "Stadt erforderlich";
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
static getValidationErrorMessage(state, isAddressOnly = false) {
|
||||
static getValidationErrorMessage(state, isAddressOnly = false, t = null) {
|
||||
const { termsAccepted } = state;
|
||||
|
||||
const addressErrors = this.validateAddressForm(state);
|
||||
const addressErrors = this.validateAddressForm(state, t);
|
||||
|
||||
if (isAddressOnly) {
|
||||
return addressErrors;
|
||||
}
|
||||
|
||||
if (Object.keys(addressErrors).length > 0) {
|
||||
return "Bitte überprüfen Sie Ihre Eingaben in den Adressfeldern.";
|
||||
return t ? t('checkout.addressValidationError') : "Bitte überprüfen Sie Ihre Eingaben in den Adressfeldern.";
|
||||
}
|
||||
|
||||
// Validate terms acceptance
|
||||
if (!termsAccepted) {
|
||||
return "Bitte akzeptieren Sie die AGBs, Datenschutzerklärung und Widerrufsrecht, um fortzufahren.";
|
||||
return t ? t('checkout.termsValidationError') : "Bitte akzeptieren Sie die AGBs, Datenschutzerklärung und Widerrufsrecht, um fortzufahren.";
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -30,7 +30,7 @@ const DeliveryMethodSelector = ({ deliveryMethod, onChange, isPickupOnly, cartIt
|
||||
},
|
||||
{
|
||||
id: 'Sperrgut',
|
||||
name: 'Sperrgut',
|
||||
name: t ? t('delivery.methods.sperrgutName') : 'Sperrgut',
|
||||
description: t ? t('delivery.descriptions.bulky') : 'Für große und schwere Artikel',
|
||||
price: t ? t('delivery.prices.sperrgut') : '28,99 €',
|
||||
disabled: true,
|
||||
|
||||
@@ -55,10 +55,10 @@ const OrderDetailsDialog = ({ open, onClose, order }) => {
|
||||
|
||||
return (
|
||||
<Dialog open={open} onClose={onClose} maxWidth="md" fullWidth>
|
||||
<DialogTitle>Bestelldetails: {order.orderId}</DialogTitle>
|
||||
<DialogTitle>{t('orders.details.title', { orderId: order.orderId })}</DialogTitle>
|
||||
<DialogContent>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="h6">Lieferadresse</Typography>
|
||||
<Typography variant="h6">{t('orders.details.deliveryAddress')}</Typography>
|
||||
<Typography>{order.shipping_address_name}</Typography>
|
||||
<Typography>{order.shipping_address_street} {order.shipping_address_house_number}</Typography>
|
||||
<Typography>{order.shipping_address_postal_code} {order.shipping_address_city}</Typography>
|
||||
@@ -66,7 +66,7 @@ const OrderDetailsDialog = ({ open, onClose, order }) => {
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="h6">Rechnungsadresse</Typography>
|
||||
<Typography variant="h6">{t('orders.details.invoiceAddress')}</Typography>
|
||||
<Typography>{order.invoice_address_name}</Typography>
|
||||
<Typography>{order.invoice_address_street} {order.invoice_address_house_number}</Typography>
|
||||
<Typography>{order.invoice_address_postal_code} {order.invoice_address_city}</Typography>
|
||||
@@ -75,28 +75,28 @@ const OrderDetailsDialog = ({ open, onClose, order }) => {
|
||||
|
||||
{/* Order Details Section */}
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="h6" gutterBottom>Bestelldetails</Typography>
|
||||
<Typography variant="h6" gutterBottom>{t('orders.details.orderDetails')}</Typography>
|
||||
<Box sx={{ display: 'flex', gap: 4 }}>
|
||||
<Box>
|
||||
<Typography variant="body2" color="text.secondary">Lieferart:</Typography>
|
||||
<Typography variant="body1">{order.deliveryMethod || order.delivery_method || 'Nicht angegeben'}</Typography>
|
||||
<Typography variant="body2" color="text.secondary">{t('orders.details.deliveryMethod')}</Typography>
|
||||
<Typography variant="body1">{order.deliveryMethod || order.delivery_method || t('orders.details.notSpecified')}</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="body2" color="text.secondary">Zahlungsart:</Typography>
|
||||
<Typography variant="body1">{order.paymentMethod || order.payment_method || 'Nicht angegeben'}</Typography>
|
||||
<Typography variant="body2" color="text.secondary">{t('orders.details.paymentMethod')}</Typography>
|
||||
<Typography variant="body1">{order.paymentMethod || order.payment_method || t('orders.details.notSpecified')}</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Typography variant="h6" gutterBottom>Bestellte Artikel</Typography>
|
||||
<Typography variant="h6" gutterBottom>{t('orders.details.orderedItems')}</Typography>
|
||||
<TableContainer component={Paper}>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Artikel</TableCell>
|
||||
<TableCell align="right">Menge</TableCell>
|
||||
<TableCell align="right">Preis</TableCell>
|
||||
<TableCell align="right">Gesamt</TableCell>
|
||||
<TableCell>{t('orders.details.item')}</TableCell>
|
||||
<TableCell align="right">{t('orders.details.quantity')}</TableCell>
|
||||
<TableCell align="right">{t('orders.details.price')}</TableCell>
|
||||
<TableCell align="right">{t('orders.details.total')}</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
@@ -162,7 +162,7 @@ const OrderDetailsDialog = ({ open, onClose, order }) => {
|
||||
<DialogActions>
|
||||
{order.status === 'new' && (
|
||||
<Button onClick={handleCancelOrder} color="error">
|
||||
Bestellung stornieren
|
||||
{t('orders.details.cancelOrder')}
|
||||
</Button>
|
||||
)}
|
||||
<Button onClick={onClose}>{t ? t('common.close') : 'Schließen'}</Button>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useState, useEffect, useContext, useCallback } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { withI18n } from "../../i18n/withTranslation.js";
|
||||
import {
|
||||
Box,
|
||||
Paper,
|
||||
@@ -20,13 +21,16 @@ import SocketContext from "../../contexts/SocketContext.js";
|
||||
import OrderDetailsDialog from "./OrderDetailsDialog.js";
|
||||
|
||||
// Constants
|
||||
const statusTranslations = {
|
||||
new: "in Bearbeitung",
|
||||
pending: "Neu",
|
||||
processing: "in Bearbeitung",
|
||||
cancelled: "Storniert",
|
||||
shipped: "Verschickt",
|
||||
delivered: "Geliefert",
|
||||
const getStatusTranslation = (status, t) => {
|
||||
const statusMap = {
|
||||
new: t ? t('orders.status.new') : "in Bearbeitung",
|
||||
pending: t ? t('orders.status.pending') : "Neu",
|
||||
processing: t ? t('orders.status.processing') : "in Bearbeitung",
|
||||
cancelled: t ? t('orders.status.cancelled') : "Storniert",
|
||||
shipped: t ? t('orders.status.shipped') : "Verschickt",
|
||||
delivered: t ? t('orders.status.delivered') : "Geliefert",
|
||||
};
|
||||
return statusMap[status] || status;
|
||||
};
|
||||
|
||||
const statusEmojis = {
|
||||
@@ -61,7 +65,7 @@ const currencyFormatter = new Intl.NumberFormat("de-DE", {
|
||||
});
|
||||
|
||||
// Orders Tab Content Component
|
||||
const OrdersTab = ({ orderIdFromHash }) => {
|
||||
const OrdersTab = ({ orderIdFromHash, t }) => {
|
||||
const [orders, setOrders] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
@@ -120,7 +124,7 @@ const OrdersTab = ({ orderIdFromHash }) => {
|
||||
}, [orderIdFromHash, orders, handleViewDetails]);
|
||||
|
||||
const getStatusDisplay = (status) => {
|
||||
return statusTranslations[status] || status;
|
||||
return getStatusTranslation(status, t);
|
||||
};
|
||||
|
||||
const getStatusEmoji = (status) => {
|
||||
@@ -160,12 +164,12 @@ const OrdersTab = ({ orderIdFromHash }) => {
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Bestellnummer</TableCell>
|
||||
<TableCell>Datum</TableCell>
|
||||
<TableCell>Status</TableCell>
|
||||
<TableCell>Artikel</TableCell>
|
||||
<TableCell align="right">Summe</TableCell>
|
||||
<TableCell align="center">Aktionen</TableCell>
|
||||
<TableCell>{t ? t('orders.table.orderNumber') : 'Bestellnummer'}</TableCell>
|
||||
<TableCell>{t ? t('orders.table.date') : 'Datum'}</TableCell>
|
||||
<TableCell>{t ? t('orders.table.status') : 'Status'}</TableCell>
|
||||
<TableCell>{t ? t('orders.table.items') : 'Artikel'}</TableCell>
|
||||
<TableCell align="right">{t ? t('orders.table.total') : 'Summe'}</TableCell>
|
||||
<TableCell align="center">{t ? t('orders.table.actions') : 'Aktionen'}</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
@@ -231,7 +235,7 @@ const OrdersTab = ({ orderIdFromHash }) => {
|
||||
</TableContainer>
|
||||
) : (
|
||||
<Alert severity="info">
|
||||
Sie haben noch keine Bestellungen aufgegeben.
|
||||
{t ? t('orders.noOrders') : 'Sie haben noch keine Bestellungen aufgegeben.'}
|
||||
</Alert>
|
||||
)}
|
||||
<OrderDetailsDialog
|
||||
@@ -243,4 +247,4 @@ const OrdersTab = ({ orderIdFromHash }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default OrdersTab;
|
||||
export default withI18n()(OrdersTab);
|
||||
|
||||
@@ -54,7 +54,7 @@ class PaymentConfirmationDialog extends Component {
|
||||
|
||||
{isCompletingOrder && (
|
||||
<Typography variant="body2" sx={{ mt: 2, color: '#2e7d32', p: 2, bgcolor: '#e8f5e8', borderRadius: 1 }}>
|
||||
Bestellung wird abgeschlossen...
|
||||
{this.props.t ? this.props.t('orders.processing') : 'Bestellung wird abgeschlossen...'}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React, { useEffect, useCallback } from "react";
|
||||
import { Box, Typography, Radio } from "@mui/material";
|
||||
import { withI18n } from "../../i18n/withTranslation.js";
|
||||
|
||||
const PaymentMethodSelector = ({ paymentMethod, onChange, deliveryMethod, onDeliveryMethodChange, cartItems = [], deliveryCost = 0 }) => {
|
||||
const PaymentMethodSelector = ({ paymentMethod, onChange, deliveryMethod, onDeliveryMethodChange, cartItems = [], deliveryCost = 0, t }) => {
|
||||
|
||||
// Calculate total amount
|
||||
const subtotal = cartItems.reduce((total, item) => total + item.price * item.quantity, 0);
|
||||
@@ -38,8 +39,8 @@ const PaymentMethodSelector = ({ paymentMethod, onChange, deliveryMethod, onDeli
|
||||
const paymentOptions = [
|
||||
{
|
||||
id: "wire",
|
||||
name: "Überweisung",
|
||||
description: "Bezahlen Sie per Banküberweisung",
|
||||
name: t ? t('payment.methods.bankTransfer') : "Überweisung",
|
||||
description: t ? t('payment.methods.bankTransferDescription') : "Bezahlen Sie per Banküberweisung",
|
||||
disabled: totalAmount === 0,
|
||||
},
|
||||
/*{
|
||||
@@ -58,10 +59,10 @@ const PaymentMethodSelector = ({ paymentMethod, onChange, deliveryMethod, onDeli
|
||||
},*/
|
||||
{
|
||||
id: "mollie",
|
||||
name: "Karte, Sofortüberweisung, Apple Pay, Google Pay, PayPal",
|
||||
name: t ? t('payment.methods.cardPayment') : "Karte, Sofortüberweisung, Apple Pay, Google Pay, PayPal",
|
||||
description: totalAmount < 0.50 && totalAmount > 0
|
||||
? "Bezahlen Sie per Karte oder Sofortüberweisung (Mindestbetrag: 0,50 €)"
|
||||
: "Bezahlen Sie per Karte oder Sofortüberweisung",
|
||||
? (t ? t('payment.methods.cardPaymentMinAmount') : "Bezahlen Sie per Karte oder Sofortüberweisung (Mindestbetrag: 0,50 €)")
|
||||
: (t ? t('payment.methods.cardPaymentDescription') : "Bezahlen Sie per Karte oder Sofortüberweisung"),
|
||||
disabled: totalAmount < 0.50 || (deliveryMethod !== "DHL" && deliveryMethod !== "DPD" && deliveryMethod !== "Abholung"),
|
||||
icons: [
|
||||
"/assets/images/giropay.png",
|
||||
@@ -72,15 +73,15 @@ const PaymentMethodSelector = ({ paymentMethod, onChange, deliveryMethod, onDeli
|
||||
},
|
||||
{
|
||||
id: "onDelivery",
|
||||
name: "Nachnahme",
|
||||
description: "Bezahlen Sie bei Lieferung (8,99 € Aufschlag)",
|
||||
name: t ? t('payment.methods.cashOnDelivery') : "Nachnahme",
|
||||
description: t ? t('payment.methods.cashOnDeliveryDescription') : "Bezahlen Sie bei Lieferung (8,99 € Aufschlag)",
|
||||
disabled: totalAmount === 0 || deliveryMethod !== "DHL",
|
||||
icons: ["/assets/images/cash.png"],
|
||||
},
|
||||
{
|
||||
id: "cash",
|
||||
name: "Zahlung in der Filiale",
|
||||
description: "Bei Abholung bezahlen",
|
||||
name: t ? t('payment.methods.cashInStore') : "Zahlung in der Filiale",
|
||||
description: t ? t('payment.methods.cashInStoreDescription') : "Bei Abholung bezahlen",
|
||||
disabled: false, // Always enabled
|
||||
icons: ["/assets/images/cash.png"],
|
||||
},
|
||||
@@ -89,7 +90,7 @@ const PaymentMethodSelector = ({ paymentMethod, onChange, deliveryMethod, onDeli
|
||||
return (
|
||||
<>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Zahlungsart wählen
|
||||
{t ? t('payment.methods.selectPaymentMethod') : 'Zahlungsart wählen'}
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ mb: 3 }}>
|
||||
@@ -189,4 +190,4 @@ const PaymentMethodSelector = ({ paymentMethod, onChange, deliveryMethod, onDeli
|
||||
);
|
||||
};
|
||||
|
||||
export default PaymentMethodSelector;
|
||||
export default withI18n()(PaymentMethodSelector);
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
Snackbar
|
||||
} from '@mui/material';
|
||||
import { ContentCopy } from '@mui/icons-material';
|
||||
import { withI18n } from '../../i18n/withTranslation.js';
|
||||
|
||||
class SettingsTab extends Component {
|
||||
constructor(props) {
|
||||
@@ -72,17 +73,17 @@ class SettingsTab extends Component {
|
||||
|
||||
// Validation
|
||||
if (!this.state.currentPassword || !this.state.newPassword || !this.state.confirmPassword) {
|
||||
this.setState({ passwordError: 'Bitte füllen Sie alle Felder aus' });
|
||||
this.setState({ passwordError: this.props.t ? this.props.t('settings.errors.fillAllFields') : 'Bitte füllen Sie alle Felder aus' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.state.newPassword !== this.state.confirmPassword) {
|
||||
this.setState({ passwordError: 'Die neuen Passwörter stimmen nicht überein' });
|
||||
this.setState({ passwordError: this.props.t ? this.props.t('settings.errors.passwordsNotMatch') : 'Die neuen Passwörter stimmen nicht überein' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.state.newPassword.length < 8) {
|
||||
this.setState({ passwordError: 'Das neue Passwort muss mindestens 8 Zeichen lang sein' });
|
||||
this.setState({ passwordError: this.props.t ? this.props.t('settings.errors.passwordTooShort') : 'Das neue Passwort muss mindestens 8 Zeichen lang sein' });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -96,14 +97,14 @@ class SettingsTab extends Component {
|
||||
|
||||
if (response.success) {
|
||||
this.setState({
|
||||
passwordSuccess: 'Passwort erfolgreich aktualisiert',
|
||||
passwordSuccess: this.props.t ? this.props.t('settings.success.passwordUpdated') : 'Passwort erfolgreich aktualisiert',
|
||||
currentPassword: '',
|
||||
newPassword: '',
|
||||
confirmPassword: ''
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
passwordError: response.message || 'Fehler beim Aktualisieren des Passworts'
|
||||
passwordError: response.message || (this.props.t ? this.props.t('settings.errors.passwordUpdateError') : 'Fehler beim Aktualisieren des Passworts')
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -121,12 +122,12 @@ class SettingsTab extends Component {
|
||||
|
||||
// Validation
|
||||
if (!this.state.password || !this.state.newEmail) {
|
||||
this.setState({ emailError: 'Bitte füllen Sie alle Felder aus' });
|
||||
this.setState({ emailError: this.props.t ? this.props.t('settings.errors.fillAllFields') : 'Bitte füllen Sie alle Felder aus' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.state.newEmail)) {
|
||||
this.setState({ emailError: 'Bitte geben Sie eine gültige E-Mail-Adresse ein' });
|
||||
this.setState({ emailError: this.props.t ? this.props.t('settings.errors.invalidEmail') : 'Bitte geben Sie eine gültige E-Mail-Adresse ein' });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -140,7 +141,7 @@ class SettingsTab extends Component {
|
||||
|
||||
if (response.success) {
|
||||
this.setState({
|
||||
emailSuccess: 'E-Mail-Adresse erfolgreich aktualisiert',
|
||||
emailSuccess: this.props.t ? this.props.t('settings.success.emailUpdated') : 'E-Mail-Adresse erfolgreich aktualisiert',
|
||||
password: ''
|
||||
});
|
||||
|
||||
@@ -157,7 +158,7 @@ class SettingsTab extends Component {
|
||||
}
|
||||
} else {
|
||||
this.setState({
|
||||
emailError: response.message || 'Fehler beim Aktualisieren der E-Mail-Adresse'
|
||||
emailError: response.message || (this.props.t ? this.props.t('settings.errors.emailUpdateError') : 'Fehler beim Aktualisieren der E-Mail-Adresse')
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -238,7 +239,7 @@ class SettingsTab extends Component {
|
||||
<Box sx={{ p: { xs: 1, sm: 3 } }}>
|
||||
<Paper sx={{ p: { xs: 2, sm: 3 } }}>
|
||||
<Typography variant="subtitle1" fontWeight="bold" gutterBottom>
|
||||
Passwort ändern
|
||||
{this.props.t ? this.props.t('settings.changePassword') : 'Passwort ändern'}
|
||||
</Typography>
|
||||
|
||||
{this.state.passwordError && <Alert severity="error" sx={{ mb: 2 }}>{this.state.passwordError}</Alert>}
|
||||
@@ -247,7 +248,7 @@ class SettingsTab extends Component {
|
||||
<Box component="form" onSubmit={this.handleUpdatePassword}>
|
||||
<TextField
|
||||
margin="normal"
|
||||
label="Aktuelles Passwort"
|
||||
label={this.props.t ? this.props.t('settings.currentPassword') : 'Aktuelles Passwort'}
|
||||
type="password"
|
||||
fullWidth
|
||||
value={this.state.currentPassword}
|
||||
@@ -257,7 +258,7 @@ class SettingsTab extends Component {
|
||||
|
||||
<TextField
|
||||
margin="normal"
|
||||
label="Neues Passwort"
|
||||
label={this.props.t ? this.props.t('settings.newPassword') : 'Neues Passwort'}
|
||||
type="password"
|
||||
fullWidth
|
||||
value={this.state.newPassword}
|
||||
@@ -267,7 +268,7 @@ class SettingsTab extends Component {
|
||||
|
||||
<TextField
|
||||
margin="normal"
|
||||
label="Neues Passwort bestätigen"
|
||||
label={this.props.t ? this.props.t('settings.confirmNewPassword') : 'Neues Passwort bestätigen'}
|
||||
type="password"
|
||||
fullWidth
|
||||
value={this.state.confirmPassword}
|
||||
@@ -282,7 +283,7 @@ class SettingsTab extends Component {
|
||||
sx={{ mt: 2, bgcolor: '#2e7d32', '&:hover': { bgcolor: '#1b5e20' } }}
|
||||
disabled={this.state.loading}
|
||||
>
|
||||
{this.state.loading ? <CircularProgress size={24} /> : 'Passwort aktualisieren'}
|
||||
{this.state.loading ? <CircularProgress size={24} /> : (this.props.t ? this.props.t('settings.updatePassword') : 'Passwort aktualisieren')}
|
||||
</Button>
|
||||
</Box>
|
||||
</Paper>
|
||||
@@ -291,7 +292,7 @@ class SettingsTab extends Component {
|
||||
|
||||
<Paper sx={{ p: { xs: 2, sm: 3 } }}>
|
||||
<Typography variant="subtitle1" fontWeight="bold" gutterBottom>
|
||||
E-Mail-Adresse ändern
|
||||
{this.props.t ? this.props.t('settings.changeEmail') : 'E-Mail-Adresse ändern'}
|
||||
</Typography>
|
||||
|
||||
{this.state.emailError && <Alert severity="error" sx={{ mb: 2 }}>{this.state.emailError}</Alert>}
|
||||
@@ -300,7 +301,7 @@ class SettingsTab extends Component {
|
||||
<Box component="form" onSubmit={this.handleUpdateEmail}>
|
||||
<TextField
|
||||
margin="normal"
|
||||
label="Passwort"
|
||||
label={this.props.t ? this.props.t('settings.password') : 'Passwort'}
|
||||
type="password"
|
||||
fullWidth
|
||||
value={this.state.password}
|
||||
@@ -310,7 +311,7 @@ class SettingsTab extends Component {
|
||||
|
||||
<TextField
|
||||
margin="normal"
|
||||
label="Neue E-Mail-Adresse"
|
||||
label={this.props.t ? this.props.t('settings.newEmail') : 'Neue E-Mail-Adresse'}
|
||||
type="email"
|
||||
fullWidth
|
||||
value={this.state.newEmail}
|
||||
@@ -325,7 +326,7 @@ class SettingsTab extends Component {
|
||||
sx={{ mt: 2, bgcolor: '#2e7d32', '&:hover': { bgcolor: '#1b5e20' } }}
|
||||
disabled={this.state.loading}
|
||||
>
|
||||
{this.state.loading ? <CircularProgress size={24} /> : 'E-Mail aktualisieren'}
|
||||
{this.state.loading ? <CircularProgress size={24} /> : (this.props.t ? this.props.t('settings.updateEmail') : 'E-Mail aktualisieren')}
|
||||
</Button>
|
||||
</Box>
|
||||
</Paper>
|
||||
@@ -334,11 +335,11 @@ class SettingsTab extends Component {
|
||||
|
||||
<Paper sx={{ p: { xs: 2, sm: 3 } }}>
|
||||
<Typography variant="subtitle1" fontWeight="bold" gutterBottom>
|
||||
API-Schlüssel
|
||||
{this.props.t ? this.props.t('settings.apiKey') : 'API-Schlüssel'}
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" color="text.secondary" gutterBottom>
|
||||
Verwenden Sie Ihren API-Schlüssel für die Integration mit externen Anwendungen.
|
||||
{this.props.t ? this.props.t('settings.apiKeyDescription') : 'Verwenden Sie Ihren API-Schlüssel für die Integration mit externen Anwendungen.'}
|
||||
</Typography>
|
||||
|
||||
{this.state.apiKeyError && <Alert severity="error" sx={{ mb: 2 }}>{this.state.apiKeyError}</Alert>}
|
||||
@@ -347,14 +348,14 @@ class SettingsTab extends Component {
|
||||
{this.state.apiKeySuccess}
|
||||
{this.state.apiKey && this.state.apiKeyDisplay !== '************' && (
|
||||
<Typography variant="body2" sx={{ mt: 1 }}>
|
||||
Speichern Sie diesen Schlüssel sicher. Er wird aus Sicherheitsgründen in 10 Sekunden ausgeblendet.
|
||||
{this.props.t ? this.props.t('settings.success.apiKeyWarning') : 'Speichern Sie diesen Schlüssel sicher. Er wird aus Sicherheitsgründen in 10 Sekunden ausgeblendet.'}
|
||||
</Typography>
|
||||
)}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Typography variant="body2" sx={{ mb: 2 }}>
|
||||
API-Dokumentation: {' '}
|
||||
{this.props.t ? this.props.t('settings.apiDocumentation') : 'API-Dokumentation:'} {' '}
|
||||
<a
|
||||
href={`${window.location.protocol}//${window.location.host}/api/`}
|
||||
target="_blank"
|
||||
@@ -367,7 +368,7 @@ class SettingsTab extends Component {
|
||||
|
||||
<Box sx={{ display: 'flex', gap: 2, alignItems: 'center', mt: 2 }}>
|
||||
<TextField
|
||||
label="API-Schlüssel"
|
||||
label={this.props.t ? this.props.t('settings.apiKey') : 'API-Schlüssel'}
|
||||
value={this.state.apiKeyDisplay}
|
||||
disabled
|
||||
fullWidth
|
||||
@@ -385,7 +386,7 @@ class SettingsTab extends Component {
|
||||
color: '#2e7d32',
|
||||
'&:hover': { bgcolor: 'rgba(46, 125, 50, 0.1)' }
|
||||
}}
|
||||
title="In Zwischenablage kopieren"
|
||||
title={this.props.t ? this.props.t('settings.copyToClipboard') : 'In Zwischenablage kopieren'}
|
||||
>
|
||||
<ContentCopy />
|
||||
</IconButton>
|
||||
@@ -405,7 +406,7 @@ class SettingsTab extends Component {
|
||||
{this.state.loadingApiKey ? (
|
||||
<CircularProgress size={24} />
|
||||
) : (
|
||||
this.state.hasApiKey ? 'Regenerieren' : 'Generieren'
|
||||
this.state.hasApiKey ? (this.props.t ? this.props.t('settings.regenerate') : 'Regenerieren') : (this.props.t ? this.props.t('settings.generate') : 'Generieren')
|
||||
)}
|
||||
</Button>
|
||||
</Box>
|
||||
@@ -415,7 +416,7 @@ class SettingsTab extends Component {
|
||||
open={this.state.copySnackbarOpen}
|
||||
autoHideDuration={3000}
|
||||
onClose={this.handleCloseSnackbar}
|
||||
message="API-Schlüssel in Zwischenablage kopiert"
|
||||
message={this.props.t ? this.props.t('settings.apiKeyCopied') : 'API-Schlüssel in Zwischenablage kopiert'}
|
||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
|
||||
/>
|
||||
</Box>
|
||||
@@ -423,4 +424,4 @@ class SettingsTab extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
export default SettingsTab;
|
||||
export default withI18n()(SettingsTab);
|
||||
Reference in New Issue
Block a user