Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9259221128 | |||
| a788931828 | |||
| 9dd49436e3 | |||
| 1913de57b3 | |||
| 44fb0be875 | |||
| a25086bb8c | |||
| da30f67717 | |||
| 2a94597682 | |||
| 54f9cb2b39 | |||
| b6996b9557 |
@@ -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'
|
||||
@@ -31,7 +31,6 @@ yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
@@ -8,20 +8,39 @@ import {
|
||||
useGetCartItems,
|
||||
useIncrementCart,
|
||||
} from "../hooks/useCartData";
|
||||
import { toast } from "@/components/Toast";
|
||||
import { extractErrorMessage } from "@/lib/func";
|
||||
|
||||
export const useCart = () => {
|
||||
const { isSuccess } = useGetProfile();
|
||||
const params = useParams();
|
||||
const currentSlug = (params.name as string) || null;
|
||||
const { clear, increment, decrement, items, slug, setSlug } = useReceiptStore();
|
||||
const { mutate: mutateIncrementCart, isPending: isIncrementPending } = useIncrementCart();
|
||||
const { mutate: mutateDecrementCart, isPending: isDecrementPending } = useDecrementCart();
|
||||
const {
|
||||
clear,
|
||||
increment,
|
||||
decrement,
|
||||
items,
|
||||
slug,
|
||||
setSlug,
|
||||
} = useReceiptStore();
|
||||
const {
|
||||
mutate: mutateIncrementCart,
|
||||
isPending: isIncrementPending,
|
||||
} = useIncrementCart();
|
||||
const {
|
||||
mutate: mutateDecrementCart,
|
||||
isPending: isDecrementPending,
|
||||
} = useDecrementCart();
|
||||
const { mutate: mutateClearCart } = useClearCart();
|
||||
const { data: cartItems } = useGetCartItems(isSuccess);
|
||||
|
||||
const addToCart = (id: string | number) => {
|
||||
if (isSuccess) {
|
||||
mutateIncrementCart(id);
|
||||
mutateIncrementCart(id, {
|
||||
onError: (error) => {
|
||||
toast(extractErrorMessage(error), "error");
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// تنظیم slug هنگام افزودن آیتم به سبد (فقط برای کاربران لاگین نشده)
|
||||
if (currentSlug && slug !== currentSlug) {
|
||||
@@ -33,7 +52,11 @@ export const useCart = () => {
|
||||
|
||||
const removeFromCart = (id: string | number) => {
|
||||
if (isSuccess) {
|
||||
mutateDecrementCart(id);
|
||||
mutateDecrementCart(id, {
|
||||
onError: (error) => {
|
||||
toast(extractErrorMessage(error), "error");
|
||||
},
|
||||
});
|
||||
} else {
|
||||
decrement(id);
|
||||
}
|
||||
@@ -41,7 +64,11 @@ export const useCart = () => {
|
||||
|
||||
const clearCart = useCallback(() => {
|
||||
if (isSuccess) {
|
||||
mutateClearCart();
|
||||
mutateClearCart(undefined, {
|
||||
onError: (error) => {
|
||||
toast(extractErrorMessage(error), "error");
|
||||
},
|
||||
});
|
||||
} else {
|
||||
clear();
|
||||
}
|
||||
@@ -53,9 +80,13 @@ export const useCart = () => {
|
||||
return {};
|
||||
}
|
||||
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;
|
||||
}, {} as Record<string | number, { quantity: number }>);
|
||||
}, {} as Record<string | number, { quantity: number; variantValue?: string; image?: string }>);
|
||||
} else {
|
||||
return items;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import Prompt from '@/components/utils/Prompt';
|
||||
import { useGetProducts } from '@/app/[name]/(Main)/hooks/useMenuData';
|
||||
import MenuItem from '@/components/listview/MenuItem';
|
||||
import MenuItemRenderer from '@/components/listview/MenuItemRenderer';
|
||||
import type { Product } from '@/app/[name]/(Main)/types/Types';
|
||||
|
||||
const CartIndex = () => {
|
||||
const { t } = useTranslation('parallels', { keyPrefix: 'Cart' });
|
||||
@@ -26,13 +25,14 @@ const CartIndex = () => {
|
||||
if (!products.length) return [];
|
||||
return Object.entries(items)
|
||||
.filter(([, detail]) => detail?.quantity && detail.quantity > 0)
|
||||
.map(([variantId]) => {
|
||||
.map(([variantId, detail]) => {
|
||||
const product = products.find((p) =>
|
||||
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]);
|
||||
|
||||
const isCartEmpty = cartProducts.length === 0;
|
||||
@@ -98,9 +98,9 @@ const CartIndex = () => {
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col gap-4 pb-24">
|
||||
{cartProducts.map(({ product, variantId }) => (
|
||||
{cartProducts.map(({ product, variantId, variantValue }) => (
|
||||
<MenuItemRenderer key={variantId}>
|
||||
<MenuItem product={product} variantId={variantId} />
|
||||
<MenuItem product={product} variantId={variantId} variantValue={variantValue} />
|
||||
</MenuItemRenderer>
|
||||
))}
|
||||
<CartSummary isPremium />
|
||||
|
||||
@@ -10,6 +10,8 @@ export type BulkCartItem = {
|
||||
export interface CartItem {
|
||||
variantId: string;
|
||||
productTitle: string;
|
||||
variantValue?: string;
|
||||
image?: string;
|
||||
quantity: number;
|
||||
price: number;
|
||||
discount: number;
|
||||
|
||||
@@ -42,52 +42,69 @@ const CategoryScroll = ({
|
||||
className,
|
||||
}: Props) => {
|
||||
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);
|
||||
|
||||
return (
|
||||
<HorizontalScrollView
|
||||
className={clsx(
|
||||
"w-full noscrollbar py-4!",
|
||||
variant === "large" && "mt-4!",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{/* <Renderer
|
||||
key="all"
|
||||
className={clsx(selectedCategory === "0" && "bg-container!")}
|
||||
onClick={handleSelect(0)}
|
||||
<div className="flex flex-col">
|
||||
<HorizontalScrollView
|
||||
className={clsx(
|
||||
"w-full noscrollbar pt-4! pb-1!",
|
||||
variant === "large" && "mt-4!",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<Image
|
||||
priority
|
||||
src="/assets/images/food-image.png"
|
||||
width={imageSize}
|
||||
height={imageSize}
|
||||
alt="category image"
|
||||
/>
|
||||
<span className="text-xs text-foreground">همه</span>
|
||||
</Renderer> */}
|
||||
{categories.map((item) => {
|
||||
const isSelected = item.id === selectedCategory;
|
||||
{categories.map((item) => {
|
||||
const isSelected =
|
||||
item.id === selectedCategory ||
|
||||
item.children?.some((ch) => ch.id === selectedCategory);
|
||||
|
||||
return (
|
||||
<Renderer
|
||||
key={item.id}
|
||||
className={clsx(isSelected && "bg-container!")}
|
||||
onClick={handleSelect(item.id)}
|
||||
>
|
||||
<Image
|
||||
priority
|
||||
src={item.avatarUrl || "/assets/images/food-image.png"}
|
||||
width={imageSize}
|
||||
height={imageSize}
|
||||
alt="category image"
|
||||
/>
|
||||
<span className="text-xs text-foreground text-center">{item.title}</span>
|
||||
</Renderer>
|
||||
);
|
||||
})}
|
||||
</HorizontalScrollView>
|
||||
return (
|
||||
<Renderer
|
||||
key={item.id}
|
||||
className={clsx(isSelected && "bg-container!")}
|
||||
onClick={handleSelect(item.id)}
|
||||
>
|
||||
<Image
|
||||
priority
|
||||
src={item.avatarUrl || "/assets/images/food-image.png"}
|
||||
width={imageSize}
|
||||
height={imageSize}
|
||||
alt="category image"
|
||||
/>
|
||||
<span className="text-xs text-foreground text-center">{item.title}</span>
|
||||
</Renderer>
|
||||
);
|
||||
})}
|
||||
</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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -109,8 +109,20 @@ const MenuIndex = () => {
|
||||
const lowerFilterQuery = filterSearch ? filterSearch.toLowerCase() : "";
|
||||
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 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 matchesSearch =
|
||||
!search || itemName.toLowerCase().includes(lowerSearch);
|
||||
@@ -149,7 +161,7 @@ const MenuIndex = () => {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}, [selectedCategory, search, filterSearch, products, sorting]);
|
||||
}, [selectedCategory, search, filterSearch, products, sorting, categories]);
|
||||
|
||||
if (isLoading) {
|
||||
return <MenuSkeleton viewMode={viewMode} />;
|
||||
|
||||
@@ -10,10 +10,13 @@ export interface Category {
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
deletedAt: string | null;
|
||||
parent: string | null;
|
||||
title: string;
|
||||
isActive: boolean;
|
||||
restaurant: string;
|
||||
shop: string;
|
||||
avatarUrl: string | null;
|
||||
order: number | null;
|
||||
children: Category[];
|
||||
}
|
||||
|
||||
export type CategoriesResponse = BaseResponse<Category[]>;
|
||||
|
||||
@@ -13,6 +13,8 @@ interface AddressDetailsModalProps {
|
||||
addressDetails: string;
|
||||
phone: string;
|
||||
postalCode: string;
|
||||
city: string;
|
||||
province: string;
|
||||
isDefault: boolean;
|
||||
};
|
||||
isPending: boolean;
|
||||
|
||||
@@ -9,6 +9,8 @@ interface AddressFormProps {
|
||||
addressDetails: string;
|
||||
phone: string;
|
||||
postalCode: string;
|
||||
city: string;
|
||||
province: string;
|
||||
isDefault: boolean;
|
||||
};
|
||||
selectedAddress: NominatimReverseGeocodingResponse | null;
|
||||
@@ -50,6 +52,24 @@ export const AddressForm = ({
|
||||
onChange={(e) => onFormDataChange({ addressDetails: e.target.value })}
|
||||
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
|
||||
htmlFor={'phone'}
|
||||
labelText={'شماره تلفن'}
|
||||
|
||||
@@ -31,6 +31,8 @@ export const useAddressForm = ({
|
||||
addressDetails: '',
|
||||
phone: initialPhone,
|
||||
postalCode: '',
|
||||
city: '',
|
||||
province: '',
|
||||
isDefault: false,
|
||||
});
|
||||
|
||||
@@ -39,6 +41,8 @@ export const useAddressForm = ({
|
||||
setFormData({
|
||||
title: addressData.title || '',
|
||||
addressDetails: addressData.address || '',
|
||||
city: addressData.city || '',
|
||||
province: addressData.province || '',
|
||||
phone: addressData.phone || initialPhone,
|
||||
postalCode: addressData.postalCode || '',
|
||||
isDefault: addressData.isDefault || false,
|
||||
@@ -76,8 +80,8 @@ export const useAddressForm = ({
|
||||
const baseAddressData = {
|
||||
title: formData.title,
|
||||
address: formData.addressDetails,
|
||||
city: selectedAddress.address.city || selectedAddress.address.district || '',
|
||||
province: selectedAddress.address.province || selectedAddress.address.state || '',
|
||||
city: formData.city || selectedAddress.address.city || selectedAddress.address.district || '',
|
||||
province: formData.province || selectedAddress.address.province || selectedAddress.address.state || '',
|
||||
postalCode: formData.postalCode || selectedAddress.address.postcode || '',
|
||||
latitude: selectedPosition[0],
|
||||
longitude: selectedPosition[1],
|
||||
|
||||
@@ -31,7 +31,7 @@ export function CartQuantityControl({
|
||||
isMutating = false,
|
||||
addDisabled = false,
|
||||
addLabel = 'افزودن',
|
||||
addingLabel = 'در حال افزودن...',
|
||||
addingLabel = '',
|
||||
fullWidth = false,
|
||||
}: CartQuantityControlProps) {
|
||||
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
|
||||
{...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}
|
||||
</div>
|
||||
|
||||
@@ -19,11 +19,13 @@ interface MenuItemProps {
|
||||
product: Product;
|
||||
/** در صفحه سبد هر خط با variantId مشخص میشود */
|
||||
variantId?: string | null;
|
||||
/** مقدار تنوع (مثلاً «قرمز»، «تنوع سوم») — برای نمایش در سبد خرید */
|
||||
variantValue?: string | null;
|
||||
/** حالت نمایش: list = یک ستونه (پیشفرض)، grid = دو ستونه */
|
||||
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 params = useParams<{ name: string }>();
|
||||
const name = params?.name || "";
|
||||
@@ -61,6 +63,10 @@ const MenuItem = ({ product, variantId: variantIdProp, viewMode = 'list' }: Menu
|
||||
[product.name, product.title]
|
||||
);
|
||||
|
||||
/** فقط در سبد خرید تنوع نمایش داده شود */
|
||||
const isInCart = variantIdProp != null;
|
||||
const variantValueDisplay = isInCart ? (variantValueProp ?? selectedVariant?.value ?? null) : null;
|
||||
|
||||
const productContent = useMemo(() => {
|
||||
const content = product.content;
|
||||
if (!content) return '';
|
||||
@@ -126,13 +132,18 @@ const MenuItem = ({ product, variantId: variantIdProp, viewMode = 'list' }: Menu
|
||||
alt={productName}
|
||||
/>
|
||||
</Link>
|
||||
<Link href={productDetailUrl} className="cursor-pointer min-w-0 mt-4">
|
||||
<div className="text-sm2 font-normal text-black dark:text-white wrap-break-word line-clamp-2">
|
||||
<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 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>
|
||||
)}
|
||||
</Link>
|
||||
<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 ? (
|
||||
<>
|
||||
<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">
|
||||
<Link href={productDetailUrl} className="cursor-pointer min-w-0">
|
||||
<div className="min-w-0">
|
||||
<div className="text-sm2 font-normal text-black dark:text-white wrap-break-word">
|
||||
{productName}
|
||||
<div className="flex items-center justify-between gap-2 min-w-0">
|
||||
<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>
|
||||
{(productContent || productDescription) && (
|
||||
<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>
|
||||
</Link>
|
||||
<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 ? (
|
||||
<>
|
||||
<span className="text-xs text-disabled-text line-through">
|
||||
@@ -206,7 +224,7 @@ const MenuItem = ({ product, variantId: variantIdProp, viewMode = 'list' }: Menu
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<span className="text-sm mt-1 text-right">
|
||||
<span className="text-sm text-right">
|
||||
{formattedPrice} T
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -4,14 +4,13 @@ import React from 'react'
|
||||
import Link from 'next/link'
|
||||
import BottomNavLink from './BottomNavLink'
|
||||
import BottomNavHighlightLink from './BottomNavLinkBig'
|
||||
import ReceiptIcon from '../icons/ReceiptIcon'
|
||||
import BagIcon from '../icons/BagIcon'
|
||||
import HeartIcon from '../icons/HeartIcon'
|
||||
import { useParams, usePathname, useRouter } from 'next/navigation'
|
||||
import HomeIcon from '../icons/HomeIcon'
|
||||
import { useTranslation } from 'react-i18next';
|
||||
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 { useGetProfile } from '@/app/[name]/(Profile)/profile/hooks/userProfileData';
|
||||
import { toast } from '../Toast';
|
||||
@@ -23,9 +22,13 @@ function BottomNavBar() {
|
||||
const pathname = usePathname();
|
||||
const isHomeRoute = pathname === `/${name}`;
|
||||
const isAboutRoute = pathname.includes('about');
|
||||
const isOrderRoute = pathname.includes('order');
|
||||
const buildingVariant = React.useMemo(() => {
|
||||
return (isAboutRoute ? 'Bold' : 'Outline') as 'Bold' | 'Outline';
|
||||
}, [isAboutRoute]);
|
||||
const orderVariant = React.useMemo(() => {
|
||||
return (isOrderRoute ? 'Bold' : 'Outline') as 'Bold' | 'Outline';
|
||||
}, [isOrderRoute]);
|
||||
const { isSuccess } = useGetProfile()
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation('common', {
|
||||
@@ -77,7 +80,27 @@ function BottomNavBar() {
|
||||
icon={<BagIcon width={20} height={20} />}
|
||||
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
|
||||
href={`/${name}`}
|
||||
icon={
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
// 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 = "http://10.191.241.88:4000";
|
||||
export const API_BASE_URL = "https://dkala-api.danakcorp.com";
|
||||
// export const API_BASE_URL = "http://10.191.241.88:4000";
|
||||
export const TOKEN_NAME = "dkala-t";
|
||||
export const REFRESH_TOKEN_NAME = "dkala-rt";
|
||||
|
||||
+42
-5
@@ -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
|
||||
export function middleware(request: NextRequest) {
|
||||
// Map host → tenant path
|
||||
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 = {
|
||||
matcher: '/((?!api|static|favicon.ico|.*\\..*|_next).*)'
|
||||
matcher: "/:path*",
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user