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>
- Add npm scripts for dev, migrate, seed, smoke
- Add key column to scrips table (unique short identifier)
- Register users and templates routes in server
- Set development: false in Bun.serve for production mode
- Add description and custom_fields to CreateTicketSchema
- Make owner_id nullable/optional for unassigned tickets
- Add migration for custom field key column
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Backend:
- POST /:id/comment endpoint accepting {body, internal?, creator_id?}
- internal=false → Correspond (public reply), internal=true → Comment
- Runs scrip engine on the new transaction so notifications fire
- CommentSchema zod validation
Frontend:
- sendComment() API function in lib/api.ts
- Send button wired with onClick, sending spinner, disabled state
- Error display below reply box, clears on new typing
- Refreshes transaction list after successful send
- Reply/Internal note mode passed as internal flag
- Remove three-column layout, inline detail panel, and properties sidebar
- Click a ticket navigates to /tickets/[id] via router.push
- Redesign TicketRow as inbox-style: status dot, bold subject on top line,
muted ID/queue/owner meta on second line, time right-aligned
- Cleaner visual hierarchy with increased padding and gap
Backend returns PascalCase (Create, StatusChange, SetOwner, Comment, Correspond).
Frontend was checking lowercase, causing transaction rendering to fall through to raw type strings.
- layout.tsx: ThemeProvider from next-themes, light mode DEFAULT, JetBrains_Mono font
- globals.css: font-mono pointing to correct variable, font-feature-settings cv01+ss03 on body
- next-themes package installed
- Build passes with zero errors