rework onboarding screen 2: PWA install instructions with platform detection

screen 1 now combines app intro + endurance message + privacy note.
screen 2 shows platform-specific PWA install steps (iOS/Android/fallback)
with warning to install before entering data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 14:57:44 +01:00
parent 88933e9c13
commit fbbfb9e7a5
@@ -1,7 +1,7 @@
import { useForm } from "@tanstack/react-form";
import { useNavigate } from "@tanstack/react-router";
import { ClipboardList, Loader2, Lock, Route } from "lucide-react";
import { useEffect, useState } from "react";
import { Loader2, Lock, Route, Smartphone } from "lucide-react";
import { useEffect, useMemo, useState } from "react";
import { Button } from "@/shared/components/ui/button";
import { Input } from "@/shared/components/ui/input";
import { Label } from "@/shared/components/ui/label";
@@ -10,6 +10,13 @@ import { dbExec } from "@/shared/hooks/use-db";
import { GKV_KASSEN } from "@/shared/lib/gkv-kassen";
import { cn } from "@/shared/lib/utils";
function detectPlatform(): "ios" | "android" | "unknown" {
const ua = navigator.userAgent;
if (/iPad|iPhone|iPod/.test(ua)) return "ios";
if (/Android/.test(ua)) return "android";
return "unknown";
}
export function OnboardingForm() {
const navigate = useNavigate();
const [step, setStep] = useState(0);
@@ -47,8 +54,8 @@ export function OnboardingForm() {
return (
<div className="flex min-h-[80vh] flex-col items-center justify-center">
<div className="w-full max-w-md space-y-6">
{step === 0 && <IntroScreen1 />}
{step === 1 && <IntroScreen2 />}
{step === 0 && <IntroScreen />}
{step === 1 && <InstallScreen />}
{step === 2 && (
<>
<div className="space-y-2 text-center">
@@ -172,7 +179,7 @@ export function OnboardingForm() {
);
}
function IntroScreen1() {
function IntroScreen() {
return (
<div className="space-y-4 text-center">
<div className="mx-auto flex size-16 items-center justify-center rounded-full bg-primary/10">
@@ -184,41 +191,86 @@ function IntroScreen1() {
hilft dir, Kontaktversuche zu dokumentieren, Absagen festzuhalten und
deinen Kostenerstattungsantrag vorzubereiten.
</p>
<div className="flex flex-col gap-3 pt-2 text-left">
<div className="flex items-start gap-3">
<ClipboardList className="mt-0.5 size-5 shrink-0 text-primary" />
<p className="text-sm text-muted-foreground">
Verwalte deine Therapeutensuche an einem Ort vom Erstgespräch bis
zum fertigen Antrag.
</p>
</div>
<p className="text-sm text-muted-foreground">
Einen Therapieplatz zu finden dauert oft Wochen oder Monate. Der Weg ist
bürokratisch und braucht Ausdauer. TherapyFinder behält den Überblick,
damit du dich auf das Wesentliche konzentrieren kannst.
</p>
<div className="flex items-start gap-3 pt-2 text-left">
<Lock className="mt-0.5 size-5 shrink-0 text-primary" />
<p className="text-sm text-muted-foreground">
Alle Daten bleiben auf deinem Gerät es gibt keine Accounts und keine
Cloud.
</p>
</div>
</div>
);
}
function IntroScreen2() {
function InstallScreen() {
const platform = useMemo(() => detectPlatform(), []);
return (
<div className="space-y-4 text-center">
<div className="mx-auto flex size-16 items-center justify-center rounded-full bg-primary/10">
<Lock className="size-8 text-primary" />
<Smartphone className="size-8 text-primary" />
</div>
<h1 className="text-2xl font-bold">Dein Begleiter im Prozess</h1>
<h1 className="text-2xl font-bold">Als App installieren</h1>
<p className="text-muted-foreground">
Einen Therapieplatz zu finden dauert oft Wochen oder Monate. Der Weg ist
bürokratisch und braucht Ausdauer. TherapyFinder behält den Überblick,
damit du dich auf das Wesentliche konzentrieren kannst.
Füge TherapyFinder zu deinem Home-Bildschirm hinzu, damit du die App
jederzeit wie eine normale App öffnen kannst auch offline.
</p>
<p className="text-sm text-muted-foreground">
Alle Daten bleiben auf deinem Gerät es gibt keine Accounts und keine
Cloud.
{platform === "ios" && (
<div className="space-y-2 rounded-lg border p-4 text-left">
<p className="text-sm font-medium">So geht's auf dem iPhone/iPad:</p>
<ol className="list-inside list-decimal space-y-1 text-sm text-muted-foreground">
<li>
Tippe auf das Teilen-Symbol{" "}
<span className="inline-block align-text-bottom text-base">
</span>
</li>
<li>Wähle „Zum Home-Bildschirm"</li>
<li>Tippe auf „Hinzufügen"</li>
</ol>
</div>
)}
{platform === "android" && (
<div className="space-y-2 rounded-lg border p-4 text-left">
<p className="text-sm font-medium">So geht's auf Android:</p>
<ol className="list-inside list-decimal space-y-1 text-sm text-muted-foreground">
<li>
Tippe auf das Menü <span className="font-mono font-bold"></span>{" "}
oben rechts
</li>
<li>Wähle Zum Startbildschirm hinzufügen"</li>
<li>Bestätige mit „Hinzufügen"</li>
</ol>
</div>
)}
{platform === "unknown" && (
<div className="space-y-2 rounded-lg border p-4 text-left">
<p className="text-sm font-medium">So geht's:</p>
<p className="text-sm text-muted-foreground">
Nutze die Zum Home-Bildschirm hinzufügen"-Funktion deines Browsers,
um TherapyFinder als App zu installieren.
</p>
</div>
)}
<p className="text-sm font-medium text-amber-600 dark:text-amber-400">
Wichtig: Installiere die App jetzt, bevor du Daten eingibst. Nach der
Installation startet der Prozess von vorne.
</p>
</div>
);
}
function DotIndicator({ current }: { current: number }) {
const dots = ["intro", "motivation", "form"] as const;
const dots = ["intro", "install", "form"] as const;
return (
<div className="flex justify-center gap-2">
{dots.map((id, i) => (