Conversation
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>
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>
|
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>
ManukMinasyan
left a comment
There was a problem hiding this comment.
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 correctnessgetBatchedSwimlaneRecordCounts()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-widthcalc getViewDataWithSwimlanes()is 60+ lines -- could extract smaller methodsgetBoardRecordsForCell()duplicates query-building logic fromgetBoardRecords()
Overall the architecture is strong, but this needs tests, performance validation, and retargeting to 4.x before it can be merged.
… cell getViewDataWithSwimlanes() batches the cell counts and then throws that work away: the loop below reads $cellCount and calls getBoardRecordsForCell() on the very next line regardless, once per column x swimlane. On a board with 39 columns and 4 swimlanes that is 156 queries to render 31 cards. 150 of them ask for the contents of a cell the loop has already been told is empty. The cost per query is not uniform either. A board query built over a derived table -- the usual shape when the grouping values are computed rather than stored -- exposes its column and swimlane keys as aliases, so the per-cell WHERE cannot use an index and every cell re-runs the full projection. One query now fetches the filtered set and groups it by column|swimlane in memory. The per-cell limit, including the load-more override in $columnCardLimits, is applied to the group rather than in SQL, so counts and pagination behave as before. Ordering mirrors what getBoardRecordsForCell() applied so the grouped result keeps the same order. getBoardRecordsForCell() is untouched and still serves the load-more path. Measured on a 39x4 board rendering 31 cards, five runs each, medians: queries 210 -> 50 database 653ms -> 22ms server time 1984ms -> 1040ms Output verified byte-identical after normalising the per-request tokens Livewire stamps on every response, in both the all-columns and filtered states. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015Vj99aoZp7rQPd7HFzG2DJ
A board whose columns, query or swimlanes derive from $tableFilters or $tableSearch rendered one round trip stale. Toggling a filter that adds or removes columns returned the previous column set, and it stayed wrong until something unrelated triggered another render -- a poll, or any other action. bootedInteractsWithBoard() builds the board, and Livewire runs booted() before it applies the incoming property update. So board() reads the filter state from the request before the one being handled. Observed by intercepting the toggle's own livewire/update response: switching a filter from 3 columns to 39 returned a payload containing 3 column headers. booted() still builds it, because cacheBoardActions() has to run before Filament processes the request. The new hook rebuilds once the new state is in place. Scoped to table state on purpose. An unguarded updated hook also fires for $columnCardLimits, which load-more-on-scroll writes to, and would throw away the pagination it had just set. Verified on a board that swings between 3 and 39 columns: four consecutive toggles, both directions, each returning the correct column set in its own response. Request cost unchanged at 50-51 queries. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015Vj99aoZp7rQPd7HFzG2DJ
# Conflicts: # resources/dist/flowforge.js # resources/js/flowforge.js
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:
Data layer:
Views:
Features: