diff options
author | Linnea Gräf <nea@nea.moe> | 2025-05-01 21:46:32 +0200 |
---|---|---|
committer | Linnea Gräf <nea@nea.moe> | 2025-05-01 21:46:42 +0200 |
commit | 00e37dc840f7dd9b4ec8d2aa7c17bebad08a2696 (patch) | |
tree | acd21a461b53b9e2990c9a313cc74efcee6b0c40 /server | |
parent | 0c60aa3278a9297e143ee4ad998c01b6ef2337b1 (diff) | |
download | LocalTransactionLedger-master.tar.gz LocalTransactionLedger-master.tar.bz2 LocalTransactionLedger-master.zip |
Diffstat (limited to 'server')
-rw-r--r-- | server/analysis/src/main/kotlin/moe/nea/ledger/analysis/DailyCashflow.kt | 52 | ||||
-rw-r--r-- | server/frontend/index.html | 2 | ||||
-rw-r--r-- | server/frontend/package.json | 5 | ||||
-rw-r--r-- | server/frontend/pnpm-lock.yaml | 340 | ||||
-rw-r--r-- | server/frontend/pnpm-workspace.yaml | 3 | ||||
-rw-r--r-- | server/frontend/src/Analysis.tsx | 63 | ||||
-rw-r--r-- | server/frontend/src/index.css | 5 | ||||
-rw-r--r-- | server/frontend/src/index.tsx | 11 | ||||
-rw-r--r-- | server/frontend/vite.config.ts | 2 |
9 files changed, 462 insertions, 21 deletions
diff --git a/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/DailyCashflow.kt b/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/DailyCashflow.kt new file mode 100644 index 0000000..3dcb438 --- /dev/null +++ b/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/DailyCashflow.kt @@ -0,0 +1,52 @@ +package moe.nea.ledger.analysis + +import com.google.auto.service.AutoService +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.TransactionType +import moe.nea.ledger.database.DBItemEntry +import moe.nea.ledger.database.DBLogEntry +import moe.nea.ledger.database.sql.Clause +import java.sql.Connection +import java.time.LocalDate +import kotlin.collections.component1 +import kotlin.collections.component2 + +@AutoService(Analysis::class) +class DailyCashflow : Analysis { + override val id: String + get() = "daily-cashflow" + override val name: String + get() = "Daily Cashflow" + + override fun perform( + database: Connection, + filter: AnalysisFilter + ): AnalysisResult { + val query = DBLogEntry.from(database) + .join(DBItemEntry, Clause { column(DBItemEntry.transactionId) eq column(DBLogEntry.transactionId) }) + .where(Clause { column(DBItemEntry.itemId) eq ItemId.COINS }) + .select(DBItemEntry.size, DBLogEntry.transactionId) + filter.applyTo(query) + val spentThatDay = mutableMapOf<LocalDate, Double>() + for (resultRow in query) { + val timestamp = resultRow[DBLogEntry.transactionId].getTimestamp() + val damage = resultRow[DBItemEntry.size] + val localZone = filter.timeZone() + val localDate = timestamp.atZone(localZone).toLocalDate() + spentThatDay.merge(localDate, damage) { a, b -> a + b } + } + return AnalysisResult( + listOf( + Visualization( + "Daily Cashflow", + xLabel = "Day", + yLabel = "Coins +/-", + dataPoints = spentThatDay.entries.map { (k, v) -> + DataPoint(k.atTime(12, 0).atZone(filter.timeZone()).toInstant(), v) + } + ) + ) + ) + } +}
\ No newline at end of file diff --git a/server/frontend/index.html b/server/frontend/index.html index 48c59fc..afc19c2 100644 --- a/server/frontend/index.html +++ b/server/frontend/index.html @@ -9,7 +9,7 @@ </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> - <div id="root"></div> + <div id="root" class="min-h-[100vh]"></div> <script src="/src/index.tsx" type="module"></script> </body> diff --git a/server/frontend/package.json b/server/frontend/package.json index a8a8880..e344700 100644 --- a/server/frontend/package.json +++ b/server/frontend/package.json @@ -21,11 +21,14 @@ }, "dependencies": { "@solidjs/router": "^0.15.3", + "@tailwindcss/vite": "^4.1.5", "apexcharts": "^4.3.0", + "chartist": "^1.3.1", "moment": "^2.30.1", "openapi-fetch": "^0.13.4", "solid-apexcharts": "^0.4.0", - "solid-js": "^1.9.3" + "solid-js": "^1.9.3", + "tailwindcss": "^4.1.5" }, "devEngines": { "packageManager": { diff --git a/server/frontend/pnpm-lock.yaml b/server/frontend/pnpm-lock.yaml index 6483404..d4b6e96 100644 --- a/server/frontend/pnpm-lock.yaml +++ b/server/frontend/pnpm-lock.yaml @@ -11,9 +11,15 @@ importers: '@solidjs/router': specifier: ^0.15.3 version: 0.15.3(solid-js@1.9.4) + '@tailwindcss/vite': + specifier: ^4.1.5 + version: 4.1.5(vite@6.0.7(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.83.4)) apexcharts: specifier: ^4.3.0 version: 4.3.0 + chartist: + specifier: ^1.3.1 + version: 1.3.1 moment: specifier: ^2.30.1 version: 2.30.1 @@ -26,22 +32,25 @@ importers: solid-js: specifier: ^1.9.3 version: 1.9.4 + tailwindcss: + specifier: ^4.1.5 + version: 4.1.5 devDependencies: openapi-typescript: specifier: ^7.5.2 version: 7.5.2(typescript@5.7.3) solid-devtools: specifier: ^0.33.0 - version: 0.33.0(solid-js@1.9.4)(vite@6.0.7(sass@1.83.4)) + version: 0.33.0(solid-js@1.9.4)(vite@6.0.7(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.83.4)) typescript: specifier: ^5.7.2 version: 5.7.3 vite: specifier: ^6.0.0 - version: 6.0.7(sass@1.83.4) + version: 6.0.7(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.83.4) vite-plugin-solid: specifier: ^2.11.0 - version: 2.11.0(solid-js@1.9.4)(vite@6.0.7(sass@1.83.4)) + version: 2.11.0(solid-js@1.9.4)(vite@6.0.7(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.83.4)) packages: @@ -600,6 +609,96 @@ packages: peerDependencies: '@svgdotjs/svg.js': ^3.2.4 + '@tailwindcss/node@4.1.5': + resolution: {integrity: sha512-CBhSWo0vLnWhXIvpD0qsPephiaUYfHUX3U9anwDaHZAeuGpTiB3XmsxPAN6qX7bFhipyGBqOa1QYQVVhkOUGxg==} + + '@tailwindcss/oxide-android-arm64@4.1.5': + resolution: {integrity: sha512-LVvM0GirXHED02j7hSECm8l9GGJ1RfgpWCW+DRn5TvSaxVsv28gRtoL4aWKGnXqwvI3zu1GABeDNDVZeDPOQrw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.5': + resolution: {integrity: sha512-//TfCA3pNrgnw4rRJOqavW7XUk8gsg9ddi8cwcsWXp99tzdBAZW0WXrD8wDyNbqjW316Pk2hiN/NJx/KWHl8oA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.5': + resolution: {integrity: sha512-XQorp3Q6/WzRd9OalgHgaqgEbjP3qjHrlSUb5k1EuS1Z9NE9+BbzSORraO+ecW432cbCN7RVGGL/lSnHxcd+7Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.5': + resolution: {integrity: sha512-bPrLWbxo8gAo97ZmrCbOdtlz/Dkuy8NK97aFbVpkJ2nJ2Jo/rsCbu0TlGx8joCuA3q6vMWTSn01JY46iwG+clg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.5': + resolution: {integrity: sha512-1gtQJY9JzMAhgAfvd/ZaVOjh/Ju/nCoAsvOVJenWZfs05wb8zq+GOTnZALWGqKIYEtyNpCzvMk+ocGpxwdvaVg==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.5': + resolution: {integrity: sha512-dtlaHU2v7MtdxBXoqhxwsWjav7oim7Whc6S9wq/i/uUMTWAzq/gijq1InSgn2yTnh43kR+SFvcSyEF0GCNu1PQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.5': + resolution: {integrity: sha512-fg0F6nAeYcJ3CriqDT1iVrqALMwD37+sLzXs8Rjy8Z1ZHshJoYceodfyUwGJEsQoTyWbliFNRs2wMQNXtT7MVA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.5': + resolution: {integrity: sha512-SO+F2YEIAHa1AITwc8oPwMOWhgorPzzcbhWEb+4oLi953h45FklDmM8dPSZ7hNHpIk9p/SCZKUYn35t5fjGtHA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.5': + resolution: {integrity: sha512-6UbBBplywkk/R+PqqioskUeXfKcBht3KU7juTi1UszJLx0KPXUo10v2Ok04iBJIaDPkIFkUOVboXms5Yxvaz+g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.5': + resolution: {integrity: sha512-hwALf2K9FHuiXTPqmo1KeOb83fTRNbe9r/Ixv9ZNQ/R24yw8Ge1HOWDDgTdtzntIaIUJG5dfXCf4g9AD4RiyhQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.5': + resolution: {integrity: sha512-oDKncffWzaovJbkuR7/OTNFRJQVdiw/n8HnzaCItrNQUeQgjy7oUiYpsm9HUBgpmvmDpSSbGaCa2Evzvk3eFmA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.5': + resolution: {integrity: sha512-WiR4dtyrFdbb+ov0LK+7XsFOsG+0xs0PKZKkt41KDn9jYpO7baE3bXiudPVkTqUEwNfiglCygQHl2jklvSBi7Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.5': + resolution: {integrity: sha512-1n4br1znquEvyW/QuqMKQZlBen+jxAbvyduU87RS8R3tUSvByAkcaMTkJepNIrTlYhD+U25K4iiCIxE6BGdRYA==} + engines: {node: '>= 10'} + + '@tailwindcss/vite@4.1.5': + resolution: {integrity: sha512-FE1stRoqdHSb7RxesMfCXE8icwI1W6zGE/512ae3ZDrpkQYTTYeSyUJPRCjZd8CwVAhpDUbi1YR8pcZioFJQ/w==} + peerDependencies: + vite: ^5.2.0 || ^6 + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -663,6 +762,10 @@ packages: change-case@5.4.4: resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} + chartist@1.3.1: + resolution: {integrity: sha512-pvdQowirS3f59tkIvUl4MJoWAKU/aVv1RqleKzOkXQ0nD4X9uu+7T1cCJSTTnbAvPNuAt9JpUu6+cffvZh5nHg==} + engines: {node: '>=14'} + chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -693,9 +796,17 @@ packages: engines: {node: '>=0.10'} hasBin: true + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + electron-to-chromium@1.5.83: resolution: {integrity: sha512-LcUDPqSt+V0QmI47XLzZrz5OqILSMGsPFkDYus22rIbgorSvBYEFqq854ltTmUdHkY92FSdAAvsh4jWEULMdfQ==} + enhanced-resolve@5.18.1: + resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + engines: {node: '>=10.13.0'} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -729,6 +840,9 @@ packages: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + html-entities@2.3.3: resolution: {integrity: sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==} @@ -759,6 +873,10 @@ packages: resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} engines: {node: '>=12.13'} + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + js-levenshtein@1.1.6: resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} engines: {node: '>=0.10.0'} @@ -783,6 +901,70 @@ packages: engines: {node: '>=6'} hasBin: true + lightningcss-darwin-arm64@1.29.2: + resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.29.2: + resolution: {integrity: sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.29.2: + resolution: {integrity: sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.29.2: + resolution: {integrity: sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.29.2: + resolution: {integrity: sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.29.2: + resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.29.2: + resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.29.2: + resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.29.2: + resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.29.2: + resolution: {integrity: sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.29.2: + resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==} + engines: {node: '>= 12.0.0'} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -922,6 +1104,13 @@ packages: resolution: {integrity: sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==} engines: {node: '>=12'} + tailwindcss@4.1.5: + resolution: {integrity: sha512-nYtSPfWGDiWgCkwQG/m+aX83XCwf62sBgg3bIlNiiOcggnS1x3uVRDAuyelBFL+vJdOPPCGElxv9DjHJjRHiVA==} + + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -1512,6 +1701,71 @@ snapshots: dependencies: '@svgdotjs/svg.js': 3.2.4 + '@tailwindcss/node@4.1.5': + dependencies: + enhanced-resolve: 5.18.1 + jiti: 2.4.2 + lightningcss: 1.29.2 + tailwindcss: 4.1.5 + + '@tailwindcss/oxide-android-arm64@4.1.5': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.5': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.5': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.5': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.5': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.5': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.5': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.5': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.5': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.5': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.5': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.5': + optional: true + + '@tailwindcss/oxide@4.1.5': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.5 + '@tailwindcss/oxide-darwin-arm64': 4.1.5 + '@tailwindcss/oxide-darwin-x64': 4.1.5 + '@tailwindcss/oxide-freebsd-x64': 4.1.5 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.5 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.5 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.5 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.5 + '@tailwindcss/oxide-linux-x64-musl': 4.1.5 + '@tailwindcss/oxide-wasm32-wasi': 4.1.5 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.5 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.5 + + '@tailwindcss/vite@4.1.5(vite@6.0.7(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.83.4))': + dependencies: + '@tailwindcss/node': 4.1.5 + '@tailwindcss/oxide': 4.1.5 + tailwindcss: 4.1.5 + vite: 6.0.7(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.83.4) + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.26.5 @@ -1589,6 +1843,8 @@ snapshots: change-case@5.4.4: {} + chartist@1.3.1: {} + chokidar@4.0.3: dependencies: readdirp: 4.1.1 @@ -1611,8 +1867,15 @@ snapshots: detect-libc@1.0.3: optional: true + detect-libc@2.0.4: {} + electron-to-chromium@1.5.83: {} + enhanced-resolve@5.18.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + entities@4.5.0: {} esbuild@0.24.2: @@ -1659,6 +1922,8 @@ snapshots: globals@11.12.0: {} + graceful-fs@4.2.11: {} + html-entities@2.3.3: {} https-proxy-agent@7.0.6(supports-color@9.4.0): @@ -1686,6 +1951,8 @@ snapshots: is-what@4.1.16: {} + jiti@2.4.2: {} + js-levenshtein@1.1.6: {} js-tokens@4.0.0: {} @@ -1700,6 +1967,51 @@ snapshots: json5@2.2.3: {} + lightningcss-darwin-arm64@1.29.2: + optional: true + + lightningcss-darwin-x64@1.29.2: + optional: true + + lightningcss-freebsd-x64@1.29.2: + optional: true + + lightningcss-linux-arm-gnueabihf@1.29.2: + optional: true + + lightningcss-linux-arm64-gnu@1.29.2: + optional: true + + lightningcss-linux-arm64-musl@1.29.2: + optional: true + + lightningcss-linux-x64-gnu@1.29.2: + optional: true + + lightningcss-linux-x64-musl@1.29.2: + optional: true + + lightningcss-win32-arm64-msvc@1.29.2: + optional: true + + lightningcss-win32-x64-msvc@1.29.2: + optional: true + + lightningcss@1.29.2: + dependencies: + detect-libc: 2.0.4 + optionalDependencies: + lightningcss-darwin-arm64: 1.29.2 + lightningcss-darwin-x64: 1.29.2 + lightningcss-freebsd-x64: 1.29.2 + lightningcss-linux-arm-gnueabihf: 1.29.2 + lightningcss-linux-arm64-gnu: 1.29.2 + lightningcss-linux-arm64-musl: 1.29.2 + lightningcss-linux-x64-gnu: 1.29.2 + lightningcss-linux-x64-musl: 1.29.2 + lightningcss-win32-arm64-msvc: 1.29.2 + lightningcss-win32-x64-msvc: 1.29.2 + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -1827,7 +2139,7 @@ snapshots: defu: 6.1.4 solid-js: 1.9.4 - solid-devtools@0.33.0(solid-js@1.9.4)(vite@6.0.7(sass@1.83.4)): + solid-devtools@0.33.0(solid-js@1.9.4)(vite@6.0.7(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.83.4)): dependencies: '@babel/core': 7.26.0 '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) @@ -1836,7 +2148,7 @@ snapshots: '@solid-devtools/shared': 0.19.0(solid-js@1.9.4) solid-js: 1.9.4 optionalDependencies: - vite: 6.0.7(sass@1.83.4) + vite: 6.0.7(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.83.4) transitivePeerDependencies: - supports-color @@ -1859,6 +2171,10 @@ snapshots: supports-color@9.4.0: {} + tailwindcss@4.1.5: {} + + tapable@2.2.1: {} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -1880,7 +2196,7 @@ snapshots: validate-html-nesting@1.2.2: {} - vite-plugin-solid@2.11.0(solid-js@1.9.4)(vite@6.0.7(sass@1.83.4)): + vite-plugin-solid@2.11.0(solid-js@1.9.4)(vite@6.0.7(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.83.4)): dependencies: '@babel/core': 7.26.0 '@types/babel__core': 7.20.5 @@ -1888,23 +2204,25 @@ snapshots: merge-anything: 5.1.7 solid-js: 1.9.4 solid-refresh: 0.6.3(solid-js@1.9.4) - vite: 6.0.7(sass@1.83.4) - vitefu: 1.0.5(vite@6.0.7(sass@1.83.4)) + vite: 6.0.7(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.83.4) + vitefu: 1.0.5(vite@6.0.7(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.83.4)) transitivePeerDependencies: - supports-color - vite@6.0.7(sass@1.83.4): + vite@6.0.7(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.83.4): dependencies: esbuild: 0.24.2 postcss: 8.5.1 rollup: 4.30.1 optionalDependencies: fsevents: 2.3.3 + jiti: 2.4.2 + lightningcss: 1.29.2 sass: 1.83.4 - vitefu@1.0.5(vite@6.0.7(sass@1.83.4)): + vitefu@1.0.5(vite@6.0.7(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.83.4)): optionalDependencies: - vite: 6.0.7(sass@1.83.4) + vite: 6.0.7(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.83.4) webidl-conversions@3.0.1: {} diff --git a/server/frontend/pnpm-workspace.yaml b/server/frontend/pnpm-workspace.yaml new file mode 100644 index 0000000..012c404 --- /dev/null +++ b/server/frontend/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +onlyBuiltDependencies: + - '@parcel/watcher' + - esbuild diff --git a/server/frontend/src/Analysis.tsx b/server/frontend/src/Analysis.tsx index 3bf9c13..3317f68 100644 --- a/server/frontend/src/Analysis.tsx +++ b/server/frontend/src/Analysis.tsx @@ -2,6 +2,7 @@ import { createAsync, useParams } from "@solidjs/router" import { client, getAnalysisList, paths } from "./api.ts"; import { createSignal, For, onMount, Show, Suspense } from "solid-js"; import { SolidApexCharts } from "solid-apexcharts"; +import { BarChart, times } from "chartist"; type AnalysisResult = { status: 'not requested' } @@ -13,11 +14,11 @@ export default function Analysis() { const analysisId = pathParams.id!; let analysis = createAsync(() => getAnalysisList()); const analysisName = () => analysis()?.data?.find(it => it.id == analysisId)?.name - const [startTimestamp, setStartTimestamp] = createSignal(new Date().getTime() - 1000 * 60 * 60 * 24 * 30); + const [startTimestamp, setStartTimestamp] = createSignal(new Date().getTime() - 1000 * 60 * 60 * 24 * 356); const [endTimestamp, setEndTimestamp] = createSignal(new Date().getTime()); const [analysisResult, setAnalysisResult] = createSignal<AnalysisResult>({ status: 'not requested' }); return <> - <h1><Suspense fallback="Name not loaded...">{analysisName()}</Suspense></h1> + <h1 class="text-xl"><Suspense fallback="Name not loaded...">{analysisName()}</Suspense></h1> <p> <label> Start: @@ -52,13 +53,42 @@ export default function Analysis() { {element => <For each={element().result.visualizations}> {item => - <div> + <div class="h-300 max-h-[90vh]"> <SolidApexCharts - width={1200} type="bar" + width={"100%"} + height={"100%"} options={{ + colors: ['#b03060'], xaxis: { - type: 'numeric' + labels: { + style: { + colors: '#A0A0A0', + }, + formatter(value, timestamp, opts) { + return formatDate(timestamp!) + }, + }, + type: 'numeric', + }, + tooltip: { + enabled: false + }, + yaxis: { + labels: { + style: { + colors: '#A0A0A0' + }, + formatter(val, opts) { + return formatMoney(val) + } + }, + decimalsInFloat: 3 + }, + dataLabels: { + formatter(val, opts) { + return formatMoney(val as number) + }, } }} series={[ @@ -76,6 +106,29 @@ export default function Analysis() { </> } +const formatMoney = (money: number): string => { + if (money < 0) return `-${formatMoney(money)}` + const moneyNames = [ + [1_000_000_000, 'b'], + [1_000_000, 'm'], + [1_000, 'k'], + [1, ''] + ] as const; + + for (const [factor, name] of moneyNames) { + if (money >= factor) { + const scaledValue = Math.round(money / factor * 10) / 10 + return `${scaledValue}${name}` + } + } + return money.toString() +} + +const formatDate = (date: number | Date) => { + const _date = new Date(date); + return `${_date.getDay()}.${_date.getMonth() + 1}.${_date.getFullYear()}` +} + function takeIf<T extends P, P>( obj: P, condition: (arg: P) => arg is T, diff --git a/server/frontend/src/index.css b/server/frontend/src/index.css index 4a1df4d..e10ddff 100644 --- a/server/frontend/src/index.css +++ b/server/frontend/src/index.css @@ -1,3 +1,6 @@ +@import "tailwindcss"; + + body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", @@ -10,4 +13,4 @@ body { code { font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; -} +}
\ No newline at end of file diff --git a/server/frontend/src/index.tsx b/server/frontend/src/index.tsx index 610a78b..3009300 100644 --- a/server/frontend/src/index.tsx +++ b/server/frontend/src/index.tsx @@ -5,7 +5,7 @@ import 'solid-devtools'; import "./index.css"; import type { RouteDefinition } from "@solidjs/router"; import { Router } from "@solidjs/router"; -import { lazy } from "solid-js"; +import { lazy, onMount } from "solid-js"; const root = document.getElementById("root"); @@ -20,4 +20,11 @@ const routes: Array<RouteDefinition> = [ { path: "/analysis/:id", component: lazy(() => import("./Analysis.tsx")) }, ]; -render(() => <Router>{routes}</Router>, root!); +const Root = () => { + + return <div class="bg-gray-800 text-white min-h-[100vh]"> + <Router>{routes}</Router> + </div> +} + +render(() => <Root />, root!); diff --git a/server/frontend/vite.config.ts b/server/frontend/vite.config.ts index 4d3fdd1..b356c7e 100644 --- a/server/frontend/vite.config.ts +++ b/server/frontend/vite.config.ts @@ -1,9 +1,11 @@ import { defineConfig } from 'vite'; import solidPlugin from 'vite-plugin-solid'; +import tailwindcss from '@tailwindcss/vite'; import devtools from 'solid-devtools/vite'; export default defineConfig({ plugins: [ solidPlugin(), + tailwindcss(), devtools({ autoname: true }) |