Skip to content

Commit 3280cae

Browse files
authored
Merge pull request #73 from codeunia-dev/ux/admin-users
Add user profile dialog and provider information to UsersPage component
2 parents a24c245 + 7b35e87 commit 3280cae

File tree

1 file changed

+76
-2
lines changed

1 file changed

+76
-2
lines changed

app/admin/users/page.tsx

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ type AdminUser = {
3636
lastActive: string | null;
3737
avatar: string | null;
3838
avatarUrl: string | null;
39+
provider?: string;
3940
};
4041

4142
// type for supabase user
@@ -49,12 +50,20 @@ type SupabaseUser = {
4950
banned?: boolean;
5051
created_at: string;
5152
last_sign_in_at: string | null;
53+
app_metadata?: {
54+
provider?: string;
55+
};
56+
identities?: {
57+
provider: string;
58+
}[];
5259
};
5360

5461
export default function UsersPage() {
5562
const [users, setUsers] = useState<AdminUser[]>([])
5663
const [searchTerm, setSearchTerm] = useState("")
5764
const [statusFilter, setStatusFilter] = useState("all")
65+
const [viewUser, setViewUser] = useState<AdminUser | null>(null)
66+
const [isProfileDialogOpen, setIsProfileDialogOpen] = useState(false)
5867

5968
useEffect(() => {
6069
fetch("/api/admin-users")
@@ -63,6 +72,13 @@ export default function UsersPage() {
6372
if (data.users) {
6473
setUsers(data.users.map((user: SupabaseUser) => {
6574
const meta = user.user_metadata || {};
75+
// Try to get provider from app_metadata or identities
76+
let provider = undefined;
77+
if (user.app_metadata && user.app_metadata.provider) {
78+
provider = user.app_metadata.provider;
79+
} else if (user.identities && user.identities.length > 0) {
80+
provider = user.identities[0].provider;
81+
}
6682
return {
6783
id: user.id,
6884
name: meta.full_name || user.email,
@@ -72,6 +88,7 @@ export default function UsersPage() {
7288
lastActive: user.last_sign_in_at,
7389
avatar: user.email[0]?.toUpperCase() || "",
7490
avatarUrl: meta.avatar_url || null,
91+
provider,
7592
};
7693
}));
7794
}
@@ -328,7 +345,11 @@ export default function UsersPage() {
328345
</TableCell>
329346
<TableCell>{getStatusBadge(user.status)}</TableCell>
330347
<TableCell className="hidden md:table-cell text-xs">{new Date(user.joinDate).toLocaleDateString()}</TableCell>
331-
<TableCell className="hidden lg:table-cell text-xs">{new Date(user.lastActive || "").toLocaleDateString()}</TableCell>
348+
<TableCell className="hidden lg:table-cell text-xs">{
349+
user.lastActive && !isNaN(new Date(user.lastActive).getTime())
350+
? new Date(user.lastActive).toLocaleDateString()
351+
: "—"
352+
}</TableCell>
332353
<TableCell className="text-right">
333354
<DropdownMenu>
334355
<DropdownMenuTrigger asChild>
@@ -338,7 +359,10 @@ export default function UsersPage() {
338359
</DropdownMenuTrigger>
339360
<DropdownMenuContent align="end" className="w-48">
340361
<DropdownMenuLabel className="text-xs">Actions</DropdownMenuLabel>
341-
<DropdownMenuItem className="text-xs">
362+
<DropdownMenuItem className="text-xs" onClick={() => {
363+
setViewUser(user);
364+
setIsProfileDialogOpen(true);
365+
}}>
342366
<Eye className="mr-2 h-4 w-4" />
343367
View Profile
344368
</DropdownMenuItem>
@@ -373,6 +397,56 @@ export default function UsersPage() {
373397
)}
374398
</CardContent>
375399
</Card>
400+
401+
{/* User Profile Dialog */}
402+
<Dialog open={isProfileDialogOpen} onOpenChange={setIsProfileDialogOpen}>
403+
<DialogContent className="sm:max-w-lg">
404+
<DialogHeader>
405+
<DialogTitle>User Profile</DialogTitle>
406+
<DialogDescription>Details for the selected user.</DialogDescription>
407+
</DialogHeader>
408+
{viewUser && (
409+
<div className="space-y-4 py-2">
410+
<div className="flex items-center gap-3">
411+
<div className="w-12 h-12 bg-gradient-to-br from-primary to-purple-600 rounded-full flex items-center justify-center text-white text-lg font-semibold">
412+
{viewUser.avatar}
413+
</div>
414+
<div>
415+
<div className="font-bold text-lg text-zinc-900 dark:text-zinc-100">{viewUser.name}</div>
416+
<div className="text-xs text-muted-foreground">{viewUser.email}</div>
417+
</div>
418+
</div>
419+
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm">
420+
<div>
421+
<Label className="text-xs text-muted-foreground">UID</Label>
422+
<div className="break-all font-mono">{viewUser.id}</div>
423+
</div>
424+
<div>
425+
<Label className="text-xs text-muted-foreground">Status</Label>
426+
<div>{getStatusBadge(viewUser.status)}</div>
427+
</div>
428+
<div>
429+
<Label className="text-xs text-muted-foreground">Provider</Label>
430+
<div>{viewUser.provider ? viewUser.provider.charAt(0).toUpperCase() + viewUser.provider.slice(1) : "—"}</div>
431+
</div>
432+
<div>
433+
<Label className="text-xs text-muted-foreground">Created at</Label>
434+
<div>{new Date(viewUser.joinDate).toLocaleString()}</div>
435+
</div>
436+
<div>
437+
<Label className="text-xs text-muted-foreground">Last sign in at</Label>
438+
<div>{viewUser.lastActive && !isNaN(new Date(viewUser.lastActive).getTime()) ? new Date(viewUser.lastActive).toLocaleString() : "—"}</div>
439+
</div>
440+
<div className="sm:col-span-2">
441+
<Label className="text-xs text-muted-foreground">Email</Label>
442+
<div>{viewUser.email}</div>
443+
</div>
444+
{/* Add more fields as needed, e.g. Providers, Phone, etc. */}
445+
</div>
446+
</div>
447+
)}
448+
</DialogContent>
449+
</Dialog>
376450
</div>
377451
)
378452
}

0 commit comments

Comments
 (0)