Skip to content

feat(ui): tile version of data lists#406

Merged
michnowak merged 18 commits intomainfrom
feature/tile-version-of-data-lists
Dec 3, 2025
Merged

feat(ui): tile version of data lists#406
michnowak merged 18 commits intomainfrom
feature/tile-version-of-data-lists

Conversation

@michnowak
Copy link
Copy Markdown
Contributor

@michnowak michnowak commented Nov 22, 2025

What does this PR do?

  • My feature

Related Ticket(s)

  • Notion Ticket

Key Changes

  • Added new DataGrid component as a tile/card-based alternative view to DataList for displaying data in a responsive grid layout
    • Implemented in packages/ui/src/components/DataGrid/ with full TypeScript types, Storybook stories, and responsive column configuration (mobile/tablet/desktop)
    • Supports same column configuration as DataList for consistency, with slot-based layout (top, left, right, bottom) for flexible card content arrangement
    • Uses Card components from UI library to render each data item as a tile with proper spacing and responsive grid layout
  • Refactored DataList component to extract cell rendering logic into shared renderCell utility function
    • Moved cell rendering logic from DataList.tsx to packages/ui/src/lib/renderCell.tsx to enable code reuse between DataList and DataGrid
    • Both components now use the same rendering logic for consistent display of text, badges, status, dates, prices, and links
  • Added FilterViewModeToggle filter type to enable users to switch between list and grid views
    • Extended framework filter models in packages/framework/src/utils/models/filters.ts with new FilterViewModeToggle class
    • Updated Filters component to detect and render view mode toggle alongside other filter items
    • Modified FilterItem component to handle view mode toggle rendering with appropriate UI controls
  • Integrated view mode functionality into TicketList block to demonstrate the new feature
    • Added view mode state management in TicketList.client.tsx with initial value extraction from filters
    • Conditionally renders DataList or DataGrid based on selected view mode
    • Updated action button rendering to use appropriate variants for list vs grid views (link variant for list, tertiary for grid)
    • Wired up FilterViewModeToggle to control view mode state and persist selection

Side Effects

  • Updated mocked integration mapper for ticket-list block to include view mode toggle in filter items
  • Modified framework CMS models to support view mode toggle in block configurations
  • All existing DataList usages remain unchanged and continue to work as before (backward compatible)

Summary by CodeRabbit

  • New Features

    • View-mode toggle (list/grid) added to filters and list UIs.
    • New DataGrid and DataView components for responsive grid/card layouts with per-item header slots.
  • Enhancements

    • Multiple list blocks (tickets, invoices, orders, notifications) now support configurable card header slots (top/left/right/bottom).
    • Unified cell rendering for consistent display across lists and grids; stories updated to showcase variants.
  • Chores

    • Release metadata added for minor version bumps.

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

@vercel
Copy link
Copy Markdown

vercel Bot commented Nov 22, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Preview Comments Updated (UTC)
o2s-docs Skipped Skipped Dec 2, 2025 2:50pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 22, 2025

Walkthrough

Adds DataGrid and DataView UI components, a shared renderCell utility, a new FilterViewModeToggle and viewMode wiring, and a cardHeaderSlots property propagated from CMS into multiple list block models, mappers, mocks, frontends, stories, and types. (28 words)

Changes

Cohort / File(s) Summary
DataGrid component & types
packages/ui/src/components/DataGrid/DataGrid.tsx, packages/ui/src/components/DataGrid/DataGrid.types.ts, packages/ui/src/components/DataGrid/index.ts, packages/ui/src/components/DataGrid/DataGrid.stories.tsx
New generic DataGrid component and associated types/exports and stories: responsive card grid rendering, cardHeaderSlots support (top/left/right/bottom), actions, getRowKey/getRowClassName hooks, and responsive columnsCount.
DataView wrapper & types
packages/ui/src/components/DataView/DataView.tsx, packages/ui/src/components/DataView/DataView.types.ts, packages/ui/src/components/DataView/index.ts, packages/ui/src/components/DataView/DataView.stories.tsx
New DataView component and types that switch between DataGrid and DataList based on viewMode, forwarding cardHeaderSlots and columnsCount; stories added.
Shared renderCell & DataList refactor
packages/ui/src/lib/renderCell.tsx, packages/ui/src/components/DataList/DataList.tsx
Added exported renderCell utility (handles custom/text/badge/status/date/price/link) and refactored DataList to delegate cell rendering to it and table primitives.
Filters: view-mode toggle & integration
packages/framework/src/utils/models/filters.ts, packages/ui/src/components/Filters/FilterItem.tsx, packages/ui/src/components/Filters/Filters.tsx, packages/ui/src/components/Filters/FiltersSection.tsx
New FilterViewModeToggle type and UI rendering; separateLeadingItem now extracts a viewModeToggle; Filters no longer accepts hasLeadingItem and renders optional view-mode toggle alongside leading item; FiltersSection adjusted.
List blocks: models & mappers (cardHeaderSlots)
packages/framework/src/modules/cms/models/blocks/*.model.ts, packages/blocks/*-list/src/api-harmonization/*-list.model.ts, packages/blocks/*-list/src/api-harmonization/*-list.mapper.ts
Added optional cardHeaderSlots property to Ticket/Invoice/Notification/Order list block models; mappers now map cms.cardHeaderSlotscardHeaderSlots; filters typings extended to include optional viewMode where applicable.
Block frontends: DataView + viewMode wiring
packages/blocks/*-list/src/frontend/*List.client.tsx, packages/blocks/ticket-list/src/frontend/TicketList.client.stories.tsx
Replaced DataList with DataView in list frontends; derive initial viewMode from filters, maintain local viewMode state, wire FilterViewModeToggle value/onChange to update viewMode, and pass viewMode and cardHeaderSlots into DataView.
Mock CMS blocks
packages/integrations/mocked/src/modules/cms/mappers/blocks/*.mapper.ts
Mock locale blocks updated (ticket/invoice/notification/order) to include cardHeaderSlots and add a FilterViewModeToggle item with default value: 'list' in filters.items.
Stories & demos
packages/ui/src/components/DataGrid/DataGrid.stories.tsx, packages/ui/src/components/DataView/DataView.stories.tsx, packages/ui/src/components/DataList/*.stories.tsx
Added/updated Storybook stories demonstrating DataGrid, DataView (list/grid), cardHeaderSlots, actions, and view-mode scenarios.
Filters & renderCell consumers
packages/ui/src/components/Filters/FilterItem.tsx, packages/ui/src/components/Filters/Filters.tsx, packages/ui/src/components/Filters/FiltersSection.tsx, packages/ui/src/lib/renderCell.tsx
UI components updated to render the new FilterViewModeToggle and to use the shared renderCell utility where applicable.
Block-specific mapper additions
packages/blocks/*-list/src/api-harmonization/*-list.mapper.ts (ticket/invoice/notification/order)
Mappers updated to include cardHeaderSlots in returned block payloads (sourced from cms.cardHeaderSlots).
Changesets / metadata
.changeset/true-words-sell.md, .changeset/long-gifts-divide.md
Changesets added to record package version bumps and the new grid/tile UI feature and related fixes.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Block as List Block Component
    participant Filters
    participant ViewState as viewMode State
    participant DataView
    participant DataGrid
    participant DataList
    participant renderCell

    User->>Block: Load block (data + filters + cardHeaderSlots)
    Block->>ViewState: extract initial viewMode from filters
    Block->>Filters: render filters (includes viewMode toggle)

    User->>Filters: toggle viewMode
    Filters->>Block: call item.onChange with new viewMode
    Block->>ViewState: set viewMode

    Block->>DataView: render(viewMode, data, columns, cardHeaderSlots)
    alt viewMode == "grid"
        DataView->>DataGrid: render grid with cardHeaderSlots + actions
        DataGrid->>User: card-grid UI
    else viewMode == "list"
        DataView->>DataList: render list with actions
        DataList->>renderCell: render cells via shared renderer
        DataList->>User: list UI
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Focus points:
    • DataGrid: responsive columnsCount logic, slot/header composition, per-row rendering and actions.
    • renderCell: price/currency resolution, link/button rendering branches, badge/status handling.
    • Filters: updated separateLeadingItem return shape and removal of hasLeadingItem from Filters signature.
    • Frontend blocks & mappers: wiring of viewMode into FiltersSection and mapping cardHeaderSlots from CMS.

Possibly related PRs

Suggested reviewers

  • marcinkrasowski

Poem

🐰 I hopped through rows to plant a grid,
Toggles clicked and tiles neatly slid,
Slots tucked left and right, top and low,
Lists or cards now proudly show,
Data hops bright — a rabbit's bid!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(ui): tile version of data lists' clearly and concisely summarizes the main feature addition—a new tile/card-based view for data lists. It is specific enough for a developer to understand the primary change.
Description check ✅ Passed The PR description covers key changes, side effects, and implementation details. However, it lacks testing instructions and related ticket links as specified in the template.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/tile-version-of-data-lists

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.

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: 4

♻️ Duplicate comments (1)
packages/blocks/ticket-list/src/frontend/TicketList.client.stories.tsx (1)

58-62: Story slots also point left to non‑existent id column

This story uses the same slots config as the mocks (left: 'id'), but the table columns still don’t include an id column, so the left slot won’t render anything in the grid header. This is the same issue noted in the CMS ticket‑list mocks; once you adjust the slots/columns there, please mirror the fix here to keep the story in sync.

🧹 Nitpick comments (9)
packages/blocks/ticket-list/src/api-harmonization/ticket-list.model.ts (1)

27-32: Slots API shape looks good and stays backward compatible

The optional slots object mirrors the CMS model and cleanly extends TicketListBlock without breaking existing consumers. If you find yourself reusing this shape elsewhere, consider extracting a shared SlotsConfig type to avoid duplication, but it’s not urgent.

packages/integrations/mocked/src/modules/cms/mappers/blocks/cms.ticket-list.mapper.ts (1)

146-150: View‑mode toggle mocks are consistent across locales

The added FilterViewModeToggle items with id: 'viewMode' and default value: 'grid' are consistent in EN/DE/PL and line up with the new filter type. If you want the legacy list view to remain the default, you might switch these defaults to 'list', but as mocks they’re fine either way.

Also applies to: 311-315, 477-481

packages/blocks/ticket-list/src/frontend/TicketList.client.tsx (1)

39-46: View‑mode state is independent of filter reset; confirm intended UX

initialViewMode is derived from the incoming filters once and stored in local state:

const initialViewMode =
    component.filters?.items.find((item) => item.__typename === 'FilterViewModeToggle')?.value || 'list';

const [viewMode, setViewMode] = useState<'list' | 'grid'>(initialViewMode);

handleReset resets backend filters/data but doesn’t reset viewMode, and FilterViewModeToggle is wired only via onChange: setViewMode. That means “Reset/Clear filters” keeps the current view mode, effectively treating it as a persistent UI preference rather than a filter.

If that’s the desired behavior, this is fine. If you expect reset to also revert the view mode to its initial value, you could additionally call setViewMode(initialViewMode) inside handleReset.

Also applies to: 58-64, 167-183

packages/framework/src/modules/cms/models/blocks/ticket-list.model.ts (1)

12-30: Filters/viewMode and slots typing look consistent; consider centralizing the slots type

The added viewMode in the filters type matches the new toggle and should integrate cleanly with the form model, and the slots shape is aligned with the DataGrid/DataList usage. As a small cleanup, you might want to extract a shared SlotsConfig type (e.g. in a common models/util file) so the { top; left; right; bottom } structure is not duplicated between CMS models and UI props.

packages/framework/src/utils/models/filters.ts (1)

12-17: Make FilterViewModeToggle.onChange required to match UI usage

FilterViewModeToggle.onChange is optional here, but the UI renderer (FilterItem case 'FilterViewModeToggle') calls item.onChange! unconditionally. This makes it easy to introduce a runtime error if a caller forgets to wire onChange. Making it required keeps the types honest and prevents silent misconfiguration.

-export class FilterViewModeToggle {
+export class FilterViewModeToggle {
     __typename!: 'FilterViewModeToggle';
     id!: 'viewMode';
-    value?: 'list' | 'grid';
-    onChange?: (value: 'list' | 'grid') => void;
+    value?: 'list' | 'grid';
+    onChange!: (value: 'list' | 'grid') => void;
 }

Also applies to: 61-66

packages/ui/src/components/DataGrid/DataGrid.types.ts (1)

29-53: Tighten slots typing to column IDs for better safety

The props shape looks good and matches the component, but slots being plain string loses type-safety w.r.t. the configured columns. You can make slot ids line up with DataListColumnConfig<T>['id'] so mismatched slot names are caught at compile time:

-export interface DataGridProps<T> {
+export interface DataGridProps<T> {
@@
-    slots?: {
-        top?: string;
-        left?: string;
-        right?: string;
-        bottom?: string;
-    };
+    slots?: {
+        top?: DataListColumnConfig<T>['id'];
+        left?: DataListColumnConfig<T>['id'];
+        right?: DataListColumnConfig<T>['id'];
+        bottom?: DataListColumnConfig<T>['id'];
+    };

Optionally, you could also align with the component by constraining T here (DataGridProps<T extends Record<string, any>>) so callers get consistent expectations about T.

packages/ui/src/lib/renderCell.tsx (1)

16-69: Loosen value typing and guard badge access to avoid unnecessary casts

renderCell currently requires value: Record<string, unknown>, but many callers pass primitives and have to cast arbitrarily (as Record<string, unknown>). You’re already checking typeof value === 'object' in most cases; you could reflect that in the type and slightly harden the badge branch:

-export function renderCell<T>(
-    value: Record<string, unknown>,
+export function renderCell<T>(
+    // Allow primitives; branches already narrow as needed
+    value: unknown,
@@
-        case 'badge': {
-            const badgeLabel = String(value[column.labelField || 'label']);
-            const badgeValue = String(value[column.valueField || 'value']);
+        case 'badge': {
+            if (typeof value !== 'object' || value === null) {
+                return null;
+            }
+            const record = value as Record<string, unknown>;
+            const badgeLabel = String(record[column.labelField || 'label']);
+            const badgeValue = String(record[column.valueField || 'value']);
             const variant = column.variant ? column.variant(badgeValue) : 'default';

This makes the signature better match actual usage and avoids unsafe indexing when a badge cell value isn’t an object.

packages/ui/src/components/DataGrid/DataGrid.stories.tsx (1)

148-261: Tighten story typings and de‑duplicate common args

The stories are fine functionally, but the repeated as Record<string, unknown>[] / as DataListColumnConfig<Record<string, unknown>>[] casts weaken type‑safety and duplicate the same data/columns/slots setup in several places.

If you want stronger typing with minimal change, consider:

  • Typing args as DataGridProps<Ticket> directly (e.g. via a shared const baseArgs: DataGridProps<Ticket> = { … }) and
  • Reusing baseArgs with spreads in individual stories to reduce duplication.

This keeps the stories easier to maintain and lets TS catch drift between Ticket and ticketColumns.

packages/ui/src/components/Filters/Filters.tsx (1)

17-33: separateLeadingItem correctly isolates the view mode toggle

The updated separateLeadingItem cleanly separates:

  • a single leadingItem,
  • a single FilterViewModeToggle, and
  • the remaining filteredItems.

This matches the intended behavior where view mode is orthogonal to form‑driven filters. It assumes there is at most one FilterViewModeToggle, which is reasonable for current usage; if that ever changes, you might want to assert or log when multiple toggles are passed instead of silently taking the last one.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ec80202 and 7f69c6f.

📒 Files selected for processing (16)
  • packages/blocks/ticket-list/src/api-harmonization/ticket-list.mapper.ts (1 hunks)
  • packages/blocks/ticket-list/src/api-harmonization/ticket-list.model.ts (1 hunks)
  • packages/blocks/ticket-list/src/frontend/TicketList.client.stories.tsx (2 hunks)
  • packages/blocks/ticket-list/src/frontend/TicketList.client.tsx (5 hunks)
  • packages/framework/src/modules/cms/models/blocks/ticket-list.model.ts (2 hunks)
  • packages/framework/src/utils/models/filters.ts (2 hunks)
  • packages/integrations/mocked/src/modules/cms/mappers/blocks/cms.ticket-list.mapper.ts (6 hunks)
  • packages/ui/src/components/DataGrid/DataGrid.stories.tsx (1 hunks)
  • packages/ui/src/components/DataGrid/DataGrid.tsx (1 hunks)
  • packages/ui/src/components/DataGrid/DataGrid.types.ts (1 hunks)
  • packages/ui/src/components/DataGrid/index.ts (1 hunks)
  • packages/ui/src/components/DataList/DataList.tsx (1 hunks)
  • packages/ui/src/components/Filters/FilterItem.tsx (2 hunks)
  • packages/ui/src/components/Filters/Filters.tsx (3 hunks)
  • packages/ui/src/components/Filters/FiltersSection.tsx (1 hunks)
  • packages/ui/src/lib/renderCell.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (9)
packages/ui/src/lib/renderCell.tsx (2)
packages/framework/src/utils/models/badge.ts (1)
  • Badge (1-4)
packages/framework/src/utils/models/price.ts (1)
  • Price (3-7)
packages/framework/src/modules/cms/models/blocks/ticket-list.model.ts (3)
packages/framework/src/utils/models/filters.ts (1)
  • Filters (1-10)
packages/ui/src/components/Filters/Filters.tsx (1)
  • Filters (35-160)
packages/blocks/ticket-list/src/api-harmonization/ticket-list.model.ts (1)
  • Ticket (35-52)
packages/ui/src/components/DataGrid/DataGrid.types.ts (1)
packages/ui/src/components/DataGrid/index.ts (2)
  • DataGridColumnsCount (2-2)
  • DataGridProps (2-2)
packages/blocks/ticket-list/src/frontend/TicketList.client.tsx (4)
packages/blocks/ticket-list/src/api-harmonization/ticket-list.model.ts (2)
  • TicketListBlock (7-33)
  • Ticket (35-52)
packages/framework/src/modules/cms/models/blocks/ticket-list.model.ts (1)
  • TicketListBlock (6-31)
packages/ui/src/components/DataGrid/DataGrid.tsx (1)
  • DataGrid (19-194)
packages/ui/src/components/DataList/DataList.tsx (1)
  • DataList (13-88)
packages/ui/src/components/Filters/FilterItem.tsx (1)
packages/ui/src/elements/toggle-group.tsx (2)
  • ToggleGroup (70-70)
  • ToggleGroupItem (70-70)
packages/ui/src/components/DataGrid/DataGrid.stories.tsx (3)
packages/ui/src/components/DataGrid/DataGrid.tsx (1)
  • DataGrid (19-194)
packages/blocks/ticket-list/src/api-harmonization/ticket-list.model.ts (1)
  • Ticket (35-52)
packages/ui/src/components/DataGrid/DataGrid.types.ts (1)
  • DataGridProps (29-74)
packages/ui/src/components/DataGrid/DataGrid.tsx (3)
packages/ui/src/components/DataGrid/DataGrid.types.ts (1)
  • DataGridProps (29-74)
packages/ui/src/lib/utils.ts (1)
  • cn (5-7)
packages/ui/src/lib/renderCell.tsx (1)
  • renderCell (16-100)
packages/ui/src/components/Filters/Filters.tsx (2)
packages/framework/src/utils/models/filters.ts (2)
  • FilterViewModeToggle (61-66)
  • FilterItem (12-17)
packages/ui/src/components/Filters/FilterItem.tsx (1)
  • FilterItem (16-227)
packages/framework/src/utils/models/filters.ts (1)
packages/ui/src/components/Filters/FilterItem.tsx (1)
  • FilterItem (16-227)
⏰ 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: deploy-preview
🔇 Additional comments (8)
packages/ui/src/components/Filters/FiltersSection.tsx (1)

23-23: Robust leading‑item check for mixed filter item types

Using 'isLeading' in item && item.isLeading === true is a good improvement: it avoids relying on properties that may not exist on all filter item variants and keeps layout logic from being triggered by items that don’t support isLeading.

packages/blocks/ticket-list/src/frontend/TicketList.client.stories.tsx (1)

216-220: Storybook wiring for FilterViewModeToggle looks good

Including a FilterViewModeToggle with value: 'grid' ensures the new grid view is exercised in Storybook and makes it easy to validate the toggle UI.

packages/blocks/ticket-list/src/api-harmonization/ticket-list.mapper.ts (1)

29-29: Slots passthrough keeps API harmonization aligned with CMS

Mapping slots: cms.slots directly into the TicketListBlock output cleanly exposes the CMS configuration to the frontend and matches the updated model type. No issues here.

packages/ui/src/components/DataGrid/index.ts (1)

1-2: Public DataGrid exports are straightforward and consistent

Re‑exporting DataGrid plus its props and DataGridColumnsCount type from an index barrel is aligned with the rest of the UI components and keeps the import surface tidy.

packages/blocks/ticket-list/src/frontend/TicketList.client.tsx (1)

97-120: Grid/list‑aware actions and body rendering look consistent

Overriding actions.render to switch button variant/layout based on viewMode, and conditionally rendering either DataGrid (with slots={data.slots}) or DataList using the same columns config, keeps behavior consistent between views while adapting the UI affordances appropriately. No issues spotted here.

Also applies to: 195-204

packages/ui/src/components/DataGrid/DataGrid.stories.tsx (1)

23-146: Sample Ticket model and column config are coherent with DataGrid expectations

The Ticket shape, sampleTickets, and ticketColumns line up well with the DataGrid + shared renderCell model (label/value objects for text/date fields and a status badge variant based on the value). This should give very representative stories for ticket‑list usage.

packages/ui/src/components/DataList/DataList.tsx (1)

3-87: Refactor to shared renderCell and table primitives looks solid

Delegating cell rendering to renderCell and using the local Table primitives keeps DataList lean while preserving behavior (row keys, header rendering, per‑column cellClassName, actions column, and text truncation). I don’t see regressions here; this should make it much easier to keep DataList/DataGrid rendering consistent.

packages/ui/src/components/Filters/Filters.tsx (1)

51-96: Rendering leading filter and view mode toggle side‑by‑side is wired correctly

The top bar now:

  • Keeps the leading filter inside the horizontal ScrollContainer, and
  • Places the viewModeToggle immediately next to it as a regular FilterItem.

viewModeToggle is excluded from the sheet’s filteredItems list, so it only appears once, and it doesn’t interfere with Formik state (consistent with FilterItem’s standalone handling for FilterViewModeToggle). The flex layout and keys all look correct.

Comment thread packages/ui/src/components/DataGrid/DataGrid.tsx Outdated
Comment thread packages/ui/src/components/Filters/FilterItem.tsx
Comment thread packages/ui/src/lib/renderCell.tsx
@marcinkrasowski marcinkrasowski self-requested a review November 24, 2025 11:10
Comment thread packages/ui/src/components/DataGrid/DataGrid.tsx Outdated
Comment thread packages/ui/src/components/DataGrid/DataGrid.tsx Outdated
Comment thread packages/ui/src/components/DataGrid/DataGrid.tsx Outdated
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

🧹 Nitpick comments (1)
packages/blocks/ticket-list/src/frontend/TicketList.client.tsx (1)

41-43: Type narrowing in the find() predicate is not being utilized, but the code is functionally correct.

The pattern component.filters?.items.find((item) => item.__typename === 'FilterViewModeToggle')?.value does not narrow TypeScript's understanding of the item type within the find predicate because it lacks an explicit type predicate. However, this doesn't create a runtime issue—the optional chaining ?.value safely handles access regardless of the union type, and TypeScript compilation passes without errors.

While the code works correctly, it could be improved by using an explicit type predicate (item is FilterViewModeToggle) for better type safety. Contrast this with line 173, where the if-statement guard does provide proper type narrowing. For consistency and clarity, consider using a type predicate in the find call:

const initialViewMode =
    component.filters?.items.find((item): item is FilterViewModeToggle => item.__typename === 'FilterViewModeToggle')?.value || 'list';
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 226272e and dcca5e6.

⛔ Files ignored due to path filters (176)
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/index.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/index.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/index.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/index.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.client.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.client.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.client.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.client.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.controller.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.controller.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.controller.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.controller.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.mapper.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.mapper.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.mapper.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.mapper.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.model.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.model.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.model.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.model.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.module.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.module.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.module.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.module.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.request.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.request.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.request.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.request.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.service.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.service.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.service.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/api-harmonization/api-harmonization/notification-summary.service.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/frontend/api-harmonization/notification-summary.client.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/frontend/api-harmonization/notification-summary.client.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/frontend/api-harmonization/notification-summary.client.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/frontend/api-harmonization/notification-summary.model.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/frontend/api-harmonization/notification-summary.model.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/frontend/api-harmonization/notification-summary.model.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/frontend/api-harmonization/notification-summary.request.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/frontend/api-harmonization/notification-summary.request.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/frontend/api-harmonization/notification-summary.request.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/frontend/frontend/NotificationSummary.client.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/frontend/frontend/NotificationSummary.client.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/frontend/frontend/NotificationSummary.client.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/frontend/frontend/NotificationSummary.client.stories.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/frontend/frontend/NotificationSummary.client.stories.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/frontend/frontend/NotificationSummary.client.stories.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/frontend/frontend/NotificationSummary.renderer.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/frontend/frontend/NotificationSummary.renderer.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/frontend/frontend/NotificationSummary.renderer.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/frontend/frontend/NotificationSummary.server.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/frontend/frontend/NotificationSummary.server.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/frontend/frontend/NotificationSummary.server.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/frontend/frontend/NotificationSummary.types.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/frontend/frontend/NotificationSummary.types.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/frontend/frontend/NotificationSummary.types.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/frontend/frontend/index.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/frontend/frontend/index.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/frontend/frontend/index.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/frontend/sdk/index.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/frontend/sdk/index.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/frontend/sdk/index.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/frontend/sdk/notification-summary.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/frontend/sdk/notification-summary.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/frontend/sdk/notification-summary.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/sdk/api-harmonization/notification-summary.client.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/sdk/api-harmonization/notification-summary.client.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/sdk/api-harmonization/notification-summary.client.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/sdk/api-harmonization/notification-summary.client.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/sdk/api-harmonization/notification-summary.model.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/sdk/api-harmonization/notification-summary.model.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/sdk/api-harmonization/notification-summary.model.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/sdk/api-harmonization/notification-summary.model.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/sdk/api-harmonization/notification-summary.request.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/sdk/api-harmonization/notification-summary.request.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/sdk/api-harmonization/notification-summary.request.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/sdk/api-harmonization/notification-summary.request.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/sdk/sdk/index.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/sdk/sdk/index.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/sdk/sdk/index.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/sdk/sdk/index.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/sdk/sdk/notification-summary.d.ts is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/sdk/sdk/notification-summary.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/sdk/sdk/notification-summary.js is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/sdk/sdk/notification-summary.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/notification-summary/dist/tsconfig.api.tsbuildinfo is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/tsconfig.frontend.tsbuildinfo is excluded by !**/dist/**
  • packages/blocks/notification-summary/dist/tsconfig.sdk.tsbuildinfo is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/index.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/index.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/index.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/index.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.client.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.client.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.client.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.client.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.controller.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.controller.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.controller.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.controller.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.mapper.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.mapper.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.mapper.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.mapper.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.model.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.model.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.model.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.model.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.module.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.module.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.module.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.module.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.request.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.request.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.request.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.request.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.service.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.service.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.service.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/api-harmonization/api-harmonization/ticket-summary.service.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/frontend/api-harmonization/ticket-summary.client.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/frontend/api-harmonization/ticket-summary.client.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/frontend/api-harmonization/ticket-summary.client.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/frontend/api-harmonization/ticket-summary.model.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/frontend/api-harmonization/ticket-summary.model.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/frontend/api-harmonization/ticket-summary.model.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/frontend/api-harmonization/ticket-summary.request.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/frontend/api-harmonization/ticket-summary.request.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/frontend/api-harmonization/ticket-summary.request.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/frontend/frontend/TicketSummary.client.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/frontend/frontend/TicketSummary.client.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/frontend/frontend/TicketSummary.client.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/frontend/frontend/TicketSummary.client.stories.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/frontend/frontend/TicketSummary.client.stories.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/frontend/frontend/TicketSummary.client.stories.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/frontend/frontend/TicketSummary.renderer.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/frontend/frontend/TicketSummary.renderer.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/frontend/frontend/TicketSummary.renderer.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/frontend/frontend/TicketSummary.server.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/frontend/frontend/TicketSummary.server.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/frontend/frontend/TicketSummary.server.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/frontend/frontend/TicketSummary.types.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/frontend/frontend/TicketSummary.types.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/frontend/frontend/TicketSummary.types.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/frontend/frontend/index.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/frontend/frontend/index.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/frontend/frontend/index.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/frontend/sdk/index.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/frontend/sdk/index.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/frontend/sdk/index.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/frontend/sdk/ticket-summary.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/frontend/sdk/ticket-summary.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/frontend/sdk/ticket-summary.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/sdk/api-harmonization/ticket-summary.client.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/sdk/api-harmonization/ticket-summary.client.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/sdk/api-harmonization/ticket-summary.client.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/sdk/api-harmonization/ticket-summary.client.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/sdk/api-harmonization/ticket-summary.model.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/sdk/api-harmonization/ticket-summary.model.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/sdk/api-harmonization/ticket-summary.model.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/sdk/api-harmonization/ticket-summary.model.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/sdk/api-harmonization/ticket-summary.request.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/sdk/api-harmonization/ticket-summary.request.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/sdk/api-harmonization/ticket-summary.request.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/sdk/api-harmonization/ticket-summary.request.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/sdk/sdk/index.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/sdk/sdk/index.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/sdk/sdk/index.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/sdk/sdk/index.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/sdk/sdk/ticket-summary.d.ts is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/sdk/sdk/ticket-summary.d.ts.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/sdk/sdk/ticket-summary.js is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/sdk/sdk/ticket-summary.js.map is excluded by !**/dist/**, !**/*.map
  • packages/blocks/ticket-summary/dist/tsconfig.api.tsbuildinfo is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/tsconfig.frontend.tsbuildinfo is excluded by !**/dist/**
  • packages/blocks/ticket-summary/dist/tsconfig.sdk.tsbuildinfo is excluded by !**/dist/**
📒 Files selected for processing (3)
  • packages/blocks/ticket-list/src/api-harmonization/ticket-list.mapper.ts (1 hunks)
  • packages/blocks/ticket-list/src/api-harmonization/ticket-list.model.ts (1 hunks)
  • packages/blocks/ticket-list/src/frontend/TicketList.client.tsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/blocks/ticket-list/src/api-harmonization/ticket-list.model.ts
  • packages/blocks/ticket-list/src/api-harmonization/ticket-list.mapper.ts
⏰ 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: deploy-preview
🔇 Additional comments (6)
packages/blocks/ticket-list/src/frontend/TicketList.client.tsx (6)

11-13: LGTM!

The new imports are necessary for the grid view functionality and are properly used throughout the component.


47-47: LGTM!

The viewMode state is properly typed and initialized from the filters, enabling the view mode toggle functionality.


101-120: LGTM!

The conditional action rendering appropriately adapts button variants for grid (tertiary) and list (link) views, maintaining visual consistency with each layout style.


60-66: Consider resetting viewMode in handleReset.

The handleReset function resets filter state to initialFilters but does not reset viewMode to initialViewMode. This means the view mode persists when users click reset.

If this is intentional (treating view mode as a separate user preference), it's fine. However, if "reset" should restore all UI state including the view mode, consider adding:

 const handleReset = () => {
     startTransition(async () => {
         const newData = await sdk.blocks.getTicketList(initialFilters, { 'x-locale': locale }, accessToken);
         setFilters(initialFilters);
+        setViewMode(initialViewMode);
         setData(newData);
     });
 };

196-205: Original review comment is incorrect; no issues with slots handling.

The slots property is defined as optional in the TicketListBlock model (line 24-29 in packages/framework/src/modules/cms/models/blocks/ticket-list.model.ts), and the DataGrid component safely handles undefined slots using optional chaining (slots?.top, slots?.left, etc.) on lines 72-75 of DataGrid.tsx. No explicit undefined check is needed—the component gracefully handles both defined and undefined slots.


168-184: Type safety for FilterViewModeToggle property assignment is correct.

The FilterViewModeToggle class (packages/framework/src/utils/models/filters.ts:61-66) already defines value?: 'list' | 'grid' and onChange?: (value: 'list' | 'grid') => void as optional properties. The code spreads the item and overrides these properties with matching types, making this pattern type-safe. No concerns.

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

♻️ Duplicate comments (1)
packages/ui/src/components/DataGrid/DataGrid.tsx (1)

43-53: Move pure helper function outside the component.

getGridColsClass doesn't depend on component state or props and can be moved outside to avoid recreation on each render.

This was previously flagged by marcinkrasowski.

🧹 Nitpick comments (1)
.changeset/long-gifts-divide.md (1)

13-13: Commit message prefix inconsistent with change type.

The description uses fix: but this PR introduces new features (DataGrid component, FilterViewModeToggle, view mode state). Consider using feat: to align with semantic versioning conventions and the minor version bumps declared above.

-fix: update FilterItem and renderCell components for improved functionality and code clarity
+feat: add DataGrid component and view mode toggle for data lists
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dcca5e6 and d1f7b3e.

📒 Files selected for processing (4)
  • .changeset/long-gifts-divide.md (1 hunks)
  • packages/ui/src/components/DataGrid/DataGrid.tsx (1 hunks)
  • packages/ui/src/components/Filters/FilterItem.tsx (2 hunks)
  • packages/ui/src/lib/renderCell.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/ui/src/components/Filters/FilterItem.tsx
  • packages/ui/src/lib/renderCell.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
packages/ui/src/components/DataGrid/DataGrid.tsx (7)
packages/ui/src/components/DataGrid/index.ts (2)
  • DataGrid (1-1)
  • DataGridProps (2-2)
packages/ui/src/components/DataGrid/DataGrid.types.ts (1)
  • DataGridProps (29-74)
packages/ui/src/lib/utils.ts (1)
  • cn (5-7)
packages/ui/src/elements/card.tsx (3)
  • CardHeader (31-31)
  • CardContent (31-31)
  • CardFooter (31-31)
packages/ui/src/lib/renderCell.tsx (1)
  • renderCell (16-100)
packages/ui/src/elements/typography.tsx (1)
  • Typography (56-56)
packages/ui/src/elements/separator.tsx (1)
  • Separator (23-23)
⏰ 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: deploy-preview
🔇 Additional comments (3)
packages/ui/src/components/DataGrid/DataGrid.tsx (3)

88-128: LGTM!

The slot-based CardHeader rendering with Typography variants for left/bottom columns and flexible layout is well-structured.


131-151: LGTM!

The body column rendering correctly handles null/undefined values with cellContent == null, and bodyColumns is computed once before iteration—both addressing previous review feedback.


153-158: LGTM!

The actions footer rendering with conditional display and Separator is clean.

Comment thread packages/ui/src/components/DataGrid/DataGrid.tsx Outdated
@vercel vercel Bot temporarily deployed to Preview – o2s-docs November 27, 2025 12:36 Inactive
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 (1)
packages/ui/src/components/DataGrid/DataGrid.tsx (1)

56-62: Dynamic Tailwind classes won't work at build time.

Tailwind CSS purges classes not present in source as complete strings. The template literals on lines 59-60 (md:${getGridColsClass(...)} and lg:${getGridColsClass(...)}) generate classes dynamically at runtime, which Tailwind's JIT compiler cannot detect during the build.

Consider using a safelist approach or pre-generating the full class strings:

+const RESPONSIVE_GRID_COLS: Record<number, { mobile: string; tablet: string; desktop: string }> = {
+    1: { mobile: 'grid-cols-1', tablet: 'md:grid-cols-1', desktop: 'lg:grid-cols-1' },
+    2: { mobile: 'grid-cols-2', tablet: 'md:grid-cols-2', desktop: 'lg:grid-cols-2' },
+    3: { mobile: 'grid-cols-3', tablet: 'md:grid-cols-3', desktop: 'lg:grid-cols-3' },
+    4: { mobile: 'grid-cols-4', tablet: 'md:grid-cols-4', desktop: 'lg:grid-cols-4' },
+    5: { mobile: 'grid-cols-5', tablet: 'md:grid-cols-5', desktop: 'lg:grid-cols-5' },
+    6: { mobile: 'grid-cols-6', tablet: 'md:grid-cols-6', desktop: 'lg:grid-cols-6' },
+};
+
+const getResponsiveGridClasses = (mobile: number, tablet: number, desktop: number): string => {
+    const mobileClass = RESPONSIVE_GRID_COLS[mobile]?.mobile || 'grid-cols-1';
+    const tabletClass = RESPONSIVE_GRID_COLS[tablet]?.tablet || 'md:grid-cols-2';
+    const desktopClass = RESPONSIVE_GRID_COLS[desktop]?.desktop || 'lg:grid-cols-3';
+    return cn(mobileClass, tabletClass, desktopClass);
+};
+
-    // Build responsive grid classes
     const gridClasses = cn(
         'grid gap-4',
-        getGridColsClass(mobileCols),
-        `md:${getGridColsClass(tabletCols)}`,
-        `lg:${getGridColsClass(desktopCols)}`,
+        getResponsiveGridClasses(mobileCols, tabletCols, desktopCols),
         className,
     );

Alternatively, add the possible classes to Tailwind's safelist in tailwind.config.js:

module.exports = {
  safelist: [
    'grid-cols-1', 'grid-cols-2', 'grid-cols-3', 'grid-cols-4', 'grid-cols-5', 'grid-cols-6',
    'md:grid-cols-1', 'md:grid-cols-2', 'md:grid-cols-3', 'md:grid-cols-4', 'md:grid-cols-5', 'md:grid-cols-6',
    'lg:grid-cols-1', 'lg:grid-cols-2', 'lg:grid-cols-3', 'lg:grid-cols-4', 'lg:grid-cols-5', 'lg:grid-cols-6',
  ],
  // ... rest of config
}
🧹 Nitpick comments (1)
packages/ui/src/components/DataGrid/DataGrid.tsx (1)

38-39: Consider using unknown instead of any in the generic constraint.

The generic constraint Record<string, any> uses any, which bypasses type checking. Consider using Record<string, unknown> for better type safety while maintaining flexibility.

Apply this diff:

-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-export function DataGrid<T extends Record<string, any>>({
+export function DataGrid<T extends Record<string, unknown>>({
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d1f7b3e and 0ad7aee.

📒 Files selected for processing (4)
  • packages/blocks/ticket-list/src/frontend/TicketList.client.tsx (5 hunks)
  • packages/integrations/mocked/src/modules/cms/mappers/blocks/cms.ticket-list.mapper.ts (6 hunks)
  • packages/ui/src/components/DataGrid/DataGrid.stories.tsx (1 hunks)
  • packages/ui/src/components/DataGrid/DataGrid.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/blocks/ticket-list/src/frontend/TicketList.client.tsx
  • packages/ui/src/components/DataGrid/DataGrid.stories.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-26T11:57:00.632Z
Learnt from: marcinkrasowski
Repo: o2sdev/openselfservice PR: 411
File: packages/framework/src/modules/cms/models/blocks/product-list.model.ts:29-46
Timestamp: 2025-11-26T11:57:00.632Z
Learning: In the framework layer (packages/framework/src/modules/cms/models/blocks/*.model.ts), block classes like ProductListBlock should NOT include explicit __typename discriminators. The __typename field is added at the API harmonization layer (packages/blocks/*/src/api-harmonization/*.model.ts) where it's needed for discriminated unions. The framework maintains normalized data models without these discriminators.

Applied to files:

  • packages/integrations/mocked/src/modules/cms/mappers/blocks/cms.ticket-list.mapper.ts
🧬 Code graph analysis (1)
packages/ui/src/components/DataGrid/DataGrid.tsx (3)
packages/ui/src/components/DataGrid/DataGrid.types.ts (1)
  • DataGridProps (29-74)
packages/ui/src/lib/utils.ts (1)
  • cn (5-7)
packages/ui/src/lib/renderCell.tsx (1)
  • renderCell (16-100)
⏰ 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: deploy-preview
🔇 Additional comments (5)
packages/integrations/mocked/src/modules/cms/mappers/blocks/cms.ticket-list.mapper.ts (2)

36-40: Slots configuration correctly references existing columns.

The slot mappings now use valid column IDs (topic, status, updatedAt) that exist in table.columns, resolving the issue flagged in previous reviews. The implementation looks good.

Optional observation: The top slot is not defined, though the DataGrid component supports it according to the PR summary. If a top row (e.g., for category badges or tags) isn't needed for ticket cards, this is fine; otherwise, consider whether any content should occupy that position.


Note: This addresses the concern raised in previous review comments where slots.left = 'id' didn't match any column.

Also applies to: 199-203, 365-369


146-150: Verify that FilterViewModeToggle requires no label.

The new FilterViewModeToggle filter item doesn't include a label property, unlike other filter types in the array (FilterSelect, FilterToggleGroup, FilterDateRange all have labels). If this toggle is rendered as an icon-only control (e.g., list/grid icons), the absence of a label is intentional and appropriate. Please confirm this design choice.

Also applies to: 311-315, 477-481

packages/ui/src/components/DataGrid/DataGrid.tsx (3)

1-21: LGTM! Clean imports and well-structured constant.

The module-level GRID_COLS_MAP constant is well-organized and addresses the past review feedback about moving helper functions outside the component.


23-32: LGTM! Helper functions are well-implemented.

Both getGridColsClass and defaultGetRowKey are correctly placed at module level and provide sensible defaults.


77-146: LGTM! Rendering logic is well-structured and addresses past review feedback.

The implementation correctly:

  • Computes bodyColumns once outside the map (line 75), avoiding unnecessary recomputation per row
  • Uses loose equality cellContent == null (line 120) to catch both null and undefined while preserving falsy values like '', 0, and false
  • Employs a clear Card-based layout with proper slot positioning
  • Uses static Tailwind classes in the body grid (line 115), which won't be affected by purging

@vercel vercel Bot temporarily deployed to Preview – o2s-docs November 27, 2025 13:32 Inactive
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)
packages/ui/src/components/DataGrid/DataGrid.tsx (1)

53-59: Dynamic Tailwind classes will be purged at build time.

Lines 56-57 use template literals (md:${getGridColsClass(tabletCols)}) to construct Tailwind classes dynamically. Tailwind's JIT compiler cannot detect these at build time, so the classes will be purged. This was flagged in a previous review but the fix appears incomplete.

Apply explicit breakpoint-prefixed mappings:

 const GRID_COLS_MAP: Record<number, string> = {
 };

+const MD_GRID_COLS_MAP: Record<number, string> = {
+    1: 'md:grid-cols-1',
+    2: 'md:grid-cols-2',
+    3: 'md:grid-cols-3',
+    4: 'md:grid-cols-4',
+    5: 'md:grid-cols-5',
+    6: 'md:grid-cols-6',
+};
+
+const LG_GRID_COLS_MAP: Record<number, string> = {
+    1: 'lg:grid-cols-1',
+    2: 'lg:grid-cols-2',
+    3: 'lg:grid-cols-3',
+    4: 'lg:grid-cols-4',
+    5: 'lg:grid-cols-5',
+    6: 'lg:grid-cols-6',
+};

 const getGridColsClass = (cols: number): string => {
     return GRID_COLS_MAP[cols] || 'grid-cols-1';
 };

+const getMdGridColsClass = (cols: number): string => {
+    return MD_GRID_COLS_MAP[cols] || 'md:grid-cols-2';
+};
+
+const getLgGridColsClass = (cols: number): string => {
+    return LG_GRID_COLS_MAP[cols] || 'lg:grid-cols-3';
+};

Then update the class construction:

     const gridClasses = cn(
         'grid gap-4',
         getGridColsClass(mobileCols),
-        `md:${getGridColsClass(tabletCols)}`,
-        `lg:${getGridColsClass(desktopCols)}`,
+        getMdGridColsClass(tabletCols),
+        getLgGridColsClass(desktopCols),
         className,
     );
🧹 Nitpick comments (4)
packages/ui/src/components/DataView/DataView.types.ts (1)

3-19: Add generic constraint for consistency with implementation.

The DataViewProps<T> interface lacks the generic constraint that exists in DataView.tsx and DataGrid.tsx (T extends Record<string, any>). This inconsistency could allow incompatible types at the interface level that would fail at runtime.

-export interface DataViewProps<T> {
+export interface DataViewProps<T extends Record<string, unknown>> {

Using Record<string, unknown> is also safer than any for the constraint.

packages/ui/src/components/DataView/DataView.tsx (2)

8-14: Component shadows global DataView - acceptable in this context.

Biome flags this as shadowing the global DataView (used for typed array buffers). While technically correct, the name is semantically appropriate for this UI component, and the global DataView is unlikely to be used in React component code. If the team prefers strict linting compliance, consider renaming to DataViewSwitch or similar.

The implementation logic is correct - slots and columnsCount are properly forwarded only to DataGrid since DataList doesn't support them.


15-23: Unnecessary fragment wrapper.

The <>...</> fragment is not needed when there's only one conditional expression.

-    return (
-        <>
-            {viewMode === 'grid' ? (
-                <DataGrid {...props} slots={slots} columnsCount={columnsCount} />
-            ) : (
-                <DataList {...props} />
-            )}
-        </>
-    );
+    return viewMode === 'grid' ? (
+        <DataGrid {...props} slots={slots} columnsCount={columnsCount} />
+    ) : (
+        <DataList {...props} />
+    );
packages/ui/src/components/DataView/DataView.stories.tsx (1)

30-38: Reduce Record<string, unknown> casts by typing stories on Ticket (optional)

The repetitive casts to Record<string, unknown> for data, columns, and actions weaken type safety around the Ticket shape in these stories. If DataViewProps and DataView support generics, you can often keep the Ticket typing end‑to‑end and avoid the as noise.

For example (adjusting to your actual component typing):

type TicketStory = StoryObj<DataViewProps<Ticket>>;

export const ListView: TicketStory = {
    args: {
        viewMode: 'list',
        data: sampleTickets,
        columns: ticketColumns,
        actions,
    },
};

export const GridView: TicketStory = {
    args: {
        viewMode: 'grid',
        data: sampleTickets,
        columns: ticketColumns,
        slots: {
            left: 'id',
            right: 'status',
            bottom: 'updatedAt',
        },
        actions,
    },
};

If DataView isn’t generic or StoryObj typing makes this awkward, feel free to keep the current approach, but this can make refactors to Ticket safer.

Please double‑check how DataViewProps is declared; if it’s not generic over row type, this suggestion may not apply as‑is.

Also applies to: 70-108, 122-143

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0ad7aee and a49805d.

📒 Files selected for processing (6)
  • packages/blocks/ticket-list/src/frontend/TicketList.client.tsx (5 hunks)
  • packages/ui/src/components/DataGrid/DataGrid.tsx (1 hunks)
  • packages/ui/src/components/DataView/DataView.stories.tsx (1 hunks)
  • packages/ui/src/components/DataView/DataView.tsx (1 hunks)
  • packages/ui/src/components/DataView/DataView.types.ts (1 hunks)
  • packages/ui/src/components/DataView/index.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
packages/ui/src/components/DataView/DataView.tsx (2)
packages/ui/src/components/DataView/DataView.types.ts (1)
  • DataViewProps (3-19)
packages/ui/src/components/DataGrid/DataGrid.tsx (1)
  • DataGrid (36-144)
packages/ui/src/components/DataGrid/DataGrid.tsx (3)
packages/ui/src/components/DataGrid/DataGrid.types.ts (1)
  • DataGridProps (29-74)
packages/ui/src/lib/utils.ts (1)
  • cn (5-7)
packages/ui/src/lib/renderCell.tsx (1)
  • renderCell (16-100)
packages/blocks/ticket-list/src/frontend/TicketList.client.tsx (4)
packages/blocks/ticket-list/src/api-harmonization/ticket-list.model.ts (2)
  • TicketListBlock (7-35)
  • Ticket (37-54)
packages/framework/src/modules/cms/models/blocks/ticket-list.model.ts (1)
  • TicketListBlock (6-32)
packages/ui/src/components/DataView/DataView.tsx (1)
  • DataView (9-24)
packages/ui/src/components/DataView/index.ts (1)
  • DataView (1-1)
🪛 Biome (2.1.2)
packages/ui/src/components/DataView/DataView.tsx

[error] 9-9: Do not shadow the global "DataView" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

packages/blocks/ticket-list/src/frontend/TicketList.client.tsx

[error] 12-12: Do not shadow the global "DataView" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

packages/ui/src/components/DataView/DataView.stories.tsx

[error] 5-5: Do not shadow the global "DataView" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

⏰ 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: deploy-preview
🔇 Additional comments (6)
packages/ui/src/components/DataGrid/DataGrid.tsx (2)

15-26: Good refactoring: helper functions moved outside component.

The GRID_COLS_MAP, getGridColsClass, and defaultGetRowKey are correctly placed outside the component to avoid recomputation on each render. This addresses the past reviewer's feedback.

Also applies to: 28-33


111-131: Clean body column rendering with proper null check.

The cellContent == null check (line 117) correctly handles both null and undefined while allowing falsy values like 0 or '' to render. The body columns are computed once before the map operation (line 72), avoiding redundant calculations per row.

packages/ui/src/components/DataView/index.ts (1)

1-2: LGTM!

Clean barrel exports following the established pattern in the codebase.

packages/blocks/ticket-list/src/frontend/TicketList.client.tsx (3)

157-173: Filter transformation correctly injects viewMode state.

The approach of mapping over filter items to inject value and onChange for FilterViewModeToggle is a clean pattern for connecting component state to filter controls. The spread operator preserves other properties appropriately.


185-191: DataView integration looks correct.

The DataView component receives all required props: viewMode, data, columns, actions, and slots. This properly delegates to either DataGrid or DataList based on the current view mode.

Note: The Biome warning about shadowing global DataView applies to the import statement, not a local redefinition. This is a false positive in the context of importing a named export.


40-42: The code is already type-safe; no type assertion or type guard is needed.

The FilterViewModeToggle type is properly defined in packages/framework/src/utils/models/filters.ts with an optional value property typed as 'list' | 'grid'. The code on line 42 correctly uses optional chaining (?.value) combined with a fallback (|| 'list'), which safely handles both cases: when value is present or when the entire filter item doesn't exist. TypeScript's type inference ensures initialViewMode is correctly typed as 'list' | 'grid' for the subsequent useState call.

Comment thread packages/ui/src/components/DataView/DataView.stories.tsx
Comment thread packages/ui/src/components/DataView/DataView.stories.tsx
@vercel vercel Bot temporarily deployed to Preview – o2s-docs November 27, 2025 13:39 Inactive
@vercel vercel Bot temporarily deployed to Preview – o2s-docs November 27, 2025 15:04 Inactive
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 (1)
packages/blocks/notification-list/src/frontend/NotificationList.client.tsx (1)

123-135: Avoid nested interactive elements in row actions (Link wrapping Button)

LinkComponent likely renders an <a> and currently wraps a <Button>, which results in an anchor containing a button – an invalid and confusing interactive structure. Use the same pattern as in TicketList.client.tsx: Button asChild with LinkComponent as the child.

-    const actions = data.table.actions
-        ? {
-              ...data.table.actions,
-              render: (notification: Model.Notification) => (
-                  <LinkComponent href={notification.detailsUrl}>
-                      <Button variant="link" className="flex items-center justify-end gap-2">
-                          {data.table.actions!.label}
-                          <ArrowRight className="h-4 w-4" />
-                      </Button>
-                  </LinkComponent>
-              ),
-          }
-        : undefined;
+    const actions = data.table.actions
+        ? {
+              ...data.table.actions,
+              render: (notification: Model.Notification) => (
+                  <Button asChild variant="link">
+                      <LinkComponent
+                          href={notification.detailsUrl}
+                          className="flex items-center justify-end gap-2"
+                      >
+                          {data.table.actions!.label}
+                          <ArrowRight className="h-4 w-4" />
+                      </LinkComponent>
+                  </Button>
+              ),
+          }
+        : undefined;

This matches the established pattern and avoids nested interactive controls.

♻️ Duplicate comments (5)
packages/ui/src/components/DataView/DataView.tsx (1)

9-9: Consider renaming to avoid confusion with the global DataView.

Same naming concern as in consumer files. The component name conflicts with JavaScript's native DataView for binary data. Consider DataListView, ListGridView, or similar alternatives.

packages/blocks/order-list/src/frontend/OrderList.client.tsx (1)

10-10: Consider renaming to avoid confusion with the global DataView.

Same naming concern applies here. See earlier comments about potential confusion with JavaScript's native DataView object.

packages/ui/src/components/DataView/DataView.stories.tsx (2)

5-5: Alias DataView in stories to avoid shadowing the global and Biome error

The story imports and types against DataView directly, which shadows the JS global and trips Biome’s noShadowRestrictedNames rule. Alias the import and update meta/Story typing:

-import { DataView } from '@o2s/ui/components/DataView';
+import { DataView as DataViewComponent } from '@o2s/ui/components/DataView';

-const meta: Meta<typeof DataView> = {
+const meta: Meta<typeof DataViewComponent> = {
     title: 'Components/DataView',
-    component: DataView,
+    component: DataViewComponent,
@@
-export default meta;
-type Story = StoryObj<typeof DataView>;
+export default meta;
+type Story = StoryObj<typeof DataViewComponent>;

This keeps Storybook typing intact while satisfying Biome.

Also applies to: 11-14, 27-28


110-120: Use a semantic link and detailsUrl in the “View Details” action

The actions renderer currently ignores _item.detailsUrl and uses Button asChild with a non‑semantic <span>, so rows are not actually navigable in the story. Consider wiring the action to detailsUrl with a link:

-const actions = {
-    title: 'Actions',
-    render: (_item: Ticket) => (
-        <Button asChild variant="link" className="flex items-center gap-2">
-            <span>
-                <ArrowRight className="h-4 w-4" />
-                View Details
-            </span>
-        </Button>
-    ),
-};
+const actions = {
+    title: 'Actions',
+    render: (item: Ticket) => (
+        <Button asChild variant="link">
+            <a href={item.detailsUrl} className="flex items-center gap-2">
+                <ArrowRight className="h-4 w-4" />
+                <span>View Details</span>
+            </a>
+        </Button>
+    ),
+};

This better demonstrates real‑world usage and keeps the action focusable per row.

packages/ui/src/components/DataGrid/DataGrid.tsx (1)

14-21: Replace dynamic Tailwind class composition with static responsive mappings

gridClasses builds md:${getGridColsClass(...)} / lg:${...} at runtime, so Tailwind’s compiler can’t see the actual class names and may not generate the responsive grid utilities. This was flagged before and is still present.

Consider precomputing breakpoint‑specific class names:

-const GRID_COLS_MAP: Record<number, string> = {
-    1: 'grid-cols-1',
-    2: 'grid-cols-2',
-    3: 'grid-cols-3',
-    4: 'grid-cols-4',
-    5: 'grid-cols-5',
-    6: 'grid-cols-6',
-};
-
-const getGridColsClass = (cols: number): string => {
-    return GRID_COLS_MAP[cols] || 'grid-cols-1';
-};
+const GRID_COLS_MAP: Record<number, string> = {
+    1: 'grid-cols-1',
+    2: 'grid-cols-2',
+    3: 'grid-cols-3',
+    4: 'grid-cols-4',
+    5: 'grid-cols-5',
+    6: 'grid-cols-6',
+};
+
+const MD_GRID_COLS_MAP: Record<number, string> = {
+    1: 'md:grid-cols-1',
+    2: 'md:grid-cols-2',
+    3: 'md:grid-cols-3',
+    4: 'md:grid-cols-4',
+    5: 'md:grid-cols-5',
+    6: 'md:grid-cols-6',
+};
+
+const LG_GRID_COLS_MAP: Record<number, string> = {
+    1: 'lg:grid-cols-1',
+    2: 'lg:grid-cols-2',
+    3: 'lg:grid-cols-3',
+    4: 'lg:grid-cols-4',
+    5: 'lg:grid-cols-5',
+    6: 'lg:grid-cols-6',
+};
@@
-    const mobileCols = columnsCount?.mobile ?? 1;
-    const tabletCols = columnsCount?.tablet ?? 2;
-    const desktopCols = columnsCount?.desktop ?? 3;
-
-    // Build responsive grid classes
-    const gridClasses = cn(
-        'grid gap-4',
-        getGridColsClass(mobileCols),
-        `md:${getGridColsClass(tabletCols)}`,
-        `lg:${getGridColsClass(desktopCols)}`,
-        className,
-    );
+    const mobileCols = columnsCount?.mobile ?? 1;
+    const tabletCols = columnsCount?.tablet ?? 2;
+    const desktopCols = columnsCount?.desktop ?? 3;
+
+    const gridClasses = cn(
+        'grid gap-4',
+        GRID_COLS_MAP[mobileCols] || 'grid-cols-1',
+        MD_GRID_COLS_MAP[tabletCols] || 'md:grid-cols-2',
+        LG_GRID_COLS_MAP[desktopCols] || 'lg:grid-cols-3',
+        className,
+    );

This keeps all possible Tailwind classes explicit so they’re preserved at build time.

Also applies to: 23-25, 47-58

🧹 Nitpick comments (7)
packages/blocks/ticket-list/src/api-harmonization/ticket-list.model.ts (1)

29-34: Extraction to shared type is valid—CardHeaderSlots is identically duplicated across 4 blocks.

Verification confirms the review comment is accurate. The cardHeaderSlots type definition is byte-for-byte identical in invoice-list, notification-list, order-list, and ticket-list blocks:

{
    top?: string;
    left?: string;
    right?: string;
    bottom?: string;
}

The codebase has an established pattern for shared types at packages/framework/src/utils/models/, which already includes similar card-related types (InfoCard, CardWithImage, PricingCard). Extracting CardHeaderSlots there would follow the existing convention and eliminate the duplication.

The refactoring suggestion is sound and recommended. Consider creating packages/framework/src/utils/models/card-header-slots.ts and updating the index export accordingly.

packages/blocks/invoice-list/src/frontend/InvoiceList.client.tsx (1)

13-13: Consider renaming to avoid confusion with the global DataView.

The component name DataView shadows the global JavaScript DataView object (used for binary data manipulation). While ES modules scope imports locally and this won't cause runtime errors, it may confuse developers or tools expecting the native DataView. Consider a more specific name like DataListView or ListGridView.

packages/ui/src/components/DataView/DataView.tsx (1)

15-23: Remove unnecessary Fragment wrapper.

The Fragment (<>...</>) wrapping the conditional render is unnecessary since there's only a single child. You can return the ternary expression directly.

Apply this diff:

-    return (
-        <>
-            {viewMode === 'grid' ? (
-                <DataGrid {...props} cardHeaderSlots={cardHeaderSlots} columnsCount={columnsCount} />
-            ) : (
-                <DataList {...props} />
-            )}
-        </>
-    );
+    return viewMode === 'grid' ? (
+        <DataGrid {...props} cardHeaderSlots={cardHeaderSlots} columnsCount={columnsCount} />
+    ) : (
+        <DataList {...props} />
+    );
packages/framework/src/modules/cms/models/blocks/notification-list.model.ts (1)

11-11: Confirm whether initialFilters should also support viewMode

filters is now typed with viewMode?: 'list' | 'grid', but initialFilters remains Partial<Notification & { sort?: string }>. If the view mode toggle is meant to participate in persisted defaults / reset behaviour, consider extending initialFilters to include viewMode as well for consistency with the filters shape and with other list block models; otherwise, it would be good to document that viewMode is intentionally client-only and excluded from initial filters.

Also applies to: 23-28

packages/blocks/ticket-list/src/frontend/TicketList.client.tsx (1)

40-47: Keep viewMode in sync with filter reset semantics

The view mode is initialized from the FilterViewModeToggle and then driven via local state and the FiltersSection mapping, which is good. However, handleReset only resets filters/data, leaving viewMode unchanged, so “Reset filters” won’t restore the initial list/grid mode.

Consider resetting viewMode alongside filters, e.g.:

 const handleReset = () => {
     startTransition(async () => {
         const newData = await sdk.blocks.getTicketList(initialFilters, { 'x-locale': locale }, accessToken);
         setFilters(initialFilters);
         setData(newData);
+        setViewMode(initialViewMode);
     });
 };

This keeps the toggle, filters, and rendered DataView aligned after a reset.

Also applies to: 59-65, 157-173

packages/blocks/notification-list/src/frontend/NotificationList.client.tsx (1)

43-49: Align viewMode with filter reset and FiltersSection behaviour

The view mode is nicely initialized from FilterViewModeToggle and wired into FiltersSection so the toggle reflects current state. However, handleReset only resets filters/data, leaving viewMode unchanged, so “Reset filters” won’t restore the initial list/grid choice.

Consider resetting viewMode as well:

const handleReset = () => {
    startTransition(async () => {
        const newData = await sdk.blocks.getNotificationList(initialFilters, { 'x-locale': locale }, accessToken);

        setFilters(initialFilters);
        setData(newData);
+       setViewMode(initialViewMode);
    });
};

This keeps the toggle, filters, and rendered DataView consistent after resets.

Also applies to: 62-69, 144-160, 169-175

packages/ui/src/components/DataGrid/DataGrid.stories.tsx (1)

160-178: Drop unnecessary asChild usage in action stories

In WithActions, CustomColumnCount, and WithTopSlot, Button is rendered with asChild but without a link/alternate root element—just icon + text children. asChild isn’t buying anything here and can be confusing given the design‑system pattern.

You can simplify to a regular button:

-        actions: {
-            title: '',
-            render: (_item) => (
-                <Button asChild variant="link" className="flex items-center gap-2">
-                    <ArrowRight className="h-4 w-4" />
-                    View Details
-                </Button>
-            ),
-        },
+        actions: {
+            title: '',
+            render: (_item) => (
+                <Button variant="link" className="flex items-center gap-2">
+                    <ArrowRight className="h-4 w-4" />
+                    View Details
+                </Button>
+            ),
+        },

Apply the same pattern to the other stories using asChild without a link.

Also applies to: 181-205, 207-225

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a607f50 and 43d0510.

📒 Files selected for processing (27)
  • packages/blocks/invoice-list/src/api-harmonization/invoice-list.mapper.ts (1 hunks)
  • packages/blocks/invoice-list/src/api-harmonization/invoice-list.model.ts (1 hunks)
  • packages/blocks/invoice-list/src/frontend/InvoiceList.client.tsx (4 hunks)
  • packages/blocks/notification-list/src/api-harmonization/notification-list.mapper.ts (1 hunks)
  • packages/blocks/notification-list/src/api-harmonization/notification-list.model.ts (1 hunks)
  • packages/blocks/notification-list/src/frontend/NotificationList.client.tsx (4 hunks)
  • packages/blocks/order-list/src/api-harmonization/order-list.mapper.ts (1 hunks)
  • packages/blocks/order-list/src/api-harmonization/order-list.model.ts (1 hunks)
  • packages/blocks/order-list/src/frontend/OrderList.client.tsx (5 hunks)
  • packages/blocks/ticket-list/src/api-harmonization/ticket-list.mapper.ts (1 hunks)
  • packages/blocks/ticket-list/src/api-harmonization/ticket-list.model.ts (1 hunks)
  • packages/blocks/ticket-list/src/frontend/TicketList.client.stories.tsx (2 hunks)
  • packages/blocks/ticket-list/src/frontend/TicketList.client.tsx (5 hunks)
  • packages/framework/src/modules/cms/models/blocks/invoice-list.model.ts (2 hunks)
  • packages/framework/src/modules/cms/models/blocks/notification-list.model.ts (2 hunks)
  • packages/framework/src/modules/cms/models/blocks/order-list.model.ts (2 hunks)
  • packages/framework/src/modules/cms/models/blocks/ticket-list.model.ts (2 hunks)
  • packages/integrations/mocked/src/modules/cms/mappers/blocks/cms.invoice-list.mapper.ts (6 hunks)
  • packages/integrations/mocked/src/modules/cms/mappers/blocks/cms.notification-list.mapper.ts (6 hunks)
  • packages/integrations/mocked/src/modules/cms/mappers/blocks/cms.order-list.mapper.ts (6 hunks)
  • packages/integrations/mocked/src/modules/cms/mappers/blocks/cms.ticket-list.mapper.ts (6 hunks)
  • packages/ui/src/components/DataGrid/DataGrid.stories.tsx (1 hunks)
  • packages/ui/src/components/DataGrid/DataGrid.tsx (1 hunks)
  • packages/ui/src/components/DataGrid/DataGrid.types.ts (1 hunks)
  • packages/ui/src/components/DataView/DataView.stories.tsx (1 hunks)
  • packages/ui/src/components/DataView/DataView.tsx (1 hunks)
  • packages/ui/src/components/DataView/DataView.types.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/integrations/mocked/src/modules/cms/mappers/blocks/cms.ticket-list.mapper.ts
  • packages/framework/src/modules/cms/models/blocks/ticket-list.model.ts
  • packages/ui/src/components/DataGrid/DataGrid.types.ts
  • packages/blocks/ticket-list/src/api-harmonization/ticket-list.mapper.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-26T11:57:00.632Z
Learnt from: marcinkrasowski
Repo: o2sdev/openselfservice PR: 411
File: packages/framework/src/modules/cms/models/blocks/product-list.model.ts:29-46
Timestamp: 2025-11-26T11:57:00.632Z
Learning: In the framework layer (packages/framework/src/modules/cms/models/blocks/*.model.ts), block classes like ProductListBlock should NOT include explicit __typename discriminators. The __typename field is added at the API harmonization layer (packages/blocks/*/src/api-harmonization/*.model.ts) where it's needed for discriminated unions. The framework maintains normalized data models without these discriminators.

Applied to files:

  • packages/framework/src/modules/cms/models/blocks/order-list.model.ts
  • packages/framework/src/modules/cms/models/blocks/invoice-list.model.ts
  • packages/framework/src/modules/cms/models/blocks/notification-list.model.ts
  • packages/integrations/mocked/src/modules/cms/mappers/blocks/cms.order-list.mapper.ts
🧬 Code graph analysis (8)
packages/framework/src/modules/cms/models/blocks/order-list.model.ts (2)
packages/blocks/order-list/src/api-harmonization/order-list.model.ts (1)
  • Order (36-59)
packages/framework/src/modules/orders/orders.model.ts (1)
  • Order (24-49)
packages/framework/src/modules/cms/models/blocks/invoice-list.model.ts (1)
packages/blocks/invoice-list/src/api-harmonization/invoice-list.model.ts (1)
  • Invoice (35-56)
packages/blocks/invoice-list/src/frontend/InvoiceList.client.tsx (1)
packages/ui/src/components/DataView/DataView.tsx (1)
  • DataView (9-24)
packages/framework/src/modules/cms/models/blocks/notification-list.model.ts (1)
packages/blocks/notification-list/src/api-harmonization/notification-list.model.ts (1)
  • Notification (31-49)
packages/ui/src/components/DataView/DataView.stories.tsx (2)
packages/ui/src/components/DataView/DataView.tsx (1)
  • DataView (9-24)
packages/ui/src/components/DataView/DataView.types.ts (1)
  • DataViewProps (3-19)
packages/blocks/notification-list/src/frontend/NotificationList.client.tsx (3)
packages/blocks/notification-list/src/api-harmonization/notification-list.model.ts (1)
  • NotificationListBlock (7-29)
packages/framework/src/modules/cms/models/blocks/notification-list.model.ts (1)
  • NotificationListBlock (5-29)
packages/ui/src/components/DataView/DataView.tsx (1)
  • DataView (9-24)
packages/blocks/order-list/src/frontend/OrderList.client.tsx (3)
packages/blocks/order-list/src/api-harmonization/order-list.model.ts (1)
  • OrderListBlock (7-34)
packages/framework/src/modules/cms/models/blocks/order-list.model.ts (1)
  • OrderListBlock (5-31)
packages/ui/src/components/DataView/DataView.tsx (1)
  • DataView (9-24)
packages/ui/src/components/DataView/DataView.tsx (2)
packages/ui/src/components/DataView/DataView.types.ts (1)
  • DataViewProps (3-19)
packages/ui/src/components/DataGrid/DataGrid.tsx (1)
  • DataGrid (35-148)
🪛 Biome (2.1.2)
packages/blocks/ticket-list/src/frontend/TicketList.client.tsx

[error] 12-12: Do not shadow the global "DataView" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

packages/blocks/invoice-list/src/frontend/InvoiceList.client.tsx

[error] 13-13: Do not shadow the global "DataView" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

packages/ui/src/components/DataView/DataView.stories.tsx

[error] 5-5: Do not shadow the global "DataView" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

packages/blocks/notification-list/src/frontend/NotificationList.client.tsx

[error] 12-12: Do not shadow the global "DataView" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

packages/blocks/order-list/src/frontend/OrderList.client.tsx

[error] 10-10: Do not shadow the global "DataView" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

packages/ui/src/components/DataView/DataView.tsx

[error] 9-9: Do not shadow the global "DataView" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

⏰ 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: deploy-preview
🔇 Additional comments (26)
packages/integrations/mocked/src/modules/cms/mappers/blocks/cms.notification-list.mapper.ts (1)

118-122: LGTM! FilterViewModeToggle and cardHeaderSlots added consistently.

The addition of FilterViewModeToggle with default value 'list' and cardHeaderSlots configuration is properly structured. The same pattern is correctly applied across all locales (EN, DE, PL).

Also applies to: 135-140

packages/blocks/invoice-list/src/api-harmonization/invoice-list.model.ts (1)

27-32: LGTM! cardHeaderSlots property added correctly.

The optional cardHeaderSlots property is properly typed and maintains backward compatibility. The structure aligns with similar additions in other blocks (NotificationListBlock, OrderListBlock, TicketListBlock).

packages/blocks/notification-list/src/api-harmonization/notification-list.model.ts (1)

23-28: LGTM! cardHeaderSlots property matches the pattern.

The implementation is consistent with other block models and properly typed as an optional property.

packages/blocks/invoice-list/src/api-harmonization/invoice-list.mapper.ts (1)

31-31: LGTM! cardHeaderSlots mapping added.

The mapper correctly passes through cardHeaderSlots from the CMS source to the InvoiceListBlock model. This aligns with the mocked integration mapper that provides this data.

packages/blocks/ticket-list/src/frontend/TicketList.client.stories.tsx (1)

58-62: LGTM! Story demonstrates grid view mode.

The story correctly includes cardHeaderSlots configuration and FilterViewModeToggle. Note that this story defaults to 'grid' mode (line 219), which is appropriate for demonstrating the new tile view feature in Storybook.

Also applies to: 216-220

packages/integrations/mocked/src/modules/cms/mappers/blocks/cms.invoice-list.mapper.ts (1)

112-116: LGTM! Consistent implementation across all locales.

The FilterViewModeToggle and cardHeaderSlots are correctly added with appropriate defaults. The slot configuration (left: id, right: paymentStatus, bottom: paymentDueDate) is well-suited for invoice display.

Also applies to: 130-134

packages/blocks/order-list/src/api-harmonization/order-list.model.ts (1)

28-33: LGTM! cardHeaderSlots property follows the established pattern.

The implementation is consistent with other block models in this PR.

packages/framework/src/modules/cms/models/blocks/invoice-list.model.ts (1)

11-11: FilterViewModeToggle type correctly constrains viewMode values to 'list' | 'grid'.

The FilterViewModeToggle class (packages/framework/src/utils/models/filters.ts, lines 61-64) properly defines value?: 'list' | 'grid', which is consistent with the filters type extension in invoice-list.model.ts. Type safety is maintained across all usages in mappers and components.

packages/blocks/invoice-list/src/frontend/InvoiceList.client.tsx (3)

40-42: LGTM!

The initialViewMode extraction safely handles missing filters or FilterViewModeToggle items with appropriate optional chaining and a sensible default.


151-167: LGTM!

The filter wiring correctly implements controlled component pattern by injecting the current viewMode value and setViewMode callback into FilterViewModeToggle items.


176-182: LGTM!

DataView integration is clean and maintains backward compatibility by forwarding all necessary props. The component will correctly delegate to DataGrid or DataList based on the viewMode state.

packages/ui/src/components/DataView/DataView.tsx (1)

17-21: LGTM!

The conditional rendering correctly delegates to DataGrid or DataList, passing appropriate props to each. DataGrid-specific props (cardHeaderSlots, columnsCount) are properly isolated from DataList.

packages/framework/src/modules/cms/models/blocks/order-list.model.ts (2)

11-11: LGTM!

The filters type extension correctly adds optional viewMode support while maintaining backward compatibility. This aligns with the FilterViewModeToggle feature introduced across list blocks.


25-30: LGTM!

The cardHeaderSlots property correctly defines the slot configuration interface. The optional nature maintains backward compatibility, and the structure aligns with DataGrid's slot-based rendering approach.

packages/ui/src/components/DataView/DataView.types.ts (1)

1-19: LGTM!

The DataViewProps interface is well-designed, reusing existing DataList types for consistency and maintaining type safety through generics. The distinction between required (viewMode, data, columns) and optional props (cardHeaderSlots, columnsCount, actions) is appropriate.

packages/blocks/notification-list/src/api-harmonization/notification-list.mapper.ts (1)

28-28: LGTM!

The cardHeaderSlots property is correctly forwarded from the CMS configuration, extending the NotificationListBlock public shape consistently with other list blocks.

packages/blocks/order-list/src/api-harmonization/order-list.mapper.ts (1)

30-30: LGTM!

The cardHeaderSlots property correctly extends the OrderListBlock shape, maintaining consistency with the notification-list and other block mappers.

packages/integrations/mocked/src/modules/cms/mappers/blocks/cms.order-list.mapper.ts (2)

92-96: LGTM!

The FilterViewModeToggle additions are consistent across all three locales (EN, DE, PL), with appropriate __typename discriminator, id, and default value. This provides the necessary mock data for testing the view mode toggle feature.

Also applies to: 207-211, 322-326


111-115: LGTM!

The cardHeaderSlots configuration is consistent across all locales and maps to valid column IDs. The slot assignment (ID left, status right, date bottom) provides a logical card layout for grid view.

Also applies to: 226-230, 341-345

packages/blocks/order-list/src/frontend/OrderList.client.tsx (4)

42-44: LGTM!

The initialViewMode extraction follows the same safe pattern as in InvoiceList, with appropriate fallback handling.


118-118: LGTM!

The conditional cellClassName appropriately adjusts action column styling based on view mode. List mode gets constrained dimensions while grid mode remains flexible, which aligns with the different layout requirements.


154-170: LGTM!

The filter wiring correctly implements the controlled component pattern, binding FilterViewModeToggle to the local viewMode state.


179-185: LGTM!

DataView integration is complete with all necessary props, maintaining consistency with the InvoiceList implementation while adapting to OrderList-specific needs.

packages/blocks/ticket-list/src/frontend/TicketList.client.tsx (1)

97-111: Row action rendering looks correct and accessible

Wrapping LinkComponent with Button asChild and using a flex container for icon + label is a solid pattern here: it avoids nested interactive elements, keeps semantics as a link per row, and matches the design system usage.

packages/ui/src/components/DataGrid/DataGrid.tsx (1)

27-32: DataGrid structure and renderCell reuse look solid

The default row‑key extractor, slot column resolution (top/left/right/bottom), and exclusion of slotIds from bodyColumns read cleanly, and using cellContent == null ensures falsy but valid values (like 0 or '') still render. Reusing the shared renderCell for both header slots and body cells should keep DataGrid aligned with DataList behaviour.

Also applies to: 65-77, 84-143

packages/blocks/notification-list/src/frontend/NotificationList.client.tsx (1)

12-12: Based on my verification, I found critical issues with the review comment:

The review comment is incorrect and based on false premises

Key findings:

  1. No Biome warning exists: The biome.json configuration uses "recommended": true but does NOT include any rule for shadowing built-in globals (such as noShadow). Biome is not flagging the DataView import as a problem.

  2. False reference to TicketList pattern: The review claims "Same as in TicketList/DataView stories," but TicketList.client.tsx also uses the unaliased import at line 12: import { DataView } from '@o2s/ui/components/DataView'; — not the aliased version suggested.

  3. Inconsistency across codebase: All similar files (InvoiceList, OrderList, and the DataView stories) use the unaliased import without issue. There is no established pattern of aliasing.

  4. No actual problem: While shadowing global constructors can theoretically be a code smell, it is not flagged by this project's Biome configuration, and the component works correctly.

Likely an incorrect or invalid review comment.

Comment thread packages/blocks/ticket-list/src/frontend/TicketList.client.tsx
@vercel vercel Bot temporarily deployed to Preview – o2s-docs November 27, 2025 15:56 Inactive
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

🧹 Nitpick comments (6)
packages/ui/src/components/DataGrid/DataGrid.stories.tsx (1)

150-151: Consider a helper to reduce type casting verbosity.

All stories repeat the same type casting pattern from Ticket[] to Record<string, unknown>[] and column types. This is verbose but may be necessary for DataGrid's generic typing.

If refactoring, consider extracting a helper:

const toDataGridArgs = (
  data: Ticket[],
  columns: DataListColumnConfig<Ticket>[],
  overrides: Partial<DataGridProps<Record<string, unknown>>> = {}
): DataGridProps<Record<string, unknown>> => ({
  data: data as Record<string, unknown>[],
  columns: columns as DataListColumnConfig<Record<string, unknown>>[],
  ...overrides,
});

Then stories would be cleaner:

export const BasicGrid: Story = {
  args: toDataGridArgs(sampleTickets, ticketColumns, {
    cardHeaderSlots: {
      left: 'id',
      right: 'status',
      bottom: 'updatedAt',
    },
  }),
};

Also applies to: 162-163, 183-184, 209-210, 231-232, 238-239

packages/blocks/order-list/src/frontend/OrderList.client.tsx (1)

10-10: Consider the naming collision with the global DataView.

The imported DataView component shadows the built-in JavaScript DataView global (used for binary data manipulation). While unlikely to cause issues in this React component context, it violates the principle of not shadowing restricted names.

If this becomes a concern, the component could be renamed in the upstream UI library (e.g., DataViewComponent, GridListView) or imported with an alias:

-import { DataView } from '@o2s/ui/components/DataView';
+import { DataView as DataViewComponent } from '@o2s/ui/components/DataView';

Then update references throughout the file accordingly.

packages/blocks/invoice-list/src/frontend/InvoiceList.client.tsx (2)

13-13: Consider the naming conflict with global DataView (optional).

The imported DataView component shadows JavaScript's built-in DataView global (used for ArrayBuffer operations). While unlikely to cause issues in this React component, you might consider using a more specific name like DataViewComponent or ListDataView if you prefer to avoid the shadowing.

As per static analysis hints: "Do not shadow the global 'DataView' property."


151-167: The onChange signature and filter transformation are type-safe; memoization is optional.

The onChange signature on FilterViewModeToggle is (value: 'list' | 'grid') => void (line 65 in packages/framework/src/utils/models/filters.ts), which matches setViewMode exactly. The filter transformation at lines 151-167 is correct.

The suggested useMemo optimization is optional, as the current implementation functions correctly. Consider it only if profiling shows unnecessary re-renders or performance concerns arise.

packages/blocks/notification-list/src/frontend/NotificationList.client.tsx (2)

43-46: Inline initialViewMode with ?? to tighten types and simplify state init

Current code is fine at runtime, but || 'list' plus optional chaining can widen the type and fight stricter TS settings. Inlining into useState with ?? keeps the type to 'list' | 'grid' and removes the extra variable.

-    // Extract initial viewMode from filters if available
-    const initialViewMode =
-        component.filters?.items?.find((item) => item.__typename === 'FilterViewModeToggle')?.value || 'list';
-
-    const [viewMode, setViewMode] = useState<'list' | 'grid'>(initialViewMode);
+    // Extract initial viewMode from filters if available
+    const [viewMode, setViewMode] = useState<'list' | 'grid'>(
+        component.filters?.items?.find((item) => item.__typename === 'FilterViewModeToggle')?.value ?? 'list',
+    );

Also applies to: 49-49


144-160: Confirm UX: Reset currently does not reset viewMode

The FilterViewModeToggle wiring (value: viewMode, onChange: setViewMode) looks consistent, and view mode is intentionally purely client‑side. However, handleReset only resets backend filters/data, not viewMode, so pressing Reset will keep whatever view (list/grid) the user last chose.

If the product expectation is that Reset should also revert the view to the server‑defined default, you may want to call setViewMode('list') (or the derived initial value) in the reset path; otherwise this behavior is fine as‑is.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 43d0510 and 2b0eb19.

📒 Files selected for processing (4)
  • packages/blocks/invoice-list/src/frontend/InvoiceList.client.tsx (4 hunks)
  • packages/blocks/notification-list/src/frontend/NotificationList.client.tsx (4 hunks)
  • packages/blocks/order-list/src/frontend/OrderList.client.tsx (5 hunks)
  • packages/ui/src/components/DataGrid/DataGrid.stories.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/blocks/invoice-list/src/frontend/InvoiceList.client.tsx (1)
packages/ui/src/components/DataView/DataView.tsx (1)
  • DataView (9-24)
🪛 Biome (2.1.2)
packages/blocks/order-list/src/frontend/OrderList.client.tsx

[error] 10-10: Do not shadow the global "DataView" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

packages/blocks/invoice-list/src/frontend/InvoiceList.client.tsx

[error] 13-13: Do not shadow the global "DataView" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

packages/blocks/notification-list/src/frontend/NotificationList.client.tsx

[error] 12-12: Do not shadow the global "DataView" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

⏰ 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: deploy-preview
🔇 Additional comments (11)
packages/ui/src/components/DataGrid/DataGrid.stories.tsx (5)

1-21: LGTM!

The imports and Storybook meta configuration are well-structured. Good reuse of DataListColumnConfig from DataList, which aligns with the shared rendering strategy mentioned in the PR.


23-35: LGTM!

The Ticket type is well-structured with a consistent label/value pattern for all data fields, which provides flexibility for display and internal representation.


37-86: LGTM!

The sample data is comprehensive, realistic, and provides good variety for testing different scenarios. Status values align correctly with the badge variant function.


88-146: LGTM!

The column configuration is well-defined with appropriate types and a sensible badge variant mapping for status values. Column IDs correctly match the Ticket type properties.


249-249: The Button component does support the "tertiary" variant—this concern is unfounded.

The tertiary variant is properly defined in packages/ui/src/lib/utils.ts (line 19–20) as part of the baseVariant configuration. It includes complete styling with tertiary-specific colors and hover states. The usage at line 249 is valid and will not cause any runtime errors.

packages/blocks/order-list/src/frontend/OrderList.client.tsx (3)

42-45: LGTM! Initial view mode extraction is well-handled.

The logic safely extracts the view mode from filters with appropriate fallback to 'list'. The optional chaining prevents runtime errors if filters or items are undefined.


180-186: Perfect! I now have all the information needed to verify the review comment. The investigation confirms that:

  1. cardHeaderSlots is properly typed as optional in DataViewProps<T> interface with the ? modifier
  2. The component safely passes it through to DataGrid when in grid mode
  3. All nested properties are also optional, providing full flexibility
  4. Type safety is enforced by TypeScript at compile time

The original review comment's approval is valid and the concern about undefined handling is already addressed by the component's design.


LGTM! DataView integration is well-implemented with proper TypeScript support for optional props.

The migration from DataList to DataView is clean and maintains existing props while adding viewMode and cardHeaderSlots. The cardHeaderSlots prop is properly typed as optional in the DataViewProps interface, so DataView gracefully handles undefined values—no runtime issues expected.


155-171: Based on my verification, I can now provide the rewritten review comment:


Type safety is verified; persistence asymmetry is intentional.

The FilterViewModeToggle.onChange signature is (value: 'list' | 'grid') => void, which perfectly matches setViewMode. View mode changes update local state only (not persisted via handleFilter like other filters), but this is intentional: view mode is a client-side UI preference affecting layout only, not a server filter criterion. The initial value is sourced from the server-provided filters but subsequent changes remain local.

packages/blocks/invoice-list/src/frontend/InvoiceList.client.tsx (2)

39-42: I'll help you verify the type safety of the initialViewMode value extraction. Let me search for the type definitions and verify the concern.
<function_calls>

#!/bin/bash

Search for FilterViewModeToggle type definition

echo "=== Searching for FilterViewModeToggle type definition ==="
rg -n "FilterViewModeToggle" --type=ts --type=tsx -C5 | head -100

echo ""
echo "=== Searching for viewMode type definition ==="
rg -n "viewMode|ViewMode" --type=ts --type=tsx -C3 | head -100


</function_calls>


176-182: Great! Now I have confirmed the components exist. Let me examine the DataView implementation and how it handles keys:
<function_calls>

#!/bin/bash

Examine DataView component implementation

echo "=== DataView.tsx content ==="
wc -l packages/ui/src/components/DataView/DataView.tsx
echo ""
cat packages/ui/src/components/DataView/DataView.tsx


</function_calls>

Now let me check the DataList and DataGrid implementations:
<function_calls>

#!/bin/bash

Examine DataList component

echo "=== DataList.tsx content ==="
wc -l packages/ui/src/components/DataList/DataList.tsx
echo ""
head -100 packages/ui/src/components/DataList/DataList.tsx


</function_calls>

Let me check DataGrid as well:
<function_calls>

#!/bin/bash

Examine DataGrid component

echo "=== DataGrid.tsx content ==="
wc -l packages/ui/src/components/DataGrid/DataGrid.tsx
echo ""
head -100 packages/ui/src/components/DataGrid/DataGrid.tsx


</function_calls>

Let me check the types to understand the prop structure:
<function_calls>

#!/bin/bash

Check DataView types

echo "=== DataView.types.ts ==="
cat packages/ui/src/components/DataView/DataView.types.ts

echo -e "\n=== DataList.types.ts ==="
cat packages/ui/src/components/DataList/DataList.types.ts

echo -e "\n=== DataGrid.types.ts ==="
cat packages/ui/src/components/DataGrid/DataGrid.types.ts


</function_calls>

Now let me check the invoice data structure to see if it has an id field:
<function_calls>

#!/bin/bash

Check invoice model and data structure

echo "=== Looking at invoice model files ==="

echo "=== invoice-list.model.ts ==="
cat packages/framework/src/modules/cms/models/blocks/invoice-list.model.ts

echo -e "\n=== InvoiceList.types.ts ==="
cat packages/blocks/invoice-list/src/frontend/InvoiceList.types.ts


</function_calls>

Let me also check the mapper to understand the data structure:
<function_calls>

#!/bin/bash

Check invoice-list mapper

echo "=== invoice-list.mapper.ts ==="
cat packages/blocks/invoice-list/src/api-harmonization/invoice-list.mapper.ts


</function_calls>

Let me check the entire InvoiceList.client.tsx file to get full context:
<function_calls>

#!/bin/bash

Read the full InvoiceList.client.tsx file

echo "=== Full InvoiceList.client.tsx ==="
cat packages/blocks/invoice-list/src/frontend/InvoiceList.client.tsx


</function_calls>

Let me check how DataList renders items with keys:
<function_calls>

#!/bin/bash

Search for key rendering patterns in DataList and DataGrid

echo "=== Searching for key patterns in DataList ==="
grep -n "key=" packages/ui/src/components/DataList/DataList.tsx

echo -e "\n=== Full DataList.tsx ==="
cat packages/ui/src/components/DataList/DataList.tsx


</function_calls>

Now let me check DataGrid more thoroughly:
<function_calls>

#!/bin/bash

Full DataGrid.tsx

echo "=== Full DataGrid.tsx ==="
cat packages/ui/src/components/DataGrid/DataGrid.tsx


</function_calls>

Let me verify by checking the stories to see how these components are used:
<function_calls>

#!/bin/bash

Check DataView stories for usage patterns

echo "=== DataView.stories.tsx ==="
cat packages/ui/src/components/DataView/DataView.stories.tsx


</function_calls>

Perfect! Now I have all the information I need. Let me verify one more detail about the prop passing:
<function_calls>

#!/bin/bash

Check if getRowKey or similar is exposed in DataView

echo "=== Checking DataView prop forwarding to DataGrid ==="
grep -n "getRowKey" packages/ui/src/components/DataView/DataView.tsx

echo -e "\n=== Checking DataGrid getRowKey usage ==="
grep -n "getRowKey" packages/ui/src/components/DataGrid/DataGrid.tsx

echo -e "\n=== Checking DataList item.id usage ==="
grep -n "item.id" packages/ui/src/components/DataList/DataList.tsx


</function_calls>

Based on my comprehensive verification of the code, I can now provide the rewritten review comment:


Row keys are properly handled through the underlying DataList/DataGrid components.

The DataView integration is correct. Row key handling is already implemented:

  • DataList renders items with explicit key={item.id} when iterating over the data array
  • DataGrid uses a getRowKey prop to extract row identifiers
  • Invoice data includes the id field required for keying

The props passed to DataView (including viewMode, data, columns, actions, and cardHeaderSlots) are correctly forwarded to the appropriate underlying component (DataList or DataGrid), ensuring no React key warnings will occur.

packages/blocks/notification-list/src/frontend/NotificationList.client.tsx (1)

12-12: Rewrite: Review comment is incorrect—no Biome error exists and DataView global is not used in this file

The DataView import does not shadow any code in use. The global DataView Web API is never referenced in this file (no new DataView(), typeof DataView, or typed array operations), so shadowing has no practical impact. Biome's recommended rule set does not include a noShadow check by default, and no such rule is explicitly enabled in biome.json. The import is correct as-is; aliasing is unnecessary.

@vercel vercel Bot temporarily deployed to Preview – o2s-docs November 28, 2025 12:35 Inactive
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 (1)
packages/ui/src/components/DataGrid/DataGrid.tsx (1)

47-57: Dynamic Tailwind class names will be purged; use static breakpoint mappings or a safelist

md:${getGridColsClass(tabletCols)} and lg:${getGridColsClass(desktopCols)} generate Tailwind classes at runtime, so the JIT compiler won’t see them and the responsive grid layout may not work in production.

Consider either:

  • Building explicit breakpoint-prefixed mappings (e.g. 'md:grid-cols-2', 'lg:grid-cols-4') and selecting from those, or
  • Adding all needed md:grid-cols-{1..6} and lg:grid-cols-{1..6} classes to your Tailwind safelist.

For example, fully static mappings could look like:

-const GRID_COLS_MAP: Record<number, string> = {
-    1: 'grid-cols-1',
-    2: 'grid-cols-2',
-    3: 'grid-cols-3',
-    4: 'grid-cols-4',
-    5: 'grid-cols-5',
-    6: 'grid-cols-6',
-};
-
-const getGridColsClass = (cols: number): string => {
-    return GRID_COLS_MAP[cols] || 'grid-cols-1';
-};
+const GRID_COLS_MAP: Record<number, string> = {
+    1: 'grid-cols-1',
+    2: 'grid-cols-2',
+    3: 'grid-cols-3',
+    4: 'grid-cols-4',
+    5: 'grid-cols-5',
+    6: 'grid-cols-6',
+};
+
+const MD_GRID_COLS_MAP: Record<number, string> = {
+    1: 'md:grid-cols-1',
+    2: 'md:grid-cols-2',
+    3: 'md:grid-cols-3',
+    4: 'md:grid-cols-4',
+    5: 'md:grid-cols-5',
+    6: 'md:grid-cols-6',
+};
+
+const LG_GRID_COLS_MAP: Record<number, string> = {
+    1: 'lg:grid-cols-1',
+    2: 'lg:grid-cols-2',
+    3: 'lg:grid-cols-3',
+    4: 'lg:grid-cols-4',
+    5: 'lg:grid-cols-5',
+    6: 'lg:grid-cols-6',
+};
+
+const getGridColsClass = (cols: number): string => GRID_COLS_MAP[cols] || 'grid-cols-1';
+const getMdGridColsClass = (cols: number): string => MD_GRID_COLS_MAP[cols] || 'md:grid-cols-2';
+const getLgGridColsClass = (cols: number): string => LG_GRID_COLS_MAP[cols] || 'lg:grid-cols-3';
@@
-    const gridClasses = cn(
-        'grid gap-4',
-        getGridColsClass(mobileCols),
-        `md:${getGridColsClass(tabletCols)}`,
-        `lg:${getGridColsClass(desktopCols)}`,
-        className,
-    );
+    const gridClasses = cn(
+        'grid gap-4',
+        getGridColsClass(mobileCols),
+        getMdGridColsClass(tabletCols),
+        getLgGridColsClass(desktopCols),
+        className,
+    );

Alternatively, if you already maintain a Tailwind safelist, adding all md:grid-cols-* and lg:grid-cols-* variants there is another valid fix.

🧹 Nitpick comments (3)
packages/ui/src/components/DataGrid/DataGrid.tsx (1)

60-77: Header slot resolution and bodyColumns computation are correct; consider a small lookup optimization

The header slot mapping and bodyColumns exclusion logic look correct and are nicely computed once per render, not per row. If you see many columns or frequent renders, you could optionally prebuild a Map from id → column to avoid multiple linear find calls, but it’s not required for correctness here.

packages/blocks/order-list/src/frontend/OrderList.client.tsx (2)

10-10: Optional: Alias DataView import to satisfy Biome and avoid global name shadowing

Biome flags DataView as shadowing the global DataView; if you want a clean lint run, consider aliasing the import:

-import { DataView } from '@o2s/ui/components/DataView';
+import { DataView as DataViewComponent } from '@o2s/ui/components/DataView';

and update the usage accordingly:

-                                <DataView
+                                <DataViewComponent

This is purely stylistic; runtime behavior is unchanged.


158-174: FilterViewModeToggle wiring looks correct; clarify whether view mode is purely client-side

Mapping FilterViewModeToggle items to a controlled value (viewMode) plus onChange: setViewMode cleanly turns the toggle into local UI state, independent of the server filters object.

If the intention is that view mode is just a presentation preference, this is perfect. If instead you also want to persist the user’s choice via filters/query params (e.g. survive full reloads or deep-links), you may eventually want to also propagate viewMode into the filter values handled by handleFilter or whatever persistence mechanism you use elsewhere.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2b0eb19 and 39aa1a0.

📒 Files selected for processing (2)
  • packages/blocks/order-list/src/frontend/OrderList.client.tsx (6 hunks)
  • packages/ui/src/components/DataGrid/DataGrid.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
packages/ui/src/components/DataGrid/DataGrid.tsx (3)
packages/ui/src/components/DataGrid/DataGrid.types.ts (1)
  • DataGridProps (29-74)
packages/ui/src/lib/utils.ts (1)
  • cn (5-7)
packages/ui/src/lib/renderCell.tsx (1)
  • renderCell (16-100)
packages/blocks/order-list/src/frontend/OrderList.client.tsx (3)
packages/blocks/order-list/src/api-harmonization/order-list.model.ts (1)
  • OrderListBlock (7-34)
packages/framework/src/modules/cms/models/blocks/order-list.model.ts (1)
  • OrderListBlock (5-31)
packages/ui/src/components/DataView/DataView.tsx (1)
  • DataView (9-24)
🪛 Biome (2.1.2)
packages/blocks/order-list/src/frontend/OrderList.client.tsx

[error] 10-10: Do not shadow the global "DataView" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

⏰ 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: deploy-preview
🔇 Additional comments (6)
packages/ui/src/components/DataGrid/DataGrid.tsx (2)

14-25: Top-level GRID_COLS_MAP / getGridColsClass extraction looks good

Nice job pulling the mapping and helper out of the component; this keeps the render path lighter and avoids recomputing on prop changes.


78-149: Row rendering, header layout, and cell null-guard look solid

The Card-based row rendering is consistent and:

  • Uses getRowKey with a sensible id/index fallback.
  • Applies per-row class names via getRowClassName.
  • Renders header slots only when configured.
  • Shares renderCell for all regions.
  • Uses cellContent == null so falsy but valid values (0, '', false) are rendered while null/undefined are skipped.
  • Computes bodyColumns once, then reuses it for all rows.

This aligns well with the shared DataList/renderCell behavior.

packages/blocks/order-list/src/frontend/OrderList.client.tsx (4)

42-49: Initial viewMode derivation is defensive and keeps a sane default

Deriving initialViewMode from the FilterViewModeToggle and falling back to 'list' if it's missing or has an unexpected value is safe and keeps behavior predictable across missing/misconfigured filters. The separate viewMode state tied to that initial value is a clean pattern here.


119-123: Conditional cellClassName for actions column matches list vs grid layouts

Tightening the actions column width only in list mode ('py-0 w-[180px]') and leaving it unconstrained for grid (undefined) is a sensible layout distinction and should prevent card layouts from being constrained by table‑style widths.


183-189: DataView integration and cardHeaderSlots propagation are consistent

Switching from direct DataList usage to DataView with viewMode, columns, actions, and cardHeaderSlots passed through aligns with the shared pattern described in the PR. This should let the same configuration drive both list and grid layouts without duplicating logic.


80-82: Grid view does have navigation affordance via primary action button, but the id column UX difference merits attention

The concern is partially addressed: while the id column in grid mode renders as plain text (not a link), the component includes a primary action button in the CardFooter (with an ArrowRight icon) that links to order.detailsUrl for every card. However, the navigation pattern is less consistent between list and grid modes—list mode makes the id cell itself clickable, while grid mode relies solely on the footer action button.

The primary action button is visible and accessible in grid view, providing the necessary navigation path. Consider whether this consistency difference affects user discoverability or if additional affordances (e.g., clickable card header or card click handler) would improve the UX.

@vercel vercel Bot temporarily deployed to Preview – o2s-docs December 2, 2025 14:50 Inactive
@michnowak michnowak merged commit 1653b74 into main Dec 3, 2025
9 checks passed
@michnowak michnowak deleted the feature/tile-version-of-data-lists branch December 3, 2025 10:03
@coderabbitai coderabbitai Bot mentioned this pull request Dec 8, 2025
1 task
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