2026-08-14 11:51:30 +02:00
|
|
|
// Offline skrive-kø, opprinnelig for scoreregistrering (ADR-028), utvidet
|
|
|
|
|
// 2026-08-14 til også å dekke kommentar-/bildeposting i rundefeeden (se
|
|
|
|
|
// round-messages.tsx). Ren IndexedDB, ingen avhengighet til service
|
|
|
|
|
// workeren -- gir umiddelbar, presis UI-tilbakemelding ("lagret lokalt,
|
|
|
|
|
// venter på synk") direkte fra kalleren, som er enklere og mer testbart
|
|
|
|
|
// enn å prøve å gjøre det samme inni en SW fetch-handler. Bevisst IKKE
|
|
|
|
|
// Background Sync API: iOS Safari støtter den ikke i det hele tatt, og en
|
|
|
|
|
// stor andel av klubb-/vennegjeng-brukerne er trolig på iPhone -- et
|
|
|
|
|
// enkelt window.online-lytter-mønster (se session-scorecard.tsx) fungerer
|
|
|
|
|
// overalt.
|
PWA er bygget og scratch/build-verifisert. Status:
Bygget:
Installerbar app: app/manifest.ts, appleWebApp-metadata for iOS, service worker (public/sw.js, håndskrevet — ingen next-pwa-avhengighet), public/offline.html.
Ikoner: enkelt grønt golf-flagg generert programmatisk (måtte omgå at verken PIL, rsvg-convert eller ImageMagick fantes i miljøet — løste det med et scratch npm-oppsett av sharp). Midlertidig, notert i FEATURE_BACKLOG.md for senere erstatning, slik du ba om. Erstattet samtidig den gamle apple-icon.png som faktisk var v0.app sin generiske plassholderlogo, ikke TeeCup-merkevare.
Full offline scoreregistrering: ny lib/offline-queue.ts (IndexedDB-kø) koblet inn i scorekort-skjermen. Slag/hull-resultat registrert offline vises umiddelbart som "Lagret lokalt · venter på synk", synkes automatisk når nettet er tilbake (pluss en manuell "Synkroniser nå"-knapp). Se ADR-028 for alle designvalgene (bl.a. hvorfor køen ligger i klientkoden og ikke i service workeren, og hvorfor jeg bevisst unngikk Background Sync API — iOS Safari støtter den ikke).
Viktig å være ærlig om: jeg har ingen nettleser-verktøy tilgjengelig i denne økten, så offline-flyten er verifisert med typesjekket produksjonsbuild + en kort container-boot (curl mot manifest/service worker/ikoner — alle svarer riktig), men ikke faktisk testet i en ekte nettleser (Chrome DevTools sin Offline-bryter, ordentlig "Legg til på hjemskjerm"). Anbefaler sterkt at du selv tester scorekort-siden med DevTools offline-modus før dere stoler på det i skarp bruk.
De fire UI-hullene du meldte inn er dokumentert i FEATURE_BACKLOG.md/CLAUDE.md med root cause (ikke bare notert som klager) — den mest presserende er /orgs/{id}/members, som er en reell rewrite/dynamisk-rute-kollisjon: siden er helt utilgjengelig i dag fordi Next.js sin /orgs/:path*-proxy-regel fanger kallet før selve siden noensinne nås.
Ingen migrasjon i denne runden — kun teecup_frontend trenger redeploy.
2026-07-19 23:51:08 +02:00
|
|
|
|
|
|
|
|
const DB_NAME = "teecup-offline"
|
|
|
|
|
const DB_VERSION = 1
|
|
|
|
|
const STORE = "pending-writes"
|
|
|
|
|
|
|
|
|
|
export type QueueEntry = {
|
|
|
|
|
id: number
|
|
|
|
|
url: string
|
|
|
|
|
method: string
|
|
|
|
|
body: unknown
|
|
|
|
|
matchId: string
|
|
|
|
|
createdAt: number
|
2026-08-10 23:19:29 +02:00
|
|
|
// Identifiserer HVILKEN rad på serveren denne skrivingen gjelder --
|
|
|
|
|
// default url (uendret oppførsel for kallere med én rad per URL, f.eks.
|
|
|
|
|
// round_hole). NØDVENDIG å sette eksplisitt når flere ulike rader deler
|
|
|
|
|
// samme URL (f.eks. scoring.py sin /hole-scores, som tar imot ALLE hull
|
|
|
|
|
// i en match på nøyaktig samme endepunkt) -- ellers ville versjons-
|
|
|
|
|
// kjedingen i flushQueue under blandet sammen versjonsnummer mellom
|
|
|
|
|
// urelaterte rader (se ADR-060).
|
|
|
|
|
resourceKey?: string
|
2026-08-14 11:51:30 +02:00
|
|
|
// Multipart-skrivinger (kommentar+bilde til rundefeeden, 2026-08-14) --
|
|
|
|
|
// `body` er da et FLATT objekt av string/Blob-felt i stedet for en
|
|
|
|
|
// JSON-serialiserbar verdi. IndexedDB sin structured-clone-algoritme
|
|
|
|
|
// lagrer Blob-verdier (og dermed File, som arver fra Blob) NATIVT --
|
|
|
|
|
// ingen egen blob-lagringsstruktur trengs. Uendret oppførsel
|
|
|
|
|
// (JSON.stringify + Content-Type: application/json) når feltet mangler.
|
|
|
|
|
isMultipart?: boolean
|
PWA er bygget og scratch/build-verifisert. Status:
Bygget:
Installerbar app: app/manifest.ts, appleWebApp-metadata for iOS, service worker (public/sw.js, håndskrevet — ingen next-pwa-avhengighet), public/offline.html.
Ikoner: enkelt grønt golf-flagg generert programmatisk (måtte omgå at verken PIL, rsvg-convert eller ImageMagick fantes i miljøet — løste det med et scratch npm-oppsett av sharp). Midlertidig, notert i FEATURE_BACKLOG.md for senere erstatning, slik du ba om. Erstattet samtidig den gamle apple-icon.png som faktisk var v0.app sin generiske plassholderlogo, ikke TeeCup-merkevare.
Full offline scoreregistrering: ny lib/offline-queue.ts (IndexedDB-kø) koblet inn i scorekort-skjermen. Slag/hull-resultat registrert offline vises umiddelbart som "Lagret lokalt · venter på synk", synkes automatisk når nettet er tilbake (pluss en manuell "Synkroniser nå"-knapp). Se ADR-028 for alle designvalgene (bl.a. hvorfor køen ligger i klientkoden og ikke i service workeren, og hvorfor jeg bevisst unngikk Background Sync API — iOS Safari støtter den ikke).
Viktig å være ærlig om: jeg har ingen nettleser-verktøy tilgjengelig i denne økten, så offline-flyten er verifisert med typesjekket produksjonsbuild + en kort container-boot (curl mot manifest/service worker/ikoner — alle svarer riktig), men ikke faktisk testet i en ekte nettleser (Chrome DevTools sin Offline-bryter, ordentlig "Legg til på hjemskjerm"). Anbefaler sterkt at du selv tester scorekort-siden med DevTools offline-modus før dere stoler på det i skarp bruk.
De fire UI-hullene du meldte inn er dokumentert i FEATURE_BACKLOG.md/CLAUDE.md med root cause (ikke bare notert som klager) — den mest presserende er /orgs/{id}/members, som er en reell rewrite/dynamisk-rute-kollisjon: siden er helt utilgjengelig i dag fordi Next.js sin /orgs/:path*-proxy-regel fanger kallet før selve siden noensinne nås.
Ingen migrasjon i denne runden — kun teecup_frontend trenger redeploy.
2026-07-19 23:51:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function openDb(): Promise<IDBDatabase> {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
const req = indexedDB.open(DB_NAME, DB_VERSION)
|
|
|
|
|
req.onupgradeneeded = () => {
|
|
|
|
|
const db = req.result
|
|
|
|
|
if (!db.objectStoreNames.contains(STORE)) {
|
|
|
|
|
const store = db.createObjectStore(STORE, { keyPath: "id", autoIncrement: true })
|
|
|
|
|
store.createIndex("matchId", "matchId", { unique: false })
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
req.onsuccess = () => resolve(req.result)
|
|
|
|
|
req.onerror = () => reject(req.error)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function enqueueWrite(
|
|
|
|
|
entry: Omit<QueueEntry, "id" | "createdAt">,
|
|
|
|
|
): Promise<number> {
|
|
|
|
|
const db = await openDb()
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
const tx = db.transaction(STORE, "readwrite")
|
|
|
|
|
const req = tx.objectStore(STORE).add({ ...entry, createdAt: Date.now() })
|
|
|
|
|
req.onsuccess = () => resolve(req.result as number)
|
|
|
|
|
req.onerror = () => reject(req.error)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function listQueue(matchId: string): Promise<QueueEntry[]> {
|
|
|
|
|
const db = await openDb()
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
const tx = db.transaction(STORE, "readonly")
|
|
|
|
|
const req = tx.objectStore(STORE).index("matchId").getAll(matchId)
|
|
|
|
|
req.onsuccess = () =>
|
|
|
|
|
resolve((req.result as QueueEntry[]).sort((a, b) => a.createdAt - b.createdAt))
|
|
|
|
|
req.onerror = () => reject(req.error)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function removeFromQueue(id: number): Promise<void> {
|
|
|
|
|
const db = await openDb()
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
const tx = db.transaction(STORE, "readwrite")
|
|
|
|
|
tx.objectStore(STORE).delete(id)
|
|
|
|
|
tx.oncomplete = () => resolve()
|
|
|
|
|
tx.onerror = () => reject(tx.error)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function queueCount(matchId: string): Promise<number> {
|
|
|
|
|
return (await listQueue(matchId)).length
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export type FlushOutcome = { entry: QueueEntry; ok: boolean; message?: string }
|
|
|
|
|
|
|
|
|
|
// Sender køede skrivinger i rekkefølge, ETT om gangen (ikke parallelt) --
|
|
|
|
|
// server-siden er en upsert per hull, så rekkefølgen har betydning hvis
|
|
|
|
|
// samme hull ble endret flere ganger offline. Stopper ved første ekte
|
|
|
|
|
// nettverksfeil (resten blir liggende til neste forsøk); en DEFINITIV
|
|
|
|
|
// HTTP-feil (f.eks. 409 "matchen er avgjort") fjernes fra køen og
|
|
|
|
|
// rapporteres tilbake i stedet for å bli hengende for alltid.
|
|
|
|
|
export async function flushQueue(matchId: string): Promise<FlushOutcome[]> {
|
|
|
|
|
const entries = await listQueue(matchId)
|
|
|
|
|
const outcomes: FlushOutcome[] = []
|
2026-08-10 18:34:31 +02:00
|
|
|
// Kjeder expected_version fremover for flere køede skrivinger til SAMME
|
2026-08-10 23:19:29 +02:00
|
|
|
// RESSURS i én flush-runde (2026-08-10, ADR-057 versjonssjekk; nøkkel
|
|
|
|
|
// generalisert til resourceKey 2026-08-10, ADR-060). Uten dette ville en
|
|
|
|
|
// spillers EGNE påfølgende offline-redigeringer av samme rad avvist
|
|
|
|
|
// hverandre som falske konflikter: kun den FØRSTE køede oppføringen har
|
|
|
|
|
// en expected_version som fortsatt stemmer med serveren -- lokal state
|
|
|
|
|
// (og dermed enqueue-tidspunktets versjon) oppdateres aldri mellom to
|
|
|
|
|
// sekvensielle avspillinger i samme batch, så oppføring 2 ville ellers
|
|
|
|
|
// sendt den samme (nå utdaterte) versjonen som 1.
|
|
|
|
|
//
|
|
|
|
|
// NØKKELEN er `resourceKey ?? url`, IKKE bare url -- for round_hole er
|
|
|
|
|
// url alene entydig (én URL per hull), men scoring.py sine /hole-scores/
|
|
|
|
|
// /hole-results tar imot ALLE hull i en match på nøyaktig samme URL. Uten
|
|
|
|
|
// en mer presis nøkkel ville versjonen fra hull 3 sin skriving blitt
|
|
|
|
|
// brukt som "forventet versjon" for en påfølgende, helt urelatert
|
|
|
|
|
// skriving til hull 7 -- verre enn ingen kjeding i det hele tatt.
|
|
|
|
|
const latestVersionByKey = new Map<string, number>()
|
PWA er bygget og scratch/build-verifisert. Status:
Bygget:
Installerbar app: app/manifest.ts, appleWebApp-metadata for iOS, service worker (public/sw.js, håndskrevet — ingen next-pwa-avhengighet), public/offline.html.
Ikoner: enkelt grønt golf-flagg generert programmatisk (måtte omgå at verken PIL, rsvg-convert eller ImageMagick fantes i miljøet — løste det med et scratch npm-oppsett av sharp). Midlertidig, notert i FEATURE_BACKLOG.md for senere erstatning, slik du ba om. Erstattet samtidig den gamle apple-icon.png som faktisk var v0.app sin generiske plassholderlogo, ikke TeeCup-merkevare.
Full offline scoreregistrering: ny lib/offline-queue.ts (IndexedDB-kø) koblet inn i scorekort-skjermen. Slag/hull-resultat registrert offline vises umiddelbart som "Lagret lokalt · venter på synk", synkes automatisk når nettet er tilbake (pluss en manuell "Synkroniser nå"-knapp). Se ADR-028 for alle designvalgene (bl.a. hvorfor køen ligger i klientkoden og ikke i service workeren, og hvorfor jeg bevisst unngikk Background Sync API — iOS Safari støtter den ikke).
Viktig å være ærlig om: jeg har ingen nettleser-verktøy tilgjengelig i denne økten, så offline-flyten er verifisert med typesjekket produksjonsbuild + en kort container-boot (curl mot manifest/service worker/ikoner — alle svarer riktig), men ikke faktisk testet i en ekte nettleser (Chrome DevTools sin Offline-bryter, ordentlig "Legg til på hjemskjerm"). Anbefaler sterkt at du selv tester scorekort-siden med DevTools offline-modus før dere stoler på det i skarp bruk.
De fire UI-hullene du meldte inn er dokumentert i FEATURE_BACKLOG.md/CLAUDE.md med root cause (ikke bare notert som klager) — den mest presserende er /orgs/{id}/members, som er en reell rewrite/dynamisk-rute-kollisjon: siden er helt utilgjengelig i dag fordi Next.js sin /orgs/:path*-proxy-regel fanger kallet før selve siden noensinne nås.
Ingen migrasjon i denne runden — kun teecup_frontend trenger redeploy.
2026-07-19 23:51:08 +02:00
|
|
|
for (const entry of entries) {
|
2026-08-10 23:19:29 +02:00
|
|
|
const key = entry.resourceKey ?? entry.url
|
2026-08-10 18:34:31 +02:00
|
|
|
let body: unknown = entry.body
|
|
|
|
|
if (
|
2026-08-14 11:51:30 +02:00
|
|
|
!entry.isMultipart &&
|
2026-08-10 18:34:31 +02:00
|
|
|
body !== null &&
|
|
|
|
|
typeof body === "object" &&
|
|
|
|
|
"expected_version" in body &&
|
2026-08-10 23:19:29 +02:00
|
|
|
latestVersionByKey.has(key)
|
2026-08-10 18:34:31 +02:00
|
|
|
) {
|
2026-08-10 23:19:29 +02:00
|
|
|
body = { ...body, expected_version: latestVersionByKey.get(key) }
|
2026-08-10 18:34:31 +02:00
|
|
|
}
|
PWA er bygget og scratch/build-verifisert. Status:
Bygget:
Installerbar app: app/manifest.ts, appleWebApp-metadata for iOS, service worker (public/sw.js, håndskrevet — ingen next-pwa-avhengighet), public/offline.html.
Ikoner: enkelt grønt golf-flagg generert programmatisk (måtte omgå at verken PIL, rsvg-convert eller ImageMagick fantes i miljøet — løste det med et scratch npm-oppsett av sharp). Midlertidig, notert i FEATURE_BACKLOG.md for senere erstatning, slik du ba om. Erstattet samtidig den gamle apple-icon.png som faktisk var v0.app sin generiske plassholderlogo, ikke TeeCup-merkevare.
Full offline scoreregistrering: ny lib/offline-queue.ts (IndexedDB-kø) koblet inn i scorekort-skjermen. Slag/hull-resultat registrert offline vises umiddelbart som "Lagret lokalt · venter på synk", synkes automatisk når nettet er tilbake (pluss en manuell "Synkroniser nå"-knapp). Se ADR-028 for alle designvalgene (bl.a. hvorfor køen ligger i klientkoden og ikke i service workeren, og hvorfor jeg bevisst unngikk Background Sync API — iOS Safari støtter den ikke).
Viktig å være ærlig om: jeg har ingen nettleser-verktøy tilgjengelig i denne økten, så offline-flyten er verifisert med typesjekket produksjonsbuild + en kort container-boot (curl mot manifest/service worker/ikoner — alle svarer riktig), men ikke faktisk testet i en ekte nettleser (Chrome DevTools sin Offline-bryter, ordentlig "Legg til på hjemskjerm"). Anbefaler sterkt at du selv tester scorekort-siden med DevTools offline-modus før dere stoler på det i skarp bruk.
De fire UI-hullene du meldte inn er dokumentert i FEATURE_BACKLOG.md/CLAUDE.md med root cause (ikke bare notert som klager) — den mest presserende er /orgs/{id}/members, som er en reell rewrite/dynamisk-rute-kollisjon: siden er helt utilgjengelig i dag fordi Next.js sin /orgs/:path*-proxy-regel fanger kallet før selve siden noensinne nås.
Ingen migrasjon i denne runden — kun teecup_frontend trenger redeploy.
2026-07-19 23:51:08 +02:00
|
|
|
let res: Response
|
|
|
|
|
try {
|
2026-08-14 11:51:30 +02:00
|
|
|
if (entry.isMultipart) {
|
|
|
|
|
const formData = new FormData()
|
|
|
|
|
for (const [k, v] of Object.entries(body as Record<string, string | Blob | undefined | null>)) {
|
|
|
|
|
if (v === undefined || v === null) continue
|
|
|
|
|
formData.append(k, v)
|
|
|
|
|
}
|
|
|
|
|
res = await fetch(entry.url, { method: entry.method, credentials: "include", body: formData })
|
|
|
|
|
} else {
|
|
|
|
|
res = await fetch(entry.url, {
|
|
|
|
|
method: entry.method,
|
|
|
|
|
headers: { "Content-Type": "application/json" },
|
|
|
|
|
credentials: "include",
|
|
|
|
|
body: JSON.stringify(body),
|
|
|
|
|
})
|
|
|
|
|
}
|
PWA er bygget og scratch/build-verifisert. Status:
Bygget:
Installerbar app: app/manifest.ts, appleWebApp-metadata for iOS, service worker (public/sw.js, håndskrevet — ingen next-pwa-avhengighet), public/offline.html.
Ikoner: enkelt grønt golf-flagg generert programmatisk (måtte omgå at verken PIL, rsvg-convert eller ImageMagick fantes i miljøet — løste det med et scratch npm-oppsett av sharp). Midlertidig, notert i FEATURE_BACKLOG.md for senere erstatning, slik du ba om. Erstattet samtidig den gamle apple-icon.png som faktisk var v0.app sin generiske plassholderlogo, ikke TeeCup-merkevare.
Full offline scoreregistrering: ny lib/offline-queue.ts (IndexedDB-kø) koblet inn i scorekort-skjermen. Slag/hull-resultat registrert offline vises umiddelbart som "Lagret lokalt · venter på synk", synkes automatisk når nettet er tilbake (pluss en manuell "Synkroniser nå"-knapp). Se ADR-028 for alle designvalgene (bl.a. hvorfor køen ligger i klientkoden og ikke i service workeren, og hvorfor jeg bevisst unngikk Background Sync API — iOS Safari støtter den ikke).
Viktig å være ærlig om: jeg har ingen nettleser-verktøy tilgjengelig i denne økten, så offline-flyten er verifisert med typesjekket produksjonsbuild + en kort container-boot (curl mot manifest/service worker/ikoner — alle svarer riktig), men ikke faktisk testet i en ekte nettleser (Chrome DevTools sin Offline-bryter, ordentlig "Legg til på hjemskjerm"). Anbefaler sterkt at du selv tester scorekort-siden med DevTools offline-modus før dere stoler på det i skarp bruk.
De fire UI-hullene du meldte inn er dokumentert i FEATURE_BACKLOG.md/CLAUDE.md med root cause (ikke bare notert som klager) — den mest presserende er /orgs/{id}/members, som er en reell rewrite/dynamisk-rute-kollisjon: siden er helt utilgjengelig i dag fordi Next.js sin /orgs/:path*-proxy-regel fanger kallet før selve siden noensinne nås.
Ingen migrasjon i denne runden — kun teecup_frontend trenger redeploy.
2026-07-19 23:51:08 +02:00
|
|
|
} catch {
|
|
|
|
|
break // fortsatt offline -- stopp, resten prøves igjen senere
|
|
|
|
|
}
|
|
|
|
|
if (res.ok) {
|
2026-08-10 18:34:31 +02:00
|
|
|
const updated: { version?: number } | null = await res.json().catch(() => null)
|
|
|
|
|
if (updated && typeof updated.version === "number") {
|
2026-08-10 23:19:29 +02:00
|
|
|
latestVersionByKey.set(key, updated.version)
|
2026-08-10 18:34:31 +02:00
|
|
|
}
|
PWA er bygget og scratch/build-verifisert. Status:
Bygget:
Installerbar app: app/manifest.ts, appleWebApp-metadata for iOS, service worker (public/sw.js, håndskrevet — ingen next-pwa-avhengighet), public/offline.html.
Ikoner: enkelt grønt golf-flagg generert programmatisk (måtte omgå at verken PIL, rsvg-convert eller ImageMagick fantes i miljøet — løste det med et scratch npm-oppsett av sharp). Midlertidig, notert i FEATURE_BACKLOG.md for senere erstatning, slik du ba om. Erstattet samtidig den gamle apple-icon.png som faktisk var v0.app sin generiske plassholderlogo, ikke TeeCup-merkevare.
Full offline scoreregistrering: ny lib/offline-queue.ts (IndexedDB-kø) koblet inn i scorekort-skjermen. Slag/hull-resultat registrert offline vises umiddelbart som "Lagret lokalt · venter på synk", synkes automatisk når nettet er tilbake (pluss en manuell "Synkroniser nå"-knapp). Se ADR-028 for alle designvalgene (bl.a. hvorfor køen ligger i klientkoden og ikke i service workeren, og hvorfor jeg bevisst unngikk Background Sync API — iOS Safari støtter den ikke).
Viktig å være ærlig om: jeg har ingen nettleser-verktøy tilgjengelig i denne økten, så offline-flyten er verifisert med typesjekket produksjonsbuild + en kort container-boot (curl mot manifest/service worker/ikoner — alle svarer riktig), men ikke faktisk testet i en ekte nettleser (Chrome DevTools sin Offline-bryter, ordentlig "Legg til på hjemskjerm"). Anbefaler sterkt at du selv tester scorekort-siden med DevTools offline-modus før dere stoler på det i skarp bruk.
De fire UI-hullene du meldte inn er dokumentert i FEATURE_BACKLOG.md/CLAUDE.md med root cause (ikke bare notert som klager) — den mest presserende er /orgs/{id}/members, som er en reell rewrite/dynamisk-rute-kollisjon: siden er helt utilgjengelig i dag fordi Next.js sin /orgs/:path*-proxy-regel fanger kallet før selve siden noensinne nås.
Ingen migrasjon i denne runden — kun teecup_frontend trenger redeploy.
2026-07-19 23:51:08 +02:00
|
|
|
await removeFromQueue(entry.id)
|
|
|
|
|
outcomes.push({ entry, ok: true })
|
|
|
|
|
} else {
|
2026-08-10 18:34:31 +02:00
|
|
|
const respBody: { detail?: { message?: string } } | null = await res.json().catch(() => null)
|
PWA er bygget og scratch/build-verifisert. Status:
Bygget:
Installerbar app: app/manifest.ts, appleWebApp-metadata for iOS, service worker (public/sw.js, håndskrevet — ingen next-pwa-avhengighet), public/offline.html.
Ikoner: enkelt grønt golf-flagg generert programmatisk (måtte omgå at verken PIL, rsvg-convert eller ImageMagick fantes i miljøet — løste det med et scratch npm-oppsett av sharp). Midlertidig, notert i FEATURE_BACKLOG.md for senere erstatning, slik du ba om. Erstattet samtidig den gamle apple-icon.png som faktisk var v0.app sin generiske plassholderlogo, ikke TeeCup-merkevare.
Full offline scoreregistrering: ny lib/offline-queue.ts (IndexedDB-kø) koblet inn i scorekort-skjermen. Slag/hull-resultat registrert offline vises umiddelbart som "Lagret lokalt · venter på synk", synkes automatisk når nettet er tilbake (pluss en manuell "Synkroniser nå"-knapp). Se ADR-028 for alle designvalgene (bl.a. hvorfor køen ligger i klientkoden og ikke i service workeren, og hvorfor jeg bevisst unngikk Background Sync API — iOS Safari støtter den ikke).
Viktig å være ærlig om: jeg har ingen nettleser-verktøy tilgjengelig i denne økten, så offline-flyten er verifisert med typesjekket produksjonsbuild + en kort container-boot (curl mot manifest/service worker/ikoner — alle svarer riktig), men ikke faktisk testet i en ekte nettleser (Chrome DevTools sin Offline-bryter, ordentlig "Legg til på hjemskjerm"). Anbefaler sterkt at du selv tester scorekort-siden med DevTools offline-modus før dere stoler på det i skarp bruk.
De fire UI-hullene du meldte inn er dokumentert i FEATURE_BACKLOG.md/CLAUDE.md med root cause (ikke bare notert som klager) — den mest presserende er /orgs/{id}/members, som er en reell rewrite/dynamisk-rute-kollisjon: siden er helt utilgjengelig i dag fordi Next.js sin /orgs/:path*-proxy-regel fanger kallet før selve siden noensinne nås.
Ingen migrasjon i denne runden — kun teecup_frontend trenger redeploy.
2026-07-19 23:51:08 +02:00
|
|
|
await removeFromQueue(entry.id)
|
2026-08-10 18:34:31 +02:00
|
|
|
outcomes.push({ entry, ok: false, message: respBody?.detail?.message ?? `Feil ${res.status}` })
|
PWA er bygget og scratch/build-verifisert. Status:
Bygget:
Installerbar app: app/manifest.ts, appleWebApp-metadata for iOS, service worker (public/sw.js, håndskrevet — ingen next-pwa-avhengighet), public/offline.html.
Ikoner: enkelt grønt golf-flagg generert programmatisk (måtte omgå at verken PIL, rsvg-convert eller ImageMagick fantes i miljøet — løste det med et scratch npm-oppsett av sharp). Midlertidig, notert i FEATURE_BACKLOG.md for senere erstatning, slik du ba om. Erstattet samtidig den gamle apple-icon.png som faktisk var v0.app sin generiske plassholderlogo, ikke TeeCup-merkevare.
Full offline scoreregistrering: ny lib/offline-queue.ts (IndexedDB-kø) koblet inn i scorekort-skjermen. Slag/hull-resultat registrert offline vises umiddelbart som "Lagret lokalt · venter på synk", synkes automatisk når nettet er tilbake (pluss en manuell "Synkroniser nå"-knapp). Se ADR-028 for alle designvalgene (bl.a. hvorfor køen ligger i klientkoden og ikke i service workeren, og hvorfor jeg bevisst unngikk Background Sync API — iOS Safari støtter den ikke).
Viktig å være ærlig om: jeg har ingen nettleser-verktøy tilgjengelig i denne økten, så offline-flyten er verifisert med typesjekket produksjonsbuild + en kort container-boot (curl mot manifest/service worker/ikoner — alle svarer riktig), men ikke faktisk testet i en ekte nettleser (Chrome DevTools sin Offline-bryter, ordentlig "Legg til på hjemskjerm"). Anbefaler sterkt at du selv tester scorekort-siden med DevTools offline-modus før dere stoler på det i skarp bruk.
De fire UI-hullene du meldte inn er dokumentert i FEATURE_BACKLOG.md/CLAUDE.md med root cause (ikke bare notert som klager) — den mest presserende er /orgs/{id}/members, som er en reell rewrite/dynamisk-rute-kollisjon: siden er helt utilgjengelig i dag fordi Next.js sin /orgs/:path*-proxy-regel fanger kallet før selve siden noensinne nås.
Ingen migrasjon i denne runden — kun teecup_frontend trenger redeploy.
2026-07-19 23:51:08 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return outcomes
|
|
|
|
|
}
|