Done
This commit is contained in:
parent
086116119c
commit
82c7c0d09b
BIN
public/Logo.png
Normal file
BIN
public/Logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
|
|
@ -8,7 +8,7 @@
|
|||
content="Web site created using create-react-app"
|
||||
/>
|
||||
|
||||
<title>Etaxi - App</title>
|
||||
<title>Hijab - App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ const App = () => {
|
|||
{/* 404 Page */}
|
||||
<Route path="*" element={<Suspense fallback={<Loading />}> <Page404 /></Suspense>} />
|
||||
{/* Login Page */}
|
||||
{/* <Route path="/auth" element={<Suspense fallback={<Loading />}> <Auth /></Suspense>} /> */}
|
||||
<Route path="/auth" element={<Suspense fallback={<Loading />}> <Auth /></Suspense>} />
|
||||
|
||||
{/* route not in navigation */}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import React from "react";
|
|||
import "./KarimField.scss";
|
||||
import { Date, Time, File, DataRange, SelectField, Default, CheckboxField } from './View';
|
||||
import { KarimFieldProps } from "./types";
|
||||
import MaltyFile from "./View/MaltyFile";
|
||||
|
||||
const KarimField: React.FC<KarimFieldProps> = ({type = "text", ...otherProps}) => {
|
||||
switch (type) {
|
||||
|
|
@ -15,6 +16,8 @@ const KarimField: React.FC<KarimFieldProps> = ({type = "text", ...otherProps}) =
|
|||
return <Time {...otherProps} />;
|
||||
case "File":
|
||||
return <File {...otherProps} />;
|
||||
case "MaltyFile":
|
||||
return <MaltyFile {...otherProps} />;
|
||||
case "Checkbox":
|
||||
return <CheckboxField {...otherProps} />;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { Form, Input } from 'antd'
|
|||
import React from 'react'
|
||||
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);
|
||||
|
||||
|
||||
|
|
@ -18,11 +18,12 @@ const Default = ({ name, label, placeholder, isDisabled, onChange, props }: any)
|
|||
>
|
||||
<Field
|
||||
as={Input}
|
||||
type="text"
|
||||
type={type}
|
||||
placeholder={t(`${placeholder ?placeholder : name}`)}
|
||||
name={name}
|
||||
disabled={isDisabled}
|
||||
size="large"
|
||||
|
||||
// onChange={onChange ? onChange : handleChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
|
|
|||
52
src/Components/Karimalden/View/MaltyFile.tsx
Normal file
52
src/Components/Karimalden/View/MaltyFile.tsx
Normal 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;
|
||||
|
|
@ -77,7 +77,7 @@
|
|||
|
||||
export interface KarimFieldPropsFile {
|
||||
name: string;
|
||||
type: "File";
|
||||
type: "File" | "MaltyFile";
|
||||
placeholder?: string;
|
||||
label?: string;
|
||||
className?: string;
|
||||
|
|
|
|||
|
|
@ -7,15 +7,15 @@ import { useTranslation } from "react-i18next";
|
|||
interface ToggleStatusProps {
|
||||
|
||||
}
|
||||
export const ToggleStatus = ({ object, toggleMutation, ...props }:any) => {
|
||||
export const ToggleStatus = ({ object, handleSwitch, toggleMutation, ...props }:any) => {
|
||||
const [t] = useTranslation();
|
||||
|
||||
const handleSwitch = () => {
|
||||
toggleMutation.mutate({
|
||||
id: object.id,
|
||||
new_status: !object.is_active,
|
||||
});
|
||||
};
|
||||
// const handleSwitch = () => {
|
||||
// toggleMutation.mutate({
|
||||
// id: object.id,
|
||||
// new_status: !object.is_active,
|
||||
// });
|
||||
// };
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -2,13 +2,15 @@ import React, { useEffect } from 'react'
|
|||
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()
|
||||
useEffect(()=>{
|
||||
|
||||
if(isSuccess){
|
||||
callbackAfterSuccess()
|
||||
if (typeof callbackAfterSuccess === 'function') {
|
||||
callbackAfterSuccess()
|
||||
}
|
||||
navigate(to_path , {replace:true})
|
||||
}
|
||||
},[isSuccess])
|
||||
|
|
|
|||
33
src/Layout/Dashboard/AddButton/AddButton.tsx
Normal file
33
src/Layout/Dashboard/AddButton/AddButton.tsx
Normal 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
|
||||
77
src/Layout/Dashboard/ViewPage.tsx
Normal file
77
src/Layout/Dashboard/ViewPage.tsx
Normal 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;
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
import React from 'react'
|
||||
import LoadingSpinner from '../../Components/Ui/LoadingSpinner'
|
||||
import { Spin } from 'antd'
|
||||
|
||||
function LoadingPage() {
|
||||
return (
|
||||
<div style={{height:"80vh", width:"100%" , display:"flex" , justifyContent:"center" , alignItems:"center"}}>
|
||||
<LoadingSpinner/>
|
||||
<Spin/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { FaAngleDown, FaAngleRight } from 'react-icons/fa';
|
||||
import { GiHamburgerMenu } from 'react-icons/gi';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
|
|
@ -7,11 +7,24 @@ import { useTranslation } from 'react-i18next';
|
|||
import KarimLogo from './KarimLogo';
|
||||
import { useWindowSize } from '../../Hooks/useWindowSize';
|
||||
import Etaxi from './Etaxi';
|
||||
import { usePageState } from '../../lib/state mangment/LayoutPagestate';
|
||||
|
||||
interface SidebarProps {}
|
||||
|
||||
const Sidebar: React.FC<SidebarProps> = () => {
|
||||
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 [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='SideBar_Top'>
|
||||
<div onClick={handleImg}>
|
||||
{/* <img src="../Layout/logorayan.png" width={isOpenSide ? 70 : 150} alt="" /> */}
|
||||
<Etaxi/>
|
||||
<img src="../Logo.png" width={isOpenSide ? 70 : 150} alt="" />
|
||||
{/* <Etaxi/> */}
|
||||
</div>
|
||||
<div className='HamburgerMenu' onClick={handleHamburgerMenu}>
|
||||
<GiHamburgerMenu />
|
||||
|
|
|
|||
|
|
@ -2,23 +2,23 @@ import React from 'react'
|
|||
import { Formik, Form, Field } from 'formik';
|
||||
import Translate from '../../Components/Utils/Translate';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { TOKEN_KEY } from '../../config/AppKey';
|
||||
import { useLoginAdmin } from '../../api/auth';
|
||||
|
||||
import * as Yup from "yup";
|
||||
import { getInitialValues, getValidationSchema } from './formUtil';
|
||||
import KarimField from '../../Components/Karimalden/KarimField';
|
||||
import { LoadingButton } from '../../Components/Ui/LoadingButton';
|
||||
import useNavigateOnSuccess from '../../Hooks/useNavigateOnSuccess';
|
||||
import useAuthState from '../../lib/state mangment/AuthState';
|
||||
import KarimField from '../../Components/Karimalden/KarimField';
|
||||
|
||||
const LoginForm = () => {
|
||||
const [t] = useTranslation();
|
||||
|
||||
const {mutate , isLoading , isSuccess, data} = useLoginAdmin()
|
||||
const {login} = useAuthState()
|
||||
const OnSuccess = ()=>{
|
||||
|
||||
}
|
||||
useNavigateOnSuccess(isSuccess , '/' , ()=>login(data?.data as any ))
|
||||
|
||||
const handelSubmit = (values:any)=>{
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const Auth = () => {
|
|||
<div className='Auth' style={{ background: `url(${LoginBg})` }}>
|
||||
<div className='In_Auth'>
|
||||
<div className="Left_Col">
|
||||
<img className='Logo' src="/Layout/etaxlogo.svg" alt="Logo" />
|
||||
<img className='Logo' src="../Logo.png" alt="Logo" />
|
||||
</div>
|
||||
<div className=" Right_Col ">
|
||||
<LoginForm />
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
@ -4,37 +4,39 @@ import DashBody from '../../Layout/Dashboard/DashBody'
|
|||
import DashHeader from '../../Layout/Dashboard/DashHeader'
|
||||
import LyTable from '../../Layout/Dashboard/LyTable'
|
||||
import useTableColumns from './useTableColumns'
|
||||
import { useGetCategories} from '../../api/Categories'
|
||||
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import AddCategoriesModal from './AddCategoriesModal'
|
||||
import { QueryStatusEnum } from '../../config/QueryStatus'
|
||||
// import * as XLSX from 'ts-xlsx';
|
||||
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'
|
||||
|
||||
function CategoriesPage() {
|
||||
function Page() {
|
||||
|
||||
const column =useTableColumns()
|
||||
const {data ,status } = useGetCategories()
|
||||
const {t} = useTranslation();
|
||||
|
||||
console.log(data);
|
||||
const [t] = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
|
||||
return (
|
||||
// Pass Status to Layout
|
||||
<DashBody status={status as QueryStatusEnum} >
|
||||
<DashHeader title={'Categories'}>
|
||||
<DashHeader showAddButton={false} title={'categories'}>
|
||||
<AddButton onClick={()=>navigate('/categories/add')}></AddButton>
|
||||
</DashHeader>
|
||||
|
||||
<LyTable
|
||||
<LyTable
|
||||
data={data?.categories}
|
||||
isLoading={false}
|
||||
columns={column}
|
||||
// is_pagination={true}
|
||||
// total={data?.meta?.total}
|
||||
/>
|
||||
|
||||
|
||||
/>
|
||||
<AddCategoriesModal />
|
||||
</DashBody>
|
||||
)
|
||||
}
|
||||
|
||||
export default CategoriesPage
|
||||
export default Page
|
||||
|
||||
|
|
@ -1,17 +1,15 @@
|
|||
|
||||
import React from 'react'
|
||||
import { Col, Row } from 'reactstrap';
|
||||
import KarimField from '../../Components/Karimalden/KarimField';
|
||||
import { FakeSelectData } from '../../Layout/app/Const';
|
||||
import KarimField from '../../../Components/Karimalden/KarimField';
|
||||
import { useFormikContext } from 'formik';
|
||||
|
||||
import { DatePicker } from 'antd';
|
||||
import { useGetCategories } from '../../api/Categories';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function AddForm() {
|
||||
function Form() {
|
||||
const formik = useFormikContext<any>();
|
||||
const {data } = useGetCategories()
|
||||
|
||||
const [t] = useTranslation();
|
||||
|
||||
return (
|
||||
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
|
||||
|
|
@ -28,10 +26,11 @@ function AddForm() {
|
|||
</Col>
|
||||
|
||||
|
||||
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
export default AddForm
|
||||
export default Form
|
||||
|
||||
|
||||
62
src/Pages/Categories/View/AddPage.tsx
Normal file
62
src/Pages/Categories/View/AddPage.tsx
Normal 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
|
||||
32
src/Pages/Categories/View/EditForm.tsx
Normal file
32
src/Pages/Categories/View/EditForm.tsx
Normal 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
|
||||
|
||||
|
||||
86
src/Pages/Categories/View/EditPage.tsx
Normal file
86
src/Pages/Categories/View/EditPage.tsx
Normal 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
|
||||
|
|
@ -20,9 +20,10 @@ interface ValidateSchema extends formUtilCommon{
|
|||
|
||||
}
|
||||
export const getInitialValues = (objectToEdit: any | null = null): any => {
|
||||
console.log(objectToEdit,"objectToEdit");
|
||||
return {
|
||||
id: objectToEdit?.id ?? 0,
|
||||
name: "",
|
||||
name:objectToEdit?.name ?? "",
|
||||
name_ar: objectToEdit?.name?.ar ?? '',
|
||||
name_en: objectToEdit?.name?.en ?? '',
|
||||
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> => {
|
||||
// Validate input
|
||||
return Yup.object().shape({
|
||||
name : Yup.string().required('Required'),
|
||||
name_ar: Yup.string().required('Required'),
|
||||
name_en: 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 => {
|
||||
const data = { ...values };
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
55
src/Pages/Coupon/FormProducts.tsx
Normal file
55
src/Pages/Coupon/FormProducts.tsx
Normal 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
|
||||
|
||||
|
||||
41
src/Pages/Coupon/ProductsPage.tsx
Normal file
41
src/Pages/Coupon/ProductsPage.tsx
Normal 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
|
||||
|
||||
72
src/Pages/Coupon/View/AddPage.tsx
Normal file
72
src/Pages/Coupon/View/AddPage.tsx
Normal 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
|
||||
47
src/Pages/Coupon/View/BasicInfo.tsx
Normal file
47
src/Pages/Coupon/View/BasicInfo.tsx
Normal 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
|
||||
87
src/Pages/Coupon/View/BasicInfo2.tsx
Normal file
87
src/Pages/Coupon/View/BasicInfo2.tsx
Normal 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
|
||||
91
src/Pages/Coupon/View/Page.tsx
Normal file
91
src/Pages/Coupon/View/Page.tsx
Normal 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
|
||||
|
|
@ -1,65 +1,70 @@
|
|||
|
||||
import * as Yup from "yup";
|
||||
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 {
|
||||
id: objectToEdit?.id ?? 0,
|
||||
name: "",
|
||||
name_ar: objectToEdit?.name?.ar ?? '',
|
||||
name_en: objectToEdit?.name?.en ?? '',
|
||||
name_de: objectToEdit?.name?.de ?? '',
|
||||
parent_id: objectToEdit?.parent_id ?? 1,
|
||||
photo: objectToEdit?.photo ?? '',
|
||||
};
|
||||
product_price:objectToEdit?.product_price??0,
|
||||
product_quantity:objectToEdit?.product_quantity??0,
|
||||
product_main_image:objectToEdit?.product_main_image??'',
|
||||
translated_fields: {
|
||||
1: {
|
||||
product_name: mapTranslatedProperties(objectToEdit?.product_translations ,'name', 1) ??'',
|
||||
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): Yup.Schema<any> => {
|
||||
// Validate input
|
||||
export const getValidationSchema = (editMode: boolean = false) => {
|
||||
// validate input
|
||||
return Yup.object().shape({
|
||||
name : Yup.string().required('Required'),
|
||||
name_ar: Yup.string().required('Required'),
|
||||
name_en: Yup.string().required('Required'),
|
||||
name_de: Yup.string().required('Required'),
|
||||
parent_id: Yup.string().required('Required'),
|
||||
photo: Yup.string().required('Required'),
|
||||
translated_fields : Yup.object().shape({
|
||||
1: Yup.object().shape({
|
||||
product_name :Yup.string().required("required"),
|
||||
product_description :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 => {
|
||||
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;
|
||||
};
|
||||
|
||||
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
|
||||
}
|
||||
|
|
@ -2,81 +2,87 @@
|
|||
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 { 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 { useDeleteProduct, useUpdateProductStatus } from "../../api/product";
|
||||
|
||||
function fnDelete(props :any ){}
|
||||
|
||||
const useTableColumns :any = () => {
|
||||
const [t] = useTranslation();
|
||||
const deleteMutation = useDeleteCoupon()
|
||||
const navigate = useNavigate()
|
||||
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",
|
||||
cell: (row:any) => row?.name
|
||||
center: true,
|
||||
selector:(row:any) => row?.name,
|
||||
|
||||
},
|
||||
{
|
||||
name: t("image"),
|
||||
name: t("price"),
|
||||
sortable: false,
|
||||
center: "true",
|
||||
cell: (row:any) => <ColumnsImage src={row?.photo} />
|
||||
center: true,
|
||||
selector:(row:any) => row?.price,
|
||||
|
||||
},
|
||||
{
|
||||
name: t("parent_id"),
|
||||
name: t("description"),
|
||||
sortable: false,
|
||||
center: "true",
|
||||
cell: (row:any) => row?.parent_id
|
||||
center: true,
|
||||
cell: (row:any) => (
|
||||
row?.description
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
name: t("product_count"),
|
||||
name: t("favorite"),
|
||||
sortable: false,
|
||||
center: "true",
|
||||
cell: (row:any) => row?.product_count
|
||||
center: true,
|
||||
cell: (row:any) => (
|
||||
<ToggleStatus handleSwitch={handleChange} object={row} toggleMutation={toggleMutation} />
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "#",
|
||||
sortable: false,
|
||||
center: true,
|
||||
center: "true",
|
||||
cell: (row:any) => (
|
||||
<Actions
|
||||
objectToEdit={row}
|
||||
showEdit
|
||||
onEdit={()=> navigate(`/Coupon/${row.id}`) }
|
||||
showView={false}
|
||||
onDelete={() => deleteMutation.mutate({ id: row.id })}
|
||||
/>
|
||||
<Actions
|
||||
onEdit={()=> navigate('/products/'+row.id)}
|
||||
objectToEdit={row}
|
||||
showEdit={true}
|
||||
showView={false}
|
||||
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]
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ import { FakeSelectData } from '../../Layout/app/Const';
|
|||
import { useFormikContext } from 'formik';
|
||||
|
||||
import { DatePicker } from 'antd';
|
||||
import { useGetCoupon } from '../../api/Coupon';
|
||||
import { useGetSlider } from '../../api/Slider';
|
||||
|
||||
function AddForm() {
|
||||
const formik = useFormikContext<any>();
|
||||
const {data } = useGetCoupon()
|
||||
const {data } = useGetSlider()
|
||||
|
||||
|
||||
return (
|
||||
|
|
@ -24,7 +24,7 @@ function CouponPage() {
|
|||
</DashHeader>
|
||||
|
||||
<LyTable
|
||||
data={data?.Coupon}
|
||||
data={data?.coupons}
|
||||
isLoading={false}
|
||||
columns={column}
|
||||
// is_pagination={true}
|
||||
|
|
@ -27,7 +27,7 @@ const EditPage = () => {
|
|||
<KarimSpinner loading={isLoading}>
|
||||
<FormPage
|
||||
handleSubmit={(values: any) => handleSubmit(values)}
|
||||
initialValues={getInitialValues(data)}
|
||||
initialValues={getInitialValues(data?.coupons)}
|
||||
validationSchema={getValidationSchema}
|
||||
title='Edit Coupon'
|
||||
>
|
||||
76
src/Pages/Coupon2/formUtil.ts
Normal file
76
src/Pages/Coupon2/formUtil.ts
Normal 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
|
||||
}
|
||||
104
src/Pages/Coupon2/useTableColumns.tsx
Normal file
104
src/Pages/Coupon2/useTableColumns.tsx
Normal 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;
|
||||
|
||||
40
src/Pages/Products/ProductsPage.tsx
Normal file
40
src/Pages/Products/ProductsPage.tsx
Normal 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
|
||||
|
||||
91
src/Pages/Products/View/AddPage.tsx
Normal file
91
src/Pages/Products/View/AddPage.tsx
Normal 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
|
||||
42
src/Pages/Products/View/BasicInfo.tsx
Normal file
42
src/Pages/Products/View/BasicInfo.tsx
Normal 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
|
||||
32
src/Pages/Products/View/BasicInfo2.tsx
Normal file
32
src/Pages/Products/View/BasicInfo2.tsx
Normal 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
|
||||
92
src/Pages/Products/View/ObjectField.tsx
Normal file
92
src/Pages/Products/View/ObjectField.tsx
Normal 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;
|
||||
92
src/Pages/Products/View/Page.tsx
Normal file
92
src/Pages/Products/View/Page.tsx
Normal 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
|
||||
13
src/Pages/Products/View/VarianInfo.tsx
Normal file
13
src/Pages/Products/View/VarianInfo.tsx
Normal 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
|
||||
14
src/Pages/Products/View/taps/NewTabs.tsx
Normal file
14
src/Pages/Products/View/taps/NewTabs.tsx
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import React from 'react'
|
||||
|
||||
const NewTabs = () => {
|
||||
function handelClick(){
|
||||
|
||||
}
|
||||
return (
|
||||
<div onClick={handelClick}>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default NewTabs
|
||||
152
src/Pages/Products/View/taps/ResposiveTabs.tsx
Normal file
152
src/Pages/Products/View/taps/ResposiveTabs.tsx
Normal 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;
|
||||
26
src/Pages/Products/View/varianForm.tsx
Normal file
26
src/Pages/Products/View/varianForm.tsx
Normal 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
|
||||
61
src/Pages/Products/formUtil.ts
Normal file
61
src/Pages/Products/formUtil.ts
Normal 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;
|
||||
};
|
||||
|
||||
92
src/Pages/Products/useTableColumns.tsx
Normal file
92
src/Pages/Products/useTableColumns.tsx
Normal 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;
|
||||
|
||||
52
src/Pages/order/OrderPage.tsx
Normal file
52
src/Pages/order/OrderPage.tsx
Normal 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
51
src/Pages/order/Select.js
Normal 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;
|
||||
61
src/Pages/order/form/formUtils.js
Normal file
61
src/Pages/order/form/formUtils.js
Normal 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 ;
|
||||
}
|
||||
113
src/Pages/order/useTableColumns.tsx
Normal file
113
src/Pages/order/useTableColumns.tsx
Normal 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;
|
||||
93
src/Pages/order/view-one/Order.js
Normal file
93
src/Pages/order/view-one/Order.js
Normal 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>
|
||||
|
||||
|
||||
)
|
||||
}
|
||||
39
src/Pages/order/view-one/OrderForm.js
Normal file
39
src/Pages/order/view-one/OrderForm.js
Normal 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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
36
src/Pages/order/view-one/OrderForm.module.scss
Normal file
36
src/Pages/order/view-one/OrderForm.module.scss
Normal 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;
|
||||
}
|
||||
31
src/Pages/order/view-one/OrderStatus.js
Normal file
31
src/Pages/order/view-one/OrderStatus.js
Normal 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;
|
||||
80
src/Pages/order/view-one/StatusOrderController.js
Normal file
80
src/Pages/order/view-one/StatusOrderController.js
Normal 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>
|
||||
)
|
||||
}
|
||||
20
src/Pages/order/view-one/index.css
Normal file
20
src/Pages/order/view-one/index.css
Normal 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;
|
||||
|
||||
}
|
||||
}
|
||||
57
src/Pages/order/view-one/useTableColumns.js
Normal file
57
src/Pages/order/view-one/useTableColumns.js
Normal 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
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
]
|
||||
}
|
||||
|
|
@ -1,18 +1,26 @@
|
|||
import { ReactNode, lazy } from "react";
|
||||
|
||||
// Icons Import
|
||||
import { FaCartArrowDown, FaImages } 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
|
||||
|
||||
import HomePage from "./Pages/Home/HomePage";
|
||||
import CategoriesPage from "./Pages/Categories/CategoriesPage";
|
||||
import EditCategories from "./Pages/Categories/EditCategories/Page";
|
||||
import EditCoupon from "./Pages/Coupon/EditCoupon/Page";
|
||||
import AddCategoriesPage from "./Pages/Categories/View/AddPage";
|
||||
import EditCategories from "./Pages/Categories/View/EditPage";
|
||||
import CategoriesPage from "./Pages/Categories/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 {
|
||||
|
|
@ -38,18 +46,31 @@ export const RoutesLinks: RoutesLinksType[] = [
|
|||
{
|
||||
name: "Categories",
|
||||
element: <CategoriesPage />,
|
||||
icon: <FaHome />,
|
||||
icon: <BiSolidCategory />
|
||||
,
|
||||
href: "/categories",
|
||||
},
|
||||
|
||||
// {
|
||||
// name: "Coupon",
|
||||
// element: <CouponPage />,
|
||||
// icon: <BiSolidCoupon /> ,
|
||||
// href: "/Coupon",
|
||||
// },
|
||||
{
|
||||
name: "Coupon",
|
||||
element: <CouponPage />,
|
||||
icon: <FaUser />,
|
||||
href: "/Coupon",
|
||||
name: "products",
|
||||
element: <ProductsPage />,
|
||||
icon: <BiSolidCoupon /> ,
|
||||
href: "/products",
|
||||
},
|
||||
|
||||
{
|
||||
name: "Order",
|
||||
element: <OrderPage />,
|
||||
icon: <BiSolidCoupon /> ,
|
||||
href: "/order",
|
||||
},
|
||||
|
||||
/////////////// hidden route
|
||||
|
||||
{
|
||||
href: "/categories/:id",
|
||||
|
|
@ -57,8 +78,33 @@ export const RoutesLinks: RoutesLinksType[] = [
|
|||
hidden:true
|
||||
},
|
||||
{
|
||||
href: "/coupon/:id",
|
||||
element: <EditCoupon />,
|
||||
href: "/categories/add",
|
||||
element: <AddCategoriesPage />,
|
||||
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
|
||||
|
||||
},
|
||||
|
||||
|
||||
]
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap');
|
||||
|
||||
|
||||
html,body {
|
||||
background: var(--bg2)
|
||||
background: var(--bg2);
|
||||
font-family: 'Poppins', sans-serif;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
:root {
|
||||
--primary:#fcb04f ;
|
||||
--secondary : #304a7e;
|
||||
--primary:#E57DB1 ;
|
||||
--secondary : #2D9CDB;
|
||||
--text: #565656;
|
||||
--bg: #ffffff;
|
||||
--bg2: #f8f8f8;
|
||||
--shadow: rgba(0, 0, 0, 0.15);
|
||||
--gray : rgb(207, 210, 214);
|
||||
--linear : linear-gradient(118deg, #fcb04f, #f78c3f)
|
||||
--linear : linear-gradient(118deg, #2D9CDB, #2D9CDB)
|
||||
}
|
||||
|
||||
:root:has(.dark) {
|
||||
|
|
|
|||
|
|
@ -361,3 +361,29 @@ background: var(--bg);
|
|||
background: var(--bg);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -84,7 +84,7 @@ overflow-x: hidden;
|
|||
background: var(--primary);
|
||||
color: var(--bg);
|
||||
border-radius: 5px;
|
||||
box-shadow: 2px 2px 7px 0 var(--primary);
|
||||
box-shadow: 2px 2px 7px 0 var(--shadow);
|
||||
cursor: pointer;
|
||||
transform: scale(1.01);
|
||||
@include Shadow;
|
||||
|
|
@ -149,7 +149,7 @@ overflow-x: hidden;
|
|||
background: var(--linear);
|
||||
color: var(--bg);
|
||||
border-radius: 5px;
|
||||
box-shadow: 2px 2px 7px 0 var(--primary);
|
||||
box-shadow: 2px 2px 7px 0 var(--shadow);
|
||||
|
||||
svg {
|
||||
background-color: transparent;
|
||||
|
|
|
|||
|
|
@ -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%;
|
||||
}
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ const KEY = "CATEGORIES"
|
|||
|
||||
|
||||
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 useUpdateCategories = () => useUpdateMutation(KEY, API.UPDATE);
|
||||
|
|
|
|||
25
src/api/Slider.ts
Normal file
25
src/api/Slider.ts
Normal 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);
|
||||
|
|
@ -5,7 +5,7 @@ import useAddMutation from "./helper/useAddMutation";
|
|||
|
||||
const KEY = "AUTH"
|
||||
const API = {
|
||||
LOGIN: `/api/admin/login`,
|
||||
LOGIN: `admin/login`,
|
||||
LOGOUT: `/api/admin/logout`,
|
||||
};
|
||||
export const useLoginAdmin = ()=>useAddMutation(KEY , API.LOGIN)
|
||||
|
|
@ -1,12 +1,16 @@
|
|||
import { useQuery } from 'react-query';
|
||||
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={}) {
|
||||
const axios = useAxios();
|
||||
const location = useLocation();
|
||||
const pagination = location?.search || '';
|
||||
// console.log(params);
|
||||
const {logout} = useAuthState()
|
||||
const language = localStorage.getItem("language") ?? "en"
|
||||
const navigate = useNavigate()
|
||||
|
||||
return useQuery(
|
||||
[KEY, pagination], async () => {
|
||||
|
|
@ -14,14 +18,19 @@ export default function useGetQueryPagination(KEY:string , Api: string , params
|
|||
return response.data;
|
||||
},
|
||||
{
|
||||
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,
|
||||
// }
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,9 +14,7 @@ function useAxios() {
|
|||
|
||||
if(isAuthenticated){
|
||||
|
||||
buildAxios.withHeaders({ Authorization: 'Bearer '+
|
||||
"31|wDE4pbzJitVVrHhYlBnRIQjIJpkDfwOjqM8Yhbmdb0a4e257"
|
||||
})
|
||||
buildAxios.withHeaders({ Authorization: 'Bearer '+ token })
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,16 +1,20 @@
|
|||
import { useQuery } from 'react-query';
|
||||
import useAxios from './useAxios';
|
||||
import useAuthState from '../../lib/state mangment/AuthState';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
function useGetOneQuery(key: string, url: string , params:any={},options:any={}) {
|
||||
const axios = useAxios();
|
||||
const {logout} = useAuthState()
|
||||
const language = localStorage.getItem("language") ?? "en"
|
||||
const navigate = useNavigate()
|
||||
const {id} = useParams()
|
||||
|
||||
return useQuery(
|
||||
params ? [key, params] : key,
|
||||
[id, key],
|
||||
async () => {
|
||||
const response = await axios.get(url+"/"+ params?.id+`?lang=${language}`);
|
||||
return response.data.data;
|
||||
const response = await axios.get(url+"/"+ id+`?lang=${language}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
|
||||
|
|
@ -18,6 +22,7 @@ function useGetOneQuery(key: string, url: string , params:any={},options:any={})
|
|||
onError: (error:any) => {
|
||||
if(error.response.status == 401 || error.response.status == 403){
|
||||
logout()
|
||||
navigate("/auth")
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import { useQuery } from 'react-query';
|
||||
import useAxios from './useAxios';
|
||||
import useAuthState from '../../lib/state mangment/AuthState';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
function useGetQuery(key: string, url: string , params:any={},options:any={}) {
|
||||
const axios = useAxios();
|
||||
const {logout} = useAuthState()
|
||||
const navigate = useNavigate()
|
||||
return useQuery(
|
||||
params ? [key, params] : key,
|
||||
async () => {
|
||||
|
|
@ -15,8 +17,13 @@ function useGetQuery(key: string, url: string , params:any={},options:any={}) {
|
|||
|
||||
{
|
||||
onError: (error:any) => {
|
||||
console.log('====================================');
|
||||
console.log(error.response.status);
|
||||
console.log('====================================');
|
||||
if(error.response.status == 401 || error.response.status == 403){
|
||||
logout()
|
||||
navigate("/auth")
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { useQueryClient, useMutation, UseMutationResult } from "react-query";
|
|||
import { toast } from "react-toastify";
|
||||
import useAxios from "./useAxios";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
type AxiosResponse = {
|
||||
message: string;
|
||||
|
|
@ -16,10 +17,11 @@ const useUpdateMutation = (
|
|||
const axios = useAxios();
|
||||
const queryClient = useQueryClient();
|
||||
const [t] = useTranslation();
|
||||
const {id}= useParams()
|
||||
|
||||
return useMutation<AxiosResponse, unknown, unknown>(
|
||||
async (dataToSend) => {
|
||||
const { data } = await axios.post(url, dataToSend);
|
||||
const { data } = await axios.post(url+"/"+id, dataToSend);
|
||||
return data;
|
||||
},
|
||||
{
|
||||
|
|
|
|||
32
src/api/order.ts
Normal file
32
src/api/order.ts
Normal 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
26
src/api/product.ts
Normal 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
25
src/api/users.ts
Normal 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);
|
||||
|
|
@ -1,15 +1,14 @@
|
|||
import create from 'zustand';
|
||||
import { TOKEN_KEY, TOKEN_KEY_SOCKET, USER_KEY } from '../../config/AppKey';
|
||||
import { IUser } from '../../types/User';
|
||||
|
||||
interface LoginResponse {
|
||||
token:string ,
|
||||
"user": IUser,
|
||||
"admin": any,
|
||||
token_node:string
|
||||
}
|
||||
|
||||
interface AuthStore {
|
||||
user: IUser | null |undefined;
|
||||
user: any | null |undefined;
|
||||
token:string |null | undefined;
|
||||
isAuthenticated: boolean;
|
||||
login: (userData: LoginResponse) => Promise<void>;
|
||||
|
|
@ -25,16 +24,15 @@ const useAuthState = create<AuthStore>((set) => {
|
|||
|
||||
return {
|
||||
user: initialUser,
|
||||
isAuthenticated: true,
|
||||
isAuthenticated: !!storedToken,
|
||||
token:storedToken,
|
||||
login: async (userData) => {
|
||||
// console.log(userData);
|
||||
console.log(userData);
|
||||
localStorage.setItem(TOKEN_KEY , userData.token)
|
||||
|
||||
localStorage.setItem(TOKEN_KEY_SOCKET , userData.token_node)
|
||||
localStorage.setItem(USER_KEY , JSON.stringify(userData.user))
|
||||
localStorage.setItem(USER_KEY , JSON.stringify(userData.admin))
|
||||
|
||||
set((state)=>({user:userData.user , isAuthenticated:true , token:userData.token}))
|
||||
set((state)=>({user:userData.admin , isAuthenticated:true , token:userData.token}))
|
||||
|
||||
},
|
||||
logout: async () => {
|
||||
|
|
|
|||
5
src/utils/Date/ChangeFormat.ts
Normal file
5
src/utils/Date/ChangeFormat.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export function ChangeformatDate(dateString:string) {
|
||||
const dateObject = new Date(dateString);
|
||||
const formattedDate = `${dateObject.toLocaleDateString()} ${dateObject.toLocaleTimeString()}`;
|
||||
return formattedDate;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user