L7 Offline & PWA: Sync-Seite, Offline-Ansicht, Sync-Badge, Installationshinweis, Logout-Schutz

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-14 17:20:44 +02:00
co-authored by Claude Opus 5
parent 4dd330b9fd
commit f0620f9c3b
15 changed files with 1633 additions and 13 deletions
+7
View File
@@ -0,0 +1,7 @@
import { requireModule } from "@/server/modules";
/** Modul-Gate „field" für die Offline-Ansicht (Lane L7). */
export default async function ModuleLayout({ children }: Readonly<{ children: React.ReactNode }>) {
await requireModule("field");
return <>{children}</>;
}
+17
View File
@@ -0,0 +1,17 @@
import { getTranslations } from "next-intl/server";
import { OfflineView } from "@/components/offline/offline-view";
/**
* `/m/offline` — offline fallback (lane L7): order list/detail from the local bundle. Cached by the
* service worker; the content is rendered on the client from IndexedDB, so the cached HTML stays
* valid while offline.
*/
export default async function OfflinePage() {
const t = await getTranslations("offline.view");
return (
<main className="space-y-4 p-4">
<h1 className="text-[26px]">{t("title")}</h1>
<OfflineView />
</main>
);
}
+7
View File
@@ -0,0 +1,7 @@
import { requireModule } from "@/server/modules";
/** Modul-Gate „field" für die Synchronisationsseite (Lane L7). */
export default async function ModuleLayout({ children }: Readonly<{ children: React.ReactNode }>) {
await requireModule("field");
return <>{children}</>;
}
+7 -13
View File
@@ -1,23 +1,17 @@
import { getTranslations } from "next-intl/server";
import { OnlineBadge } from "@/components/field/online-badge";
import { card } from "@/components/field/ui";
import { SyncPanel } from "@/components/offline/sync-panel";
/**
* PLACEHOLDER (lane L4) — `/m/sync` belongs to lane L7 (Offline/PWA), which replaces this page
* with the outbox status, errors and conflicts. Until then it shows the connection state and that
* ops are sent immediately (src/lib/field/client-ops.ts).
* `/m/sync` (lane L7, Spec §22/§23.4, US-012): connection, last sync, pending changes and uploads
* with progress, errors/conflicts in plain language (retry / discard), storage usage, reset. The
* data lives on the device (IndexedDB), so the panel is a client component.
*/
export default async function SyncPlaceholderPage() {
const t = await getTranslations("field.sync");
export default async function SyncPage() {
const t = await getTranslations("offline.sync");
return (
<main className="space-y-4 p-4">
<h1 className="text-[26px]">{t("title")}</h1>
<section className={`${card} space-y-3`}>
<p className="text-[13px] font-semibold text-muted-foreground">{t("status")}</p>
<OnlineBadge large />
<p className="text-[15px]">{t("immediate")}</p>
<p className="text-[14px] text-muted-foreground">{t("offlineHint")}</p>
</section>
<SyncPanel />
</main>
);
}