Replace non-null assertion with optional chaining on dataRequestCooldownUntil to satisfy oxlint no-non-null-assertion rule. https://claude.ai/code/session_017Hvmof82Xr3UwKFc3pnC4X
161 lines
5.6 KiB
TypeScript
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 & 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'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>
|
|
);
|
|
}
|