"feat: enhance order management with new APIs and schema changes"

This commit is contained in:
louiscklaw
2025-05-30 11:40:25 +08:00
parent 834f58bde1
commit 5a707427c6
32 changed files with 1004 additions and 122 deletions

View File

@@ -1,3 +1,4 @@
// src/sections/order/order-details-history.tsx
import type { IOrderHistory } from 'src/types/order';
import Box from '@mui/material/Box';
@@ -13,6 +14,7 @@ import TimelineConnector from '@mui/lab/TimelineConnector';
import TimelineItem, { timelineItemClasses } from '@mui/lab/TimelineItem';
import { fDateTime } from 'src/utils/format-time';
import { useTranslation } from 'react-i18next';
// ----------------------------------------------------------------------
@@ -21,6 +23,8 @@ type Props = {
};
export function OrderDetailsHistory({ history }: Props) {
const { t } = useTranslation();
const renderSummary = () => (
<Paper
variant="outlined"
@@ -37,22 +41,22 @@ export function OrderDetailsHistory({ history }: Props) {
}}
>
<div>
<Box sx={{ mb: 0.5, color: 'text.disabled' }}>Order time</Box>
<Box sx={{ mb: 0.5, color: 'text.disabled' }}>{t('Order time')}</Box>
{fDateTime(history?.orderTime)}
</div>
<div>
<Box sx={{ mb: 0.5, color: 'text.disabled' }}>Payment time</Box>
<Box sx={{ mb: 0.5, color: 'text.disabled' }}>{t('Payment time')}</Box>
{fDateTime(history?.orderTime)}
</div>
<div>
<Box sx={{ mb: 0.5, color: 'text.disabled' }}>Delivery time for the carrier</Box>
<Box sx={{ mb: 0.5, color: 'text.disabled' }}>{t('Delivery time for the carrier')}</Box>
{fDateTime(history?.orderTime)}
</div>
<div>
<Box sx={{ mb: 0.5, color: 'text.disabled' }}>Completion time</Box>
<Box sx={{ mb: 0.5, color: 'text.disabled' }}>{t('Completion time')}</Box>
{fDateTime(history?.orderTime)}
</div>
</Paper>

View File

@@ -12,6 +12,7 @@ import { fCurrency } from 'src/utils/format-number';
import { Iconify } from 'src/components/iconify';
import { Scrollbar } from 'src/components/scrollbar';
import { useTranslation } from 'react-i18next';
// ----------------------------------------------------------------------
@@ -34,6 +35,7 @@ export function OrderDetailsItems({
totalAmount,
...other
}: Props) {
const { t } = useTranslation();
const renderTotal = () => (
<Box
sx={{
@@ -47,32 +49,32 @@ export function OrderDetailsItems({
}}
>
<Box sx={{ display: 'flex' }}>
<Box sx={{ color: 'text.secondary' }}>Subtotal</Box>
<Box sx={{ color: 'text.secondary' }}>{t('Subtotal')}</Box>
<Box sx={{ width: 160, typography: 'subtitle2' }}>{fCurrency(subtotal) || '-'}</Box>
</Box>
<Box sx={{ display: 'flex' }}>
<Box sx={{ color: 'text.secondary' }}>Shipping</Box>
<Box sx={{ color: 'text.secondary' }}>{t('Shipping')}</Box>
<Box sx={{ width: 160, ...(shipping && { color: 'error.main' }) }}>
{shipping ? `- ${fCurrency(shipping)}` : '-'}
</Box>
</Box>
<Box sx={{ display: 'flex' }}>
<Box sx={{ color: 'text.secondary' }}>Discount</Box>
<Box sx={{ color: 'text.secondary' }}>{t('Discount')}</Box>
<Box sx={{ width: 160, ...(discount && { color: 'error.main' }) }}>
{discount ? `- ${fCurrency(discount)}` : '-'}
</Box>
</Box>
<Box sx={{ display: 'flex' }}>
<Box sx={{ color: 'text.secondary' }}>Taxes</Box>
<Box sx={{ color: 'text.secondary' }}>{t('Taxes')}</Box>
<Box sx={{ width: 160 }}>{taxes ? fCurrency(taxes) : '-'}</Box>
</Box>
<Box sx={{ display: 'flex', typography: 'subtitle1' }}>
<div>Total</div>
<div>{t('Total')}</div>
<Box sx={{ width: 160 }}>{fCurrency(totalAmount) || '-'}</Box>
</Box>
</Box>

View File

@@ -1,3 +1,5 @@
// src/sections/order/order-details-toolbar.tsx
import type { IDateValue } from 'src/types/common';
import { usePopover } from 'minimal-shared/hooks';
@@ -17,6 +19,7 @@ import { fDateTime } from 'src/utils/format-time';
import { Label } from 'src/components/label';
import { Iconify } from 'src/components/iconify';
import { CustomPopover } from 'src/components/custom-popover';
import { useTranslation } from 'react-i18next';
// ----------------------------------------------------------------------
@@ -37,6 +40,7 @@ export function OrderDetailsToolbar({
statusOptions,
onChangeStatus,
}: Props) {
const { t } = useTranslation();
const menuActions = usePopover();
const renderMenuActions = () => (
@@ -56,7 +60,7 @@ export function OrderDetailsToolbar({
onChangeStatus(option.value);
}}
>
{option.label}
{t(option.label)}
</MenuItem>
))}
</MenuList>
@@ -124,11 +128,11 @@ export function OrderDetailsToolbar({
variant="outlined"
startIcon={<Iconify icon="solar:printer-minimalistic-bold" />}
>
Print
{t('Print (not implemented)')}
</Button>
<Button color="inherit" variant="contained" startIcon={<Iconify icon="solar:pen-bold" />}>
Edit
{t('Edit')}
</Button>
</Box>
</Box>

View File

@@ -1,6 +1,8 @@
// src/sections/order/view/order-details-view.tsx
import type { IOrderItem } from 'src/types/order';
import { useState, useCallback } from 'react';
import { useState, useCallback, useEffect } from 'react';
import Box from '@mui/material/Box';
import Card from '@mui/material/Card';
@@ -19,19 +21,39 @@ import { OrderDetailsPayment } from '../order-details-payment';
import { OrderDetailsCustomer } from '../order-details-customer';
import { OrderDetailsDelivery } from '../order-details-delivery';
import { OrderDetailsShipping } from '../order-details-shipping';
import { useTranslate } from 'src/locales';
import { useTranslation } from 'react-i18next';
import { changeStatus } from 'src/actions/order';
import { toast } from 'sonner';
// ----------------------------------------------------------------------
type Props = {
order?: IOrderItem;
order: IOrderItem;
};
export function OrderDetailsView({ order }: Props) {
const [status, setStatus] = useState(order?.status);
const { t } = useTranslation();
const handleChangeStatus = useCallback((newValue: string) => {
setStatus(newValue);
}, []);
const [status, setStatus] = useState(order.status);
const handleChangeStatus = useCallback(
async (newValue: string) => {
setStatus(newValue);
// change order status
try {
if (order?.id) {
await changeStatus(order.id, newValue);
toast.success('order status updated');
}
} catch (error) {
console.error(error);
toast.warning('error during update order status');
}
},
[order.id]
);
return (
<DashboardContent>
@@ -47,7 +69,12 @@ export function OrderDetailsView({ order }: Props) {
<Grid container spacing={3}>
<Grid size={{ xs: 12, md: 8 }}>
<Box
sx={{ gap: 3, display: 'flex', flexDirection: { xs: 'column-reverse', md: 'column' } }}
sx={{
//
gap: 3,
display: 'flex',
flexDirection: { xs: 'column-reverse', md: 'column' },
}}
>
<OrderDetailsItems
items={order?.items}

View File

@@ -3,7 +3,7 @@
import type { TableHeadCellProps } from 'src/components/table';
import type { IOrderItem, IOrderTableFilters } from 'src/types/order';
import { useState, useCallback } from 'react';
import { useState, useCallback, useEffect } from 'react';
import { varAlpha } from 'minimal-shared/utils';
import { useBoolean, useSetState } from 'minimal-shared/hooks';
@@ -46,6 +46,8 @@ import { OrderTableRow } from '../order-table-row';
import { OrderTableToolbar } from '../order-table-toolbar';
import { OrderTableFiltersResult } from '../order-table-filters-result';
import { useTranslation } from 'react-i18next';
import { useRouter } from 'src/routes/hooks';
import { deleteOrder, useGetOrders } from 'src/actions/order';
// ----------------------------------------------------------------------
@@ -55,12 +57,7 @@ const STATUS_OPTIONS = [{ value: 'all', label: 'All' }, ...ORDER_STATUS_OPTIONS]
export function OrderListView() {
const { t } = useTranslation();
const table = useTable({ defaultOrderBy: 'orderNumber' });
const confirmDialog = useBoolean();
const [tableData, setTableData] = useState<IOrderItem[]>(_orders);
const router = useRouter();
const TABLE_HEAD: TableHeadCellProps[] = [
{ id: 'orderNumber', label: t('Order'), width: 88 },
@@ -72,6 +69,18 @@ export function OrderListView() {
{ id: '', width: 88 },
];
const { orders, mutate, ordersLoading } = useGetOrders();
const table = useTable({ defaultOrderBy: 'orderNumber' });
const confirmDialog = useBoolean();
const [tableData, setTableData] = useState<IOrderItem[]>([]);
useEffect(() => {
setTableData(orders);
}, [orders]);
const filters = useSetState<IOrderTableFilters>({
name: '',
status: 'all',
@@ -99,16 +108,23 @@ export function OrderListView() {
const notFound = (!dataFiltered.length && canReset) || !dataFiltered.length;
const handleDeleteRow = useCallback(
(id: string) => {
const deleteRow = tableData.filter((row) => row.id !== id);
async (id: string) => {
// const deleteRow = tableData.filter((row) => row.id !== id);
toast.success('Delete success!');
try {
await deleteOrder(id);
toast.success('Delete success!');
mutate();
} catch (error) {
console.error(error);
toast.error('Delete failed!');
}
setTableData(deleteRow);
table.onUpdatePageDeleteRow(dataInPage.length);
// toast.success('Delete success!');
// setTableData(deleteRow);
// table.onUpdatePageDeleteRow(dataInPage.length);
},
[dataInPage.length, table, tableData]
[table, tableData, mutate]
);
const handleDeleteRows = useCallback(() => {
@@ -133,7 +149,7 @@ export function OrderListView() {
<ConfirmDialog
open={confirmDialog.value}
onClose={confirmDialog.onFalse}
title="Delete"
title={t('Delete')}
content={
<>
Are you sure want to delete <strong> {table.selected.length} </strong> items?
@@ -145,7 +161,7 @@ export function OrderListView() {
color="error"
onClick={() => {
handleDeleteRows();
confirmDialog.onFalse();
// confirmDialog.onFalse();
}}
>
{t('Delete')}
@@ -154,12 +170,20 @@ export function OrderListView() {
/>
);
useEffect(() => {
mutate();
}, []);
if (!orders) return <>loading</>;
if (ordersLoading) return <>loading</>;
return (
<>
<DashboardContent>
<CustomBreadcrumbs
heading="List"
links={[
//
{ name: t('Dashboard'), href: paths.dashboard.root },
{ name: t('Order'), href: paths.dashboard.order.root },
{ name: t('List') },