Compare commits
8 Commits
39014708d7
...
0ae8f6185a
| Author | SHA1 | Date |
|---|---|---|
|
|
0ae8f6185a | |
|
|
882e8349c9 | |
|
|
d641e9432d | |
|
|
0328183852 | |
|
|
880ef9eae5 | |
|
|
ff32a54aac | |
|
|
6a122f3d51 | |
|
|
e324beca50 |
|
|
@ -11,6 +11,7 @@ node_modules
|
||||||
dist
|
dist
|
||||||
dist-react
|
dist-react
|
||||||
dist-electron
|
dist-electron
|
||||||
|
release
|
||||||
*.local
|
*.local
|
||||||
release
|
release
|
||||||
|
|
||||||
|
|
|
||||||
BIN
appIcon.png
BIN
appIcon.png
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 435 KiB |
|
|
@ -1,16 +0,0 @@
|
||||||
{
|
|
||||||
"appId": "com.n-ziermann.electron-course",
|
|
||||||
"files": ["dist-electron", "dist-react"],
|
|
||||||
"extraResources": ["dist-electron/preload.cjs", "src/assets/**"],
|
|
||||||
"icon": "./desktopIcon.png",
|
|
||||||
"mac": {
|
|
||||||
"target": "dmg"
|
|
||||||
},
|
|
||||||
"linux": {
|
|
||||||
"target": "AppImage",
|
|
||||||
"category": "Utility"
|
|
||||||
},
|
|
||||||
"win": {
|
|
||||||
"target": ["portable", "msi"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
|
@ -17,8 +17,10 @@
|
||||||
"lucide-react": "^0.552.0",
|
"lucide-react": "^0.552.0",
|
||||||
"luxon": "^3.7.2",
|
"luxon": "^3.7.2",
|
||||||
"os-utils": "^0.0.14",
|
"os-utils": "^0.0.14",
|
||||||
|
"quill": "^2.0.3",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
|
"react-quill-new": "^3.6.0",
|
||||||
"react-redux": "^9.2.0",
|
"react-redux": "^9.2.0",
|
||||||
"react-router-dom": "^7.9.5",
|
"react-router-dom": "^7.9.5",
|
||||||
"recharts": "^2.12.7",
|
"recharts": "^2.12.7",
|
||||||
|
|
@ -82,7 +84,6 @@
|
||||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
"@babel/generator": "^7.28.5",
|
"@babel/generator": "^7.28.5",
|
||||||
|
|
@ -2876,7 +2877,6 @@
|
||||||
"integrity": "sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==",
|
"integrity": "sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
|
|
@ -3147,7 +3147,6 @@
|
||||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.1",
|
"fast-deep-equal": "^3.1.1",
|
||||||
"fast-json-stable-stringify": "^2.0.0",
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
|
|
@ -3350,6 +3349,7 @@
|
||||||
"integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==",
|
"integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"archiver-utils": "^2.1.0",
|
"archiver-utils": "^2.1.0",
|
||||||
"async": "^3.2.4",
|
"async": "^3.2.4",
|
||||||
|
|
@ -3369,6 +3369,7 @@
|
||||||
"integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
|
"integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"glob": "^7.1.4",
|
"glob": "^7.1.4",
|
||||||
"graceful-fs": "^4.2.0",
|
"graceful-fs": "^4.2.0",
|
||||||
|
|
@ -3390,7 +3391,8 @@
|
||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/archiver-utils/node_modules/readable-stream": {
|
"node_modules/archiver-utils/node_modules/readable-stream": {
|
||||||
"version": "2.3.8",
|
"version": "2.3.8",
|
||||||
|
|
@ -3398,6 +3400,7 @@
|
||||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-util-is": "~1.0.0",
|
"core-util-is": "~1.0.0",
|
||||||
"inherits": "~2.0.3",
|
"inherits": "~2.0.3",
|
||||||
|
|
@ -3413,7 +3416,8 @@
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/archiver-utils/node_modules/string_decoder": {
|
"node_modules/archiver-utils/node_modules/string_decoder": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
|
|
@ -3421,6 +3425,7 @@
|
||||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safe-buffer": "~5.1.0"
|
"safe-buffer": "~5.1.0"
|
||||||
}
|
}
|
||||||
|
|
@ -3705,7 +3710,6 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.8.25",
|
"baseline-browser-mapping": "^2.8.25",
|
||||||
"caniuse-lite": "^1.0.30001754",
|
"caniuse-lite": "^1.0.30001754",
|
||||||
|
|
@ -4323,6 +4327,7 @@
|
||||||
"integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==",
|
"integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"buffer-crc32": "^0.2.13",
|
"buffer-crc32": "^0.2.13",
|
||||||
"crc32-stream": "^4.0.2",
|
"crc32-stream": "^4.0.2",
|
||||||
|
|
@ -4524,6 +4529,7 @@
|
||||||
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
|
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"crc32": "bin/crc32.njs"
|
"crc32": "bin/crc32.njs"
|
||||||
},
|
},
|
||||||
|
|
@ -4537,6 +4543,7 @@
|
||||||
"integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==",
|
"integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"crc-32": "^1.2.0",
|
"crc-32": "^1.2.0",
|
||||||
"readable-stream": "^3.4.0"
|
"readable-stream": "^3.4.0"
|
||||||
|
|
@ -5018,7 +5025,6 @@
|
||||||
"integrity": "sha512-NoXo6Liy2heSklTI5OIZbCgXC1RzrDQsZkeEwXhdOro3FT1VBOvbubvscdPnjVuQ4AMwwv61oaH96AbiYg9EnQ==",
|
"integrity": "sha512-NoXo6Liy2heSklTI5OIZbCgXC1RzrDQsZkeEwXhdOro3FT1VBOvbubvscdPnjVuQ4AMwwv61oaH96AbiYg9EnQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"app-builder-lib": "25.1.8",
|
"app-builder-lib": "25.1.8",
|
||||||
"builder-util": "25.1.7",
|
"builder-util": "25.1.7",
|
||||||
|
|
@ -5238,6 +5244,7 @@
|
||||||
"integrity": "sha512-2ntkJ+9+0GFP6nAISiMabKt6eqBB0kX1QqHNWFWAXgi0VULKGisM46luRFpIBiU3u/TDmhZMM8tzvo2Abn3ayg==",
|
"integrity": "sha512-2ntkJ+9+0GFP6nAISiMabKt6eqBB0kX1QqHNWFWAXgi0VULKGisM46luRFpIBiU3u/TDmhZMM8tzvo2Abn3ayg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"app-builder-lib": "25.1.8",
|
"app-builder-lib": "25.1.8",
|
||||||
"archiver": "^5.3.1",
|
"archiver": "^5.3.1",
|
||||||
|
|
@ -5251,6 +5258,7 @@
|
||||||
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
|
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graceful-fs": "^4.2.0",
|
"graceful-fs": "^4.2.0",
|
||||||
"jsonfile": "^6.0.1",
|
"jsonfile": "^6.0.1",
|
||||||
|
|
@ -5266,6 +5274,7 @@
|
||||||
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
|
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"universalify": "^2.0.0"
|
"universalify": "^2.0.0"
|
||||||
},
|
},
|
||||||
|
|
@ -5279,6 +5288,7 @@
|
||||||
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
|
|
@ -6034,6 +6044,12 @@
|
||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-diff": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/fast-equals": {
|
"node_modules/fast-equals": {
|
||||||
"version": "5.3.3",
|
"version": "5.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.3.3.tgz",
|
||||||
|
|
@ -6215,7 +6231,8 @@
|
||||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
|
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/fs-extra": {
|
"node_modules/fs-extra": {
|
||||||
"version": "8.1.0",
|
"version": "8.1.0",
|
||||||
|
|
@ -7151,7 +7168,7 @@
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||||
|
|
@ -7902,6 +7919,7 @@
|
||||||
"integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
|
"integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"readable-stream": "^2.0.5"
|
"readable-stream": "^2.0.5"
|
||||||
},
|
},
|
||||||
|
|
@ -7914,7 +7932,8 @@
|
||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/lazystream/node_modules/readable-stream": {
|
"node_modules/lazystream/node_modules/readable-stream": {
|
||||||
"version": "2.3.8",
|
"version": "2.3.8",
|
||||||
|
|
@ -7922,6 +7941,7 @@
|
||||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-util-is": "~1.0.0",
|
"core-util-is": "~1.0.0",
|
||||||
"inherits": "~2.0.3",
|
"inherits": "~2.0.3",
|
||||||
|
|
@ -7937,7 +7957,8 @@
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/lazystream/node_modules/string_decoder": {
|
"node_modules/lazystream/node_modules/string_decoder": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
|
|
@ -7945,6 +7966,7 @@
|
||||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safe-buffer": "~5.1.0"
|
"safe-buffer": "~5.1.0"
|
||||||
}
|
}
|
||||||
|
|
@ -8220,25 +8242,47 @@
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash-es": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/lodash.clonedeep": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
||||||
|
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/lodash.defaults": {
|
"node_modules/lodash.defaults": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||||
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
|
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/lodash.difference": {
|
"node_modules/lodash.difference": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
|
||||||
"integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==",
|
"integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/lodash.flatten": {
|
"node_modules/lodash.flatten": {
|
||||||
"version": "4.4.0",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
|
||||||
"integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==",
|
"integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/lodash.isequal": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||||
|
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
|
||||||
|
"deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/lodash.isplainobject": {
|
"node_modules/lodash.isplainobject": {
|
||||||
|
|
@ -8246,14 +8290,16 @@
|
||||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||||
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
|
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/lodash.union": {
|
"node_modules/lodash.union": {
|
||||||
"version": "4.6.0",
|
"version": "4.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
|
||||||
"integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==",
|
"integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/log-symbols": {
|
"node_modules/log-symbols": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
|
|
@ -9058,6 +9104,7 @@
|
||||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
|
|
@ -9481,6 +9528,12 @@
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "BlueOak-1.0.0"
|
"license": "BlueOak-1.0.0"
|
||||||
},
|
},
|
||||||
|
"node_modules/parchment": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/parchment/-/parchment-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/parse-json": {
|
"node_modules/parse-json": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
|
||||||
|
|
@ -9737,7 +9790,8 @@
|
||||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/progress": {
|
"node_modules/progress": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
|
|
@ -9827,6 +9881,41 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/quill": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/quill/-/quill-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-xEYQBqfYx/sfb33VJiKnSJp8ehloavImQ2A6564GAbqG55PGw1dAWUn1MUbQB62t0azawUS2CZZhWCjO8gRvTw==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"eventemitter3": "^5.0.1",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
|
"parchment": "^3.0.0",
|
||||||
|
"quill-delta": "^5.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"npm": ">=8.2.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/quill-delta": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"fast-diff": "^1.3.0",
|
||||||
|
"lodash.clonedeep": "^4.5.0",
|
||||||
|
"lodash.isequal": "^4.5.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/quill/node_modules/eventemitter3": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/rcedit": {
|
"node_modules/rcedit": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/rcedit/-/rcedit-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/rcedit/-/rcedit-5.0.2.tgz",
|
||||||
|
|
@ -9845,7 +9934,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0"
|
"loose-envify": "^1.1.0"
|
||||||
},
|
},
|
||||||
|
|
@ -9858,7 +9946,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0",
|
"loose-envify": "^1.1.0",
|
||||||
"scheduler": "^0.23.2"
|
"scheduler": "^0.23.2"
|
||||||
|
|
@ -9873,12 +9960,26 @@
|
||||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/react-quill-new": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-quill-new/-/react-quill-new-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-weU6YfB2+7Cujw5Hjgmi0aN/qJd3B6ADWrxgUJMp2MO3tEvKX5kfB0sg3P0UdOVfU0z8icsKFzlnEIpeW1mLhw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
|
"quill": "~2.0.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"quill-delta": "^5.1.0",
|
||||||
|
"react": "^16 || ^17 || ^18 || ^19",
|
||||||
|
"react-dom": "^16 || ^17 || ^18 || ^19"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-redux": {
|
"node_modules/react-redux": {
|
||||||
"version": "9.2.0",
|
"version": "9.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
||||||
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/use-sync-external-store": "^0.0.6",
|
"@types/use-sync-external-store": "^0.0.6",
|
||||||
"use-sync-external-store": "^1.4.0"
|
"use-sync-external-store": "^1.4.0"
|
||||||
|
|
@ -10025,6 +10126,7 @@
|
||||||
"integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
|
"integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimatch": "^5.1.0"
|
"minimatch": "^5.1.0"
|
||||||
}
|
}
|
||||||
|
|
@ -10035,6 +10137,7 @@
|
||||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^1.0.0"
|
"balanced-match": "^1.0.0"
|
||||||
}
|
}
|
||||||
|
|
@ -10045,6 +10148,7 @@
|
||||||
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
|
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"brace-expansion": "^2.0.1"
|
"brace-expansion": "^2.0.1"
|
||||||
},
|
},
|
||||||
|
|
@ -10088,8 +10192,7 @@
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
||||||
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/redux-thunk": {
|
"node_modules/redux-thunk": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
|
|
@ -11368,6 +11471,7 @@
|
||||||
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
|
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bl": "^4.0.3",
|
"bl": "^4.0.3",
|
||||||
"end-of-stream": "^1.4.1",
|
"end-of-stream": "^1.4.1",
|
||||||
|
|
@ -11838,7 +11942,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
|
||||||
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.21.3",
|
"esbuild": "^0.21.3",
|
||||||
"postcss": "^8.4.43",
|
"postcss": "^8.4.43",
|
||||||
|
|
@ -12400,6 +12503,7 @@
|
||||||
"integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==",
|
"integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"archiver-utils": "^3.0.4",
|
"archiver-utils": "^3.0.4",
|
||||||
"compress-commons": "^4.1.2",
|
"compress-commons": "^4.1.2",
|
||||||
|
|
@ -12415,6 +12519,7 @@
|
||||||
"integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==",
|
"integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"glob": "^7.2.3",
|
"glob": "^7.2.3",
|
||||||
"graceful-fs": "^4.2.0",
|
"graceful-fs": "^4.2.0",
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@
|
||||||
"axios": "^1.13.2",
|
"axios": "^1.13.2",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
"electron-store": "^11.0.2",
|
"electron-store": "^11.0.2",
|
||||||
|
"quill": "^2.0.3",
|
||||||
|
"react-quill-new": "^3.6.0",
|
||||||
"https": "^1.0.0",
|
"https": "^1.0.0",
|
||||||
"lucide-react": "^0.552.0",
|
"lucide-react": "^0.552.0",
|
||||||
"luxon": "^3.7.2",
|
"luxon": "^3.7.2",
|
||||||
|
|
@ -69,7 +71,9 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"mac": {
|
"mac": {
|
||||||
"target": ["dmg"],
|
"target": [
|
||||||
|
"dmg"
|
||||||
|
],
|
||||||
"icon": "build/icon.icns",
|
"icon": "build/icon.icns",
|
||||||
"extendInfo": {
|
"extendInfo": {
|
||||||
"LSUIElement": false,
|
"LSUIElement": false,
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 435 KiB |
|
|
@ -340,6 +340,28 @@ ipcMain.handle("create-timesheet", async (event, data) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
ipcMain.handle("create-leave-request", async (event, payload) => {
|
||||||
|
try {
|
||||||
|
console.log(" Leave Request Received:", payload);
|
||||||
|
|
||||||
|
const response = await api.post("/leave/request", payload);
|
||||||
|
|
||||||
|
console.log(" Leave Saved:", response.data);
|
||||||
|
|
||||||
|
return { success: true, data: response.data };
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ Leave Save Failed:", error);
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error || "Unknown error"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const sendTokenExpired = () => {
|
export const sendTokenExpired = () => {
|
||||||
console.log("⚠️ Sending token-expired event to renderer");
|
console.log("⚠️ Sending token-expired event to renderer");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,28 @@
|
||||||
|
// import path from 'path';
|
||||||
|
// import { app } from 'electron';
|
||||||
|
// import { isDev } from './util.js';
|
||||||
|
|
||||||
|
// export function getPreloadPath() {
|
||||||
|
// return path.join(
|
||||||
|
// app.getAppPath(),
|
||||||
|
// isDev() ? '.' : '..',
|
||||||
|
// '/dist-electron/preload.cjs'
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export function getUIPath() {
|
||||||
|
// return path.join(app.getAppPath(), "dist-react", "index.html");
|
||||||
|
|
||||||
|
// // return "C:\\Users\\user\\OneDrive\\Desktop\\electron-main\\dist-react\\index.html";
|
||||||
|
// // return path.join(app.getAppPath(), '/dist-react/index.html');
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export function getAssetPath() {
|
||||||
|
// return path.join(app.getAppPath(), isDev() ? '.' : '..', '/src/assets');
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { app } from 'electron';
|
import { app } from 'electron';
|
||||||
import { isDev } from './util.js';
|
import { isDev } from './util.js';
|
||||||
|
|
@ -6,17 +31,22 @@ export function getPreloadPath() {
|
||||||
return path.join(
|
return path.join(
|
||||||
app.getAppPath(),
|
app.getAppPath(),
|
||||||
isDev() ? '.' : '..',
|
isDev() ? '.' : '..',
|
||||||
'/dist-electron/preload.cjs'
|
'/dist-electron/preload.cjs' // ✅ Changed to .js
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUIPath() {
|
export function getUIPath() {
|
||||||
return path.join(app.getAppPath(), "dist-react", "index.html");
|
return path.join(app.getAppPath(), "dist-react", "index.html");
|
||||||
|
|
||||||
// return "C:\\Users\\user\\OneDrive\\Desktop\\electron-main\\dist-react\\index.html";
|
|
||||||
// return path.join(app.getAppPath(), '/dist-react/index.html');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAssetPath() {
|
export function getAssetPath() {
|
||||||
return path.join(app.getAppPath(), isDev() ? '.' : '..', '/src/assets');
|
return path.join(app.getAppPath(), isDev() ? '.' : '..', '/assets');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getIconPath() {
|
||||||
|
return path.join(getAssetPath(), 'icons', 'appIcon.ico');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSplashPath() {
|
||||||
|
return path.join(getAssetPath(), 'splash.html');
|
||||||
}
|
}
|
||||||
|
|
@ -50,6 +50,10 @@ contextBridge.exposeInMainWorld('electron', {
|
||||||
|
|
||||||
createTimesheet: (data: any) => ipcRenderer.invoke('create-timesheet', data),
|
createTimesheet: (data: any) => ipcRenderer.invoke('create-timesheet', data),
|
||||||
|
|
||||||
|
createPTOAndSickLeaveRequest: (data) =>
|
||||||
|
ipcRenderer.invoke("create-leave-request", data),
|
||||||
|
|
||||||
|
|
||||||
} satisfies Window['electron']);
|
} satisfies Window['electron']);
|
||||||
|
|
||||||
// function ipcInvoke<Key extends keyof EventPayloadMapping>(
|
// function ipcInvoke<Key extends keyof EventPayloadMapping>(
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,6 @@ export const handleLogin = async (credentials: { email: string, password: string
|
||||||
console.log("UIPATH", getUIPath());
|
console.log("UIPATH", getUIPath());
|
||||||
console.log("Doom 1");
|
console.log("Doom 1");
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log("➡️ Calling API...");
|
console.log("➡️ Calling API...");
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
|
|
@ -191,3 +190,6 @@ export const captureSpecificApp = async (windowTitle: string): Promise<string> =
|
||||||
return 'Failed to capture screenshot.';
|
return 'Failed to capture screenshot.';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// timmer stop after 4-5 sec check it..
|
||||||
|
|
@ -107,7 +107,7 @@ const DashboardLayout = () => {
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{ label: "Dashboard", path: "/dashboard", icon: <Home size={18} />, exact: true },
|
{ label: "Dashboard", path: "/dashboard", icon: <Home size={18} />, exact: true },
|
||||||
{ label: "Timesheet", path: "/dashboard/timesheet", icon: <TimerIcon size={18} /> },
|
{ label: "Timesheet", path: "/dashboard/timesheet", icon: <TimerIcon size={18} /> },
|
||||||
{ label: "PTO", path: "/dashboard/leave-adjustments", icon: <CalendarDays size={18} /> },
|
{ label: "Leave & Adjust", path: "/dashboard/leave-adjustments", icon: <CalendarDays size={18} /> },
|
||||||
{ label: "Settings", path: "/dashboard/settings", icon: <SettingsIcon size={18} /> },
|
{ label: "Settings", path: "/dashboard/settings", icon: <SettingsIcon size={18} /> },
|
||||||
{ label: "VPN", path: "/dashboard/vpn", icon: <GlobeLock size={18} /> },
|
{ label: "VPN", path: "/dashboard/vpn", icon: <GlobeLock size={18} /> },
|
||||||
{ label: "Help & Support", path: "/dashboard/help-support", icon: <HelpCircleIcon size={18} /> },
|
{ label: "Help & Support", path: "/dashboard/help-support", icon: <HelpCircleIcon size={18} /> },
|
||||||
|
|
@ -211,6 +211,54 @@ const DashboardLayout = () => {
|
||||||
|
|
||||||
// STEP 6: Clear active project log ID
|
// STEP 6: Clear active project log ID
|
||||||
setActiveProjectLogId("");
|
setActiveProjectLogId("");
|
||||||
|
|
||||||
|
// ✨ NEW STEP 7: Refresh project list to get updated work hours
|
||||||
|
try {
|
||||||
|
console.log("🔄 Refreshing project list...");
|
||||||
|
const res = await window.electron.getProjectList();
|
||||||
|
console.log("📊 Updated project list:", res);
|
||||||
|
|
||||||
|
if (res?.success && Array.isArray(res.data)) {
|
||||||
|
// Update projects data
|
||||||
|
dispatch(setProjectsDataSlice(res.data));
|
||||||
|
|
||||||
|
// Update project summary
|
||||||
|
dispatch(
|
||||||
|
setProjectSummarySlice({
|
||||||
|
totalProjects: res.totalProjects || 0,
|
||||||
|
activeProjects: res.activeProjects || 0,
|
||||||
|
awayProjects: res.awayProjects || 0,
|
||||||
|
totalWorkTime: res.totalWorkTime || "0h 0m 0s",
|
||||||
|
totalWorkMs: res.totalWorkMs || 0,
|
||||||
|
vaName: res.vaName || "",
|
||||||
|
vaId: res.vaId || "",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update selected project with fresh data
|
||||||
|
const updatedProject = res.data.find(
|
||||||
|
(p: Project) => p.projectId === projectId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (updatedProject) {
|
||||||
|
dispatch(setSelectedProjectSlice(updatedProject));
|
||||||
|
|
||||||
|
// Update tracking map with new total work time
|
||||||
|
const initialElapsed = Math.floor((updatedProject.totalWorkMs || 0) / 1000);
|
||||||
|
dispatch(
|
||||||
|
initializeProjectTracking({
|
||||||
|
projectId: updatedProject.projectId,
|
||||||
|
initialTime: initialElapsed,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("✅ Selected project updated with fresh data");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ Error refreshing project list:", error);
|
||||||
|
}
|
||||||
|
|
||||||
setLoadingStop(false);
|
setLoadingStop(false);
|
||||||
console.log("🧹 Active project log ID cleared");
|
console.log("🧹 Active project log ID cleared");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { RootState } from '../../store';
|
import { RootState } from '../../store';
|
||||||
|
|
@ -9,23 +10,46 @@ type TabType = 'pto' | 'sick' | 'adjustment';
|
||||||
|
|
||||||
const LeaveAndAdjustments: React.FC = () => {
|
const LeaveAndAdjustments: React.FC = () => {
|
||||||
const [activeTab, setActiveTab] = useState<TabType>('pto');
|
const [activeTab, setActiveTab] = useState<TabType>('pto');
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false); // 🚀 NEW
|
||||||
const projects = useSelector((state: RootState) => state.projects.projectsData);
|
const projects = useSelector((state: RootState) => state.projects.projectsData);
|
||||||
|
|
||||||
console.log("LeaveAndAdjustments", projects);
|
// ------- COMMON API CALL FOR BOTH TABS -------
|
||||||
|
const submitLeave = async (payload: {
|
||||||
|
dateFrom: string;
|
||||||
|
dateTo: string;
|
||||||
|
leaveType: "PTO" | "SICK";
|
||||||
|
comment: string;
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
setIsSubmitting(true);
|
||||||
|
|
||||||
|
console.log("Leave Payload:", payload);
|
||||||
|
|
||||||
|
const response = await window.electron.createPTOAndSickLeaveRequest(payload)
|
||||||
|
console.log("Leave Payload:" ,response)
|
||||||
|
if (response.success) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-h-screen">
|
<div className="max-h-screen">
|
||||||
<div className="w-full mx-auto">
|
<div className="w-full mx-auto">
|
||||||
{/* Header */}
|
|
||||||
<div className="">
|
|
||||||
<h1 className="text-mb font-bold mb-2">Leave & Adjust Timesheet</h1>
|
<h1 className="text-mb font-bold mb-2">Leave & Adjust Timesheet</h1>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Tabs Navigation */}
|
{/* Tabs — Disabled while submitting */}
|
||||||
<div role="tablist" className="tabs tabs-border w-full mb-1">
|
<div role="tablist" className="tabs tabs-border w-full mb-1 opacity-100">
|
||||||
<a
|
<a
|
||||||
role="tab"
|
role="tab"
|
||||||
className={`tab text-xs font-semibold ${activeTab === "pto" ? "tab-active text-primary" : ""}`}
|
className={`tab text-xs font-semibold ${
|
||||||
|
activeTab === "pto" ? "tab-active text-primary" : ""
|
||||||
|
} ${isSubmitting ? "pointer-events-none opacity-50" : ""}`}
|
||||||
onClick={() => setActiveTab("pto")}
|
onClick={() => setActiveTab("pto")}
|
||||||
>
|
>
|
||||||
PTO Leave
|
PTO Leave
|
||||||
|
|
@ -33,7 +57,9 @@ const LeaveAndAdjustments: React.FC = () => {
|
||||||
|
|
||||||
<a
|
<a
|
||||||
role="tab"
|
role="tab"
|
||||||
className={`tab text-xs font-semibold ${activeTab === "sick" ? "tab-active text-primary" : ""}`}
|
className={`tab text-xs font-semibold ${
|
||||||
|
activeTab === "sick" ? "tab-active text-primary" : ""
|
||||||
|
} ${isSubmitting ? "pointer-events-none opacity-50" : ""}`}
|
||||||
onClick={() => setActiveTab("sick")}
|
onClick={() => setActiveTab("sick")}
|
||||||
>
|
>
|
||||||
Sick Leave
|
Sick Leave
|
||||||
|
|
@ -41,7 +67,9 @@ const LeaveAndAdjustments: React.FC = () => {
|
||||||
|
|
||||||
<a
|
<a
|
||||||
role="tab"
|
role="tab"
|
||||||
className={`tab text-xs font-semibold ${activeTab === "adjustment" ? "tab-active text-primary" : ""}`}
|
className={`tab text-xs font-semibold ${
|
||||||
|
activeTab === "adjustment" ? "tab-active text-primary" : ""
|
||||||
|
} ${isSubmitting ? "pointer-events-none opacity-50" : ""}`}
|
||||||
onClick={() => setActiveTab("adjustment")}
|
onClick={() => setActiveTab("adjustment")}
|
||||||
>
|
>
|
||||||
Adjustment Submission
|
Adjustment Submission
|
||||||
|
|
@ -51,43 +79,22 @@ const LeaveAndAdjustments: React.FC = () => {
|
||||||
{/* Main Container */}
|
{/* Main Container */}
|
||||||
<div className="rounded-lg bg-base-100 overflow-hidden">
|
<div className="rounded-lg bg-base-100 overflow-hidden">
|
||||||
<div className="px-8 py-4 overflow-y-auto no-scrollbar max-h-[81vh]">
|
<div className="px-8 py-4 overflow-y-auto no-scrollbar max-h-[81vh]">
|
||||||
{activeTab === 'pto' && (
|
|
||||||
<div className="animate-fade-in">
|
{activeTab === "pto" && (
|
||||||
<PtoLeaveTab />
|
<PtoLeaveTab submitLeave={submitLeave} isSubmitting={isSubmitting} />
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{activeTab === 'sick' && (
|
{activeTab === "sick" && (
|
||||||
<div className="animate-fade-in">
|
<SickLeaveTab submitLeave={submitLeave} isSubmitting={isSubmitting} />
|
||||||
<SickLeaveTab />
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{activeTab === 'adjustment' && (
|
{activeTab === "adjustment" && (
|
||||||
<div className="animate-fade-in">
|
|
||||||
<AdjustmentTab projects={projects} />
|
<AdjustmentTab projects={projects} />
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>{`
|
</div>
|
||||||
@keyframes fade-in {
|
</div>
|
||||||
from {
|
</div>
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(10px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.animate-fade-in {
|
|
||||||
animation: fade-in 0.3s ease-out;
|
|
||||||
}
|
|
||||||
`}</style>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { DateTime } from 'luxon';
|
||||||
interface Project {
|
interface Project {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
projectName: string;
|
projectName: string;
|
||||||
|
status?: string | boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ProjectSummaryResponse {
|
interface ProjectSummaryResponse {
|
||||||
|
|
@ -370,7 +371,7 @@ const AdjustmentTab: React.FC<AdjustmentTabProps> = ({ projects }) => {
|
||||||
Pick a project
|
Pick a project
|
||||||
</option>
|
</option>
|
||||||
{projects.map((item, index) => (
|
{projects.map((item, index) => (
|
||||||
<option key={item.projectId || index} value={item.projectId}>
|
<option key={item.projectId || index} value={item.projectId} className={`${item.status ? "" : ""}`}>
|
||||||
{item.projectName}
|
{item.projectName}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import React, { useState } from 'react';
|
// PtoLeaveTab.tsx
|
||||||
|
import React, { useState } from "react";
|
||||||
|
|
||||||
interface LeaveFormData {
|
interface LeaveFormData {
|
||||||
fromDate: string;
|
fromDate: string;
|
||||||
|
|
@ -6,68 +7,77 @@ interface LeaveFormData {
|
||||||
comment: string;
|
comment: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PtoLeaveTab: React.FC = () => {
|
const PtoLeaveTab: React.FC<{
|
||||||
|
submitLeave: Function;
|
||||||
|
isSubmitting: boolean;
|
||||||
|
}> = ({ submitLeave, isSubmitting }) => {
|
||||||
|
|
||||||
const [ptoLeave, setPtoLeave] = useState<LeaveFormData>({
|
const [ptoLeave, setPtoLeave] = useState<LeaveFormData>({
|
||||||
fromDate: '',
|
fromDate: "",
|
||||||
toDate: '',
|
toDate: "",
|
||||||
comment: ''
|
comment: "",
|
||||||
});
|
});
|
||||||
const [ptoErrors, setPtoErrors] = useState<Partial<LeaveFormData>>({});
|
|
||||||
|
|
||||||
const validateLeaveForm = (data: LeaveFormData): Partial<LeaveFormData> => {
|
const [errors, setErrors] = useState<Partial<LeaveFormData>>({});
|
||||||
const errors: Partial<LeaveFormData> = {};
|
|
||||||
|
|
||||||
if (!data.fromDate) {
|
const validate = () => {
|
||||||
errors.fromDate = 'From date is required';
|
const e: Partial<LeaveFormData> = {};
|
||||||
|
|
||||||
|
const today = new Date();
|
||||||
|
today.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const from = ptoLeave.fromDate ? new Date(ptoLeave.fromDate) : null;
|
||||||
|
const to = ptoLeave.toDate ? new Date(ptoLeave.toDate) : null;
|
||||||
|
|
||||||
|
// From date required
|
||||||
|
if (!from) {
|
||||||
|
e.fromDate = "From date is required";
|
||||||
|
} else if (from < today) {
|
||||||
|
e.fromDate = "From date cannot be in the past";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data.toDate) {
|
// To date required
|
||||||
errors.toDate = 'To date is required';
|
if (!to) {
|
||||||
|
e.toDate = "To date is required";
|
||||||
|
} else if (to < today) {
|
||||||
|
e.toDate = "To date cannot be in the past";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data.comment.trim()) {
|
// To date must be same or after From date
|
||||||
errors.comment = 'Comment is required';
|
if (from && to && to < from) {
|
||||||
|
e.toDate = "To date cannot be before From date";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.fromDate && data.toDate && new Date(data.fromDate) > new Date(data.toDate)) {
|
// Comment validation
|
||||||
errors.toDate = 'To date must be after from date';
|
if (!ptoLeave.comment.trim()) {
|
||||||
|
e.comment = "Comment required";
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors;
|
return e;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePtoChange = (field: keyof LeaveFormData, value: string) => {
|
const handleSubmit = async () => {
|
||||||
setPtoLeave(prev => ({ ...prev, [field]: value }));
|
const e = validate();
|
||||||
// Clear error for the field being changed
|
if (Object.keys(e).length) return setErrors(e);
|
||||||
if (ptoErrors[field]) {
|
|
||||||
setPtoErrors(prev => ({ ...prev, [field]: undefined }));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePtoSubmit = async () => {
|
const success = await submitLeave({
|
||||||
const errors = validateLeaveForm(ptoLeave);
|
dateFrom: ptoLeave.fromDate,
|
||||||
|
dateTo: ptoLeave.toDate,
|
||||||
|
leaveType: "PTO",
|
||||||
|
comment: ptoLeave.comment,
|
||||||
|
});
|
||||||
|
|
||||||
if (Object.keys(errors).length > 0) {
|
if (success) {
|
||||||
setPtoErrors(errors);
|
setPtoLeave({ fromDate: "", toDate: "", comment: "" });
|
||||||
return;
|
setErrors({});
|
||||||
}
|
alert("PTO submitted!");
|
||||||
|
|
||||||
try {
|
|
||||||
console.log('Submitting PTO:', ptoLeave);
|
|
||||||
alert('PTO request submitted successfully!');
|
|
||||||
|
|
||||||
setPtoLeave({ fromDate: '', toDate: '', comment: "" });
|
|
||||||
setPtoErrors({});
|
|
||||||
} catch (error) {
|
|
||||||
alert('Failed to submit request. Please try again.');
|
|
||||||
console.error('Error:', error);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-w-4xl">
|
<div className="max-w-4xl space-y-3">
|
||||||
<div className="space-y-3">
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
|
||||||
{/* From Date */}
|
{/* From Date */}
|
||||||
<div className="form-control">
|
<div className="form-control">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
|
|
@ -75,17 +85,14 @@ const PtoLeaveTab: React.FC = () => {
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
|
disabled={isSubmitting}
|
||||||
value={ptoLeave.fromDate}
|
value={ptoLeave.fromDate}
|
||||||
onChange={(e) => handlePtoChange('fromDate', e.target.value)}
|
min={new Date().toISOString().split("T")[0]}
|
||||||
className={`input input-bordered input-sm w-full focus:input-primary transition-all ${
|
onChange={(e) => setPtoLeave({ ...ptoLeave, fromDate: e.target.value })}
|
||||||
ptoErrors.fromDate ? 'input-error' : ''
|
className={`input input-bordered input-sm ${errors.fromDate ? "input-error" : ""
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
{ptoErrors.fromDate && (
|
{errors.fromDate && <p className="text-error text-xs">{errors.fromDate}</p>}
|
||||||
<label className="label">
|
|
||||||
<span className="label-text-alt text-xs text-error font-medium">{ptoErrors.fromDate}</span>
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* To Date */}
|
{/* To Date */}
|
||||||
|
|
@ -95,54 +102,45 @@ const PtoLeaveTab: React.FC = () => {
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
|
disabled={isSubmitting}
|
||||||
value={ptoLeave.toDate}
|
value={ptoLeave.toDate}
|
||||||
onChange={(e) => handlePtoChange('toDate', e.target.value)}
|
min={new Date().toISOString().split("T")[0]}
|
||||||
className={`input input-bordered input-sm w-full focus:input-primary transition-all ${
|
onChange={(e) => setPtoLeave({ ...ptoLeave, toDate: e.target.value })}
|
||||||
ptoErrors.toDate ? 'input-error' : ''
|
className={`input input-bordered input-sm ${errors.toDate ? "input-error" : ""
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
{ptoErrors.toDate && (
|
{errors.toDate && <p className="text-error text-xs">{errors.toDate}</p>}
|
||||||
<label className="label">
|
|
||||||
<span className="label-text-alt text-xs text-error font-medium">{ptoErrors.toDate}</span>
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Description/Comment Field */}
|
{/* Comment */}
|
||||||
<div className="form-control flex flex-col gap-1 col-span-2">
|
<div className="form-control col-span-2">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-semibold text-sm">Description / Reason</span>
|
<span className="label-text font-semibold text-sm">Description</span>
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
className={`textarea text-xs textarea-bordered h-40 w-full focus:textarea-primary transition-all ${
|
disabled={isSubmitting}
|
||||||
ptoErrors.comment ? 'textarea-error' : ''
|
|
||||||
}`}
|
|
||||||
placeholder="Please enter the reason for your PTO request to help us better understand your needs."
|
|
||||||
value={ptoLeave.comment}
|
value={ptoLeave.comment}
|
||||||
onChange={(e) => handlePtoChange('comment', e.target.value)}
|
placeholder="Please describe your symptoms or reason for sick leave to help us better understand your situation."
|
||||||
|
onChange={(e) => setPtoLeave({ ...ptoLeave, comment: e.target.value })}
|
||||||
|
className={`textarea textarea-bordered text-xs h-40 ${errors.comment ? "textarea-error" : ""
|
||||||
|
}`}
|
||||||
/>
|
/>
|
||||||
{ptoErrors.comment && (
|
{errors.comment && <p className="text-error text-xs">{errors.comment}</p>}
|
||||||
<label className="label">
|
|
||||||
<span className="label-text-alt text-xs text-error font-medium">{ptoErrors.comment}</span>
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Submit Button */}
|
|
||||||
<div className="flex gap-3">
|
|
||||||
<button
|
<button
|
||||||
onClick={handlePtoSubmit}
|
onClick={handleSubmit}
|
||||||
className="btn btn-primary flex-1 hover:shadow-lg transition-all duration-300"
|
disabled={isSubmitting}
|
||||||
|
className="btn btn-primary flex-1"
|
||||||
>
|
>
|
||||||
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
{isSubmitting ? (
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
<span className="loading loading-spinner loading-sm"></span>
|
||||||
</svg>
|
) : (
|
||||||
Submit PTO Request
|
"Submit PTO Request"
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,10 @@ interface LeaveFormData {
|
||||||
comment: string;
|
comment: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SickLeaveTab: React.FC = () => {
|
const SickLeaveTab: React.FC<{
|
||||||
|
submitLeave: Function;
|
||||||
|
isSubmitting: boolean;
|
||||||
|
}> = ({ submitLeave, isSubmitting }) => {
|
||||||
const [sickLeave, setSickLeave] = useState<LeaveFormData>({
|
const [sickLeave, setSickLeave] = useState<LeaveFormData>({
|
||||||
fromDate: '',
|
fromDate: '',
|
||||||
toDate: '',
|
toDate: '',
|
||||||
|
|
@ -43,7 +46,7 @@ const SickLeaveTab: React.FC = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSickSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
const errors = validateLeaveForm(sickLeave);
|
const errors = validateLeaveForm(sickLeave);
|
||||||
|
|
||||||
if (Object.keys(errors).length > 0) {
|
if (Object.keys(errors).length > 0) {
|
||||||
|
|
@ -51,18 +54,23 @@ const SickLeaveTab: React.FC = () => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
const success = await submitLeave({
|
||||||
console.log('Submitting Sick Leave:', sickLeave);
|
dateFrom: sickLeave.fromDate,
|
||||||
alert('Sick Leave request submitted successfully!');
|
dateTo: sickLeave.toDate,
|
||||||
|
leaveType: "SICK",
|
||||||
|
comment: sickLeave.comment,
|
||||||
|
});
|
||||||
|
|
||||||
setSickLeave({ fromDate: '', toDate: '', comment: '' });
|
if (success) {
|
||||||
|
setSickLeave({ fromDate: "", toDate: "", comment: "" });
|
||||||
setSickErrors({});
|
setSickErrors({});
|
||||||
} catch (error) {
|
alert("SICK submitted!");
|
||||||
alert('Failed to submit request. Please try again.');
|
|
||||||
console.error('Error:', error);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-w-4xl">
|
<div className="max-w-4xl">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
|
|
@ -75,9 +83,10 @@ const SickLeaveTab: React.FC = () => {
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
value={sickLeave.fromDate}
|
value={sickLeave.fromDate}
|
||||||
|
min={new Date().toISOString().split("T")[0]}
|
||||||
|
disabled={isSubmitting}
|
||||||
onChange={(e) => handleSickChange('fromDate', e.target.value)}
|
onChange={(e) => handleSickChange('fromDate', e.target.value)}
|
||||||
className={`input input-bordered input-sm w-full focus:input-primary transition-all ${
|
className={`input input-bordered input-sm w-full focus:input-primary transition-all ${sickErrors.fromDate ? 'input-error' : ''
|
||||||
sickErrors.fromDate ? 'input-error' : ''
|
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
{sickErrors.fromDate && (
|
{sickErrors.fromDate && (
|
||||||
|
|
@ -95,9 +104,10 @@ const SickLeaveTab: React.FC = () => {
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
value={sickLeave.toDate}
|
value={sickLeave.toDate}
|
||||||
|
min={new Date().toISOString().split("T")[0]}
|
||||||
|
disabled={isSubmitting}
|
||||||
onChange={(e) => handleSickChange('toDate', e.target.value)}
|
onChange={(e) => handleSickChange('toDate', e.target.value)}
|
||||||
className={`input input-bordered input-sm w-full focus:input-primary transition-all ${
|
className={`input input-bordered input-sm w-full focus:input-primary transition-all ${sickErrors.toDate ? 'input-error' : ''
|
||||||
sickErrors.toDate ? 'input-error' : ''
|
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
{sickErrors.toDate && (
|
{sickErrors.toDate && (
|
||||||
|
|
@ -110,14 +120,14 @@ const SickLeaveTab: React.FC = () => {
|
||||||
{/* Description/Comment Field */}
|
{/* Description/Comment Field */}
|
||||||
<div className="form-control flex flex-col gap-1 col-span-2">
|
<div className="form-control flex flex-col gap-1 col-span-2">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-semibold text-sm">Description / Reason</span>
|
<span className="label-text font-semibold text-sm">Description</span>
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
className={`textarea text-xs textarea-bordered h-40 w-full focus:textarea-primary transition-all ${
|
className={`textarea text-xs textarea-bordered h-40 w-full focus:textarea-primary transition-all ${sickErrors.comment ? 'textarea-error' : ''
|
||||||
sickErrors.comment ? 'textarea-error' : ''
|
|
||||||
}`}
|
}`}
|
||||||
placeholder="Please describe your symptoms or reason for sick leave to help us better understand your situation."
|
placeholder="Please describe your symptoms or reason for sick leave to help us better understand your situation."
|
||||||
value={sickLeave.comment}
|
value={sickLeave.comment}
|
||||||
|
disabled={isSubmitting}
|
||||||
onChange={(e) => handleSickChange('comment', e.target.value)}
|
onChange={(e) => handleSickChange('comment', e.target.value)}
|
||||||
/>
|
/>
|
||||||
{sickErrors.comment && (
|
{sickErrors.comment && (
|
||||||
|
|
@ -129,19 +139,19 @@ const SickLeaveTab: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Submit Button */}
|
{/* Submit Button */}
|
||||||
<div className="flex gap-3">
|
|
||||||
<button
|
<button
|
||||||
onClick={handleSickSubmit}
|
onClick={handleSubmit}
|
||||||
className="btn btn-primary flex-1 hover:shadow-lg transition-all duration-300"
|
disabled={isSubmitting}
|
||||||
|
className="btn btn-primary flex-1"
|
||||||
>
|
>
|
||||||
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
{isSubmitting ? (
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
<span className="loading loading-spinner loading-sm"></span>
|
||||||
</svg>
|
) : (
|
||||||
Submit Sick Leave Request
|
"Submit Sick Request"
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { setUserSlice } from '../store/userSlice';
|
||||||
import DocVaLogoLight from '../../assets/logo-colored-vertical.svg'
|
import DocVaLogoLight from '../../assets/logo-colored-vertical.svg'
|
||||||
import DocVaLogoDark from '../../assets/logo-white-vertical.svg'
|
import DocVaLogoDark from '../../assets/logo-white-vertical.svg'
|
||||||
import { useTheme } from '../helper/ThemeToggle';
|
import { useTheme } from '../helper/ThemeToggle';
|
||||||
import { KeyRound, Mail } from 'lucide-react';
|
import { Eye, EyeOff, KeyRound, Mail } from 'lucide-react';
|
||||||
|
|
||||||
const Login = () => {
|
const Login = () => {
|
||||||
const { theme, setTheme } = useTheme();
|
const { theme, setTheme } = useTheme();
|
||||||
|
|
@ -18,6 +18,7 @@ const Login = () => {
|
||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("");
|
||||||
const [isForgotMode, setIsForgotMode] = useState(false);
|
const [isForgotMode, setIsForgotMode] = useState(false);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.documentElement.setAttribute("data-theme", theme);
|
document.documentElement.setAttribute("data-theme", theme);
|
||||||
|
|
@ -116,7 +117,7 @@ const Login = () => {
|
||||||
<span className="label-text font-medium">Email Address</span>
|
<span className="label-text font-medium">Email Address</span>
|
||||||
</label>
|
</label>
|
||||||
<label className="input input-bordered flex items-center gap-2" tabIndex={0}>
|
<label className="input input-bordered flex items-center gap-2" tabIndex={0}>
|
||||||
<Mail size={18} />
|
{/* <Mail size={18} /> */}
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
name="email"
|
name="email"
|
||||||
|
|
@ -129,14 +130,17 @@ const Login = () => {
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!isForgotMode && <div className="form-control w-90 px-5 mt-3">
|
{!isForgotMode && (
|
||||||
|
<div className="form-control w-90 px-5 mt-3">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-medium">Password</span>
|
<span className="label-text font-medium">Password</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label className="input input-bordered flex items-center gap-2" tabIndex={0}>
|
<label className="input input-bordered flex items-center gap-2" tabIndex={0}>
|
||||||
<KeyRound size={18} />
|
{/* <KeyRound size={18} /> */}
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="password"
|
type={showPassword ? "text" : "password"}
|
||||||
name="password"
|
name="password"
|
||||||
placeholder="Enter your password"
|
placeholder="Enter your password"
|
||||||
className="grow"
|
className="grow"
|
||||||
|
|
@ -144,8 +148,23 @@ const Login = () => {
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Toggle Icon */}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="cursor-pointer"
|
||||||
|
onClick={() => setShowPassword((prev) => !prev)}
|
||||||
|
>
|
||||||
|
{showPassword ? (
|
||||||
|
<EyeOff size={18} className="opacity-70" />
|
||||||
|
) : (
|
||||||
|
<Eye size={18} className="opacity-70" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
</label>
|
</label>
|
||||||
</div>}
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
<div className="form-control w-90 px-5 mt-4">
|
<div className="form-control w-90 px-5 mt-4">
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ interface Project {
|
||||||
totalWorkTime: string;
|
totalWorkTime: string;
|
||||||
totalWorkMs: number;
|
totalWorkMs: number;
|
||||||
totalLogs: number;
|
totalLogs: number;
|
||||||
|
status:string
|
||||||
}
|
}
|
||||||
|
|
||||||
const Timesheets: React.FC = () => {
|
const Timesheets: React.FC = () => {
|
||||||
|
|
@ -161,7 +162,7 @@ const Timesheets: React.FC = () => {
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{projectsData.map((entry, index) => (
|
{projectsData.map((entry, index) => (
|
||||||
<tr key={entry.projectId}>
|
<tr key={entry.projectId} className={`${entry.status ==='INACTIVE' && 'hidden'}`}>
|
||||||
<td>{index + 1}</td>
|
<td>{index + 1}</td>
|
||||||
<td>{entry.projectName}</td>
|
<td>{entry.projectName}</td>
|
||||||
<td>{formatWorkTime(entry.totalWorkTime)}</td>
|
<td>{formatWorkTime(entry.totalWorkTime)}</td>
|
||||||
|
|
@ -181,10 +182,10 @@ const Timesheets: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer Summary */}
|
{/* Footer Summary */}
|
||||||
<div className="flex justify-between items-center text-xs mt-2">
|
<div className="flex justify-end items-center text-xs mt-2">
|
||||||
<span className="text-base-content/60">
|
{/* <span className="text-base-content/60">
|
||||||
Showing {timesheetData.length} entries
|
Showing {timesheetData.length} entries
|
||||||
</span>
|
</span> */}
|
||||||
<span className="text-base-content/60">
|
<span className="text-base-content/60">
|
||||||
Last updated: {new Date().toLocaleDateString()}
|
Last updated: {new Date().toLocaleDateString()}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import ReactQuill from "react-quill-new";
|
||||||
|
import Quill from "quill";
|
||||||
|
// import ImageResize from "quill-image-resize-module-react";
|
||||||
|
|
||||||
|
// Quill.register("modules/imageResize", ImageResize);
|
||||||
|
|
||||||
|
export const quillModules = {
|
||||||
|
toolbar: [
|
||||||
|
[{ header: [1, 2, 3, false] }],
|
||||||
|
["bold", "italic", "underline"],
|
||||||
|
["image", "link"],
|
||||||
|
],
|
||||||
|
// imageResize: {},
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
// utils/timeDisplay.js
|
||||||
|
export const getDisplayTime = (project:any, trackingData:any) => {
|
||||||
|
if (!project) return "0h 0m 0s";
|
||||||
|
|
||||||
|
// If tracking is active, calculate current time based on server time + session time
|
||||||
|
if (trackingData?.isTracking && trackingData.startTime) {
|
||||||
|
const baseSeconds = parseTimeStringToSeconds(project.totalWorkTime);
|
||||||
|
const sessionSeconds = Math.floor((Date.now() - trackingData.startTime) / 1000);
|
||||||
|
const totalSeconds = baseSeconds + sessionSeconds;
|
||||||
|
return formatSecondsToTimeString(totalSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not tracking, just return the server's totalWorkTime
|
||||||
|
return project.totalWorkTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const parseTimeStringToSeconds = (timeString:any) => {
|
||||||
|
if (!timeString || typeof timeString !== 'string') return 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const hourMatch = timeString.match(/(\d+)h/);
|
||||||
|
const minMatch = timeString.match(/(\d+)m/);
|
||||||
|
const secMatch = timeString.match(/(\d+)s/);
|
||||||
|
|
||||||
|
const hours = hourMatch ? parseInt(hourMatch[1]) : 0;
|
||||||
|
const minutes = minMatch ? parseInt(minMatch[1]) : 0;
|
||||||
|
const seconds = secMatch ? parseInt(secMatch[1]) : 0;
|
||||||
|
|
||||||
|
return hours * 3600 + minutes * 60 + seconds;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error parsing time string:', timeString, error);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const formatSecondsToTimeString = (totalSeconds:any) => {
|
||||||
|
const hours = Math.floor(totalSeconds / 3600);
|
||||||
|
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
||||||
|
const seconds = totalSeconds % 60;
|
||||||
|
return `${hours}h ${minutes}m ${seconds}s`;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// utils/timeParser.js
|
||||||
|
export const parseTimeStringToMs = (timeString:string) => {
|
||||||
|
if (!timeString) return 0;
|
||||||
|
|
||||||
|
const hourMatch = timeString.match(/(\d+)h/);
|
||||||
|
const minMatch = timeString.match(/(\d+)m/);
|
||||||
|
const secMatch = timeString.match(/(\d+)s/);
|
||||||
|
|
||||||
|
const hours = hourMatch ? parseInt(hourMatch[1]) : 0;
|
||||||
|
const minutes = minMatch ? parseInt(minMatch[1]) : 0;
|
||||||
|
const seconds = secMatch ? parseInt(secMatch[1]) : 0;
|
||||||
|
|
||||||
|
return (hours * 3600 + minutes * 60 + seconds) * 1000;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const parseTimeStringToSeconds = (timeString:string) => {
|
||||||
|
return Math.floor(parseTimeStringToMs(timeString) / 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -86,6 +86,12 @@ interface ProfileData {
|
||||||
_id: string;
|
_id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LeaveRequestResponse = {
|
||||||
|
success: boolean;
|
||||||
|
data?: any;
|
||||||
|
message?: string;
|
||||||
|
};
|
||||||
|
|
||||||
interface Window {
|
interface Window {
|
||||||
electron: {
|
electron: {
|
||||||
login: (credentials: { email: string; password: string }) => Promise<any>;
|
login: (credentials: { email: string; password: string }) => Promise<any>;
|
||||||
|
|
@ -163,7 +169,14 @@ interface Window {
|
||||||
data?: any;
|
data?: any;
|
||||||
message?: string;
|
message?: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
createPTOAndSickLeaveRequest: (
|
||||||
|
payload: LeaveRequestPayload
|
||||||
|
) => Promise<LeaveRequestResponse>;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue