Compare commits

2 Commits

Author SHA1 Message Date
be5dafe539 Updated the logo
All checks were successful
Docker Deploy / build-and-push (push) Successful in 3m50s
2026-02-14 01:47:14 -07:00
6233380682 Cleaned up the theme a bit 2026-02-14 01:11:01 -07:00
45 changed files with 187 additions and 266 deletions

View File

@@ -24,7 +24,7 @@
"bcryptjs": "^3.0.3", "bcryptjs": "^3.0.3",
"chart.js": "^4.5.1", "chart.js": "^4.5.1",
"daisyui": "^5.5.18", "daisyui": "^5.5.18",
"dotenv": "^17.3.0", "dotenv": "^17.3.1",
"drizzle-orm": "0.45.1", "drizzle-orm": "0.45.1",
"jsonwebtoken": "^9.0.3", "jsonwebtoken": "^9.0.3",
"nanoid": "^5.1.6", "nanoid": "^5.1.6",
@@ -34,7 +34,6 @@
"vue-chartjs": "^5.3.3" "vue-chartjs": "^5.3.3"
}, },
"devDependencies": { "devDependencies": {
"@catppuccin/daisyui": "^2.1.1",
"@react-pdf/types": "^2.9.2", "@react-pdf/types": "^2.9.2",
"@types/jsonwebtoken": "^9.0.10", "@types/jsonwebtoken": "^9.0.10",
"drizzle-kit": "0.31.9" "drizzle-kit": "0.31.9"

175
pnpm-lock.yaml generated
View File

@@ -45,8 +45,8 @@ importers:
specifier: ^5.5.18 specifier: ^5.5.18
version: 5.5.18 version: 5.5.18
dotenv: dotenv:
specifier: ^17.3.0 specifier: ^17.3.1
version: 17.3.0 version: 17.3.1
drizzle-orm: drizzle-orm:
specifier: 0.45.1 specifier: 0.45.1
version: 0.45.1(@libsql/client@0.17.0)(@types/better-sqlite3@7.6.13)(better-sqlite3@12.6.0) version: 0.45.1(@libsql/client@0.17.0)(@types/better-sqlite3@7.6.13)(better-sqlite3@12.6.0)
@@ -69,9 +69,6 @@ importers:
specifier: ^5.3.3 specifier: ^5.3.3
version: 5.3.3(chart.js@4.5.1)(vue@3.5.28(typescript@5.9.3)) version: 5.3.3(chart.js@4.5.1)(vue@3.5.28(typescript@5.9.3))
devDependencies: devDependencies:
'@catppuccin/daisyui':
specifier: ^2.1.1
version: 2.1.1(tailwindcss@4.1.18)
'@react-pdf/types': '@react-pdf/types':
specifier: ^2.9.2 specifier: ^2.9.2
version: 2.9.2 version: 2.9.2
@@ -285,14 +282,6 @@ packages:
resolution: {integrity: sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==} resolution: {integrity: sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==}
engines: {node: '>=18'} engines: {node: '>=18'}
'@catppuccin/daisyui@2.1.1':
resolution: {integrity: sha512-PrZttjj8kwfDBJ34sR+DN25Xtjvxx4T5p8uu/iiGYZR8UOsNwzMlO/alYDBwwTOLzP1NKLNRax09kCT39+QM+A==}
peerDependencies:
tailwindcss: ^4.0.17
'@catppuccin/palette@1.7.1':
resolution: {integrity: sha512-aRc1tbzrevOTV7nFTT9SRdF26w/MIwT4Jwt4fDMc9itRZUDXCuEDBLyz4TQMlqO9ZP8mf5Hu4Jr6D03NLFc6Gw==}
'@ceereals/vue-pdf@0.2.1': '@ceereals/vue-pdf@0.2.1':
resolution: {integrity: sha512-E7Y2GyHTYEmZ2U5ZlVuJrOWdHhco49ZTdKVOo/wcOhlfNFG+W5pAZ6rOcaua+owspC4BgGzAxlmqj/jdEM9ehA==} resolution: {integrity: sha512-E7Y2GyHTYEmZ2U5ZlVuJrOWdHhco49ZTdKVOo/wcOhlfNFG+W5pAZ6rOcaua+owspC4BgGzAxlmqj/jdEM9ehA==}
peerDependencies: peerDependencies:
@@ -810,105 +799,89 @@ packages:
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-arm@1.2.4': '@img/sharp-libvips-linux-arm@1.2.4':
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-ppc64@1.2.4': '@img/sharp-libvips-linux-ppc64@1.2.4':
resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-riscv64@1.2.4': '@img/sharp-libvips-linux-riscv64@1.2.4':
resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-s390x@1.2.4': '@img/sharp-libvips-linux-s390x@1.2.4':
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-x64@1.2.4': '@img/sharp-libvips-linux-x64@1.2.4':
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-libvips-linuxmusl-arm64@1.2.4': '@img/sharp-libvips-linuxmusl-arm64@1.2.4':
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@img/sharp-libvips-linuxmusl-x64@1.2.4': '@img/sharp-libvips-linuxmusl-x64@1.2.4':
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@img/sharp-linux-arm64@0.34.5': '@img/sharp-linux-arm64@0.34.5':
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-linux-arm@0.34.5': '@img/sharp-linux-arm@0.34.5':
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-linux-ppc64@0.34.5': '@img/sharp-linux-ppc64@0.34.5':
resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-linux-riscv64@0.34.5': '@img/sharp-linux-riscv64@0.34.5':
resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-linux-s390x@0.34.5': '@img/sharp-linux-s390x@0.34.5':
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-linux-x64@0.34.5': '@img/sharp-linux-x64@0.34.5':
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-linuxmusl-arm64@0.34.5': '@img/sharp-linuxmusl-arm64@0.34.5':
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@img/sharp-linuxmusl-x64@0.34.5': '@img/sharp-linuxmusl-x64@0.34.5':
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@img/sharp-wasm32@0.34.5': '@img/sharp-wasm32@0.34.5':
resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
@@ -1021,35 +994,35 @@ packages:
'@peculiar/asn1-android@2.6.0': '@peculiar/asn1-android@2.6.0':
resolution: {integrity: sha512-cBRCKtYPF7vJGN76/yG8VbxRcHLPF3HnkoHhKOZeHpoVtbMYfY9ROKtH3DtYUY9m8uI1Mh47PRhHf2hSK3xcSQ==} resolution: {integrity: sha512-cBRCKtYPF7vJGN76/yG8VbxRcHLPF3HnkoHhKOZeHpoVtbMYfY9ROKtH3DtYUY9m8uI1Mh47PRhHf2hSK3xcSQ==}
'@peculiar/asn1-cms@2.6.0': '@peculiar/asn1-cms@2.6.1':
resolution: {integrity: sha512-2uZqP+ggSncESeUF/9Su8rWqGclEfEiz1SyU02WX5fUONFfkjzS2Z/F1Li0ofSmf4JqYXIOdCAZqIXAIBAT1OA==} resolution: {integrity: sha512-vdG4fBF6Lkirkcl53q6eOdn3XYKt+kJTG59edgRZORlg/3atWWEReRCx5rYE1ZzTTX6vLK5zDMjHh7vbrcXGtw==}
'@peculiar/asn1-csr@2.6.0': '@peculiar/asn1-csr@2.6.1':
resolution: {integrity: sha512-BeWIu5VpTIhfRysfEp73SGbwjjoLL/JWXhJ/9mo4vXnz3tRGm+NGm3KNcRzQ9VMVqwYS2RHlolz21svzRXIHPQ==} resolution: {integrity: sha512-WRWnKfIocHyzFYQTka8O/tXCiBquAPSrRjXbOkHbO4qdmS6loffCEGs+rby6WxxGdJCuunnhS2duHURhjyio6w==}
'@peculiar/asn1-ecc@2.6.0': '@peculiar/asn1-ecc@2.6.1':
resolution: {integrity: sha512-FF3LMGq6SfAOwUG2sKpPXblibn6XnEIKa+SryvUl5Pik+WR9rmRA3OCiwz8R3lVXnYnyRkSZsSLdml8H3UiOcw==} resolution: {integrity: sha512-+Vqw8WFxrtDIN5ehUdvlN2m73exS2JVG0UAyfVB31gIfor3zWEAQPD+K9ydCxaj3MLen9k0JhKpu9LqviuCE1g==}
'@peculiar/asn1-pfx@2.6.0': '@peculiar/asn1-pfx@2.6.1':
resolution: {integrity: sha512-rtUvtf+tyKGgokHHmZzeUojRZJYPxoD/jaN1+VAB4kKR7tXrnDCA/RAWXAIhMJJC+7W27IIRGe9djvxKgsldCQ==} resolution: {integrity: sha512-nB5jVQy3MAAWvq0KY0R2JUZG8bO/bTLpnwyOzXyEh/e54ynGTatAR+csOnXkkVD9AFZ2uL8Z7EV918+qB1qDvw==}
'@peculiar/asn1-pkcs8@2.6.0': '@peculiar/asn1-pkcs8@2.6.1':
resolution: {integrity: sha512-KyQ4D8G/NrS7Fw3XCJrngxmjwO/3htnA0lL9gDICvEQ+GJ+EPFqldcJQTwPIdvx98Tua+WjkdKHSC0/Km7T+lA==} resolution: {integrity: sha512-JB5iQ9Izn5yGMw3ZG4Nw3Xn/hb/G38GYF3lf7WmJb8JZUydhVGEjK/ZlFSWhnlB7K/4oqEs8HnfFIKklhR58Tw==}
'@peculiar/asn1-pkcs9@2.6.0': '@peculiar/asn1-pkcs9@2.6.1':
resolution: {integrity: sha512-b78OQ6OciW0aqZxdzliXGYHASeCvvw5caqidbpQRYW2mBtXIX2WhofNXTEe7NyxTb0P6J62kAAWLwn0HuMF1Fw==} resolution: {integrity: sha512-5EV8nZoMSxeWmcxWmmcolg22ojZRgJg+Y9MX2fnE2bGRo5KQLqV5IL9kdSQDZxlHz95tHvIq9F//bvL1OeNILw==}
'@peculiar/asn1-rsa@2.6.0': '@peculiar/asn1-rsa@2.6.1':
resolution: {integrity: sha512-Nu4C19tsrTsCp9fDrH+sdcOKoVfdfoQQ7S3VqjJU6vedR7tY3RLkQ5oguOIB3zFW33USDUuYZnPEQYySlgha4w==} resolution: {integrity: sha512-1nVMEh46SElUt5CB3RUTV4EG/z7iYc7EoaDY5ECwganibQPkZ/Y2eMsTKB/LeyrUJ+W/tKoD9WUqIy8vB+CEdA==}
'@peculiar/asn1-schema@2.6.0': '@peculiar/asn1-schema@2.6.0':
resolution: {integrity: sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg==} resolution: {integrity: sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg==}
'@peculiar/asn1-x509-attr@2.6.0': '@peculiar/asn1-x509-attr@2.6.1':
resolution: {integrity: sha512-MuIAXFX3/dc8gmoZBkwJWxUWOSvG4MMDntXhrOZpJVMkYX+MYc/rUAU2uJOved9iJEoiUx7//3D8oG83a78UJA==} resolution: {integrity: sha512-tlW6cxoHwgcQghnJwv3YS+9OO1737zgPogZ+CgWRUK4roEwIPzRH4JEiG770xe5HX2ATfCpmX60gurfWIF9dcQ==}
'@peculiar/asn1-x509@2.6.0': '@peculiar/asn1-x509@2.6.1':
resolution: {integrity: sha512-uzYbPEpoQiBoTq0/+jZtpM6Gq6zADBx+JNFP3yqRgziWBxQ/Dt/HcuvRfm9zJTPdRcBqPNdaRHTVwpyiq6iNMA==} resolution: {integrity: sha512-O9jT5F1A2+t3r7C4VT7LYGXqkGLK7Kj1xFpz7U0isPrubwU5PbDoyYtx6MiGst29yq7pXN5vZbQFKRCP+lLZlA==}
'@peculiar/x509@1.14.3': '@peculiar/x509@1.14.3':
resolution: {integrity: sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==} resolution: {integrity: sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==}
@@ -1140,79 +1113,66 @@ packages:
resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==} resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.57.1': '@rollup/rollup-linux-arm-musleabihf@4.57.1':
resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==} resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.57.1': '@rollup/rollup-linux-arm64-gnu@4.57.1':
resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==} resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.57.1': '@rollup/rollup-linux-arm64-musl@4.57.1':
resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==} resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.57.1': '@rollup/rollup-linux-loong64-gnu@4.57.1':
resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==} resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==}
cpu: [loong64] cpu: [loong64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-loong64-musl@4.57.1': '@rollup/rollup-linux-loong64-musl@4.57.1':
resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==} resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==}
cpu: [loong64] cpu: [loong64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-ppc64-gnu@4.57.1': '@rollup/rollup-linux-ppc64-gnu@4.57.1':
resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==} resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-ppc64-musl@4.57.1': '@rollup/rollup-linux-ppc64-musl@4.57.1':
resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==} resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-riscv64-gnu@4.57.1': '@rollup/rollup-linux-riscv64-gnu@4.57.1':
resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==} resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.57.1': '@rollup/rollup-linux-riscv64-musl@4.57.1':
resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==} resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.57.1': '@rollup/rollup-linux-s390x-gnu@4.57.1':
resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==} resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.57.1': '@rollup/rollup-linux-x64-gnu@4.57.1':
resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==} resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.57.1': '@rollup/rollup-linux-x64-musl@4.57.1':
resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==} resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-openbsd-x64@4.57.1': '@rollup/rollup-openbsd-x64@4.57.1':
resolution: {integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==} resolution: {integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==}
@@ -1313,28 +1273,24 @@ packages:
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-arm64-musl@4.1.18': '@tailwindcss/oxide-linux-arm64-musl@4.1.18':
resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@tailwindcss/oxide-linux-x64-gnu@4.1.18': '@tailwindcss/oxide-linux-x64-gnu@4.1.18':
resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-x64-musl@4.1.18': '@tailwindcss/oxide-linux-x64-musl@4.1.18':
resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@tailwindcss/oxide-wasm32-wasi@4.1.18': '@tailwindcss/oxide-wasm32-wasi@4.1.18':
resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==}
@@ -1926,8 +1882,8 @@ packages:
domutils@3.2.2: domutils@3.2.2:
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
dotenv@17.3.0: dotenv@17.3.1:
resolution: {integrity: sha512-i3z5dx/8F45f+Dj0B/qG8oKip9luzyHz6dfJMOKG7zQW/12tT7CrIjs/0J10uNK/Z5O7O0UtfEmx6yFKRQCl4g==} resolution: {integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==}
engines: {node: '>=12'} engines: {node: '>=12'}
drizzle-kit@0.31.9: drizzle-kit@0.31.9:
@@ -2492,28 +2448,24 @@ packages:
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
lightningcss-linux-arm64-musl@1.30.2: lightningcss-linux-arm64-musl@1.30.2:
resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
lightningcss-linux-x64-gnu@1.30.2: lightningcss-linux-x64-gnu@1.30.2:
resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
lightningcss-linux-x64-musl@1.30.2: lightningcss-linux-x64-musl@1.30.2:
resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
lightningcss-win32-arm64-msvc@1.30.2: lightningcss-win32-arm64-msvc@1.30.2:
resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==}
@@ -3295,8 +3247,8 @@ packages:
unified@11.0.5: unified@11.0.5:
resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
unifont@0.7.3: unifont@0.7.4:
resolution: {integrity: sha512-b0GtQzKCyuSHGsfj5vyN8st7muZ6VCI4XD4vFlr7Uy1rlWVYxC3npnfk8MyreHxJYrz1ooLDqDzFe9XqQTlAhA==} resolution: {integrity: sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg==}
unist-util-find-after@5.0.0: unist-util-find-after@5.0.0:
resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==}
@@ -4031,13 +3983,6 @@ snapshots:
dependencies: dependencies:
fontkitten: 1.0.2 fontkitten: 1.0.2
'@catppuccin/daisyui@2.1.1(tailwindcss@4.1.18)':
dependencies:
'@catppuccin/palette': 1.7.1
tailwindcss: 4.1.18
'@catppuccin/palette@1.7.1': {}
'@ceereals/vue-pdf@0.2.1(vue@3.5.28(typescript@5.9.3))': '@ceereals/vue-pdf@0.2.1(vue@3.5.28(typescript@5.9.3))':
dependencies: dependencies:
'@react-pdf/fns': 3.1.2 '@react-pdf/fns': 3.1.2
@@ -4510,59 +4455,59 @@ snapshots:
asn1js: 3.0.7 asn1js: 3.0.7
tslib: 2.8.1 tslib: 2.8.1
'@peculiar/asn1-cms@2.6.0': '@peculiar/asn1-cms@2.6.1':
dependencies: dependencies:
'@peculiar/asn1-schema': 2.6.0 '@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0 '@peculiar/asn1-x509': 2.6.1
'@peculiar/asn1-x509-attr': 2.6.0 '@peculiar/asn1-x509-attr': 2.6.1
asn1js: 3.0.7 asn1js: 3.0.7
tslib: 2.8.1 tslib: 2.8.1
'@peculiar/asn1-csr@2.6.0': '@peculiar/asn1-csr@2.6.1':
dependencies: dependencies:
'@peculiar/asn1-schema': 2.6.0 '@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0 '@peculiar/asn1-x509': 2.6.1
asn1js: 3.0.7 asn1js: 3.0.7
tslib: 2.8.1 tslib: 2.8.1
'@peculiar/asn1-ecc@2.6.0': '@peculiar/asn1-ecc@2.6.1':
dependencies: dependencies:
'@peculiar/asn1-schema': 2.6.0 '@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0 '@peculiar/asn1-x509': 2.6.1
asn1js: 3.0.7 asn1js: 3.0.7
tslib: 2.8.1 tslib: 2.8.1
'@peculiar/asn1-pfx@2.6.0': '@peculiar/asn1-pfx@2.6.1':
dependencies: dependencies:
'@peculiar/asn1-cms': 2.6.0 '@peculiar/asn1-cms': 2.6.1
'@peculiar/asn1-pkcs8': 2.6.0 '@peculiar/asn1-pkcs8': 2.6.1
'@peculiar/asn1-rsa': 2.6.0 '@peculiar/asn1-rsa': 2.6.1
'@peculiar/asn1-schema': 2.6.0 '@peculiar/asn1-schema': 2.6.0
asn1js: 3.0.7 asn1js: 3.0.7
tslib: 2.8.1 tslib: 2.8.1
'@peculiar/asn1-pkcs8@2.6.0': '@peculiar/asn1-pkcs8@2.6.1':
dependencies: dependencies:
'@peculiar/asn1-schema': 2.6.0 '@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0 '@peculiar/asn1-x509': 2.6.1
asn1js: 3.0.7 asn1js: 3.0.7
tslib: 2.8.1 tslib: 2.8.1
'@peculiar/asn1-pkcs9@2.6.0': '@peculiar/asn1-pkcs9@2.6.1':
dependencies: dependencies:
'@peculiar/asn1-cms': 2.6.0 '@peculiar/asn1-cms': 2.6.1
'@peculiar/asn1-pfx': 2.6.0 '@peculiar/asn1-pfx': 2.6.1
'@peculiar/asn1-pkcs8': 2.6.0 '@peculiar/asn1-pkcs8': 2.6.1
'@peculiar/asn1-schema': 2.6.0 '@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0 '@peculiar/asn1-x509': 2.6.1
'@peculiar/asn1-x509-attr': 2.6.0 '@peculiar/asn1-x509-attr': 2.6.1
asn1js: 3.0.7 asn1js: 3.0.7
tslib: 2.8.1 tslib: 2.8.1
'@peculiar/asn1-rsa@2.6.0': '@peculiar/asn1-rsa@2.6.1':
dependencies: dependencies:
'@peculiar/asn1-schema': 2.6.0 '@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0 '@peculiar/asn1-x509': 2.6.1
asn1js: 3.0.7 asn1js: 3.0.7
tslib: 2.8.1 tslib: 2.8.1
@@ -4572,14 +4517,14 @@ snapshots:
pvtsutils: 1.3.6 pvtsutils: 1.3.6
tslib: 2.8.1 tslib: 2.8.1
'@peculiar/asn1-x509-attr@2.6.0': '@peculiar/asn1-x509-attr@2.6.1':
dependencies: dependencies:
'@peculiar/asn1-schema': 2.6.0 '@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0 '@peculiar/asn1-x509': 2.6.1
asn1js: 3.0.7 asn1js: 3.0.7
tslib: 2.8.1 tslib: 2.8.1
'@peculiar/asn1-x509@2.6.0': '@peculiar/asn1-x509@2.6.1':
dependencies: dependencies:
'@peculiar/asn1-schema': 2.6.0 '@peculiar/asn1-schema': 2.6.0
asn1js: 3.0.7 asn1js: 3.0.7
@@ -4588,13 +4533,13 @@ snapshots:
'@peculiar/x509@1.14.3': '@peculiar/x509@1.14.3':
dependencies: dependencies:
'@peculiar/asn1-cms': 2.6.0 '@peculiar/asn1-cms': 2.6.1
'@peculiar/asn1-csr': 2.6.0 '@peculiar/asn1-csr': 2.6.1
'@peculiar/asn1-ecc': 2.6.0 '@peculiar/asn1-ecc': 2.6.1
'@peculiar/asn1-pkcs9': 2.6.0 '@peculiar/asn1-pkcs9': 2.6.1
'@peculiar/asn1-rsa': 2.6.0 '@peculiar/asn1-rsa': 2.6.1
'@peculiar/asn1-schema': 2.6.0 '@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0 '@peculiar/asn1-x509': 2.6.1
pvtsutils: 1.3.6 pvtsutils: 1.3.6
reflect-metadata: 0.2.2 reflect-metadata: 0.2.2
tslib: 2.8.1 tslib: 2.8.1
@@ -4807,10 +4752,10 @@ snapshots:
'@hexagon/base64': 1.1.28 '@hexagon/base64': 1.1.28
'@levischuck/tiny-cbor': 0.2.11 '@levischuck/tiny-cbor': 0.2.11
'@peculiar/asn1-android': 2.6.0 '@peculiar/asn1-android': 2.6.0
'@peculiar/asn1-ecc': 2.6.0 '@peculiar/asn1-ecc': 2.6.1
'@peculiar/asn1-rsa': 2.6.0 '@peculiar/asn1-rsa': 2.6.1
'@peculiar/asn1-schema': 2.6.0 '@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0 '@peculiar/asn1-x509': 2.6.1
'@peculiar/x509': 1.14.3 '@peculiar/x509': 1.14.3
'@swc/helpers@0.5.18': '@swc/helpers@0.5.18':
@@ -5250,7 +5195,7 @@ snapshots:
tinyglobby: 0.2.15 tinyglobby: 0.2.15
tsconfck: 3.1.6(typescript@5.9.3) tsconfck: 3.1.6(typescript@5.9.3)
ultrahtml: 1.6.0 ultrahtml: 1.6.0
unifont: 0.7.3 unifont: 0.7.4
unist-util-visit: 5.1.0 unist-util-visit: 5.1.0
unstorage: 1.17.4 unstorage: 1.17.4
vfile: 6.0.3 vfile: 6.0.3
@@ -5603,7 +5548,7 @@ snapshots:
domelementtype: 2.3.0 domelementtype: 2.3.0
domhandler: 5.0.3 domhandler: 5.0.3
dotenv@17.3.0: {} dotenv@17.3.1: {}
drizzle-kit@0.31.9: drizzle-kit@0.31.9:
dependencies: dependencies:
@@ -7245,7 +7190,7 @@ snapshots:
trough: 2.2.0 trough: 2.2.0
vfile: 6.0.3 vfile: 6.0.3
unifont@0.7.3: unifont@0.7.4:
dependencies: dependencies:
css-tree: 3.1.0 css-tree: 3.1.0
ofetch: 1.5.1 ofetch: 1.5.1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 732 B

After

Width:  |  Height:  |  Size: 744 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 530 KiB

View File

@@ -9,7 +9,7 @@ const initial = name ? name.charAt(0).toUpperCase() : '?';
--- ---
<div class:list={["avatar placeholder", className]}> <div class:list={["avatar placeholder", className]}>
<div class="bg-primary/15 text-primary w-9 h-9 rounded-full flex items-center justify-center"> <div class="bg-base-300 text-primary w-9 h-9 rounded-full flex items-center justify-center">
<span class="text-sm font-semibold">{initial}</span> <span class="text-sm font-semibold">{initial}</span>
</div> </div>
</div> </div>

View File

@@ -148,7 +148,7 @@ function clearForm() {
<template> <template>
<div <div
class="card bg-base-200/50 backdrop-blur-sm shadow-lg border border-base-300/50 hover:border-base-300 transition-all duration-200" class="card bg-base-200 backdrop-blur-sm shadow-lg border border-base-content/20 hover:border-base-content/20 transition-all duration-200"
> >
<div class="card-body gap-6"> <div class="card-body gap-6">
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
@@ -183,7 +183,7 @@ function clearForm() {
<select <select
id="manual-client" id="manual-client"
v-model="selectedClientId" v-model="selectedClientId"
class="select select-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors" class="select select-bordered w-full bg-base-300 hover:bg-base-300 focus:bg-base-300 border-base-content/20 focus:border-primary transition-colors"
:disabled="isSubmitting" :disabled="isSubmitting"
> >
<option value="">Select a client...</option> <option value="">Select a client...</option>
@@ -203,7 +203,7 @@ function clearForm() {
id="manual-start-date" id="manual-start-date"
v-model="startDate" v-model="startDate"
type="date" type="date"
class="input input-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors" class="input input-bordered w-full bg-base-300 hover:bg-base-300 focus:bg-base-300 border-base-content/20 focus:border-primary transition-colors"
:disabled="isSubmitting" :disabled="isSubmitting"
/> />
</div> </div>
@@ -216,7 +216,7 @@ function clearForm() {
id="manual-start-time" id="manual-start-time"
v-model="startTime" v-model="startTime"
type="time" type="time"
class="input input-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors" class="input input-bordered w-full bg-base-300 hover:bg-base-300 focus:bg-base-300 border-base-content/20 focus:border-primary transition-colors"
:disabled="isSubmitting" :disabled="isSubmitting"
/> />
</div> </div>
@@ -232,7 +232,7 @@ function clearForm() {
id="manual-end-date" id="manual-end-date"
v-model="endDate" v-model="endDate"
type="date" type="date"
class="input input-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors" class="input input-bordered w-full bg-base-300 hover:bg-base-300 focus:bg-base-300 border-base-content/20 focus:border-primary transition-colors"
:disabled="isSubmitting" :disabled="isSubmitting"
/> />
</div> </div>
@@ -245,7 +245,7 @@ function clearForm() {
id="manual-end-time" id="manual-end-time"
v-model="endTime" v-model="endTime"
type="time" type="time"
class="input input-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors" class="input input-bordered w-full bg-base-300 hover:bg-base-300 focus:bg-base-300 border-base-content/20 focus:border-primary transition-colors"
:disabled="isSubmitting" :disabled="isSubmitting"
/> />
</div> </div>
@@ -261,7 +261,7 @@ function clearForm() {
v-model="description" v-model="description"
type="text" type="text"
placeholder="What did you work on?" placeholder="What did you work on?"
class="input input-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors" class="input input-bordered w-full bg-base-300 hover:bg-base-300 focus:bg-base-300 border-base-content/20 focus:border-primary transition-colors"
:disabled="isSubmitting" :disabled="isSubmitting"
/> />
</div> </div>
@@ -278,7 +278,7 @@ function clearForm() {
'badge badge-lg cursor-pointer transition-all hover:scale-105', 'badge badge-lg cursor-pointer transition-all hover:scale-105',
selectedTagId === tag.id selectedTagId === tag.id
? 'badge-primary shadow-lg shadow-primary/20' ? 'badge-primary shadow-lg shadow-primary/20'
: 'badge-outline hover:bg-base-300/50', : 'badge-outline hover:bg-base-300',
]" ]"
:disabled="isSubmitting" :disabled="isSubmitting"
type="button" type="button"

View File

@@ -18,12 +18,12 @@ const { title, value, description, icon, color = 'text-primary', valueClass } =
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<span class="text-xs font-medium uppercase tracking-wider text-base-content/60">{title}</span> <span class="text-xs font-medium uppercase tracking-wider text-base-content/60">{title}</span>
{icon && ( {icon && (
<div class:list={[color, "opacity-40"]}> <div class:list={[color, "opacity-70"]}>
<Icon name={icon} class="w-5 h-5" /> <Icon name={icon} class="w-5 h-5" />
</div> </div>
)} )}
</div> </div>
<div class:list={["text-2xl font-bold", color, valueClass]}>{value}</div> <div class:list={["text-2xl font-bold", color, valueClass]}>{value}</div>
{description && <div class="text-xs text-base-content/50">{description}</div>} {description && <div class="text-xs text-base-content/60">{description}</div>}
</div> </div>
</div> </div>

View File

@@ -1,22 +1,22 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue'; import { ref, onMounted } from "vue";
import Icon from './Icon.vue'; import Icon from "./Icon.vue";
const theme = ref('macchiato'); const theme = ref("sunset");
onMounted(() => { onMounted(() => {
const stored = localStorage.getItem('theme'); const stored = localStorage.getItem("theme");
if (stored) { if (stored) {
theme.value = stored; theme.value = stored;
document.documentElement.setAttribute('data-theme', stored); document.documentElement.setAttribute("data-theme", stored);
} }
}); });
function toggleTheme() { function toggleTheme() {
const newTheme = theme.value === 'macchiato' ? 'latte' : 'macchiato'; const newTheme = theme.value === "sunset" ? "winter" : "sunset";
theme.value = newTheme; theme.value = newTheme;
document.documentElement.setAttribute('data-theme', newTheme); document.documentElement.setAttribute("data-theme", newTheme);
localStorage.setItem('theme', newTheme); localStorage.setItem("theme", newTheme);
} }
</script> </script>
@@ -26,9 +26,6 @@ function toggleTheme() {
class="btn btn-ghost btn-circle" class="btn btn-ghost btn-circle"
aria-label="Toggle Theme" aria-label="Toggle Theme"
> >
<Icon <Icon :name="theme === 'sunset' ? 'moon' : 'sun'" class="w-5 h-5" />
:name="theme === 'macchiato' ? 'moon' : 'sun'"
class="w-5 h-5"
/>
</button> </button>
</template> </template>

View File

@@ -118,7 +118,7 @@ async function stopTimer() {
<template> <template>
<div <div
class="card bg-base-200/50 backdrop-blur-sm shadow-lg border border-base-300/50 mb-6 hover:border-base-300 transition-all duration-200" class="card bg-base-200 backdrop-blur-sm shadow-lg border border-base-content/20 mb-6 hover:border-base-content/20 transition-all duration-200"
> >
<div class="card-body gap-6"> <div class="card-body gap-6">
<!-- Client Row --> <!-- Client Row -->
@@ -129,7 +129,7 @@ async function stopTimer() {
<select <select
id="timer-client" id="timer-client"
v-model="selectedClientId" v-model="selectedClientId"
class="select select-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors" class="select select-bordered w-full bg-base-300 hover:bg-base-300 focus:bg-base-300 border-base-content/20 focus:border-primary transition-colors"
:disabled="isRunning" :disabled="isRunning"
> >
<option value="">Select a client...</option> <option value="">Select a client...</option>
@@ -149,7 +149,7 @@ async function stopTimer() {
v-model="description" v-model="description"
type="text" type="text"
placeholder="What are you working on?" placeholder="What are you working on?"
class="input input-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors" class="input input-bordered w-full bg-base-300 hover:bg-base-300 focus:bg-base-300 border-base-content/20 focus:border-primary transition-colors"
:disabled="isRunning" :disabled="isRunning"
/> />
</div> </div>
@@ -166,7 +166,7 @@ async function stopTimer() {
'badge badge-lg cursor-pointer transition-all hover:scale-105', 'badge badge-lg cursor-pointer transition-all hover:scale-105',
selectedTagId === tag.id selectedTagId === tag.id
? 'badge-primary shadow-lg shadow-primary/20' ? 'badge-primary shadow-lg shadow-primary/20'
: 'badge-outline hover:bg-base-300/50', : 'badge-outline hover:bg-base-300',
]" ]"
:disabled="isRunning" :disabled="isRunning"
type="button" type="button"

View File

@@ -107,7 +107,7 @@ function closeShowTokenModal() {
<template> <template>
<div> <div>
<div class="card bg-base-100 shadow-xl border border-base-200 mb-6"> <div class="card bg-base-100 shadow-xl border border-base-content/20 mb-6">
<div class="card-body p-4 sm:p-6"> <div class="card-body p-4 sm:p-6">
<div class="flex justify-between items-center mb-6"> <div class="flex justify-between items-center mb-6">
<h2 class="card-title text-lg sm:text-xl"> <h2 class="card-title text-lg sm:text-xl">

View File

@@ -94,7 +94,7 @@ async function deletePasskey(id: string) {
</script> </script>
<template> <template>
<div class="card bg-base-100 shadow-xl border border-base-200 mb-6"> <div class="card bg-base-100 shadow-xl border border-base-content/20 mb-6">
<div class="card-body p-4 sm:p-6"> <div class="card-body p-4 sm:p-6">
<div class="flex justify-between items-center mb-6"> <div class="flex justify-between items-center mb-6">
<h2 class="card-title text-lg sm:text-xl"> <h2 class="card-title text-lg sm:text-xl">

View File

@@ -86,7 +86,7 @@ async function changePassword() {
<span>{{ message.text }}</span> <span>{{ message.text }}</span>
</div> </div>
<div class="card bg-base-100 shadow-xl border border-base-200 mb-6"> <div class="card bg-base-100 shadow-xl border border-base-content/20 mb-6">
<div class="card-body p-4 sm:p-6"> <div class="card-body p-4 sm:p-6">
<h2 class="card-title mb-6 text-lg sm:text-xl"> <h2 class="card-title mb-6 text-lg sm:text-xl">
<Icon name="key" class="w-5 h-5 sm:w-6 sm:h-6" /> <Icon name="key" class="w-5 h-5 sm:w-6 sm:h-6" />

View File

@@ -71,7 +71,7 @@ async function updateProfile() {
<span>{{ message.text }}</span> <span>{{ message.text }}</span>
</div> </div>
<div class="card bg-base-100 shadow-xl border border-base-200 mb-6"> <div class="card bg-base-100 shadow-xl border border-base-content/20 mb-6">
<div class="card-body p-4 sm:p-6"> <div class="card-body p-4 sm:p-6">
<h2 class="card-title mb-6 text-lg sm:text-xl"> <h2 class="card-title mb-6 text-lg sm:text-xl">
<Icon name="user-circle" class="w-5 h-5 sm:w-6 sm:h-6" /> <Icon name="user-circle" class="w-5 h-5 sm:w-6 sm:h-6" />

View File

@@ -28,7 +28,6 @@ const userMemberships = await db.select({
.all(); .all();
const currentTeamId = Astro.cookies.get('currentTeamId')?.value || userMemberships[0]?.organization.id; const currentTeamId = Astro.cookies.get('currentTeamId')?.value || userMemberships[0]?.organization.id;
const currentTeam = userMemberships.find(m => m.organization.id === currentTeamId);
const navItems = [ const navItems = [
{ href: '/dashboard', label: 'Dashboard', icon: 'home', exact: true }, { href: '/dashboard', label: 'Dashboard', icon: 'home', exact: true },
@@ -54,8 +53,8 @@ function isActive(item: { href: string; exact?: boolean }) {
<link rel="icon" type="image/x-icon" href="/favicon.ico" /> <link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="generator" content={Astro.generator} /> <meta name="generator" content={Astro.generator} />
<title>{title}</title> <title>{title}</title>
<script> <script is:inline>
const theme = localStorage.getItem('theme') || 'macchiato'; const theme = localStorage.getItem('theme') || 'sunset';
document.documentElement.setAttribute('data-theme', theme); document.documentElement.setAttribute('data-theme', theme);
</script> </script>
</head> </head>
@@ -64,7 +63,7 @@ function isActive(item: { href: string; exact?: boolean }) {
<input id="my-drawer-2" type="checkbox" class="drawer-toggle" /> <input id="my-drawer-2" type="checkbox" class="drawer-toggle" />
<div class="drawer-content flex flex-col h-full overflow-auto"> <div class="drawer-content flex flex-col h-full overflow-auto">
<!-- Mobile Navbar --> <!-- Mobile Navbar -->
<div class="navbar bg-base-100 sticky top-0 z-50 lg:hidden border-b border-base-200"> <div class="navbar bg-base-100 sticky top-0 z-50 lg:hidden border-b border-base-content/20">
<div class="flex-none"> <div class="flex-none">
<label for="my-drawer-2" aria-label="open sidebar" class="btn btn-square btn-ghost btn-sm"> <label for="my-drawer-2" aria-label="open sidebar" class="btn btn-square btn-ghost btn-sm">
<Icon name="bars-3" class="w-5 h-5" /> <Icon name="bars-3" class="w-5 h-5" />
@@ -87,7 +86,7 @@ function isActive(item: { href: string; exact?: boolean }) {
<div class="drawer-side z-50"> <div class="drawer-side z-50">
<label for="my-drawer-2" aria-label="close sidebar" class="drawer-overlay"></label> <label for="my-drawer-2" aria-label="close sidebar" class="drawer-overlay"></label>
<aside class="bg-base-200 min-h-full w-72 flex flex-col border-r border-base-300/40"> <aside class="bg-base-200 min-h-full w-72 flex flex-col border-r border-base-content/20">
<!-- Logo --> <!-- Logo -->
<div class="px-5 pt-5 pb-3"> <div class="px-5 pt-5 pb-3">
<a href="/dashboard" class="flex items-center gap-2.5 group"> <a href="/dashboard" class="flex items-center gap-2.5 group">
@@ -100,7 +99,7 @@ function isActive(item: { href: string; exact?: boolean }) {
{userMemberships.length > 0 && ( {userMemberships.length > 0 && (
<div class="px-4 pb-2"> <div class="px-4 pb-2">
<select <select
class="select select-sm w-full bg-base-300/40 border-base-300/60 focus:border-primary/50 focus:outline-none text-sm font-medium" class="select select-sm w-full bg-base-300 border-base-content/20 focus:border-primary focus:outline-none text-sm font-medium"
id="team-switcher" id="team-switcher"
aria-label="Switch team" aria-label="Switch team"
> >
@@ -135,8 +134,8 @@ function isActive(item: { href: string; exact?: boolean }) {
<a href={item.href} class:list={[ <a href={item.href} class:list={[
"rounded-lg gap-3 px-3 py-2.5 font-medium text-sm", "rounded-lg gap-3 px-3 py-2.5 font-medium text-sm",
isActive(item) isActive(item)
? "bg-primary/10 text-primary" ? "bg-primary text-primary-content"
: "text-base-content/70 hover:text-base-content hover:bg-base-300/50" : "text-base-content/70 hover:text-base-content hover:bg-base-300"
]}> ]}>
<Icon name={item.icon} class="w-[18px] h-[18px]" /> <Icon name={item.icon} class="w-[18px] h-[18px]" />
{item.label} {item.label}
@@ -153,8 +152,8 @@ function isActive(item: { href: string; exact?: boolean }) {
<a href="/admin" class:list={[ <a href="/admin" class:list={[
"rounded-lg gap-3 px-3 py-2.5 font-medium text-sm", "rounded-lg gap-3 px-3 py-2.5 font-medium text-sm",
Astro.url.pathname.startsWith("/admin") Astro.url.pathname.startsWith("/admin")
? "bg-primary/10 text-primary" ? "bg-primary text-primary-content"
: "text-base-content/70 hover:text-base-content hover:bg-base-300/50" : "text-base-content/70 hover:text-base-content hover:bg-base-300"
]}> ]}>
<Icon name="cog-6-tooth" class="w-[18px] h-[18px]" /> <Icon name="cog-6-tooth" class="w-[18px] h-[18px]" />
Site Admin Site Admin
@@ -166,25 +165,25 @@ function isActive(item: { href: string; exact?: boolean }) {
</nav> </nav>
<!-- Bottom Section --> <!-- Bottom Section -->
<div class="mt-auto border-t border-base-300/40"> <div class="mt-auto border-t border-base-content/20">
<div class="p-3"> <div class="p-3">
<a href="/dashboard/settings" class="flex items-center gap-3 rounded-lg p-2.5 hover:bg-base-300/40 group"> <a href="/dashboard/settings" class="flex items-center gap-3 rounded-lg p-2.5 hover:bg-base-300 group">
<Avatar name={user.name} /> <Avatar name={user.name} />
<div class="flex-1 min-w-0"> <div class="flex-1 min-w-0">
<div class="font-medium text-sm truncate">{user.name}</div> <div class="font-medium text-sm truncate">{user.name}</div>
<div class="text-xs text-base-content/50 truncate">{user.email}</div> <div class="text-xs text-base-content/60 truncate">{user.email}</div>
</div> </div>
<Icon name="chevron-right" class="w-4 h-4 text-base-content/30 group-hover:text-base-content/50" /> <Icon name="chevron-right" class="w-4 h-4 text-base-content/50 group-hover:text-base-content/70" />
</a> </a>
</div> </div>
<div class="flex items-center justify-between px-5 pb-2"> <div class="flex items-center justify-between px-5 pb-2">
<span class="text-xs text-base-content/40 font-medium">Theme</span> <span class="text-xs text-base-content/60 font-medium">Theme</span>
<ThemeToggle client:load /> <ThemeToggle client:load />
</div> </div>
<div class="px-3 pb-3"> <div class="px-3 pb-3">
<button id="logout-btn" type="button" class="btn btn-ghost btn-sm btn-block justify-start gap-2 text-base-content/60 hover:text-error hover:bg-error/10 font-medium"> <button id="logout-btn" type="button" class="btn btn-ghost btn-sm btn-block justify-start gap-2 text-base-content/60 hover:text-error hover:bg-base-300 font-medium">
<Icon name="arrow-right-on-rectangle" class="w-[18px] h-[18px]" /> <Icon name="arrow-right-on-rectangle" class="w-[18px] h-[18px]" />
Logout Logout
</button> </button>

View File

@@ -17,8 +17,8 @@ const { title } = Astro.props;
<link rel="icon" type="image/x-icon" href="/favicon.ico" /> <link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="generator" content={Astro.generator} /> <meta name="generator" content={Astro.generator} />
<title>{title}</title> <title>{title}</title>
<script> <script is:inline>
const theme = localStorage.getItem('theme') || 'macchiato'; const theme = localStorage.getItem('theme') || 'sunset';
document.documentElement.setAttribute('data-theme', theme); document.documentElement.setAttribute('data-theme', theme);
</script> </script>
</head> </head>

View File

@@ -32,7 +32,7 @@ const allUsers = await db.select().from(users).all();
title="Total Users" title="Total Users"
value={String(allUsers.length)} value={String(allUsers.length)}
description="Registered accounts" description="Registered accounts"
icon="heroicons:users" icon="users"
color="text-primary" color="text-primary"
/> />
</div> </div>
@@ -63,7 +63,7 @@ const allUsers = await db.select().from(users).all();
<!-- Users List --> <!-- Users List -->
<div class="card card-border bg-base-100"> <div class="card card-border bg-base-100">
<div class="card-body p-0"> <div class="card-body p-0">
<div class="px-4 py-3 border-b border-base-200"> <div class="px-4 py-3 border-b border-base-content/20">
<h2 class="text-sm font-semibold">All Users</h2> <h2 class="text-sm font-semibold">All Users</h2>
</div> </div>
<div class="overflow-x-auto"> <div class="overflow-x-auto">
@@ -93,7 +93,7 @@ const allUsers = await db.select().from(users).all();
<span class="badge badge-xs badge-ghost">No</span> <span class="badge badge-xs badge-ghost">No</span>
)} )}
</td> </td>
<td class="text-base-content/40">{u.createdAt?.toLocaleDateString() ?? 'N/A'}</td> <td class="text-base-content/60">{u.createdAt?.toLocaleDateString() ?? 'N/A'}</td>
</tr> </tr>
))} ))}
</tbody> </tbody>

View File

@@ -31,7 +31,7 @@ const allClients = await db.select()
<div class="card-body p-4 gap-1"> <div class="card-body p-4 gap-1">
<h2 class="font-semibold">{client.name}</h2> <h2 class="font-semibold">{client.name}</h2>
{client.email && <p class="text-sm text-base-content/60">{client.email}</p>} {client.email && <p class="text-sm text-base-content/60">{client.email}</p>}
<p class="text-xs text-base-content/40">Created {client.createdAt?.toLocaleDateString() ?? 'N/A'}</p> <p class="text-xs text-base-content/60">Created {client.createdAt?.toLocaleDateString() ?? 'N/A'}</p>
<div class="card-actions justify-end mt-3"> <div class="card-actions justify-end mt-3">
<a href={`/dashboard/clients/${client.id}`} class="btn btn-xs btn-ghost">View</a> <a href={`/dashboard/clients/${client.id}`} class="btn btn-xs btn-ghost">View</a>
<a href={`/dashboard/clients/${client.id}/edit`} class="btn btn-xs btn-primary">Edit</a> <a href={`/dashboard/clients/${client.id}/edit`} class="btn btn-xs btn-primary">Edit</a>
@@ -43,7 +43,7 @@ const allClients = await db.select()
{allClients.length === 0 && ( {allClients.length === 0 && (
<div class="flex flex-col items-center justify-center py-12 text-center"> <div class="flex flex-col items-center justify-center py-12 text-center">
<p class="text-base-content/50 text-sm mb-4">No clients yet</p> <p class="text-base-content/60 text-sm mb-4">No clients yet</p>
<a href="/dashboard/clients/new" class="btn btn-primary btn-sm">Add Your First Client</a> <a href="/dashboard/clients/new" class="btn btn-primary btn-sm">Add Your First Client</a>
</div> </div>
)} )}

View File

@@ -75,7 +75,7 @@ if (!client) return Astro.redirect('/dashboard/clients');
/> />
</fieldset> </fieldset>
<div class="divider text-xs text-base-content/40">Address Details</div> <div class="divider text-xs text-base-content/60">Address Details</div>
<fieldset class="fieldset"> <fieldset class="fieldset">
<legend class="fieldset-legend text-xs">Street Address (optional)</legend> <legend class="fieldset-legend text-xs">Street Address (optional)</legend>

View File

@@ -149,7 +149,7 @@ const totalEntriesCount = totalEntriesResult?.count || 0;
<div class="space-y-3"> <div class="space-y-3">
<div> <div>
<div class="text-xs text-base-content/40">Created</div> <div class="text-xs text-base-content/60">Created</div>
<div class="text-sm">{client.createdAt?.toLocaleDateString() ?? 'N/A'}</div> <div class="text-sm">{client.createdAt?.toLocaleDateString() ?? 'N/A'}</div>
</div> </div>
</div> </div>
@@ -160,7 +160,7 @@ const totalEntriesCount = totalEntriesResult?.count || 0;
<!-- Recent Activity --> <!-- Recent Activity -->
<div class="card card-border bg-base-100"> <div class="card card-border bg-base-100">
<div class="card-body p-0"> <div class="card-body p-0">
<div class="px-4 py-3 border-b border-base-200"> <div class="px-4 py-3 border-b border-base-content/20">
<h2 class="text-sm font-semibold">Recent Activity</h2> <h2 class="text-sm font-semibold">Recent Activity</h2>
</div> </div>
@@ -191,7 +191,7 @@ const totalEntriesCount = totalEntriesResult?.count || 0;
) : '-'} ) : '-'}
</td> </td>
<td class="text-base-content/60">{entryUser?.name || 'Unknown'}</td> <td class="text-base-content/60">{entryUser?.name || 'Unknown'}</td>
<td class="text-base-content/40">{entry.startTime.toLocaleDateString()}</td> <td class="text-base-content/60">{entry.startTime.toLocaleDateString()}</td>
<td class="font-mono">{formatTimeRange(entry.startTime, entry.endTime)}</td> <td class="font-mono">{formatTimeRange(entry.startTime, entry.endTime)}</td>
</tr> </tr>
))} ))}
@@ -199,13 +199,13 @@ const totalEntriesCount = totalEntriesResult?.count || 0;
</table> </table>
</div> </div>
) : ( ) : (
<div class="text-center py-8 text-base-content/40 text-sm"> <div class="text-center py-8 text-base-content/60 text-sm">
No time entries recorded for this client yet. No time entries recorded for this client yet.
</div> </div>
)} )}
{recentEntries.length > 0 && ( {recentEntries.length > 0 && (
<div class="flex justify-center py-3 border-t border-base-200"> <div class="flex justify-center py-3 border-t border-base-content/20">
<a href={`/dashboard/tracker?client=${client.id}`} class="btn btn-ghost btn-xs"> <a href={`/dashboard/tracker?client=${client.id}`} class="btn btn-ghost btn-xs">
View All Entries View All Entries
</a> </a>

View File

@@ -45,7 +45,7 @@ if (!user) return Astro.redirect('/login');
/> />
</fieldset> </fieldset>
<div class="divider text-xs text-base-content/40">Address Details</div> <div class="divider text-xs text-base-content/60">Address Details</div>
<fieldset class="fieldset"> <fieldset class="fieldset">
<legend class="fieldset-legend text-xs">Street Address (optional)</legend> <legend class="fieldset-legend text-xs">Street Address (optional)</legend>

View File

@@ -201,9 +201,9 @@ const hasMembership = userOrgs.length > 0;
{stats.recentEntries.length > 0 ? ( {stats.recentEntries.length > 0 ? (
<ul class="space-y-2 mt-3"> <ul class="space-y-2 mt-3">
{stats.recentEntries.map(({ entry, client, tag }) => ( {stats.recentEntries.map(({ entry, client, tag }) => (
<ColorDot client:load as="li" color={tag?.color || 'oklch(var(--p))'} borderColor class="p-2.5 rounded-lg bg-base-200/50 border-l-3 hover:bg-base-200 transition-colors"> <ColorDot client:load as="li" color={tag?.color || 'oklch(var(--p))'} borderColor class="p-2.5 rounded-lg bg-base-200 border-l-3 hover:bg-base-300 transition-colors">
<div class="font-medium text-sm">{client.name}</div> <div class="font-medium text-sm">{client.name}</div>
<div class="text-xs text-base-content/50 mt-0.5 flex flex-wrap gap-2 items-center"> <div class="text-xs text-base-content/60 mt-0.5 flex flex-wrap gap-2 items-center">
<span class="flex gap-1 flex-wrap"> <span class="flex gap-1 flex-wrap">
{tag ? ( {tag ? (
<span class="badge badge-xs badge-outline">{tag.name}</span> <span class="badge badge-xs badge-outline">{tag.name}</span>
@@ -216,8 +216,8 @@ const hasMembership = userOrgs.length > 0;
</ul> </ul>
) : ( ) : (
<div class="flex flex-col items-center justify-center py-6 text-center mt-3"> <div class="flex flex-col items-center justify-center py-6 text-center mt-3">
<Icon name="clock" class="w-10 h-10 text-base-content/15 mb-2" /> <Icon name="clock" class="w-10 h-10 text-base-content/30 mb-2" />
<p class="text-base-content/40 text-sm">No recent time entries</p> <p class="text-base-content/60 text-sm">No recent time entries</p>
</div> </div>
)} )}
</div> </div>

View File

@@ -113,10 +113,10 @@ const isDraft = invoice.status === 'draft';
</form> </form>
)} )}
<div class="dropdown dropdown-end"> <div class="dropdown dropdown-end">
<div role="button" tabindex="0" class="btn btn-square btn-ghost btn-sm border border-base-200"> <div role="button" tabindex="0" class="btn btn-square btn-ghost btn-sm border border-base-content/20">
<Icon name="ellipsis-horizontal" class="w-4 h-4" /> <Icon name="ellipsis-horizontal" class="w-4 h-4" />
</div> </div>
<ul tabindex="0" class="dropdown-content z-1 menu p-2 bg-base-100 rounded-box w-52 border border-base-200"> <ul tabindex="0" class="dropdown-content z-1 menu p-2 bg-base-100 rounded-box w-52 border border-base-content/20">
<li> <li>
<a href={`/dashboard/invoices/${invoice.id}/edit`}> <a href={`/dashboard/invoices/${invoice.id}/edit`}>
<Icon name="pencil-square" class="w-4 h-4" /> <Icon name="pencil-square" class="w-4 h-4" />
@@ -174,7 +174,7 @@ const isDraft = invoice.status === 'draft';
)} )}
</div> </div>
<div class="text-right"> <div class="text-right">
<div class="text-4xl font-light text-base-content/30 uppercase tracking-widest mb-4"> <div class="text-4xl font-light text-base-content/50 uppercase tracking-widest mb-4">
{invoice.type} {invoice.type}
</div> </div>
<div class="grid grid-cols-2 gap-x-4 gap-y-1 text-sm"> <div class="grid grid-cols-2 gap-x-4 gap-y-1 text-sm">
@@ -190,7 +190,7 @@ const isDraft = invoice.status === 'draft';
<!-- Bill To --> <!-- Bill To -->
<div class="mb-12"> <div class="mb-12">
<div class="text-xs font-bold uppercase tracking-wider text-base-content/40 mb-2">Bill To</div> <div class="text-xs font-bold uppercase tracking-wider text-base-content/60 mb-2">Bill To</div>
{client ? ( {client ? (
<div> <div>
<div class="font-bold text-lg">{client.name}</div> <div class="font-bold text-lg">{client.name}</div>
@@ -209,7 +209,7 @@ const isDraft = invoice.status === 'draft';
)} )}
</div> </div>
) : ( ) : (
<div class="italic text-base-content/40">Client deleted</div> <div class="italic text-base-content/60">Client deleted</div>
)} )}
</div> </div>
@@ -218,7 +218,7 @@ const isDraft = invoice.status === 'draft';
<div class="overflow-x-auto"> <div class="overflow-x-auto">
<table class="w-full min-w-150"> <table class="w-full min-w-150">
<thead> <thead>
<tr class="border-b-2 border-base-200 text-left text-xs font-bold uppercase tracking-wider text-base-content/40"> <tr class="border-b-2 border-base-content/20 text-left text-xs font-bold uppercase tracking-wider text-base-content/60">
<th class="py-3">Description</th> <th class="py-3">Description</th>
<th class="py-3 text-right w-24">Qty</th> <th class="py-3 text-right w-24">Qty</th>
<th class="py-3 text-right w-32">Price</th> <th class="py-3 text-right w-32">Price</th>
@@ -226,7 +226,7 @@ const isDraft = invoice.status === 'draft';
{isDraft && <th class="py-3 w-10"></th>} {isDraft && <th class="py-3 w-10"></th>}
</tr> </tr>
</thead> </thead>
<tbody class="divide-y divide-base-200"> <tbody class="divide-y divide-base-content/20">
{items.map(item => ( {items.map(item => (
<tr> <tr>
<td class="py-4">{item.description}</td> <td class="py-4">{item.description}</td>
@@ -237,7 +237,7 @@ const isDraft = invoice.status === 'draft';
<td class="py-4 text-right"> <td class="py-4 text-right">
<form method="POST" action={`/api/invoices/${invoice.id}/items/delete`}> <form method="POST" action={`/api/invoices/${invoice.id}/items/delete`}>
<input type="hidden" name="itemId" value={item.id} /> <input type="hidden" name="itemId" value={item.id} />
<button type="submit" class="btn btn-ghost btn-xs btn-square text-error opacity-50 hover:opacity-100"> <button type="submit" class="btn btn-ghost btn-xs btn-square text-error opacity-70 hover:opacity-100">
<Icon name="trash" class="w-4 h-4" /> <Icon name="trash" class="w-4 h-4" />
</button> </button>
</form> </form>
@@ -247,7 +247,7 @@ const isDraft = invoice.status === 'draft';
))} ))}
{items.length === 0 && ( {items.length === 0 && (
<tr> <tr>
<td colspan={isDraft ? 5 : 4} class="py-8 text-center text-base-content/40 italic"> <td colspan={isDraft ? 5 : 4} class="py-8 text-center text-base-content/60 italic">
No items added yet. No items added yet.
</td> </td>
</tr> </tr>
@@ -266,7 +266,7 @@ const isDraft = invoice.status === 'draft';
</ModalButton> </ModalButton>
</div> </div>
<form method="POST" action={`/api/invoices/${invoice.id}/items/add`} class="bg-base-200/50 p-4 rounded-lg mb-8 border border-base-200"> <form method="POST" action={`/api/invoices/${invoice.id}/items/add`} class="bg-base-200 p-4 rounded-lg mb-8 border border-base-content/20">
<h4 class="text-xs font-semibold mb-3">Add Item</h4> <h4 class="text-xs font-semibold mb-3">Add Item</h4>
<div class="grid grid-cols-1 sm:grid-cols-12 gap-3 items-end"> <div class="grid grid-cols-1 sm:grid-cols-12 gap-3 items-end">
<div class="sm:col-span-6"> <div class="sm:col-span-6">
@@ -329,8 +329,8 @@ const isDraft = invoice.status === 'draft';
<!-- Notes --> <!-- Notes -->
{invoice.notes && ( {invoice.notes && (
<div class="mt-12 pt-8 border-t border-base-200"> <div class="mt-12 pt-8 border-t border-base-content/20">
<div class="text-xs font-bold uppercase tracking-wider text-base-content/40 mb-2">Notes</div> <div class="text-xs font-bold uppercase tracking-wider text-base-content/60 mb-2">Notes</div>
<div class="text-sm whitespace-pre-wrap opacity-80">{invoice.notes}</div> <div class="text-sm whitespace-pre-wrap opacity-80">{invoice.notes}</div>
</div> </div>
)} )}

View File

@@ -211,8 +211,8 @@ const getStatusColor = (status: string) => {
<div class="card card-border bg-base-100"> <div class="card card-border bg-base-100">
<div class="card-body p-0"> <div class="card-body p-0">
<div class="px-4 py-3 border-b border-base-200"> <div class="px-4 py-3 border-b border-base-content/20">
<p class="text-xs text-base-content/50"> <p class="text-xs text-base-content/60">
Showing <span class="font-semibold text-base-content">{allInvoices.length}</span> Showing <span class="font-semibold text-base-content">{allInvoices.length}</span>
{allInvoices.length === 1 ? 'result' : 'results'} {allInvoices.length === 1 ? 'result' : 'results'}
{selectedYear === 'current' ? ` for ${currentYear} (year to date)` : ` for ${selectedYear}`} {selectedYear === 'current' ? ` for ${currentYear} (year to date)` : ` for ${selectedYear}`}
@@ -235,7 +235,7 @@ const getStatusColor = (status: string) => {
<tbody> <tbody>
{allInvoices.length === 0 ? ( {allInvoices.length === 0 ? (
<tr> <tr>
<td colspan="8" class="text-center py-8 text-base-content/50 text-sm"> <td colspan="8" class="text-center py-8 text-base-content/60 text-sm">
No invoices or quotes found. Create one to get started. No invoices or quotes found. Create one to get started.
</td> </td>
</tr> </tr>
@@ -251,7 +251,7 @@ const getStatusColor = (status: string) => {
{client ? ( {client ? (
<div class="font-medium">{client.name}</div> <div class="font-medium">{client.name}</div>
) : ( ) : (
<span class="text-base-content/40 italic">Deleted Client</span> <span class="text-base-content/60 italic">Deleted Client</span>
)} )}
</td> </td>
<td>{invoice.issueDate.toLocaleDateString()}</td> <td>{invoice.issueDate.toLocaleDateString()}</td>
@@ -272,7 +272,7 @@ const getStatusColor = (status: string) => {
<div role="button" tabindex="0" class="btn btn-ghost btn-xs btn-square"> <div role="button" tabindex="0" class="btn btn-ghost btn-xs btn-square">
<Icon name="ellipsis-vertical" class="w-4 h-4" /> <Icon name="ellipsis-vertical" class="w-4 h-4" />
</div> </div>
<ul tabindex="0" class="dropdown-content menu p-2 bg-base-100 rounded-box w-52 border border-base-200 z-100"> <ul tabindex="0" class="dropdown-content menu p-2 bg-base-100 rounded-box w-52 border border-base-content/20 z-100">
<li> <li>
<a href={`/dashboard/invoices/${invoice.id}`}> <a href={`/dashboard/invoices/${invoice.id}`}>
<Icon name="eye" class="w-4 h-4" /> <Icon name="eye" class="w-4 h-4" />
@@ -306,7 +306,7 @@ const getStatusColor = (status: string) => {
<li> <li>
<ConfirmForm client:load message="Are you sure? This action cannot be undone." action="/api/invoices/delete" class="w-full"> <ConfirmForm client:load message="Are you sure? This action cannot be undone." action="/api/invoices/delete" class="w-full">
<input type="hidden" name="id" value={invoice.id} /> <input type="hidden" name="id" value={invoice.id} />
<button type="submit" class="w-full justify-start text-error hover:bg-error/10"> <button type="submit" class="w-full justify-start text-error hover:bg-base-300">
<Icon name="trash" class="w-4 h-4" /> <Icon name="trash" class="w-4 h-4" />
Delete Delete
</button> </button>

View File

@@ -104,11 +104,11 @@ const defaultDueDate = nextMonth.toISOString().split('T')[0];
<fieldset class="fieldset"> <fieldset class="fieldset">
<legend class="fieldset-legend text-xs">Document Type</legend> <legend class="fieldset-legend text-xs">Document Type</legend>
<div class="flex gap-3"> <div class="flex gap-3">
<label class="label cursor-pointer justify-start gap-2 border border-base-200 rounded-lg p-3 flex-1 hover:border-primary has-checked:border-primary has-checked:bg-primary/5 transition-all font-medium text-sm" for="document-type-invoice"> <label class="label cursor-pointer justify-start gap-2 border border-base-content/20 rounded-lg p-3 flex-1 hover:border-primary has-checked:border-primary has-checked:bg-base-200 transition-all font-medium text-sm" for="document-type-invoice">
<input type="radio" id="document-type-invoice" name="type" value="invoice" class="radio radio-primary radio-sm" checked /> <input type="radio" id="document-type-invoice" name="type" value="invoice" class="radio radio-primary radio-sm" checked />
Invoice Invoice
</label> </label>
<label class="label cursor-pointer justify-start gap-2 border border-base-200 rounded-lg p-3 flex-1 hover:border-primary has-checked:border-primary has-checked:bg-primary/5 transition-all font-medium text-sm" for="document-type-quote"> <label class="label cursor-pointer justify-start gap-2 border border-base-content/20 rounded-lg p-3 flex-1 hover:border-primary has-checked:border-primary has-checked:bg-base-200 transition-all font-medium text-sm" for="document-type-quote">
<input type="radio" id="document-type-quote" name="type" value="quote" class="radio radio-primary radio-sm" /> <input type="radio" id="document-type-quote" name="type" value="quote" class="radio radio-primary radio-sm" />
Quote / Estimate Quote / Estimate
</label> </label>

View File

@@ -394,11 +394,11 @@ function getTimeRangeLabel(range: string) {
<div class="stat-title text-xs">Total Invoices</div> <div class="stat-title text-xs">Total Invoices</div>
<div class="stat-value text-2xl">{invoiceStats.total}</div> <div class="stat-value text-2xl">{invoiceStats.total}</div>
</div> </div>
<div class="stat bg-success/10 rounded-lg"> <div class="stat bg-base-200 rounded-lg">
<div class="stat-title text-xs">Paid</div> <div class="stat-title text-xs">Paid</div>
<div class="stat-value text-2xl text-success">{invoiceStats.paid}</div> <div class="stat-value text-2xl text-success">{invoiceStats.paid}</div>
</div> </div>
<div class="stat bg-info/10 rounded-lg"> <div class="stat bg-base-200 rounded-lg">
<div class="stat-title text-xs">Sent</div> <div class="stat-title text-xs">Sent</div>
<div class="stat-value text-2xl text-info">{invoiceStats.sent}</div> <div class="stat-value text-2xl text-info">{invoiceStats.sent}</div>
</div> </div>
@@ -430,15 +430,15 @@ function getTimeRangeLabel(range: string) {
<div class="stat-title text-xs">Total Quotes</div> <div class="stat-title text-xs">Total Quotes</div>
<div class="stat-value text-2xl">{quoteStats.total}</div> <div class="stat-value text-2xl">{quoteStats.total}</div>
</div> </div>
<div class="stat bg-success/10 rounded-lg"> <div class="stat bg-base-200 rounded-lg">
<div class="stat-title text-xs">Accepted</div> <div class="stat-title text-xs">Accepted</div>
<div class="stat-value text-2xl text-success">{quoteStats.accepted}</div> <div class="stat-value text-2xl text-success">{quoteStats.accepted}</div>
</div> </div>
<div class="stat bg-info/10 rounded-lg"> <div class="stat bg-base-200 rounded-lg">
<div class="stat-title text-xs">Pending</div> <div class="stat-title text-xs">Pending</div>
<div class="stat-value text-2xl text-info">{quoteStats.sent}</div> <div class="stat-value text-2xl text-info">{quoteStats.sent}</div>
</div> </div>
<div class="stat bg-error/10 rounded-lg"> <div class="stat bg-base-200 rounded-lg">
<div class="stat-title text-xs">Declined</div> <div class="stat-title text-xs">Declined</div>
<div class="stat-value text-2xl text-error">{quoteStats.declined}</div> <div class="stat-value text-2xl text-error">{quoteStats.declined}</div>
</div> </div>
@@ -591,7 +591,7 @@ function getTimeRangeLabel(range: string) {
<td> <td>
<div> <div>
<div class="font-medium">{stat.member.name}</div> <div class="font-medium">{stat.member.name}</div>
<div class="text-xs text-base-content/40">{stat.member.email}</div> <div class="text-xs text-base-content/60">{stat.member.email}</div>
</div> </div>
</td> </td>
<td class="font-mono text-sm">{formatDuration(stat.totalTime)}</td> <td class="font-mono text-sm">{formatDuration(stat.totalTime)}</td>
@@ -738,7 +738,7 @@ function getTimeRangeLabel(range: string) {
<tr> <tr>
<td class="whitespace-nowrap"> <td class="whitespace-nowrap">
{e.entry.startTime.toLocaleDateString()}<br/> {e.entry.startTime.toLocaleDateString()}<br/>
<span class="text-xs text-base-content/40"> <span class="text-xs text-base-content/60">
{e.entry.startTime.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})} {e.entry.startTime.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}
</span> </span>
</td> </td>
@@ -753,7 +753,7 @@ function getTimeRangeLabel(range: string) {
<span>{e.tag.name}</span> <span>{e.tag.name}</span>
</div> </div>
) : ( ) : (
<span class="text-base-content/30">-</span> <span class="text-base-content/60">-</span>
)} )}
</td> </td>
<td class="text-base-content/60">{e.entry.description || '-'}</td> <td class="text-base-content/60">{e.entry.description || '-'}</td>
@@ -770,9 +770,9 @@ function getTimeRangeLabel(range: string) {
</div> </div>
) : ( ) : (
<div class="flex flex-col items-center justify-center py-10 text-center"> <div class="flex flex-col items-center justify-center py-10 text-center">
<Icon name="inbox" class="w-12 h-12 text-base-content/15 mb-3" /> <Icon name="inbox" class="w-12 h-12 text-base-content/30 mb-3" />
<h3 class="text-base font-semibold mb-1">No time entries found</h3> <h3 class="text-base font-semibold mb-1">No time entries found</h3>
<p class="text-base-content/50 text-sm mb-4">Try adjusting your filters or select a different time range.</p> <p class="text-base-content/60 text-sm mb-4">Try adjusting your filters or select a different time range.</p>
<a href="/dashboard/tracker" class="btn btn-primary btn-sm"> <a href="/dashboard/tracker" class="btn btn-primary btn-sm">
<Icon name="play" class="w-4 h-4" /> <Icon name="play" class="w-4 h-4" />
Start Tracking Time Start Tracking Time

View File

@@ -77,11 +77,11 @@ const userPasskeys = await db.select()
</h2> </h2>
<div class="space-y-3"> <div class="space-y-3">
<div class="flex flex-col sm:flex-row sm:justify-between py-3 border-b border-base-200 gap-2 sm:gap-0"> <div class="flex flex-col sm:flex-row sm:justify-between py-3 border-b border-base-content/20 gap-2 sm:gap-0">
<span class="text-base-content/60 text-sm">Account ID</span> <span class="text-base-content/60 text-sm">Account ID</span>
<span class="font-mono text-xs break-all">{user.id}</span> <span class="font-mono text-xs break-all">{user.id}</span>
</div> </div>
<div class="flex flex-col sm:flex-row sm:justify-between py-3 border-b border-base-200 gap-2 sm:gap-0"> <div class="flex flex-col sm:flex-row sm:justify-between py-3 border-b border-base-content/20 gap-2 sm:gap-0">
<span class="text-base-content/60 text-sm">Email</span> <span class="text-base-content/60 text-sm">Email</span>
<span class="text-sm break-all">{user.email}</span> <span class="text-sm break-all">{user.email}</span>
</div> </div>

View File

@@ -82,7 +82,7 @@ const isAdmin = currentUserMember?.member.role === 'owner' || currentUserMember?
{member.role} {member.role}
</span> </span>
</td> </td>
<td class="text-base-content/40">{member.joinedAt?.toLocaleDateString() ?? 'N/A'}</td> <td class="text-base-content/60">{member.joinedAt?.toLocaleDateString() ?? 'N/A'}</td>
{isAdmin && ( {isAdmin && (
<td> <td>
{teamUser.id !== user.id && member.role !== 'owner' && ( {teamUser.id !== user.id && member.role !== 'owner' && (
@@ -90,7 +90,7 @@ const isAdmin = currentUserMember?.member.role === 'owner' || currentUserMember?
<div role="button" tabindex="0" class="btn btn-ghost btn-xs btn-square"> <div role="button" tabindex="0" class="btn btn-ghost btn-xs btn-square">
<Icon name="ellipsis-vertical" class="w-4 h-4" /> <Icon name="ellipsis-vertical" class="w-4 h-4" />
</div> </div>
<ul tabindex="0" class="dropdown-content z-1 menu p-2 bg-base-100 rounded-box w-52 border border-base-200"> <ul tabindex="0" class="dropdown-content z-1 menu p-2 bg-base-100 rounded-box w-52 border border-base-content/20">
<li> <li>
<form method="POST" action={`/api/team/change-role`}> <form method="POST" action={`/api/team/change-role`}>
<input type="hidden" name="userId" value={teamUser.id} /> <input type="hidden" name="userId" value={teamUser.id} />

View File

@@ -56,7 +56,7 @@ if (!isAdmin) return Astro.redirect('/dashboard/team');
<option value="member">Member</option> <option value="member">Member</option>
<option value="admin">Admin</option> <option value="admin">Admin</option>
</select> </select>
<p class="text-xs text-base-content/40 mt-1">Members can track time. Admins can manage team and clients.</p> <p class="text-xs text-base-content/60 mt-1">Members can track time. Admins can manage team and clients.</p>
</fieldset> </fieldset>
<div class="flex justify-end gap-2 mt-4"> <div class="flex justify-end gap-2 mt-4">

View File

@@ -73,7 +73,7 @@ const successType = url.searchParams.get('success');
<legend class="fieldset-legend text-xs">Team Logo</legend> <legend class="fieldset-legend text-xs">Team Logo</legend>
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<div class="avatar placeholder"> <div class="avatar placeholder">
<div class="bg-base-200 text-neutral-content rounded-xl w-20 border border-base-200 flex items-center justify-center overflow-hidden"> <div class="bg-base-200 text-neutral-content rounded-xl w-20 border border-base-content/20 flex items-center justify-center overflow-hidden">
{organization.logoUrl ? ( {organization.logoUrl ? (
<img <img
src={organization.logoUrl} src={organization.logoUrl}
@@ -83,7 +83,7 @@ const successType = url.searchParams.get('success');
) : ( ) : (
<Icon <Icon
name="photo" name="photo"
class="w-6 h-6 opacity-40 text-base-content" class="w-6 h-6 opacity-70 text-base-content"
/> />
)} )}
</div> </div>
@@ -95,7 +95,7 @@ const successType = url.searchParams.get('success');
accept="image/png, image/jpeg" accept="image/png, image/jpeg"
class="file-input file-input-bordered file-input-sm w-full max-w-xs" class="file-input file-input-bordered file-input-sm w-full max-w-xs"
/> />
<div class="text-xs text-base-content/40 mt-1"> <div class="text-xs text-base-content/60 mt-1">
Upload a company logo (PNG, JPG). Will be displayed on invoices and quotes. Upload a company logo (PNG, JPG). Will be displayed on invoices and quotes.
</div> </div>
</div> </div>
@@ -113,10 +113,10 @@ const successType = url.searchParams.get('success');
class="input w-full" class="input w-full"
required required
/> />
<p class="text-xs text-base-content/40 mt-1">This name is visible to all team members</p> <p class="text-xs text-base-content/60 mt-1">This name is visible to all team members</p>
</fieldset> </fieldset>
<div class="divider text-xs text-base-content/40 my-2">Address Information</div> <div class="divider text-xs text-base-content/60 my-2">Address Information</div>
<fieldset class="fieldset"> <fieldset class="fieldset">
<legend class="fieldset-legend text-xs">Street Address</legend> <legend class="fieldset-legend text-xs">Street Address</legend>
@@ -182,7 +182,7 @@ const successType = url.searchParams.get('success');
</fieldset> </fieldset>
</div> </div>
<div class="divider text-xs text-base-content/40 my-2">Defaults</div> <div class="divider text-xs text-base-content/60 my-2">Defaults</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-3"> <div class="grid grid-cols-1 md:grid-cols-2 gap-3">
<fieldset class="fieldset"> <fieldset class="fieldset">
@@ -216,7 +216,7 @@ const successType = url.searchParams.get('success');
</div> </div>
<div class="flex flex-col sm:flex-row justify-between items-center gap-3 mt-4"> <div class="flex flex-col sm:flex-row justify-between items-center gap-3 mt-4">
<span class="text-xs text-base-content/40 text-center sm:text-left"> <span class="text-xs text-base-content/60 text-center sm:text-left">
Address information appears on invoices and quotes Address information appears on invoices and quotes
</span> </span>
@@ -280,7 +280,7 @@ const successType = url.searchParams.get('success');
{tag.rate ? ( {tag.rate ? (
<span class="font-mono text-sm">{new Intl.NumberFormat('en-US', { style: 'currency', currency: organization.defaultCurrency || 'USD' }).format(tag.rate / 100)}</span> <span class="font-mono text-sm">{new Intl.NumberFormat('en-US', { style: 'currency', currency: organization.defaultCurrency || 'USD' }).format(tag.rate / 100)}</span>
) : ( ) : (
<span class="text-base-content/40 text-xs italic">No rate</span> <span class="text-base-content/60 text-xs italic">No rate</span>
)} )}
</td> </td>
<td> <td>
@@ -315,7 +315,7 @@ const successType = url.searchParams.get('success');
<fieldset class="fieldset mb-4"> <fieldset class="fieldset mb-4">
<legend class="fieldset-legend text-xs">Hourly Rate (cents)</legend> <legend class="fieldset-legend text-xs">Hourly Rate (cents)</legend>
<input type="number" name="rate" value={tag.rate || 0} min="0" class="input w-full" /> <input type="number" name="rate" value={tag.rate || 0} min="0" class="input w-full" />
<p class="text-xs text-base-content/40 mt-1">Enter rate in cents (e.g. 5000 = $50.00)</p> <p class="text-xs text-base-content/60 mt-1">Enter rate in cents (e.g. 5000 = $50.00)</p>
</fieldset> </fieldset>
<div class="modal-action"> <div class="modal-action">
<ModalButton client:load modalId={`edit_tag_modal_${tag.id}`} action="close" class="btn btn-sm">Cancel</ModalButton> <ModalButton client:load modalId={`edit_tag_modal_${tag.id}`} action="close" class="btn btn-sm">Cancel</ModalButton>
@@ -353,7 +353,7 @@ const successType = url.searchParams.get('success');
<fieldset class="fieldset mb-4"> <fieldset class="fieldset mb-4">
<legend class="fieldset-legend text-xs">Hourly Rate (cents)</legend> <legend class="fieldset-legend text-xs">Hourly Rate (cents)</legend>
<input type="number" name="rate" value="0" min="0" class="input w-full" /> <input type="number" name="rate" value="0" min="0" class="input w-full" />
<p class="text-xs text-base-content/40 mt-1">Enter rate in cents (e.g. 5000 = $50.00)</p> <p class="text-xs text-base-content/60 mt-1">Enter rate in cents (e.g. 5000 = $50.00)</p>
</fieldset> </fieldset>
<div class="modal-action"> <div class="modal-action">
<ModalButton client:load modalId="new_tag_modal" action="close" class="btn btn-sm">Cancel</ModalButton> <ModalButton client:load modalId="new_tag_modal" action="close" class="btn btn-sm">Cancel</ModalButton>

View File

@@ -144,9 +144,9 @@ const paginationPages = getPaginationPages(page, totalPages);
<h1 class="text-2xl font-extrabold tracking-tight mb-6">Time Tracker</h1> <h1 class="text-2xl font-extrabold tracking-tight mb-6">Time Tracker</h1>
<!-- Tabs for Timer and Manual Entry --> <!-- Tabs for Timer and Manual Entry -->
<div class="tabs tabs-lift mb-6"> <div class="tabs tabs-border mb-6">
<input type="radio" name="tracker_tabs" class="tab" aria-label="Timer" checked="checked" /> <input type="radio" name="tracker_tabs" class="tab" aria-label="Timer" checked="checked" />
<div class="tab-content bg-base-100 border-base-300 p-6"> <div class="tab-content border-base-content/20 p-6">
{allClients.length === 0 ? ( {allClients.length === 0 ? (
<div class="alert alert-warning flex flex-col sm:flex-row items-center gap-4"> <div class="alert alert-warning flex flex-col sm:flex-row items-center gap-4">
<Icon name="exclamation-triangle" class="stroke-current shrink-0 h-6 w-6" /> <Icon name="exclamation-triangle" class="stroke-current shrink-0 h-6 w-6" />
@@ -169,7 +169,7 @@ const paginationPages = getPaginationPages(page, totalPages);
</div> </div>
<input type="radio" name="tracker_tabs" class="tab" aria-label="Manual Entry" /> <input type="radio" name="tracker_tabs" class="tab" aria-label="Manual Entry" />
<div class="tab-content bg-base-100 border-base-300 p-6"> <div class="tab-content border-base-content/20 p-6">
{allClients.length === 0 ? ( {allClients.length === 0 ? (
<div class="alert alert-warning flex flex-col sm:flex-row items-center gap-4"> <div class="alert alert-warning flex flex-col sm:flex-row items-center gap-4">
<Icon name="exclamation-triangle" class="stroke-current shrink-0 h-6 w-6" /> <Icon name="exclamation-triangle" class="stroke-current shrink-0 h-6 w-6" />
@@ -314,7 +314,7 @@ const paginationPages = getPaginationPages(page, totalPages);
<td>{entryUser?.name || 'Unknown'}</td> <td>{entryUser?.name || 'Unknown'}</td>
<td class="whitespace-nowrap"> <td class="whitespace-nowrap">
{entry.startTime.toLocaleDateString()}<br/> {entry.startTime.toLocaleDateString()}<br/>
<span class="text-xs text-base-content/40"> <span class="text-xs text-base-content/60">
{entry.startTime.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})} {entry.startTime.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}
</span> </span>
</td> </td>
@@ -322,7 +322,7 @@ const paginationPages = getPaginationPages(page, totalPages);
{entry.endTime ? ( {entry.endTime ? (
<> <>
{entry.endTime.toLocaleDateString()}<br/> {entry.endTime.toLocaleDateString()}<br/>
<span class="text-xs text-base-content/40"> <span class="text-xs text-base-content/60">
{entry.endTime.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})} {entry.endTime.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}
</span> </span>
</> </>

View File

@@ -31,11 +31,11 @@ if (Astro.locals.user) {
</div> </div>
<!-- Features --> <!-- Features -->
<div class="bg-base-200/50 border-t border-base-200 px-4 py-16 sm:py-20"> <div class="bg-base-200 border-t border-base-content/20 px-4 py-16 sm:py-20">
<div class="max-w-4xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-6"> <div class="max-w-4xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="card bg-base-100 card-border"> <div class="card bg-base-100 card-border">
<div class="card-body"> <div class="card-body">
<div class="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center mb-2"> <div class="w-10 h-10 rounded-lg bg-base-200 flex items-center justify-center mb-2">
<Icon name="bolt" class="h-5 w-5 text-primary" /> <Icon name="bolt" class="h-5 w-5 text-primary" />
</div> </div>
<h3 class="card-title text-base">Lightning Fast</h3> <h3 class="card-title text-base">Lightning Fast</h3>
@@ -44,7 +44,7 @@ if (Astro.locals.user) {
</div> </div>
<div class="card bg-base-100 card-border"> <div class="card bg-base-100 card-border">
<div class="card-body"> <div class="card-body">
<div class="w-10 h-10 rounded-lg bg-info/10 flex items-center justify-center mb-2"> <div class="w-10 h-10 rounded-lg bg-base-200 flex items-center justify-center mb-2">
<Icon name="chart-bar" class="h-5 w-5 text-info" /> <Icon name="chart-bar" class="h-5 w-5 text-info" />
</div> </div>
<h3 class="card-title text-base">Detailed Reports</h3> <h3 class="card-title text-base">Detailed Reports</h3>
@@ -53,7 +53,7 @@ if (Astro.locals.user) {
</div> </div>
<div class="card bg-base-100 card-border"> <div class="card bg-base-100 card-border">
<div class="card-body"> <div class="card-body">
<div class="w-10 h-10 rounded-lg bg-accent/10 flex items-center justify-center mb-2"> <div class="w-10 h-10 rounded-lg bg-base-200 flex items-center justify-center mb-2">
<Icon name="users" class="h-5 w-5 text-accent" /> <Icon name="users" class="h-5 w-5 text-accent" />
</div> </div>
<h3 class="card-title text-base">Team Collaboration</h3> <h3 class="card-title text-base">Team Collaboration</h3>

View File

@@ -18,7 +18,7 @@ const errorMessage =
<Layout title="Login - Chronus"> <Layout title="Login - Chronus">
<div class="flex justify-center items-center flex-1 bg-base-100"> <div class="flex justify-center items-center flex-1 bg-base-100">
<div class="card card-border bg-base-100 w-full max-w-sm mx-4"> <div class="card card-border bg-base-200 w-full max-w-sm mx-4">
<div class="card-body gap-0"> <div class="card-body gap-0">
<img src="/logo.webp" alt="Chronus" class="h-14 w-14 mx-auto mb-3" /> <img src="/logo.webp" alt="Chronus" class="h-14 w-14 mx-auto mb-3" />
<h2 class="text-2xl font-extrabold tracking-tight text-center">Welcome Back</h2> <h2 class="text-2xl font-extrabold tracking-tight text-center">Welcome Back</h2>

View File

@@ -34,7 +34,7 @@ const errorMessage =
<Layout title="Sign Up - Chronus"> <Layout title="Sign Up - Chronus">
<div class="flex justify-center items-center flex-1 bg-base-100"> <div class="flex justify-center items-center flex-1 bg-base-100">
<div class="card card-border bg-base-100 w-full max-w-sm mx-4"> <div class="card card-border bg-base-200 w-full max-w-sm mx-4">
<div class="card-body gap-0"> <div class="card-body gap-0">
<img src="/logo.webp" alt="Chronus" class="h-14 w-14 mx-auto mb-3" /> <img src="/logo.webp" alt="Chronus" class="h-14 w-14 mx-auto mb-3" />
<h2 class="text-2xl font-extrabold tracking-tight text-center">Create Account</h2> <h2 class="text-2xl font-extrabold tracking-tight text-center">Create Account</h2>

View File

@@ -52,8 +52,6 @@ export const GET: APIRoute = async ({ params }) => {
case ".gif": case ".gif":
contentType = "image/gif"; contentType = "image/gif";
break; break;
// SVG excluded to prevent stored XSS
// WebP omitted — not supported in PDF generation
} }
return new Response(fileContent, { return new Response(fileContent, {

View File

@@ -305,7 +305,6 @@ export function createInvoiceDocument(props: InvoiceDocumentProps) {
if (organization.logoUrl) { if (organization.logoUrl) {
try { try {
let logoPath; let logoPath;
// Handle uploads directory which might be external to public/
if (organization.logoUrl.startsWith("/uploads/")) { if (organization.logoUrl.startsWith("/uploads/")) {
const dataDir = process.env.DATA_DIR const dataDir = process.env.DATA_DIR
? process.env.DATA_DIR ? process.env.DATA_DIR

View File

@@ -1,9 +1,7 @@
@import "tailwindcss"; @import "tailwindcss";
@plugin "daisyui" { @plugin "daisyui" {
themes: false; themes: sunset --default, winter;
} }
@plugin "./theme-dark.ts";
@plugin "./theme-light.ts";
/* Smoother transitions globally */ /* Smoother transitions globally */
@layer base { @layer base {
@@ -15,3 +13,8 @@
transition: none; transition: none;
} }
} }
/* Override DaisyUI card-border to use a visible border in both themes */
.card-border {
border-color: color-mix(in oklab, var(--color-base-content) 20%, transparent);
}

View File

@@ -1,9 +0,0 @@
import { createCatppuccinPlugin } from "@catppuccin/daisyui";
export default createCatppuccinPlugin(
"macchiato",
{},
{
default: true,
},
);

View File

@@ -1,9 +0,0 @@
import { createCatppuccinPlugin } from "@catppuccin/daisyui";
export default createCatppuccinPlugin(
"latte",
{},
{
default: false,
},
);

View File

@@ -34,7 +34,6 @@ export async function recalculateInvoiceTotals(invoiceId: string) {
if (discountType === "percentage") { if (discountType === "percentage") {
discountAmount = Math.round(subtotal * (discountValue / 100)); discountAmount = Math.round(subtotal * (discountValue / 100));
} else { } else {
// Fixed amount is assumed to be in cents
discountAmount = Math.round(discountValue); discountAmount = Math.round(discountValue);
} }