refactor: more use cases with useToggle

This commit is contained in:
Mahyar Khanbolooki
2025-08-10 18:29:48 +03:30
parent 389ac492c2
commit 7fca4375fc
45 changed files with 155 additions and 133 deletions
@@ -4,6 +4,7 @@ import AnimatedBottomSheet from '@/components/bottomsheet/AnimatedBottomSheet';
import Button from '@/components/button/PrimaryButton';
import Combobox, { ComboboxOption } from '@/components/combobox/Combobox';
import InputField from '@/components/input/InputField';
import useToggle from '@/hooks/helpers/useToggle';
import clsx from 'clsx';
import { motion } from 'framer-motion';
import { ArrowLeft, Box, Card, Cup, Icon, Shop, Ticket, TicketDiscount, TruckTick, Wallet2 } from 'iconsax-react';
@@ -26,12 +27,12 @@ function OrderDetailInex() {
const [paymentType, setPaymentType] = React.useState<string>('0');
const [couponType, setCouponType] = React.useState<string>('0');
const [couponCode, setCouponCode] = React.useState<string>('');
const [cartModal, setCartModal] = React.useState<boolean>(false);
const [couponCodeError, setCouponCodeError] = React.useState<string>(''); // TODO: handle coupon code error
const [tableNumber, setTableNumber] = useState(0) // TODO: must be set to table number
const incrementTableNumber = () => setTableNumber((prev) => prev + 1); // TODO: must be set to table number
const decrementTableNumber = () => setTableNumber((prev) => prev - 1); // TODO: must be set to table number
const address = "اراک، مصطفی خمینی، خیابان سینا کوچه چمران ساختمان شهرزاد"
const { state: cartModal, toggle: toggleCartModal } = useToggle();
const deliveryOptions: Array<ComboboxOption> = [
{ id: '0', title: t("SectionShipping.InputDeliveryType.Options.Delivery"), icon: TruckTick },
@@ -76,10 +77,6 @@ function OrderDetailInex() {
setCouponCode(id);
}, []);
const toggleCartModal = useCallback(() => {
setCartModal((prev) => !prev);
}, []);
const addressSection = () => {
if (deliveryType !== '0') {
return null;
@@ -313,4 +310,4 @@ function OrderDetailInex() {
)
}
export default OrderDetailInex
export default OrderDetailInex
@@ -8,11 +8,11 @@ import Button from '@/components/button/PrimaryButton';
import { Skeleton } from '@/components/ui/skeleton';
import { MarkerData } from '@/app/[name]/(Profile)/profile/address/new/page';
import { HandPlatter, Package } from 'lucide-react';
import { motion } from 'framer-motion';
import AnimatedBar from '@/components/utils/AnimatedBar';
import { Clock } from 'iconsax-react';
import BlurredOverlayContainer from '@/components/overlays/BlurredOverlayContainer';
import clsx from 'clsx';
import useToggle from '@/hooks/helpers/useToggle';
import Prompt from '@/components/utils/Prompt';
const CustomMap = dynamic(
() => import('@/components/map/CustomMap'),
@@ -29,9 +29,8 @@ function OrderTrackingPage() {
const [markers, setMarkers] = useState<MarkerData[]>([]);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [mapCenter, setMapCenter] = useState<[number, number]>([Number(params?.get('lon')) || 35.6892, Number(params?.get('lon')) || 51.3890]);
const [bottomSheet1State, setBottomSheet1State] = useState<boolean>(false);
const [cancelModalVisibility, setCancelModalVisibility] = useState<boolean>(false);
const router = useRouter();
const { state: cancelModal, toggle: toggleCancelModal } = useToggle();
// Example initial position (Tehran)
const initialPosition = React.useMemo<[number, number]>(() => [35.6892, 51.3890], []);
@@ -68,17 +67,7 @@ function OrderTrackingPage() {
console.log('Map clicked at:', e.latlng);
};
const toggleBottomSheet1 = () => {
setBottomSheet1State((prev) => !prev);
}
const toggleCancelModal = async (e?: React.MouseEvent) => {
e?.preventDefault();
e?.stopPropagation();
setCancelModalVisibility((state) => !state);
}
const onCancelOrder = (e: React.MouseEvent<HTMLButtonElement> | null) => {
const onCancelOrder = (e: React.MouseEvent | null) => {
if (!e) return;
};
@@ -99,30 +88,6 @@ function OrderTrackingPage() {
<div className="absolute bottom-0 left-0 right-0 z-[1000]">
<div className='relative w-full h-full' >
<AnimatedBottomSheet
bgOpacity={0}
inDuration={0}
blurOpacity={100}
noBlur
visible={bottomSheet1State}
showCloseButton={false}
showTitle={false}
overrideClassName='!pt-6 bg-container!'
>
<div className='grid grid-cols-2 gap-4 px-4'>
<Button
onClick={toggleCancelModal}
>
انتخاب
</Button>
<Button
onClick={toggleBottomSheet1}
className='bg-disabled! text-primary!'
>
انصراف
</Button>
</div>
</AnimatedBottomSheet>
<AnimatedBottomSheet
bgOpacity={0}
@@ -193,32 +158,18 @@ function OrderTrackingPage() {
<div className={clsx(
'fixed inset-0 z-1001',
!cancelModalVisibility && 'pointer-events-none'
!cancelModal && 'pointer-events-none'
)}>
<BlurredOverlayContainer visible={cancelModalVisibility} onClick={toggleCancelModal} inDuration={100} outDuration={100} outDelay={75}>
<div className="absolute top-1/2 left-6 right-6 px-6">
<motion.div
animate={{ y: cancelModalVisibility ? [10, 0] : [0, 10] }}
transition={{ duration: 0.1, ease: 'easeInOut' }}
className="top-1/2 absolute left-1/2 min-w-xs w-full max-w-sm text-center -translate-1/2 px-6 pt-8 pb-9 drop-shadow-container bg-white/63 rounded-4xl"
>
<h2 className="text-base font-medium">
لفو سفارش
</h2>
<p className="text-xs font-medium mt-6">
آیا از درخواست خود اطمینان دارید؟
</p>
<div className="grid grid-cols-2 gap-5.5 mt-8">
<Button onClick={onCancelOrder} className="text-sm font-normal">
بله
</Button>
<Button onClick={toggleCancelModal} className="bg-disabled! text-disabled2! text-sm font-normal">
منصرف شدم
</Button>
</div>
</motion.div>
</div>
</BlurredOverlayContainer>
<Prompt
title={'لفو سفارش'}
description={'آیا از درخواست خود اطمینان دارید؟'}
textConfirm={'بله'}
textCancel={'منصرف شدم'}
onConfirm={onCancelOrder}
visible={cancelModal}
onClick={toggleCancelModal}
onCancel={toggleCancelModal}
/>
</div>
</div>
);
+4 -6
View File
@@ -6,11 +6,12 @@ import TabContainer from '@/components/tab/TabContainer';
import { TabHeader } from '@/components/tab/TabHeader';
import Comment from '@/components/utils/Comment';
import RateBar from '@/components/utils/RateBar';
import useToggle from '@/hooks/helpers/useToggle';
import { AboutDataModel } from '@/lib/api/info/getAboutData';
import { ArrowDown2, CallCalling, Clock, Gallery, InfoCircle, Instagram, Location, Star1, Whatsapp } from 'iconsax-react';
import { useQueryState } from 'next-usequerystate';
import Image from 'next/image';
import React, { useCallback, useState } from 'react'
import React from 'react'
const sortings = [
'جدیدترین',
@@ -18,12 +19,8 @@ const sortings = [
]
function AboutPage({ data }: Readonly<{ data: AboutDataModel; }>) {
const [sortingModal, setSortingModal] = useState(false);
const [sorting, setSorting] = useQueryState('sortBy', { defaultValue: '0' });
const toggleSortingModal = useCallback(() => {
setSortingModal((state) => !state);
}, []);
const { state: sortingModal, toggle: toggleSortingModal, set: setSortingModal } = useToggle();
const changeSorting = (index: number) => {
setSorting(() => String(index));
@@ -31,6 +28,7 @@ function AboutPage({ data }: Readonly<{ data: AboutDataModel; }>) {
}
const firstTab = () => {
return (
<section aria-labelledby="about-title" className='py-4'>
<section
+3 -3
View File
@@ -35,7 +35,7 @@ function ChatIndex({ }: Props) {
const [search, setSearch] = useState('');
const [selectedChats, setSelectedChats] = useState<Array<string>>([]);
const router = useRouter();
const { active: deleteModal, onToggle: onToggleDeleteModal } = useToggle();
const { state: deleteModal, toggle: toggleDeleteModal } = useToggle();
const [chatsList, setChatList] = useState<Array<ChatEntryModel>>([
{
@@ -106,7 +106,7 @@ function ChatIndex({ }: Props) {
if (deleteModal) {
setSelectedChats([]);
}
onToggleDeleteModal()
toggleDeleteModal()
};
const deleteSelectedChats = () => {
@@ -153,7 +153,7 @@ function ChatIndex({ }: Props) {
<SearchBox placeholder={t('InputSearch.Placeholder')} value={search} onChange={(e) => setSearch(e.target.value)} />
{selectedChats.length > 0 ?
<span onClick={onToggleDeleteModal} className='bg-container p-2 rounded-xl active:bg-gray-50'>
<span onClick={toggleDeleteModal} className='bg-container p-2 rounded-xl active:bg-gray-50'>
<Trash size={24} className='stroke-primary' />
</span>
:
+4 -10
View File
@@ -22,6 +22,7 @@ import CategorySmallItemRenderer from "@/components/listview/CategorySmallItemRe
import { motion } from "framer-motion";
import { useTranslation } from "react-i18next";
import ComboBox, { ComboboxOption } from "@/components/combobox/Combobox";
import useToggle from "@/hooks/helpers/useToggle";
const categories = new Array(13).fill({ title: "خوراک", icon: "" });
@@ -148,8 +149,9 @@ const MenuIndex = () => {
{ id: '0', title: tMenu('MenuFilterDrawer.SelectDelivery.Options.Courier'), label: '' },
]
const [filterModal, setFilterModal] = useState(false);
const [sortingModal, setSortingModal] = useState(false);
const { state: filterModal, toggle: toggleFilterModal } = useToggle();
const { state: sortingModal, toggle: toggleSortingModal } = useToggle();
const [, setSorting] = useQueryState('sortBy', { defaultValue: '0' });
const [search, setSearch] = useQueryState("q", { defaultValue: '' });
const [selectedCategory, setSelectedCategory] = useQueryState('category', { defaultValue: '0' });
@@ -191,14 +193,6 @@ const MenuIndex = () => {
setSelectedCategory(String(id));
}, [setSelectedCategory]);
const toggleFilterModal = useCallback(() => {
setFilterModal((state) => !state);
}, []);
const toggleSortingModal = useCallback(() => {
setSortingModal((state) => !state);
}, []);
const changeSorting = (index: number) => {
setSorting(() => String(index));
}
@@ -8,6 +8,7 @@ import AnimatedBottomSheet from '@/components/bottomsheet/AnimatedBottomSheet';
import Button from '@/components/button/PrimaryButton';
import InputField from '@/components/input/InputField';
import { Skeleton } from '@/components/ui/skeleton';
import useToggle from '@/hooks/helpers/useToggle';
const CustomMap = dynamic(
() => import('@/components/map/CustomMap'),
@@ -60,8 +61,8 @@ function OrderTrackingPage() {
const [markers, setMarkers] = useState<MarkerData[]>([]);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [mapCenter, setMapCenter] = useState<[number, number]>([Number(params?.get('lon')) || 35.6892, Number(params?.get('lon')) || 51.3890]);
const [bottomSheet1State, setBottomSheet1State] = useState<boolean>(false);
const [bottomSheet2State, setBottomSheet2State] = useState<boolean>(false);
const { state: confirmModal, toggle: toggleConfirmModal, set: setConfirmModal } = useToggle();
const { state: detailsModal, toggle: _toggleDetailsModal, set: setDetailsModal } = useToggle();
// Example initial position (Tehran)
const initialPosition = React.useMemo<[number, number]>(() => [35.6892, 51.3890], []);
@@ -97,8 +98,8 @@ function OrderTrackingPage() {
setSelectedAddress(null);
return position;
});
setBottomSheet1State(position && true);
setBottomSheet2State(false);
setConfirmModal(position && true);
setDetailsModal(false);
// You can handle the selected position here
console.log('Selected position:', position);
};
@@ -111,19 +112,16 @@ function OrderTrackingPage() {
console.log('Map clicked at:', e.latlng);
};
const toggleBottomSheet1 = () => {
setBottomSheet1State((prev) => !prev);
}
const toggleBottomSheet2 = async () => {
const toggleDetailsModal = async () => {
if (!selectedPosition) {
setBottomSheet1State(false);
setBottomSheet2State(false);
setConfirmModal(false);
setDetailsModal(false);
return;
}
setBottomSheet1State(false);
setBottomSheet2State((prev) => !prev);
if (!bottomSheet2State) {
setConfirmModal(false);
_toggleDetailsModal();
if (!detailsModal) {
const address = await fetchAddressFromCoordinates(selectedPosition[0], selectedPosition[1]);
setSelectedAddress(address);
}
@@ -148,6 +146,32 @@ function OrderTrackingPage() {
console.log(formData);
}
const getSelectedAddress = () => {
if (!selectedAddress) {
return "آدرس یافت نشد"
}
try {
return [
selectedAddress.address.state ?? selectedAddress.address.province,
selectedAddress.address.city,
selectedAddress.address.neighbourhood,
!selectedAddress.address.road.startsWith('میدان') &&
!selectedAddress.address.road.startsWith('بلوار') &&
!selectedAddress.address.road.startsWith('خیابان') &&
!selectedAddress.address.road.startsWith('کوچه') ?
selectedAddress.type === 'secondary' ? `بلوار ${selectedAddress.address.road}` :
selectedAddress.type === 'tertiary' ? `خیابان ${selectedAddress.address.road}` :
selectedAddress.type === 'residential' ? `کوچه ${selectedAddress.address.road}` :
selectedAddress.address.road : selectedAddress.address.road
].filter(x => x).join('، ')
} catch {
if (selectedAddress.display_name)
return selectedAddress.display_name;
else
return "آدرس یافت نشد"
}
}
return (
<div className="fixed inset-0 bg-gray-50">
@@ -175,19 +199,19 @@ function OrderTrackingPage() {
inDuration={0}
blurOpacity={null}
noBlur
visible={bottomSheet1State}
visible={confirmModal}
showCloseButton={false}
showTitle={false}
overrideClassName='!pt-6 bg-container!'
>
<div className='grid grid-cols-2 gap-4 px-4'>
<Button
onClick={toggleBottomSheet2}
onClick={toggleDetailsModal}
>
انتخاب
</Button>
<Button
onClick={toggleBottomSheet1}
onClick={toggleConfirmModal}
className='bg-disabled! text-foreground!'
>
انصراف
@@ -200,9 +224,9 @@ function OrderTrackingPage() {
inDuration={0}
blurOpacity={null}
noBlur
visible={bottomSheet2State}
visible={detailsModal}
title='آدرس'
onClick={toggleBottomSheet2}
onClick={toggleDetailsModal}
overrideClassName='bg-container!'
>
<form onSubmit={submitAddressAction}>
@@ -214,19 +238,7 @@ function OrderTrackingPage() {
className='h-6 w-full'
/>
:
[
selectedAddress.address.state ?? selectedAddress.address.province,
selectedAddress.address.city,
selectedAddress.address.neighbourhood,
!selectedAddress.address.road.startsWith('میدان') &&
!selectedAddress.address.road.startsWith('بلوار') &&
!selectedAddress.address.road.startsWith('خیابان') &&
!selectedAddress.address.road.startsWith('کوچه') ?
selectedAddress.type === 'secondary' ? `بلوار ${selectedAddress.address.road}` :
selectedAddress.type === 'tertiary' ? `خیابان ${selectedAddress.address.road}` :
selectedAddress.type === 'residential' ? `کوچه ${selectedAddress.address.road}` :
selectedAddress.address.road : selectedAddress.address.road
].filter(x => x).join('، ')
getSelectedAddress()
}
</span>
<hr className='border border-border mt-3 mb-10 ' />
@@ -252,7 +264,7 @@ function OrderTrackingPage() {
</Button>
<Button
type='button'
onClick={toggleBottomSheet2}
onClick={toggleDetailsModal}
className='bg-disabled! text-foreground!'
>
انصراف
+1 -1
View File
@@ -1,6 +1,6 @@
import { Metadata, Viewport } from "next";
import "./globals.css";
import { ReactQueryProvider } from "@/components/providers/ReactQueryProvider";
import { ReactQueryProvider } from "@/features/providers/ReactQueryProvider";
import { notFound } from "next/navigation";
import i18nConfig from "../../i18nConfig";
import initTranslations from '@/lib/i18n';