merge 11/6

This commit is contained in:
JaiPandey425 2025-11-06 12:29:55 +05:30
parent 27c6caceb4
commit e11d4e52f2
11 changed files with 981 additions and 328 deletions

57
package-lock.json generated
View File

@ -77,6 +77,7 @@
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz",
"integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==", "integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@ampproject/remapping": "^2.2.0", "@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.24.2", "@babel/code-frame": "^7.24.2",
@ -2788,6 +2789,7 @@
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true, "dev": true,
"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",
@ -2976,7 +2978,6 @@
"resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz",
"integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"archiver-utils": "^2.1.0", "archiver-utils": "^2.1.0",
"async": "^3.2.4", "async": "^3.2.4",
@ -2995,7 +2996,6 @@
"resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz",
"integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"glob": "^7.1.4", "glob": "^7.1.4",
"graceful-fs": "^4.2.0", "graceful-fs": "^4.2.0",
@ -3017,7 +3017,6 @@
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"core-util-is": "~1.0.0", "core-util-is": "~1.0.0",
"inherits": "~2.0.3", "inherits": "~2.0.3",
@ -3032,15 +3031,13 @@
"version": "5.1.2", "version": "5.1.2",
"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
"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",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"safe-buffer": "~5.1.0" "safe-buffer": "~5.1.0"
} }
@ -3284,6 +3281,7 @@
"url": "https://github.com/sponsors/ai" "url": "https://github.com/sponsors/ai"
} }
], ],
"peer": true,
"dependencies": { "dependencies": {
"caniuse-lite": "^1.0.30001587", "caniuse-lite": "^1.0.30001587",
"electron-to-chromium": "^1.4.668", "electron-to-chromium": "^1.4.668",
@ -3856,7 +3854,6 @@
"resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz",
"integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"buffer-crc32": "^0.2.13", "buffer-crc32": "^0.2.13",
"crc32-stream": "^4.0.2", "crc32-stream": "^4.0.2",
@ -4038,7 +4035,6 @@
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
"dev": true, "dev": true,
"peer": true,
"bin": { "bin": {
"crc32": "bin/crc32.njs" "crc32": "bin/crc32.njs"
}, },
@ -4051,7 +4047,6 @@
"resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz",
"integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"crc-32": "^1.2.0", "crc-32": "^1.2.0",
"readable-stream": "^3.4.0" "readable-stream": "^3.4.0"
@ -4463,6 +4458,7 @@
"resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-25.0.5.tgz", "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-25.0.5.tgz",
"integrity": "sha512-ocnZV44ZqInoSFaY54fF7BlCtw+WtbrjyPrkBhaB+Ztn7GPKjmFgRbIKytifJ8h9Cib8jdFRMgjCUtkU45Y6DA==", "integrity": "sha512-ocnZV44ZqInoSFaY54fF7BlCtw+WtbrjyPrkBhaB+Ztn7GPKjmFgRbIKytifJ8h9Cib8jdFRMgjCUtkU45Y6DA==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"app-builder-lib": "25.0.5", "app-builder-lib": "25.0.5",
"builder-util": "25.0.3", "builder-util": "25.0.3",
@ -4686,7 +4682,6 @@
"resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-25.0.5.tgz", "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-25.0.5.tgz",
"integrity": "sha512-N2U7LGSdt4hmEhjEeIV2XJbjj2YIrTL6enfsGKfOhGTpL6GEejUmT3gjdKUqKBS5+NBx0GWhnEwD3MpO2P6Nfg==", "integrity": "sha512-N2U7LGSdt4hmEhjEeIV2XJbjj2YIrTL6enfsGKfOhGTpL6GEejUmT3gjdKUqKBS5+NBx0GWhnEwD3MpO2P6Nfg==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"app-builder-lib": "25.0.5", "app-builder-lib": "25.0.5",
"archiver": "^5.3.1", "archiver": "^5.3.1",
@ -4699,7 +4694,6 @@
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"graceful-fs": "^4.2.0", "graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1", "jsonfile": "^6.0.1",
@ -4714,7 +4708,6 @@
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"universalify": "^2.0.0" "universalify": "^2.0.0"
}, },
@ -4727,7 +4720,6 @@
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true, "dev": true,
"peer": true,
"engines": { "engines": {
"node": ">= 10.0.0" "node": ">= 10.0.0"
} }
@ -5440,8 +5432,7 @@
"version": "1.0.0", "version": "1.0.0",
"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
"peer": true
}, },
"node_modules/fs-extra": { "node_modules/fs-extra": {
"version": "8.1.0", "version": "8.1.0",
@ -6369,8 +6360,7 @@
"version": "1.0.0", "version": "1.0.0",
"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
"peer": true
}, },
"node_modules/isbinaryfile": { "node_modules/isbinaryfile": {
"version": "5.0.2", "version": "5.0.2",
@ -6631,7 +6621,6 @@
"resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
"integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"readable-stream": "^2.0.5" "readable-stream": "^2.0.5"
}, },
@ -6644,7 +6633,6 @@
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"core-util-is": "~1.0.0", "core-util-is": "~1.0.0",
"inherits": "~2.0.3", "inherits": "~2.0.3",
@ -6659,15 +6647,13 @@
"version": "5.1.2", "version": "5.1.2",
"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
"peer": true
}, },
"node_modules/lazystream/node_modules/string_decoder": { "node_modules/lazystream/node_modules/string_decoder": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"safe-buffer": "~5.1.0" "safe-buffer": "~5.1.0"
} }
@ -6945,36 +6931,31 @@
"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
"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
"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
"peer": true
}, },
"node_modules/lodash.isplainobject": { "node_modules/lodash.isplainobject": {
"version": "4.0.6", "version": "4.0.6",
"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
"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
"peer": true
}, },
"node_modules/log-symbols": { "node_modules/log-symbols": {
"version": "4.1.0", "version": "4.1.0",
@ -7577,7 +7558,6 @@
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true, "dev": true,
"peer": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -8177,8 +8157,7 @@
"version": "2.0.1", "version": "2.0.1",
"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
"peer": true
}, },
"node_modules/progress": { "node_modules/progress": {
"version": "2.0.3", "version": "2.0.3",
@ -8259,6 +8238,7 @@
"version": "18.3.1", "version": "18.3.1",
"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==",
"peer": true,
"dependencies": { "dependencies": {
"loose-envify": "^1.1.0" "loose-envify": "^1.1.0"
}, },
@ -8270,6 +8250,7 @@
"version": "18.3.1", "version": "18.3.1",
"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==",
"peer": true,
"dependencies": { "dependencies": {
"loose-envify": "^1.1.0", "loose-envify": "^1.1.0",
"scheduler": "^0.23.2" "scheduler": "^0.23.2"
@ -8421,7 +8402,6 @@
"resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz",
"integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"minimatch": "^5.1.0" "minimatch": "^5.1.0"
} }
@ -8431,7 +8411,6 @@
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"brace-expansion": "^2.0.1" "brace-expansion": "^2.0.1"
}, },
@ -9386,7 +9365,6 @@
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"bl": "^4.0.3", "bl": "^4.0.3",
"end-of-stream": "^1.4.1", "end-of-stream": "^1.4.1",
@ -9811,6 +9789,7 @@
"version": "5.4.7", "version": "5.4.7",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.7.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.7.tgz",
"integrity": "sha512-5l2zxqMEPVENgvzTuBpHer2awaetimj2BGkhBPdnwKbPNOlHsODU+oiazEZzLK7KhAnOrO+XGYJYn4ZlUhDtDQ==", "integrity": "sha512-5l2zxqMEPVENgvzTuBpHer2awaetimj2BGkhBPdnwKbPNOlHsODU+oiazEZzLK7KhAnOrO+XGYJYn4ZlUhDtDQ==",
"peer": true,
"dependencies": { "dependencies": {
"esbuild": "^0.21.3", "esbuild": "^0.21.3",
"postcss": "^8.4.43", "postcss": "^8.4.43",
@ -10225,7 +10204,6 @@
"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz",
"integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"archiver-utils": "^3.0.4", "archiver-utils": "^3.0.4",
"compress-commons": "^4.1.2", "compress-commons": "^4.1.2",
@ -10240,7 +10218,6 @@
"resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz",
"integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"glob": "^7.2.3", "glob": "^7.2.3",
"graceful-fs": "^4.2.0", "graceful-fs": "^4.2.0",

View File

@ -8,6 +8,9 @@ import ProtectedRoute from "./routes/ProtectedRoute"
import PublicRoute from "./routes/PublicRoute"; import PublicRoute from "./routes/PublicRoute";
import { ThemeToggle } from "./helper/ThemeToggle" import { ThemeToggle } from "./helper/ThemeToggle"
import LeaveAndAdjustments from "./pages/LeaveAndAdjustments/LeaveAndAdjustments"; import LeaveAndAdjustments from "./pages/LeaveAndAdjustments/LeaveAndAdjustments";
import Timesheets from "./pages/Timesheets/Timesheets";
import Settings from "./pages/Settings/Settings";
import HelpSupport from "./pages/HelpSupport/HelpSupport";
// ✅ Define User type (customize based on your structure) // ✅ Define User type (customize based on your structure)
interface User { interface User {
@ -61,6 +64,9 @@ const App: React.FC = () => {
<Route index element={<Dashboard />} /> <Route index element={<Dashboard />} />
<Route path="projects" element={<Projects />} /> <Route path="projects" element={<Projects />} />
<Route path="leave-adjustments" element={<LeaveAndAdjustments />} /> <Route path="leave-adjustments" element={<LeaveAndAdjustments />} />
<Route path="timesheet" element={<Timesheets />} />
<Route path="settings" element={<Settings/>} />
<Route path="help-support" element={<HelpSupport/>} />
</Route> </Route>
{/* Default Redirect */} {/* Default Redirect */}

View File

@ -21,3 +21,13 @@
/* Firefox */ /* Firefox */
} }
} }
input[type=number] {
-moz-appearance: textfield;
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}

View File

@ -1,7 +1,24 @@
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from "react";
import { Outlet, NavLink, useNavigate, useLocation } from 'react-router-dom' import {
import { Bell, Menu, LogOut, User, Settings, Home, FolderKanban, X, CalendarDays } from 'lucide-react' Outlet,
NavLink,
useNavigate,
useLocation,
} from "react-router-dom";
import {
Bell,
Menu,
LogOut,
User,
Settings,
Home,
FolderKanban,
X,
CalendarDays,
SettingsIcon,
TimerIcon,
HelpCircleIcon,
} from "lucide-react";
interface User { interface User {
email: string; email: string;
@ -14,64 +31,68 @@ interface DashboardLayoutProps {
} }
const DashboardLayout: React.FC<DashboardLayoutProps> = ({ setUser }) => { const DashboardLayout: React.FC<DashboardLayoutProps> = ({ setUser }) => {
const [isSidebarOpen, setSidebarOpen] = useState(true) const [isSidebarOpen, setSidebarOpen] = useState(true);
const [isMobile, setIsMobile] = useState(window.innerWidth < 768) const [isMobile, setIsMobile] = useState(window.innerWidth < 768);
const navigate = useNavigate() const navigate = useNavigate();
const location = useLocation() const location = useLocation();
// ✅ Handle responsive behavior properly
useEffect(() => { useEffect(() => {
const handleResize = () => { const handleResize = () => {
const mobile = window.innerWidth < 768 const mobile = window.innerWidth < 768;
setIsMobile(mobile) setIsMobile(mobile);
setSidebarOpen(!mobile) // open on desktop, closed on mobile setSidebarOpen(!mobile);
} };
handleResize() handleResize();
window.addEventListener('resize', handleResize) window.addEventListener("resize", handleResize);
return () => window.removeEventListener('resize', handleResize) return () => window.removeEventListener("resize", handleResize);
}, []) }, []);
const handleLogout = () => { const handleLogout = () => {
localStorage.removeItem('user') localStorage.removeItem("user");
setUser(null) setUser(null);
navigate('/login') navigate("/login");
} };
const toggleSidebar = () => setSidebarOpen(!isSidebarOpen) const toggleSidebar = () => setSidebarOpen(!isSidebarOpen);
const closeSidebarOnMobile = () => { const closeSidebarOnMobile = () => {
if (isMobile) setSidebarOpen(false) if (isMobile) setSidebarOpen(false);
} };
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: 'Projects', path: '/dashboard/projects', icon: <FolderKanban size={18} /> }, { label: "Projects", path: "/dashboard/projects", icon: <FolderKanban size={18} /> },
{ label: 'Leave & Adjustments', path: '/dashboard/leave-adjustments', icon: <CalendarDays size={18} /> }, { label: "PTO", path: "/dashboard/leave-adjustments", icon: <CalendarDays size={18} /> },
] { label: "Timesheet", path: "/dashboard/timesheet", icon: <TimerIcon size={18} /> },
{ label: "Settings", path: "/dashboard/settings", icon: <SettingsIcon size={18} /> },
{ label: "Help & Support", path: "/dashboard/help-support", icon: <HelpCircleIcon size={18} /> },
];
return ( return (
<div className="flex h-screen bg-base-200 overflow-hidden"> <div className="flex h-screen bg-base-200 overflow-hidden">
{/* Sidebar */} {/* Sidebar */}
<aside <aside
className={` className={`
fixed md:relative inset-y-0 left-0 z-40 transform py-1 fixed md:relative inset-y-0 left-0 z-40 transform
bg-base-100 border-r border-t border-base-300 transition-all duration-300 ease-in-out bg-base-100 border-r border-t border-base-300 transition-all duration-300 ease-in-out
${isMobile ${
isMobile
? isSidebarOpen ? isSidebarOpen
? 'translate-x-0 w-64' ? "translate-x-0 w-64"
: '-translate-x-full w-64' : "-translate-x-full w-64"
: isSidebarOpen : isSidebarOpen
? 'w-64' ? "w-50"
: 'w-20'} : "w-20"
}
`} `}
> >
{/* Header */} {/* Sidebar Header */}
<div className="p-4 text-xl font-bold flex justify-between items-center border-b border-base-300"> <div className="h-14 text-xl font-bold flex justify-between items-center border-b border-base-300">
{isSidebarOpen ? ( {isSidebarOpen ? (
<span className="whitespace-nowrap">VA Tracker</span> <span className="whitespace-nowrap mx-auto">VA Tracker</span>
) : ( ) : (
<span className="text-primary font-extrabold">VA</span> <span className="text-primary font-extrabold mx-auto">VA</span>
)} )}
{isMobile && ( {isMobile && (
<button <button
@ -94,8 +115,8 @@ const DashboardLayout: React.FC<DashboardLayoutProps> = ({ setUser }) => {
className={({ isActive }) => className={({ isActive }) =>
`flex items-center gap-3 my-1 font-medium transition-all duration-150 ${ `flex items-center gap-3 my-1 font-medium transition-all duration-150 ${
isActive isActive
? 'bg-primary text-primary-content rounded-lg' ? "bg-primary text-primary-content rounded-lg"
: 'hover:bg-base-300 rounded-lg' : "hover:bg-base-300 rounded-lg"
}` }`
} }
onClick={closeSidebarOnMobile} onClick={closeSidebarOnMobile}
@ -108,7 +129,7 @@ const DashboardLayout: React.FC<DashboardLayoutProps> = ({ setUser }) => {
</ul> </ul>
</aside> </aside>
{/* Overlay for mobile */} {/* Mobile overlay */}
{isMobile && isSidebarOpen && ( {isMobile && isSidebarOpen && (
<div <div
className="fixed inset-0 bg-black/20 backdrop-blur-[1px] z-30 md:hidden" className="fixed inset-0 bg-black/20 backdrop-blur-[1px] z-30 md:hidden"
@ -117,14 +138,11 @@ const DashboardLayout: React.FC<DashboardLayoutProps> = ({ setUser }) => {
)} )}
{/* Main Content */} {/* Main Content */}
<div <div className="flex flex-col flex-1 bg-base-200 transition-all duration-300 ease-in-out">
className={`flex flex-col flex-1 transition-all duration-300 ease-in-out bg-base-200 ${
!isMobile ? (isSidebarOpen ? 'md:ml-0' : 'md:ml-0') : ''
}`}
>
{/* Navbar */} {/* Navbar */}
<nav className="navbar bg-base-100 border-b border-t border-base-300 p-3 sticky top-0 z-30 flex justify-between items-center"> <nav className="h-14 bg-base-100 border-b border-t border-base-300 px-4 flex justify-between items-center sticky top-0 z-30">
<div className="flex items-center"> {/* Left */}
<div className="flex items-center gap-2">
<button <button
onClick={toggleSidebar} onClick={toggleSidebar}
className="btn btn-ghost btn-square" className="btn btn-ghost btn-square"
@ -132,20 +150,25 @@ const DashboardLayout: React.FC<DashboardLayoutProps> = ({ setUser }) => {
> >
<Menu size={20} /> <Menu size={20} />
</button> </button>
<span className="text-lg font-semibold ml-2 md:hidden"> <span className="text-lg font-semibold md:hidden">
{location.pathname.includes('projects') ? 'Projects' : 'Dashboard'} {location.pathname.includes("projects")
? "Projects"
: location.pathname.includes("leave-adjustments")
? "Leave & Adjustments"
: "Dashboard"}
</span> </span>
</div> </div>
<div className="flex items-center gap-2"> {/* Right */}
<div className="flex items-center gap-1">
<button className="btn btn-ghost btn-circle"> <button className="btn btn-ghost btn-circle">
<Bell size={20} /> <Bell size={20} />
</button> </button>
<div className="dropdown dropdown-end"> <div className="dropdown dropdown-end">
<label tabIndex={0} className="btn btn-ghost btn-circle avatar"> <label tabIndex={0} className="btn btn-ghost btn-circle avatar">
<div className="w-10 rounded-full ring ring-primary ring-offset-base-100 ring-offset-2 flex justify-center items-center"> <div className="w-8 h-8 rounded-full ring ring-primary ring-offset-base-100 ring-offset-2 flex justify-center items-center">
<User size={22} /> <User size={18} className="text-base-content" />
</div> </div>
</label> </label>
<ul <ul
@ -173,7 +196,7 @@ const DashboardLayout: React.FC<DashboardLayoutProps> = ({ setUser }) => {
</main> </main>
</div> </div>
</div> </div>
) );
} };
export default DashboardLayout export default DashboardLayout;

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect, useRef } from "react";
import { Clock, TrendingUp, Play, Square, ProjectorIcon } from "lucide-react"; import { Clock, TrendingUp, Play, Square, ProjectorIcon, Pause } from "lucide-react";
const projectsData = [ const projectsData = [
{ {
@ -153,6 +153,12 @@ interface WidgetProps {
color?: "primary" | "secondary" | "accent" | "success" | "warning" | "error" | "info"; color?: "primary" | "secondary" | "accent" | "success" | "warning" | "error" | "info";
} }
interface Project {
id: number;
projectName: string;
clientName: string;
}
// Widget Component // Widget Component
const Widget: React.FC<WidgetProps> = ({ const Widget: React.FC<WidgetProps> = ({
title, title,
@ -163,7 +169,7 @@ const Widget: React.FC<WidgetProps> = ({
color = "primary", color = "primary",
}) => { }) => {
return ( return (
<div className="card bg-base-100 shadow-md hover:shadow-lg transition-shadow"> <div className="card bg-base-100 shadow-sm hover:shadow-lg transition-shadow">
<div className="card-body"> <div className="card-body">
<div className="flex items-start justify-between"> <div className="flex items-start justify-between">
<div className="flex-1"> <div className="flex-1">
@ -179,10 +185,116 @@ const Widget: React.FC<WidgetProps> = ({
); );
}; };
// Main Dashboard Component // Main Dashboard Component
const Dashboard = () => { const Dashboard: React.FC = () => {
const [selectedProject, setSelectedProject] = useState<Project | null>(null);
const [isTracking, setIsTracking] = useState(false);
const [isPaused, setIsPaused] = useState(false);
const [elapsedTime, setElapsedTime] = useState(0); // in seconds
const [startTime, setStartTime] = useState<Date | null>(null);
const [endTime, setEndTime] = useState<Date | null>(null);
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
// Load cached project or auto-select single project
useEffect(() => {
const cachedProject = localStorage.getItem("activeProject");
if (cachedProject) {
const parsed = JSON.parse(cachedProject) as Project;
setSelectedProject(parsed);
} else if (projectsData.length === 1) {
setSelectedProject(projectsData[0]);
}
}, []);
// Format elapsed time (hh:mm:ss)
const formatTime = (seconds: number) => {
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = seconds % 60;
return `${h}h ${m}m ${s}s`;
};
// Start tracking manually
const handleStartTracking = () => {
if (!selectedProject) return;
setIsTracking(true);
setIsPaused(false);
setElapsedTime(0);
setStartTime(new Date());
setEndTime(null);
// Cache selected project
localStorage.setItem("activeProject", JSON.stringify(selectedProject));
};
// ⏱ Timer logic
useEffect(() => {
if (isTracking && !isPaused) {
timerRef.current = setInterval(() => {
setElapsedTime((prev) => prev + 1);
}, 1000);
} else if (timerRef.current) {
clearInterval(timerRef.current);
}
return () => {
if (timerRef.current) clearInterval(timerRef.current);
};
}, [isTracking, isPaused]);
// ⏸ Pause/Resume button handler
const handlePauseResume = () => {
setIsPaused((prev) => !prev);
};
// Stop tracking
const handleStopTracking = () => {
if (!selectedProject) return;
setIsTracking(false);
setIsPaused(false);
setEndTime(new Date());
if (timerRef.current) clearInterval(timerRef.current);
// Keep project in view, just stop tracking.
// Remove tracking state but keep the selected project cached.
const summary = {
project: selectedProject.projectName,
totalTime: formatTime(elapsedTime),
start: startTime?.toLocaleTimeString(),
end: new Date().toLocaleTimeString(),
};
console.log("✅ Final Tracking Summary:", summary);
// Save the stopped state for reference
localStorage.setItem(
"activeProject",
JSON.stringify({
...selectedProject,
lastSession: summary,
})
);
// Reset timer values but keep selected project
setElapsedTime(0);
};
// 🧾 When user picks a project manually
const handleProjectSelect = (id: number) => {
if (isTracking) return;
const project = projectsData.find((item) => item.id === id);
if (project) {
setSelectedProject(project);
localStorage.setItem("activeProject", JSON.stringify(project));
}
};
return ( return (
<main className="flex-1 overflow-y-auto p-4 md:px-2 md:py-0.5 no-scrollbar"> <main className="flex-1 overflow-y-auto md:px-0.5 md:py-0 no-scrollbar">
{/* Welcome Section */} {/* Welcome Section */}
<div className="mb-4"> <div className="mb-4">
<h1 className="text-lg font-bold">Dashboard</h1> <h1 className="text-lg font-bold">Dashboard</h1>
@ -198,7 +310,6 @@ const Dashboard = () => {
{/* Widgets Grid */} {/* Widgets Grid */}
<div className="mb-4"> <div className="mb-4">
<p className="text-md mb-3 font-bold">Overview</p>
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-3 gap-4 "> <div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-3 gap-4 ">
<Widget <Widget
title="Logged Hours Today" title="Logged Hours Today"
@ -218,99 +329,143 @@ const Dashboard = () => {
/> />
<Widget <Widget
title="Active Project" title="Active Project"
value="VA Tracker Project" value={selectedProject ? selectedProject.projectName : "—"}
subtitle="Until day ends" subtitle="Until day ends"
icon={<ProjectorIcon size={24} />} icon={<ProjectorIcon size={24} />}
color="warning" color="warning"
/> />
{/* <Widget
title="Status"
value="Online"
subtitle={`Active since ${formatTime(new Date(currentTime.getTime() - 6.4 * 60 * 60 * 1000))}`}
icon={<Activity size={24} />}
color="success"
/> */}
</div> </div>
</div> </div>
{/* Tracking Card */}
<div className="w-full mb-4"> <div className="w-full mb-4">
<div className="text-md mb-2 font-bold flex justify-between"> <div className="text-md mb-2 font-bold flex justify-between items-center">
<span>Tracking</span> <span>Tracking</span>
<div> {isTracking && (
<button className="ms-1 btn btn-xs btn-success btn-wide sm:w-auto rounded-full shadow-md hover:shadow-lg hover:scale-105 transition-all"> <button
<Play className="w-3 h-3" /> className="btn btn-xs btn-error rounded-full"
Start Tracking onClick={handleStopTracking}
</button> >
<button className="ms-1 btn btn-xs btn-error btn-wide sm:w-auto rounded-full shadow-md hover:shadow-lg hover:scale-105 transition-all">
<Square className="w-3 h-3" /> <Square className="w-3 h-3" />
Stop Tracking Stop Tracking
</button> </button>
</div> )}
</div> </div>
<div className="card bg-base-100 shadow-md border border-base-200 transition-all duration-300 w-full"> <div className="card bg-base-100 shadow-sm border border-base-200 transition-all duration-300 w-full">
<div className="card-body"> <div className="card-body">
{selectedProject ? (
<>
<div className="flex flex-col sm:flex-row items-center justify-between gap-4"> <div className="flex flex-col sm:flex-row items-center justify-between gap-4">
<div className="text-center sm:text-left"> <div className="text-center sm:text-left">
<p className="text-sm font-extrabold text-success tracking-tight"> <p className="text-sm font-extrabold text-success tracking-tight">
0h 15m {formatTime(elapsedTime)}
</p> </p>
<p className="text-xs text-base-content/70 mt-1"> <p className="text-xs text-base-content/70 mt-1">
Current Session {isTracking ? "Current Session" : "Ready to Start"}
</p> </p>
</div> </div>
<button className="btn btn-success btn-sm rounded-xl hover:scale-105 transition-transform duration-200 w-full sm:w-auto"> {!isTracking ? (
Start <button
onClick={handleStartTracking}
className="btn btn-sm btn-success rounded-xl hover:scale-105 transition-transform duration-200 w-full sm:w-auto"
>
<Play className="w-3 h-3" />
Start Tracking
</button> </button>
) : (
<button
onClick={handlePauseResume}
className={`btn btn-sm rounded-xl transition-transform duration-200 w-full sm:w-auto ${isPaused ? "btn-primary" : "btn-warning"
}`}
>
{isPaused ? (
<>
<Play className="w-3 h-3" />
Resume
</>
) : (
<>
<Pause className="w-3 h-3" />
Pause
</>
)}
</button>
)}
</div> </div>
<div className="divider my-2"></div> <div className="divider my-2"></div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 text-sm"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3 text-sm">
<div className="flex justify-between"> {/* Project Name */}
<div className="flex flex-col sm:flex-row sm:items-center sm:gap-2">
<span className="text-base-content/70">Project:</span> <span className="text-base-content/70">Project:</span>
<span className="font-semibold text-base-content"> <span className="font-semibold text-base-content truncate">
Employee Tracker {selectedProject.projectName}
</span> </span>
</div> </div>
<div className="flex justify-between"> {/* Client Name */}
<div className="flex flex-col sm:flex-row sm:items-center sm:gap-2">
<span className="text-base-content/70">Client:</span>
<span className="truncate">{selectedProject.clientName}</span>
</div>
{/* Status */}
<div className="flex flex-col sm:flex-row sm:items-center sm:gap-2">
<span className="text-base-content/70">Status:</span> <span className="text-base-content/70">Status:</span>
<span className="badge badge-success">Active</span> <span
className={`badge ${isPaused
? "badge-warning"
: isTracking
? "badge-success"
: "badge-neutral"
}`}
>
{isPaused ? "Paused" : isTracking ? "Active" : "Ready"}
</span>
</div>
{/* Start Time */}
<div className="flex flex-col sm:flex-row sm:items-center sm:gap-2">
<span className="text-base-content/70">Start Time:</span>
<span>{startTime?.toLocaleTimeString() || "—"}</span>
</div> </div>
</div> </div>
</>
) : (
<p className="text-sm text-base-content/60 text-center">
Select a project to start tracking.
</p>
)}
</div> </div>
</div> </div>
</div> </div>
<div className=""> {/* Projects Table */}
<p className="text-md mb-2 font-bold flex"> <div>
<span>Projects</span> <p className="text-md mb-2 font-bold">Projects</p>
</p>
<div <div
className="overflow-y-auto no-scrollbar overflow-x-auto rounded-box border border-base-content/10 bg-base-100 shadow-md" className="overflow-y-auto no-scrollbar overflow-x-auto rounded-box border border-base-content/10 bg-base-100 shadow-md"
style={{ minHeight: "100px", maxHeight: "250px" }} style={{ minHeight: "100px", maxHeight: "30vh" }}
> >
<table className="table table-zebra w-full text-sm"> <table className="table table-zebra w-full text-sm">
{/* Table Head */}
<thead className="sticky top-0 bg-base-200 z-10"> <thead className="sticky top-0 bg-base-200 z-10">
<tr> <tr>
<th>#</th> <th>#</th>
<th>Project Name</th> <th>Project Name</th>
<th>Client Name</th> <th>Client Name</th>
<th>Status</th>
<th>Action</th> <th>Action</th>
</tr> </tr>
</thead> </thead>
{/* Table Body */}
<tbody> <tbody>
{projectsData.map((project) => ( {projectsData.map((project) => (
<tr key={project.id} className="align-middle"> <tr key={project.id} className="align-middle">
<th>{project.id}</th> <th>{project.id}</th>
<td className="whitespace-normal break-words max-w-[150px]">{project.projectName}</td> <td>{project.projectName}</td>
<td className="whitespace-normal break-words max-w-[150px]">{project.clientName}</td> <td>{project.clientName}</td>
<td> <td>
<div <div
className={`badge badge-sm whitespace-nowrap ${project.status === "Active" className={`badge badge-sm whitespace-nowrap ${project.status === "Active"
@ -334,15 +489,15 @@ const Dashboard = () => {
: "badge-primary" : "badge-primary"
}`} }`}
> >
{project.action} Select
</div> </button>
</td> </td>
</tr> </tr>
))} ))}
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
</main> </main>
); );

View File

@ -0,0 +1,51 @@
import React from "react";
import { HelpCircle, Mail, Phone, Send, MessageSquare } from "lucide-react";
const HelpSupport: React.FC = () => {
return (
<div className="flex flex-col gap-2 max-h-screen">
{/* Header */}
<div className="flex items-center justify-between">
<h2 className="text-md font-bold flex items-center gap-2">
Help & Support
</h2>
</div>
{/* Contact Info */}
<div className="card bg-base-100 border border-base-300 shadow-sm p-4">
<h3 className="text-md font-semibold mb-2">Need Assistance?</h3>
<p className="text-sm text-base-content/70">
We're here to help! If you’re facing any issues, please reach out to our support team.
</p>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 mt-3">
<div className="flex items-center gap-3 border border-base-300 rounded-lg p-3 bg-base-200/40">
<Mail size={18} className="text-primary" />
<div>
<p className="text-xs text-base-content/70">Email</p>
<p className="font-medium text-sm">support@vatechcorp.com</p>
</div>
</div>
<div className="flex items-center gap-3 border border-base-300 rounded-lg p-3 bg-base-200/40">
<Phone size={18} className="text-primary" />
<div>
<p className="text-xs text-base-content/70">Phone</p>
<p className="font-medium text-sm">+91 98765 43210</p>
</div>
</div>
<div className="flex items-center gap-3 border border-base-300 rounded-lg p-3 bg-base-200/40">
<MessageSquare size={18} className="text-primary" />
<div>
<p className="text-xs text-base-content/70">Chat</p>
<p className="font-medium text-sm">Available 9 AM - 6 PM</p>
</div>
</div>
</div>
</div>
</div>
);
};
export default HelpSupport;

View File

@ -0,0 +1,101 @@
import React from "react";
interface AdjustmentEntry {
id: number;
date: string;
loggedHours: number;
}
interface AdjustmentModalProps {
editingEntry: AdjustmentEntry | null;
newHours: number;
setNewHours: React.Dispatch<React.SetStateAction<number>>;
handleCloseModal: () => void;
handleSave: () => void;
}
const AdjustmentModal: React.FC<AdjustmentModalProps> = ({
editingEntry,
newHours,
setNewHours,
handleCloseModal,
handleSave,
}) => {
return (
<dialog id="editModal" className="modal modal-bottom sm:modal-middle">
<div className="modal-box">
<h3 className="font-bold text-md mb-4">Logged Hours</h3>
{editingEntry && (
<div className="flex flex-col gap-4">
{/* Date */}
<div className="form-control">
<div className="flex items-center justify-between gap-3">
<label className="label-text text-sm font-medium w-32">Date</label>
<input
type="text"
value={new Date(editingEntry.date).toLocaleDateString("en-US")}
readOnly
className="input input-bordered input-sm bg-base-200 flex-1"
/>
</div>
</div>
{/* Current Hours */}
<div className="form-control">
<div className="flex items-center justify-between gap-3">
<label className="label-text text-sm font-medium w-32">
Current Hours
</label>
<input
type="text"
value={`${editingEntry.loggedHours} hrs`}
readOnly
className="input input-bordered input-sm bg-base-200 flex-1"
/>
</div>
</div>
{/* New Hours */}
<div className="form-control">
<div className="flex items-center justify-between gap-3">
<label className="label-text text-sm font-medium w-32">
New Hours
</label>
<div className="flex items-center flex-1">
<input
type="number"
min="0"
max="24"
step="0.5"
placeholder="Enter hours"
value={newHours}
onChange={(e) => setNewHours(Number(e.target.value))}
className="input input-bordered input-sm flex-1 rounded-r-none"
/>
<span className="bg-base-200 border border-l-0 border-base-300 text-sm px-2 py-1.5 rounded-r-lg">
hrs
</span>
</div>
</div>
</div>
</div>
)}
{/* Modal Actions */}
<div className="modal-action flex justify-end gap-2 mt-4">
<button onClick={handleCloseModal} className="btn btn-sm btn-ghost">
Cancel
</button>
<button onClick={handleSave} className="btn btn-sm btn-primary">
Save Changes
</button>
</div>
</div>
</dialog>
);
};
export default AdjustmentModal;

View File

@ -1,4 +1,5 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import AdjustmentModal from './AdjustmentModal';
interface LeaveFormData { interface LeaveFormData {
fromDate: string; fromDate: string;
@ -12,6 +13,22 @@ interface AdjustmentEntry {
loggedHours: number; loggedHours: number;
} }
interface LogEntry {
id: number;
date: string;
hours: number;
task: string;
status: "Completed" | "In Progress" | "Pending";
}
const data: LogEntry[] = [
{ id: 1, date: "2025-11-01", hours: 8, task: "Frontend Development", status: "Completed" },
{ id: 2, date: "2025-11-02", hours: 6, task: "API Integration", status: "In Progress" },
{ id: 3, date: "2025-11-03", hours: 7.5, task: "UI Testing", status: "Pending" },
{ id: 4, date: "2025-11-04", hours: 9, task: "Bug Fixing", status: "Completed" },
{ id: 5, date: "2025-11-05", hours: 5.5, task: "Code Review", status: "Completed" },
];
type TabType = 'pto' | 'sick' | 'adjustment'; type TabType = 'pto' | 'sick' | 'adjustment';
const LeaveAndAdjustments: React.FC = () => { const LeaveAndAdjustments: React.FC = () => {
@ -122,85 +139,51 @@ const LeaveAndAdjustments: React.FC = () => {
} }
}; };
// Adjustment Handlers const [editingEntry, setEditingEntry] = useState<AdjustmentEntry | null>(null);
const handlePeriodChange = (period: string) => { const [newHours, setNewHours] = useState<number>(0);
setSelectedPeriod(period);
if (period === 'month') { const handlePeriodChange = (value: string) => setSelectedPeriod(value);
setAdjustmentData([
{ id: 1, date: '2025-11-01', loggedHours: 8 },
{ id: 2, date: '2025-11-02', loggedHours: 7.5 },
{ id: 3, date: '2025-11-03', loggedHours: 8 },
{ id: 4, date: '2025-11-04', loggedHours: 6 },
{ id: 5, date: '2025-11-05', loggedHours: 8 },
]);
} else {
setAdjustmentData([
{ id: 1, date: '2025-11-01', loggedHours: 8 },
{ id: 2, date: '2025-11-02', loggedHours: 7.5 },
{ id: 3, date: '2025-11-03', loggedHours: 8 },
]);
}
};
const handleEditClick = (entry: AdjustmentEntry) => { const handleEditClick = (entry: AdjustmentEntry) => {
setEditingId(entry.id); setEditingEntry(entry);
setEditHours(entry.loggedHours.toString()); const modal = document.getElementById("editModal") as HTMLDialogElement;
modal?.showModal();
}; };
const handleSaveEdit = async (id: number) => { const handleCloseModal = () => {
const hours = parseFloat(editHours); setEditingEntry(null);
setNewHours(0)
if (isNaN(hours) || hours < 0 || hours > 24) { const modal = document.getElementById("editModal") as HTMLDialogElement;
alert('Please enter valid hours (0-24)'); modal?.close();
return;
}
try {
console.log('Updating hours for entry:', id, 'New hours:', hours);
setAdjustmentData(prev =>
prev.map(entry =>
entry.id === id ? { ...entry, loggedHours: hours } : entry
)
);
setEditingId(null);
setEditHours('');
alert('Hours updated successfully!');
} catch (error) {
alert('Failed to update hours. Please try again.');
console.error('Error:', error);
}
}; };
const handleCancelEdit = () => { const handleSave = () => {
setEditingId(null); console.log("Saving:", { ...editingEntry, updatedHours: newHours });
setEditHours(''); handleCloseModal();
}; };
return ( return (
<div className="min-h-screen"> <div className="min-h-screen">
<div className="max-w-6xl mx-auto"> <div className="w-full mx-auto">
{/* Header */} {/* Header */}
<div className=" "> <div className=" ">
<h1 className="text-lg font-bold mb-3">Leave & Adjustments</h1> <h1 className="text-mb font-bold mb-2">Leave & Adjustments</h1>
</div> </div>
{/* Main Container */} {/* Main Container */}
<div className=" shadow-lg rounded-lg bg-base-100 overflow-hidden"> <div className=" rounded-lg bg-base-100 overflow-hidden">
{/* Tabs Navigation */} {/* Tabs Navigation */}
<div className="flex "> <div className="flex ">
<button <button
onClick={() => setActiveTab('pto')} onClick={() => setActiveTab('pto')}
className={`flex-1 px-6 py-4 text-sm font-semibold transition-all duration-300 relative ${ className={`flex-1 px-6 py-3 text-xs font-semibold transition-all duration-300 relative ${activeTab === 'pto'
activeTab === 'pto'
? ' text-primary border-b-2 border-primary' ? ' text-primary border-b-2 border-primary'
: '' : ''
}`} }`}
> >
<span className="flex items-center justify-center gap-2 cursor-pointer"> <span className="flex items-center justify-center gap-2 cursor-pointer">
<svg <svg
className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg> </svg>
PTO Request PTO Request
@ -208,14 +191,13 @@ const LeaveAndAdjustments: React.FC = () => {
</button> </button>
<button <button
onClick={() => setActiveTab('sick')} onClick={() => setActiveTab('sick')}
className={`flex-1 px-6 py-4 text-sm font-semibold transition-all duration-300 relative cursor-pointer ${ className={`flex-1 px-6 py-3 text-xs font-semibold transition-all duration-300 relative cursor-pointer ${activeTab === 'sick'
activeTab === 'sick'
? ' text-primary border-b-2 border-primary' ? ' text-primary border-b-2 border-primary'
: '' : ''
}`} }`}
> >
<span className="flex items-center justify-center gap-2"> <span className="flex items-center justify-center gap-2">
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg> </svg>
Sick Leave Sick Leave
@ -223,14 +205,13 @@ const LeaveAndAdjustments: React.FC = () => {
</button> </button>
<button <button
onClick={() => setActiveTab('adjustment')} onClick={() => setActiveTab('adjustment')}
className={`flex-1 px-6 py-4 text-sm font-semibold cursor-pointer transition-all duration-300 relative ${ className={`flex-1 px-6 py-3 text-xs font-semibold cursor-pointer transition-all duration-300 relative ${activeTab === 'adjustment'
activeTab === 'adjustment'
? ' text-primary border-b-2 border-primary' ? ' text-primary border-b-2 border-primary'
: '' : ''
}`} }`}
> >
<span className="flex items-center justify-center gap-2"> <span className="flex items-center justify-center gap-2">
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
</svg> </svg>
Adjustments Adjustments
@ -243,12 +224,12 @@ const LeaveAndAdjustments: React.FC = () => {
{/* PTO Tab */} {/* PTO Tab */}
{activeTab === 'pto' && ( {activeTab === 'pto' && (
<div className="animate-fade-in"> <div className="animate-fade-in">
<div className="max-w-2xl "> <div className="max-w-4xl ">
<div className="mb-3"> <div className="mb-1">
<h2 className="text-sm font-bold mb-2">Paid Time Off Request</h2> <h2 className="text-sm font-bold mb-2">Paid Time Off Request</h2>
</div> </div>
<div className="space-y-5"> <div className="space-y-3">
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
<div className="form-control"> <div className="form-control">
<label className="label"> <label className="label">
@ -292,7 +273,7 @@ const LeaveAndAdjustments: React.FC = () => {
<textarea <textarea
value={ptoLeave.description} value={ptoLeave.description}
onChange={(e) => handlePtoChange('description', e.target.value)} onChange={(e) => handlePtoChange('description', e.target.value)}
className={`textarea textarea-bordered h-32 w-full focus:textarea-primary transition-all ${ptoErrors.description ? 'textarea-error' : ''}`} className={`textarea textarea-bordered h-20 w-full focus:textarea-primary transition-all ${ptoErrors.description ? 'textarea-error' : ''}`}
placeholder="Please provide a reason for your PTO request..." placeholder="Please provide a reason for your PTO request..."
/> />
{ptoErrors.description && ( {ptoErrors.description && (
@ -330,12 +311,12 @@ const LeaveAndAdjustments: React.FC = () => {
{/* Sick Leave Tab */} {/* Sick Leave Tab */}
{activeTab === 'sick' && ( {activeTab === 'sick' && (
<div className="animate-fade-in"> <div className="animate-fade-in">
<div className="max-w-2xl "> <div className="max-w-4xl ">
<div className="mb-3"> <div className="mb-1">
<h2 className="text-sm font-bold mb-2">Sick Leave Request</h2> <h2 className="text-sm font-bold mb-2">Sick Leave Request</h2>
</div> </div>
<div className="space-y-5"> <div className="space-y-3">
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
<div className="form-control"> <div className="form-control">
<label className="label"> <label className="label">
@ -379,7 +360,7 @@ const LeaveAndAdjustments: React.FC = () => {
<textarea <textarea
value={sickLeave.description} value={sickLeave.description}
onChange={(e) => handleSickChange('description', e.target.value)} onChange={(e) => handleSickChange('description', e.target.value)}
className={`textarea textarea-bordered h-32 w-full focus:textarea-primary transition-all ${sickErrors.description ? 'textarea-error' : ''}`} className={`textarea textarea-bordered h-20 w-full focus:textarea-primary transition-all ${sickErrors.description ? 'textarea-error' : ''}`}
placeholder="Please describe your illness or condition..." placeholder="Please describe your illness or condition..."
/> />
{sickErrors.description && ( {sickErrors.description && (
@ -389,7 +370,7 @@ const LeaveAndAdjustments: React.FC = () => {
)} )}
</div> </div>
<div className="flex gap-3 pt-4"> <div className="flex gap-3 ">
<button <button
onClick={handleSickSubmit} onClick={handleSickSubmit}
className="btn btn-primary flex-1 hover:shadow-lg transition-all duration-300" className="btn btn-primary flex-1 hover:shadow-lg transition-all duration-300"
@ -404,7 +385,7 @@ const LeaveAndAdjustments: React.FC = () => {
setSickLeave({ fromDate: '', toDate: '', description: '' }); setSickLeave({ fromDate: '', toDate: '', description: '' });
setSickErrors({}); setSickErrors({});
}} }}
className="btn btn-ghost hover:bg-slate-100 transition-all" className="btn btn-secondary transition-all"
> >
Clear Clear
</button> </button>
@ -417,11 +398,10 @@ const LeaveAndAdjustments: React.FC = () => {
{/* Adjustment Submissions Tab */} {/* Adjustment Submissions Tab */}
{activeTab === 'adjustment' && ( {activeTab === 'adjustment' && (
<div className="animate-fade-in"> <div className="animate-fade-in">
<div className="mb-6 flex items-center justify-between">
<div>
<h2 className="text-md font-bold mb-2">Time Adjustments</h2>
</div>
{/* Header */}
<div className="mb-6 flex items-center justify-between">
<h2 className="text-md font-bold mb-2">Time Adjustments</h2>
<div className="form-control w-48"> <div className="form-control w-48">
<select <select
value={selectedPeriod} value={selectedPeriod}
@ -434,74 +414,52 @@ const LeaveAndAdjustments: React.FC = () => {
</div> </div>
</div> </div>
{/* Table */}
<div className="overflow-hidden rounded-lg border border-base-content/10"> <div className="overflow-hidden rounded-lg border border-base-content/10">
<table className="table w-full"> <table className="table w-full">
<thead className="bg-base-100"> <thead className="bg-base-100">
<tr> <tr>
<th className=" font-bold text-base">Date</th> <th className="font-bold text-base">Date</th>
<th className=" font-bold text-base">Logged Hours</th> <th className="font-bold text-base">Logged Hours</th>
<th className=" font-bold text-base text-right">Actions</th> <th className="font-bold text-base text-right">Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{adjustmentData.map((entry) => ( {adjustmentData.map((entry) => (
<tr key={entry.id} className=" transition-colors"> <tr key={entry.id} className="transition-colors">
<td className="font-medium "> <td className="font-medium">
{new Date(entry.date).toLocaleDateString('en-US', { {new Date(entry.date).toLocaleDateString("en-US", {
weekday: 'short', weekday: "short",
year: 'numeric', year: "numeric",
month: 'short', month: "short",
day: 'numeric' day: "numeric",
})} })}
</td> </td>
<td> <td>
{editingId === entry.id ? (
<input
type="number"
value={editHours}
onChange={(e) => setEditHours(e.target.value)}
className="input input-bordered input-sm w-28 focus:input-primary"
min="0"
max="24"
step="0.5"
autoFocus
/>
) : (
<span className="badge badge-lg badge-ghost font-semibold"> <span className="badge badge-lg badge-ghost font-semibold">
{entry.loggedHours} hours {entry.loggedHours} hrs
</span> </span>
)}
</td> </td>
<td className="text-right"> <td className="text-right">
{editingId === entry.id ? (
<div className="flex gap-2 justify-end">
<button <button
// onClick={() => handleSaveEdit(entry.id)} onClick={() => handleEditClick(entry)}
className="btn btn-success btn-sm hover:shadow-md transition-all"
>
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
Save
</button>
<button
onClick={handleCancelEdit}
className="btn btn-ghost btn-sm hover:bg-slate-100 transition-all"
>
Cancel
</button>
</div>
) : (
<button
// onClick={() => handleEditClick(entry)}
className="btn btn-primary btn-sm hover:shadow-md transition-all" className="btn btn-primary btn-sm hover:shadow-md transition-all"
> >
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" /> className="h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
/>
</svg> </svg>
Edit Edit
</button> </button>
)}
</td> </td>
</tr> </tr>
))} ))}
@ -509,10 +467,21 @@ const LeaveAndAdjustments: React.FC = () => {
</table> </table>
</div> </div>
{/* No Data */}
{adjustmentData.length === 0 && ( {adjustmentData.length === 0 && (
<div className="text-center py-12 text-slate-500"> <div className="text-center py-12 text-slate-500">
<svg className="h-16 w-16 mx-auto mb-4 opacity-50" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4" /> className="h-16 w-16 mx-auto mb-4 opacity-50"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"
/>
</svg> </svg>
<p className="font-medium">No time entries found for this period</p> <p className="font-medium">No time entries found for this period</p>
</div> </div>
@ -523,6 +492,50 @@ const LeaveAndAdjustments: React.FC = () => {
</div> </div>
</div> </div>
{/* View Status */}
<div className='mt-4'>
<h1 className="text-md font-bold mb-2">View Status</h1>
<div
className="overflow-y-auto rounded-box border border-base-content/5 bg-base-100 shadow-md"
style={{ minHeight: "150px", maxHeight: "300px" }}
>
<table className="table table-zebra w-full">
{/* Table Head */}
<thead className="sticky top-0 bg-base-200 z-10">
<tr>
<th>#</th>
<th>Date</th>
<th>Logged Hours</th>
<th>Task</th>
<th>Status</th>
</tr>
</thead>
{/* Table Body */}
<tbody>
{data.map((log, index) => (
<tr key={log.id}>
<td>{index + 1}</td>
<td>{log.date}</td>
<td className="font-medium">{log.hours} hrs</td>
<td className="whitespace-normal break-words">{log.task}</td>
<td>
<div
className={`badge ${log.status === "Completed"
? "badge-success"
: log.status === "In Progress"
? "badge-info"
: "badge-warning"
}`}
>
{log.status}
</div>
</td>
</tr>
))}
</tbody>
</table>
</div></div>
<style>{` <style>{`
@keyframes fade-in { @keyframes fade-in {
from { from {
@ -539,6 +552,14 @@ const LeaveAndAdjustments: React.FC = () => {
animation: fade-in 0.3s ease-out; animation: fade-in 0.3s ease-out;
} }
`}</style> `}</style>
<AdjustmentModal
editingEntry={editingEntry}
newHours={newHours}
setNewHours={setNewHours}
handleCloseModal={handleCloseModal}
handleSave={handleSave}
/>
</div> </div>
); );
}; };

View File

@ -1,9 +1,87 @@
import React from 'react' import React from "react";
const Projects = () => { const projectsData = [
{ id: 1, projectName: "Employee Tracker", clientName: "TechCorp Pvt Ltd", status: "Active", action: "Start Tracking" },
{ id: 2, projectName: "Sales Dashboard", clientName: "BlueEdge Analytics", status: "Pending", action: "Start Tracking" },
{ id: 3, projectName: "Inventory System", clientName: "LogiFlow Inc", status: "Completed", action: "View Report" },
{ id: 4, projectName: "CRM Upgrade", clientName: "NextWave Solutions", status: "On Hold", action: "Resume" },
{ id: 5, projectName: "Mobile App Redesign", clientName: "PixelFusion", status: "Active", action: "Start Tracking" },
{ id: 6, projectName: "E-Commerce Backend", clientName: "ShopEasy", status: "Pending", action: "Start Tracking" },
{ id: 7, projectName: "Finance Tracker", clientName: "MoneyWise", status: "Active", action: "Start Tracking" },
{ id: 8, projectName: "Helpdesk Portal", clientName: "ServePro", status: "Completed", action: "View Report" },
{ id: 9, projectName: "Marketing Automation", clientName: "AdBoost", status: "Active", action: "Start Tracking" },
{ id: 10, projectName: "Data Pipeline", clientName: "CloudWave", status: "On Hold", action: "Resume" },
{ id: 11, projectName: "Support Chatbot", clientName: "SmartServe", status: "Active", action: "Start Tracking" },
{ id: 12, projectName: "Payroll System", clientName: "PeopleFirst HR", status: "Pending", action: "Start Tracking" },
{ id: 13, projectName: "IoT Dashboard", clientName: "SensorGrid", status: "Completed", action: "View Report" },
{ id: 14, projectName: "Security Portal", clientName: "SafeNet", status: "Active", action: "Start Tracking" },
{ id: 15, projectName: "Social Media Tool", clientName: "Buzzify", status: "Pending", action: "Start Tracking" },
{ id: 16, projectName: "AI Model Trainer", clientName: "NeuroTech", status: "On Hold", action: "Resume" },
{ id: 17, projectName: "Project Tracker", clientName: "TaskFlow", status: "Active", action: "Start Tracking" },
{ id: 18, projectName: "Booking Portal", clientName: "GoTravel", status: "Completed", action: "View Report" },
{ id: 19, projectName: "Time Logger", clientName: "Clockify Pro", status: "Active", action: "Start Tracking" },
{ id: 20, projectName: "Analytics Engine", clientName: "DataStream", status: "Pending", action: "Start Tracking" },
];
const Projects: React.FC = () => {
return ( return (
<div>Projects</div> <div className="h-full">
) <p className="text-md mb-2 font-bold flex">
} <span>Projects</span>
</p>
export default Projects <div
className="overflow-y-auto overflow-x-auto no-scrollbar rounded-box border border-base-content/10 bg-base-100 shadow-md"
style={{ maxHeight: "80vh", minHeight: "200px" }} // 👈 auto scroll region
>
<table className="table table-zebra w-full text-sm">
{/* Table Head */}
<thead className="sticky top-0 bg-base-200 z-10">
<tr>
<th>#</th>
<th>Project Name</th>
<th>Client Name</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
{/* Table Body */}
<tbody>
{projectsData.map((project) => (
<tr key={project.id} className="align-middle">
<th>{project.id}</th>
<td className="whitespace-normal break-words max-w-[160px]">{project.projectName}</td>
<td className="whitespace-normal break-words max-w-[160px]">{project.clientName}</td>
<td>
<div
className={`badge badge-sm ${
project.status === "Active"
? "badge-success"
: project.status === "Pending"
? "badge-warning"
: project.status === "Completed"
? "badge-info"
: "badge-neutral"
}`}
>
{project.status}
</div>
</td>
<td>
<button
className={`btn btn-xs btn-primary`}
>
Edit
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
};
export default Projects;

View File

@ -0,0 +1,109 @@
import React from "react";
import { User, Mail, Lock, Edit2, Shield, CalendarDays } from "lucide-react";
interface UserProfile {
name: string;
email: string;
role: string;
employeeId: string;
joinDate: string;
department: string;
profileImage?: string;
}
const userData: UserProfile = {
name: "Jai Pandey",
email: "jai.pandey@vatechcorp.com",
role: "Frontend Developer",
employeeId: "EMP-1023",
joinDate: "2022-07-15",
department: "Engineering",
profileImage: "https://ui-avatars.com/api/?name=Jai+Pandey&background=random",
};
const Settings: React.FC = () => {
return (
<div className="flex flex-col gap-2 max-h-screen">
{/* Header */}
<div className="flex items-center justify-between">
<h2 className="text-md font-bold flex items-center gap-2">
Profile Settings
</h2>
<button className="btn btn-sm btn-primary gap-1">
<Edit2 size={16} /> Edit Profile
</button>
</div>
{/* Profile Section */}
<div className="card bg-base-100 border border-base-300 shadow-sm p-4 flex flex-col md:flex-row gap-4">
{/* Profile Picture */}
<div className="flex flex-col items-center md:w-1/3">
<div className="avatar">
<div className="w-28 rounded-full ring ring-primary ring-offset-base-100 ring-offset-2 flex justify-center items-center">
<User size={60} />
</div>
</div>
<h3 className="mt-3 text-lg font-semibold">{userData.name}</h3>
<p className="text-sm text-base-content/70">{userData.role}</p>
</div>
<div className="divider md:divider-horizontal" />
{/* Details Section */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 flex-1">
<div className="p-2 rounded-md bg-base-200/50">
<p className="text-xs text-base-content/70 flex items-center gap-2">
<Mail size={14} /> Email
</p>
<p className="text-sm font-medium">{userData.email}</p>
</div>
<div className="p-2 rounded-md bg-base-200/50">
<p className="text-xs text-base-content/70 flex items-center gap-2">
<Shield size={14} /> Role
</p>
<p className="text-sm font-medium">{userData.role}</p>
</div>
<div className="p-2 rounded-md bg-base-200/50">
<p className="text-xs text-base-content/70 flex items-center gap-2">
<Lock size={14} /> Employee ID
</p>
<p className="text-sm font-medium">{userData.employeeId}</p>
</div>
<div className="p-2 rounded-md bg-base-200/50">
<p className="text-xs text-base-content/70 flex items-center gap-2">
<CalendarDays size={14} /> Joining Date
</p>
<p className="text-sm font-medium">{userData.joinDate}</p>
</div>
<div className="p-2 rounded-md bg-base-200/50 sm:col-span-2">
<p className="text-xs text-base-content/70 flex items-center gap-2">
<User size={14} /> Department
</p>
<p className="text-sm font-medium">{userData.department}</p>
</div>
</div>
</div>
{/* Account Settings */}
<div className="card bg-base-100 border border-base-300 shadow-sm p-4">
<h3 className="text-md font-semibold mb-3">Account Security</h3>
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between border-t border-base-300 pt-3">
<p className="text-sm flex items-center gap-2">
<Lock size={16} /> Password last changed on{" "}
<span className="font-medium text-base-content">Aug 12, 2024</span>
</p>
<button className="btn btn-sm btn-outline btn-primary">
Change Password
</button>
</div>
</div>
</div>
);
};
export default Settings;

View File

@ -0,0 +1,122 @@
import React from "react";
import { CalendarDays, Clock, FileText, CheckCircle, XCircle } from "lucide-react";
interface TimesheetEntry {
id: number;
date: string;
project: string;
hours: number;
status: "Approved" | "Pending" | "Rejected";
remarks?: string;
}
const timesheetData: TimesheetEntry[] = [
{ id: 1, date: "2025-10-28", project: "Employee Tracker", hours: 8, status: "Approved", remarks: "Full day" },
{ id: 2, date: "2025-10-29", project: "CRM Upgrade", hours: 6, status: "Pending", remarks: "Client call delays" },
{ id: 3, date: "2025-10-30", project: "Sales Dashboard", hours: 7, status: "Rejected", remarks: "Missing details" },
{ id: 4, date: "2025-10-31", project: "Finance Tracker", hours: 9, status: "Approved", remarks: "Overtime" },
{ id: 5, date: "2025-11-01", project: "AI Model Trainer", hours: 8, status: "Approved", remarks: "Normal day" },
];
const Timesheets: React.FC = () => {
const totalHours = timesheetData.reduce((acc, t) => acc + t.hours, 0);
const approvedCount = timesheetData.filter((t) => t.status === "Approved").length;
return (
<div className="flex flex-col gap-2 max-h-screen">
{/* Header */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<h2 className="text-md font-bold">Timesheets</h2>
</div>
</div>
{/* Summary Cards */}
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
<div className="card bg-base-100 border border-base-300 shadow-sm">
<div className="card-body py-3 px-4 flex flex-row items-center justify-between">
<div>
<p className="text-xs text-base-content/70">Total Logged Hours</p>
<h3 className="text-lg font-semibold">{totalHours} hrs</h3>
</div>
<Clock size={20} className="text-primary" />
</div>
</div>
<div className="card bg-base-100 border border-base-300 shadow-sm">
<div className="card-body py-3 px-4 flex flex-row items-center justify-between">
<div>
<p className="text-xs text-base-content/70">Approved Days</p>
<h3 className="text-lg font-semibold">{approvedCount}</h3>
</div>
<CheckCircle size={20} className="text-success" />
</div>
</div>
<div className="card bg-base-100 border border-base-300 shadow-sm">
<div className="card-body py-3 px-4 flex flex-row items-center justify-between">
<div>
<p className="text-xs text-base-content/70">Pending Days</p>
<h3 className="text-lg font-semibold">
{timesheetData.length - approvedCount}
</h3>
</div>
<FileText size={20} className="text-warning" />
</div>
</div>
</div>
{/* Timesheet Table */}
<div className="overflow-y-auto rounded-box border border-base-content/10 bg-base-100 shadow-md max-h-[60vh]">
<table className="table table-zebra w-full text-sm">
<thead className="sticky top-0 bg-base-200 z-10 text-sm">
<tr>
<th>#</th>
<th>Date</th>
<th>Project</th>
<th>Hours</th>
<th>Status</th>
<th>Remarks</th>
</tr>
</thead>
<tbody>
{timesheetData.map((entry) => (
<tr key={entry.id}>
<td>{entry.id}</td>
<td className="whitespace-nowrap">{entry.date}</td>
<td>{entry.project}</td>
<td>{entry.hours} hrs</td>
<td>
<div
className={`badge badge-sm ${
entry.status === "Approved"
? "badge-success"
: entry.status === "Pending"
? "badge-warning"
: "badge-error"
}`}
>
{entry.status}
</div>
</td>
<td>{entry.remarks || "-"}</td>
</tr>
))}
</tbody>
</table>
</div>
{/* Footer Summary */}
<div className="flex justify-between items-center text-xs mt-2">
<span className="text-base-content/60">
Showing {timesheetData.length} entries
</span>
<span className="text-base-content/60">
Last updated: {new Date().toLocaleDateString()}
</span>
</div>
</div>
);
};
export default Timesheets;