- Pass teams list to TransactionCard
- Resolve team name from tx.new_value (team_id)
- Shows 'Team -> Support' instead of just 'Team changed'
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Added SetTeam to isSystem check
- Shows 'Team changed' in the timeline instead of raw 'SetTeam' text
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- System events now render as subtle timeline entries (icon dot + text)
instead of heavy bordered boxes
- Message cards are cleaner: rounded avatars, no card borders,
just typography and spacing. Internal badge is subtler.
- Removed border/shadow from message cards — cleaner, more modern
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Colored status dot per row for instant status recognition
- Checkbox appears on hover (group-hover) for batch selection
- No side panel — full width, clean list
- Click row → navigate to ticket detail
- Removed hover status change dots per user feedback
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stripped back to clean table before redesign. Removed selectedId tracking,
transactions fetching, and all side-panel related code. Row click now
navigates directly to ticket detail.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Colored status dot with glow ring in header (at-a-glance)
- Status change via colored dots (click circles, not text pills)
- Subject + ID + queue + owner in one compact line
- Open full view is now a subtle chevron button, not a giant CTA
- Take it button appears inline when unassigned
- Activity feed as a timeline with connecting line and dots
- Shorter transaction labels with inline values
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Store button position in state on click, then pass to the portal
popover via style. Eliminates getElementById race condition where
the portal hadn't rendered yet when trying to set DOM styles.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- table-layout: fixed with minWidth instead of width:100% to avoid
browser recalculating explicit column widths
- Wider resize handles (w-3, 12px) for easier grabbing
- Better visibility on hover
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- table-layout: fixed on the table wrapper so browser respects explicit widths
- All cells (header + rows) now use the same col.width consistently
- Subject column no longer special-cased for width
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Left column gets wider, right column gets narrower by same amount.
Subject column now has fixed width (not flexible) so the table
doesn't redistribute space unpredictably.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Handle on the left edge resizes the PREVIOUS column, not itself.
This matches the mental model: dragging the boundary between Subject
and Status changes Subject's width. First column has no handle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Handles now sit on the left edge (the boundary between columns).
Dragging feels natural — you pull the dividing line between two columns.
Wider hit area (w-2) for easier grabbing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replaced flex containers with display: table/table-row/table-cell.
This guarantees column widths are shared between header and all rows,
fixing the misalignment. Subject column gets remaining width, all
others use fixed pixel widths. Resize handles on header cells still work.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Column header was missing the w-9 spacer that ticket rows have
for the batch checkbox. Added spacer so columns align properly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Checkbox column on every ticket row
- Select multiple tickets via checkboxes
- Floating sticky action bar at bottom when tickets selected:
- Shows count: "3 selected" with Clear button
- Quick status change buttons
- Assign to me button
- Checkbox click stops propagation (doesn't select ticket for triage panel)
- Batch operations run sequentially via API
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Column config saves to localStorage on every change
- Load from localStorage on mount (survive reloads without saved view)
- Custom fields appear as column options in picker
- Custom field values render in ticket rows
- Backend now always includes custom_fields in GET /tickets response
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Column picker dropdown (grid icon next to sort/density)
- Check/uncheck columns: ID, Subject, Status, Queue, Owner, Created, Updated
- Subject column auto-expands (flex), others have fixed width
- Column resize handles: drag right edge of any column header
- Min 50px, max 800px, body gets select-none during drag
- Columns persist with saved views (columns jsonb field)
- Reset to defaults when navigating away from a saved view
- Sticky column header row with muted background
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Fix sidebar All tickets link: /?view=all instead of / (avoids dashboard redirect)
- Replace useless side panel with triage command center:
- Quick status change buttons (click to transition inline)
- Assign to me button (appears when unassigned)
- Mini activity feed showing last 8 transactions with type labels,
status badges, old→new values, and comment previews
- Relative timestamps
- Open full view button
- Fetches ticket transactions on selection
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- team_id on queues table (optional, can be overridden per-ticket)
- Ticket creation auto-sets team_id from the queue's default
- Queue admin form has team selector (scrip flow node 03)
- Queue API (POST/PATCH) accepts team_id
No enforcement — just a helpful default. Teams and queues
are loosely coupled, not hierarchically locked.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Backend:
- team_id column on tickets table
- team_id filter in GET /tickets (resolves team members)
- team_id in UpdateTicketSchema + PATCH handler
- SetTeam transaction type
Frontend:
- Team selector in ticket detail properties sidebar
- My team's tickets in sidebar (when user belongs to a team)
- team_id passed through to API from ticket list page
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Grip handle now uses mousedown/mousemove/mouseup (same as resize)
- Widget position updates in real-time as you drag — no ghost image
- Grid snapping from actual mouse coordinates
- Overlap resolution on mouseup
- Cleaner: no draggable attribute, no dataTransfer
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Widgets can now be dragged anywhere on the grid, not just swapped.
Drop position is calculated from mouse coordinates relative to the grid,
snapped to grid units. Overlapping widgets are pushed down automatically.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The onUp handler was capturing stale widgets from the render closure,
overwriting the resize dimensions. Now uses setWidgets(current => ...)
to read latest state and apply overlap resolution correctly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When resizing a widget, any widget that gets overlapped is automatically
pushed down to clear the collision. Multi-pass overlap detection ensures
cascading widgets are all resolved. Positions persisted to API.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Edit/Done toggle button in header (pencil icon)
- Widget delete buttons only visible in edit mode
- Add widget button only visible in edit mode
- Empty state prompts to enter edit mode instead of adding directly
- Default is view mode — clean, no accidental deletes
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Dropdown to assign/unassign dashboard to a team
- Updates immediately via PATCH
- createDashboard and updateDashboard API now accept team_id
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Schema:
- teams table (name unique, description)
- team_members table (team_id, user_id, unique constraint)
- team_id column on dashboards
API:
- GET/POST/PATCH/DELETE /teams
- POST /teams/:id/members (add user)
- DELETE /teams/:id/members/:userId (remove user)
- dashboards support team_id on create/update
Frontend:
- Teams tab in admin: CRUD + member management with add/remove
- Sidebar: dashboards filtered to user's teams
(unassigned dashboards visible to all)
- Compact dashboard picker dropdown in sidebar
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Single-line select dropdown instead of one list item per dashboard
- Scales to any number of teams without clutter
- "+ New dashboard" as last option in dropdown
- Preserves the create flow with inline name input
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Popover now renders via createPortal into document.body with z-index 9999
- This avoids the header backdrop-blur stacking context trapping it
- Add + button in Dashboards sidebar section to create dashboards
- Inline input on Dashboards section header, Enter to create/Escape to cancel
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add demo dashboard with 7 widgets to seed script
- Dashboard is_default=true — appears as home page on fresh seed
- Add views/dashboards/dashboardWidgets to seed reset
- Fix My tickets: now filters by first non-system user (not any owner)
- Pass owner param in sidebar My tickets link
- Update page.tsx view=my to respect owner URL param
- Scrip engine already sorts by sort_order (verified)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Move ticket filtering from in-memory to SQL WHERE clauses
(queue_id, status, owner use Drizzle eq/isNull; text search uses ilike;
custom field filters use EXISTS subqueries)
- Add limit param to GET /tickets
- Add POST/PATCH/DELETE /users routes
- Add Users tab to admin page with create/edit/delete
- Smart widget positioning in dashboard (3-column grid fill)
- Show pattern hint below CF inputs in New Ticket dialog
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- DELETE /templates/:id — backend route
- deleteTemplate() API client function
- Trash icon on each template list item (shows on hover)
- Confirms inline, no dialog needed
- Resets builder if the deleted template was being edited
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Fixed popover z-index: uses fixed positioning with z-50 above backdrop
- Stepped flow: select field → set operator (is/is_not) → choose/write value → Apply
- Removed old inline CF value inputs (handled inline in the new flow)
- Fixed filter persistence: clear filters when navigating away from saved view
- Fixed home redirect: check for default dashboard on load
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Show CF values as read-only text with edit affordance (pencil icon on hover)
- Click to enter edit mode: inline input (free-text) or select (choice fields)
- Save on blur or Enter, cancel on Escape — reverts to original value
- Auto-save for select fields on change
- Loading spinner while saving
- Remove now-unused customFieldValue helper
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- POST /tickets now returns { ticket, scrip_results } matching PATCH pattern
- createTicket API function returns UpdateResult instead of Ticket
- Update call site to use data.ticket.id
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- src/db/seed.ts: comprehensive seed data with idempotent upserts
- 5 users (system, gjermund, operator, technician, analyst)
- 5 queues (Support Desk, Operations, IT Infrastructure, Facilities, Field Ops)
- 1 lifecycle (Demo service lifecycle with new→open→in_progress→resolved→closed)
- 5 custom fields (impact, location, channel, urgency, outcome) with short keys
- 10 realistic support tickets with varied statuses, custom fields, and history
- 3 scrips (OnCreate email, OnResolve custom field, customer notification)
- 2 templates (auto-response, resolve notification)
- --reset flag to truncate all data before seeding
- scripts/smoke-test.ts: API smoke tests
- scripts/watch-frontend.sh: frontend dev helper
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New routes:
- GET /users — list all users
- GET/POST /templates — list and create templates
- PATCH /templates/:id — update template
- POST /templates/preview — render template with ticket/demo context
Enhanced routes:
- tickets: custom field support on create, status classification helper
- custom-fields: PATCH endpoint, auto-generate short key from name
- lifecycles: PATCH endpoint
- queues: PATCH endpoint
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- SendEmail: real nodemailer transport with SMTP config, dynamic recipient resolution
(static + ticket creator/owner lookup), Handlebars template support
- Webhook: HTTP POST/any method with configurable headers and JSON body
- FetchMetadata: external HTTP fetch, Handlebars URL/body templating,
auto-adds result as comment/correspondence on ticket
- RunScript: arbitrary async JS execution with helpers (addComment,
createTransaction, updateTicket, touchTicket), ticket context, and
Drizzle ORM access
- SetCustomField: lookup by id/key/name, clear+insert value, record
CustomFieldChange transaction
- CreateTransaction: insert arbitrary transaction record
- Add OnCustomFieldChange condition
- Pass condition_config to evaluator in engine
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>