Features: - User authentication via WorkOS (Apple, Google, Phone) - Daily check-in dialog for usage tracking - Calendar view with usage heatmap - Personalized reduction plan generator after 7 days of tracking - Custom OKLCH color theme with DM Sans and Space Mono fonts Tech stack: - Next.js 15 with App Router - Shadcn/UI components - Prisma with SQLite database - Tailwind CSS v4 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
118 lines
3.5 KiB
TypeScript
118 lines
3.5 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from "@/components/ui/dialog";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Label } from "@/components/ui/label";
|
|
|
|
interface OnboardingDialogProps {
|
|
open: boolean;
|
|
onComplete: (preferences: {
|
|
substanceType: string;
|
|
stayLoggedIn: boolean;
|
|
}) => Promise<void>;
|
|
}
|
|
|
|
export function OnboardingDialog({ open, onComplete }: OnboardingDialogProps) {
|
|
const [step, setStep] = useState<"substance" | "login">("substance");
|
|
const [substanceType, setSubstanceType] = useState<string>("");
|
|
const [stayLoggedIn, setStayLoggedIn] = useState<boolean | null>(null);
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
|
|
const handleSubstanceSelect = (type: string) => {
|
|
setSubstanceType(type);
|
|
setStep("login");
|
|
};
|
|
|
|
const handleLoginPreference = async (stay: boolean) => {
|
|
setStayLoggedIn(stay);
|
|
setIsSubmitting(true);
|
|
await onComplete({
|
|
substanceType,
|
|
stayLoggedIn: stay,
|
|
});
|
|
setIsSubmitting(false);
|
|
};
|
|
|
|
return (
|
|
<Dialog open={open}>
|
|
<DialogContent className="sm:max-w-md" onInteractOutside={(e) => e.preventDefault()}>
|
|
<DialogHeader>
|
|
<DialogTitle>
|
|
{step === "substance" ? "What are you tracking?" : "Stay Logged In?"}
|
|
</DialogTitle>
|
|
<DialogDescription>
|
|
{step === "substance"
|
|
? "Select the substance you want to track and reduce."
|
|
: "Would you like to stay logged in on this device?"}
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
{step === "substance" ? (
|
|
<div className="space-y-4 py-4">
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<Button
|
|
variant="outline"
|
|
size="lg"
|
|
className="h-24 flex flex-col gap-2"
|
|
onClick={() => handleSubstanceSelect("nicotine")}
|
|
>
|
|
<span className="text-2xl">🚬</span>
|
|
<span>Nicotine</span>
|
|
</Button>
|
|
<Button
|
|
variant="outline"
|
|
size="lg"
|
|
className="h-24 flex flex-col gap-2"
|
|
onClick={() => handleSubstanceSelect("weed")}
|
|
>
|
|
<span className="text-2xl">🌿</span>
|
|
<span>Cannabis</span>
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div className="space-y-4 py-4">
|
|
<p className="text-sm text-muted-foreground text-center">
|
|
If you choose to stay logged in, you won't need to sign in again
|
|
for 30 days on this device.
|
|
</p>
|
|
<div className="flex gap-4 justify-center">
|
|
<Button
|
|
variant="outline"
|
|
size="lg"
|
|
onClick={() => handleLoginPreference(false)}
|
|
disabled={isSubmitting}
|
|
>
|
|
No, log me out
|
|
</Button>
|
|
<Button
|
|
size="lg"
|
|
onClick={() => handleLoginPreference(true)}
|
|
disabled={isSubmitting}
|
|
>
|
|
Yes, stay logged in
|
|
</Button>
|
|
</div>
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
className="w-full"
|
|
onClick={() => setStep("substance")}
|
|
disabled={isSubmitting}
|
|
>
|
|
Back
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|