This commit is contained in:
KarimAldeen 2024-02-21 15:12:12 +03:00
parent 086116119c
commit 82c7c0d09b
81 changed files with 2751 additions and 298 deletions

BIN
public/Logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -8,7 +8,7 @@
content="Web site created using create-react-app" content="Web site created using create-react-app"
/> />
<title>Etaxi - App</title> <title>Hijab - App</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

@ -16,7 +16,7 @@ const App = () => {
{/* 404 Page */} {/* 404 Page */}
<Route path="*" element={<Suspense fallback={<Loading />}> <Page404 /></Suspense>} /> <Route path="*" element={<Suspense fallback={<Loading />}> <Page404 /></Suspense>} />
{/* Login Page */} {/* Login Page */}
{/* <Route path="/auth" element={<Suspense fallback={<Loading />}> <Auth /></Suspense>} /> */} <Route path="/auth" element={<Suspense fallback={<Loading />}> <Auth /></Suspense>} />
{/* route not in navigation */} {/* route not in navigation */}

View File

@ -2,6 +2,7 @@ import React from "react";
import "./KarimField.scss"; import "./KarimField.scss";
import { Date, Time, File, DataRange, SelectField, Default, CheckboxField } from './View'; import { Date, Time, File, DataRange, SelectField, Default, CheckboxField } from './View';
import { KarimFieldProps } from "./types"; import { KarimFieldProps } from "./types";
import MaltyFile from "./View/MaltyFile";
const KarimField: React.FC<KarimFieldProps> = ({type = "text", ...otherProps}) => { const KarimField: React.FC<KarimFieldProps> = ({type = "text", ...otherProps}) => {
switch (type) { switch (type) {
@ -15,6 +16,8 @@ const KarimField: React.FC<KarimFieldProps> = ({type = "text", ...otherProps}) =
return <Time {...otherProps} />; return <Time {...otherProps} />;
case "File": case "File":
return <File {...otherProps} />; return <File {...otherProps} />;
case "MaltyFile":
return <MaltyFile {...otherProps} />;
case "Checkbox": case "Checkbox":
return <CheckboxField {...otherProps} />; return <CheckboxField {...otherProps} />;
default: default:

View File

@ -2,7 +2,7 @@ import { Form, Input } from 'antd'
import React from 'react' import React from 'react'
import useFormField from '../../../Hooks/useFormField'; import useFormField from '../../../Hooks/useFormField';
const Default = ({ name, label, placeholder, isDisabled, onChange, props }: any) => { const Default = ({ name, label, placeholder, isDisabled, onChange, props,type="text" }: any) => {
const { Field, formik, isError, errorMsg, t } = useFormField(name, props); const { Field, formik, isError, errorMsg, t } = useFormField(name, props);
@ -18,11 +18,12 @@ const Default = ({ name, label, placeholder, isDisabled, onChange, props }: any)
> >
<Field <Field
as={Input} as={Input}
type="text" type={type}
placeholder={t(`${placeholder ?placeholder : name}`)} placeholder={t(`${placeholder ?placeholder : name}`)}
name={name} name={name}
disabled={isDisabled} disabled={isDisabled}
size="large" size="large"
// onChange={onChange ? onChange : handleChange} // onChange={onChange ? onChange : handleChange}
/> />
</Form.Item> </Form.Item>

View File

@ -0,0 +1,52 @@
import { Button, Upload } from 'antd';
import { UploadOutlined } from '@ant-design/icons';
import { ImageBaseURL } from '../../../api/config';
import { useTranslation } from 'react-i18next';
import useFormField from '../../../Hooks/useFormField';
const MaltyFile = ({ name, label, onChange, isDisabled, placholder, className, props }: any) => {
const { formik, t, isError } = useFormField(name, props);
const imageUrl = formik.values[name] ? ImageBaseURL + formik.values[name] : '';
const fileList = formik.values[name] ? formik.values[name].map((file: any, index: number) => ({
uid: index,
name: file.name,
status: 'done',
url: file.url || '',
thumbUrl: file.url || '',
})) : [];
const FilehandleChange = ({ file, fileList }: any) => {
formik.setFieldValue(name, fileList.map((file: any) => file.originFileObj));
};
const customRequest = async ({ onSuccess }: any) => {
// Perform any necessary actions before onSuccess is called
onSuccess();
};
return (
<div className="KarimField">
<label htmlFor={name} className="text">
{t(`${label || name}`)}
</label>
<Upload
disabled={isDisabled}
listType="picture"
defaultFileList={[...fileList]}
onChange={onChange || FilehandleChange}
customRequest={customRequest}
className={`${className} w-100`}
multiple // Allow multiple files to be selected
>
<Button className={isError ? "isError w-100" : " w-100"} icon={<UploadOutlined />}>
{placholder ?? t("upload_image")}
</Button>
<div className='Error_color'> {isError ? "required" : ""}</div>
</Upload>
</div>
);
};
export default MaltyFile;

View File

@ -77,7 +77,7 @@
export interface KarimFieldPropsFile { export interface KarimFieldPropsFile {
name: string; name: string;
type: "File"; type: "File" | "MaltyFile";
placeholder?: string; placeholder?: string;
label?: string; label?: string;
className?: string; className?: string;

View File

@ -7,15 +7,15 @@ import { useTranslation } from "react-i18next";
interface ToggleStatusProps { interface ToggleStatusProps {
} }
export const ToggleStatus = ({ object, toggleMutation, ...props }:any) => { export const ToggleStatus = ({ object, handleSwitch, toggleMutation, ...props }:any) => {
const [t] = useTranslation(); const [t] = useTranslation();
const handleSwitch = () => { // const handleSwitch = () => {
toggleMutation.mutate({ // toggleMutation.mutate({
id: object.id, // id: object.id,
new_status: !object.is_active, // new_status: !object.is_active,
}); // });
}; // };
return ( return (
<> <>

View File

@ -2,13 +2,15 @@ import React, { useEffect } from 'react'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
function useNavigateOnSuccess(isSuccess :boolean , to_path:string , callbackAfterSuccess:any) { function useNavigateOnSuccess(isSuccess :boolean , to_path:string , callbackAfterSuccess?:any) {
const navigate = useNavigate() const navigate = useNavigate()
useEffect(()=>{ useEffect(()=>{
if(isSuccess){ if(isSuccess){
if (typeof callbackAfterSuccess === 'function') {
callbackAfterSuccess() callbackAfterSuccess()
}
navigate(to_path , {replace:true}) navigate(to_path , {replace:true})
} }
},[isSuccess]) },[isSuccess])

View File

@ -0,0 +1,33 @@
import React from 'react'
import './Add_Button.scss'
import { useTranslation } from 'react-i18next'
import { usePageState } from '../../../lib/state mangment/LayoutPagestate'
const AddButton = (props :any) => {
const [t] = useTranslation();
return (
<div className='Add_Button' {...props} >
<button >
<span >
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={24}
height={24}
>
<path fill="none" d="M0 0h24v24H0z" />
<path fill="currentColor" d="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z" />
</svg>{" "}
{t("Add")}
</span>
</button>
</div>
)
}
export default AddButton

View File

@ -0,0 +1,77 @@
import React from "react";
import { Card, CardHeader, CardTitle, CardBody, Button } from "reactstrap";
import { Formik, Form } from "formik";
import { LoadingButton } from "../../Components/Ui/LoadingButton";
import ProgressBar from "../../Components/Ui/ProgressBar";
import { useNavigate } from "react-router-dom";
import { usePageState } from "../../lib/state mangment/LayoutPagestate";
import { useTranslation } from "react-i18next";
type TViewPage ={
children: React.ReactNode,
getInitialValues:any,
getValidationSchema:any,
getDataToSend:any,
handleSubmit:any,
// BarStatus:any,
showProgressBar?:boolean,
}
const ViewPage: React.FC<TViewPage>= ({children,getInitialValues, getValidationSchema,handleSubmit,showProgressBar = true})=> {
const {objectToEdit} = usePageState()
const {t} = useTranslation();
const navigate = useNavigate();
// console.log(BarStatus);
return (
<Card className="ViewTapPage">
<CardHeader className="CardHeader" >
<CardTitle className="View_information">
{t("View_information")}
</CardTitle>
<Button onClick={() => { navigate(-1);}}> {t("back")} </Button>
</CardHeader>
<CardBody>
{
<Formik
onSubmit={handleSubmit}
initialValues={getInitialValues(objectToEdit)}
validationSchema={getValidationSchema()}
>
{(formik) => (
<Form>
{/* <HeadTabs tabs={tabs} /> */}
{children}
{showProgressBar &&
<>
{/* <ProgressBar
value={BarStatus?.value}
isLoading={BarStatus?.isLoading}
isError={BarStatus?.isError}
isSuccess={BarStatus?.isSuccess}
/> */}
<div className="d-flex mt-4 justify-content-center align-items-center">
{/* <LoadingButton
type="submit"
color="primary"
// isLoading={BarStatus?.isLoading}
>
{t("save")}
</LoadingButton> */}
<Button type="submit" color="primary"> {t("save")} </Button>
</div>
</>
}
</Form>
)}
</Formik>
}
</CardBody>
</Card>
);
};
export default ViewPage;

View File

@ -1,10 +1,11 @@
import React from 'react' import React from 'react'
import LoadingSpinner from '../../Components/Ui/LoadingSpinner' import LoadingSpinner from '../../Components/Ui/LoadingSpinner'
import { Spin } from 'antd'
function LoadingPage() { function LoadingPage() {
return ( return (
<div style={{height:"80vh", width:"100%" , display:"flex" , justifyContent:"center" , alignItems:"center"}}> <div style={{height:"80vh", width:"100%" , display:"flex" , justifyContent:"center" , alignItems:"center"}}>
<LoadingSpinner/> <Spin/>
</div> </div>
) )
} }

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react'; import React, { useEffect, useState } from 'react';
import { FaAngleDown, FaAngleRight } from 'react-icons/fa'; import { FaAngleDown, FaAngleRight } from 'react-icons/fa';
import { GiHamburgerMenu } from 'react-icons/gi'; import { GiHamburgerMenu } from 'react-icons/gi';
import { Link, useLocation } from 'react-router-dom'; import { Link, useLocation } from 'react-router-dom';
@ -7,11 +7,24 @@ import { useTranslation } from 'react-i18next';
import KarimLogo from './KarimLogo'; import KarimLogo from './KarimLogo';
import { useWindowSize } from '../../Hooks/useWindowSize'; import { useWindowSize } from '../../Hooks/useWindowSize';
import Etaxi from './Etaxi'; import Etaxi from './Etaxi';
import { usePageState } from '../../lib/state mangment/LayoutPagestate';
interface SidebarProps {} interface SidebarProps {}
const Sidebar: React.FC<SidebarProps> = () => { const Sidebar: React.FC<SidebarProps> = () => {
const { pathname } = useLocation(); const { pathname } = useLocation();
// const {isOpenAddModel ,isOpenEditModel} = usePageState(state => state)
// useEffect(() => {
// if(isOpenAddModel || isOpenEditModel){
// setIsOpenSide(true);
// document.getElementById('DashboardLayout_Body')?.classList.add('DashboardLayout_Body_Open');
// }
// }, [isOpenAddModel ,isOpenEditModel])
const [isOpenSide, setIsOpenSide] = useState<boolean>(false); const [isOpenSide, setIsOpenSide] = useState<boolean>(false);
const [openDropdown, setOpenDropdown] = useState<number | null>(null); const [openDropdown, setOpenDropdown] = useState<number | null>(null);
@ -37,8 +50,8 @@ const Sidebar: React.FC<SidebarProps> = () => {
<div className={isOpenSide ? 'SideBar SideBar_Open' : 'SideBar noOpen'}> <div className={isOpenSide ? 'SideBar SideBar_Open' : 'SideBar noOpen'}>
<div className='SideBar_Top'> <div className='SideBar_Top'>
<div onClick={handleImg}> <div onClick={handleImg}>
{/* <img src="../Layout/logorayan.png" width={isOpenSide ? 70 : 150} alt="" /> */} <img src="../Logo.png" width={isOpenSide ? 70 : 150} alt="" />
<Etaxi/> {/* <Etaxi/> */}
</div> </div>
<div className='HamburgerMenu' onClick={handleHamburgerMenu}> <div className='HamburgerMenu' onClick={handleHamburgerMenu}>
<GiHamburgerMenu /> <GiHamburgerMenu />

View File

@ -2,23 +2,23 @@ import React from 'react'
import { Formik, Form, Field } from 'formik'; import { Formik, Form, Field } from 'formik';
import Translate from '../../Components/Utils/Translate'; import Translate from '../../Components/Utils/Translate';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { TOKEN_KEY } from '../../config/AppKey';
import { useLoginAdmin } from '../../api/auth'; import { useLoginAdmin } from '../../api/auth';
import * as Yup from "yup"; import * as Yup from "yup";
import { getInitialValues, getValidationSchema } from './formUtil'; import { getInitialValues, getValidationSchema } from './formUtil';
import KarimField from '../../Components/Karimalden/KarimField';
import { LoadingButton } from '../../Components/Ui/LoadingButton'; import { LoadingButton } from '../../Components/Ui/LoadingButton';
import useNavigateOnSuccess from '../../Hooks/useNavigateOnSuccess'; import useNavigateOnSuccess from '../../Hooks/useNavigateOnSuccess';
import useAuthState from '../../lib/state mangment/AuthState'; import useAuthState from '../../lib/state mangment/AuthState';
import KarimField from '../../Components/Karimalden/KarimField';
const LoginForm = () => { const LoginForm = () => {
const [t] = useTranslation(); const [t] = useTranslation();
const {mutate , isLoading , isSuccess, data} = useLoginAdmin() const {mutate , isLoading , isSuccess, data} = useLoginAdmin()
const {login} = useAuthState() const {login} = useAuthState()
const OnSuccess = ()=>{
}
useNavigateOnSuccess(isSuccess , '/' , ()=>login(data?.data as any )) useNavigateOnSuccess(isSuccess , '/' , ()=>login(data?.data as any ))
const handelSubmit = (values:any)=>{ const handelSubmit = (values:any)=>{

View File

@ -18,7 +18,7 @@ const Auth = () => {
<div className='Auth' style={{ background: `url(${LoginBg})` }}> <div className='Auth' style={{ background: `url(${LoginBg})` }}>
<div className='In_Auth'> <div className='In_Auth'>
<div className="Left_Col"> <div className="Left_Col">
<img className='Logo' src="/Layout/etaxlogo.svg" alt="Logo" /> <img className='Logo' src="../Logo.png" alt="Logo" />
</div> </div>
<div className=" Right_Col "> <div className=" Right_Col ">
<LoginForm /> <LoginForm />

View File

@ -1,43 +0,0 @@
import React from 'react'
import LayoutModal from '../../Layout/Dashboard/LayoutModal'
import AddForm from './AddForm'
import { useAddCategories } from '../../api/Categories'
import { getDataToSend, getInitialValues, getValidationSchema } from './formUtil'
import { QueryStatusEnum } from '../../config/QueryStatus'
import { useTranslation } from 'react-i18next'
function AddCategoriesModal() {
const [t] = useTranslation()
const {mutate , status} = useAddCategories()
const handelSubmit = (values:any )=>{
values['name'] = {
"en" : values.name_en,
"ar" : values.name_ar,
"de" : values.name_de,
}
console.log(values);
mutate(values)
}
return (
<LayoutModal
isAddModal={true}
getInitialValues={getInitialValues()}
handleSubmit={handelSubmit}
status={status as QueryStatusEnum}
headerText={t('Add') +t('Categories')}
getValidationSchema={getValidationSchema()}>
<AddForm />
</LayoutModal>
)
}
export default AddCategoriesModal

View File

@ -1,41 +0,0 @@
import React from 'react';
import FormPage from '../../../Layout/Dashboard/FormPage';
import EditForm from './EditForm';
import { getInitialValues, getValidationSchema } from '../formUtil';
import { useParams } from 'react-router-dom';
import KarimSpinner from '../../../Components/Karimalden/Ui/KarimSpinner';
import { useAddCategories, useGetOneCategories } from '../../../api/Categories';
const EditPage = () => {
const { id } = useParams()
const { data,isLoading } = useGetOneCategories({id})
const {mutate} = useAddCategories()
const handleSubmit = (values: any) => {
const newdata = {} as any;
for (const key in data) {
if (values[key] !== data[key]) {
newdata[key] = values[key];
}
}
return mutate(newdata);
};
return (
<KarimSpinner loading={isLoading}>
<FormPage
handleSubmit={(values: any) => handleSubmit(values)}
initialValues={getInitialValues(data)}
validationSchema={getValidationSchema}
title='Edit Categories'
>
<EditForm />
</FormPage>
</KarimSpinner>
);
};
export default EditPage;

View File

@ -4,37 +4,39 @@ import DashBody from '../../Layout/Dashboard/DashBody'
import DashHeader from '../../Layout/Dashboard/DashHeader' import DashHeader from '../../Layout/Dashboard/DashHeader'
import LyTable from '../../Layout/Dashboard/LyTable' import LyTable from '../../Layout/Dashboard/LyTable'
import useTableColumns from './useTableColumns' import useTableColumns from './useTableColumns'
import { QueryStatusEnum } from '../../config/QueryStatus'
import { useGetProduct } from '../../api/product'
import { Button } from 'antd'
import { useTranslation } from 'react-i18next'
import { useNavigate } from 'react-router-dom'
import AddButton from '../../Layout/Dashboard/AddButton/AddButton'
import { useGetCategories } from '../../api/Categories' import { useGetCategories } from '../../api/Categories'
import { useTranslation } from 'react-i18next' function Page() {
import AddCategoriesModal from './AddCategoriesModal'
import { QueryStatusEnum } from '../../config/QueryStatus'
// import * as XLSX from 'ts-xlsx';
function CategoriesPage() {
const column =useTableColumns() const column =useTableColumns()
const {data ,status } = useGetCategories() const {data ,status } = useGetCategories()
const {t} = useTranslation(); console.log(data);
const [t] = useTranslation()
const navigate = useNavigate()
return ( return (
// Pass Status to Layout
<DashBody status={status as QueryStatusEnum} > <DashBody status={status as QueryStatusEnum} >
<DashHeader title={'Categories'}> <DashHeader showAddButton={false} title={'categories'}>
<AddButton onClick={()=>navigate('/categories/add')}></AddButton>
</DashHeader> </DashHeader>
<LyTable <LyTable
data={data?.categories} data={data?.categories}
isLoading={false} isLoading={false}
columns={column} columns={column}
// is_pagination={true}
// total={data?.meta?.total}
/> />
<AddCategoriesModal />
</DashBody> </DashBody>
) )
} }
export default CategoriesPage export default Page

View File

@ -1,17 +1,15 @@
import React from 'react' import React from 'react'
import { Col, Row } from 'reactstrap'; import { Col, Row } from 'reactstrap';
import KarimField from '../../Components/Karimalden/KarimField'; import KarimField from '../../../Components/Karimalden/KarimField';
import { FakeSelectData } from '../../Layout/app/Const';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import { DatePicker } from 'antd'; import { DatePicker } from 'antd';
import { useGetCategories } from '../../api/Categories'; import { useTranslation } from 'react-i18next';
function AddForm() { function Form() {
const formik = useFormikContext<any>(); const formik = useFormikContext<any>();
const {data } = useGetCategories() const [t] = useTranslation();
return ( return (
<Row xs={1} sm={1} md={1} lg={2} xl={2}> <Row xs={1} sm={1} md={1} lg={2} xl={2}>
@ -28,10 +26,11 @@ function AddForm() {
</Col> </Col>
</Row> </Row>
) )
} }
export default AddForm export default Form

View File

@ -0,0 +1,62 @@
import React, { useEffect, useState } from 'react'
import { getInitialValues, getValidationSchema, getDataToSend } from '../formUtil'
import { Tab, TabList, TabPanel as TabBody, Tabs } from 'react-tabs'
import 'react-tabs/style/react-tabs.css';
import { MdLanguage } from 'react-icons/md'
import ViewPage from '../../../Layout/Dashboard/ViewPage';
import { useTranslation } from 'react-i18next';
import { BsInfoCircle } from 'react-icons/bs';
import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess';
import { useAddCategories } from '../../../api/Categories';
import Form from './AddForm';
const AddcategoriesPage = () => {
const {mutate , isLoading , isSuccess} = useAddCategories()
const handleSubmit = (values:any)=>{
values['name'] = {
"en" : values.name_en,
"ar" : values.name_ar,
"de" : values.name_de,
}
mutate(values)
}
const {t} = useTranslation();
useNavigateOnSuccess(isSuccess , '/categories' )
const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit };
return (
<div className='ViewPage'>
<ViewPage {...ViewProps}>
<Tabs>
<TabList>
<Tab><div className='SignleDriverContainer'><span className='SignleDriverInfoIcon'><MdLanguage size={20} /></span> <h6 className='SingleDriverInfo'>{t("BasicInfo")}</h6></div></Tab>
</TabList>
<TabBody >
<div className=" mt-4"><Form /></div>
</TabBody>
</Tabs>
</ViewPage>
</div>
)
}
export default AddcategoriesPage

View File

@ -0,0 +1,32 @@
import React from 'react'
import { Col, Row } from 'reactstrap';
import KarimField from '../../../Components/Karimalden/KarimField';
import { useFormikContext } from 'formik';
import { DatePicker } from 'antd';
import { useTranslation } from 'react-i18next';
function Form() {
const formik = useFormikContext<any>();
const [t] = useTranslation();
return (
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
<Col>
<KarimField name="name" />
<KarimField name="parent_id" type="Select" option={[]} />
</Col>
<Col>
<KarimField name="photo" type="File" />
</Col>
</Row>
)
}
export default Form

View File

@ -0,0 +1,86 @@
import React, { useEffect, useState } from 'react'
import { getInitialValues, getDataToSend } from '../formUtil'
import { Tab, TabList, TabPanel as TabBody, Tabs } from 'react-tabs'
import 'react-tabs/style/react-tabs.css';
import { MdLanguage } from 'react-icons/md'
import { FaSadCry } from 'react-icons/fa'
import ViewPage from '../../../Layout/Dashboard/ViewPage';
import { Rate } from 'antd';
import { usePageState } from '../../../lib/state mangment/LayoutPagestate';
import { useParams } from 'react-router-dom';
import LoadingPage from '../../../Layout/app/LoadingPage';
import { useTranslation } from 'react-i18next';
import { BsInfoCircle } from 'react-icons/bs';
import { useGetOneCategories, useUpdateCategories } from '../../../api/Categories';
import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess';
import Form from './EditForm';
const EditPage = () => {
const { setObjectToEdit, objectToEdit } = usePageState()
const {t} = useTranslation();
const { data } = useGetOneCategories()
const {mutate ,isSuccess} = useUpdateCategories()
const FormatedData = data?.category ;
const handleSubmit = (values:any)=>{
const newData = {} as any;
for (const key in FormatedData) {
if (values[key] !== FormatedData[key]) {
newData[key] = values[key];
}
const language = localStorage.getItem("language") ?? "en";
newData['name'] = {
[language]: values.name,
};
}
return mutate(newData);
}
useNavigateOnSuccess(isSuccess , '/categories')
useEffect(() => {
console.log(data);
setObjectToEdit(data?.category);
}, [data]);
const getValidationSchema = () => {
return null
};
const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit };
return (
<div className='ViewPage'>
{objectToEdit && data ?
<ViewPage {...ViewProps}>
<Tabs>
<TabList>
<Tab><div className='SignleDriverContainer'><span className='SignleDriverInfoIcon'><MdLanguage size={20} /></span> <h6 className='SingleDriverInfo'>{t("BasicInfo")}</h6></div></Tab>
</TabList>
<TabBody >
<div className=" mt-4"><Form /></div>
</TabBody>
</Tabs>
</ViewPage>
: <LoadingPage />}
</div>
)
}
export default EditPage

View File

@ -20,9 +20,10 @@ interface ValidateSchema extends formUtilCommon{
} }
export const getInitialValues = (objectToEdit: any | null = null): any => { export const getInitialValues = (objectToEdit: any | null = null): any => {
console.log(objectToEdit,"objectToEdit");
return { return {
id: objectToEdit?.id ?? 0, id: objectToEdit?.id ?? 0,
name: "", name:objectToEdit?.name ?? "",
name_ar: objectToEdit?.name?.ar ?? '', name_ar: objectToEdit?.name?.ar ?? '',
name_en: objectToEdit?.name?.en ?? '', name_en: objectToEdit?.name?.en ?? '',
name_de: objectToEdit?.name?.de ?? '', name_de: objectToEdit?.name?.de ?? '',
@ -35,7 +36,6 @@ export const getInitialValues = (objectToEdit: any | null = null): any => {
export const getValidationSchema = (editMode: boolean = false): Yup.Schema<any> => { export const getValidationSchema = (editMode: boolean = false): Yup.Schema<any> => {
// Validate input // Validate input
return Yup.object().shape({ return Yup.object().shape({
name : Yup.string().required('Required'),
name_ar: Yup.string().required('Required'), name_ar: Yup.string().required('Required'),
name_en: Yup.string().required('Required'), name_en: Yup.string().required('Required'),
name_de: Yup.string().required('Required'), name_de: Yup.string().required('Required'),
@ -45,6 +45,7 @@ export const getValidationSchema = (editMode: boolean = false): Yup.Schema<any>
}; };
export const getDataToSend = (values: any): FormData => { export const getDataToSend = (values: any): FormData => {
const data = { ...values }; const data = { ...values };

View File

@ -1,23 +0,0 @@
import React from 'react'
import { Col, Row } from "reactstrap";
import KarimField from '../../../Components/Karimalden/KarimField';
const CreateForm = () => {
return (
<Row>
<Col>
<KarimField name="name" />
<KarimField name="parent_id" type="Select" option={[]} />
</Col>
<Col>
<KarimField name="photo" type="File" />
</Col>
</Row>
)
}
export default CreateForm

View File

@ -0,0 +1,55 @@
import React from 'react'
import { Col, Row } from 'reactstrap';
import KarimField from '../../Components/Karimalden/KarimField';
import { useFormikContext } from 'formik';
import { DatePicker } from 'antd';
import { useTranslation } from 'react-i18next';
function FormProduct() {
const formik = useFormikContext<any>();
const [t] = useTranslation();
return (
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
<Col>
<KarimField
name="translated_fields[1][product_name]"
label={`${t("product_name")} (${t("en")})`}
placeholder={`${t("product_name")} (${t("en")})`}
/>
<KarimField
name="translated_fields[1][product_description]"
label={`${t("product_description")} (${t("en")})`}
placeholder={`${t("product_description")} (${t("en")})`}
/>
</Col>
<Col>
<KarimField
dir="rtl"
name="translated_fields[2][product_name]"
label={`${t("product_name")} (${t("ar")})`}
placeholder={`${t("product_name")} (${t("ar")})`}
/>
<KarimField
dir="rtl"
name="translated_fields[2][product_description]"
label={`${t("product_description")} (${t("ar")})`}
placeholder={`${t("product_description")} (${t("ar")})`}
/>
</Col>
</Row>
)
}
export default FormProduct

View File

@ -0,0 +1,41 @@
import React from 'react'
import DashBody from '../../Layout/Dashboard/DashBody'
import DashHeader from '../../Layout/Dashboard/DashHeader'
import LyTable from '../../Layout/Dashboard/LyTable'
import useTableColumns from './useTableColumns'
import { QueryStatusEnum } from '../../config/QueryStatus'
import { useGetProduct } from '../../api/product'
import { Button } from 'antd'
import { useTranslation } from 'react-i18next'
import { useNavigate } from 'react-router-dom'
import AddButton from '../../Layout/Dashboard/AddButton/AddButton'
function ProductsPage() {
const column =useTableColumns()
const {data ,status } = useGetProduct("")
console.log(data);
const [t] = useTranslation()
const navigate = useNavigate()
return (
// Pass Status to Layout
<DashBody status={status as QueryStatusEnum} >
<DashHeader showAddButton={false} title={'products'}>
<AddButton onClick={()=>navigate('/products/add')}></AddButton>
</DashHeader>
<LyTable
data={data?.products}
isLoading={false}
columns={column}
/>
</DashBody>
)
}
export default ProductsPage

View File

@ -0,0 +1,72 @@
import React, { useEffect, useState } from 'react'
import { getInitialValues, getValidationSchema, getDataToSend } from '../formUtil'
import { Tab, TabList, TabPanel as TabBody, Tabs } from 'react-tabs'
import 'react-tabs/style/react-tabs.css';
import { MdLanguage } from 'react-icons/md'
import { FaSadCry } from 'react-icons/fa'
import ViewPage from '../../../Layout/Dashboard/ViewPage';
import { Rate } from 'antd';
import BasicInfo from './BasicInfo';
import { usePageState } from '../../../lib/state mangment/LayoutPagestate';
import { useParams } from 'react-router-dom';
import LoadingPage from '../../../Layout/app/LoadingPage';
import { useTranslation } from 'react-i18next';
import { BsInfoCircle } from 'react-icons/bs';
import BasicInfo2 from './BasicInfo2';
import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess';
import { useAddProduct } from '../../../api/product';
const AddProductPage = () => {
const {mutate , isLoading , isSuccess} = useAddProduct()
const [BarStatus, setBarStatus] = useState({ value: 0, isLoading: false, isError: false, isSuccess: false })
const handleSubmit = (values:any)=>{
console.log(values);
const formToSend = getDataToSend(values)
mutate(formToSend);
}
const {t} = useTranslation();
useNavigateOnSuccess(isSuccess , '/products' )
const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit, BarStatus };
return (
<div className='ViewPage'>
<ViewPage {...ViewProps}>
<Tabs>
<TabList>
<Tab><div className='SignleDriverContainer'><span className='SignleDriverInfoIcon'><MdLanguage size={20} /></span> <h6 className='SingleDriverInfo'>{t("BasicInfo")}</h6></div></Tab>
<Tab><div className='SignleDriverContainer'><span className='SignleDriverInfoIcon'><BsInfoCircle size={20} /></span> <h6 className='SingleDriverInfo'>{t("BasicInfo2")}</h6></div></Tab>
</TabList>
<TabBody >
<div className=" mt-4"><BasicInfo /></div>
</TabBody>
<TabBody >
<div className=" mt-4"><BasicInfo2 /></div>
</TabBody>
</Tabs>
</ViewPage>
</div>
)
}
export default AddProductPage

View File

@ -0,0 +1,47 @@
import React from 'react'
import { Col, Row } from 'reactstrap'
import KarimField from '../../../Components/Karimalden/KarimField'
import { useTranslation } from 'react-i18next';
const BasicInfo = () => {
const [t] = useTranslation();
return (
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
<Col>
<KarimField
name="translated_fields[1][product_name]"
label={`${t("product_name")} (${t("en")})`}
placeholder={`${t("product_name")} (${t("en")})`}
/>
<KarimField
name="translated_fields[1][product_description]"
label={`${t("product_description")} (${t("en")})`}
placeholder={`${t("product_description")} (${t("en")})`}
/>
</Col>
<Col>
<KarimField
dir="rtl"
name="translated_fields[2][product_name]"
label={`${t("product_name")} (${t("ar")})`}
placeholder={`${t("product_name")} (${t("ar")})`}
/>
<KarimField
dir="rtl"
name="translated_fields[2][product_description]"
label={`${t("product_description")} (${t("ar")})`}
placeholder={`${t("product_description")} (${t("ar")})`}
/>
</Col>
</Row>
)
}
export default BasicInfo

View File

@ -0,0 +1,87 @@
import React from 'react'
import { Col, Row } from 'reactstrap'
import KarimField from '../../../Components/Karimalden/KarimField'
import { useTranslation } from 'react-i18next';
const BasicInfo2 = () => {
const [t] = useTranslation();
const categoryOption = [] as any
return (
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
<Col>
<KarimField name="product_main_image" type="File" label='image' placeholder='image' />
<KarimField
type='Select'
option={categoryOption}
name="category_id"
label={`${t("category")}`}
placeholder={`${t("category")}`}
/>
{/* <KarimField
name="is_cheapest"
label={t("is_cheapest")}
placeholder={t("is_cheapest")}
type="Checkbox"
/> */}
<KarimField
name="is_most_purchase"
label={t("is_most_purchase")}
// placeholder={t("is_most_purchase")}
type="Checkbox"
/>
</Col>
<Col>
<KarimField
name="product_price"
label={t("price")}
placeholder={t("price")}
type="number"
/>
<KarimField
name="product_quantity"
label={t("product_quantity")}
placeholder={t("product_quantity")}
type="number"
/>
{/*
<KarimField
name="is_latest"
label={t("is_latest")}
placeholder={t("is_latest")}
type="Checkbox"
/>
*/}
<KarimField
name="is_highlight"
label={t("is_highlight")}
// placeholder={t("is_highlight")}
type="Checkbox"
/>
</Col>
</Row>
)
}
export default BasicInfo2

View File

@ -0,0 +1,91 @@
import React, { useEffect, useState } from 'react'
import { getInitialValues, getValidationSchema, getDataToSend } from '../formUtil'
import { Tab, TabList, TabPanel as TabBody, Tabs } from 'react-tabs'
import 'react-tabs/style/react-tabs.css';
import { MdLanguage } from 'react-icons/md'
import { FaSadCry } from 'react-icons/fa'
import ViewPage from '../../../Layout/Dashboard/ViewPage';
import { Rate } from 'antd';
import BasicInfo from './BasicInfo';
import { usePageState } from '../../../lib/state mangment/LayoutPagestate';
import { useParams } from 'react-router-dom';
import LoadingPage from '../../../Layout/app/LoadingPage';
import { useTranslation } from 'react-i18next';
import { BsInfoCircle } from 'react-icons/bs';
import { useGetOneProduct, useUpdateProduct } from '../../../api/product';
import BasicInfo2 from './BasicInfo2';
import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess';
const ViewProduct = () => {
const { setObjectToEdit, objectToEdit } = usePageState()
const {t} = useTranslation();
const { id } = useParams()
const { data } = useGetOneProduct({id:id})
const [BarStatus, setBarStatus] = useState({ value: 0, isLoading: false, isError: false, isSuccess: false })
const {mutate ,isSuccess} = useUpdateProduct()
const handleSubmit = (values:any)=>{
values['product_id'] = id
values['is_highlight'] =values['is_highlight'] == true ?1 :0
values['is_most_purchase'] =values['is_most_purchase'] == true ?1 :0
const formToSend = getDataToSend(values)
mutate(formToSend)
}
useNavigateOnSuccess(isSuccess , '/products')
useEffect(() => {
console.log(data);
setObjectToEdit(data);
}, [data]);
useEffect(()=>{
return ()=>{
setObjectToEdit(null)
}
},[])
const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit, BarStatus };
return (
<div className='ViewPage'>
{objectToEdit && data ?
<ViewPage {...ViewProps}>
<Tabs>
<TabList>
<Tab><div className='SignleDriverContainer'><span className='SignleDriverInfoIcon'><MdLanguage size={20} /></span> <h6 className='SingleDriverInfo'>{t("BasicInfo")}</h6></div></Tab>
<Tab><div className='SignleDriverContainer'><span className='SignleDriverInfoIcon'><BsInfoCircle size={20} /></span> <h6 className='SingleDriverInfo'>{t("BasicInfo2")}</h6></div></Tab>
</TabList>
<TabBody >
<div className=" mt-4"><BasicInfo /></div>
</TabBody>
<TabBody >
<div className=" mt-4"><BasicInfo2 /></div>
</TabBody>
</Tabs>
</ViewPage>
: <LoadingPage />}
</div>
)
}
export default ViewProduct

View File

@ -1,65 +1,70 @@
import * as Yup from "yup"; import * as Yup from "yup";
import { buildFormData } from "../../api/helper/buildFormData"; import { buildFormData } from "../../api/helper/buildFormData";
import { mapTranslatedProperties } from "../../utils/language/mapTranslatedProperties";
interface formUtilCommon {
number:number,
value:number
}
interface ObjectToEdit extends formUtilCommon {
id?:number, export const getInitialValues = (objectToEdit: any | null = null) => {
} // console.log(objectToEdit);
interface InitialValues extends ObjectToEdit {
}
interface ValidateSchema extends formUtilCommon{
}
export const getInitialValues = (objectToEdit: any | null = null): any => {
return { return {
id: objectToEdit?.id ?? 0, product_price:objectToEdit?.product_price??0,
name: "", product_quantity:objectToEdit?.product_quantity??0,
name_ar: objectToEdit?.name?.ar ?? '', product_main_image:objectToEdit?.product_main_image??'',
name_en: objectToEdit?.name?.en ?? '', translated_fields: {
name_de: objectToEdit?.name?.de ?? '', 1: {
parent_id: objectToEdit?.parent_id ?? 1, product_name: mapTranslatedProperties(objectToEdit?.product_translations ,'name', 1) ??'',
photo: objectToEdit?.photo ?? '', product_description: mapTranslatedProperties(objectToEdit?.product_translations ,'description', 1) ?? '',
};
},
2: {
product_name: mapTranslatedProperties(objectToEdit?.product_translations ,'name', 2) ?? '',
product_description: mapTranslatedProperties(objectToEdit?.product_translations ,'description', 2) ?? '',
},
},
category_id:objectToEdit?.category_id ?? null,
is_highlight:objectToEdit?.is_highlight??false,
is_most_purchase:objectToEdit?.is_most_purchase??false,
is_cheapest:objectToEdit?.is_cheapest??false
}
}; };
export const getValidationSchema = (editMode: boolean = false) => {
export const getValidationSchema = (editMode: boolean = false): Yup.Schema<any> => { // validate input
// Validate input
return Yup.object().shape({ return Yup.object().shape({
name : Yup.string().required('Required'), translated_fields : Yup.object().shape({
name_ar: Yup.string().required('Required'), 1: Yup.object().shape({
name_en: Yup.string().required('Required'), product_name :Yup.string().required("required"),
name_de: Yup.string().required('Required'), product_description :Yup.string().required("required")
parent_id: Yup.string().required('Required'), }),
photo: Yup.string().required('Required'), 2: Yup.object().shape({
product_name :Yup.string().required("required"),
product_description :Yup.string().required("required")
})
}),
category_id :Yup.string().required("required"),
...(!editMode && {
product_main_image: Yup.mixed().required('required'),
}),
}); });
}; };
export const getDataToSend = (values: any): FormData => { export const getDataToSend = (values: any): FormData => {
const data = { ...values }; const data = { ...values };
// console.log(data);
if(typeof data['product_main_image'] == 'string') delete data['product_main_image']
data['en_product_name'] = values['translated_fields']['1']['product_name']
data['ar_product_name'] =values['translated_fields']['2']['product_name']
data['ar_product_description'] =values['translated_fields']['2']['product_description']
data['en_product_description'] =values['translated_fields']['1']['product_description']
const formData = new FormData(); const formData = new FormData();
buildFormData(formData, data); buildFormData(formData, data);
return formData; return formData;
}; };
export const ChangeDataToPrint = (data:any)=>{
let new_array = data
for(let i =0 ; i<data.length ; i++){
new_array[i]['status'] =!data[i]['deleted_at'] ?'available':'unavailable'
delete new_array[i]['deleted_at']
}
return new_array
}

View File

@ -2,81 +2,87 @@
import React, { useMemo } from "react"; import React, { useMemo } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import Actions from "../../Components/Ui/tables/Actions"; import Actions from "../../Components/Ui/tables/Actions";
import { HovarableImage } from "../../Components/Ui";
import { BaseURL } from "../../api/config";
import { ToggleStatus } from "../../Components/Ui/ToggleStatus";
import ColumnsImage from "../../Components/Columns/ColumnsImage"; import ColumnsImage from "../../Components/Columns/ColumnsImage";
import { useDeleteCoupon } from "../../api/Coupon"; import LoadingSpinner from "../../Components/Ui/LoadingSpinner";
import { Switch } from "antd";
import { mapTranslatedProperties } from "../../utils/language/mapTranslatedProperties";
// import { useDeleteProduct, useUpdateProductStatus } from "../../api/owner_products";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { useDeleteProduct, useUpdateProductStatus } from "../../api/product";
function fnDelete(props :any ){}
const useTableColumns :any = () => { const useTableColumns :any = () => {
const [t] = useTranslation(); const [t] = useTranslation();
const deleteMutation = useDeleteCoupon() const toggleMutation = useUpdateProductStatus();
const deleteMutation = useDeleteProduct();
const navigate = useNavigate() const navigate = useNavigate()
const handleChange = (row:any)=> {
const status = row?.favorite ;
toggleMutation.mutate({id:row?.id,new_status:status})
}
return useMemo( return useMemo(
() => [ () => [
{
name: t("image"),
center: "true",
cell: (row: any) => {
return (
<ColumnsImage src={row?.main_photo} />
)
}
},
{ {
name: t("name"), name: t("name"),
sortable: false, sortable: false,
center: "true", center: true,
cell: (row:any) => row?.name selector:(row:any) => row?.name,
}, },
{ {
name: t("image"), name: t("price"),
sortable: false, sortable: false,
center: "true", center: true,
cell: (row:any) => <ColumnsImage src={row?.photo} /> selector:(row:any) => row?.price,
}, },
{ {
name: t("parent_id"), name: t("description"),
sortable: false, sortable: false,
center: "true", center: true,
cell: (row:any) => row?.parent_id cell: (row:any) => (
row?.description
),
}, },
{ {
name: t("product_count"), name: t("favorite"),
sortable: false, sortable: false,
center: "true", center: true,
cell: (row:any) => row?.product_count cell: (row:any) => (
<ToggleStatus handleSwitch={handleChange} object={row} toggleMutation={toggleMutation} />
),
}, },
{ {
name: "#", name: "#",
sortable: false, sortable: false,
center: true, center: "true",
cell: (row:any) => ( cell: (row:any) => (
<Actions <Actions
onEdit={()=> navigate('/products/'+row.id)}
objectToEdit={row} objectToEdit={row}
showEdit showEdit={true}
onEdit={()=> navigate(`/Coupon/${row.id}`) }
showView={false} showView={false}
onDelete={() => deleteMutation.mutate({ id: row.id })} onDelete={() => deleteMutation.mutate({ product_id: row.id })}
/> />
), ),
}, },
// {
// name: t("status"),
// sortable: false,
// center: "true",
// cell: (row:any) => {
// return(
// <p style={{
// background:!row.deleted_at ?'#19ab27':'#ea5454',
// color:"white",
// padding:6,
// borderRadius:10,
// position:"relative",
// top:'7px'
// }}>
// {
// !row.deleted_at ? t('available') : t('unavailable')
// }
// </p>
// )
// }
// },
], ],
[t] [t]
); );

View File

@ -6,11 +6,11 @@ import { FakeSelectData } from '../../Layout/app/Const';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import { DatePicker } from 'antd'; import { DatePicker } from 'antd';
import { useGetCoupon } from '../../api/Coupon'; import { useGetSlider } from '../../api/Slider';
function AddForm() { function AddForm() {
const formik = useFormikContext<any>(); const formik = useFormikContext<any>();
const {data } = useGetCoupon() const {data } = useGetSlider()
return ( return (

View File

@ -24,7 +24,7 @@ function CouponPage() {
</DashHeader> </DashHeader>
<LyTable <LyTable
data={data?.Coupon} data={data?.coupons}
isLoading={false} isLoading={false}
columns={column} columns={column}
// is_pagination={true} // is_pagination={true}

View File

@ -27,7 +27,7 @@ const EditPage = () => {
<KarimSpinner loading={isLoading}> <KarimSpinner loading={isLoading}>
<FormPage <FormPage
handleSubmit={(values: any) => handleSubmit(values)} handleSubmit={(values: any) => handleSubmit(values)}
initialValues={getInitialValues(data)} initialValues={getInitialValues(data?.coupons)}
validationSchema={getValidationSchema} validationSchema={getValidationSchema}
title='Edit Coupon' title='Edit Coupon'
> >

View File

@ -0,0 +1,76 @@
import * as Yup from "yup";
import { buildFormData } from "../../api/helper/buildFormData";
interface formUtilCommon {
number:number,
value:number
}
interface ObjectToEdit extends formUtilCommon {
id?:number,
}
interface InitialValues extends ObjectToEdit {
}
interface ValidateSchema extends formUtilCommon{
}
export const getInitialValues = (objectToEdit: any | null = null): any => {
return {
id: objectToEdit?.id ?? 0,
name: objectToEdit?.name??"",
code: objectToEdit?.code??"",
coupon_value: objectToEdit?.coupon_value??"",
status: objectToEdit?.status??"",
active_to: objectToEdit?.active_to??"",
discount_type: objectToEdit?.discount_type??"",
coupon_type: objectToEdit?.coupon_type??"",
minimum_total_to_order: objectToEdit?.minimum_total_to_order??"",
maximum_number_of_uses: objectToEdit?.maximum_number_of_uses??"",
useable_by_guest: objectToEdit?.useable_by_guest??"",
};
};
export const getValidationSchema = (editMode: boolean = false): Yup.Schema<any> => {
// Validate input
return Yup.object().shape({
name : Yup.string().required('Required'),
code : Yup.number().required('Required'),
coupon_value : Yup.number().required('Required'),
status : Yup.string().required('Required'),
active_to : Yup.string().required('Required'),
discount_type : Yup.string().required('Required'),
coupon_type : Yup.string().required('Required'),
minimum_total_to_order : Yup.number().required('Required'),
maximum_number_of_uses : Yup.number().required('Required'),
useable_by_guest : Yup.string().required('Required'),
});
};
export const getDataToSend = (values: any): FormData => {
const data = { ...values };
const formData = new FormData();
buildFormData(formData, data);
return formData;
};
export const ChangeDataToPrint = (data:any)=>{
let new_array = data
for(let i =0 ; i<data.length ; i++){
new_array[i]['status'] =!data[i]['deleted_at'] ?'available':'unavailable'
delete new_array[i]['deleted_at']
}
return new_array
}

View File

@ -0,0 +1,104 @@
import React, { useMemo } from "react";
import { useTranslation } from "react-i18next";
import Actions from "../../Components/Ui/tables/Actions";
import ColumnsImage from "../../Components/Columns/ColumnsImage";
import { useDeleteCoupon } from "../../api/Coupon";
import { useNavigate } from "react-router-dom";
import { Button } from "antd";
function fnDelete(props :any ){}
const useTableColumns :any = () => {
const [t] = useTranslation();
const deleteMutation = useDeleteCoupon()
const navigate = useNavigate()
return useMemo(
() => [
{
name: t("name"),
sortable: false,
center: "true",
cell: (row:any) => row?.name
},
{
name: t("code"),
sortable: false,
center: "true",
cell: (row:any) => row?.code
},
{
name: t("coupon_value"),
sortable: false,
center: "true",
cell: (row:any) => row?.coupon_value
}
,
{
name: t("status"),
sortable: false,
center: true,
cell: (row:any) => {
return <Button type="primary" style={{ backgroundColor: row.status === 'active' ? 'green' : 'red' }}>{row.status}</Button>
}
},
{
name: t("discount_type"),
sortable: false,
center: "true",
cell: (row:any) => row?.discount_type
},
{
name: t("coupon_type"),
sortable: false,
center: "true",
cell: (row:any) => row?.coupon_type
},
{
name: "#",
sortable: false,
center: true,
cell: (row:any) => (
<Actions
objectToEdit={row}
showEdit
onEdit={()=> navigate(`/Coupon/${row.id}`) }
showView={false}
onDelete={() => deleteMutation.mutate({ id: row.id })}
/>
),
},
// {
// name: t("status"),
// sortable: false,
// center: "true",
// cell: (row:any) => {
// return(
// <p style={{
// background:!row.deleted_at ?'#19ab27':'#ea5454',
// color:"white",
// padding:6,
// borderRadius:10,
// position:"relative",
// top:'7px'
// }}>
// {
// !row.deleted_at ? t('available') : t('unavailable')
// }
// </p>
// )
// }
// },
],
[t]
);
};
export default useTableColumns;

View File

@ -0,0 +1,40 @@
import React from 'react'
import DashBody from '../../Layout/Dashboard/DashBody'
import DashHeader from '../../Layout/Dashboard/DashHeader'
import LyTable from '../../Layout/Dashboard/LyTable'
import useTableColumns from './useTableColumns'
import { QueryStatusEnum } from '../../config/QueryStatus'
import { useGetProduct } from '../../api/product'
import { useTranslation } from 'react-i18next'
import { useNavigate } from 'react-router-dom'
import AddButton from '../../Layout/Dashboard/AddButton/AddButton'
function ProductsPage() {
const column =useTableColumns()
const {data ,status } = useGetProduct()
console.log(data);
const [t] = useTranslation()
const navigate = useNavigate()
return (
// Pass Status to Layout
<DashBody status={status as QueryStatusEnum} >
<DashHeader showAddButton={false} title={'products'}>
<AddButton onClick={()=>navigate('/products/add')}></AddButton>
</DashHeader>
<LyTable
data={data?.products}
isLoading={false}
columns={column}
/>
</DashBody>
)
}
export default ProductsPage

View File

@ -0,0 +1,91 @@
import React, { useEffect, useState } from 'react'
import { getInitialValues, getValidationSchema, getDataToSend } from '../formUtil'
import { Tab, TabList, TabPanel as TabBody, Tabs } from 'react-tabs'
import 'react-tabs/style/react-tabs.css';
import { MdLanguage } from 'react-icons/md'
import { FaCheck, FaSadCry } from 'react-icons/fa'
import ViewPage from '../../../Layout/Dashboard/ViewPage';
import { Rate } from 'antd';
import BasicInfo from './BasicInfo';
import { usePageState } from '../../../lib/state mangment/LayoutPagestate';
import { useParams } from 'react-router-dom';
import LoadingPage from '../../../Layout/app/LoadingPage';
import { useTranslation } from 'react-i18next';
import { BsInfoCircle } from 'react-icons/bs';
import BasicInfo2 from './BasicInfo2';
import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess';
import { useAddProduct } from '../../../api/product';
import VarianInfo from './VarianInfo';
const AddProductPage = () => {
const { mutate, isLoading, isSuccess } = useAddProduct()
const [BarStatus, setBarStatus] = useState({ value: 0, isLoading: false, isError: false, isSuccess: false })
const [IsValed, setIsValed] = useState(1)
const handleSubmit = (values: any) => {
values['name'] = {
"en" : values.name_en,
"ar" : values.name_ar,
"de" : values.name_de,
}
values['description'] = {
"en" : values.description_en,
"ar" : values.description_ar,
"de" : values.description_de,
}
const infoArray = Object.entries(values.info).map(([key, value]: [string, any]) => ({
[key.split('.')[1]]: value
}));
values["info"]= infoArray
console.log(infoArray);
// mutate(values);
// console.log(values);
}
const { t } = useTranslation();
useNavigateOnSuccess(isSuccess, '/products')
const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit, BarStatus };
return (
<div className='ViewPage'>
<ViewPage {...ViewProps}>
<Tabs>
<TabList>
<Tab><div className='SignleDriverContainer'><span className='SignleDriverInfoIcon'><MdLanguage size={20} /></span> <h6 className='SingleInfo'>{t("Base_info")} {IsValed ? <FaCheck />: "" } </h6></div></Tab>
<Tab disabled={IsValed === 0}><div className='SignleDriverContainer'><span className='SignleDriverInfoIcon'><BsInfoCircle size={20} /></span> <h6 className='SingleInfo'>{t("VarianInfo")}</h6></div></Tab>
</TabList>
<TabBody >
<div className=" mt-4"><BasicInfo setIsValed={setIsValed} /></div>
</TabBody>
<TabBody >
<div className=" mt-4"><VarianInfo /></div>
</TabBody>
</Tabs>
</ViewPage>
</div>
)
}
export default AddProductPage

View File

@ -0,0 +1,42 @@
import React, { useEffect, useState } from 'react'
import { Col, Row } from 'reactstrap'
import KarimField from '../../../Components/Karimalden/KarimField'
import { useTranslation } from 'react-i18next';
import { useFormikContext } from 'formik';
const BasicInfo = ({setIsValed}:any) => {
const [t] = useTranslation();
const formikContext = useFormikContext();
const { values, submitForm,isValid } = formikContext;
useEffect(() => {
//@ts-ignore
if (values.name_ar !== "" && values.name_en !== "" && values.name_de !== "" && values.main_photo !== "" && values.category_id !== "" ) {
setIsValed(1);
console.log(isValid, 'isValid');
} else {
console.log(values);
}
}, [isValid,values]);
return (
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
<Col>
<KarimField name="name_ar" />
<KarimField name="name_en" />
<KarimField name="name_de" />
</Col>
<Col>
<KarimField name="main_photo" type='File' />
<KarimField name="category_id" type='Select' option={[]} label='category' placeholder='category' />
</Col>
</Row>
)
}
export default BasicInfo

View File

@ -0,0 +1,32 @@
import React from 'react'
import { Col, Row } from 'reactstrap'
import KarimField from '../../../Components/Karimalden/KarimField'
import { useTranslation } from 'react-i18next';
import ObjectField from './ObjectField';
const BasicInfo2 = () => {
const [t] = useTranslation();
const categoryOption = [] as any
return (
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
<Col>
{/* <KarimField name="images" type='MaltyFile' /> */}
</Col>
<Col>
{/* <KarimField name="price" /> */}
{/* <KarimField name="quantity" type='number' /> */}
{/* <KarimField name="info" /> */}
<ObjectField/>
</Col>
</Row>
)
}
export default BasicInfo2

View File

@ -0,0 +1,92 @@
import React, { useEffect, useState } from 'react';
import { CloseOutlined } from '@ant-design/icons';
import { Button, Card, Form, Input, Space, Typography } from 'antd';
import { useFormikContext } from 'formik';
const ObjectField: React.FC = () => {
const [form] = Form.useForm();
const formik = useFormikContext<any>();
const [FieldItems, setFieldItems] = useState<any>([])
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFieldItems((prevState:any) => ({
...prevState,
[name]: value
}));
formik.setFieldValue("info", FieldItems)
};
useEffect(() => {
form.setFieldsValue({
items: [{ list: [{ Attribute: '', Description: '' }] }]
});
}, []);
return (
<Form
labelCol={{ span: 6 }}
wrapperCol={{ span: 18 }}
form={form}
name="dynamic_form_complex"
style={{ width: '100%' }} // Set width to 100%
autoComplete="off"
>
<Form.List name="items">
{(fields, { add, remove }) => (
<div style={{ display: 'flex', rowGap: 16, flexDirection: 'column' }}>
{fields.map((field, index) => (
<div key={field.key}>
<Typography.Text strong style={{ marginBottom: 8 }}>
Information
</Typography.Text>
{/* Nested Form.List for sub-items */}
<Form.Item>
<Form.List name={[field.name, 'list']}>
{(subFields, subOpt) => (
<div style={{ display: 'flex', flexDirection: 'column', rowGap: 16 }}>
{subFields.map((subField) => (
<Space key={subField.key}>
<Form.Item noStyle name={[subField.name, 'Attribute']}>
<Input
placeholder="Attribute"
onChange={handleChange} // Assign onChange handler
name={`${subField.name}.Attribute`} // Ensure proper name for dynamic state update
/>
</Form.Item>
<Form.Item noStyle name={[subField.name, 'Description']}>
<Input
placeholder="Description"
onChange={handleChange} // Assign onChange handler
name={`${subField.name}.Description`} // Ensure proper name for dynamic state update
/>
</Form.Item>
<CloseOutlined
onClick={() => {
subOpt.remove(subField.name);
}}
/>
</Space>
))}
<Button type="dashed" onClick={() => subOpt.add()} block>
+ Add Another Item
</Button>
</div>
)}
</Form.List>
</Form.Item>
</div>
))}
</div>
)}
</Form.List>
</Form>
);
};
export default ObjectField;

View File

@ -0,0 +1,92 @@
import React, { useEffect, useState } from 'react'
import { getInitialValues, getValidationSchema, getDataToSend } from '../formUtil'
import { Tab, TabList, TabPanel as TabBody, Tabs } from 'react-tabs'
import 'react-tabs/style/react-tabs.css';
import { MdLanguage } from 'react-icons/md'
import { FaSadCry } from 'react-icons/fa'
import ViewPage from '../../../Layout/Dashboard/ViewPage';
import { Rate } from 'antd';
import BasicInfo from './BasicInfo';
import { usePageState } from '../../../lib/state mangment/LayoutPagestate';
import { useParams } from 'react-router-dom';
import LoadingPage from '../../../Layout/app/LoadingPage';
import { useTranslation } from 'react-i18next';
import { BsInfoCircle } from 'react-icons/bs';
import { useGetOneProduct, useUpdateProduct } from '../../../api/product';
import BasicInfo2 from './BasicInfo2';
import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess';
import VarianInfo from './VarianInfo';
const ViewProduct = () => {
const { setObjectToEdit, objectToEdit } = usePageState()
const {t} = useTranslation();
const { id } = useParams()
const { data } = useGetOneProduct({id:id})
const [BarStatus, setBarStatus] = useState({ value: 0, isLoading: false, isError: false, isSuccess: false })
const {mutate ,isSuccess} = useUpdateProduct()
const handleSubmit = (values:any)=>{
values['product_id'] = id
values['is_highlight'] =values['is_highlight'] == true ?1 :0
values['is_most_purchase'] =values['is_most_purchase'] == true ?1 :0
const formToSend = getDataToSend(values)
mutate(formToSend)
}
useNavigateOnSuccess(isSuccess , '/products')
useEffect(() => {
console.log(data);
setObjectToEdit(data);
}, [data]);
useEffect(()=>{
return ()=>{
setObjectToEdit(null)
}
},[])
const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit, BarStatus };
return (
<div className='ViewPage'>
{objectToEdit && data ?
<ViewPage {...ViewProps}>
<Tabs>
<TabList>
<Tab><div className='SignleDriverContainer'><span className='SignleDriverInfoIcon'><MdLanguage size={20} /></span> <h6 className='SingleDriverInfo'>{t("Base_info")}</h6></div></Tab>
<Tab><div className='SignleDriverContainer'><span className='SignleDriverInfoIcon'><BsInfoCircle size={20} /></span> <h6 className='SingleDriverInfo'>{t("VarianInfo")}</h6></div></Tab>
</TabList>
<TabBody >
<div className=" mt-4"><BasicInfo /></div>
</TabBody>
<TabBody >
<div className=" mt-4"><VarianInfo /></div>
</TabBody>
</Tabs>
</ViewPage>
: <LoadingPage />}
</div>
)
}
export default ViewProduct

View File

@ -0,0 +1,13 @@
import React, { useState } from 'react'
import ResposiveTabs from './taps/ResposiveTabs'
const VarianInfo = () => {
return (
<div className='VarianInfo'>
<ResposiveTabs/>
</div>
)
}
export default VarianInfo

View File

@ -0,0 +1,14 @@
import React from 'react'
const NewTabs = () => {
function handelClick(){
}
return (
<div onClick={handelClick}>
</div>
)
}
export default NewTabs

View File

@ -0,0 +1,152 @@
import React, { useState } from 'react';
import { Tabs, Space, Input } from 'antd';
import { CopyOutlined } from '@ant-design/icons';
import { useTranslation } from 'react-i18next';
const { TabPane } = Tabs;
interface TabItem {
label: string;
children: string;
key: string;
closable: boolean;
}
const initialItemShape: TabItem = {
label: 'variable 1',
children: 'Content of Tab 1',
key: '1',
closable: false,
};
const App: React.FC = () => {
const [activeKey, setActiveKey] = useState(initialItemShape.key);
const [items, setItems] = useState([initialItemShape]);
const [inputValues, setInputValues] = useState<{ [key: string]: string[] }>({});
const onChange = (newActiveKey: string) => {
setActiveKey(newActiveKey);
};
const add = () => {
const newActiveKey = `${items.length + 1}`;
const newItem: TabItem = {
...initialItemShape,
key: newActiveKey,
label: `variable ${newActiveKey}`,
};
const newPanes = [...items, newItem];
setItems(newPanes);
setActiveKey(newActiveKey);
};
const duplicate = (targetKey: string) => {
const targetItem = items.find((item) => item.key === targetKey);
if (targetItem) {
const newActiveKey = `${items.length + 1}`;
const newItem: TabItem = {
...targetItem,
key: newActiveKey,
label: `variable ${newActiveKey}`,
};
const newPanes = [...items, newItem];
setItems(newPanes);
setActiveKey(newActiveKey);
}
};
const remove = (targetKey: string) => {
let newActiveKey = activeKey;
let lastIndex = -1;
items.forEach((item, i) => {
if (item.key === targetKey) {
lastIndex = i - 1;
}
});
const newPanes = items.filter((item) => item.key !== targetKey);
if (newPanes.length && newActiveKey === targetKey) {
if (lastIndex >= 0) {
newActiveKey = newPanes[lastIndex].key;
} else {
newActiveKey = newPanes[0].key;
}
}
setItems(newPanes);
setActiveKey(newActiveKey);
};
const handleInputChange = (key: string, values: string[]) => {
setInputValues((prevInputValues) => ({
...prevInputValues,
[key]: values,
}));
console.log(inputValues);
};
const onEdit = (
targetKey: string | React.MouseEvent | React.KeyboardEvent,
action: 'add' | 'remove'
) => {
if (action === 'add') {
add();
} else {
remove(targetKey as string);
}
};
return (
<Tabs
type="editable-card"
onChange={onChange}
activeKey={activeKey}
onEdit={onEdit}
tabPosition="left"
>
{items.map((item) => (
<TabPane
key={item.key}
tab={
<Space>
{item.label}
<CopyOutlined onClick={() => duplicate(item.key)} />
</Space>
}
closable={item.closable}
>
<VariableTabs
value={inputValues[item.key] || ['', '', '', '']}
onChange={(values: string[]) => handleInputChange(item.key, values)}
/>
</TabPane>
))}
</Tabs>
);
};
interface VariableTabsProps {
value: string[];
onChange: (value: string[]) => void;
}
const VariableTabs: React.FC<VariableTabsProps> = ({ value, onChange }) => {
const handleInputChange = (index: number) => (e: React.ChangeEvent<HTMLInputElement>) => {
const newValues = [...value];
newValues[index] = e.target.value;
onChange(newValues);
};
const [t] = useTranslation()
return (
<div>
<label className="text">
{t(`Name`)}
</label>
<Input value={value[0]} onChange={handleInputChange(0)} />
<Input value={value[1]} onChange={handleInputChange(1)} />
<Input value={value[2]} onChange={handleInputChange(2)} />
<Input value={value[3]} onChange={handleInputChange(3)} />
</div>
);
};
export default App;

View File

@ -0,0 +1,26 @@
import React from 'react'
import { Col, Row } from 'reactstrap'
import KarimField from '../../../Components/Karimalden/KarimField'
import { useTranslation } from 'react-i18next';
const varianForm = () => {
const [t] = useTranslation();
return (
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
<Col>
<KarimField name="name" />
<KarimField name="description" />
<KarimField name="images" type='MaltyFile' />
{/* <KarimField name="quantity" type='number' /> */}
{/* <KarimField name="info" /> */}
</Col>
</Row>
)
}
export default varianForm

View File

@ -0,0 +1,61 @@
import * as Yup from "yup";
import { buildFormData } from "../../api/helper/buildFormData";
import { mapTranslatedProperties } from "../../utils/language/mapTranslatedProperties";
export const getInitialValues = (objectToEdit: any | null = null) => {
// console.log(objectToEdit);
return {
id: objectToEdit?.id ?? 0,
name:objectToEdit?.name ?? "",
name_ar: objectToEdit?.name?.ar ?? '',
name_en: objectToEdit?.name?.en ?? '',
name_de: objectToEdit?.name?.de ?? '',
description: objectToEdit?.description ?? '',
price:objectToEdit?.price??"",
main_photo:objectToEdit?.main_photo??"",
images:objectToEdit?.images??"",
category_id:objectToEdit?.category_id??1,
variable:[],
}
};
export const getValidationSchema = (editMode: boolean = false) => {
// validate input
return Yup.object().shape({
// name_ar: Yup.string().required('Required'),
// name_en: Yup.string().required('Required'),
// name_de: Yup.string().required('Required'),
// description_ar: Yup.string().required('Required'),
// description_en: Yup.string().required('Required'),
// description_de: Yup.string().required('Required'),
// price: Yup.number().required('Required'),
// // info: Yup.string().required('Required'),
// main_photo: Yup.string().required('Required'),
// // images: Yup.string().required('Required'),
// category_id: Yup.string().required('Required'),
// quantity: Yup.number().required('Required'),
});
};
export const getDataToSend = (values: any): FormData => {
const data = { ...values };
// console.log(data);
if(typeof data['product_main_image'] == 'string') delete data['product_main_image']
data['en_product_name'] = values['translated_fields']['1']['product_name']
data['ar_product_name'] =values['translated_fields']['2']['product_name']
data['ar_product_description'] =values['translated_fields']['2']['product_description']
data['en_product_description'] =values['translated_fields']['1']['product_description']
const formData = new FormData();
buildFormData(formData, data);
return formData;
};

View File

@ -0,0 +1,92 @@
import React, { useMemo } from "react";
import { useTranslation } from "react-i18next";
import Actions from "../../Components/Ui/tables/Actions";
import { HovarableImage } from "../../Components/Ui";
import { BaseURL } from "../../api/config";
import { ToggleStatus } from "../../Components/Ui/ToggleStatus";
import ColumnsImage from "../../Components/Columns/ColumnsImage";
import LoadingSpinner from "../../Components/Ui/LoadingSpinner";
import { Switch } from "antd";
import { mapTranslatedProperties } from "../../utils/language/mapTranslatedProperties";
// import { useDeleteProduct, useUpdateProductStatus } from "../../api/owner_products";
import { useNavigate } from "react-router-dom";
import { useDeleteProduct, useUpdateProductStatus } from "../../api/product";
const useTableColumns :any = () => {
const [t] = useTranslation();
const toggleMutation = useUpdateProductStatus();
const deleteMutation = useDeleteProduct();
const navigate = useNavigate()
const handleChange = (row:any)=> {
const status = row?.favorite ;
toggleMutation.mutate({id:row?.id,new_status:status})
}
return useMemo(
() => [
{
name: t("image"),
center: "true",
cell: (row: any) => {
return (
<ColumnsImage src={row?.main_photo} />
)
}
},
{
name: t("name"),
sortable: false,
center: true,
selector:(row:any) => row?.name,
},
{
name: t("price"),
sortable: false,
center: true,
selector:(row:any) => row?.price,
},
{
name: t("description"),
sortable: false,
center: true,
cell: (row:any) => (
row?.description
),
},
{
name: t("favorite"),
sortable: false,
center: true,
cell: (row:any) => (
<ToggleStatus handleSwitch={handleChange} object={row} toggleMutation={toggleMutation} />
),
},
{
name: "#",
sortable: false,
center: "true",
cell: (row:any) => (
<Actions
onEdit={()=> navigate('/products/'+row.id)}
objectToEdit={row}
showEdit={true}
showView={false}
onDelete={() => deleteMutation.mutate({ product_id: row.id })}
/>
),
},
],
[t]
);
};
export default useTableColumns;

View File

@ -0,0 +1,52 @@
import React from "react";
import useTableColumns from "./useTableColumns";
import DashBody from "../../Layout/Dashboard/DashBody";
import DashHeader from "../../Layout/Dashboard/DashHeader";
import { QueryStatusEnum } from "../../config/QueryStatus";
import LyTable from "../../Layout/Dashboard/LyTable";
import { useTranslation } from "react-i18next";
import { useGetOrder } from "../../api/order";
// import SearchField from "../../Components/Karimalden/View/SearchField";
const OrderPage = () => {
const [t] = useTranslation();
//filters
const [search, setSearchText] = React.useState("");
const filterIsApplied = search !== "";
//Table Content -- Data + Columns
const { data, isLoading , status } = useGetOrder({search});
console.log(data);
const totalRows = data?.pagination?.total || 0;
const columns = useTableColumns();
return (
<>
<DashBody status={status as QueryStatusEnum} >
<DashHeader title="orders" showAddButton={false}>
{/* <SearchField/> */}
</DashHeader>
<LyTable
data={data?.Orders}
total={totalRows}
column={columns}
is_pagination={true}
/>
</DashBody>
</>
);
};
export default OrderPage;

51
src/Pages/order/Select.js Normal file
View File

@ -0,0 +1,51 @@
import React, { useState } from 'react';
import {
Dropdown,
DropdownToggle,
DropdownMenu,
DropdownItem,
} from 'reactstrap';
import PropTypes from 'prop-types';
import {BsArrowDownShort} from 'react-icons/bs'
import { useTranslation } from 'utility/language';
function SelectType({ direction, title , setSelectType,setRefreshPage}) {
const t = useTranslation()
const [dropdownOpen, setDropdownOpen] = useState(false);
const toggle = () => setDropdownOpen((prevState) => !prevState);
return (
<div className="d-flex " style={{marginInline:10 }}>
<Dropdown isOpen={dropdownOpen} toggle={toggle} direction={direction} size={30}
>
<DropdownToggle outline style={{color:"black" , width:"100%" , border:"gray solid 2px" , borderRadius:"10px" , padding:"8px"}} nav caret >{t(localStorage.getItem('order_status') || 'all')} <BsArrowDownShort size={17} style={{height:20}} /></DropdownToggle>
<DropdownMenu >
<DropdownItem onClick={()=>{
setRefreshPage(v => !v)
setSelectType('status')
localStorage.setItem('order_status','')
}} style={{width:"100%", }}>{t('all')}</DropdownItem>
<DropdownItem onClick={()=>{
setRefreshPage(v => !v)
setSelectType('complete')
localStorage.setItem('order_status','complete')
}} style={{color:"black", width:"100%"}}>{t('complete')}</DropdownItem>
<DropdownItem onClick={()=>{
setRefreshPage(v => !v)
setSelectType('canceled')
localStorage.setItem('order_status','canceled')
}} style={{color:"black", width:"100%"}}>{t('canceled')}</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
);
}
SelectType.propTypes = {
direction: PropTypes.string,
};
export default SelectType;

View File

@ -0,0 +1,61 @@
import * as Yup from "yup";
import { buildFormData } from "api/helpers";
export const getInitialValues = (objectToEdit = null) => {
if (!objectToEdit) {
return {
driver_name:"",
driver_id_number: "",
driver_car_back_side:""
};
}
return {
driver_name:objectToEdit.driver_name,
driver_id_number: objectToEdit.driver_id_numbert,
customer_image: "",
driver_car_back_side:""
};
};
export const getValidationSchema = (editMode = false) => {
return Yup.object().shape({
customer_name:Yup.string().required("required"),
driver_id_number: Yup.number().required("required"),
...(!editMode && {
subcategory_image: Yup.mixed().required("required"),
}),
});
};
export const getDataToSend = (values) => {
const data = { ...values };
if (values.subcategory_image === "") {
delete data["subcategory_image"];
}
const formData = new FormData();
buildFormData(formData, data);
return formData;
};
export const selectFailGender = [
{value : "female" , label:"female"},
{value:"male" , label:"male"}
]
export const ChangePointShape = (array_of_points)=>{
let my_new_array = [];
for (let index = 0; index < array_of_points.length; index+=5) {
const my_latlong =(array_of_points[index]).split(',')
my_new_array.push({
lat:+ my_latlong[0],
lng:+my_latlong[1]
})
}
return my_new_array ;
}

View File

@ -0,0 +1,113 @@
import React, { useMemo } from "react";
// import { history } from "../../../history";
// import { GrView } from "react-icons/gr";
// import OrderStatus from "components/OrderStatus";
// import Actions from "components/table/TableActions";
import { useTranslation } from "react-i18next";
import { useDeleteOrder } from "../../api/order";
import { GrView } from "react-icons/gr";
import Actions from "../../Components/Ui/tables/Actions";
import { history } from "../../ProviderContainer";
import { useNavigate } from "react-router-dom";
import ColumnsImage from "../../Components/Columns/ColumnsImage";
const useTableColumns = () => {
const [t] = useTranslation();
const navigate = useNavigate()
const deleteMutation = useDeleteOrder()
let column = [
{
name: t("order_code"),
sortable: false,
center:true,
selector:(row:any) => row?.id,
},
{
name: t("image"),
sortable: false,
center:true,
selector:(row:any) => {
const {avatar} = row.user
return(
<ColumnsImage src={avatar} />
)
}
},
{
name: t("name"),
sortable: false,
center:true,
selector:(row:any) => row?.user?.name,
},
{
name: t("email"),
sortable: false,
center:true,
selector:(row:any) => row?.user?.email,
},
{
name: t("status"),
center: true,
cell:(row:any)=>{
return row?.order_status;
// return <OrderStatus order_status={row?.order_status}/>
}
},
{
name: t("payment_method"),
selector: "payment_method",
center: true,
cell:(row:any)=>{
return t(row?.payment_method)
}
},
{
name: t("price"),
center: true,
cell:(row:any)=>{
console.log(row);
return (row?.order_total)
}
},
{
name: "#",
center: true,
cell: (row:any) =>
<span style={{display:"flex" , alignItems:"center" , justifyContent:"space-around" , width:"100px" }} >
<Actions
showDelete={false}
objectToEdit={row}
onDelete={() => deleteMutation.mutate({order_id:row.id })}
showEdit={false}
onView={()=>navigate(`/order/${row?.id}` , {replace:true})}
/>
</span>
},
]
return column
};
export default useTableColumns;

View File

@ -0,0 +1,93 @@
import Reac from 'react'
import { useNavigate, useParams } from 'react-router-dom';
import { Button, Card, CardBody, CardFooter, CardHeader, CardTitle } from 'reactstrap';
import OrderForm from './OrderForm';
import DataTable from 'react-data-table-component';
import useTableColumns from './useTableColumns';
import { useTranslation } from 'react-i18next';
import { useGetOneOrder } from '../../../api/order';
import LoadingPage from '../../../Layout/app/LoadingPage';
import { history } from '../../../ProviderContainer';
import StatusOrderController from './StatusOrderController';
export default function Order() {
const { id } = useParams();
const [t] = useTranslation();
const navigate = useNavigate()
const { data, isLoading, notFound } = useGetOneOrder({id: id })
const order = data?.data || {};
const columns = useTableColumns();
const items = order?.products ;
console.log(items);
console.log(order);
if (isLoading) {
return (<LoadingPage />)
}
return (
<Card style={{paddingBottom:"2.5rem"}}>
<CardHeader style={{display:"flex" , justifyContent:"space-between" , margin:"20px"}}>
<CardTitle>
<p>{t("order_id")} : {order?.id}</p>
</CardTitle>
<span style={{display:"flex" , height:37, marginRight:7,justifyContent:"space-between" }}>
<Button
onClick={() => navigate(-1)}
color="danger"
>
{t("back")}
</Button>
</span>
</CardHeader>
{/* <div style={{padding:"1.5rem",display:"flex",alignItems:"center",justifyContent:"space-between"}}>
<div>
</div>
</div> */}
<CardBody style={{padding:" 1.5rem"}}>
<OrderForm order={order} />
<DataTable
columns={columns}
data={items}
progressPending={isLoading}
// noDataComponent={<h6 className="my-4">{("no_records")}</h6>}
// noHeader
/>
</CardBody>
</Card>
)
}

View File

@ -0,0 +1,39 @@
import React from 'react'
import { Row, Col } from 'reactstrap'
import classes from './OrderForm.module.scss';
import { useTranslation } from 'react-i18next';
import { ChangeformatDate } from '../../../utils/Date/ChangeFormat';
export default function OrderForm({ order }) {
const [t] = useTranslation();
return (
<>
<Row xs={1} sm={1} md={2} lg={3} xl={3}>
<Col className={classes.test} >
<p >{t("customer_name")}{" : "}{order.user?.name}</p>
<p >{t("customer_phone_number")}{" : "}{order.user?.phone}</p>
<p >{t("order_created_at")}{" : "}{ChangeformatDate(order.created_at)}</p>
</Col>
<Col className={classes.test} >
<p >{t("address")}{" : "}{order?.form?.address}</p>
<p >{t("country")}{" : "}{order?.form?.country}</p>
<p >{t("note")}{" : "}{order?.form?.note}</p>
</Col>
</Row>
<Row className={classes.Wrapper}>
<div className={classes.totalsForm}>
<h1 >{t("totals")}</h1>
<p >{t("sub_total")}{" : "}{order?.total}</p>
<p >{t("delivery_fee")}{" : "}{0}</p>
<p >{t("overall_total")}{" : "}{order?.total}</p>
</div>
</Row>
</>
)
}

View File

@ -0,0 +1,36 @@
.totalsForm {
width: 60%;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
padding: 1rem;
border-radius: 10px;
border: 3px solid #122236;
margin-bottom: 10px;
}
.Wrapper {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 1rem;
}
.Wrapper h1{
color: #122236;
}
.test {
padding: 1rem;
display: flex;
align-items: flex-start;
justify-content: center;
flex-direction: column;
}
p {
font-weight: 600;
}

View File

@ -0,0 +1,31 @@
import React from "react";
import { Badge } from "reactstrap";
import PropTypes from "prop-types";
import { useTranslation } from "react-i18next";
const OrderStatus = ({ order_status }) => {
const [t] = useTranslation();
const all={
pending:{color:"secondary"},
accepted:{color:"success"},
accept:{color:"success"},
delivering:{color:"primary"},
delivered:{color:"success"},
canceled:{color:"danger"}
}
return (
<Badge color={all[order_status].color}>
{t(order_status)}
</Badge>
);
};
OrderStatus.propTypes = {
order_status: PropTypes.string.isRequired,
};
export default OrderStatus;

View File

@ -0,0 +1,80 @@
import React from 'react'
import OrderStatus from './OrderStatus';
import { useTranslation } from 'react-i18next';
import { useAcceptOrder, useCancelOrder, useDeliverOrder, useDeliveredOrder } from '../../../api/order';
import { LoadingButton } from '../../../Components/Ui/LoadingButton';
export default function StatusOrderController({ order_status, order_id }) {
const acceptMutation = useAcceptOrder();
const cancelMutation = useCancelOrder();
const deliverMutation = useDeliverOrder();
const deliveredMutation = useDeliveredOrder();
const [t] = useTranslation();
const controll = {
pending: {
nextMutation: acceptMutation,
nextLabel: "accept",
nextColor: "primary",
prevMutation: cancelMutation,
prevLabel: "cancel",
prevColor: "danger",
},
accepted: {
nextMutation: deliverMutation,
nextLabel: "deliver",
nextColor: "primary",
prevMutation: cancelMutation,
prevLabel: 'cancel',
prevColor: "danger",
},
delivering: {
nextMutation: deliveredMutation,
nextLabel: "delivered",
nextColor: "primary",
prevMutation: cancelMutation,
prevLabel: 'cancel',
prevColor: "danger",
},
delivered: {
nextMutation: null,
nextLabel: null,
prevMutation: null,
prevLabel: null
},
canceled: {
nextMutation: null,
nextLabel: null,
prevMutation: null,
prevLabel: null
},
}
return (
<div >
<div className="d-flex align-items-center justify-content-start m-1" style={{gap:"10px"}}>
<p >{t("order_status")}{" : "}{<OrderStatus order_status={order_status} />}</p>
{
controll[order_status].nextMutation && controll[order_status].nextLabel && <LoadingButton
style={{ margin: "10px" }}
color={controll[order_status].nextColor}
isLoading={controll[order_status].nextMutation.isLoading}
onClick={() => controll[order_status].nextMutation.mutate({ order_id: order_id })}
>
{t(controll[order_status].nextLabel)}
</LoadingButton>
}
{
controll[order_status].prevMutation && controll[order_status].prevLabel && <LoadingButton
color={controll[order_status].prevColor}
isLoading={controll[order_status].prevMutation.isLoading}
onClick={() => controll[order_status].prevMutation.mutate({ order_id: order_id })}
>
{t(controll[order_status].prevLabel)}
</LoadingButton>
}
</div>
</div>
)
}

View File

@ -0,0 +1,20 @@
.print-source {
display: none;
}
body > .print-source {
display: block;
}
.inner{
display: flex;
align-items: center;
justify-content: space-between;
}
@media print {
.print-source {
display: block;
}
}

View File

@ -0,0 +1,57 @@
import React from 'react'
import { useTranslation } from 'react-i18next';
import ColumnsImage from '../../../Components/Columns/ColumnsImage';
import { mapTranslatedProperties } from '../../../utils/language/mapTranslatedProperties';
export default function useTableColumns() {
const [t] = useTranslation();
return [
{
name: t("id"),
sortable: true,
center: true,
cell:(row) => row.id
},
{
name: `${t('name')}`,
sortable: true,
center: true,
cell:(row) => row.name
},
{
name: `${t("image")}`,
sortable: false,
center: true,
cell: (row) => {
const imgSource = row?.main_photo
return (
<ColumnsImage
src={imgSource}
/>
);
},
},
{
name: t("quantity"),
sortable: true,
center: true,
cell:(row)=>row?.quantity
},
{
name: t("price"),
sortable: true,
center: true,
cell:(row)=>row?.price
},
]
}

View File

@ -1,18 +1,26 @@
import { ReactNode, lazy } from "react"; import { ReactNode, lazy } from "react";
// Icons Import // Icons Import
import { FaCartArrowDown, FaImages } from "react-icons/fa";
import { FaUser, FaHome, FaSadCry } from "react-icons/fa" import { FaUser, FaHome, FaSadCry } from "react-icons/fa"
import { BiSolidCategory } from "react-icons/bi";
import { BiSolidCoupon } from "react-icons/bi";
// Pages Import // Pages Import
import HomePage from "./Pages/Home/HomePage"; import AddCategoriesPage from "./Pages/Categories/View/AddPage";
import CategoriesPage from "./Pages/Categories/CategoriesPage"; import EditCategories from "./Pages/Categories/View/EditPage";
import EditCategories from "./Pages/Categories/EditCategories/Page"; import CategoriesPage from "./Pages/Categories/Page";
import EditCoupon from "./Pages/Coupon/EditCoupon/Page";
import CouponPage from "./Pages/Coupon/CouponPage"; import HomePage from "./Pages/Home/HomePage";
import ProductsPage from "./Pages/Products/ProductsPage";
import AddProductPage from "./Pages/Products/View/AddPage";
import EditProduct from "./Pages/Products/View/Page";
import Order from "./Pages/order/view-one/Order";
import OrderPage from "./Pages/order/OrderPage";
interface RoutesLinksType { interface RoutesLinksType {
@ -38,18 +46,31 @@ export const RoutesLinks: RoutesLinksType[] = [
{ {
name: "Categories", name: "Categories",
element: <CategoriesPage />, element: <CategoriesPage />,
icon: <FaHome />, icon: <BiSolidCategory />
,
href: "/categories", href: "/categories",
}, },
// {
// name: "Coupon",
// element: <CouponPage />,
// icon: <BiSolidCoupon /> ,
// href: "/Coupon",
// },
{ {
name: "Coupon", name: "products",
element: <CouponPage />, element: <ProductsPage />,
icon: <FaUser />, icon: <BiSolidCoupon /> ,
href: "/Coupon", href: "/products",
},
{
name: "Order",
element: <OrderPage />,
icon: <BiSolidCoupon /> ,
href: "/order",
}, },
/////////////// hidden route
{ {
href: "/categories/:id", href: "/categories/:id",
@ -57,8 +78,33 @@ export const RoutesLinks: RoutesLinksType[] = [
hidden:true hidden:true
}, },
{ {
href: "/coupon/:id", href: "/categories/add",
element: <EditCoupon />, element: <AddCategoriesPage />,
hidden:true hidden:true
} },
{
name: "add_products",
element: <AddProductPage />,
// icon: <BiSolidCoupon /> ,
href: "/products/add",
hidden:true
},
{
name: "edit_products",
element: <EditProduct />,
// icon: <BiSolidCoupon /> ,
href: "/products/:id",
hidden:true
},
{
name: "view_order",
element: <Order />,
href: "/order/:id",
hidden:true
},
] ]

View File

@ -1,5 +1,9 @@
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap');
html,body { html,body {
background: var(--bg2) background: var(--bg2);
font-family: 'Poppins', sans-serif;
} }

View File

@ -1,12 +1,12 @@
:root { :root {
--primary:#fcb04f ; --primary:#E57DB1 ;
--secondary : #304a7e; --secondary : #2D9CDB;
--text: #565656; --text: #565656;
--bg: #ffffff; --bg: #ffffff;
--bg2: #f8f8f8; --bg2: #f8f8f8;
--shadow: rgba(0, 0, 0, 0.15); --shadow: rgba(0, 0, 0, 0.15);
--gray : rgb(207, 210, 214); --gray : rgb(207, 210, 214);
--linear : linear-gradient(118deg, #fcb04f, #f78c3f) --linear : linear-gradient(118deg, #2D9CDB, #2D9CDB)
} }
:root:has(.dark) { :root:has(.dark) {

View File

@ -361,3 +361,29 @@ background: var(--bg);
background: var(--bg); background: var(--bg);
border-radius: 20px; border-radius: 20px;
} }
.SingleInfo{
svg{
color: green !important;
}
}
.ResposiveTabs{
padding-block: 20px;
min-height: 300px;
}
/* Ant tabs tab active */
.ant-tabs-nav-wrap .ant-tabs-nav-list .ant-tabs-tab-active{
display:inline-block;
transform:translatex(0px) translatey(0px) !important;
}
/* Ant tabs content holder */
.VarianInfo .ant-tabs-left .ant-tabs-content-holder{
transform:translatex(0px) translatey(0px);
}
.ant-tabs-nav-wrap .ant-tabs-nav-list .ant-tabs-tab{
align-self: center !important;
}

View File

@ -84,7 +84,7 @@ overflow-x: hidden;
background: var(--primary); background: var(--primary);
color: var(--bg); color: var(--bg);
border-radius: 5px; border-radius: 5px;
box-shadow: 2px 2px 7px 0 var(--primary); box-shadow: 2px 2px 7px 0 var(--shadow);
cursor: pointer; cursor: pointer;
transform: scale(1.01); transform: scale(1.01);
@include Shadow; @include Shadow;
@ -149,7 +149,7 @@ overflow-x: hidden;
background: var(--linear); background: var(--linear);
color: var(--bg); color: var(--bg);
border-radius: 5px; border-radius: 5px;
box-shadow: 2px 2px 7px 0 var(--primary); box-shadow: 2px 2px 7px 0 var(--shadow);
svg { svg {
background-color: transparent; background-color: transparent;

View File

@ -54,3 +54,27 @@
} }
.ImageWname{
display: flex;
align-items: center;
justify-content: flex-start;
}
/* Ant form item control */
#dynamic_form_complex div .ant-form-item-control{
transform:translatex(0px) translatey(0px);
min-width:93%;
}
/* Ant form item row */
#dynamic_form_complex div .ant-form-item-row{
transform:translatex(0px) translatey(0px);
overflow:hidden;
}
/* Ant space item */
#dynamic_form_complex div .ant-space-item{
min-width:47%;
}

View File

@ -17,7 +17,7 @@ const KEY = "CATEGORIES"
export const useGetCategories = (params?:any) => useGetQueryPagination(KEY, API.GET_ALL,params); export const useGetCategories = (params?:any) => useGetQueryPagination(KEY, API.GET_ALL,params);
export const useGetOneCategories = (params?:any) => useGetOneQuery(KEY, API.GET_ALL,params); export const useGetOneCategories = () => useGetOneQuery(KEY, API.GET_ALL);
export const useAddCategories = () => useAddMutation(KEY, API.ADD); export const useAddCategories = () => useAddMutation(KEY, API.ADD);
export const useUpdateCategories = () => useUpdateMutation(KEY, API.UPDATE); export const useUpdateCategories = () => useUpdateMutation(KEY, API.UPDATE);

25
src/api/Slider.ts Normal file
View File

@ -0,0 +1,25 @@
import useGetQueryPagination from "./helper/ueGetPagination";
import useAddMutation from "./helper/useAddMutation"
import useDeleteMutation from "./helper/useDeleteMutation"
import useGetOneQuery from "./helper/useGetOneQuery";
import useGetQuery from "./helper/useGetQuery"
import useUpdateMutation from "./helper/useUpdateMutation";
const API = {
ADD: `slider`,
GET_ALL: `slider`,
DELETE: `slider`,
UPDATE: `slider`,
};
const KEY = "SLIDER"
export const useGetSlider = (params?:any) => useGetQueryPagination(KEY, API.GET_ALL,params);
export const useGetOneSlider = (params?:any) => useGetOneQuery(KEY, API.GET_ALL,params);
export const useAddSlider = () => useAddMutation(KEY, API.ADD);
export const useUpdateSlider = () => useUpdateMutation(KEY, API.UPDATE);
export const useDeleteSlider = () =>useDeleteMutation(KEY, API.DELETE);

View File

@ -5,7 +5,7 @@ import useAddMutation from "./helper/useAddMutation";
const KEY = "AUTH" const KEY = "AUTH"
const API = { const API = {
LOGIN: `/api/admin/login`, LOGIN: `admin/login`,
LOGOUT: `/api/admin/logout`, LOGOUT: `/api/admin/logout`,
}; };
export const useLoginAdmin = ()=>useAddMutation(KEY , API.LOGIN) export const useLoginAdmin = ()=>useAddMutation(KEY , API.LOGIN)

View File

@ -1,12 +1,16 @@
import { useQuery } from 'react-query'; import { useQuery } from 'react-query';
import useAxios from './useAxios'; import useAxios from './useAxios';
import { useLocation } from 'react-router-dom'; import { useLocation, useNavigate } from 'react-router-dom';
import useAuthState from '../../lib/state mangment/AuthState';
export default function useGetQueryPagination(KEY:string , Api: string , params:any={},options:any={}) { export default function useGetQueryPagination(KEY:string , Api: string , params:any={},options:any={}) {
const axios = useAxios(); const axios = useAxios();
const location = useLocation(); const location = useLocation();
const pagination = location?.search || ''; const pagination = location?.search || '';
// console.log(params); // console.log(params);
const {logout} = useAuthState()
const language = localStorage.getItem("language") ?? "en"
const navigate = useNavigate()
return useQuery( return useQuery(
[KEY, pagination], async () => { [KEY, pagination], async () => {
@ -14,14 +18,19 @@ export default function useGetQueryPagination(KEY:string , Api: string , params
return response.data; return response.data;
}, },
{ {
...options onError: (error:any) => {
if(error.response.status == 401 || error.response.status == 403){
logout()
navigate("/auth")
}
},
refetchOnWindowFocus: false,
...options
} }
// {
// onError: (error) => {
// console.error('An error occurred:', error);
// },
// refetchOnWindowFocus: false,
// }
); );
} }

View File

@ -14,9 +14,7 @@ function useAxios() {
if(isAuthenticated){ if(isAuthenticated){
buildAxios.withHeaders({ Authorization: 'Bearer '+ buildAxios.withHeaders({ Authorization: 'Bearer '+ token })
"31|wDE4pbzJitVVrHhYlBnRIQjIJpkDfwOjqM8Yhbmdb0a4e257"
})
} }
return ( return (

View File

@ -1,16 +1,20 @@
import { useQuery } from 'react-query'; import { useQuery } from 'react-query';
import useAxios from './useAxios'; import useAxios from './useAxios';
import useAuthState from '../../lib/state mangment/AuthState'; import useAuthState from '../../lib/state mangment/AuthState';
import { useNavigate, useParams } from 'react-router-dom';
function useGetOneQuery(key: string, url: string , params:any={},options:any={}) { function useGetOneQuery(key: string, url: string , params:any={},options:any={}) {
const axios = useAxios(); const axios = useAxios();
const {logout} = useAuthState() const {logout} = useAuthState()
const language = localStorage.getItem("language") ?? "en" const language = localStorage.getItem("language") ?? "en"
const navigate = useNavigate()
const {id} = useParams()
return useQuery( return useQuery(
params ? [key, params] : key, [id, key],
async () => { async () => {
const response = await axios.get(url+"/"+ params?.id+`?lang=${language}`); const response = await axios.get(url+"/"+ id+`?lang=${language}`);
return response.data.data; return response.data;
}, },
@ -18,6 +22,7 @@ function useGetOneQuery(key: string, url: string , params:any={},options:any={})
onError: (error:any) => { onError: (error:any) => {
if(error.response.status == 401 || error.response.status == 403){ if(error.response.status == 401 || error.response.status == 403){
logout() logout()
navigate("/auth")
} }

View File

@ -1,10 +1,12 @@
import { useQuery } from 'react-query'; import { useQuery } from 'react-query';
import useAxios from './useAxios'; import useAxios from './useAxios';
import useAuthState from '../../lib/state mangment/AuthState'; import useAuthState from '../../lib/state mangment/AuthState';
import { useNavigate } from 'react-router-dom';
function useGetQuery(key: string, url: string , params:any={},options:any={}) { function useGetQuery(key: string, url: string , params:any={},options:any={}) {
const axios = useAxios(); const axios = useAxios();
const {logout} = useAuthState() const {logout} = useAuthState()
const navigate = useNavigate()
return useQuery( return useQuery(
params ? [key, params] : key, params ? [key, params] : key,
async () => { async () => {
@ -15,8 +17,13 @@ function useGetQuery(key: string, url: string , params:any={},options:any={}) {
{ {
onError: (error:any) => { onError: (error:any) => {
console.log('====================================');
console.log(error.response.status);
console.log('====================================');
if(error.response.status == 401 || error.response.status == 403){ if(error.response.status == 401 || error.response.status == 403){
logout() logout()
navigate("/auth")
} }

View File

@ -2,6 +2,7 @@ import { useQueryClient, useMutation, UseMutationResult } from "react-query";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import useAxios from "./useAxios"; import useAxios from "./useAxios";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useParams } from "react-router-dom";
type AxiosResponse = { type AxiosResponse = {
message: string; message: string;
@ -16,10 +17,11 @@ const useUpdateMutation = (
const axios = useAxios(); const axios = useAxios();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const [t] = useTranslation(); const [t] = useTranslation();
const {id}= useParams()
return useMutation<AxiosResponse, unknown, unknown>( return useMutation<AxiosResponse, unknown, unknown>(
async (dataToSend) => { async (dataToSend) => {
const { data } = await axios.post(url, dataToSend); const { data } = await axios.post(url+"/"+id, dataToSend);
return data; return data;
}, },
{ {

32
src/api/order.ts Normal file
View File

@ -0,0 +1,32 @@
import useGetQueryPagination from "./helper/ueGetPagination";
import useAddMutation from "./helper/useAddMutation"
import useDeleteMutation from "./helper/useDeleteMutation"
import useGetOneQuery from "./helper/useGetOneQuery";
import useGetQuery from "./helper/useGetQuery"
import useUpdateMutation from "./helper/useUpdateMutation";
const API = {
ADD: `order`,
GET_ALL: `order`,
DELETE: `order`,
UPDATE: `order`,
};
const KEY = "Order"
export const useGetOrder = (params?:any) => useGetQueryPagination(KEY, API.GET_ALL,params);
export const useGetOneOrder = (params?:any) => useGetOneQuery(KEY, API.GET_ALL,params);
export const useAddOrder = () => useAddMutation(KEY, API.ADD);
export const useUpdateOrder = () => useUpdateMutation(KEY, API.UPDATE);
export const useDeleteOrder = () =>useDeleteMutation(KEY, API.DELETE);
export const useAcceptOrder = ()=> useAddMutation(KEY, API.ADD)
export const useCancelOrder = ()=> useAddMutation(KEY, API.ADD)
export const useDeliverOrder = ()=> useAddMutation(KEY, API.ADD)
export const useDeliveredOrder = ()=> useAddMutation(KEY, API.ADD)

26
src/api/product.ts Normal file
View File

@ -0,0 +1,26 @@
import useGetQueryPagination from "./helper/ueGetPagination";
import useAddMutation from "./helper/useAddMutation"
import useDeleteMutation from "./helper/useDeleteMutation"
import useGetOneQuery from "./helper/useGetOneQuery";
import useGetQuery from "./helper/useGetQuery"
import useUpdateMutation from "./helper/useUpdateMutation";
const API = {
ADD: `product`,
GET_ALL: `product`,
DELETE: `product`,
UPDATE: `product`,
};
const KEY = "Product"
export const useGetProduct = (params?:any) => useGetQueryPagination(KEY, API.GET_ALL,params);
export const useGetOneProduct = (params?:any) => useGetOneQuery(KEY, API.GET_ALL,params);
export const useAddProduct = () => useAddMutation(KEY, API.ADD);
export const useUpdateProduct = () => useUpdateMutation(KEY, API.UPDATE);
export const useUpdateProductStatus = () => useUpdateMutation(KEY, API.UPDATE);
export const useDeleteProduct = () =>useDeleteMutation(KEY, API.DELETE);

25
src/api/users.ts Normal file
View File

@ -0,0 +1,25 @@
import useGetQueryPagination from "./helper/ueGetPagination";
import useAddMutation from "./helper/useAddMutation"
import useDeleteMutation from "./helper/useDeleteMutation"
import useGetOneQuery from "./helper/useGetOneQuery";
import useGetQuery from "./helper/useGetQuery"
import useUpdateMutation from "./helper/useUpdateMutation";
const API = {
ADD: `user`,
GET_ALL: `user`,
DELETE: `user`,
UPDATE: `user`,
};
const KEY = "User"
export const useGetUsers = (params?:any) => useGetQueryPagination(KEY, API.GET_ALL,params);
export const useGetOneUser = (params?:any) => useGetOneQuery(KEY, API.GET_ALL,params);
export const useAddUsers = () => useAddMutation(KEY, API.ADD);
export const useUpdateUsers = () => useUpdateMutation(KEY, API.UPDATE);
export const useDeleteUsers = () =>useDeleteMutation(KEY, API.DELETE);

View File

@ -1,15 +1,14 @@
import create from 'zustand'; import create from 'zustand';
import { TOKEN_KEY, TOKEN_KEY_SOCKET, USER_KEY } from '../../config/AppKey'; import { TOKEN_KEY, TOKEN_KEY_SOCKET, USER_KEY } from '../../config/AppKey';
import { IUser } from '../../types/User';
interface LoginResponse { interface LoginResponse {
token:string , token:string ,
"user": IUser, "admin": any,
token_node:string token_node:string
} }
interface AuthStore { interface AuthStore {
user: IUser | null |undefined; user: any | null |undefined;
token:string |null | undefined; token:string |null | undefined;
isAuthenticated: boolean; isAuthenticated: boolean;
login: (userData: LoginResponse) => Promise<void>; login: (userData: LoginResponse) => Promise<void>;
@ -25,16 +24,15 @@ const useAuthState = create<AuthStore>((set) => {
return { return {
user: initialUser, user: initialUser,
isAuthenticated: true, isAuthenticated: !!storedToken,
token:storedToken, token:storedToken,
login: async (userData) => { login: async (userData) => {
// console.log(userData); console.log(userData);
localStorage.setItem(TOKEN_KEY , userData.token) localStorage.setItem(TOKEN_KEY , userData.token)
localStorage.setItem(TOKEN_KEY_SOCKET , userData.token_node) localStorage.setItem(USER_KEY , JSON.stringify(userData.admin))
localStorage.setItem(USER_KEY , JSON.stringify(userData.user))
set((state)=>({user:userData.user , isAuthenticated:true , token:userData.token})) set((state)=>({user:userData.admin , isAuthenticated:true , token:userData.token}))
}, },
logout: async () => { logout: async () => {

View File

@ -0,0 +1,5 @@
export function ChangeformatDate(dateString:string) {
const dateObject = new Date(dateString);
const formattedDate = `${dateObject.toLocaleDateString()} ${dateObject.toLocaleTimeString()}`;
return formattedDate;
}