Skip to content

Commit 75568fb

Browse files
perf: lazy-load settings page content to reduce first-load JS (#952) (#955)
Co-authored-by: Ona <no-reply@ona.com>
1 parent 62e1137 commit 75568fb

4 files changed

Lines changed: 94 additions & 34 deletions

File tree

.agents/architecture.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,8 @@ src/
541541
│ ├── route-error.tsx # Reusable error boundary UI (Sentry capture + retry button)
542542
│ ├── workspace-home.tsx # Workspace home: page list or empty state with create CTA
543543
│ ├── workspace-home-client.tsx # Client wrapper: lazy-loads WorkspaceHome via next/dynamic
544+
│ ├── settings-page-client.tsx # Client wrapper: lazy-loads SettingsPageContent via next/dynamic
545+
│ ├── settings-page-content.tsx # Settings page layout: form + change password + danger zone
544546
│ ├── workspace-settings-form.tsx # Edit workspace name/slug, lazy-loads delete workspace section
545547
│ ├── delete-workspace-section.tsx # Workspace deletion danger zone with AlertDialog confirmation
546548
│ ├── danger-zone-settings.tsx # Client wrapper: lazy-loads DeleteAccountSection via next/dynamic

src/app/(app)/[workspaceSlug]/settings/page.tsx

Lines changed: 6 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import type { Metadata } from "next";
2-
import Link from "next/link";
32
import { notFound, redirect } from "next/navigation";
43
import { createClient } from "@/lib/supabase/server";
5-
import { WorkspaceSettingsForm } from "@/components/workspace-settings-form";
6-
import { ChangePasswordSection } from "@/components/change-password-section";
7-
import { DangerZoneSettings } from "@/components/danger-zone-settings";
8-
import { Separator } from "@/components/ui/separator";
4+
import { SettingsPageClient } from "@/components/settings-page-client";
95

106
export async function generateMetadata({
117
params,
@@ -56,34 +52,10 @@ export default async function WorkspaceSettingsPage({
5652
const userEmail = user.email ?? "";
5753

5854
return (
59-
<div className="mx-auto max-w-xl p-6">
60-
<div className="flex items-center gap-4">
61-
<h1 className="text-2xl font-semibold">Workspace settings</h1>
62-
<Link
63-
href={`/${workspaceSlug}/settings/members`}
64-
className="text-sm text-accent underline-offset-4 hover:underline"
65-
>
66-
Members
67-
</Link>
68-
</div>
69-
<p className="mt-1 text-sm text-muted-foreground">
70-
Manage your workspace name, URL, and other settings.
71-
</p>
72-
<div className="mt-6">
73-
<WorkspaceSettingsForm workspace={workspace} userId={user.id} />
74-
</div>
75-
{workspace.is_personal && (
76-
<>
77-
<Separator className="mt-8 bg-overlay-border" />
78-
<div className="mt-8">
79-
<ChangePasswordSection />
80-
</div>
81-
<Separator className="mt-8 bg-overlay-border" />
82-
<div className="mt-8">
83-
<DangerZoneSettings userEmail={userEmail} />
84-
</div>
85-
</>
86-
)}
87-
</div>
55+
<SettingsPageClient
56+
workspace={workspace}
57+
userId={user.id}
58+
userEmail={userEmail}
59+
/>
8860
);
8961
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"use client";
2+
3+
import dynamic from "next/dynamic";
4+
import type { Workspace } from "@/lib/types";
5+
6+
const SettingsPageContent = dynamic(
7+
() =>
8+
import("@/components/settings-page-content").then(
9+
(mod) => mod.SettingsPageContent,
10+
),
11+
);
12+
13+
interface SettingsPageClientProps {
14+
workspace: Workspace;
15+
userId: string;
16+
userEmail: string;
17+
}
18+
19+
export function SettingsPageClient(props: SettingsPageClientProps) {
20+
return <SettingsPageContent {...props} />;
21+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
"use client";
2+
3+
import dynamic from "next/dynamic";
4+
import Link from "next/link";
5+
import { WorkspaceSettingsForm } from "@/components/workspace-settings-form";
6+
import { Separator } from "@/components/ui/separator";
7+
import type { Workspace } from "@/lib/types";
8+
9+
const ChangePasswordSection = dynamic(
10+
() =>
11+
import("@/components/change-password-section").then(
12+
(mod) => mod.ChangePasswordSection,
13+
),
14+
);
15+
16+
const DangerZoneSettings = dynamic(
17+
() =>
18+
import("@/components/danger-zone-settings").then(
19+
(mod) => mod.DangerZoneSettings,
20+
),
21+
);
22+
23+
interface SettingsPageContentProps {
24+
workspace: Workspace;
25+
userId: string;
26+
userEmail: string;
27+
}
28+
29+
export function SettingsPageContent({
30+
workspace,
31+
userId,
32+
userEmail,
33+
}: SettingsPageContentProps) {
34+
return (
35+
<div className="mx-auto max-w-xl p-6">
36+
<div className="flex items-center gap-4">
37+
<h1 className="text-2xl font-semibold">Workspace settings</h1>
38+
<Link
39+
href={`/${workspace.slug}/settings/members`}
40+
className="text-sm text-accent underline-offset-4 hover:underline"
41+
>
42+
Members
43+
</Link>
44+
</div>
45+
<p className="mt-1 text-sm text-muted-foreground">
46+
Manage your workspace name, URL, and other settings.
47+
</p>
48+
<div className="mt-6">
49+
<WorkspaceSettingsForm workspace={workspace} userId={userId} />
50+
</div>
51+
{workspace.is_personal && (
52+
<>
53+
<Separator className="mt-8 bg-overlay-border" />
54+
<div className="mt-8">
55+
<ChangePasswordSection />
56+
</div>
57+
<Separator className="mt-8 bg-overlay-border" />
58+
<div className="mt-8">
59+
<DangerZoneSettings userEmail={userEmail} />
60+
</div>
61+
</>
62+
)}
63+
</div>
64+
);
65+
}

0 commit comments

Comments
 (0)