import { useEffect, useMemo, useState } from "react"; import { Moon, Sun, ArrowRight, ArrowLeft, Check, RotateCcw, Bookmark, History, Trash2 } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Slider } from "@/components/ui/slider"; import { Textarea } from "@/components/ui/textarea"; import { Input } from "@/components/ui/input"; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "@/components/ui/sheet"; import { cn } from "@/lib/utils"; import { toast } from "@/hooks/use-toast"; import logo from "@/assets/jessica-logo.png"; import { useAIReflection } from "@/useAIReflection"; const FEELINGS = [ "easy", "energizing", "calming", "playful", "thoughtful", "emotionally warm", "exciting", "comfortable", "awkward but interesting", "draining", "confusing", "surface-level", "tense", "disconnected", "natural", "hard to read", ]; const SLIDERS = [ "I felt comfortable being myself", "Conversation flowed naturally", "Curiosity felt mutual", "I felt heard and understood", "I found myself overthinking", "We handled awkward moments well", "I wanted to know more about them", "The energy between us felt balanced", ]; const EASIEST = [ "conversation", "humor", "curiosity", "shared energy", "silence", "emotional openness", "practical conversation", "getting comfortable", "nothing yet", ]; type State = { name: string; meeting: string; date: string; feelings: string[]; sticking: string; sliders: number[]; easiest: string[]; surprise: string; curious: string; }; type Saved = State & { savedAt: string; reflection?: string; ponderings?: string[] }; const STORAGE_KEY = "between-dates-state"; const THEME_KEY = "between-dates-theme"; const SAVED_KEY = "between-dates-reflections"; const initialState: State = { name: "", meeting: "", date: "", feelings: [], sticking: "", sliders: SLIDERS.map(() => 3), easiest: [], surprise: "", curious: "", }; const SCREENS = 6; // 0 welcome, 1 details, 2 first impressions, 3 dynamic, 4 looking back, 5 reflection function formatList(items: string[]): string { if (items.length === 0) return ""; if (items.length === 1) return items[0]; if (items.length === 2) return `${items[0]} and ${items[1]}`; return `${items.slice(0, -1).join(", ")}, and ${items[items.length - 1]}`; } function normalizeState(raw?: Partial | null): State { const rawFeelings = Array.isArray(raw?.feelings) ? raw.feelings : []; const rawEasiest = Array.isArray(raw?.easiest) ? raw.easiest : []; const rawSliders = Array.isArray(raw?.sliders) ? raw.sliders : []; const feelings = Array.from(new Set(rawFeelings .map((item) => item === "awkward but intrigued" ? "awkward but interesting" : item) .filter(Boolean))); const easiest = Array.from(new Set(rawEasiest .map((item) => item === "nothing clicked naturally yet" ? "nothing yet" : item) .filter(Boolean))); const sliders = SLIDERS.map((_, i) => { const value = rawSliders[i]; return typeof value === "number" && Number.isFinite(value) ? value : 3; }); return { ...initialState, ...raw, feelings, easiest, sliders, sticking: raw?.sticking ?? "", surprise: raw?.surprise ?? "", curious: raw?.curious ?? "", }; } function formatDate(iso: string): string { try { const d = new Date(iso); return d.toLocaleString(undefined, { month: "short", day: "numeric", year: "numeric", hour: "numeric", minute: "2-digit", }); } catch { return iso; } } export default function Index() { const [screen, setScreen] = useState(0); const [state, setState] = useState(initialState); const [dark, setDark] = useState(false); const [historyOpen, setHistoryOpen] = useState(false); const [saved, setSaved] = useState([]); useEffect(() => { try { const t = localStorage.getItem(THEME_KEY); if (t === "dark") { setDark(true); document.documentElement.classList.add("dark"); } // Always start each session fresh - do not restore active draft answers. // Previous reflections are kept under SAVED_KEY for the History archive only. localStorage.removeItem(STORAGE_KEY); const savedRaw = localStorage.getItem(SAVED_KEY); if (savedRaw) { const parsed: Saved[] = JSON.parse(savedRaw); setSaved(parsed.map((p) => ({ ...normalizeState(p), savedAt: p.savedAt }))); } } catch {} }, []); const toggleDark = () => { const next = !dark; setDark(next); document.documentElement.classList.toggle("dark", next); try { localStorage.setItem(THEME_KEY, next ? "dark" : "light"); } catch {} }; const toggleIn = (key: "feelings" | "easiest", v: string) => setState((s) => ({ ...s, [key]: s[key].includes(v) ? s[key].filter((x) => x !== v) : [...s[key], v], })); const reset = () => { setState(initialState); try { localStorage.setItem(STORAGE_KEY, JSON.stringify(initialState)); } catch {} setScreen(0); }; const saveReflection = (reflectionOverride?: string, ponderingsOverride?: string[]) => { try { const rawList: Saved[] = JSON.parse(localStorage.getItem(SAVED_KEY) || "[]"); const list: Saved[] = rawList.map((p) => ({ ...normalizeState(p), savedAt: p.savedAt })); const normalized = normalizeState(state); const reflection = reflectionOverride && reflectionOverride.trim().length > 0 ? reflectionOverride : buildSummary(normalized); const ponderings = ponderingsOverride && ponderingsOverride.length > 0 ? ponderingsOverride : buildPonderingsList(normalized); const next: Saved[] = [{ ...normalized, savedAt: new Date().toISOString(), reflection, ponderings }, ...list].slice(0, 50); localStorage.setItem(SAVED_KEY, JSON.stringify(next)); setSaved(next); setState(initialState); localStorage.setItem(STORAGE_KEY, JSON.stringify(initialState)); setScreen(0); toast({ title: "Saved.", description: "Private and just yours." }); } catch { toast({ title: "Couldn't save", description: "Storage is unavailable." }); } }; const deleteSaved = (savedAt: string) => { const next = saved.filter((s) => s.savedAt !== savedAt); setSaved(next); try { localStorage.setItem(SAVED_KEY, JSON.stringify(next)); } catch {} }; return (
Between Dates
Your check-ins
{screen > 0 && screen < SCREENS - 1 && (
)}
{screen === 0 && setScreen(1)} />} {screen === 1 && (
setState((s) => ({ ...s, name: v }))} placeholder="David" /> setState((s) => ({ ...s, meeting: v }))} placeholder="3rd meeting" /> setState((s) => ({ ...s, date: v }))} placeholder="5/5/25" />
)} {screen === 2 && ( toggleIn("feelings", v)} /> setState((s) => ({ ...s, sticking: v }))} placeholder="a moment, a look, something someone said..." /> )} {screen === 3 && (
{SLIDERS.map((label, i) => ( setState((s) => { const next = [...s.sliders]; next[i] = v; return { ...s, sliders: next }; }) } /> ))}
)} {screen === 4 && (

What felt easiest between you?

toggleIn("easiest", v)} />
setState((s) => ({ ...s, surprise: v }))} placeholder="Something you didn't expect…" /> setState((s) => ({ ...s, curious: v }))} placeholder="something about them, a question you have, something unsaid..." />
)} {screen === 5 && ( )} {screen > 0 && screen < SCREENS - 1 && (
); } /* ---------------- components ---------------- */ function LogoBadge({ size = 32 }: { size?: number }) { return (
Jessica Feldman, Dating and Marriage Coach
); } function Progress({ current, total }: { current: number; total: number }) { const pct = (current / total) * 100; return (
Step {current} of {total}
); } function Welcome({ onStart }: { onStart: () => void }) { return (

Between Dates

The date just ended.
Let's figure out how you really feel about it.

); } function Screen({ title, subtitle, children, }: { title: string; subtitle?: string; children: React.ReactNode }) { return (

{title}

{subtitle &&

{subtitle}

}
{children}
); } function ChipGrid({ options, selected, onToggle, stacked, }: { options: string[]; selected: string[]; onToggle: (v: string) => void; stacked?: boolean }) { return (
{options.map((o) => { const on = selected.includes(o); return ( ); })}
); } function Field({ label, value, onChange, placeholder, }: { label: string; value: string; onChange: (v: string) => void; placeholder?: string }) { return (