Skip to content

new collection ui#1918

Merged
tlgimenes merged 3 commits intomainfrom
valls/collection-ui
Dec 3, 2025
Merged

new collection ui#1918
tlgimenes merged 3 commits intomainfrom
valls/collection-ui

Conversation

@rafavalls
Copy link
Copy Markdown
Collaborator

@rafavalls rafavalls commented Dec 2, 2025

Summary by CodeRabbit

  • New Features

    • Dual view modes (cards ↔ table) with header toggle, search bar, and sortable columns
    • New collection page/header components and integration icons; added a compact collection table wrapper
  • Improvements

    • Slimmed, consistent card design (title/description + icon), unified loading/empty states, refreshed table visuals
    • Cleaner header/layout and simplified connection management flows; CTA updated to "Add MCP"
  • Refactor

    • Per-item action UX simplified; custom card renderer support removed.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 2, 2025

Warning

Rate limit exceeded

@tlgimenes has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 1 minutes and 24 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between a53c6ed and 5207094.

📒 Files selected for processing (1)
  • apps/mesh/src/web/components/collections/collections-list.tsx (1 hunks)

Walkthrough

Replaces ResourceTable with CollectionTable, adds reusable collection UI primitives (page, header, search, display button, table wrapper, integration icon), simplifies card/table UIs and CollectionsList (schema-driven columns, new sortableFields), removes several legacy components (collection-table, user-indicator), and migrates many imports to the new collection-table types/components.

Changes

Cohort / File(s) Summary
Table rename & styling
packages/ui/src/components/collection-table.tsx
Renamed ResourceTable* → CollectionTable* exports/aliases; updated types, header/row/cell styling, and sort-icon rendering.
New collection primitives
apps/mesh/src/web/components/collections/collection-page.tsx, apps/mesh/src/web/components/collections/collection-header.tsx, apps/mesh/src/web/components/collections/collection-search.tsx, apps/mesh/src/web/components/collections/collection-display-button.tsx, apps/mesh/src/web/components/collections/collection-table-wrapper.tsx
Added CollectionPage, CollectionHeader, CollectionSearch, CollectionDisplayButton, CollectionTableWrapper for layout, header/actions, search, view-mode toggle + sort UI, and loading/empty-state handling.
Card simplification
apps/mesh/src/web/components/collections/collection-card.tsx
Simplified card UI: removed image header, dropdown actions, user/date details; now renders IntegrationIcon, title, and description; public props reduced to { item, schema }.
Collections list refactor
apps/mesh/src/web/components/collections/collections-list.tsx, apps/mesh/src/web/components/collections/types.ts
Reworked CollectionsList to use new components, added sortableFields?: string[], removed renderCard and onSearchChange, added local generateColumnsFromSchema helper, and adjusted props/sorting flow.
Deleted components
apps/mesh/src/web/components/collections/collection-table.tsx, apps/mesh/src/web/components/collections/user-indicator.tsx
Removed legacy CollectionTable (resource-table variant) and UserIndicator components.
Integration icon
apps/mesh/src/web/components/integration-icon.tsx
Added IntegrationIcon component with size variants and fallback icon.
Connection/details & layout updates
apps/mesh/src/web/components/details/connection.tsx, apps/mesh/src/web/components/details/layout.tsx
Reworked Tools/Collections rendering to use collection-driven components, added search/sort/view-mode, changed ToolsList shape, and adjusted header/back-button layout and actions placement.
Connections page rewrite
apps/web/src/routes/orgs/connections.tsx
Replaced MCP UI with collection-driven layout (CollectionPage/Header/Search/TableWrapper), removed session usage, simplified row actions and create/delete flows, and added icon/title/description handling.
Hook tweak
apps/mesh/src/web/hooks/use-collections.ts
Trim search term once (trimmedSearchTerm), use it consistently in filters/deps, and simplify field checks.
Empty state + minor UI
apps/mesh/src/web/components/empty-state.tsx, packages/ui/src/components/deco-chat-aside.tsx, packages/ui/src/styles/global.css
EmptyState omits image when prop is null; chat-aside header height h-10→h-12; CSS --card color adjusted.
Import migration (many files)
apps/web/src/components/... (agents, audit, documents, integrations, invites, resources-v2, settings, views, workflows, etc.)
Updated numerous imports to use @deco/ui/components/collection-table.tsx (Table/TableColumn) instead of resource-table.tsx.
Package pinning
apps/mesh/package.json
Pinned @better-auth/sso and better-auth from caret ranges to exact versions.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Page as CollectionPage
    participant Header as CollectionHeader
    participant Search as CollectionSearch
    participant DisplayBtn as CollectionDisplayButton
    participant Wrapper as CollectionTableWrapper
    participant Cards as CardGrid

    User->>Page: open collections view
    Page->>Header: mount (provide viewMode, sort state)
    Header->>DisplayBtn: render toggle & sort options
    User->>Search: type query
    Search->>Page: emit onChange(trimmed)
    Page->>Wrapper: apply filter/sort -> update data
    User->>DisplayBtn: toggle view (cards/table)
    DisplayBtn->>Page: emit onViewModeChange
    alt cards
        Page->>Cards: render card grid (IntegrationIcon, title, desc)
        Cards->>User: show cards
    else table
        Page->>Wrapper: render table view
        Wrapper->>User: show rows (sorted/filtered)
    end
    User->>DisplayBtn: pick sort option
    DisplayBtn->>Page: emit onSort(key)
    Page->>Wrapper: re-sort & re-render
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas to focus:
    • collections-list refactor (generateColumnsFromSchema, prop changes, removal of renderCard/onSearchChange)
    • CollectionCard public API change (removed readOnly/onAction) — ensure consumers updated
    • Wide import migration — verify no leftover resource-table imports and type compatibility
    • CollectionTable rename & styling changes — confirm behavior and sort UI expectations
    • Connections page rewrites (create/update/delete flows, removed session usage, navigation changes)

Possibly related PRs

Suggested reviewers

  • rafavalls
  • viktormarinho
  • mcandeia

Poem

"I dug a little tunnel through the code tonight,
Cards and tables now spring into light,
Icons snug like carrots, tidy and small,
Search trims the hedges, sorts stand tall,
I hopped around the UI — what a delight! 🥕🐇"

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is empty; no content was provided by the author to explain the changes, rationale, or testing performed. Add a detailed description explaining what was changed and why, including screenshots or videos if UI changes were made, and confirm that the review checklist items are addressed.
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'new collection ui' is vague and lacks specificity about what changes were made or what aspects of the collection UI were modified. Provide a more descriptive title that specifies the main change, such as 'Refactor collection components with new display modes and simplified card layout' or 'Introduce collection UI overhaul with cards and table view modes'.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Dec 2, 2025

Deploying decocms-admin-frontend with  Cloudflare Pages  Cloudflare Pages

Latest commit: a53c6ed
Status: ✅  Deploy successful!
Preview URL: https://d4b4815f.admin-decocms-frontend.pages.dev
Branch Preview URL: https://valls-collection-ui.admin-decocms-frontend.pages.dev

View logs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/mesh/src/web/components/collections/collection-card.tsx (1)

7-12: Remove unused props from interface.

The CollectionCardProps interface declares readOnly and onAction props (lines 10-11), but the component implementation doesn't use them. This creates confusion about the component's actual API surface.

Apply this diff to align the interface with actual usage:

 interface CollectionCardProps<T extends BaseCollectionEntity> {
   item: T;
   schema: z.AnyZodObject;
-  readOnly?: boolean;
-  onAction: (action: "open" | "delete" | "duplicate", item: T) => void;
 }
apps/mesh/src/web/components/collections/collection-table.tsx (1)

9-18: Remove or document unused props in interface.

The CollectionTableProps interface declares readOnly and onAction props (lines 12-13), but the component implementation prefixes them with underscores (lines 23-24), indicating they're intentionally unused. Since the Actions column functionality was removed entirely, these props appear to be dead code in the public API.

If these props are no longer needed, remove them from the interface:

 interface CollectionTableProps<T extends BaseCollectionEntity> {
   data: T[];
   schema: z.AnyZodObject;
-  readOnly?: boolean;
-  onAction: (action: "open" | "delete" | "duplicate", item: T) => void;
   sortKey?: string;
   sortDirection?: "asc" | "desc" | null;
   onSort?: (key: string) => void;
   onRowClick?: (item: T) => void;
 }

And update the function signature:

 export function CollectionTable<T extends BaseCollectionEntity>({
   data,
   schema,
-  readOnly: _readOnly,
-  onAction: _onAction,
   sortKey,
   sortDirection,
   onSort,
   onRowClick,
 }: CollectionTableProps<T>) {

If they're kept for backward compatibility, add a JSDoc comment explaining this.

Also applies to: 23-24

🧹 Nitpick comments (10)
apps/mesh/src/web/components/integration-icon.tsx (1)

4-9: Consider reusing the existing IntegrationIconProps interface.

An IntegrationIconProps interface already exists in apps/web/src/components/integrations/common.tsx (lines 4-9) with a more comprehensive set of size options ("xs" | "sm" | "base" | "lg" | "xl" | "2xl" | "3xl"). Duplicating this interface creates maintenance overhead and potential type inconsistencies.

Consider importing and extending the existing interface, or adjusting the size mapping to align with the shared definition:

+import type { IntegrationIconProps as BaseIntegrationIconProps } from "apps/web/src/components/integrations/common.tsx";
+
-interface IntegrationIconProps {
-  icon: string | null | undefined;
-  name: string;
-  size?: "sm" | "md" | "lg";
-  className?: string;
-}
+interface IntegrationIconProps extends Omit<BaseIntegrationIconProps, 'icon'> {
+  icon: string | null | undefined;
+}

Alternatively, if the simplified size options are intentional for this context, consider renaming to avoid confusion (e.g., IntegrationIconDisplayProps).

apps/mesh/src/web/components/collections/collection-table-wrapper.tsx (1)

26-32: Consider using a loading spinner for better visual feedback.

The coding guidelines recommend "loading spinners" for proper UI feedback. A simple text message may not provide sufficient visual indication of loading state.

+import { Loader2 } from "lucide-react";
+
 // In the loading block:
   if (isLoading) {
     return (
       <div className="flex items-center justify-center h-full">
-        <div className="text-muted-foreground">Loading...</div>
+        <Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
       </div>
     );
   }
apps/mesh/src/web/components/collections/collection-display-button.tsx (2)

79-116: Add accessibility attributes to view mode toggle buttons.

The toggle buttons lack accessibility attributes. Screen readers won't announce which view mode is currently active.

               <button
                 ref={tableRef}
                 type="button"
+                aria-pressed={viewMode === "table"}
+                aria-label="Table view"
                 onClick={() => {
                   onViewModeChange("table");
                 }}
                 className="relative z-10 flex-1 flex items-center justify-center gap-2 h-12 px-4 rounded-lg"
               >
                 ...
               </button>
               <button
                 ref={cardsRef}
                 type="button"
+                aria-pressed={viewMode === "cards"}
+                aria-label="Cards view"
                 onClick={() => {
                   onViewModeChange("cards");
                 }}
                 className="relative z-10 flex-1 flex items-center justify-center gap-2 h-12 px-4 rounded-lg"
               >

54-63: Consider requestAnimationFrame instead of setTimeout(0) for more reliable DOM measurement timing.

setTimeout(0) can be unreliable for post-render DOM measurements. requestAnimationFrame ensures the callback runs after the browser has painted.

       onOpenChange={(open) => {
         if (open) {
-          setTimeout(() => {
+          requestAnimationFrame(() => {
             const ref = viewMode === "table" ? tableRef : cardsRef;
             updateIndicator(ref);
-          }, 0);
+          });
         }
       }}
apps/mesh/src/web/routes/orgs/connections.tsx (3)

214-214: Address the TODO: hardcoded created_by value.

The created_by field is hardcoded to "system", which will produce incorrect audit trails. Consider using a user context hook to get the actual user ID.

Would you like me to open an issue to track this, or help implement user ID retrieval from context?


510-520: Consider useDeferredValue for search to prevent blocking user input.

Per coding guidelines, useDeferredValue should be used for search operations to keep the input responsive during filtering. This is especially important as the connections list grows.

import { useDeferredValue } from "react";

// In the component:
const deferredSearch = useDeferredValue(listState.search);

// Pass deferredSearch to the data-fetching hook instead of listState.search
// Keep listState.search for the input value to maintain responsive typing

Based on learnings, useDeferredValue prevents blocking user input during filtering.


525-528: Loading UI is duplicated between cards view and CollectionTableWrapper.

Consider extracting the loading state into a shared component or pattern to maintain consistency and reduce duplication.

apps/mesh/src/web/components/details/connection.tsx (3)

698-713: Consider using useDeferredValue for search to prevent blocking user input.

Per coding guidelines, search operations should use useDeferredValue to prevent blocking user input during filtering. Additionally, the sorting state cycle logic is correct but could be simplified.

 const [search, setSearch] = useState("");
+const deferredSearch = useDeferredValue(search);
 const [viewMode, setViewMode] = useState<"table" | "cards">("table");
 const [sortKey, setSortKey] = useState<string | undefined>("name");
 const [sortDirection, setSortDirection] = useState<"asc" | "desc" | null>(
   "asc",
 );

 const handleSort = (key: string) => {
   if (sortKey === key) {
-    setSortDirection((prev) =>
-      prev === "asc" ? "desc" : prev === "desc" ? null : "asc",
-    );
-    if (sortDirection === "desc") setSortKey(undefined);
+    if (sortDirection === "asc") {
+      setSortDirection("desc");
+    } else if (sortDirection === "desc") {
+      setSortKey(undefined);
+      setSortDirection(null);
+    } else {
+      setSortDirection("asc");
+    }
   } else {
     setSortKey(key);
     setSortDirection("asc");
   }
 };

Then update the filteredTools dependency to use deferredSearch:

 const filteredTools = useMemo(() => {
   if (!tools || tools.length === 0) return [];
-  if (!search.trim()) return tools;
-  const searchLower = search.toLowerCase();
+  if (!deferredSearch.trim()) return tools;
+  const searchLower = deferredSearch.toLowerCase();
   return tools.filter(
     (t) =>
       t.name.toLowerCase().includes(searchLower) ||
       (t.description && t.description.toLowerCase().includes(searchLower)),
   );
-}, [tools, search]);
+}, [tools, deferredSearch]);

726-735: Casting to Record<string, unknown> could be avoided with better typing.

The cast (a as unknown as Record<string, unknown>) is a workaround for accessing dynamic keys. Since the tool type is { name: string; description?: string }, you could simplify this with a type guard or index signature.

 const sortedTools = useMemo(() => {
   if (!sortKey || !sortDirection) return filteredTools;

   return [...filteredTools].sort((a, b) => {
-    const aVal = (a as unknown as Record<string, unknown>)[sortKey] || "";
-    const bVal = (b as unknown as Record<string, unknown>)[sortKey] || "";
+    const aVal = sortKey === "name" ? a.name : (a.description ?? "");
+    const bVal = sortKey === "name" ? b.name : (b.description ?? "");
     const comparison = String(aVal).localeCompare(String(bVal));
     return sortDirection === "asc" ? comparison : -comparison;
   });
 }, [filteredTools, sortKey, sortDirection]);

977-995: Duplicated sort options generation logic between ToolsList and CollectionContent.

Both components generate sort options from schema/columns with similar filtering logic. Consider extracting this into a shared utility function for consistency and maintainability.

You could create a shared helper:

// In a shared utils file
export function generateSortOptionsFromSchema(
  schema: z.AnyZodObject,
  excludeFields: string[] = ["id", "created_at", "updated_at", "created_by", "updated_by", "organization_id"]
): Array<{ id: string; label: string }> {
  return Object.keys(schema.shape)
    .filter((key) => !excludeFields.includes(key))
    .map((key) => ({
      id: key,
      label: key.charAt(0).toUpperCase() + key.slice(1).replace(/_/g, " "),
    }));
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54476e5 and 4cb70d2.

⛔ Files ignored due to path filters (1)
  • apps/mesh/public/emptystate-mcp.svg is excluded by !**/*.svg
📒 Files selected for processing (18)
  • apps/mesh/src/web/components/collections/collection-card.tsx (3 hunks)
  • apps/mesh/src/web/components/collections/collection-display-button.tsx (1 hunks)
  • apps/mesh/src/web/components/collections/collection-header.tsx (1 hunks)
  • apps/mesh/src/web/components/collections/collection-page.tsx (1 hunks)
  • apps/mesh/src/web/components/collections/collection-search.tsx (1 hunks)
  • apps/mesh/src/web/components/collections/collection-table-wrapper.tsx (1 hunks)
  • apps/mesh/src/web/components/collections/collection-table.tsx (4 hunks)
  • apps/mesh/src/web/components/collections/collections-list.tsx (2 hunks)
  • apps/mesh/src/web/components/collections/types.ts (1 hunks)
  • apps/mesh/src/web/components/details/connection.tsx (6 hunks)
  • apps/mesh/src/web/components/details/layout.tsx (1 hunks)
  • apps/mesh/src/web/components/empty-state.tsx (1 hunks)
  • apps/mesh/src/web/components/empty-states/search-empty-state.tsx (1 hunks)
  • apps/mesh/src/web/components/integration-icon.tsx (1 hunks)
  • apps/mesh/src/web/routes/orgs/connections.tsx (10 hunks)
  • packages/ui/src/components/deco-chat-aside.tsx (1 hunks)
  • packages/ui/src/components/resource-table.tsx (3 hunks)
  • packages/ui/src/styles/global.css (1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/data-flow.mdc)

Handle loading and error states appropriately when using React Query hooks in components

Files:

  • packages/ui/src/components/deco-chat-aside.tsx
  • apps/mesh/src/web/components/collections/collection-page.tsx
  • apps/mesh/src/web/components/empty-states/search-empty-state.tsx
  • apps/mesh/src/web/components/collections/collection-search.tsx
  • apps/mesh/src/web/components/empty-state.tsx
  • apps/mesh/src/web/components/collections/collection-header.tsx
  • apps/mesh/src/web/components/collections/collection-display-button.tsx
  • apps/mesh/src/web/components/details/layout.tsx
  • apps/mesh/src/web/components/integration-icon.tsx
  • apps/mesh/src/web/components/collections/collection-card.tsx
  • apps/mesh/src/web/components/details/connection.tsx
  • apps/mesh/src/web/components/collections/collections-list.tsx
  • packages/ui/src/components/resource-table.tsx
  • apps/mesh/src/web/components/collections/collection-table-wrapper.tsx
  • apps/mesh/src/web/routes/orgs/connections.tsx
  • apps/mesh/src/web/components/collections/collection-table.tsx
**/*.{ts,tsx,js,jsx,css}

📄 CodeRabbit inference engine (.cursor/rules/structure.mdc)

Run formatting with bun run fmt before opening pull requests

Keep Tailwind design tokens consistent per plugins/ensure-tailwind-design-system-tokens.ts which will fail builds otherwise

Files:

  • packages/ui/src/components/deco-chat-aside.tsx
  • packages/ui/src/styles/global.css
  • apps/mesh/src/web/components/collections/collection-page.tsx
  • apps/mesh/src/web/components/empty-states/search-empty-state.tsx
  • apps/mesh/src/web/components/collections/collection-search.tsx
  • apps/mesh/src/web/components/empty-state.tsx
  • apps/mesh/src/web/components/collections/collection-header.tsx
  • apps/mesh/src/web/components/collections/collection-display-button.tsx
  • apps/mesh/src/web/components/details/layout.tsx
  • apps/mesh/src/web/components/integration-icon.tsx
  • apps/mesh/src/web/components/collections/collection-card.tsx
  • apps/mesh/src/web/components/details/connection.tsx
  • apps/mesh/src/web/components/collections/collections-list.tsx
  • packages/ui/src/components/resource-table.tsx
  • apps/mesh/src/web/components/collections/types.ts
  • apps/mesh/src/web/components/collections/collection-table-wrapper.tsx
  • apps/mesh/src/web/routes/orgs/connections.tsx
  • apps/mesh/src/web/components/collections/collection-table.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/structure.mdc)

Run type checking with bun run check before opening pull requests

Favor TypeScript types over any keyword

**/*.{ts,tsx}: Write concise, maintainable, and technically accurate TypeScript code with relevant examples
Use functional and declarative programming patterns; avoid classes
Favor iteration and modularization to adhere to DRY principles and avoid code duplication
Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError)
Organize files systematically: each file should contain only related content, such as exported components, subcomponents, helpers, static content, and types
Favor named exports for functions
Use TypeScript for all code
Prefer interfaces over types for their extendability and ability to merge
Avoid enums; use maps instead for better type safety and flexibility
Use the "function" keyword for pure functions to benefit from hoisting and clarity

Files:

  • packages/ui/src/components/deco-chat-aside.tsx
  • apps/mesh/src/web/components/collections/collection-page.tsx
  • apps/mesh/src/web/components/empty-states/search-empty-state.tsx
  • apps/mesh/src/web/components/collections/collection-search.tsx
  • apps/mesh/src/web/components/empty-state.tsx
  • apps/mesh/src/web/components/collections/collection-header.tsx
  • apps/mesh/src/web/components/collections/collection-display-button.tsx
  • apps/mesh/src/web/components/details/layout.tsx
  • apps/mesh/src/web/components/integration-icon.tsx
  • apps/mesh/src/web/components/collections/collection-card.tsx
  • apps/mesh/src/web/components/details/connection.tsx
  • apps/mesh/src/web/components/collections/collections-list.tsx
  • packages/ui/src/components/resource-table.tsx
  • apps/mesh/src/web/components/collections/types.ts
  • apps/mesh/src/web/components/collections/collection-table-wrapper.tsx
  • apps/mesh/src/web/routes/orgs/connections.tsx
  • apps/mesh/src/web/components/collections/collection-table.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/structure.mdc)

Run linting with bun run lint before opening pull requests

**/*.{ts,tsx,js,jsx}: Use PascalCase for component and class names
Use camelCase for hooks and utility function names

Files:

  • packages/ui/src/components/deco-chat-aside.tsx
  • apps/mesh/src/web/components/collections/collection-page.tsx
  • apps/mesh/src/web/components/empty-states/search-empty-state.tsx
  • apps/mesh/src/web/components/collections/collection-search.tsx
  • apps/mesh/src/web/components/empty-state.tsx
  • apps/mesh/src/web/components/collections/collection-header.tsx
  • apps/mesh/src/web/components/collections/collection-display-button.tsx
  • apps/mesh/src/web/components/details/layout.tsx
  • apps/mesh/src/web/components/integration-icon.tsx
  • apps/mesh/src/web/components/collections/collection-card.tsx
  • apps/mesh/src/web/components/details/connection.tsx
  • apps/mesh/src/web/components/collections/collections-list.tsx
  • packages/ui/src/components/resource-table.tsx
  • apps/mesh/src/web/components/collections/types.ts
  • apps/mesh/src/web/components/collections/collection-table-wrapper.tsx
  • apps/mesh/src/web/routes/orgs/connections.tsx
  • apps/mesh/src/web/components/collections/collection-table.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Keep imports sorted in all code files

Files:

  • packages/ui/src/components/deco-chat-aside.tsx
  • apps/mesh/src/web/components/collections/collection-page.tsx
  • apps/mesh/src/web/components/empty-states/search-empty-state.tsx
  • apps/mesh/src/web/components/collections/collection-search.tsx
  • apps/mesh/src/web/components/empty-state.tsx
  • apps/mesh/src/web/components/collections/collection-header.tsx
  • apps/mesh/src/web/components/collections/collection-display-button.tsx
  • apps/mesh/src/web/components/details/layout.tsx
  • apps/mesh/src/web/components/integration-icon.tsx
  • apps/mesh/src/web/components/collections/collection-card.tsx
  • apps/mesh/src/web/components/details/connection.tsx
  • apps/mesh/src/web/components/collections/collections-list.tsx
  • packages/ui/src/components/resource-table.tsx
  • apps/mesh/src/web/components/collections/types.ts
  • apps/mesh/src/web/components/collections/collection-table-wrapper.tsx
  • apps/mesh/src/web/routes/orgs/connections.tsx
  • apps/mesh/src/web/components/collections/collection-table.tsx
packages/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Enforce kebab-case filenames in shared packages via plugins/enforce-kebab-case-file-names.ts

Files:

  • packages/ui/src/components/deco-chat-aside.tsx
  • packages/ui/src/components/resource-table.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/react-ts.mdc)

**/*.tsx: Use functional components with TypeScript interfaces
Favor using the already-present ui components in packages/ui folder instead of creating custom components
Use useMemo for expensive computations like filtering and sorting operations to optimize performance in data-intensive applications
Memoize objects and arrays using useMemo to maintain stable references across renders
Use useDeferredValue for search operations to prevent blocking user input during filtering
Follow MCP (Model Context Protocol) patterns for consistent data fetching with hooks like useAgents, useIntegrations, and useUpdateAgent
Use react-hook-form with Zod validation instead of manual useState for form management
Avoid prop drilling by using Form Context to share form state across components
Use design system Form components (Form, FormControl, FormField, FormItem, FormLabel, FormMessage) from @deco/ui with proper error handling
Apply Single Responsibility Principle to components: each component should have a focused purpose and be composable
Ensure dependency arrays contain stable references; avoid creating inline objects or arrays that cause unnecessary re-renders
Implement consistent error and loading state handling with proper UI feedback (loading spinners, error messages, empty states)
Apply changes immediately to DOM for instant visual feedback, then debounce state updates for features like theme editors and live previews
Use custom events dispatched via window.dispatchEvent and window.addEventListener for cross-component communication of state updates
Implement single-level undo that reverts to the saved state, not just the previous change; use refs to track original saved values
Use useSetThreadContextEffect to inject rules and toolsets into AI chat threads; use explicit union types for context items
Extract complex logic from JSX into separate components instead of using IIFEs (immediately invoked function expressions)

Files:

  • packages/ui/src/components/deco-chat-aside.tsx
  • apps/mesh/src/web/components/collections/collection-page.tsx
  • apps/mesh/src/web/components/empty-states/search-empty-state.tsx
  • apps/mesh/src/web/components/collections/collection-search.tsx
  • apps/mesh/src/web/components/empty-state.tsx
  • apps/mesh/src/web/components/collections/collection-header.tsx
  • apps/mesh/src/web/components/collections/collection-display-button.tsx
  • apps/mesh/src/web/components/details/layout.tsx
  • apps/mesh/src/web/components/integration-icon.tsx
  • apps/mesh/src/web/components/collections/collection-card.tsx
  • apps/mesh/src/web/components/details/connection.tsx
  • apps/mesh/src/web/components/collections/collections-list.tsx
  • packages/ui/src/components/resource-table.tsx
  • apps/mesh/src/web/components/collections/collection-table-wrapper.tsx
  • apps/mesh/src/web/routes/orgs/connections.tsx
  • apps/mesh/src/web/components/collections/collection-table.tsx
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/api-development.mdc)

Define both input and output Zod schemas for all API operations with appropriate type constraints (min/max length, optional fields, descriptions) and map them to TypeScript interfaces

Files:

  • apps/mesh/src/web/components/collections/types.ts
🧠 Learnings (27)
📚 Learning: 2025-12-01T21:09:54.719Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: docs/.cursor/rules/main.mdc:0-0
Timestamp: 2025-12-01T21:09:54.719Z
Learning: Applies to docs/view/{astro.config.mjs,src/styles/global.css} : Customize theme through view/astro.config.mjs for basic configuration and view/src/styles/global.css for Tailwind CSS theming including fonts and colors

Applied to files:

  • packages/ui/src/styles/global.css
📚 Learning: 2025-12-01T22:44:41.561Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-12-01T22:44:41.561Z
Learning: Applies to **/*.tsx : Use functional components with TypeScript interfaces

Applied to files:

  • apps/mesh/src/web/components/collections/collection-page.tsx
  • apps/mesh/src/web/components/collections/collection-card.tsx
  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T21:09:05.323Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/structure.mdc:0-0
Timestamp: 2025-12-01T21:09:05.323Z
Learning: Applies to apps/web/src/**/*.{ts,tsx} : Use React 19 for UI development in the decocms admin shell and marketplace

Applied to files:

  • apps/mesh/src/web/components/collections/collection-page.tsx
  • apps/mesh/src/web/components/collections/collection-card.tsx
  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T22:44:41.561Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-12-01T22:44:41.561Z
Learning: Applies to **/*.tsx : Implement consistent error and loading state handling with proper UI feedback (loading spinners, error messages, empty states)

Applied to files:

  • apps/mesh/src/web/components/empty-states/search-empty-state.tsx
  • apps/mesh/src/web/components/empty-state.tsx
📚 Learning: 2025-12-01T22:44:41.561Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-12-01T22:44:41.561Z
Learning: Applies to **/*.tsx : Use useDeferredValue for search operations to prevent blocking user input during filtering

Applied to files:

  • apps/mesh/src/web/components/collections/collection-search.tsx
📚 Learning: 2025-12-01T22:44:41.561Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-12-01T22:44:41.561Z
Learning: Applies to **/*.tsx : Use design system Form components (Form, FormControl, FormField, FormItem, FormLabel, FormMessage) from deco/ui with proper error handling

Applied to files:

  • apps/mesh/src/web/components/collections/collection-search.tsx
  • apps/mesh/src/web/components/collections/collection-card.tsx
  • apps/mesh/src/web/components/details/connection.tsx
  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T21:08:29.999Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/native-apps-and-views.mdc:0-0
Timestamp: 2025-12-01T21:08:29.999Z
Learning: Applies to apps/web/src/components/layout/*.tsx : Conditionally render AI chat panel based on current route to show it only on specific pages where it provides value

Applied to files:

  • apps/mesh/src/web/components/empty-state.tsx
📚 Learning: 2025-12-01T21:09:05.323Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/structure.mdc:0-0
Timestamp: 2025-12-01T21:09:05.323Z
Learning: Applies to apps/web/src/**/*.{ts,tsx} : Use shadcn/ui shared design system components from packages/ui

Applied to files:

  • apps/mesh/src/web/components/integration-icon.tsx
  • apps/mesh/src/web/components/collections/collection-card.tsx
  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T21:08:29.999Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/native-apps-and-views.mdc:0-0
Timestamp: 2025-12-01T21:08:29.999Z
Learning: Applies to apps/web/src/components/integrations/apps.ts : Add native app to NATIVE_APPS array with id prefixed with 'native:::', descriptive name, icon, and provider set to 'native'

Applied to files:

  • apps/mesh/src/web/components/integration-icon.tsx
📚 Learning: 2025-12-01T21:08:29.999Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/native-apps-and-views.mdc:0-0
Timestamp: 2025-12-01T21:08:29.999Z
Learning: Applies to apps/web/src/components/**/*-view.tsx : Use explicit union type annotation for threadContextItems array: Array<{ id: string; type: 'rule'; text: string } | { id: string; type: 'toolset'; integrationId: string; enabledTools: string[] }>

Applied to files:

  • apps/mesh/src/web/components/integration-icon.tsx
  • apps/mesh/src/web/components/collections/collection-card.tsx
  • apps/mesh/src/web/components/details/connection.tsx
  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T22:44:41.561Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-12-01T22:44:41.561Z
Learning: Applies to **/*.tsx : Favor using the already-present ui components in `packages/ui` folder instead of creating custom components

Applied to files:

  • apps/mesh/src/web/components/collections/collection-card.tsx
  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T21:07:47.300Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/data-flow.mdc:0-0
Timestamp: 2025-12-01T21:07:47.300Z
Learning: Applies to **/*{tools,api,index}*.ts : Organize MCP tools into typed collections (GLOBAL_TOOLS, WORKSPACE_TOOLS, AGENT_TOOLS, EMAIL_TOOLS) by functionality and scope

Applied to files:

  • apps/mesh/src/web/components/details/connection.tsx
📚 Learning: 2025-12-01T22:44:21.356Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/api-development.mdc:0-0
Timestamp: 2025-12-01T22:44:21.356Z
Learning: Applies to packages/sdk/src/mcp/index.ts : Export all new API tools in the WORKSPACE_TOOLS array with proper import statements from feature-specific api.ts files

Applied to files:

  • apps/mesh/src/web/components/details/connection.tsx
📚 Learning: 2025-12-01T21:08:29.999Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/native-apps-and-views.mdc:0-0
Timestamp: 2025-12-01T21:08:29.999Z
Learning: Applies to apps/web/src/components/chat/provider.tsx : In chat provider, handle tool calls and invalidate/refetch queries when AI updates features; dispatch custom events for UI updates

Applied to files:

  • apps/mesh/src/web/components/details/connection.tsx
📚 Learning: 2025-12-01T21:08:29.999Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/native-apps-and-views.mdc:0-0
Timestamp: 2025-12-01T21:08:29.999Z
Learning: Applies to apps/web/src/components/sidebar/index.tsx : Update sidebar navigation by adding feature name to wellKnownItems array and resourceTypeOrder array

Applied to files:

  • apps/mesh/src/web/components/details/connection.tsx
  • apps/mesh/src/web/components/collections/collections-list.tsx
📚 Learning: 2025-12-01T21:08:29.999Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/native-apps-and-views.mdc:0-0
Timestamp: 2025-12-01T21:08:29.999Z
Learning: Applies to apps/web/src/components/**/*-view.tsx : In main view components, use `useSetThreadContextEffect` hook to inject context items (rules and toolsets) for AI chat integration

Applied to files:

  • apps/mesh/src/web/components/details/connection.tsx
  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T21:09:54.719Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: docs/.cursor/rules/main.mdc:0-0
Timestamp: 2025-12-01T21:09:54.719Z
Learning: Applies to docs/server/**/*.ts : Define tools as functions that create tool instances with id, description, inputSchema, outputSchema, and execute method in server/main.ts

Applied to files:

  • apps/mesh/src/web/components/details/connection.tsx
📚 Learning: 2025-12-01T22:44:41.561Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-12-01T22:44:41.561Z
Learning: Applies to **/*.tsx : Follow MCP (Model Context Protocol) patterns for consistent data fetching with hooks like useAgents, useIntegrations, and useUpdateAgent

Applied to files:

  • apps/mesh/src/web/components/details/connection.tsx
  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T22:44:41.561Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-12-01T22:44:41.561Z
Learning: Applies to **/*.tsx : Use react-hook-form with Zod validation instead of manual useState for form management

Applied to files:

  • apps/mesh/src/web/components/details/connection.tsx
  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T21:09:54.719Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: docs/.cursor/rules/main.mdc:0-0
Timestamp: 2025-12-01T21:09:54.719Z
Learning: Applies to docs/server/**/*.ts : Use Zod (z.object) for defining inputSchema and outputSchema in tools and workflows

Applied to files:

  • apps/mesh/src/web/components/details/connection.tsx
📚 Learning: 2025-12-01T22:44:41.561Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-12-01T22:44:41.561Z
Learning: Applies to **/*.tsx : Ensure dependency arrays contain stable references; avoid creating inline objects or arrays that cause unnecessary re-renders

Applied to files:

  • apps/mesh/src/web/components/collections/collections-list.tsx
📚 Learning: 2025-12-01T21:08:29.999Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/native-apps-and-views.mdc:0-0
Timestamp: 2025-12-01T21:08:29.999Z
Learning: Applies to apps/web/src/components/**/resource-list.tsx : Create resource list component that uses `useTrackNativeViewVisit` hook to track view visits for recents and pinning functionality

Applied to files:

  • apps/mesh/src/web/components/collections/collections-list.tsx
  • apps/mesh/src/web/components/collections/collection-table-wrapper.tsx
📚 Learning: 2025-12-01T21:08:08.813Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/deconfig.mdc:0-0
Timestamp: 2025-12-01T21:08:08.813Z
Learning: Applies to packages/sdk/src/mcp/deconfig/**/*.ts : Implement modular composition where merge() reuses diff() + transactionalWrite(), and transactionalWrite() has internal method for different scenarios to ensure code reuse and consistency

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T22:44:41.561Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-12-01T22:44:41.561Z
Learning: Applies to **/*.tsx : Avoid prop drilling by using Form Context to share form state across components

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T21:08:29.999Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/native-apps-and-views.mdc:0-0
Timestamp: 2025-12-01T21:08:29.999Z
Learning: Applies to apps/web/src/components/*-editor/*.tsx : Use debounced form updates with immediate DOM manipulation for real-time visual feedback in editors

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T21:08:29.999Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/native-apps-and-views.mdc:0-0
Timestamp: 2025-12-01T21:08:29.999Z
Learning: Applies to packages/sdk/src/hooks/*.ts : Create React Query hooks using `useQuery` for data fetching and `useMutation` for updates, with proper cache invalidation on success

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T21:07:47.300Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/data-flow.mdc:0-0
Timestamp: 2025-12-01T21:07:47.300Z
Learning: Applies to **/*.{tsx,jsx} : Handle loading and error states appropriately when using React Query hooks in components

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
🧬 Code graph analysis (8)
apps/mesh/src/web/components/collections/collection-header.tsx (1)
apps/mesh/src/web/components/collections/collection-display-button.tsx (1)
  • CollectionDisplayButton (21-179)
apps/mesh/src/web/components/collections/collection-display-button.tsx (1)
packages/ui/src/components/dropdown-menu.tsx (4)
  • DropdownMenu (242-242)
  • DropdownMenuTrigger (256-256)
  • DropdownMenuContent (244-244)
  • DropdownMenuItem (246-246)
apps/mesh/src/web/components/integration-icon.tsx (1)
apps/web/src/components/integrations/common.tsx (1)
  • IntegrationIconProps (5-10)
apps/mesh/src/web/components/collections/collection-card.tsx (2)
packages/ui/src/components/card.tsx (1)
  • Card (79-79)
apps/mesh/src/web/components/integration-icon.tsx (1)
  • IntegrationIcon (11-59)
apps/mesh/src/web/components/collections/collections-list.tsx (5)
apps/mesh/src/web/components/collections/types.ts (1)
  • CollectionsListProps (7-132)
apps/mesh/src/web/components/collections/collection-display-button.tsx (1)
  • CollectionDisplayButton (21-179)
apps/mesh/src/web/components/empty-state.tsx (1)
  • EmptyState (28-63)
apps/mesh/src/web/components/collections/collection-card.tsx (1)
  • CollectionCard (64-97)
apps/mesh/src/web/components/collections/collection-table-wrapper.tsx (1)
  • CollectionTableWrapper (16-58)
packages/ui/src/components/resource-table.tsx (1)
packages/ui/src/components/table.tsx (3)
  • TableHeader (114-114)
  • TableRow (115-115)
  • TableHead (113-113)
apps/mesh/src/web/components/collections/collection-table-wrapper.tsx (1)
packages/ui/src/components/resource-table.tsx (2)
  • ResourceTable (34-133)
  • ResourceTable (136-136)
apps/mesh/src/web/components/collections/collection-table.tsx (1)
apps/mesh/src/web/components/collections/user-indicator.tsx (1)
  • UserIndicator (24-54)
🪛 GitHub Actions: Tests
apps/mesh/src/web/components/collections/collections-list.tsx

[error] 90-90: Type '((mode: "table" | "cards") => void) | undefined' is not assignable to type '(mode: "table" | "cards") => void'.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (26)
packages/ui/src/components/deco-chat-aside.tsx (1)

44-44: LGTM! Header height increase aligns with collection UI updates.

The height increase from h-10 to h-12 is intentional and consistent with the PR's goal of standardizing header layouts across the collection UI. The flex layout structure will adapt properly to the increased header height.

packages/ui/src/styles/global.css (1)

30-30: The --card color change is valid and requires no action.

The --card variable at line 30 has been updated to oklch(1 0 0), matching the --card-foreground approach established in the design system. Visual distinction is maintained through borders (--border: oklch(0.85 0 0)) and foreground colors (--card-foreground: oklch(0.205 0 0)). The design system validation plugin is currently disabled and only applies to JSX className attributes, not CSS custom properties, so no design system validation check is needed for this CSS file.

apps/mesh/src/web/components/collections/collection-page.tsx (1)

1-13: LGTM! Clean wrapper component.

The implementation is straightforward and follows functional component patterns with proper TypeScript interfaces. The component serves as a focused layout container for collection pages.

apps/mesh/src/web/components/integration-icon.tsx (1)

29-41: LGTM! Proper fallback handling.

The icon rendering logic correctly handles both the presence and absence of icon URLs with appropriate fallback UI.

apps/mesh/src/web/components/empty-states/search-empty-state.tsx (1)

1-17: LGTM! Consistent empty state implementation.

The component follows proper empty state patterns and provides clear user feedback for empty search results. The use of HTML entities for quotes and the default entityName parameter enhance reusability.

apps/mesh/src/web/components/collections/collection-card.tsx (1)

78-95: LGTM! Simplified card rendering.

The updated card layout using IntegrationIcon provides a clean, consistent presentation with proper fallback for missing descriptions.

packages/ui/src/components/resource-table.tsx (2)

43-55: Improved sort icon rendering.

The fixed-width container with conditional rendering prevents layout shifts when toggling sort states, providing a better user experience.


57-62: LGTM! Updated header styling.

The refined header styling with reduced height (h-9) and tracking adjustments creates a more compact, polished table header appearance.

apps/mesh/src/web/components/collections/collection-table.tsx (1)

35-36: LGTM! Enhanced sortability.

Adding sortable flags to columns by default (excluding "actions") provides a better user experience and aligns with the broader collection UI improvements in this PR.

Also applies to: 45-46, 60-61, 83-84

apps/mesh/src/web/components/empty-state.tsx (1)

43-47: LGTM! Enhanced image rendering control.

The updated condition (image !== null) provides better API flexibility:

  • undefined → shows default illustration
  • null → hides image section entirely
  • Custom ReactNode → shows custom image

This allows consumers to explicitly control whether the image section appears.

apps/mesh/src/web/components/collections/types.ts (1)

114-119: LGTM! Well-documented type addition.

The new sortableFields prop is clearly documented with its purpose, default behavior, and usage patterns. This provides fine-grained control over column sortability in the collection UI.

apps/mesh/src/web/components/collections/collection-search.tsx (1)

21-45: LGTM!

Clean presentational component that properly delegates state management to the parent. The use of design system components (Icon, Input) and TypeScript interface aligns with coding guidelines.

apps/mesh/src/web/components/collections/collection-table-wrapper.tsx (1)

46-57: LGTM!

The data rendering path correctly delegates to ResourceTable with all necessary props forwarded. The overflow container ensures proper scrolling behavior.

apps/mesh/src/web/components/collections/collection-header.tsx (1)

15-43: LGTM!

Well-structured header component that follows the Single Responsibility Principle. Clean composition of CollectionDisplayButton with proper prop forwarding and sensible defaults.

apps/mesh/src/web/components/details/layout.tsx (1)

44-67: LGTM!

The header restructuring aligns well with the new collection-driven UI pattern. The use of ml-auto for action alignment and the icon-only back button provide a cleaner, more compact layout.

apps/mesh/src/web/components/collections/collection-display-button.tsx (1)

130-175: LGTM!

The sort options section has good UX with clear visual indicators for the selected option and sort direction. The conditional rendering based on sortOptions.length > 0 && onSort is appropriate.

apps/mesh/src/web/routes/orgs/connections.tsx (1)

329-626: Good integration of the new collection UI components.

The component properly uses react-hook-form with Zod validation, dialog state management via useReducer, and the new collection-based layout pattern. The separation between cards and table views is well-handled.

apps/mesh/src/web/components/details/connection.tsx (5)

4-10: LGTM! Clean import organization for new collection UI components.

The new imports are well-organized and bring in the necessary components for the enhanced collection UI.


53-53: LGTM! Layout components imported for structured view composition.

The ViewLayout, ViewTabs, and ViewActions imports enable a consistent layout pattern across views.


279-324: LGTM! Improved conditional rendering with cleaner structure.

The restructured conditional rendering provides clear separation between authentication states, tools view, settings, and collection content. The use of wrapper divs with consistent padding improves layout uniformity.


768-868: LGTM! Well-structured tools list UI with proper empty states.

The implementation properly handles:

  • Search with escape key to clear
  • Toggle between card and table views
  • Consistent empty state messaging for both views
  • Click-based navigation to tool details

997-1054: LGTM! CollectionContent properly integrates with the new collection UI components.

The component correctly:

  • Uses ViewActions for consistent toolbar placement
  • Implements search with escape key handling
  • Passes schema-derived sort options to CollectionDisplayButton
  • Uses hideToolbar={true} on CollectionsList to avoid duplicate controls
apps/mesh/src/web/components/collections/collections-list.tsx (4)

2-9: LGTM! Imports updated for new component dependencies.

The new imports align with the component's enhanced functionality for table/card views and sorting.


38-74: LGTM! Sort options generation logic is well-structured.

The implementation correctly:

  • Derives options from columns when available, falling back to schema
  • Filters out internal fields consistently
  • Respects the sortableFields prop for customization
  • Generates human-readable labels from field keys

121-144: LGTM! Card grid rendering with proper fallback chain.

The implementation properly handles:

  • Custom renderCard function when provided
  • Schema-based CollectionCard as secondary option
  • Basic card fallback when neither is available
  • Click handlers with appropriate cursor styling

176-217: LGTM! Well-designed schema-to-columns generator.

The generateColumnsFromSchema helper:

  • Correctly filters out internal fields
  • Provides special date formatting for _at suffixed fields
  • Handles null/undefined/object values gracefully
  • Respects sortableFields for sortability control

Comment thread apps/mesh/src/web/components/collections/collections-list.tsx
Comment on lines +111 to +117
<EmptyState
icon="inbox"
title="No items found"
description={
search ? "Try adjusting your search" : "No items to display"
}
/>
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.

⚠️ Potential issue | 🟠 Major

EmptyState prop mismatch: icon should be image.

According to the EmptyState component signature in the codebase, it expects an image prop (which can be a ReactNode or null), not an icon prop. This will cause a runtime error or the icon won't render.

             <EmptyState
-              icon="inbox"
+              image={null}
               title="No items found"
               description={
                 search ? "Try adjusting your search" : "No items to display"
               }
             />

Apply the same fix at lines 161-167:

           <EmptyState
-            icon="inbox"
+            image={null}
             title="No items found"
             description={
               search ? "Try adjusting your search" : "No items to display"
             }
           />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<EmptyState
icon="inbox"
title="No items found"
description={
search ? "Try adjusting your search" : "No items to display"
}
/>
<EmptyState
image={null}
title="No items found"
description={
search ? "Try adjusting your search" : "No items to display"
}
/>
Suggested change
<EmptyState
icon="inbox"
title="No items found"
description={
search ? "Try adjusting your search" : "No items to display"
}
/>
<EmptyState
image={null}
title="No items found"
description={
search ? "Try adjusting your search" : "No items to display"
}
/>
🤖 Prompt for AI Agents
In apps/mesh/src/web/components/collections/collections-list.tsx around lines
111-117 and 161-167, the EmptyState component is being passed an icon prop but
its API expects image (ReactNode|null); replace icon="inbox" with image={<Icon
name="inbox" />} or the appropriate ReactNode (or null) in both locations so the
prop matches the component signature and the icon renders without runtime
errors.

Comment on lines 110 to +118
const collection = useConnectionsCollection();
const { data: connections, isLoading, isError } = useConnections(listState);
const { data: connections, isLoading } = useConnections(listState);
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.

⚠️ Potential issue | 🟡 Minor

Handle error state from useConnections hook.

The coding guidelines require "Handle loading and error states appropriately when using React Query hooks." The isLoading state is handled, but error state is not destructured or displayed to users.

-  const { data: connections, isLoading } = useConnections(listState);
+  const { data: connections, isLoading, error } = useConnections(listState);

Then render an error state when error is truthy, or at minimum show a toast notification.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const collection = useConnectionsCollection();
const { data: connections, isLoading, isError } = useConnections(listState);
const { data: connections, isLoading } = useConnections(listState);
const collection = useConnectionsCollection();
const { data: connections, isLoading, error } = useConnections(listState);
🤖 Prompt for AI Agents
In apps/mesh/src/web/routes/orgs/connections.tsx around lines 110-111, the call
to useConnections only destructures isLoading but not error; update the hook
call to also destructure error, then handle it by rendering an error state
(e.g., an inline error message or ErrorFallback component) or at minimum fire a
toast notification and return early so the rest of the component does not
attempt to render with invalid data; keep the existing isLoading handling and
ensure any error details are surfaced (and optionally logged) for debugging.

aria-hidden="true"
/>
}
title={listState.search ? "No MCPs found" : "No MCPs found"}
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.

⚠️ Potential issue | 🟡 Minor

Redundant ternary - both branches have the same title.

The ternary condition evaluates to "No MCPs found" regardless of listState.search. Consider differentiating the messaging, e.g., "No results" vs "No MCPs yet".

-              title={listState.search ? "No MCPs found" : "No MCPs found"}
+              title={listState.search ? "No results" : "No MCPs yet"}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
title={listState.search ? "No MCPs found" : "No MCPs found"}
title={listState.search ? "No results" : "No MCPs yet"}
🤖 Prompt for AI Agents
In apps/mesh/src/web/routes/orgs/connections.tsx around line 540, the title prop
uses a redundant ternary that returns "No MCPs found" for both branches; replace
it with a meaningful conditional message (for example, when listState.search is
truthy use "No results" or "No MCPs found for your search", otherwise use "No
MCPs yet" or "No MCPs found") so the UI communicates different states; update
the ternary or switch to a small helper that returns the appropriate string and
ensure any i18n keys are used if applicable.

Comment on lines 114 to 116
col.wrap
? "whitespace-normal break-words"
? "whitespace-normal wrap-break-word"
: "truncate overflow-hidden whitespace-nowrap",
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for wrap-break-word definition in the entire codebase
rg "wrap-break-word" --type=ts --type=tsx --type=js --type=jsx --type=json -C 3

Repository: decocms/admin

Length of output: 84


🏁 Script executed:

#!/bin/bash
# Search for Tailwind config files and check for custom utility definitions
find . -name "tailwind.config.*" -o -name "tailwind.config.*.ts" | head -20

Repository: decocms/admin

Length of output: 39


🏁 Script executed:

#!/bin/bash
# Check the plugin file that enforces design system tokens
cat packages/ui/src/components/resource-table.tsx | head -120 | tail -15

Repository: decocms/admin

Length of output: 688


🏁 Script executed:

#!/bin/bash
# Look for any custom break word utilities in Tailwind config
rg "break-word|wrapBreak" --type=ts --type=tsx --type=js --type=json -C 2

Repository: decocms/admin

Length of output: 84


🏁 Script executed:

#!/bin/bash
# Search for wrap-break-word without file type restriction
rg "wrap-break-word" -C 3

Repository: decocms/admin

Length of output: 2956


🏁 Script executed:

#!/bin/bash
# Search for break-words (standard Tailwind) to compare usage
rg "break-words" -C 2

Repository: decocms/admin

Length of output: 9329


🏁 Script executed:

#!/bin/bash
# Find and examine tailwind config files
find . -name "tailwind.config*" -type f 2>/dev/null | head -5

Repository: decocms/admin

Length of output: 39


🏁 Script executed:

#!/bin/bash
# Check if there's a custom utility definition in the config
find . -path "*/node_modules" -prune -o -name "*.config.*" -type f -print | grep -E "(tailwind|design|theme)" | head -10

Repository: decocms/admin

Length of output: 39


🏁 Script executed:

#!/bin/bash
# Search for wrap-break-word in CSS files
rg "wrap-break-word" --type=css -C 3

Repository: decocms/admin

Length of output: 39


🏁 Script executed:

#!/bin/bash
# Look for Tailwind config or custom utility definitions
find . -path "*/node_modules" -prune -o -type f \( -name "*.css" -o -name "*.ts" -o -name "*.js" \) -print | xargs grep -l "extend\|@apply" 2>/dev/null | grep -E "(tailwind|global|theme)" | head -10

Repository: decocms/admin

Length of output: 92


🏁 Script executed:

#!/bin/bash
# Check for CSS files that might define wrap-break-word
find . -path "*/node_modules" -prune -o -name "*.css" -type f -print | head -20

Repository: decocms/admin

Length of output: 169


🏁 Script executed:

#!/bin/bash
# Look at the plugin file mentioned in the guidelines
find . -path "*/node_modules" -prune -o -name "ensure-tailwind-design-system-tokens.ts" -type f -print

Repository: decocms/admin

Length of output: 39


🏁 Script executed:

#!/bin/bash
# Check the global CSS files for custom utilities
cat packages/ui/src/styles/global.css

Repository: decocms/admin

Length of output: 11650


🏁 Script executed:

#!/bin/bash
# Find the plugins directory
find . -path "*/node_modules" -prune -o -type d -name "plugins" -print | head -5

Repository: decocms/admin

Length of output: 66


🏁 Script executed:

#!/bin/bash
# Search more broadly for any definition of wrap-break-word
rg "wrap-break-word" -C 5 | head -50

Repository: decocms/admin

Length of output: 4417


🏁 Script executed:

#!/bin/bash
# Verify the plugin exists and check its content
find ./plugins -name "*tailwind*" -o -name "*design*" | head -10

Repository: decocms/admin

Length of output: 106


🏁 Script executed:

#!/bin/bash
# Check plugin directory structure
ls -la ./plugins/ 2>/dev/null | head -20

Repository: decocms/admin

Length of output: 424


🏁 Script executed:

#!/bin/bash
# Read the plugin that enforces design tokens
cat ./plugins/ensure-tailwind-design-system-tokens.js

Repository: decocms/admin

Length of output: 2582


Replace wrap-break-word with the standard Tailwind utility break-words.

Line 115 uses "whitespace-normal wrap-break-word", but wrap-break-word is not a valid Tailwind utility. The correct standard class is break-words. Additionally, this class appears in 3 other files in the codebase (view-console.tsx, document-detail.tsx), indicating a systematic issue.

While the design token enforcement plugin is currently disabled, the coding guidelines require consistency with Tailwind design tokens, and using non-existent utilities will cause issues with Tailwind's purging and type checking.

Apply this fix:

                      col.wrap
-                        ? "whitespace-normal wrap-break-word"
+                        ? "whitespace-normal break-words"
                        : "truncate overflow-hidden whitespace-nowrap",

Also update the other 3 occurrences in apps/web/src/components/views/view-console.tsx and apps/web/src/components/documents/document-detail.tsx (2 instances).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
col.wrap
? "whitespace-normal break-words"
? "whitespace-normal wrap-break-word"
: "truncate overflow-hidden whitespace-nowrap",
col.wrap
? "whitespace-normal break-words"
: "truncate overflow-hidden whitespace-nowrap",
🤖 Prompt for AI Agents
In packages/ui/src/components/resource-table.tsx around lines 114-116, replace
the non-standard Tailwind class "wrap-break-word" with the correct utility
"break-words" so the conditional becomes "whitespace-normal break-words"; also
update the same incorrect usage found in
apps/web/src/components/views/view-console.tsx and
apps/web/src/components/documents/document-detail.tsx (two instances) to use
"break-words" to ensure consistency with Tailwind design tokens and proper
purging/type-checking.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
apps/mesh/src/web/routes/orgs/connections.tsx (2)

118-120: Handle useConnections error state and guard against undefined data.

useConnections(listState) is only using data and isLoading, but:

  • connections is assumed to always be an array (connections.length, connections.map), which will throw if it’s ever undefined.
  • Error state from the underlying React Query hook is not surfaced, which conflicts with the guidelines around explicit loading/error handling. Based on learnings, this should show either an error empty state or at least a clear message.

Consider:

-  const { data: connections, isLoading } = useConnections(listState);
+  const {
+    data: connections = [],
+    isLoading,
+    error,
+  } = useConnections(listState);

And gate the main content with an explicit error state:

-      {/* Content: Cards or Table */}
-      {listState.viewMode === "cards" ? (
+      {/* Content: Error / Cards or Table */}
+      {error ? (
+        <div className="flex-1 overflow-auto p-5">
+          <EmptyState
+            image={null}
+            title="Failed to load MCPs"
+            description="There was a problem loading your MCPs. Please try again."
+          />
+        </div>
+      ) : listState.viewMode === "cards" ? (
         <div className="flex-1 overflow-auto p-5">
           {isLoading ? (
             <div className="flex items-center justify-center h-full">
               <div className="text-muted-foreground">Loading...</div>
             </div>
           ) : connections.length === 0 ? (
             …
           ) : (
             …
           )}
         </div>
       ) : (
         <CollectionTableWrapper
           columns={columns}
           data={connections}
           isLoading={isLoading}
           …
         />
       )}

This aligns with the “Handle loading and error states appropriately when using React Query hooks in components” guideline and avoids runtime errors if data is ever undefined.

Also applies to: 571-627, 629-673


578-595: Differentiate empty-state titles for “no results” vs “no items”.

The card-view EmptyState title currently uses a redundant ternary, and the table-view “no items yet” case shares the same "No MCPs found" copy as the “no results” case. That makes it harder for users to distinguish “nothing here yet” from “search returned no matches.”

You can keep semantics while simplifying and improving messaging, e.g.:

-              title={listState.search ? "No MCPs found" : "No MCPs found"}
+              title={listState.search ? "No MCPs found" : "No MCPs yet"}

And for the non-search table empty state:

-              <EmptyState
+              <EmptyState
                 image={
                   <img
                     src="/emptystate-mcp.svg"
                     alt=""
                     width={500}
                     height={223}
                     aria-hidden="true"
                   />
                 }
-                title="No MCPs found"
+                title="No MCPs yet"
                 description="Create a connection to get started."
               />

This mirrors the earlier review feedback and makes the two states clearer to the user.

Also applies to: 643-670

🧹 Nitpick comments (2)
apps/mesh/src/web/routes/orgs/connections.tsx (2)

211-233: Verify default metadata for new connections (created_by, status).

For new inserts you’re hardcoding:

  • created_by: "system" (with a TODO to wire the actual user).
  • status: "inactive" as the initial status.

Both are reasonable defaults but have product implications (audit trails and activation behavior). Please confirm with the domain model that:

  • "inactive" is the correct initial state and is supported by downstream logic (filters, badges, activation flows).
  • Using "system" won’t confuse audit/reporting until real user IDs are plumbed through, or track this via a follow-up issue.

No code change strictly required here, but it’s worth validating before this ships.


559-563: Minor UX copy nit: placeholder phrasing.

"Search for a MCP..." is a bit awkward grammatically; consider making it plural or dropping the article:

-        placeholder="Search for a MCP..."
+        placeholder="Search MCPs..."

(or "Search for MCPs...") for slightly cleaner copy.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4cb70d2 and d6fed5e.

📒 Files selected for processing (3)
  • apps/mesh/src/web/components/collections/collection-table.tsx (0 hunks)
  • apps/mesh/src/web/components/collections/user-indicator.tsx (0 hunks)
  • apps/mesh/src/web/routes/orgs/connections.tsx (11 hunks)
💤 Files with no reviewable changes (2)
  • apps/mesh/src/web/components/collections/user-indicator.tsx
  • apps/mesh/src/web/components/collections/collection-table.tsx
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/data-flow.mdc)

Handle loading and error states appropriately when using React Query hooks in components

Files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
**/*.{ts,tsx,js,jsx,css}

📄 CodeRabbit inference engine (.cursor/rules/structure.mdc)

Run formatting with bun run fmt before opening pull requests

Keep Tailwind design tokens consistent per plugins/ensure-tailwind-design-system-tokens.ts which will fail builds otherwise

Files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/structure.mdc)

Run type checking with bun run check before opening pull requests

Favor TypeScript types over any keyword

**/*.{ts,tsx}: Write concise, maintainable, and technically accurate TypeScript code with relevant examples
Use functional and declarative programming patterns; avoid classes
Favor iteration and modularization to adhere to DRY principles and avoid code duplication
Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError)
Organize files systematically: each file should contain only related content, such as exported components, subcomponents, helpers, static content, and types
Favor named exports for functions
Use TypeScript for all code
Prefer interfaces over types for their extendability and ability to merge
Avoid enums; use maps instead for better type safety and flexibility
Use the "function" keyword for pure functions to benefit from hoisting and clarity

Files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/structure.mdc)

Run linting with bun run lint before opening pull requests

**/*.{ts,tsx,js,jsx}: Use PascalCase for component and class names
Use camelCase for hooks and utility function names

Files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Keep imports sorted in all code files

Files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/react-ts.mdc)

**/*.tsx: Use functional components with TypeScript interfaces
Favor using the already-present ui components in packages/ui folder instead of creating custom components
Use useMemo for expensive computations like filtering and sorting operations to optimize performance in data-intensive applications
Memoize objects and arrays using useMemo to maintain stable references across renders
Use useDeferredValue for search operations to prevent blocking user input during filtering
Follow MCP (Model Context Protocol) patterns for consistent data fetching with hooks like useAgents, useIntegrations, and useUpdateAgent
Use react-hook-form with Zod validation instead of manual useState for form management
Avoid prop drilling by using Form Context to share form state across components
Use design system Form components (Form, FormControl, FormField, FormItem, FormLabel, FormMessage) from @deco/ui with proper error handling
Apply Single Responsibility Principle to components: each component should have a focused purpose and be composable
Ensure dependency arrays contain stable references; avoid creating inline objects or arrays that cause unnecessary re-renders
Implement consistent error and loading state handling with proper UI feedback (loading spinners, error messages, empty states)
Apply changes immediately to DOM for instant visual feedback, then debounce state updates for features like theme editors and live previews
Use custom events dispatched via window.dispatchEvent and window.addEventListener for cross-component communication of state updates
Implement single-level undo that reverts to the saved state, not just the previous change; use refs to track original saved values
Use useSetThreadContextEffect to inject rules and toolsets into AI chat threads; use explicit union types for context items
Extract complex logic from JSX into separate components instead of using IIFEs (immediately invoked function expressions)

Files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
🧠 Learnings (17)
📚 Learning: 2025-12-01T22:44:41.561Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-12-01T22:44:41.561Z
Learning: Applies to **/*.tsx : Follow MCP (Model Context Protocol) patterns for consistent data fetching with hooks like useAgents, useIntegrations, and useUpdateAgent

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T21:08:08.813Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/deconfig.mdc:0-0
Timestamp: 2025-12-01T21:08:08.813Z
Learning: Applies to packages/sdk/src/mcp/deconfig/**/*.ts : Implement modular composition where merge() reuses diff() + transactionalWrite(), and transactionalWrite() has internal method for different scenarios to ensure code reuse and consistency

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T21:07:47.300Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/data-flow.mdc:0-0
Timestamp: 2025-12-01T21:07:47.300Z
Learning: Applies to **/*{tools,api,index}*.ts : Organize MCP tools into typed collections (GLOBAL_TOOLS, WORKSPACE_TOOLS, AGENT_TOOLS, EMAIL_TOOLS) by functionality and scope

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T21:08:08.813Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/deconfig.mdc:0-0
Timestamp: 2025-12-01T21:08:08.813Z
Learning: Applies to packages/sdk/src/mcp/deconfig/**/*.ts : Ensure full TypeScript type safety with interfaces for all data structures, generated RPC types for tool integration, and Zod schema validation where needed

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T21:07:47.300Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/data-flow.mdc:0-0
Timestamp: 2025-12-01T21:07:47.300Z
Learning: Applies to **/*.{tsx,jsx} : Handle loading and error states appropriately when using React Query hooks in components

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T22:44:41.561Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-12-01T22:44:41.561Z
Learning: Applies to **/*.tsx : Implement consistent error and loading state handling with proper UI feedback (loading spinners, error messages, empty states)

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T21:07:47.300Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/data-flow.mdc:0-0
Timestamp: 2025-12-01T21:07:47.300Z
Learning: Applies to **/*{hook,mutation,use}*.{ts,tsx} : Implement optimistic updates in React Query mutations with proper rollback on error using `onMutate` and `onError` callbacks

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T21:08:29.999Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/native-apps-and-views.mdc:0-0
Timestamp: 2025-12-01T21:08:29.999Z
Learning: Applies to apps/web/src/components/**/*-view.tsx : Use explicit union type annotation for threadContextItems array: Array<{ id: string; type: 'rule'; text: string } | { id: string; type: 'toolset'; integrationId: string; enabledTools: string[] }>

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T22:44:41.561Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-12-01T22:44:41.561Z
Learning: Applies to **/*.tsx : Use design system Form components (Form, FormControl, FormField, FormItem, FormLabel, FormMessage) from deco/ui with proper error handling

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T21:09:05.323Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/structure.mdc:0-0
Timestamp: 2025-12-01T21:09:05.323Z
Learning: Applies to apps/web/src/**/*.{ts,tsx} : Use shadcn/ui shared design system components from packages/ui

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T22:44:41.561Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-12-01T22:44:41.561Z
Learning: Applies to **/*.tsx : Avoid prop drilling by using Form Context to share form state across components

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T22:44:41.561Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-12-01T22:44:41.561Z
Learning: Applies to **/*.tsx : Use react-hook-form with Zod validation instead of manual useState for form management

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T21:09:05.323Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/structure.mdc:0-0
Timestamp: 2025-12-01T21:09:05.323Z
Learning: Applies to apps/web/src/**/*.{ts,tsx} : Use React 19 for UI development in the decocms admin shell and marketplace

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T21:08:29.999Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/native-apps-and-views.mdc:0-0
Timestamp: 2025-12-01T21:08:29.999Z
Learning: Applies to apps/web/src/components/*-editor/*.tsx : Use debounced form updates with immediate DOM manipulation for real-time visual feedback in editors

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T21:08:29.999Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/native-apps-and-views.mdc:0-0
Timestamp: 2025-12-01T21:08:29.999Z
Learning: Applies to apps/web/src/components/**/*-view.tsx : In main view components, use `useSetThreadContextEffect` hook to inject context items (rules and toolsets) for AI chat integration

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T21:08:29.999Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/native-apps-and-views.mdc:0-0
Timestamp: 2025-12-01T21:08:29.999Z
Learning: Applies to packages/sdk/src/hooks/*.ts : Create React Query hooks using `useQuery` for data fetching and `useMutation` for updates, with proper cache invalidation on success

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
📚 Learning: 2025-12-01T22:44:41.561Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-12-01T22:44:41.561Z
Learning: Applies to **/*.tsx : Use functional components with TypeScript interfaces

Applied to files:

  • apps/mesh/src/web/routes/orgs/connections.tsx
🧬 Code graph analysis (1)
apps/mesh/src/web/routes/orgs/connections.tsx (7)
apps/mesh/src/web/components/integration-icon.tsx (1)
  • IntegrationIcon (11-59)
apps/mesh/src/web/components/collections/collection-page.tsx (1)
  • CollectionPage (7-13)
apps/mesh/src/web/components/collections/collection-header.tsx (1)
  • CollectionHeader (15-43)
apps/mesh/src/web/components/collections/collection-search.tsx (1)
  • CollectionSearch (21-45)
apps/mesh/src/web/components/empty-state.tsx (1)
  • EmptyState (28-63)
packages/ui/src/components/empty-state.tsx (1)
  • EmptyState (6-60)
apps/mesh/src/web/components/collections/collection-table-wrapper.tsx (1)
  • CollectionTableWrapper (16-58)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (1)
apps/mesh/src/web/routes/orgs/connections.tsx (1)

255-268: Table columns and row actions wiring look solid.

The icon column using IntegrationIcon, the status badge variant, and the actions dropdown (with stopPropagation on buttons/items) are all wired cleanly and match the new collection-table semantics and UX expectations. The explicit cellClassName widths also help keep the table layout stable.

Also applies to: 313-364

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/mesh/src/web/components/collections/collection-card.tsx (1)

7-12: Remove unused readOnly and onAction props from the interface.

The interface defines readOnly and onAction props that are not used by the component (only item and schema are destructured at line 64-67). This leaves dead code that can confuse future maintainers.

Apply this diff to clean up the interface:

-interface CollectionCardProps<T extends BaseCollectionEntity> {
-  item: T;
-  schema: z.AnyZodObject;
-  readOnly?: boolean;
-  onAction: (action: "open" | "delete" | "duplicate", item: T) => void;
-}
+interface CollectionCardProps<T extends BaseCollectionEntity> {
+  item: T;
+  schema: z.AnyZodObject;
+}
🧹 Nitpick comments (2)
apps/mesh/src/web/components/collections/collection-card.tsx (2)

77-95: Add hover state for interactive feedback.

The Card uses cursor-pointer and transition-colors, but there's no hover state to provide visual feedback. Users expect clickable elements to respond visually on hover.

-    <Card className="cursor-pointer transition-colors">
+    <Card className="cursor-pointer transition-colors hover:bg-accent/50">

Also, if the card is meant to be clickable, ensure the click handler is either passed as a prop or the component is wrapped appropriately at the parent level.


86-86: gap-0 is redundant.

The default gap value for flex containers is already 0.

-          <div className="flex flex-col gap-0">
+          <div className="flex flex-col">
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d6fed5e and 87637d4.

📒 Files selected for processing (1)
  • apps/mesh/src/web/components/collections/collection-card.tsx (3 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/data-flow.mdc)

Handle loading and error states appropriately when using React Query hooks in components

Files:

  • apps/mesh/src/web/components/collections/collection-card.tsx
**/*.{ts,tsx,js,jsx,css}

📄 CodeRabbit inference engine (.cursor/rules/structure.mdc)

Run formatting with bun run fmt before opening pull requests

Keep Tailwind design tokens consistent per plugins/ensure-tailwind-design-system-tokens.ts which will fail builds otherwise

Files:

  • apps/mesh/src/web/components/collections/collection-card.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/structure.mdc)

Run type checking with bun run check before opening pull requests

Favor TypeScript types over any keyword

**/*.{ts,tsx}: Write concise, maintainable, and technically accurate TypeScript code with relevant examples
Use functional and declarative programming patterns; avoid classes
Favor iteration and modularization to adhere to DRY principles and avoid code duplication
Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError)
Organize files systematically: each file should contain only related content, such as exported components, subcomponents, helpers, static content, and types
Favor named exports for functions
Use TypeScript for all code
Prefer interfaces over types for their extendability and ability to merge
Avoid enums; use maps instead for better type safety and flexibility
Use the "function" keyword for pure functions to benefit from hoisting and clarity

Files:

  • apps/mesh/src/web/components/collections/collection-card.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/structure.mdc)

Run linting with bun run lint before opening pull requests

**/*.{ts,tsx,js,jsx}: Use PascalCase for component and class names
Use camelCase for hooks and utility function names

Files:

  • apps/mesh/src/web/components/collections/collection-card.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Keep imports sorted in all code files

Files:

  • apps/mesh/src/web/components/collections/collection-card.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/react-ts.mdc)

**/*.tsx: Use functional components with TypeScript interfaces
Favor using the already-present ui components in packages/ui folder instead of creating custom components
Use useMemo for expensive computations like filtering and sorting operations to optimize performance in data-intensive applications
Memoize objects and arrays using useMemo to maintain stable references across renders
Use useDeferredValue for search operations to prevent blocking user input during filtering
Follow MCP (Model Context Protocol) patterns for consistent data fetching with hooks like useAgents, useIntegrations, and useUpdateAgent
Use react-hook-form with Zod validation instead of manual useState for form management
Avoid prop drilling by using Form Context to share form state across components
Use design system Form components (Form, FormControl, FormField, FormItem, FormLabel, FormMessage) from @deco/ui with proper error handling
Apply Single Responsibility Principle to components: each component should have a focused purpose and be composable
Ensure dependency arrays contain stable references; avoid creating inline objects or arrays that cause unnecessary re-renders
Implement consistent error and loading state handling with proper UI feedback (loading spinners, error messages, empty states)
Apply changes immediately to DOM for instant visual feedback, then debounce state updates for features like theme editors and live previews
Use custom events dispatched via window.dispatchEvent and window.addEventListener for cross-component communication of state updates
Implement single-level undo that reverts to the saved state, not just the previous change; use refs to track original saved values
Use useSetThreadContextEffect to inject rules and toolsets into AI chat threads; use explicit union types for context items
Extract complex logic from JSX into separate components instead of using IIFEs (immediately invoked function expressions)

Files:

  • apps/mesh/src/web/components/collections/collection-card.tsx
🧠 Learnings (7)
📚 Learning: 2025-12-01T21:09:30.412Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/wallet.mdc:0-0
Timestamp: 2025-12-01T21:09:30.412Z
Learning: Applies to **/*wallet*.ts : Remove payer field from transaction records and eliminate trial credits tracking - use plan-based monthly credits instead.

Applied to files:

  • apps/mesh/src/web/components/collections/collection-card.tsx
📚 Learning: 2025-12-01T21:09:05.323Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/structure.mdc:0-0
Timestamp: 2025-12-01T21:09:05.323Z
Learning: Applies to apps/web/src/**/*.{ts,tsx} : Use shadcn/ui shared design system components from packages/ui

Applied to files:

  • apps/mesh/src/web/components/collections/collection-card.tsx
📚 Learning: 2025-12-01T22:44:41.561Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-12-01T22:44:41.561Z
Learning: Applies to **/*.tsx : Use design system Form components (Form, FormControl, FormField, FormItem, FormLabel, FormMessage) from deco/ui with proper error handling

Applied to files:

  • apps/mesh/src/web/components/collections/collection-card.tsx
📚 Learning: 2025-12-01T21:09:05.323Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/structure.mdc:0-0
Timestamp: 2025-12-01T21:09:05.323Z
Learning: Applies to apps/web/src/**/*.{ts,tsx} : Use React 19 for UI development in the decocms admin shell and marketplace

Applied to files:

  • apps/mesh/src/web/components/collections/collection-card.tsx
📚 Learning: 2025-12-01T22:44:41.561Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-12-01T22:44:41.561Z
Learning: Applies to **/*.tsx : Favor using the already-present ui components in `packages/ui` folder instead of creating custom components

Applied to files:

  • apps/mesh/src/web/components/collections/collection-card.tsx
📚 Learning: 2025-12-01T21:08:29.999Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/native-apps-and-views.mdc:0-0
Timestamp: 2025-12-01T21:08:29.999Z
Learning: Applies to apps/web/src/components/**/*-view.tsx : Use explicit union type annotation for threadContextItems array: Array<{ id: string; type: 'rule'; text: string } | { id: string; type: 'toolset'; integrationId: string; enabledTools: string[] }>

Applied to files:

  • apps/mesh/src/web/components/collections/collection-card.tsx
📚 Learning: 2025-12-01T22:44:41.561Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-12-01T22:44:41.561Z
Learning: Applies to **/*.tsx : Use functional components with TypeScript interfaces

Applied to files:

  • apps/mesh/src/web/components/collections/collection-card.tsx
🧬 Code graph analysis (1)
apps/mesh/src/web/components/collections/collection-card.tsx (2)
packages/ui/src/components/card.tsx (1)
  • Card (79-79)
apps/mesh/src/web/components/integration-icon.tsx (1)
  • IntegrationIcon (11-59)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cloudflare Pages

…pty state handling

- Added `CollectionHeader`, `CollectionDisplayButton`, and `CollectionPage` components for enhanced collection management.
- Implemented `CollectionSearch` for improved item searching within collections.
- Introduced `CollectionTableWrapper` to handle table display logic, replacing the deprecated `CollectionTable`.
- Added `IntegrationIcon` for better visual representation of collection items.
- Created a new SVG file for empty state illustration in collections.
- Updated `EmptyState` component to provide clearer messaging when no items are found.
- Refactored `CollectionsList` to utilize new components and improve overall structure.
- Removed unused `UserIndicator` component to streamline the codebase.
@tlgimenes tlgimenes force-pushed the valls/collection-ui branch from 87637d4 to d9e4de9 Compare December 3, 2025 00:31
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
apps/web/src/components/views/internal-resource-list.tsx (1)

80-111: Avoid stale search results and duplicate searches in runSearch

runSearch is async and invoked both via useEffect on q and via the input’s Enter key, without any request tracking. If multiple searches overlap, a slower, older request can overwrite items and loading from a newer term, and typing triggers a request on every keystroke plus Enter.

Consider:

  • Tracking a lastRequestId with useRef and only applying results/loading state when the current request is still the latest, and
  • Either debouncing q or relying on Enter-only search (not both) to avoid unnecessary calls, optionally via useDeferredValue for smoother typing, as per the search guidelines.

Example sketch:

+ const lastRequestIdRef = useRef(0);
+
  async function runSearch(term: string) {
-   setLoading(true);
+   const requestId = ++lastRequestIdRef.current;
+   setLoading(true);
    try {
      const result = (await callTool(integration?.connection, {
        name: "DECO_CHAT_RESOURCES_SEARCH",
        arguments: { name, term, limit: 50 },
      })) as { structuredContent?: { items?: Array<...> } };
      const itemsRaw = result.structuredContent?.items ?? [];
      const mapped = itemsRaw.map((r) => ({ ... }));
-     setItems(mapped);
+     if (lastRequestIdRef.current === requestId) {
+       setItems(mapped);
+     }
    } finally {
-     setLoading(false);
+     if (lastRequestIdRef.current === requestId) {
+       setLoading(false);
+     }
    }
  }

And then decide whether useEffect([q]) alone should drive search, or Enter should, but not both.

Also applies to: 113-115, 290-303

apps/web/src/components/settings/members/roles.tsx (1)

1420-1427: Add missing dependencies to useMemo hooks to prevent stale filtered results

Three useMemo hooks read external data that is not listed in their dependency arrays. When these data sources update from their React Query hooks, the memoized results won't recalculate, causing stale data to display:

  • Lines 1420–1427: integrationsWithTools uses integrations but depends only on teamRoleData?.tools
  • Lines 1479–1484: roleMembersList uses members but depends only on teamRoleData?.members
  • Lines 1534–1537: roleAgentsList uses agents but depends only on teamRoleData?.agents

Add the missing variables to each dependency array:

-  }, [teamRoleData?.tools]);
+  }, [integrations, teamRoleData?.tools]);
-  }, [teamRoleData?.members]);
+  }, [members, teamRoleData?.members]);
-  }, [teamRoleData?.agents]);
+  }, [agents, teamRoleData?.agents]);
apps/mesh/src/web/components/collections/collection-card.tsx (1)

7-12: Interface declares props that are not used.

CollectionCardProps still declares readOnly and onAction props (lines 10-11), but the component signature only destructures item and schema. This creates a mismatch: callers (e.g., CollectionsList) pass these props expecting them to work, but they're silently ignored.

Either remove the unused props from the interface or implement the functionality:

 interface CollectionCardProps<T extends BaseCollectionEntity> {
   item: T;
   schema: z.AnyZodObject;
-  readOnly?: boolean;
-  onAction: (action: "open" | "delete" | "duplicate", item: T) => void;
 }

Also applies to: 64-67

♻️ Duplicate comments (5)
packages/ui/src/components/collection-table.tsx (1)

114-116: Replace wrap-break-word with standard Tailwind utility break-words.

Line 115 uses "whitespace-normal wrap-break-word", but wrap-break-word is not a valid Tailwind utility. The correct standard class is break-words.

As per coding guidelines, Tailwind design tokens must be consistent with plugins/ensure-tailwind-design-system-tokens.ts, and using non-existent utilities will cause issues with Tailwind's purging and type checking.

Apply this fix:

                      col.wrap
-                        ? "whitespace-normal wrap-break-word"
+                        ? "whitespace-normal break-words"
                        : "truncate overflow-hidden whitespace-nowrap",
apps/mesh/src/web/components/collections/collections-list.tsx (2)

111-117: Verify EmptyState prop matches import source.

The import is from @deco/ui/components/empty-state.tsx which accepts an icon prop. However, there's also @/web/components/empty-state.tsx (used elsewhere in the PR) which expects an image prop. Ensure the correct component is being used here.

If you intend to use the mesh app's EmptyState (which has richer illustration support), update the import:

-import { EmptyState } from "@deco/ui/components/empty-state.tsx";
+import { EmptyState } from "@/web/components/empty-state.tsx";

Then change icon to image:

-              icon="inbox"
+              image={null}

Also applies to: 161-167


88-95: Fix type error: onViewModeChange may be undefined.

The pipeline failure indicates onViewModeChange is optional but CollectionDisplayButton expects a required function. Guard against undefined:

             <CollectionDisplayButton
               viewMode={viewMode}
-              onViewModeChange={onViewModeChange}
+              onViewModeChange={onViewModeChange ?? (() => {})}
               sortKey={sortKey}
               sortDirection={sortDirection}
               onSort={onSort}
               sortOptions={sortOptions}
             />
apps/mesh/src/web/routes/orgs/connections.tsx (2)

117-118: Handle error state from useConnections hook.

Per coding guidelines, error states should be handled when using React Query hooks. Destructure error and provide user feedback:

-  const { data: connections, isLoading } = useConnections(listState);
+  const { data: connections, isLoading, error } = useConnections(listState);

Then handle the error state in the render, e.g., show a toast or error UI.


589-589: Redundant ternary: both branches have the same title.

Differentiate the messaging for better UX:

-              title={listState.search ? "No MCPs found" : "No MCPs found"}
+              title={listState.search ? "No results" : "No MCPs yet"}
🧹 Nitpick comments (10)
apps/mesh/src/web/hooks/use-collections.ts (2)

417-424: Potential unreachable fallback in field filter logic.

Line 419's truthiness check (if (!item[filter.column as keyof T]) continue;) ensures that the field is truthy before reaching line 421. This makes the ?? "" fallback on line 421 logically unreachable, since we only proceed when the field has a truthy value.

If the fallback is required for TypeScript type safety (because TS cannot narrow keyof T sufficiently), consider adding a comment explaining this. Otherwise, the fallback could be removed.

 for (const filter of filters) {
   if (!item[filter.column as keyof T]) continue;
-  // Column must match an entity property
-  const field = item[filter.column as keyof T] ?? "";
+  // Column must match an entity property (truthiness checked above)
+  const field = item[filter.column as keyof T] as string;
   conditions.push(eq(field as string, filter.value));
 }

435-435: Consider using useDeferredValue for search to improve UX.

The dependency array correctly uses trimmedSearchTerm and contains stable references.

However, per coding guidelines learnings, consider wrapping trimmedSearchTerm with useDeferredValue to prevent blocking user input during filtering, especially when dealing with large collections:

const deferredSearchTerm = useDeferredValue(trimmedSearchTerm);

Then use deferredSearchTerm in the query logic and dependency array. This keeps the search input responsive while filtering runs in the background.

Based on learnings, useDeferredValue improves search UX by preventing input blocking.

apps/web/src/components/settings/usage/users-table.tsx (2)

155-162: Remove debug console.log calls from render path

The console.log in the total column renderer and the two logs before the <Table> render will generate noisy output and can impact performance on large datasets.

Consider stripping them before shipping:

-    {
-      id: "total",
-      header: "Total Cost",
-      render: (user) => {
-        console.log(
-          `Rendering user ${user.userId} total cost:`,
-          user.totalCost,
-          typeof user.totalCost,
-        );
-        return (
-          <span className="font-medium">$ {user.totalCost.toFixed(2)}</span>
-        );
-      },
-      sortable: true,
-    },
+    {
+      id: "total",
+      header: "Total Cost",
+      render: (user) => (
+        <span className="font-medium">$ {user.totalCost.toFixed(2)}</span>
+      ),
+      sortable: true,
+    },
-  console.log("UsersTable - enrichedUsers:", enrichedUsers);
-  console.log("UsersTable - sortedUsers:", sortedUsers);

Also applies to: 219-220


17-18: Narrow sort key type to avoid invalid values

sortKey and related APIs are plain string, but only a fixed set of keys is valid. Tightening this to a union improves type safety and avoids accidental mismatches between column IDs and sort keys.

Example refactor:

-  const [sortKey, setSortKey] = useState<string>("total");
+  type SortKey =
+    | "user"
+    | "agentsUsed"
+    | "threadsCount"
+    | "totalTokens"
+    | "total";
+
+  const [sortKey, setSortKey] = useState<SortKey>("total");
@@
-  const getSortValue = (
-    user: (typeof enrichedUsers)[0],
-    key: string,
-  ): string | number => {
+  const getSortValue = (
+    user: (typeof enrichedUsers)[0],
+    key: SortKey,
+  ): string | number => {
@@
-  const handleSort = (key: string) => {
+  const handleSort = (key: SortKey) => {

This keeps behavior identical while letting TypeScript catch invalid sort keys at compile time.

Also applies to: 169-187, 189-198

apps/mesh/src/web/components/collections/collection-search.tsx (1)

1-45: Search component is clean; consider explicit React type import

The CollectionSearch component is a good, thin wrapper around the design‑system Input and Icon, and its value/onChange API is straightforward and composable with useDeferredValue in callers.

You are using React.KeyboardEvent in the props type without importing React as a type. To avoid relying on the global React namespace from @types/react, you could tighten this:

-import { Icon } from "@deco/ui/components/icon.tsx";
-import { Input } from "@deco/ui/components/input.tsx";
+import { Icon } from "@deco/ui/components/icon.tsx";
+import { Input } from "@deco/ui/components/input.tsx";
+import type * as React from "react";
@@
-  onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
+  onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;

(or import KeyboardEvent/KeyboardEventHandler from react and use those types directly).

apps/mesh/src/web/components/details/layout.tsx (1)

44-68: Header layout works; minor flex alignment cleanup possible

The new header/back‑button layout is fine functionally. In the {/* Tabs and Actions */} row you have both justify-between on the container and ml-auto on the actions div; only one is needed to push actions to the right:

-        <div className="flex justify-between px-4 items-center gap-4 flex-1">
+        <div className="flex px-4 items-center gap-4 flex-1">
@@
-          <div
-            id={ACTIONS_PORTAL_ID}
-            className="flex items-center gap-2 ml-auto"
-          />
+          <div
+            id={ACTIONS_PORTAL_ID}
+            className="flex items-center gap-2 ml-auto"
+          />

(or keep justify-between and drop ml-auto). This is purely cosmetic.

apps/mesh/src/web/components/collections/types.ts (1)

5-5: Collections list types align with new collection table and sorting

Importing TableColumn from @deco/ui/components/collection-table.tsx and adding sortableFields?: string[] cleanly expose the new sorting capabilities to callers without breaking existing usage.

If you want stronger typing, you could later narrow sortableFields to (keyof T)[] (or a mapped union of valid sort keys), but the current string list is fine for now and keeps the API simple.

Also applies to: 114-120

apps/mesh/src/web/components/collections/collection-display-button.tsx (1)

21-45: Collection display button behavior looks solid; prefer explicit RefObject import

The dropdown UI for toggling between table/cards and selecting sort fields is well factored and matches the collection‑UI patterns introduced elsewhere. Indicator updates on viewMode and when the menu opens, which should keep the highlight in sync.

You’re typing updateIndicator with React.RefObject but don’t import React as a type. To avoid relying on the global React namespace, you could import RefObject directly:

-import { useEffect, useRef, useState } from "react";
+import { useEffect, useRef, useState, type RefObject } from "react";
@@
-  const tableRef = useRef<HTMLButtonElement>(null);
-  const cardsRef = useRef<HTMLButtonElement>(null);
+  const tableRef = useRef<HTMLButtonElement>(null);
+  const cardsRef = useRef<HTMLButtonElement>(null);
@@
-  const updateIndicator = (ref: React.RefObject<HTMLButtonElement | null>) => {
+  const updateIndicator = (ref: RefObject<HTMLButtonElement>) => {

Behavior stays the same, but the typing is clearer and doesn’t depend on ambient React types.

Also applies to: 130-175

apps/mesh/src/web/components/details/connection.tsx (1)

704-720: Sort state toggle logic could become null unexpectedly.

The handleSort function sets setSortKey(undefined) when cycling through sort states, but sortKey is initialized to "name". After a full cycle (asc → desc → null), sortKey becomes undefined but the column still expects the sorted state indicator to work. Consider keeping the key but setting direction to null:

   const handleSort = (key: string) => {
     if (sortKey === key) {
       setSortDirection((prev) =>
         prev === "asc" ? "desc" : prev === "desc" ? null : "asc",
       );
-      if (sortDirection === "desc") setSortKey(undefined);
+      // Keep sortKey; null direction means unsorted
     } else {
       setSortKey(key);
       setSortDirection("asc");
     }
   };
apps/mesh/src/web/routes/orgs/connections.tsx (1)

642-672: Consider consolidating duplicate EmptyState blocks.

The two EmptyState components share the same image but differ only in title/description. This could be simplified:

           emptyState={
-            listState.search ? (
-              <EmptyState
-                image={...}
-                title="No MCPs found"
-                description={`No MCPs match "${listState.search}"`}
-              />
-            ) : (
-              <EmptyState
-                image={...}
-                title="No MCPs found"
-                description="Create a connection to get started."
-              />
-            )
+            <EmptyState
+              image={<img src="/emptystate-mcp.svg" alt="" ... />}
+              title={listState.search ? "No results" : "No MCPs yet"}
+              description={
+                listState.search
+                  ? `No MCPs match "${listState.search}"`
+                  : "Create a connection to get started."
+              }
+            />
           }

Comment on lines +1037 to +1038
search={search}
onSearchChange={setSearch}
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.

⚠️ Potential issue | 🟡 Minor

onSearchChange prop is passed but not used by CollectionsList.

Looking at collections-list.tsx, onSearchChange is not destructured from props, meaning this callback is ignored. Since hideToolbar={true} is set and search is handled externally via CollectionSearch, this prop is redundant here:

             <CollectionsList
               data={items ?? []}
               schema={schema}
               viewMode={viewMode}
               onViewModeChange={setViewMode}
               search={search}
-              onSearchChange={setSearch}
               sortKey={sortKey as string}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
search={search}
onSearchChange={setSearch}
<CollectionsList
data={items ?? []}
schema={schema}
viewMode={viewMode}
onViewModeChange={setViewMode}
search={search}
sortKey={sortKey as string}
🤖 Prompt for AI Agents
In apps/mesh/src/web/components/details/connection.tsx around lines 1037-1038,
the onSearchChange={setSearch} prop is being passed into CollectionsList but
CollectionsList does not accept or use that prop (so it’s redundant). Remove the
onSearchChange={setSearch} prop from the CollectionsList JSX here (or
alternatively, if you intend CollectionsList to handle external search updates,
add onSearchChange to its props/interface and forward it to the internal search
handler), and update any related prop types/interfaces accordingly.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
apps/mesh/src/web/components/collections/collections-list.tsx (1)

110-116: Past review unresolved: EmptyState prop should be image, not icon.

This issue was previously flagged: the EmptyState component expects an image prop (ReactNode or null), not an icon prop. Please address the past review comment by replacing icon="inbox" with the appropriate image value.

The same issue exists at lines 158-164.

🧹 Nitpick comments (2)
apps/mesh/src/web/components/collections/collections-list.tsx (2)

15-15: Reconsider no-op defaults for user interaction callbacks.

While providing onViewModeChange = () => {} as a default resolves the type error flagged in the past review, it creates a poor user experience: the view mode toggle button will be interactive but won't do anything if the parent doesn't provide a handler.

Consider one of these approaches instead:

  1. Only render CollectionDisplayButton when onViewModeChange is defined
  2. Make the callback optional in CollectionDisplayButton and disable/hide the toggle when absent
  3. If this component is meant to control its own state when no callback is provided, manage viewMode internally with useState

The same concern applies to other interaction callbacks with no-op defaults (lines 19-21).


173-214: Add type guards and error handling in column generation.

The helper function uses type assertions that may not be safe:

  1. Line 186: row[key as keyof T] assumes schema keys match the type T, but there's no compile-time guarantee
  2. Line 196: new Date(val as string) assumes date values are strings, but they could be Date objects or timestamps
  3. Line 208: typeof val === "object" then JSON.stringify(val) could fail for circular references or throw for BigInt values
 function generateColumnsFromSchema<T extends BaseCollectionEntity>(
   schema: z.AnyZodObject,
   sortableFields?: string[],
 ): TableColumn<T>[] {
   return Object.keys(schema.shape)
     .filter(
       (key) => !["organization_id", "created_by", "updated_by"].includes(key),
     )
     .map((key) => {
       const isSortable = sortableFields
         ? sortableFields.includes(key)
         : !["id"].includes(key);

       if (key.endsWith("_at")) {
         return {
           id: key,
           header: key.charAt(0).toUpperCase() + key.slice(1).replace(/_/g, " "),
           render: (row) => {
             const val = row[key as keyof T];
             if (!val) return "—";
-            return new Date(val as string).toLocaleDateString();
+            try {
+              const date = typeof val === 'string' || typeof val === 'number' 
+                ? new Date(val as string | number)
+                : val instanceof Date ? val : null;
+              return date?.toLocaleDateString() ?? "—";
+            } catch {
+              return "—";
+            }
           },
           sortable: isSortable,
         };
       }

       return {
         id: key,
         header: key.charAt(0).toUpperCase() + key.slice(1).replace(/_/g, " "),
         render: (row) => {
           const val = row[key as keyof T];
           if (val === null || val === undefined) return "—";
-          if (typeof val === "object") return JSON.stringify(val);
+          if (typeof val === "object") {
+            try {
+              return JSON.stringify(val);
+            } catch {
+              return "[Object]";
+            }
+          }
           return String(val);
         },
         sortable: isSortable,
       };
     });
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d9e4de9 and 4cbc9f2.

📒 Files selected for processing (2)
  • apps/mesh/src/web/components/collections/collections-list.tsx (1 hunks)
  • apps/mesh/src/web/components/collections/types.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx,js,jsx,css}

📄 CodeRabbit inference engine (.cursor/rules/structure.mdc)

Run formatting with bun run fmt before opening pull requests

Keep Tailwind design tokens consistent per plugins/ensure-tailwind-design-system-tokens.ts which will fail builds otherwise

Files:

  • apps/mesh/src/web/components/collections/types.ts
  • apps/mesh/src/web/components/collections/collections-list.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/structure.mdc)

Run type checking with bun run check before opening pull requests

Favor TypeScript types over any keyword

**/*.{ts,tsx}: Write concise, maintainable, and technically accurate TypeScript code with relevant examples
Use functional and declarative programming patterns; avoid classes
Favor iteration and modularization to adhere to DRY principles and avoid code duplication
Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError)
Organize files systematically: each file should contain only related content, such as exported components, subcomponents, helpers, static content, and types
Favor named exports for functions
Use TypeScript for all code
Prefer interfaces over types for their extendability and ability to merge
Avoid enums; use maps instead for better type safety and flexibility
Use the "function" keyword for pure functions to benefit from hoisting and clarity

Files:

  • apps/mesh/src/web/components/collections/types.ts
  • apps/mesh/src/web/components/collections/collections-list.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/structure.mdc)

Run linting with bun run lint before opening pull requests

**/*.{ts,tsx,js,jsx}: Use PascalCase for component and class names
Use camelCase for hooks and utility function names

Files:

  • apps/mesh/src/web/components/collections/types.ts
  • apps/mesh/src/web/components/collections/collections-list.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Keep imports sorted in all code files

Files:

  • apps/mesh/src/web/components/collections/types.ts
  • apps/mesh/src/web/components/collections/collections-list.tsx
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/api-development.mdc)

Define both input and output Zod schemas for all API operations with appropriate type constraints (min/max length, optional fields, descriptions) and map them to TypeScript interfaces

Files:

  • apps/mesh/src/web/components/collections/types.ts
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/data-flow.mdc)

Handle loading and error states appropriately when using React Query hooks in components

Files:

  • apps/mesh/src/web/components/collections/collections-list.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/react-ts.mdc)

**/*.tsx: Use functional components with TypeScript interfaces
Favor using the already-present ui components in packages/ui folder instead of creating custom components
Use useMemo for expensive computations like filtering and sorting operations to optimize performance in data-intensive applications
Memoize objects and arrays using useMemo to maintain stable references across renders
Use useDeferredValue for search operations to prevent blocking user input during filtering
Follow MCP (Model Context Protocol) patterns for consistent data fetching with hooks like useAgents, useIntegrations, and useUpdateAgent
Use react-hook-form with Zod validation instead of manual useState for form management
Avoid prop drilling by using Form Context to share form state across components
Use design system Form components (Form, FormControl, FormField, FormItem, FormLabel, FormMessage) from @deco/ui with proper error handling
Apply Single Responsibility Principle to components: each component should have a focused purpose and be composable
Ensure dependency arrays contain stable references; avoid creating inline objects or arrays that cause unnecessary re-renders
Implement consistent error and loading state handling with proper UI feedback (loading spinners, error messages, empty states)
Apply changes immediately to DOM for instant visual feedback, then debounce state updates for features like theme editors and live previews
Use custom events dispatched via window.dispatchEvent and window.addEventListener for cross-component communication of state updates
Implement single-level undo that reverts to the saved state, not just the previous change; use refs to track original saved values
Use useSetThreadContextEffect to inject rules and toolsets into AI chat threads; use explicit union types for context items
Extract complex logic from JSX into separate components instead of using IIFEs (immediately invoked function expressions)

Files:

  • apps/mesh/src/web/components/collections/collections-list.tsx
🧠 Learnings (5)
📚 Learning: 2025-12-01T21:08:29.999Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/native-apps-and-views.mdc:0-0
Timestamp: 2025-12-01T21:08:29.999Z
Learning: Applies to apps/web/src/components/sidebar/index.tsx : Update sidebar navigation by adding feature name to wellKnownItems array and resourceTypeOrder array

Applied to files:

  • apps/mesh/src/web/components/collections/types.ts
  • apps/mesh/src/web/components/collections/collections-list.tsx
📚 Learning: 2025-12-01T21:08:29.999Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/native-apps-and-views.mdc:0-0
Timestamp: 2025-12-01T21:08:29.999Z
Learning: Applies to apps/web/src/components/**/resource-list.tsx : Create resource list component that uses `useTrackNativeViewVisit` hook to track view visits for recents and pinning functionality

Applied to files:

  • apps/mesh/src/web/components/collections/types.ts
📚 Learning: 2025-12-01T21:08:29.999Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/native-apps-and-views.mdc:0-0
Timestamp: 2025-12-01T21:08:29.999Z
Learning: Applies to apps/web/src/components/**/*-view.tsx : Use explicit union type annotation for threadContextItems array: Array<{ id: string; type: 'rule'; text: string } | { id: string; type: 'toolset'; integrationId: string; enabledTools: string[] }>

Applied to files:

  • apps/mesh/src/web/components/collections/collections-list.tsx
📚 Learning: 2025-12-01T22:44:41.561Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-12-01T22:44:41.561Z
Learning: Applies to **/*.tsx : Ensure dependency arrays contain stable references; avoid creating inline objects or arrays that cause unnecessary re-renders

Applied to files:

  • apps/mesh/src/web/components/collections/collections-list.tsx
📚 Learning: 2025-12-01T22:44:41.561Z
Learnt from: CR
Repo: decocms/admin PR: 0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-12-01T22:44:41.561Z
Learning: Applies to **/*.tsx : Implement consistent error and loading state handling with proper UI feedback (loading spinners, error messages, empty states)

Applied to files:

  • apps/mesh/src/web/components/collections/collections-list.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (4)
apps/mesh/src/web/components/collections/types.ts (2)

5-5: LGTM! Import path updated correctly.

The import path change from resource-table.tsx to collection-table.tsx aligns with the PR's objective to rename ResourceTable to CollectionTable.


109-114: LGTM! Well-documented new property.

The sortableFields property is clearly documented and provides flexible control over which fields can be sorted. The three behavior modes (undefined, empty array, specific fields) are well explained.

apps/mesh/src/web/components/collections/collections-list.tsx (2)

120-141: LGTM! Cards grid rendering looks good.

The conditional rendering between CollectionCard (when schema is present) and fallback Card is appropriate. The grid layout with responsive columns is well-structured.

Note: The optional chaining on onItemClick?.(item) (line 124) is now redundant since line 21 provides a default no-op function. This is a minor consistency issue, not a blocker.


76-98: LGTM! Clean layout structure.

The flex layout with overflow handling and conditional toolbar rendering is well-structured. The separation of header and content areas is clear and maintainable.

Comment thread apps/mesh/src/web/components/collections/collections-list.tsx
Comment thread apps/mesh/src/web/components/collections/collections-list.tsx
@tlgimenes tlgimenes force-pushed the valls/collection-ui branch from 5207094 to a53c6ed Compare December 3, 2025 01:06
@tlgimenes tlgimenes merged commit 2d5146d into main Dec 3, 2025
4 checks passed
@tlgimenes tlgimenes deleted the valls/collection-ui branch December 3, 2025 01:08
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.

2 participants