Done
This commit is contained in:
parent
cd66a46088
commit
4edad39749
216
package-lock.json
generated
216
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
1
public/Layout/De.svg
Normal 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 |
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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,60 +22,73 @@ i18n.use(initReactI18next).init({
|
|||
escapeValue: false
|
||||
}
|
||||
});
|
||||
let What_the_language = localStorage.getItem('language') ?? "en";
|
||||
|
||||
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{
|
||||
i18n.changeLanguage('ar');
|
||||
document.body.setAttribute('dir', 'rtl'); document.body.classList.add('ar');
|
||||
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');
|
||||
} 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');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='Translate'>
|
||||
<Menu menuButton={<MenuButton>
|
||||
{What_the_language === "ar" ?
|
||||
<>
|
||||
<img alt='' src={`/Layout/Ar.svg`} width={20} height={20} /> {t("Arabic")}
|
||||
</>
|
||||
|
||||
:
|
||||
<>
|
||||
<img alt='' src={`/Layout/En.svg`} width={20} height={20} /> {t("English")}
|
||||
|
||||
</>
|
||||
}
|
||||
</MenuButton>} transition>
|
||||
<MenuItem onClick={() => changeLanguage('Ar')}>
|
||||
<img alt='' src='/Layout/Ar.svg' width={20} height={20} /> {t("Arabic")}
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => changeLanguage('En')}>
|
||||
<img alt='' src='/Layout/En.svg' width={20} height={20} /> {t("English")}
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
|
||||
</div>
|
||||
<div className='Translate'>
|
||||
<Menu menuButton={<MenuButton>
|
||||
{What_the_language === "ar" ?
|
||||
<>
|
||||
<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')}>
|
||||
<img alt='' src='/Layout/Ar.svg' width={20} height={20} /> {t("Arabic")}
|
||||
</MenuItem>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
48
src/Components/ValidationField/Ui/SearchBar.scss
Normal file
48
src/Components/ValidationField/Ui/SearchBar.scss
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
35
src/Components/ValidationField/Ui/SearchBar.tsx
Normal file
35
src/Components/ValidationField/Ui/SearchBar.tsx
Normal 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
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
.KarimField{
|
||||
.ValidationField{
|
||||
>*{
|
||||
width: 100%;
|
||||
}
|
||||
|
|
@ -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 KarimField: React.FC<KarimFieldProps> = ({type = "text", ...otherProps}) => {
|
||||
const ValidationField: React.FC<ValidationFieldProps> = ({type , ...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);
|
||||
|
|
@ -25,7 +25,7 @@ const CheckboxField = ({ name, label, isDisabled, onChange,Group,className, prop
|
|||
|
||||
|
||||
>
|
||||
{t(label)}
|
||||
{t(`${label ? label : name}`)}
|
||||
</Checkbox>
|
||||
</Form.Item>
|
||||
|
||||
|
|
@ -5,34 +5,35 @@ import useFormField from '../../../Hooks/useFormField';
|
|||
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
const DataRange = ({ name, label,Format ,props ,onChange,isDisabled,placeholder,className}: any) => {
|
||||
const DataRange = ({ name, label, Format, props, onChange, isDisabled, placeholder, className }: any) => {
|
||||
|
||||
const { errorMsg, isError, t, formik } = useFormField(name, props)
|
||||
const onCalendarChange = (value: any) => {
|
||||
|
||||
|
||||
formik.setFieldValue(name, value)
|
||||
|
||||
};
|
||||
return (
|
||||
|
||||
<div className='KarimField'>
|
||||
<div className='ValidationField'>
|
||||
<label htmlFor={name} className="text">
|
||||
{t(`${label}`)}
|
||||
{t(`${label ? label : name}`)}
|
||||
</label>
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
validateStatus={isError ? "error" : ""}
|
||||
help={isError ? errorMsg : ""}
|
||||
>
|
||||
<RangePicker
|
||||
placeholder={placeholder}
|
||||
size="large"
|
||||
allowClear
|
||||
className={`${className} w-100`}
|
||||
format={Format}
|
||||
onChange={onChange || onCalendarChange}
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
<RangePicker
|
||||
placeholder={placeholder}
|
||||
size="large"
|
||||
allowClear
|
||||
className={`${className} w-100`}
|
||||
format={Format}
|
||||
onChange={onChange || onCalendarChange}
|
||||
disabled={isDisabled}
|
||||
defaultValue={formik.values[name]}
|
||||
/>
|
||||
|
||||
|
||||
</Form.Item>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
56
src/Components/ValidationField/View/SearchField.tsx
Normal file
56
src/Components/ValidationField/View/SearchField.tsx
Normal 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);
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
3
src/Hooks/isEmpty.tsx
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export const isEmpty = (Type:any) => {
|
||||
return !Type || (Array.isArray(Type) && Type.length === 0);
|
||||
};
|
||||
14
src/Hooks/useFormatToSelect.tsx
Normal file
14
src/Hooks/useFormatToSelect.tsx
Normal 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;
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ const Header = () => {
|
|||
</WithDrawer>
|
||||
</div>
|
||||
<div className='Header_Right'>
|
||||
<Theme />
|
||||
{/* <Theme /> */}
|
||||
<Translate />
|
||||
<Menu menuButton={<MenuButton>
|
||||
|
||||
|
|
|
|||
|
|
@ -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}>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ export interface FormTableState {
|
|||
OpenAdd: boolean;
|
||||
}
|
||||
|
||||
export type KarimFieldProps = {
|
||||
export type ValidationFieldProps = {
|
||||
name: string;
|
||||
name2?: string;
|
||||
type: string;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
/>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,33 +1,45 @@
|
|||
|
||||
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" />
|
||||
<Col>
|
||||
<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" />
|
||||
|
||||
</Col>
|
||||
</Col>
|
||||
<Col>
|
||||
<ValidationField name="parent_id" type="Search" option={SelectData} searchBy={"search"} />
|
||||
<ValidationField name="photo" type="File" />
|
||||
<ObjectField/>
|
||||
{/* {values?.id &&
|
||||
<Atteibute />
|
||||
} */}
|
||||
</Col>
|
||||
|
||||
|
||||
|
||||
</Row>
|
||||
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,28 +9,38 @@ 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
|
||||
}
|
||||
|
||||
console.log();
|
||||
|
||||
mutate(values)
|
||||
|
||||
|
||||
}
|
||||
const {t} = useTranslation();
|
||||
|
||||
useNavigateOnSuccess(isSuccess , '/categories' )
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
setObjectToEdit([]);
|
||||
|
||||
}, []);
|
||||
|
||||
|
||||
|
||||
const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit };
|
||||
|
||||
|
||||
|
|
@ -59,4 +69,20 @@ const AddcategoriesPage = () => {
|
|||
|
||||
}
|
||||
|
||||
export default 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
|
||||
}
|
||||
37
src/Pages/Categories/View/Atteibute.tsx
Normal file
37
src/Pages/Categories/View/Atteibute.tsx
Normal 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;
|
||||
|
|
@ -1,29 +1,51 @@
|
|||
|
||||
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={[]} />
|
||||
</Col>
|
||||
<Col>
|
||||
<KarimField name="photo" type="File" />
|
||||
|
||||
</Col>
|
||||
|
||||
</Row>
|
||||
<Col>
|
||||
<ValidationField name="name" />
|
||||
<ValidationField name="parent_id" type="Search" option={SelectData} searchBy={"name"} />
|
||||
</Col>
|
||||
<Col>
|
||||
<ValidationField name="photo" type="File" />
|
||||
{values?.id &&
|
||||
<Atteibute />
|
||||
}
|
||||
|
||||
{/* {
|
||||
AttributeData?.map((item:any)=>{
|
||||
return(
|
||||
<ValidationField
|
||||
name="attributes"
|
||||
option={item?.data}
|
||||
|
||||
/>
|
||||
)
|
||||
})
|
||||
} */}
|
||||
|
||||
</Col>
|
||||
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 language = localStorage.getItem("language") ?? "en";
|
||||
|
||||
newData['name'] = {
|
||||
[language]: values.name,
|
||||
};
|
||||
const Imagetype = typeof values?.photo
|
||||
values['attribute'] = changeShapeInfo(values?.attribute)
|
||||
if(Imagetype === "string") {
|
||||
delete values['photo']
|
||||
}
|
||||
|
||||
return mutate(newData);
|
||||
mutate(values)
|
||||
// return mutate(newData);
|
||||
}
|
||||
|
||||
useNavigateOnSuccess(isSuccess , '/categories')
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
console.log(data);
|
||||
|
||||
setObjectToEdit(data?.category);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,29 +2,49 @@ 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) => ({
|
||||
...prevState,
|
||||
[name]: value
|
||||
}));
|
||||
|
||||
formik.setFieldValue("info", FieldItems)
|
||||
|
||||
setFieldItems((prevState:any) =>{
|
||||
formik.setFieldValue(`new_attribute`, {
|
||||
...prevState,
|
||||
[name]: value
|
||||
});
|
||||
|
||||
return ({
|
||||
...prevState,
|
||||
[name]: value
|
||||
})
|
||||
} )
|
||||
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
form.setFieldsValue({
|
||||
items: [{ list: [{ Attribute: '', Description: '' }] }]
|
||||
});
|
||||
|
||||
|
||||
}, []);
|
||||
|
||||
if (formik?.values?.new_attribute) {
|
||||
const defaultValues = formik.values.new_attribute;
|
||||
|
||||
console.log(objectToArray( formik?.values?.new_attribute));
|
||||
|
||||
form.setFieldsValue({
|
||||
items: [{ list:objectToArray( formik?.values?.new_attribute)}]
|
||||
|
||||
});
|
||||
}
|
||||
else {
|
||||
form.setFieldsValue({
|
||||
items: [{ list: [{ key: '', Description: '' }] },], });
|
||||
}
|
||||
}, []); // Update when tabKey or info[tabKey] changes
|
||||
|
||||
|
||||
return (
|
||||
<Form
|
||||
|
|
@ -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
|
||||
|
|
@ -3,34 +3,49 @@ import * as Yup from "yup";
|
|||
import { buildFormData } from "../../api/helper/buildFormData";
|
||||
|
||||
interface formUtilCommon {
|
||||
number:number,
|
||||
value:number
|
||||
number: number,
|
||||
value: number
|
||||
}
|
||||
|
||||
interface ObjectToEdit extends formUtilCommon {
|
||||
|
||||
id?:number,
|
||||
id?: number,
|
||||
|
||||
}
|
||||
|
||||
interface InitialValues extends ObjectToEdit {
|
||||
|
||||
}
|
||||
interface ValidateSchema extends formUtilCommon{
|
||||
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: 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'),
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -48,18 +64,18 @@ export const getValidationSchema = (editMode: boolean = false): Yup.Schema<any>
|
|||
|
||||
export const getDataToSend = (values: any): FormData => {
|
||||
const data = { ...values };
|
||||
|
||||
|
||||
|
||||
|
||||
const formData = new FormData();
|
||||
buildFormData(formData, data);
|
||||
return formData;
|
||||
};
|
||||
|
||||
export const ChangeDataToPrint = (data:any)=>{
|
||||
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'
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -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
44
src/Pages/Coupon/Page.tsx
Normal 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
|
||||
|
||||
60
src/Pages/Coupon/View/AddForm.tsx
Normal file
60
src/Pages/Coupon/View/AddForm.tsx
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -3,65 +3,68 @@ 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 AddCouponPage = () => {
|
||||
|
||||
|
||||
const { mutate, isLoading, isSuccess } = useAddCoupon()
|
||||
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 AddProductPage = () => {
|
||||
|
||||
|
||||
const {mutate , isLoading , isSuccess} = useAddProduct()
|
||||
const [BarStatus, setBarStatus] = useState({ value: 0, isLoading: false, isError: false, isSuccess: false })
|
||||
const handleSubmit = (values:any)=>{
|
||||
const products = values?.product_attr?.map((item: any) => {
|
||||
return { "itemable_type": "product", "itemable_id": item };
|
||||
})?.filter((item: any) => item.itemable_id !== "")|| [];
|
||||
|
||||
console.log(values);
|
||||
const category = values?.category_attr?.map((item: any) => {
|
||||
return { "itemable_type": "category", "itemable_id": item };
|
||||
})?.filter((item: any) => item.itemable_id !== "") || [];
|
||||
|
||||
const formToSend = getDataToSend(values)
|
||||
values['items'] = [...products, ...category];
|
||||
|
||||
console.log(values, "values");
|
||||
|
||||
mutate(values)
|
||||
|
||||
};
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
useNavigateOnSuccess(isSuccess, '/Coupon')
|
||||
|
||||
|
||||
mutate(formToSend);
|
||||
|
||||
}
|
||||
const {t} = useTranslation();
|
||||
|
||||
useNavigateOnSuccess(isSuccess , '/products' )
|
||||
|
||||
|
||||
|
||||
const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit, BarStatus };
|
||||
const ViewProps = { getInitialValues, getValidationSchema, getDataToSend, handleSubmit };
|
||||
|
||||
|
||||
return (
|
||||
<div className='ViewPage'>
|
||||
|
||||
<ViewPage {...ViewProps}>
|
||||
<Tabs>
|
||||
<TabList>
|
||||
<Tab><div className='SignleDriverContainer'><span className='SignleDriverInfoIcon'><MdLanguage size={20} /></span> <h6 className='SingleDriverInfo'>{t("BasicInfo")}</h6></div></Tab>
|
||||
|
||||
<Tab><div className='SignleDriverContainer'><span className='SignleDriverInfoIcon'><BsInfoCircle size={20} /></span> <h6 className='SingleDriverInfo'>{t("BasicInfo2")}</h6></div></Tab>
|
||||
<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"><BasicInfo /></div>
|
||||
</TabBody>
|
||||
<TabBody >
|
||||
<div className=" mt-4"><BasicInfo2 /></div>
|
||||
</TabBody>
|
||||
|
||||
</Tabs>
|
||||
</ViewPage>
|
||||
|
||||
|
||||
</TabList>
|
||||
<TabBody >
|
||||
<div className=" mt-4"><Form /></div>
|
||||
</TabBody>
|
||||
|
||||
</Tabs>
|
||||
</ViewPage>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
|
@ -69,4 +72,4 @@ const AddProductPage = () => {
|
|||
|
||||
}
|
||||
|
||||
export default AddProductPage
|
||||
export default AddCouponPage
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
60
src/Pages/Coupon/View/EditForm.tsx
Normal file
60
src/Pages/Coupon/View/EditForm.tsx
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
89
src/Pages/Coupon/View/EditPage.tsx
Normal file
89
src/Pages/Coupon/View/EditPage.tsx
Normal 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
|
||||
|
|
@ -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'
|
||||
|
||||
interface formUtilCommon {
|
||||
number: number,
|
||||
value: number
|
||||
}
|
||||
|
||||
interface ObjectToEdit extends formUtilCommon {
|
||||
|
||||
export const getInitialValues = (objectToEdit: any | null = null) => {
|
||||
|
||||
// console.log(objectToEdit);
|
||||
|
||||
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 {
|
||||
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) ?? '',
|
||||
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 ?? "",
|
||||
|
||||
},
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
|
@ -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 navigate = useNavigate()
|
||||
const handleChange = (row:any)=> {
|
||||
const status = row?.favorite ;
|
||||
toggleMutation.mutate({id:row?.id,new_status:status})
|
||||
|
||||
}
|
||||
|
||||
const deleteMutation = useDeleteCoupon()
|
||||
const navigate = useNavigate()
|
||||
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}
|
||||
showView={false}
|
||||
onDelete={() => deleteMutation.mutate({ product_id: row.id })}
|
||||
/>
|
||||
<Actions
|
||||
objectToEdit={row}
|
||||
showEdit
|
||||
onEdit={()=> navigate(`/coupon/${row.id}`) }
|
||||
showView={false}
|
||||
onDelete={() => deleteMutation.mutate({ id: row.id })}
|
||||
/>
|
||||
),
|
||||
},
|
||||
|
||||
],
|
||||
[t]
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
53
src/Pages/Home/Chart.tsx
Normal 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;
|
||||
|
|
@ -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" }}>
|
||||
<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')}
|
||||
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
<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={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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,12 +12,13 @@ const useTableColumns = () => {
|
|||
() => [
|
||||
|
||||
{
|
||||
name: t("driver_name"),
|
||||
name: t("order_code"),
|
||||
sortable: false,
|
||||
center: true,
|
||||
cell:(row:any)=>{
|
||||
|
||||
return (row?.driver?.full_name)
|
||||
console.log(row);
|
||||
|
||||
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>
|
||||
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
@ -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>
|
||||
<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}
|
||||
/>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,62 +1,102 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { getInitialValues, getValidationSchema, getDataToSend } from '../formUtil'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
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) => {
|
||||
|
||||
|
||||
|
||||
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);
|
||||
setInfoTaps(values?.info?.slice(1)?.map((taps:any) => {
|
||||
return (changeShapeInfo(taps));
|
||||
|
||||
}));
|
||||
|
||||
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>
|
||||
|
|
@ -88,4 +128,20 @@ const AddProductPage = () => {
|
|||
|
||||
}
|
||||
|
||||
export default 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
|
||||
}
|
||||
37
src/Pages/Products/View/Atteibute.tsx
Normal file
37
src/Pages/Products/View/Atteibute.tsx
Normal 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;
|
||||
|
|
@ -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');
|
||||
} else {
|
||||
console.log(values);
|
||||
}
|
||||
}, [isValid,values]);
|
||||
|
||||
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>
|
||||
<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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
45
src/Pages/Products/View/Edit/BasicInfo.tsx
Normal file
45
src/Pages/Products/View/Edit/BasicInfo.tsx
Normal 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
|
||||
36
src/Pages/Products/View/Edit/FormikTab/Field/Atteibute.tsx
Normal file
36
src/Pages/Products/View/Edit/FormikTab/Field/Atteibute.tsx
Normal 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;
|
||||
64
src/Pages/Products/View/Edit/FormikTab/Field/File.tsx
Normal file
64
src/Pages/Products/View/Edit/FormikTab/Field/File.tsx
Normal 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
|
||||
50
src/Pages/Products/View/Edit/FormikTab/Field/FileImage.tsx
Normal file
50
src/Pages/Products/View/Edit/FormikTab/Field/FileImage.tsx
Normal 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;
|
||||
|
|
@ -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) => ({
|
||||
...prevState,
|
||||
[name]: value
|
||||
}));
|
||||
formik.setFieldValue("info", FieldItems)
|
||||
|
||||
setFieldItems((prevState:any) =>{
|
||||
|
||||
formik.setFieldValue(`info.${tabKey}`, {
|
||||
...prevState,
|
||||
[name]: value
|
||||
});
|
||||
|
||||
return ({
|
||||
...prevState,
|
||||
[name]: value
|
||||
})
|
||||
} )
|
||||
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
form.setFieldsValue({
|
||||
items: [{ list: [{ Attribute: '', Description: '' }] }]
|
||||
});
|
||||
|
||||
}, []);
|
||||
|
||||
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
|
||||
41
src/Pages/Products/View/Edit/FormikTab/Field/Select.tsx
Normal file
41
src/Pages/Products/View/Edit/FormikTab/Field/Select.tsx
Normal 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);
|
||||
20
src/Pages/Products/View/Edit/FormikTab/FormItem.tsx
Normal file
20
src/Pages/Products/View/Edit/FormikTab/FormItem.tsx
Normal 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} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
13
src/Pages/Products/View/Edit/FormikTab/TabItem.tsx
Normal file
13
src/Pages/Products/View/Edit/FormikTab/TabItem.tsx
Normal 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} />
|
||||
);
|
||||
};
|
||||
115
src/Pages/Products/View/Edit/FormikTab/TabsContainer.tsx
Normal file
115
src/Pages/Products/View/Edit/FormikTab/TabsContainer.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
71
src/Pages/Products/View/Edit/FormikTab/VariableTabs.tsx
Normal file
71
src/Pages/Products/View/Edit/FormikTab/VariableTabs.tsx
Normal 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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
13
src/Pages/Products/View/Edit/VarianInfo.tsx
Normal file
13
src/Pages/Products/View/Edit/VarianInfo.tsx
Normal 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
|
||||
137
src/Pages/Products/View/EditPage.tsx
Normal file
137
src/Pages/Products/View/EditPage.tsx
Normal 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
|
||||
|
||||
|
||||
37
src/Pages/Products/View/FormikTab/Field/Atteibute.tsx
Normal file
37
src/Pages/Products/View/FormikTab/Field/Atteibute.tsx
Normal 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;
|
||||
63
src/Pages/Products/View/FormikTab/Field/File.tsx
Normal file
63
src/Pages/Products/View/FormikTab/Field/File.tsx
Normal 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
|
||||
49
src/Pages/Products/View/FormikTab/Field/FileImage.tsx
Normal file
49
src/Pages/Products/View/FormikTab/Field/FileImage.tsx
Normal 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;
|
||||
116
src/Pages/Products/View/FormikTab/Field/Object.tsx
Normal file
116
src/Pages/Products/View/FormikTab/Field/Object.tsx
Normal 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
|
||||
60
src/Pages/Products/View/FormikTab/Field/SearchTabs.tsx
Normal file
60
src/Pages/Products/View/FormikTab/Field/SearchTabs.tsx
Normal 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;
|
||||
41
src/Pages/Products/View/FormikTab/Field/Select.tsx
Normal file
41
src/Pages/Products/View/FormikTab/Field/Select.tsx
Normal 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);
|
||||
19
src/Pages/Products/View/FormikTab/FormItem.tsx
Normal file
19
src/Pages/Products/View/FormikTab/FormItem.tsx
Normal 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} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
13
src/Pages/Products/View/FormikTab/TabItem.tsx
Normal file
13
src/Pages/Products/View/FormikTab/TabItem.tsx
Normal 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} />
|
||||
);
|
||||
};
|
||||
104
src/Pages/Products/View/FormikTab/TabsContainer.tsx
Normal file
104
src/Pages/Products/View/FormikTab/TabsContainer.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
130
src/Pages/Products/View/FormikTab/VariableTabs.tsx
Normal file
130
src/Pages/Products/View/FormikTab/VariableTabs.tsx
Normal 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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
@ -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
|
||||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
import React from 'react'
|
||||
|
||||
const NewTabs = () => {
|
||||
function handelClick(){
|
||||
|
||||
}
|
||||
return (
|
||||
<div onClick={handelClick}>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default NewTabs
|
||||
|
|
@ -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;
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
import React from 'react'
|
||||
|
||||
const VariableTabs = () => {
|
||||
return (
|
||||
<div>VariableTabs</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default VariableTabs
|
||||
|
|
@ -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
|
||||
|
|
@ -1,62 +1,86 @@
|
|||
|
||||
import * as Yup from "yup";
|
||||
import { buildFormData } from "../../api/helper/buildFormData";
|
||||
import { mapTranslatedProperties } from "../../utils/language/mapTranslatedProperties";
|
||||
|
||||
|
||||
|
||||
export const getInitialValues = (objectToEdit: any | null = null) => {
|
||||
|
||||
// console.log(objectToEdit);
|
||||
|
||||
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];
|
||||
|
||||
|
||||
|
||||
return {
|
||||
id: objectToEdit?.id ?? 0,
|
||||
name:objectToEdit?.name ?? "",
|
||||
name: objectToEdit?.name ?? "",
|
||||
name_ar: objectToEdit?.name?.ar ?? '',
|
||||
name_en: objectToEdit?.name?.en ?? '',
|
||||
name_de: objectToEdit?.name?.de ?? '',
|
||||
description: objectToEdit?.description ?? '',
|
||||
price:objectToEdit?.price??"",
|
||||
main_photo:objectToEdit?.main_photo??"",
|
||||
images:objectToEdit?.images??"",
|
||||
category_id:objectToEdit?.category_id??1,
|
||||
variable:[],
|
||||
info : []
|
||||
|
||||
price: objectToEdit?.price ?? "",
|
||||
images: objectToEdit?.images ?? "",
|
||||
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
|
||||
// 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
Loading…
Reference in New Issue
Block a user