-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Staging #762
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Staging #762
Changes from all commits
1a0ad90
1622cca
eea8827
3a2860e
397090c
cc52f73
b66393c
aaf0678
0214edb
3253ec3
181f606
281e215
de0ea3f
80e1af9
2763b45
3ac1712
37e5190
b3415c3
3364807
d57570c
1d37659
797a8cd
57936a1
3b338fb
9a75453
acbf4b6
f59fb33
156c2fa
d8065ba
f892073
9d5ff36
934e336
67a393a
f443556
aac4405
84fce88
0ff71d8
c504834
46e49d4
89e730a
69500a8
f8d0d2e
89af662
bd6b50d
d1b1b30
2aa5007
b086ebc
b80a60c
47d4ffc
21f52a0
56cd464
0474e57
bf38622
ae40c1f
56665d0
7b8efca
e5edcbe
00bfe7c
29d6be2
c438279
c7ddfde
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| 'use server'; | ||
|
|
||
| import { headers } from 'next/headers'; | ||
| import { auth } from '@/lib/auth'; | ||
|
|
||
| export async function deleteUser() { | ||
| const headersList = await headers(); | ||
| const session = await auth.api.getSession({ headers: headersList }); | ||
|
|
||
| if (!session) return { success: false, error: 'Session not found' }; | ||
|
|
||
| const userId = session.user?.id; | ||
|
|
||
| if (!userId) return { success: false, error: 'User not found' }; | ||
|
|
||
| const { success, message } = await auth.api.deleteUser({ | ||
| body: { | ||
| callbackURL: '/', | ||
| }, | ||
| headers: headersList, | ||
| }); | ||
|
|
||
| return { success, message }; | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,132 @@ | ||||||
| 'use client'; | ||||||
|
|
||||||
| import { | ||||||
| Dialog, | ||||||
| DialogContent, | ||||||
| DialogDescription, | ||||||
| DialogHeader, | ||||||
| DialogTitle, | ||||||
| DialogTrigger, | ||||||
| } from '@/components/ui/dialog'; | ||||||
| import { | ||||||
| Form, | ||||||
| FormControl, | ||||||
| FormDescription, | ||||||
| FormField, | ||||||
| FormItem, | ||||||
| FormLabel, | ||||||
| } from '@/components/ui/form'; | ||||||
| import { SettingsCard } from '@/components/settings/settings-card'; | ||||||
| import { zodResolver } from '@hookform/resolvers/zod'; | ||||||
| import { AlertTriangle, Route } from 'lucide-react'; | ||||||
| import { Button } from '@/components/ui/button'; | ||||||
| import { Input } from '@/components/ui/input'; | ||||||
| import { useRouter } from 'next/navigation'; | ||||||
| import { deleteUser } from '@/actions/user'; | ||||||
| import { useTranslations } from 'next-intl'; | ||||||
| import { useForm } from 'react-hook-form'; | ||||||
| import { useState } from 'react'; | ||||||
| import { toast } from 'sonner'; | ||||||
| import * as z from 'zod'; | ||||||
|
|
||||||
| const CONFIRMATION_TEXT = 'DELETE'; | ||||||
|
|
||||||
| const formSchema = z.object({ | ||||||
| confirmText: z.string().refine((val) => val === CONFIRMATION_TEXT, { | ||||||
| message: `Please type ${CONFIRMATION_TEXT} to confirm`, | ||||||
| }), | ||||||
| }); | ||||||
|
|
||||||
| function DeleteAccountDialog() { | ||||||
| const [isOpen, setIsOpen] = useState(false); | ||||||
| const [isDeleting, setIsDeleting] = useState(false); | ||||||
| const t = useTranslations(); | ||||||
| const router = useRouter(); | ||||||
|
|
||||||
| const form = useForm<z.infer<typeof formSchema>>({ | ||||||
| resolver: zodResolver(formSchema), | ||||||
| defaultValues: { | ||||||
| confirmText: '' as 'DELETE', | ||||||
| }, | ||||||
| }); | ||||||
|
|
||||||
| async function onSubmit(values: z.infer<typeof formSchema>) { | ||||||
| setIsDeleting(true); | ||||||
| try { | ||||||
| const { success, message } = await deleteUser(); | ||||||
| if (!success) { | ||||||
| toast.error(message); | ||||||
| return; | ||||||
| } | ||||||
| toast.success('Account deleted successfully'); | ||||||
| router.push('/'); | ||||||
| setIsOpen(false); | ||||||
| } catch (error) { | ||||||
| console.error('Failed to delete account:', error); | ||||||
| toast.error('Failed to delete account'); | ||||||
| } finally { | ||||||
| setIsDeleting(false); | ||||||
| form.reset(); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| return ( | ||||||
| <Dialog open={isOpen} onOpenChange={setIsOpen}> | ||||||
| <DialogTrigger asChild> | ||||||
| <Button variant="destructive">{t('pages.settings.danger-zone.deleteAccount')}</Button> | ||||||
| </DialogTrigger> | ||||||
| <DialogContent> | ||||||
| <DialogHeader> | ||||||
| <DialogTitle>{t('pages.settings.danger-zone.title')}</DialogTitle> | ||||||
| <DialogDescription>{t('pages.settings.danger-zone.description')}</DialogDescription> | ||||||
| </DialogHeader> | ||||||
|
|
||||||
| <div className="border-destructive/50 bg-destructive/10 flex items-center gap-2 rounded-md border px-3 py-2 text-sm text-red-600 dark:text-red-400"> | ||||||
| <AlertTriangle className="h-4 w-4" /> | ||||||
| <span>{t('pages.settings.danger-zone.warning')}</span> | ||||||
| </div> | ||||||
|
|
||||||
| <Form {...form}> | ||||||
| <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6"> | ||||||
| <FormField | ||||||
| control={form.control} | ||||||
| name="confirmText" | ||||||
| render={({ field }) => ( | ||||||
| <FormItem> | ||||||
| <FormLabel>Confirmation</FormLabel> | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Missing translation for form label. The form label "Confirmation" is not using the translation system, unlike other UI text in this component. Replace the hardcoded string with a translation key: - <FormLabel>Confirmation</FormLabel>
+ <FormLabel>{t('pages.settings.danger-zone.confirmation_label')}</FormLabel>Make sure to add the corresponding translation key in your localization files. 📝 Committable suggestion
Suggested change
|
||||||
| <FormDescription>{t('pages.settings.danger-zone.confirmation')}</FormDescription> | ||||||
| <FormControl> | ||||||
| <Input placeholder="DELETE" {...field} className="max-w-[200px]" /> | ||||||
| </FormControl> | ||||||
| </FormItem> | ||||||
| )} | ||||||
| /> | ||||||
|
|
||||||
| <div className="flex justify-end"> | ||||||
| <Button type="submit" variant="destructive" disabled={isDeleting}> | ||||||
| {isDeleting | ||||||
| ? t('pages.settings.danger-zone.deleting') | ||||||
| : t('pages.settings.danger-zone.deleteAccount')} | ||||||
| </Button> | ||||||
| </div> | ||||||
| </form> | ||||||
| </Form> | ||||||
| </DialogContent> | ||||||
| </Dialog> | ||||||
| ); | ||||||
| } | ||||||
|
|
||||||
| export default function DangerPage() { | ||||||
| const t = useTranslations(); | ||||||
|
|
||||||
| return ( | ||||||
| <div className="grid gap-6"> | ||||||
| <SettingsCard | ||||||
| title={t('pages.settings.danger-zone.title')} | ||||||
| description={t('pages.settings.danger-zone.description')} | ||||||
| > | ||||||
| <DeleteAccountDialog /> | ||||||
| </SettingsCard> | ||||||
| </div> | ||||||
| ); | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| export const COLORS = [ | ||
| '#000000', | ||
| '#434343', | ||
| '#666666', | ||
| '#999999', | ||
| '#cccccc', | ||
| '#efefef', | ||
| '#f3f3f3', | ||
| '#ffffff', | ||
| '#fb4c2f', | ||
| '#ffad47', | ||
| '#fad165', | ||
| '#16a766', | ||
| '#43d692', | ||
| '#4a86e8', | ||
| '#a479e2', | ||
| '#f691b3', | ||
| '#f6c5be', | ||
| '#ffe6c7', | ||
| '#fef1d1', | ||
| '#b9e4d0', | ||
| '#c6f3de', | ||
| '#c9daf8', | ||
| '#e4d7f5', | ||
| '#fcdee8', | ||
| '#efa093', | ||
| '#ffd6a2', | ||
| '#fce8b3', | ||
| '#89d3b2', | ||
| '#a0eac9', | ||
| '#a4c2f4', | ||
| '#d0bcf1', | ||
| '#fbc8d9', | ||
| '#e66550', | ||
| '#ffbc6b', | ||
| '#fcda83', | ||
| '#44b984', | ||
| '#68dfa9', | ||
| '#6d9eeb', | ||
| '#b694e8', | ||
| '#f7a7c0', | ||
| '#cc3a21', | ||
| '#eaa041', | ||
| '#f2c960', | ||
| '#149e60', | ||
| '#3dc789', | ||
| '#3c78d8', | ||
| '#8e63ce', | ||
| '#e07798', | ||
| '#ac2b16', | ||
| '#cf8933', | ||
| '#d5ae49', | ||
| '#0b804b', | ||
| '#2a9c68', | ||
| '#285bac', | ||
| '#653e9b', | ||
| '#b65775', | ||
| '#822111', | ||
| '#a46a21', | ||
| '#aa8831', | ||
| '#076239', | ||
| '#1a764d', | ||
| '#1c4587', | ||
| '#41236d', | ||
| '#83334c', | ||
| '#464646', | ||
| '#e7e7e7', | ||
| '#0d3472', | ||
| '#b6cff5', | ||
| '#0d3b44', | ||
| '#98d7e4', | ||
| '#3d188e', | ||
| '#e3d7ff', | ||
| '#711a36', | ||
| '#fbd3e0', | ||
| '#8a1c0a', | ||
| '#f2b2a8', | ||
| '#7a2e0b', | ||
| '#ffc8af', | ||
| '#7a4706', | ||
| '#ffdeb5', | ||
| '#594c05', | ||
| '#fbe983', | ||
| '#684e07', | ||
| '#fdedc1', | ||
| '#0b4f30', | ||
| '#b3efd3', | ||
| '#04502e', | ||
| '#a2dcc1', | ||
| '#c2c2c2', | ||
| '#4986e7', | ||
| '#2da2bb', | ||
| '#b99aff', | ||
| '#994a64', | ||
| '#f691b2', | ||
| '#ff7537', | ||
| '#ffad46', | ||
| '#662e37', | ||
| '#ebdbde', | ||
| '#cca6ac', | ||
| '#094228', | ||
| '#42d692', | ||
| '#16a765', | ||
| ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify redirection after account deletion
The implementation uses a root path ('/') as the
callbackURLfor redirecting after account deletion. Ensure this is the intended behavior and consider whether a more specific landing page or confirmation page would be more appropriate.🏁 Script executed:
Length of output: 197
🏁 Script executed:
Length of output: 618
Ensure post-deletion redirect
The call in apps/mail/actions/user.ts (line 18) uses
callbackURL: '/'. Currently there’s no dedicated “account deleted” or confirmation page in the codebase—other flows send users to/settings/connections(email verify) or/mail(login). Please update this to a more appropriate route or create a confirmation page, for example:/account/deleted(or/goodbye) and setcallbackURLto that./) clearly displays a post-deletion message.