Skip to content

Add table view for the conversation list (smaller-screen friendly)#287

Open
mageaustralia wants to merge 3 commits intoabhinavxd:mainfrom
mageaustralia:feat/table-view
Open

Add table view for the conversation list (smaller-screen friendly)#287
mageaustralia wants to merge 3 commits intoabhinavxd:mainfrom
mageaustralia:feat/table-view

Conversation

@mageaustralia
Copy link
Copy Markdown

@mageaustralia mageaustralia commented Apr 20, 2026

This branch is stacked on top of feat/bulk-actions (#286), so the diff currently shows both commits. If you merge #286 first I'll rebase this onto main and it'll shrink to just the table view changes. Or you can land them together as-is.

Picks up Peter2121's ask on #259 ("It would be nice to see Table view layout (like FreshDesk) integrated in mainstream").

What

A card/table view-mode switcher in the filter row of the conversation list. Card view is unchanged. Table view shows columns: Contact, Subject (with reference number and unread count badge), Status, Priority, Updated. Per-row checkboxes integrate with the bulk-action toolbar from #286. Click a row to open; click the checkbox cell to toggle selection (shift-click for range).

Columns are resizable: drag the right edge of any header (pointer events, so works for mouse, touch, and pen). Double-click resets that column. Widths persist per-browser via useStorage. View mode also persists and syncs across tabs.

Layout change (deliberate, bundled)

Worth flagging up front: the existing ResizablePanelGroup caps the conversation list at 45% width. A 6-column table at that width is unusable on a typical laptop. So when viewMode === 'table', InboxLayout.vue switches to a single-pane layout: full-width list when no conversation is open, full-width detail with a "Back to list" button when one is. Card mode is untouched.

I did consider splitting this into two PRs (table view inside the existing capped panel + layout-mode change separately), but the table feature is barely usable without the layout fix and reverting the two together is cleaner than reverting one without the other. Happy to split if you'd prefer.

i18n

10 new keys under conversation.list.*. All in alphabetical order in i18n/en.json. Crowdin will pick them up on merge.

Things I considered but left out

These each need their own PR and are not in scope here:

  • Inline assign-agent / assign-team dropdowns per row (would need assigned_user_name / assigned_team_name exposed on the conversation list response)
  • Status colored badges (would need a color field on the statuses schema)
  • Subject hover preview tooltip
  • Presence viewer indicator
  • Keyboard a11y on resize handles (currently mouse/touch/pen only)
  • A POST /api/v1/conversations/bulk endpoint (mentioned in Add bulk actions on conversations (assign, status, priority) #286)

Implementation notes

  • Uses the shared-ui Table* primitives (TableHeader, TableBody, TableRow, TableHead, TableCell) for consistency with DataTable.vue. The shared Table wrapper itself isn't used because its built-in overflow-auto would create a nested scroll context; horizontal scroll lives on the parent content div instead, which puts the scrollbar at the viewport edge rather than mid-page.
  • useViewMode and columnWidths use useStorage from @vueuse/core to match the rest of the persisted UI state in this codebase. localStorage keys are conversationViewMode and conversationTableColumnWidths.
  • The route-building logic for opening a conversation is now in useConversationRoute. ConversationListItem was using an inline copy of the same logic; both now go through the composable.

Testing

Tested manually against a cloned production dataset (~5k conversations, ~80k contacts). Verified:

  • Switcher persists across reload and across tabs
  • Column resize works via mouse and touchpad; double-click resets
  • Click row to open, checkbox to select, shift-click for range
  • Bulk action toolbar from Add bulk actions on conversations (assign, status, priority) #286 works identically in table mode
  • Switching mode mid-conversation behaves correctly (split-pane resumes / single-pane resumes without losing the open conversation)
  • Loading skeleton appears in both modes

Per-row checkboxes on the conversation list. When at least one
conversation is selected, the filters bar swaps for a small toolbar with
three dropdowns: Assign (agent or team), Status, Priority. Shift-click
selects a range. The X on the right clears.

Frontend only. The bulk operations call the existing per-conversation
API endpoints (updateAssignee, updateConversationStatus,
updateConversationPriority) in parallel via Promise.allSettled. No new
backend handler.

Each dropdown is gated on the corresponding existing permission
(conversations:update_user_assignee, _team_assignee, _status,
_priority). Toolbar strings are localized via 8 new keys under
conversation.bulkActions.* in i18n/en.json.
Adds a card/table view switcher to the conversation list. Table view
shows Contact, Subject (with reference number and unread badge), Status,
Priority, Updated. Columns are resizable via pointer events and the
widths persist per browser via useStorage. View mode also persists and
syncs across tabs.

Layout: when table mode is active the resizable split-pane gives way to
a single-pane layout (full-width list, full-width detail with a Back
button) so the table has room to breathe on small screens. Card mode
keeps the existing split layout untouched.

Other small changes:
- Extracts the per-conversation route building into useConversationRoute,
  used by both ConversationListItem and the new ConversationTableView.
- Uses shared-ui Table primitives, Button, useStorage, pointer events.
When the user clicks a sidebar inbox link (My Inbox, Team Inbox, View)
while a conversation is open, the existing behavior reuses the
conversation so the right pane stays populated. That works in card mode
because the resizable split keeps the list panel visible. In table mode
the layout is single-pane (list OR detail), so retaining the
conversation hides the list the user just clicked toward.

Card mode behavior is unchanged. Only table mode falls back to the bare
inbox/team/view route.
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.

1 participant