bulk cart
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
import { useMutation } from "@tanstack/react-query";
|
||||||
|
import * as api from "../service/CartService";
|
||||||
|
|
||||||
|
export const useBulkCart = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: api.bulkCart,
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { api } from "@/config/axios";
|
||||||
|
import { BulkCartItem } from "../types/Types";
|
||||||
|
|
||||||
|
export const bulkCart = async (params: BulkCartItem) => {
|
||||||
|
const { data } = await api.post("/public/cart/items/bulk", params);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export type BulkCartItem = {
|
||||||
|
items: {
|
||||||
|
foodId: string;
|
||||||
|
quantity: number;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
@@ -10,17 +10,19 @@ import Button from '../button/PrimaryButton';
|
|||||||
import { useParams, usePathname } from 'next/navigation';
|
import { useParams, usePathname } from 'next/navigation';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { CalendarSearch, Cup, DirectboxReceive, DirectInbox, DocumentCopy, Game, Icon, Instagram, Like1, LogoutCurve, Notification, Receipt1, Setting2, Share, TicketDiscount, Whatsapp } from 'iconsax-react';
|
import { CalendarSearch, Cup, DirectboxReceive, DirectInbox, DocumentCopy, Game, Icon, Instagram, Like1, Login, LogoutCurve, Notification, Receipt1, Setting2, Share, TicketDiscount, Whatsapp } from 'iconsax-react';
|
||||||
import TelegramIcon from '../icons/TelegramIcon';
|
import TelegramIcon from '../icons/TelegramIcon';
|
||||||
import Modal from '../utils/Modal';
|
import Modal from '../utils/Modal';
|
||||||
import LogoutPrompt from '@/features/general/LogoutPrompt';
|
import LogoutPrompt from '@/features/general/LogoutPrompt';
|
||||||
import useToggle from '@/hooks/helpers/useToggle';
|
import useToggle from '@/hooks/helpers/useToggle';
|
||||||
|
import { useAuthStore } from '@/zustand/authStore';
|
||||||
|
|
||||||
type MenuItemType = {
|
type MenuItemType = {
|
||||||
href: string | undefined;
|
href: string | undefined;
|
||||||
title: string;
|
title: string;
|
||||||
icon: Icon;
|
icon: Icon;
|
||||||
auth?: boolean;
|
auth?: boolean;
|
||||||
|
guestOnly?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const menuItems: Array<Array<MenuItemType>> = [
|
const menuItems: Array<Array<MenuItemType>> = [
|
||||||
@@ -38,7 +40,8 @@ const menuItems: Array<Array<MenuItemType>> = [
|
|||||||
[],
|
[],
|
||||||
[
|
[
|
||||||
{ auth: true, href: 'profile/settings', title: 'Preferences', icon: Setting2 },
|
{ auth: true, href: 'profile/settings', title: 'Preferences', icon: Setting2 },
|
||||||
{ auth: true, href: '?logout', title: 'Logout', icon: LogoutCurve }
|
{ auth: true, href: '?logout', title: 'Logout', icon: LogoutCurve },
|
||||||
|
{ guestOnly: true, href: 'auth', title: 'LoginSignup', icon: Login }
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -52,7 +55,7 @@ type Props = {
|
|||||||
function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeState }: Props) {
|
function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeState }: Props) {
|
||||||
const menuStateMemo = useMemo(() => menuState, [menuState]);
|
const menuStateMemo = useMemo(() => menuState, [menuState]);
|
||||||
const closeRef = useRef<HTMLDivElement>(null);
|
const closeRef = useRef<HTMLDivElement>(null);
|
||||||
const userIsAuthenticated = true; // useAuthStore((state) => state.isAuthenticated);
|
const userIsAuthenticated = useAuthStore((state) => state.isAuthenticated);
|
||||||
const { state: logoutModal, toggle: toggleLogoutModal } = useToggle();
|
const { state: logoutModal, toggle: toggleLogoutModal } = useToggle();
|
||||||
const { state: shareModal, toggle: toggleShareModal } = useToggle();
|
const { state: shareModal, toggle: toggleShareModal } = useToggle();
|
||||||
const { state: installPwaModal, toggle: toggleInstallPwaModal } = useToggle();
|
const { state: installPwaModal, toggle: toggleInstallPwaModal } = useToggle();
|
||||||
@@ -92,7 +95,11 @@ function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeS
|
|||||||
<nav aria-label={tMenu('NavAriaLabel')}>
|
<nav aria-label={tMenu('NavAriaLabel')}>
|
||||||
<ul>
|
<ul>
|
||||||
{menuItems[0]
|
{menuItems[0]
|
||||||
.filter(item => !item.auth || (item.auth && userIsAuthenticated))
|
.filter(item => {
|
||||||
|
if (item.auth && !userIsAuthenticated) return false;
|
||||||
|
if (item.guestOnly && userIsAuthenticated) return false;
|
||||||
|
return true;
|
||||||
|
})
|
||||||
.map(({ icon: Icon, ...item }, index) => {
|
.map(({ icon: Icon, ...item }, index) => {
|
||||||
const href = `/${name}/${item.href}`;
|
const href = `/${name}/${item.href}`;
|
||||||
const isActive = pathname === href;
|
const isActive = pathname === href;
|
||||||
@@ -126,7 +133,11 @@ function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeS
|
|||||||
<section aria-label={tMenu('ControlsAriaLabel')}>
|
<section aria-label={tMenu('ControlsAriaLabel')}>
|
||||||
<ul className="flex flex-col pt-16">
|
<ul className="flex flex-col pt-16">
|
||||||
{menuItems[2]
|
{menuItems[2]
|
||||||
.filter(item => !item.auth || (item.auth && userIsAuthenticated))
|
.filter(item => {
|
||||||
|
if (item.auth && !userIsAuthenticated) return false;
|
||||||
|
if (item.guestOnly && userIsAuthenticated) return false;
|
||||||
|
return true;
|
||||||
|
})
|
||||||
.map(({ icon: Icon, ...item }, index) => {
|
.map(({ icon: Icon, ...item }, index) => {
|
||||||
const href = `/${name}/${item.href}`;
|
const href = `/${name}/${item.href}`;
|
||||||
const isActive = pathname === href;
|
const isActive = pathname === href;
|
||||||
@@ -203,6 +214,7 @@ function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeS
|
|||||||
<LogoutPrompt
|
<LogoutPrompt
|
||||||
visible={logoutModal}
|
visible={logoutModal}
|
||||||
onClick={toggleLogoutModal}
|
onClick={toggleLogoutModal}
|
||||||
|
slug={name as string}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Share modal */}
|
{/* Share modal */}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ type StepEnterNumberProps = {
|
|||||||
value?: string
|
value?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function StepEnterNumber (_props: StepEnterNumberProps = {}) {
|
function StepEnterNumber(_props: StepEnterNumberProps = {}) {
|
||||||
void _props
|
void _props
|
||||||
|
|
||||||
const { t } = useTranslation('auth')
|
const { t } = useTranslation('auth')
|
||||||
@@ -31,6 +31,7 @@ function StepEnterNumber (_props: StepEnterNumberProps = {}) {
|
|||||||
|
|
||||||
const { mutate: mutateOtpRequest, isPending } = useOtpRequest()
|
const { mutate: mutateOtpRequest, isPending } = useOtpRequest()
|
||||||
|
|
||||||
|
|
||||||
const formik = useFormik<LoginOTPRequestType>({
|
const formik = useFormik<LoginOTPRequestType>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
phone: '',
|
phone: '',
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import { useOtpVerify } from '@/app/auth/login/hooks/useAuthData'
|
|||||||
import { toast } from '@/components/Toast'
|
import { toast } from '@/components/Toast'
|
||||||
import Button from '@/components/button/PrimaryButton'
|
import Button from '@/components/button/PrimaryButton'
|
||||||
import { extractErrorMessage } from '@/lib/func'
|
import { extractErrorMessage } from '@/lib/func'
|
||||||
|
import { useReceiptStore } from '@/zustand/receiptStore'
|
||||||
|
import { useBulkCart } from '@/app/[name]/(Dialogs)/cart/hooks/useCartData'
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -19,6 +21,10 @@ const StepOtp = ({ phone, slug }: Props) => {
|
|||||||
|
|
||||||
const { t } = useTranslation('auth')
|
const { t } = useTranslation('auth')
|
||||||
const { mutate: mutateOtpVerify, isPending } = useOtpVerify()
|
const { mutate: mutateOtpVerify, isPending } = useOtpVerify()
|
||||||
|
|
||||||
|
const { items, clear } = useReceiptStore()
|
||||||
|
const { mutate: mutateBulkCart, isPending: isBulkCartPending } = useBulkCart()
|
||||||
|
|
||||||
const formik = useFormik<LoginVerifyOTPType>({
|
const formik = useFormik<LoginVerifyOTPType>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
phone: phone,
|
phone: phone,
|
||||||
@@ -34,7 +40,31 @@ const StepOtp = ({ phone, slug }: Props) => {
|
|||||||
toast(t('otp_verified'), 'success')
|
toast(t('otp_verified'), 'success')
|
||||||
localStorage.setItem(process.env.NEXT_PUBLIC_TOKEN_NAME as string, data?.data?.tokens?.accessToken?.token as string)
|
localStorage.setItem(process.env.NEXT_PUBLIC_TOKEN_NAME as string, data?.data?.tokens?.accessToken?.token as string)
|
||||||
localStorage.setItem(process.env.NEXT_PUBLIC_REFRESH_TOKEN_NAME as string, data?.data?.tokens?.refreshToken?.token as string)
|
localStorage.setItem(process.env.NEXT_PUBLIC_REFRESH_TOKEN_NAME as string, data?.data?.tokens?.refreshToken?.token as string)
|
||||||
window.location.href = `/${slug}`
|
|
||||||
|
if (Object.keys(items).length > 0) {
|
||||||
|
const bulkCartItems = Object.entries(items).map(([key, value]) => {
|
||||||
|
if (value.quantity > 0) {
|
||||||
|
return {
|
||||||
|
foodId: key,
|
||||||
|
quantity: value.quantity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}).filter((item): item is { foodId: string; quantity: number } => item !== undefined)
|
||||||
|
mutateBulkCart({ items: bulkCartItems }, {
|
||||||
|
onSuccess: () => {
|
||||||
|
clear()
|
||||||
|
window.location.href = `/${slug}`
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast(extractErrorMessage(error), 'error')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
window.location.href = `/${slug}`
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
toast(extractErrorMessage(error), 'error')
|
toast(extractErrorMessage(error), 'error')
|
||||||
@@ -64,7 +94,7 @@ const StepOtp = ({ phone, slug }: Props) => {
|
|||||||
<Button
|
<Button
|
||||||
className='mt-10'
|
className='mt-10'
|
||||||
disabled={!formik.isValid}
|
disabled={!formik.isValid}
|
||||||
pending={isPending}
|
pending={isPending || isBulkCartPending}
|
||||||
type='button'
|
type='button'
|
||||||
onClick={() => formik.handleSubmit()}
|
onClick={() => formik.handleSubmit()}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -5,9 +5,13 @@ import { useAuthStore } from '@/zustand/authStore';
|
|||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { removeToken, removeRefreshToken } from '@/lib/api/func';
|
||||||
|
|
||||||
|
export type LogoutPromptProps = Omit<PromptProps, 'onConfirm' | 'onCancel' | 'children'> & {
|
||||||
|
slug?: string;
|
||||||
|
};
|
||||||
|
|
||||||
function LogoutPrompt({ ...rest }: Omit<PromptProps, 'onConfirm' | 'onCancel' | 'children'>) {
|
function LogoutPrompt({ slug, ...rest }: LogoutPromptProps) {
|
||||||
const { t } = useTranslation('common', {
|
const { t } = useTranslation('common', {
|
||||||
keyPrefix: 'LogoutModal'
|
keyPrefix: 'LogoutModal'
|
||||||
});
|
});
|
||||||
@@ -15,12 +19,15 @@ function LogoutPrompt({ ...rest }: Omit<PromptProps, 'onConfirm' | 'onCancel' |
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const userIsAuthenticated = useAuthStore((state) => state.isAuthenticated);
|
const userIsAuthenticated = useAuthStore((state) => state.isAuthenticated);
|
||||||
|
|
||||||
const onConfirmLogout = (e: React.MouseEvent) => {
|
const onConfirmLogout = async (e: React.MouseEvent) => {
|
||||||
if (!e) return;
|
if (!e) return;
|
||||||
if (userIsAuthenticated) {
|
if (userIsAuthenticated) {
|
||||||
authLogout();
|
authLogout();
|
||||||
|
await removeToken();
|
||||||
|
await removeRefreshToken();
|
||||||
}
|
}
|
||||||
router.replace('/');
|
const redirectPath = slug ? `/${slug}` : '/';
|
||||||
|
router.replace(redirectPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleShareModalVisiblity = (e: React.MouseEvent) => {
|
const toggleShareModalVisiblity = (e: React.MouseEvent) => {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
"InstallApp": "نصب اپلیکیشن",
|
"InstallApp": "نصب اپلیکیشن",
|
||||||
"Preferences": "تنظیمات",
|
"Preferences": "تنظیمات",
|
||||||
"Logout": "خروج",
|
"Logout": "خروج",
|
||||||
|
"LoginSignup": "ورود / عضویت",
|
||||||
"AriaLabel": "منوی کناری",
|
"AriaLabel": "منوی کناری",
|
||||||
"NavAriaLabel": "لینکهای اصلی",
|
"NavAriaLabel": "لینکهای اصلی",
|
||||||
"ControlsAriaLabel": "پایین منو"
|
"ControlsAriaLabel": "پایین منو"
|
||||||
|
|||||||
Reference in New Issue
Block a user