diff --git a/package-lock.json b/package-lock.json index 003039d..956d819 100644 --- a/package-lock.json +++ b/package-lock.json @@ -292,9 +292,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz", + "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==", "dev": true, "license": "MIT", "optional": true, @@ -2996,6 +2996,18 @@ "node": ">=14.0.0" } }, + "node_modules/@unrs/resolver-binding-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, "node_modules/@unrs/resolver-binding-wasm32-wasi/node_modules/@emnapi/runtime": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", @@ -3007,6 +3019,17 @@ "tslib": "^2.4.0" } }, + "node_modules/@unrs/resolver-binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz", diff --git a/src/app/(customer)/(shop)/orders/(routes)/(landing)/page.tsx b/src/app/(customer)/(shop)/orders/(routes)/(landing)/page.tsx index a348c2e..534c2ec 100644 --- a/src/app/(customer)/(shop)/orders/(routes)/(landing)/page.tsx +++ b/src/app/(customer)/(shop)/orders/(routes)/(landing)/page.tsx @@ -1,76 +1,43 @@ -import Link from "next/link"; -import { requireSession } from "@/lib/auth/session"; -import { getOrderGroups } from "@/lib/api/orders"; -import { PAYMENT_STATUS_LABELS_FA } from "@/lib/order-status"; +"use client"; + +import { useOrders } from "@/lib/providers/orders-provider"; import OrderItem from "@/components/orders/order-item"; import OrderEmpty from "@/components/orders/order-empty"; import OrderServerError from "@/components/orders/order-server-error"; import OrdersSkeleton from "@/components/orders/orders-skeleton"; -import OrderHeader from "@/components/orders/order-header"; -export default async function OrdersPage() { - return
- {/* */} - {/* */} - { - Array.from({ length: 20 }, (_, index) => ) +export default function OrdersPage() { + const { data, error, isLoading } = useOrders(); + + if (isLoading) { + return ( +
+ +
+ ); } - {/* - - */} - {/* */} - {/* */} - {/* */} - {/* */} + if (error) { + return ( +
+ +
+ ); + } - {/* - - - - - - - - - - - */} -
- // await requireSession("/orders"); - // const orderGroups = await getOrderGroups(); + if (!data || data.length === 0) { + return ( +
+ +
+ ); + } - // return ( - //
- //

سفارش‌های من

- - // {orderGroups.length === 0 &&

هنوز سفارشی ثبت نکرده‌اید.

} - - //
- // {orderGroups.map((group) => ( - // - //
- // {new Date(group.created_at).toLocaleDateString("fa-IR")} - // {PAYMENT_STATUS_LABELS_FA[group.payment_status]} - //
- //
- // {group.orders.map((order) => ( - // - // {order.store.name} - // - // ))} - //
- //
- // مجموع - // {group.total_amount.toLocaleString("fa-IR")} تومان - //
- // - // ))} - //
- //
- // ); -} + return ( +
+ {data.map((group) => ( + + ))} +
+ ); +} \ No newline at end of file diff --git a/src/app/(customer)/(shop)/orders/layout.tsx b/src/app/(customer)/(shop)/orders/layout.tsx index 3bff83a..549d60a 100644 --- a/src/app/(customer)/(shop)/orders/layout.tsx +++ b/src/app/(customer)/(shop)/orders/layout.tsx @@ -1,15 +1,30 @@ import OrderHeader from '@/components/orders/order-header'; -import React from 'react' +import { requireSession } from "@/lib/auth/session"; +import { getOrderGroups } from "@/lib/api/orders"; +import { OrdersProvider } from "@/lib/providers/orders-provider"; interface OrderLayoutInterface { children: React.ReactNode; } -const OrderLayout = async ({ children }: OrderLayoutInterface) => { - return
- - {children} -
-} +export default async function OrderLayout({ children }: OrderLayoutInterface) { + await requireSession("/login"); -export default OrderLayout \ No newline at end of file + let orderGroups: Awaited> | null = null; + let error: string | null = null; + + try { + orderGroups = await getOrderGroups(); + } catch (e: any) { + error = e?.message ?? "خطا در دریافت سفارش‌ها"; + } + + return ( +
+ + + {children} + +
+ ); +} \ No newline at end of file diff --git a/src/components/orders/order-item.tsx b/src/components/orders/order-item.tsx index 6dd23b9..3d60c47 100644 --- a/src/components/orders/order-item.tsx +++ b/src/components/orders/order-item.tsx @@ -1,95 +1,126 @@ 'use client'; import { useRouter } from 'next/navigation'; -import React from 'react'; +import type { OrderGroup } from "@/types/api"; +import { PAYMENT_STATUS_LABELS_FA } from "@/lib/order-status"; // اگه داری -const OrderItem = () => { +interface OrderItemProps { + orderGroup: OrderGroup; +} + +const OrderItem = ({ orderGroup }: OrderItemProps) => { const router = useRouter(); - const goToOrderDetail = () => router.push(`/orders/uuid`); - - const statusClasses: Record = { - 'ارسال شد': 'bg-teal-bg text-primary-action', - 'در حال آماده‌سازی': 'bg-[#E5F3FF] text-[#007BFF]', - 'در انتظار تأیید': 'bg-[#FFF4E5] text-[#E67E22]', - 'آماده تحویل حضوری': 'bg-[#EBE5FF] text-[#734DBF]', - 'لغو شد': 'bg-[#FCE8E8] text-error', - 'تحویل داده شد': 'bg-emerald-bg text-success', + const goToOrderDetail = () => { + router.push(`/orders/${orderGroup.uuid}`); // یا مسیر واقعی جزئیات }; - const order = { - id: 1, - restaurant: 'فست‌فود برگر', - description: 'ساندویچ + نوشیدنی', - status: 'آماده تحویل حضوری', - time: 'امروز، 11:30', - itemsCount: 3, - totalPrice: 45000, - images: Array.from({ length: 5 }, (_, i) => ({ - id: i, - src: `https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQqIof_DOo6I5-oOLJ1V9_HPfSudXfhewy_5C5Ry7YOO-UXSZ1jvK7-0hw&s=10`, - alt: `product-${i}`, - })), + // وضعیت کلی (از اولین سفارش یا payment_status) + const mainOrder = orderGroup.orders[0]; + const status = mainOrder?.status ?? "placed"; + + // مپ وضعیت به فارسی و کلاس + const statusMap: Record = { + placed: { label: "ثبت شده", className: "bg-[#FFF4E5] text-[#E67E22]" }, + preparing: { label: "در حال آماده‌سازی", className: "bg-[#E5F3FF] text-[#007BFF]" }, + ready_to_ship: { label: "آماده ارسال", className: "bg-[#EBE5FF] text-[#734DBF]" }, + handed_to_courier: { label: "ارسال شد", className: "bg-teal-bg text-primary-action" }, + delivered: { label: "تحویل داده شد", className: "bg-emerald-bg text-success" }, + cancelled: { label: "لغو شد", className: "bg-[#FCE8E8] text-error" }, }; + const currentStatus = statusMap[status] ?? { label: status, className: "bg-gray-100 text-gray-700" }; + + // نام فروشگاه‌ها + const storeNames = orderGroup.orders.map(o => o.store.name).join(" + "); + + // تعداد کل کالاها + const totalItems = orderGroup.orders.reduce((sum, order) => { + return sum + order.items.reduce((s, item) => s + item.quantity, 0); + }, 0); + + // تصاویر محصولات (حداکثر ۵ تا) + const images = orderGroup.orders + .flatMap(order => order.items) + .slice(0, 5) + .map((item, index) => ({ + id: index, + src: "", // اگر تصویر محصول داری اینجا بذار، فعلاً خالی + alt: item.product_name_snapshot, + })); + + // تاریخ و ساعت + const createdDate = new Date(orderGroup.created_at); + const timeText = createdDate.toLocaleDateString("fa-IR", { + weekday: "long", + hour: "2-digit", + minute: "2-digit", + }); + return ( -
-
-
-
- {order.restaurant.charAt(0)} -
-
-

{order.restaurant}

-

{order.description}

-
-
- - - {order.status} - -
- -
- -
-
- {order.images.map((img) => ( -
- {img.alt} +
+ {/* هدر */} +
+
+
+ {mainOrder?.store.logo_url ? ( + {mainOrder.store.name} + ) : ( + mainOrder?.store.name?.charAt(0) ?? "س" + )}
- ))} +
+

{storeNames}

+

+ {orderGroup.orders.length > 1 + ? `${orderGroup.orders.length} فروشگاه` + : mainOrder?.items[0]?.product_name_snapshot ?? "سفارش"} +

+
+
+ + + {currentStatus.label} +
-
-
- {order.time} +
+ + {/* پایین کارت */} +
+
+ {images.length > 0 ? ( + images.map((img) => ( +
+ {img.src ? ( + {img.alt} + ) : ( +
+ {img.alt.charAt(0)} +
+ )} +
+ )) + ) : ( +
بدون تصویر
+ )}
-
- - {order.itemsCount} کالا • {order.totalPrice.toLocaleString('fa-IR')} تومان - + +
+
{timeText}
+
+ {totalItems} کالا • {orderGroup.total_amount.toLocaleString("fa-IR")} تومان +
+ +
- - -
); }; diff --git a/src/lib/api/gooyal-accounts.ts b/src/lib/api/gooyal-accounts.ts index 92a0e16..041b5ad 100644 --- a/src/lib/api/gooyal-accounts.ts +++ b/src/lib/api/gooyal-accounts.ts @@ -1,7 +1,7 @@ import "server-only"; import { getValidSession } from "@/lib/auth/session"; import { ApiError, type WinofyErrorDetails } from "./errors"; -import { logWinofyRequest, logWinofyResponse } from "./dev-logger"; +// import { logWinofyRequest, logWinofyResponse } from "./dev-logger"; const BASE_URL = process.env.GOOYAL_ACCOUNTS_BASE_URL!; @@ -35,7 +35,7 @@ export async function gooyalFetch(path: string, options: GooyalFetchOptions = } } - logWinofyRequest?.(method, url.toString(), body); + // logWinofyRequest?.(method, url.toString(), body); const res = await fetch(url.toString(), { ...rest, @@ -45,12 +45,12 @@ export async function gooyalFetch(path: string, options: GooyalFetchOptions = }); if (res.status === 204) { - logWinofyResponse?.(method, url.toString(), res.status, null); + // logWinofyResponse?.(method, url.toString(), res.status, null); return undefined as T; } const data = await res.json().catch(() => null); - logWinofyResponse?.(method, url.toString(), res.status, data); + // logWinofyResponse?.(method, url.toString(), res.status, data); if (!res.ok) { const envelope = data as { status_message?: string; details?: WinofyErrorDetails } | null; diff --git a/src/lib/providers/orders-provider.tsx b/src/lib/providers/orders-provider.tsx new file mode 100644 index 0000000..12b3900 --- /dev/null +++ b/src/lib/providers/orders-provider.tsx @@ -0,0 +1,42 @@ +"use client"; + +import { createContext, useContext, type ReactNode } from "react"; +import type { OrderGroup } from "@/types/api"; + +type OrdersContextValue = { + data: OrderGroup[] | null; + error: string | null; + isLoading: boolean; +}; + +const OrdersContext = createContext(undefined); + +export function OrdersProvider({ + children, + orderGroups, + error = null, + }: { + children: ReactNode; + orderGroups: OrderGroup[] | null; + error?: string | null; +}) { + return ( + + {children} + + ); +} + +export function useOrders() { + const ctx = useContext(OrdersContext); + if (ctx === undefined) { + throw new Error("useOrders must be used within OrdersProvider"); + } + return ctx; +} \ No newline at end of file