This commit is contained in:
KarimAldeen 2024-02-26 09:07:16 +03:00
parent cd66a46088
commit 4edad39749
137 changed files with 3903 additions and 3556 deletions

216
package-lock.json generated
View File

@ -10,6 +10,7 @@
"dependencies": {
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.19",
"@mui/x-charts": "^6.19.4",
"@react-google-maps/api": "^2.19.2",
"@szhsin/react-menu": "github:szhsin/react-menu",
"@testing-library/jest-dom": "^5.17.0",
@ -33,6 +34,7 @@
"i18next": "^23.6.0",
"i18next-browser-languagedetector": "^7.1.0",
"json-server": "^0.17.4",
"moment": "^2.30.1",
"react": "^18.2.0",
"react-apexcharts": "^1.4.1",
"react-bootstrap": "^2.9.1",
@ -2895,7 +2897,6 @@
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.4.tgz",
"integrity": "sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==",
"peer": true,
"dependencies": {
"@floating-ui/dom": "^1.5.1"
},
@ -3394,7 +3395,6 @@
"version": "5.0.0-beta.26",
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.26.tgz",
"integrity": "sha512-gPMRKC84VRw+tjqYoyBzyrBUqHQucMXdlBpYazHa5rCXrb91fYEQk5SqQ2U5kjxx9QxZxTBvWAmZ6DblIgaGhQ==",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.23.4",
"@floating-ui/react-dom": "^2.0.4",
@ -3605,7 +3605,6 @@
"version": "7.2.10",
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.10.tgz",
"integrity": "sha512-wX1vbDC+lzF7FlhT6A3ffRZgEoKWPF8VqRoTu4lZwouFX2t90KyCMsgepMw5DxLak1BSp/KP86CmtZttikb/gQ==",
"peer": true,
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0"
},
@ -3619,7 +3618,6 @@
"version": "5.14.20",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.20.tgz",
"integrity": "sha512-Y6yL5MoFmtQml20DZnaaK1znrCEwG6/vRSzW8PKOTrzhyqKIql0FazZRUR7sA5EPASgiyKZfq0FPwISRXm5NdA==",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.23.4",
"@types/prop-types": "^15.7.11",
@ -3643,6 +3641,41 @@
}
}
},
"node_modules/@mui/x-charts": {
"version": "6.19.4",
"resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-6.19.4.tgz",
"integrity": "sha512-0kNg/6jVuG4GoZbV6qb9pYmL8Bhor1m55VIuVlu3p9WdsZFLUyksS4r08viII3YMxosl6MJdFnEfMjWJDAswXw==",
"dependencies": {
"@babel/runtime": "^7.23.2",
"@mui/base": "^5.0.0-beta.22",
"@react-spring/rafz": "^9.7.3",
"@react-spring/web": "^9.7.3",
"clsx": "^2.0.0",
"d3-color": "^3.1.0",
"d3-scale": "^4.0.2",
"d3-shape": "^3.2.0",
"prop-types": "^15.8.1"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@mui/material": "^5.4.1",
"@mui/system": "^5.4.1",
"react": "^17.0.0 || ^18.0.0",
"react-dom": "^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@emotion/react": {
"optional": true
},
"@emotion/styled": {
"optional": true
}
}
},
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
"version": "5.1.1-v1",
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
@ -3922,6 +3955,71 @@
"resolved": "https://registry.npmjs.org/@react-google-maps/marker-clusterer/-/marker-clusterer-2.19.2.tgz",
"integrity": "sha512-x9ibmsP0ZVqzyCo1Pitbw+4b6iEXRw/r1TCy3vOUR3eKrzWLnHYZMR325BkZW2r8fnuWE/V3Fp4QZOP9qYORCw=="
},
"node_modules/@react-spring/animated": {
"version": "9.7.3",
"resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.3.tgz",
"integrity": "sha512-5CWeNJt9pNgyvuSzQH+uy2pvTg8Y4/OisoscZIR8/ZNLIOI+CatFBhGZpDGTF/OzdNFsAoGk3wiUYTwoJ0YIvw==",
"dependencies": {
"@react-spring/shared": "~9.7.3",
"@react-spring/types": "~9.7.3"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/@react-spring/core": {
"version": "9.7.3",
"resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.7.3.tgz",
"integrity": "sha512-IqFdPVf3ZOC1Cx7+M0cXf4odNLxDC+n7IN3MDcVCTIOSBfqEcBebSv+vlY5AhM0zw05PDbjKrNmBpzv/AqpjnQ==",
"dependencies": {
"@react-spring/animated": "~9.7.3",
"@react-spring/shared": "~9.7.3",
"@react-spring/types": "~9.7.3"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/react-spring/donate"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/@react-spring/rafz": {
"version": "9.7.3",
"resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.7.3.tgz",
"integrity": "sha512-9vzW1zJPcC4nS3aCV+GgcsK/WLaB520Iyvm55ARHfM5AuyBqycjvh1wbmWmgCyJuX4VPoWigzemq1CaaeRSHhQ=="
},
"node_modules/@react-spring/shared": {
"version": "9.7.3",
"resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.3.tgz",
"integrity": "sha512-NEopD+9S5xYyQ0pGtioacLhL2luflh6HACSSDUZOwLHoxA5eku1UPuqcJqjwSD6luKjjLfiLOspxo43FUHKKSA==",
"dependencies": {
"@react-spring/types": "~9.7.3"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/@react-spring/types": {
"version": "9.7.3",
"resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.3.tgz",
"integrity": "sha512-Kpx/fQ/ZFX31OtlqVEFfgaD1ACzul4NksrvIgYfIFq9JpDHFwQkMVZ10tbo0FU/grje4rcL4EIrjekl3kYwgWw=="
},
"node_modules/@react-spring/web": {
"version": "9.7.3",
"resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.3.tgz",
"integrity": "sha512-BXt6BpS9aJL/QdVqEIX9YoUy8CE6TJrU0mNCqSoxdXlIeNcEBWOfIyE6B14ENNsyQKS3wOWkiJfco0tCr/9tUg==",
"dependencies": {
"@react-spring/animated": "~9.7.3",
"@react-spring/core": "~9.7.3",
"@react-spring/shared": "~9.7.3",
"@react-spring/types": "~9.7.3"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/@remix-run/router": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.13.1.tgz",
@ -7501,6 +7599,100 @@
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"node_modules/d3-array": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
"dependencies": {
"internmap": "1 - 2"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-color": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-format": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-interpolate": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
"dependencies": {
"d3-color": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-path": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-scale": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
"dependencies": {
"d3-array": "2.10.0 - 3",
"d3-format": "1 - 3",
"d3-interpolate": "1.2.0 - 3",
"d3-time": "2.1.1 - 3",
"d3-time-format": "2 - 4"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-shape": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
"dependencies": {
"d3-path": "^3.1.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
"dependencies": {
"d3-array": "2 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time-format": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
"dependencies": {
"d3-time": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/damerau-levenshtein": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
@ -10317,6 +10509,14 @@
"node": ">= 0.4"
}
},
"node_modules/internmap": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
"engines": {
"node": ">=12"
}
},
"node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@ -12557,6 +12757,14 @@
"mkdirp": "bin/cmd.js"
}
},
"node_modules/moment": {
"version": "2.30.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
"engines": {
"node": "*"
}
},
"node_modules/morgan": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",

View File

@ -5,6 +5,7 @@
"dependencies": {
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.19",
"@mui/x-charts": "^6.19.4",
"@react-google-maps/api": "^2.19.2",
"@szhsin/react-menu": "github:szhsin/react-menu",
"@testing-library/jest-dom": "^5.17.0",
@ -28,6 +29,7 @@
"i18next": "^23.6.0",
"i18next-browser-languagedetector": "^7.1.0",
"json-server": "^0.17.4",
"moment": "^2.30.1",
"react": "^18.2.0",
"react-apexcharts": "^1.4.1",
"react-bootstrap": "^2.9.1",

1
public/Layout/De.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55.2 38.4"><g fill-rule="evenodd" clip-rule="evenodd"><path d="M3.03 0h49.13c1.67 0 3.03 1.36 3.03 3.03v32.33c0 1.66-1.36 3.02-3.02 3.03H3.02C1.36 38.4 0 37.03 0 35.37V3.03C0 1.36 1.36 0 3.03 0z"/><path d="M0 12.8h55.2v22.57c0 1.67-1.36 3.03-3.03 3.03H3.03C1.36 38.4 0 37.04 0 35.37V12.8z" fill="#d00"/><path d="M0 25.6h55.2v9.77c0 1.66-1.36 3.02-3.02 3.03H3.03A3.04 3.04 0 010 35.37V25.6z" fill="#ffce00"/></g></svg>

After

Width:  |  Height:  |  Size: 470 B

View File

@ -14,8 +14,11 @@ import useImageError from '../../Hooks/useImageError';
const ColumnsImage= ({src}:any) => {
const ErrorImage = "https://upload.wikimedia.org/wikipedia/commons/thumb/6/65/No-Image-Placeholder.svg/832px-No-Image-Placeholder.svg.png"
console.log(ImageBaseURL + src,"ColumnsImage");
const imageUrl = ImageBaseURL + src || ErrorImage;
console.log(imageUrl);
const handleError = useImageError;
// or you can download flipped and rotated image
// https://codesandbox.io/s/zi-ding-yi-gong-ju-lan-antd-5-7-0-forked-c9jvmp

View File

@ -1,46 +0,0 @@
import { Input } from 'antd';
import { SearchProps } from 'antd/es/input'
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
const { Search } = Input;
const SearchField = () => {
const navigate = useNavigate()
const [searchParams,] = useSearchParams();
const location =useLocation()
const {t} = useTranslation();
const [searchValue, setSearchValue] = useState(searchParams.get('search')|| "");
const onSearch: SearchProps['onSearch'] = (value, _e, info) => {
// console.log(value);
navigate(`${location?.pathname}?search=${value}`, { replace: true });
}
const onChange = (e :any) => {
setSearchValue(e.target.value);
}
return (
<div className='SearchField'>
<Search
allowClear
enterButton={t("search")}
// size="middle"
size="large"
placeholder={t("search")}
onSearch={onSearch}
style={{ width: 250 }}
value={searchValue}
onChange={onChange}
/>
</div>
)
}
export default SearchField

View File

@ -1,6 +1,5 @@
import React from "react";
import { Card, CardBody } from "reactstrap";
import Chart from "react-apexcharts";
import { ChartTypeEnum } from "../../../enums/ChartTypeEnum";
import { history } from "../../../ProviderContainer";
import { useNavigate } from "react-router-dom";
@ -33,7 +32,7 @@ const StatisticsCard = (props :StatisticsCardProps) => {
} = props;
return (
<Card {...rest} onClick={()=>navigate(pathWhenClick , {replace:true})}>
<Card className="p-4" {...rest} onClick={()=>navigate(pathWhenClick , {replace:true})}>
<CardBody
className={`${
className ? className : "stats-card-body"
@ -43,12 +42,12 @@ const StatisticsCard = (props :StatisticsCardProps) => {
<div
className={`avatar avatar-stats p-50 m-0 ${ "bg-rgba-primary"}`}
>
<p className="mb-0 text-bold-700">{CardTitle}</p>
{/* <p className="mb-0 text-bold-700">{CardTitle}</p> */}
<div className="avatar-content ">{icon}</div>
</div>
</div>
<div className={"title-section " + (iconLeft ? "ml-2" : "")}>
<h2 className="text-bold-600 mb-25 ">{count}</h2>
{/* <h2 className="text-bold-600 mb-25 ">{count}</h2> */}
<p className="mb-2 Content">{CardContent}</p>
</div>
</CardBody>

View File

@ -1,8 +1,9 @@
import { Menu, MenuItem, MenuButton } from '@szhsin/react-menu';
import { useTranslation, initReactI18next } from 'react-i18next';
import i18n from 'i18next'; // Make sure this import is correct
import i18n from 'i18next';
import translationEN from '../../translate/en.json';
import translationAR from '../../translate/ar.json';
import translationDE from '../../translate/de.json';
i18n.use(initReactI18next).init({
resources: {
@ -11,6 +12,9 @@ i18n.use(initReactI18next).init({
},
ar: {
translation: translationAR
},
de: {
translation: translationDE
}
},
lng: 'en',
@ -18,34 +22,42 @@ i18n.use(initReactI18next).init({
escapeValue: false
}
});
let What_the_language = localStorage.getItem('language') ?? "en";
if (What_the_language === "en") {
i18n.changeLanguage('en');
document.body.setAttribute('dir', 'ltr'); document.body.classList.add('en')}
else{
document.body.setAttribute('dir', 'ltr');
document.body.classList.add('en');
} else if (What_the_language === "ar") {
i18n.changeLanguage('ar');
document.body.setAttribute('dir', 'rtl'); document.body.classList.add('ar');
document.body.setAttribute('dir', 'rtl');
document.body.classList.add('ar');
} else if (What_the_language === "de") {
i18n.changeLanguage('de');
document.body.setAttribute('dir', 'ltr');
document.body.classList.add('de');
}
export default function Translate() {
const { t, i18n } = useTranslation();
const changeLanguage = (newLanguage : any) => {
const changeLanguage = (newLanguage : string) => {
i18n.changeLanguage(newLanguage);
if(newLanguage === "Ar"){
i18n.changeLanguage('ar');
document.body.setAttribute('dir', 'rtl'); document.body.classList.add('ar');localStorage.setItem("language", "ar");
What_the_language = "ar"
}
else if(newLanguage === "En"){
i18n.changeLanguage('en');
document.body.setAttribute('dir', 'ltr'); document.body.classList.remove('ar');localStorage.setItem("language", "en");
What_the_language = "en"
localStorage.setItem("language", newLanguage);
What_the_language = newLanguage;
if (newLanguage === "ar") {
document.body.setAttribute('dir', 'rtl');
document.body.classList.remove('de');
document.body.classList.add('ar');
} else if (newLanguage === "en") {
document.body.setAttribute('dir', 'ltr');
document.body.classList.remove('ar', 'de');
document.body.classList.add('en');
} else if (newLanguage === "de") {
document.body.setAttribute('dir', 'ltr');
document.body.classList.remove('ar');
document.body.classList.add('de');
}
};
@ -56,22 +68,27 @@ export default function Translate() {
<>
<img alt='' src={`/Layout/Ar.svg`} width={20} height={20} /> {t("Arabic")}
</>
:
What_the_language === "de" ?
<>
<img alt='' src={`/Layout/De.svg`} width={20} height={20} /> {t("German")}
</>
:
<>
<img alt='' src={`/Layout/En.svg`} width={20} height={20} /> {t("English")}
</>
}
</MenuButton>} transition>
<MenuItem onClick={() => changeLanguage('Ar')}>
<MenuItem onClick={() => changeLanguage('ar')}>
<img alt='' src='/Layout/Ar.svg' width={20} height={20} /> {t("Arabic")}
</MenuItem>
<MenuItem onClick={() => changeLanguage('En')}>
<MenuItem onClick={() => changeLanguage('en')}>
<img alt='' src='/Layout/En.svg' width={20} height={20} /> {t("English")}
</MenuItem>
<MenuItem onClick={() => changeLanguage('de')}>
<img alt='' src='/Layout/De.svg' width={20} height={20} /> {t("German")}
</MenuItem>
</Menu>
</div>
);
}

View File

@ -0,0 +1,48 @@
.SearchBar{
// margin-top: 20px;
.group {
display: flex;
align-items: center;
position: relative;
max-width: 350px;
width: 350px;
}
.input {
width: 100%;
height: 40px;
padding: 0 1rem;
padding-left: 2.5rem;
border-radius: 8px;
outline: none;
font-weight: 500;
background: var(--bg);
color: var(--text);
border: none;
box-shadow: 2px 2px 7px 0 var(--bg);
}
.input::placeholder {
color: var(--subtext);
opacity: .4;
}
.icon {
position: absolute;
left: 1rem;
fill: var(--subtext);
width: 1rem;
height: 1rem;
}
}

View File

@ -0,0 +1,35 @@
import React, { useState } from 'react'
import './SearchBar.scss'
import { useNavigate, useSearchParams } from 'react-router-dom';
const SearchBar = () => {
const [searchQuery, setSearchQuery] = useState('');
const [searchParams] = useSearchParams()
const navigate = useNavigate();
const handleChange = (event:any) => {
const { value } = event.target;
setSearchQuery(value);
updateUrlParams(value);
};
const updateUrlParams = (value:any) => {
navigate(`?search=${value}`, { replace: true });
};
return (
<div className='SearchBar'>
<div className="group">
<svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" className="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium css-1b5stb0 icon" focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="SearchIcon"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path></svg>
<input placeholder="Search Product...." type="search"
className="input"
value={searchQuery}
onChange={handleChange}
/>
</div>
</div>
)
}
export default SearchBar

View File

@ -1,4 +1,4 @@
.KarimField{
.ValidationField{
>*{
width: 100%;
}

View File

@ -1,13 +1,17 @@
import React from "react";
import "./KarimField.scss";
import "./ValidationField.scss";
import { Date, Time, File, DataRange, SelectField, Default, CheckboxField } from './View';
import { KarimFieldProps } from "./types";
import { ValidationFieldProps } from "./types";
import MaltyFile from "./View/MaltyFile";
import SearchField from "./View/SearchField";
const ValidationField: React.FC<ValidationFieldProps> = ({type , ...otherProps}) => {
const KarimField: React.FC<KarimFieldProps> = ({type = "text", ...otherProps}) => {
switch (type) {
case 'Select':
return <SelectField {...otherProps} />;
case 'Search':
return <SearchField {...otherProps} />;
case "DataRange":
return <DataRange {...otherProps} />;
case "Date":
@ -21,8 +25,8 @@ const KarimField: React.FC<KarimFieldProps> = ({type = "text", ...otherProps}) =
case "Checkbox":
return <CheckboxField {...otherProps} />;
default:
return <Default {...otherProps} />;
return <Default {...otherProps} type={type}/>;
}
};
export default React.memo(KarimField);
export default React.memo(ValidationField);

View File

@ -25,7 +25,7 @@ const CheckboxField = ({ name, label, isDisabled, onChange,Group,className, prop
>
{t(label)}
{t(`${label ? label : name}`)}
</Checkbox>
</Form.Item>

View File

@ -15,9 +15,9 @@ const DataRange = ({ name, label,Format ,props ,onChange,isDisabled,placeholder,
};
return (
<div className='KarimField'>
<div className='ValidationField'>
<label htmlFor={name} className="text">
{t(`${label}`)}
{t(`${label ? label : name}`)}
</label>
<Form.Item
hasFeedback
@ -32,6 +32,7 @@ const DataRange = ({ name, label,Format ,props ,onChange,isDisabled,placeholder,
format={Format}
onChange={onChange || onCalendarChange}
disabled={isDisabled}
defaultValue={formik.values[name]}
/>

View File

@ -13,7 +13,7 @@ const Date = ({ name, label,picker="date" ,isDisabled,props,onChange,placeholder
};
return (
<div className='KarimField'>
<div className='ValidationField'>
<label htmlFor={name} className="text">
{t(`${label}`)}
</label>

View File

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

View File

@ -7,15 +7,17 @@ import { useTranslation } from 'react-i18next';
const File = ({ name, label, onChange, isDisabled,placholder,className, props }: any) => {
const { formik, t ,isError} = useFormField(name, props)
const imageUrl = formik.values[name] ? ImageBaseURL + formik.values[name] : '';
let FormikName = formik.values[name];
const imageUrl = formik.values[name] ? ImageBaseURL + FormikName : '';
const fileList: UploadFile[] = [
{
uid: '-1',
name: '',
status: 'done',
url: imageUrl,
thumbUrl: imageUrl,
url: FormikName == ""? imageUrl : imageUrl?.replace("public", "/storage"),
thumbUrl: FormikName == ""? imageUrl : imageUrl?.replace("public", "/storage")
}
];
const FilehandleChange = (value:any) => {
@ -27,7 +29,7 @@ const File = ({ name, label, onChange, isDisabled,placholder,className, props }:
onSuccess();
};
return (
<div className="KarimField">
<div className="ValidationField">
<label htmlFor={name} className="text">
{t(`${label || name}`)}
</label>

View File

@ -6,8 +6,8 @@ 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) => ({
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',
@ -25,7 +25,7 @@ const MaltyFile = ({ name, label, onChange, isDisabled, placholder, className, p
};
return (
<div className="KarimField">
<div className="ValidationField">
<label htmlFor={name} className="text">
{t(`${label || name}`)}
</label>

View File

@ -0,0 +1,56 @@
import { Form, Select } from 'antd';
import React, { useEffect, useState } from 'react';
import useFormField from '../../../Hooks/useFormField';
import { useNavigate } from 'react-router-dom';
const SearchField = ({ name, label, placeholder, isDisabled, searchBy, option, isMulti, onChange, className, props }: any) => {
const { errorMsg, isError, t, formik } = useFormField(name, props);
const [searchQuery, setSearchQuery] = useState<string>('');
const navigate = useNavigate()
useEffect(() => {
const searchParams = new URLSearchParams(window.location.search);
setSearchQuery(searchParams?.get('search') || '');
}, []);
const SelecthandleChange = (value: { value: string; label: React.ReactNode }) => {
formik.setFieldValue(name, value);
};
const SearchHandleChange = (value:any) => {
navigate(`${window?.location?.pathname}?${searchBy}=${value}`, { replace: true });
};
return (
<div className='ValidationField'>
<label htmlFor={name} className="text">
{t(`${label ? label : name}`)}
</label>
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
<Select
placeholder={t(`${placeholder ? placeholder : name}`)}
disabled={isDisabled}
options={option}
size="large"
className={`${className} w-100`}
defaultValue={formik.values[name]}
allowClear
{...(isMulti && { mode: "multiple" })}
onChange={onChange || SelecthandleChange}
showSearch
optionFilterProp="label"
onSearch={SearchHandleChange}
/>
</Form.Item>
</div>
);
};
export default React.memo(SearchField);

View File

@ -10,7 +10,7 @@ const SelectField = ({ name, label, placeholder, isDisabled,option,isMulti,onCha
};
return (
<div className='KarimField'>
<div className='ValidationField'>
<label htmlFor={name} className="text">
{t(`${label ? label : name}`)}
</label>

View File

@ -12,7 +12,7 @@ const Time = ({ name, label,className,isDisabled,onChange,props }: any) => {
};
return (
<div className='KarimField'>
<div className='ValidationField'>
<label htmlFor={name} className="text">
{t(`${label}`)}
</label>

View File

@ -1,5 +1,5 @@
// export interface KarimFieldProps {
// export interface ValidationFieldProps {
// name: string;
// type?: "text" | "Select" | "DataRange" | "Date" | "Time" | "File" | "number" | "Checkbox" | "password";
// placeholder?: string;
@ -15,7 +15,7 @@
// dir?:'ltr' | "rtl"
// }
export interface KarimFieldPropsText {
export interface ValidationFieldPropsText {
name: string;
type: "text";
placeholder?: string;
@ -26,7 +26,7 @@
dir?:'ltr' | "rtl"
}
export interface KarimFieldPropsSelect {
export interface ValidationFieldPropsSelect {
name: string;
type: "Select";
placeholder?: string;
@ -39,7 +39,22 @@
isMulti?: boolean;
}
export interface KarimFieldPropsDataRange {
export interface ValidationFieldPropsSearch{
name: string;
type: "Search";
placeholder?: string;
label?: string;
className?: string;
isDisabled?: boolean;
onChange?: (value: any) => void;
dir?:'ltr' | "rtl";
option: any[];
isMulti?: boolean;
searchBy:string;
}
export interface ValidationFieldPropsDataRange {
name: string;
type: "DataRange";
placeholder?: string;
@ -48,9 +63,9 @@
isDisabled?: boolean;
onChange?: (value: any) => void;
dir?:'ltr' | "rtl"
Format?: "YYYY/MM/DD" | "MM/DD" | "YYYY/MM";
Format?: "YYYY/MM/DD" | "MM/DD" | "YYYY/MM" | "YYYY-MM-DD HH:mm:ss.SSS";
}
export interface KarimFieldPropsDate {
export interface ValidationFieldPropsDate {
name: string;
type: "Date";
placeholder?: string;
@ -63,7 +78,7 @@
}
export interface KarimFieldPropsTime {
export interface ValidationFieldPropsTime {
name: string;
type: "Time";
label?: string;
@ -75,7 +90,7 @@
}
export interface KarimFieldPropsFile {
export interface ValidationFieldPropsFile {
name: string;
type: "File" | "MaltyFile";
placeholder?: string;
@ -86,7 +101,7 @@
dir?:'ltr' | "rtl"
}
export interface KarimFieldPropsCheckbox {
export interface ValidationFieldPropsCheckbox {
name: string;
type: "Checkbox";
label?: string;
@ -97,7 +112,7 @@
Group?: boolean
}
export interface KarimFieldPropstext {
export interface ValidationFieldPropstext {
name: string;
type?: "text" | "number" | "password";
label?: string;
@ -111,4 +126,4 @@
}
export type KarimFieldProps = KarimFieldPropsText| KarimFieldPropsSelect| KarimFieldPropsDataRange| KarimFieldPropsDate| KarimFieldPropsTime| KarimFieldPropsFile| KarimFieldPropsCheckbox| KarimFieldPropstext;
export type ValidationFieldProps = ValidationFieldPropsText| ValidationFieldPropsSelect| ValidationFieldPropsDataRange| ValidationFieldPropsDate| ValidationFieldPropsTime| ValidationFieldPropsFile| ValidationFieldPropsCheckbox| ValidationFieldPropstext | ValidationFieldPropsSearch;

View File

@ -12,7 +12,7 @@ if (!fileName) {
let FileContiner = `
import React from 'react'
import { Col, Row } from 'reactstrap';
import KarimField from '../../Components/Karimalden/KarimField';
import ValidationField from '../../Components/ValidationField/ValidationField';
import { FakeSelectData } from '../../Layout/app/Const';
import { useFormikContext } from 'formik';
@ -27,18 +27,18 @@ function Form${capitalizeFirstLetter(fileName)}() {
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
<Col>
// name from form utils
<KarimField name="name" type="text"label='name' placeholder='placeholder' />
<KarimField name="number" type="number" label='number' placeholder='placeholder' />
<KarimField name="select" type="select"label='select' option={FakeSelectData} isMulti={true} placeholder='placeholder' />
<KarimField name="Multiselect" type="select"label='Multiselect' option={FakeSelectData} Disabled={true} placeholder='placeholder'/>
<ValidationField name="name" type="text"label='name' placeholder='placeholder' />
<ValidationField name="number" type="number" label='number' placeholder='placeholder' />
<ValidationField name="select" type="select"label='select' option={FakeSelectData} isMulti={true} placeholder='placeholder' />
<ValidationField name="Multiselect" type="select"label='Multiselect' option={FakeSelectData} Disabled={true} placeholder='placeholder'/>
</Col>
<Col>
<KarimField name="date" type="date" label='date' placeholder='placeholder' />
<KarimField name="time" type="text"label='time' placeholder='placeholder' />
<KarimField name="CheckBox" name2='CheckBox2' type="checkbox" label='CheckBox' placeholder='placeholder' group={true} />
<KarimField name="DateFrom" name2="DateTo" type="DataRange" />
<ValidationField name="date" type="date" label='date' placeholder='placeholder' />
<ValidationField name="time" type="text"label='time' placeholder='placeholder' />
<ValidationField name="CheckBox" name2='CheckBox2' type="checkbox" label='CheckBox' placeholder='placeholder' group={true} />
<ValidationField name="DateFrom" name2="DateTo" type="DataRange" />
</Col>

3
src/Hooks/isEmpty.tsx Normal file
View File

@ -0,0 +1,3 @@
export const isEmpty = (Type:any) => {
return !Type || (Array.isArray(Type) && Type.length === 0);
};

View File

@ -0,0 +1,14 @@
const useFormatToSelect = (Data : any) => {
const format = (data :any) => {
if (!data) return [];
return data.map((item :any) => ({
value: item?.id,
label: item?.name,
}));
};
return format(Data);
};
export default useFormatToSelect;

View File

@ -1,4 +1,5 @@
import { Pagination } from "antd";
import { useTranslation } from "react-i18next";
import { useLocation, useNavigate, useSearchParams } from "react-router-dom";
export const PaginationBody = ({ data }: any) => {
@ -6,29 +7,32 @@ export const PaginationBody = ({ data }: any) => {
const location = useLocation();
const pagination = location?.search || '';
const currentPage = parseInt(new URLSearchParams(location.search).get("page") || "1", 10);
const pageSize = parseInt(new URLSearchParams(location.search).get("per_page") || "8", 10);
const pageSize = parseInt(new URLSearchParams(location.search).get("per_page") || "15", 10);
const [searchParams] = useSearchParams()
const onChange = (page: number, pageSize?: number) => {
navigate(`?page=${page}&per_page=${pageSize || data?.per_page}&search=${searchParams.get('search')}`, { replace: true });
navigate(`?page=${page}&per_page=${pageSize || data?.per_page}`, { replace: true });
};
const onShowSizeChange = (current: number, pageSize: number) => {
navigate(`?page=${current}&per_page=${pageSize}&search=${searchParams.get('search')}`, { replace: true });
navigate(`?page=${current}&per_page=${pageSize}`, { replace: true });
};
const [t] = useTranslation()
return (
<Pagination
className='text-center mt-3 paginateStyle'
total={data}
showTotal={(total: any) => `Total ${total} items`}
showTotal={(total: any) => `${t(`Total`)} ${total} ${t(`items`)}`}
pageSize={pageSize}
pageSizeOptions={[8, 16, 24, 32, 40]}
pageSizeOptions={[6, 15, 22, 30]}
defaultCurrent={currentPage}
current={currentPage} // Adding this line will set the current page correctly
current={currentPage}
onChange={onChange}
onShowSizeChange={onShowSizeChange}
// showQuickJumper
showSizeChanger
/>
);
};

View File

@ -49,7 +49,7 @@ const Header = () => {
</WithDrawer>
</div>
<div className='Header_Right'>
<Theme />
{/* <Theme /> */}
<Translate />
<Menu menuButton={<MenuButton>

View File

@ -50,7 +50,7 @@ const Sidebar: React.FC<SidebarProps> = () => {
<div className={isOpenSide ? 'SideBar SideBar_Open' : 'SideBar noOpen'}>
<div className='SideBar_Top'>
<div onClick={handleImg}>
<img src="../Logo.png" width={isOpenSide ? 70 : 150} alt="" />
<img src="../Logo.png" width={isOpenSide ? 70 : 130} alt="" />
{/* <Etaxi/> */}
</div>
<div className='HamburgerMenu' onClick={handleHamburgerMenu}>

View File

@ -4,7 +4,7 @@ export interface FormTableState {
OpenAdd: boolean;
}
export type KarimFieldProps = {
export type ValidationFieldProps = {
name: string;
name2?: string;
type: string;

View File

@ -15,7 +15,7 @@ import { convet_data_to_select } from "./Utils";
import { useTranslation } from "react-i18next";
import { ValidatedField } from "../../../Components/Ui";
import { LoadingButton } from "../../../Components/Ui/LoadingButton";
import KarimField from "../../../Components/Karimalden/KarimField";
import ValidationField from "../../../Components/ValidationField/ValidationField";
interface RegisterFormProps {
mutation: any;
@ -50,7 +50,7 @@ export const RegisterForm: FC<RegisterFormProps> = ({ mutation, editMode = false
<Form>
<Row lg={2} xl={2}>
<Col>
<KarimField
<ValidationField
name="full_name"
label="full_name"
placeholder="full_name"
@ -58,7 +58,7 @@ export const RegisterForm: FC<RegisterFormProps> = ({ mutation, editMode = false
/>
</Col>
<Col>
<KarimField
<ValidationField
type="Select"
option={rolesOptions}
label="role"
@ -70,7 +70,7 @@ export const RegisterForm: FC<RegisterFormProps> = ({ mutation, editMode = false
</Row>
<Row xs={1} sm={1} md={2} lg={2} xl={2}>
<Col>
<KarimField
<ValidationField
name="email"
label="email"
placeholder="email"
@ -78,7 +78,7 @@ export const RegisterForm: FC<RegisterFormProps> = ({ mutation, editMode = false
/>
</Col>
<Col>
<KarimField
<ValidationField
name="phone"
label="phone"
placeholder="phone"
@ -88,7 +88,7 @@ export const RegisterForm: FC<RegisterFormProps> = ({ mutation, editMode = false
{!editMode && (
<>
<Col>
<KarimField
<ValidationField
name="password"
label="password"
placeholder="password"
@ -96,7 +96,7 @@ export const RegisterForm: FC<RegisterFormProps> = ({ mutation, editMode = false
/>
</Col>
<Col>
<KarimField
<ValidationField
name="password_confirmation"
label="confirm_password"
placeholder="confirm_password"

View File

@ -1,7 +1,7 @@
import React from 'react'
import { Col, Row } from 'reactstrap';
import KarimField from '../../../Components/Karimalden/KarimField';
import ValidationField from '../../../Components/ValidationField/ValidationField';
import { FakeSelectData } from '../../../Layout/app/Const';
import { useFormikContext } from 'formik';
@ -15,12 +15,12 @@ function FormViewAccount() {
return (
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
<Col>
<KarimField name="name" type="text"label='name' placeholder='placeholder' />
<ValidationField name="name" type="text"label='name' placeholder='placeholder' />
</Col>
<Col>
<KarimField name="date" type="Date" label='date' placeholder='placeholder' />
<ValidationField name="date" type="Date" label='date' placeholder='placeholder' />
</Col>

View File

@ -9,7 +9,7 @@ import { getInitialValues, getValidationSchema } from './formUtil';
import { LoadingButton } from '../../Components/Ui/LoadingButton';
import useNavigateOnSuccess from '../../Hooks/useNavigateOnSuccess';
import useAuthState from '../../lib/state mangment/AuthState';
import KarimField from '../../Components/Karimalden/KarimField';
import ValidationField from '../../Components/ValidationField/ValidationField';
const LoginForm = () => {
const [t] = useTranslation();
@ -47,7 +47,7 @@ const OnSuccess = ()=>{
{t("Welcome back, please login to your account.")}
</h4>
<KarimField
<ValidationField
placeholder={t('email')}
type='text'
name='email'
@ -56,7 +56,7 @@ const OnSuccess = ()=>{
</div>
<div className='form-outline mb-4'>
<KarimField
<ValidationField
placeholder={t('password')}
type='password'
name='password'
@ -71,7 +71,7 @@ const OnSuccess = ()=>{
{t("Sign in")}
{/* </button> */}
</LoadingButton>
<p className='Reserved'>{t("SDNone © 2022 | All Rights Reserved")}</p>
<p className='Reserved'>{t("Point © 2022 | All Rights Reserved")}</p>
</Form>
</Formik>

View File

@ -16,9 +16,9 @@ function Page() {
const column =useTableColumns()
const {data ,status } = useGetCategories()
console.log(data);
const [t] = useTranslation()
const navigate = useNavigate()
const totalRows = data?.meta?.total;
return (
// Pass Status to Layout
@ -31,6 +31,8 @@ function Page() {
data={data?.categories}
isLoading={false}
columns={column}
total={totalRows }
is_pagination={true}
/>

View File

@ -1,28 +1,40 @@
import React from 'react'
import { Col, Row } from 'reactstrap';
import KarimField from '../../../Components/Karimalden/KarimField';
import ValidationField from '../../../Components/ValidationField/ValidationField';
import { useFormikContext } from 'formik';
import { DatePicker } from 'antd';
import { useTranslation } from 'react-i18next';
import { useGetCategories } from '../../../api/Categories';
import useFormatToSelect from '../../../Hooks/useFormatToSelect';
import { useGetOneAttribute } from '../../../api/attribute';
import Atteibute from './Atteibute';
import ObjectField from './Field/Object';
function Form() {
const formik = useFormikContext<any>();
const [t] = useTranslation();
const { data } = useGetCategories()
const SelectData = useFormatToSelect(data?.categories)
const { values } = useFormikContext<any>();
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" />
<ValidationField name="name_ar" />
<ValidationField name="name_en" />
<ValidationField name="name_de" />
</Col>
<Col>
<KarimField name="parent_id" type="Select" option={[]} />
<KarimField name="photo" type="File" />
<ValidationField name="parent_id" type="Search" option={SelectData} searchBy={"search"} />
<ValidationField name="photo" type="File" />
<ObjectField/>
{/* {values?.id &&
<Atteibute />
} */}
</Col>

View File

@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react'
import { getInitialValues, getValidationSchema, getDataToSend } from '../formUtil'
import { getValidationSchema, getDataToSend ,getInitialValuesForAdd as getInitialValues} 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'
@ -9,26 +9,36 @@ import { BsInfoCircle } from 'react-icons/bs';
import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess';
import { useAddCategories } from '../../../api/Categories';
import Form from './AddForm';
import { usePageState } from '../../../lib/state mangment/LayoutPagestate';
const AddcategoriesPage = () => {
const { setObjectToEdit, objectToEdit } = usePageState()
const {mutate , isLoading , isSuccess} = useAddCategories()
const handleSubmit = (values:any)=>{
values['name'] = {
"en" : values.name_en,
"ar" : values.name_ar,
"de" : values.name_de,
values['attribute'] = changeShapeInfo(values?.new_attribute)
values["name"]= {
en:values?.name_en,
ar:values?.name_ar,
de:values?.name_de
}
mutate(values)
console.log();
mutate(values)
}
const {t} = useTranslation();
useNavigateOnSuccess(isSuccess , '/categories' )
useEffect(() => {
setObjectToEdit([]);
}, []);
const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit };
@ -60,3 +70,19 @@ const AddcategoriesPage = () => {
}
export default AddcategoriesPage
function changeShapeInfo(originalObject: any) {
const transformedObject: any = {};
for (const key in originalObject) {
if (originalObject.hasOwnProperty(key)) {
const index = key.split('.')[0]; // Extract index from key
const attribute = key.split('.')[1]; // Extract attribute from key
transformedObject[originalObject[`${index}.key`]] = originalObject[`${index}.Description`];
}
}
return transformedObject
}

View File

@ -0,0 +1,37 @@
import React from 'react';
import { useFormikContext } from 'formik';
import ValidationField from '../../../Components/ValidationField/ValidationField';
import { useGetSingleAttribute } from '../../../api/attribute';
const Attribute = () => {
const { values, setFieldValue } = useFormikContext<any>();
const { data: attributeData } = useGetSingleAttribute({ name: "category_id", id: values?.id });
const handleAttributeChange = (name: string, value: any) => {
setFieldValue(`attribute[${name}]`, value);
};
return (
attributeData?.data?.map((item: any) => {
const options = item?.attribute_value?.map((attr: any) => ({
label: attr?.value,
value: attr?.attribute_id
}));
return (
<ValidationField
key={item?.name}
name={`attribute[${item?.name}]`}
label={item?.name}
placeholder={item?.name}
type="Select"
option={options}
onChange={(value: any) => handleAttributeChange(item?.name, value)}
/>
);
})
);
};
export default Attribute;

View File

@ -1,25 +1,47 @@
import React from 'react'
import { Col, Row } from 'reactstrap';
import KarimField from '../../../Components/Karimalden/KarimField';
import ValidationField from '../../../Components/ValidationField/ValidationField';
import { useFormikContext } from 'formik';
import { DatePicker } from 'antd';
import { useTranslation } from 'react-i18next';
import { useGetCategories } from '../../../api/Categories';
import useFormatToSelect from '../../../Hooks/useFormatToSelect';
import { useGetOneAttribute } from '../../../api/attribute';
import Atteibute from './Atteibute';
function Form() {
const formik = useFormikContext<any>();
const [t] = useTranslation();
const {values} = useFormikContext<any>();
const [t] = useTranslation()
const { data } = useGetCategories()
const SelectData = useFormatToSelect(data?.categories)
const {data : AttributeData} = useGetOneAttribute()
return (
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
<Col>
<KarimField name="name" />
<KarimField name="parent_id" type="Select" option={[]} />
<ValidationField name="name" />
<ValidationField name="parent_id" type="Search" option={SelectData} searchBy={"name"} />
</Col>
<Col>
<KarimField name="photo" type="File" />
<ValidationField name="photo" type="File" />
{values?.id &&
<Atteibute />
}
{/* {
AttributeData?.map((item:any)=>{
return(
<ValidationField
name="attributes"
option={item?.data}
/>
)
})
} */}
</Col>

View File

@ -14,37 +14,28 @@ import { BsInfoCircle } from 'react-icons/bs';
import { useGetOneCategories, useUpdateCategories } from '../../../api/Categories';
import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess';
import Form from './EditForm';
import { changeShapeInfo } from '../../../utils/Array/changeShapeInfo';
const EditPage = () => {
const { setObjectToEdit, objectToEdit } = usePageState()
const {t} = useTranslation();
const { data } = useGetOneCategories()
const {mutate ,isSuccess} = useUpdateCategories()
const {mutate ,isSuccess} = useUpdateCategories("post")
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 Imagetype = typeof values?.photo
values['attribute'] = changeShapeInfo(values?.attribute)
if(Imagetype === "string") {
delete values['photo']
}
const language = localStorage.getItem("language") ?? "en";
newData['name'] = {
[language]: values.name,
};
}
return mutate(newData);
mutate(values)
// return mutate(newData);
}
useNavigateOnSuccess(isSuccess , '/categories')
useEffect(() => {
console.log(data);
setObjectToEdit(data?.category);

View File

@ -2,30 +2,50 @@ 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';
import { objectToArray } from '../../../../utils/Array/ArrayToObjectFormik';
import { useTranslation } from 'react-i18next';
const ObjectField = () => {
const [form] = Form.useForm();
const formik = useFormikContext<any>();
const [FieldItems, setFieldItems] = useState<any>([])
const [FieldItems, setFieldItems] = useState<any>(formik?.values?.new_attribute)
const [t] = useTranslation()
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFieldItems((prevState:any) => ({
setFieldItems((prevState:any) =>{
formik.setFieldValue(`new_attribute`, {
...prevState,
[name]: value
}));
});
formik.setFieldValue("info", FieldItems)
return ({
...prevState,
[name]: value
})
} )
};
useEffect(() => {
if (formik?.values?.new_attribute) {
const defaultValues = formik.values.new_attribute;
console.log(objectToArray( formik?.values?.new_attribute));
form.setFieldsValue({
items: [{ list: [{ Attribute: '', Description: '' }] }]
items: [{ list:objectToArray( formik?.values?.new_attribute)}]
});
}
else {
form.setFieldsValue({
items: [{ list: [{ key: '', Description: '' }] },], });
}
}, []); // Update when tabKey or info[tabKey] changes
}, []);
return (
<Form
labelCol={{ span: 6 }}
@ -41,7 +61,7 @@ const ObjectField = () => {
{fields.map((field, index) => (
<div key={field.key}>
<Typography.Text strong style={{ marginBottom: 8 }}>
Information
{t("Information")}
</Typography.Text>
{/* Nested Form.List for sub-items */}
@ -51,17 +71,17 @@ const ObjectField = () => {
<div style={{ display: 'flex', flexDirection: 'column', rowGap: 16 }}>
{subFields.map((subField) => (
<Space key={subField.key}>
<Form.Item noStyle name={[subField.name, 'Attribute']}>
<Form.Item noStyle name={[subField.name, 'key']}>
<Input
placeholder="Attribute"
placeholder={t("key")}
onChange={handleChange} // Assign onChange handler
name={`${subField.name}.Attribute`} // Ensure proper name for dynamic state update
name={`${subField.name}.key`} // Ensure proper name for dynamic state update
/>
</Form.Item>
<Form.Item noStyle name={[subField.name, 'Description']}>
<Input
placeholder="Description"
placeholder={t("Description")}
onChange={handleChange} // Assign onChange handler
name={`${subField.name}.Description`} // Ensure proper name for dynamic state update
@ -76,7 +96,7 @@ const ObjectField = () => {
</Space>
))}
<Button type="dashed" onClick={() => subOpt.add()} block>
+ Add Another Item
{t("+ Add Another Item")}
</Button>
</div>
)}
@ -91,4 +111,4 @@ const ObjectField = () => {
);
};
export default ObjectField;
export default ObjectField

View File

@ -20,17 +20,32 @@ interface ValidateSchema extends formUtilCommon{
}
export const getInitialValues = (objectToEdit: any | null = null): any => {
console.log(objectToEdit,"objectToEdit");
return {
id: objectToEdit?.id ?? 0,
name: objectToEdit?.name ?? "",
name_ar: objectToEdit?.name?.ar ?? '',
name_en: objectToEdit?.name?.en ?? '',
name_de: objectToEdit?.name?.de ?? '',
parent_id: objectToEdit?.parent_id ?? 1,
parent_id: objectToEdit?.parent_id ?? "",
photo: objectToEdit?.photo ?? '',
attribute: objectToEdit?.attribute ?? "",
new_attribute: objectToEdit?.attribute ?? ""
};
};
export const getInitialValuesForAdd = (objectToEdit: any | null = null): any => {
return {
name_ar: '',
name_en: '',
name_de: '',
parent_id: "",
photo: '',
attribute: "",
new_attribute: ""
};
};
export const getValidationSchema = (editMode: boolean = false): Yup.Schema<any> => {
@ -41,6 +56,7 @@ export const getValidationSchema = (editMode: boolean = false): Yup.Schema<any>
name_de: Yup.string().required('Required'),
parent_id: Yup.string().required('Required'),
photo: Yup.string().required('Required'),
});
};

View File

@ -25,7 +25,12 @@ const useTableColumns :any = () => {
name: t("image"),
sortable: false,
center: "true",
cell: (row:any) => <ColumnsImage src={row?.photo} />
cell: (row:any) => {
let str = row?.photo;
str = str?.replace(`public`, "/storage") ?? "";
return <ColumnsImage src={str} />
}
},
{
name: t("parent_id"),

View File

@ -1,55 +0,0 @@
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

44
src/Pages/Coupon/Page.tsx Normal file
View File

@ -0,0 +1,44 @@
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'
import { useGetCoupon } from '../../api/Coupon'
function Page() {
const column =useTableColumns()
const {data ,status } = useGetCoupon()
const [t] = useTranslation()
const navigate = useNavigate()
const totalRows = data?.meta?.total;
return (
// Pass Status to Layout
<DashBody status={status as QueryStatusEnum} >
<DashHeader showAddButton={false} title={'coupon'}>
<AddButton onClick={()=>navigate('/coupon/add')}></AddButton>
</DashHeader>
<LyTable
data={data?.coupons}
isLoading={false}
columns={column}
total={totalRows }
is_pagination={true}
/>
</DashBody>
)
}
export default Page

View File

@ -0,0 +1,60 @@
import React from 'react'
import { Col, Row } from 'reactstrap';
import ValidationField from '../../../Components/ValidationField/ValidationField';
import { useFormikContext } from 'formik';
import { DatePicker } from 'antd';
import { useTranslation } from 'react-i18next';
import { isEmpty } from '../../../Hooks/isEmpty';
import { useGetCategories } from '../../../api/Categories';
import useFormatToSelect from '../../../Hooks/useFormatToSelect';
import { useGetProduct } from '../../../api/product';
function Form() {
const { values } = useFormikContext<any>();
const [t] = useTranslation(); // FLAT coupn not be spacified && flate
const coupon_type = [{ lable: "general", value: "general" },{ lable: "specified", value: "specified" }]
const coupon_type_discount_flat = [{ lable: "general", value: "general" }]
const discount_type = [{ lable: "percentage", value: "percentage" },{ lable: "flat", value: "flat" }]
const { data: CategoriesData } = useGetCategories()
const { data: ProductData } = useGetProduct()
const SelectCategoriesData = useFormatToSelect(CategoriesData?.categories)
const SelectProductData = useFormatToSelect(ProductData?.BaseProducts)
return (
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
<Col>
<ValidationField name="name" />
<ValidationField name="code" />
<ValidationField name="active" label='active_from_to' type='DataRange' Format="YYYY/MM/DD" />
<ValidationField name="maximum_number_of_uses" type='number' />
<ValidationField name="maximum_number_of_uses_per_user" type='number' />
<ValidationField name="minimum_total_to_order" type='number' />
</Col>
<Col>
<ValidationField name="coupon_value" type='number' />
<ValidationField name="discount_type" type="Select" option={discount_type} />
<ValidationField name="coupon_type" type="Select" option={values?.discount_type !== 'flat' ? coupon_type :coupon_type_discount_flat } />
{/* <ValidationField name="itemable_type" label='coupon_item_type' type="Select" option={itemable_type} isDisabled={values?.coupon_type !== "specified"} isMulti/> */}
<ValidationField name="product_attr" label='product_item' type="Search" option={SelectProductData} searchBy={"search"} isDisabled={values?.coupon_type !== "specified"}isMulti />
<ValidationField name="category_attr" label='categories_item_name' type="Search" option={SelectCategoriesData} searchBy={"search"} isDisabled={values?.coupon_type !== "specified"}isMulti />
<ValidationField name="status" type='Checkbox' label='status' />
</Col>
</Row>
)
}
export default Form

View File

@ -3,41 +3,48 @@ import { getInitialValues, getValidationSchema, getDataToSend } from '../formUti
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';
import { useAddCoupon } from '../../../api/Coupon';
import Form from './AddForm';
const AddProductPage = () => {
const AddCouponPage = () => {
const {mutate , isLoading , isSuccess} = useAddProduct()
const [BarStatus, setBarStatus] = useState({ value: 0, isLoading: false, isError: false, isSuccess: false })
const { mutate, isLoading, isSuccess } = useAddCoupon()
const handleSubmit = (values: any) => {
console.log(values);
const formToSend = getDataToSend(values)
values.active_at = values.active[0].format('YYYY-MM-DD HH:mm:ss.SSS')
values.active_to = values.active[1].format('YYYY-MM-DD HH:mm:ss.SSS')
values.status = values.active ? "active" : "inactive"
mutate(formToSend);
}
const products = values?.product_attr?.map((item: any) => {
return { "itemable_type": "product", "itemable_id": item };
})?.filter((item: any) => item.itemable_id !== "")|| [];
const category = values?.category_attr?.map((item: any) => {
return { "itemable_type": "category", "itemable_id": item };
})?.filter((item: any) => item.itemable_id !== "") || [];
values['items'] = [...products, ...category];
console.log(values, "values");
mutate(values)
};
const { t } = useTranslation();
useNavigateOnSuccess(isSuccess , '/products' )
useNavigateOnSuccess(isSuccess, '/Coupon')
const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit, BarStatus };
const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit };
return (
@ -48,15 +55,11 @@ const AddProductPage = () => {
<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>
<div className=" mt-4"><Form /></div>
</TabBody>
</Tabs>
@ -69,4 +72,4 @@ const AddProductPage = () => {
}
export default AddProductPage
export default AddCouponPage

View File

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

View File

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

View File

@ -0,0 +1,60 @@
import React from 'react'
import { Col, Row } from 'reactstrap';
import ValidationField from '../../../Components/ValidationField/ValidationField';
import { useFormikContext } from 'formik';
import { DatePicker } from 'antd';
import { useTranslation } from 'react-i18next';
import { isEmpty } from '../../../Hooks/isEmpty';
import { useGetCategories } from '../../../api/Categories';
import useFormatToSelect from '../../../Hooks/useFormatToSelect';
import { useGetProduct } from '../../../api/product';
function Form() {
const { values } = useFormikContext<any>();
const [t] = useTranslation(); // FLAT coupn not be spacified && flate
const coupon_type = [{ lable: "general", value: "general" },{ lable: "specified", value: "specified" }]
const coupon_type_discount_flat = [{ lable: "general", value: "general" }]
const discount_type = [{ lable: "percentage", value: "percentage" },{ lable: "flat", value: "flat" }]
const { data: CategoriesData } = useGetCategories()
const { data: ProductData } = useGetProduct()
const SelectCategoriesData = useFormatToSelect(CategoriesData?.categories)
const SelectProductData = useFormatToSelect(ProductData?.BaseProducts)
return (
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
<Col>
<ValidationField name="name" />
<ValidationField name="code" />
<ValidationField name="active" label='active_from_to' type='DataRange' Format="YYYY/MM/DD" />
<ValidationField name="maximum_number_of_uses" type='number' />
<ValidationField name="maximum_number_of_uses_per_user" type='number' />
<ValidationField name="minimum_total_to_order" type='number' />
</Col>
<Col>
<ValidationField name="coupon_value" type='number' />
<ValidationField name="discount_type" type="Select" option={discount_type} />
<ValidationField name="coupon_type" type="Select" option={values?.discount_type !== 'flat' ? coupon_type :coupon_type_discount_flat } />
{/* <ValidationField name="itemable_type" label='coupon_item_type' type="Select" option={itemable_type} isDisabled={values?.coupon_type !== "specified"} isMulti/> */}
<ValidationField name="product_attr" label='product_item' type="Search" option={SelectProductData} searchBy={"search"} isDisabled={values?.coupon_type !== "specified"}isMulti />
<ValidationField name="category_attr" label='categories_item_name' type="Search" option={SelectCategoriesData} searchBy={"search"} isDisabled={values?.coupon_type !== "specified"}isMulti />
<ValidationField name="status" type='Checkbox' label='status' />
</Col>
</Row>
)
}
export default Form

View File

@ -0,0 +1,89 @@
import React, { useEffect, useState } from 'react'
import {getInitialValuesForAdd as 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 { useGetOneCoupon, useUpdateCoupon } from '../../../api/Coupon';
import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess';
import Form from './EditForm';
const EditPage = () => {
const { setObjectToEdit, objectToEdit } = usePageState()
const { t } = useTranslation();
const { data } = useGetOneCoupon()
const { mutate, isSuccess } = useUpdateCoupon("put")
const FormatedData = data?.coupon;
const handleSubmit = (values: any) => {
values.active_at = values.active[0].format('YYYY-MM-DD HH:mm:ss.SSS')
values.active_to = values.active[1].format('YYYY-MM-DD HH:mm:ss.SSS')
values.status = values.active ? "active" : "inactive"
const products = values?.product_attr?.map((item: any) => {
return { "itemable_type": "product", "itemable_id": item };
})?.filter((item: any) => item.itemable_id !== "") || [];
const category = values?.category_attr?.map((item: any) => {
return { "itemable_type": "category", "itemable_id": item };
})?.filter((item: any) => item.itemable_id !== "") || [];
values['items'] = [...products, ...category];
console.log(values, "values");
mutate(values)
}
useNavigateOnSuccess(isSuccess, '/Coupon')
useEffect(() => {
setObjectToEdit(data?.coupon);
}, [data]);
const getValidationSchema = () => {
return null
};
const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit };
return (
<div className='ViewPage'>
{objectToEdit && data ?
<ViewPage {...ViewProps}>
<Tabs>
<TabList>
<Tab><div className='SignleDriverContainer'><span className='SignleDriverInfoIcon'><MdLanguage size={20} /></span> <h6 className='SingleDriverInfo'>{t("BasicInfo")}</h6></div></Tab>
</TabList>
<TabBody >
<div className=" mt-4"><Form /></div>
</TabBody>
</Tabs>
</ViewPage>
: <LoadingPage />}
</div>
)
}
export default EditPage

View File

@ -1,70 +1,118 @@
import * as Yup from "yup";
import { buildFormData } from "../../api/helper/buildFormData";
import { mapTranslatedProperties } from "../../utils/language/mapTranslatedProperties";
import moment from 'moment';
import * as dayjs from 'dayjs'
export const getInitialValues = (objectToEdit: any | null = null) => {
// console.log(objectToEdit);
return {
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
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 => {
//@ts-ignore
const products = [] as any;
const categories = [] as any;
// Map over the items array and push items into the appropriate array
objectToEdit?.items?.forEach((item: any) => {
if (item?.itemable_type === "product") {
products.push({ value: item?.itemable?.name, label: item?.itemable?.name });
} else if (item?.itemable_type === "category") {
categories.push({ value: item?.itemable?.name, label: item?.itemable?.name });
}
});
return {
id: objectToEdit?.id ?? 0,
name: objectToEdit?.name ?? "",
code: objectToEdit?.code ?? "",
//@ts-ignore
active: objectToEdit?.active_to ? [dayjs(objectToEdit?.active_from), dayjs(objectToEdit?.active_to)] : "",
minimum_total_to_order: objectToEdit?.minimum_total_to_order ?? "",
maximum_number_of_uses_per_user: objectToEdit?.maximum_number_of_uses_per_user ?? "",
maximum_number_of_uses: objectToEdit?.maximum_number_of_uses ?? "",
coupon_value: objectToEdit?.coupon_value ?? "",
coupon_type: objectToEdit?.coupon_type ?? "",
discount_type: objectToEdit?.discount_type ?? "",
product_attr: products ?? "",
category_attr: categories ?? "",
status: objectToEdit?.status ?? 0,
};
};
export const getValidationSchema = (editMode: boolean = false) => {
// validate input
export const getInitialValuesForAdd = (objectToEdit: any | null = null): any => {
return {
id: '',
name: '',
code: '',
active: '',
minimum_total_to_order: '',
maximum_number_of_uses_per_user: '',
maximum_number_of_uses: '',
coupon_value: '',
coupon_type: '',
discount_type: '',
product_attr: '',
category_attr: '',
status: '',
};
};
export const getValidationSchema = (editMode: boolean = false): Yup.Schema<any> => {
// Validate input
return Yup.object().shape({
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")
name: Yup.string().required('Required'),
code: Yup.string().required('Required'),
coupon_value: Yup.string().required('Required'),
status: Yup.string().required('Required'),
active: Yup.mixed().required('Required'),
discount_type: Yup.string().required('Required'),
})
}),
category_id :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'),
maximum_number_of_uses_per_user: Yup.number().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
}

View File

@ -2,87 +2,64 @@
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";
import { useDeleteCoupon } from "../../api/Coupon";
function fnDelete(props :any ){}
const useTableColumns :any = () => {
const [t] = useTranslation();
const toggleMutation = useUpdateProductStatus();
const deleteMutation = useDeleteProduct();
const deleteMutation = useDeleteCoupon()
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,
center: "true",
cell: (row:any) => row?.name
},
{
name: t("price"),
name: t("discount_type"),
sortable: false,
center: true,
selector:(row:any) => row?.price,
center: "true",
cell: (row:any) => row?.discount_type
},
{
name: t("description"),
name: t("coupon_type"),
sortable: false,
center: true,
cell: (row:any) => (
row?.description
),
center: "true",
cell: (row:any) => row?.coupon_type
},
{
name: t("favorite"),
name: t("code"),
sortable: false,
center: true,
cell: (row:any) => (
<ToggleStatus handleSwitch={handleChange} object={row} toggleMutation={toggleMutation} />
),
center: "true",
cell: (row:any) => row?.code
},
{
name: t("coupon_value"),
sortable: false,
center: "true",
cell: (row:any) => row?.coupon_value
},
{
name: "#",
sortable: false,
center: "true",
center: true,
cell: (row:any) => (
<Actions
onEdit={()=> navigate('/products/'+row.id)}
objectToEdit={row}
showEdit={true}
showEdit
onEdit={()=> navigate(`/coupon/${row.id}`) }
showView={false}
onDelete={() => deleteMutation.mutate({ product_id: row.id })}
onDelete={() => deleteMutation.mutate({ id: row.id })}
/>
),
},
],
[t]
);

View File

@ -1,43 +0,0 @@
import React from 'react'
import LayoutModal from '../../Layout/Dashboard/LayoutModal'
import AddForm from './AddForm'
import { useAddCoupon } from '../../api/Coupon'
import { getDataToSend, getInitialValues, getValidationSchema } from './formUtil'
import { QueryStatusEnum } from '../../config/QueryStatus'
import { useTranslation } from 'react-i18next'
function AddCouponModal() {
const [t] = useTranslation()
const {mutate , status} = useAddCoupon()
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('Coupon')}
getValidationSchema={getValidationSchema()}>
<AddForm />
</LayoutModal>
)
}
export default AddCouponModal

View File

@ -1,37 +0,0 @@
import React from 'react'
import { Col, Row } from 'reactstrap';
import KarimField from '../../Components/Karimalden/KarimField';
import { FakeSelectData } from '../../Layout/app/Const';
import { useFormikContext } from 'formik';
import { DatePicker } from 'antd';
import { useGetSlider } from '../../api/Slider';
function AddForm() {
const formik = useFormikContext<any>();
const {data } = useGetSlider()
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="parent_id" type="Select" option={[]} />
<KarimField name="photo" type="File" />
</Col>
</Row>
)
}
export default AddForm

View File

@ -1,40 +0,0 @@
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 { useGetCoupon} from '../../api/Coupon'
import { useTranslation } from 'react-i18next'
import AddCouponModal from './AddCouponModal'
import { QueryStatusEnum } from '../../config/QueryStatus'
// import * as XLSX from 'ts-xlsx';
function CouponPage() {
const column =useTableColumns()
const {data ,status } = useGetCoupon()
const {t} = useTranslation();
return (
<DashBody status={status as QueryStatusEnum} >
<DashHeader title={'Coupon'}>
</DashHeader>
<LyTable
data={data?.coupons}
isLoading={false}
columns={column}
// is_pagination={true}
// total={data?.meta?.total}
/>
<AddCouponModal />
</DashBody>
)
}
export default CouponPage

View File

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

View File

@ -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 { useAddCoupon, useGetOneCoupon } from '../../../api/Coupon';
const EditPage = () => {
const { id } = useParams()
const { data,isLoading } = useGetOneCoupon({id})
const {mutate} = useAddCoupon()
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?.coupons)}
validationSchema={getValidationSchema}
title='Edit Coupon'
>
<EditForm />
</FormPage>
</KarimSpinner>
);
};
export default EditPage;

53
src/Pages/Home/Chart.tsx Normal file
View File

@ -0,0 +1,53 @@
import * as React from 'react';
//@ts-ignore
import { BarChart } from '@mui/x-charts/BarChart';
import { axisClasses } from '@mui/x-charts';
import { useTranslation } from 'react-i18next';
const chartSetting = {
yAxis: [
{
label: 'rainfall (mm)',
},
],
sx: {
[`.${axisClasses.left} .${axisClasses.label}`]: {
transform: 'translate(-20px, 0)',
},
},
};
const BarsDataset = () => {
const [t] = useTranslation()
const dataset = [
{london: 59, paris: 57, newYork: 86, seoul: 21, month: t('January')},
{london: 59, paris: 57, newYork: 86, seoul: 21, month: t('February')},
{london: 59, paris: 57, newYork: 86, seoul: 21, month: t('March')},
{london: 59, paris: 57, newYork: 86, seoul: 21, month: t('April')},
];
const series = [
{ dataKey: 'london', label: t('London'), valueFormatter: (value: number) => `${value}mm` },
{ dataKey: 'paris', label: t('Paris'), valueFormatter: (value: number) => `${value}mm` },
{ dataKey: 'newYork', label: t('New York'), valueFormatter: (value: number) => `${value}mm` },
{ dataKey: 'seoul', label: t('Seoul'), valueFormatter: (value: number) => `${value}mm` },
];
const months = dataset.map(data => data.month);
return (
<div className="Card BarChart" >
<BarChart
dataset={dataset}
xAxis={[{ scaleType: 'band', dataKey: 'month' }]}
series={series}
{...chartSetting}
/>
</div>
);
};
export default BarsDataset;

View File

@ -1,87 +1,59 @@
import React from "react";
// import StatisticsCard from "../../Components/Ui/StaticsCard/StaticCard";
import StatisticsCard from "../../Components/Ui/StaticsCard/StaticCard";
import { FaRedRiver } from "react-icons/fa";
import { useTranslation } from "react-i18next";
// import { ChartTypeEnum } from "../../enums/ChartTypeEnum";
// import { Card, Col, Row } from "reactstrap";
// import { AiOutlineUser } from 'react-icons/ai';
// import { useGetStatistics } from "../../api/statistics";
// import { BsCart3 } from "react-icons/bs";
// import YearChart from "./YearChart";
// import HighDriverRateTable from "./tables/HighDriverRate/HighDriverRateTable";
// import LastOrderTable from "./tables/LastOrder/LastOrderTable";
// import LoadingSpinner from "../../Components/Ui/LoadingSpinner";
// import LoadingPage from "../../Layout/app/LoadingPage";
import { Card, Col, Row } from "reactstrap";
import { AiOutlineUser } from 'react-icons/ai';
import { BsCart3 } from "react-icons/bs";
import Chart from "./Chart";
export default function HomePage() {
const { t } = useTranslation()
// const { data: statistics, isLoading } = useGetStatistics({
// order_daily_date: null
// });
// if(isLoading){
// return <LoadingPage/>
// }
const { t } = useTranslation();
const cardsData = [
{
icon: <FaRedRiver className="warning" size={24} />,
count: 20, // Example count
pathWhenClick: "products",
titleKey: "products",
contentKey: "Product_in_your_Application"
},
{
icon: <AiOutlineUser className="warning" size={24} />,
count: 20, // Example count
pathWhenClick: "categories",
titleKey: "categories",
contentKey: "categories_in_your_Application"
},
{
icon: <BsCart3 className="warning" size={24} />,
count: 20, // Example count
pathWhenClick: "order",
titleKey: "order",
contentKey: "Order_in_your_Application"
}
];
return (
<>
Home Page
{/* <Row xs={1} sm={1} md={1} lg={3} xl={3} >
<Col style={{ padding: "0.5rem" }}>
<Row xs={1} sm={1} md={1} lg={3} xl={3}>
{cardsData.map((card, index) => (
<Col key={index} style={{ padding: "0.5rem" }}>
<div style={{ cursor: "pointer" }}>
<StatisticsCard
pathWhenClick="Drivers"
icon={<FaRedRiver className="warning" size={24} />}
count={`${(statistics?.drivers_count) ?? 1}`}
CardContent={t(`You_have`) + " " + ((statistics?.drivers_count) ?? 1) + " " + t(`Driver_in_your_Application`)}
CardTitle={t("drivers")}
/>
</div>
</Col>
<Col style={{ padding: "0.5rem" }}>
<div style={{ cursor: "pointer" }}>
<StatisticsCard
icon={<AiOutlineUser className="warning" size={24} />}
count={`${(statistics?.users_count) ?? 1}`}
CardContent={t(`You_have`) + " " + ((statistics?.users_count) ?? 1) + " " + t(`User_in_your_Application`)}
pathWhenClick="customers"
CardTitle={t("users")}
/>
</div>
</Col>
<Col style={{ padding: "0.5rem" }}>
<div style={{ cursor: "pointer" }} >
<StatisticsCard
icon={<BsCart3 className="warning" size={24} />}
count={`${(statistics?.orders_count) ?? 1}`}
CardContent={t(`You_have`) + " " + ((statistics?.orders_count) ?? 1) + " " + t(`Order_in_your_Application`)}
pathWhenClick="order"
CardTitle={t('order')}
pathWhenClick={card.pathWhenClick}
icon={card.icon}
count={`${card.count ?? 1}`}
CardContent={t(`You_have`) + " " + ((card.count) ?? 1) + " " + t(card.contentKey)}
CardTitle={t(card.titleKey)}
/>
</div>
</Col>
))}
</Row>
<Row xs={1} sm={1} md={1} lg={2} xl={2} style={{margin:"30px 0 "}}>
<Col>
<LastOrderTable latest_Orders={statistics?.latest_orders}/>
</Col>
<Col>
<HighDriverRateTable most_driver_rate={statistics?.most_driver_rate}/>
</Col>
</Row> */}
{/* <Card>
<YearChart/>
</Card> */}
<Row xs={1} sm={1} md={1} lg={2} xl={2} style={{ margin: "30px 0 " }}>
<Chart />
</Row>
</>
);
}

View File

@ -12,12 +12,13 @@ const useTableColumns = () => {
() => [
{
name: t("driver_name"),
name: t("order_code"),
sortable: false,
center: true,
cell:(row:any)=>{
console.log(row);
return (row?.driver?.full_name)
return (row?.order_code)
}
},
@ -26,15 +27,14 @@ const useTableColumns = () => {
name: t("order_total"),
sortable: true,
center: true,
selector:"average_cost"
cell:(row)=>row["order_total"]
},
{
name: t("order_status"),
sortable: false,
center: true,
cell:(row:any)=><span style={{
backgroundColor:row.status==='accepted'||row.status==='complete'||row.status==='pick_up'?"#28c76f":row.status==='pending'?'#b8c2cc':"#ff0000"
,padding:8, color:'white',borderRadius:10,fontSize:10}}>{t(row.status)}</span>
padding:8, color:'black',borderRadius:10,fontSize:10}}>{t(row.order_status)}</span>
},
{

View File

@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next';
import { LoadingButton } from '../../../../Components/Ui/LoadingButton';
import { useNavigate } from 'react-router-dom';
export default function HighDriverRateTable({ most_driver_rate }:any) {
export default function LastUserTable({ most_driver_rate }:any) {
const columns = useTableColumns();
const {t} = useTranslation();
const navigate = useNavigate()
@ -16,11 +16,11 @@ import { useNavigate } from 'react-router-dom';
return (
<Card>
<div className='primary' style={{display:"flex" , justifyContent:"space-between" , padding:"20px", marginTop:"10px"}}>
{t("high_drivers_rate")}
{t("last_users")}
<LoadingButton color="primary" onClick={() => navigate("/Drivers" , {replace:true})}>
{t("show_all_driver")}
</LoadingButton>
{/* <LoadingButton color="primary" onClick={() => navigate("/user" , {replace:true})}>
{t("show_all_users")}
</LoadingButton> */}
</div>
<CardBody>

View File

@ -7,9 +7,10 @@ import { Rating } from "react-simple-star-rating";
interface RowData {
driver_name: string;
rate: number;
driver_id: string;
name:string ,
phone:string ,
email:string ,
id:number
}
const useTableColumns = () => {
@ -22,29 +23,33 @@ const useTableColumns = () => {
name: t("full_name"),
sortable: false,
center: true,
cell: (row: RowData) => row?.driver_name,
cell: (row: RowData) => row?.name,
},
{
name: t("rate"),
name: t("email"),
sortable: false,
center: true,
cell: (row: RowData) => (
<Rating initialValue={row?.rate} size={23} readonly={true} />
),
cell: (row: RowData) => row?.email,
},
{
name: "#",
selector: "action",
name: t("phone"),
sortable: false,
center: true,
cell: (row: RowData) => (
<GrView
onClick={() => navigate(`/information/driver/${row?.driver_id}`, {replace:true})}
size={22}
style={{ cursor: "pointer" }}
/>
),
cell: (row: RowData) => row?.phone,
},
// {
// name: "#",
// selector: "action",
// sortable: false,
// center: true,
// cell: (row: RowData) => (
// <GrView
// onClick={() => navigate(`/user/${row?.id}`, {replace:true})}
// size={22}
// style={{ cursor: "pointer" }}
// />
// ),
// },
];
return columns;

View File

@ -14,21 +14,22 @@ function ProductsPage() {
const column =useTableColumns()
const {data ,status } = useGetProduct()
console.log(data);
const [t] = useTranslation()
const navigate = useNavigate()
const totalRows = data?.meta?.total;
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}
data={data?.BaseProducts}
isLoading={false}
columns={column}
total={totalRows }
is_pagination={true}
/>

View File

@ -1,62 +1,102 @@
import React, { useEffect, useState } from 'react'
import { getInitialValues, getValidationSchema, getDataToSend } from '../formUtil'
import {getInitialValuesAdd as 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 { useAddProduct, useAddProductVariation } from '../../../api/product';
import VarianInfo from './VarianInfo';
import { usePageState } from '../../../lib/state mangment/LayoutPagestate';
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 { mutate, isLoading , data, isSuccess } = useAddProduct()
const { mutate:AddVariation, isSuccess:SuccessVariation } = useAddProductVariation()
const [IsValed, setIsValed] = useState(false)
const [infotaps , setInfoTaps] = useState<any[]>([])
const [Varibletaps , setVaribleTaps] = useState<any[]>([])
const handleSubmit = (values: any) => {
setInfoTaps(values?.info?.slice(1)?.map((taps:any) => {
return (changeShapeInfo(taps));
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);
setVaribleTaps(values?.variable?.slice(1))
mutate({
name:{
en:values?.name_en,
ar:values?.name_ar,
de:values?.name_de
},
category_id:1
})
}
useEffect(()=>{
if(isSuccess){
const baseProductId = (data as any )?.id ;
console.log(infotaps);
console.log(Varibletaps);
Varibletaps?.map((dataToSend:any , index:number)=>{
const varible = dataToSend
const info = infotaps[index]
const jsonString = JSON.stringify(info);
const Newproduct = {
name:{
en:varible?.name_en,
ar:varible?.name_ar,
de:varible?.name_de
},
description:{
en:varible?.description_en,
ar:varible?.description_ar,
de:varible?.description_de
},
quantity:varible?.quantity,
main_photo:varible?.main_photo,
images:varible?.images,
info : jsonString,
product_attributes:[
{attribute_value_id: 1, attribute_id: 1}
],
base_product_id:baseProductId
}
console.log(Newproduct);
AddVariation(Newproduct)
})
}
},[isSuccess])
const { setObjectToEdit, objectToEdit } = usePageState()
useEffect(() => {
setObjectToEdit([]);
}, []);
const { t } = useTranslation();
useNavigateOnSuccess(isSuccess, '/products')
useNavigateOnSuccess(SuccessVariation, '/products')
const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit, BarStatus };
const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit };
return (
@ -65,14 +105,14 @@ const AddProductPage = () => {
<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><div className='SignleDriverContainer'><span className='SignleDriverInfoIcon'><MdLanguage size={20} /></span> <h6 className='SingleInfo'>{t("Base_info")} </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>
<Tab ><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>
<div className=" mt-4"><BasicInfo setIsValed={setIsValed} IsValed={IsValed} /></div>
</TabBody>
<TabBody >
<div className=" mt-4"><VarianInfo /></div>
@ -89,3 +129,19 @@ const AddProductPage = () => {
}
export default AddProductPage
function changeShapeInfo(originalObject:any){
const transformedObject:any = {};
for (const key in originalObject) {
if (originalObject.hasOwnProperty(key)) {
const index = key.split('.')[0]; // Extract index from key
const attribute = key.split('.')[1]; // Extract attribute from key
transformedObject[originalObject[`${index}.key`]] = originalObject[`${index}.Description`];
}
}
return transformedObject
}

View File

@ -0,0 +1,37 @@
import React from 'react';
import { useFormikContext } from 'formik';
import ValidationField from '../../../Components/ValidationField/ValidationField';
import { useGetSingleAttribute } from '../../../api/attribute';
const Attribute = () => {
const { values, setFieldValue } = useFormikContext<any>();
const { data: attributeData } = useGetSingleAttribute({ name: "product_id", id: values?.id });
const handleAttributeChange = (name: string, value: any) => {
setFieldValue(`attribute[${name}]`, value);
};
return (
attributeData?.data?.map((item: any) => {
const options = item?.attribute_value?.map((attr: any) => ({
label: attr?.value,
value: attr?.attribute_id
}));
return (
<ValidationField
key={item?.name}
name={`attribute[${item?.name}]`}
label={item?.name}
placeholder={item?.name}
type="Select"
option={options}
onChange={(value: any) => handleAttributeChange(item?.name, value)}
/>
);
})
);
};
export default Attribute;

View File

@ -1,35 +1,41 @@
import React, { useEffect, useState } from 'react'
import { Col, Row } from 'reactstrap'
import KarimField from '../../../Components/Karimalden/KarimField'
import ValidationField from '../../../Components/ValidationField/ValidationField'
import { useTranslation } from 'react-i18next';
import { useFormikContext } from 'formik';
import { toast } from 'react-toastify';
import { useGetCategories } from '../../../api/Categories';
import useFormatToSelect from '../../../Hooks/useFormatToSelect';
const BasicInfo = ({setIsValed}:any) => {
const BasicInfo = ({ setIsValed, IsValed }: any) => {
const [t] = useTranslation();
const formikContext = useFormikContext();
const { values, submitForm,isValid } = formikContext;
const { values, isValid } = formikContext;
const { data } = useGetCategories()
const SelectData = useFormatToSelect(data?.categories)
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');
const { name_ar, name_en, name_de, main_photo, category_id } = values as any;
if (name_ar && name_en && name_de && main_photo && category_id && IsValed === false) {
toast.success(t("view_information_filed_fill_sucsessfully"));
setIsValed(true)
} else {
console.log(values);
// console.log(isValid, "isValid");
}
}, [isValid,values]);
}, [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" />
<ValidationField name="name_ar" />
<ValidationField name="name_en" />
</Col>
<Col>
<KarimField name="main_photo" type='File' />
<KarimField name="category_id" type='Select' option={[]} label='category' placeholder='category' />
<ValidationField name="name_de" />
{/* <ValidationField name="main_photo" type='File' /> */}
<ValidationField name="category_id" type="Search" label='category' placeholder='category' option={SelectData} searchBy={"name"} />
</Col>

View File

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

View File

@ -0,0 +1,45 @@
import React, { useEffect, useState } from 'react'
import { Col, Row } from 'reactstrap'
import ValidationField from '../../../../Components/ValidationField/ValidationField'
import { useTranslation } from 'react-i18next';
import { useFormikContext } from 'formik';
import { toast } from 'react-toastify';
import { useGetCategories } from '../../../../api/Categories';
import useFormatToSelect from '../../../../Hooks/useFormatToSelect';
const BasicInfo = ({ setIsValed, IsValed }: any) => {
const [t] = useTranslation();
const formikContext = useFormikContext();
const { values, isValid } = formikContext;
const { data } = useGetCategories()
const SelectData = useFormatToSelect(data?.categories)
useEffect(() => {
const { name_ar, name_en, name_de, main_photo, category_id } = values as any;
if (name_ar && name_en && name_de && main_photo && category_id && IsValed === false) {
toast.success(t("view_information_filed_fill_sucsessfully"));
setIsValed(true)
} else {
// console.log(isValid, "isValid");
}
}, [values]);
return (
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
<Col>
<ValidationField name="name" />
</Col>
<Col>
<ValidationField name="category_id" type="Search" label='category' placeholder='category' option={SelectData} searchBy={"name"} />
</Col>
</Row>
)
}
export default BasicInfo

View File

@ -0,0 +1,36 @@
import React from 'react';
import { useFormikContext } from 'formik';
import ValidationField from '../../../../../../Components/ValidationField/ValidationField';
import { useGetSingleAttribute } from '../../../../../../api/attribute';
const Attribute = ({data,tabKey}:any) => {
const { values, setFieldValue } = useFormikContext<any>();
const handleAttributeChange = (name: string, value: any) => {
setFieldValue(`variable.${tabKey}.attribute"[${name}]`, value);
};
return (
data?.map((item: any) => {
const options = item?.attribute_value?.map((attr: any) => ({
label: attr?.value,
value: attr?.attribute_id
}));
return (
<ValidationField
key={item?.name}
name={`attribute[${item?.name}]`}
label={item?.name}
placeholder={item?.name}
type="Select"
option={options}
onChange={(value: any) => handleAttributeChange(item?.name, value)}
/>
);
})
);
};
export default Attribute;

View File

@ -0,0 +1,64 @@
import { Button, Upload, UploadFile } from 'antd'
import { UploadOutlined } from '@ant-design/icons';
import { useTranslation } from 'react-i18next';
import { useFormikContext } from 'formik';
import { ImageBaseURL } from '../../../../../../api/config';
const File = ({ tabKey}:any) => {
const { t } = useTranslation();
const formik = useFormikContext<any>();
const name = `variable[${tabKey}].${"main_photo"}`;
const imageUrl = formik?.values?.variable[tabKey]?.main_photo ? ImageBaseURL + formik?.values?.variable[tabKey]?.main_photo : "" ;
console.log(imageUrl);
const fileList: UploadFile[] = [
{
uid: '-1',
name: formik?.values?.variable[tabKey]?.main_photo?.name ?? "",
status: 'done',
url: imageUrl ,
thumbUrl: imageUrl ,
}
];
const FilehandleChange = (value:any) => {
formik.setFieldValue(name, value.file.originFileObj)
};
const customRequest = async ({ onSuccess}: any) => {
onSuccess();
};
return (
<div className="ValidationField">
<label className="text">
{t(`main_photo`)}
</label>
<Upload
listType="picture"
maxCount={1}
defaultFileList={[...fileList]}
onChange={ FilehandleChange}
customRequest={customRequest}
className={`w-100`}
>
<Button className={" w-100"} icon={<UploadOutlined />}>
{ t("upload_image") }
</Button>
</Upload>
</div>
)
}
export default File

View File

@ -0,0 +1,50 @@
import { Button, Upload, UploadFile } from 'antd';
import { UploadOutlined } from '@ant-design/icons';
import { useTranslation } from 'react-i18next';
import { useFormikContext } from 'formik';
import { BaseURL_IMAGE } from '../../../../../../api/config';
const MaltyFile = ({ tabKey }: any) => {
const { t } = useTranslation();
const formik = useFormikContext<any>();
const name = `variable[${tabKey}].Images`;
const imageFiles = formik.values?.variable?.[tabKey]?.Images || [];
const defaultFileList = imageFiles?.map((file: any, index: number) => ({
uid: `-${index}`,
name: file?.name, // Use a default name if file.name is undefined
status: 'done',
url: file ? URL.createObjectURL(file) : BaseURL_IMAGE + file?.path?.replace("public", "/storage"),
thumbUrl: file ?URL.createObjectURL(file):"",
}));
const FilehandleChange = ({ fileList }: { fileList: any }) => {
formik.setFieldValue(name, fileList.map((file: any) => file?.originFileObj));
};
const customRequest = async ({ onSuccess }: any) => {
onSuccess();
};
return (
<div className="ValidationField">
<label className="text">
{t(`Images`)}
</label>
<Upload
listType="picture"
defaultFileList={defaultFileList}
onChange={FilehandleChange}
customRequest={customRequest}
className="w-100"
maxCount={10}
>
<Button className="w-100" icon={<UploadOutlined />}>
{t("upload_image")}
</Button>
</Upload>
</div>
);
}
export default MaltyFile;

View File

@ -2,27 +2,51 @@ 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';
import { objectToArray } from '../../../../../../utils/Array/ArrayToObjectFormik';
import { useTranslation } from 'react-i18next';
const ObjectField = ({value , onChange}:any) => {
const ObjectField = ({tabKey}:any) => {
const [form] = Form.useForm();
const formik = useFormikContext<any>();
const [FieldItems, setFieldItems] = useState<any>([])
const [FieldItems, setFieldItems] = useState<any>(formik?.values?.info[tabKey])
const [t] = useTranslation()
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFieldItems((prevState:any) => ({
setFieldItems((prevState:any) =>{
formik.setFieldValue(`info.${tabKey}`, {
...prevState,
[name]: value
}));
formik.setFieldValue("info", FieldItems)
});
return ({
...prevState,
[name]: value
})
} )
};
useEffect(() => {
form.setFieldsValue({
items: [{ list: [{ Attribute: '', Description: '' }] }]
});
}, []);
useEffect(() => {
if (formik?.values?.info[tabKey]) {
const defaultValues = formik.values.info[tabKey];
console.log(objectToArray( formik?.values?.info[tabKey]));
form.setFieldsValue({
items: [{ list:objectToArray( formik?.values?.info[tabKey])}]
});
}
else {
form.setFieldsValue({
items: [{ list: [{ key: '', Description: '' }] },], });
}
}, []); // Update when tabKey or info[tabKey] changes
return (
<Form
@ -39,7 +63,7 @@ const ObjectField = ({value , onChange}:any) => {
{fields.map((field, index) => (
<div key={field.key}>
<Typography.Text strong style={{ marginBottom: 8 }}>
Information
{t("Information")}
</Typography.Text>
{/* Nested Form.List for sub-items */}
@ -49,17 +73,17 @@ const ObjectField = ({value , onChange}:any) => {
<div style={{ display: 'flex', flexDirection: 'column', rowGap: 16 }}>
{subFields.map((subField) => (
<Space key={subField.key}>
<Form.Item noStyle name={[subField.name, 'Attribute']}>
<Form.Item noStyle name={[subField.name, 'key']}>
<Input
placeholder="Attribute"
placeholder={t("key")}
onChange={handleChange} // Assign onChange handler
name={`${subField.name}.Attribute`} // Ensure proper name for dynamic state update
name={`${subField.name}.key`} // Ensure proper name for dynamic state update
/>
</Form.Item>
<Form.Item noStyle name={[subField.name, 'Description']}>
<Input
placeholder="Description"
placeholder={t("Description")}
onChange={handleChange} // Assign onChange handler
name={`${subField.name}.Description`} // Ensure proper name for dynamic state update
@ -74,7 +98,7 @@ const ObjectField = ({value , onChange}:any) => {
</Space>
))}
<Button type="dashed" onClick={() => subOpt.add()} block>
+ Add Another Item
{t("+ Add Another Item")}
</Button>
</div>
)}
@ -89,4 +113,4 @@ const ObjectField = ({value , onChange}:any) => {
);
};
export default ObjectField;
export default ObjectField

View File

@ -0,0 +1,41 @@
import { Form, Select } from 'antd'
import { useFormikContext } from 'formik';
import React from 'react'
import { useTranslation } from 'react-i18next';
const SelectField = ({tabKey,option,name,id,label}: any) => {
const { t } = useTranslation();
const formik = useFormikContext<any>();
const Formikname = `variable[${tabKey}].${name}`;
const FormikValue = formik?.values?.variable[tabKey]?.[name];
const onChange = (value:any) => {
formik.setFieldValue(Formikname,[ {
attribute_value_id:value,
attribute_id:id
}])
console.log(value);
};
return (
<div className='ValidationField'>
<label className="text">
{t(`${label}`)}
</label>
<Select
placeholder={t(`${label}`)}
options={option}
size="large"
className={` w-100`}
defaultValue={FormikValue?.attribute_value_id}
allowClear
onChange={onChange}
/>
</div>
)
}
export default React.memo(SelectField);

View File

@ -0,0 +1,20 @@
// FormItem.tsx
import React from 'react';
import { Input, Label } from 'reactstrap';
interface FormItemProps {
label: string;
value: string;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
type?: any
}
export const FormItem: React.FC<FormItemProps> = ({ label, value, onChange ,type = "text"}) => {
return (
<>
<Label className="tabstext">{label}</Label>
<Input value={value} type={type} onChange={onChange} />
</>
);
};

View File

@ -0,0 +1,13 @@
// TabItem.tsx
import React from 'react';
import { VariableTabs } from './VariableTabs';
interface TabItemProps {
UKey: any;
}
export const TabItem: React.FC<TabItemProps> = ({ UKey }) => {
return (
<VariableTabs tabKey={UKey} />
);
};

View File

@ -0,0 +1,115 @@
// TabsContainer.tsx
import React, { useEffect, useState } from 'react';
import { Tabs, Space } from 'antd';
import { CopyOutlined } from '@ant-design/icons';
import { TabItem } from './TabItem';
import { toast } from 'react-toastify';
import { FormikContext, FormikValues, useFormikContext } from 'formik';
import { useTranslation } from 'react-i18next';
const { TabPane } = Tabs;
const initialItemShape: any = {
label: 'variable 1',
key: '1',
closable: true,
};
export const TabsContainer: React.FC = () => {
const [activeKey, setActiveKey] = useState('1');
// const [items, setItems] = useState<TabItem[]>([]);
const [t] = useTranslation()
const { setFieldValue } = useFormikContext();
const formikContext = useFormikContext<FormikValues>();
const { values, handleChange } = formikContext;
const [width, setWidth] = useState(window.innerWidth);
const varianCount = values?.variable?.slice(1).map((item:any,index:any)=>{
return {
label: `${t(`variable`)}`+ `${index+1}`,
key: index+1,
closable: true,
}
})
const [items, setItems] = useState(varianCount);
const handleAdd = () => {
const newKey = `${items.length + 1}`;
setItems([...items, { key: newKey, label: `variable ${newKey}`, closable: true }]);
setActiveKey(newKey);
};
const handleDuplicate = (targetKey: string) => {
const targetItem = items.find((item:any) => item.key === targetKey);
if (targetItem) {
const newKey = `${items.length + 1}`;
const newItem = { ...targetItem, key: newKey, label: `variable ${newKey}` };
setItems([...items, newItem]);
setActiveKey(newKey);
const originalValues = values?.variable?.[targetKey];
setFieldValue(`variable.${newKey}`, originalValues);
const originalInfo = values?.info?.[targetKey];
console.log(originalInfo);
setFieldValue(`info.${newKey}`, originalInfo);
}
};
const handleRemove = (targetKey: string) => {
const newItems = items.filter((item:any) => item.key !== targetKey);
if (newItems.length > 0) {
const newActiveKey = newItems.length ? newItems[newItems.length - 1].key : '1';
setItems(newItems);
setActiveKey(newActiveKey);
} else {
toast.error("Cannot close the last tab");
}
};
useEffect(() => {
const handleResize = () => {
setWidth(window.innerWidth);
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
const tabPosition = width > 1000 ? 'left' : 'top';
return (
<Tabs
type="editable-card"
onChange={setActiveKey}
activeKey={activeKey}
onEdit={(targetKey:any, action) => (action === 'add' ? handleAdd() : handleRemove(targetKey))}
tabPosition={tabPosition}
>
{items.map((item :any) =>{
return (
<TabPane
key={item?.key}
tab={
<Space>
{t(`${item.label}`)}
<CopyOutlined onClick={() => handleDuplicate(item.key)} />
</Space>
}
closable={item.closable}
>
<TabItem UKey={item?.key} />
</TabPane>
)
})}
</Tabs>
);
};

View File

@ -0,0 +1,71 @@
// VariableTabs.tsx
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Col, Row } from 'reactstrap';
import { FormItem } from './FormItem';
import { useFormikContext, FormikValues } from 'formik';
import File from './Field/File';
import MaltyFile from './Field/FileImage';
import Select from './Field/Select';
import ObjectField from './Field/Object';
import Atteibute from './Field/Atteibute';
interface VariableTabsProps {
tabKey: string;
}
export const VariableTabs: React.FC<VariableTabsProps> = ({ tabKey }) => {
const { t } = useTranslation();
const formikContext = useFormikContext<FormikValues>();
const { values, handleChange } = formikContext;
const handleFieldChange = (fieldName: string) => (
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement> | any
) => {
handleChange(`variable.${tabKey}.${fieldName}`)(e); // Prepend "variable"
};
const FormikName = (FormikFieldname: any) => values?.variable?.[tabKey]?.[FormikFieldname];
return (
<>
<h5>variables {tabKey}</h5>
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
<Col>
<FormItem
label={t(`name`)}
value={FormikName("name")}
onChange={handleFieldChange('name')}
/>
{values?.other_attributes &&
<Atteibute data={values?.other_attributes} tabKey={tabKey} />
}
<ObjectField tabKey={tabKey}/>
</Col>
<Col>
<FormItem
label={t(`description`)}
value={FormikName("description")}
onChange={handleFieldChange('description')}
/>
<FormItem
label={t(`price`)}
value={FormikName("price")}
onChange={handleFieldChange('price')}
type="number"
/>
<File tabKey={tabKey}/>
<MaltyFile tabKey={tabKey}/>
</Col>
</Row>
</>
);
};

View File

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

View File

@ -0,0 +1,137 @@
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 ViewPage from '../../../Layout/Dashboard/ViewPage';
import BasicInfo from './Edit/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, useUpdateProductVariation } from '../../../api/product';
import useNavigateOnSuccess from '../../../Hooks/useNavigateOnSuccess';
import VarianInfo from './Edit/VarianInfo';
import { changeShapeInfo } from '../../../utils/Array/changeShapeInfo';
import { Spin } from 'antd';
const ViewProduct = () => {
const { setObjectToEdit, objectToEdit } = usePageState()
const { t } = useTranslation();
const { id } = useParams()
const { data , isLoading,isRefetching} = useGetOneProduct({ id: id })
const [Data, setData] = useState([])
const { mutate, isSuccess } = useUpdateProduct()
const { mutate: AddVariation, isSuccess: SuccessVariation } = useUpdateProductVariation()
const [infotaps, setInfoTaps] = useState<any[]>([])
const [Varibletaps, setVaribleTaps] = useState<any[]>([])
const handleSubmit = (values: any) => {
setInfoTaps(values?.info?.slice(1)?.map((taps: any) => {
return (changeShapeInfo(taps));
}));
setVaribleTaps(values?.variable?.slice(1))
mutate({
name: values?.name,
category_id: 1
})
}
useEffect(() => {
if (isSuccess) {
const baseProductId = (data as any)?.id;
console.log(infotaps);
console.log(Varibletaps);
Varibletaps?.map((dataToSend: any, index: number) => {
const varible = dataToSend
const info = infotaps[index]
const jsonString = JSON.stringify(info);
const Newproduct : any = {
name: varible?.name,
description: varible?.description,
quantity: varible?.quantity,
info: jsonString,
product_attributes: [
{ attribute_value_id: 1, attribute_id: 1 }
],
base_product_id: baseProductId
}
if (data?.product?.images !== varible?.images) {
Newproduct.images = varible?.images ;
}
if (data?.product?.main_photo?.replace("public", "/storage") !== varible?.main_photo) {
Newproduct.main_photo = varible?.main_photo ;
console.log(data?.product?.main_photo?.replace("public", "/storage"));
}
AddVariation(Newproduct)
})
}
}, [isSuccess])
useNavigateOnSuccess(isSuccess, '/products')
useEffect(() => {
// refetch()
setObjectToEdit(data?.data);
}, [data,id,isRefetching]);
const getValidationSchema = () => {
return null
};
if(isRefetching){
return <Spin/>
}
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("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"> {isLoading ? <Spin/> : <VarianInfo />} </div>
</TabBody>
</Tabs>
</ViewPage>
: <LoadingPage />}
</div>
)
}
export default ViewProduct

View File

@ -0,0 +1,37 @@
import React from 'react';
import { useFormikContext } from 'formik';
import ValidationField from '../../../../../Components/ValidationField/ValidationField';
import { useGetSingleAttribute } from '../../../../../api/attribute';
const Attribute = ({tabKey}:any) => {
const { values, setFieldValue } = useFormikContext<any>();
const { data: attributeData } = useGetSingleAttribute({ name: "category_id", id: values?.category_id });
const handleAttributeChange = (name: string, value: any) => {
setFieldValue(`variable.${tabKey}.attribute"[${name}]`, value);
};
return (
attributeData?.data?.map((item: any) => {
const options = item?.attribute_value?.map((attr: any) => ({
label: attr?.value,
value: attr?.attribute_id
}));
return (
<ValidationField
key={item?.name}
name={`attribute[${item?.name}]`}
label={item?.name}
placeholder={item?.name}
type="Select"
option={options}
onChange={(value: any) => handleAttributeChange(item?.name, value)}
/>
);
})
);
};
export default Attribute;

View File

@ -0,0 +1,63 @@
import { Button, Upload, UploadFile } from 'antd'
import { UploadOutlined } from '@ant-design/icons';
import { useTranslation } from 'react-i18next';
import { useFormikContext } from 'formik';
import { ImageBaseURL } from '../../../../../api/config';
const File = ({ tabKey}:any) => {
const { t } = useTranslation();
const formik = useFormikContext<any>();
const name = `variable[${tabKey}].${"main_photo"}`;
const imageUrl = formik?.values?.variable[tabKey]?.main_photo ? URL.createObjectURL(formik?.values?.variable[tabKey]?.main_photo) : "" ;
const fileList: UploadFile[] = [
{
uid: '-1',
name: formik?.values?.variable[tabKey]?.main_photo?.name ?? "",
status: 'done',
url: imageUrl ,
thumbUrl: imageUrl ,
}
];
const FilehandleChange = (value:any) => {
formik.setFieldValue(name, value.file.originFileObj)
};
const customRequest = async ({ onSuccess}: any) => {
onSuccess();
};
return (
<div className="ValidationField">
<label className="text">
{t(`main_photo`)}
</label>
<Upload
listType="picture"
maxCount={1}
defaultFileList={[...fileList]}
onChange={ FilehandleChange}
customRequest={customRequest}
className={`w-100`}
>
<Button className={" w-100"} icon={<UploadOutlined />}>
{ t("upload_image") }
</Button>
</Upload>
</div>
)
}
export default File

View File

@ -0,0 +1,49 @@
import { Button, Upload, UploadFile } from 'antd';
import { UploadOutlined } from '@ant-design/icons';
import { useTranslation } from 'react-i18next';
import { useFormikContext } from 'formik';
const MaltyFile = ({ tabKey }: any) => {
const { t } = useTranslation();
const formik = useFormikContext<any>();
const name = `variable[${tabKey}].images`;
const imageFiles = formik.values?.variable?.[tabKey]?.images || [];
const defaultFileList = imageFiles?.map((file: any, index: number) => ({
uid: `-${index}`,
name: file?.name, // Use a default name if file.name is undefined
status: 'done',
url: file ? URL?.createObjectURL(file) : "",
thumbUrl: file ?URL?.createObjectURL(file):"",
}));
const FilehandleChange = ({ fileList }: { fileList: any }) => {
formik.setFieldValue(name, fileList.map((file: any) => file?.originFileObj));
};
const customRequest = async ({ onSuccess }: any) => {
onSuccess();
};
return (
<div className="ValidationField">
<label className="text">
{t(`images`)}
</label>
<Upload
listType="picture"
defaultFileList={defaultFileList}
onChange={FilehandleChange}
customRequest={customRequest}
className="w-100"
maxCount={10}
>
<Button className="w-100" icon={<UploadOutlined />}>
{t("upload_image")}
</Button>
</Upload>
</div>
);
}
export default MaltyFile;

View File

@ -0,0 +1,116 @@
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';
import { objectToArray } from '../../../../../utils/Array/ArrayToObjectFormik';
import { useTranslation } from 'react-i18next';
const ObjectField = ({tabKey}:any) => {
const [form] = Form.useForm();
const formik = useFormikContext<any>();
const [FieldItems, setFieldItems] = useState<any>(formik?.values?.info[tabKey])
const [t] = useTranslation()
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFieldItems((prevState:any) =>{
formik.setFieldValue(`info.${tabKey}`, {
...prevState,
[name]: value
});
return ({
...prevState,
[name]: value
})
} )
};
useEffect(() => {
if (formik?.values?.info[tabKey]) {
const defaultValues = formik.values.info[tabKey];
console.log(objectToArray( formik?.values?.info[tabKey]));
form.setFieldsValue({
items: [{ list:objectToArray( formik?.values?.info[tabKey])}]
});
}
else {
form.setFieldsValue({
items: [{ list: [{ key: '', Description: '' }] },], });
}
}, []); // Update when tabKey or info[tabKey] changes
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 }}>
{t("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, 'key']}>
<Input
placeholder={t("key")}
onChange={handleChange} // Assign onChange handler
name={`${subField.name}.key`} // Ensure proper name for dynamic state update
/>
</Form.Item>
<Form.Item noStyle name={[subField.name, 'Description']}>
<Input
placeholder={t("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>
{t("+ Add Another Item")}
</Button>
</div>
)}
</Form.List>
</Form.Item>
</div>
))}
</div>
)}
</Form.List>
</Form>
);
};
export default ObjectField

View File

@ -0,0 +1,60 @@
import React from 'react';
import { Select } from 'antd';
import { useTranslation } from 'react-i18next';
interface SearchTabsProps {
value: string;
onChange:any
name:any
}
const SearchTabs: React.FC<SearchTabsProps> = ({ value, onChange ,name}) =>{
const [t] = useTranslation()
return (
<div className='SellectTab'>
<label className="tabstext"> {t(`${name}`)} </label>
<Select
showSearch
style={{ width: "100%" }}
placeholder="Search to Select"
optionFilterProp="children"
filterOption={(input, option) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase())}
filterSort={(optionA, optionB) =>
(optionA?.label ?? '').toLowerCase().localeCompare((optionB?.label ?? '').toLowerCase())
}
onChange={onChange}
value={value}
options={[
{
value: '1',
label: 'label 1',
},
{
value: '2',
label: 'label 2',
},
{
value: '3',
label: 'label 3',
},
{
value: '4',
label: 'label 4',
},
{
value: '5',
label: 'label 5',
},
{
value: '6',
label: 'label 6',
},
]}
/>
</div>
);
}
export default SearchTabs;

View File

@ -0,0 +1,41 @@
import { Form, Select } from 'antd'
import { useFormikContext } from 'formik';
import React from 'react'
import { useTranslation } from 'react-i18next';
const SelectField = ({tabKey,option,name,id,label}: any) => {
const { t } = useTranslation();
const formik = useFormikContext<any>();
const Formikname = `variable[${tabKey}].${name}`;
const FormikValue = formik?.values?.variable[tabKey]?.[name];
const onChange = (value:any) => {
formik.setFieldValue(Formikname,[ {
attribute_value_id:value,
attribute_id:id
}])
console.log(value);
};
return (
<div className='ValidationField'>
<label className="text">
{t(`${label}`)}
</label>
<Select
placeholder={t(`${label}`)}
options={option}
size="large"
className={` w-100`}
defaultValue={FormikValue?.attribute_value_id}
allowClear
onChange={onChange}
/>
</div>
)
}
export default React.memo(SelectField);

View File

@ -0,0 +1,19 @@
// FormItem.tsx
import React from 'react';
import { Input, Label } from 'reactstrap';
interface FormItemProps {
label: string;
value: string;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
type?: any
}
export const FormItem: React.FC<FormItemProps> = ({ label, value, onChange ,type = "text"}) => {
return (
<>
<Label className="tabstext">{label}</Label>
<Input value={value} type={type} onChange={onChange} />
</>
);
};

View File

@ -0,0 +1,13 @@
// TabItem.tsx
import React from 'react';
import { VariableTabs } from './VariableTabs';
interface TabItemProps {
UKey: any;
}
export const TabItem: React.FC<TabItemProps> = ({ UKey }) => {
return (
<VariableTabs tabKey={UKey} />
);
};

View File

@ -0,0 +1,104 @@
// TabsContainer.tsx
import React, { useEffect, useState } from 'react';
import { Tabs, Space } from 'antd';
import { CopyOutlined } from '@ant-design/icons';
import { TabItem } from './TabItem';
import { toast } from 'react-toastify';
import { FormikContext, FormikValues, useFormikContext } from 'formik';
import { useTranslation } from 'react-i18next';
const { TabPane } = Tabs;
const initialItemShape: any = {
label: 'variable 1',
key: '1',
closable: true,
};
export const TabsContainer: React.FC = () => {
const [activeKey, setActiveKey] = useState('1');
// const [items, setItems] = useState<TabItem[]>([]);
const [items, setItems] = useState([initialItemShape]);
const { setFieldValue } = useFormikContext();
const formikContext = useFormikContext<FormikValues>();
const { values, handleChange } = formikContext;
const [width, setWidth] = useState(window.innerWidth);
const [t] = useTranslation()
const handleAdd = () => {
const newKey = `${items.length + 1}`;
setItems([...items, { key: newKey, label: `variable ${newKey}`, closable: true }]);
setActiveKey(newKey);
};
const handleDuplicate = (targetKey: string) => {
const targetItem = items.find((item) => item.key === targetKey);
if (targetItem) {
const newKey = `${items.length + 1}`;
const newItem = { ...targetItem, key: newKey, label: `variable ${newKey}` };
setItems([...items, newItem]);
setActiveKey(newKey);
const originalValues = values?.variable?.[targetKey];
setFieldValue(`variable.${newKey}`, originalValues);
const originalInfo = values?.info?.[targetKey];
console.log(originalInfo);
setFieldValue(`info.${newKey}`, originalInfo);
}
};
const handleRemove = (targetKey: string) => {
const newItems = items.filter((item) => item.key !== targetKey);
if (newItems.length > 0) {
const newActiveKey = newItems.length ? newItems[newItems.length - 1].key : '1';
setItems(newItems);
setActiveKey(newActiveKey);
} else {
toast.error("Cannot close the last tab");
}
};
useEffect(() => {
const handleResize = () => {
setWidth(window.innerWidth);
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
const tabPosition = width > 1000 ? 'left' : 'top';
return (
<Tabs
type="editable-card"
onChange={setActiveKey}
activeKey={activeKey}
onEdit={(targetKey:any, action) => (action === 'add' ? handleAdd() : handleRemove(targetKey))}
tabPosition={tabPosition}
>
{items.map((item :any) =>{
return (
<TabPane
key={item?.key}
tab={
<Space>
{t(`${item.label}`)}
<CopyOutlined onClick={() => handleDuplicate(item.key)} />
</Space>
}
closable={item.closable}
>
<TabItem UKey={item?.key} />
</TabPane>
)
})}
</Tabs>
);
};

View File

@ -0,0 +1,130 @@
// VariableTabs.tsx
import React from 'react';
import { Col, Row } from 'reactstrap';
import { FormItem } from './FormItem';
import { useFormikContext, FormikValues } from 'formik';
import File from './Field/File';
import MaltyFile from './Field/FileImage';
import Select from './Field/Select';
import ObjectField from './Field/Object';
import Atteibute from './Field/Atteibute';
import { useTranslation } from 'react-i18next';
interface VariableTabsProps {
tabKey: string;
}
export const VariableTabs: React.FC<VariableTabsProps> = ({ tabKey }) => {
const { t } = useTranslation();
const formikContext = useFormikContext<FormikValues>();
const { values, handleChange } = formikContext;
const handleFieldChange = (fieldName: string) => (
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement> | any
) => {
handleChange(`variable.${tabKey}.${fieldName}`)(e); // Prepend "variable"
};
const FormikName = (FormikFieldname: any) => values?.variable?.[tabKey]?.[FormikFieldname];
const SelectedCategoriesAttribute = [
{
name: "color",
id: 1,
data: [
{
value: 1,
label: "red"
},
{
value: 2,
label: "green"
},
{
value: 3,
label: "blue"
}
]
},
{
name: "size",
id: 1,
data: [
{
value: 1,
label: "xs"
},
{
value: 2,
label: "sm"
},
{
value: 3,
label: "lg"
}
]
}
]
return (
<>
<h5>{t("variables")} {tabKey}</h5>
<Row xs={1} sm={1} md={1} lg={2} xl={2}>
<Col>
<FormItem
label={t(`name_en`)}
value={FormikName("name_en")}
onChange={handleFieldChange('name_en')}
/>
<FormItem
label={t(`name_ar`)}
value={FormikName("name_ar")}
onChange={handleFieldChange('name_ar')}
/>
<FormItem
label={t(`name_de`)}
value={FormikName("name_de")}
onChange={handleFieldChange('name_de')}
/>
<FormItem
label={t(`quantity`)}
value={FormikName("quantity")}
onChange={handleFieldChange('quantity')}
type="number"
/>
{values?.category_id &&
<Atteibute tabKey={tabKey} />
}
<ObjectField tabKey={tabKey} />
</Col>
<Col>
<FormItem
label={t(`description_en`)}
value={FormikName("description_en")}
onChange={handleFieldChange('description_en')}
/>
<FormItem
label={t(`description_ar`)}
value={FormikName("description_ar")}
onChange={handleFieldChange('description_ar')}
/>
<FormItem
label={t(`description_de`)}
value={FormikName("description_de")}
onChange={handleFieldChange('description_de')}
/>
<FormItem
label={t(`price`)}
value={FormikName("price")}
onChange={handleFieldChange('price')}
type="number"
/>
<File tabKey={tabKey} />
<MaltyFile tabKey={tabKey} />
</Col>
</Row>
</>
);
};

View File

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

View File

@ -1,11 +1,11 @@
import React, { useState } from 'react'
import ResposiveTabs from './taps/ResposiveTabs'
import { TabsContainer } from './FormikTab/TabsContainer'
const VarianInfo = () => {
return (
<div className='VarianInfo'>
<ResposiveTabs/>
<TabsContainer/>
</div>
)
}

View File

@ -1,41 +0,0 @@
import React from 'react';
import { UploadOutlined } from '@ant-design/icons';
import { Button, Upload } from 'antd';
import type { UploadFile } from 'antd';
const fileList: UploadFile[] = [
];
const App = ({value, onChange}:any) => {
const FilehandleChange = (data:any) => {
console.log('====================================');
console.log(data?.fileList);
console.log('====================================');
};
const customRequest = async ({ onSuccess}: any) => {
onSuccess();
};
return(
<>
<Upload
action="https://run.mocky.io/v3/435e224c-44fb-4773-9faf-380c5e6a2188"
listType="picture"
maxCount={1}
defaultFileList={[...fileList]}
className="upload-list-inline"
onChange={(data:any)=>onChange(data?.fileList)}
customRequest={customRequest}
>
<Button icon={<UploadOutlined />}>Upload</Button>
</Upload>
</>
);
}
export default App;

View File

@ -1,39 +0,0 @@
import React from 'react';
import { UploadOutlined } from '@ant-design/icons';
import { Button, Upload } from 'antd';
import type { UploadFile } from 'antd';
const fileList: UploadFile[] = [
];
const App = ({value, onChange}:any) => {
const FilehandleChange = (data:any) => {
console.log('====================================');
console.log(data?.fileList);
console.log('====================================');
};
const customRequest = async ({ onSuccess}: any) => {
onSuccess();
};
return(
<>
<Upload
action="https://run.mocky.io/v3/435e224c-44fb-4773-9faf-380c5e6a2188"
listType="picture"
defaultFileList={[...fileList]}
className="upload-list-inline"
onChange={(data:any)=>onChange(data?.fileList)}
customRequest={customRequest}
>
<Button icon={<UploadOutlined />}>Upload</Button>
</Upload>
</>
);
}
export default App;

View File

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

View File

@ -1,232 +0,0 @@
import React, { useEffect, useState } from 'react';
import { Tabs, Space, Input } from 'antd';
import { CopyOutlined } from '@ant-design/icons';
import { useTranslation } from 'react-i18next';
import { Col, Row } from 'reactstrap';
import FileImage from './FileImage';
import SearchTabs from './SearchTabs';
import ObjectField from '../ObjectField';
import { useFormikContext } from 'formik';
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);
};
useEffect(() => {
console.log(inputValues);
}, [inputValues])
const formikContext = useFormikContext();
const { values } :any = formikContext;
useEffect(() => {
setInputValues((prevInputValues:any) => ({
...prevInputValues,
[14]: values?.info,
}));
}, [values?.info])
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}`,
};
// Get the values of the original tab
const originalValues = inputValues[targetKey] || ['', '', '', ''];
const newPanes = [...items, newItem];
setItems(newPanes);
setActiveKey(newActiveKey);
// Update the inputValues state with the original values for the new tab
setInputValues((prevInputValues) => ({
...prevInputValues,
[newActiveKey]: originalValues,
}));
}
};
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);
};
//@ts-ignore
const SelecthandleChange = (index: number) => (Selectvalue:any) => {
const newValues = [...value];
newValues[index] = Selectvalue;
onChange(newValues);
};
const FilehandleChange = (index: number) => (data:any) => {
const newValues = [...value];
newValues[index] = data;
onChange(newValues);
};
const [t] = useTranslation();
return (
<>
<Row>
<Col>
<label className="tabstext"> {t(`Name`)} </label>
<Input value={value[0]} onChange={handleInputChange(0)} />
<label className="tabstext"> {t(`Description`)} </label>
<Input value={value[1]} onChange={handleInputChange(1)} />
<SearchTabs value={value[2]} onChange={SelecthandleChange(2)} name={"color"} />
<label className="tabstext"> {t(`main_photo`)} </label>
<FileImage value={value[13]} onChange={FilehandleChange(13)} />
<label className="tabstext"> {t(`name_en`)} </label>
<Input value={value[6]} onChange={handleInputChange(0)} />
<label className="tabstext"> {t(`name_ar`)} </label>
<Input value={value[7]} onChange={handleInputChange(0)} />
<label className="tabstext"> {t(`name_de`)} </label>
<Input value={value[8]} onChange={handleInputChange(0)} />
<ObjectField key="14" />
</Col>
<Col>
<label className="tabstext"> {t(`Image`)} </label>
<FileImage value={value[3]} onChange={FilehandleChange(3)} />
<SearchTabs value={value[4]} onChange={SelecthandleChange(4)} name={"size"} />
<label className="tabstext"> {t(`Price`)} </label>
<Input value={value[5]} onChange={handleInputChange(5)} />
<label className="tabstext"> {t(`description_en`)} </label>
<Input value={value[9]} onChange={handleInputChange(0)} />
<label className="tabstext"> {t(`description_ar`)} </label>
<Input value={value[10]} onChange={handleInputChange(0)} />
<label className="tabstext"> {t(`description_de`)} </label>
<Input value={value[11]} onChange={handleInputChange(0)} />
<label className="tabstext"> {t(`quantity`)} </label>
<Input value={value[12]} onChange={handleInputChange(0)} />
</Col>
</Row>
</>
);
};
export default App;

View File

@ -1,9 +0,0 @@
import React from 'react'
const VariableTabs = () => {
return (
<div>VariableTabs</div>
)
}
export default VariableTabs

View File

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

View File

@ -1,13 +1,36 @@
import * as Yup from "yup";
import { buildFormData } from "../../api/helper/buildFormData";
import { mapTranslatedProperties } from "../../utils/language/mapTranslatedProperties";
export const getInitialValues = (objectToEdit: any | null = null) => {
const product = Array.isArray(objectToEdit?.product) ? objectToEdit?.product.map((item: any) => ({
name: item?.name,
description: item?.description,
images: item?.images,
main_photo: item?.main_photo,
price: item?.price,
quantity: item?.quantity,
product_attributes: item?.product_attributes,
})) : (
{
...objectToEdit?.product,
main_photo: objectToEdit?.product?.main_photo?.replace("public", "/storage"),
}
);
console.log(product, "product");
const productInfo = objectToEdit?.product?.info || {}; // Ensure product.info exists and initialize as an empty object if it doesn't
const formattedData = Object.entries(productInfo).map(([key, value], index) => ({
[`${index}.Description`]: key,
[`${index}.key`]: value,
}));
const info = [undefined, ...formattedData];
// console.log(objectToEdit);
return {
id: objectToEdit?.id ?? 0,
@ -17,46 +40,47 @@ export const getInitialValues = (objectToEdit: any | null = null) => {
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:[],
info : []
attribute: objectToEdit?.attribute ?? "",
category_id: objectToEdit?.category?.id ?? "",
variable: [{}, product ?? {}],
info: [undefined, ...formattedData] ?? [],
}
};
export const getInitialValuesAdd = (objectToEdit: any | null = null) => {
return {
id: "",
name: "",
name_ar:'',
name_en: '',
name_de: '',
description: '',
price: "",
images: "",
attribute: "",
category_id: "",
variable: [],
info: [],
}
};
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'),
name_ar: Yup.string().required('Required'),
name_en: Yup.string().required('Required'),
name_de: Yup.string().required('Required'),
category_id: Yup.string().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;
return data;
};

Some files were not shown because too many files have changed in this diff Show More