Skip to content

Add swimlane support for 2D kanban boards (columns × rows)#81

Draft
optmsp wants to merge 6 commits intorelaticle:3.xfrom
giantrocketship:3.x
Draft

Add swimlane support for 2D kanban boards (columns × rows)#81
optmsp wants to merge 6 commits intorelaticle:3.xfrom
giantrocketship:3.x

Conversation

@optmsp
Copy link
Copy Markdown

@optmsp optmsp commented Feb 1, 2026

Introduce optional swimlane grouping that creates a grid layout with columns as the primary axis and swimlanes as secondary row grouping. Boards without swimlanes configured continue to work identically.

New classes:

  • Swimlane: ViewComponent mirroring Column (name, label, color, icon)
  • HasBoardSwimlanes: Trait for swimlane configuration on Board

Data layer:

  • getBoardRecordsForCell(): query records by column × swimlane
  • getBatchedSwimlaneRecordCounts(): GROUP BY column, swimlane counts
  • Board::getViewData() branches into flat vs 2D swimlane structure

Views:

  • swimlane-board.blade.php: HTML table with sticky headers/labels, inline x-data for collapse state (avoids x-load timing issues)
  • swimlane-cell.blade.php: per-cell card list with independent scroll
  • index.blade.php: conditional flat vs swimlane rendering

Features:

  • Collapsible swimlane rows with localStorage persistence
  • Per-cell pagination via composite "columnId|swimlaneId" keys
  • Horizontal-only drag via per-swimlane sortable groups
  • Uncategorized swimlane for records with no matching lane value
  • Filament theme-compatible styling (bg-white/dark:bg-gray-900)

optmsp and others added 4 commits January 31, 2026 22:39
Introduce optional swimlane grouping that creates a grid layout with
columns as the primary axis and swimlanes as secondary row grouping.
Boards without swimlanes configured continue to work identically.

New classes:
- Swimlane: ViewComponent mirroring Column (name, label, color, icon)
- HasBoardSwimlanes: Trait for swimlane configuration on Board

Data layer:
- getBoardRecordsForCell(): query records by column × swimlane
- getBatchedSwimlaneRecordCounts(): GROUP BY column, swimlane counts
- Board::getViewData() branches into flat vs 2D swimlane structure

Views:
- swimlane-board.blade.php: HTML table with sticky headers/labels,
  inline x-data for collapse state (avoids x-load timing issues)
- swimlane-cell.blade.php: per-cell card list with independent scroll
- index.blade.php: conditional flat vs swimlane rendering

Features:
- Collapsible swimlane rows with localStorage persistence
- Per-cell pagination via composite "columnId|swimlaneId" keys
- Horizontal-only drag via per-swimlane sortable groups
- Uncategorized swimlane for records with no matching lane value
- Filament theme-compatible styling (bg-white/dark:bg-gray-900)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove max-height and overflow-auto from swimlane-board container,
  keep only overflow-x-auto for wide boards
- Remove max-height and overflow-y-auto from swimlane cells so cards
  expand to natural height
- Remove overflow-hidden wrapper from swimlane board in index view
  so content is not clipped to viewport height

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use inline vertical-align: top on td elements instead of Tailwind
  align-top class (not compiled from vendor blade by JIT)
- Use inline padding on swimlane cells instead of Tailwind p-2
- Remove dashed placeholder boxes from empty cells
- Remove min-height constraint from cells

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Reduce card margin (mb-3 → mb-2), title size (text-sm → text-xs),
  and padding (p-3 → px-3 pt-2 / px-3 pb-2)
- Remove bottom margin from header row
- Wrap schema output in .flowforge-card-schema div
- Add scoped CSS to collapse Filament's default gap-6 between schema
  entries and gap-y-2 within entry wrappers to zero

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@optmsp optmsp marked this pull request as draft February 1, 2026 05:45
Columns expand to fill available space (w-full table, min-width 300px)
while cards are capped at max-w-[300px] for consistent sizing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@optmsp
Copy link
Copy Markdown
Author

optmsp commented Feb 1, 2026

I need to remove my customization of cards, etc., and make that user-defined before this PR is good.

New cardHeaderSchema() and cardFooterSchema() methods mirror the
existing cardSchema() pattern. Header renders above the title,
footer renders below body with a border separator. Both are optional
and only render when configured.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@ManukMinasyan ManukMinasyan left a comment

Choose a reason for hiding this comment

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

This is a well-architected feature with excellent pattern consistency (Swimlane mirrors Column). The backward compatibility is solid -- boards without swimlanes work identically. However, several issues need attention before merge:

Critical Issues

1. Zero Test Coverage

No tests added for +838 lines of new functionality. Need tests for:

  • getBoardRecordsForCell() query correctness
  • getBatchedSwimlaneRecordCounts() GROUP BY logic
  • Uncategorized record handling
  • Cell pagination ("load more" per cell)
  • Board rendering with swimlanes enabled

2. Wrong Target Branch

This targets 3.x (maintenance-only) but should target 4.x (active development with Filament 5). Merging to 3.x creates version confusion.

3. Cross-Swimlane Drag Not Supported

moveCard() only receives column ID, not swimlane ID. Cards cannot be moved between swimlanes. If swimlanes are read-only grouping, this should be documented. If mutable, moveCard() needs a swimlane parameter.

Performance Concerns

4. N+1 Query Risk

getBoardRecordsForCell() is called per column x swimlane intersection. With 5 columns x 5 swimlanes = 25 separate queries on page load. Consider batching or eager loading.

5. Unoptimized Lookups

in_array($laneId, $definedSwimlaneIds, true) inside nested loops -- use array_flip() for O(1) lookup.

Moderate Issues

6. localStorage Key Collision

window.location.pathname means two different boards on the same URL path share collapse state. Should include a board-specific identifier.

7. Missing Accessibility

Collapse buttons lack aria-expanded and aria-controls attributes. No keyboard navigation for collapse/expand.

8. Trait Inconsistency

Swimlane lacks translateLabel() that Column has. Icon-related traits (HasIconColor, HasIconPosition) present in Column are missing from Swimlane.

Minor Issues

  • Hardcoded 300px column width in min-width calc
  • getViewDataWithSwimlanes() is 60+ lines -- could extract smaller methods
  • getBoardRecordsForCell() duplicates query-building logic from getBoardRecords()

Overall the architecture is strong, but this needs tests, performance validation, and retargeting to 4.x before it can be merged.

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