Skip to content

Commit 4f390de

Browse files
committed
cleanup transfer / danger zone
1 parent ccc6165 commit 4f390de

File tree

6 files changed

+490
-616
lines changed

6 files changed

+490
-616
lines changed

apps/dashboard/app/(main)/organizations/settings/danger/danger-zone-settings.tsx

Lines changed: 103 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
"use client";
22

33
import { authClient } from "@databuddy/auth/client";
4-
import { SignOutIcon, TrashIcon } from "@phosphor-icons/react";
4+
import {
5+
BookOpenIcon,
6+
SignOutIcon,
7+
TrashIcon,
8+
WarningIcon,
9+
} from "@phosphor-icons/react";
510
import { useRouter } from "next/navigation";
611
import { useEffect, useState } from "react";
712
import { toast } from "sonner";
@@ -64,7 +69,7 @@ export function DangerZoneSettings({
6469
try {
6570
await deleteOrganizationAsync(organization.id);
6671
router.push("/organizations");
67-
} catch (_error) {
72+
} catch {
6873
toast.error("Failed to delete organization");
6974
} finally {
7075
setIsDeleting(false);
@@ -77,7 +82,7 @@ export function DangerZoneSettings({
7782
try {
7883
await leaveOrganizationAsync(organization.id);
7984
router.push("/organizations");
80-
} catch (_error) {
85+
} catch {
8186
toast.error("Failed to leave organization");
8287
} finally {
8388
setIsLeaving(false);
@@ -86,76 +91,107 @@ export function DangerZoneSettings({
8691
};
8792

8893
return (
89-
<div className="h-full p-6">
90-
<div className="space-y-8">
91-
{/* Content Sections */}
92-
<div className="space-y-8">
93-
{/* Transfer Assets Section */}
94-
<div className="rounded-lg border bg-card">
95-
<div className="border-b p-6">
96-
<h3 className="font-semibold text-lg">Transfer Assets</h3>
97-
<p className="text-muted-foreground text-sm">
98-
Move websites between your personal account and organization
94+
<div className="h-full lg:grid lg:grid-cols-[1fr_18rem]">
95+
{/* Main Content */}
96+
<div className="flex flex-col gap-6 border-b p-5 lg:border-b-0 lg:border-r">
97+
{/* Transfer Assets Section */}
98+
<section>
99+
<div className="mb-4">
100+
<h3 className="font-semibold">Transfer Assets</h3>
101+
<p className="text-muted-foreground text-sm">
102+
Move websites between your personal account and this organization
103+
</p>
104+
</div>
105+
<TransferAssets organizationId={organization.id} />
106+
</section>
107+
108+
{/* Destructive Action */}
109+
<section className="mt-auto rounded border border-destructive/20 bg-destructive/5 p-4">
110+
<div className="flex items-start justify-between gap-4">
111+
<div>
112+
<h3 className="font-semibold text-destructive">
113+
{isOwner === null
114+
? "Loading..."
115+
: isOwner
116+
? "Delete Organization"
117+
: "Leave Organization"}
118+
</h3>
119+
<p className="mt-1 text-destructive/70 text-sm">
120+
{isOwner === null
121+
? "Checking permissions..."
122+
: isOwner
123+
? "Permanently delete this organization and all its data"
124+
: "You will lose access to all resources"}
99125
</p>
100126
</div>
101-
<div className="p-6">
102-
<TransferAssets organizationId={organization.id} />
103-
</div>
127+
{isOwner === null ? (
128+
<Button disabled size="sm" variant="destructive">
129+
<div className="mr-2 h-3 w-3 animate-spin rounded-full border border-destructive-foreground/30 border-t-destructive-foreground" />
130+
Loading
131+
</Button>
132+
) : isOwner ? (
133+
<Button
134+
onClick={() => setShowDeleteDialog(true)}
135+
size="sm"
136+
variant="destructive"
137+
>
138+
<TrashIcon className="mr-2" size={14} />
139+
Delete
140+
</Button>
141+
) : (
142+
<Button
143+
onClick={() => setShowLeaveDialog(true)}
144+
size="sm"
145+
variant="destructive"
146+
>
147+
<SignOutIcon className="mr-2" size={14} />
148+
Leave
149+
</Button>
150+
)}
104151
</div>
152+
</section>
153+
</div>
105154

106-
{/* Leave/Delete Organization Section */}
107-
<div className="rounded-lg border border-destructive/20 bg-destructive/5">
108-
<div className="p-6">
109-
<div className="space-y-4">
110-
<div>
111-
<h3 className="font-semibold text-destructive text-lg">
112-
{isOwner === null
113-
? "Loading..."
114-
: isOwner
115-
? "Delete Organization"
116-
: "Leave Organization"}
117-
</h3>
118-
<p className="text-destructive/80 text-sm">
119-
{isOwner === null
120-
? "Checking permissions..."
121-
: isOwner
122-
? "Once you delete an organization, there is no going back. Please be certain."
123-
: "You will lose access to this organization and all its resources."}
124-
</p>
125-
</div>
126-
127-
<div className="flex justify-end">
128-
{isOwner === null ? (
129-
<Button disabled size="default" variant="destructive">
130-
<div className="mr-2 h-4 w-4 animate-spin rounded-full border border-destructive-foreground/30 border-t-destructive-foreground" />
131-
Loading...
132-
</Button>
133-
) : isOwner ? (
134-
<Button
135-
onClick={() => setShowDeleteDialog(true)}
136-
size="default"
137-
variant="destructive"
138-
>
139-
<TrashIcon className="mr-2 h-4 w-4" size={16} />
140-
Delete Organization
141-
</Button>
142-
) : (
143-
<Button
144-
onClick={() => setShowLeaveDialog(true)}
145-
size="default"
146-
variant="destructive"
147-
>
148-
<SignOutIcon className="mr-2 h-4 w-4" size={16} />
149-
Leave Organization
150-
</Button>
151-
)}
152-
</div>
153-
</div>
154-
</div>
155+
{/* Sidebar */}
156+
<aside className="flex flex-col gap-4 bg-muted/30 p-5">
157+
{/* Warning Card */}
158+
<div className="flex items-start gap-3 rounded border border-amber-500/20 bg-amber-500/5 p-4">
159+
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded bg-amber-500/10">
160+
<WarningIcon className="text-amber-600" size={16} weight="fill" />
161+
</div>
162+
<div>
163+
<p className="font-medium text-amber-700 text-sm dark:text-amber-400">
164+
Danger Zone
165+
</p>
166+
<p className="mt-1 text-amber-600/80 text-xs dark:text-amber-500/80">
167+
Actions here can result in permanent data loss
168+
</p>
155169
</div>
156170
</div>
157-
</div>
158171

172+
{/* Docs Link */}
173+
<Button asChild className="w-full justify-start" variant="outline">
174+
<a
175+
href="https://www.databuddy.cc/docs/getting-started"
176+
rel="noopener noreferrer"
177+
target="_blank"
178+
>
179+
<BookOpenIcon className="mr-2" size={16} />
180+
Documentation
181+
</a>
182+
</Button>
183+
184+
{/* Tip */}
185+
<div className="mt-auto rounded border border-dashed bg-background/50 p-4">
186+
<p className="mb-2 font-medium text-sm">Need help?</p>
187+
<p className="text-muted-foreground text-xs leading-relaxed">
188+
Contact support if you need to recover deleted data or transfer
189+
ownership of an organization.
190+
</p>
191+
</div>
192+
</aside>
193+
194+
{/* Delete Dialog */}
159195
<AlertDialog onOpenChange={setShowDeleteDialog} open={showDeleteDialog}>
160196
<AlertDialogContent>
161197
<AlertDialogHeader>
@@ -185,6 +221,7 @@ export function DangerZoneSettings({
185221
</AlertDialogContent>
186222
</AlertDialog>
187223

224+
{/* Leave Dialog */}
188225
<AlertDialog onOpenChange={setShowLeaveDialog} open={showLeaveDialog}>
189226
<AlertDialogContent>
190227
<AlertDialogHeader>

apps/dashboard/app/(main)/organizations/settings/danger/page.tsx

Lines changed: 35 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,63 @@
11
"use client";
22

33
import { Suspense } from "react";
4+
import { Skeleton } from "@/components/ui/skeleton";
45
import { useOrganizations } from "@/hooks/use-organizations";
56
import { DangerZoneSettings } from "./danger-zone-settings";
67

7-
const ComponentSkeleton = () => (
8-
<div className="h-full p-6">
9-
<div className="space-y-8">
10-
{/* Transfer assets skeleton */}
11-
<div className="rounded border bg-card">
12-
<div className="border-b p-6">
13-
<div className="h-5 w-32 animate-pulse rounded bg-muted" />
14-
<div className="mt-2 h-4 w-64 animate-pulse rounded bg-muted" />
15-
</div>
16-
<div className="p-6">
17-
<div className="grid grid-cols-1 gap-4 lg:grid-cols-[1fr_auto_1fr]">
18-
<div className="space-y-3">
19-
{Array.from({ length: 3 }).map((_, i) => (
20-
<div
21-
className="flex items-center gap-2 rounded border p-2"
22-
key={i.toString()}
23-
>
24-
<div className="h-4 w-4 animate-pulse rounded bg-muted" />
25-
<div className="space-y-1">
26-
<div className="h-3 w-20 animate-pulse rounded bg-muted" />
27-
<div className="h-2 w-24 animate-pulse rounded bg-muted" />
28-
</div>
29-
</div>
30-
))}
8+
function PageSkeleton() {
9+
return (
10+
<div className="h-full lg:grid lg:grid-cols-[1fr_18rem]">
11+
<div className="space-y-6 border-b p-5 lg:border-b-0 lg:border-r">
12+
{/* Transfer section */}
13+
<div>
14+
<Skeleton className="mb-1 h-5 w-32" />
15+
<Skeleton className="mb-4 h-4 w-64" />
16+
<div className="grid gap-4 lg:grid-cols-[1fr_auto_1fr]">
17+
<div className="space-y-2">
18+
<Skeleton className="h-4 w-24" />
19+
<Skeleton className="h-32 w-full rounded" />
3120
</div>
3221
<div className="flex items-center justify-center">
33-
<div className="h-8 w-8 animate-pulse rounded bg-muted" />
22+
<Skeleton className="h-9 w-9 rounded" />
3423
</div>
35-
<div className="space-y-3">
36-
{Array.from({ length: 2 }).map((_, i) => (
37-
<div
38-
className="flex items-center gap-2 rounded border p-2"
39-
key={i.toString()}
40-
>
41-
<div className="h-4 w-4 animate-pulse rounded bg-muted" />
42-
<div className="space-y-1">
43-
<div className="h-3 w-20 animate-pulse rounded bg-muted" />
44-
<div className="h-2 w-24 animate-pulse rounded bg-muted" />
45-
</div>
46-
</div>
47-
))}
24+
<div className="space-y-2">
25+
<Skeleton className="h-4 w-32" />
26+
<Skeleton className="h-32 w-full rounded" />
4827
</div>
4928
</div>
5029
</div>
51-
</div>
5230

53-
{/* Delete section skeleton */}
54-
<div className="rounded border border-destructive/20 bg-destructive/5 p-6">
55-
<div className="space-y-4">
56-
<div className="h-5 w-40 animate-pulse rounded bg-muted" />
57-
<div className="h-4 w-72 animate-pulse rounded bg-muted" />
58-
<div className="flex justify-end">
59-
<div className="h-10 w-32 animate-pulse rounded bg-muted" />
31+
{/* Destructive action */}
32+
<div className="rounded border border-destructive/20 bg-destructive/5 p-4">
33+
<div className="flex items-start justify-between gap-4">
34+
<div className="space-y-2">
35+
<Skeleton className="h-5 w-40" />
36+
<Skeleton className="h-4 w-56" />
37+
</div>
38+
<Skeleton className="h-8 w-20" />
6039
</div>
6140
</div>
6241
</div>
42+
43+
<div className="space-y-4 bg-muted/30 p-5">
44+
<Skeleton className="h-20 w-full rounded" />
45+
<Skeleton className="h-10 w-full" />
46+
<Skeleton className="mt-auto h-20 w-full rounded" />
47+
</div>
6348
</div>
64-
</div>
65-
);
49+
);
50+
}
6651

6752
export default function DangerZoneSettingsPage() {
6853
const { activeOrganization } = useOrganizations();
6954

7055
if (!activeOrganization) {
71-
return null;
56+
return <PageSkeleton />;
7257
}
7358

7459
return (
75-
<Suspense fallback={<ComponentSkeleton />}>
60+
<Suspense fallback={<PageSkeleton />}>
7661
<DangerZoneSettings organization={activeOrganization} />
7762
</Suspense>
7863
);

0 commit comments

Comments
 (0)