diff --git a/src/app/[name]/(Dialogs)/order/checkout/[id]/components/SummarySection.tsx b/src/app/[name]/(Dialogs)/order/checkout/[id]/components/SummarySection.tsx index 514a620..bc242d9 100644 --- a/src/app/[name]/(Dialogs)/order/checkout/[id]/components/SummarySection.tsx +++ b/src/app/[name]/(Dialogs)/order/checkout/[id]/components/SummarySection.tsx @@ -8,6 +8,7 @@ import { useCheckoutStore } from '../../store/Store'; import { toast } from '@/components/Toast'; import { useCreateOrder } from '../../hooks/useOrderData'; import { extractErrorMessage } from '@/lib/func'; +import { useParams } from 'next/navigation'; type SummarySectionProps = { cartModal: boolean; @@ -19,6 +20,8 @@ export const SummarySection = ({ cartModal, onToggleCartModal }: SummarySectionP const { t } = useTranslation('parallels', { keyPrefix: 'OrderDetail' }); const { isSelectedAddress, isSelectedShipment, isSelectedPayment } = useCheckoutStore(); const { mutate: createOrder } = useCreateOrder(); + const params = useParams(); + const name = params.name as string; const handleSubmit = () => { if (!isSelectedAddress) { @@ -40,6 +43,7 @@ export const SummarySection = ({ cartModal, onToggleCartModal }: SummarySectionP window.location.href = data?.data?.paymentUrl; toast('در حال ریدایرکت به صفحه پرداخت...', 'success'); } else { + window.location.href = `/${name}/verify?orderId=${data?.data?.order?.id}`; toast('سفارش با موفقیت ثبت شد', 'success'); } }, diff --git a/src/app/[name]/(Main)/order/history/hooks/useHistoryData.ts b/src/app/[name]/(Main)/order/history/hooks/useHistoryData.ts new file mode 100644 index 0000000..82e2663 --- /dev/null +++ b/src/app/[name]/(Main)/order/history/hooks/useHistoryData.ts @@ -0,0 +1,9 @@ +import { useQuery } from "@tanstack/react-query"; +import { getOrders } from "../service/Service"; + +export const useGetOrders = () => { + return useQuery({ + queryKey: ["orders"], + queryFn: getOrders, + }); +}; diff --git a/src/app/[name]/(Main)/order/history/page.tsx b/src/app/[name]/(Main)/order/history/page.tsx index 61b11fd..aea4c1f 100644 --- a/src/app/[name]/(Main)/order/history/page.tsx +++ b/src/app/[name]/(Main)/order/history/page.tsx @@ -9,103 +9,48 @@ import { TabHeader } from '@/components/tab/TabHeader'; import { Receipt2, ReceiptItem } from 'iconsax-react'; import Image from 'next/image'; import Link from 'next/link'; -import React from 'react' +import React, { useMemo } from 'react' import { useTranslation } from 'react-i18next'; +import { useGetOrders } from './hooks/useHistoryData'; +import { HistoryOrderItem } from './types/Types'; -const currentOrders = [ - { - id: 0, - address: 'خیابان دانشگاه روبروی بیمارستان خوانساری ساختمان مهر...', - date: 'دوشنبه، 28 مرداد 1403', - price: '560,000', - items: [ - { id: 0, quantity: 1, image: '/assets/images/food-preview.png' }, - { id: 1, quantity: 1, image: '/assets/images/food-preview.png' }, - { id: 2, quantity: 2, image: '/assets/images/food-preview.png' }, - ] - }, - { - id: 1, - address: '1...', - date: 'دوشنبه، 28 مرداد 1403', - price: '560,000', - items: [ - { id: 0, quantity: 1, image: '/assets/images/food-preview.png' }, - { id: 1, quantity: 1, image: '/assets/images/food-preview.png' }, - { id: 2, quantity: 2, image: '/assets/images/food-preview.png' }, - ] - }, - { - id: 2, - address: '2...', - date: 'دوشنبه، 28 مرداد 1403', - price: '560,000', - items: [ - { id: 0, quantity: 1, image: '/assets/images/food-preview.png' }, - { id: 1, quantity: 1, image: '/assets/images/food-preview.png' }, - { id: 2, quantity: 2, image: '/assets/images/food-preview.png' }, - ] - }, - { - id: 3, - address: '3...', - date: 'دوشنبه، 28 مرداد 1403', - price: '560,000', - items: [ - { id: 0, quantity: 1, image: '/assets/images/food-preview.png' }, - { id: 1, quantity: 1, image: '/assets/images/food-preview.png' }, - { id: 2, quantity: 2, image: '/assets/images/food-preview.png' }, - ] - }, - { - id: 7, - address: '2...', - date: 'دوشنبه، 28 مرداد 1403', - price: '560,000', - items: [ - { id: 0, quantity: 1, image: '/assets/images/food-preview.png' }, - { id: 1, quantity: 1, image: '/assets/images/food-preview.png' }, - { id: 2, quantity: 2, image: '/assets/images/food-preview.png' }, - ] - }, - { - id: 4, - address: '3...', - date: 'دوشنبه، 28 مرداد 1403', - price: '560,000', - items: [ - { id: 0, quantity: 1, image: '/assets/images/food-preview.png' }, - { id: 1, quantity: 1, image: '/assets/images/food-preview.png' }, - { id: 2, quantity: 2, image: '/assets/images/food-preview.png' }, - ] - }, - { - id: 5, - address: '2...', - date: 'دوشنبه، 28 مرداد 1403', - price: '560,000', - items: [ - { id: 0, quantity: 1, image: '/assets/images/food-preview.png' }, - { id: 1, quantity: 1, image: '/assets/images/food-preview.png' }, - { id: 2, quantity: 2, image: '/assets/images/food-preview.png' }, - ] - }, - { - id: 6, - address: '3...', - date: 'دوشنبه، 28 مرداد 1403', - price: '560,000', - items: [ - { id: 0, quantity: 1, image: '/assets/images/food-preview.png' }, - { id: 1, quantity: 1, image: '/assets/images/food-preview.png' }, - { id: 2, quantity: 2, image: '/assets/images/food-preview.png' }, - ] - }, -] +const fallbackImage = '/assets/images/food-preview.png'; + +const getFoodImage = (images: string | null | undefined): string => { + if (!images || typeof images !== 'string') { + return fallbackImage; + } + return images; +}; + +const formatDate = (dateString: string): string => { + const date = new Date(dateString); + return date.toLocaleDateString('fa-IR', { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric' + }); +}; function OrdersIndex() { const { t } = useTranslation('orders'); + const { data: ordersData } = useGetOrders(); + + const orders = useMemo(() => ordersData?.data || [], [ordersData?.data]); + + const activeOrders = useMemo(() => { + return orders.filter((order) => { + return order.status !== 'delivered' && order.status !== 'cancelled'; + }); + }, [orders]); + + const previousOrders = useMemo(() => { + return orders.filter((order) => { + return order.status === 'delivered' || order.status === 'cancelled'; + }); + }, [orders]); const categories = [ { id: 0, title: t('Tab.ActiveOrders'), icon: }, @@ -113,17 +58,26 @@ function OrdersIndex() { ] const firstTab = () => { - return currentOrders.map((order) => { + if (activeOrders.length === 0) { + return [
سفارش فعالی وجود ندارد
]; + } + + return activeOrders.map((order) => { + const address = order.address?.address || 'آدرس ثبت نشده'; + const formattedDate = formatDate(order.createdAt); + const formattedPrice = order.total.toLocaleString('fa-IR'); + const items: HistoryOrderItem[] = order.items || []; + return (
-

{order.address}

+

{address}

-

{order.date}

+

{formattedDate}

@@ -131,13 +85,14 @@ function OrdersIndex() {
- {order.items.map((item) => { + {items.map((item) => { + const imageUrl = getFoodImage(item.food.images); return ( order image
- {order.price} T + {formattedPrice} T
@@ -163,17 +118,26 @@ function OrdersIndex() { } const secondTab = () => { - return currentOrders.map((order) => { + if (previousOrders.length === 0) { + return [
سفارش قبلی وجود ندارد
]; + } + + return previousOrders.map((order) => { + const address = order.address?.address || 'آدرس ثبت نشده'; + const formattedDate = formatDate(order.createdAt); + const formattedPrice = order.total.toLocaleString('fa-IR'); + const items: HistoryOrderItem[] = order.items || []; + return (
-

{order.address}

+

{address}

-

{order.date}

+

{formattedDate}

@@ -181,13 +145,14 @@ function OrdersIndex() {
- {order.items.map((item) => { + {items.map((item) => { + const imageUrl = getFoodImage(item.food.images); return ( order image
- {order.price} T + {formattedPrice} T
@@ -222,12 +187,14 @@ function OrdersIndex() { {firstTab()}} - title={categories[0].title} icon={categories[0].icon}> - + title={categories[0].title} + icon={categories[0].icon} + /> {secondTab()}} - title={categories[1].title} icon={categories[1].icon}> - + title={categories[1].title} + icon={categories[1].icon} + />
) diff --git a/src/app/[name]/(Main)/order/history/service/Service.ts b/src/app/[name]/(Main)/order/history/service/Service.ts new file mode 100644 index 0000000..c8f0cfc --- /dev/null +++ b/src/app/[name]/(Main)/order/history/service/Service.ts @@ -0,0 +1,7 @@ +import { api } from "@/config/axios"; +import { OrdersResponse } from "../types/Types"; + +export const getOrders = async (): Promise => { + const { data } = await api.get("/public/orders"); + return data; +}; diff --git a/src/app/[name]/(Main)/order/history/types/Types.ts b/src/app/[name]/(Main)/order/history/types/Types.ts new file mode 100644 index 0000000..2eb1af5 --- /dev/null +++ b/src/app/[name]/(Main)/order/history/types/Types.ts @@ -0,0 +1,165 @@ +import { BaseResponse } from "@/app/[name]/(Main)/types/Types"; +import { ServiceArea } from "@/app/[name]/(Dialogs)/order/checkout/types/Types"; + +export interface HistoryOrderUser { + id: string; + createdAt: string; + updatedAt: string; + deletedAt: string | null; + firstName: string; + lastName: string; + phone: string; + birthDate: string; + marriageDate: string; + referrer: string | null; + isActive: boolean; + gender: boolean; + wallet: number; + points: number; +} + +export interface HistoryOrderRestaurant { + id: string; + createdAt: string; + updatedAt: string; + deletedAt: string | null; + name: string; + slug: string; + logo: string | null; + address: string | null; + menuColor: string | null; + latitude: number | null; + longitude: number | null; + serviceArea: ServiceArea; + isActive: boolean; + establishedYear: number | null; + phoneNumber: string | null; + phone: string; + instagram: string | null; + telegram: string | null; + whatsapp: string | null; + description: string | null; + seoTitle: string | null; + seoDescription: string | null; + tagNames: string | null; + images: string | null; + vat: number; + domain: string; +} + +export interface HistoryOrderDeliveryMethod { + id: string; + createdAt: string; + updatedAt: string; + deletedAt: string | null; + title: string; + method: "dineIn" | "pickup" | "deliveryCar" | "deliveryCourier"; + restaurant: string; + deliveryFee: number; + minOrderPrice: number; + description: string; + enabled: boolean; + order: number; +} + +export interface HistoryOrderAddress { + id: string; + createdAt: string; + updatedAt: string; + deletedAt: string | null; + user: string; + title: string; + address: string; + city: string; + province: string; + postalCode: string; + latitude: number; + longitude: number; + phone: string; + isDefault: boolean; +} + +export interface HistoryOrderPaymentMethod { + id: string; + createdAt: string; + updatedAt: string; + deletedAt: string | null; + restaurant: string; + title: string; + method: "CardOnDelivery" | "Cash" | "Online"; + gateway: string | null; + description: string; + enabled: boolean; + order: number; + merchantId: string | null; +} + +export interface HistoryOrderFood { + id: string; + createdAt: string; + updatedAt: string; + deletedAt: string | null; + restaurant: string; + category: string; + title: string; + desc: string | null; + content: string | null; + price: number; + points: number | null; + order: number | null; + prepareTime: number | null; + sat: boolean; + sun: boolean; + mon: boolean; + breakfast: boolean; + noon: boolean; + dinner: boolean; + stock: number; + stockDefault: number; + isActive: boolean; + images: string | null; + inPlaceServe: boolean; + pickupServe: boolean; + rate: number; + discount: number; +} + +export interface HistoryOrderItem { + id: string; + createdAt: string; + updatedAt: string; + deletedAt: string | null; + order: string; + food: HistoryOrderFood; + quantity: number; + unitPrice: number; + discount: number; + totalPrice: number; +} + +export interface HistoryOrder { + id: string; + createdAt: string; + updatedAt: string; + deletedAt: string | null; + user: HistoryOrderUser; + restaurant: HistoryOrderRestaurant; + deliveryMethod: HistoryOrderDeliveryMethod; + address: HistoryOrderAddress | null; + paymentMethod: HistoryOrderPaymentMethod; + orderNumber: number; + couponDiscount: number; + itemsDiscount: number; + totalDiscount: number; + subTotal: number; + tax: number; + shipmentFee: number; + total: number; + totalItems: number; + status: string; + paymentStatus: string; + items: HistoryOrderItem[]; +} + +export type OrdersResponse = BaseResponse; + diff --git a/src/lib/api/axiosInstance.ts b/src/lib/api/axiosInstance.ts index 112657f..c2d51e3 100644 --- a/src/lib/api/axiosInstance.ts +++ b/src/lib/api/axiosInstance.ts @@ -59,15 +59,8 @@ axiosInstance.interceptors.response.use( token: refreshTokenValue, }); - // Support both response structures - const accessToken = - data?.results?.data?.accessToken?.token || - data?.data?.tokens?.accessToken?.token || - data?.accessToken; - const refreshTokenValueNew = - data?.results?.data?.refreshToken?.token || - data?.data?.tokens?.refreshToken?.token || - data?.refreshToken; + const accessToken = data?.data?.accessToken?.token; + const refreshTokenValueNew = data?.data?.refreshToken?.token; if (accessToken) { await setToken(accessToken); @@ -88,9 +81,9 @@ axiosInstance.interceptors.response.use( await removeToken(); await removeRefreshToken(); if (typeof window !== "undefined") { - const pathname = window.location.pathname; - const slug = pathname.split("/")[1]; - window.location.href = slug ? `/${slug}` : `/`; + // const pathname = window.location.pathname; + // const slug = pathname.split("/")[1]; + // window.location.href = slug ? `/${slug}` : `/`; } return Promise.reject(refreshError); } finally {