Compare commits

..

No commits in common. "82ebe286a8b8ccd9611d6dcacf5f77af1864b484" and "cf568582de2745da9ddfe6f9773bbfb2cb75b909" have entirely different histories.

42 changed files with 120 additions and 4946 deletions

190
.gitignore vendored
View File

@ -1,90 +1,138 @@
# =====================
# ---> Node
# Logs
# =====================
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
.pnpm-debug.log*
# =====================
# Dependencies
# =====================
node_modules
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# =====================
# Build Output
# =====================
dist
dist-ssr
build
coverage
# =====================
# Environment Files
# =====================
.env
.env.*
!.env.example
# Local env files
*.local
# =====================
# Docker
# =====================
# Docker volumes/data
docker-data/
volumes/
# Docker override files
docker-compose.override.yml
# Temporary docker files
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# =====================
# IDEs
# =====================
.vscode/*
!.vscode/extensions.json
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
.idea
# Coverage directory used by tools like istanbul
coverage
*.lcov
# =====================
# OS Files
# =====================
.DS_Store
Thumbs.db
# nyc test coverage
.nyc_output
# =====================
# Visual Studio
# =====================
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# =====================
# Cache
# =====================
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
.eslintcache
.vite
# =====================
# Test Reports
# =====================
coverage/
playwright-report/
test-results/
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# vitepress build output
**/.vitepress/dist
# vitepress cache directory
**/.vitepress/cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# =====================
# Misc
# =====================
*.tgz

View File

@ -1,75 +1,2 @@
# React + TypeScript + Vite
# hrms-frontend
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
## React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
```js
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,
// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```

View File

@ -1,22 +0,0 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
globals: globals.browser,
},
},
])

View File

@ -1,16 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="clri_logo_fav.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CLRI-HRMS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

3310
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,42 +0,0 @@
{
"name": "clri-hrms",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@reduxjs/toolkit": "^2.12.0",
"@tailwindcss/vite": "^4.3.2",
"@tanstack/react-query": "^5.101.2",
"date-fns": "^4.4.0",
"lucide-react": "^1.23.0",
"protobufjs": "^8.6.5",
"react": "^19.2.7",
"react-dom": "^19.2.7",
"react-redux": "^9.3.0",
"react-router-dom": "^7.18.1",
"sonner": "^2.0.7"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@types/node": "^24.13.2",
"@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.3",
"autoprefixer": "^10.5.2",
"eslint": "^10.6.0",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.5.3",
"globals": "^17.7.0",
"postcss": "^8.5.16",
"tailwindcss": "^4.3.2",
"typescript": "~6.0.2",
"typescript-eslint": "^8.62.0",
"vite": "^8.1.1"
}
}

View File

@ -1,94 +0,0 @@
<svg width="9085" height="1492" viewBox="0 0 9085 1492" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M695.725 800.703C692.581 795.937 688.018 789.547 682.034 782.448C677.673 777.276 658.201 754.558 623.922 734.172C623.922 734.172 598.67 719.162 569.766 710.035C515.407 692.895 434.273 695.43 378.596 718.554C242.799 774.943 148.481 999.383 241.582 1154.45C294.623 1242.89 397.459 1290.15 485.691 1292.18C508.003 1292.69 568.143 1294.01 628.588 1256.28C660.635 1236.2 681.527 1212.26 693.798 1195.63C771.281 1196.24 848.662 1196.95 926.144 1197.56C915.597 1231.43 893.691 1286.7 847.14 1341.77C832.334 1359.32 781.118 1416.93 693.089 1455.36C605.566 1493.6 529.503 1492.08 481.635 1490.56C441.575 1489.34 364.499 1486.2 276.672 1444.21C255.881 1434.27 197.668 1404.55 139.658 1345.02C78.1993 1281.94 48.18 1217.43 35.1987 1185.08C0.412773 1098.67 -0.0943219 1025.55 0.00709474 988.43C0.00709474 947.457 0.91983 874.942 36.5171 790.257C45.5432 768.655 81.7489 687.114 166.432 613.382C196.755 586.912 263.69 535.087 361.557 508.312C415.207 493.708 459.222 491.679 477.984 491.375C494.21 491.071 539.037 491.071 594.613 503.748C632.239 512.369 716.617 532.551 797.548 601.617C883.549 674.943 918.538 762.164 932.026 803.036C885.983 801.616 838.926 800.703 791.057 800.399C758.908 800.196 727.165 800.297 695.827 800.602L695.725 800.703Z" fill="url(#paint0_linear_122_7)"/>
<path d="M1040.03 513.992V1471.19H1529.06V1300.6H1227.96C1228.57 1038.43 1229.07 776.161 1229.68 513.992H1040.03Z" fill="url(#paint1_linear_122_7)"/>
<path d="M1630.89 513.891C1630.48 833.158 1629.97 1152.32 1629.57 1471.59C1692.95 1471.39 1756.34 1471.29 1819.62 1471.08V513.891C1756.74 513.891 1693.77 513.891 1630.89 513.891Z" fill="url(#paint2_linear_122_7)"/>
<path d="M1979.76 513.992V1471.69L2162.51 1471.19V859.628C2292.13 1063.48 2421.84 1267.33 2551.45 1471.19H2723.75V513.992H2541C2541 709.021 2540.8 904.05 2540.8 1099.08C2413.01 904.05 2285.13 709.021 2157.34 513.992H1979.86H1979.76Z" fill="url(#paint3_linear_122_7)"/>
<path d="M3073.44 513.992H2885.51V1471.19H3073.44V513.992Z" fill="url(#paint4_linear_122_7)"/>
<path d="M3617.74 513.992V1471.69L4103.33 1471.19V1298.67H3800.5V514.093H3617.74V513.992Z" fill="url(#paint5_linear_122_7)"/>
<path d="M4166.91 1471.19L4529.78 513.992H4694.28L5054.51 1471.19H4832.61C4803.91 1383.36 4775.21 1295.53 4746.51 1207.7C4700.36 1068.45 4654.32 929.202 4608.18 790.055C4570.55 891.271 4532.93 992.386 4495.3 1093.6C4448.55 1219.46 4401.69 1345.32 4354.94 1471.19H4167.01H4166.91Z" fill="url(#paint6_linear_122_7)"/>
<path d="M5248.93 515.108C5248.01 642.592 5247.2 770.075 5246.29 897.559C5247.61 994.617 5248.93 1091.57 5250.25 1188.63C5250.25 1195.12 5251.16 1203.64 5253.69 1213.28C5256.63 1223.93 5260.79 1232.34 5264.34 1238.33C5267.89 1244.72 5273.06 1252.42 5280.47 1260.23C5291.22 1271.49 5302.27 1278.49 5310.28 1282.55C5321.64 1286.4 5338.07 1290.56 5358.15 1291.27C5377.93 1291.98 5394.36 1289.14 5405.92 1286.2C5413.32 1282.85 5425.39 1276.06 5436.85 1263.58C5448.31 1251 5453.89 1238.12 5456.53 1230.42C5458.56 1224.13 5460.58 1217.13 5462.31 1209.42C5464.03 1201.51 5465.15 1194.21 5465.86 1187.52C5468.39 1091.78 5470.93 995.936 5473.46 900.196C5473.06 770.988 5472.55 641.78 5472.14 512.572C5538.27 513.079 5604.39 513.587 5670.52 513.992L5664.03 881.941L5670.52 1136.5C5669.81 1158.21 5668.79 1180.52 5667.27 1203.24C5665.34 1233.66 5664.53 1242.49 5664.03 1247.45C5660.78 1276.46 5658.75 1293.7 5651.15 1314.9C5640.9 1343.4 5626.4 1362.67 5618.49 1373.01C5605.2 1390.35 5593.64 1400.39 5573.46 1418.04C5551.05 1437.62 5535.02 1451.31 5509.97 1463.27C5493.85 1470.98 5479.95 1475.04 5472.55 1477.17C5453.99 1482.54 5437.16 1485.38 5422.96 1486.7C5404.09 1487.31 5384.32 1487.31 5363.63 1486.7C5334.42 1485.89 5307.04 1483.76 5281.48 1480.92C5266.78 1478.99 5246.9 1474.84 5224.89 1465.51C5207.35 1458 5195.99 1450.29 5175.7 1436.6C5154.61 1422.3 5140.11 1412.46 5123.07 1395.73C5111.61 1384.47 5094.57 1367.54 5079.97 1340.25C5076.72 1334.17 5067.39 1316.12 5061 1290.66C5055.42 1268.25 5054.71 1250.7 5054.21 1235.59C5053.4 1207.7 5052.79 1168.45 5054.21 1119.46V513.992C5119.01 514.398 5183.82 514.803 5248.72 515.108H5248.93Z" fill="url(#paint7_linear_122_7)"/>
<path d="M5818.89 521.092V1478.79L6001.64 1478.29V866.728C6131.25 1070.58 6260.96 1274.43 6390.58 1478.29H6562.88V521.092H6380.13C6380.13 716.121 6379.93 911.15 6379.93 1106.18C6252.14 911.15 6124.25 716.121 5996.47 521.092H5818.99H5818.89Z" fill="url(#paint8_linear_122_7)"/>
<path d="M7380.1 800.703C7376.96 795.937 7372.39 789.547 7366.41 782.448C7362.05 777.276 7342.58 754.558 7308.3 734.172C7308.3 734.172 7283.04 719.162 7254.14 710.035C7199.78 692.895 7118.65 695.43 7062.97 718.554C6927.17 774.943 6832.86 999.383 6925.96 1154.45C6979 1242.89 7081.83 1290.15 7170.07 1292.18C7192.38 1292.69 7252.52 1294.01 7312.96 1256.28C7345.01 1236.2 7365.9 1212.26 7378.17 1195.63C7455.66 1196.24 7533.04 1196.95 7610.52 1197.56C7599.97 1231.43 7578.07 1286.7 7531.52 1341.77C7516.71 1359.32 7465.49 1416.93 7377.46 1455.36C7289.94 1493.6 7213.88 1492.08 7166.01 1490.56C7125.95 1489.34 7048.87 1486.2 6961.05 1444.21C6940.26 1434.27 6882.04 1404.55 6824.03 1345.02C6762.57 1281.94 6732.55 1217.43 6719.57 1185.08C6684.79 1098.67 6684.28 1025.55 6684.38 988.43C6684.38 947.457 6685.29 874.942 6720.89 790.257C6729.92 768.655 6766.12 687.114 6850.81 613.382C6881.13 586.912 6948.07 535.087 7045.93 508.312C7099.58 493.708 7143.6 491.679 7162.36 491.375C7178.59 491.071 7223.41 491.071 7278.99 503.748C7316.61 512.369 7400.99 532.551 7481.92 601.617C7567.92 674.943 7602.91 762.164 7616.4 803.036C7570.36 801.616 7523.3 800.703 7475.43 800.399C7443.28 800.196 7411.54 800.297 7380.2 800.602L7380.1 800.703Z" fill="url(#paint9_linear_122_7)"/>
<path d="M7736.27 513.992L7725.83 1471.19H7921.56V1080.82H8213.95V1471.19H8412.32V520.99C8349.64 519.672 8287.07 518.252 8224.39 516.933C8218.41 644.823 8212.53 772.712 8206.54 900.602C8109.89 901.514 8013.14 902.427 7916.49 903.34C7918.22 773.523 7919.84 643.707 7921.56 513.992H7736.17H7736.27Z" fill="url(#paint10_linear_122_7)"/>
<path d="M8353.59 71.8047H8464.54V368.151H8548.11V71.8047H8659.06V0H8353.59V71.8047Z" fill="black"/>
<path d="M8702.16 0V368.05H8783.09C8781.37 279.207 8779.65 190.364 8777.92 101.52C8801.35 190.364 8824.88 279.207 8848.31 368.05H8926.6C8949.62 277.077 8972.74 186.205 8995.76 95.2325C8998.4 186.205 9000.94 277.077 9003.57 368.05H9084.5V0H8952.66C8930.96 84.6849 8909.26 169.471 8887.55 254.156C8864.94 169.471 8842.22 84.6849 8819.6 0H8702.16Z" fill="black"/>
<defs>
<linearGradient id="paint0_linear_122_7" x1="459.12" y1="601.415" x2="480.013" y2="1520.37" gradientUnits="userSpaceOnUse">
<stop offset="0.08" stop-color="#00AAE9"/>
<stop offset="0.23" stop-color="#039FE1"/>
<stop offset="0.5" stop-color="#0B83CB"/>
<stop offset="0.84" stop-color="#1955A9"/>
<stop offset="1" stop-color="#213E98"/>
</linearGradient>
<linearGradient id="paint1_linear_122_7" x1="1266.29" y1="583.16" x2="1287.19" y2="1502.02" gradientUnits="userSpaceOnUse">
<stop offset="0.08" stop-color="#00AAE9"/>
<stop offset="0.23" stop-color="#039FE1"/>
<stop offset="0.5" stop-color="#0B83CB"/>
<stop offset="0.84" stop-color="#1955A9"/>
<stop offset="1" stop-color="#213E98"/>
</linearGradient>
<linearGradient id="paint2_linear_122_7" x1="1715.16" y1="572.917" x2="1735.96" y2="1491.77" gradientUnits="userSpaceOnUse">
<stop offset="0.08" stop-color="#00AAE9"/>
<stop offset="0.23" stop-color="#039FE1"/>
<stop offset="0.5" stop-color="#0B83CB"/>
<stop offset="0.84" stop-color="#1955A9"/>
<stop offset="1" stop-color="#213E98"/>
</linearGradient>
<linearGradient id="paint3_linear_122_7" x1="2341.82" y1="558.718" x2="2362.71" y2="1477.57" gradientUnits="userSpaceOnUse">
<stop offset="0.08" stop-color="#00AAE9"/>
<stop offset="0.23" stop-color="#039FE1"/>
<stop offset="0.5" stop-color="#0B83CB"/>
<stop offset="0.84" stop-color="#1955A9"/>
<stop offset="1" stop-color="#213E98"/>
</linearGradient>
<linearGradient id="paint4_linear_122_7" x1="2969.39" y1="544.418" x2="2990.28" y2="1463.27" gradientUnits="userSpaceOnUse">
<stop offset="0.08" stop-color="#00AAE9"/>
<stop offset="0.23" stop-color="#039FE1"/>
<stop offset="0.5" stop-color="#0B83CB"/>
<stop offset="0.84" stop-color="#1955A9"/>
<stop offset="1" stop-color="#213E98"/>
</linearGradient>
<linearGradient id="paint5_linear_122_7" x1="3840.96" y1="524.641" x2="3861.85" y2="1443.5" gradientUnits="userSpaceOnUse">
<stop offset="0.08" stop-color="#00AAE9"/>
<stop offset="0.23" stop-color="#039FE1"/>
<stop offset="0.5" stop-color="#0B83CB"/>
<stop offset="0.84" stop-color="#1955A9"/>
<stop offset="1" stop-color="#213E98"/>
</linearGradient>
<linearGradient id="paint6_linear_122_7" x1="4588.81" y1="507.603" x2="4609.7" y2="1426.46" gradientUnits="userSpaceOnUse">
<stop offset="0.08" stop-color="#00AAE9"/>
<stop offset="0.23" stop-color="#039FE1"/>
<stop offset="0.5" stop-color="#0B83CB"/>
<stop offset="0.84" stop-color="#1955A9"/>
<stop offset="1" stop-color="#213E98"/>
</linearGradient>
<linearGradient id="paint7_linear_122_7" x1="5353.69" y1="490.26" x2="5374.58" y2="1409.12" gradientUnits="userSpaceOnUse">
<stop offset="0.08" stop-color="#00AAE9"/>
<stop offset="0.23" stop-color="#039FE1"/>
<stop offset="0.5" stop-color="#0B83CB"/>
<stop offset="0.84" stop-color="#1955A9"/>
<stop offset="1" stop-color="#213E98"/>
</linearGradient>
<linearGradient id="paint8_linear_122_7" x1="6178.92" y1="471.498" x2="6199.81" y2="1390.35" gradientUnits="userSpaceOnUse">
<stop offset="0.08" stop-color="#00AAE9"/>
<stop offset="0.23" stop-color="#039FE1"/>
<stop offset="0.5" stop-color="#0B83CB"/>
<stop offset="0.84" stop-color="#1955A9"/>
<stop offset="1" stop-color="#213E98"/>
</linearGradient>
<linearGradient id="paint9_linear_122_7" x1="7140.05" y1="449.59" x2="7160.94" y2="1368.55" gradientUnits="userSpaceOnUse">
<stop offset="0.08" stop-color="#00AAE9"/>
<stop offset="0.23" stop-color="#039FE1"/>
<stop offset="0.5" stop-color="#0B83CB"/>
<stop offset="0.84" stop-color="#1955A9"/>
<stop offset="1" stop-color="#213E98"/>
</linearGradient>
<linearGradient id="paint10_linear_122_7" x1="8056.24" y1="428.8" x2="8077.14" y2="1347.66" gradientUnits="userSpaceOnUse">
<stop offset="0.08" stop-color="#00AAE9"/>
<stop offset="0.23" stop-color="#039FE1"/>
<stop offset="0.5" stop-color="#0B83CB"/>
<stop offset="0.84" stop-color="#1955A9"/>
<stop offset="1" stop-color="#213E98"/>
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@ -1,32 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" version="1.2" viewBox="0 0 150 150" preserveAspectRatio="xMidYMid meet" id="svg131" baseProfile="tiny-ps" xml:space="preserve">
<title>CliniLaunch Logo</title>
<circle cx="75" cy="75" r="70" fill="#ffffff" />
<defs id="defs131"/>
<g id="g234">
<g fill="#058dec" id="g119">
<path d="m 71.13,72.04 c -0.12,-0.44 -0.12,-2.43 0,-2.86 0.06,-0.19 0.19,-0.34 0.29,-0.34 0.16,0 0.21,0.35 0.21,1.76 0,1.41 -0.04,1.76 -0.21,1.76 -0.1,0 -0.23,-0.15 -0.29,-0.32 z" id="path118"/>
<path d="m 118.39,65 0.04,-5.3 0.48,-1 c 0.82,-1.66 2.17,-2.52 3.91,-2.5 1.67,0.01 3.18,1.04 3.93,2.67 0.31,0.66 0.32,0.92 0.37,6.01 0.04,5.27 0.04,5.3 -0.25,5.3 -0.25,0 -0.31,-0.09 -0.31,-0.51 0,-0.48 -0.03,-0.51 -0.47,-0.53 -0.26,0 -1.54,-0.04 -2.83,-0.07 -4.26,-0.13 -4.32,-0.13 -4.32,0.69 0,0.48 -0.04,0.57 -0.31,0.57 -0.29,0 -0.31,-0.01 -0.25,-5.32 z" id="path119"/>
</g>
<g fill="#0473d9" id="g122">
<path d="m 124.29,75.15 c -0.4,-0.07 -1.9,-0.13 -3.33,-0.13 l -2.61,-0.01 0.01,-2.37 c 0.01,-2.23 0.21,-5.49 0.25,-4.38 0.03,0.44 0.06,0.47 0.28,0.29 0.13,-0.12 0.26,-0.19 0.29,-0.16 0.13,0.09 0.34,0.01 0.26,-0.12 -0.04,-0.07 0.01,-0.15 0.15,-0.15 0.12,0 0.26,0.09 0.29,0.19 0.06,0.12 0.48,0.18 1.46,0.16 0.91,0 1.39,0.04 1.39,0.15 0,0.1 0.04,0.12 0.12,0.01 0.15,-0.21 1.54,-0.22 1.67,-0.01 0.07,0.1 0.12,0.1 0.12,-0.03 0,-0.1 0.07,-0.15 0.15,-0.1 0.09,0.06 0.25,0.03 0.34,-0.06 0.13,-0.1 0.31,-0.09 0.57,0.04 0.21,0.1 0.54,0.21 0.76,0.21 0.34,0 0.38,-0.07 0.41,-0.54 0.04,-1.22 0.23,2.17 0.25,4.57 l 0.01,2.59 -1.05,-0.01 c -0.59,-0.01 -1.39,-0.07 -1.8,-0.13 z" id="path120"/>
<path d="m 56.94,68.64 c -0.66,-0.25 -1.46,-0.85 -4.83,-3.65 l -2.45,-2.05 0.03,-5.87 0.04,-5.87 0.51,-0.04 c 0.28,-0.03 0.92,0.06 1.42,0.19 0.51,0.15 1.03,0.21 1.14,0.16 0.13,-0.04 0.37,-0.38 0.53,-0.75 l 0.29,-0.67 -0.44,-0.51 c -0.38,-0.42 -0.45,-0.63 -0.45,-1.25 0,-0.66 0.06,-0.81 0.5,-1.25 0.44,-0.44 0.59,-0.5 1.29,-0.5 1.25,0 1.88,0.66 1.88,1.95 0,0.79 -0.34,1.26 -1.16,1.64 -0.47,0.21 -0.63,0.38 -0.75,0.79 -0.26,0.94 -0.19,1.26 0.41,1.8 1.27,1.16 3.03,2.17 3.74,2.17 0.48,0 1.68,-0.64 2.33,-1.23 0.5,-0.45 0.54,-0.56 0.41,-0.89 -0.26,-0.7 -0.18,-1.89 0.19,-2.43 0.72,-1.07 2.18,-1.45 3.33,-0.86 1.44,0.73 1.82,2.58 0.79,3.81 -0.59,0.69 -1.11,0.89 -2.21,0.86 -0.86,-0.03 -0.98,0 -1.23,0.32 -0.41,0.54 -0.72,1.57 -0.72,2.42 0,0.69 0.09,0.89 0.88,2.07 1.54,2.29 1.71,4.41 0.57,6.67 -0.44,0.86 -1.76,2.2 -2.65,2.68 -0.92,0.5 -2.49,0.64 -3.38,0.31 z" id="path121"/>
<path d="M 81.96,61.07 C 81.4,61 78.77,60.95 76.1,60.97 L 71.27,61 71.31,60.63 c 0.09,-0.64 0.76,-1.86 1.3,-2.34 0.31,-0.25 0.83,-0.6 1.2,-0.78 0.66,-0.31 0.85,-0.31 11.15,-0.35 13.07,-0.06 14.05,0.03 16.93,1.45 1.29,0.63 2.92,1.85 2.92,2.18 0,0.31 -0.7,0.34 -1.86,0.1 -0.56,-0.12 -1.05,-0.16 -1.17,-0.09 -0.25,0.16 -3.05,0.18 -3.05,0.01 0,-0.15 -7.02,-0.07 -7.4,0.06 -0.13,0.06 -0.78,0.06 -1.42,0 -0.78,-0.06 -1.25,-0.04 -1.36,0.06 -0.13,0.12 -0.92,0.15 -2.33,0.1 -1.17,-0.03 -2.14,0 -2.14,0.07 0,0.13 -0.69,0.12 -2.12,-0.04 z" id="path122"/>
</g>
<g fill="#055fcb" id="g126">
<path d="m 41.15,116.94 c -0.15,-0.03 -0.29,-0.21 -0.34,-0.38 -0.04,-0.18 -0.09,-2.05 -0.09,-4.16 v -3.82 l -1.13,-0.1 c -2.9,-0.23 -6.68,-1.08 -9.16,-2.07 -0.69,-0.26 -0.69,-0.26 -1.03,0.04 -1.26,1.19 -3.4,0.97 -4.32,-0.42 -0.34,-0.5 -0.41,-0.78 -0.41,-1.54 l -0.01,-0.94 -1.3,-0.97 C 19.52,99.77 16.59,96.27 14.47,91.97 13.31,89.63 12.58,87.52 12.07,85 c -0.21,-1.03 -0.38,-1.88 -0.4,-1.88 -0.01,0 -0.29,-0.13 -0.62,-0.29 -1.05,-0.54 -1.6,-1.76 -1.25,-2.9 0.19,-0.64 1,-1.57 1.36,-1.57 0.18,0 0.25,-0.28 0.31,-1.27 0.21,-2.84 0.92,-6.33 1.77,-8.61 1.52,-4.15 3.85,-7.68 7.19,-10.91 1.79,-1.73 2.05,-1.95 4.47,-3.57 3.68,-2.49 9.01,-4.39 13.21,-4.7 0.59,-0.04 1.41,-0.12 1.83,-0.16 l 0.76,-0.07 v -2.83 c 0,-2.62 0.01,-2.86 0.34,-3.46 1.07,-2.09 3.78,-2.15 4.91,-0.09 0.23,0.42 0.25,2.81 0.32,34.37 l 0.07,33.91 39.48,0.07 39.48,0.07 0.63,0.34 c 0.91,0.48 1.49,1.46 1.49,2.48 0,1.22 -0.57,2.12 -1.68,2.72 l -0.59,0.32 -41.89,0.01 c -23.04,0.01 -42,0 -42.13,-0.04 z m -0.42,-15.7 v -4.97 l -0.76,-0.06 -0.78,-0.06 -2.84,2.21 c -1.55,1.2 -3.59,2.78 -4.54,3.49 -0.94,0.7 -1.71,1.41 -1.74,1.57 -0.03,0.16 0.09,0.37 0.29,0.48 0.81,0.53 4.95,1.7 7,1.99 0.6,0.09 1.26,0.19 1.46,0.22 0.21,0.03 0.72,0.06 1.14,0.07 l 0.76,0.01 v -4.97 z m -10.25,0.37 c 1.71,-1.27 7.1,-5.54 7.15,-5.67 0.03,-0.07 -0.04,-0.13 -0.18,-0.13 -0.48,0 -2.72,-0.81 -3.97,-1.42 -1.8,-0.91 -3.31,-1.96 -4.61,-3.28 -1.35,-1.33 -2.21,-2.52 -3.25,-4.45 -0.67,-1.25 -0.85,-1.46 -1.29,-1.63 -0.28,-0.09 -0.85,-0.31 -1.25,-0.47 l -0.73,-0.29 -0.41,0.35 -0.41,0.35 0.53,1.71 c 0.29,0.95 0.92,2.92 1.39,4.37 0.47,1.45 1.36,4.26 1.98,6.27 0.6,1.99 1.17,3.78 1.25,3.97 0.1,0.28 0.26,0.35 0.95,0.42 0.45,0.04 1.03,0.22 1.29,0.38 0.51,0.32 0.45,0.34 1.57,-0.48 z m -4.91,-0.48 c -0.1,-0.29 -3.52,-11 -4.42,-13.95 L 20.59,85.36 20,85.15 c -0.95,-0.32 -1.54,-1.05 -1.54,-1.92 0,-0.42 -0.06,-0.45 -1.93,-1.17 l -1.95,-0.73 -0.34,0.67 -0.35,0.69 0.32,1.61 c 1.29,6.5 4.89,12.26 10.17,16.27 1.1,0.83 1.35,0.95 1.19,0.54 z M 40.73,78.73 V 63.54 l -1.23,0.07 c -1.2,0.09 -1.26,0.1 -1.7,0.62 -0.54,0.62 -1.23,0.79 -2.07,0.54 -0.67,-0.21 -1.11,-0.01 -3.09,1.33 -3.38,2.31 -5.73,5.93 -6.5,10.03 -0.35,1.88 -0.22,4.78 0.29,6.56 0.53,1.83 1.8,4.31 2.93,5.73 2.56,3.21 6.69,5.29 10.83,5.46 l 0.54,0.01 V 78.71 Z m -16.57,4.03 c -0.38,-1.51 -0.54,-4.98 -0.29,-6.52 0.59,-3.82 2.49,-7.41 5.39,-10.18 1.32,-1.26 2.43,-2.05 4.1,-2.93 1.01,-0.53 1.64,-1.04 1.66,-1.33 0,-0.04 -0.51,-0.53 -1.13,-1.05 -1.1,-0.95 -1.14,-0.97 -1.96,-0.97 H 31.1 l -2.62,2.72 -2.61,2.72 v 0.85 c 0,1.36 -0.85,2.26 -2.14,2.29 H 23 l -1.13,1.32 c -2.39,2.74 -6.58,7.63 -7.25,8.45 l -0.69,0.86 0.29,0.41 c 0.16,0.22 0.29,0.51 0.29,0.63 0,0.22 0.82,0.59 3.38,1.51 l 0.97,0.35 0.7,-0.31 c 1.22,-0.54 2.36,-0.04 2.74,1.2 0.15,0.5 0.25,0.59 1.13,0.91 0.53,0.21 0.98,0.35 1.01,0.32 0.01,-0.01 -0.1,-0.59 -0.28,-1.26 z m -9.49,-6.28 c 0.35,-0.44 1.1,-1.33 1.67,-1.98 0.56,-0.64 1.83,-2.12 2.81,-3.3 1,-1.17 2.07,-2.43 2.4,-2.81 l 0.6,-0.7 -0.23,-0.62 C 21.8,66.72 21.67,66.25 21.66,66 c -0.04,-0.41 -0.19,-0.51 -1.77,-1.3 -1.48,-0.75 -1.76,-0.83 -1.93,-0.66 -0.7,0.7 -2.65,4.97 -3.21,7.05 -0.69,2.52 -1.26,6.83 -0.85,6.34 0.07,-0.09 0.41,-0.51 0.78,-0.95 z m 8.12,-12.16 c 0.69,-0.35 1.08,-0.37 1.77,-0.07 l 0.53,0.22 2.59,-2.65 2.58,-2.67 v -0.92 l -0.01,-0.92 -1.36,-1.04 c -0.75,-0.56 -1.46,-1.03 -1.61,-1.03 -0.53,0 -3.16,1.92 -5.05,3.68 -1.14,1.07 -3.46,3.72 -3.46,3.97 0,0.12 3.13,1.73 3.35,1.73 0.06,0 0.35,-0.13 0.67,-0.29 z m 17.36,-3.06 0.51,-0.1 0.03,-2.04 c 0.03,-1.88 0.01,-2.08 -0.26,-2.4 -0.28,-0.34 -0.34,-0.35 -0.91,-0.18 -0.34,0.1 -1.33,0.34 -2.2,0.53 -0.88,0.21 -2.02,0.48 -2.55,0.63 -0.88,0.23 -0.97,0.31 -1.14,0.79 l -0.18,0.53 1.36,1.19 c 0.75,0.64 1.45,1.16 1.55,1.11 0.1,-0.04 0.4,-0.01 0.63,0.07 0.44,0.13 2.21,0.07 3.15,-0.13 z M 35.46,56.48 C 37.64,56 39.8,55.35 40.25,55.04 l 0.48,-0.32 v -1.66 -1.67 l -1.42,0.1 c -2.23,0.15 -4.07,0.45 -5.82,0.97 -1.82,0.51 -5.1,1.86 -5.04,2.05 0.03,0.07 0.56,0.51 1.19,1 l 1.14,0.88 0.92,-0.09 c 0.82,-0.07 0.95,-0.04 1.33,0.28 0.34,0.31 0.51,0.35 0.86,0.26 0.25,-0.06 0.95,-0.22 1.55,-0.37 z" id="path123"/>
<path d="m 60.72,108.98 c -0.25,-0.07 -0.64,-0.34 -0.88,-0.59 -0.37,-0.4 -0.42,-0.56 -0.42,-1.26 0.01,-0.78 0,-0.82 -0.7,-1.45 -1.01,-0.91 -1.79,-1.25 -2.87,-1.25 -0.79,0 -1.05,0.09 -2.26,0.72 -1.51,0.79 -3.24,1.41 -3.56,1.27 -0.19,-0.07 -0.22,-0.98 -0.22,-6.34 0,-3.44 0.06,-6.34 0.12,-6.43 0.06,-0.09 0.76,-0.59 1.57,-1.13 0.81,-0.53 2.09,-1.49 2.86,-2.14 1.64,-1.39 2.21,-1.63 3.94,-1.63 1.08,0 1.39,0.06 2.14,0.42 0.95,0.45 1.79,1.25 2.61,2.53 0.28,0.45 0.59,0.86 0.66,0.91 0.09,0.04 0.66,0.16 1.27,0.23 1.13,0.15 1.13,0.15 1.66,-0.29 0.88,-0.72 2.02,-0.54 2.5,0.4 0.51,1.01 -0.18,2.24 -1.26,2.24 -0.56,0 -1.51,-0.45 -1.51,-0.73 0,-0.25 -1.36,-0.16 -2.01,0.13 -0.53,0.25 -0.63,0.37 -0.7,0.85 -0.35,2.3 -0.53,2.59 -2.96,4.94 -2.2,2.12 -2.31,2.55 -1.1,4.34 l 0.54,0.81 0.75,-0.04 c 1.36,-0.09 2.11,0.6 2.11,1.93 0,0.53 -0.09,0.73 -0.45,1.1 -0.25,0.25 -0.53,0.45 -0.62,0.45 -0.1,0 -0.31,0.03 -0.47,0.06 -0.16,0.04 -0.5,0.01 -0.73,-0.06 z" id="path124"/>
<path d="m 126.56,80.2 c 0,-0.12 -0.1,-0.22 -0.22,-0.22 -0.12,0 -0.22,-0.06 -0.22,-0.12 0,-0.41 -1.01,-0.5 -5.52,-0.48 l -1.66,0.01 v 0.44 c 0,0.34 -0.06,0.44 -0.29,0.44 -0.28,0 -0.29,-0.1 -0.29,-3.38 0,-2.21 0.04,-3.34 0.15,-3.28 0.09,0.04 0.15,0.25 0.15,0.44 0,0.4 0.23,0.62 0.34,0.34 0.06,-0.15 0.09,-0.15 0.09,0 0.01,0.09 0.09,0.18 0.18,0.18 0.07,0 0.1,-0.06 0.06,-0.15 -0.04,-0.07 -0.03,-0.15 0.06,-0.15 0.07,0 0.18,0.06 0.23,0.13 0.1,0.19 3.25,0.19 3.37,0.01 0.04,-0.09 0.19,-0.15 0.31,-0.15 0.13,0 0.18,0.07 0.12,0.16 -0.07,0.12 0.01,0.15 0.28,0.07 0.21,-0.04 0.42,-0.03 0.47,0.06 0.06,0.09 0.19,0.1 0.34,0.01 0.16,-0.09 0.35,-0.09 0.51,0 0.19,0.12 0.28,0.09 0.34,-0.09 0.07,-0.19 0.13,-0.21 0.22,-0.06 0.07,0.12 0.04,0.25 -0.06,0.31 -0.12,0.07 -0.1,0.12 0.07,0.12 0.13,0 0.26,-0.09 0.31,-0.18 0.04,-0.15 0.1,-0.15 0.29,0.01 0.21,0.16 0.25,0.16 0.25,0 0,-0.16 0.03,-0.16 0.13,0 0.12,0.18 0.16,0.13 0.23,-0.18 0.25,-0.95 0.37,-0.01 0.37,2.84 0,2.96 -0.01,3.06 -0.29,3.06 -0.16,0 -0.29,-0.1 -0.29,-0.22 z" id="path125"/>
<path d="m 79.39,67.73 c 0,-0.82 -0.04,-1.01 -0.32,-1.25 -0.19,-0.15 -0.53,-0.26 -0.76,-0.28 -0.25,0 -0.54,-0.13 -0.7,-0.32 -0.21,-0.23 -0.31,-0.28 -0.44,-0.15 -0.22,0.22 -3.78,0.25 -3.78,0.03 0,-0.07 -0.1,-0.15 -0.22,-0.15 -0.12,0 -0.22,0.07 -0.22,0.15 0,0.09 -0.15,0.15 -0.32,0.15 -0.38,0.01 -0.85,0.32 -0.85,0.57 0,0.09 -0.13,0.16 -0.31,0.16 -0.29,0 -0.29,-0.04 -0.25,-3.13 0.03,-1.73 0.09,-3.19 0.16,-3.25 0.06,-0.06 0.19,0.01 0.29,0.15 0.18,0.25 0.22,0.25 0.42,0.03 0.18,-0.18 0.26,-0.19 0.31,-0.04 0.06,0.13 0.09,0.13 0.09,0 0.01,-0.1 0.12,-0.19 0.25,-0.19 0.12,0 0.18,-0.06 0.13,-0.15 -0.04,-0.07 0.01,-0.15 0.13,-0.15 0.29,0 0.29,0.07 0,0.35 -0.21,0.22 -0.21,0.23 0,0.23 0.13,0 0.23,-0.06 0.23,-0.15 0,-0.07 0.1,-0.15 0.23,-0.15 0.12,0 0.18,0.07 0.13,0.15 -0.04,0.09 -0.01,0.15 0.06,0.15 0.09,0 0.16,-0.07 0.16,-0.15 0,-0.09 0.13,-0.07 0.31,0.03 0.25,0.15 0.34,0.15 0.38,0.01 0.04,-0.12 0.18,-0.13 0.41,-0.04 0.47,0.18 1.98,0.13 1.98,-0.06 0,-0.1 0.04,-0.12 0.12,-0.01 0.16,0.23 1.64,0.26 1.64,0.04 0,-0.12 0.04,-0.13 0.12,-0.04 0.15,0.21 0.47,0.25 0.47,0.07 0,-0.07 0.1,-0.15 0.23,-0.15 0.12,0 0.18,0.07 0.12,0.16 -0.12,0.21 0.56,0.12 0.72,-0.09 0.06,-0.09 0.15,-0.07 0.23,0.06 0.09,0.13 0.28,0.18 0.59,0.12 0.23,-0.06 0.47,-0.04 0.51,0.01 0.09,0.16 1.44,0.12 1.68,-0.04 0.16,-0.09 0.19,-0.06 0.12,0.12 -0.04,0.15 -0.03,0.25 0.03,0.25 0.07,0 0.16,-0.06 0.19,-0.15 0.06,-0.16 0.95,-0.23 1.38,-0.09 0.21,0.06 0.4,0.01 0.56,-0.13 0.13,-0.13 0.23,-0.18 0.23,-0.1 0,0.15 0.44,0.13 0.7,-0.03 0.1,-0.06 0.18,-0.03 0.18,0.1 0,0.25 1.19,0.25 1.48,0 0.25,-0.21 1.61,-0.22 1.85,-0.01 0.13,0.1 0.19,0.1 0.19,-0.01 0,-0.09 0.15,-0.16 0.34,-0.16 0.26,0 0.28,0.03 0.1,0.15 -0.19,0.12 -0.19,0.15 0.03,0.15 0.13,0 0.29,-0.07 0.34,-0.15 0.06,-0.09 0.38,-0.15 0.72,-0.13 0.34,0.03 0.69,-0.01 0.78,-0.06 0.09,-0.06 0.19,-0.04 0.22,0.04 0.03,0.09 0.32,0.1 0.82,0.01 0.41,-0.06 0.76,-0.07 0.76,-0.01 0,0.07 0.23,0.06 0.51,0 0.31,-0.07 0.51,-0.06 0.51,0.03 0,0.07 0.09,0.07 0.23,0 0.13,-0.07 0.5,-0.09 0.81,-0.03 0.31,0.06 0.57,0.07 0.57,0.01 0,-0.04 0.22,-0.07 0.47,-0.06 0.26,0.01 0.81,0.03 1.19,0.03 0.42,0.01 0.67,0.07 0.62,0.16 -0.04,0.09 0.01,0.15 0.15,0.15 0.13,0 0.19,-0.06 0.15,-0.15 -0.04,-0.07 0.04,-0.15 0.21,-0.15 0.15,0 0.32,0.09 0.35,0.19 0.06,0.1 0.09,0.09 0.09,-0.06 0.01,-0.37 0.38,-0.44 0.7,-0.13 0.19,0.18 0.37,0.22 0.51,0.13 0.26,-0.15 2.02,-0.12 1.93,0.03 -0.09,0.16 0.88,0.25 1.05,0.12 0.21,-0.16 1.32,1.01 2.07,2.18 0.63,0.98 1.36,2.68 1.36,3.16 0,0.35 -0.06,0.38 -0.59,0.38 -0.32,0 -0.59,-0.06 -0.59,-0.15 0,-0.07 -0.13,-0.15 -0.29,-0.15 -0.16,0 -0.29,-0.06 -0.29,-0.15 0,-0.25 -1.63,-0.18 -1.82,0.07 -0.22,0.29 -2.74,0.45 -3.97,0.25 -0.48,-0.07 -0.91,-0.09 -0.95,-0.03 -0.04,0.07 -0.26,0.18 -0.48,0.22 -0.34,0.09 -0.51,0 -1.27,-0.7 -0.98,-0.91 -2.01,-1.45 -3.3,-1.73 -1.3,-0.28 -13.7,-0.29 -14.03,0 -0.22,0.16 -0.25,0.54 -0.25,2.52 0,2.23 -0.01,2.33 -0.29,2.33 -0.26,0 -0.29,-0.1 -0.29,-0.97 z" id="path126"/>
</g>
<g fill="#0d4ec9" id="g129">
<path d="m 118.37,81.59 c 0.01,-1.42 0.21,-3.76 0.25,-3.18 0.03,0.23 0.19,0.31 0.59,0.26 0.09,-0.01 0.18,0.04 0.18,0.13 0,0.07 0.16,0.15 0.37,0.15 0.21,0 0.37,-0.09 0.37,-0.18 0,-0.15 0.04,-0.13 0.13,0.01 0.12,0.19 0.45,0.18 2.04,-0.03 0.25,-0.04 0.47,-0.01 0.47,0.06 0,0.07 0.13,0.13 0.29,0.13 0.16,0 0.29,-0.07 0.29,-0.16 0,-0.07 0.07,-0.1 0.18,-0.04 0.28,0.18 1.41,0.22 1.51,0.06 0.04,-0.07 0.16,-0.15 0.23,-0.15 0.09,0 0.1,0.07 0.06,0.15 -0.04,0.09 -0.03,0.15 0.06,0.15 0.07,0 0.16,-0.07 0.19,-0.18 0.04,-0.12 0.16,-0.07 0.37,0.12 0.75,0.67 0.73,0.67 0.86,0.09 0.12,-0.54 0.12,-0.53 0.22,0.34 0.06,0.48 0.1,1.55 0.12,2.37 l 0.01,1.49 -4.39,-0.03 -4.39,-0.03 0.01,-1.54 z" id="path127"/>
<path d="M 98.57,73.7 C 99.92,71.61 99.9,68.76 98.51,66.68 98.23,66.24 98,65.89 98,65.87 c 0,-0.01 0.18,-0.04 0.4,-0.06 0.22,0 0.42,-0.09 0.45,-0.18 0.03,-0.09 0.15,-0.16 0.26,-0.16 0.13,0 0.18,-0.07 0.13,-0.15 -0.06,-0.09 0.07,-0.15 0.29,-0.15 0.22,0 0.35,0.06 0.29,0.15 -0.1,0.16 0.06,0.19 0.69,0.15 0.21,-0.01 0.42,0.04 0.47,0.13 0.06,0.07 0.47,0.13 0.94,0.13 0.57,0 0.75,-0.04 0.54,-0.12 -0.16,-0.07 -0.22,-0.13 -0.15,-0.13 0.09,-0.01 0.04,-0.09 -0.07,-0.16 -0.16,-0.1 0.04,-0.15 0.66,-0.15 0.54,0 0.81,0.06 0.67,0.13 -0.13,0.09 -0.15,0.16 -0.03,0.22 0.09,0.06 0.21,0 0.25,-0.12 0.07,-0.19 0.35,-0.23 1.48,-0.23 0.88,0 1.35,0.06 1.29,0.15 -0.06,0.09 0.07,0.15 0.29,0.15 0.22,0 0.35,0.06 0.29,0.15 -0.04,0.07 -0.01,0.15 0.07,0.15 0.1,0 0.13,-0.18 0.09,-0.44 -0.09,-0.59 0.12,-0.56 0.4,0.04 0.78,1.63 0.89,5.46 0.25,7.84 l -0.32,1.22 -4.06,-0.01 c -2.24,-0.01 -4.41,0.03 -4.83,0.09 l -0.76,0.1 z" id="path128"/>
<path d="m 71.22,70.17 c 0.04,-4.04 0.06,-4.25 0.34,-4.45 0.16,-0.13 0.45,-0.23 0.66,-0.23 0.29,0 0.32,-0.03 0.15,-0.15 -0.16,-0.1 0.01,-0.15 0.62,-0.15 0.53,0 0.85,0.07 0.89,0.19 0.06,0.13 0.09,0.13 0.09,0 0.01,-0.1 0.19,-0.19 0.4,-0.19 0.21,0 0.34,0.06 0.28,0.15 -0.04,0.09 0.01,0.15 0.13,0.15 0.13,0 0.23,-0.07 0.23,-0.18 0,-0.13 0.04,-0.13 0.18,0 0.23,0.23 0.81,0.22 0.89,0 0.06,-0.15 0.09,-0.15 0.09,0 0.01,0.23 0.38,0.22 0.59,-0.03 0.1,-0.12 0.29,-0.19 0.42,-0.16 0.13,0.03 0.45,0.06 0.69,0.07 0.25,0.01 0.37,0.06 0.26,0.1 -0.15,0.04 -0.15,0.1 0,0.25 0.12,0.15 0.26,0.16 0.5,0.04 0.26,-0.12 0.38,-0.07 0.69,0.28 l 0.37,0.42 0.03,-0.59 c 0.06,-1.11 0.23,2.93 0.26,5.83 l 0.01,2.89 H 75.6 71.19 l 0.04,-4.25 z" id="path129"/>
</g>
<g fill="#2728b2" id="g131">
<path d="m 74.25,103.64 c -0.94,-0.29 -2.07,-1.36 -2.58,-2.39 l -0.41,-0.83 -0.04,-13.26 -0.04,-13.26 4.37,0.03 4.37,0.03 0.04,1.41 0.04,1.39 h 6.77 c 6.15,0 6.86,-0.03 7.72,-0.28 1.38,-0.38 2.43,-0.97 3.33,-1.82 l 0.81,-0.75 4.53,-0.01 c 3.71,0 4.53,0.03 4.53,0.19 0,0.12 -0.26,0.72 -0.6,1.36 -1.05,2.02 -2.99,3.84 -5.38,5.02 -1.17,0.59 -3.25,1.3 -4.25,1.48 -0.35,0.06 -0.62,0.16 -0.59,0.22 0.03,0.06 0.6,0.56 1.29,1.11 2.65,2.09 4.79,4.58 7.03,8.19 1.32,2.11 3.05,5.51 3.52,6.94 0.76,2.33 0.16,4 -1.82,5.02 -0.45,0.25 -0.86,0.32 -1.7,0.32 -0.97,0.01 -1.25,-0.06 -2.09,-0.47 -1.64,-0.82 -2.14,-1.48 -6.39,-8.6 C 92.49,87.59 90.95,85.58 89,84.54 88.52,84.29 87.43,83.87 86.58,83.6 85.13,83.13 84.9,83.1 82.52,83.06 L 80,83 79.96,91.77 c -0.04,8.6 -0.04,8.77 -0.35,9.43 -0.94,2.02 -3.28,3.06 -5.35,2.4 z" id="path130"/>
<path d="m 120.89,103.34 c -0.92,-0.47 -1.57,-1.17 -2.08,-2.27 l -0.38,-0.81 v -8.79 -8.79 h 4.32 4.32 v 8.94 8.94 l -0.4,0.76 c -0.86,1.63 -1.92,2.29 -3.78,2.36 -1.11,0.04 -1.33,0 -2.01,-0.34 z" id="path131"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,16 +0,0 @@
[
{ "date": "2026-07-01", "timeIn": "09:05 AM", "timeOut": "06:15 PM", "loggedHours": "9h 10m", "status": "Present" },
{ "date": "2026-07-02", "timeIn": "10:30 AM", "timeOut": "06:30 PM", "loggedHours": "8h 00m", "status": "Late" },
{ "date": "2026-07-03", "timeIn": "-", "timeOut": "-", "loggedHours": "-", "status": "Leave" },
{ "date": "2026-07-04", "timeIn": "10:12 AM", "timeOut": "06:30 PM", "loggedHours": "8h 18m", "status": "Late" },
{ "date": "2026-07-06", "timeIn": "09:00 AM", "timeOut": "05:05 PM", "loggedHours": "8h 05m", "status": "Present" },
{ "date": "2026-07-07", "timeIn": "-", "timeOut": "-", "loggedHours": "-", "status": "Holiday" },
{ "date": "2026-07-08", "timeIn": "09:15 AM", "timeOut": "02:00 PM", "loggedHours": "4h 45m", "status": "Half Day" },
{ "date": "2026-07-10", "timeIn": "08:45 AM", "timeOut": "05:30 PM", "loggedHours": "8h 45m", "status": "WFH" },
{ "date": "2026-07-13", "timeIn": "08:50 AM", "timeOut": "06:00 PM", "loggedHours": "9h 10m", "status": "Present" },
{ "date": "2026-07-15", "timeIn": "-", "timeOut": "-", "loggedHours": "-", "status": "Absent" },
{ "date": "2026-07-17", "timeIn": "09:15 AM", "timeOut": "-", "loggedHours": "-", "status": "Mispunch" },
{ "date": "2026-07-20", "timeIn": "-", "timeOut": "-", "loggedHours": "-", "status": "Holiday" },
{ "date": "2026-07-21", "timeIn": "09:00 AM", "timeOut": "06:00 PM", "loggedHours": "9h 00m", "status": "Present" },
{ "date": "2026-07-26", "timeIn": "-", "timeOut": "-", "loggedHours": "-", "status": "Holiday" }
]

View File

@ -1,24 +0,0 @@
import { Provider } from 'react-redux';
import { store } from './store/store';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { Toaster } from 'sonner';
import MainLayout from './components/MainLayout';
import { BrowserRouter } from 'react-router-dom'; // <-- Import BrowserRouter
const queryClient = new QueryClient();
function App() {
return (
<Provider store={store}>
<QueryClientProvider client={queryClient}>
{/* Wrap MainLayout in BrowserRouter */}
<BrowserRouter>
<MainLayout />
</BrowserRouter>
<Toaster richColors position="top-right" />
</QueryClientProvider>
</Provider>
);
}
export default App;

View File

@ -1,148 +0,0 @@
import { useState, useMemo } from 'react';
import { ArrowUpDown, ArrowUp, ArrowDown, Calendar } from 'lucide-react';
import type { AttendanceRecord, SortKey } from '../types/attendance';
type SortDirection = 'ascending' | 'descending';
interface AttendanceRecordsTableProps {
records: AttendanceRecord[];
title?: string;
initialFromDate?: string;
initialToDate?: string;
}
export default function AttendanceRecordsTable({
records,
title = "Attendance Records",
initialFromDate,
initialToDate
}: AttendanceRecordsTableProps) {
// Default to current month if no initial dates are provided
const currentYear = new Date().getFullYear();
const currentMonth = new Date().getMonth();
const defaultFrom = initialFromDate || new Date(currentYear, currentMonth, 1).toISOString().split('T')[0];
const defaultTo = initialToDate || new Date(currentYear, currentMonth + 1, 0).toISOString().split('T')[0];
const [fromDate, setFromDate] = useState(defaultFrom);
const [toDate, setToDate] = useState(defaultTo);
const [sortConfig, setSortConfig] = useState<{ key: SortKey; direction: SortDirection }>({
key: 'date',
direction: 'descending'
});
const filteredData = useMemo(() => {
return records.filter((record) => record.date >= fromDate && record.date <= toDate);
}, [records, fromDate, toDate]);
const sortedData = useMemo(() => {
const sortableData = [...filteredData];
sortableData.sort((a, b) => {
if (a[sortConfig.key] < b[sortConfig.key]) return sortConfig.direction === 'ascending' ? -1 : 1;
if (a[sortConfig.key] > b[sortConfig.key]) return sortConfig.direction === 'ascending' ? 1 : -1;
return 0;
});
return sortableData;
}, [filteredData, sortConfig]);
const requestSort = (key: SortKey) => {
let direction: SortDirection = 'ascending';
if (sortConfig.key === key && sortConfig.direction === 'ascending') direction = 'descending';
setSortConfig({ key, direction });
};
const getSortIcon = (key: SortKey) => {
if (sortConfig.key !== key) return <ArrowUpDown size={14} className="ml-1 text-text-light" />;
if (sortConfig.direction === 'ascending') return <ArrowUp size={14} className="ml-1 text-primary" />;
return <ArrowDown size={14} className="ml-1 text-primary" />;
};
const getStatusClass = (status: string) => {
switch (status) {
case 'Present': return 'bg-present-100 text-present-700';
case 'Late': return 'bg-late-100 text-late-700';
case 'Half Day': return 'bg-halfday-100 text-halfday-700';
case 'WFH': return 'bg-wfh-100 text-wfh-700';
case 'Absent': return 'bg-absent-100 text-absent-700';
case 'Leave': return 'bg-leave-100 text-leave-700';
case 'Mispunch': return 'bg-mispunch-100 text-mispunch-700';
case 'Holiday': return 'bg-holiday-100 text-holiday-700';
default: return 'bg-app-muted text-text-secondary';
}
};
return (
<div className="bg-app-card p-6 rounded-lg shadow-sm mt-6">
<div className="flex flex-col md:flex-row md:items-center justify-between mb-6 gap-4">
<h2 className="text-lg font-semibold text-text-primary">{title}</h2>
<div className="flex items-center space-x-2">
<div className="relative">
<Calendar size={16} className="absolute left-3 top-1/2 -translate-y-1/2 text-text-light" />
<input
type="date"
value={fromDate}
onChange={(e) => setFromDate(e.target.value)}
className="pl-9 pr-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
/>
</div>
<span className="text-text-muted text-sm">to</span>
<div className="relative">
<Calendar size={16} className="absolute left-3 top-1/2 -translate-y-1/2 text-text-light" />
<input
type="date"
value={toDate}
onChange={(e) => setToDate(e.target.value)}
className="pl-9 pr-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
/>
</div>
</div>
</div>
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-app-border">
<thead>
<tr>
{(['date', 'timeIn', 'timeOut', 'loggedHours', 'status'] as SortKey[]).map((key) => (
<th
key={key}
onClick={() => requestSort(key)}
className="px-6 py-3 text-left text-xs font-semibold text-text-muted uppercase tracking-wider cursor-pointer hover:bg-app-muted transition-colors"
>
<div className="flex items-center">
{key.replace(/([A-Z])/g, ' $1').replace(/^./, (str: string) => str.toUpperCase())} {getSortIcon(key)}
</div>
</th>
))}
</tr>
</thead>
<tbody className="divide-y divide-app-border">
{sortedData.length > 0 ? (
sortedData.map((record, index) => (
<tr key={index} className="hover:bg-app-muted transition-colors">
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-primary">
{new Date(record.date + 'T00:00:00').toLocaleDateString('en-US', { day: '2-digit', month: 'short', year: 'numeric' })}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-secondary">{record.timeIn}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-secondary">{record.timeOut}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-text-primary">{record.loggedHours}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm">
<span className={`px-2 py-1 rounded-full text-xs font-medium ${getStatusClass(record.status)}`}>
{record.status}
</span>
</td>
</tr>
))
) : (
<tr>
<td colSpan={5} className="px-6 py-8 text-center text-sm text-text-muted">
No records found for the selected period.
</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
);
}

View File

@ -1,111 +0,0 @@
import { useState } from 'react';
import { CalendarCheck } from 'lucide-react';
interface CalendarProps {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
data: Record<string, any>;
selectedDate: string;
onDateSelect: (date: string) => void;
}
const formatDate = (date: Date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
};
export default function Calendar({ data, selectedDate, onDateSelect }: CalendarProps) {
const initialDate = new Date(selectedDate);
const [currentMonth, setCurrentMonth] = useState(initialDate.getMonth());
const [currentYear, setCurrentYear] = useState(initialDate.getFullYear());
const today = formatDate(new Date());
const daysInMonth = new Date(currentYear, currentMonth + 1, 0).getDate();
const firstDayOfMonth = new Date(currentYear, currentMonth, 1).getDay();
const monthsArray = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
const currentSystemYear = new Date().getFullYear();
const yearsArray = Array.from({ length: 16 }, (_, i) => currentSystemYear - 5 + i);
const handleDateClick = (day: number) => {
onDateSelect(formatDate(new Date(currentYear, currentMonth, day)));
};
const goToToday = () => {
const todayDate = new Date();
setCurrentMonth(todayDate.getMonth());
setCurrentYear(todayDate.getFullYear());
onDateSelect(formatDate(todayDate));
};
const getStatusClass = (dateStr: string, dayOfWeek: number) => {
const dayData = data[dateStr];
if (dayData?.status === 'Present') return 'bg-present-100 text-present-800 border-present-300 hover:bg-present-200';
if (dayData?.status === 'Late') return 'bg-late-100 text-late-800 border-late-300 hover:bg-late-200';
if (dayData?.status === 'Half Day') return 'bg-halfday-100 text-halfday-800 border-halfday-300 hover:bg-halfday-200';
if (dayData?.status === 'WFH') return 'bg-wfh-100 text-wfh-800 border-wfh-300 hover:bg-wfh-200';
if (dayData?.status === 'Absent') return 'bg-absent-100 text-absent-800 border-absent-300 hover:bg-absent-200';
if (dayData?.status === 'Leave') return 'bg-leave-100 text-leave-800 border-leave-300 hover:bg-leave-200';
if (dayData?.status === 'Mispunch') return 'bg-mispunch-100 text-mispunch-800 border-mispunch-300 hover:bg-mispunch-200';
if (dayData?.status === 'Holiday') return 'bg-holiday-100 text-holiday-800 border-holiday-300 hover:bg-holiday-200';
if (dayOfWeek === 0) return 'bg-app-muted text-text-light border-app-border';
if (dateStr === today) return 'bg-today-50 text-today-800 border-today-300 hover:bg-today-100';
return 'bg-app-card text-text-secondary border-app-border hover:bg-app-muted';
};
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
return (
<div className="bg-app-card p-6 rounded-lg shadow-sm">
<div className="flex items-center justify-between mb-6 flex-wrap gap-4">
<h2 className="text-lg font-semibold text-text-primary">My Attendance</h2>
<div className="flex items-center space-x-2">
<button onClick={goToToday} className="flex items-center px-3 py-2 text-sm font-medium text-text-secondary border border-app-border rounded-lg hover:bg-app-muted focus:outline-none focus:ring-2 focus:ring-primary transition-colors">
<CalendarCheck size={16} className="mr-2 text-text-muted" /> Today
</button>
<select value={currentMonth} onChange={(e) => setCurrentMonth(Number(e.target.value))} className="px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card">
{monthsArray.map((month, idx) => <option key={idx} value={idx}>{month}</option>)}
</select>
<select value={currentYear} onChange={(e) => setCurrentYear(Number(e.target.value))} className="px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card">
{yearsArray.map((year) => <option key={year} value={year}>{year}</option>)}
</select>
</div>
</div>
<div className="grid grid-cols-7 gap-2 mb-2">
{days.map(day => <div key={day} className="text-center text-xs font-semibold text-text-muted uppercase">{day}</div>)}
</div>
<div className="grid grid-cols-7 gap-2">
{Array.from({ length: firstDayOfMonth }).map((_, index) => <div key={`empty-${index}`} className="h-12"></div>)}
{Array.from({ length: daysInMonth }).map((_, index) => {
const day = index + 1;
const dateStr = formatDate(new Date(currentYear, currentMonth, day));
const dayOfWeek = new Date(currentYear, currentMonth, day).getDay();
const isSelected = selectedDate === dateStr;
return (
<button key={day} onClick={() => handleDateClick(day)} className={`h-12 flex flex-col items-center justify-center rounded-md border text-sm font-medium transition-colors ${getStatusClass(dateStr, dayOfWeek)} ${isSelected ? 'ring-2 ring-primary ring-offset-1' : ''}`}>
{day}
{data[dateStr] && <span className="w-1.5 h-1.5 rounded-full bg-text-muted mt-0.5"></span>}
</button>
);
})}
</div>
<div className="mt-8 pt-6 border-t border-app-border flex flex-wrap gap-4">
<div className="flex items-center space-x-2"><div className="w-4 h-4 rounded bg-present-100 border border-present-300"></div><span className="text-xs text-text-secondary">Present</span></div>
<div className="flex items-center space-x-2"><div className="w-4 h-4 rounded bg-late-100 border border-late-300"></div><span className="text-xs text-text-secondary">Late Arrival</span></div>
<div className="flex items-center space-x-2"><div className="w-4 h-4 rounded bg-halfday-100 border border-halfday-300"></div><span className="text-xs text-text-secondary">Half Day</span></div>
<div className="flex items-center space-x-2"><div className="w-4 h-4 rounded bg-wfh-100 border border-wfh-300"></div><span className="text-xs text-text-secondary">Work From Home</span></div>
<div className="flex items-center space-x-2"><div className="w-4 h-4 rounded bg-absent-100 border border-absent-300"></div><span className="text-xs text-text-secondary">Absent</span></div>
<div className="flex items-center space-x-2"><div className="w-4 h-4 rounded bg-leave-100 border border-leave-300"></div><span className="text-xs text-text-secondary">Leave</span></div>
<div className="flex items-center space-x-2"><div className="w-4 h-4 rounded bg-mispunch-100 border border-mispunch-300"></div><span className="text-xs text-text-secondary">Mispunch</span></div>
<div className="flex items-center space-x-2"><div className="w-4 h-4 rounded bg-holiday-100 border border-holiday-300"></div><span className="text-xs text-text-secondary">Holiday</span></div>
<div className="flex items-center space-x-2"><div className="w-4 h-4 rounded bg-app-muted border border-app-border"></div><span className="text-xs text-text-secondary">Weekly Off</span></div>
</div>
</div>
);
}

View File

@ -1,73 +0,0 @@
import { LogIn, LogOut, Clock } from 'lucide-react';
interface DailySummaryProps {
selectedDate: string;
data: {
status: string;
login: string;
logout: string;
hours: string;
} | null;
}
export default function DailySummary({ selectedDate, data }: DailySummaryProps) {
// Fallback if no data exists for the selected date
const selectedData = data || { status: 'No Data', login: '-', logout: '-', hours: '-' };
return (
<div className="w-full lg:w-96 bg-app-card p-6 rounded-lg shadow-sm h-fit">
<h3 className="text-lg font-semibold text-text-primary mb-1">Daily Summary</h3>
<p className="text-sm text-text-muted mb-6">
{new Date(selectedDate + 'T00:00:00').toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' })}
</p>
<div className="mb-6">
<span className={`px-3 py-1 rounded-full text-xs font-semibold uppercase tracking-wide
${selectedData.status === 'Present' ? 'bg-present-100 text-present-700' : ''}
${selectedData.status === 'Late' ? 'bg-late-100 text-late-700' : ''}
${selectedData.status === 'Half Day' ? 'bg-halfday-100 text-halfday-700' : ''}
${selectedData.status === 'WFH' ? 'bg-wfh-100 text-wfh-700' : ''}
${selectedData.status === 'Absent' ? 'bg-absent-100 text-absent-700' : ''}
${selectedData.status === 'Leave' ? 'bg-leave-100 text-leave-700' : ''}
${selectedData.status === 'Mispunch' ? 'bg-mispunch-100 text-mispunch-700' : ''}
${selectedData.status === 'Holiday' ? 'bg-holiday-100 text-holiday-700' : ''}
${selectedData.status === 'No Data' ? 'bg-app-muted text-text-secondary' : ''}
`}>
{selectedData.status}
</span>
</div>
<div className="space-y-4">
<div className="flex items-center justify-between p-4 bg-app-muted rounded-lg">
<div className="flex items-center space-x-3">
<div className="p-2 bg-present-100 rounded-lg">
<LogIn size={20} className="text-present-600" />
</div>
<span className="text-sm font-medium text-text-secondary">Check-In</span>
</div>
<span className="text-sm font-bold text-text-primary">{selectedData.login}</span>
</div>
<div className="flex items-center justify-between p-4 bg-app-muted rounded-lg">
<div className="flex items-center space-x-3">
<div className="p-2 bg-absent-100 rounded-lg">
<LogOut size={20} className="text-absent-600" />
</div>
<span className="text-sm font-medium text-text-secondary">Check-Out</span>
</div>
<span className="text-sm font-bold text-text-primary">{selectedData.logout}</span>
</div>
<div className="flex items-center justify-between p-4 bg-primary/5 rounded-lg border border-primary/20">
<div className="flex items-center space-x-3">
<div className="p-2 bg-today-100 rounded-lg">
<Clock size={20} className="text-primary" />
</div>
<span className="text-sm font-medium text-text-secondary">Total Hours</span>
</div>
<span className="text-base font-extrabold text-primary">{selectedData.hours}</span>
</div>
</div>
</div>
);
}

View File

@ -1,20 +0,0 @@
import { AlertCircle } from 'lucide-react';
interface ErrorStateProps {
message?: string;
fullScreen?: boolean;
}
export default function ErrorState({
message = 'Something went wrong. Please try again later.',
fullScreen = false
}: ErrorStateProps) {
return (
<div className={`flex items-center justify-center bg-app-card rounded-lg shadow-sm text-absent-500 ${
fullScreen ? 'h-screen w-screen fixed top-0 left-0 z-50' : 'h-64 w-full'
}`}>
<AlertCircle size={24} />
<span className="ml-3">{message}</span>
</div>
);
}

View File

@ -1,22 +0,0 @@
import { Loader2 } from 'lucide-react';
interface LoaderProps {
message?: string;
size?: number;
fullScreen?: boolean;
}
export default function Loader({
message = 'Loading...',
size = 32,
fullScreen = false
}: LoaderProps) {
return (
<div className={`flex flex-col items-center justify-center bg-app-card rounded-lg shadow-sm ${
fullScreen ? 'h-screen w-screen fixed top-0 left-0 z-50' : 'h-64 w-full'
}`}>
<Loader2 className="animate-spin text-primary" size={size} />
{message && <span className="mt-3 text-text-muted">{message}</span>}
</div>
);
}

View File

@ -1,40 +0,0 @@
import { useState } from 'react';
import { Routes, Route, Navigate } from 'react-router-dom';
import Sidebar from './Sidebar';
import Navbar from './Navbar';
import DashboardRouter from '../features/dashboard/DashboardRouter';
import AttendanceRouter from '../features/attendance/AttendanceRouter';
import LeaveRouter from '../features/leave/LeaveRouter';
import ApprovalRouter from '../features/approval/ApprovalRouter';
import AttendanceSummaryRouter from '../features/summary/AttendanceSummaryRouter';
import LeaveSummaryRouter from '../features/summary/LeaveSummaryRouter';
import ManageEmployeesRouter from '../features/manage/ManageEmployeesRouter';
import ManageAdminsRouter from '../features/manage/ManageAdminsRouter';
import PolicyRouter from '../features/policy/PolicyRouter';
export default function MainLayout() {
const [isCollapsed, setIsCollapsed] = useState(false);
return (
<div className="flex h-screen bg-app-bg overflow-hidden">
<Sidebar isCollapsed={isCollapsed} setIsCollapsed={setIsCollapsed} />
<div className="flex-1 flex flex-col">
<Navbar />
<main className="flex-1 overflow-y-auto p-6">
<Routes>
<Route path="/dashboard" element={<DashboardRouter />} />
<Route path="/attendance" element={<AttendanceRouter />} />
<Route path="/leave" element={<LeaveRouter />} />
<Route path="/approval" element={<ApprovalRouter />} />
<Route path="/attendance-summary" element={<AttendanceSummaryRouter />} />
<Route path="/leave-summary" element={<LeaveSummaryRouter />} />
<Route path="/manage-employees" element={<ManageEmployeesRouter />} />
<Route path="/manage-admins" element={<ManageAdminsRouter />} />
<Route path="/policies" element={<PolicyRouter />} />
<Route path="*" element={<Navigate to="/dashboard" replace />} />
</Routes>
</main>
</div>
</div>
);
}

View File

@ -1,179 +0,0 @@
import { useDispatch, useSelector } from 'react-redux';
import type { RootState } from '../store/store';
import { setRole, type UserRole } from '../store/roleSlice';
import { Search, UserCircle, ChevronDown, X, ArrowRight } from 'lucide-react';
import { useState, useRef, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
const roleSearchData: Record<UserRole, { name: string; path: string }[]> = {
employee: [
{ name: 'My Dashboard', path: '/dashboard' },
{ name: 'Mark Attendance', path: '/attendance' },
{ name: 'Apply for Leave', path: '/leave' },
],
manager: [
{ name: 'Manager Dashboard', path: '/dashboard' },
{ name: 'Team Attendance', path: '/attendance' },
{ name: 'Team Leaves', path: '/leave' },
{ name: 'Pending Approvals', path: '/approval' },
],
admin: [
{ name: 'Admin Dashboard', path: '/dashboard' },
{ name: 'All Attendance Logs', path: '/attendance' },
{ name: 'All Leave Requests', path: '/leave' },
{ name: 'Attendance Summary Report', path: '/attendance-summary' },
{ name: 'Leave Summary Report', path: '/leave-summary' },
{ name: 'Manage Employees', path: '/manage-employees' },
],
superadmin: [
{ name: 'SuperAdmin Dashboard', path: '/dashboard' },
{ name: 'Global Attendance Logs', path: '/attendance' },
{ name: 'Global Leave Requests', path: '/leave' },
{ name: 'Global Attendance Summary', path: '/attendance-summary' },
{ name: 'Global Leave Summary', path: '/leave-summary' },
{ name: 'Manage Admins', path: '/manage-admins' },
{ name: 'Manage Employees', path: '/manage-employees' },
{ name: 'Policy and Rules', path: '/policies' },
],
};
export default function Navbar() {
const dispatch = useDispatch();
const navigate = useNavigate();
const currentRole = useSelector((state: RootState) => state.role.currentRole);
const [dropdownOpen, setDropdownOpen] = useState(false);
const [isSearchOpen, setIsSearchOpen] = useState(false);
const [searchQuery, setSearchQuery] = useState('');
const searchInputRef = useRef<HTMLInputElement>(null);
const roles: UserRole[] = ['employee', 'manager', 'admin', 'superadmin'];
useEffect(() => {
if (isSearchOpen && searchInputRef.current) {
searchInputRef.current.focus();
}
}, [isSearchOpen]);
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
setIsSearchOpen(false);
setSearchQuery('');
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, []);
const availableItems = roleSearchData[currentRole] || [];
const searchResults = availableItems.filter(item =>
item.name.toLowerCase().includes(searchQuery.toLowerCase())
);
const handleSearchClick = (path: string) => {
navigate(path);
setIsSearchOpen(false);
setSearchQuery('');
};
return (
<div className="bg-app-card shadow-sm h-16 flex items-center justify-between px-6 border-b border-app-border relative z-30">
<div className="flex items-center">
<h2 className="text-lg font-bold text-primary tracking-wide">CLRI HRMS</h2>
</div>
<div className="flex items-center space-x-4">
<div className="relative flex items-center">
{isSearchOpen ? (
<div className="flex items-center border border-app-border rounded-lg shadow-sm bg-app-card">
<Search size={18} className="ml-3 text-text-light" />
<input
ref={searchInputRef}
type="text"
placeholder="Search..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-56 px-3 py-2 text-sm focus:outline-none rounded-lg text-text-primary"
/>
<button
onClick={() => { setIsSearchOpen(false); setSearchQuery(''); }}
className="p-2 text-text-light hover:text-text-muted"
>
<X size={18} />
</button>
</div>
) : (
<button
onClick={() => setIsSearchOpen(true)}
className="p-2 text-text-secondary hover:bg-app-muted rounded-full transition-colors"
>
<Search size={22} />
</button>
)}
{isSearchOpen && searchQuery && (
<div className="absolute right-0 top-14 w-72 bg-app-card border border-app-border rounded-lg shadow-lg overflow-hidden z-50">
{searchResults.length > 0 ? (
<ul>
{searchResults.map((item) => (
<li key={item.path}>
<button
onClick={() => handleSearchClick(item.path)}
className="flex items-center justify-between w-full px-4 py-3 text-left text-sm text-text-secondary hover:bg-app-muted transition-colors"
>
<span>{item.name}</span>
<ArrowRight size={14} className="text-text-light" />
</button>
</li>
))}
</ul>
) : (
<div className="px-4 py-3 text-sm text-text-muted">
No results found for "{searchQuery}"
</div>
)}
</div>
)}
</div>
<div className="relative">
<button
onClick={() => setDropdownOpen(!dropdownOpen)}
className="flex items-center space-x-2 p-1 rounded-lg hover:bg-app-muted transition-colors"
>
<UserCircle size={28} className="text-text-secondary" />
<div className="text-left hidden md:block">
<p className="text-sm font-medium text-text-primary capitalize">{currentRole}</p>
<p className="text-xs text-text-muted">Test Mode</p>
</div>
<ChevronDown size={16} className="text-text-muted" />
</button>
{dropdownOpen && (
<div className="absolute right-0 mt-2 w-48 bg-app-card border border-app-border rounded-lg shadow-lg z-50">
<p className="px-4 py-2 text-xs font-semibold text-text-light uppercase">Switch Role (Test)</p>
{roles.map((role) => (
<button
key={role}
onClick={() => {
dispatch(setRole(role));
setDropdownOpen(false);
}}
className={`block w-full text-left px-4 py-2 text-sm capitalize hover:bg-app-muted ${currentRole === role
? 'bg-primary/10 text-primary font-medium'
: 'text-text-secondary'
}`}
>
{role}
</button>
))}
</div>
)}
</div>
</div>
</div>
);
}

View File

@ -1,82 +0,0 @@
import { useSelector } from 'react-redux';
import type { RootState } from '../store/store';
import { getSidebarItems } from '../types/sidebarConfig';
import { Link, useLocation } from 'react-router-dom';
import {
LayoutDashboard, CalendarCheck, Plane, CheckCircle, BarChart3, ClipboardList,
Menu, Users, ShieldUser, ScrollText
} from 'lucide-react';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const iconMap: Record<string, any> = {
LayoutDashboard,
CalendarCheck,
Plane,
CheckCircle,
BarChart3,
ClipboardList,
Users,
ShieldUser,
ScrollText,
};
interface SidebarProps {
isCollapsed: boolean;
setIsCollapsed: (value: boolean) => void;
}
export default function Sidebar({ isCollapsed, setIsCollapsed }: SidebarProps) {
const role = useSelector((state: RootState) => state.role.currentRole);
const navItems = getSidebarItems(role);
const location = useLocation();
const currentPath = location.pathname;
return (
<div className={`bg-sidebar text-white h-screen transition-all duration-300 flex flex-col ${isCollapsed ? 'w-20' : 'w-64'}`}>
<div className={`flex items-center h-16 border-b border-primary/30 px-4 flex-shrink-0 ${isCollapsed ? 'justify-center' : 'justify-between'}`}>
{!isCollapsed && (
<Link to="/dashboard">
<img src="clri_logo.svg" alt="Company Logo" className="h-8 w-auto cursor-pointer" />
</Link>
)}
<button
onClick={() => setIsCollapsed(!isCollapsed)}
className="p-2 rounded hover:bg-sidebar-hover transition-colors"
>
<Menu size={24} />
</button>
</div>
<nav className="flex-1 mt-4 overflow-x-visible">
<ul>
{navItems.map((item) => {
const Icon = iconMap[item.icon];
const isActive = currentPath === item.path;
return (
<li key={item.name} className="mb-1 relative group">
<Link
to={item.path}
className={`flex items-center px-4 py-3 transition-colors duration-200 ${isCollapsed ? 'justify-center' : ''} ${isActive
? 'bg-sidebar-active text-white border-l-4 border-action'
: 'text-blue-100 hover:bg-sidebar-hover hover:text-white border-l-4 border-transparent'
}`}
>
{Icon && <Icon size={20} className="flex-shrink-0" />}
{!isCollapsed && <span className="ml-4 text-sm font-medium">{item.name}</span>}
</Link>
{isCollapsed && (
<div className="absolute left-full ml-4 top-1/2 -translate-y-1/2 z-50 bg-sidebar-active text-white text-sm px-3 py-1 rounded shadow-lg opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none whitespace-nowrap">
{item.name}
</div>
)}
</li>
);
})}
</ul>
</nav>
</div>
);
}

View File

@ -1,14 +0,0 @@
import { useSelector } from 'react-redux';
import type { RootState } from '../../store/store';
const ManagerApproval = () => <div className="bg-app-card p-6 rounded-lg shadow"><h2 className="text-2xl font-bold text-primary">Pending Approvals</h2><p className="mt-2 text-text-muted">Approve or reject team leave and regularization requests.</p></div>;
export default function ApprovalRouter() {
const role = useSelector((state: RootState) => state.role.currentRole);
if (role === 'manager') {
return <ManagerApproval />;
}
return <div className="bg-app-card p-6 rounded-lg shadow text-absent-500">Access Denied: Managers only.</div>;
}

View File

@ -1,18 +0,0 @@
import { useSelector } from 'react-redux';
import type { RootState } from '../../store/store';
import EmployeeAttendance from './EmployeeAttendance';
const ManagerAttendance = () => <div className="bg-app-card p-6 rounded-lg shadow"><h2 className="text-2xl font-bold text-primary">Team Attendance</h2><p className="mt-2 text-text-muted">View your team's daily attendance logs.</p></div>;
const AdminAttendance = () => <div className="bg-app-card p-6 rounded-lg shadow"><h2 className="text-2xl font-bold text-primary">Branch Attendance</h2><p className="mt-2 text-text-muted">Raw attendance logs for all employees.</p></div>;
export default function AttendanceRouter() {
const role = useSelector((state: RootState) => state.role.currentRole);
switch (role) {
case 'employee': return <EmployeeAttendance />;
case 'manager': return <ManagerAttendance />;
case 'admin':
case 'superadmin': return <AdminAttendance />;
default: return <div>Unauthorized</div>;
}
}

View File

@ -1,78 +0,0 @@
import { useState, useMemo } from 'react';
import Calendar from '../../components/Calendar';
import AttendanceRecordsTable from '../../components/AttendanceRecordsTable';
import DailySummary from '../../components/DailySummary';
import Loader from '../../components/Loader';
import ErrorState from '../../components/ErrorState';
import { useAttendanceData } from './useAttendanceData';
// Helper to get current system date in YYYY-MM-DD format
const getTodayDate = () => {
const today = new Date();
const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, '0');
const day = String(today.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
};
export default function EmployeeAttendance() {
// Initialize with the current system date
const [selectedDate, setSelectedDate] = useState(getTodayDate());
// Fetch data using TanStack Query
const { data: attendanceRecords, isLoading, isError } = useAttendanceData();
// Map array data to dictionary for the Calendar
const calendarData = useMemo(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const map: Record<string, any> = {};
if (attendanceRecords) {
attendanceRecords.forEach(record => {
map[record.date] = {
status: record.status,
login: record.timeIn,
logout: record.timeOut,
hours: record.loggedHours
};
});
}
return map;
}, [attendanceRecords]);
const selectedDayData = calendarData[selectedDate] || null;
// 1. Loading State
if (isLoading) {
return <Loader message="Loading attendance data..." />;
}
// 2. Error State
if (isError) {
return <ErrorState message="Failed to load attendance data. Please try again later." />;
}
// 3. Success State
return (
<div className="flex flex-col gap-6">
{/* Top Section: Calendar and Daily Summary */}
<div className="flex flex-col lg:flex-row gap-6">
<div className="flex-1">
<Calendar data={calendarData} selectedDate={selectedDate} onDateSelect={setSelectedDate} />
</div>
<DailySummary selectedDate={selectedDate} data={selectedDayData} />
</div>
{/* Bottom Section: Reusable Attendance Records Table */}
{/* Removed hardcoded dates so it defaults to the current system month */}
{attendanceRecords && (
<AttendanceRecordsTable
records={attendanceRecords}
title="My Attendance Records"
/>
)}
</div>
);
}

View File

@ -1,18 +0,0 @@
import { useQuery } from '@tanstack/react-query';
import type { AttendanceRecord } from '../../types/attendance';
export const useAttendanceData = () => {
return useQuery<AttendanceRecord[]>({
queryKey: ['attendanceRecords'],
queryFn: async () => {
// Simulate network delay (500ms)
await new Promise((resolve) => setTimeout(resolve, 500));
const response = await fetch('/mockApi/attendance.json');
if (!response.ok) {
throw new Error('Failed to fetch attendance data');
}
return response.json();
},
});
};

View File

@ -1,27 +0,0 @@
export default function AdminDashboard() {
return (
<div className="bg-app-card rounded-lg shadow-sm p-6">
<h2 className="text-2xl font-bold text-primary">Admin / SuperAdmin Dashboard</h2>
<p className="text-text-muted mt-2">Global branch analytics and summaries.</p>
<div className="mt-6 grid grid-cols-4 gap-4">
<div className="p-4 border rounded-lg bg-app-muted">
<h3 className="font-semibold text-sm text-text-muted">Total Employees</h3>
<p className="text-2xl text-primary">245</p>
</div>
<div className="p-4 border rounded-lg bg-app-muted">
<h3 className="font-semibold text-sm text-text-muted">Present Today</h3>
<p className="text-2xl text-present-600">198</p>
</div>
<div className="p-4 border rounded-lg bg-app-muted">
<h3 className="font-semibold text-sm text-text-muted">On Leave</h3>
<p className="text-2xl text-action">12</p>
</div>
<div className="p-4 border rounded-lg bg-app-muted">
<h3 className="font-semibold text-sm text-text-muted">Mispunch</h3>
<p className="text-2xl text-absent-500">5</p>
</div>
</div>
</div>
);
}

View File

@ -1,21 +0,0 @@
import { useSelector } from 'react-redux';
import type { RootState } from '../../store/store';
import EmployeeDashboard from './EmployeeDashboard';
import ManagerDashboard from './ManagerDashboard';
import AdminDashboard from './AdminDashboard';
export default function DashboardRouter() {
const role = useSelector((state: RootState) => state.role.currentRole);
switch (role) {
case 'employee':
return <EmployeeDashboard />;
case 'manager':
return <ManagerDashboard />;
case 'admin':
case 'superadmin':
return <AdminDashboard />;
default:
return <div>No role assigned</div>;
}
}

View File

@ -1,19 +0,0 @@
export default function EmployeeDashboard() {
return (
<div className="bg-app-card rounded-lg shadow-sm p-6">
<h2 className="text-2xl font-bold text-primary">Employee Dashboard</h2>
<p className="text-text-muted mt-2">Welcome! Here you can mark your attendance and check your leave balance.</p>
<div className="mt-6 grid grid-cols-3 gap-4">
<div className="p-4 border rounded-lg">
<h3 className="font-semibold">Leave Balance</h3>
<p className="text-2xl text-action">12 Days</p>
</div>
<div className="p-4 border rounded-lg">
<h3 className="font-semibold">Attendance Status</h3>
<button className="mt-2 px-4 py-1 bg-action text-white rounded">Check In</button>
</div>
</div>
</div>
);
}

View File

@ -1,22 +0,0 @@
export default function ManagerDashboard() {
return (
<div className="bg-app-card rounded-lg shadow-sm p-6">
<h2 className="text-2xl font-bold text-primary">Manager Dashboard</h2>
<p className="text-text-muted mt-2">Welcome Manager! Review your team's pending requests below.</p>
<div className="mt-6">
<h3 className="font-semibold text-lg">Pending Approvals</h3>
<ul className="mt-2 space-y-2">
<li className="p-3 border rounded flex justify-between items-center">
<span>John Doe - Casual Leave (2 Days)</span>
<button className="px-3 py-1 bg-primary text-white rounded text-sm">Review</button>
</li>
<li className="p-3 border rounded flex justify-between items-center">
<span>Jane Smith - Late Regularization</span>
<button className="px-3 py-1 bg-primary text-white rounded text-sm">Review</button>
</li>
</ul>
</div>
</div>
);
}

View File

@ -1,18 +0,0 @@
import { useSelector } from 'react-redux';
import type { RootState } from '../../store/store';
const EmployeeLeave = () => <div className="bg-app-card p-6 rounded-lg shadow"><h2 className="text-2xl font-bold text-primary">My Leaves</h2><p className="mt-2 text-text-muted">Apply for leave and view your leave history.</p></div>;
const ManagerLeave = () => <div className="bg-app-card p-6 rounded-lg shadow"><h2 className="text-2xl font-bold text-primary">Team Leave Calendar</h2><p className="mt-2 text-text-muted">See who is on leave in your team.</p></div>;
const AdminLeave = () => <div className="bg-app-card p-6 rounded-lg shadow"><h2 className="text-2xl font-bold text-primary">All Leave Requests</h2><p className="mt-2 text-text-muted">Manage leave types and balances for branches.</p></div>;
export default function LeaveRouter() {
const role = useSelector((state: RootState) => state.role.currentRole);
switch (role) {
case 'employee': return <EmployeeLeave />;
case 'manager': return <ManagerLeave />;
case 'admin':
case 'superadmin': return <AdminLeave />;
default: return <div>Unauthorized</div>;
}
}

View File

@ -1,17 +0,0 @@
import { useSelector } from 'react-redux';
import type { RootState } from '../../store/store';
export default function ManageAdminsRouter() {
const role = useSelector((state: RootState) => state.role.currentRole);
if (role === 'superadmin') {
return (
<div className="bg-app-card p-6 rounded-lg shadow">
<h2 className="text-2xl font-bold text-primary">Manage Admins</h2>
<p className="mt-2 text-text-muted">Create and manage branch administrators and their permissions.</p>
</div>
);
}
return <div className="bg-app-card p-6 rounded-lg shadow text-absent-500">Access Denied: SuperAdmins only.</div>;
}

View File

@ -1,17 +0,0 @@
import { useSelector } from 'react-redux';
import type { RootState } from '../../store/store';
export default function ManageEmployeesRouter() {
const role = useSelector((state: RootState) => state.role.currentRole);
if (role === 'admin' || role === 'superadmin') {
return (
<div className="bg-app-card p-6 rounded-lg shadow">
<h2 className="text-2xl font-bold text-primary">Manage Employees</h2>
<p className="mt-2 text-text-muted">Add, edit, or remove employee records and manage branch assignments.</p>
</div>
);
}
return <div className="bg-app-card p-6 rounded-lg shadow text-absent-500">Access Denied: Admins only.</div>;
}

View File

@ -1,17 +0,0 @@
import { useSelector } from 'react-redux';
import type { RootState } from '../../store/store';
export default function PolicyRouter() {
const role = useSelector((state: RootState) => state.role.currentRole);
if (role === 'superadmin') {
return (
<div className="bg-app-card p-6 rounded-lg shadow">
<h2 className="text-2xl font-bold text-primary">Policy and Rules</h2>
<p className="mt-2 text-text-muted">Configure global leave policies, LOP rules, and attendance shift timings.</p>
</div>
);
}
return <div className="bg-app-card p-6 rounded-lg shadow text-absent-500">Access Denied: SuperAdmins only.</div>;
}

View File

@ -1,14 +0,0 @@
import { useSelector } from 'react-redux';
import type { RootState } from '../../store/store';
const AdminAttendanceSummary = () => <div className="bg-app-card p-6 rounded-lg shadow"><h2 className="text-2xl font-bold text-primary">Attendance Summary Report</h2><p className="mt-2 text-text-muted">Generate branch-wise attendance reports.</p></div>;
export default function AttendanceSummaryRouter() {
const role = useSelector((state: RootState) => state.role.currentRole);
if (role === 'admin' || role === 'superadmin') {
return <AdminAttendanceSummary />;
}
return <div className="bg-app-card p-6 rounded-lg shadow text-absent-500">Access Denied: Admins only.</div>;
}

View File

@ -1,14 +0,0 @@
import { useSelector } from 'react-redux';
import type { RootState } from '../../store/store';
const AdminLeaveSummary = () => <div className="bg-app-card p-6 rounded-lg shadow"><h2 className="text-2xl font-bold text-primary">Leave Summary Report</h2><p className="mt-2 text-text-muted">Analyze leave trends across the organization.</p></div>;
export default function LeaveSummaryRouter() {
const role = useSelector((state: RootState) => state.role.currentRole);
if (role === 'admin' || role === 'superadmin') {
return <AdminLeaveSummary />;
}
return <div className="bg-app-card p-6 rounded-lg shadow text-absent-500">Access Denied: Admins only.</div>;
}

View File

@ -1,89 +0,0 @@
@import "tailwindcss";
@theme {
/* Brand Colors */
--color-primary: #0a66c2;
--color-primary-hover: #0854a0;
--color-action: #f36f21;
--color-action-hover: #d65e15;
/* Semantic Status Colors */
--color-present-100: #dcfce7;
--color-present-200: #bbf7d0;
--color-present-300: #86efac;
--color-present-600: #16a34a;
--color-present-700: #15803d;
--color-present-800: #166534;
--color-wfh-100: #e0e7ff;
--color-wfh-200: #c7d2fe;
--color-wfh-300: #a5b4fc;
--color-wfh-700: #4338ca;
--color-wfh-800: #3730a3;
--color-absent-100: #fee2e2;
--color-absent-200: #fecaca;
--color-absent-300: #fca5a5;
--color-absent-500: #ef4444;
--color-absent-600: #dc2626;
--color-absent-700: #b91c1c;
--color-absent-800: #991b1b;
--color-leave-100: #fef9c3;
--color-leave-200: #fef08a;
--color-leave-300: #fde047;
--color-leave-700: #a16207;
--color-leave-800: #854d0e;
--color-mispunch-100: #ffedd5;
--color-mispunch-200: #fed7aa;
--color-mispunch-300: #fdba74;
--color-mispunch-700: #c2410c;
--color-mispunch-800: #9a3412;
--color-holiday-100: #fce7f3;
--color-holiday-200: #fbcfe8;
--color-holiday-300: #f9a8d4;
--color-holiday-700: #be185d;
--color-holiday-800: #9d174d;
--color-today-50: #eff6ff;
--color-today-100: #dbeafe;
--color-today-300: #93c5fd;
--color-today-700: #1d4ed8;
--color-today-800: #1e40af;
/* Add these inside @theme */
--color-late-100: #fef3c7; /* Amber */
--color-late-200: #fde68a;
--color-late-300: #fcd34d;
--color-late-700: #b45309;
--color-late-800: #92400e;
--color-halfday-100: #ccfbf1; /* Teal */
--color-halfday-200: #99f6e4;
--color-halfday-300: #5eead4;
--color-halfday-700: #0f766e;
--color-halfday-800: #115e59;
/* UI Neutral Colors */
--color-sidebar: #1e293b;
--color-sidebar-hover: #334155;
--color-sidebar-active: #0f172a;
--color-app-bg: #f8fafc;
--color-app-card: #ffffff;
--color-app-border: #e2e8f0;
--color-app-muted: #f1f5f9;
--color-text-primary: #0f172a;
--color-text-secondary: #334155;
--color-text-muted: #64748b;
--color-text-light: #94a3b8;
}
body {
margin: 0;
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

View File

@ -1,11 +0,0 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)

View File

@ -1,26 +0,0 @@
import { createSlice } from '@reduxjs/toolkit';
import type { PayloadAction } from '@reduxjs/toolkit';
export type UserRole = 'employee' | 'manager' | 'admin' | 'superadmin';
interface RoleState {
currentRole: UserRole;
}
// Change this initial state to test different users immediately on load
const initialState: RoleState = {
currentRole: 'employee',
};
const roleSlice = createSlice({
name: 'role',
initialState,
reducers: {
setRole: (state, action: PayloadAction<UserRole>) => {
state.currentRole = action.payload;
},
},
});
export const { setRole } = roleSlice.actions;
export default roleSlice.reducer;

View File

@ -1,11 +0,0 @@
import { configureStore } from '@reduxjs/toolkit';
import roleReducer from './roleSlice';
export const store = configureStore({
reducer: {
role: roleReducer,
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

View File

@ -1,12 +0,0 @@
export type AttendanceStatus = 'Present' | 'WFH' | 'Absent' | 'Leave' | 'Mispunch' | 'Holiday' | 'Late' | 'Half Day';
export interface AttendanceRecord {
date: string;
timeIn: string;
timeOut: string;
loggedHours: string;
status: AttendanceStatus;
}
// Add this line to fix the error
export type SortKey = keyof AttendanceRecord;

View File

@ -1,41 +0,0 @@
import type { UserRole } from '../store/roleSlice';
export interface NavItem {
name: string;
icon: string;
path: string;
}
export const getSidebarItems = (role: UserRole): NavItem[] => {
const baseItems: NavItem[] = [
{ name: 'Dashboard', icon: 'LayoutDashboard', path: '/dashboard' },
{ name: 'Attendance', icon: 'CalendarCheck', path: '/attendance' },
{ name: 'Leave', icon: 'Plane', path: '/leave' },
];
if (role === 'manager') {
return [...baseItems, { name: 'Approval', icon: 'CheckCircle', path: '/approval' }];
}
if (role === 'admin') {
return [
...baseItems,
{ name: 'Attendance Summary', icon: 'BarChart3', path: '/attendance-summary' },
{ name: 'Leave Summary', icon: 'ClipboardList', path: '/leave-summary' },
{ name: 'Manage Employees', icon: 'Users', path: '/manage-employees' },
];
}
if (role === 'superadmin') {
return [
...baseItems,
{ name: 'Attendance Summary', icon: 'BarChart3', path: '/attendance-summary' },
{ name: 'Leave Summary', icon: 'ClipboardList', path: '/leave-summary' },
{ name: 'Manage Admins', icon: 'ShieldUser', path: '/manage-admins' }, // New
{ name: 'Manage Employees', icon: 'Users', path: '/manage-employees' }, // New
{ name: 'Policy and Rules', icon: 'ScrollText', path: '/policies' }, // New
];
}
return baseItems; // employee
};

View File

@ -1,26 +0,0 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "es2023",
"lib": ["ES2023", "DOM"],
"module": "esnext",
"types": ["vite/client"],
"allowArbitraryExtensions": true,
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
}

View File

@ -1,7 +0,0 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

View File

@ -1,23 +0,0 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "es2023",
"lib": ["ES2023"],
"types": ["node"],
"skipLibCheck": true,
/* Bundler mode */
"module": "nodenext",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true
},
"include": ["vite.config.ts"]
}

View File

@ -1,10 +0,0 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
// https://vite.dev/config/
export default defineConfig({
plugins: [react(),
tailwindcss(),
],
})