@deenruv/admin-dashboard 1.0.17-dev.18 → 1.0.17-dev.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/DeenruvAdminPanel.js +14 -8
- package/dist/access/admin-router.js +42 -3
- package/dist/access/built-in-route-permissions.js +44 -0
- package/dist/access/built-in-routes.js +22 -21
- package/dist/access/channel-selection.js +9 -0
- package/dist/components/Menu/ChannelSwitcher.js +8 -1
- package/dist/graphql/scalars.js +4 -0
- package/dist/locales/en/orders.json +9 -1
- package/dist/locales/pl/orders.json +9 -1
- package/dist/pages/Root.js +24 -19
- package/dist/pages/assets/List.js +1 -1
- package/dist/pages/collections/Detail.js +5 -0
- package/dist/pages/collections/List.js +1 -1
- package/dist/pages/collections/_components/ContentsCard.js +1 -1
- package/dist/pages/countries/Detail.js +5 -0
- package/dist/pages/countries/List.js +1 -1
- package/dist/pages/facets/Detail.js +5 -0
- package/dist/pages/facets/List.js +1 -1
- package/dist/pages/facets/_components/FacetDetailView.js +1 -1
- package/dist/pages/orders/_components/CancelAndRefundDialog.js +80 -19
- package/dist/pages/orders/_components/Payments.js +39 -23
- package/dist/pages/orders/_components/ProductsTable.js +13 -25
- package/dist/pages/orders/_components/TopActions.js +14 -8
- package/dist/pages/payment-methods/Detail.js +5 -0
- package/dist/pages/payment-methods/List.js +1 -1
- package/dist/pages/product-variants/List.js +4 -3
- package/dist/pages/products/Detail.js +3 -3
- package/dist/pages/products/List.js +3 -2
- package/dist/pages/shipping-methods/Detail.js +5 -0
- package/dist/pages/shipping-methods/List.js +1 -1
- package/dist/pages/tax-categories/Detail.js +3 -3
- package/dist/pages/tax-categories/List.js +1 -1
- package/dist/pages/tax-rates/Detail.js +5 -0
- package/dist/pages/tax-rates/List.js +1 -1
- package/dist/pages/zones/Detail.js +5 -0
- package/dist/pages/zones/List.js +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -5
|
@@ -1,28 +1,89 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useState } from 'react';
|
|
3
|
-
import { Button, Dialog,
|
|
2
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { Button, Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, DropdownMenuItem, getRefundablePayments, getRefundedQuantities, Input, planOrderRefund, priceFormatter, Textarea, useOrder, useTranslation, } from '@deenruv/react-ui-devkit';
|
|
4
4
|
import { Undo2 } from 'lucide-react';
|
|
5
|
-
import { ProductsTable } from
|
|
6
|
-
|
|
7
|
-
export const CancelAndRefundDialog = ({ refundReason, setRefundReason, onConfirm, }) => {
|
|
5
|
+
import { ProductsTable } from './ProductsTable.js';
|
|
6
|
+
export const CancelAndRefundDialog = ({ onConfirm }) => {
|
|
8
7
|
const { t } = useTranslation('orders');
|
|
9
8
|
const { order } = useOrder();
|
|
10
9
|
const [open, setOpen] = useState(false);
|
|
11
|
-
const [
|
|
12
|
-
const [
|
|
10
|
+
const [selections, setSelections] = useState({});
|
|
11
|
+
const [reason, setReason] = useState('');
|
|
12
|
+
const [refundShipping, setRefundShipping] = useState(false);
|
|
13
13
|
const [cancelShipping, setCancelShipping] = useState(false);
|
|
14
|
+
const [manualAmount, setManualAmount] = useState('');
|
|
15
|
+
const [allocationOverrides, setAllocationOverrides] = useState({});
|
|
16
|
+
const [error, setError] = useState('');
|
|
17
|
+
const [submitting, setSubmitting] = useState(false);
|
|
18
|
+
const capacities = useMemo(() => getRefundablePayments(order?.payments ?? []), [order?.payments]);
|
|
19
|
+
const refundedQuantities = useMemo(() => getRefundedQuantities(order?.payments ?? []), [order?.payments]);
|
|
20
|
+
const reset = () => {
|
|
21
|
+
setSelections({});
|
|
22
|
+
setReason('');
|
|
23
|
+
setRefundShipping(false);
|
|
24
|
+
setCancelShipping(false);
|
|
25
|
+
setManualAmount('');
|
|
26
|
+
setAllocationOverrides({});
|
|
27
|
+
setError('');
|
|
28
|
+
setSubmitting(false);
|
|
29
|
+
};
|
|
14
30
|
useEffect(() => {
|
|
15
|
-
if (!
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
31
|
+
if (!open)
|
|
32
|
+
reset();
|
|
33
|
+
}, [open]);
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
setOpen(false);
|
|
36
|
+
reset();
|
|
37
|
+
}, [order?.id]);
|
|
38
|
+
if (!order)
|
|
39
|
+
return null;
|
|
40
|
+
const itemAmount = order.lines.reduce((total, line) => total + line.proratedUnitPriceWithTax * (selections[line.id]?.refundQuantity ?? 0), 0);
|
|
41
|
+
const shipping = refundShipping ? order.shipping : 0;
|
|
42
|
+
const suggestedAmount = itemAmount + shipping;
|
|
43
|
+
const hasCancellationLines = Object.values(selections).some((selection) => selection.cancelQuantity > 0);
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!hasCancellationLines)
|
|
46
|
+
setCancelShipping(false);
|
|
47
|
+
}, [hasCancellationLines]);
|
|
48
|
+
const submit = async () => {
|
|
49
|
+
try {
|
|
50
|
+
setError('');
|
|
51
|
+
const overrides = Object.entries(allocationOverrides)
|
|
52
|
+
.filter(([, amount]) => amount !== '')
|
|
53
|
+
.map(([paymentId, amount]) => ({ paymentId, amount: Number(amount) }));
|
|
54
|
+
const plan = planOrderRefund({
|
|
55
|
+
lines: order.lines.map((line) => ({
|
|
56
|
+
id: line.id,
|
|
57
|
+
maxRefundQuantity: Math.max(0, line.orderPlacedQuantity - (refundedQuantities[line.id] ?? 0)),
|
|
58
|
+
maxCancelQuantity: line.quantity,
|
|
59
|
+
unitPriceWithTax: line.proratedUnitPriceWithTax,
|
|
60
|
+
})),
|
|
61
|
+
selections,
|
|
62
|
+
shippingAmount: shipping,
|
|
63
|
+
cancelShipping,
|
|
64
|
+
reason,
|
|
65
|
+
capacities,
|
|
66
|
+
amountOverride: manualAmount === '' ? undefined : Number(manualAmount),
|
|
67
|
+
allocationOverrides: overrides.length > 0 ? overrides : undefined,
|
|
68
|
+
});
|
|
69
|
+
setSubmitting(true);
|
|
70
|
+
await onConfirm({
|
|
71
|
+
cancelLines: plan.cancelLines,
|
|
72
|
+
refundLines: plan.refundLines,
|
|
73
|
+
allocations: plan.allocations,
|
|
74
|
+
reason: reason.trim(),
|
|
75
|
+
shipping,
|
|
76
|
+
cancelShipping,
|
|
77
|
+
adjustment: plan.totalAmount - plan.itemAmount - shipping,
|
|
78
|
+
});
|
|
79
|
+
setOpen(false);
|
|
80
|
+
}
|
|
81
|
+
catch (submitError) {
|
|
82
|
+
setError(submitError instanceof Error ? submitError.message : t('cancelAndRefund.error'));
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
setSubmitting(false);
|
|
23
86
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}, [cancelShipping, refundLines, order]);
|
|
27
|
-
return (_jsxs(Dialog, { open: open, onOpenChange: setOpen, children: [_jsx(DialogTrigger, { asChild: true, children: _jsx(DropdownMenuItem, { asChild: true, onSelect: (e) => e.preventDefault(), children: _jsx(Button, { variant: "ghost", className: "w-full justify-start px-4 py-2 text-red-400 hover:text-red-400 dark:text-red-400 dark:hover:text-red-400", children: t('cancelAndRefund.trigger') }) }) }), _jsxs(DialogContent, { className: "flex h-[80vh] max-w-[80vw] flex-col gap-0 overflow-hidden", children: [_jsx(DialogHeader, { children: _jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx(Undo2, { className: "h-5 w-5 text-rose-500 dark:text-rose-400" }), _jsx(DialogTitle, { children: t('cancelAndRefund.title') })] }) }), _jsx(ProductsTable, { refundLines, setRefundLines }), _jsx(RefundPaymentCard, { refundReason, setRefundReason, cancelShipping, setCancelShipping, refundAmount, setRefundAmount }), _jsxs(DialogFooter, { className: "mt-auto gap-2", children: [_jsx(DialogClose, { asChild: true, children: _jsx(Button, { variant: "outline", children: t('payments.cancel') }) }), _jsxs(Button, { type: "submit", className: "gap-2", onClick: () => onConfirm(refundAmount, refundLines, refundReason, 0, cancelShipping, 0), children: [_jsx(Undo2, { className: "h-4 w-4" }), t('cancelAndRefund.refund')] })] })] })] }));
|
|
87
|
+
};
|
|
88
|
+
return (_jsxs(Dialog, { open: open, onOpenChange: setOpen, children: [_jsx(DialogTrigger, { asChild: true, children: _jsx(DropdownMenuItem, { asChild: true, onSelect: (event) => event.preventDefault(), children: _jsx(Button, { variant: "ghost", className: "w-full justify-start px-4 py-2 text-red-400 hover:text-red-400", children: t('cancelAndRefund.trigger') }) }) }), _jsxs(DialogContent, { className: "flex h-[85vh] max-w-[90vw] flex-col gap-4 overflow-auto", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: t('cancelAndRefund.title') }) }), _jsx(ProductsTable, { selections: selections, setSelections: setSelections }), _jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-3", children: [_jsxs("label", { className: "flex gap-2", children: [_jsx("input", { type: "checkbox", checked: refundShipping, onChange: (e) => setRefundShipping(e.target.checked) }), t('cancelAndRefund.refundShipping')] }), _jsxs("label", { className: "flex gap-2", children: [_jsx("input", { type: "checkbox", checked: cancelShipping, disabled: !hasCancellationLines, onChange: (e) => setCancelShipping(e.target.checked) }), t('cancelAndRefund.cancelShipping')] }), !hasCancellationLines && (_jsx("p", { className: "text-xs text-muted-foreground", children: t('cancelAndRefund.cancelShippingRequiresLines') })), _jsx("label", { className: "block text-sm", children: t('refund.amount') }), _jsx(Input, { type: "number", min: 0, step: 1, value: manualAmount, placeholder: String(suggestedAmount), onChange: (e) => setManualAmount(e.target.value) }), _jsxs("p", { className: "text-sm text-muted-foreground", children: [t('cancelAndRefund.suggestedAmount'), ": ", priceFormatter(suggestedAmount, order.currencyCode)] })] }), _jsxs("div", { className: "space-y-3", children: [_jsx("label", { className: "block text-sm", children: t('cancelAndRefund.reason') }), _jsx(Textarea, { value: reason, onChange: (e) => setReason(e.target.value) }), capacities.map((payment) => (_jsxs("div", { className: "grid grid-cols-[1fr_140px] items-center gap-2 text-sm", children: [_jsxs("span", { children: [payment.paymentId, " (", priceFormatter(payment.capacity, order.currencyCode), ")"] }), _jsx(Input, { type: "number", min: 0, max: payment.capacity, step: 1, placeholder: t('cancelAndRefund.auto'), value: allocationOverrides[payment.paymentId] ?? '', onChange: (e) => setAllocationOverrides((current) => ({ ...current, [payment.paymentId]: e.target.value })) })] }, payment.paymentId)))] })] }), error && (_jsx("p", { role: "alert", className: "text-sm text-destructive", children: error })), _jsxs(DialogFooter, { className: "mt-auto", children: [_jsx(Button, { variant: "outline", onClick: () => setOpen(false), children: t('payments.cancel') }), _jsxs(Button, { disabled: submitting, onClick: submit, children: [_jsx(Undo2, { className: "mr-2 h-4 w-4" }), t('cancelAndRefund.submit')] })] })] })] }));
|
|
28
89
|
};
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import { useOrder, Button, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, ScrollArea, Badge, DropdownMenuItem, ContextMenu, ConfirmationDialog, CustomCard, EmptyState, useTranslation, apiClient, DialogTrigger, Input, priceFormatter, } from '@deenruv/react-ui-devkit';
|
|
4
|
+
import { useOrder, Button, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, ScrollArea, Badge, DropdownMenuItem, ContextMenu, ConfirmationDialog, CustomCard, EmptyState, useTranslation, apiClient, DialogTrigger, Input, priceFormatter, useServer, } from '@deenruv/react-ui-devkit';
|
|
5
5
|
import { format } from 'date-fns';
|
|
6
6
|
import { CreditCard, Calendar, CheckCircle2, XCircle, Clock, AlertCircle, Wallet, Receipt, CheckCircle, Ban, ArrowDownCircle, ArrowUpCircle, RefreshCcw, } from 'lucide-react';
|
|
7
7
|
import { useState } from 'react';
|
|
8
8
|
import { AddPaymentDialog } from './index.js';
|
|
9
9
|
import { PAYMENT_STATE } from "../../../graphql/base";
|
|
10
|
+
import { Permission } from '@deenruv/admin-types';
|
|
10
11
|
import { MetadataDisplay } from './PaymentMetadata.js';
|
|
11
12
|
export const Payments = () => {
|
|
12
13
|
const { order, addPaymentToOrder, settlePayment, cancelPayment } = useOrder();
|
|
13
14
|
const { t } = useTranslation('orders');
|
|
15
|
+
const canUpdateOrder = useServer((state) => state.userPermissions.includes(Permission.UpdateOrder));
|
|
14
16
|
const [paymentToBeSettled, setPaymentToBeSettled] = useState('');
|
|
15
17
|
const [expandedRefunds, setExpandedRefunds] = useState([]);
|
|
16
18
|
if (!order)
|
|
@@ -77,37 +79,51 @@ export const Payments = () => {
|
|
|
77
79
|
const toggleRefundExpand = (id) => {
|
|
78
80
|
setExpandedRefunds((prev) => (prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]));
|
|
79
81
|
};
|
|
80
|
-
const [settleRefundTransactionId, setSettleRefundTransactionId] = useState(
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
const [settleRefundTransactionId, setSettleRefundTransactionId] = useState('');
|
|
83
|
+
const [refundToSettle, setRefundToSettle] = useState('');
|
|
84
|
+
const [settleRefundError, setSettleRefundError] = useState('');
|
|
85
|
+
const settleRefund = async () => {
|
|
86
|
+
const transactionId = settleRefundTransactionId.trim();
|
|
87
|
+
if (!transactionId)
|
|
88
|
+
return setSettleRefundError(t('refunds.transactionRequired', 'Transaction ID is required'));
|
|
89
|
+
try {
|
|
90
|
+
const { settleRefund } = await apiClient('mutation')({
|
|
91
|
+
settleRefund: [
|
|
92
|
+
{ input: { id: refundToSettle, transactionId } },
|
|
93
|
+
{
|
|
94
|
+
__typename: true,
|
|
95
|
+
'...on Refund': { id: true },
|
|
96
|
+
'...on RefundStateTransitionError': { errorCode: true, fromState: true, message: true },
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
});
|
|
100
|
+
if (settleRefund.__typename === 'Refund') {
|
|
101
|
+
await useOrder.getState().fetchOrder(order.id);
|
|
102
|
+
await useOrder.getState().fetchOrderHistory();
|
|
103
|
+
setSettleRefundTransactionId('');
|
|
104
|
+
setRefundToSettle('');
|
|
105
|
+
setSettleRefundError('');
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
setSettleRefundError(settleRefund.message);
|
|
109
|
+
}
|
|
84
110
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
{ input: { id, transactionId: settleRefundTransactionId } },
|
|
88
|
-
{
|
|
89
|
-
__typename: true,
|
|
90
|
-
'...on Refund': { id: true },
|
|
91
|
-
'...on RefundStateTransitionError': { errorCode: true, fromState: true, message: true },
|
|
92
|
-
},
|
|
93
|
-
],
|
|
94
|
-
});
|
|
95
|
-
if (settleRefund.__typename === 'Refund') {
|
|
96
|
-
setSettleRefundTransactionId(null);
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
console.error(settleRefund);
|
|
111
|
+
catch (error) {
|
|
112
|
+
setSettleRefundError(error instanceof Error ? error.message : t('refunds.settleError', 'Could not settle refund'));
|
|
100
113
|
}
|
|
101
114
|
};
|
|
102
|
-
return (_jsxs(CustomCard, { color: "teal", description: t('payments.subTitle'), title: t('payments.title'), icon: _jsx(Wallet, { className: "size-5 text-teal-500 dark:text-teal-400" }), upperRight: _jsx(AddPaymentDialog, { order: order, onSubmit: (v) => addPaymentToOrder(v) }), children: [_jsx(Dialog, { open: !!paymentToBeSettled, onOpenChange: () => setPaymentToBeSettled(''), children: _jsxs(DialogContent, { className: "max-w-md", children: [_jsxs(DialogHeader, { children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx(CheckCircle2, { className: "size-5 text-teal-500" }), _jsx(DialogTitle, { children: t('payments.settle.title', 'Settle Payment') })] }), _jsx(DialogDescription, { children: t('payments.settle.description', 'Are you sure you want to settle this payment? This action cannot be undone.') })] }), _jsxs(DialogFooter, { className: "mt-4 gap-2", children: [_jsx(DialogClose, { asChild: true, children: _jsx(Button, { variant: "outline", children: t('payments.settle.cancel', 'Cancel') }) }), _jsxs(Button, { variant: "default", onClick: () => settlePayment({ id: paymentToBeSettled }), className: "gap-2", children: [_jsx(CheckCircle2, { className: "size-4" }), t('payments.settle.confirm', 'Confirm Settlement')] })] })] }) }), _jsx(ScrollArea, { className: "max-h-[400px] px-6", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { noHover: true, className: "border-b border-border", children: [_jsx(TableHead, { className: "w-[80px] py-3", children: t('payments.id') }), _jsx(TableHead, { className: "py-3", children: t('payments.created') }), _jsx(TableHead, { className: "py-3", children: t('payments.method') }), _jsx(TableHead, { className: "py-3", children: t('payments.status') }), _jsx(TableHead, { className: "py-3", children: t('payments.amount') }), _jsx(TableHead, { className: "py-3", children: t('payments.refunds', 'Refunds') }), _jsx(TableHead, { className: "py-3 text-center", children: t('payments.extra') }), _jsx(TableHead, { className: "ml-auto py-3" })] }) }), _jsx(TableBody, { children: payments?.length ? (payments.map(({ amount, id, method, state, createdAt, metadata, refunds
|
|
115
|
+
return (_jsxs(CustomCard, { color: "teal", description: t('payments.subTitle'), title: t('payments.title'), icon: _jsx(Wallet, { className: "size-5 text-teal-500 dark:text-teal-400" }), upperRight: canUpdateOrder ? _jsx(AddPaymentDialog, { order: order, onSubmit: (v) => addPaymentToOrder(v) }) : undefined, children: [_jsx(Dialog, { open: !!paymentToBeSettled, onOpenChange: () => setPaymentToBeSettled(''), children: _jsxs(DialogContent, { className: "max-w-md", children: [_jsxs(DialogHeader, { children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx(CheckCircle2, { className: "size-5 text-teal-500" }), _jsx(DialogTitle, { children: t('payments.settle.title', 'Settle Payment') })] }), _jsx(DialogDescription, { children: t('payments.settle.description', 'Are you sure you want to settle this payment? This action cannot be undone.') })] }), _jsxs(DialogFooter, { className: "mt-4 gap-2", children: [_jsx(DialogClose, { asChild: true, children: _jsx(Button, { variant: "outline", children: t('payments.settle.cancel', 'Cancel') }) }), _jsxs(Button, { variant: "default", onClick: () => settlePayment({ id: paymentToBeSettled }).then(() => setPaymentToBeSettled('')), className: "gap-2", children: [_jsx(CheckCircle2, { className: "size-4" }), t('payments.settle.confirm', 'Confirm Settlement')] })] })] }) }), _jsx(ScrollArea, { className: "max-h-[400px] px-6", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { noHover: true, className: "border-b border-border", children: [_jsx(TableHead, { className: "w-[80px] py-3", children: t('payments.id') }), _jsx(TableHead, { className: "py-3", children: t('payments.created') }), _jsx(TableHead, { className: "py-3", children: t('payments.method') }), _jsx(TableHead, { className: "py-3", children: t('payments.status') }), _jsx(TableHead, { className: "py-3", children: t('payments.amount') }), _jsx(TableHead, { className: "py-3", children: t('payments.refunds', 'Refunds') }), _jsx(TableHead, { className: "py-3 text-center", children: t('payments.extra') }), _jsx(TableHead, { className: "ml-auto py-3" })] }) }), _jsx(TableBody, { children: payments?.length ? (payments.map(({ amount, id, method, state, createdAt, metadata, refunds }) => {
|
|
103
116
|
const statusBadge = getStatusBadge(state);
|
|
104
117
|
const hasRefunds = refunds && refunds.length > 0;
|
|
105
118
|
const isRefundExpanded = expandedRefunds.includes(id);
|
|
106
119
|
const totalRefunded = hasRefunds ? refunds.reduce((sum, refund) => sum + refund.total, 0) : 0;
|
|
107
|
-
return (_jsxs(React.Fragment, { children: [_jsxs(TableRow, { noHover: true, className: "group", children: [_jsx(TableCell, { className: "py-3 font-mono text-xs text-muted-foreground", children: id }), _jsx(TableCell, { className: "py-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Calendar, { className: "size-4 text-teal-500 dark:text-teal-400" }), _jsx("span", { children: format(new Date(createdAt), 'dd/LL/Y, kk:mm') })] }) }), _jsx(TableCell, { className: "py-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CreditCard, { className: "size-4 text-teal-500 dark:text-teal-400" }), _jsx("span", { className: "font-medium", children: method })] }) }), _jsx(TableCell, { className: "py-3", children: _jsxs(Badge, { variant: statusBadge.variant, className: "flex w-fit items-center gap-1", children: [statusBadge.icon, statusBadge.label] }) }), _jsx(TableCell, { className: "py-3 font-mono text-sm font-medium", children: priceFormatter(amount, order.currencyCode) }), _jsx(TableCell, { className: "py-3", children: hasRefunds ? (_jsx("div", { className: "flex flex-col gap-1", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(RefreshCcw, { className: "size-4 text-orange-500 dark:text-orange-400" }), _jsx("span", { className: "font-mono text-sm font-medium text-orange-600 dark:text-orange-400", children: priceFormatter(totalRefunded, order.currencyCode) }), _jsx(Button, { variant: "ghost", size: "sm", className: "h-6 p-0", onClick: () => toggleRefundExpand(id), children: isRefundExpanded ? (_jsx(ArrowUpCircle, { className: "size-4 text-muted-foreground" })) : (_jsx(ArrowDownCircle, { className: "size-4 text-muted-foreground" })) })] }) })) : (_jsx("span", { className: "text-sm text-muted-foreground", children: "-" })) }), _jsx(TableCell, { className: "flex items-center justify-center", children: _jsx(MetadataDisplay, { metadata: metadata }) }), _jsx(TableCell, { className: "py-3 text-end", children: _jsxs(ContextMenu, { children: [state !== PAYMENT_STATE.SETTLED && state !== PAYMENT_STATE.CANCELLED && (_jsxs(DropdownMenuItem, { onClick: () => setPaymentToBeSettled(id), className: "flex cursor-pointer items-center gap-2", children: [_jsx(CheckCircle2, { size: 16 }), t('payments.settle.settle', 'Settle')] }, 'set')), !hasRefunds && state !== PAYMENT_STATE.CANCELLED && (_jsx(ConfirmationDialog, { onConfirm: () => cancelPayment(id), children: _jsxs(DropdownMenuItem, { className: "flex cursor-pointer items-center gap-2 text-red-500", onSelect: (e) => e.preventDefault(), children: [_jsx(XCircle, { size: 16 }), t('payments.cancel', 'Cancel')] }, 'cancel') }))] }) })] }), hasRefunds && isRefundExpanded && (_jsx(TableRow, { noHover: true, className: "bg-muted/30", children: _jsx(TableCell, { colSpan: 8, className: "p-0", children: _jsxs("div", { className: "px-8 py-3", children: [_jsx("div", { className: "mb-2 text-sm font-medium", children: t('refunds.details', 'Refund Details') }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { noHover: true, className: "border-b border-border", children: [_jsx(TableHead, { className: "py-2 text-xs", children: t('refunds.state', 'Status') }), _jsx(TableHead, { className: "py-2 text-xs", children: t('refunds.amount', 'Amount') }), _jsx(TableHead, { className: "py-2 text-xs", children: t('refunds.items', 'Items') }), refunds.some((refund) => refund.state.toLowerCase() === 'pending') && (_jsx(TableHead, { className: "py-2 text-xs", children: t('refunds.actions', 'Actions') }))] }) }), _jsx(TableBody, { children: refunds.map((refund, index) => {
|
|
120
|
+
return (_jsxs(React.Fragment, { children: [_jsxs(TableRow, { noHover: true, className: "group", children: [_jsx(TableCell, { className: "py-3 font-mono text-xs text-muted-foreground", children: id }), _jsx(TableCell, { className: "py-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Calendar, { className: "size-4 text-teal-500 dark:text-teal-400" }), _jsx("span", { children: format(new Date(createdAt), 'dd/LL/Y, kk:mm') })] }) }), _jsx(TableCell, { className: "py-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CreditCard, { className: "size-4 text-teal-500 dark:text-teal-400" }), _jsx("span", { className: "font-medium", children: method })] }) }), _jsx(TableCell, { className: "py-3", children: _jsxs(Badge, { variant: statusBadge.variant, className: "flex w-fit items-center gap-1", children: [statusBadge.icon, statusBadge.label] }) }), _jsx(TableCell, { className: "py-3 font-mono text-sm font-medium", children: priceFormatter(amount, order.currencyCode) }), _jsx(TableCell, { className: "py-3", children: hasRefunds ? (_jsx("div", { className: "flex flex-col gap-1", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(RefreshCcw, { className: "size-4 text-orange-500 dark:text-orange-400" }), _jsx("span", { className: "font-mono text-sm font-medium text-orange-600 dark:text-orange-400", children: priceFormatter(totalRefunded, order.currencyCode) }), _jsx(Button, { variant: "ghost", size: "sm", className: "h-6 p-0", onClick: () => toggleRefundExpand(id), children: isRefundExpanded ? (_jsx(ArrowUpCircle, { className: "size-4 text-muted-foreground" })) : (_jsx(ArrowDownCircle, { className: "size-4 text-muted-foreground" })) })] }) })) : (_jsx("span", { className: "text-sm text-muted-foreground", children: "-" })) }), _jsx(TableCell, { className: "flex items-center justify-center", children: _jsx(MetadataDisplay, { metadata: metadata }) }), _jsx(TableCell, { className: "py-3 text-end", children: canUpdateOrder && (_jsxs(ContextMenu, { children: [state !== PAYMENT_STATE.SETTLED && state !== PAYMENT_STATE.CANCELLED && (_jsxs(DropdownMenuItem, { onClick: () => setPaymentToBeSettled(id), className: "flex cursor-pointer items-center gap-2", children: [_jsx(CheckCircle2, { size: 16 }), t('payments.settle.settle', 'Settle')] }, 'set')), !hasRefunds && state !== PAYMENT_STATE.CANCELLED && (_jsx(ConfirmationDialog, { onConfirm: () => cancelPayment(id), children: _jsxs(DropdownMenuItem, { className: "flex cursor-pointer items-center gap-2 text-red-500", onSelect: (e) => e.preventDefault(), children: [_jsx(XCircle, { size: 16 }), t('payments.cancel', 'Cancel')] }, 'cancel') }))] })) })] }), hasRefunds && isRefundExpanded && (_jsx(TableRow, { noHover: true, className: "bg-muted/30", children: _jsx(TableCell, { colSpan: 8, className: "p-0", children: _jsxs("div", { className: "px-8 py-3", children: [_jsx("div", { className: "mb-2 text-sm font-medium", children: t('refunds.details', 'Refund Details') }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { noHover: true, className: "border-b border-border", children: [_jsx(TableHead, { className: "py-2 text-xs", children: t('refunds.state', 'Status') }), _jsx(TableHead, { className: "py-2 text-xs", children: t('refunds.amount', 'Amount') }), _jsx(TableHead, { className: "py-2 text-xs", children: t('refunds.items', 'Items') }), refunds.some((refund) => refund.state.toLowerCase() === 'pending') && (_jsx(TableHead, { className: "py-2 text-xs", children: t('refunds.actions', 'Actions') }))] }) }), _jsx(TableBody, { children: refunds.map((refund, index) => {
|
|
108
121
|
const refundStatusBadge = getRefundStatusBadge(refund.state);
|
|
109
122
|
const state = refund.state.toLowerCase();
|
|
110
|
-
return (_jsxs(TableRow, { noHover: true, className: "border-b border-border/50", children: [_jsx(TableCell, { className: "py-2", children: _jsxs(Badge, { variant: refundStatusBadge.variant, className: "flex w-fit items-center gap-1", children: [refundStatusBadge.icon, refundStatusBadge.label] }) }), _jsx(TableCell, { className: "py-2 font-mono text-sm", children: priceFormatter(refund.total, order.currencyCode) }), _jsx(TableCell, { className: "py-2", children: _jsx("div", { className: "text-sm", children: refund.lines.map((line, lineIndex) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("span", { className: "font-mono text-xs text-muted-foreground", children: [line.orderLineId, " |"] }), _jsxs("span", { className: "text-xs", children: ["\u00D7", line.quantity] })] }, `${id}-refund-${index}-line-${lineIndex}`))) }) }), state === 'pending' && (_jsx(TableCell, { className: "py-2", children: _jsxs(Dialog, {
|
|
123
|
+
return (_jsxs(TableRow, { noHover: true, className: "border-b border-border/50", children: [_jsx(TableCell, { className: "py-2", children: _jsxs(Badge, { variant: refundStatusBadge.variant, className: "flex w-fit items-center gap-1", children: [refundStatusBadge.icon, refundStatusBadge.label] }) }), _jsx(TableCell, { className: "py-2 font-mono text-sm", children: priceFormatter(refund.total, order.currencyCode) }), _jsx(TableCell, { className: "py-2", children: _jsx("div", { className: "text-sm", children: refund.lines.map((line, lineIndex) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("span", { className: "font-mono text-xs text-muted-foreground", children: [line.orderLineId, " |"] }), _jsxs("span", { className: "text-xs", children: ["\u00D7", line.quantity] })] }, `${id}-refund-${index}-line-${lineIndex}`))) }) }), state === 'pending' && canUpdateOrder && (_jsx(TableCell, { className: "py-2", children: _jsxs(Dialog, { open: refundToSettle === refund.id, onOpenChange: (nextOpen) => {
|
|
124
|
+
setRefundToSettle(nextOpen ? refund.id : '');
|
|
125
|
+
setSettleRefundError('');
|
|
126
|
+
}, children: [_jsx(DialogTrigger, { asChild: true, children: _jsx(Button, { children: "Settle refund" }) }), _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Settle refund" }), _jsx(DialogDescription, { children: "After manually refunding via your payment provider (standard-payment), enter the transaction ID here." })] }), _jsx("div", { children: _jsx(Input, { placeholder: "Transaction ID", value: settleRefundTransactionId, onChange: (e) => setSettleRefundTransactionId(e.target.value) }) }), settleRefundError && (_jsx("p", { role: "alert", className: "text-sm text-destructive", children: settleRefundError })), _jsxs(DialogFooter, { children: [_jsx(DialogClose, { asChild: true, children: _jsx(Button, { variant: "outline", children: t('payments.settle.cancel', 'Cancel') }) }), _jsxs(Button, { variant: "default", onClick: settleRefund, className: "gap-2", children: [_jsx(CheckCircle2, { className: "size-4" }), t('payments.settle.confirm', 'Confirm Settlement')] })] })] })] }) }))] }, `${id}-refund-${index}`));
|
|
111
127
|
}) })] })] }) }) }))] }, id));
|
|
112
128
|
})) : (_jsx(EmptyState, { columnsLength: 8, title: t('payments.notFound', 'No payments found'), color: "teal", description: t('payments.addPaymentHint'), small: true, icon: _jsx(Receipt, {}) })) })] }) })] }));
|
|
113
129
|
};
|
|
@@ -1,28 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import { Tag } from 'lucide-react';
|
|
5
|
-
import { useCallback } from 'react';
|
|
6
|
-
export const ProductsTable = ({ setRefundLines, refundLines }) => {
|
|
2
|
+
import { ImageWithPreview, Input, getRefundedQuantities, priceFormatter, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, useOrder, useTranslation, } from '@deenruv/react-ui-devkit';
|
|
3
|
+
export const ProductsTable = ({ selections, setSelections }) => {
|
|
7
4
|
const { t } = useTranslation('orders');
|
|
8
|
-
const {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
return newState;
|
|
21
|
-
});
|
|
22
|
-
}, [refundLines]);
|
|
23
|
-
return (_jsx("div", { className: "rounded-lg border-0 border-border shadow-sm", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { noHover: true, className: "hover:bg-transparent", children: [_jsx(TableHead, { className: "py-3 font-semibold", children: t('create.product', 'Product') }), _jsx(TableHead, { className: "py-3 font-semibold", children: t('create.sku', 'SKU') }), _jsx(TableHead, { className: "py-3 font-semibold", children: t('create.customFields', 'Custom Fields') }), _jsx(TableHead, { className: "py-3 font-semibold", children: t('create.price', 'Price') }), _jsx(TableHead, { className: "py-3 font-semibold", children: t('create.priceWithTax', 'Price with Tax') }), _jsx(TableHead, { className: "py-3 font-semibold", children: t('cancelAndRefund.refund') }), _jsx(TableHead, { className: "py-3 font-semibold", children: t('cancelAndRefund.returnToStock') })] }) }), _jsx(TableBody, { children: currentOrder.lines.map((line) => (_jsxs(TableRow, { className: "hover:bg-muted/20", children: [_jsx(TableCell, { className: "py-3", children: _jsxs("div", { className: "flex w-max items-center gap-3", children: [_jsx(ImageWithPreview, { imageClassName: "aspect-square w-12 h-12 rounded-md object-cover border border-border", src: line.productVariant.featuredAsset?.preview ||
|
|
24
|
-
line.productVariant.product?.featuredAsset?.preview ||
|
|
25
|
-
'/placeholder.svg' }), _jsx("div", { className: "font-medium text-primary", children: line.productVariant.product.name })] }) }), _jsx(TableCell, { className: "min-w-[200px] py-3 font-mono text-sm text-muted-foreground", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Tag, { className: "h-4 w-4 text-blue-500 dark:text-blue-400" }), line.productVariant.sku] }) }), _jsx(TableCell, { className: "py-3", children: _jsx(OrderLineCustomFields, { line: line, order: currentOrder, mode: mode }) }), _jsx(TableCell, { className: "py-3 font-medium", children: priceFormatter(line.linePrice, line.productVariant.currencyCode) }), _jsx(TableCell, { className: "py-3 font-medium", children: priceFormatter(line.linePriceWithTax, line.productVariant.currencyCode) }), _jsx(TableCell, { className: "py-3", children: _jsx(Input, { type: "number", wrapperClassName: "w-24", endAdornment: '/' + line.quantity, defaultValue: 0, max: line.quantity, onChange: (e) => {
|
|
26
|
-
handleLineChange(line.id, +e.target.value);
|
|
27
|
-
} }) }), _jsx(TableCell, { className: "py-3", children: _jsxs("div", { className: "flex gap-2", children: [_jsx(Checkbox, {}), _jsx(Label, { children: t('cancelAndRefund.returnToStock') })] }) })] }, line.id))) })] }) }));
|
|
5
|
+
const { order } = useOrder();
|
|
6
|
+
if (!order)
|
|
7
|
+
return null;
|
|
8
|
+
const refundedQuantities = getRefundedQuantities(order.payments ?? []);
|
|
9
|
+
const update = (id, key, value) => setSelections((current) => ({
|
|
10
|
+
...current,
|
|
11
|
+
[id]: { ...(current[id] ?? { refundQuantity: 0, cancelQuantity: 0 }), [key]: value },
|
|
12
|
+
}));
|
|
13
|
+
return (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: t('create.product') }), _jsx(TableHead, { children: t('create.sku') }), _jsx(TableHead, { children: t('create.priceWithTax') }), _jsx(TableHead, { children: t('cancelAndRefund.refundQuantity') }), _jsx(TableHead, { children: t('cancelAndRefund.returnToStock') })] }) }), _jsx(TableBody, { children: order.lines.map((line) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(ImageWithPreview, { imageClassName: "size-12 rounded-md object-cover", src: line.productVariant.featuredAsset?.preview ||
|
|
14
|
+
line.productVariant.product?.featuredAsset?.preview ||
|
|
15
|
+
'/placeholder.svg' }), _jsx("span", { children: line.productVariant.product.name })] }) }), _jsx(TableCell, { children: line.productVariant.sku }), _jsx(TableCell, { children: priceFormatter(line.proratedUnitPriceWithTax, order.currencyCode) }), _jsx(TableCell, { children: _jsx(Input, { type: "number", min: 0, max: Math.max(0, line.orderPlacedQuantity - (refundedQuantities[line.id] ?? 0)), step: 1, value: selections[line.id]?.refundQuantity ?? 0, onChange: (e) => update(line.id, 'refundQuantity', Number(e.target.value)) }) }), _jsx(TableCell, { children: _jsx(Input, { type: "number", min: 0, max: line.quantity, step: 1, value: selections[line.id]?.cancelQuantity ?? 0, onChange: (e) => update(line.id, 'cancelQuantity', Number(e.target.value)) }) })] }, line.id))) })] }));
|
|
28
16
|
};
|
|
@@ -3,7 +3,7 @@ import { AlertDialogHeader, AlertDialogFooter, Button, DropdownMenu, AlertDialog
|
|
|
3
3
|
import { FulfillmentModal } from "./FulfillmentModal";
|
|
4
4
|
import { ManualOrderChangeModal } from "./ManualOrderChangeModal";
|
|
5
5
|
import { PossibleOrderStates } from "./PossibleOrderStates";
|
|
6
|
-
import { DeletionResult, HistoryEntryType } from '@deenruv/admin-types';
|
|
6
|
+
import { DeletionResult, HistoryEntryType, Permission } from '@deenruv/admin-types';
|
|
7
7
|
import { ChevronLeft, EllipsisVerticalIcon, Info } from 'lucide-react';
|
|
8
8
|
import { useMemo, useState } from 'react';
|
|
9
9
|
import { useShallow } from 'zustand/react/shallow';
|
|
@@ -16,6 +16,9 @@ import React from 'react';
|
|
|
16
16
|
export const TopActions = () => {
|
|
17
17
|
const { currentPossibilities, manualChange, setManualChange, fetchOrderHistory, fetchOrder, order, changeOrderState, cancelOrder: _cancelOrder, cancelAndRefundOrder, } = useOrder();
|
|
18
18
|
const orderProcess = useServer(useShallow((p) => p.serverConfig?.orderProcess || []));
|
|
19
|
+
const userPermissions = useServer((state) => state.userPermissions);
|
|
20
|
+
const canUpdateOrder = userPermissions.includes(Permission.UpdateOrder);
|
|
21
|
+
const canDeleteOrder = userPermissions.includes(Permission.DeleteOrder);
|
|
19
22
|
const { t } = useTranslation('orders');
|
|
20
23
|
const navigate = useNavigate();
|
|
21
24
|
const { getDetailViewActions } = usePluginStore();
|
|
@@ -115,12 +118,15 @@ export const TopActions = () => {
|
|
|
115
118
|
.catch((err) => {
|
|
116
119
|
toast.error(t('topActions.orderCancelError', { value: err.message }));
|
|
117
120
|
});
|
|
118
|
-
const cancelAndRefund = (
|
|
119
|
-
.then(() => {
|
|
120
|
-
toast.info(
|
|
121
|
+
const cancelAndRefund = (input) => cancelAndRefundOrder(input)
|
|
122
|
+
.then((result) => {
|
|
123
|
+
toast.info(result?.outcome === 'cancellation'
|
|
124
|
+
? t('topActions.orderCanceledSuccessfully')
|
|
125
|
+
: t('topActions.orderRefundedSuccessfully'));
|
|
121
126
|
})
|
|
122
|
-
.catch(() => {
|
|
127
|
+
.catch((error) => {
|
|
123
128
|
toast.error(t('topActions.orderRefundError'));
|
|
129
|
+
throw error;
|
|
124
130
|
});
|
|
125
131
|
const deleteDraftOrder = async () => {
|
|
126
132
|
if (!order)
|
|
@@ -206,15 +212,15 @@ export const TopActions = () => {
|
|
|
206
212
|
}
|
|
207
213
|
else
|
|
208
214
|
navigate(Routes.orders.list, { viewTransition: true });
|
|
209
|
-
}, children: [_jsx(ChevronLeft, { className: "size-4" }), _jsx("span", { className: "sr-only", children: t('create.back') })] }), _jsx("h1", { className: "flex-1 shrink-0 text-xl font-semibold tracking-tight whitespace-nowrap sm:grow-0", children: t('create.orderId', { value: order?.id }) }), _jsx(OrderStateBadge, { state: order?.state }), _jsxs("div", { className: "hidden items-center gap-2 md:ml-auto md:flex", children: [actions?.inline?.map(({ component }) => React.createElement(component)) || null, exitingModifyStates ? (_jsx(Button, { size: "sm", onClick: onSubmit, disabled: !isOrderValid, children: order.state === ORDER_STATE.ARRANGING_ADDITIONAL_PAYMENT
|
|
215
|
+
}, children: [_jsx(ChevronLeft, { className: "size-4" }), _jsx("span", { className: "sr-only", children: t('create.back') })] }), _jsx("h1", { className: "flex-1 shrink-0 text-xl font-semibold tracking-tight whitespace-nowrap sm:grow-0", children: t('create.orderId', { value: order?.id }) }), _jsx(OrderStateBadge, { state: order?.state }), canUpdateOrder && (_jsxs("div", { className: "hidden items-center gap-2 md:ml-auto md:flex", children: [actions?.inline?.map(({ component }) => React.createElement(component)) || null, exitingModifyStates ? (_jsx(Button, { size: "sm", onClick: onSubmit, disabled: !isOrderValid, children: order.state === ORDER_STATE.ARRANGING_ADDITIONAL_PAYMENT
|
|
210
216
|
? t('create.addPaymentButton')
|
|
211
217
|
: t('create.completeOrderButton') })) : needFulfillment ? (_jsx(FulfillmentModal, { order: order, onSubmitted: fulfillOrder, disabled: !canAddFulfillment })) : inModifyState ? (_jsx(ModifyAcceptModal, {})) : null, (order.state === ORDER_STATE.ARRANGING_PAYMENT ||
|
|
212
|
-
order.state === ORDER_STATE.ARRANGING_ADDITIONAL_PAYMENT) && (_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx(Info, { size: 20, className: "text-blue-500" }), _jsx("p", { children: t('addPaymentInfo') })] })), order.state === ORDER_STATE.SHIPPED && (_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx(Info, { size: 20, className: "text-blue-500" }), _jsx("p", { children: t('markFulfillmentInfo') })] }))] }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "outline", size: "icon", children: _jsx(EllipsisVerticalIcon, { className: "size-4" }) }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsx(DropdownMenuItem, { asChild: true, children: _jsx(PossibleOrderStates, { orderState: order.state }) }), order.state !== ORDER_STATE.CANCELLED &&
|
|
218
|
+
order.state === ORDER_STATE.ARRANGING_ADDITIONAL_PAYMENT) && (_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx(Info, { size: 20, className: "text-blue-500" }), _jsx("p", { children: t('addPaymentInfo') })] })), order.state === ORDER_STATE.SHIPPED && (_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx(Info, { size: 20, className: "text-blue-500" }), _jsx("p", { children: t('markFulfillmentInfo') })] }))] })), canUpdateOrder && (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "outline", size: "icon", children: _jsx(EllipsisVerticalIcon, { className: "size-4" }) }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsx(DropdownMenuItem, { asChild: true, children: _jsx(PossibleOrderStates, { orderState: order.state }) }), order.state !== ORDER_STATE.CANCELLED &&
|
|
213
219
|
order.state !== ORDER_STATE.DRAFT &&
|
|
214
220
|
currentPossibilities?.to.length && (_jsx(DropdownMenuItem, { asChild: true, children: _jsx(Button, { onClick: () => setManualChange({ state: true, toAction: undefined }), variant: "ghost", className: "w-full cursor-pointer justify-start px-4 py-2 text-orange-400 hover:text-orange-400 focus-visible:ring-transparent dark:text-orange-400 dark:hover:text-orange-400 dark:focus-visible:ring-transparent", children: t('topActions.manualChangeStatus') }) })), actions?.dropdown?.map(({ component }) => React.createElement(component)) || null, order.state === ORDER_STATE.PARTIALLY_DELIVERED ||
|
|
215
221
|
order.state === ORDER_STATE.SHIPPED ||
|
|
216
222
|
order.state === ORDER_STATE.PAYMENT_SETTLED ||
|
|
217
223
|
order.state === ORDER_STATE.PAYMENT_AUTHORIZED ||
|
|
218
224
|
order.state === ORDER_STATE.PARTIALLY_SHIPPED ? (_jsx(DropdownMenuItem, { asChild: true, children: _jsx(Button, { variant: "ghost", className: "w-full cursor-pointer justify-start px-4 py-2 text-blue-400 hover:text-blue-400 dark:text-blue-400 dark:hover:text-blue-400", onClick: changeOrderStatus.bind(null, ORDER_STATE.MODIFYING), children: t('create.modifyOrder') }) })) : null, order.state === ORDER_STATE.ARRANGING_PAYMENT && (_jsx(DropdownMenuItem, { asChild: true, children: _jsx(ConfirmationDialog, { onConfirm: () => cancelOrder(), title: t('create.areYouSure'), description: t('create.cancelOrderMessage'), additionalElement: _jsx(SimpleSelect, { label: t('cancellationLabel'), value: cancellationReason, onValueChange: setCancellationReason, options: reasonOptions }), children: _jsx(Button, { variant: "ghost", className: "w-full justify-start px-4 py-2 text-red-400 hover:text-red-400 dark:text-red-400 dark:hover:text-red-400", children: t('create.cancelOrder') }) }) })), (order.state === ORDER_STATE.PAYMENT_SETTLED ||
|
|
219
|
-
order.state === ORDER_STATE.ARRANGING_ADDITIONAL_PAYMENT) && (_jsx(CancelAndRefundDialog, {
|
|
225
|
+
order.state === ORDER_STATE.ARRANGING_ADDITIONAL_PAYMENT) && (_jsx(CancelAndRefundDialog, { onConfirm: cancelAndRefund })), order.state === ORDER_STATE.DRAFT && canDeleteOrder && (_jsx(DropdownMenuItem, { asChild: true, children: _jsxs(AlertDialog, { children: [_jsx(AlertDialogTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", className: "w-full justify-start px-4 py-2 text-red-400 hover:text-red-400 dark:hover:text-red-400", children: t('deleteDraft.button') }) }), _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: t('deleteDraft.title') }), _jsx(AlertDialogDescription, { children: t('deleteDraft.descriptionDraft') })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: t('deleteDraft.cancel') }), _jsx(AlertDialogAction, { onClick: () => deleteDraftOrder(), children: t('deleteDraft.confirm') })] })] })] }) }))] })] }))] }));
|
|
220
226
|
};
|
|
@@ -2,6 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useCallback } from 'react';
|
|
3
3
|
import { useParams } from 'react-router';
|
|
4
4
|
import { useValidators, DetailView, createDeenruvForm, useMutation, getMutation, useTranslation, } from '@deenruv/react-ui-devkit';
|
|
5
|
+
import { Permission } from '@deenruv/admin-types';
|
|
5
6
|
import { PaymentMethodDetailView } from "./_components/PaymentMethodDetailView.js";
|
|
6
7
|
const CreatePaymentMethodMutation = getMutation('createPaymentMethod');
|
|
7
8
|
const EditPaymentMethodMutation = getMutation('updatePaymentMethod');
|
|
@@ -64,5 +65,9 @@ export const PaymentMethodsDetailPage = () => {
|
|
|
64
65
|
onSubmitted: onSubmitHandler,
|
|
65
66
|
onDeleted: onDeleteHandler,
|
|
66
67
|
}),
|
|
68
|
+
}, permissions: {
|
|
69
|
+
create: [Permission.CreatePaymentMethod, Permission.CreateSettings],
|
|
70
|
+
edit: [Permission.UpdatePaymentMethod, Permission.UpdateSettings],
|
|
71
|
+
delete: [Permission.DeletePaymentMethod, Permission.DeleteSettings],
|
|
67
72
|
} }) }));
|
|
68
73
|
};
|
|
@@ -37,5 +37,5 @@ export const PaymentMethodsListPage = () => {
|
|
|
37
37
|
{ key: 'name', operator: 'StringOperators' },
|
|
38
38
|
{ key: 'code', operator: 'StringOperators' },
|
|
39
39
|
{ key: 'enabled', operator: 'BooleanOperators' },
|
|
40
|
-
], additionalBulkActions: [...EntityChannelManagementBulkAction(tableId)], detailLinkColumn: "id", searchFields: ['name', 'code'], hideColumns: ['customFields', 'translations', 'collections', 'variantList'], entityName: 'PaymentMethod', route: Routes['paymentMethods'], tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreatePaymentMethod], deletePermissions: [Permission.DeletePaymentMethod] }));
|
|
40
|
+
], additionalBulkActions: [...EntityChannelManagementBulkAction(tableId)], detailLinkColumn: "id", searchFields: ['name', 'code'], hideColumns: ['customFields', 'translations', 'collections', 'variantList'], entityName: 'PaymentMethod', route: Routes['paymentMethods'], tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreatePaymentMethod, Permission.CreateSettings], deletePermissions: [Permission.DeletePaymentMethod, Permission.DeleteSettings] }));
|
|
41
41
|
};
|
|
@@ -4,7 +4,7 @@ import { useTranslation, apiClient, Badge, deepMerge, DetailList, ListLocations,
|
|
|
4
4
|
import { useNavigate } from 'react-router';
|
|
5
5
|
const tableId = 'productVariants-list-view';
|
|
6
6
|
const { selector } = ListLocations[tableId];
|
|
7
|
-
const fetch = async ({ page, perPage, filter, filterOperator, sort }, additionalSelector) => {
|
|
7
|
+
const fetch = async ({ page, perPage, filter, filterOperator, searchTerm, sort }, additionalSelector) => {
|
|
8
8
|
const response = await apiClient('query')({
|
|
9
9
|
productVariants: [
|
|
10
10
|
{
|
|
@@ -12,6 +12,7 @@ const fetch = async ({ page, perPage, filter, filterOperator, sort }, additional
|
|
|
12
12
|
take: perPage,
|
|
13
13
|
skip: (page - 1) * perPage,
|
|
14
14
|
filterOperator: filterOperator,
|
|
15
|
+
...(searchTerm && { searchTerm }),
|
|
15
16
|
sort: sort ? { [sort.key]: sort.sortDir } : { createdAt: SortOrder.DESC },
|
|
16
17
|
...(filter && { filter }),
|
|
17
18
|
},
|
|
@@ -56,14 +57,14 @@ export const ProductVariantsListPage = () => {
|
|
|
56
57
|
}, facetValuesIds: props.value.in }));
|
|
57
58
|
},
|
|
58
59
|
},
|
|
59
|
-
], detailLinkColumn: "id", searchFields: ['name', 'sku'], hideColumns: ['customFields', 'productId', 'stockOnHand', 'stockAllocated'], entityName: 'ProductVariant', route: {
|
|
60
|
+
], detailLinkColumn: "id", searchFields: ['name', 'sku'], searchTransport: "searchTerm", hideColumns: ['customFields', 'productId', 'stockOnHand', 'stockAllocated'], entityName: 'ProductVariant', route: {
|
|
60
61
|
...Routes.productVariants,
|
|
61
62
|
edit: (variantId, row) => {
|
|
62
63
|
navigate(`/admin-ui/products/${row.original['productId']}?tab=variants&variantId=${variantId}`, {
|
|
63
64
|
viewTransition: true,
|
|
64
65
|
});
|
|
65
66
|
},
|
|
66
|
-
}, noCreateButton: true, tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreateProduct], deletePermissions: [Permission.DeleteProduct], additionalColumns: [
|
|
67
|
+
}, noCreateButton: true, tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreateProduct, Permission.CreateCatalog], deletePermissions: [Permission.DeleteProduct, Permission.DeleteCatalog], additionalColumns: [
|
|
67
68
|
{
|
|
68
69
|
id: 'stock',
|
|
69
70
|
accessorKey: 'stock',
|
|
@@ -116,8 +116,8 @@ export const ProductsDetailPage = () => {
|
|
|
116
116
|
}, defaultTabs: defaultTabs, topActions: {
|
|
117
117
|
inline: [_jsx(ProductStorefrontAction, {}, "product-storefront-action")],
|
|
118
118
|
}, permissions: {
|
|
119
|
-
create: Permission.CreateProduct,
|
|
120
|
-
edit: Permission.UpdateProduct,
|
|
121
|
-
delete: Permission.DeleteProduct,
|
|
119
|
+
create: [Permission.CreateProduct, Permission.CreateCatalog],
|
|
120
|
+
edit: [Permission.UpdateProduct, Permission.UpdateCatalog],
|
|
121
|
+
delete: [Permission.DeleteProduct, Permission.DeleteCatalog],
|
|
122
122
|
} }) }));
|
|
123
123
|
};
|
|
@@ -4,7 +4,7 @@ import { apiClient, deepMerge, DetailList, ListBadge, ListLocations, Routes, Tab
|
|
|
4
4
|
// import { FacetValueSelector } from '@deenruv/react-ui-devkit/FacetValueSelector.js';
|
|
5
5
|
const tableId = 'products-list-view';
|
|
6
6
|
const { selector } = ListLocations[tableId];
|
|
7
|
-
const fetch = async ({ page, perPage, filter, filterOperator, sort }, customFieldsSelector, additionalSelector) => {
|
|
7
|
+
const fetch = async ({ page, perPage, filter, filterOperator, searchTerm, sort }, customFieldsSelector, additionalSelector) => {
|
|
8
8
|
const response = await apiClient('query')({
|
|
9
9
|
['products']: [
|
|
10
10
|
{
|
|
@@ -12,6 +12,7 @@ const fetch = async ({ page, perPage, filter, filterOperator, sort }, customFiel
|
|
|
12
12
|
take: perPage,
|
|
13
13
|
skip: (page - 1) * perPage,
|
|
14
14
|
filterOperator: filterOperator,
|
|
15
|
+
...(searchTerm && { searchTerm }),
|
|
15
16
|
sort: sort ? { [sort.key]: sort.sortDir } : { createdAt: SortOrder.DESC },
|
|
16
17
|
...(filter && { filter }),
|
|
17
18
|
},
|
|
@@ -52,7 +53,7 @@ export const ProductsListPage = () => {
|
|
|
52
53
|
}, facetValuesIds: props.value.in }));
|
|
53
54
|
},
|
|
54
55
|
},
|
|
55
|
-
], additionalBulkActions: [...EntityChannelManagementBulkAction(tableId), EntityFacetManagementBulkAction(tableId)], detailLinkColumn: "id", searchFields: ['name', 'slug', 'sku'], hideColumns: ['customFields', 'translations', 'collections', 'variantList'], entityName: 'Product', route: Routes['products'], tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreateProduct], deletePermissions: [Permission.DeleteProduct], suggestedOrderColumns: {
|
|
56
|
+
], additionalBulkActions: [...EntityChannelManagementBulkAction(tableId), EntityFacetManagementBulkAction(tableId)], detailLinkColumn: "id", searchFields: ['name', 'slug', 'sku'], searchTransport: "searchTerm", hideColumns: ['customFields', 'translations', 'collections', 'variantList'], entityName: 'Product', route: Routes['products'], tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreateProduct, Permission.CreateCatalog], deletePermissions: [Permission.DeleteProduct, Permission.DeleteCatalog], suggestedOrderColumns: {
|
|
56
57
|
featuredAsset: 4,
|
|
57
58
|
variants: 99,
|
|
58
59
|
}, additionalColumns: [
|
|
@@ -2,6 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useCallback } from 'react';
|
|
3
3
|
import { useParams } from 'react-router';
|
|
4
4
|
import { useTranslation, DetailView, createDeenruvForm, getMutation, useMutation } from '@deenruv/react-ui-devkit';
|
|
5
|
+
import { Permission } from '@deenruv/admin-types';
|
|
5
6
|
import { ShippingMethodDetailView } from "./_components/ShippingMethodDetailView.js";
|
|
6
7
|
const isMissingConfigArgValue = (value) => value == null || value.trim() === '';
|
|
7
8
|
const hasMissingConfigArgs = (operation) => {
|
|
@@ -107,5 +108,9 @@ export const ShippingMethodsDetailPage = () => {
|
|
|
107
108
|
onSubmitted: onSubmitHandler,
|
|
108
109
|
onDeleted: onDeleteHandler,
|
|
109
110
|
}),
|
|
111
|
+
}, permissions: {
|
|
112
|
+
create: [Permission.CreateShippingMethod, Permission.CreateSettings],
|
|
113
|
+
edit: [Permission.UpdateShippingMethod, Permission.UpdateSettings],
|
|
114
|
+
delete: [Permission.DeleteShippingMethod, Permission.DeleteSettings],
|
|
110
115
|
} }) }));
|
|
111
116
|
};
|
|
@@ -36,5 +36,5 @@ export const ShippingMethodsListPage = () => {
|
|
|
36
36
|
return (_jsx(DetailList, { filterFields: [
|
|
37
37
|
{ key: 'name', operator: 'StringOperators' },
|
|
38
38
|
{ key: 'code', operator: 'StringOperators' },
|
|
39
|
-
], additionalBulkActions: [...EntityChannelManagementBulkAction(tableId)], detailLinkColumn: "id", searchFields: ['name', 'code'], hideColumns: ['customFields', 'translations', 'collections', 'variantList'], entityName: 'ShippingMethod', route: Routes['shippingMethods'], tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreateShippingMethod], deletePermissions: [Permission.DeleteShippingMethod] }));
|
|
39
|
+
], additionalBulkActions: [...EntityChannelManagementBulkAction(tableId)], detailLinkColumn: "id", searchFields: ['name', 'code'], hideColumns: ['customFields', 'translations', 'collections', 'variantList'], entityName: 'ShippingMethod', route: Routes['shippingMethods'], tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreateShippingMethod, Permission.CreateSettings], deletePermissions: [Permission.DeleteShippingMethod, Permission.DeleteSettings] }));
|
|
40
40
|
};
|
|
@@ -78,8 +78,8 @@ export const TaxCategoriesDetailPage = () => {
|
|
|
78
78
|
onDeleted: onDeleteHandler,
|
|
79
79
|
}),
|
|
80
80
|
}, permissions: {
|
|
81
|
-
create: Permission.CreateTaxCategory,
|
|
82
|
-
edit: Permission.UpdateTaxCategory,
|
|
83
|
-
delete: Permission.DeleteTaxCategory,
|
|
81
|
+
create: [Permission.CreateTaxCategory, Permission.CreateSettings],
|
|
82
|
+
edit: [Permission.UpdateTaxCategory, Permission.UpdateSettings],
|
|
83
|
+
delete: [Permission.DeleteTaxCategory, Permission.DeleteSettings],
|
|
84
84
|
} }) }));
|
|
85
85
|
};
|
|
@@ -42,5 +42,5 @@ export const TaxCategoriesListPage = () => {
|
|
|
42
42
|
return (_jsx(DetailList, { filterFields: [
|
|
43
43
|
{ key: 'name', operator: 'StringOperators' },
|
|
44
44
|
{ key: 'isDefault', operator: 'BooleanOperators' },
|
|
45
|
-
], detailLinkColumn: "id", searchFields: ['name'], hideColumns: ['customFields', 'translations'], entityName: 'TaxCategory', route: Routes['taxCategories'], tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreateTaxCategory], deletePermissions: [Permission.DeleteTaxCategory] }));
|
|
45
|
+
], detailLinkColumn: "id", searchFields: ['name'], hideColumns: ['customFields', 'translations'], entityName: 'TaxCategory', route: Routes['taxCategories'], tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreateTaxCategory, Permission.CreateSettings], deletePermissions: [Permission.DeleteTaxCategory, Permission.DeleteSettings] }));
|
|
46
46
|
};
|
|
@@ -3,6 +3,7 @@ import { useCallback } from 'react';
|
|
|
3
3
|
import { useParams } from 'react-router';
|
|
4
4
|
import { useValidators, DetailView, createDeenruvForm, getMutation, useMutation, useTranslation, } from '@deenruv/react-ui-devkit';
|
|
5
5
|
import { TaxRateDetailView } from "./_components/TaxRateDetailView.js";
|
|
6
|
+
import { Permission } from '@deenruv/admin-types';
|
|
6
7
|
const CreateTaxRateMutation = getMutation('createTaxRate');
|
|
7
8
|
const EditTaxRateMutation = getMutation('updateTaxRate');
|
|
8
9
|
const DeleteTaxRateMutation = getMutation('deleteTaxRate');
|
|
@@ -58,5 +59,9 @@ export const TaxRatesDetailPage = () => {
|
|
|
58
59
|
onSubmitted: onSubmitHandler,
|
|
59
60
|
onDeleted: onDeleteHandler,
|
|
60
61
|
}),
|
|
62
|
+
}, permissions: {
|
|
63
|
+
create: [Permission.CreateTaxRate, Permission.CreateSettings],
|
|
64
|
+
edit: [Permission.UpdateTaxRate, Permission.UpdateSettings],
|
|
65
|
+
delete: [Permission.DeleteTaxRate, Permission.DeleteSettings],
|
|
61
66
|
} }) }));
|
|
62
67
|
};
|
|
@@ -60,5 +60,5 @@ export const TaxRatesListPage = () => {
|
|
|
60
60
|
header: () => _jsx(TableLabel, { children: t('table.customerGroup') }),
|
|
61
61
|
cell: ({ row }) => row.original.customerGroup?.name ?? '—',
|
|
62
62
|
},
|
|
63
|
-
], entityName: 'TaxRate', route: Routes['taxRates'], tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreateTaxRate], deletePermissions: [Permission.DeleteTaxRate] }));
|
|
63
|
+
], entityName: 'TaxRate', route: Routes['taxRates'], tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreateTaxRate, Permission.CreateSettings], deletePermissions: [Permission.DeleteTaxRate, Permission.DeleteSettings] }));
|
|
64
64
|
};
|
|
@@ -3,6 +3,7 @@ import { useCallback } from 'react';
|
|
|
3
3
|
import { useParams } from 'react-router';
|
|
4
4
|
import { useValidators, apiClient, useMutation, DetailView, getMutation, createDeenruvForm, useTranslation, } from '@deenruv/react-ui-devkit';
|
|
5
5
|
import { ZoneDetailView } from "./_components/ZoneDetailView.js";
|
|
6
|
+
import { Permission } from '@deenruv/admin-types';
|
|
6
7
|
const CreateZoneMutation = getMutation('createZone');
|
|
7
8
|
const EditZoneMutation = getMutation('updateZone');
|
|
8
9
|
const DeleteZoneMutation = getMutation('deleteZone');
|
|
@@ -82,5 +83,9 @@ export const ZonesDetailPage = () => {
|
|
|
82
83
|
onSubmitted: onSubmitHandler,
|
|
83
84
|
onDeleted: onDeleteHandler,
|
|
84
85
|
}),
|
|
86
|
+
}, permissions: {
|
|
87
|
+
create: [Permission.CreateZone, Permission.CreateSettings],
|
|
88
|
+
edit: [Permission.UpdateZone, Permission.UpdateSettings],
|
|
89
|
+
delete: [Permission.DeleteZone, Permission.DeleteSettings],
|
|
85
90
|
} }) }));
|
|
86
91
|
};
|
package/dist/pages/zones/List.js
CHANGED
|
@@ -48,5 +48,5 @@ export const ZonesListPage = () => {
|
|
|
48
48
|
header: () => _jsx(TableLabel, { children: t('table.members') }),
|
|
49
49
|
cell: ({ row }) => row.original.members.length,
|
|
50
50
|
},
|
|
51
|
-
], entityName: 'Zone', route: Routes['zones'], tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreateZone], deletePermissions: [Permission.DeleteZone] }));
|
|
51
|
+
], entityName: 'Zone', route: Routes['zones'], tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreateZone, Permission.CreateSettings], deletePermissions: [Permission.DeleteZone, Permission.DeleteSettings] }));
|
|
52
52
|
};
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const ADMIN_DASHBOARD_VERSION = '1.0.17-dev.
|
|
1
|
+
export const ADMIN_DASHBOARD_VERSION = '1.0.17-dev.23';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deenruv/admin-dashboard",
|
|
3
|
-
"version": "1.0.17-dev.
|
|
3
|
+
"version": "1.0.17-dev.23",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"style": "dist/index.css",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -80,10 +80,10 @@
|
|
|
80
80
|
"yup": "^1.7.1",
|
|
81
81
|
"zod": "^4.4.3",
|
|
82
82
|
"zustand": "^5.0.14",
|
|
83
|
-
"@deenruv/
|
|
84
|
-
"@deenruv/admin-types": "1.0.17-dev.
|
|
85
|
-
"@deenruv/react-ui-devkit": "1.0.17-dev.
|
|
86
|
-
"@deenruv/
|
|
83
|
+
"@deenruv/admin-dashboard": "1.0.17-dev.23",
|
|
84
|
+
"@deenruv/admin-types": "1.0.17-dev.23",
|
|
85
|
+
"@deenruv/react-ui-devkit": "1.0.17-dev.23",
|
|
86
|
+
"@deenruv/deenruv-examples-plugin": "1.0.17-dev.23"
|
|
87
87
|
},
|
|
88
88
|
"devDependencies": {
|
|
89
89
|
"@tailwindcss/typography": "^0.5.20",
|