diff --git a/ui/components/invitations/table/skeleton-table-invitations.tsx b/ui/components/invitations/table/skeleton-table-invitations.tsx
index 1117358e32..2533655a1d 100644
--- a/ui/components/invitations/table/skeleton-table-invitations.tsx
+++ b/ui/components/invitations/table/skeleton-table-invitations.tsx
@@ -1,37 +1,102 @@
-import React from "react";
-
-import { Card } from "@/components/shadcn/card/card";
import { Skeleton } from "@/components/shadcn/skeleton/skeleton";
-export const SkeletonTableInvitation = () => {
+const SkeletonTableRow = () => {
return (
-
- {/* Table headers */}
-
-
-
-
-
-
-
-
-
- {/* Table body */}
-
- {[...Array(10)].map((_, index) => (
-
-
-
-
-
-
-
-
- ))}
-
-
+
+ {/* Email */}
+ |
+
+ |
+ {/* State - text */}
+
+
+ |
+ {/* Role - text */}
+
+
+ |
+ {/* Inserted At - date */}
+
+
+ |
+ {/* Expires At - date */}
+
+
+ |
+ {/* Actions */}
+
+
+ |
+
+ );
+};
+
+export const SkeletonTableInvitation = () => {
+ const rows = 10;
+
+ return (
+
+ {/* Toolbar: Search + Total entries */}
+
+ {/* Search icon button */}
+
+ {/* Total entries */}
+
+
+
+ {/* Table */}
+
+
+
+ {/* Email */}
+ |
+
+ |
+ {/* State */}
+
+
+ |
+ {/* Role */}
+
+
+ |
+ {/* Inserted At */}
+
+
+ |
+ {/* Expires At */}
+
+
+ |
+ {/* Actions - empty header */}
+ |
+
+
+
+ {Array.from({ length: rows }).map((_, i) => (
+
+ ))}
+
+
+
+ {/* Pagination */}
+
+ {/* Rows per page */}
+
+
+
+
+ {/* Page info + navigation */}
+
+
+
);
};
diff --git a/ui/components/manage-groups/table/skeleton-table-groups.tsx b/ui/components/manage-groups/table/skeleton-table-groups.tsx
index 03c817b9d1..1da8eba819 100644
--- a/ui/components/manage-groups/table/skeleton-table-groups.tsx
+++ b/ui/components/manage-groups/table/skeleton-table-groups.tsx
@@ -1,39 +1,94 @@
-import React from "react";
-
-import { Card } from "@/components/shadcn/card/card";
import { Skeleton } from "@/components/shadcn/skeleton/skeleton";
-export const SkeletonTableGroups = () => {
+const SkeletonTableRow = () => {
return (
-
- {/* Table headers */}
-
-
-
-
-
-
-
-
-
-
- {/* Table body */}
-
- {[...Array(3)].map((_, index) => (
-
-
-
-
-
-
-
-
-
- ))}
-
-
+
+ {/* Name - text */}
+ |
+
+ |
+ {/* Providers - count in circle */}
+
+
+ |
+ {/* Roles - count in circle */}
+
+
+ |
+ {/* Added - date */}
+
+
+ |
+ {/* Actions */}
+
+
+ |
+
+ );
+};
+
+export const SkeletonTableGroups = () => {
+ const rows = 10;
+
+ return (
+
+ {/* Toolbar: Search + Total entries */}
+
+ {/* Search icon button */}
+
+ {/* Total entries */}
+
+
+
+ {/* Table */}
+
+
+
+ {/* Name */}
+ |
+
+ |
+ {/* Providers */}
+
+
+ |
+ {/* Roles */}
+
+
+ |
+ {/* Added */}
+
+
+ |
+ {/* Actions - empty header */}
+ |
+
+
+
+ {Array.from({ length: rows }).map((_, i) => (
+
+ ))}
+
+
+
+ {/* Pagination */}
+
+ {/* Rows per page */}
+
+
+
+
+ {/* Page info + navigation */}
+
+
+
);
};
diff --git a/ui/components/roles/table/skeleton-table-roles.tsx b/ui/components/roles/table/skeleton-table-roles.tsx
index 15372fdc91..1bc62af5fb 100644
--- a/ui/components/roles/table/skeleton-table-roles.tsx
+++ b/ui/components/roles/table/skeleton-table-roles.tsx
@@ -1,37 +1,102 @@
-import React from "react";
-
-import { Card } from "@/components/shadcn/card/card";
import { Skeleton } from "@/components/shadcn/skeleton/skeleton";
-export const SkeletonTableRoles = () => {
+const SkeletonTableRow = () => {
return (
-
- {/* Table headers */}
-
-
-
-
-
-
-
-
-
- {/* Table body */}
-
- {[...Array(10)].map((_, index) => (
-
-
-
-
-
-
-
-
- ))}
-
-
+
+ {/* Role - bold name text */}
+ |
+
+ |
+ {/* Users - count text */}
+
+
+ |
+ {/* Invitations - count text */}
+
+
+ |
+ {/* Permissions - state text */}
+
+
+ |
+ {/* Added - date */}
+
+
+ |
+ {/* Actions */}
+
+
+ |
+
+ );
+};
+
+export const SkeletonTableRoles = () => {
+ const rows = 10;
+
+ return (
+
+ {/* Toolbar: Search + Total entries */}
+
+ {/* Search icon button */}
+ {/* */}
+ {/* Total entries */}
+
+
+
+ {/* Table */}
+
+
+
+ {/* Role */}
+ |
+
+ |
+ {/* Users */}
+
+
+ |
+ {/* Invitations */}
+
+
+ |
+ {/* Permissions */}
+
+
+ |
+ {/* Added */}
+
+
+ |
+ {/* Actions - empty header */}
+ |
+
+
+
+ {Array.from({ length: rows }).map((_, i) => (
+
+ ))}
+
+
+
+ {/* Pagination */}
+
+ {/* Rows per page */}
+
+
+
+
+ {/* Page info + navigation */}
+
+
+
);
};
diff --git a/ui/components/users/profile/skeleton-user-info.tsx b/ui/components/users/profile/skeleton-user-info.tsx
index 7e580593fd..ecbe6b498e 100644
--- a/ui/components/users/profile/skeleton-user-info.tsx
+++ b/ui/components/users/profile/skeleton-user-info.tsx
@@ -1,74 +1,156 @@
-import { Card, CardContent, Skeleton } from "@/components/shadcn";
+import {
+ Card,
+ CardAction,
+ CardContent,
+ CardHeader,
+ Separator,
+ Skeleton,
+} from "@/components/shadcn";
+// Mirrors the real profile layout (UserBasicInfoCard full-width + a two-column
+// grid with RolesCard and Organizations) so the page does not shift when the
+// data resolves. Conditional cards (SAML / API keys) depend on permissions not
+// known while loading, so we mirror only the always-present structure.
export const SkeletonUserInfo = () => {
return (
-
- {/* User Information */}
-
-
-
- {/* Name */}
-
- {/* Email */}
-
- {/* Company */}
-
- {/* Date Joined */}
-
- {/* Tenant ID */}
-
-
-
-
-
- {/* Roles */}
-
-
- Roles
-
- {[1, 2, 3].map((i) => (
-
- ))}
-
-
-
-
- {/* Memberships */}
-
-
- Memberships
-
- {[1, 2].map((i) => (
-
- ))}
-
-
-
+
+ );
+};
+
+const UserBasicInfoSkeleton = () => {
+ return (
+
+
+ {/* Avatar + name / email */}
+
+
+ {/* Date Joined + Organization ID */}
+
+
+
+ );
+};
+
+const RolesCardSkeleton = () => {
+ return (
+
+
+ {/* Header: "Active roles" + subtitle */}
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+// Mirrors
: inner card with a role badge + state on the left, a
+// details toggle on the right, and a grid of permission rows below.
+const RoleItemSkeleton = () => {
+ return (
+
+
+
+ {[0, 1, 2, 3].map((i) => (
+
+
+
+
+ ))}
+
+
+ );
+};
+
+const OrganizationsCardSkeleton = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+// Mirrors : role badge (left), Name / Joined fields, and the
+// "Active" badge (right).
+const MembershipItemSkeleton = () => {
+ return (
+
+
+
+ );
+};
+
+// Label + value pair. `inline` lays them side by side (info fields inside a
+// row); otherwise stacked (a standalone field).
+const FieldSkeleton = ({
+ labelWidth,
+ valueWidth,
+ inline = false,
+}: {
+ labelWidth: string;
+ valueWidth: string;
+ inline?: boolean;
+}) => {
+ return (
+
+
+
);
};
diff --git a/ui/components/users/table/skeleton-table-user.tsx b/ui/components/users/table/skeleton-table-user.tsx
index c16074f976..c6d990a864 100644
--- a/ui/components/users/table/skeleton-table-user.tsx
+++ b/ui/components/users/table/skeleton-table-user.tsx
@@ -1,37 +1,102 @@
-import React from "react";
-
-import { Card } from "@/components/shadcn/card/card";
import { Skeleton } from "@/components/shadcn/skeleton/skeleton";
-export const SkeletonTableUser = () => {
+const SkeletonTableRow = () => {
return (
-
- {/* Table headers */}
-
-
-
-
-
-
-
-
-
- {/* Table body */}
-
- {[...Array(10)].map((_, index) => (
-
-
-
-
-
-
-
-
- ))}
-
-
+
+ {/* Name */}
+ |
+
+ |
+ {/* Email */}
+
+
+ |
+ {/* Role */}
+
+
+ |
+ {/* Company name */}
+
+
+ |
+ {/* Joined - date */}
+
+
+ |
+ {/* Actions */}
+
+
+ |
+
+ );
+};
+
+export const SkeletonTableUser = () => {
+ const rows = 10;
+
+ return (
+
+ {/* Toolbar: Search + Total entries */}
+
+ {/* Search icon button */}
+
+ {/* Total entries */}
+
+
+
+ {/* Table */}
+
+
+
+ {/* Name */}
+ |
+
+ |
+ {/* Email */}
+
+
+ |
+ {/* Role */}
+
+
+ |
+ {/* Company name */}
+
+
+ |
+ {/* Joined */}
+
+
+ |
+ {/* Actions - empty header */}
+ |
+
+
+
+ {Array.from({ length: rows }).map((_, i) => (
+
+ ))}
+
+
+
+ {/* Pagination */}
+
+ {/* Rows per page */}
+
+
+
+
+ {/* Page info + navigation */}
+
+
+
);
};