feat: Integrate Girocode functionality for wire payments in OrdersTab and LoginComponent, enhance user experience with pending payment notifications, and update translations across multiple locales

This commit is contained in:
sebseb7
2026-03-24 00:48:22 +01:00
parent f47fbc5c39
commit a9bf1aee5f
31 changed files with 969 additions and 93 deletions

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect, useCallback } from "react";
import React, { useState, useEffect, useCallback, Fragment } from "react";
import { useNavigate } from "react-router-dom";
import { withI18n } from "../../i18n/withTranslation.js";
import {
@@ -24,6 +24,12 @@ import {
import SearchIcon from "@mui/icons-material/Search";
import CancelIcon from "@mui/icons-material/Cancel";
import OrderDetailsDialog from "./OrderDetailsDialog.js";
import WireOrderGirocode from "./WireOrderGirocode.js";
import {
isWireGirocodeEligible,
hasPendingWirePaymentOrder,
WIRE_PAYMENT_PENDING_EVENT,
} from "../../utils/wireGirocodeEligibility.js";
// Constants
const getStatusTranslation = (status, t) => {
@@ -100,8 +106,20 @@ const OrdersTab = ({ orderIdFromHash, t }) => {
window.socketManager.emit("getOrders", (response) => {
if (response.success) {
setOrders(response.orders);
window.dispatchEvent(
new CustomEvent(WIRE_PAYMENT_PENDING_EVENT, {
detail: {
pending: hasPendingWirePaymentOrder(response.orders),
},
})
);
} else {
setError(response.error || "Failed to fetch orders.");
window.dispatchEvent(
new CustomEvent(WIRE_PAYMENT_PENDING_EVENT, {
detail: { pending: false },
})
);
}
setLoading(false);
});
@@ -216,89 +234,107 @@ const OrdersTab = ({ orderIdFromHash, t }) => {
0
);
return (
<TableRow key={order.orderId} hover>
<TableCell>{order.orderId}</TableCell>
<TableCell>
{new Date(order.created_at).toLocaleDateString()}
</TableCell>
<TableCell>
<Box
sx={{
display: "flex",
alignItems: "center",
gap: "8px",
color: getStatusColor(order.status),
}}
>
<span style={{ fontSize: "1.2rem" }}>
{getStatusEmoji(order.status)}
</span>
<Typography
variant="body2"
component="span"
sx={{ fontWeight: "medium" }}
<Fragment key={order.orderId}>
<TableRow hover>
<TableCell>{order.orderId}</TableCell>
<TableCell>
{new Date(order.created_at).toLocaleDateString()}
</TableCell>
<TableCell>
<Box
sx={{
display: "flex",
alignItems: "center",
gap: "8px",
color: getStatusColor(order.status),
}}
>
{displayStatus}
</Typography>
</Box>
{order.delivery_method === 'DHL' && order.trackingCode && (
<Box sx={{ mt: 0.5 }}>
<a
href={`https://www.dhl.de/de/privatkunden/dhl-sendungsverfolgung.html?piececode=${order.trackingCode}`}
target="_blank"
rel="noopener noreferrer"
style={{ fontSize: '0.85rem', color: '#d40511' }}
<span style={{ fontSize: "1.2rem" }}>
{getStatusEmoji(order.status)}
</span>
<Typography
variant="body2"
component="span"
sx={{ fontWeight: "medium" }}
>
📦 {t ? t('orders.trackShipment') : 'Sendung verfolgen'}
</a>
{displayStatus}
</Typography>
</Box>
)}
</TableCell>
<TableCell>
{order.items
.filter(item => {
// Exclude delivery items - backend uses deliveryMethod ID as item name
const itemName = item.name || '';
return itemName !== 'DHL' &&
itemName !== 'DPD' &&
itemName !== 'Sperrgut' &&
itemName !== 'Abholung';
})
.reduce(
(acc, item) => acc + item.quantity_ordered,
0
{order.delivery_method === 'DHL' && order.trackingCode && (
<Box sx={{ mt: 0.5 }}>
<a
href={`https://www.dhl.de/de/privatkunden/dhl-sendungsverfolgung.html?piececode=${order.trackingCode}`}
target="_blank"
rel="noopener noreferrer"
style={{ fontSize: '0.85rem', color: '#d40511' }}
>
📦 {t ? t('orders.trackShipment') : 'Sendung verfolgen'}
</a>
</Box>
)}
</TableCell>
<TableCell align="right">
{currencyFormatter.format(total)}
</TableCell>
<TableCell align="center">
<Box sx={{ display: 'flex', gap: 1, justifyContent: 'center' }}>
<Tooltip title={t ? t('orders.tooltips.viewDetails') : 'Details anzeigen'}>
<IconButton
size="small"
color="primary"
onClick={() => handleViewDetails(order.orderId)}
aria-label={t ? t('orders.tooltips.viewDetails') : 'Details anzeigen'}
>
<SearchIcon />
</IconButton>
</Tooltip>
{isOrderCancelable(order) && (
<Tooltip title={t ? t('orders.tooltips.cancelOrder') : 'Bestellung stornieren'}>
</TableCell>
<TableCell>
{order.items
.filter(item => {
// Exclude delivery items - backend uses deliveryMethod ID as item name
const itemName = item.name || '';
return itemName !== 'DHL' &&
itemName !== 'DPD' &&
itemName !== 'Sperrgut' &&
itemName !== 'Abholung';
})
.reduce(
(acc, item) => acc + item.quantity_ordered,
0
)}
</TableCell>
<TableCell align="right">
{currencyFormatter.format(total)}
</TableCell>
<TableCell align="center">
<Box sx={{ display: 'flex', gap: 1, justifyContent: 'center' }}>
<Tooltip title={t ? t('orders.tooltips.viewDetails') : 'Details anzeigen'}>
<IconButton
size="small"
color="error"
onClick={() => handleCancelClick(order)}
aria-label={t ? t('orders.tooltips.cancelOrder') : 'Bestellung stornieren'}
color="primary"
onClick={() => handleViewDetails(order.orderId)}
aria-label={t ? t('orders.tooltips.viewDetails') : 'Details anzeigen'}
>
<CancelIcon />
<SearchIcon />
</IconButton>
</Tooltip>
)}
</Box>
</TableCell>
</TableRow>
{isOrderCancelable(order) && (
<Tooltip title={t ? t('orders.tooltips.cancelOrder') : 'Bestellung stornieren'}>
<IconButton
size="small"
color="error"
onClick={() => handleCancelClick(order)}
aria-label={t ? t('orders.tooltips.cancelOrder') : 'Bestellung stornieren'}
>
<CancelIcon />
</IconButton>
</Tooltip>
)}
</Box>
</TableCell>
</TableRow>
{isWireGirocodeEligible(order) && (
<TableRow>
<TableCell
colSpan={6}
sx={{
py: 2,
px: { xs: 1, sm: 2 },
verticalAlign: "top",
bgcolor: "action.hover",
borderTop: (theme) => `1px solid ${theme.palette.divider}`,
}}
>
<WireOrderGirocode order={order} t={t} />
</TableCell>
</TableRow>
)}
</Fragment>
);
})}
</TableBody>