brackt/app/components/user/settings/PrivacySection.tsx
Claude f92bc9e549
Fix no-non-null-assertion lint error in PrivacySection
Replace non-null assertion with optional chaining on dataRequestCooldownUntil
to satisfy oxlint no-non-null-assertion rule.

https://claude.ai/code/session_017Hvmof82Xr3UwKFc3pnC4X
2026-05-11 00:17:55 +00:00

161 lines
5.6 KiB
TypeScript

import { Form } from "react-router";
import { useState } from "react";
import { Button } from "~/components/ui/button";
import { Label } from "~/components/ui/label";
import { Textarea } from "~/components/ui/textarea";
import {
AlertDialog,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
AlertDialogCancel,
} from "~/components/ui/alert-dialog";
type Props = {
userEmail: string;
dataRequestCooldownUntil: Date | null;
dataRequestSuccess?: boolean;
dataRequestError?: string;
};
export function PrivacySection({
userEmail,
dataRequestCooldownUntil,
dataRequestSuccess,
dataRequestError,
}: Props) {
const [confirmed, setConfirmed] = useState(false);
const [dialogOpen, setDialogOpen] = useState(false);
const handleDialogOpenChange = (open: boolean) => {
setDialogOpen(open);
if (!open) setConfirmed(false);
};
const inCooldown = dataRequestCooldownUntil !== null && dataRequestCooldownUntil > new Date();
return (
<div className="space-y-8">
<div>
<h2 className="text-lg font-semibold">Data &amp; Privacy</h2>
<p className="text-sm text-muted-foreground">
Request a copy of your data or permanently delete your account.
</p>
</div>
{/* Data Request */}
<div className="rounded-lg border p-5 space-y-4">
<div>
<h3 className="text-sm font-semibold">Request My Data</h3>
<p className="mt-1 text-sm text-muted-foreground">
Under GDPR and CCPA you have the right to a copy of all personal data
we hold about you. We will respond within 30 days to{" "}
<span className="font-medium">{userEmail}</span>.
</p>
</div>
{dataRequestSuccess && (
<p className="text-sm text-green-500">
Request received. We&apos;ll email you within 30 days.
</p>
)}
{dataRequestError && (
<p className="text-sm text-destructive">{dataRequestError}</p>
)}
{inCooldown && !dataRequestSuccess && (
<p className="text-sm text-muted-foreground">
Your data request is being processed. You can submit another request
after {dataRequestCooldownUntil?.toLocaleDateString()}.
</p>
)}
{!dataRequestSuccess && !inCooldown && (
<Form method="post" className="space-y-3">
<div className="space-y-2">
<Label htmlFor="dataRequestNotes">
Additional notes{" "}
<span className="text-muted-foreground font-normal">(optional)</span>
</Label>
<Textarea
id="dataRequestNotes"
name="dataRequestNotes"
placeholder="e.g. specific data types you are interested in"
rows={3}
className="resize-none"
/>
</div>
<Button
type="submit"
name="intent"
value="request-data"
variant="outline"
>
Submit Data Request
</Button>
</Form>
)}
</div>
{/* Danger Zone */}
<div className="rounded-lg border border-destructive/40 p-5 space-y-4">
<div>
<h3 className="text-sm font-semibold text-destructive">Delete Account</h3>
<p className="mt-1 text-sm text-muted-foreground">
Permanently remove your personal information from Brackt. Your leagues and
draft history will be preserved but your name, email, and avatar will be
wiped. This action cannot be undone.
</p>
</div>
<AlertDialog open={dialogOpen} onOpenChange={handleDialogOpenChange}>
<AlertDialogTrigger asChild>
<Button variant="destructive" size="sm">
Delete Account
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete your account?</AlertDialogTitle>
<AlertDialogDescription>
Your email, display name, username, and avatar will be permanently
erased. Your leagues and draft history remain intact but will show{" "}
<span className="font-medium">Deleted User</span> in your place. You
will be signed out immediately.
</AlertDialogDescription>
</AlertDialogHeader>
<div className="flex items-start gap-3 rounded-md border border-destructive/30 bg-destructive/5 p-3">
<input
type="checkbox"
id="confirm-delete"
checked={confirmed}
onChange={(e) => setConfirmed(e.target.checked)}
className="mt-0.5 h-4 w-4 shrink-0"
/>
<Label htmlFor="confirm-delete" className="text-sm leading-snug cursor-pointer">
I understand this is permanent and cannot be undone
</Label>
</div>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<Form method="post">
<Button
type="submit"
name="intent"
value="delete-account"
variant="destructive"
disabled={!confirmed}
>
Delete My Account
</Button>
</Form>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
</div>
);
}