This commit is contained in:
KarimAldeen 2024-03-30 10:15:43 +03:00
parent 469ab0f3b3
commit 86fe1eedb4
20 changed files with 343 additions and 27 deletions

View File

@ -43,6 +43,8 @@
"react-feather": "^2.0.10", "react-feather": "^2.0.10",
"react-i18next": "^13.3.1", "react-i18next": "^13.3.1",
"react-icons": "^4.11.0", "react-icons": "^4.11.0",
"react-input-range": "^1.3.0",
"react-number-format": "^5.3.4",
"react-query": "^3.39.3", "react-query": "^3.39.3",
"react-redux": "^8.1.3", "react-redux": "^8.1.3",
"react-router-dom": "^6.18.0", "react-router-dom": "^6.18.0",

View File

@ -13,7 +13,7 @@ const SearchBar = () => {
}; };
const updateUrlParams = (value:any) => { const updateUrlParams = (value:any) => {
navigate(`?search=${value}`, { replace: true }); navigate(`?search=${value}`);
}; };

View File

@ -23,11 +23,11 @@ const SearchField = ({ name, label, placeholder, isDisabled, searchBy, option, i
}; };
const SearchHandleChange = (value:any) => { const SearchHandleChange = (value:any) => {
if (value || value !== "") { if (value || value !== "") {
navigate(`${window?.location?.pathname}?${searchBy}=${value}`, { replace: true }); navigate(`${window?.location?.pathname}?${searchBy}=${value}`);
} else { } else {
const params = new URLSearchParams(location.search); const params = new URLSearchParams(location.search);
params.delete(searchBy ?? "search"); params.delete(searchBy ?? "search");
navigate(`${window?.location.pathname}?${params.toString()}`, { replace: true }); navigate(`${window?.location.pathname}?${params.toString()}`);
} }
}; };

View File

@ -11,11 +11,11 @@ export const PaginationBody = ({ data }: any) => {
const [searchParams] = useSearchParams() const [searchParams] = useSearchParams()
const onChange = (page: number, pageSize?: number) => { const onChange = (page: number, pageSize?: number) => {
navigate(`?page=${page}&per_page=${pageSize || data?.per_page}`, { replace: true }); navigate(`?page=${page}&per_page=${pageSize || data?.per_page}`);
}; };
const onShowSizeChange = (current: number, pageSize: number) => { const onShowSizeChange = (current: number, pageSize: number) => {
navigate(`?page=${current}&per_page=${pageSize}`, { replace: true }); navigate(`?page=${current}&per_page=${pageSize}`);
}; };
const [t] = useTranslation() const [t] = useTranslation()

View File

@ -16,11 +16,11 @@ const SearchField = ({ searchBy }: any) => {
const onSearch: SearchProps['onSearch'] = (value, _e, info) => { const onSearch: SearchProps['onSearch'] = (value, _e, info) => {
if (value || value !== "") { if (value || value !== "") {
navigate(`${location?.pathname}?${searchBy ?? "search"}=${value}`, { replace: true }); navigate(`${location?.pathname}?${searchBy ?? "search"}=${value}`);
} else { } else {
const params = new URLSearchParams(location.search); const params = new URLSearchParams(location.search);
params.delete(searchBy ?? "search"); params.delete(searchBy ?? "search");
navigate(`${location.pathname}?${params.toString()}`, { replace: true }); navigate(`${location.pathname}?${params.toString()}`);
} }
} }
@ -30,7 +30,7 @@ const SearchField = ({ searchBy }: any) => {
if (value === "") { if (value === "") {
const params = new URLSearchParams(location.search); const params = new URLSearchParams(location.search);
params.delete(searchBy ?? "search"); params.delete(searchBy ?? "search");
navigate(`${location.pathname}?${params.toString()}`, { replace: true }); navigate(`${location.pathname}?${params.toString()}`);
} }
} }

View File

@ -18,7 +18,7 @@ const SelectField = ({ selectBy, lebel, option }: any) => {
const handleSelectChange = (value: any) => { const handleSelectChange = (value: any) => {
if (value) { if (value) {
console.log(`${location.pathname}?${selectBy}=${value}`); console.log(`${location.pathname}?${selectBy}=${value}`);
navigate(`${location.pathname}?${selectBy}=${value}`, { replace: true }); navigate(`${location.pathname}?${selectBy}=${value}`);
} }
} }

View File

@ -16,11 +16,11 @@ const SelectWSearchField = ({ selectBy, submiteBy, lebel, option }: any) => {
const handleSearchChange = (value: any) => { const handleSearchChange = (value: any) => {
if (value || value !== "") { if (value || value !== "") {
navigate(`${location.pathname}?${selectBy}=${value}`, { replace: true }); navigate(`${location.pathname}?${selectBy}=${value}`);
} else { } else {
const params = new URLSearchParams(location.search); const params = new URLSearchParams(location.search);
params.delete(selectBy ?? "search"); params.delete(selectBy ?? "search");
navigate(`${location.pathname}?${params.toString()}`, { replace: true }); navigate(`${location.pathname}?${params.toString()}`);
} }
}; };
@ -28,7 +28,7 @@ const SelectWSearchField = ({ selectBy, submiteBy, lebel, option }: any) => {
if (value) { if (value) {
console.log(`${location.pathname}?${submiteBy}=${value}`); console.log(`${location.pathname}?${submiteBy}=${value}`);
navigate(`${location.pathname}?${submiteBy}=${value}`, { replace: true }); navigate(`${location.pathname}?${submiteBy}=${value}`);
} }
} }

View File

@ -29,7 +29,7 @@ const Header = () => {
const { logout , user} = useAuthState() const { logout , user} = useAuthState()
const handelClick = () => { const handelClick = () => {
logout() logout()
navigate('/auth', { replace: true }) navigate('/auth')
} }

View File

@ -12,7 +12,7 @@ const Layout = ({ children }: { children: React.ReactNode }) => {
useEffect(() => { useEffect(() => {
if (!isAuthenticated) { if (!isAuthenticated) {
navigate('/auth', { replace: true }) navigate('/auth')
} }
}, [navigate]) }, [navigate])
return ( return (

View File

@ -40,7 +40,7 @@ const AddProductPage = () => {
ar: values?.name_ar, ar: values?.name_ar,
de: values?.name_de de: values?.name_de
}, },
category_id: 1 category_id: values?.category_id
} }
mutate(new_category) mutate(new_category)
} }

View File

@ -6,6 +6,7 @@ import { useFormikContext } from 'formik';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { useGetCategories } from '../../../api/Categories'; import { useGetCategories } from '../../../api/Categories';
import useFormatToSelect from '../../../Hooks/useFormatToSelect'; import useFormatToSelect from '../../../Hooks/useFormatToSelect';
import { useParams } from 'react-router-dom';
const BasicInfo = ({ setIsValed, IsValed }: any) => { const BasicInfo = ({ setIsValed, IsValed }: any) => {
const [t] = useTranslation(); const [t] = useTranslation();
@ -13,6 +14,7 @@ const BasicInfo = ({ setIsValed, IsValed }: any) => {
const { values, isValid } = formikContext; const { values, isValid } = formikContext;
const { data } = useGetCategories() const { data } = useGetCategories()
const language = localStorage.getItem("language") ?? "en" const language = localStorage.getItem("language") ?? "en"
const {id}=useParams()
const SelectData = data?.categories?.map((item:any)=>( const SelectData = data?.categories?.map((item:any)=>(
{ {
@ -41,7 +43,10 @@ const BasicInfo = ({ setIsValed, IsValed }: any) => {
<Col> <Col>
<ValidationField name="name_de" /> <ValidationField name="name_de" />
{/* <ValidationField name="main_photo" type='File' /> */} {/* <ValidationField name="main_photo" type='File' /> */}
<ValidationField name="category_id" type="Search" label='category' placeholder='category' option={SelectData} searchBy={"name"} /> {!id &&
<ValidationField name="category_id" type="Search" label='category' placeholder='category' option={SelectData} searchBy={"name"} />
}
</Col> </Col>

View File

@ -5,12 +5,17 @@ import DashHeader from "../../Layout/Dashboard/DashHeader";
import { QueryStatusEnum } from "../../config/QueryStatus"; import { QueryStatusEnum } from "../../config/QueryStatus";
import LyTable from "../../Layout/Dashboard/LyTable"; import LyTable from "../../Layout/Dashboard/LyTable";
import { useGetOrder } from "../../api/order"; import { useGetOrder } from "../../api/order";
import SearchField from "../../Layout/Dashboard/SearchField"; import SelectField from "./ui/CustomSelectField";
import SelectField from "../../Layout/Dashboard/SelectField";
import { useGetCoupon } from "../../api/Coupon"; import { useGetCoupon } from "../../api/Coupon";
import SelectWSearchField from "../../Layout/Dashboard/SelectWSearchField"; import SelectWSearchField from "./ui/CustomSelectWSearchField";
// import SearchField from "../../Components/ValidationField/View/SearchField"; // import SearchField from "../../Components/ValidationField/View/SearchField";
import CustomDateRange from "./ui/CustomDateRange"; import CustomDateRange from "./ui/CustomDateRange";
import { FaCheck, FaTimes, FaClock, FaBan } from 'react-icons/fa';
import { Button } from "antd";
import { useLocation, useNavigate } from "react-router-dom";
import { useOrderFillterState } from "../../zustand/OrderFillter";
import CustomSearchField from "./ui/CustomSearchField";
import CustomNumber from "./ui/CustomNumber";
const OrderPage = () => { const OrderPage = () => {
@ -21,8 +26,12 @@ const OrderPage = () => {
const columns = useTableColumns(); const columns = useTableColumns();
/// Coupon status Created from -> to Price from => to /// Coupon status Created from -> to Price from => to
const statusData = [{label:"active",value:"active"},{label:"inActive",value:"inActive"}] const stateSelect = [
{ label: <div className='orderStatus_select'><FaClock /> Pending Approve</div>, value: "pending_approve" },
{ label: <div className='orderStatus_select'><FaCheck /> Approved</div>, value: "approved" },
{ label: <div className='orderStatus_select'><FaTimes /> Rejected</div>, value: "rejected" },
{ label: <div className='orderStatus_select'><FaBan /> Pending Cancellation</div>, value: "pending_cancellation" }
];
const { data:Coupon } = useGetCoupon() const { data:Coupon } = useGetCoupon()
@ -32,17 +41,66 @@ const OrderPage = () => {
value : item?.id value : item?.id
} }
)) ))
const location = useLocation();
const navigate = useNavigate();
const { username, coupon, state, fromDate, toDate, totalFrom, totalTo,reset } = useOrderFillterState();
const handleSubmit = () => {
let queryParams = [];
if (username !== null) {
queryParams.push(`username=${username}`);
}
if (coupon !== null) {
queryParams.push(`coupon=${coupon}`);
}
if (state !== null) {
queryParams.push(`state=${state}`);
}
if (fromDate !== null) {
queryParams.push(`fromDate=${fromDate}`);
}
if (toDate !== null) {
queryParams.push(`toDate=${toDate}`);
}
if (totalFrom !== null) {
queryParams.push(`totalFrom=${totalFrom}`);
}
if (totalTo !== null) {
queryParams.push(`totalTo=${totalTo}`);
}
const queryString = queryParams.join('&');
const newPathname = `${location.pathname}${queryString ? '?' : ''}${queryString}`;
navigate(newPathname);
};
const handelReset=()=>{
navigate(`${location.pathname}`);
reset()
}
return ( return (
<> <>
<DashBody status={status as QueryStatusEnum} > <DashBody status={status as QueryStatusEnum} >
<DashHeader title="orders" showAddButton={false}> <DashHeader title="orders" showAddButton={false}>
<div className='FillterSection'> <div className='FillterSection'>
<SearchField searchBy={"username"} /> <CustomSearchField searchBy={"username"} />
<SelectWSearchField selectBy="coupon" submiteBy="coupon_id" lebel="Coupon" option={SelectData} /> <SelectWSearchField selectBy="coupon" submiteBy="coupon_id" lebel="Coupon" option={SelectData} />
<SelectField selectBy="status" lebel="status" option={statusData} /> <SelectField selectBy="state" lebel="status" option={stateSelect} />
<CustomDateRange/> <CustomDateRange/>
<CustomNumber/>
<Button type="dashed" onClick={handelReset} >reset</Button>
<Button type="primary" onClick={handleSubmit} >submite</Button>
</div> </div>
</DashHeader> </DashHeader>

View File

@ -1,16 +1,19 @@
import React from 'react' import React from 'react'
import { DatePicker } from 'antd' import { DatePicker } from 'antd'
import { useLocation, useNavigate } from 'react-router-dom';
import { useOrderFillterState } from '../../../zustand/OrderFillter';
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
const CustomDateRange = () => { const CustomDateRange = () => {
const dateFormat = 'YYYY-MM-DD'; const dateFormat = 'YYYY-MM-DD';
const { toDate,fromDate,setFromDate,setToDate } = useOrderFillterState(); // Moved hook call inside the functional component
const onCalendarChange = (value: any) => { const onCalendarChange = (value: any) => {
const FromData = value[0]?.format(dateFormat) const FromData = value[0]?.format(dateFormat)
const ToData = value[1]?.format(dateFormat) const ToData = value[1]?.format(dateFormat)
console.log(FromData,"FromData"); setFromDate(FromData)
console.log(ToData,"ToData"); setToDate(ToData)
}; };
return ( return (

View File

@ -0,0 +1,30 @@
import { InputNumber } from 'antd';
import { useState, useEffect } from 'react';
import { useOrderFillterState } from '../../../zustand/OrderFillter';
const CustomNumber = () => {
const [valueFrom, setValueFrom] = useState<number | null>(null);
const [valueTo, setValueTo] = useState<number | null>(null);
const { totalFrom, setTotalFrom, totalTo, setTotalTo } = useOrderFillterState();
// Set initial state based on totalFrom and totalTo
const onChangeFrom = (value: number | null) => {
setValueFrom(value);
setTotalFrom(value);
};
const onChangeTo = (value: number | null) => {
setValueTo(value);
setTotalTo(value);
};
return (
<div className='CustomNumber'>
<InputNumber placeholder='PriceFrom' value={valueFrom} onChange={onChangeFrom} />
<InputNumber placeholder='PriceTo' value={valueTo} onChange={onChangeTo} />
</div>
);
};
export default CustomNumber;

View File

@ -0,0 +1,50 @@
import { Input } from 'antd';
import { SearchProps } from 'antd/es/input'
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useSearchParams } from 'react-router-dom';
import { useOrderFillterState } from '../../../zustand/OrderFillter';
const { Search } = Input;
const CustomSearchField = ({ searchBy }: any) => {
const { t } = useTranslation();
const {setUsername,username} = useOrderFillterState((state) => state)
const [searchParams,] = useSearchParams();
const [searchValue, setSearchValue] = useState(searchParams.get(searchBy ) || "");
const onSearch: SearchProps['onSearch'] = (value, _e, info) => {
if (value || value !== "") {
setUsername(value)
} else {
setUsername(value)
}
}
const onChange = (e: any) => {
const value = e.target.value
setSearchValue(value);
setUsername(value)
}
return (
<div className='SearchField'
>
<Search
allowClear
enterButton={t("search")}
size="middle"
placeholder={t(searchBy ?? "search")}
onSearch={onSearch}
style={{ width: 250 }}
value={username??searchValue}
onChange={onChange}
/>
</div>
)
}
export default CustomSearchField

View File

@ -0,0 +1,43 @@
import { Select } from 'antd';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useLocation, useNavigate } from 'react-router-dom';
import { useOrderFillterState } from '../../../zustand/OrderFillter';
const SelectField = ({ selectBy, lebel, option }: any) => {
const [searchQuery, setSearchQuery] = useState<string>('');
const location = useLocation();
const navigate = useNavigate();
const [t] = useTranslation();
const {setstate,state} = useOrderFillterState((state) => state)
useEffect(() => {
const searchParams = new URLSearchParams(location.search);
setSearchQuery(searchParams.get( selectBy) || '');
}, []);
const handleSelectChange = (value: any) => {
if (value) {
console.log(`${location.pathname}?${selectBy}=${value}`);
// navigate(`${location.pathname}?${selectBy}=${value}`);
setstate(value)
}
}
return (
<div className='SelectField'>
<Select
placeholder={t(`${lebel}`)}
options={option}
size="large"
className={`w-100`}
allowClear
onChange={handleSelectChange}
/>
</div>
);
};
export default React.memo(SelectField);

View File

@ -0,0 +1,55 @@
import { Select } from 'antd';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useLocation, useNavigate } from 'react-router-dom';
import { useOrderFillterState } from '../../../zustand/OrderFillter';
const SelectWSearchField = ({ selectBy, submiteBy, lebel, option }: any) => {
const [searchQuery, setSearchQuery] = useState<string>('');
const location = useLocation();
const navigate = useNavigate();
const [t] = useTranslation();
const { coupon, setCoupon } = useOrderFillterState(); // Moved hook call inside the functional component
useEffect(() => {
const searchParams = new URLSearchParams(location.search);
setSearchQuery(searchParams.get(submiteBy) || '');
}, []);
const handleSearchChange = (value: any) => {
if (value || value !== "") {
navigate(`${location.pathname}?${selectBy}=${value}`);
} else {
const params = new URLSearchParams(location.search);
params.delete(selectBy ?? "search");
navigate(`${location.pathname}?${params.toString()}`);
}
};
const handleSelectChange = (value: any) => {
if (value) {
setCoupon(value)
// navigate(`${location.pathname}?${submiteBy}=${value}`);
}
}
return (
<div className='SelectWSearchField'>
<Select
placeholder={t(`${lebel}`)}
options={option}
size="large"
className={`w-100`}
allowClear
// defaultValue={searchQuery}
onChange={handleSelectChange}
showSearch
optionFilterProp="label"
onSearch={handleSearchChange}
/>
</div>
);
};
export default React.memo(SelectWSearchField);

View File

@ -36,7 +36,7 @@ const useTableColumns = () => {
{ {
name: t("status"), name: t("status"),
center: true, center: true,
width:"250", width:"300",
cell: (row: any) => { cell: (row: any) => {
let status = row?.state; let status = row?.state;
let icon; let icon;

View File

@ -588,7 +588,7 @@ padding: 10px 40px;
gap: 5px; gap: 5px;
padding-inline: 10px; padding-inline: 10px;
text-wrap: nowrap; text-wrap: nowrap;
width: 250px; width: 300px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
svg{ svg{
@ -612,3 +612,23 @@ padding: 10px 40px;
.OrderDetails{ .OrderDetails{
padding: 20px 4vw ; padding: 20px 4vw ;
} }
.CustomNumber{
display: flex;
gap: 20px;
}
.SliderDataRange{
display: flex;
// flex-direction: column;
// justify-content: center;
// align-items: center;
// background: var(--bg);
// gap: 20px;
// width: 300px;
padding-inline: 20px;
h1{
font-size: 18px;
text-wrap: nowrap;
white-space: nowrap;
}
}

View File

@ -0,0 +1,50 @@
import { create } from 'zustand';
interface ModalState {
username: string | null;
coupon: string | null;
state: string | null;
fromDate: Date | null;
toDate: Date | null;
totalFrom: number | null;
totalTo: number | null;
setUsername: (value: string | null) => void;
setCoupon: (value: string | null) => void;
setstate: (value: string | null) => void;
setFromDate: (value: Date | null) => void;
setToDate: (value: Date | null) => void;
setTotalFrom: (value: number | null) => void;
setTotalTo: (value: number | null) => void;
reset: () => void;
}
export const useOrderFillterState = create<ModalState>((set) => {
return {
username: null,
coupon: null,
state: null,
fromDate: null,
toDate: null,
totalFrom: null,
totalTo: null,
setUsername: (value) => set((state) => ({ ...state, username: value })),
setCoupon: (value) => set((state) => ({ ...state, coupon: value })),
setstate: (value) => set((state) => ({ ...state, state: value })),
setFromDate: (value) => set((state) => ({ ...state, fromDate: value })),
setToDate: (value) => set((state) => ({ ...state, toDate: value })),
setTotalFrom: (value) => set((state) => ({ ...state, totalFrom: value })),
setTotalTo: (value) => set((state) => ({ ...state, totalTo: value })),
reset: () => {
set({
username: null,
coupon: null,
fromDate: null,
toDate: null,
totalFrom: null,
totalTo: null,
state:null
});
},
};
});