replace dev section with dev mode toggle, seed/delete mock data on toggle, bump to 2026.03.10.1

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-10 17:50:04 +01:00
parent d9f9006d4b
commit cd2d51ecbe
5 changed files with 229 additions and 212 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "abgeordnetenwatch-pwa",
"version": "2026.03.10",
"version": "2026.03.10.1",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -10,6 +10,7 @@ import { fetchTopics } from "@/shared/lib/aw-api"
import {
APP_VERSION,
BACKEND_URL,
STORAGE_KEYS,
VAPID_PUBLIC_KEY,
} from "@/shared/lib/constants"
import { useCallback, useEffect, useState } from "react"
@@ -52,7 +53,9 @@ export function SettingsPage() {
string | null
>(null)
const [devReload, setDevReload] = useState<string | null>(null)
const [devSeedMock, setDevSeedMock] = useState<string | null>(null)
const [devMode, setDevMode] = useState(
() => localStorage.getItem(STORAGE_KEYS.devMode) === "true",
)
useEffect(() => {
loadCachedResult(db).then((cached) => {
@@ -247,11 +250,32 @@ export function SettingsPage() {
{deviceId}
</span>
</div>
<div className="flex items-center justify-between px-4 py-3">
<span className="text-sm" id="dev-mode-label">
Entwicklermodus
</span>
<Switch
checked={devMode}
aria-labelledby="dev-mode-label"
onCheckedChange={async (checked) => {
setDevMode(checked)
localStorage.setItem(STORAGE_KEYS.devMode, String(checked))
try {
await fetch(`${BACKEND_URL}/legislation/seed-mock`, {
method: checked ? "POST" : "DELETE",
})
} catch {
// best-effort
}
}}
/>
</div>
</CardContent>
</Card>
</section>
{/* --- Developer --- */}
{/* --- Developer (visible only in dev mode) --- */}
{devMode && (
<section>
<h2 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide mb-2">
Entwickler
@@ -380,7 +404,11 @@ export function SettingsPage() {
return
}
for (const m of cached.mandates) {
follow("politician", m.politician.id, m.politician.label)
follow(
"politician",
m.politician.id,
m.politician.label,
)
}
setDevPoliticians(`${cached.mandates.length}`)
}}
@@ -410,41 +438,6 @@ export function SettingsPage() {
</Button>
</div>
</div>
<div className="flex items-center justify-between px-4 py-3">
<span className="text-sm">Testdaten laden</span>
<div className="flex items-center gap-2">
{devSeedMock && (
<span
className={`text-xs ${devSeedMock.startsWith("ok") ? "text-green-600" : "text-destructive"}`}
>
{devSeedMock}
</span>
)}
<Button
size="sm"
variant="outline"
onClick={async () => {
setDevSeedMock(null)
try {
const res = await fetch(
`${BACKEND_URL}/legislation/seed-mock`,
{ method: "POST" },
)
if (!res.ok) {
setDevSeedMock(`${res.status}`)
return
}
const data = await res.json()
setDevSeedMock(`ok (${data.count})`)
} catch (e) {
setDevSeedMock(String(e))
}
}}
>
Laden
</Button>
</div>
</div>
<div className="flex items-center justify-between px-4 py-3">
<span className="text-sm">Abgeordnete neu laden</span>
<div className="flex items-center gap-2">
@@ -468,6 +461,7 @@ export function SettingsPage() {
</CardContent>
</Card>
</section>
)}
</div>
)
}

View File

@@ -1,4 +1,4 @@
export const APP_VERSION = "2026.03.10"
export const APP_VERSION = "2026.03.10.1"
export const AW_API_BASE = "https://www.abgeordnetenwatch.de/api/v2"
export const AW_API_TIMEOUT_MS = 20_000
@@ -20,4 +20,5 @@ export const STORAGE_KEYS = {
follows: "agw_follows",
geoCache: "agw_geo_cache",
pushEnabled: "agw_push_enabled",
devMode: "agw_dev_mode",
} as const

View File

@@ -2,6 +2,7 @@ import { Hono } from "hono"
import { castVoteSchema } from "./schema"
import {
castVote,
deleteMockLegislation,
getLegislation,
getLegislationResults,
getLegislationText,
@@ -18,6 +19,11 @@ legislationRouter.post("/seed-mock", async (c) => {
return c.json({ ok: true, count: ids.length, ids }, 201)
})
legislationRouter.delete("/seed-mock", async (c) => {
const count = await deleteMockLegislation()
return c.json({ ok: true, deleted: count })
})
legislationRouter.get("/upcoming", async (c) => {
const items = await getUpcomingLegislation()
return c.json(items)

View File

@@ -1,4 +1,4 @@
import { and, desc, eq } from "drizzle-orm"
import { and, desc, eq, inArray } from "drizzle-orm"
import { db } from "../../shared/db/client"
import {
legislationSummaries,
@@ -254,3 +254,19 @@ export async function seedMockLegislation() {
return inserted
}
const MOCK_IDS = MOCK_LEGISLATION.map((m) => m.dipVorgangsId)
export async function deleteMockLegislation() {
const rows = await db
.select({ id: legislationTexts.id })
.from(legislationTexts)
.where(inArray(legislationTexts.dipVorgangsId, MOCK_IDS))
if (rows.length === 0) return 0
const ids = rows.map((r) => r.id)
await db.delete(legislationTexts).where(inArray(legislationTexts.id, ids))
return ids.length
}