Skip to content

Commit 96cc4db

Browse files
committed
feat: orgs design, UI/UX
1 parent b5b5615 commit 96cc4db

File tree

18 files changed

+722
-502
lines changed

18 files changed

+722
-502
lines changed

apps/dashboard/app/(main)/layout.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
import { auth } from '@databuddy/auth';
22
import { headers } from 'next/headers';
33
import { redirect } from 'next/navigation';
4+
import { cache } from 'react';
45
import { Sidebar } from '@/components/layout/sidebar';
56

6-
async function AuthGuard({ children }: { children: React.ReactNode }) {
7+
const getSession = cache(async () => {
78
const session = await auth.api.getSession({ headers: await headers() });
9+
return session;
10+
});
11+
12+
async function AuthGuard({ children }: { children: React.ReactNode }) {
13+
const session = await getSession();
814
if (!session) {
915
redirect('/login');
1016
}
Lines changed: 77 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
11
'use client';
22

3-
import { FloppyDiskIcon, GearIcon } from '@phosphor-icons/react';
3+
import { FloppyDiskIcon } from '@phosphor-icons/react';
44
import { useState } from 'react';
55
import { toast } from 'sonner';
66
import { Button } from '@/components/ui/button';
7-
import {
8-
Card,
9-
CardContent,
10-
CardDescription,
11-
CardHeader,
12-
CardTitle,
13-
} from '@/components/ui/card';
7+
148
import { Input } from '@/components/ui/input';
159
import { Label } from '@/components/ui/label';
1610
import { type Organization, useOrganizations } from '@/hooks/use-organizations';
@@ -69,80 +63,87 @@ export function GeneralSettings({ organization }: GeneralSettingsProps) {
6963
const hasChanges = name !== organization.name || slug !== organization.slug;
7064

7165
return (
72-
<div className="space-y-6">
73-
<Card>
74-
<CardHeader>
75-
<div className="flex items-center gap-2">
76-
<div className="rounded border p-2">
77-
<GearIcon
78-
className="h-5 w-5 not-dark:text-primary"
79-
size={16}
80-
weight="duotone"
81-
/>
82-
</div>
83-
<div>
84-
<CardTitle>Organization Details</CardTitle>
85-
<CardDescription>
86-
Update your organization's basic information and settings.
87-
</CardDescription>
88-
</div>
89-
</div>
90-
</CardHeader>
91-
<CardContent className="space-y-6">
66+
<div className="h-full p-6">
67+
<div className="space-y-8">
68+
{/* Content Sections */}
69+
<div className="space-y-8">
9270
{/* Logo Upload Section */}
93-
<div className="space-y-4">
94-
<OrganizationLogoUploader organization={organization} />
71+
<div className="rounded border bg-card p-6">
72+
<div className="space-y-4">
73+
<div>
74+
<h3 className="font-semibold text-lg">Organization Logo</h3>
75+
<p className="text-muted-foreground text-sm">
76+
Upload a logo to represent your organization
77+
</p>
78+
</div>
79+
<OrganizationLogoUploader organization={organization} />
80+
</div>
9581
</div>
9682

9783
{/* Name and Slug Section */}
98-
<div className="grid gap-4 sm:grid-cols-2">
99-
<div className="space-y-2">
100-
<Label htmlFor="name">Organization Name</Label>
101-
<Input
102-
id="name"
103-
onChange={(e) => setName(e.target.value)}
104-
placeholder="Enter organization name"
105-
value={name}
106-
/>
107-
</div>
108-
<div className="space-y-2">
109-
<Label htmlFor="slug">Organization Slug</Label>
110-
<Input
111-
id="slug"
112-
onChange={(e) => handleSlugChange(e.target.value)}
113-
placeholder="organization-slug"
114-
value={slug}
115-
/>
116-
<p className="text-muted-foreground text-xs">
117-
This will be used in your organization URL
118-
</p>
119-
</div>
120-
</div>
84+
<div className="rounded border bg-card p-6">
85+
<div className="space-y-6">
86+
<div>
87+
<h3 className="font-semibold text-lg">Basic Information</h3>
88+
<p className="text-muted-foreground text-sm">
89+
Configure your organization's name and URL identifier
90+
</p>
91+
</div>
12192

122-
{/* Save Button */}
123-
{hasChanges && (
124-
<div className="flex justify-end">
125-
<Button
126-
className="rounded"
127-
disabled={isSaving}
128-
onClick={handleSave}
129-
>
130-
{isSaving ? (
131-
<>
132-
<div className="mr-2 h-4 w-4 animate-spin rounded-full border border-primary-foreground/30 border-t-primary-foreground" />
133-
Saving...
134-
</>
135-
) : (
136-
<>
137-
<FloppyDiskIcon className="mr-2 h-4 w-4" size={16} />
138-
Save Changes
139-
</>
140-
)}
141-
</Button>
93+
<div className="grid gap-6 sm:grid-cols-2">
94+
<div className="space-y-3">
95+
<Label className="font-medium" htmlFor="name">
96+
Organization Name
97+
</Label>
98+
<Input
99+
id="name"
100+
onChange={(e) => setName(e.target.value)}
101+
placeholder="Enter organization name"
102+
value={name}
103+
/>
104+
</div>
105+
<div className="space-y-3">
106+
<Label className="font-medium" htmlFor="slug">
107+
Organization Slug
108+
</Label>
109+
<Input
110+
id="slug"
111+
onChange={(e) => handleSlugChange(e.target.value)}
112+
placeholder="organization-slug"
113+
value={slug}
114+
/>
115+
<p className="text-muted-foreground text-xs">
116+
This will be used in your organization URL
117+
</p>
118+
</div>
119+
</div>
120+
121+
{/* Save Button */}
122+
{hasChanges && (
123+
<div className="flex justify-end border-t pt-4">
124+
<Button
125+
className="px-6"
126+
disabled={isSaving}
127+
onClick={handleSave}
128+
>
129+
{isSaving ? (
130+
<>
131+
<div className="mr-2 h-4 w-4 animate-spin rounded-full border border-primary-foreground/30 border-t-primary-foreground" />
132+
Saving...
133+
</>
134+
) : (
135+
<>
136+
<FloppyDiskIcon className="mr-2 h-4 w-4" size={16} />
137+
Save Changes
138+
</>
139+
)}
140+
</Button>
141+
</div>
142+
)}
142143
</div>
143-
)}
144-
</CardContent>
145-
</Card>
144+
</div>
145+
</div>
146+
</div>
146147
</div>
147148
);
148149
}

apps/dashboard/app/(main)/organizations/components/organization-provider.tsx

Lines changed: 28 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,10 @@ import {
1313
} from '@phosphor-icons/react';
1414
import { usePathname } from 'next/navigation';
1515
import { useState } from 'react';
16-
import { ApiKeyCreateDialog } from '@/components/organizations/api-key-create-dialog';
16+
1717
import { CreateOrganizationDialog } from '@/components/organizations/create-organization-dialog';
1818
import { InviteMemberDialog } from '@/components/organizations/invite-member-dialog';
1919
import { Button } from '@/components/ui/button';
20-
import { Card } from '@/components/ui/card';
2120
import { Skeleton } from '@/components/ui/skeleton';
2221
import { useOrganizations } from '@/hooks/use-organizations';
2322

@@ -30,7 +29,6 @@ export function OrganizationProvider({
3029
const pathname = usePathname();
3130
const [showCreateDialog, setShowCreateDialog] = useState(false);
3231
const [showInviteMemberDialog, setShowInviteMemberDialog] = useState(false);
33-
const [showCreateApiKeyDialog, setShowCreateApiKeyDialog] = useState(false);
3432

3533
const getPageInfo = () => {
3634
if (pathname === '/organizations') {
@@ -93,11 +91,6 @@ export function OrganizationProvider({
9391
description: 'Create and manage API keys for this organization',
9492
icon: KeyIcon,
9593
requiresOrg: true,
96-
actionButton: {
97-
text: 'Create API Key',
98-
icon: PlusIcon,
99-
action: () => setShowCreateApiKeyDialog(true),
100-
},
10194
};
10295
}
10396
if (pathname === '/organizations/settings/danger') {
@@ -135,7 +128,7 @@ export function OrganizationProvider({
135128
<div className="flex flex-col justify-between gap-3 p-4 sm:flex-row sm:items-center sm:gap-0 sm:px-6 sm:py-6">
136129
<div className="min-w-0 flex-1">
137130
<div className="flex items-center gap-4">
138-
<div className="rounded-xl border border-primary/20 bg-primary/10 p-3">
131+
<div className="rounded border border-primary/20 bg-primary/10 p-3">
139132
<Skeleton className="h-6 w-6" />
140133
</div>
141134
<div>
@@ -162,7 +155,7 @@ export function OrganizationProvider({
162155
<div className="flex flex-col justify-between gap-3 p-4 sm:flex-row sm:items-center sm:gap-0 sm:px-6 sm:py-6">
163156
<div className="min-w-0 flex-1">
164157
<div className="flex items-center gap-4">
165-
<div className="rounded-xl border border-primary/20 bg-primary/10 p-3">
158+
<div className="rounded border border-primary/20 bg-primary/10 p-3">
166159
<Icon
167160
className="h-6 w-6 text-primary"
168161
size={24}
@@ -192,33 +185,29 @@ export function OrganizationProvider({
192185
</div>
193186
</div>
194187

195-
<main className="flex-1 overflow-y-auto p-4 sm:p-6">
196-
<div className="mx-auto max-w-6xl">
197-
<Card className="p-6">
198-
<div className="text-center">
199-
<Icon
200-
className="mx-auto mb-4 h-12 w-12 text-muted-foreground"
201-
size={48}
202-
weight="duotone"
203-
/>
204-
<h3 className="mb-2 font-semibold text-lg">
205-
Select an Organization
206-
</h3>
207-
<p className="text-muted-foreground text-sm">
208-
This feature requires an active organization.
209-
</p>
210-
<div className="mt-4">
211-
<Button
212-
className="rounded"
213-
onClick={() => setShowCreateDialog(true)}
214-
size="sm"
215-
>
216-
<BuildingsIcon className="mr-2 h-5 w-5" size={20} />
217-
Create organization
218-
</Button>
219-
</div>
220-
</div>
221-
</Card>
188+
<main className="flex flex-1 items-center justify-center p-6">
189+
<div className="w-full max-w-md rounded-lg border bg-card p-8 text-center">
190+
<Icon
191+
className="mx-auto mb-4 h-12 w-12 text-muted-foreground"
192+
size={48}
193+
weight="duotone"
194+
/>
195+
<h3 className="mb-2 font-semibold text-lg">
196+
Select an Organization
197+
</h3>
198+
<p className="text-muted-foreground text-sm">
199+
This feature requires an active organization.
200+
</p>
201+
<div className="mt-6">
202+
<Button
203+
className="rounded"
204+
onClick={() => setShowCreateDialog(true)}
205+
size="default"
206+
>
207+
<BuildingsIcon className="mr-2 h-5 w-5" size={20} />
208+
Create organization
209+
</Button>
210+
</div>
222211
</div>
223212
</main>
224213
</div>
@@ -231,7 +220,7 @@ export function OrganizationProvider({
231220
<div className="flex flex-col justify-between gap-3 p-4 sm:flex-row sm:items-center sm:gap-0 sm:px-6 sm:py-6">
232221
<div className="min-w-0 flex-1">
233222
<div className="flex items-center gap-4">
234-
<div className="rounded-xl border border-primary/20 bg-primary/10 p-3">
223+
<div className="rounded border border-primary/20 bg-primary/10 p-3">
235224
<Icon
236225
className="h-6 w-6 text-primary"
237226
size={24}
@@ -261,9 +250,7 @@ export function OrganizationProvider({
261250
</div>
262251
</div>
263252

264-
<main className="flex-1 overflow-y-auto p-4 sm:p-6">
265-
<div className="mx-auto max-w-6xl">{children}</div>
266-
</main>
253+
<main className="flex-1 overflow-y-auto">{children}</main>
267254

268255
<CreateOrganizationDialog
269256
isOpen={showCreateDialog}
@@ -277,14 +264,6 @@ export function OrganizationProvider({
277264
organizationId={activeOrganization.id}
278265
/>
279266
)}
280-
281-
{activeOrganization && (
282-
<ApiKeyCreateDialog
283-
onOpenChange={setShowCreateApiKeyDialog}
284-
open={showCreateApiKeyDialog}
285-
organizationId={activeOrganization.id}
286-
/>
287-
)}
288267
</div>
289268
);
290269
}

0 commit comments

Comments
 (0)