Skip to content

Feature/66 admin dashboard users tab#67

Merged
Berny-ft merged 17 commits into
developfrom
feature/66-admin-dashboard-users-tab
May 19, 2026
Merged

Feature/66 admin dashboard users tab#67
Berny-ft merged 17 commits into
developfrom
feature/66-admin-dashboard-users-tab

Conversation

@Berny-ft
Copy link
Copy Markdown
Contributor

@Berny-ft Berny-ft commented May 14, 2026

Closes #66

Overview

Implemented the fully functional Admin Users Dashboard. This includes a new /users route with server-side data fetching and a dynamic client-side interface for managing user records.

Key Changes:

  • Server-Side Integration: Implemented data fetching for user profiles and subscriptions, including a query to list distinct roles for filtering.
  • Dynamic Data Table: Created UsersDataTable which uses a ResizeObserver to automatically calculate the optimal number of rows to display based on available vertical screen space.
  • Robust Filtering & Sorting: Added UsersClient to manage status tabs (All, Active, Inactive), a role-based filter dropdown, and a real-time name search.
  • UI/UX Refinements:
    • Fixed table borders to wrap tightly around content rather than stretching to the bottom.
    • Resolved focus-ring clipping on the search input.
    • Enhanced pagination button visibility with blue-tinted hover states.
    • Implemented a pinned footer to keep pagination controls always accessible.

Testing

Manual Testing:

  • Verified that the table correctly measures available space and updates the pageSize when the browser window is resized.
  • Tested the Search bar with various names to ensure immediate filtering.
  • Validated Status Tabs (All/Active/Inactive) correctly filter based on subscription data.
  • Confirmed the Role Filter dropdown dynamically populates and filters correctly.
  • Verified that Sorting (A-Z/Z-A) works across the entire filtered dataset.
  • Checked that Pagination controls are disabled/enabled appropriately based on the number of results.

Checklist

  • Code is neat, readable, and works
  • Code is commented where appropriate and well-documented
  • Commit messages follow our guidelines
  • Issue number is linked
  • Branch is linked
  • Reviewers are assigned

Notes

  • The dynamic pagination logic in UsersDataTable relies on the parent container having a bounded height (implemented in users/layout.tsx).
  • The bottom border of the table is now decoupled from the scroll container to ensure it always hugs the last row, providing a cleaner look when there are few entries.
  • The db schema was modified to add a last logged in property and its documentation was also modified accordingly.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements the Admin Users dashboard route with server-side user data loading and a client-side table for filtering, sorting, and pagination.

Changes:

  • Adds /users page structure, bounded layout, profile role helpers, and role query.
  • Adds users table components with name/status/role filtering, alphabetical sorting, dynamic page sizing, and action icons.
  • Updates shared authenticated layout and generic data table pagination styling/behavior.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
components/data-table.tsx Updates generic table footer to show page counts and numbered pagination.
app/(authenticated)/users/queries.ts Adds query for distinct profile roles.
app/(authenticated)/users/profile-role-label.ts Adds role label mapping and shared UserRow type.
app/(authenticated)/users/page.tsx Adds server-side user fetching and renders the users dashboard.
app/(authenticated)/users/layout.tsx Adds bounded-height layout for the users route.
app/(authenticated)/users/_components/users-data-table.tsx Adds dynamic paginated users table with resize-based page sizing.
app/(authenticated)/users/_components/users-columns.tsx Defines users table columns and row actions.
app/(authenticated)/users/_components/users-client.tsx Adds client-side filters, tabs, search, sort, and table wiring.
app/(authenticated)/layout.tsx Adjusts authenticated layout sizing/overflow for nested dashboard content.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread app/(authenticated)/users/page.tsx Outdated
Comment on lines +42 to +46
async function UsersContent() {
const [users, distinctRoles] = await Promise.all([
fetchUsers(),
listDistinctProfileRoles(),
]);
Comment on lines +34 to +35
const fullName = `${u.firstName} ${u.lastName}`;
const email = `${u.firstName.toLowerCase()}.${u.lastName.toLowerCase()}@mcld.ca`;
Comment on lines +88 to +97
id: "createdAt",
header: "Last Login",
meta: { colWidth: "18%" },
cell: ({ row }) => (
<span className="block min-w-0 truncate text-sm text-muted-foreground">
{new Intl.DateTimeFormat("en-CA", {
year: "numeric",
month: "short",
day: "numeric",
}).format(new Date(row.original.createdAt))}
Copy link
Copy Markdown
Member

@RenaudBernier RenaudBernier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me

Copy link
Copy Markdown
Member

@RenaudBernier RenaudBernier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Add loader for when the table is loading
  • Remove the Add user button, you can add it in your next issue

@Berny-ft
Copy link
Copy Markdown
Contributor Author

  • Add loader for when the table is loading
  • Remove the Add user button, you can add it in your next issue

done

Copy link
Copy Markdown
Contributor

@martin0024 martin0024 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job, nothing to say really. Looks good.
Only thing is why everyone is Last Login is 16 of May, check it works.

Comment on lines +14 to +21
function AvatarCircle({ name }: { name: string }) {
const [first = "", last = ""] = name.trim().split(" ");
const initials = `${first[0] ?? ""}${last[0] ?? ""}`.toUpperCase();
return (
<span className="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-semibold text-muted-foreground">
{initials}
</span>
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use avatar component: npx shadcn@latest add avatar

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use avatar component: npx shadcn@latest add avatar

done

Comment on lines +69 to +84
return (
<span className="inline-flex items-center gap-1.5 text-sm">
<span
className={`h-2 w-2 rounded-full ${
active ? "bg-green-500" : "bg-muted-foreground/50"
}`}
/>
<span
className={
active ? "text-foreground" : "text-muted-foreground"
}
>
{active ? "Active" : "Inactive"}
</span>
</span>
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return (
<span className="inline-flex items-center gap-1.5 text-sm">
<span
className={`h-2 w-2 rounded-full ${
active ? "bg-green-500" : "bg-muted-foreground/50"
}`}
/>
<span
className={
active ? "text-foreground" : "text-muted-foreground"
}
>
{active ? "Active" : "Inactive"}
</span>
</span>
);
<Badge variant={active ? "default" : "secondary"} className="gap-1.5">
<span
className={`h-2 w-2 rounded-full ${
active ? "bg-green-500" : "bg-muted-foreground/50"
}`}
/>
{active ? "Active" : "Inactive"}
</Badge>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utils/supabase/middleware.ts gates /dashboard/* on ROLES.ADMIN but /users is unprotected, so any signed-in user can access the page.

cell: ({ row }) => {
const u = row.original;
const fullName = `${u.firstName} ${u.lastName}`;
const email = `${u.firstName.toLowerCase()}.${u.lastName.toLowerCase()}@mcld.ca`;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is there fake email rendered in the table?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is there fake email rendered in the table?

fixed

Comment thread components/ui/spinner.tsx Outdated
@@ -0,0 +1,18 @@
import { LoaderIcon } from "lucide-react";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import { LoaderIcon } from "lucide-react";
import { Loader2Icon } from "lucide-react";

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment thread components/ui/spinner.tsx Outdated
function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
return (
<div className="flex flex-1 items-center justify-center">
<LoaderIcon
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<LoaderIcon
<Loader2Icon

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Copy link
Copy Markdown
Member

@RenaudBernier RenaudBernier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work, lgtm

@Berny-ft Berny-ft merged commit f9efa90 into develop May 19, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants