From 95f8b7a8db06557b56b636d2fe88564e8fb9b38c Mon Sep 17 00:00:00 2001 From: Moritz Ruth Date: Mon, 17 Jun 2024 13:38:21 +0200 Subject: [PATCH] Replace the fake backend with actual API calls --- package.json | 1 + pnpm-lock.yaml | 9 ++++ src/backend.ts | 83 ++++++++++++++++++++++++++++++------ src/fakeServerState.ts | 97 ------------------------------------------ vite.config.ts | 8 ++++ 5 files changed, 87 insertions(+), 111 deletions(-) delete mode 100644 src/fakeServerState.ts diff --git a/package.json b/package.json index 3cf79cc..475002c 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "chartjs-adapter-date-fns": "^3.0.0", "color-alpha": "^2.0.0", "date-fns": "^3.6.0", + "ky": "^1.3.0", "lodash-es": "^4.17.21", "nanoid": "^5.0.7", "temporal-polyfill": "^0.2.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d27a6aa..593c07e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,6 +38,9 @@ importers: date-fns: specifier: ^3.6.0 version: 3.6.0 + ky: + specifier: ^1.3.0 + version: 1.3.0 lodash-es: specifier: ^4.17.21 version: 4.17.21 @@ -965,6 +968,10 @@ packages: kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + ky@1.3.0: + resolution: {integrity: sha512-QUViPXlgP6NKA57IAPff/aZSmRA6qs9wKxlEpayBorwRZG+x2LG7jD4kXh8lnH3q/gkUr64NyZ7kwErUEZJmlw==} + engines: {node: '>=18'} + local-pkg@0.5.0: resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} engines: {node: '>=14'} @@ -2254,6 +2261,8 @@ snapshots: kolorist@1.8.0: {} + ky@1.3.0: {} + local-pkg@0.5.0: dependencies: mlly: 1.7.0 diff --git a/src/backend.ts b/src/backend.ts index e762af8..e1e5f72 100644 --- a/src/backend.ts +++ b/src/backend.ts @@ -1,14 +1,29 @@ import { Temporal } from "temporal-polyfill" -import { cloneDeep } from "lodash-es" -import { fakeServerState } from "@/fakeServerState" +import ky from "ky" + +const api = ky.extend({ + prefixUrl: "https://ias-tea-axum.shuttleapp.rs/", + mode: "cors" +}) export interface TypeOfTea { id: string name: string + notes: string + waterTemperatureInCelsius: number steepingTime: Temporal.Duration registrationTimestamp: Temporal.Instant } +interface WireTypeOfTea { + id: number + tea_name: string + tea_notes: string + water_temp: number // Celsius + steeping_seconds: number + registration_timestamp: string +} + export interface SteepingTimeChange { id: string typeOfTeaId: string @@ -22,34 +37,74 @@ export interface BrewingEvent { timestamp: Temporal.Instant } +interface WireBrewingEvent { + change_id: string + tea_id: number + steeping_seconds: number + steeping_tested_time: string +} + export interface Configuration { defaultSteepingTime: Temporal.Duration feedbackTimeout: Temporal.Duration } +interface WireConfiguration { + default_steeping_time: number // seconds + feedback_timeout: number // seconds +} + const delay = () => new Promise(resolve => setTimeout(resolve, 1000)) export async function fetchConfiguration(): Promise { - await delay() - return cloneDeep(fakeServerState.configuration) + const result = await api.get("configuration").json() + + return { + defaultSteepingTime: Temporal.Duration.from({ seconds: result.default_steeping_time }).round({ largestUnit: "minutes" }), + feedbackTimeout: Temporal.Duration.from({ seconds: result.feedback_timeout }).round({ largestUnit: "hour" }), + } } -export async function updateConfiguration(value: Configuration) { - await delay() - fakeServerState.configuration = cloneDeep(value) +export async function updateConfiguration(data: Configuration) { + const wireData: WireConfiguration = { + default_steeping_time: data.defaultSteepingTime.total("seconds"), + feedback_timeout: data.feedbackTimeout.total("seconds") + } + + await api.post("configuration", { json: wireData }) } export async function fetchBrewingEvents(): Promise { - await delay() - return cloneDeep(fakeServerState.brewingEvents) + const result = await api.get("brewing-events").json() + return result.map(e => ({ + id: e.change_id, + typeOfTeaId: e.tea_id.toString(), + timestamp: Temporal.PlainDateTime.from(e.steeping_tested_time).toZonedDateTime("UTC").toInstant() + })) } export async function fetchTypesOfTeaById(): Promise> { - await delay() - return new Map(fakeServerState.typesOfTea.map(t => [t.id, t])) + const result = await api.get("types-of-tea").json() + + result.sort((a, b) => a.id - b.id) + + return new Map(result.map(t => [t.id.toString(), { + id: t.id.toString(), + name: t.tea_name, + notes: t.tea_notes, + waterTemperatureInCelsius: t.water_temp, + steepingTime: Temporal.Duration.from({ seconds: t.steeping_seconds }).round({ largestUnit: "minutes" }), // round for balancing + registrationTimestamp: Temporal.PlainDateTime.from(t.registration_timestamp).toZonedDateTime("UTC").toInstant() + }])) } -export async function updateTypeOfTea(id: string, data: Pick) { - await delay() - Object.assign(fakeServerState.typesOfTea.find(t => t.id === id)!, data) +export async function updateTypeOfTea(id: string, data: Pick) { + const wireData: Pick = { + tea_name: data.name, + tea_notes: data.notes, + water_temp: data.waterTemperatureInCelsius, + steeping_seconds: data.steepingTime.total("seconds") + } + + await api.put(`types-of-tea/${id}`, { json: wireData }) } \ No newline at end of file diff --git a/src/fakeServerState.ts b/src/fakeServerState.ts deleted file mode 100644 index 7a81a98..0000000 --- a/src/fakeServerState.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { Temporal } from "temporal-polyfill" -import type { BrewingEvent, Configuration, SteepingTimeChange, TypeOfTea } from "@/backend" -import { random } from "lodash-es" -import { nanoid } from "nanoid" - -const localTimeZoneId = Temporal.Now.timeZoneId() - -const configuration: Configuration = { - defaultSteepingTime: Temporal.Duration.from({ minutes: 4, seconds: 30 }), - feedbackTimeout: Temporal.Duration.from({ hours: 2 }) -} - -const typesOfTea: TypeOfTea[] = [ - { - id: nanoid(), - name: "Pfefferminz", - steepingTime: Temporal.Duration.from({ minutes: 5, seconds: 20 }), - registrationTimestamp: Temporal.ZonedDateTime.from({ timeZone: localTimeZoneId, year: 2024, month: 2, day: 20, hour: 14, minute: 24 }).toInstant() - }, - { - id: nanoid(), - name: "Türkischer Apfel", - steepingTime: Temporal.Duration.from({ minutes: 3, seconds: 50 }), - registrationTimestamp: Temporal.ZonedDateTime.from({ timeZone: localTimeZoneId, year: 2024, month: 3, day: 1, hour: 16, minute: 1 }).toInstant() - }, - { - id: nanoid(), - name: "Fenchel-Anis-Kümmel", - steepingTime: Temporal.Duration.from({ minutes: 8 }), - registrationTimestamp: Temporal.ZonedDateTime.from({ timeZone: localTimeZoneId, year: 2024, month: 3, day: 9, hour: 9, minute: 55 }).toInstant() - }, - { - id: nanoid(), - name: "Hagebutte", - steepingTime: Temporal.Duration.from({ minutes: 4, seconds: 30 }), - registrationTimestamp: Temporal.ZonedDateTime.from({ timeZone: localTimeZoneId, year: 2024, month: 3, day: 29, hour: 15, minute: 34 }).toInstant() - }, -] - -const steepingTimeChanges: SteepingTimeChange[] = [] -const brewingEvents: BrewingEvent[] = [] - -for (const type of typesOfTea) { - const numberOfBrewingEvents = random(5, 10) - let nextTimestamp = type.registrationTimestamp - let lastTwoSteepingTimeSeconds: number[] = [type.steepingTime.total({ unit: "seconds" })] - - for (let i = 0; i < numberOfBrewingEvents; i++) { - brewingEvents.push({ - id: nanoid(), - typeOfTeaId: type.id, - timestamp: nextTimestamp, - }) - - if (random(0, 100) < 20) { - let newSteepingTimeSeconds: number - const direction = random(0, 1) === 1 ? "increase" : "decrease" - const currentSeconds = lastTwoSteepingTimeSeconds[0] - - if (lastTwoSteepingTimeSeconds.length === 1) { - newSteepingTimeSeconds = direction === "increase" ? currentSeconds + 30 : currentSeconds - 30 - } else { - const lastDelta = lastTwoSteepingTimeSeconds[1] - lastTwoSteepingTimeSeconds[0] - const lastDirection = lastDelta > 0 ? "increase" : "decrease" - if (direction === lastDirection) { - newSteepingTimeSeconds = direction === "increase" ? currentSeconds + 30 : currentSeconds - 30 - } else { - newSteepingTimeSeconds = Math.round(Math.abs(lastTwoSteepingTimeSeconds[0] - lastTwoSteepingTimeSeconds[1]) / 2) - } - } - - steepingTimeChanges.push({ - id: nanoid(), - typeOfTeaId: type.id, - timestamp: nextTimestamp.add({ hours: random(0, 1), minutes: random(0, 30) }), - newValue: Temporal.Duration.from({ seconds: newSteepingTimeSeconds }) - }) - } - - nextTimestamp = nextTimestamp.add(Temporal.Duration.from({ hours: random(0, 14 * 24), minutes: random(0, 60) })) - } -} - -brewingEvents.sort((a, b) => Temporal.Instant.compare(a.timestamp, b.timestamp)) -steepingTimeChanges.sort((a, b) => Temporal.Instant.compare(a.timestamp, b.timestamp)) - -export const fakeServerState: { - configuration: Configuration - typesOfTea: TypeOfTea[] - steepingTimeChanges: SteepingTimeChange[] - brewingEvents: BrewingEvent[] -} = { - configuration, - typesOfTea, - steepingTimeChanges, - brewingEvents -} \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 61ea996..a9a5c09 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -19,5 +19,13 @@ export default defineConfig({ alias: { "@": resolve(__dirname, "./src") } + }, + server: { + proxy: { + "/api": { + target: "http://localhost:8000", + ws: true + } + } } })