Compare commits

..

10 Commits

Author SHA1 Message Date
hamid zarghami 9259221128 fix build
deploy to danak / build_and_deploy (push) Has been cancelled
2026-02-25 19:32:03 +03:30
hamid zarghami a788931828 middleware dkala 2026-02-25 19:25:34 +03:30
hamid zarghami 9dd49436e3 Add state and city 2026-02-16 10:06:14 +03:30
hamid zarghami 1913de57b3 base url 2026-02-15 10:41:07 +03:30
hamid zarghami 44fb0be875 show variant in carts 2026-02-15 10:39:31 +03:30
hamid zarghami a25086bb8c equal size menu items 2026-02-15 10:00:53 +03:30
hamid zarghami da30f67717 show subcategory 2026-02-15 09:56:43 +03:30
hamid zarghami 2a94597682 add env 2026-02-14 14:36:52 +03:30
hamid zarghami 54f9cb2b39 api base url 2026-02-14 14:32:39 +03:30
hamid zarghami b6996b9557 fix order history bold 2026-02-14 14:32:17 +03:30
18 changed files with 255 additions and 83 deletions
+4
View File
@@ -0,0 +1,4 @@
# NEXT_PUBLIC_API_BASE_URL = 'https://dmenuplus-api.dev.danakcorp.com'
NEXT_PUBLIC_API_BASE_URL = 'https://dkala-api.danakcorp.com'
NEXT_PUBLIC_TOKEN_NAME = 'dkala-t'
NEXT_PUBLIC_REFRESH_TOKEN_NAME = 'dkala-rt'
-1
View File
@@ -31,7 +31,6 @@ yarn-error.log*
.pnpm-debug.log* .pnpm-debug.log*
# env files (can opt-in for committing if needed) # env files (can opt-in for committing if needed)
.env*
# vercel # vercel
.vercel .vercel
+39 -8
View File
@@ -8,20 +8,39 @@ import {
useGetCartItems, useGetCartItems,
useIncrementCart, useIncrementCart,
} from "../hooks/useCartData"; } from "../hooks/useCartData";
import { toast } from "@/components/Toast";
import { extractErrorMessage } from "@/lib/func";
export const useCart = () => { export const useCart = () => {
const { isSuccess } = useGetProfile(); const { isSuccess } = useGetProfile();
const params = useParams(); const params = useParams();
const currentSlug = (params.name as string) || null; const currentSlug = (params.name as string) || null;
const { clear, increment, decrement, items, slug, setSlug } = useReceiptStore(); const {
const { mutate: mutateIncrementCart, isPending: isIncrementPending } = useIncrementCart(); clear,
const { mutate: mutateDecrementCart, isPending: isDecrementPending } = useDecrementCart(); increment,
decrement,
items,
slug,
setSlug,
} = useReceiptStore();
const {
mutate: mutateIncrementCart,
isPending: isIncrementPending,
} = useIncrementCart();
const {
mutate: mutateDecrementCart,
isPending: isDecrementPending,
} = useDecrementCart();
const { mutate: mutateClearCart } = useClearCart(); const { mutate: mutateClearCart } = useClearCart();
const { data: cartItems } = useGetCartItems(isSuccess); const { data: cartItems } = useGetCartItems(isSuccess);
const addToCart = (id: string | number) => { const addToCart = (id: string | number) => {
if (isSuccess) { if (isSuccess) {
mutateIncrementCart(id); mutateIncrementCart(id, {
onError: (error) => {
toast(extractErrorMessage(error), "error");
},
});
} else { } else {
// تنظیم slug هنگام افزودن آیتم به سبد (فقط برای کاربران لاگین نشده) // تنظیم slug هنگام افزودن آیتم به سبد (فقط برای کاربران لاگین نشده)
if (currentSlug && slug !== currentSlug) { if (currentSlug && slug !== currentSlug) {
@@ -33,7 +52,11 @@ export const useCart = () => {
const removeFromCart = (id: string | number) => { const removeFromCart = (id: string | number) => {
if (isSuccess) { if (isSuccess) {
mutateDecrementCart(id); mutateDecrementCart(id, {
onError: (error) => {
toast(extractErrorMessage(error), "error");
},
});
} else { } else {
decrement(id); decrement(id);
} }
@@ -41,7 +64,11 @@ export const useCart = () => {
const clearCart = useCallback(() => { const clearCart = useCallback(() => {
if (isSuccess) { if (isSuccess) {
mutateClearCart(); mutateClearCart(undefined, {
onError: (error) => {
toast(extractErrorMessage(error), "error");
},
});
} else { } else {
clear(); clear();
} }
@@ -53,9 +80,13 @@ export const useCart = () => {
return {}; return {};
} }
return cartItems.data.items.reduce((acc, item) => { return cartItems.data.items.reduce((acc, item) => {
acc[item.variantId] = { quantity: item.quantity }; acc[item.variantId] = {
quantity: item.quantity,
...(item.variantValue != null && { variantValue: item.variantValue }),
...(item.image != null && { image: item.image }),
};
return acc; return acc;
}, {} as Record<string | number, { quantity: number }>); }, {} as Record<string | number, { quantity: number; variantValue?: string; image?: string }>);
} else { } else {
return items; return items;
} }
+6 -6
View File
@@ -11,7 +11,6 @@ import Prompt from '@/components/utils/Prompt';
import { useGetProducts } from '@/app/[name]/(Main)/hooks/useMenuData'; import { useGetProducts } from '@/app/[name]/(Main)/hooks/useMenuData';
import MenuItem from '@/components/listview/MenuItem'; import MenuItem from '@/components/listview/MenuItem';
import MenuItemRenderer from '@/components/listview/MenuItemRenderer'; import MenuItemRenderer from '@/components/listview/MenuItemRenderer';
import type { Product } from '@/app/[name]/(Main)/types/Types';
const CartIndex = () => { const CartIndex = () => {
const { t } = useTranslation('parallels', { keyPrefix: 'Cart' }); const { t } = useTranslation('parallels', { keyPrefix: 'Cart' });
@@ -26,13 +25,14 @@ const CartIndex = () => {
if (!products.length) return []; if (!products.length) return [];
return Object.entries(items) return Object.entries(items)
.filter(([, detail]) => detail?.quantity && detail.quantity > 0) .filter(([, detail]) => detail?.quantity && detail.quantity > 0)
.map(([variantId]) => { .map(([variantId, detail]) => {
const product = products.find((p) => const product = products.find((p) =>
p.variants?.some((v) => v.id === variantId) p.variants?.some((v) => v.id === variantId)
); );
return product ? { product, variantId } : null; const variantValue = (detail as { variantValue?: string } | undefined)?.variantValue;
return product ? { product, variantId, variantValue } : null;
}) })
.filter((x): x is { product: Product; variantId: string } => Boolean(x)); .filter((x): x is NonNullable<typeof x> => x != null);
}, [products, items]); }, [products, items]);
const isCartEmpty = cartProducts.length === 0; const isCartEmpty = cartProducts.length === 0;
@@ -98,9 +98,9 @@ const CartIndex = () => {
</div> </div>
) : ( ) : (
<div className="flex flex-col gap-4 pb-24"> <div className="flex flex-col gap-4 pb-24">
{cartProducts.map(({ product, variantId }) => ( {cartProducts.map(({ product, variantId, variantValue }) => (
<MenuItemRenderer key={variantId}> <MenuItemRenderer key={variantId}>
<MenuItem product={product} variantId={variantId} /> <MenuItem product={product} variantId={variantId} variantValue={variantValue} />
</MenuItemRenderer> </MenuItemRenderer>
))} ))}
<CartSummary isPremium /> <CartSummary isPremium />
@@ -10,6 +10,8 @@ export type BulkCartItem = {
export interface CartItem { export interface CartItem {
variantId: string; variantId: string;
productTitle: string; productTitle: string;
variantValue?: string;
image?: string;
quantity: number; quantity: number;
price: number; price: number;
discount: number; discount: number;
@@ -42,52 +42,69 @@ const CategoryScroll = ({
className, className,
}: Props) => { }: Props) => {
const { renderer: Renderer, imageSize } = variantConfig[variant]; const { renderer: Renderer, imageSize } = variantConfig[variant];
const selectedParent =
categories.find((c) => c.id === selectedCategory) ??
categories.find((c) => c.children?.some((ch) => ch.id === selectedCategory));
const children = selectedParent?.children ?? [];
const handleSelect = (categoryId: string) => () => onSelect(categoryId); const handleSelect = (categoryId: string) => () => onSelect(categoryId);
return ( return (
<HorizontalScrollView <div className="flex flex-col">
className={clsx( <HorizontalScrollView
"w-full noscrollbar py-4!", className={clsx(
variant === "large" && "mt-4!", "w-full noscrollbar pt-4! pb-1!",
className variant === "large" && "mt-4!",
)} className
> )}
{/* <Renderer
key="all"
className={clsx(selectedCategory === "0" && "bg-container!")}
onClick={handleSelect(0)}
> >
<Image {categories.map((item) => {
priority const isSelected =
src="/assets/images/food-image.png" item.id === selectedCategory ||
width={imageSize} item.children?.some((ch) => ch.id === selectedCategory);
height={imageSize}
alt="category image"
/>
<span className="text-xs text-foreground">همه</span>
</Renderer> */}
{categories.map((item) => {
const isSelected = item.id === selectedCategory;
return ( return (
<Renderer <Renderer
key={item.id} key={item.id}
className={clsx(isSelected && "bg-container!")} className={clsx(isSelected && "bg-container!")}
onClick={handleSelect(item.id)} onClick={handleSelect(item.id)}
> >
<Image <Image
priority priority
src={item.avatarUrl || "/assets/images/food-image.png"} src={item.avatarUrl || "/assets/images/food-image.png"}
width={imageSize} width={imageSize}
height={imageSize} height={imageSize}
alt="category image" alt="category image"
/> />
<span className="text-xs text-foreground text-center">{item.title}</span> <span className="text-xs text-foreground text-center">{item.title}</span>
</Renderer> </Renderer>
); );
})} })}
</HorizontalScrollView> </HorizontalScrollView>
{children.length > 0 && (
<HorizontalScrollView
className={clsx(
"w-full noscrollbar py-2!",
variant === "small" && "py-1!"
)}
>
{children.map((child) => {
const isChildSelected = child.id === selectedCategory;
return (
<CategorySmallItemRenderer
key={child.id}
className={clsx(isChildSelected && "bg-container!")}
onClick={handleSelect(child.id)}
>
<span className="text-[10px] text-foreground whitespace-nowrap">{child.title}</span>
</CategorySmallItemRenderer>
);
})}
</HorizontalScrollView>
)}
</div>
); );
}; };
+14 -2
View File
@@ -109,8 +109,20 @@ const MenuIndex = () => {
const lowerFilterQuery = filterSearch ? filterSearch.toLowerCase() : ""; const lowerFilterQuery = filterSearch ? filterSearch.toLowerCase() : "";
const selectedCatId = selectedCategory === '0' ? null : selectedCategory; const selectedCatId = selectedCategory === '0' ? null : selectedCategory;
// اگر دستهٔ اصلی انتخاب شده، خودش + همهٔ زیردسته‌ها را در نظر بگیر تا غذاهای زیردسته هم بیایند
const effectiveCategoryIds = (() => {
if (!selectedCatId) return null;
const parent = categories.find((c) => c.id === selectedCatId);
if (parent?.children?.length) {
return new Set([parent.id, ...parent.children.map((ch) => ch.id)]);
}
return new Set([selectedCatId]);
})();
const filtered = products.filter((item) => { const filtered = products.filter((item) => {
const matchesCategory = !selectedCatId || item.category?.id === selectedCatId; const matchesCategory =
!selectedCatId ||
(item.category?.id != null && effectiveCategoryIds?.has(item.category.id));
const itemName = item.name ?? item.title; const itemName = item.name ?? item.title;
const matchesSearch = const matchesSearch =
!search || itemName.toLowerCase().includes(lowerSearch); !search || itemName.toLowerCase().includes(lowerSearch);
@@ -149,7 +161,7 @@ const MenuIndex = () => {
return 0; return 0;
} }
}); });
}, [selectedCategory, search, filterSearch, products, sorting]); }, [selectedCategory, search, filterSearch, products, sorting, categories]);
if (isLoading) { if (isLoading) {
return <MenuSkeleton viewMode={viewMode} />; return <MenuSkeleton viewMode={viewMode} />;
+4 -1
View File
@@ -10,10 +10,13 @@ export interface Category {
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
deletedAt: string | null; deletedAt: string | null;
parent: string | null;
title: string; title: string;
isActive: boolean; isActive: boolean;
restaurant: string; shop: string;
avatarUrl: string | null; avatarUrl: string | null;
order: number | null;
children: Category[];
} }
export type CategoriesResponse = BaseResponse<Category[]>; export type CategoriesResponse = BaseResponse<Category[]>;
@@ -13,6 +13,8 @@ interface AddressDetailsModalProps {
addressDetails: string; addressDetails: string;
phone: string; phone: string;
postalCode: string; postalCode: string;
city: string;
province: string;
isDefault: boolean; isDefault: boolean;
}; };
isPending: boolean; isPending: boolean;
@@ -9,6 +9,8 @@ interface AddressFormProps {
addressDetails: string; addressDetails: string;
phone: string; phone: string;
postalCode: string; postalCode: string;
city: string;
province: string;
isDefault: boolean; isDefault: boolean;
}; };
selectedAddress: NominatimReverseGeocodingResponse | null; selectedAddress: NominatimReverseGeocodingResponse | null;
@@ -50,6 +52,24 @@ export const AddressForm = ({
onChange={(e) => onFormDataChange({ addressDetails: e.target.value })} onChange={(e) => onFormDataChange({ addressDetails: e.target.value })}
inputClassName='text-xs!' inputClassName='text-xs!'
/> />
<InputField
htmlFor={'province'}
labelText={'استان'}
placeholder={selectedAddress?.address?.state || selectedAddress?.address?.province || 'مثال: تهران'}
className='bg-inherit mt-8'
value={formData.province || selectedAddress?.address?.state || selectedAddress?.address?.province || ''}
onChange={(e) => onFormDataChange({ province: e.target.value })}
inputClassName='text-xs!'
/>
<InputField
htmlFor={'city'}
labelText={'شهر'}
placeholder={selectedAddress?.address?.city || selectedAddress?.address?.district || 'مثال: تهران'}
className='bg-inherit mt-8'
value={formData.city || selectedAddress?.address?.city || selectedAddress?.address?.district || ''}
onChange={(e) => onFormDataChange({ city: e.target.value })}
inputClassName='text-xs!'
/>
<InputField <InputField
htmlFor={'phone'} htmlFor={'phone'}
labelText={'شماره تلفن'} labelText={'شماره تلفن'}
@@ -31,6 +31,8 @@ export const useAddressForm = ({
addressDetails: '', addressDetails: '',
phone: initialPhone, phone: initialPhone,
postalCode: '', postalCode: '',
city: '',
province: '',
isDefault: false, isDefault: false,
}); });
@@ -39,6 +41,8 @@ export const useAddressForm = ({
setFormData({ setFormData({
title: addressData.title || '', title: addressData.title || '',
addressDetails: addressData.address || '', addressDetails: addressData.address || '',
city: addressData.city || '',
province: addressData.province || '',
phone: addressData.phone || initialPhone, phone: addressData.phone || initialPhone,
postalCode: addressData.postalCode || '', postalCode: addressData.postalCode || '',
isDefault: addressData.isDefault || false, isDefault: addressData.isDefault || false,
@@ -76,8 +80,8 @@ export const useAddressForm = ({
const baseAddressData = { const baseAddressData = {
title: formData.title, title: formData.title,
address: formData.addressDetails, address: formData.addressDetails,
city: selectedAddress.address.city || selectedAddress.address.district || '', city: formData.city || selectedAddress.address.city || selectedAddress.address.district || '',
province: selectedAddress.address.province || selectedAddress.address.state || '', province: formData.province || selectedAddress.address.province || selectedAddress.address.state || '',
postalCode: formData.postalCode || selectedAddress.address.postcode || '', postalCode: formData.postalCode || selectedAddress.address.postcode || '',
latitude: selectedPosition[0], latitude: selectedPosition[0],
longitude: selectedPosition[1], longitude: selectedPosition[1],
+1 -1
View File
@@ -31,7 +31,7 @@ export function CartQuantityControl({
isMutating = false, isMutating = false,
addDisabled = false, addDisabled = false,
addLabel = 'افزودن', addLabel = 'افزودن',
addingLabel = 'در حال افزودن...', addingLabel = '',
fullWidth = false, fullWidth = false,
}: CartQuantityControlProps) { }: CartQuantityControlProps) {
return ( return (
@@ -11,7 +11,7 @@ function CategorySmallItemRenderer({ children, ...rest }: Props) {
<div className={`${rest.className} cursor-pointer transition-all duration-200 ease-out gradient-border overflow-hidden rounded-xl backdrop-blur-md bg-container/40 dark:bg-background`}> <div className={`${rest.className} cursor-pointer transition-all duration-200 ease-out gradient-border overflow-hidden rounded-xl backdrop-blur-md bg-container/40 dark:bg-background`}>
<div <div
{...rest} {...rest}
className="rounded-normal h-[44px] flex flex-row justify-center items-center p-2.5 gap-2" className="rounded-normal flex flex-row justify-center items-center h-8 px-2 gap-2"
> >
{children} {children}
</div> </div>
+26 -8
View File
@@ -19,11 +19,13 @@ interface MenuItemProps {
product: Product; product: Product;
/** در صفحه سبد هر خط با variantId مشخص می‌شود */ /** در صفحه سبد هر خط با variantId مشخص می‌شود */
variantId?: string | null; variantId?: string | null;
/** مقدار تنوع (مثلاً «قرمز»، «تنوع سوم») — برای نمایش در سبد خرید */
variantValue?: string | null;
/** حالت نمایش: list = یک ستونه (پیش‌فرض)، grid = دو ستونه */ /** حالت نمایش: list = یک ستونه (پیش‌فرض)، grid = دو ستونه */
viewMode?: MenuItemViewMode; viewMode?: MenuItemViewMode;
} }
const MenuItem = ({ product, variantId: variantIdProp, viewMode = 'list' }: MenuItemProps) => { const MenuItem = ({ product, variantId: variantIdProp, variantValue: variantValueProp, viewMode = 'list' }: MenuItemProps) => {
const { items, addToCart, removeFromCart, isCartMutating } = useCart(); const { items, addToCart, removeFromCart, isCartMutating } = useCart();
const params = useParams<{ name: string }>(); const params = useParams<{ name: string }>();
const name = params?.name || ""; const name = params?.name || "";
@@ -61,6 +63,10 @@ const MenuItem = ({ product, variantId: variantIdProp, viewMode = 'list' }: Menu
[product.name, product.title] [product.name, product.title]
); );
/** فقط در سبد خرید تنوع نمایش داده شود */
const isInCart = variantIdProp != null;
const variantValueDisplay = isInCart ? (variantValueProp ?? selectedVariant?.value ?? null) : null;
const productContent = useMemo(() => { const productContent = useMemo(() => {
const content = product.content; const content = product.content;
if (!content) return ''; if (!content) return '';
@@ -126,13 +132,18 @@ const MenuItem = ({ product, variantId: variantIdProp, viewMode = 'list' }: Menu
alt={productName} alt={productName}
/> />
</Link> </Link>
<Link href={productDetailUrl} className="cursor-pointer min-w-0 mt-4"> <Link href={productDetailUrl} className="cursor-pointer min-w-0 mt-4 flex items-center justify-between gap-2">
<div className="text-sm2 font-normal text-black dark:text-white wrap-break-word line-clamp-2"> <div className="text-sm2 font-normal text-black dark:text-white wrap-break-word line-clamp-2 min-w-0">
{productName} {productName}
</div> </div>
{variantValueDisplay && (
<span className="bg-primary text-primary-foreground text-[10px] px-1 py-0.5 rounded-md shrink-0">
{variantValueDisplay}
</span>
)}
</Link> </Link>
<div className="flex flex-col gap-2 mt-1"> <div className="flex flex-col gap-2 mt-1">
<div className="flex flex-col gap-1" dir="ltr"> <div className="flex flex-col gap-1 min-h-10 justify-center" dir="ltr">
{hasDiscount ? ( {hasDiscount ? (
<> <>
<span className="text-xs text-disabled-text line-through"> <span className="text-xs text-disabled-text line-through">
@@ -184,8 +195,15 @@ const MenuItem = ({ product, variantId: variantIdProp, viewMode = 'list' }: Menu
<div className="w-full inline-flex flex-col justify-between min-w-0"> <div className="w-full inline-flex flex-col justify-between min-w-0">
<Link href={productDetailUrl} className="cursor-pointer min-w-0"> <Link href={productDetailUrl} className="cursor-pointer min-w-0">
<div className="min-w-0"> <div className="min-w-0">
<div className="text-sm2 font-normal text-black dark:text-white wrap-break-word"> <div className="flex items-center justify-between gap-2 min-w-0">
{productName} <div className="text-sm2 font-normal text-black dark:text-white wrap-break-word min-w-0">
{productName}
</div>
{variantValueDisplay && (
<span className="bg-primary text-primary-foreground text-[10px] px-1 py-0.5 rounded-md shrink-0">
{variantValueDisplay}
</span>
)}
</div> </div>
{(productContent || productDescription) && ( {(productContent || productDescription) && (
<div className="text-[#7F7F7F] line-clamp-2 text-xs leading-5 font-normal mt-2 wrap-break-word overflow-hidden"> <div className="text-[#7F7F7F] line-clamp-2 text-xs leading-5 font-normal mt-2 wrap-break-word overflow-hidden">
@@ -195,7 +213,7 @@ const MenuItem = ({ product, variantId: variantIdProp, viewMode = 'list' }: Menu
</div> </div>
</Link> </Link>
<div className="flex flex-col mt-2 gap-2 w-full"> <div className="flex flex-col mt-2 gap-2 w-full">
<div className="flex flex-col gap-1" dir="ltr"> <div className="flex flex-col gap-1 min-h-10 justify-center" dir="ltr">
{hasDiscount ? ( {hasDiscount ? (
<> <>
<span className="text-xs text-disabled-text line-through"> <span className="text-xs text-disabled-text line-through">
@@ -206,7 +224,7 @@ const MenuItem = ({ product, variantId: variantIdProp, viewMode = 'list' }: Menu
</span> </span>
</> </>
) : ( ) : (
<span className="text-sm mt-1 text-right"> <span className="text-sm text-right">
{formattedPrice} T {formattedPrice} T
</span> </span>
)} )}
+26 -3
View File
@@ -4,14 +4,13 @@ import React from 'react'
import Link from 'next/link' import Link from 'next/link'
import BottomNavLink from './BottomNavLink' import BottomNavLink from './BottomNavLink'
import BottomNavHighlightLink from './BottomNavLinkBig' import BottomNavHighlightLink from './BottomNavLinkBig'
import ReceiptIcon from '../icons/ReceiptIcon'
import BagIcon from '../icons/BagIcon' import BagIcon from '../icons/BagIcon'
import HeartIcon from '../icons/HeartIcon' import HeartIcon from '../icons/HeartIcon'
import { useParams, usePathname, useRouter } from 'next/navigation' import { useParams, usePathname, useRouter } from 'next/navigation'
import HomeIcon from '../icons/HomeIcon' import HomeIcon from '../icons/HomeIcon'
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useCart } from '@/app/[name]/(Dialogs)/cart/hook/useCart'; import { useCart } from '@/app/[name]/(Dialogs)/cart/hook/useCart';
import { Building } from 'iconsax-react'; import { Building, Receipt21 } from 'iconsax-react';
import clsx from 'clsx'; import clsx from 'clsx';
import { useGetProfile } from '@/app/[name]/(Profile)/profile/hooks/userProfileData'; import { useGetProfile } from '@/app/[name]/(Profile)/profile/hooks/userProfileData';
import { toast } from '../Toast'; import { toast } from '../Toast';
@@ -23,9 +22,13 @@ function BottomNavBar() {
const pathname = usePathname(); const pathname = usePathname();
const isHomeRoute = pathname === `/${name}`; const isHomeRoute = pathname === `/${name}`;
const isAboutRoute = pathname.includes('about'); const isAboutRoute = pathname.includes('about');
const isOrderRoute = pathname.includes('order');
const buildingVariant = React.useMemo(() => { const buildingVariant = React.useMemo(() => {
return (isAboutRoute ? 'Bold' : 'Outline') as 'Bold' | 'Outline'; return (isAboutRoute ? 'Bold' : 'Outline') as 'Bold' | 'Outline';
}, [isAboutRoute]); }, [isAboutRoute]);
const orderVariant = React.useMemo(() => {
return (isOrderRoute ? 'Bold' : 'Outline') as 'Bold' | 'Outline';
}, [isOrderRoute]);
const { isSuccess } = useGetProfile() const { isSuccess } = useGetProfile()
const router = useRouter() const router = useRouter()
const { t } = useTranslation('common', { const { t } = useTranslation('common', {
@@ -77,7 +80,27 @@ function BottomNavBar() {
icon={<BagIcon width={20} height={20} />} icon={<BagIcon width={20} height={20} />}
value={t('Cart')} value={t('Cart')}
/> />
<BottomNavLink href={`/${name}/order/history`} icon={<ReceiptIcon width={20} height={20} />} value={t('Orders')} />
<Link
href={`/${name}/order/history`}
className={clsx(
'flex flex-col justify-arround items-center gap-[5px] text-primary pointer-events-auto **:stroke-primary min-w-0 dark:text-white dark:**:stroke-white'
)}
>
<div className="shrink-0 text-primary dark:text-white">
<Receipt21
key={`receipt-${orderVariant}-${isOrderRoute}`}
size={20}
variant={orderVariant}
color="currentColor"
className='stroke-0'
/>
</div>
<span className="text-xs2 text-center truncate w-full dark:text-white">
{t('Orders')}
</span>
</Link>
{/* <BottomNavLink href={`/${name}/order/history`} icon={<ReceiptIcon width={20} height={20} />} value={t('Orders')} /> */}
<BottomNavHighlightLink <BottomNavHighlightLink
href={`/${name}`} href={`/${name}`}
icon={ icon={
+2 -2
View File
@@ -1,5 +1,5 @@
// export const API_BASE_URL = "https://dmenuplus-api.dev.danakcorp.com"; // export const API_BASE_URL = "https://dmenuplus-api.dev.danakcorp.com";
// export const API_BASE_URL = "https://dkala-api.danakcorp.com"; export const API_BASE_URL = "https://dkala-api.danakcorp.com";
export const API_BASE_URL = "http://10.191.241.88:4000"; // export const API_BASE_URL = "http://10.191.241.88:4000";
export const TOKEN_NAME = "dkala-t"; export const TOKEN_NAME = "dkala-t";
export const REFRESH_TOKEN_NAME = "dkala-rt"; export const REFRESH_TOKEN_NAME = "dkala-rt";
+42 -5
View File
@@ -1,11 +1,48 @@
import { NextRequest } from 'next/server'; import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
// eslint-disable-next-line @typescript-eslint/no-unused-vars // Map host → tenant path
export function middleware(request: NextRequest) { const HOST_MAP: Record<string, string> = {
"avitaprotein.com": "/avita",
// دامنه‌های جدید اینجا اضافه می‌شوند
};
export function middleware(req: NextRequest) {
const host = req.headers.get("host") || "";
const tenantPath = HOST_MAP[host.toLowerCase()] || "";
if (!tenantPath) {
return NextResponse.next();
}
const pathname = req.nextUrl.pathname;
// مسیرهای api و استاتیک را rewrite نکن
if (
pathname.startsWith("/api") ||
pathname.startsWith("/_next") ||
pathname.includes(".")
) {
return NextResponse.next();
}
// اگر کاربر ریشه دامنه را باز کرده، به مسیر tenant ریدایرکت کن (آدرس‌بار /passata و غیره نشون بده)
if (pathname === "/") {
const url = req.nextUrl.clone();
url.pathname = tenantPath;
return NextResponse.redirect(url);
}
// اگر مسیر از قبل با tenant یکی است (مثلاً /passata یا /passata/menu)، rewrite نکن
if (pathname === tenantPath || pathname.startsWith(tenantPath + "/")) {
return NextResponse.next();
}
const url = req.nextUrl.clone();
url.pathname = `${tenantPath}${pathname}`;
return NextResponse.rewrite(url);
} }
// only applies this middleware to files in the app directory
export const config = { export const config = {
matcher: '/((?!api|static|favicon.ico|.*\\..*|_next).*)' matcher: "/:path*",
}; };
+1 -1
View File
File diff suppressed because one or more lines are too long