aboutsummaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/analysis/build.gradle.kts16
-rw-r--r--server/analysis/src/main/kotlin/moe/nea/ledger/analysis/Analysis.kt9
-rw-r--r--server/analysis/src/main/kotlin/moe/nea/ledger/analysis/AnalysisFilter.kt26
-rw-r--r--server/analysis/src/main/kotlin/moe/nea/ledger/analysis/AnalysisResult.kt8
-rw-r--r--server/analysis/src/main/kotlin/moe/nea/ledger/analysis/CoinsSpentOnAuctions.kt49
-rw-r--r--server/analysis/src/main/kotlin/moe/nea/ledger/analysis/DailyCashflow.kt52
-rw-r--r--server/analysis/src/main/kotlin/moe/nea/ledger/analysis/Visualization.kt30
-rw-r--r--server/core/build.gradle.kts1
-rw-r--r--server/core/src/main/kotlin/moe/nea/ledger/server/core/api/BaseApi.kt109
-rw-r--r--server/frontend/index.html2
-rw-r--r--server/frontend/package.json14
-rw-r--r--server/frontend/pnpm-lock.yaml916
-rw-r--r--server/frontend/pnpm-workspace.yaml3
-rw-r--r--server/frontend/src/Analysis.tsx137
-rw-r--r--server/frontend/src/App.tsx17
-rw-r--r--server/frontend/src/api-schema.d.ts98
-rw-r--r--server/frontend/src/api.ts7
-rw-r--r--server/frontend/src/index.css5
-rw-r--r--server/frontend/src/index.tsx13
-rw-r--r--server/frontend/tsconfig.json4
-rw-r--r--server/frontend/vite.config.ts11
-rw-r--r--server/swagger/src/main/kotlin/moe/nea/ledger/server/core/api/OpenApiModel.kt1
22 files changed, 1480 insertions, 48 deletions
diff --git a/server/analysis/build.gradle.kts b/server/analysis/build.gradle.kts
new file mode 100644
index 0000000..d5d48a0
--- /dev/null
+++ b/server/analysis/build.gradle.kts
@@ -0,0 +1,16 @@
+plugins {
+ kotlin("jvm")
+ kotlin("plugin.serialization")
+ id("com.google.devtools.ksp")
+}
+
+dependencies {
+ api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.8.0")
+ ksp("dev.zacsweers.autoservice:auto-service-ksp:1.2.0")
+ implementation("com.google.auto.service:auto-service-annotations:1.1.1")
+ implementation(project(":database:impl"))
+}
+
+java {
+ toolchain.languageVersion.set(JavaLanguageVersion.of(21))
+}
diff --git a/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/Analysis.kt b/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/Analysis.kt
new file mode 100644
index 0000000..abcf8ed
--- /dev/null
+++ b/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/Analysis.kt
@@ -0,0 +1,9 @@
+package moe.nea.ledger.analysis
+
+import java.sql.Connection
+
+interface Analysis {
+ val id: String
+ val name: String
+ fun perform(database: Connection, filter: AnalysisFilter): AnalysisResult
+} \ No newline at end of file
diff --git a/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/AnalysisFilter.kt b/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/AnalysisFilter.kt
new file mode 100644
index 0000000..10d9b9c
--- /dev/null
+++ b/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/AnalysisFilter.kt
@@ -0,0 +1,26 @@
+package moe.nea.ledger.analysis
+
+import moe.nea.ledger.database.DBLogEntry
+import moe.nea.ledger.database.Query
+import moe.nea.ledger.database.columns.DBUlid
+import moe.nea.ledger.database.sql.Clause
+import moe.nea.ledger.utils.ULIDWrapper
+import java.time.Instant
+import java.time.ZoneId
+import java.util.UUID
+
+interface AnalysisFilter {
+ fun applyTo(query: Query) {
+ query.where(Clause { column(DBLogEntry.transactionId) ge value(DBUlid, ULIDWrapper.lowerBound(startWindow)) })
+ .where(Clause { column(DBLogEntry.transactionId) le value(DBUlid, ULIDWrapper.upperBound(endWindow)) })
+//TODO: .where(Clause { column(DBLogEntry.profileId) inList profiles })
+ }
+
+ fun timeZone(): ZoneId {
+ return ZoneId.systemDefault()
+ }
+
+ val startWindow: Instant
+ val endWindow: Instant
+ val profiles: List<UUID>
+}
diff --git a/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/AnalysisResult.kt b/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/AnalysisResult.kt
new file mode 100644
index 0000000..4ad47f7
--- /dev/null
+++ b/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/AnalysisResult.kt
@@ -0,0 +1,8 @@
+package moe.nea.ledger.analysis
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class AnalysisResult(
+ val visualizations: List<Visualization>
+) \ No newline at end of file
diff --git a/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/CoinsSpentOnAuctions.kt b/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/CoinsSpentOnAuctions.kt
new file mode 100644
index 0000000..d1ce52b
--- /dev/null
+++ b/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/CoinsSpentOnAuctions.kt
@@ -0,0 +1,49 @@
+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
+
+@AutoService(Analysis::class)
+class CoinsSpentOnAuctions : Analysis {
+ override val name: String
+ get() = "Shopping Costs"
+ override val id: String
+ get() = "coins-spent-on-auctions"
+
+ 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 })
+ .where(Clause { column(DBItemEntry.mode) eq ItemChange.ChangeDirection.LOST })
+ .where(Clause { column(DBLogEntry.type) eq TransactionType.AUCTION_BOUGHT })
+ .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(
+ "Coins spent on auctions",
+ xLabel = "Time",
+ yLabel = "Coins Spent that day",
+ 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/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/analysis/src/main/kotlin/moe/nea/ledger/analysis/Visualization.kt b/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/Visualization.kt
new file mode 100644
index 0000000..d0c0d56
--- /dev/null
+++ b/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/Visualization.kt
@@ -0,0 +1,30 @@
+package moe.nea.ledger.analysis
+
+import kotlinx.serialization.KSerializer
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.descriptors.PrimitiveKind
+import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
+import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.encoding.Decoder
+import kotlinx.serialization.encoding.Encoder
+import java.time.Instant
+
+@Serializable
+data class Visualization(
+ val label: String,
+ val xLabel: String,
+ val yLabel: String,
+ val dataPoints: List<DataPoint>
+)
+
+@Serializable
+data class DataPoint(
+ val time: @Serializable(InstantSerializer::class) Instant,
+ val value: Double,
+)
+
+object InstantSerializer : KSerializer<Instant> {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("java.time.Instant", PrimitiveKind.LONG)
+ override fun serialize(encoder: Encoder, value: Instant) = encoder.encodeLong(value.toEpochMilli())
+ override fun deserialize(decoder: Decoder): Instant = Instant.ofEpochMilli(decoder.decodeLong())
+} \ No newline at end of file
diff --git a/server/core/build.gradle.kts b/server/core/build.gradle.kts
index 6300a4b..b2a3222 100644
--- a/server/core/build.gradle.kts
+++ b/server/core/build.gradle.kts
@@ -15,6 +15,7 @@ dependencies {
api("io.ktor:ktor-server-compression")
api("io.ktor:ktor-server-cors")
api("sh.ondr:kotlin-json-schema:0.1.1")
+ api(project(":server:analysis"))
api(project(":database:impl"))
api(project(":server:swagger"))
diff --git a/server/core/src/main/kotlin/moe/nea/ledger/server/core/api/BaseApi.kt b/server/core/src/main/kotlin/moe/nea/ledger/server/core/api/BaseApi.kt
index 4bc6472..3240a65 100644
--- a/server/core/src/main/kotlin/moe/nea/ledger/server/core/api/BaseApi.kt
+++ b/server/core/src/main/kotlin/moe/nea/ledger/server/core/api/BaseApi.kt
@@ -6,8 +6,10 @@ import io.ktor.server.response.respond
import io.ktor.server.routing.Route
import io.ktor.server.routing.get
import kotlinx.coroutines.DelicateCoroutinesApi
+import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
+import kotlinx.coroutines.withContext
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
@@ -19,14 +21,29 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import moe.nea.ledger.ItemChange
import moe.nea.ledger.TransactionType
+import moe.nea.ledger.analysis.Analysis
+import moe.nea.ledger.analysis.AnalysisFilter
+import moe.nea.ledger.analysis.AnalysisResult
import moe.nea.ledger.database.DBItemEntry
import moe.nea.ledger.database.DBLogEntry
import moe.nea.ledger.database.Database
import moe.nea.ledger.database.sql.Clause
import moe.nea.ledger.server.core.Profile
import moe.nea.ledger.utils.ULIDWrapper
+import java.time.Instant
+import java.util.ServiceLoader
+import java.util.UUID
fun Route.apiRouting(database: Database) {
+ val allOfferedAnalysisServices: Map<String, Analysis> = run {
+ val serviceLoader = ServiceLoader.load(Analysis::class.java, environment.classLoader)
+ val map = mutableMapOf<String, Analysis>()
+ serviceLoader.forEach {
+ map[it.id] = it
+ }
+ map
+ }
+
get("/profiles") {
val profiles = DBLogEntry.from(database.connection)
.select(DBLogEntry.playerId, DBLogEntry.profileId)
@@ -49,6 +66,48 @@ fun Route.apiRouting(database: Database) {
Url("https://github.com/nea89o/ledger-auxiliary-data/raw/refs/heads/master/data/item_names.json")
Json.decodeFromStream<Map<String, String>>(itemNamesUrl.toURI().toURL().openStream())
}
+ get("/analysis/execute") {
+ val analysis = allOfferedAnalysisServices[call.queryParameters["analysis"]] ?: TODO()
+ val start = call.queryParameters["tStart"]?.toLongOrNull()?.let { Instant.ofEpochMilli(it) } ?: TODO()
+ val end = call.queryParameters["tEnd"]?.toLongOrNull()?.let { Instant.ofEpochMilli(it) } ?: TODO()
+ val analysisResult = withContext(Dispatchers.IO) {
+ analysis.perform(
+ database.connection,
+ object : AnalysisFilter {
+ override val startWindow: Instant
+ get() = start
+ override val endWindow: Instant
+ get() = end
+ override val profiles: List<UUID>
+ get() = listOf()
+ }
+ )
+ }
+ call.respond(analysisResult)
+ }.docs {
+ summary = "Execute an analysis on a given timeframe"
+ operationId = "executeAnalysis"
+ queryParameter<String>("analysis", description = "An analysis id obtained from getAnalysis")
+ queryParameter<Long>("tStart", description = "The start of the timeframe to analyze")
+ queryParameter<Long>("tEnd",
+ description = "The end of the timeframe to analyze. Make sure to use the end of the day if you want the entire day included.")
+ tag(Tags.DATA)
+ respondsOk {
+ schema<AnalysisResult>()
+ }
+ }
+ get("/analysis/list") {
+ call.respond(allOfferedAnalysisServices.values.map {
+ AnalysisListing(it.name, it.id)
+ })
+ }.docs {
+ summary = "List all installed analysis"
+ operationId = "getAnalysis"
+ tag(Tags.DATA)
+ respondsOk {
+ schema<List<AnalysisListing>>()
+ }
+ }
get("/item") {
val itemIds = call.queryParameters.getAll("itemId")?.toSet() ?: emptySet()
val itemNameMap = itemNames.await()
@@ -65,28 +124,30 @@ fun Route.apiRouting(database: Database) {
get("/entries") {
val logs = mutableMapOf<ULIDWrapper, LogEntry>()
val items = mutableMapOf<ULIDWrapper, MutableList<SerializableItemChange>>()
- DBLogEntry.from(database.connection)
- .join(DBItemEntry, Clause { column(DBItemEntry.transactionId) eq column(DBLogEntry.transactionId) })
- .select(DBLogEntry.profileId,
- DBLogEntry.playerId,
- DBLogEntry.transactionId,
- DBLogEntry.type,
- DBItemEntry.mode,
- DBItemEntry.itemId,
- DBItemEntry.size)
- .forEach { row ->
- logs.getOrPut(row[DBLogEntry.transactionId]) {
- LogEntry(row[DBLogEntry.type],
- row[DBLogEntry.transactionId],
- listOf())
+ withContext(Dispatchers.IO) {
+ DBLogEntry.from(database.connection)
+ .join(DBItemEntry, Clause { column(DBItemEntry.transactionId) eq column(DBLogEntry.transactionId) })
+ .select(DBLogEntry.profileId,
+ DBLogEntry.playerId,
+ DBLogEntry.transactionId,
+ DBLogEntry.type,
+ DBItemEntry.mode,
+ DBItemEntry.itemId,
+ DBItemEntry.size)
+ .forEach { row ->
+ logs.getOrPut(row[DBLogEntry.transactionId]) {
+ LogEntry(row[DBLogEntry.type],
+ row[DBLogEntry.transactionId],
+ listOf())
+ }
+ items.getOrPut(row[DBLogEntry.transactionId]) { mutableListOf() }
+ .add(SerializableItemChange(
+ row[DBItemEntry.itemId].string,
+ row[DBItemEntry.mode],
+ row[DBItemEntry.size],
+ ))
}
- items.getOrPut(row[DBLogEntry.transactionId]) { mutableListOf() }
- .add(SerializableItemChange(
- row[DBItemEntry.itemId].string,
- row[DBItemEntry.mode],
- row[DBItemEntry.size],
- ))
- }
+ }
val compiled = logs.values.map { it.copy(items = items[it.id]!!) }
call.respond(compiled)
}.docs {
@@ -100,6 +161,12 @@ fun Route.apiRouting(database: Database) {
}
@Serializable
+data class AnalysisListing(
+ val name: String,
+ val id: String,
+)
+
+@Serializable
data class LogEntry(
val type: TransactionType,
val id: @Serializable(ULIDSerializer::class) ULIDWrapper,
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 f8a89f1..e344700 100644
--- a/server/frontend/package.json
+++ b/server/frontend/package.json
@@ -9,22 +9,28 @@
"build": "vite build",
"serve": "vite preview",
"test:ts": "tsc --noEmit",
- "genApi": "pnpx openapi-typescript http://localhost:8080/api.json -o src/api-schema.d.ts"
+ "genApi": "openapi-typescript http://localhost:8080/api.json -o src/api-schema.d.ts"
},
"license": "MIT",
"devDependencies": {
"openapi-typescript": "^7.5.2",
+ "solid-devtools": "^0.33.0",
"typescript": "^5.7.2",
"vite": "^6.0.0",
"vite-plugin-solid": "^2.11.0"
},
"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-js": "^1.9.3"
+ "solid-apexcharts": "^0.4.0",
+ "solid-js": "^1.9.3",
+ "tailwindcss": "^4.1.5"
},
-
-"devEngines": {
+ "devEngines": {
"packageManager": {
"name": "pnpm",
"onFail": "error"
diff --git a/server/frontend/pnpm-lock.yaml b/server/frontend/pnpm-lock.yaml
index e7a2e34..d4b6e96 100644
--- a/server/frontend/pnpm-lock.yaml
+++ b/server/frontend/pnpm-lock.yaml
@@ -11,25 +11,46 @@ 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
openapi-fetch:
specifier: ^0.13.4
version: 0.13.4
+ solid-apexcharts:
+ specifier: ^0.4.0
+ version: 0.4.0(apexcharts@4.3.0)(solid-js@1.9.4)
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(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
+ 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)
+ 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:
@@ -102,6 +123,12 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
+ '@babel/plugin-syntax-typescript@7.25.9':
+ resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
'@babel/template@7.25.9':
resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==}
engines: {node: '>=6.9.0'}
@@ -282,6 +309,91 @@ packages:
'@jridgewell/trace-mapping@0.3.25':
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+ '@nothing-but/utils@0.17.0':
+ resolution: {integrity: sha512-TuCHcHLOqDL0SnaAxACfuRHBNRgNJcNn9X0GiH5H3YSDBVquCr3qEIG3FOQAuMyZCbu9w8nk2CHhOsn7IvhIwQ==}
+
+ '@parcel/watcher-android-arm64@2.5.0':
+ resolution: {integrity: sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ '@parcel/watcher-darwin-arm64@2.5.0':
+ resolution: {integrity: sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@parcel/watcher-darwin-x64@2.5.0':
+ resolution: {integrity: sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@parcel/watcher-freebsd-x64@2.5.0':
+ resolution: {integrity: sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@parcel/watcher-linux-arm-glibc@2.5.0':
+ resolution: {integrity: sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ '@parcel/watcher-linux-arm-musl@2.5.0':
+ resolution: {integrity: sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ '@parcel/watcher-linux-arm64-glibc@2.5.0':
+ resolution: {integrity: sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@parcel/watcher-linux-arm64-musl@2.5.0':
+ resolution: {integrity: sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@parcel/watcher-linux-x64-glibc@2.5.0':
+ resolution: {integrity: sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ '@parcel/watcher-linux-x64-musl@2.5.0':
+ resolution: {integrity: sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ '@parcel/watcher-win32-arm64@2.5.0':
+ resolution: {integrity: sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@parcel/watcher-win32-ia32@2.5.0':
+ resolution: {integrity: sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@parcel/watcher-win32-x64@2.5.0':
+ resolution: {integrity: sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ '@parcel/watcher@2.5.0':
+ resolution: {integrity: sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==}
+ engines: {node: '>= 10.0.0'}
+
'@redocly/ajv@8.11.2':
resolution: {integrity: sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==}
@@ -387,11 +499,206 @@ packages:
cpu: [x64]
os: [win32]
+ '@solid-devtools/debugger@0.26.0':
+ resolution: {integrity: sha512-36QxZ+s/lY60E+Pb9q0eTsdqgaog4c823WIj5dC2LFdGrGXbVGBQEj6k7CgvMnEETdwndrd0Fm72fQyYPlZrVA==}
+ peerDependencies:
+ solid-js: ^1.9.0
+
+ '@solid-devtools/shared@0.19.0':
+ resolution: {integrity: sha512-OGo6l84f9X5YEAqSEM4Xl94+xKXSqmACMzKWsAqO0BStLBMVL0vIVu286AQk5XkNxn11/EB9wrdkZc9GUzKlxA==}
+ peerDependencies:
+ solid-js: ^1.9.0
+
+ '@solid-primitives/bounds@0.0.122':
+ resolution: {integrity: sha512-kUq/IprOdFr/rg2upon5lQGOoTnDAmxQS4ASKK2l+VwoKSctdPwgu/4qJxEITZikL+nB0myYZzBZWptySV0cRg==}
+ peerDependencies:
+ solid-js: ^1.6.12
+
+ '@solid-primitives/cursor@0.0.115':
+ resolution: {integrity: sha512-8nEmUN/sacXPChwuJOAi6Yi6VnxthW/Jk8VGvvcF38AenjUvOA6FHI6AkJILuFXjQw1PGxia1YbH/Mn77dPiOA==}
+ peerDependencies:
+ solid-js: ^1.6.12
+
+ '@solid-primitives/event-listener@2.3.3':
+ resolution: {integrity: sha512-DAJbl+F0wrFW2xmcV8dKMBhk9QLVLuBSW+TR4JmIfTaObxd13PuL7nqaXnaYKDWOYa6otB00qcCUIGbuIhSUgQ==}
+ peerDependencies:
+ solid-js: ^1.6.12
+
+ '@solid-primitives/keyboard@1.2.8':
+ resolution: {integrity: sha512-pJtcbkjozS6L1xvTht9rPpyPpX55nAkfBzbFWdf3y0Suwh6qClTibvvObzKOf7uzQ+8aZRDH4LsoGmbTKXtJjQ==}
+ peerDependencies:
+ solid-js: ^1.6.12
+
+ '@solid-primitives/media@2.2.10':
+ resolution: {integrity: sha512-zICx9lXvevycyHmzUp1AfrxmUsF27JGvDygf51mHUpvy/Y2SmxkM6UHKstBDlRSpLUhPTnF0iHCfdfne6g4Fow==}
+ peerDependencies:
+ solid-js: ^1.6.12
+
+ '@solid-primitives/platform@0.1.2':
+ resolution: {integrity: sha512-sSxcZfuUrtxcwV0vdjmGnZQcflACzMfLriVeIIWXKp8hzaS3Or3tO6EFQkTd3L8T5dTq+kTtLvPscXIpL0Wzdg==}
+ peerDependencies:
+ solid-js: ^1.6.12
+
+ '@solid-primitives/refs@1.0.8':
+ resolution: {integrity: sha512-+jIsWG8/nYvhaCoG2Vg6CJOLgTmPKFbaCrNQKWfChalgUf9WrVxWw0CdJb3yX15n5lUcQ0jBo6qYtuVVmBLpBw==}
+ peerDependencies:
+ solid-js: ^1.6.12
+
+ '@solid-primitives/resize-observer@2.0.27':
+ resolution: {integrity: sha512-RmusjHqoA4U6MKI/T9yBJVDttASHpWBki1+YwM9zGXEDBqbysTa3lZpnlB244LzphQmobgeXVS78v0KtXVsF9g==}
+ peerDependencies:
+ solid-js: ^1.6.12
+
+ '@solid-primitives/rootless@1.4.5':
+ resolution: {integrity: sha512-GFJE9GC3ojx0aUKqAUZmQPyU8fOVMtnVNrkdk2yS4kd17WqVSpXpoTmo9CnOwA+PG7FTzdIkogvfLQSLs4lrww==}
+ peerDependencies:
+ solid-js: ^1.6.12
+
+ '@solid-primitives/scheduled@1.4.4':
+ resolution: {integrity: sha512-BTGdFP7t+s7RSak+s1u0eTix4lHP23MrbGkgQTFlt1E+4fmnD/bEx3ZfNW7Grylz3GXgKyXrgDKA7jQ/wuWKgA==}
+ peerDependencies:
+ solid-js: ^1.6.12
+
+ '@solid-primitives/static-store@0.0.8':
+ resolution: {integrity: sha512-ZecE4BqY0oBk0YG00nzaAWO5Mjcny8Fc06CdbXadH9T9lzq/9GefqcSe/5AtdXqjvY/DtJ5C6CkcjPZO0o/eqg==}
+ peerDependencies:
+ solid-js: ^1.6.12
+
+ '@solid-primitives/static-store@0.0.9':
+ resolution: {integrity: sha512-8zaTXTEnQFqdwfkqWmGVb/OYgSTbRgxJSWQNfLuA+KnuW4RzTRQE2jzgnNJjJjaloruv9EHGvikmJzQJ5aOrEw==}
+ peerDependencies:
+ solid-js: ^1.6.12
+
+ '@solid-primitives/styles@0.0.114':
+ resolution: {integrity: sha512-SFXr16mgr6LvZAIj6L7i59HHg+prAmIF8VP/U3C6jSHz68Eh1G71vaWr9vlJVpy/j6bh1N8QUzu5CgtvIC92OQ==}
+ peerDependencies:
+ solid-js: ^1.6.12
+
+ '@solid-primitives/utils@6.2.3':
+ resolution: {integrity: sha512-CqAwKb2T5Vi72+rhebSsqNZ9o67buYRdEJrIFzRXz3U59QqezuuxPsyzTSVCacwS5Pf109VRsgCJQoxKRoECZQ==}
+ peerDependencies:
+ solid-js: ^1.6.12
+
'@solidjs/router@0.15.3':
resolution: {integrity: sha512-iEbW8UKok2Oio7o6Y4VTzLj+KFCmQPGEpm1fS3xixwFBdclFVBvaQVeibl1jys4cujfAK5Kn6+uG2uBm3lxOMw==}
peerDependencies:
solid-js: ^1.8.6
+ '@svgdotjs/svg.draggable.js@3.0.5':
+ resolution: {integrity: sha512-ljL/fB0tAjRfFOJGhXpr7rEx9DJ6D7Pxt3AXvgxjEM17g6wK3Ho9nXhntraOMx8JLZdq4NBMjokeXMvnQzJVYA==}
+ peerDependencies:
+ '@svgdotjs/svg.js': ^3.2.4
+
+ '@svgdotjs/svg.filter.js@3.0.8':
+ resolution: {integrity: sha512-YshF2YDaeRA2StyzAs5nUPrev7npQ38oWD0eTRwnsciSL2KrRPMoUw8BzjIXItb3+dccKGTX3IQOd2NFzmHkog==}
+ engines: {node: '>= 0.8.0'}
+
+ '@svgdotjs/svg.js@3.2.4':
+ resolution: {integrity: sha512-BjJ/7vWNowlX3Z8O4ywT58DqbNRyYlkk6Yz/D13aB7hGmfQTvGX4Tkgtm/ApYlu9M7lCQi15xUEidqMUmdMYwg==}
+
+ '@svgdotjs/svg.resize.js@2.0.5':
+ resolution: {integrity: sha512-4heRW4B1QrJeENfi7326lUPYBCevj78FJs8kfeDxn5st0IYPIRXoTtOSYvTzFWgaWWXd3YCDE6ao4fmv91RthA==}
+ engines: {node: '>= 14.18'}
+ peerDependencies:
+ '@svgdotjs/svg.js': ^3.2.4
+ '@svgdotjs/svg.select.js': ^4.0.1
+
+ '@svgdotjs/svg.select.js@4.0.2':
+ resolution: {integrity: sha512-5gWdrvoQX3keo03SCmgaBbD+kFftq0F/f2bzCbNnpkkvW6tk4rl4MakORzFuNjvXPWwB4az9GwuvVxQVnjaK2g==}
+ engines: {node: '>= 14.18'}
+ 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==}
@@ -407,6 +714,9 @@ packages:
'@types/estree@1.0.6':
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
+ '@yr/monotone-cubic-spline@1.0.3':
+ resolution: {integrity: sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA==}
+
agent-base@7.1.3:
resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==}
engines: {node: '>= 14'}
@@ -415,6 +725,9 @@ packages:
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
engines: {node: '>=6'}
+ apexcharts@4.3.0:
+ resolution: {integrity: sha512-PfvZQpv91T68hzry9l5zP3Gip7sQvF0nFK91uCBrswIKX7rbIdbVNS4fOks9m9yP3Ppgs6LHgU2M/mjoG4NM0A==}
+
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
@@ -434,6 +747,10 @@ packages:
brace-expansion@2.0.1:
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
+ braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+
browserslist@4.24.4:
resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
@@ -445,6 +762,14 @@ 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'}
+
colorette@1.4.0:
resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==}
@@ -463,9 +788,25 @@ packages:
supports-color:
optional: true
+ defu@6.1.4:
+ resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
+
+ detect-libc@1.0.3:
+ resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
+ 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'}
@@ -482,6 +823,10 @@ packages:
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+ fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -495,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==}
@@ -502,14 +850,33 @@ packages:
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
engines: {node: '>= 14'}
+ immutable@5.0.3:
+ resolution: {integrity: sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==}
+
index-to-position@0.1.2:
resolution: {integrity: sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g==}
engines: {node: '>=18'}
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
is-what@4.1.16:
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'}
@@ -534,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==}
@@ -541,10 +972,17 @@ packages:
resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==}
engines: {node: '>=12.13'}
+ micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+
minimatch@5.1.6:
resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
engines: {node: '>=10'}
+ moment@2.30.1:
+ resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==}
+
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@@ -553,6 +991,9 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
+ node-addon-api@7.1.1:
+ resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
+
node-fetch@2.7.0:
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
engines: {node: 4.x || >=6.0.0}
@@ -587,6 +1028,10 @@ packages:
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+ picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+
pluralize@8.0.0:
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
engines: {node: '>=4'}
@@ -595,6 +1040,10 @@ packages:
resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==}
engines: {node: ^10 || ^12 || >=14}
+ readdirp@4.1.1:
+ resolution: {integrity: sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==}
+ engines: {node: '>= 14.18.0'}
+
require-from-string@2.0.2:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
@@ -604,6 +1053,11 @@ packages:
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
+ sass@1.83.4:
+ resolution: {integrity: sha512-B1bozCeNQiOgDcLd33e2Cs2U60wZwjUUXzh900ZyQF5qUasvMdDZYbQ566LJu7cqR+sAHlAfO6RMkaID5s6qpA==}
+ engines: {node: '>=14.0.0'}
+ hasBin: true
+
semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
@@ -618,6 +1072,22 @@ packages:
resolution: {integrity: sha512-GURoU99ko2UiAgUC3qDCk59Jb3Ss4Po8VIMGkG8j5PFo2Q7y0YSMP8QG9NuL/fJCoTz9V1XZUbpNIMXPOfaGpA==}
engines: {node: '>=10'}
+ solid-apexcharts@0.4.0:
+ resolution: {integrity: sha512-b3tjFaYNF2ggvFq+VSaxxj2ocxfKKkB7jnWL60uDaokv2A3RwFXXzmPYZMkN80FQaLnxeNzkUdre/S9HgshLnA==}
+ engines: {node: '>=18', pnpm: '>=8.6.0'}
+ peerDependencies:
+ apexcharts: ^4.0.0
+ solid-js: ^1.6.0
+
+ solid-devtools@0.33.0:
+ resolution: {integrity: sha512-xRB4Jhgns3dBuM/s0j70BpXKy77sNjISud9xXBv60qC4cnJ/TcuVHI1t+05luj1BEKJVQSokqIaVoZWcjqA9yw==}
+ peerDependencies:
+ solid-js: ^1.9.0
+ vite: ^2.2.3 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0
+ peerDependenciesMeta:
+ vite:
+ optional: true
+
solid-js@1.9.4:
resolution: {integrity: sha512-ipQl8FJ31bFUoBNScDQTG3BjN6+9Rg+Q+f10bUbnO6EOTTf5NGerJeHc7wyu5I4RMHEl/WwZwUmy/PTRgxxZ8g==}
@@ -634,6 +1104,17 @@ 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'}
+
tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
@@ -825,6 +1306,11 @@ snapshots:
'@babel/core': 7.26.0
'@babel/helper-plugin-utils': 7.26.5
+ '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/template@7.25.9':
dependencies:
'@babel/code-frame': 7.26.2
@@ -940,6 +1426,69 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.0
+ '@nothing-but/utils@0.17.0': {}
+
+ '@parcel/watcher-android-arm64@2.5.0':
+ optional: true
+
+ '@parcel/watcher-darwin-arm64@2.5.0':
+ optional: true
+
+ '@parcel/watcher-darwin-x64@2.5.0':
+ optional: true
+
+ '@parcel/watcher-freebsd-x64@2.5.0':
+ optional: true
+
+ '@parcel/watcher-linux-arm-glibc@2.5.0':
+ optional: true
+
+ '@parcel/watcher-linux-arm-musl@2.5.0':
+ optional: true
+
+ '@parcel/watcher-linux-arm64-glibc@2.5.0':
+ optional: true
+
+ '@parcel/watcher-linux-arm64-musl@2.5.0':
+ optional: true
+
+ '@parcel/watcher-linux-x64-glibc@2.5.0':
+ optional: true
+
+ '@parcel/watcher-linux-x64-musl@2.5.0':
+ optional: true
+
+ '@parcel/watcher-win32-arm64@2.5.0':
+ optional: true
+
+ '@parcel/watcher-win32-ia32@2.5.0':
+ optional: true
+
+ '@parcel/watcher-win32-x64@2.5.0':
+ optional: true
+
+ '@parcel/watcher@2.5.0':
+ dependencies:
+ detect-libc: 1.0.3
+ is-glob: 4.0.3
+ micromatch: 4.0.8
+ node-addon-api: 7.1.1
+ optionalDependencies:
+ '@parcel/watcher-android-arm64': 2.5.0
+ '@parcel/watcher-darwin-arm64': 2.5.0
+ '@parcel/watcher-darwin-x64': 2.5.0
+ '@parcel/watcher-freebsd-x64': 2.5.0
+ '@parcel/watcher-linux-arm-glibc': 2.5.0
+ '@parcel/watcher-linux-arm-musl': 2.5.0
+ '@parcel/watcher-linux-arm64-glibc': 2.5.0
+ '@parcel/watcher-linux-arm64-musl': 2.5.0
+ '@parcel/watcher-linux-x64-glibc': 2.5.0
+ '@parcel/watcher-linux-x64-musl': 2.5.0
+ '@parcel/watcher-win32-arm64': 2.5.0
+ '@parcel/watcher-win32-ia32': 2.5.0
+ '@parcel/watcher-win32-x64': 2.5.0
+ optional: true
+
'@redocly/ajv@8.11.2':
dependencies:
fast-deep-equal: 3.1.3
@@ -1022,10 +1571,201 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.30.1':
optional: true
+ '@solid-devtools/debugger@0.26.0(solid-js@1.9.4)':
+ dependencies:
+ '@nothing-but/utils': 0.17.0
+ '@solid-devtools/shared': 0.19.0(solid-js@1.9.4)
+ '@solid-primitives/bounds': 0.0.122(solid-js@1.9.4)
+ '@solid-primitives/cursor': 0.0.115(solid-js@1.9.4)
+ '@solid-primitives/event-listener': 2.3.3(solid-js@1.9.4)
+ '@solid-primitives/keyboard': 1.2.8(solid-js@1.9.4)
+ '@solid-primitives/platform': 0.1.2(solid-js@1.9.4)
+ '@solid-primitives/rootless': 1.4.5(solid-js@1.9.4)
+ '@solid-primitives/scheduled': 1.4.4(solid-js@1.9.4)
+ '@solid-primitives/static-store': 0.0.8(solid-js@1.9.4)
+ '@solid-primitives/utils': 6.2.3(solid-js@1.9.4)
+ solid-js: 1.9.4
+
+ '@solid-devtools/shared@0.19.0(solid-js@1.9.4)':
+ dependencies:
+ '@nothing-but/utils': 0.17.0
+ '@solid-primitives/event-listener': 2.3.3(solid-js@1.9.4)
+ '@solid-primitives/media': 2.2.10(solid-js@1.9.4)
+ '@solid-primitives/refs': 1.0.8(solid-js@1.9.4)
+ '@solid-primitives/rootless': 1.4.5(solid-js@1.9.4)
+ '@solid-primitives/scheduled': 1.4.4(solid-js@1.9.4)
+ '@solid-primitives/static-store': 0.0.8(solid-js@1.9.4)
+ '@solid-primitives/styles': 0.0.114(solid-js@1.9.4)
+ '@solid-primitives/utils': 6.2.3(solid-js@1.9.4)
+ solid-js: 1.9.4
+
+ '@solid-primitives/bounds@0.0.122(solid-js@1.9.4)':
+ dependencies:
+ '@solid-primitives/event-listener': 2.3.3(solid-js@1.9.4)
+ '@solid-primitives/resize-observer': 2.0.27(solid-js@1.9.4)
+ '@solid-primitives/static-store': 0.0.8(solid-js@1.9.4)
+ '@solid-primitives/utils': 6.2.3(solid-js@1.9.4)
+ solid-js: 1.9.4
+
+ '@solid-primitives/cursor@0.0.115(solid-js@1.9.4)':
+ dependencies:
+ '@solid-primitives/utils': 6.2.3(solid-js@1.9.4)
+ solid-js: 1.9.4
+
+ '@solid-primitives/event-listener@2.3.3(solid-js@1.9.4)':
+ dependencies:
+ '@solid-primitives/utils': 6.2.3(solid-js@1.9.4)
+ solid-js: 1.9.4
+
+ '@solid-primitives/keyboard@1.2.8(solid-js@1.9.4)':
+ dependencies:
+ '@solid-primitives/event-listener': 2.3.3(solid-js@1.9.4)
+ '@solid-primitives/rootless': 1.4.5(solid-js@1.9.4)
+ '@solid-primitives/utils': 6.2.3(solid-js@1.9.4)
+ solid-js: 1.9.4
+
+ '@solid-primitives/media@2.2.10(solid-js@1.9.4)':
+ dependencies:
+ '@solid-primitives/event-listener': 2.3.3(solid-js@1.9.4)
+ '@solid-primitives/rootless': 1.4.5(solid-js@1.9.4)
+ '@solid-primitives/static-store': 0.0.9(solid-js@1.9.4)
+ '@solid-primitives/utils': 6.2.3(solid-js@1.9.4)
+ solid-js: 1.9.4
+
+ '@solid-primitives/platform@0.1.2(solid-js@1.9.4)':
+ dependencies:
+ solid-js: 1.9.4
+
+ '@solid-primitives/refs@1.0.8(solid-js@1.9.4)':
+ dependencies:
+ '@solid-primitives/utils': 6.2.3(solid-js@1.9.4)
+ solid-js: 1.9.4
+
+ '@solid-primitives/resize-observer@2.0.27(solid-js@1.9.4)':
+ dependencies:
+ '@solid-primitives/event-listener': 2.3.3(solid-js@1.9.4)
+ '@solid-primitives/rootless': 1.4.5(solid-js@1.9.4)
+ '@solid-primitives/static-store': 0.0.9(solid-js@1.9.4)
+ '@solid-primitives/utils': 6.2.3(solid-js@1.9.4)
+ solid-js: 1.9.4
+
+ '@solid-primitives/rootless@1.4.5(solid-js@1.9.4)':
+ dependencies:
+ '@solid-primitives/utils': 6.2.3(solid-js@1.9.4)
+ solid-js: 1.9.4
+
+ '@solid-primitives/scheduled@1.4.4(solid-js@1.9.4)':
+ dependencies:
+ solid-js: 1.9.4
+
+ '@solid-primitives/static-store@0.0.8(solid-js@1.9.4)':
+ dependencies:
+ '@solid-primitives/utils': 6.2.3(solid-js@1.9.4)
+ solid-js: 1.9.4
+
+ '@solid-primitives/static-store@0.0.9(solid-js@1.9.4)':
+ dependencies:
+ '@solid-primitives/utils': 6.2.3(solid-js@1.9.4)
+ solid-js: 1.9.4
+
+ '@solid-primitives/styles@0.0.114(solid-js@1.9.4)':
+ dependencies:
+ '@solid-primitives/rootless': 1.4.5(solid-js@1.9.4)
+ '@solid-primitives/utils': 6.2.3(solid-js@1.9.4)
+ solid-js: 1.9.4
+
+ '@solid-primitives/utils@6.2.3(solid-js@1.9.4)':
+ dependencies:
+ solid-js: 1.9.4
+
'@solidjs/router@0.15.3(solid-js@1.9.4)':
dependencies:
solid-js: 1.9.4
+ '@svgdotjs/svg.draggable.js@3.0.5(@svgdotjs/svg.js@3.2.4)':
+ dependencies:
+ '@svgdotjs/svg.js': 3.2.4
+
+ '@svgdotjs/svg.filter.js@3.0.8':
+ dependencies:
+ '@svgdotjs/svg.js': 3.2.4
+
+ '@svgdotjs/svg.js@3.2.4': {}
+
+ '@svgdotjs/svg.resize.js@2.0.5(@svgdotjs/svg.js@3.2.4)(@svgdotjs/svg.select.js@4.0.2(@svgdotjs/svg.js@3.2.4))':
+ dependencies:
+ '@svgdotjs/svg.js': 3.2.4
+ '@svgdotjs/svg.select.js': 4.0.2(@svgdotjs/svg.js@3.2.4)
+
+ '@svgdotjs/svg.select.js@4.0.2(@svgdotjs/svg.js@3.2.4)':
+ 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
@@ -1049,10 +1789,21 @@ snapshots:
'@types/estree@1.0.6': {}
+ '@yr/monotone-cubic-spline@1.0.3': {}
+
agent-base@7.1.3: {}
ansi-colors@4.1.3: {}
+ apexcharts@4.3.0:
+ dependencies:
+ '@svgdotjs/svg.draggable.js': 3.0.5(@svgdotjs/svg.js@3.2.4)
+ '@svgdotjs/svg.filter.js': 3.0.8
+ '@svgdotjs/svg.js': 3.2.4
+ '@svgdotjs/svg.resize.js': 2.0.5(@svgdotjs/svg.js@3.2.4)(@svgdotjs/svg.select.js@4.0.2(@svgdotjs/svg.js@3.2.4))
+ '@svgdotjs/svg.select.js': 4.0.2(@svgdotjs/svg.js@3.2.4)
+ '@yr/monotone-cubic-spline': 1.0.3
+
argparse@2.0.1: {}
babel-plugin-jsx-dom-expressions@0.39.5(@babel/core@7.26.0):
@@ -1076,6 +1827,11 @@ snapshots:
dependencies:
balanced-match: 1.0.2
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+ optional: true
+
browserslist@4.24.4:
dependencies:
caniuse-lite: 1.0.30001692
@@ -1087,6 +1843,13 @@ snapshots:
change-case@5.4.4: {}
+ chartist@1.3.1: {}
+
+ chokidar@4.0.3:
+ dependencies:
+ readdirp: 4.1.1
+ optional: true
+
colorette@1.4.0: {}
convert-source-map@2.0.0: {}
@@ -1099,8 +1862,20 @@ snapshots:
optionalDependencies:
supports-color: 9.4.0
+ defu@6.1.4: {}
+
+ 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:
@@ -1135,6 +1910,11 @@ snapshots:
fast-deep-equal@3.1.3: {}
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+ optional: true
+
fsevents@2.3.3:
optional: true
@@ -1142,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):
@@ -1151,10 +1933,26 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ immutable@5.0.3:
+ optional: true
+
index-to-position@0.1.2: {}
+ is-extglob@2.1.1:
+ optional: true
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+ optional: true
+
+ is-number@7.0.0:
+ optional: true
+
is-what@4.1.16: {}
+ jiti@2.4.2: {}
+
js-levenshtein@1.1.6: {}
js-tokens@4.0.0: {}
@@ -1169,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
@@ -1177,14 +2020,25 @@ snapshots:
dependencies:
is-what: 4.1.16
+ micromatch@4.0.8:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.1
+ optional: true
+
minimatch@5.1.6:
dependencies:
brace-expansion: 2.0.1
+ moment@2.30.1: {}
+
ms@2.1.3: {}
nanoid@3.3.8: {}
+ node-addon-api@7.1.1:
+ optional: true
+
node-fetch@2.7.0:
dependencies:
whatwg-url: 5.0.0
@@ -1221,6 +2075,9 @@ snapshots:
picocolors@1.1.1: {}
+ picomatch@2.3.1:
+ optional: true
+
pluralize@8.0.0: {}
postcss@8.5.1:
@@ -1229,6 +2086,9 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
+ readdirp@4.1.1:
+ optional: true
+
require-from-string@2.0.2: {}
rollup@4.30.1:
@@ -1256,6 +2116,15 @@ snapshots:
'@rollup/rollup-win32-x64-msvc': 4.30.1
fsevents: 2.3.3
+ sass@1.83.4:
+ dependencies:
+ chokidar: 4.0.3
+ immutable: 5.0.3
+ source-map-js: 1.2.1
+ optionalDependencies:
+ '@parcel/watcher': 2.5.0
+ optional: true
+
semver@6.3.1: {}
seroval-plugins@1.2.0(seroval@1.2.0):
@@ -1264,6 +2133,25 @@ snapshots:
seroval@1.2.0: {}
+ solid-apexcharts@0.4.0(apexcharts@4.3.0)(solid-js@1.9.4):
+ dependencies:
+ apexcharts: 4.3.0
+ defu: 6.1.4
+ solid-js: 1.9.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)
+ '@babel/types': 7.26.5
+ '@solid-devtools/debugger': 0.26.0(solid-js@1.9.4)
+ '@solid-devtools/shared': 0.19.0(solid-js@1.9.4)
+ solid-js: 1.9.4
+ optionalDependencies:
+ vite: 6.0.7(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.83.4)
+ transitivePeerDependencies:
+ - supports-color
+
solid-js@1.9.4:
dependencies:
csstype: 3.1.3
@@ -1283,6 +2171,15 @@ 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
+ optional: true
+
tr46@0.0.3: {}
type-fest@4.32.0: {}
@@ -1299,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):
+ 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
@@ -1307,22 +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
- vitefu: 1.0.5(vite@6.0.7)
+ 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:
+ 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):
+ 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
+ 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
new file mode 100644
index 0000000..3317f68
--- /dev/null
+++ b/server/frontend/src/Analysis.tsx
@@ -0,0 +1,137 @@
+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' }
+ | { status: 'loading' }
+ | { status: 'loaded', result: paths['/analysis/execute']['get']['responses'][200]['content']['application/json'] }
+
+export default function Analysis() {
+ const pathParams = useParams();
+ 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 * 356);
+ const [endTimestamp, setEndTimestamp] = createSignal(new Date().getTime());
+ const [analysisResult, setAnalysisResult] = createSignal<AnalysisResult>({ status: 'not requested' });
+ return <>
+ <h1 class="text-xl"><Suspense fallback="Name not loaded...">{analysisName()}</Suspense></h1>
+ <p>
+ <label>
+ Start:
+ <input type="date" value={new Date(startTimestamp()).toISOString().substring(0, 10)} onInput={it => setStartTimestamp(it.target.valueAsNumber)}></input>
+ </label>
+ <label>
+ End:
+ <input type="date" value={new Date(endTimestamp()).toISOString().substring(0, 10)} onInput={it => setEndTimestamp(it.target.valueAsNumber)}></input>
+ </label>
+ <button disabled={analysisResult().status === 'loading'} onClick={() => {
+ setAnalysisResult({ status: 'loading' });
+ (async () => {
+ const result = await client.GET('/analysis/execute', {
+ params: {
+ query: {
+ analysis: analysisId,
+ tEnd: endTimestamp(),
+ tStart: startTimestamp()
+ }
+ }
+ });
+ setAnalysisResult({
+ status: "loaded",
+ result: result.data!
+ });
+ })();
+ }}>
+ Refresh
+ </button>
+
+ <Show when={takeIf(analysisResult(), it => it.status == 'loaded')}>
+ {element =>
+ <For each={element().result.visualizations}>
+ {item =>
+ <div class="h-300 max-h-[90vh]">
+ <SolidApexCharts
+ type="bar"
+ width={"100%"}
+ height={"100%"}
+ options={{
+ colors: ['#b03060'],
+ xaxis: {
+ 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={[
+ {
+ name: item.label,
+ data: item.dataPoints.map(it => ([it.time, it.value]))
+ }
+ ]}
+ ></SolidApexCharts>
+ </div>
+ }
+ </For>}
+ </Show>
+ </p >
+ </>
+}
+
+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,
+): T | false {
+ return condition(obj) ? obj : false;
+} \ No newline at end of file
diff --git a/server/frontend/src/App.tsx b/server/frontend/src/App.tsx
index e35bb42..bdc1007 100644
--- a/server/frontend/src/App.tsx
+++ b/server/frontend/src/App.tsx
@@ -1,11 +1,20 @@
-import type { Component } from "solid-js";
-import { A } from "@solidjs/router";
+import { For, Suspense, type Component } from "solid-js";
+import { A, createAsync } from "@solidjs/router";
+import { client, getAnalysisList } from "./api.ts";
const App: Component = () => {
+ let analysis = createAsync(() => getAnalysisList());
return (
<>
- Hello World
- <A href="/test">Test Page</A>
+ <Suspense fallback="Loading analysis...">
+ <ul>
+ <For each={analysis()?.data}>
+ {item =>
+ <li><A href={`/analysis/${item.id}`}>{item.name}</A></li>
+ }
+ </For>
+ </ul>
+ </Suspense>
</>
);
};
diff --git a/server/frontend/src/api-schema.d.ts b/server/frontend/src/api-schema.d.ts
index eab0d7e..7ba1db4 100644
--- a/server/frontend/src/api-schema.d.ts
+++ b/server/frontend/src/api-schema.d.ts
@@ -21,6 +21,40 @@ export interface paths {
patch?: never;
trace?: never;
};
+ "/analysis/execute": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** Execute an analysis on a given timeframe */
+ get: operations["executeAnalysis"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/analysis/list": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** List all installed analysis */
+ get: operations["getAnalysis"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
"/item": {
parameters: {
query?: never;
@@ -89,10 +123,68 @@ export interface operations {
};
};
};
+ executeAnalysis: {
+ parameters: {
+ query: {
+ /** @description An analysis id obtained from getAnalysis */
+ analysis: string;
+ /** @description The start of the timeframe to analyze */
+ tStart: number;
+ /** @description The end of the timeframe to analyze. Make sure to use the end of the day if you want the entire day included. */
+ tEnd: number;
+ };
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ visualizations: {
+ label: string;
+ xLabel: string;
+ yLabel: string;
+ dataPoints: {
+ time: number;
+ value: number;
+ }[];
+ }[];
+ };
+ };
+ };
+ };
+ };
+ getAnalysis: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ name: string;
+ id: string;
+ }[];
+ };
+ };
+ };
+ };
getItemNames: {
parameters: {
- query?: {
- itemId?: string[];
+ query: {
+ itemId: string[];
};
header?: never;
path?: never;
@@ -128,7 +220,7 @@ export interface operations {
content: {
"application/json": {
/** @enum {string} */
- type: "ACCESSORIES_SWAPPING" | "ALLOWANCE_GAIN" | "AUCTION_BOUGHT" | "AUCTION_LISTING_CHARGE" | "AUCTION_SOLD" | "AUTOMERCHANT_PROFIT_COLLECT" | "BANK_DEPOSIT" | "BANK_WITHDRAW" | "BAZAAR_BUY_INSTANT" | "BAZAAR_BUY_ORDER" | "BAZAAR_SELL_INSTANT" | "BAZAAR_SELL_ORDER" | "BITS_PURSE_STATUS" | "BOOSTER_COOKIE_ATE" | "CAPSAICIN_EYEDROPS_USED" | "COMMUNITY_SHOP_BUY" | "CORPSE_DESECRATED" | "DIE_ROLLED" | "DRACONIC_SACRIFICE" | "DUNGEON_CHEST_OPEN" | "FORGED" | "GOD_POTION_DRANK" | "GOD_POTION_MIXIN_DRANK" | "GUMMY_POLAR_BEAR_ATE" | "KAT_TIMESKIP" | "KAT_UPGRADE" | "KISMET_REROLL" | "KUUDRA_CHEST_OPEN" | "NPC_BUY" | "NPC_SELL" | "PEST_REPELLENT_USED" | "VISITOR_BARGAIN" | "WYRM_EVOKED";
+ type: "ACCESSORIES_SWAPPING" | "ALLOWANCE_GAIN" | "AUCTION_BOUGHT" | "AUCTION_LISTING_CHARGE" | "AUCTION_SOLD" | "AUTOMERCHANT_PROFIT_COLLECT" | "BANK_DEPOSIT" | "BANK_INTEREST" | "BANK_WITHDRAW" | "BASIC_REFORGE" | "BAZAAR_BUY_INSTANT" | "BAZAAR_BUY_ORDER" | "BAZAAR_SELL_INSTANT" | "BAZAAR_SELL_ORDER" | "BITS_PURSE_STATUS" | "BOOSTER_COOKIE_ATE" | "CAPSAICIN_EYEDROPS_USED" | "COMMUNITY_SHOP_BUY" | "CORPSE_DESECRATED" | "DIE_ROLLED" | "DRACONIC_SACRIFICE" | "DUNGEON_CHEST_OPEN" | "FORGED" | "GOD_POTION_DRANK" | "GOD_POTION_MIXIN_DRANK" | "GUMMY_POLAR_BEAR_ATE" | "KAT_TIMESKIP" | "KAT_UPGRADE" | "KISMET_REROLL" | "KUUDRA_CHEST_OPEN" | "NPC_BUY" | "NPC_SELL" | "PEST_REPELLENT_USED" | "VISITOR_BARGAIN" | "WYRM_EVOKED";
id: string;
items: {
itemId: string;
diff --git a/server/frontend/src/api.ts b/server/frontend/src/api.ts
index 22cf6ca..8ab6272 100644
--- a/server/frontend/src/api.ts
+++ b/server/frontend/src/api.ts
@@ -1,6 +1,13 @@
import createClient from "openapi-fetch";
import type { paths } from "./api-schema.js";
+import { query } from "@solidjs/router";
+export { type paths };
const apiRoot = import.meta.env.DEV ? "//localhost:8080/api" : "/api";
export const client = createClient<paths>({ baseUrl: apiRoot });
+
+export const getAnalysisList = query(
+ () => client.GET("/analysis/list"),
+ "getAnalysisList"
+)
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 6ab7d4e..3009300 100644
--- a/server/frontend/src/index.tsx
+++ b/server/frontend/src/index.tsx
@@ -1,10 +1,11 @@
/* @refresh reload */
import { render } from "solid-js/web";
+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");
@@ -16,6 +17,14 @@ if (!(root instanceof HTMLElement)) {
const routes: Array<RouteDefinition> = [
{ path: "/", component: lazy(() => import("./App.tsx")) },
{ path: "/test/", component: lazy(() => import("./Test.tsx")) },
+ { 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/tsconfig.json b/server/frontend/tsconfig.json
index 73eb483..548d331 100644
--- a/server/frontend/tsconfig.json
+++ b/server/frontend/tsconfig.json
@@ -6,8 +6,8 @@
"isolatedModules": true,
"jsx": "preserve",
"jsxImportSource": "solid-js",
- "module": "ESNext",
- "moduleResolution": "NodeNext",
+ "module": "Preserve",
+ "moduleResolution": "bundler",
"noEmit": true,
"noUncheckedIndexedAccess": true,
"strict": true,
diff --git a/server/frontend/vite.config.ts b/server/frontend/vite.config.ts
index 9ff59a1..b356c7e 100644
--- a/server/frontend/vite.config.ts
+++ b/server/frontend/vite.config.ts
@@ -1,8 +1,15 @@
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()],
+ plugins: [
+ solidPlugin(),
+ tailwindcss(),
+ devtools({
+ autoname: true
+ })
+ ],
server: {
port: 3000,
},
diff --git a/server/swagger/src/main/kotlin/moe/nea/ledger/server/core/api/OpenApiModel.kt b/server/swagger/src/main/kotlin/moe/nea/ledger/server/core/api/OpenApiModel.kt
index 8392c5c..af9d3f4 100644
--- a/server/swagger/src/main/kotlin/moe/nea/ledger/server/core/api/OpenApiModel.kt
+++ b/server/swagger/src/main/kotlin/moe/nea/ledger/server/core/api/OpenApiModel.kt
@@ -52,6 +52,7 @@ data class OpenApiParameter(
val name: String,
val description: String,
val schema: JsonSchema?,
+ val required: Boolean = true,
)
@Serializable