Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Merge account deletion: soft delete for 30 days, staff restore and purge, ghost for what remains (identity 0037) | 1 | import { useState } from "react"; |
| 2 | import { Form, Link, useNavigation } from "react-router"; | |
| 3 | ||
| 4 | import { ACCOUNT_RESTORE_DAYS, type AccountDeletion } from "@g1t/contracts"; | |
| 5 | ||
| 6 | import { DangerAction } from "./danger-zone"; | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 7 | import { ErrorText } from "./ui"; |
| Merge account deletion: soft delete for 30 days, staff restore and purge, ghost for what remains (identity 0037) | 8 | import { |
| 9 | AlertDialog, | |
| 10 | AlertDialogCancel, | |
| 11 | AlertDialogContent, | |
| 12 | AlertDialogDescription, | |
| 13 | AlertDialogFooter, | |
| 14 | AlertDialogHeader, | |
| 15 | AlertDialogTitle, | |
| 16 | } from "./ui/alert-dialog"; | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 17 | import { Button } from "./ui/button"; |
| 18 | import { Card } from "./ui/card"; | |
| Merge account deletion: soft delete for 30 days, staff restore and purge, ghost for what remains (identity 0037) | 19 | import { FieldDescription, FieldLabel, Field as FormField } from "./ui/field"; |
| 20 | import { Input as TextInput } from "./ui/input"; | |
| 21 | import { accountDeletionRefusal, confirmsUsername, whatAccountDeletionTakes } from "../lib/account-deletion"; | |
| 22 | ||
| 23 | /** | |
| 24 | * Deleting your account, in one step, typed out to confirm, with your | |
| 25 | * password unless you signed in within the last few minutes. Kept for | |
| 26 | * `ACCOUNT_RESTORE_DAYS`, when support can restore it. Workspaces you own | |
| 27 | * alone are listed instead of the button until they have another owner or | |
| 28 | * are deleted. | |
| 29 | */ | |
| 30 | export function DeleteAccountAction({ | |
| 31 | username, | |
| 32 | deletion, | |
| 33 | hasPassword, | |
| 34 | error, | |
| 35 | defaultOpen = false, | |
| 36 | }: { | |
| 37 | username: string; | |
| 38 | deletion: AccountDeletion | null; | |
| 39 | hasPassword: boolean; | |
| 40 | error?: string; | |
| 41 | /** Open on first render: for previews of the dialog. */ | |
| 42 | defaultOpen?: boolean; | |
| 43 | }) { | |
| 44 | const [open, setOpen] = useState(Boolean(error) || defaultOpen); | |
| 45 | const [confirm, setConfirm] = useState(""); | |
| 46 | const navigation = useNavigation(); | |
| 47 | const deleting = navigation.state !== "idle" && navigation.formData?.get("intent") === "delete-account"; | |
| 48 | const refusal = accountDeletionRefusal(deletion); | |
| 49 | const goes = deletion ? whatAccountDeletionTakes(deletion) : []; | |
| 50 | if (deletion?.protected) { | |
| 51 | return ( | |
| 52 | <DangerAction title="Delete your account" action={null}> | |
| 53 | <span role="status">{refusal}</span> | |
| 54 | </DangerAction> | |
| 55 | ); | |
| 56 | } | |
| 57 | return ( | |
| 58 | <DangerAction | |
| 59 | title="Delete your account" | |
| 60 | action={ | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 61 | <Button type="button" variant="destructive" disabled={Boolean(refusal)} onClick={() => setOpen(true)}> |
| Merge account deletion: soft delete for 30 days, staff restore and purge, ghost for what remains (identity 0037) | 62 | Delete account |
| 63 | </Button> | |
| 64 | } | |
| 65 | > | |
| 66 | {refusal ? ( | |
| 67 | <> | |
| 68 | <span role="status">{refusal}</span> | |
| 69 | <ul className="mt-3 space-y-2"> | |
| 70 | {deletion?.sole_owner_of.map((workspace) => ( | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 71 | <Card asChild key={workspace.slug} radius="lg" className="px-3 py-2"> |
| 72 | <li> | |
| 73 | <p className="text-fg"> | |
| 74 | <span className="font-medium">{workspace.name}</span>{" "} | |
| 75 | <span className="font-mono text-xs text-faint">{workspace.slug}</span> | |
| 76 | </p> | |
| 77 | <p className="mt-1 flex flex-wrap gap-x-3 gap-y-1 text-xs"> | |
| 78 | {workspace.members > 1 ? ( | |
| 79 | <Link to={`/${workspace.slug}/-/members`} className="text-accent underline-offset-4 hover:underline"> | |
| 80 | Make someone else an owner | |
| 81 | </Link> | |
| 82 | ) : null} | |
| 83 | <Link to={`/${workspace.slug}/-/settings`} className="text-accent underline-offset-4 hover:underline"> | |
| 84 | Delete the workspace | |
| Merge account deletion: soft delete for 30 days, staff restore and purge, ghost for what remains (identity 0037) | 85 | </Link> |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 86 | </p> |
| 87 | {workspace.billing ? <p className="mt-1 text-xs text-warn">{workspace.billing}</p> : null} | |
| 88 | </li> | |
| 89 | </Card> | |
| Merge account deletion: soft delete for 30 days, staff restore and purge, ghost for what remains (identity 0037) | 90 | ))} |
| 91 | </ul> | |
| 92 | </> | |
| 93 | ) : ( | |
| 94 | <> | |
| 95 | You sign out everywhere and leave every workspace at once. Your account is kept for {ACCOUNT_RESTORE_DAYS}{" "} | |
| 96 | days, and support can restore it until then. | |
| 97 | </> | |
| 98 | )} | |
| 99 | <AlertDialog | |
| 100 | open={open} | |
| 101 | onOpenChange={(next) => { | |
| 102 | setOpen(next); | |
| 103 | setConfirm(""); | |
| 104 | }} | |
| 105 | > | |
| 106 | <AlertDialogContent> | |
| 107 | <Form method="post" className="grid gap-4"> | |
| 108 | <input type="hidden" name="intent" value="delete-account" /> | |
| 109 | <AlertDialogHeader> | |
| 110 | <AlertDialogTitle>Delete your account?</AlertDialogTitle> | |
| 111 | <AlertDialogDescription> | |
| 112 | Everything you sign in with stops working now. For {ACCOUNT_RESTORE_DAYS} days, support can restore | |
| 113 | your account; after that it is gone for good. | |
| 114 | </AlertDialogDescription> | |
| 115 | </AlertDialogHeader> | |
| 116 | {goes.length > 0 ? ( | |
| 117 | <div className="grid gap-1.5"> | |
| 118 | <p className="text-sm font-medium">What goes with it</p> | |
| 119 | <ul className="list-disc space-y-1 pl-5 text-sm text-muted"> | |
| 120 | {goes.map((line) => ( | |
| 121 | <li key={line}>{line}</li> | |
| 122 | ))} | |
| 123 | </ul> | |
| 124 | </div> | |
| 125 | ) : null} | |
| 126 | <ul className="list-disc space-y-1.5 pl-5 text-sm text-muted"> | |
| 127 | <li>You are signed out everywhere. Your access tokens, SSH keys, deploy keys you added and applications stop working.</li> | |
| 128 | <li>You leave every workspace, team and repository. Workspaces you share keep everything in them.</li> | |
| 129 | <li>Your profile is no longer found, and nobody can add you to anything.</li> | |
| 130 | <li> | |
| 131 | To get it back within {ACCOUNT_RESTORE_DAYS} days, write to support from one of its addresses. After | |
| 132 | that your addresses, keys, profile and settings are removed for good. | |
| 133 | </li> | |
| 134 | <li> | |
| 135 | What you wrote stays where it is: issues, pull requests, comments and reviews show as{" "} | |
| 136 | <span className="font-mono text-fg">ghost</span> once it is removed. | |
| 137 | </li> | |
| 138 | <li> | |
| 139 | The username <span className="font-mono text-fg">{username}</span> is never given to anyone else. | |
| 140 | </li> | |
| 141 | </ul> | |
| 142 | <FormField> | |
| 143 | <FieldLabel htmlFor="confirm-account"> | |
| 144 | Type <span className="font-mono text-fg">{username}</span> to confirm | |
| 145 | </FieldLabel> | |
| 146 | <TextInput | |
| 147 | id="confirm-account" | |
| 148 | name="confirm" | |
| 149 | value={confirm} | |
| 150 | spellCheck={false} | |
| 151 | autoCapitalize="off" | |
| 152 | autoComplete="off" | |
| 153 | onChange={(event) => setConfirm(event.target.value)} | |
| 154 | className="font-mono" | |
| 155 | /> | |
| 156 | </FormField> | |
| 157 | {hasPassword ? ( | |
| 158 | <FormField> | |
| 159 | <FieldLabel htmlFor="confirm-password">Your password</FieldLabel> | |
| 160 | <TextInput id="confirm-password" name="password" type="password" autoComplete="current-password" /> | |
| 161 | <FieldDescription>Not needed if you signed in within the last 10 minutes.</FieldDescription> | |
| 162 | </FormField> | |
| 163 | ) : ( | |
| 164 | <p className="text-sm text-muted"> | |
| 165 | Your account signs in with GitHub only: sign out, sign in with GitHub again, and delete it within 10 | |
| 166 | minutes. | |
| 167 | </p> | |
| 168 | )} | |
| 169 | <ErrorText>{error}</ErrorText> | |
| 170 | <AlertDialogFooter> | |
| 171 | <AlertDialogCancel type="button">Cancel</AlertDialogCancel> | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 172 | <Button type="submit" variant="destructive" disabled={!confirmsUsername(username, confirm) || deleting}> |
| Merge account deletion: soft delete for 30 days, staff restore and purge, ghost for what remains (identity 0037) | 173 | {deleting ? "Deleting…" : "Delete account"} |
| 174 | </Button> | |
| 175 | </AlertDialogFooter> | |
| 176 | </Form> | |
| 177 | </AlertDialogContent> | |
| 178 | </AlertDialog> | |
| 179 | </DangerAction> | |
| 180 | ); | |
| 181 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.