mirror of
https://github.com/eliasstepanik/core.git
synced 2026-01-11 17:58:28 +00:00
217 lines
7.5 KiB
TypeScript
217 lines
7.5 KiB
TypeScript
import { json, type LoaderFunctionArgs } from "@remix-run/node";
|
|
import { useLoaderData, useFetcher, useNavigate } from "@remix-run/react";
|
|
import { requireUser } from "~/services/session.server";
|
|
import { Card } from "~/components/ui/card";
|
|
import { Button } from "~/components/ui/button";
|
|
import { Input } from "~/components/ui/input";
|
|
import { Label } from "~/components/ui/label";
|
|
import { AlertTriangle } from "lucide-react";
|
|
import { useState } from "react";
|
|
import {
|
|
AlertDialog,
|
|
AlertDialogAction,
|
|
AlertDialogCancel,
|
|
AlertDialogContent,
|
|
AlertDialogDescription,
|
|
AlertDialogFooter,
|
|
AlertDialogHeader,
|
|
AlertDialogTitle,
|
|
} from "~/components/ui/alert-dialog";
|
|
import { SettingSection } from "~/components/setting-section";
|
|
|
|
interface SuccessDataResponse {
|
|
success: boolean;
|
|
}
|
|
|
|
interface ErrorDataResponse {
|
|
error: string;
|
|
}
|
|
|
|
export const loader = async ({ request }: LoaderFunctionArgs) => {
|
|
const user = await requireUser(request);
|
|
|
|
return json({
|
|
user,
|
|
});
|
|
};
|
|
|
|
export default function AccountSettings() {
|
|
const { user } = useLoaderData<typeof loader>();
|
|
const fetcher = useFetcher<SuccessDataResponse | ErrorDataResponse>();
|
|
const navigate = useNavigate();
|
|
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
|
const [confirmText, setConfirmText] = useState("");
|
|
const isDeleting = fetcher.state === "submitting";
|
|
|
|
const handleDeleteAccount = () => {
|
|
fetcher.submit(
|
|
{},
|
|
{
|
|
method: "DELETE",
|
|
action: "/api/v1/user/delete",
|
|
},
|
|
);
|
|
};
|
|
|
|
// Redirect to login after successful deletion
|
|
if (fetcher.data && "success" in fetcher.data && fetcher.data.success) {
|
|
setTimeout(() => {
|
|
navigate("/login");
|
|
}, 1000);
|
|
}
|
|
|
|
const canDelete = confirmText === user.email;
|
|
|
|
return (
|
|
<div className="mx-auto flex w-3xl flex-col gap-4 px-4 py-6">
|
|
<SettingSection
|
|
title="Account Settings"
|
|
description="Manage your account information and preferences"
|
|
>
|
|
<>
|
|
{/* Account Information */}
|
|
<div className="mb-8">
|
|
<h2 className="mb-4 text-lg font-semibold">Account Information</h2>
|
|
<Card className="p-6">
|
|
<div className="space-y-4">
|
|
<div>
|
|
<Label className="text-muted-foreground text-sm">Email</Label>
|
|
<p className="text-base font-medium">{user.email}</p>
|
|
</div>
|
|
{user.name && (
|
|
<div>
|
|
<Label className="text-muted-foreground text-sm">
|
|
Name
|
|
</Label>
|
|
<p className="text-base font-medium">{user.name}</p>
|
|
</div>
|
|
)}
|
|
{user.displayName && (
|
|
<div>
|
|
<Label className="text-muted-foreground text-sm">
|
|
Display Name
|
|
</Label>
|
|
<p className="text-base font-medium">{user.displayName}</p>
|
|
</div>
|
|
)}
|
|
<div>
|
|
<Label className="text-muted-foreground text-sm">
|
|
Account Created
|
|
</Label>
|
|
<p className="text-base font-medium">
|
|
{new Date(user.createdAt).toLocaleDateString()}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
|
|
{/* Danger Zone */}
|
|
<div className="mb-8">
|
|
<h2 className="mb-4 text-lg font-semibold text-red-600 dark:text-red-400">
|
|
Danger Zone
|
|
</h2>
|
|
<Card className="p-6">
|
|
<div className="flex items-start gap-3">
|
|
<AlertTriangle className="mt-1 h-5 w-5 text-red-600 dark:text-red-400" />
|
|
<div className="flex-1">
|
|
<h3 className="font-semibold text-red-900 dark:text-red-100">
|
|
Delete Account
|
|
</h3>
|
|
<p className="mb-4 text-sm text-red-700 dark:text-red-300">
|
|
Permanently delete your account and all associated data.
|
|
This action cannot be undone.
|
|
</p>
|
|
<ul className="mb-4 list-inside list-disc space-y-1 text-sm">
|
|
<li>All your memories and conversations will be deleted</li>
|
|
<li>All integration connections will be removed</li>
|
|
<li>All API keys and webhooks will be revoked</li>
|
|
<li>
|
|
Your workspace and all its data will be permanently lost
|
|
</li>
|
|
<li>Active subscriptions will be cancelled immediately</li>
|
|
</ul>
|
|
<Button
|
|
variant="destructive"
|
|
onClick={() => setShowDeleteDialog(true)}
|
|
disabled={isDeleting}
|
|
>
|
|
Delete My Account
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
</>
|
|
</SettingSection>
|
|
|
|
{/* Delete Confirmation Dialog */}
|
|
<AlertDialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
|
<AlertDialogDescription asChild>
|
|
<div className="space-y-4">
|
|
<p>
|
|
This action <strong>cannot be undone</strong>. This will
|
|
permanently delete your account and remove all your data from
|
|
our servers.
|
|
</p>
|
|
<div>
|
|
<Label
|
|
htmlFor="confirm-email"
|
|
className="text-sm font-medium"
|
|
>
|
|
To confirm, type your email address:{" "}
|
|
<span className="font-mono">{user.email}</span>
|
|
</Label>
|
|
<Input
|
|
id="confirm-email"
|
|
type="text"
|
|
value={confirmText}
|
|
onChange={(e) => setConfirmText(e.target.value)}
|
|
placeholder="Enter your email"
|
|
className="mt-2"
|
|
autoComplete="off"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel
|
|
onClick={() => {
|
|
setConfirmText("");
|
|
}}
|
|
disabled={isDeleting}
|
|
>
|
|
Cancel
|
|
</AlertDialogCancel>
|
|
<AlertDialogAction
|
|
onClick={handleDeleteAccount}
|
|
disabled={!canDelete || isDeleting}
|
|
className="bg-red-600 text-white hover:bg-red-700"
|
|
>
|
|
{isDeleting ? "Deleting..." : "Delete Account Permanently"}
|
|
</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
|
|
{/* Success Message */}
|
|
{fetcher.data && "success" in fetcher.data && fetcher.data.success && (
|
|
<div className="fixed right-4 bottom-4 rounded-md bg-green-600 p-4 text-white shadow-lg">
|
|
Account deleted successfully. Redirecting...
|
|
</div>
|
|
)}
|
|
|
|
{/* Error Message */}
|
|
{fetcher.data && "error" in fetcher.data && (
|
|
<div className="fixed right-4 bottom-4 rounded-md bg-red-600 p-4 text-white shadow-lg">
|
|
{fetcher.data.error}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|