@@ -56,77 +390,551 @@ export function LoginPage() {

-
شیرینیفروشی بلوط
+
هلدینگ غذایی بلوط
+
+
+
+
+
-
ورود به پنل
-
برای مدیریت فروشگاه وارد شوید
+
{title}
+
{subtitle}
-
+ )
+}
diff --git a/src/pages/OrdersPage.module.css b/src/pages/OrdersPage.module.css
index dff1cd1..4123ff8 100644
--- a/src/pages/OrdersPage.module.css
+++ b/src/pages/OrdersPage.module.css
@@ -59,6 +59,29 @@
background: rgba(255, 250, 250, 0.9);
}
+.customerRow {
+ grid-template-columns:
+ minmax(100px, 0.9fr)
+ minmax(100px, 0.95fr)
+ 80px
+ minmax(110px, 1.1fr)
+ minmax(120px, 1.1fr)
+ auto;
+}
+
+.statusBadge {
+ justify-self: stretch;
+ padding: 8px 10px;
+ border-radius: 999px;
+ border: 1px solid rgba(143, 65, 12, 0.16);
+ background: rgba(143, 65, 12, 0.06);
+ color: var(--brown);
+ font-size: 0.78rem;
+ font-weight: 500;
+ line-height: 1.3;
+ text-align: center;
+}
+
.orderId,
.when,
.customer,
@@ -124,15 +147,24 @@
font-size: 0.82rem;
color: var(--text-secondary);
font-variant-numeric: tabular-nums;
+ unicode-bidi: isolate;
}
.statValue {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
font-size: 0.9rem;
font-weight: 500;
color: var(--text-primary);
font-variant-numeric: tabular-nums;
}
+.statValueLine {
+ display: block;
+ white-space: nowrap;
+}
+
.statusBtn {
justify-self: stretch;
padding: 8px 10px;
@@ -363,12 +395,17 @@
grid-template-columns: 1fr 1fr 1.2fr;
}
+ .customerRow {
+ grid-template-columns: 1fr 1fr;
+ }
+
.stat:nth-of-type(1),
.controls {
grid-column: auto;
}
- .statusBtn {
+ .statusBtn,
+ .statusBadge {
grid-column: 1 / -1;
justify-self: start;
}
diff --git a/src/pages/OrdersPage.tsx b/src/pages/OrdersPage.tsx
index e2e7eaa..941b1ad 100644
--- a/src/pages/OrdersPage.tsx
+++ b/src/pages/OrdersPage.tsx
@@ -1,9 +1,8 @@
-import { useEffect, useMemo, useState } from 'react'
-import { Link, useLocation } from 'react-router-dom'
+import { useCallback, useEffect, useState } from 'react'
+import { Link, useLocation, useNavigate } from 'react-router-dom'
import { ChevronRight, Eye, Plus } from 'lucide-react'
import { Header } from '../components/Header'
import {
- buildOrderFromCreateValues,
CreateOrderModal,
type CreateOrderValues,
} from '../components/CreateOrderModal'
@@ -11,12 +10,20 @@ import { OrderDetailsModal } from '../components/OrderDetailsModal'
import { OrderStatusModal } from '../components/OrderStatusModal'
import {
formatOrderDateTime,
+ formatOrderQuantityByUnit,
orderStatusLabel,
- orders as initialOrders,
type Order,
type OrderStatus,
} from '../data/orders'
-import { formatCellNumber } from '../data/users'
+import { formatCellNumber, stripUserTitle } from '../data/users'
+import { ApiError } from '../lib/api'
+import { clearSession } from '../lib/auth'
+import { redirectToLogin } from '../lib/host'
+import {
+ createOrder,
+ listOrders,
+ updateOrderStatus,
+} from '../lib/ordersApi'
import { formatPrice } from '../utils/price'
import styles from './HomePage.module.css'
import pageStyles from './OrdersPage.module.css'
@@ -25,42 +32,105 @@ const PAGE_SIZE = 10
export function OrdersPage() {
const location = useLocation()
- const [orders, setOrders] = useState
(initialOrders)
+ const navigate = useNavigate()
+ const [orders, setOrders] = useState([])
+ const [total, setTotal] = useState(0)
const [page, setPage] = useState(1)
+ const [loading, setLoading] = useState(true)
+ const [error, setError] = useState('')
+ const [busy, setBusy] = useState(false)
const [statusOrder, setStatusOrder] = useState(null)
const [detailsOrder, setDetailsOrder] = useState(null)
const [createOpen, setCreateOpen] = useState(false)
- const totalPages = Math.max(1, Math.ceil(orders.length / PAGE_SIZE))
+ const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE))
+
+ const handleAuthError = useCallback(
+ (err: unknown) => {
+ if (err instanceof ApiError && err.status === 401) {
+ clearSession()
+ redirectToLogin()
+ return true
+ }
+ return false
+ },
+ [navigate],
+ )
+
+ const loadOrders = useCallback(async () => {
+ setLoading(true)
+ setError('')
+ try {
+ const result = await listOrders({ page, pageSize: PAGE_SIZE })
+ setOrders(result.items)
+ setTotal(result.total)
+ } catch (err) {
+ if (handleAuthError(err)) return
+ setOrders([])
+ setTotal(0)
+ setError(
+ err instanceof ApiError
+ ? err.message
+ : 'بارگذاری سفارشها ناموفق بود.',
+ )
+ } finally {
+ setLoading(false)
+ }
+ }, [page, handleAuthError])
+
+ useEffect(() => {
+ void loadOrders()
+ }, [loadOrders])
useEffect(() => {
if (page > totalPages) setPage(totalPages)
}, [page, totalPages])
- const pageOrders = useMemo(() => {
- const start = (page - 1) * PAGE_SIZE
- return orders.slice(start, start + PAGE_SIZE)
- }, [orders, page])
-
- function handleStatusSubmit(status: OrderStatus) {
+ async function handleStatusSubmit(status: OrderStatus) {
if (!statusOrder) return
- setOrders((current) =>
- current.map((item) =>
- item.id === statusOrder.id ? { ...item, status } : item,
- ),
- )
- setStatusOrder(null)
+ setBusy(true)
+ setError('')
+ try {
+ const updated = await updateOrderStatus(statusOrder.id, status)
+ setOrders((current) =>
+ current.map((item) => (item.id === updated.id ? updated : item)),
+ )
+ setStatusOrder(null)
+ } catch (err) {
+ if (handleAuthError(err)) return
+ setError(
+ err instanceof ApiError
+ ? err.message
+ : 'بهروزرسانی وضعیت ناموفق بود.',
+ )
+ } finally {
+ setBusy(false)
+ }
}
- function handleCreateOrder(values: CreateOrderValues) {
- const maxNum = orders.reduce((max, order) => {
- const n = Number(order.id.replace(/\D/g, ''))
- return Number.isFinite(n) ? Math.max(max, n) : max
- }, 1399)
- const order = buildOrderFromCreateValues(values, `BL-${maxNum + 1}`)
- setOrders((current) => [order, ...current])
- setCreateOpen(false)
- setPage(1)
+ async function handleCreateOrder(values: CreateOrderValues) {
+ setBusy(true)
+ setError('')
+ try {
+ await createOrder({
+ customerId: values.customerId,
+ deliveryType: values.deliveryType,
+ branchId: values.branchId,
+ shippingAddressId: values.shippingAddressId,
+ note: values.note || undefined,
+ items: values.items,
+ })
+ setCreateOpen(false)
+ setPage(1)
+ if (page === 1) {
+ await loadOrders()
+ }
+ } catch (err) {
+ if (handleAuthError(err)) return
+ throw err
+ } finally {
+ setBusy(false)
+ }
}
return (
@@ -89,21 +159,34 @@ export function OrdersPage() {