diff --git a/src/routes/(app)/[slug]/+page.svelte b/src/routes/(app)/[slug]/+page.svelte index b825992..63b3cbe 100644 --- a/src/routes/(app)/[slug]/+page.svelte +++ b/src/routes/(app)/[slug]/+page.svelte @@ -128,17 +128,16 @@ import * as Chart from "$lib/components/ui/chart/index.js"; import * as Card from "$lib/components/ui/card/index.js"; import * as Select from "$lib/components/ui/select/index.js"; + import * as DropdownMenu from "$lib/components/ui/dropdown-menu/index.js"; + import { scaleUtc } from "d3-scale"; + import { Area, AreaChart, ChartClipPath } from "layerchart"; + import { curveNatural } from "d3-shape"; + import ChartContainer from "$lib/components/ui/chart/chart-container.svelte"; + import { cubicInOut } from "svelte/easing"; + import { ChevronDownIcon, Columns2 } from "@lucide/svelte"; let chartData = $state([]); - let newChartData = $derived( - chartData.filter((item: object) => { - if (item.sensorId === selectedESP) { - return { ...item }; - } - }), - ); - let timeRange = $state("90d"); const selectedLabel = $derived.by(() => { @@ -155,8 +154,8 @@ }); const filteredData = $derived( - newChartData.filter((item) => { - const now = new Date(); + chartData.filter((item) => { + const referenceDate = new Date("2024-06-30"); let daysToSubtract = 90; if (timeRange === "30d") { daysToSubtract = 30; @@ -164,132 +163,75 @@ daysToSubtract = 7; } - const cutoffDate = new Date(now.getTime() - daysToSubtract * 24 * 60 * 60 * 1000); - return item.date >= cutoffDate; + referenceDate.setDate(referenceDate.getDate() - daysToSubtract); + return item.date >= referenceDate; }), ); const chartConfig = { - temperature: { label: "Temperature (°C)", color: "var(--chart-1)" }, - humidity: { label: "Humidity (%)", color: "var(--chart-2)" }, - altitude: { label: "Altitude (m)", color: "var(--chart-3)" }, - pressure: { label: "Pressure (kPa)", color: "var(--chart-4)" }, + temperature: { label: "Temperature", color: "var(--chart-1)" }, + humidity: { label: "Humidity", color: "var(--chart-2)" }, + altitude: { label: "Altitude", color: "var(--chart-1)" }, + pressure: { label: "Pressure", color: "var(--chart-2)" }, } satisfies Chart.ChartConfig; - let selectedSensor = $state("temperature"); - - const sensorLabel = $derived.by(() => { - switch (selectedSensor) { - case "altitude": - return "Altitude (m)"; - case "humidity": - return "Humidity (%)"; - case "pressure": - return "Pressure (kPa)"; - case "temperature": - default: - return "Temperature (°C)"; - } - }); - - const sensorOptions = [ + const table = $state([ { - key: "temperature", - label: chartConfig.temperature.label, - color: chartConfig.temperature.color, + visible: true, + key: "altitude", + label: chartConfig.altitude.label, + color: chartConfig.altitude.color, }, { + visible: true, key: "humidity", label: chartConfig.humidity.label, color: chartConfig.humidity.color, }, { + visible: true, key: "pressure", label: chartConfig.pressure.label, color: chartConfig.pressure.color, }, { - key: "altitude", - label: chartConfig.altitude.label, - color: chartConfig.altitude.color, + visible: true, + key: "temperature", + label: chartConfig.temperature.label, + color: chartConfig.temperature.color, }, - ]; + ]); - const selectedSensorConfig = $derived( - sensorOptions.find((s) => s.key === selectedSensor) || sensorOptions[0], + const tables = $derived( + table + .filter((item) => { + if (item.visible) return true; + return false; + }) + .map((item) => { + return { key: item.key, label: item.label, color: item.color }; + }), ); - let esps = $state([]); - let selectedESP = $state("Select ESP sensor"); - - let isLoading = $state(false); - let hasError = $state(false); - let onOpenChange = $state(async (open: boolean) => { - console.log("onOpenChange called with:", open); if (open) { - console.log("Starting to load statistics..."); - isLoading = true; - hasError = false; - try { - console.log(`Fetching stats for floor: ${data.slug}`); - const response = await fetch(`/${data.slug}/stats`); - console.log("Stats response status:", response.status); + const response = await fetch("/" + data.slug + "/stats"); + const number = await response.json(); + console.log(number); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const rawData = await response.json(); - console.log("Raw stats data:", rawData); - const allesps = rawData.map((i: any) => { - return i.sensorId; - }); - esps = allesps.filter((v: any, i: any, a: any) => a.indexOf(v) === i); - - if (!rawData || rawData.length === 0) { - console.log("No data available for statistics"); - chartData = []; - } else { - chartData = rawData.map((obj: any) => { - return { ...obj, date: new Date(obj.date) }; - }); - console.log("Processed chart data:", chartData); - } - } catch (error) { - console.error("Error loading statistics:", error); - hasError = true; - chartData = []; - } finally { - isLoading = false; - } + chartData = number.map((obj: any) => { + return { ...obj, date: new Date(obj.date) }; + }); } }); - - function exportCSV(): void { - let csv = "data:text/csv;charset=utf-8,"; - - const headers = "time," + selectedSensorConfig.key; - csv += headers + "\n"; - - newChartData.forEach((obj: any) => { - csv += obj.date.toISOString() + "," + obj[selectedSensorConfig.key] + "\n"; - }); - - const encodedUri = encodeURI(csv); - const link = document.createElement("a"); - link.setAttribute("href", encodedUri); - link.setAttribute("download", selectedESP + "_" + selectedSensorConfig.key + "_data.csv"); - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - }
+

Floor {data.slug}

{#if data.hasConfig && data.floorConfig} +
{#if data.floorConfig.image}
@@ -366,6 +308,7 @@ {/if}
{:else} +
{mqttMessage} @@ -373,7 +316,7 @@ {/if} Statistics - +
Statistics for floor {data.slug} @@ -382,31 +325,28 @@
- - - {selectedESP} - - - {#each esps as esp} - {esp} + + + {#snippet child({ props })} + + {/snippet} + + + {#each table as column} + + {column.key} + {/each} - - - - - - {sensorLabel} - - - Altitude (m) - Humidity (%) - Pressure (kPa) - Temperature (°C) - - + + - + {selectedLabel} @@ -416,221 +356,77 @@ -
- {#if isLoading} -
-
-
-

Loading statistics...

-
-
- {:else if hasError} -
-
-

Error loading statistics

-

Please try again later

-
-
- {:else if newChartData.length === 0} -
-
-

No data available

-

- Statistics will appear once sensor data is collected -

-
-
- {:else} - - - {selectedSensorConfig.label} - - -
- {#if filteredData.length > 0} - -
-
-
Latest
-
- {filteredData[filteredData.length - 1]?.[selectedSensor]?.toFixed(1)} - {#if selectedSensor === "temperature"}°C - {:else if selectedSensor === "humidity"}% - {:else if selectedSensor === "pressure"}kPa - {:else if selectedSensor === "altitude"}m - {/if} -
-
-
-
Average
-
- {( - filteredData.reduce((sum, item) => sum + item[selectedSensor], 0) / - filteredData.length - ).toFixed(1)} - {#if selectedSensor === "temperature"}°C - {:else if selectedSensor === "humidity"}% - {:else if selectedSensor === "pressure"}kPa - {:else if selectedSensor === "altitude"}m - {/if} -
-
-
-
Range
-
- {Math.min(...filteredData.map((d) => d[selectedSensor])).toFixed(1)} - {Math.max( - ...filteredData.map((d) => d[selectedSensor]), - ).toFixed(1)} - {#if selectedSensor === "temperature"}°C - {:else if selectedSensor === "humidity"}% - {:else if selectedSensor === "pressure"}kPa - {:else if selectedSensor === "altitude"}m - {/if} -
-
-
+ + { + return v.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + }); + }, + }, - -
- - - {#if filteredData.length > 1} - {@const minValue = Math.min(...filteredData.map((d) => d[selectedSensor]))} - {@const maxValue = Math.max(...filteredData.map((d) => d[selectedSensor]))} - {@const valueRange = maxValue - minValue || 1} - {@const padding = 30} - {@const chartWidth = 800 - padding * 2} - {@const chartHeight = 400 - padding * 2} - - - - - - - - - - - {maxValue.toFixed(1)} - {((minValue + maxValue) / 2).toFixed(1)} - {minValue.toFixed(1)} - - - {#if filteredData.length > 0} - {@const firstDate = filteredData[0].date} - {@const lastDate = filteredData[filteredData.length - 1].date} - {@const midIndex = Math.floor(filteredData.length / 2)} - {@const midDate = filteredData[midIndex].date} - - - {@const formatDate = (date) => { - if (timeRange === "7d") { - return date.toLocaleDateString("en-US", { - weekday: "short", - month: "short", - day: "numeric", - }); - } else if (timeRange === "30d") { - return date.toLocaleDateString("en-US", { - month: "short", - day: "numeric", - }); - } else { - return date.toLocaleDateString("en-US", { month: "short" }); - } - }} - - {formatDate(firstDate)} - {formatDate(midDate)} - {formatDate(lastDate)} - {/if} - - - { - const x = padding + (i / (filteredData.length - 1)) * chartWidth; - const y = - padding + - chartHeight - - ((d[selectedSensor] - minValue) / valueRange) * chartHeight; - return `${x},${y}`; - }) - .join(" ")} - /> - {/if} - -
- -
- Data from {filteredData[0]?.date?.toLocaleDateString()} to {filteredData[ - filteredData.length - 1 - ]?.date?.toLocaleDateString()} -
- {:else} -
-
-

No data available

-

- No data found for the selected time period -

-
-
- {/if} -
-
-
- {/if} + yAxis: { format: () => "" }, + }} + > + {#snippet marks({ series, getAreaProps })} + + + + + + + + + + + + {#each series as s, i (s.key)} + + {/each} + + {/snippet} + {#snippet tooltip()} + { + return v.toLocaleDateString("en-US", { + month: "long", + }); + }} + indicator="line" + /> + {/snippet} + + diff --git a/src/routes/(app)/[slug]/stats/+server.ts b/src/routes/(app)/[slug]/stats/+server.ts index 226f421..32fa2db 100644 --- a/src/routes/(app)/[slug]/stats/+server.ts +++ b/src/routes/(app)/[slug]/stats/+server.ts @@ -1,28 +1,9 @@ import { db } from "$lib/server/db"; import * as table from "$lib/server/db/schema" -import { eq } from "drizzle-orm"; -export const GET = async (event) => { - // Get all sensor data (like the original working version) - console.log(event.locals.session.userId) - const rawData = await db.select({ - altitude: table.sensorData.altitude, - humidity: table.sensorData.humidity, - pressure: table.sensorData.pressure, - temperature: table.sensorData.temperature, - date: table.sensorData.time, - sensorId: table.sensorData.sensor, - user: table.sensors.user, - }).from(table.sensorData).innerJoin(table.sensors, eq(table.sensors.id, table.sensorData.sensor)).where(eq(table.sensors.user, event.locals.session.userId)); - - // Scale pressure values to be in a similar range as other sensors - // Divide by 1000 to convert from Pa to kPa (more reasonable scale) - const data = rawData.map(item => ({ - ...item, - pressure: Math.round((item.pressure / 1000) * 10) / 10 // Convert to kPa with 1 decimal place - })); - - console.log(`Returning ${data.length} data points`); +export const GET = async () => { + const data = await db.select({ altitude: table.sensorData.altitude, humidity: table.sensorData.humidity, pressure: table.sensorData.pressure, temperature: table.sensorData.temperature, date: table.sensorData.time, }).from(table.sensorData); + console.log(data); return new Response(JSON.stringify(data), { headers: {