diff --git a/ecosystem.config.js.example b/ecosystem.config.js.example
new file mode 100644
index 0000000..75150b2
--- /dev/null
+++ b/ecosystem.config.js.example
@@ -0,0 +1,18 @@
+module.exports = {
+ apps: [{
+ name: "rms-v3",
+ script: 'node_modules/next/dist/bin/next', // cluster mode run with node only, not npm
+ args: 'start -p 3000',
+ exec_mode: "cluster", // default fork
+ instances: "max",
+ kill_timeout: 4000,
+ wait_ready: true,
+ autorestart: true,
+ watch: false,
+ max_memory_restart: "1G",
+ log_date_format: "YYYY-MM-DD HH:mm Z",
+ env_prod: {
+ APP_ENV: 'prod' // APP_ENV=prod
+ }
+ }],
+};
\ No newline at end of file
diff --git a/jsconfig.json b/jsconfig.json
index b8d6842..d9112b4 100644
--- a/jsconfig.json
+++ b/jsconfig.json
@@ -1,7 +1,12 @@
{
"compilerOptions": {
"paths": {
- "@/*": ["./src/*"]
+ "@/*": [
+ "./src/*"
+ ],
+ "^/*": [
+ "./public/*"
+ ]
}
}
}
diff --git a/package.json b/package.json
index 7ca72ba..3e7a01c 100644
--- a/package.json
+++ b/package.json
@@ -15,9 +15,17 @@
"@mui/icons-material": "^5.15.6",
"@mui/material": "^5.15.6",
"@mui/material-nextjs": "^5.15.6",
+ "@mui/x-date-pickers": "^6.19.5",
+ "date-fns": "^3.3.1",
+ "date-fns-jalali": "^2.13.0-0",
+ "dayjs": "^1.11.10",
+ "formik": "^2.4.5",
+ "jalali-moment": "^3.3.11",
+ "material-react-table": "^2.11.2",
"next": "14.1.0",
"react": "^18",
"react-dom": "^18",
+ "sass": "^1.75.0",
"stylis": "^4.3.1",
"stylis-plugin-rtl": "^2.1.1"
},
diff --git a/public/fontiran.scss b/public/fontiran.scss
new file mode 100644
index 0000000..5ee0036
--- /dev/null
+++ b/public/fontiran.scss
@@ -0,0 +1,176 @@
+/**
+*
+* Name: IRAN Sans-Serif Font
+* Version: 5.0
+* Author: Moslem Ebrahimi (moslemebrahimi.com)
+* Created on: Dec 25, 2012
+* Updated on: Sep 01, 2017
+* Website: http://fontiran.com
+* Copyright: Commercial/Proprietary Software
+--------------------------------------------------------------------------------------
+فونت های ایران سن سریف یک نرم افزار مالکیتی محسوب می شود. جهت آگاهی از قوانین استفاده از این فونت ها لطفا به وب سایت (فونت ایران دات کام) مراجعه نمایید
+--------------------------------------------------------------------------------------
+IRAN Sans-serif fonts are considered a proprietary software. To gain information about the laws regarding the use of these fonts, please visit www.fontiran.com
+--------------------------------------------------------------------------------------
+This set of fonts are used in this project under the license: (.....)
+--------------------------------------------------------------------------------------
+*
+**/
+@font-face {
+ font-family: IRANSansFaNum;
+ font-style: normal;
+ font-weight: 900;
+ src: url('./fonts/eot/IRANSansWeb(FaNum)_Black.eot');
+ src: url('./fonts/eot/IRANSansWeb(FaNum)_Black.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('./fonts/woff2/IRANSansWeb(FaNum)_Black.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
+ url('./fonts/woff/IRANSansWeb(FaNum)_Black.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('./fonts/ttf/IRANSansWeb(FaNum)_Black.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: IRANSansFaNum;
+ font-style: normal;
+ font-weight: bold;
+ src: url('./fonts/eot/IRANSansWeb(FaNum)_Bold.eot');
+ src: url('./fonts/eot/IRANSansWeb(FaNum)_Bold.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('./fonts/woff2/IRANSansWeb(FaNum)_Bold.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
+ url('./fonts/woff/IRANSansWeb(FaNum)_Bold.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('./fonts/ttf/IRANSansWeb(FaNum)_Bold.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: IRANSansFaNum;
+ font-style: normal;
+ font-weight: 500;
+ src: url('./fonts/eot/IRANSansWeb(FaNum)_Medium.eot');
+ src: url('./fonts/eot/IRANSansWeb(FaNum)_Medium.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('./fonts/woff2/IRANSansWeb(FaNum)_Medium.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
+ url('./fonts/woff/IRANSansWeb(FaNum)_Medium.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('./fonts/ttf/IRANSansWeb(FaNum)_Medium.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: IRANSansFaNum;
+ font-style: normal;
+ font-weight: 300;
+ src: url('./fonts/eot/IRANSansWeb(FaNum)_Light.eot');
+ src: url('./fonts/eot/IRANSansWeb(FaNum)_Light.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('./fonts/woff2/IRANSansWeb(FaNum)_Light.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
+ url('./fonts/woff/IRANSansWeb(FaNum)_Light.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('./fonts/ttf/IRANSansWeb(FaNum)_Light.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: IRANSansFaNum;
+ font-style: normal;
+ font-weight: 200;
+ src: url('./fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot');
+ src: url('./fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('./fonts/woff2/IRANSansWeb(FaNum)_UltraLight.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
+ url('./fonts/woff/IRANSansWeb(FaNum)_UltraLight.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('./fonts/ttf/IRANSansWeb(FaNum)_UltraLight.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: IRANSansFaNum;
+ font-style: normal;
+ font-weight: normal;
+ src: url('./fonts/eot/IRANSansWeb(FaNum).eot');
+ src: url('./fonts/eot/IRANSansWeb(FaNum).eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('./fonts/woff2/IRANSansWeb(FaNum).woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
+ url('./fonts/woff/IRANSansWeb(FaNum).woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('./fonts/ttf/IRANSansWeb(FaNum).ttf') format('truetype');
+}
+
+@font-face {
+ font-family: IRANSans;
+ font-style: normal;
+ font-weight: 900;
+ src: url('./fonts/eot/IRANSansWeb_Black.eot');
+ src: url('./fonts/eot/IRANSansWeb_Black.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('./fonts/woff2/IRANSansWeb_Black.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
+ url('./fonts/woff/IRANSansWeb_Black.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('./fonts/ttf/IRANSansWeb_Black.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: IRANSans;
+ font-style: normal;
+ font-weight: bold;
+ src: url('./fonts/eot/IRANSansWeb_Bold.eot');
+ src: url('./fonts/eot/IRANSansWeb_Bold.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('./fonts/woff2/IRANSansWeb_Bold.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
+ url('./fonts/woff/IRANSansWeb_Bold.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('./fonts/ttf/IRANSansWeb_Bold.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: IRANSans;
+ font-style: normal;
+ font-weight: 500;
+ src: url('./fonts/eot/IRANSansWeb_Medium.eot');
+ src: url('./fonts/eot/IRANSansWeb_Medium.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('./fonts/woff2/IRANSansWeb_Medium.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
+ url('./fonts/woff/IRANSansWeb_Medium.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('./fonts/ttf/IRANSansWeb_Medium.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: IRANSans;
+ font-style: normal;
+ font-weight: 300;
+ src: url('./fonts/eot/IRANSansWeb_Light.eot');
+ src: url('./fonts/eot/IRANSansWeb_Light.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('./fonts/woff2/IRANSansWeb_Light.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
+ url('./fonts/woff/IRANSansWeb_Light.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('./fonts/ttf/IRANSansWeb_Light.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: IRANSans;
+ font-style: normal;
+ font-weight: 200;
+ src: url('./fonts/eot/IRANSansWeb_UltraLight.eot');
+ src: url('./fonts/eot/IRANSansWeb_UltraLight.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('./fonts/woff2/IRANSansWeb_UltraLight.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
+ url('./fonts/woff/IRANSansWeb_UltraLight.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('./fonts/ttf/IRANSansWeb_UltraLight.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: IRANSans;
+ font-style: normal;
+ font-weight: normal;
+ src: url('./fonts/eot/IRANSansWeb.eot');
+ src: url('./fonts/eot/IRANSansWeb.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('./fonts/woff2/IRANSansWeb.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
+ url('./fonts/woff/IRANSansWeb.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('./fonts/ttf/IRANSansWeb.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: Parastoo;
+ font-style: normal;
+ font-weight: normal;
+ src: url('./fonts/eot/Parastoo.eot');
+ src: url('./fonts/eot/Parastoo.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('./fonts/woff/Parastoo.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('./fonts/ttf/Parastoo.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: Parastoo;
+ font-style: normal;
+ font-weight: bold;
+ src: url('./fonts/eot/Parastoo-Bold.eot');
+ src: url('./fonts/eot/Parastoo-Bold.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('./fonts/woff/Parastoo-Bold.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('./fonts/ttf/Parastoo-Bold.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: Bnazanin;
+ font-style: normal;
+ font-weight: normal;
+ src: url('./fonts/ttf/BNazanin.ttf') format('truetype');
+}
diff --git a/public/fonts/eot/IRANSansWeb(FaNum).eot b/public/fonts/eot/IRANSansWeb(FaNum).eot
new file mode 100644
index 0000000..5a6fdd0
Binary files /dev/null and b/public/fonts/eot/IRANSansWeb(FaNum).eot differ
diff --git a/public/fonts/eot/IRANSansWeb(FaNum)_Black.eot b/public/fonts/eot/IRANSansWeb(FaNum)_Black.eot
new file mode 100644
index 0000000..a012bc6
Binary files /dev/null and b/public/fonts/eot/IRANSansWeb(FaNum)_Black.eot differ
diff --git a/public/fonts/eot/IRANSansWeb(FaNum)_Bold.eot b/public/fonts/eot/IRANSansWeb(FaNum)_Bold.eot
new file mode 100644
index 0000000..82525e5
Binary files /dev/null and b/public/fonts/eot/IRANSansWeb(FaNum)_Bold.eot differ
diff --git a/public/fonts/eot/IRANSansWeb(FaNum)_Light.eot b/public/fonts/eot/IRANSansWeb(FaNum)_Light.eot
new file mode 100644
index 0000000..f899008
Binary files /dev/null and b/public/fonts/eot/IRANSansWeb(FaNum)_Light.eot differ
diff --git a/public/fonts/eot/IRANSansWeb(FaNum)_Medium.eot b/public/fonts/eot/IRANSansWeb(FaNum)_Medium.eot
new file mode 100644
index 0000000..117c2c8
Binary files /dev/null and b/public/fonts/eot/IRANSansWeb(FaNum)_Medium.eot differ
diff --git a/public/fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot b/public/fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot
new file mode 100644
index 0000000..9cd9b66
Binary files /dev/null and b/public/fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot differ
diff --git a/public/fonts/eot/IRANSansWeb.eot b/public/fonts/eot/IRANSansWeb.eot
new file mode 100644
index 0000000..553eec8
Binary files /dev/null and b/public/fonts/eot/IRANSansWeb.eot differ
diff --git a/public/fonts/eot/IRANSansWeb_Black.eot b/public/fonts/eot/IRANSansWeb_Black.eot
new file mode 100644
index 0000000..ee7c6a0
Binary files /dev/null and b/public/fonts/eot/IRANSansWeb_Black.eot differ
diff --git a/public/fonts/eot/IRANSansWeb_Bold.eot b/public/fonts/eot/IRANSansWeb_Bold.eot
new file mode 100644
index 0000000..746a6e6
Binary files /dev/null and b/public/fonts/eot/IRANSansWeb_Bold.eot differ
diff --git a/public/fonts/eot/IRANSansWeb_Light.eot b/public/fonts/eot/IRANSansWeb_Light.eot
new file mode 100644
index 0000000..f79a681
Binary files /dev/null and b/public/fonts/eot/IRANSansWeb_Light.eot differ
diff --git a/public/fonts/eot/IRANSansWeb_Medium.eot b/public/fonts/eot/IRANSansWeb_Medium.eot
new file mode 100644
index 0000000..8b31d6b
Binary files /dev/null and b/public/fonts/eot/IRANSansWeb_Medium.eot differ
diff --git a/public/fonts/eot/IRANSansWeb_UltraLight.eot b/public/fonts/eot/IRANSansWeb_UltraLight.eot
new file mode 100644
index 0000000..fd4b205
Binary files /dev/null and b/public/fonts/eot/IRANSansWeb_UltraLight.eot differ
diff --git a/public/fonts/eot/Parastoo-Bold.eot b/public/fonts/eot/Parastoo-Bold.eot
new file mode 100644
index 0000000..4dc70ba
Binary files /dev/null and b/public/fonts/eot/Parastoo-Bold.eot differ
diff --git a/public/fonts/eot/Parastoo.eot b/public/fonts/eot/Parastoo.eot
new file mode 100644
index 0000000..fd15eda
Binary files /dev/null and b/public/fonts/eot/Parastoo.eot differ
diff --git a/public/fonts/ttf/BNazanin.ttf b/public/fonts/ttf/BNazanin.ttf
new file mode 100644
index 0000000..4e59251
Binary files /dev/null and b/public/fonts/ttf/BNazanin.ttf differ
diff --git a/public/fonts/ttf/IRANSansWeb(FaNum).ttf b/public/fonts/ttf/IRANSansWeb(FaNum).ttf
new file mode 100644
index 0000000..cd74265
Binary files /dev/null and b/public/fonts/ttf/IRANSansWeb(FaNum).ttf differ
diff --git a/public/fonts/ttf/IRANSansWeb(FaNum)_Black.ttf b/public/fonts/ttf/IRANSansWeb(FaNum)_Black.ttf
new file mode 100644
index 0000000..0f84dbf
Binary files /dev/null and b/public/fonts/ttf/IRANSansWeb(FaNum)_Black.ttf differ
diff --git a/public/fonts/ttf/IRANSansWeb(FaNum)_Bold.ttf b/public/fonts/ttf/IRANSansWeb(FaNum)_Bold.ttf
new file mode 100644
index 0000000..6fa2412
Binary files /dev/null and b/public/fonts/ttf/IRANSansWeb(FaNum)_Bold.ttf differ
diff --git a/public/fonts/ttf/IRANSansWeb(FaNum)_Light.ttf b/public/fonts/ttf/IRANSansWeb(FaNum)_Light.ttf
new file mode 100644
index 0000000..d9000c9
Binary files /dev/null and b/public/fonts/ttf/IRANSansWeb(FaNum)_Light.ttf differ
diff --git a/public/fonts/ttf/IRANSansWeb(FaNum)_Medium.ttf b/public/fonts/ttf/IRANSansWeb(FaNum)_Medium.ttf
new file mode 100644
index 0000000..e20001c
Binary files /dev/null and b/public/fonts/ttf/IRANSansWeb(FaNum)_Medium.ttf differ
diff --git a/public/fonts/ttf/IRANSansWeb(FaNum)_UltraLight.ttf b/public/fonts/ttf/IRANSansWeb(FaNum)_UltraLight.ttf
new file mode 100644
index 0000000..0072606
Binary files /dev/null and b/public/fonts/ttf/IRANSansWeb(FaNum)_UltraLight.ttf differ
diff --git a/public/fonts/ttf/IRANSansWeb.ttf b/public/fonts/ttf/IRANSansWeb.ttf
new file mode 100644
index 0000000..7416c53
Binary files /dev/null and b/public/fonts/ttf/IRANSansWeb.ttf differ
diff --git a/public/fonts/ttf/IRANSansWeb_Black.ttf b/public/fonts/ttf/IRANSansWeb_Black.ttf
new file mode 100644
index 0000000..df4928f
Binary files /dev/null and b/public/fonts/ttf/IRANSansWeb_Black.ttf differ
diff --git a/public/fonts/ttf/IRANSansWeb_Bold.ttf b/public/fonts/ttf/IRANSansWeb_Bold.ttf
new file mode 100644
index 0000000..158fbaa
Binary files /dev/null and b/public/fonts/ttf/IRANSansWeb_Bold.ttf differ
diff --git a/public/fonts/ttf/IRANSansWeb_Light.ttf b/public/fonts/ttf/IRANSansWeb_Light.ttf
new file mode 100644
index 0000000..3060c60
Binary files /dev/null and b/public/fonts/ttf/IRANSansWeb_Light.ttf differ
diff --git a/public/fonts/ttf/IRANSansWeb_Medium.ttf b/public/fonts/ttf/IRANSansWeb_Medium.ttf
new file mode 100644
index 0000000..6dd6c5d
Binary files /dev/null and b/public/fonts/ttf/IRANSansWeb_Medium.ttf differ
diff --git a/public/fonts/ttf/IRANSansWeb_UltraLight.ttf b/public/fonts/ttf/IRANSansWeb_UltraLight.ttf
new file mode 100644
index 0000000..7ebe1bf
Binary files /dev/null and b/public/fonts/ttf/IRANSansWeb_UltraLight.ttf differ
diff --git a/public/fonts/ttf/Parastoo-Bold.ttf b/public/fonts/ttf/Parastoo-Bold.ttf
new file mode 100644
index 0000000..5ef27c9
Binary files /dev/null and b/public/fonts/ttf/Parastoo-Bold.ttf differ
diff --git a/public/fonts/ttf/Parastoo.ttf b/public/fonts/ttf/Parastoo.ttf
new file mode 100644
index 0000000..f52889c
Binary files /dev/null and b/public/fonts/ttf/Parastoo.ttf differ
diff --git a/public/fonts/woff/IRANSansWeb(FaNum).woff b/public/fonts/woff/IRANSansWeb(FaNum).woff
new file mode 100644
index 0000000..e34dfc4
Binary files /dev/null and b/public/fonts/woff/IRANSansWeb(FaNum).woff differ
diff --git a/public/fonts/woff/IRANSansWeb(FaNum)_Black.woff b/public/fonts/woff/IRANSansWeb(FaNum)_Black.woff
new file mode 100644
index 0000000..bbbca77
Binary files /dev/null and b/public/fonts/woff/IRANSansWeb(FaNum)_Black.woff differ
diff --git a/public/fonts/woff/IRANSansWeb(FaNum)_Bold.woff b/public/fonts/woff/IRANSansWeb(FaNum)_Bold.woff
new file mode 100644
index 0000000..05d933e
Binary files /dev/null and b/public/fonts/woff/IRANSansWeb(FaNum)_Bold.woff differ
diff --git a/public/fonts/woff/IRANSansWeb(FaNum)_Light.woff b/public/fonts/woff/IRANSansWeb(FaNum)_Light.woff
new file mode 100644
index 0000000..fe80d47
Binary files /dev/null and b/public/fonts/woff/IRANSansWeb(FaNum)_Light.woff differ
diff --git a/public/fonts/woff/IRANSansWeb(FaNum)_Medium.woff b/public/fonts/woff/IRANSansWeb(FaNum)_Medium.woff
new file mode 100644
index 0000000..f13a870
Binary files /dev/null and b/public/fonts/woff/IRANSansWeb(FaNum)_Medium.woff differ
diff --git a/public/fonts/woff/IRANSansWeb(FaNum)_UltraLight.woff b/public/fonts/woff/IRANSansWeb(FaNum)_UltraLight.woff
new file mode 100644
index 0000000..9c653a4
Binary files /dev/null and b/public/fonts/woff/IRANSansWeb(FaNum)_UltraLight.woff differ
diff --git a/public/fonts/woff/IRANSansWeb.woff b/public/fonts/woff/IRANSansWeb.woff
new file mode 100644
index 0000000..abb63f2
Binary files /dev/null and b/public/fonts/woff/IRANSansWeb.woff differ
diff --git a/public/fonts/woff/IRANSansWeb_Black.woff b/public/fonts/woff/IRANSansWeb_Black.woff
new file mode 100644
index 0000000..12c8a6e
Binary files /dev/null and b/public/fonts/woff/IRANSansWeb_Black.woff differ
diff --git a/public/fonts/woff/IRANSansWeb_Bold.woff b/public/fonts/woff/IRANSansWeb_Bold.woff
new file mode 100644
index 0000000..801c55d
Binary files /dev/null and b/public/fonts/woff/IRANSansWeb_Bold.woff differ
diff --git a/public/fonts/woff/IRANSansWeb_Light.woff b/public/fonts/woff/IRANSansWeb_Light.woff
new file mode 100644
index 0000000..99b8d5d
Binary files /dev/null and b/public/fonts/woff/IRANSansWeb_Light.woff differ
diff --git a/public/fonts/woff/IRANSansWeb_Medium.woff b/public/fonts/woff/IRANSansWeb_Medium.woff
new file mode 100644
index 0000000..322c396
Binary files /dev/null and b/public/fonts/woff/IRANSansWeb_Medium.woff differ
diff --git a/public/fonts/woff/IRANSansWeb_UltraLight.woff b/public/fonts/woff/IRANSansWeb_UltraLight.woff
new file mode 100644
index 0000000..31c21ca
Binary files /dev/null and b/public/fonts/woff/IRANSansWeb_UltraLight.woff differ
diff --git a/public/fonts/woff/Parastoo-Bold.woff b/public/fonts/woff/Parastoo-Bold.woff
new file mode 100644
index 0000000..c6c05ff
Binary files /dev/null and b/public/fonts/woff/Parastoo-Bold.woff differ
diff --git a/public/fonts/woff/Parastoo.woff b/public/fonts/woff/Parastoo.woff
new file mode 100644
index 0000000..e5efc4d
Binary files /dev/null and b/public/fonts/woff/Parastoo.woff differ
diff --git a/public/fonts/woff2/IRANSansWeb(FaNum).woff2 b/public/fonts/woff2/IRANSansWeb(FaNum).woff2
new file mode 100644
index 0000000..30a377d
Binary files /dev/null and b/public/fonts/woff2/IRANSansWeb(FaNum).woff2 differ
diff --git a/public/fonts/woff2/IRANSansWeb(FaNum)_Black.woff2 b/public/fonts/woff2/IRANSansWeb(FaNum)_Black.woff2
new file mode 100644
index 0000000..0b15441
Binary files /dev/null and b/public/fonts/woff2/IRANSansWeb(FaNum)_Black.woff2 differ
diff --git a/public/fonts/woff2/IRANSansWeb(FaNum)_Bold.woff2 b/public/fonts/woff2/IRANSansWeb(FaNum)_Bold.woff2
new file mode 100644
index 0000000..f2353b2
Binary files /dev/null and b/public/fonts/woff2/IRANSansWeb(FaNum)_Bold.woff2 differ
diff --git a/public/fonts/woff2/IRANSansWeb(FaNum)_Light.woff2 b/public/fonts/woff2/IRANSansWeb(FaNum)_Light.woff2
new file mode 100644
index 0000000..562d663
Binary files /dev/null and b/public/fonts/woff2/IRANSansWeb(FaNum)_Light.woff2 differ
diff --git a/public/fonts/woff2/IRANSansWeb(FaNum)_Medium.woff2 b/public/fonts/woff2/IRANSansWeb(FaNum)_Medium.woff2
new file mode 100644
index 0000000..a69f4d0
Binary files /dev/null and b/public/fonts/woff2/IRANSansWeb(FaNum)_Medium.woff2 differ
diff --git a/public/fonts/woff2/IRANSansWeb(FaNum)_UltraLight.woff2 b/public/fonts/woff2/IRANSansWeb(FaNum)_UltraLight.woff2
new file mode 100644
index 0000000..1114f71
Binary files /dev/null and b/public/fonts/woff2/IRANSansWeb(FaNum)_UltraLight.woff2 differ
diff --git a/public/fonts/woff2/IRANSansWeb.woff2 b/public/fonts/woff2/IRANSansWeb.woff2
new file mode 100644
index 0000000..21bbba5
Binary files /dev/null and b/public/fonts/woff2/IRANSansWeb.woff2 differ
diff --git a/public/fonts/woff2/IRANSansWeb_Black.woff2 b/public/fonts/woff2/IRANSansWeb_Black.woff2
new file mode 100644
index 0000000..da34aad
Binary files /dev/null and b/public/fonts/woff2/IRANSansWeb_Black.woff2 differ
diff --git a/public/fonts/woff2/IRANSansWeb_Bold.woff2 b/public/fonts/woff2/IRANSansWeb_Bold.woff2
new file mode 100644
index 0000000..dd1436c
Binary files /dev/null and b/public/fonts/woff2/IRANSansWeb_Bold.woff2 differ
diff --git a/public/fonts/woff2/IRANSansWeb_Light.woff2 b/public/fonts/woff2/IRANSansWeb_Light.woff2
new file mode 100644
index 0000000..16dc9f1
Binary files /dev/null and b/public/fonts/woff2/IRANSansWeb_Light.woff2 differ
diff --git a/public/fonts/woff2/IRANSansWeb_Medium.woff2 b/public/fonts/woff2/IRANSansWeb_Medium.woff2
new file mode 100644
index 0000000..5fb39d4
Binary files /dev/null and b/public/fonts/woff2/IRANSansWeb_Medium.woff2 differ
diff --git a/public/fonts/woff2/IRANSansWeb_UltraLight.woff2 b/public/fonts/woff2/IRANSansWeb_UltraLight.woff2
new file mode 100644
index 0000000..eeeec11
Binary files /dev/null and b/public/fonts/woff2/IRANSansWeb_UltraLight.woff2 differ
diff --git a/public/global.scss b/public/global.scss
new file mode 100644
index 0000000..7802651
--- /dev/null
+++ b/public/global.scss
@@ -0,0 +1 @@
+@import "fontiran";
\ No newline at end of file
diff --git a/src/app/(withAuth)/dashboard/template.js b/src/app/(withAuth)/dashboard/template.js
index 1d86153..c44d626 100644
--- a/src/app/(withAuth)/dashboard/template.js
+++ b/src/app/(withAuth)/dashboard/template.js
@@ -1,11 +1,16 @@
+import {Box, Stack} from "@mui/material";
+import HeaderWithLogo from "@/components/layouts/dashboard/headerWithLogo";
+import HeaderWithSidebar from "@/components/layouts/dashboard/headerWithSidebar";
+
const Template = ({children}) => {
- return (
- <>
- header
- sidebar
- {children}
- >
- )
+ return (
+
+
+
+ {children}
+
+
+ )
}
export default Template
\ No newline at end of file
diff --git a/src/app/layout.js b/src/app/layout.js
index ab64299..c23a7ec 100644
--- a/src/app/layout.js
+++ b/src/app/layout.js
@@ -1,11 +1,17 @@
+import {TableSettingProvider} from "@/lib/contexts/tableSetting";
+
export const metadata = {
title: 'سامانه جامع راهداری',
}
export default function RootLayout({children}) {
- return (
-
- {children}
-
- )
+ return (
+
+
+
+ {children}
+
+
+
+ )
}
diff --git a/src/app/page.js b/src/app/page.js
index 7be9aa2..ed41c91 100644
--- a/src/app/page.js
+++ b/src/app/page.js
@@ -1,5 +1,148 @@
-const Page = () => {
- return (<>>)
+'use client'
+import DataTable from "@/core/components/DataTable";
+import { useMemo } from "react";
+
+function Page() {
+ const columns = useMemo(
+ () => [
+ {
+ accessorKey: 'fullname',
+ header: 'نام کامل',
+ id: "fullname",
+ columns: [
+ {
+ accessorKey: 'name.firstName',
+ header: 'نام',
+ id: "firstName",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "contains",
+ enableClickToCopy: true,
+ columnFilterModeOptions: [
+ "equals",
+ "notEquals",
+ "contains"
+ ],
+ },
+ {
+ accessorKey: 'name.lastName',
+ header: 'نام خانوادگی',
+ id: "lastName",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "contains",
+ columnFilterModeOptions: [
+ "equals",
+ "notEquals",
+ "contains"
+ ],
+ },
+ ],
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "contains",
+ columnFilterModeOptions: [
+ "equals",
+ "notEquals",
+ "contains"
+ ],
+ },
+ {
+ accessorKey: 'address',
+ header: 'آدرس',
+ id: "address",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "contains",
+ columnFilterModeOptions: [
+ "equals",
+ "notEquals",
+ "contains"
+ ],
+ },
+ {
+ accessorKey: 'city',
+ header: 'شهر',
+ id: "city",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "contains",
+ columnFilterModeOptions: [
+ "equals",
+ "notEquals",
+ "contains",
+ "lessThan",
+ "greaterThan",
+ "between",
+ ],
+
+ },
+ {
+ accessorKey: 'state',
+ header: 'وضعیت',
+ id: "state",
+ text: 'نام',
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "contains",
+ columnFilterModeOptions: [
+ "equals",
+ "notEquals",
+ "contains"
+ ],
+ },
+ {
+ accessorKey: 'state',
+ header: 'محدوده',
+ id: "betweenNum",
+ text: 'محدوده',
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "betweenNum",
+ columnFilterModeOptions: [],
+ },
+ {
+ accessorKey: 'state',
+ header: 'تاریخ',
+ id: "betweenDate",
+ text: 'تاریخ',
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "betweenDate",
+ columnFilterModeOptions: [],
+ },
+ {
+ accessorKey: 'state',
+ header: 'شلکت',
+ id: "select2",
+ text: 'تاریخ',
+ type: 'select2',
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "equals",
+ columnFilterModeOptions: [],
+ },
+ {
+ accessorKey: 'state',
+ header: 'سلکت',
+ id: "select",
+ text: 'تنتنت',
+ type: 'select',
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "equals",
+ columnFilterModeOptions: [],
+ },
+ ],
+ [],
+ );
+
+ return (
+ <>
+
+ >
+
+ );
}
-export default Page
\ No newline at end of file
+export default Page;
diff --git a/src/app/template.js b/src/app/template.js
index 0443d64..cc8a8f2 100644
--- a/src/app/template.js
+++ b/src/app/template.js
@@ -2,6 +2,7 @@ import cacheProviderRtl from "@/core/utils/cacheRtl";
import {CssBaseline, ThemeProvider} from "@mui/material";
import theme from "@/core/utils/theme";
import {AppRouterCacheProvider} from "@mui/material-nextjs/v14-appRouter";
+import "^/global.scss"
const Template = ({children}) => {
return (
diff --git a/src/components/BetweenDateFilter.jsx b/src/components/BetweenDateFilter.jsx
new file mode 100644
index 0000000..e8e8c18
--- /dev/null
+++ b/src/components/BetweenDateFilter.jsx
@@ -0,0 +1,31 @@
+import {Grid} from '@mui/material';
+import MuiDatePicker from "@/components/MuiDatePicker";
+import {useEffect, useState} from "react";
+
+const BetWeenDateFilter = ({formik, id , defaultValue}) => {
+ useEffect(() => {
+ formik.setFieldValue(`${id}[0]`, defaultValue?.[0] || '');
+ formik.setFieldValue(`${id}[1]`, defaultValue?.[1] || '');
+ }, []);
+
+ const [dateRange, setDateRange] = useState(defaultValue);
+
+ const handleDateChange = (index, value) => {
+ const newRange = [...dateRange];
+ newRange[index] = value;
+ setDateRange(newRange);
+ };
+
+ return (
+
+
+ handleDateChange(0, value)}/>
+
+
+ handleDateChange(1, value)}/>
+
+
+ );
+};
+
+export default BetWeenDateFilter;
diff --git a/src/components/BetweenNumberFilter.jsx b/src/components/BetweenNumberFilter.jsx
new file mode 100644
index 0000000..0b3512a
--- /dev/null
+++ b/src/components/BetweenNumberFilter.jsx
@@ -0,0 +1,58 @@
+import {Grid, TextField} from '@mui/material';
+import {useEffect} from "react";
+
+const BetweenNumberFilter = ({formik, id , defaultValue}) => {
+
+ useEffect(() => {
+ formik.setFieldValue(`${id}[0]`, defaultValue?.[0] || '');
+ formik.setFieldValue(`${id}[1]`, defaultValue?.[1] || '');
+ }, []);
+ const handleBlur = (index) => () => {
+ const minValue = formik.values[id][0];
+ const maxValue = formik.values[id][1];
+
+ if (index === 0 && minValue !== '' && maxValue !== '' && parseInt(minValue) > parseInt(maxValue)) {
+ formik.setFieldValue(`${id}[0]`, maxValue);
+ }
+
+ if (index === 1 && minValue !== '' && maxValue !== '' && parseInt(maxValue) < parseInt(minValue)) {
+ formik.setFieldValue(`${id}[1]`, minValue);
+ }
+ };
+ return (
+
+
+
+
+
+
+
+
+ );
+};
+
+export default BetweenNumberFilter;
diff --git a/src/components/CustomToolbar.jsx b/src/components/CustomToolbar.jsx
new file mode 100644
index 0000000..344b5da
--- /dev/null
+++ b/src/components/CustomToolbar.jsx
@@ -0,0 +1,20 @@
+import FilterColumn from "@/components/FilterColumn";
+
+const CustomToolbar = ({columns}) => {
+ return (
+
+ )
+}
+export default CustomToolbar
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/DataTableMain.jsx b/src/components/DataTableMain.jsx
new file mode 100644
index 0000000..a6e8c91
--- /dev/null
+++ b/src/components/DataTableMain.jsx
@@ -0,0 +1,141 @@
+import DataTableTest from "@/components/DataTableTest";
+import FilterColumn from "@/components/FilterColumn";
+import {useMemo} from "react";
+import {Button} from "@mui/material";
+
+function DataTableMain() {
+ const columns = useMemo(
+ () => [
+ {
+ accessorKey: 'name.firstName',
+ header: 'نام',
+ size: 150,
+ id: "firstName",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "contains",
+ columnFilterModeOptions: [
+ "equals",
+ "notEquals",
+ "contains"
+ ],
+ },
+ {
+ accessorKey: 'name.lastName',
+ header: 'نام خانوادگی',
+ size: 150,
+ id: "lastName",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "contains",
+ columnFilterModeOptions: [
+ "equals",
+ "notEquals",
+ "contains"
+ ],
+ },
+ {
+ accessorKey: 'address',
+ header: 'آدرس',
+ size: 200,
+ id: "address",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "contains",
+ columnFilterModeOptions: [
+ "equals",
+ "notEquals",
+ "contains"
+ ],
+ },
+ {
+ accessorKey: 'city',
+ header: 'شهر',
+ size: 150,
+ id: "city",
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "contains",
+ columnFilterModeOptions: [
+ "equals",
+ "notEquals",
+ "contains",
+ "lessThan",
+ "greaterThan",
+ "between",
+ ],
+
+ },
+ {
+ accessorKey: 'state',
+ header: 'وضعیت',
+ size: 150,
+ id: "state",
+ text:'نام',
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "contains",
+ columnFilterModeOptions: [
+ "equals",
+ "notEquals",
+ "contains"
+ ],
+ },
+ {
+ accessorKey: 'state',
+ header: 'محدوده',
+ size: 150,
+ id: "betweenNum",
+ text:'محدوده',
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "betweenNum",
+ columnFilterModeOptions: [],
+ },
+ {
+ accessorKey: 'state',
+ header: 'تاریخ',
+ size: 150,
+ id: "betweenDate",
+ text:'تاریخ',
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "betweenDate",
+ columnFilterModeOptions: [],
+ },
+ {
+ accessorKey: 'state',
+ header: 'شلکت',
+ size: 150,
+ id: "select2",
+ text:'تاریخ',
+ type: 'select2',
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "equals",
+ columnFilterModeOptions: [],
+ },
+ {
+ accessorKey: 'state',
+ header: 'سلکت',
+ size: 150,
+ id: "select",
+ text:'تنتنت',
+ type: 'select',
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "equals",
+ columnFilterModeOptions: [],
+ },
+ ],
+ [],
+ );
+ return (
+ <>
+
+ >
+
+ );
+}
+
+export default DataTableMain;
diff --git a/src/components/DataTableTest.jsx b/src/components/DataTableTest.jsx
new file mode 100644
index 0000000..bf610fb
--- /dev/null
+++ b/src/components/DataTableTest.jsx
@@ -0,0 +1,104 @@
+"use client"
+import {useMemo, useState} from 'react';
+import {MaterialReactTable} from 'material-react-table';
+import useTableSetting from "@/lib/hooks/useTableSetting";
+import {IconButton, Tooltip, Typography} from "@mui/material";
+import CustomToolbar from "@/components/CustomToolbar";
+
+
+const data = [
+ {
+ name: {
+ firstName: 'John',
+ lastName: 'Doe',
+ },
+ address: '261 Erdman Ford',
+ city: 'East Daphne',
+ state: 'Kentucky',
+ },
+ {
+ name: {
+ firstName: 'Jane',
+ lastName: 'Doe',
+ },
+ address: '769 Dominic Grove',
+ city: 'Columbus',
+ state: 'Ohio',
+ },
+ {
+ name: {
+ firstName: 'Joe',
+ lastName: 'Doe',
+ },
+ address: '566 Brakus Inlet',
+ city: 'South Linda',
+ state: 'West Virginia',
+ },
+ {
+ name: {
+ firstName: 'Kevin',
+ lastName: 'Vandy',
+ },
+ address: '722 Emie Stream',
+ city: 'Lincoln',
+ state: 'Nebraska',
+ },
+ {
+ name: {
+ firstName: 'Joshua',
+ lastName: 'Rolluffs',
+ },
+ address: '32188 Larkin Turnpike',
+ city: 'Charleston',
+ state: 'South Carolina',
+ },
+];
+
+function RefreshIcon() {
+ return null;
+}
+
+const TestDataTable = ({columns}) => {
+ const [userId, setUserId] = useState(2);
+ const [pageName, setPageName] = useState('loan');
+ const [tableName, setTableName] = useState('loan_table');
+ const {settingStore, hideAction, filterAction, sortAction,} = useTableSetting();
+
+ const onColumnVisibilityChange = (event) => {
+ const settingValue = event();
+ hideAction(userId, pageName, tableName, settingValue, columns);
+ };
+ const onSortingChange = (event) => {
+ const settingValue = event(settingStore?.[userId]?.[pageName]?.[tableName]?.['sorts'] || []);
+ sortAction(userId, pageName, tableName, settingValue, columns);
+ }
+
+ return (
+ <>
+ {settingStore?.[userId]?.[pageName]?.[tableName]?.['summary']}
+ (<>
+
+ >)}
+ initialState={{
+ columnVisibility: settingStore?.[userId]?.[pageName]?.[tableName]?.['hides'],
+ sorting: settingStore?.[userId]?.[pageName]?.[tableName]?.['sorts'] || []
+ }}
+ state={{
+ columnVisibility: settingStore?.[userId]?.[pageName]?.[tableName]?.['hides'],
+ sorting: settingStore?.[userId]?.[pageName]?.[tableName]?.['sorts'] || []
+ }}
+ onColumnVisibilityChange={onColumnVisibilityChange}
+ onSortingChange={onSortingChange}
+ />
+ >
+ );
+};
+
+export default TestDataTable;
+
diff --git a/src/components/FilterBox.jsx b/src/components/FilterBox.jsx
new file mode 100644
index 0000000..f887918
--- /dev/null
+++ b/src/components/FilterBox.jsx
@@ -0,0 +1,30 @@
+import { Popper, Paper, List, ListItem, ListItemText, ClickAwayListener } from '@mui/material';
+
+function FilterBox({anchorEl , open = false, handleClose = () => {} , columnFilterModeOptions , onSelectFilter , defaultFilter}) {
+
+ const id = open ? 'simple-popper' : undefined;
+ const handleSelectFilter = (filterFn) => {
+ onSelectFilter(filterFn);
+ handleClose();
+ };
+
+ return (
+
+
+
+
+
+ {columnFilterModeOptions.map((option, index) => (
+ handleSelectFilter(option)} selected={option === defaultFilter} sx={{ cursor: 'pointer' }}>
+
+
+ ))}
+
+
+
+
+
+ );
+}
+
+export default FilterBox;
diff --git a/src/components/FilterColumn.jsx b/src/components/FilterColumn.jsx
new file mode 100644
index 0000000..08e42c0
--- /dev/null
+++ b/src/components/FilterColumn.jsx
@@ -0,0 +1,204 @@
+"use client"
+import {
+ Box,
+ Button, Divider, Drawer,
+ FormControl, IconButton, styled, Typography
+} from '@mui/material';
+import {useFormik} from 'formik';
+import TextFieldWithFnBox from "@/components/TextFieldWithFnBox";
+import {useEffect, useState} from "react";
+import useTableSetting from "@/lib/hooks/useTableSetting";
+import BetweenNumberFilter from "@/components/BetweenNumberFilter";
+import BetWeenDateFilter from "@/components/BetweenDateFilter";
+import SelectBoxMultiple from "@/components/SelectBoxMultiple";
+import SelectBox from "@/components/SelectBox";
+import CloseIcon from '@mui/icons-material/Close';
+import { useTheme } from '@mui/material/styles';
+
+function FilterColumn({ columns }) {
+ const [open, setOpen] = useState(false);
+ const [selectedFilters, setSelectedFilters] = useState([]);
+ const [userId, setUserId] = useState(2);
+ const [pageName, setPageName] = useState('loan');
+ const [tableName, setTableName] = useState('loan_table');
+ const {settingStore, hideAction, filterAction, sortAction,} = useTableSetting();
+
+ const DrawerHeader = styled('div')(({ theme }) => ({
+ display: 'flex',
+ alignItems: 'center',
+ padding: theme.spacing(0, 1),
+ justifyContent: 'space-between',
+ }));
+ const toggleDrawer = (newOpen) => () => {
+ setOpen(newOpen);
+ };
+ const handleDrawerClose = () => {
+ setOpen(false);
+ };
+ useEffect(() => {
+ const settingValue = settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || [];
+ setSelectedFilters(settingValue.length > 0 ? settingValue : []);
+ }, [settingStore]);
+
+ const handleSelectFilter = (fieldName, filterFn) => {
+ const filterIndex = selectedFilters.findIndex(filter => filter.id === fieldName);
+
+ if (filterIndex !== -1) {
+ const updatedFilters = [...selectedFilters];
+ updatedFilters[filterIndex].fn = filterFn;
+ setSelectedFilters(updatedFilters);
+ } else {
+ const newFilter = { id: fieldName, fn: filterFn };
+ const updatedFilters = [...selectedFilters, newFilter];
+ setSelectedFilters(updatedFilters);
+ }
+ };
+
+ const initialValues = columns.reduce((acc, column) => {
+ if (column.enableColumnFilter) {
+ const filter = settingStore?.[userId]?.[pageName]?.[tableName]?.['filters']?.find((filter) => filter.id === column.id);
+ if (column.filterFn === 'betweenNum' || column.filterFn === 'betweenDate') {
+ acc[column.id] = filter?.value || ["", ""];
+ }
+ else if ( column.type === 'select2' ) {
+ acc[column.id] = filter?.value || [];
+ }
+ else {
+ acc[column.id] = filter?.value || '';
+ }
+ }
+ return acc;
+ }, {});
+
+ const formik = useFormik({
+ initialValues,
+ onSubmit: (values) => {
+ const updatedFilters = selectedFilters.map(filter => {
+ const value = values[filter.id];
+ if (value !== undefined) {
+ return { ...filter, value };
+ }
+ return filter;
+ });
+ Object.entries(values).forEach(([fieldName, value]) => {
+ const filterIndex = updatedFilters.findIndex(filter => filter.id === fieldName);
+ if (filterIndex === -1 && value !== '' && value !== null && value !== undefined) {
+ const column = columns.find(col => col.id === fieldName);
+ updatedFilters.push({ id: fieldName, value, fn: column.filterFn, datatype: column.datatype });
+ }
+ });
+
+ const newArrayFilter = updatedFilters.filter((filter) => {
+ const value = values[filter.id];
+
+ return (
+ value !== '' &&
+ value !== null &&
+ value !== undefined &&
+ !(Array.isArray(value) && (value.length === 0 || (value.length === 2 && value.every((v) => v === ''))))
+ );
+ });
+
+ setSelectedFilters(newArrayFilter);
+ filterAction(userId, pageName, tableName, newArrayFilter, columns);
+ }
+
+ });
+
+ return (
+ <>
+
+
+
+ فیلتر
+
+
+
+
+
+
+ {columns.map((column, index) => (
+ column.enableColumnFilter &&
+ (column.filterFn === 'betweenDate' ?
+ handleSelectFilter(column.id, filterFn)}
+ defaultValue={(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || ''}
+ />
+ : column.filterFn === 'betweenNum' ?
+ handleSelectFilter(column.id, filterFn)}
+ defaultValue={(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || ["", ""]}
+ />
+ : column.type === 'select2' ?
+ handleSelectFilter(column.id, filterFn)}
+ defaultValue={(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || []}
+ />
+ : column.type === 'select' ?
+ handleSelectFilter(column.id, filterFn)}
+ defaultValue={(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || ''}
+ />
+ :
+ filter.id === column.id)?.fn || column.filterFn}
+ columnFilterModeOptions={column.columnFilterModeOptions}
+ onSelectFilter={(filterFn) => handleSelectFilter(column.id, filterFn)}
+ defaultValue={formik.values[column.id] || (settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || ''}
+ />
+ )
+ ))}
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
+
+export default FilterColumn;
+
+
diff --git a/src/components/MapTest.jsx b/src/components/MapTest.jsx
new file mode 100644
index 0000000..8ccea0a
--- /dev/null
+++ b/src/components/MapTest.jsx
@@ -0,0 +1,41 @@
+"use client"
+import {Typography} from "@mui/material";
+
+const data = [
+ {
+ id : "hello",
+ Subitems : [
+ {id : "amin",firstName : "amin", lastName:"ali",Subitems : [{id : "shahrokh",shahrokh : "shahrokh"}], hasSubitems : true, showSubitems : false},
+ {id : "amir",firstName : "amir", lastName:"akbar", hasSubitems : false, showSubitems : false},
+ ],
+ hasSubitems : true,
+ showSubitems : false
+ },
+ {
+ id : "hi",
+ Subitems : [
+ {id : "ali",firstName : "ali", lastName:"ali", hasSubitems : false, showSubitems : false},
+ {id : "akbar",firstName : "akbar", lastName:"akbar", hasSubitems : false, showSubitems : false},
+ ],
+ hasSubitems : true,
+ showSubitems : false
+ }
+]
+const MapTest = () => {
+ const Amin = data.map((item) => {
+ return item.hasSubitems ? {...item, Subitems: item.Subitems.map((subitem) => true ? {
+ ...subitem, showSubitems: !item.showSubitems
+ } : subitem)} : item
+ })
+
+
+ console.log(Amin)
+
+
+ return (
+ <>
+ hello
+ >
+ )
+}
+export default MapTest
\ No newline at end of file
diff --git a/src/components/MuiDatePicker.jsx b/src/components/MuiDatePicker.jsx
new file mode 100644
index 0000000..f2b0876
--- /dev/null
+++ b/src/components/MuiDatePicker.jsx
@@ -0,0 +1,67 @@
+import { LocalizationProvider, MobileDateTimePicker } from "@mui/x-date-pickers";
+import { AdapterDateFnsJalali } from "@mui/x-date-pickers/AdapterDateFnsJalali";
+import moment from "jalali-moment";
+import { faIR } from "@mui/x-date-pickers/locales";
+import { Box, IconButton, InputAdornment, TextField } from "@mui/material";
+import ClearIcon from "@mui/icons-material/Clear";
+import { useState } from "react";
+
+export default function MuiDatePicker({label, name, formik, defaultValue, minDate, maxDate, onDateChange }) {
+ const [pickerValue, setPickerValue] = useState(defaultValue ? new Date(defaultValue) : null);
+
+ return (
+
+
+ {
+ const date = new Date(newValue);
+ const formattedDate = moment(date)
+ .locale("en")
+ .format("YYYY-MM-DD HH:mm");
+ formik.setFieldValue(name, formattedDate);
+ onDateChange(formattedDate);
+ setPickerValue(newValue);
+ }}
+ slotProps={{
+ textField: {
+ placeholder: "تاریخ خود را وارد کنید",
+ helperText: label,
+ InputProps: {
+ endAdornment: (
+
+ {
+ event.stopPropagation();
+ formik.setFieldValue(name, "");
+ setPickerValue(null);
+ }}
+ sx={{
+ color: "#bfbfbf",
+ '&:hover': {
+ backgroundColor: 'rgba(189, 189, 189, 0.1)',
+ },
+ }}
+ >
+
+
+
+ ),
+ },
+ },
+ }}
+ value={pickerValue}
+ minDate={minDate ? new Date(minDate) : null}
+ maxDate={maxDate ? new Date(maxDate) : null}
+ />
+
+
+ );
+}
diff --git a/src/components/SelectBox.jsx b/src/components/SelectBox.jsx
new file mode 100644
index 0000000..88cce6e
--- /dev/null
+++ b/src/components/SelectBox.jsx
@@ -0,0 +1,53 @@
+import InputLabel from '@mui/material/InputLabel';
+import MenuItem from '@mui/material/MenuItem';
+import FormControl from '@mui/material/FormControl';
+import Select from '@mui/material/Select';
+import {useEffect, useState} from "react";
+const names = [
+ 'Oliver Hansen',
+ 'Van Henry',
+ 'April Tucker',
+ 'Ralph Hubbard',
+ 'Omar Alexander',
+ 'Carlos Abbott',
+ 'Miriam Wagner',
+ 'Bradley Wilkerson',
+ 'Virginia Andrews',
+ 'Kelly Snyder',
+];
+const SelectBox = ({ formik, id, defaultValue }) => {
+
+ useEffect(() => {
+ formik.setFieldValue(id, defaultValue || '');
+ }, []);
+
+ const [selectedValue, setSelectedValue] = useState(defaultValue || '');
+
+ const handleChange = (event) => {
+ const { value } = event.target;
+ setSelectedValue(value);
+ formik.setFieldValue(id, value);
+ };
+
+ return (
+ <>
+
+ انتخاب
+
+
+ >
+ );
+};
+
+export default SelectBox;
diff --git a/src/components/SelectBoxMultiple.jsx b/src/components/SelectBoxMultiple.jsx
new file mode 100644
index 0000000..4355c72
--- /dev/null
+++ b/src/components/SelectBoxMultiple.jsx
@@ -0,0 +1,116 @@
+import {useTheme} from '@mui/material/styles';
+import Box from '@mui/material/Box';
+import OutlinedInput from '@mui/material/OutlinedInput';
+import InputLabel from '@mui/material/InputLabel';
+import MenuItem from '@mui/material/MenuItem';
+import FormControl from '@mui/material/FormControl';
+import Select from '@mui/material/Select';
+import Chip from '@mui/material/Chip';
+import {useEffect, useState} from "react";
+
+const ITEM_HEIGHT = 48;
+const ITEM_PADDING_TOP = 8;
+const MenuProps = {
+ PaperProps: {
+ style: {
+ maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
+ width: 250,
+ },
+ },
+};
+
+const names = [
+ 'Oliver Hansen',
+ 'Van Henry',
+ 'April Tucker',
+ 'Ralph Hubbard',
+ 'Omar Alexander',
+ 'Carlos Abbott',
+ 'Miriam Wagner',
+ 'Bradley Wilkerson',
+ 'Virginia Andrews',
+ 'Kelly Snyder',
+];
+
+function getStyles(name, personName, theme) {
+ return {
+ fontWeight:
+ personName.indexOf(name) === -1
+ ? theme.typography.fontWeightRegular
+ : theme.typography.fontWeightMedium,
+ };
+}
+
+const SelectBoxMultiple = ({formik, id, defaultValue}) => {
+ const theme = useTheme();
+ const [personName, setPersonName] = useState(defaultValue || []);
+
+ useEffect(() => {
+ defaultValue.forEach((value, index) => {
+ formik.setFieldValue(`${id}[${index}]`, value || '');
+ });
+
+ }, []);
+
+ const handleChange = (event) => {
+ const {
+ target: {value},
+ } = event;
+
+ const selectedValues = typeof value === 'string' ? value.split(',').filter(item => item.trim() !== '') : value;
+
+ formik.setFieldValue(id, []);
+
+ if (selectedValues.length === 0) {
+ setPersonName([]);
+ formik.setFieldValue(id, []);
+ } else {
+ setPersonName(selectedValues);
+ selectedValues.forEach((selectedValue, index) => {
+ formik.setFieldValue(`${id}[${index}]`, selectedValue);
+ });
+ }
+ };
+
+ return (
+ <>
+
+ انتخاب
+ }
+ renderValue={(selected) => (
+
+ {selected.map((value) => (
+
+ ))}
+
+ )}
+ MenuProps={MenuProps}
+ sx={{
+ "&.MuiSelect-select": {
+ minWidth: 0,
+ maxWidth: '100%',
+ overflow: 'hidden',
+ },
+ }}
+ >
+ {names.map((name) => (
+
+ ))}
+
+
+ >
+ );
+};
+
+export default SelectBoxMultiple;
diff --git a/src/components/TextFieldWithFnBox.jsx b/src/components/TextFieldWithFnBox.jsx
new file mode 100644
index 0000000..6ba443b
--- /dev/null
+++ b/src/components/TextFieldWithFnBox.jsx
@@ -0,0 +1,51 @@
+import {IconButton, Paper, TextField} from '@mui/material';
+import ArtTrackIcon from '@mui/icons-material/ArtTrack';
+import {useEffect, useState} from "react";
+import FilterBox from "@/components/FilterBox";
+
+const TextFieldWithFnBox = ({formik, id , header, defaultValue, defaultFilter , columnFilterModeOptions ,onSelectFilter}) => {
+
+ const [anchorEl, setAnchorEl] = useState(null);
+ const [open, setOpen] = useState(false);
+
+ useEffect(() => {
+ formik.setFieldValue(id, defaultValue || '');
+ }, []);
+
+ const handleClick = (event) => {
+ setOpen((prevOpen) => !prevOpen);
+ setAnchorEl(anchorEl ? null : event.currentTarget);
+ };
+
+ const handleClose = () => {
+ setOpen(false);
+ setAnchorEl(null);
+ };
+
+ return (
+ <>
+
+
+
+ )
+ }}
+ sx={{
+ position: 'relative'
+ }}
+ />
+
+ >
+ );
+};
+
+export default TextFieldWithFnBox;
diff --git a/src/components/layouts/dashboard/headerWithLogo/index.jsx b/src/components/layouts/dashboard/headerWithLogo/index.jsx
new file mode 100644
index 0000000..f16f58c
--- /dev/null
+++ b/src/components/layouts/dashboard/headerWithLogo/index.jsx
@@ -0,0 +1,9 @@
+import {Box, Stack} from "@mui/material";
+
+const HeaderWithLogo = () => {
+ return (
+ header1
+ header2
+ )
+}
+export default HeaderWithLogo
\ No newline at end of file
diff --git a/src/components/layouts/dashboard/headerWithSidebar/index.jsx b/src/components/layouts/dashboard/headerWithSidebar/index.jsx
new file mode 100644
index 0000000..eeb88a3
--- /dev/null
+++ b/src/components/layouts/dashboard/headerWithSidebar/index.jsx
@@ -0,0 +1,94 @@
+'use client'
+import {useState} from "react";
+import {Box, Drawer, IconButton, Paper, styled, Toolbar, Typography} from "@mui/material";
+import MuiAppBar from '@mui/material/AppBar';
+import MenuIcon from '@mui/icons-material/Menu';
+import ChevronRightIcon from '@mui/icons-material/ChevronRight';
+import SidebarMenu from "@/core/components/SidebarMenu";
+
+const drawerWidth = 260;
+
+const Main = styled(Paper, {shouldForwardProp: (prop) => prop !== 'open'})(({theme, open}) => ({
+ flexGrow: 1, transition: theme.transitions.create('margin', {
+ easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.leavingScreen,
+ }), marginLeft: `-${drawerWidth}px`, ...(open && {
+ transition: theme.transitions.create('margin', {
+ easing: theme.transitions.easing.easeOut, duration: theme.transitions.duration.enteringScreen,
+ }), marginLeft: 0,
+ }),
+}),);
+
+const AppBar = styled(MuiAppBar, {
+ shouldForwardProp: (prop) => prop !== 'open',
+})(({theme, open}) => ({
+ transition: theme.transitions.create(['margin', 'width'], {
+ easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.leavingScreen,
+ }), ...(open && {
+ width: `calc(100% - ${drawerWidth}px)`,
+ marginLeft: `${drawerWidth}px`,
+ transition: theme.transitions.create(['margin', 'width'], {
+ easing: theme.transitions.easing.easeOut, duration: theme.transitions.duration.enteringScreen,
+ }),
+ }),
+}));
+
+const DrawerHeader = styled('div')(({theme}) => ({
+ display: 'flex', alignItems: 'center', padding: theme.spacing(0, 1), // necessary for content to be below app bar
+ ...theme.mixins.toolbar, justifyContent: 'flex-end',
+}));
+
+const HeaderWithSidebar = ({children}) => {
+ const [open, setOpen] = useState(false);
+
+ const handleDrawerOpen = () => {
+ setOpen(true);
+ };
+
+ const handleDrawerClose = () => {
+ setOpen(false);
+ };
+ return (
+
+
+
+
+
+
+ menu
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {children}
+
+ )
+}
+export default HeaderWithSidebar
\ No newline at end of file
diff --git a/src/core/components/DataTable/Main.js b/src/core/components/DataTable/Main.js
new file mode 100644
index 0000000..4a38956
--- /dev/null
+++ b/src/core/components/DataTable/Main.js
@@ -0,0 +1,19 @@
+import { useMaterialReactTable } from "material-react-table";
+import DataTable_Paper from "./table/Paper";
+
+const isTableInstanceProp = (props) => props.table !== undefined;
+
+const DataTable_Main = (props) => {
+ let table;
+
+ if (isTableInstanceProp(props)) {
+ table = props.table;
+ } else {
+ // eslint-disable-next-line react-hooks/rules-of-hooks
+ table = useMaterialReactTable(props);
+ }
+
+ return ;
+};
+
+export default DataTable_Main;
diff --git a/src/core/components/DataTable/body/TableBody.js b/src/core/components/DataTable/body/TableBody.js
new file mode 100644
index 0000000..e1620a3
--- /dev/null
+++ b/src/core/components/DataTable/body/TableBody.js
@@ -0,0 +1,207 @@
+import { parseFromValuesOrFunc } from "@/core/utils/utils";
+import { TableBody, Typography } from "@mui/material";
+import { useMRT_RowVirtualizer, useMRT_Rows } from "material-react-table";
+import { memo, useMemo } from "react";
+import DataTable_TableBodyRow, { Memo_DataTable_TableBodyRow } from "./TableBodyRow";
+
+const DataTable_TableBody = ({
+ columnVirtualizer,
+ table,
+ ...rest
+}) => {
+ const {
+ getBottomRows,
+ getIsSomeRowsPinned,
+ getRowModel,
+ getState,
+ getTopRows,
+ options: {
+ enableStickyFooter,
+ enableStickyHeader,
+ layoutMode,
+ localization,
+ memoMode,
+ muiTableBodyProps,
+ renderDetailPanel,
+ renderEmptyRowsFallback,
+ rowPinningDisplayMode,
+ },
+ refs: { tableFooterRef, tableHeadRef, tablePaperRef },
+ } = table;
+ const { columnFilters, globalFilter, isFullScreen, rowPinning } = getState();
+
+ const tableBodyProps = {
+ ...parseFromValuesOrFunc(muiTableBodyProps, { table }),
+ ...rest,
+ };
+
+ const tableHeadHeight =
+ ((enableStickyHeader || isFullScreen) &&
+ tableHeadRef.current?.clientHeight) ||
+ 0;
+ const tableFooterHeight =
+ (enableStickyFooter && tableFooterRef.current?.clientHeight) || 0;
+
+ const pinnedRowIds = useMemo(() => {
+ if (!rowPinning.bottom?.length && !rowPinning.top?.length) return [];
+ return getRowModel()
+ .rows.filter((row) => row.getIsPinned())
+ .map((r) => r.id);
+ }, [rowPinning, getRowModel().rows]);
+
+ const rows = useMRT_Rows(table);
+
+ const rowVirtualizer = useMRT_RowVirtualizer(table, rows);
+
+ const { virtualRows } = rowVirtualizer ?? {};
+
+ const commonRowProps = {
+ columnVirtualizer,
+ numRows: rows.length,
+ table,
+ };
+
+ return (
+ <>
+ {!rowPinningDisplayMode?.includes('sticky') &&
+ getIsSomeRowsPinned('top') && (
+ ({
+ display: layoutMode?.startsWith('grid') ? 'grid' : undefined,
+ position: 'sticky',
+ top: tableHeadHeight - 1,
+ zIndex: 1,
+ ...(parseFromValuesOrFunc(tableBodyProps?.sx, theme)),
+ })}
+ >
+ {getTopRows().map((row, staticRowIndex) => {
+ const props = {
+ ...commonRowProps,
+ row,
+ staticRowIndex,
+ };
+ return memoMode === 'rows' ? (
+
+ ) : (
+
+ );
+ })}
+
+ )}
+ ({
+ display: layoutMode?.startsWith('grid') ? 'grid' : undefined,
+ height: rowVirtualizer
+ ? `${rowVirtualizer.getTotalSize()}px`
+ : undefined,
+ minHeight: !rows.length ? '100px' : undefined,
+ position: 'relative',
+ ...(parseFromValuesOrFunc(tableBodyProps?.sx, theme)),
+ })}
+ >
+ {tableBodyProps?.children ??
+ (!rows.length ? (
+
+ |
+ {renderEmptyRowsFallback?.({ table }) ?? (
+
+ {globalFilter || columnFilters.length
+ ? localization.noResultsFound
+ : localization.noRecordsToDisplay}
+
+ )}
+ |
+
+ ) : (
+ <>
+ {(virtualRows ?? rows).map((rowOrVirtualRow, staticRowIndex) => {
+ let row = rowOrVirtualRow;
+ if (rowVirtualizer) {
+ if (renderDetailPanel) {
+ if (rowOrVirtualRow.index % 2 === 1) {
+ return null;
+ } else {
+ staticRowIndex = rowOrVirtualRow.index / 2;
+ }
+ } else {
+ staticRowIndex = rowOrVirtualRow.index;
+ }
+ row = rows[staticRowIndex];
+ }
+ const props = {
+ ...commonRowProps,
+ pinnedRowIds,
+ row,
+ rowVirtualizer,
+ staticRowIndex,
+ virtualRow: rowVirtualizer
+ ? (rowOrVirtualRow)
+ : undefined,
+ };
+ const key = `${row.id}-${row.index}`;
+ return memoMode === 'rows' ? (
+
+ ) : (
+
+ );
+ })}
+ >
+ ))}
+
+ {!rowPinningDisplayMode?.includes('sticky') &&
+ getIsSomeRowsPinned('bottom') && (
+ ({
+ bottom: tableFooterHeight - 1,
+ display: layoutMode?.startsWith('grid') ? 'grid' : undefined,
+ position: 'sticky',
+ zIndex: 1,
+ ...(parseFromValuesOrFunc(tableBodyProps?.sx, theme)),
+ })}
+ >
+ {getBottomRows().map((row, staticRowIndex) => {
+ const props = {
+ ...commonRowProps,
+ row,
+ staticRowIndex,
+ };
+ return memoMode === 'rows' ? (
+
+ ) : (
+
+ );
+ })}
+
+ )}
+ >
+ );
+
+}
+export default DataTable_TableBody
+
+export const Memo_DataTable_TableBody = memo(
+ DataTable_TableBody,
+ (prev, next) => prev.table.options.data === next.table.options.data,
+);
\ No newline at end of file
diff --git a/src/core/components/DataTable/body/TableBodyCell.js b/src/core/components/DataTable/body/TableBodyCell.js
new file mode 100644
index 0000000..9ef022d
--- /dev/null
+++ b/src/core/components/DataTable/body/TableBodyCell.js
@@ -0,0 +1,306 @@
+import { isCellEditable, openEditingCell } from "material-react-table";
+
+const { parseFromValuesOrFunc, getCommonMRTCellStyles } = require("@/core/utils/utils");
+const { useTheme } = require("@emotion/react");
+const { TableCell, Skeleton } = require("@mui/material");
+const { useState, useEffect, useMemo, memo } = require("react");
+const { default: DataTable_CopyButton } = require("../buttons/CopyButton");
+const { default: DataTable_TableBodyCellValue } = require("./TableBodyCellValue");
+
+const DataTable_TableBodyCell = ({
+ cell,
+ numRows,
+ rowRef,
+ staticColumnIndex,
+ staticRowIndex,
+ table,
+ ...rest
+}) => {
+ const theme = useTheme();
+ const {
+ getState,
+ options: {
+ columnResizeDirection,
+ columnResizeMode,
+ createDisplayMode,
+ editDisplayMode,
+ enableCellActions,
+ enableClickToCopy,
+ enableColumnOrdering,
+ enableColumnPinning,
+ enableGrouping,
+ layoutMode,
+ mrtTheme: { draggingBorderColor },
+ muiSkeletonProps,
+ muiTableBodyCellProps,
+ },
+ setHoveredColumn,
+ } = table;
+ const {
+ actionCell,
+ columnSizingInfo,
+ creatingRow,
+ density,
+ draggingColumn,
+ draggingRow,
+ editingCell,
+ editingRow,
+ hoveredColumn,
+ hoveredRow,
+ isLoading,
+ showSkeletons,
+ } = getState();
+ const { column, row } = cell;
+ const { columnDef } = column;
+ const { columnDefType } = columnDef;
+
+ const args = { cell, column, row, table };
+ const tableCellProps = {
+ ...parseFromValuesOrFunc(muiTableBodyCellProps, args),
+ ...parseFromValuesOrFunc(columnDef.muiTableBodyCellProps, args),
+ ...rest,
+ };
+
+ const skeletonProps = parseFromValuesOrFunc(muiSkeletonProps, {
+ cell,
+ column,
+ row,
+ table,
+ });
+
+ const [skeletonWidth, setSkeletonWidth] = useState(100);
+ useEffect(() => {
+ if ((!isLoading && !showSkeletons) || skeletonWidth !== 100) return;
+ const size = column.getSize();
+ setSkeletonWidth(
+ columnDefType === 'display'
+ ? size / 2
+ : Math.round(Math.random() * (size - size / 3) + size / 3),
+ );
+ }, [isLoading, showSkeletons]);
+
+ const draggingBorders = useMemo(() => {
+ const isDraggingColumn = draggingColumn?.id === column.id;
+ const isHoveredColumn = hoveredColumn?.id === column.id;
+ const isDraggingRow = draggingRow?.id === row.id;
+ const isHoveredRow = hoveredRow?.id === row.id;
+ const isFirstColumn = column.getIsFirstColumn();
+ const isLastColumn = column.getIsLastColumn();
+ const isLastRow = numRows && staticRowIndex === numRows - 1;
+ const isResizingColumn = columnSizingInfo.isResizingColumn === column.id;
+ const showResizeBorder =
+ isResizingColumn && columnResizeMode === 'onChange';
+
+ const borderStyle = showResizeBorder
+ ? `2px solid ${draggingBorderColor} !important`
+ : isDraggingColumn || isDraggingRow
+ ? `1px dashed ${theme.palette.grey[500]} !important`
+ : isHoveredColumn || isHoveredRow || isResizingColumn
+ ? `2px dashed ${draggingBorderColor} !important`
+ : undefined;
+
+ if (showResizeBorder) {
+ return columnResizeDirection === 'ltr'
+ ? { borderRight: borderStyle }
+ : { borderLeft: borderStyle };
+ }
+
+ return borderStyle
+ ? {
+ borderBottom:
+ isDraggingRow || isHoveredRow || (isLastRow && !isResizingColumn)
+ ? borderStyle
+ : undefined,
+ borderLeft:
+ isDraggingColumn ||
+ isHoveredColumn ||
+ ((isDraggingRow || isHoveredRow) && isFirstColumn)
+ ? borderStyle
+ : undefined,
+ borderRight:
+ isDraggingColumn ||
+ isHoveredColumn ||
+ ((isDraggingRow || isHoveredRow) && isLastColumn)
+ ? borderStyle
+ : undefined,
+ borderTop: isDraggingRow || isHoveredRow ? borderStyle : undefined,
+ }
+ : undefined;
+ }, [
+ columnSizingInfo.isResizingColumn,
+ draggingColumn,
+ draggingRow,
+ hoveredColumn,
+ hoveredRow,
+ staticRowIndex,
+ ]);
+
+ const isColumnPinned =
+ enableColumnPinning &&
+ columnDef.columnDefType !== 'group' &&
+ column.getIsPinned();
+
+ const isEditable = isCellEditable({ cell, table });
+
+ const isEditing =
+ isEditable &&
+ !['custom', 'modal'].includes(editDisplayMode) &&
+ (editDisplayMode === 'table' ||
+ editingRow?.id === row.id ||
+ editingCell?.id === cell.id) &&
+ !row.getIsGrouped();
+
+ const isCreating =
+ isEditable && createDisplayMode === 'row' && creatingRow?.id === row.id;
+
+ const showClickToCopyButton =
+ (parseFromValuesOrFunc(enableClickToCopy, cell) === true ||
+ parseFromValuesOrFunc(columnDef.enableClickToCopy, cell) === true) &&
+ !['context-menu', false].includes(
+ // @ts-ignore
+ parseFromValuesOrFunc(columnDef.enableClickToCopy, cell),
+ );
+
+ const isRightClickable = parseFromValuesOrFunc(enableCellActions, cell);
+
+ const cellValueProps = {
+ cell,
+ table,
+ };
+
+ const handleDoubleClick = (event) => {
+ tableCellProps?.onDoubleClick?.(event);
+ openEditingCell({ cell, table });
+ };
+
+ const handleDragEnter = (e) => {
+ tableCellProps?.onDragEnter?.(e);
+ if (enableGrouping && hoveredColumn?.id === 'drop-zone') {
+ setHoveredColumn(null);
+ }
+ if (enableColumnOrdering && draggingColumn) {
+ setHoveredColumn(
+ columnDef.enableColumnOrdering !== false ? column : null,
+ );
+ }
+ };
+
+ const handleDragOver = (e) => {
+ if (columnDef.enableColumnOrdering !== false) {
+ e.preventDefault();
+ }
+ };
+
+ const handleContextMenu = (e) => {
+ tableCellProps?.onContextMenu?.(e);
+ if (isRightClickable) {
+ e.preventDefault();
+ table.setActionCell(cell);
+ table.refs.actionCellRef.current = e.currentTarget;
+ }
+ };
+
+ return (
+ ({
+ '&:hover': {
+ outline:
+ actionCell?.id === cell.id ||
+ (editDisplayMode === 'cell' && isEditable) ||
+ (editDisplayMode === 'table' && (isCreating || isEditing))
+ ? `1px solid ${theme.palette.grey[500]}`
+ : undefined,
+ textOverflow: 'clip',
+ },
+ alignItems: layoutMode?.startsWith('grid') ? 'center' : undefined,
+ cursor: isRightClickable
+ ? 'context-menu'
+ : isEditable && editDisplayMode === 'cell'
+ ? 'pointer'
+ : 'inherit',
+ outline:
+ actionCell?.id === cell.id
+ ? `1px solid ${theme.palette.grey[500]}`
+ : undefined,
+ outlineOffset: '-1px',
+ overflow: 'hidden',
+ p:
+ density === 'compact'
+ ? columnDefType === 'display'
+ ? '0 0.5rem'
+ : '0.5rem'
+ : density === 'comfortable'
+ ? columnDefType === 'display'
+ ? '0.5rem 0.75rem'
+ : '1rem'
+ : columnDefType === 'display'
+ ? '1rem 1.25rem'
+ : '1.5rem',
+
+ textOverflow: columnDefType !== 'display' ? 'ellipsis' : undefined,
+ whiteSpace:
+ row.getIsPinned() || density === 'compact' ? 'nowrap' : 'normal',
+ ...getCommonMRTCellStyles({
+ column,
+ table,
+ tableCellProps,
+ theme,
+ }),
+ ...draggingBorders,
+ })}
+ >
+ {tableCellProps.children ?? (
+ <>
+ {cell.getIsPlaceholder() ? (
+ columnDef.PlaceholderCell?.({ cell, column, row, table }) ?? null
+ ) : showSkeletons !== false && (isLoading || showSkeletons) ? (
+
+ ) : columnDefType === 'display' &&
+ (['mrt-row-expand', 'mrt-row-numbers', 'mrt-row-select'].includes(
+ column.id,
+ ) ||
+ !row.getIsGrouped()) ? (
+ columnDef.Cell?.({
+ cell,
+ column,
+ renderedCellValue: cell.renderValue(),
+ row,
+ rowRef,
+ staticColumnIndex,
+ staticRowIndex,
+ table,
+ })
+ ) : showClickToCopyButton && columnDef.enableClickToCopy !== false ? (
+
+
+
+ ) : (
+
+ )}
+ {cell.getIsGrouped() && !columnDef.GroupedCell && (
+ <> ({row.subRows?.length})>
+ )}
+ >
+ )}
+
+ );
+
+}
+export default DataTable_TableBodyCell;
+
+export const Memo_DataTable_TableBodyCell = memo(
+ DataTable_TableBodyCell,
+ (prev, next) => next.cell === prev.cell,
+);
\ No newline at end of file
diff --git a/src/core/components/DataTable/body/TableBodyCellValue.js b/src/core/components/DataTable/body/TableBodyCellValue.js
new file mode 100644
index 0000000..6d4f05f
--- /dev/null
+++ b/src/core/components/DataTable/body/TableBodyCellValue.js
@@ -0,0 +1,112 @@
+import { Box } from "@mui/material";
+
+const allowedTypes = ["string", "number"];
+
+const DataTable_TableBodyCellValue = ({
+ cell,
+ rowRef,
+ staticColumnIndex,
+ staticRowIndex,
+ table,
+}) => {
+ const {
+ getState,
+ options: {
+ enableFilterMatchHighlighting,
+ mrtTheme: { matchHighlightColor },
+ },
+ } = table;
+ const { column, row } = cell;
+ const { columnDef } = column;
+ const { globalFilter, globalFilterFn } = getState();
+ const filterValue = column.getFilterValue();
+
+ let renderedCellValue =
+ cell.getIsAggregated() && columnDef.AggregatedCell
+ ? columnDef.AggregatedCell({
+ cell,
+ column,
+ row,
+ table,
+ })
+ : row.getIsGrouped() && !cell.getIsGrouped()
+ ? null
+ : cell.getIsGrouped() && columnDef.GroupedCell
+ ? columnDef.GroupedCell({
+ cell,
+ column,
+ row,
+ table,
+ })
+ : undefined;
+
+ const isGroupedValue = renderedCellValue !== undefined;
+
+ if (!isGroupedValue) {
+ renderedCellValue = cell.renderValue();
+ }
+
+ if (
+ enableFilterMatchHighlighting &&
+ columnDef.enableFilterMatchHighlighting !== false &&
+ String(renderedCellValue) &&
+ allowedTypes.includes(typeof renderedCellValue) &&
+ ((filterValue &&
+ allowedTypes.includes(typeof filterValue) &&
+ ["autocomplete", "text"].includes(columnDef.filterVariant)) ||
+ (globalFilter &&
+ allowedTypes.includes(typeof globalFilter) &&
+ column.getCanGlobalFilter()))
+ ) {
+ const chunks = highlightWords?.({
+ matchExactly:
+ (filterValue ? columnDef._filterFn : globalFilterFn) !== "fuzzy",
+ query: (filterValue ?? globalFilter ?? "").toString(),
+ text: renderedCellValue?.toString(),
+ });
+ if (chunks?.length > 1 || chunks?.[0]?.match) {
+ renderedCellValue = (
+
+ {chunks?.map(({ key, match, text }) => (
+
+ theme.palette.mode === "dark"
+ ? theme.palette.common.white
+ : theme.palette.common.black,
+ padding: "2px 1px",
+ }
+ : undefined
+ }
+ >
+ {text}
+
+ )) ?? renderedCellValue}
+
+ );
+ }
+ }
+
+ if (columnDef.Cell && !isGroupedValue) {
+ renderedCellValue = columnDef.Cell({
+ cell,
+ column,
+ renderedCellValue,
+ row,
+ rowRef,
+ staticColumnIndex,
+ staticRowIndex,
+ table,
+ });
+ }
+
+ return renderedCellValue;
+};
+export default DataTable_TableBodyCellValue;
diff --git a/src/core/components/DataTable/body/TableBodyRow.js b/src/core/components/DataTable/body/TableBodyRow.js
new file mode 100644
index 0000000..e500eb0
--- /dev/null
+++ b/src/core/components/DataTable/body/TableBodyRow.js
@@ -0,0 +1,254 @@
+import { commonCellBeforeAfterStyles, getCommonPinnedCellStyles, parseFromValuesOrFunc } from "@/core/utils/utils";
+import { useTheme } from "@emotion/react";
+import { TableRow, alpha, darken, lighten } from "@mui/material";
+import { getIsRowSelected } from "material-react-table";
+import { memo, useMemo, useRef } from "react";
+import DataTable_TableBodyCell, { Memo_DataTable_TableBodyCell } from "./TableBodyCell";
+import DataTable_TableDetailPanel from "./TableDetailPanel";
+
+const DataTable_TableBodyRow = ({
+ columnVirtualizer,
+ numRows,
+ pinnedRowIds,
+ row,
+ rowVirtualizer,
+ staticRowIndex,
+ table,
+ virtualRow,
+ ...rest
+}) => {
+ const theme = useTheme();
+
+ const {
+ getState,
+ options: {
+ enableRowOrdering,
+ enableRowPinning,
+ enableStickyFooter,
+ enableStickyHeader,
+ layoutMode,
+ memoMode,
+ mrtTheme: {
+ baseBackgroundColor,
+ pinnedRowBackgroundColor,
+ selectedRowBackgroundColor,
+ },
+ muiTableBodyRowProps,
+ renderDetailPanel,
+ rowPinningDisplayMode,
+ },
+ refs: { tableFooterRef, tableHeadRef },
+ setHoveredRow,
+ } = table;
+ const {
+ density,
+ draggingColumn,
+ draggingRow,
+ editingCell,
+ editingRow,
+ hoveredRow,
+ isFullScreen,
+ rowPinning,
+ } = getState();
+
+ const visibleCells = row.getVisibleCells();
+
+ const { virtualColumns, virtualPaddingLeft, virtualPaddingRight } =
+ columnVirtualizer ?? {};
+
+ const isRowSelected = getIsRowSelected({ row, table });
+ const isRowPinned = enableRowPinning && row.getIsPinned();
+ const isDraggingRow = draggingRow?.id === row.id;
+ const isHoveredRow = hoveredRow?.id === row.id;
+
+ const tableRowProps = {
+ ...parseFromValuesOrFunc(muiTableBodyRowProps, {
+ row,
+ staticRowIndex,
+ table,
+ }),
+ ...rest,
+ };
+
+ const [bottomPinnedIndex, topPinnedIndex] = useMemo(() => {
+ if (
+ !enableRowPinning ||
+ !rowPinningDisplayMode?.includes('sticky') ||
+ !pinnedRowIds ||
+ !row.getIsPinned()
+ )
+ return [];
+ return [
+ [...pinnedRowIds].reverse().indexOf(row.id),
+ pinnedRowIds.indexOf(row.id),
+ ];
+ }, [pinnedRowIds, rowPinning]);
+
+ const tableHeadHeight =
+ ((enableStickyHeader || isFullScreen) &&
+ tableHeadRef.current?.clientHeight) ||
+ 0;
+ const tableFooterHeight =
+ (enableStickyFooter && tableFooterRef.current?.clientHeight) || 0;
+
+ const sx = parseFromValuesOrFunc(tableRowProps?.sx, theme);
+
+ const defaultRowHeight =
+ density === 'compact' ? 37 : density === 'comfortable' ? 53 : 69;
+
+ const customRowHeight =
+ // @ts-ignore
+ parseInt(tableRowProps?.style?.height ?? sx?.height, 10) || undefined;
+
+ const rowHeight = customRowHeight || defaultRowHeight;
+
+ const handleDragEnter = (_e) => {
+ if (enableRowOrdering && draggingRow) {
+ setHoveredRow(row);
+ }
+ };
+
+ const handleDragOver = (e) => {
+ e.preventDefault();
+ };
+
+ const rowRef = useRef(null);
+
+ const cellHighlightColor = isRowSelected
+ ? selectedRowBackgroundColor
+ : isRowPinned
+ ? pinnedRowBackgroundColor
+ : undefined;
+
+ const cellHighlightColorHover =
+ tableRowProps?.hover !== false
+ ? isRowSelected
+ ? cellHighlightColor
+ : theme.palette.mode === 'dark'
+ ? `${lighten(baseBackgroundColor, 0.3)}`
+ : `${darken(baseBackgroundColor, 0.3)}`
+ : undefined;
+
+ return (
+ <>
+ {
+ if (node) {
+ rowRef.current = node;
+ rowVirtualizer?.measureElement(node);
+ }
+ }}
+ selected={isRowSelected}
+ {...tableRowProps}
+ style={{
+ transform: virtualRow
+ ? `translateY(${virtualRow.start}px)`
+ : undefined,
+ ...tableRowProps?.style,
+ }}
+ sx={(theme) => ({
+ '&:hover td:after': cellHighlightColorHover
+ ? {
+ backgroundColor: alpha(cellHighlightColorHover, 0.3),
+ ...commonCellBeforeAfterStyles,
+ }
+ : undefined,
+ backgroundColor: `${baseBackgroundColor} !important`,
+ bottom:
+ !virtualRow && bottomPinnedIndex !== undefined && isRowPinned
+ ? `${bottomPinnedIndex * rowHeight +
+ (enableStickyFooter ? tableFooterHeight - 1 : 0)
+ }px`
+ : undefined,
+ boxSizing: 'border-box',
+ display: layoutMode?.startsWith('grid') ? 'flex' : undefined,
+ opacity: isRowPinned ? 0.97 : isDraggingRow || isHoveredRow ? 0.5 : 1,
+ position: virtualRow
+ ? 'absolute'
+ : rowPinningDisplayMode?.includes('sticky') && isRowPinned
+ ? 'sticky'
+ : 'relative',
+ td: {
+ ...getCommonPinnedCellStyles({ table, theme }),
+ },
+ 'td:after': cellHighlightColor
+ ? {
+ backgroundColor: cellHighlightColor,
+ ...commonCellBeforeAfterStyles,
+ }
+ : undefined,
+ top: virtualRow
+ ? 0
+ : topPinnedIndex !== undefined && isRowPinned
+ ? `${topPinnedIndex * rowHeight +
+ (enableStickyHeader || isFullScreen ? tableHeadHeight - 1 : 0)
+ }px`
+ : undefined,
+ transition: virtualRow ? 'none' : 'all 150ms ease-in-out',
+ width: '100%',
+ zIndex:
+ rowPinningDisplayMode?.includes('sticky') && isRowPinned ? 2 : 0,
+ ...(sx),
+ })}
+ >
+ {virtualPaddingLeft ? (
+ |
+ ) : null}
+ {(virtualColumns ?? visibleCells).map(
+ (cellOrVirtualCell, staticColumnIndex) => {
+ let cell = cellOrVirtualCell;
+ if (columnVirtualizer) {
+ staticColumnIndex = (cellOrVirtualCell).index;
+ cell = visibleCells[staticColumnIndex];
+ }
+ const props = {
+ cell,
+ numRows,
+ rowRef,
+ staticColumnIndex,
+ staticRowIndex,
+ table,
+ };
+ return cell ? (
+ memoMode === 'cells' &&
+ cell.column.columnDef.columnDefType === 'data' &&
+ !draggingColumn &&
+ !draggingRow &&
+ editingCell?.id !== cell.id &&
+ editingRow?.id !== row.id ? (
+
+ ) : (
+
+ )
+ ) : null;
+ },
+ )}
+ {virtualPaddingRight ? (
+ |
+ ) : null}
+
+ {renderDetailPanel && !row.getIsGrouped() && (
+
+ )}
+ >
+ );
+}
+export default DataTable_TableBodyRow
+
+export const Memo_DataTable_TableBodyRow = memo(
+ DataTable_TableBodyRow,
+ (prev, next) =>
+ prev.row === next.row && prev.staticRowIndex === next.staticRowIndex,
+);
\ No newline at end of file
diff --git a/src/core/components/DataTable/body/TableDetailPanel.js b/src/core/components/DataTable/body/TableDetailPanel.js
new file mode 100644
index 0000000..0c9f8a9
--- /dev/null
+++ b/src/core/components/DataTable/body/TableDetailPanel.js
@@ -0,0 +1,93 @@
+import { parseFromValuesOrFunc } from "@/core/utils/utils";
+import { Collapse, TableCell, TableRow } from "@mui/material";
+
+const DataTable_TableDetailPanel = ({
+ parentRowRef,
+ row,
+ rowVirtualizer,
+ staticRowIndex,
+ table,
+ virtualRow,
+ ...rest
+}) => {
+ const {
+ getState,
+ getVisibleLeafColumns,
+ options: {
+ layoutMode,
+ mrtTheme: { baseBackgroundColor },
+ muiDetailPanelProps,
+ muiTableBodyRowProps,
+ renderDetailPanel,
+ },
+ } = table;
+ const { isLoading } = getState();
+
+
+ const tableRowProps = parseFromValuesOrFunc(muiTableBodyRowProps, {
+ isDetailPanel: true,
+ row,
+ staticRowIndex,
+ table,
+ });
+
+ const tableCellProps = {
+ ...parseFromValuesOrFunc(muiDetailPanelProps, {
+ row,
+ table,
+ }),
+ ...rest,
+ };
+
+ const DetailPanel = !isLoading && renderDetailPanel?.({ row, table });
+
+ return (
+ {
+ if (node) {
+ rowVirtualizer?.measureElement?.(node);
+ }
+ }}
+ {...tableRowProps}
+ sx={(theme) => ({
+ display: layoutMode?.startsWith('grid') ? 'flex' : undefined,
+ position: virtualRow ? 'absolute' : undefined,
+ top: virtualRow
+ ? `${parentRowRef.current?.getBoundingClientRect()?.height}px`
+ : undefined,
+ transform: virtualRow
+ ? `translateY(${virtualRow?.start}px)`
+ : undefined,
+ width: '100%',
+ ...(parseFromValuesOrFunc(tableRowProps?.sx, theme)),
+ })}
+ >
+ ({
+ backgroundColor: virtualRow ? baseBackgroundColor : undefined,
+ borderBottom: !row.getIsExpanded() ? 'none' : undefined,
+ display: layoutMode?.startsWith('grid') ? 'flex' : undefined,
+ py: !!DetailPanel && row.getIsExpanded() ? '1rem' : 0,
+ transition: !virtualRow ? 'all 150ms ease-in-out' : undefined,
+ width: `100%`,
+ ...(parseFromValuesOrFunc(tableCellProps?.sx, theme)),
+ })}
+ >
+ {virtualRow ? (
+ row.getIsExpanded() && DetailPanel
+ ) : (
+
+ {DetailPanel}
+
+ )}
+
+
+ );
+
+}
+export default DataTable_TableDetailPanel
\ No newline at end of file
diff --git a/src/core/components/DataTable/buttons/CopyButton.js b/src/core/components/DataTable/buttons/CopyButton.js
new file mode 100644
index 0000000..c8878e0
--- /dev/null
+++ b/src/core/components/DataTable/buttons/CopyButton.js
@@ -0,0 +1,75 @@
+import { getCommonTooltipProps, parseFromValuesOrFunc } from "@/core/utils/utils";
+import { Button, Tooltip } from "@mui/material";
+import { useState } from "react";
+
+const DataTable_CopyButton = ({
+ cell,
+ table,
+ ...rest
+}) => {
+ const {
+ options: { localization, muiCopyButtonProps },
+ } = table;
+ const { column, row } = cell;
+ const { columnDef } = column;
+
+ const [copied, setCopied] = useState(false);
+
+ const handleCopy = (event, text) => {
+ event.stopPropagation();
+ navigator.clipboard.writeText(text);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 4000);
+ };
+
+ const buttonProps = {
+ ...parseFromValuesOrFunc(muiCopyButtonProps, {
+ cell,
+ column,
+ row,
+ table,
+ }),
+ ...parseFromValuesOrFunc(columnDef.muiCopyButtonProps, {
+ cell,
+ column,
+ row,
+ table,
+ }),
+ ...rest,
+ };
+
+ return (
+
+
+ );
+}
+export default DataTable_CopyButton
\ No newline at end of file
diff --git a/src/core/components/DataTable/buttons/GrabHandleButton.js b/src/core/components/DataTable/buttons/GrabHandleButton.js
new file mode 100644
index 0000000..5619abb
--- /dev/null
+++ b/src/core/components/DataTable/buttons/GrabHandleButton.js
@@ -0,0 +1,54 @@
+import { getCommonTooltipProps, parseFromValuesOrFunc } from "@/core/utils/utils";
+import { IconButton, Tooltip } from "@mui/material";
+
+const DataTable_GrabHandleButton = ({
+ location,
+ table,
+ ...rest
+}) => {
+ const {
+ options: {
+ icons: { DragHandleIcon },
+ localization,
+ },
+ } = table;
+
+ return (
+
+ {
+ e.stopPropagation();
+ rest?.onClick?.(e);
+ }}
+ sx={(theme) => ({
+ '&:active': {
+ cursor: 'grabbing',
+ },
+ '&:hover': {
+ backgroundColor: 'transparent',
+ opacity: 1,
+ },
+ cursor: 'grab',
+ m: '0 -0.1rem',
+ opacity: location === 'row' ? 1 : 0.5,
+ p: '2px',
+ transition: 'all 150ms ease-in-out',
+ ...(parseFromValuesOrFunc(rest?.sx, theme)),
+ })}
+ title={undefined}
+ >
+
+
+
+ );
+
+}
+export default DataTable_GrabHandleButton
\ No newline at end of file
diff --git a/src/core/components/DataTable/buttons/ShowHideColumnsButton.js b/src/core/components/DataTable/buttons/ShowHideColumnsButton.js
new file mode 100644
index 0000000..d46281e
--- /dev/null
+++ b/src/core/components/DataTable/buttons/ShowHideColumnsButton.js
@@ -0,0 +1,41 @@
+import { IconButton, Tooltip } from "@mui/material";
+import { useState } from "react";
+import DataTable_ShowHideColumnsMenu from "../menus/ShowHideColumnsMenu";
+
+const DataTable_ShowHideColumnsButton = ({ table, ...rest }) => {
+ const {
+ options: {
+ icons: { ViewColumnIcon },
+ localization,
+ },
+ } = table;
+
+ const [anchorEl, setAnchorEl] = useState(null);
+
+ const handleClick = (event) => {
+ setAnchorEl(event.currentTarget);
+ };
+
+ return (
+ <>
+
+
+
+
+
+ {anchorEl && (
+
+ )}
+ >
+ );
+}
+export default DataTable_ShowHideColumnsButton
\ No newline at end of file
diff --git a/src/core/components/DataTable/buttons/ToggleFiltersButton.js b/src/core/components/DataTable/buttons/ToggleFiltersButton.js
new file mode 100644
index 0000000..9885183
--- /dev/null
+++ b/src/core/components/DataTable/buttons/ToggleFiltersButton.js
@@ -0,0 +1,4 @@
+const DataTable_ToggleFiltersButton = ({ table, ...rest }) => {
+ return <>>
+}
+export default DataTable_ToggleFiltersButton
\ No newline at end of file
diff --git a/src/core/components/DataTable/head/TableHead.js b/src/core/components/DataTable/head/TableHead.js
new file mode 100644
index 0000000..055a4ed
--- /dev/null
+++ b/src/core/components/DataTable/head/TableHead.js
@@ -0,0 +1,63 @@
+import { parseFromValuesOrFunc } from "@/core/utils/utils";
+import { TableHead } from "@mui/material";
+import DataTable_TableHeadRow from "./TableHeadRow";
+
+const DataTable_TableHead = ({
+ columnVirtualizer,
+ table,
+ ...rest
+}) => {
+ const {
+ getState,
+ options: {
+ enableStickyHeader,
+ layoutMode,
+ muiTableHeadProps,
+ positionToolbarAlertBanner,
+ },
+ refs: { tableHeadRef },
+ } = table;
+ const { isFullScreen, showAlertBanner } = getState();
+
+ const tableHeadProps = {
+ ...parseFromValuesOrFunc(muiTableHeadProps, { table }),
+ ...rest,
+ };
+
+ const stickyHeader = enableStickyHeader || isFullScreen;
+
+ return (
+ {
+ tableHeadRef.current = ref;
+ if (tableHeadProps?.ref) {
+ // @ts-ignore
+ tableHeadProps.ref.current = ref;
+ }
+ }}
+ sx={(theme) => ({
+ display: layoutMode?.startsWith('grid') ? 'grid' : undefined,
+ opacity: 0.97,
+ position: stickyHeader ? 'sticky' : 'relative',
+ top: stickyHeader && layoutMode?.startsWith('grid') ? 0 : undefined,
+ zIndex: stickyHeader ? 2 : undefined,
+ ...(parseFromValuesOrFunc(tableHeadProps?.sx, theme)),
+ })}
+ >
+ {table
+ .getHeaderGroups()
+ .map((headerGroup) => (
+
+ ))
+ }
+
+ );
+
+}
+export default DataTable_TableHead
\ No newline at end of file
diff --git a/src/core/components/DataTable/head/TableHeadCell.js b/src/core/components/DataTable/head/TableHeadCell.js
new file mode 100644
index 0000000..78a9933
--- /dev/null
+++ b/src/core/components/DataTable/head/TableHeadCell.js
@@ -0,0 +1,283 @@
+import { getCommonMRTCellStyles, parseFromValuesOrFunc } from "@/core/utils/utils";
+import { useTheme } from "@emotion/react";
+import { Box, TableCell } from "@mui/material";
+import { MRT_TableHeadCellSortLabel } from "material-react-table";
+import { useMemo } from "react";
+
+const DataTable_TableHeadCell = ({
+ columnVirtualizer,
+ header,
+ staticColumnIndex,
+ table,
+ ...rest
+}) => {
+ const theme = useTheme();
+ const {
+ getState,
+ options: {
+ columnResizeDirection,
+ columnResizeMode,
+ enableColumnActions,
+ enableColumnDragging,
+ enableColumnOrdering,
+ enableColumnPinning,
+ enableGrouping,
+ enableMultiSort,
+ layoutMode,
+ mrtTheme: { draggingBorderColor },
+ muiTableHeadCellProps,
+ },
+ refs: { tableHeadCellRefs },
+ setHoveredColumn,
+ } = table;
+ const {
+ columnSizingInfo,
+ density,
+ draggingColumn,
+ grouping,
+ hoveredColumn,
+ showColumnFilters,
+ } = getState();
+ const { column } = header;
+ const { columnDef } = column;
+ const { columnDefType } = columnDef;
+
+ const tableCellProps = {
+ ...parseFromValuesOrFunc(muiTableHeadCellProps, { column, table }),
+ ...parseFromValuesOrFunc(columnDef.muiTableHeadCellProps, {
+ column,
+ table,
+ }),
+ ...rest,
+ };
+
+ const isColumnPinned =
+ enableColumnPinning &&
+ columnDef.columnDefType !== 'group' &&
+ column.getIsPinned();
+
+ const showColumnActions =
+ (enableColumnActions || columnDef.enableColumnActions) &&
+ columnDef.enableColumnActions !== false;
+
+ const showDragHandle =
+ enableColumnDragging !== false &&
+ columnDef.enableColumnDragging !== false &&
+ (enableColumnDragging ||
+ (enableColumnOrdering && columnDef.enableColumnOrdering !== false) ||
+ (enableGrouping &&
+ columnDef.enableGrouping !== false &&
+ !grouping.includes(column.id)));
+
+ const headerPL = useMemo(() => {
+ let pl = 0;
+ if (column.getCanSort()) pl += 1;
+ if (showColumnActions) pl += 1.75;
+ if (showDragHandle) pl += 1.5;
+ return pl;
+ }, [showColumnActions, showDragHandle]);
+
+ const draggingBorders = useMemo(() => {
+ const showResizeBorder =
+ columnSizingInfo.isResizingColumn === column.id &&
+ columnResizeMode === 'onChange' &&
+ !header.subHeaders.length;
+
+ const borderStyle = showResizeBorder
+ ? `2px solid ${draggingBorderColor} !important`
+ : draggingColumn?.id === column.id
+ ? `1px dashed ${theme.palette.grey[500]}`
+ : hoveredColumn?.id === column.id
+ ? `2px dashed ${draggingBorderColor}`
+ : undefined;
+
+ if (showResizeBorder) {
+ return columnResizeDirection === 'ltr'
+ ? { borderRight: borderStyle }
+ : { borderLeft: borderStyle };
+ }
+ const draggingBorders = borderStyle
+ ? {
+ borderLeft: borderStyle,
+ borderRight: borderStyle,
+ borderTop: borderStyle,
+ }
+ : undefined;
+
+ return draggingBorders;
+ }, [draggingColumn, hoveredColumn, columnSizingInfo.isResizingColumn]);
+
+ const handleDragEnter = (_e) => {
+ if (enableGrouping && hoveredColumn?.id === 'drop-zone') {
+ setHoveredColumn(null);
+ }
+ if (enableColumnOrdering && draggingColumn && columnDefType !== 'group') {
+ setHoveredColumn(
+ columnDef.enableColumnOrdering !== false ? column : null,
+ );
+ }
+ };
+
+ const handleDragOver = (e) => {
+ if (columnDef.enableColumnOrdering !== false) {
+ e.preventDefault();
+ }
+ };
+
+
+ const HeaderElement =
+ parseFromValuesOrFunc(columnDef.Header, {
+ column,
+ header,
+ table,
+ }) ?? columnDef.header;
+
+
+ const columnRelativeDepth = header.depth - column.depth;
+
+ if (
+ columnRelativeDepth > 1
+ ) {
+ return null;
+ }
+
+ let rowSpan = 1;
+ if (header.isPlaceholder) {
+ const leafs = header.getLeafHeaders();
+ rowSpan = leafs[leafs.length - 1].depth - header.depth;
+ }
+
+ return (
+ {
+ if (node) {
+ tableHeadCellRefs.current[column.id] = node;
+ if (columnDefType !== 'group') {
+ columnVirtualizer?.measureElement?.(node);
+ }
+ }
+ }}
+ {...tableCellProps}
+ sx={(theme) => ({
+ '& :hover': {
+ '.MuiButtonBase-root': {
+ opacity: 1,
+ },
+ },
+ flexDirection: layoutMode?.startsWith('grid') ? 'column' : undefined,
+ fontWeight: 'bold',
+ overflow: 'visible',
+ p:
+ density === 'compact'
+ ? '0.5rem'
+ : density === 'comfortable'
+ ? columnDefType === 'display'
+ ? '0.75rem'
+ : '1rem'
+ : columnDefType === 'display'
+ ? '1rem 1.25rem'
+ : '1.5rem',
+ pb:
+ columnDefType === 'display'
+ ? 0
+ : showColumnFilters || density === 'compact'
+ ? '0.4rem'
+ : '0.6rem',
+ pt:
+ columnDefType === 'group' || density === 'compact'
+ ? '0.25rem'
+ : density === 'comfortable'
+ ? '.75rem'
+ : '1.25rem',
+ userSelect: enableMultiSort && column.getCanSort() ? 'none' : undefined,
+ verticalAlign: 'middle',
+ ...getCommonMRTCellStyles({
+ column,
+ header,
+ table,
+ tableCellProps,
+ theme,
+ }),
+ ...draggingBorders,
+ }
+ )}
+ >
+ {tableCellProps.children ?? (
+
+
+
+ {HeaderElement}
+
+ {(column.getCanSort() && columnDefType !== 'group') && (
+
+ )}
+
+
+ )}
+
+ );
+
+}
+export default DataTable_TableHeadCell;
\ No newline at end of file
diff --git a/src/core/components/DataTable/head/TableHeadRow.js b/src/core/components/DataTable/head/TableHeadRow.js
new file mode 100644
index 0000000..f876cf1
--- /dev/null
+++ b/src/core/components/DataTable/head/TableHeadRow.js
@@ -0,0 +1,76 @@
+import { parseFromValuesOrFunc } from "@/core/utils/utils";
+import { TableRow } from "@mui/material";
+import DataTable_TableHeadCell from "./TableHeadCell";
+
+const DataTable_TableHeadRow = ({
+ columnVirtualizer,
+ headerGroup,
+ table,
+ ...rest
+}) => {
+ const {
+ options: {
+ enableStickyHeader,
+ layoutMode,
+ mrtTheme: { baseBackgroundColor },
+ muiTableHeadRowProps,
+ },
+ } = table;
+
+ const { virtualColumns, virtualPaddingLeft, virtualPaddingRight } =
+ columnVirtualizer ?? {};
+
+ const tableRowProps = {
+ ...parseFromValuesOrFunc(muiTableHeadRowProps, {
+ headerGroup,
+ table,
+ }),
+ ...rest,
+ };
+
+ return (
+ ({
+ backgroundColor: baseBackgroundColor,
+ display: layoutMode?.startsWith('grid') ? 'flex' : undefined,
+ position:
+ enableStickyHeader && layoutMode === 'semantic'
+ ? 'sticky'
+ : 'relative',
+ top: 0,
+ ...(parseFromValuesOrFunc(tableRowProps?.sx, theme)),
+ })}
+ >
+ {virtualPaddingLeft ? (
+ |
+ ) : null}
+ {(virtualColumns ?? headerGroup.headers).map(
+ (headerOrVirtualHeader, staticColumnIndex) => {
+ let header = headerOrVirtualHeader;
+
+ if (columnVirtualizer) {
+ staticColumnIndex = (headerOrVirtualHeader)
+ .index;
+ header = headerGroup.headers[staticColumnIndex];
+ }
+
+ return header ? (
+
+ ) : null;
+ },
+ )}
+ {virtualPaddingRight ? (
+ |
+ ) : null}
+
+ );
+
+}
+export default DataTable_TableHeadRow
\ No newline at end of file
diff --git a/src/core/components/DataTable/index.js b/src/core/components/DataTable/index.js
new file mode 100644
index 0000000..f453ade
--- /dev/null
+++ b/src/core/components/DataTable/index.js
@@ -0,0 +1,123 @@
+import { useMaterialReactTable } from "material-react-table";
+import DataTable_Main from "./Main";
+import { FA_DATATABLE_LOCALIZATION } from "./localization/fa/datatable";
+import CustomToolbar from "@/components/CustomToolbar";
+import useTableSetting from "@/lib/hooks/useTableSetting";
+import {flattenArrayOfObjects} from "@/core/utils/flattenArrayOfObjects";
+
+const data = [
+ {
+ name: {
+ firstName: 'John',
+ lastName: 'Doe',
+ },
+ address: '261 Erdman Ford',
+ city: 'East Daphne',
+ state: 'Kentucky',
+ },
+ {
+ name: {
+ firstName: 'Jane',
+ lastName: 'Doe',
+ },
+ address: '769 Dominic Grove',
+ city: 'Columbus',
+ state: 'Ohio',
+ },
+ {
+ name: {
+ firstName: 'Joe',
+ lastName: 'Doe',
+ },
+ address: '566 Brakus Inlet',
+ city: 'South Linda',
+ state: 'West Virginia',
+ },
+ {
+ name: {
+ firstName: 'Kevin',
+ lastName: 'Vandy',
+ },
+ address: '722 Emie Stream',
+ city: 'Lincoln',
+ state: 'Nebraska',
+ },
+ {
+ name: {
+ firstName: 'Joshua',
+ lastName: 'Rolluffs',
+ },
+ address: '32188 Larkin Turnpike',
+ city: 'Charleston',
+ state: 'South Carolina',
+ },
+];
+
+const DataTable = (props) => {
+ const { userId, pageName, tableName, columns, initialStateProps } = props
+ const { settingStore, hideAction, sortAction } = useTableSetting();
+ const flatColumns = flattenArrayOfObjects(columns,'columns')
+
+ const onColumnVisibilityChange = (event) => {
+ const settingValue = event();
+ hideAction(userId, pageName, tableName, settingValue, flatColumns);
+ };
+ const onSortingChange = (event) => {
+ const settingValue = event(settingStore?.[userId]?.[pageName]?.[tableName]?.['sorts'] || []);
+ sortAction(userId, pageName, tableName, settingValue, flatColumns);
+ }
+
+
+ const table = useMaterialReactTable({
+ localization: FA_DATATABLE_LOCALIZATION,
+ columns,
+ data: data,
+ // manualSorting: true,
+ renderTopToolbarCustomActions: ({ table }) => (<>
+
+ >),
+ initialState: {
+ density: 'compact',
+ columnVisibility: settingStore?.[userId]?.[pageName]?.[tableName]?.['hides'],
+ sorting: settingStore?.[userId]?.[pageName]?.[tableName]?.['sorts'] || [],
+ ...initialStateProps
+ },
+ state: {
+ columnVisibility: settingStore?.[userId]?.[pageName]?.[tableName]?.['hides'],
+ sorting: settingStore?.[userId]?.[pageName]?.[tableName]?.['sorts'] || []
+ },
+ muiTableHeadProps: {
+ sx: {
+ borderTop: "1px solid #e1e1e1",
+ borderBottom: "2px solid #e1e1e1",
+ }
+ },
+ muiTableHeadCellProps: {
+ sx: {
+ color: "primary.main",
+ borderLeft: "1px solid #e1e1e1",
+ "&:first-of-type": {
+ borderLeft: "unset"
+ }
+ },
+ },
+ muiTableBodyCellProps: {
+ sx: {
+ borderLeft: "1px solid #e1e1e1",
+ "&:first-of-type": {
+ borderLeft: "unset"
+ }
+ },
+ },
+ onColumnVisibilityChange: onColumnVisibilityChange,
+ onSortingChange: onSortingChange,
+ ...props
+ })
+
+
+ return ;
+};
+
+export default DataTable;
diff --git a/src/core/components/DataTable/localization/fa/datatable.js b/src/core/components/DataTable/localization/fa/datatable.js
new file mode 100644
index 0000000..f80b7e0
--- /dev/null
+++ b/src/core/components/DataTable/localization/fa/datatable.js
@@ -0,0 +1,92 @@
+export const FA_DATATABLE_LOCALIZATION = {
+ actions: "عملیات",
+ and: "و",
+ cancel: "لغو",
+ changeFilterMode: "تغییر حالت فیلتر",
+ changeSearchMode: "تغییر حالت جستجو",
+ clearFilter: "پاک کردن فیلتر",
+ clearSearch: "پاک کردن جستجو",
+ clearSort: "پاک کردن مرتب سازی",
+ clickToCopy: "کلیک برای کپی",
+ collapse: "جمع شدن",
+ collapseAll: "جمع شدن همه",
+ columnActions: "عملیات ستون",
+ copiedToClipboard: "کپی شد",
+ dropToGroupBy: "رها کردن برای گروه بندی بر اساس {column}",
+ edit: "ویرایش",
+ expand: "باز شدن",
+ expandAll: "باز شدن همه",
+ filterArrIncludes: "شامل",
+ filterArrIncludesAll: "شامل همه",
+ filterArrIncludesSome: "شامل",
+ filterBetween: "میان",
+ filterBetweenInclusive: "میان با احتساب هر دو",
+ filterByColumn: "فیلتر بر اساس {column}",
+ filterContains: "شامل",
+ filterEmpty: "خالی",
+ filterEndsWith: "به پایان میرسد با",
+ filterEquals: "برابر",
+ filterEqualsString: "برابر",
+ filterFuzzy: "نزدیک",
+ filterGreaterThan: "بزرگتر از",
+ filterGreaterThanOrEqualTo: "بزرگتر یا مساوی",
+ filterInNumberRange: "میان",
+ filterIncludesString: "شامل",
+ filterIncludesStringSensitive: "شامل",
+ filterLessThan: "کوچکتر از",
+ filterLessThanOrEqualTo: "کوچکتر یا مساوی",
+ filterMode: "حالت فیلتر: {filterType}",
+ filterNotEmpty: "غیر خالی",
+ filterNotEquals: "نا برابر",
+ filterStartsWith: "شروع میشود با",
+ filterWeakEquals: "برابر",
+ filteringByColumn: "فیلتر بر اساس {column} - {filterType} {filterValue}",
+ goToFirstPage: "رفتن به صفحه اول",
+ goToLastPage: "رفتن به صفحه آخر",
+ goToNextPage: "رفتن به صفحه بعدی",
+ goToPreviousPage: "رفتن به صفحه قبلی",
+ grab: "گرفتن",
+ groupByColumn: "گروه بندی بر اساس {column}",
+ groupedBy: "گروه بندی شده بر اساس",
+ hideAll: "پنهان کردن همه",
+ hideColumn: "پنهان کردن ستون {column}",
+ max: "حداکثر",
+ min: "حداقل",
+ move: "انتقال",
+ noRecordsToDisplay: "هیچ رکوردی برای نمایش وجود ندارد",
+ noResultsFound: "نتیجهای یافت نشد",
+ of: "از",
+ or: "یا",
+ pinToLeft: "چسباندن به سمت چپ",
+ pinToRight: "چسباندن به سمت راست",
+ resetColumnSize: "بازنشانی اندازه ستون",
+ resetOrder: "بازنشانی ترتیب",
+ rowActions: "عملیات ردیف",
+ rowNumber: "#",
+ rowNumbers: "شماره ردیف",
+ rowsPerPage: "ردیف در هر صفحه",
+ save: "ذخیره",
+ search: "جستجو",
+ selectedCountOfRowCountRowsSelected:
+ "{selectedCount} از {rowCount} ردیف انتخاب شده",
+ select: "انتخاب",
+ showAll: "نمایش همه",
+ showAllColumns: "نمایش همه ستونها",
+ showHideColumns: "نمایش/مخفی کردن ستونها",
+ showHideFilters: "نمایش/مخفی کردن فیلترها",
+ showHideSearch: "نمایش/مخفی کردن جستجو",
+ sortByColumnAsc: "مرتب سازی بر اساس {column} صعودی",
+ sortByColumnDesc: "مرتب سازی بر اساس {column} نزولی",
+ sortedByColumnAsc: "مرتب شده بر اساس {column} صعودی",
+ sortedByColumnDesc: "مرتب شده بر اساس {column} نزولی",
+ thenBy: "، سپس بر اساس ",
+ toggleDensity: "تغییر تراکم",
+ toggleFullScreen: "تغییر حالت تمام صفحه",
+ toggleSelectAll: "انتخاب/عدم انتخاب همه",
+ toggleSelectRow: "انتخاب/عدم انتخاب ردیف",
+ toggleVisibility: "تغییر پیدا/پنهان",
+ ungroupByColumn: "لغو گروه بندی بر اساس {column}",
+ unpin: "رها کردن",
+ unpinAll: "رها کردن همه",
+ unsorted: "بدون مرتب سازی",
+};
\ No newline at end of file
diff --git a/src/core/components/DataTable/menus/ActionMenuItem.js b/src/core/components/DataTable/menus/ActionMenuItem.js
new file mode 100644
index 0000000..2b208b9
--- /dev/null
+++ b/src/core/components/DataTable/menus/ActionMenuItem.js
@@ -0,0 +1,50 @@
+import { Box, IconButton, ListItemIcon, MenuItem } from "@mui/material";
+
+const DataTable_ActionMenuItem = ({
+ icon,
+ label,
+ onOpenSubMenu,
+ table,
+ ...rest
+}) => {
+ const {
+ options: {
+ icons: { ArrowRightIcon },
+ },
+ } = table;
+
+ return (
+
+ );
+
+}
+export default DataTable_ActionMenuItem
\ No newline at end of file
diff --git a/src/core/components/DataTable/menus/CellActionMenu.js b/src/core/components/DataTable/menus/CellActionMenu.js
new file mode 100644
index 0000000..36638e0
--- /dev/null
+++ b/src/core/components/DataTable/menus/CellActionMenu.js
@@ -0,0 +1,100 @@
+import { parseFromValuesOrFunc } from "@/core/utils/utils";
+import { Menu } from "@mui/material";
+import DataTable_ActionMenuItem from "./ActionMenuItem";
+
+const DataTable_CellActionMenu = ({
+ table,
+ ...rest
+}) => {
+ const {
+ getState,
+ options: {
+ editDisplayMode,
+ enableClickToCopy,
+ enableEditing,
+ icons: { ContentCopy, EditIcon },
+ localization,
+ mrtTheme: { menuBackgroundColor },
+ renderCellActionMenuItems,
+ },
+ refs: { actionCellRef },
+ } = table;
+ const { actionCell, density } = getState();
+ const cell = actionCell || null;
+ const { row } = cell;
+ const { column } = cell;
+ const { columnDef } = column;
+
+ const handleClose = (event) => {
+ event?.stopPropagation();
+ table.setActionCell(null);
+ actionCellRef.current = null;
+ };
+
+ const internalMenuItems = [
+ (parseFromValuesOrFunc(enableClickToCopy, cell) === 'context-menu' ||
+ parseFromValuesOrFunc(columnDef.enableClickToCopy, cell) ===
+ 'context-menu') && (
+ }
+ key={'mrt-copy'}
+ label={localization.copy}
+ onClick={(event) => {
+ event.stopPropagation();
+ navigator.clipboard.writeText(cell.getValue());
+ handleClose();
+ }}
+ table={table}
+ />
+ ),
+ parseFromValuesOrFunc(enableEditing, row) && editDisplayMode === 'cell' && (
+ }
+ key={'mrt-edit'}
+ label={localization.edit}
+ onClick={() => {
+ openEditingCell({ cell, table });
+ handleClose();
+ }}
+ table={table}
+ />
+ ),
+ ].filter(Boolean);
+
+ const renderActionProps = {
+ cell,
+ closeMenu: handleClose,
+ column,
+ internalMenuItems,
+ row,
+ table,
+ };
+
+ const menuItems =
+ columnDef.renderCellActionMenuItems?.(renderActionProps) ??
+ renderCellActionMenuItems?.(renderActionProps);
+
+ return (
+ (!!menuItems?.length || !!internalMenuItems?.length) && (
+
+ )
+ );
+
+}
+export default DataTable_CellActionMenu
\ No newline at end of file
diff --git a/src/core/components/DataTable/menus/ShowHideColumnsMenu.js b/src/core/components/DataTable/menus/ShowHideColumnsMenu.js
new file mode 100644
index 0000000..12efae8
--- /dev/null
+++ b/src/core/components/DataTable/menus/ShowHideColumnsMenu.js
@@ -0,0 +1,140 @@
+import { Box, Button, Divider, Menu } from "@mui/material";
+import { useMemo, useState } from "react";
+import DataTable_ShowHideColumnsMenuItems from "./ShowHideColumnsMenuItems";
+
+const DataTable_ShowHideColumnsMenu = ({ anchorEl,
+ setAnchorEl,
+ table,
+ ...rest
+}) => {
+ const {
+ getAllColumns,
+ getAllLeafColumns,
+ getCenterLeafColumns,
+ getIsAllColumnsVisible,
+ getIsSomeColumnsPinned,
+ getIsSomeColumnsVisible,
+ getLeftLeafColumns,
+ getRightLeafColumns,
+ getState,
+ options: {
+ enableColumnOrdering,
+ enableColumnPinning,
+ enableHiding,
+ localization,
+ mrtTheme: { menuBackgroundColor },
+ },
+ } = table;
+
+ const { columnOrder, columnPinning, density } = getState();
+
+ const handleToggleAllColumns = (value) => {
+ getAllLeafColumns()
+ .filter((col) => col.columnDef.enableHiding !== false)
+ .forEach((col) => col.toggleVisibility(value));
+ };
+
+ const allColumns = useMemo(() => {
+ const columns = getAllColumns();
+ if (
+ columnOrder.length > 0 &&
+ !columns.some((col) => col.columnDef.columnDefType === 'group')
+ ) {
+ return [
+ ...getLeftLeafColumns(),
+ ...Array.from(new Set(columnOrder)).map((colId) =>
+ getCenterLeafColumns().find((col) => col?.id === colId),
+ ),
+ ...getRightLeafColumns(),
+ ].filter(Boolean);
+ }
+ return columns;
+ }, [
+ columnOrder,
+ columnPinning,
+ getAllColumns(),
+ getCenterLeafColumns(),
+ getLeftLeafColumns(),
+ getRightLeafColumns(),
+ ]);
+
+ const isNestedColumns = allColumns.some(
+ (col) => col.columnDef.columnDefType === 'group',
+ );
+
+ const [hoveredColumn, setHoveredColumn] = useState(null);
+
+ return (
+
+ );
+}
+export default DataTable_ShowHideColumnsMenu
\ No newline at end of file
diff --git a/src/core/components/DataTable/menus/ShowHideColumnsMenuItems.js b/src/core/components/DataTable/menus/ShowHideColumnsMenuItems.js
new file mode 100644
index 0000000..58425d9
--- /dev/null
+++ b/src/core/components/DataTable/menus/ShowHideColumnsMenuItems.js
@@ -0,0 +1,160 @@
+import { getCommonTooltipProps, parseFromValuesOrFunc } from "@/core/utils/utils";
+import { Box, FormControlLabel, MenuItem, Switch, Tooltip, Typography } from "@mui/material";
+import { useRef, useState } from "react";
+import DataTable_GrabHandleButton from "../buttons/GrabHandleButton";
+
+const DataTable_ShowHideColumnsMenuItems = ({
+ allColumns,
+ column,
+ hoveredColumn,
+ isNestedColumns,
+ setHoveredColumn,
+ table,
+ ...rest
+}) => {
+ const {
+ getState,
+ options: {
+ enableColumnOrdering,
+ enableHiding,
+ localization,
+ mrtTheme: { draggingBorderColor },
+ },
+ setColumnOrder,
+ } = table;
+
+ const { columnOrder } = getState();
+ const { columnDef } = column;
+ const { columnDefType } = columnDef;
+
+ const switchChecked = column.getIsVisible();
+
+ const handleToggleColumnHidden = (column) => {
+ if (columnDefType === 'group') {
+ column?.columns?.forEach?.((childColumn) => {
+ childColumn.toggleVisibility(!switchChecked);
+ });
+ } else {
+ column.toggleVisibility();
+ }
+ };
+
+ const menuItemRef = useRef(null);
+
+ const [isDragging, setIsDragging] = useState(false);
+
+ const handleDragStart = (e) => {
+ setIsDragging(true);
+ try {
+ e.dataTransfer.setDragImage(menuItemRef.current, 0, 0);
+ } catch (e) {
+ console.error(e);
+ }
+ };
+
+ const handleDragEnd = (_e) => {
+ setIsDragging(false);
+ setHoveredColumn(null);
+ if (hoveredColumn) {
+ setColumnOrder(reorderColumn(column, hoveredColumn, columnOrder));
+ }
+ };
+
+ const handleDragEnter = (_e) => {
+ if (!isDragging && columnDef.enableColumnOrdering !== false) {
+ setHoveredColumn(column);
+ }
+ };
+
+ if (!columnDef.header || columnDef.visibleInShowHideMenu === false) {
+ return null;
+ }
+
+ return (
+ <>
+
+ {column.columns?.map((c, i) => (
+
+ ))}
+ >
+ );
+
+}
+export default DataTable_ShowHideColumnsMenuItems;
\ No newline at end of file
diff --git a/src/core/components/DataTable/table/Paper.js b/src/core/components/DataTable/table/Paper.js
new file mode 100644
index 0000000..db3bbec
--- /dev/null
+++ b/src/core/components/DataTable/table/Paper.js
@@ -0,0 +1,78 @@
+import { parseFromValuesOrFunc } from "@/core/utils/utils";
+import { Paper } from "@mui/material";
+import DataTable_BottomToolbar from "../toolbar/BottomToolbar";
+import DataTable_TopToolbar from "../toolbar/TopToolbar";
+import DataTable_TableContainer from "./TableContainer";
+
+const DataTable_Paper = ({ table, ...rest }) => {
+ const {
+ getState,
+ options: {
+ enableBottomToolbar,
+ enableTopToolbar,
+ mrtTheme: { baseBackgroundColor },
+ muiTablePaperProps,
+ renderBottomToolbar,
+ renderTopToolbar,
+ },
+ refs: { tablePaperRef },
+ } = table;
+
+ const { isFullScreen } = getState();
+
+ const paperProps = {
+ ...parseFromValuesOrFunc(muiTablePaperProps, { table }),
+ ...rest,
+ };
+
+ return (
+ {
+ tablePaperRef.current = ref;
+ if (paperProps?.ref) {
+ //@ts-ignore
+ paperProps.ref.current = ref;
+ }
+ }}
+ style={{
+ ...(isFullScreen
+ ? {
+ bottom: 0,
+ height: '100dvh',
+ left: 0,
+ margin: 0,
+ maxHeight: '100dvh',
+ maxWidth: '100dvw',
+ padding: 0,
+ position: 'fixed',
+ right: 0,
+ top: 0,
+ width: '100dvw',
+ zIndex: 999,
+ }
+ : {}),
+ ...paperProps?.style,
+ }}
+ sx={(theme) => ({
+ backgroundColor: baseBackgroundColor,
+ backgroundImage: 'unset',
+ overflow: 'hidden',
+ transition: 'all 100ms ease-in-out',
+ ...(parseFromValuesOrFunc(paperProps?.sx, theme)),
+ })}
+ >
+ {enableTopToolbar &&
+ (parseFromValuesOrFunc(renderTopToolbar, { table }) ?? (
+
+ ))}
+
+ {enableBottomToolbar &&
+ (parseFromValuesOrFunc(renderBottomToolbar, { table }) ?? (
+
+ ))}
+
+ );
+};
+export default DataTable_Paper;
diff --git a/src/core/components/DataTable/table/Table.js b/src/core/components/DataTable/table/Table.js
new file mode 100644
index 0000000..ca49d0c
--- /dev/null
+++ b/src/core/components/DataTable/table/Table.js
@@ -0,0 +1,77 @@
+import { parseCSSVarId, parseFromValuesOrFunc } from "@/core/utils/utils";
+import { Table } from "@mui/material";
+import { useMRT_ColumnVirtualizer } from "material-react-table";
+import { useMemo } from "react";
+import DataTable_TableBody, { Memo_DataTable_TableBody } from "../body/TableBody";
+import DataTable_TableHead from "../head/TableHead";
+
+const DataTable_Table = ({
+ table,
+ ...rest
+}) => {
+ const {
+ getFlatHeaders,
+ getState,
+ options: {
+ columns,
+ enableStickyHeader,
+ enableTableFooter,
+ enableTableHead,
+ layoutMode,
+ memoMode,
+ muiTableProps,
+ renderCaption,
+ },
+ } = table;
+ const { columnSizing, columnSizingInfo, columnVisibility, isFullScreen } =
+ getState();
+
+ const tableProps = {
+ ...parseFromValuesOrFunc(muiTableProps, { table }),
+ ...rest,
+ };
+
+ const Caption = parseFromValuesOrFunc(renderCaption, { table });
+
+ const columnSizeVars = useMemo(() => {
+ const headers = getFlatHeaders();
+ const colSizes = {};
+ for (let i = 0; i < headers.length; i++) {
+ const header = headers[i];
+ const colSize = header.getSize();
+ colSizes[`--header-${parseCSSVarId(header.id)}-size`] = colSize;
+ colSizes[`--col-${parseCSSVarId(header.column.id)}-size`] = colSize;
+ }
+ return colSizes;
+ }, [columns, columnSizing, columnSizingInfo, columnVisibility]);
+
+ const columnVirtualizer = useMRT_ColumnVirtualizer(table);
+
+ const commonTableGroupProps = {
+ columnVirtualizer,
+ table,
+ };
+
+ return (
+ ({
+ display: layoutMode?.startsWith('grid') ? 'grid' : undefined,
+ position: 'relative',
+ ...(parseFromValuesOrFunc(tableProps?.sx, theme)),
+ })}
+ >
+ {!!Caption && {Caption}}
+ {enableTableHead && }
+ {memoMode === 'table-body' || columnSizingInfo.isResizingColumn ? (
+
+ ) : (
+
+ )}
+ {/* {enableTableFooter && } */}
+
+ );
+}
+export default DataTable_Table
\ No newline at end of file
diff --git a/src/core/components/DataTable/table/TableContainer.js b/src/core/components/DataTable/table/TableContainer.js
new file mode 100644
index 0000000..aa4b625
--- /dev/null
+++ b/src/core/components/DataTable/table/TableContainer.js
@@ -0,0 +1,94 @@
+import { parseFromValuesOrFunc } from "@/core/utils/utils";
+import { TableContainer } from "@mui/material";
+import { useEffect, useLayoutEffect, useState } from "react";
+import DataTable_CellActionMenu from "../menus/CellActionMenu";
+import DataTable_Table from "./Table";
+import DataTable_TableLoadingOverlay from "./TableLoadingOverlay";
+
+const useIsomorphicLayoutEffect =
+ typeof window !== 'undefined' ? useLayoutEffect : useEffect;
+
+const DataTable_TableContainer = ({
+ table,
+ ...rest
+}) => {
+ const {
+ getState,
+ options: {
+ enableCellActions,
+ enableStickyHeader,
+ muiTableContainerProps,
+ },
+ refs: { bottomToolbarRef, tableContainerRef, topToolbarRef },
+ } = table;
+ const {
+ actionCell,
+ isFullScreen,
+ isLoading,
+ showLoadingOverlay,
+ } = getState();
+
+ const loading =
+ showLoadingOverlay !== false && (isLoading || showLoadingOverlay);
+
+ const [totalToolbarHeight, setTotalToolbarHeight] = useState(0);
+
+ const tableContainerProps = {
+ ...parseFromValuesOrFunc(muiTableContainerProps, {
+ table,
+ }),
+ ...rest,
+ };
+
+ useIsomorphicLayoutEffect(() => {
+ const topToolbarHeight =
+ typeof document !== 'undefined'
+ ? topToolbarRef.current?.offsetHeight ?? 0
+ : 0;
+
+ const bottomToolbarHeight =
+ typeof document !== 'undefined'
+ ? bottomToolbarRef?.current?.offsetHeight ?? 0
+ : 0;
+
+ setTotalToolbarHeight(topToolbarHeight + bottomToolbarHeight);
+ });
+
+ return (
+ {
+ if (node) {
+ tableContainerRef.current = node;
+ if (tableContainerProps?.ref) {
+ //@ts-ignore
+ tableContainerProps.ref.current = node;
+ }
+ }
+ }}
+ style={{
+ maxHeight: isFullScreen
+ ? `calc(100vh - ${totalToolbarHeight}px)`
+ : undefined,
+ ...tableContainerProps?.style,
+ }}
+ sx={(theme) => ({
+ maxHeight: enableStickyHeader
+ ? `clamp(350px, calc(100vh - ${totalToolbarHeight}px), 9999px)`
+ : undefined,
+ maxWidth: '100%',
+ overflow: 'auto',
+ position: 'relative',
+ ...(parseFromValuesOrFunc(tableContainerProps?.sx, theme)),
+ })}
+ >
+ {loading ? : null}
+
+ {enableCellActions && actionCell && }
+
+ );
+
+}
+export default DataTable_TableContainer
\ No newline at end of file
diff --git a/src/core/components/DataTable/table/TableLoadingOverlay.js b/src/core/components/DataTable/table/TableLoadingOverlay.js
new file mode 100644
index 0000000..55e4ae5
--- /dev/null
+++ b/src/core/components/DataTable/table/TableLoadingOverlay.js
@@ -0,0 +1,48 @@
+import { Box, CircularProgress } from "@mui/material";
+
+const DataTable_TableLoadingOverlay = ({
+ table,
+ ...rest
+}) => {
+ const {
+ options: {
+ localization,
+ mrtTheme: { baseBackgroundColor },
+ muiCircularProgressProps,
+ },
+ } = table;
+
+ const circularProgressProps = {
+ ...parseFromValuesOrFunc(muiCircularProgressProps, { table }),
+ ...rest,
+ };
+
+ return (
+
+ {circularProgressProps?.Component ?? (
+
+ )}
+
+ );
+
+}
+export default DataTable_TableLoadingOverlay
\ No newline at end of file
diff --git a/src/core/components/DataTable/toolbar/BottomToolbar.js b/src/core/components/DataTable/toolbar/BottomToolbar.js
new file mode 100644
index 0000000..15e7f5a
--- /dev/null
+++ b/src/core/components/DataTable/toolbar/BottomToolbar.js
@@ -0,0 +1,88 @@
+import { getCommonToolbarStyles, parseFromValuesOrFunc } from "@/core/utils/utils";
+import { Box, alpha, useMediaQuery } from "@mui/material";
+import DataTable_LinearProgressBar from "./LinearProgressBar";
+import DataTable_TablePagination from "./TablePagination";
+
+const DataTable_BottomToolbar = ({
+ table,
+ ...rest
+}) => {
+ const {
+ getState,
+ options: {
+ enablePagination,
+ muiBottomToolbarProps,
+ positionPagination,
+ renderBottomToolbarCustomActions,
+ },
+ refs: { bottomToolbarRef },
+ } = table;
+ const { isFullScreen } = getState();
+
+ const isMobile = useMediaQuery('(max-width:720px)');
+ const toolbarProps = {
+ ...parseFromValuesOrFunc(muiBottomToolbarProps, { table }),
+ ...rest,
+ };
+
+ const stackAlertBanner = isMobile || !!renderBottomToolbarCustomActions;
+ return (
+ {
+ if (node) {
+ bottomToolbarRef.current = node;
+ if (toolbarProps?.ref) {
+ // @ts-ignore
+ toolbarProps.ref.current = node;
+ }
+ }
+ }}
+ sx={(theme) => ({
+ ...getCommonToolbarStyles({ table, theme }),
+ bottom: isFullScreen ? '0' : undefined,
+ boxShadow: `0 1px 2px -1px ${alpha(
+ theme.palette.grey[700],
+ 0.5,
+ )} inset`,
+ left: 0,
+ position: isFullScreen ? 'fixed' : 'relative',
+ right: 0,
+ ...(parseFromValuesOrFunc(toolbarProps?.sx, theme)),
+ })}
+ >
+
+
+ {renderBottomToolbarCustomActions ? (
+ renderBottomToolbarCustomActions({ table })
+ ) : (
+
+ )}
+
+ {enablePagination &&
+ ['both', 'bottom'].includes(positionPagination ?? '') && (
+
+ )}
+
+
+
+ );
+}
+export default DataTable_BottomToolbar
\ No newline at end of file
diff --git a/src/core/components/DataTable/toolbar/LinearProgressBar.js b/src/core/components/DataTable/toolbar/LinearProgressBar.js
new file mode 100644
index 0000000..cdcbc0a
--- /dev/null
+++ b/src/core/components/DataTable/toolbar/LinearProgressBar.js
@@ -0,0 +1,44 @@
+import { parseFromValuesOrFunc } from "@/core/utils/utils";
+import { Collapse, LinearProgress } from "@mui/material";
+
+const DataTable_LinearProgressBar = ({
+ isTopToolbar,
+ table,
+ ...rest
+}) => {
+ const {
+ getState,
+ options: { muiLinearProgressProps },
+ } = table;
+ const { isSaving, showProgressBars } = getState();
+
+ const linearProgressProps = {
+ ...parseFromValuesOrFunc(muiLinearProgressProps, {
+ isTopToolbar,
+ table,
+ }),
+ ...rest,
+ };
+
+ return (
+
+
+
+ );
+}
+export default DataTable_LinearProgressBar
\ No newline at end of file
diff --git a/src/core/components/DataTable/toolbar/TablePagination.js b/src/core/components/DataTable/toolbar/TablePagination.js
new file mode 100644
index 0000000..cd70fda
--- /dev/null
+++ b/src/core/components/DataTable/toolbar/TablePagination.js
@@ -0,0 +1,217 @@
+import { flipIconStyles, getCommonTooltipProps, parseFromValuesOrFunc } from "@/core/utils/utils";
+import { useTheme } from "@emotion/react";
+import { Box, IconButton, InputLabel, MenuItem, Pagination, PaginationItem, Select, Tooltip, Typography, useMediaQuery } from "@mui/material";
+
+const defaultRowsPerPage = [5, 10, 15, 20, 25, 30, 50, 100];
+
+const DataTable_TablePagination = ({
+ position = 'bottom',
+ table,
+ ...rest
+}) => {
+ const theme = useTheme();
+ const isMobile = useMediaQuery('(max-width: 720px)');
+
+ const {
+ getState,
+ options: {
+ enableToolbarInternalActions,
+ icons: { ChevronLeftIcon, ChevronRightIcon, FirstPageIcon, LastPageIcon },
+ localization,
+ muiPaginationProps,
+ paginationDisplayMode,
+ },
+ } = table;
+
+ const {
+ pagination: { pageIndex = 0, pageSize = 10 },
+ showGlobalFilter,
+ } = getState();
+
+ const paginationProps = {
+ ...parseFromValuesOrFunc(muiPaginationProps, {
+ table,
+ }),
+ ...rest,
+ };
+
+ const totalRowCount = table.getRowCount();
+ const numberOfPages = table.getPageCount();
+ const showFirstLastPageButtons = numberOfPages > 2;
+ const firstRowIndex = pageIndex * pageSize;
+ const lastRowIndex = Math.min(pageIndex * pageSize + pageSize, totalRowCount);
+
+ const {
+ SelectProps = {},
+ disabled = false,
+ rowsPerPageOptions = defaultRowsPerPage,
+ showFirstButton = showFirstLastPageButtons,
+ showLastButton = showFirstLastPageButtons,
+ showRowsPerPage = true,
+ ...restPaginationProps
+ } = paginationProps ?? {};
+
+ const disableBack = pageIndex <= 0 || disabled;
+ const disableNext = lastRowIndex >= totalRowCount || disabled;
+
+ if (isMobile && SelectProps?.native !== false) {
+ SelectProps.native = true;
+ }
+
+ const tooltipProps = getCommonTooltipProps();
+
+ return (
+
+ {showRowsPerPage && (
+
+
+ {localization.rowsPerPage}
+
+
+
+ )}
+ {paginationDisplayMode === 'pages' ? (
+ table.setPageIndex(newPageIndex - 1)}
+ page={pageIndex + 1}
+ renderItem={(item) => (
+
+ )}
+ showFirstButton={showFirstButton}
+ showLastButton={showLastButton}
+ {...restPaginationProps}
+ />
+ ) : paginationDisplayMode === 'default' ? (
+ <>
+ {`${lastRowIndex === 0 ? 0 : (firstRowIndex + 1).toLocaleString()
+ }-${lastRowIndex.toLocaleString()} ${localization.of
+ } ${totalRowCount.toLocaleString()}`}
+
+ {showFirstButton && (
+
+
+ table.firstPage()}
+ size="small"
+ >
+
+
+
+
+ )}
+
+
+ table.previousPage()}
+ size="small"
+ >
+
+
+
+
+
+
+ table.nextPage()}
+ size="small"
+ >
+
+
+
+
+ {showLastButton && (
+
+
+ table.lastPage()}
+ size="small"
+ >
+
+
+
+
+ )}
+
+ >
+ ) : null}
+
+ );
+}
+export default DataTable_TablePagination
\ No newline at end of file
diff --git a/src/core/components/DataTable/toolbar/ToolbarInternalButtons.js b/src/core/components/DataTable/toolbar/ToolbarInternalButtons.js
new file mode 100644
index 0000000..606add0
--- /dev/null
+++ b/src/core/components/DataTable/toolbar/ToolbarInternalButtons.js
@@ -0,0 +1,47 @@
+import { parseFromValuesOrFunc } from "@/core/utils/utils";
+import { Box } from "@mui/material";
+import DataTable_ShowHideColumnsButton from "../buttons/ShowHideColumnsButton";
+import DataTable_ToggleFiltersButton from "../buttons/ToggleFiltersButton";
+
+const DataTable_ToolbarInternalButtons = ({ table, ...rest }) => {
+ const {
+ options: {
+ columnFilterDisplayMode,
+ enableColumnFilters,
+ enableColumnOrdering,
+ enableColumnPinning,
+ enableFilters,
+ enableHiding,
+ renderToolbarInternalActions,
+ },
+ } = table;
+
+
+ return (
+ ({
+ alignItems: 'center',
+ display: 'flex',
+ zIndex: 3,
+ ...(parseFromValuesOrFunc(rest?.sx, theme)),
+ })}
+ >
+ {renderToolbarInternalActions?.({
+ table,
+ }) ?? (
+ <>
+ {enableFilters &&
+ enableColumnFilters &&
+ columnFilterDisplayMode !== 'popover' && (
+
+ )}
+ {(enableHiding || enableColumnOrdering || enableColumnPinning) && (
+
+ )}
+ >
+ )}
+
+ );
+}
+export default DataTable_ToolbarInternalButtons
\ No newline at end of file
diff --git a/src/core/components/DataTable/toolbar/TopToolbar.js b/src/core/components/DataTable/toolbar/TopToolbar.js
new file mode 100644
index 0000000..4eb0aba
--- /dev/null
+++ b/src/core/components/DataTable/toolbar/TopToolbar.js
@@ -0,0 +1,86 @@
+import { getCommonToolbarStyles, parseFromValuesOrFunc } from "@/core/utils/utils";
+import { Box, useMediaQuery } from "@mui/material";
+import DataTable_LinearProgressBar from "./LinearProgressBar";
+import DataTable_TablePagination from "./TablePagination";
+import DataTable_ToolbarInternalButtons from "./ToolbarInternalButtons";
+
+const DataTable_TopToolbar = ({ table }) => {
+ const {
+ getState,
+ options: {
+ enablePagination,
+ enableToolbarInternalActions,
+ muiTopToolbarProps,
+ positionPagination,
+ renderTopToolbarCustomActions,
+ },
+ refs: { topToolbarRef },
+ } = table;
+ const { isFullScreen, showGlobalFilter } = getState();
+
+ const isMobile = useMediaQuery('(max-width:720px)');
+ const isTablet = useMediaQuery('(max-width:1024px)');
+
+ const toolbarProps = parseFromValuesOrFunc(muiTopToolbarProps, { table });
+
+ const stackAlertBanner =
+ isMobile ||
+ !!renderTopToolbarCustomActions ||
+ (showGlobalFilter && isTablet);
+
+ return (
+ {
+ topToolbarRef.current = ref;
+ if (toolbarProps?.ref) {
+ // @ts-ignore
+ toolbarProps.ref.current = ref;
+ }
+ }}
+ sx={(theme) => ({
+ ...getCommonToolbarStyles({ table, theme }),
+ position: isFullScreen ? 'sticky' : 'relative',
+ top: isFullScreen ? '0' : undefined,
+ ...(parseFromValuesOrFunc(toolbarProps?.sx, theme)),
+ })}
+ >
+
+ {renderTopToolbarCustomActions?.({ table }) ?? }
+ {enableToolbarInternalActions && (
+
+
+
+ )}
+
+ {enablePagination &&
+ ['both', 'top'].includes(positionPagination ?? '') && (
+
+ )}
+
+
+ );
+}
+
+export default DataTable_TopToolbar
\ No newline at end of file
diff --git a/src/core/components/SidebarListItems.jsx b/src/core/components/SidebarListItems.jsx
new file mode 100644
index 0000000..9ffcac2
--- /dev/null
+++ b/src/core/components/SidebarListItems.jsx
@@ -0,0 +1,46 @@
+import {Collapse, ListItem, ListItemButton, ListItemIcon, ListItemText} from "@mui/material";
+import { ExpandLess, ExpandMore } from '@mui/icons-material';
+import WifiIcon from '@mui/icons-material/Wifi';
+import SidebarSubitems from "@/core/components/SidebarSubitems";
+
+const SidebarListItems = ({menuItem, dispatch}) => {
+ return(
+ <>
+
+ {
+ dispatch({type: "COLLAPSE_MENU", id: menuItem.id})
+ }}>
+
+ {menuItem.icon}
+
+
+ {menuItem.hasSubitems ? (menuItem.showSubitems ? : ) : null }
+
+
+
+ {
+ menuItem.hasSubitems ? (menuItem.Subitems.map((subitem,index)=>{
+ return(
+
+ )
+ })) : null
+ }
+
+
+ >
+ )
+}
+export default SidebarListItems
\ No newline at end of file
diff --git a/src/core/components/SidebarMenu.jsx b/src/core/components/SidebarMenu.jsx
new file mode 100644
index 0000000..f7589c3
--- /dev/null
+++ b/src/core/components/SidebarMenu.jsx
@@ -0,0 +1,201 @@
+"use client"
+import {List} from "@mui/material";
+import SidebarListItems from "@/core/components/SidebarListItems";
+import {useEffect, useReducer, useState} from "react";
+import {usePathname} from 'next/navigation'
+import SecurityIcon from '@mui/icons-material/Security';
+import SpaceDashboardIcon from "@mui/icons-material/SpaceDashboard";
+import AssessmentIcon from '@mui/icons-material/Assessment';
+import AirlineSeatReclineNormalIcon from "@mui/icons-material/AirlineSeatReclineNormal";
+import RepartitionIcon from '@mui/icons-material/Repartition';
+
+const data = [
+ {
+ id: "hello",
+ type: "menu",
+ route: "/hello",
+ icon: ,
+ selected: false,
+ Subitems: [
+ {
+ id: "amin",
+ route: "/s",
+ icon: ,
+ firstName: "amin",
+ lastName: "ali",
+ Subitems: [
+ {
+ id: "shahrokh", shahrokh: "shahrokh", type: "page", selected: false, route: "/dashbssoard", icon:
+ },
+ {
+ id: "shasdasdsahrokh", shahrokh: "shahroasdasdkh", type: "page", selected: false, route: "/dashboardsd", icon:
+ }
+ ],
+ hasSubitems: true,
+ showSubitems: false,
+ type: "menu",
+ selected: false,
+ },
+ {
+ id: "amir",
+ route: "/dashboarsad",
+ icon: ,
+ firstName: "amir",
+ lastName: "akbar",
+ hasSubitems: false,
+ showSubitems: false,
+ selected: false,
+ type: "page",
+ },
+ ],
+ hasSubitems: true,
+ showSubitems: false
+ },
+ {
+ id: "hi",
+ type: "menu",
+ icon: ,
+ route: "/hi",
+ selected: false,
+ Subitems: [
+ {
+ id: "ali",
+ route: "/dashboardss",
+ firstName: "ali",
+ lastName: "ali",
+ icon: ,
+ hasSubitems: false,
+ showSubitems: false,
+ type: "page",
+ selected: false,
+ },
+ {
+ id: "akbar",
+ route: "/akbar",
+ firstName: "akbar",
+ icon: ,
+ lastName: "akbar",
+ hasSubitems: false,
+ showSubitems: false,
+ type: "page",
+ selected: false,
+ },
+ ],
+ hasSubitems: true,
+ showSubitems: false
+ },
+ {
+ id: "hoo",
+ type: "page",
+ route: "/dashboard",
+ icon: ,
+ selected: false,
+ hasSubitems: false,
+ showSubitems: false
+ }
+]
+
+function reducer(state, action) {
+ switch (action.type) {
+ case "COLLAPSE_MENU":
+ return state.map((item) => action.id == item.id ? {
+ ...item, showSubitems: !item.showSubitems
+ } : item)
+ case "COLLAPSE_SUB_ITEMS" :
+ return state.map((item) => {
+ return item.hasSubitems ? {
+ ...item, Subitems: item.Subitems.map((subitem) => action.id === subitem.id ? {
+ ...subitem, showSubitems: !subitem.showSubitems
+ } : subitem)
+ } : item
+ })
+ case "COLLAPSE_SUB_SECOND_ITEMS" :
+ return state.map((item) => {
+ return item.hasSubitems ? {
+ ...item, Subitems: item.Subitems.map((subitem) => {
+ return subitem.hasSubitems ? {
+ ...subitem,
+ Subitems: subitem.Subitems.map((secondSubitem) => action.id === secondSubitem.id ? {
+ ...secondSubitem, showSubitems: !secondSubitem.showSubitems
+ } : secondSubitem)
+ } : subitem
+ })
+ } : item
+ })
+ case "SELECTED":
+ return state.map((item) => {
+ return item.type === "page" ? {
+ ...item, selected: action.route === item.route,showSubitems: item.route === action.route
+ } : item.Subitems && Array.isArray(item.Subitems) ? {
+ ...item, Subitems: item.Subitems.map((subitem) => {
+ return subitem.type === "page" ? {
+ ...subitem, selected: action.route === subitem.route,showSubitems: subitem.route === action.route
+ } : subitem.Subitems && Array.isArray(subitem.Subitems) ? {
+ ...subitem, Subitems: subitem.Subitems.map((secondSubItem) => ({
+ ...secondSubItem, selected: secondSubItem.route === action.route
+ })),showSubitems: subitem.Subitems.some((secondSubItem) => secondSubItem.route === action.route),
+ } : subitem
+ }),showSubitems: item.Subitems.some((subitem) => subitem.showSubitems),
+ } : item
+ });
+ // case "SELECTED":
+ // return state.map((item) => {
+ // return item.type === "page" ? {
+ // ...item,
+ // selected: action.route === item.route
+ // } : item.Subitems && Array.isArray(item.Subitems) ? {
+ // ...item,
+ // Subitems: item.Subitems.map((subitem) => {
+ // const updatedSubitem = subitem.type === "page" ? {
+ // ...subitem,
+ // selected: action.route === subitem.route
+ // } : subitem.Subitems && Array.isArray(subitem.Subitems) ? {
+ // ...subitem,
+ // Subitems: subitem.Subitems.map((secondSubItem) => ({
+ // ...secondSubItem,
+ // selected: secondSubItem.route === action.route
+ // })),
+ // showSubitems: subitem.Subitems.some((secondSubItem) => secondSubItem.route === action.route),
+ // } : subitem;
+ //
+ // console.log("Updated subitem:", updatedSubitem); // Log the updated subitem
+ // return updatedSubitem;
+ // }),
+ // showSubitems: item.Subitems.some((subitem) => subitem.showSubitems),
+ // } : item
+ // });
+
+
+ default:
+ throw new Error();
+ }
+}
+
+const SidebarMenu = () => {
+ const [menuItems, dispatch] = useReducer(reducer, data);
+ const pathname = usePathname()
+ const [selectedKey, setSelectedKey] = useState(null);
+ useEffect(() => {
+ dispatch({type: "SELECTED", route: pathname});
+ setSelectedKey(pathname);
+ }, [pathname]);
+
+ useEffect(() => {
+ selectedKey && document.querySelector(`[data-route="${selectedKey}"]`)?.scrollIntoView({
+ behavior: "smooth", block: "center"
+ });
+ }, [selectedKey]);
+
+ return (
+
+ {
+ menuItems.map((menuItem) => {
+ return (
+
+ )
+ })
+ }
+
+ )
+}
+export default SidebarMenu
\ No newline at end of file
diff --git a/src/core/components/SidebarSubitems.jsx b/src/core/components/SidebarSubitems.jsx
new file mode 100644
index 0000000..275befb
--- /dev/null
+++ b/src/core/components/SidebarSubitems.jsx
@@ -0,0 +1,74 @@
+import {Collapse, List, ListItem, ListItemButton, ListItemIcon, ListItemText} from "@mui/material";
+import {ExpandLess, ExpandMore} from "@mui/icons-material";
+
+const SidebarSubitems = ({subitem, dispatch}) => {
+ return (
+ <>
+
+
+ {
+ dispatch({type: "COLLAPSE_SUB_ITEMS", id: subitem.id})
+ }}>
+
+ {subitem.icon}
+
+
+ {subitem.hasSubitems ? (subitem.showSubitems ? : ) : null
+ }
+
+
+
+ {
+ subitem.hasSubitems ? (subitem.Subitems.map((subSubitem, index) => {
+ return (
+
+
+
+ {
+ dispatch({type: "COLLAPSE_SUB_SECOND_ITEMS", id: subSubitem.id})
+ }}
+ selected={subSubitem.selected}
+ sx={{
+ '&.Mui-selected': {
+ backgroundColor: 'rgba(0, 0, 0, 0.05)',
+ },
+ }}
+ >
+
+ {subSubitem.icon}
+
+
+
+
+
+
+ )
+ })) : null
+ }
+ >
+ )
+}
+export default SidebarSubitems
\ No newline at end of file
diff --git a/src/core/utils/flattenArrayOfObjects.js b/src/core/utils/flattenArrayOfObjects.js
new file mode 100644
index 0000000..4dd4612
--- /dev/null
+++ b/src/core/utils/flattenArrayOfObjects.js
@@ -0,0 +1,10 @@
+export const flattenArrayOfObjects = (array, key) => {
+ return array.reduce((acc, obj) => {
+ if (Array.isArray(obj[key])) {
+ acc.push(...flattenArrayOfObjects(obj[key]));
+ } else {
+ acc.push(obj);
+ }
+ return acc;
+ }, []);
+};
\ No newline at end of file
diff --git a/src/core/utils/theme.js b/src/core/utils/theme.js
index be10390..3abbb52 100644
--- a/src/core/utils/theme.js
+++ b/src/core/utils/theme.js
@@ -1,8 +1,12 @@
'use client'
-import {createTheme} from "@mui/material";
+import { createTheme } from "@mui/material";
const theme = createTheme({
- direction: 'rtl'
+ direction: 'rtl',
+ typography: {
+ fontFamily: `IRANSansFaNum, sans-serif`,
+ fontSize: 12
+ },
});
export default theme
\ No newline at end of file
diff --git a/src/core/utils/utils.js b/src/core/utils/utils.js
new file mode 100644
index 0000000..98e0252
--- /dev/null
+++ b/src/core/utils/utils.js
@@ -0,0 +1,162 @@
+import { alpha, darken } from "@mui/material";
+
+export const parseCSSVarId = (id) => id.replace(/[^a-zA-Z0-9]/g, '_');
+
+export const parseFromValuesOrFunc = (
+ fn,
+ arg
+) => (fn instanceof Function ? fn(arg) : fn);
+
+export const getCommonToolbarStyles = ({
+ table,
+}) => ({
+ alignItems: 'flex-start',
+ backgroundColor: table.options.mrtTheme.baseBackgroundColor,
+ display: 'grid',
+ flexWrap: 'wrap-reverse',
+ minHeight: '3.5rem',
+ overflow: 'hidden',
+ position: 'relative',
+ transition: 'all 150ms ease-in-out',
+ zIndex: 1,
+});
+
+export const getCommonTooltipProps = (
+ placement,
+) => ({
+ disableInteractive: true,
+ enterDelay: 500,
+ enterNextDelay: 500,
+ placement,
+});
+
+export const flipIconStyles = (theme) =>
+ theme.direction === 'rtl'
+ ? { style: { transform: 'scaleX(-1)' } }
+ : undefined;
+
+export const getCommonPinnedCellStyles = ({
+ column,
+ table,
+ theme,
+}) => {
+ const { baseBackgroundColor } = table.options.mrtTheme;
+ const isPinned = column?.getIsPinned();
+
+ return {
+ '&[data-pinned="true"]': {
+ '&:before': {
+ backgroundColor: alpha(
+ darken(
+ baseBackgroundColor,
+ theme.palette.mode === 'dark' ? 0.05 : 0.01,
+ ),
+ 0.97,
+ ),
+ boxShadow: column
+ ? isPinned === 'left' && column.getIsLastColumn(isPinned)
+ ? `-4px 0 4px -4px ${alpha(theme.palette.grey[700], 0.5)} inset`
+ : isPinned === 'right' && column.getIsFirstColumn(isPinned)
+ ? `4px 0 4px -4px ${alpha(theme.palette.grey[700], 0.5)} inset`
+ : undefined
+ : undefined,
+ ...commonCellBeforeAfterStyles,
+ },
+ },
+ };
+};
+
+export const getCommonMRTCellStyles = ({
+ column,
+ header,
+ table,
+ tableCellProps,
+ theme,
+}) => {
+ const {
+ getState,
+ options: { enableColumnVirtualization, layoutMode },
+ } = table;
+ const { draggingColumn } = getState();
+ const { columnDef } = column;
+ const { columnDefType } = columnDef;
+
+ const isColumnPinned =
+ columnDef.columnDefType !== 'group' && column.getIsPinned();
+
+ const widthStyles = {
+ minWidth: `max(calc(var(--${header ? 'header' : 'col'}-${parseCSSVarId(
+ header?.id ?? column.id,
+ )}-size) * 1px), ${columnDef.minSize ?? 30}px)`,
+ width: `calc(var(--${header ? 'header' : 'col'}-${parseCSSVarId(
+ header?.id ?? column.id,
+ )}-size) * 1px)`,
+ };
+
+ if (layoutMode === 'grid') {
+ widthStyles.flex = `${[0, false].includes(columnDef.grow)
+ ? 0
+ : `var(--${header ? 'header' : 'col'}-${parseCSSVarId(
+ header?.id ?? column.id,
+ )}-size)`
+ } 0 auto`;
+ } else if (layoutMode === 'grid-no-grow') {
+ widthStyles.flex = `${+(columnDef.grow || 0)} 0 auto`;
+ }
+
+ const pinnedStyles = isColumnPinned
+ ? {
+ ...getCommonPinnedCellStyles({ column, table, theme }),
+ left:
+ isColumnPinned === 'left'
+ ? `${column.getStart('left')}px`
+ : undefined,
+ opacity: 0.97,
+ position: 'sticky',
+ right:
+ isColumnPinned === 'right'
+ ? `${column.getAfter('right')}px`
+ : undefined,
+ }
+ : {};
+
+ return {
+ backgroundColor: 'inherit',
+ backgroundImage: 'inherit',
+ display: layoutMode?.startsWith('grid') ? 'flex' : undefined,
+ justifyContent:
+ columnDefType === 'group'
+ ? 'center'
+ : layoutMode?.startsWith('grid')
+ ? tableCellProps.align
+ : undefined,
+ opacity:
+ table.getState().draggingColumn?.id === column.id ||
+ table.getState().hoveredColumn?.id === column.id
+ ? 0.5
+ : 1,
+ position: 'relative',
+ transition: enableColumnVirtualization
+ ? 'none'
+ : `padding 150ms ease-in-out`,
+ zIndex:
+ column.getIsResizing() || draggingColumn?.id === column.id
+ ? 2
+ : columnDefType !== 'group' && isColumnPinned
+ ? 1
+ : 0,
+ ...pinnedStyles,
+ ...widthStyles,
+ ...(parseFromValuesOrFunc(tableCellProps?.sx, theme)),
+ };
+};
+
+export const commonCellBeforeAfterStyles = {
+ content: '""',
+ height: '100%',
+ left: 0,
+ position: 'absolute',
+ top: 0,
+ width: '100%',
+ zIndex: -1,
+};
\ No newline at end of file
diff --git a/src/lib/contexts/tableSetting.jsx b/src/lib/contexts/tableSetting.jsx
new file mode 100644
index 0000000..0c2262d
--- /dev/null
+++ b/src/lib/contexts/tableSetting.jsx
@@ -0,0 +1,129 @@
+'use client'
+import {createContext, useCallback, useEffect, useState} from "react";
+import {flattenArrayOfObjects} from "@/core/utils/flattenArrayOfObjects";
+
+export const TableSettingContext = createContext();
+export const TableSettingProvider = ({children}) => {
+ const [settingStore, setSettingStore] = useState({});
+
+ useEffect(() => {
+ const _localStorage = localStorage.getItem("_setting-app")
+ if (_localStorage) {
+ setSettingStore(JSON.parse(_localStorage))
+ }
+ }, []);
+
+ const hideAction = useCallback((user_id, page_name, table_name, settingValue, columns) => {
+ updateSettingStorage(user_id, page_name, table_name, "hides", settingValue, columns);
+ }, [])
+ const sortAction = useCallback((user_id, page_name, table_name, settingValue, columns) => {
+ updateSettingStorage(user_id, page_name, table_name, "sorts", settingValue, columns);
+ }, [])
+ const filterAction = (user_id, page_name, table_name, settingValue, columns) => {
+ updateSettingStorage(user_id, page_name, table_name, "filters", settingValue, columns);
+ }
+ const updateSettingStorage = (user_id, page_name, table_name, settingType, settingValue, columns) => {
+ const prevLocalStorage = JSON.parse(localStorage.getItem("_setting-app")) || {};
+ const userSettings = prevLocalStorage[user_id] || {};
+ const pageSettings = userSettings[page_name] || {};
+ const tableSettings = pageSettings[table_name] || {};
+ let newSettings;
+ if (settingType === "sorts" || settingType === "filters") {
+ newSettings = [...settingValue];
+ } else {
+ newSettings = {...tableSettings[settingType] || {}, ...settingValue};
+ }
+ const updatedLocalStorage = {
+ ...prevLocalStorage,
+ [user_id]: {
+ ...userSettings,
+ [page_name]: {
+ ...pageSettings,
+ [table_name]: {
+ ...tableSettings,
+ [settingType]: Array.isArray(settingValue) ? [...newSettings] : {...newSettings},
+ summary: SummaryTextMessage({
+ ...tableSettings,
+ [settingType]: Array.isArray(settingValue) ? [...newSettings] : {...newSettings},
+ }, columns)
+ },
+ },
+ },
+ };
+ localStorage.setItem("_setting-app", JSON.stringify(updatedLocalStorage));
+ setSettingStore(updatedLocalStorage);
+ }
+
+ const SummaryTextMessage = (values, columns) => {
+ let SummaryText = "";
+ let HideText = "";
+ let FilterText = "";
+ let SortText = "";
+
+ if (values.sorts && values.sorts.length > 0) {
+ const _list = []
+ for (const sort of values.sorts) {
+ const _column = columns.find(column => column.id == sort.id)
+ if (_column)
+ _list.push(`${_column.header}` + `(${sort.desc ? "نزولی" : "صعودی"})`)
+ }
+ if (_list.length > 0)
+ SortText = " مرتب شده بر اساس : " + _list.join(", ")
+ }
+ if (values.filters && values.filters.length > 0) {
+ const _list = []
+ for (const filter of values.filters) {
+ const _column = columns.find(column => column.id == filter.id)
+ if (_column)
+ _list.push(`${_column.header}`)
+ }
+ if (_list.length > 0)
+ FilterText = " فیلتر شده بر اساس : " + _list.join(", ")
+ }
+
+ if (values.hides) {
+ const hidesArray = Object.entries(values.hides).map((e) => ({id: e[0], value: e[1]}))
+ if (hidesArray.length > 0) {
+ const _list = []
+ for (const hide of hidesArray) {
+ if (hide.value) continue
+ const _column = columns.find(c => c.id == hide.id)
+ if (_column)
+ _list.push(_column.header)
+ }
+ if (_list.length > 0)
+ HideText = "ستون های مخفی شده : " + _list.join(", ");
+ }
+ }
+
+ if (HideText || SortText || FilterText) {
+ if (SortText) {
+ SummaryText += SortText;
+ if (HideText) {
+ SummaryText += " ; ";
+ }
+ if (FilterText) {
+ SummaryText += " ; ";
+ }
+ }
+ if (HideText) {
+ SummaryText += HideText;
+ if (FilterText) {
+ SummaryText += " ; ";
+ }
+ }
+ if (FilterText) {
+ SummaryText += FilterText
+ }
+ }
+
+ return SummaryText;
+ };
+
+ return (
+
+ {children}
+
+ );
+};
\ No newline at end of file
diff --git a/src/lib/hooks/useTableSetting.jsx b/src/lib/hooks/useTableSetting.jsx
new file mode 100644
index 0000000..14d9d4e
--- /dev/null
+++ b/src/lib/hooks/useTableSetting.jsx
@@ -0,0 +1,10 @@
+import { useContext } from "react";
+import { TableSettingContext } from "../contexts/tableSetting";
+
+const useTableSetting = () => {
+ const { settingStore, hideAction, sortAction, filterAction } =
+ useContext(TableSettingContext);
+ return { settingStore, hideAction, sortAction, filterAction };
+};
+
+export default useTableSetting;