diff --git a/app/controllers/users_controller.ts b/app/controllers/users_controller.ts index a2585c8..bc43b3d 100644 --- a/app/controllers/users_controller.ts +++ b/app/controllers/users_controller.ts @@ -6,10 +6,14 @@ import { errorsReducer } from '#utils/index' import { UserService } from '#services/user_service' import { UserResponse } from '#interfaces/user' import { PageObject } from '@adonisjs/inertia/types' +import AuthService from '#services/auth_service' @inject() export default class UsersController { - constructor(private readonly service: UserService) {} + constructor( + private readonly authService: AuthService, + private readonly service: UserService + ) {} async index(ctx: HttpContext) { const searchTerm = ctx.request.qs().search || '' @@ -57,7 +61,14 @@ export default class UsersController { } } - async destroy() { - // TODO: Implement. + async destroy(ctx: HttpContext) { + const user = ctx.auth.user! + try { + await this.service.destroy(user) + await this.authService.destroy(ctx) + } catch (error) { + ctx.session.flash('errors', { message: 'Error deleting user.' }) + return ctx.response.redirect().back() + } } } diff --git a/app/services/user_service.ts b/app/services/user_service.ts index b30bbb8..52ffc77 100644 --- a/app/services/user_service.ts +++ b/app/services/user_service.ts @@ -60,6 +60,10 @@ export class UserService { await user.save() } + async destroy(user: User) { + await user.delete() + } + async storeAttachments(ctx: HttpContext) { const currentUserId = ctx.auth.user?.id! diff --git a/database/migrations/1732887344164_add_user_delete_cascade_posts_table.ts b/database/migrations/1732887344164_add_user_delete_cascade_posts_table.ts new file mode 100644 index 0000000..0e3ea28 --- /dev/null +++ b/database/migrations/1732887344164_add_user_delete_cascade_posts_table.ts @@ -0,0 +1,19 @@ +import { BaseSchema } from '@adonisjs/lucid/schema' + +export default class extends BaseSchema { + protected tableName = 'posts' + + async up() { + this.schema.alterTable(this.tableName, (table) => { + table.dropForeign('user_id'); + table.uuid('user_id').references('users.id').notNullable().onDelete('CASCADE').alter() + }) + } + + async down() { + this.schema.alterTable(this.tableName, (table) => { + table.dropForeign('user_id'); + table.uuid('user_id').references('users.id').notNullable().alter() + }) + } +} diff --git a/inertia/pages/users/settings.tsx b/inertia/pages/users/settings.tsx index d61136d..8be6055 100644 --- a/inertia/pages/users/settings.tsx +++ b/inertia/pages/users/settings.tsx @@ -4,6 +4,14 @@ import HeadOG from '@/components/generic/head_og' import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' import { Button } from '@/components/ui/button' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@/components/ui/dialog' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { useToast } from '@/components/ui/use_toast' @@ -23,6 +31,7 @@ export default function UserSettings({ () => user.attachments.avatar?.link || undefined ) const [coverPreview, setCoverPreview] = useState(() => user.attachments.cover?.link || undefined) + const [deleteIntentModal, setDeleteIntentModal] = useState(false) if (!user) return <>> @@ -30,7 +39,13 @@ export default function UserSettings({ const { toast } = useToast() - const { data, setData, patch, processing } = useForm<{ + const { + data, + setData, + patch, + delete: deleteReq, + processing, + } = useForm<{ name: string surname: string username: string @@ -51,7 +66,7 @@ export default function UserSettings({ function handleSubmit(e: React.FormEvent) { e.preventDefault() - patch(route('users.update', { params: { id: user?.id! } }).path, { + patch(route('users.update').path, { preserveState: true, preserveScroll: true, onSuccess: () => { @@ -86,9 +101,20 @@ export default function UserSettings({ fileReader.readAsDataURL(e.target.files[0]) } + function deleteAccount() { + deleteReq(route('users.destroy').path, { + preserveState: true, + preserveScroll: true, + data: undefined, + onSuccess: () => { + toast({ title: 'Account succesfully deleted.' }) + }, + }) + } + useEffect(() => { if (props?.errors && Object.entries(props.errors).length) { - toast({ title: 'Error updating profile details.' }) + toast({ title: props?.errors.message || 'Error updating profile details.' }) } }, [props?.errors]) @@ -99,7 +125,7 @@ export default function UserSettings({ description="Your profile settings on Social Adonis." url={route('settings.show').path} /> -