Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions front/components/members/InvitationsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
normalizeDisplayRole,
ROLES_DATA,
} from "@app/components/members/Roles";
import type { UsageTableSkeletonCellProps } from "@app/components/workspace/UsageTableSkeleton";
import { UsageTableSkeleton } from "@app/components/workspace/UsageTableSkeleton";
import { useSendNotification } from "@app/hooks/useNotification";
import { sendInvitations } from "@app/lib/invitations";
import { useWorkspaceInvitations } from "@app/lib/swr/memberships";
Expand All @@ -13,8 +15,9 @@ import { isAdmin } from "@app/types/user";
import {
Button,
Chip,
cn,
DataTable,
DataTableLoadingSkeleton,
LoadingBlock,
Mail01,
Page,
} from "@dust-tt/sparkle";
Expand All @@ -26,6 +29,27 @@ type RowData = MembershipInvitationType & {
onClick: () => void;
};

function InvitationSkeletonCell({
columnId,
rowIndex,
}: UsageTableSkeletonCellProps) {
switch (columnId) {
case "inviteEmail":
return (
<LoadingBlock
className={cn(
"h-3 max-w-full",
["w-48", "w-56", "w-40"][rowIndex % 3]
)}
/>
);
case "initialRole":
return <LoadingBlock className="h-6 w-16 rounded-[9px]" />;
default:
return null;
}
}

export function InvitationsList({
owner,
searchText,
Expand Down Expand Up @@ -143,7 +167,11 @@ export function InvitationsList({
/>
<div className="flex flex-col gap-1 pt-2">
{isInvitationsLoading && (
<DataTableLoadingSkeleton showSelectionColumn={false} rows={3} />
<UsageTableSkeleton
columns={columns}
SkeletonCell={InvitationSkeletonCell}
rowCount={3}
/>
)}
{!isInvitationsLoading && invitations.length === 0 && (
<div className="flex flex-col items-center justify-center py-8 text-center">
Expand Down
34 changes: 32 additions & 2 deletions front/components/pages/workspace/developers/SecretsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { AdminPageContainer } from "@app/components/layouts/AdminPageContainer";
import type { UsageTableSkeletonCellProps } from "@app/components/workspace/UsageTableSkeleton";
import { UsageTableSkeleton } from "@app/components/workspace/UsageTableSkeleton";
import { useSendNotification } from "@app/hooks/useNotification";
import { useAuth, useWorkspace } from "@app/lib/auth/AuthContext";
import { useSubmitFunction } from "@app/lib/client/utils";
Expand All @@ -8,8 +10,8 @@ import type { DustAppSecretType } from "@app/types/dust_app_secret";
import {
BookOpen01,
Button,
cn,
DataTable,
DataTableLoadingSkeleton,
Dialog,
DialogContainer,
DialogContent,
Expand All @@ -18,6 +20,7 @@ import {
DialogTitle,
Edit04,
Input,
LoadingBlock,
Page,
Plus,
SearchInput,
Expand Down Expand Up @@ -337,14 +340,41 @@ interface SecretsTableProps {
searchQuery: string;
}

function SecretSkeletonCell({
columnId,
rowIndex,
}: UsageTableSkeletonCellProps) {
switch (columnId) {
case "name":
return (
<div className="flex items-center gap-2">
<LoadingBlock
className={cn(
"h-3 max-w-full",
["w-56", "w-64", "w-48", "w-60", "w-52"][rowIndex % 5]
)}
/>
<LoadingBlock className="h-6 w-6 shrink-0 rounded-lg" />
</div>
);
case "actions":
// Edit and delete only appear on hover in the loaded table.
return null;
default:
return null;
}
}

function SecretsTable({
isLoading,
isError,
rows,
searchQuery,
}: SecretsTableProps) {
if (isLoading) {
return <DataTableLoadingSkeleton showSelectionColumn={false} />;
return (
<UsageTableSkeleton columns={columns} SkeletonCell={SecretSkeletonCell} />
);
}

if (isError) {
Expand Down
61 changes: 59 additions & 2 deletions front/components/skills/import/DetectedSkillsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import { isImportableSkillStatus } from "@app/lib/skill_detection";
import {
Chip,
ContentMessage,
cn,
createSelectionColumn,
DataTable,
DataTableLoadingSkeleton,
InfoCircle,
LoadingBlock,
PuzzlePiece01,
ScrollableDataTable,
} from "@dust-tt/sparkle";
Expand All @@ -36,6 +37,34 @@ interface SkillRowData {

type SkillCellInfo = CellContext<SkillRowData, unknown>;

const DETECTED_SKILLS_SKELETON_ROWS: SkillRowData[] = Array.from(
{ length: 3 },
() => ({ name: "", status: "ready" })
);

function renderDetectedSkillSkeletonCell(columnId: string, rowIndex: number) {
switch (columnId) {
case "select":
return <LoadingBlock className="h-4 w-4 rounded-sm" />;
case "name":
return (
<div className="flex items-center gap-2">
<LoadingBlock className="h-5 w-5 shrink-0" />
<LoadingBlock
className={cn(
"h-3 max-w-full",
["w-32", "w-40", "w-28"][rowIndex % 3]
)}
/>
</div>
);
case "status":
return <LoadingBlock className="h-6 w-40 max-w-full rounded-[9px]" />;
default:
return null;
}
}

function getColumns(): ColumnDef<SkillRowData>[] {
return [
createSelectionColumn<SkillRowData>(),
Expand Down Expand Up @@ -103,6 +132,24 @@ export function DetectedSkillsList({
);

const columns = useMemo(() => getColumns(), []);
const skeletonColumns = useMemo(
() =>
columns.map((column) => {
const skeletonColumn = {
...column,
cell: (info: SkillCellInfo) =>
renderDetectedSkillSkeletonCell(info.column.id, info.row.index),
};
return column.id === "select"
? {
...skeletonColumn,
id: "select",
header: () => <LoadingBlock className="h-4 w-4 rounded-sm" />,
}
: skeletonColumn;
}),
[columns]
);

// Build rowSelection state from selectedSkillNames form field.
const rowSelection = useMemo(() => {
Expand Down Expand Up @@ -131,7 +178,17 @@ export function DetectedSkillsList({

return (
<>
{isDetecting && <DataTableLoadingSkeleton rows={3} />}
{isDetecting && (
<div role="status" aria-label="Detecting skills" aria-busy="true">
<div aria-hidden="true">
<ScrollableDataTable
data={DETECTED_SKILLS_SKELETON_ROWS}
columns={skeletonColumns}
maxHeight="max-h-64"
/>
</div>
</div>
)}
{detectError && (
<ContentMessage
title="Detection failed"
Expand Down
14 changes: 10 additions & 4 deletions front/components/workspace/UsageTableSkeleton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,25 @@ export interface UsageTableSkeletonCellProps {
rowIndex: number;
}

interface UsageTableSkeletonProps<TData> {
columns: ColumnDef<TData, string>[];
interface UsageTableSkeletonProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
SkeletonCell: ComponentType<UsageTableSkeletonCellProps>;
rowCount?: number;
rowHeight?: number;
}

export function UsageTableSkeleton<TData>({
/**
* @cc [owner:aubin-tchoi,label:react] content-specific-skeleton-cells
* Callers MUST reuse their loaded table's column definitions and provide a
* SkeletonCell that matches each column's content shape, including icons,
* avatars, text lines, and controls where present.
*/
export function UsageTableSkeleton<TData, TValue = string>({
columns,
SkeletonCell,
rowCount = 5,
rowHeight = 48,
}: UsageTableSkeletonProps<TData>) {
}: UsageTableSkeletonProps<TData, TValue>) {
const table = useReactTable({
data: [],
columns,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { ConfirmContext } from "@app/components/Confirm";
import { AllowSlackWorkflowDialog } from "@app/components/workspace/analytics/automations/AllowSlackWorkflowDialog";
import { SummaryCard } from "@app/components/workspace/analytics/SummaryCard";
import type { UsageTableSkeletonCellProps } from "@app/components/workspace/UsageTableSkeleton";
import { UsageTableSkeleton } from "@app/components/workspace/UsageTableSkeleton";
import { useSlackWorkflowsOverview } from "@app/hooks/useSlackWorkflowsOverview";
import type { ConsumptionPeriodSelection } from "@app/lib/analytics/consumption_period";
import { formatCredits } from "@app/lib/client/credits";
Expand All @@ -14,8 +16,8 @@ import { GLOBAL_SPACE_NAME } from "@app/types/groups";
import type { LightWorkspaceType } from "@app/types/user";
import {
Button,
cn,
DataTable,
DataTableLoadingSkeleton,
LoadingBlock,
Plus,
SearchInput,
Expand Down Expand Up @@ -271,6 +273,40 @@ interface SlackWorkflowsTableBodyProps {
setPagination: (pagination: PaginationState) => void;
}

function SlackWorkflowSkeletonCell({
columnId,
rowIndex,
}: UsageTableSkeletonCellProps) {
switch (columnId) {
case "botName":
return (
<LoadingBlock
className={cn(
"h-3 max-w-full",
["w-36", "w-44", "w-32", "w-40", "w-48"][rowIndex % 5]
)}
/>
);
case "spaceNames":
return (
<LoadingBlock
className={cn(
"h-3 max-w-full",
["w-24", "w-40", "w-32", "w-48", "w-28"][rowIndex % 5]
)}
/>
);
case "createdAt":
return <LoadingBlock className="h-3 w-20 max-w-full" />;
case "revoke":
return (
<LoadingBlock className="ml-auto h-6 w-6 rounded-lg pointer-fine:invisible" />
);
default:
return null;
}
}

function SlackWorkflowsTableBody({
columns,
rows,
Expand All @@ -282,7 +318,18 @@ function SlackWorkflowsTableBody({
}: SlackWorkflowsTableBodyProps) {
if (isLoading) {
return (
<DataTableLoadingSkeleton showSelectionColumn={false} showTrailingCell />
<div className="flex flex-col gap-2 overflow-x-auto">
<UsageTableSkeleton
columns={columns}
SkeletonCell={SlackWorkflowSkeletonCell}
/>
<div
aria-hidden="true"
className="flex h-8 items-center justify-end px-1"
>
<LoadingBlock className="h-3 w-14" />
</div>
</div>
);
}

Expand Down
Loading
Loading