Skip to main content

AiQlick Support/Help Desk System — Complete Documentation

Status: Frontend fully implemented. Backend implementation required. Date: March 2026 Backend specification: See DOCS/SUPPORT_SYSTEM.md for detailed backend-only spec (database DDL, resolvers, etc.)


Table of Contents

  1. System Overview
  2. Architecture Diagram
  3. User Flows (Visual)
  4. Access Control & Roles
  5. Frontend Implementation — Complete File Map
  6. Frontend Components Detail
  7. GraphQL API Contract
  8. Ticket Lifecycle
  9. FAQ System Flow
  10. Backend Implementation Steps
  11. Modified Existing Files
  12. Checklist — What's Done vs What's Needed

GraphQL Type Convention: The backend uses Output-suffixed type names (e.g., SupportTicketOutput, TicketMessageOutput). Frontend fragments target these types. See Section 7 for details.


1. System Overview

The support system replaces the old "Need Help?" mailto link with a full ticket-based help desk. It has three tiers:

TierAudienceRoutePurpose
User-FacingAll authenticated usersSidebar drawer + /supportSubmit tickets, view FAQs, track ticket status
Support PortalSupport agents + super admins/support-portalManage ticket queue, reply, internal notes, FAQ management
Admin PanelSuper admins only/admin/supportOverview dashboard, agent management, global audit trail

Key Design Principles

  1. Agent identity masking — Users always see "AiQlick Support" as the replier, never individual agent names. The backend returns real sender data; the frontend masks it in user-facing views.
  2. Internal notes — Support agents can add internal notes visible only to the support team, never shown to users.
  3. Full audit trail — Every action (status change, assignment, message, note) is logged with actor identity and timestamp.
  4. FAQ from resolved tickets — When support resolves/closes a ticket, they're prompted to document it as a FAQ entry, optionally using AI to auto-generate the Q&A.
  5. Graceful error handling — All hooks handle GraphQL errors gracefully, showing empty states when the backend isn't ready.

2. Architecture Diagram

┌─────────────────────────────────────────────────────────────────────┐
│ FRONTEND (Next.js 15) │
│ │
│ ┌──────────────┐ ┌──────────────────┐ ┌───────────────────────┐ │
│ │ User-Facing │ │ Support Portal │ │ Admin Panel │ │
│ │ │ │ │ │ │ │
│ │ SupportDrawer│ │ /support-portal │ │ /admin/support │ │
│ │ /support │ │ ┌──────────────┐ │ │ ┌─────────────────┐ │ │
│ │ │ │ │TicketQueue │ │ │ │AdminSupOverview │ │ │
│ │ ┌──────────┐│ │ │TicketDetail │ │ │ │AdminSupAgents │ │ │
│ │ │FAQ Tab ││ │ │SupportDash │ │ │ │Global AuditLog │ │ │
│ │ │Ticket Tab││ │ │FAQ Manager │ │ │ └─────────────────┘ │ │
│ │ │New Ticket││ │ │AuditLogView │ │ │ │ │
│ │ └──────────┘│ │ │ResolveFaqMdl │ │ │ │ │
│ └──────────────┘ │ └──────────────┘ │ └───────────────────────┘ │
│ └──────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ Shared Layer │ │
│ │ lib/hooks/useSupport.ts — All queries, mutations, hooks │ │
│ │ lib/types/support.ts — TypeScript interfaces & enums │ │
│ │ graphql/operations/support/ — Queries, mutations, subs │ │
│ │ graphql/operations/schema/support/ — Fragments │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │ │
│ Apollo Client (GraphQL) │
└──────────────────────────────┼───────────────────────────────────────┘

┌──────────▼──────────┐
│ BACKEND (Node.js) │
│ │
│ GraphQL API Server │
│ ┌────────────────┐ │
│ │ SupportTicket │ │
│ │ TicketMessage │ │
│ │ TicketAuditLog │ │
│ │ FaqEntry │ │
│ │ UserRole │ │
│ └────────────────┘ │
│ │
│ PostgreSQL Database │
└──────────────────────┘

3. User Flows (Visual)

3a. User Creates a Support Ticket

┌──────────────┐     ┌───────────────────┐     ┌─────────────────────┐
│ User clicks │ │ SupportDrawer │ │ "New Ticket" tab │
│ "Need help?" │────▶│ opens (modal) │────▶│ Category, Priority │
│ in sidebar │ │ FAQs tab shown │ │ Subject, Desc │
└──────────────┘ │ first │ └─────────┬───────────┘
└───────────────────┘ │

┌───────────────────┐ ┌─────────────────────┐
│ User can also │ │ createSupportTicket│
│ click "Open full │ │ mutation called │
│ Support Center" │ │ Status → OPEN │
│ → /support page │ │ Toast: "Created" │
└───────────────────┘ └─────────┬───────────┘


┌─────────────────────┐
│ Redirected to │
│ Conversation view │
│ (within drawer or │
│ /support page) │
└─────────────────────┘

3b. User Checks FAQs Before Creating Ticket

┌──────────────┐     ┌───────────────────┐     ┌─────────────────────┐
│ User clicks │ │ SupportDrawer │ │ FAQs tab │
│ "Need help?" │────▶│ opens │────▶│ Search bar │
└──────────────┘ │ FAQs tab first │ │ Category filters │
└───────────────────┘ │ Expandable cards │
└─────────┬───────────┘

┌──────────────┼──────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌──────────┐
│ FAQ answers │ │ "Was this │ │ "Still │
│ the question│ │ helpful?" │ │ need │
│ → User done │ │ thumbs up │ │ help?" │
└────────────┘ └────────────┘ └────┬─────┘


┌────────────┐
│ Switches to│
│ "New Ticket"│
│ tab │
└────────────┘

3c. Support Agent Handles a Ticket

┌──────────────────┐     ┌────────────────────┐     ┌──────────────────────┐
│ Agent opens │ │ Ticket Queue │ │ Ticket Detail View │
│ /support-portal │────▶│ Filter by status, │────▶│ ┌────────────────┐ │
│ (profile menu) │ │ priority, search │ │ │ Conversation │ │
└──────────────────┘ └────────────────────┘ │ │ tab │ │
│ ├────────────────┤ │
│ │ Audit Log tab │ │
│ └────────────────┘ │
│ │
│ Right sidebar: │
│ • Status selector │
│ • Priority selector │
│ • Category selector │
│ • Assign to agent │
│ • "Assign to Me" │
│ • Resolve / Close │
└──────────┬───────────┘

┌─────────────────────┤
▼ ▼
┌────────────┐ ┌──────────────────┐
│ Reply to │ │ Add Internal │
│ user │ │ Note (checkbox) │
│ (visible) │ │ (hidden from │
└────────────┘ │ user, amber bg) │
└──────────────────┘

3d. Ticket Resolution → FAQ Creation Flow

┌──────────────────┐     ┌────────────────────────────────────────────────┐
│ Agent clicks │ │ TicketResolveFaqModal opens │
│ "Resolve Ticket" │────▶│ │
│ or "Close Ticket"│ │ ┌──────────────────────────────────────────┐ │
└──────────────────┘ │ │ "Use AI to Summarize" button │ │
│ │ (calls generateFaqSuggestion mutation) │ │
│ │ Pre-fills question, answer, tags │ │
│ └──────────────────────────────────────────┘ │
│ │
│ Fields: │
│ • Question (pre-filled from ticket subject) │
│ • Answer (editable textarea) │
│ • Category (auto-set from ticket category) │
│ • Tags (Enter to add, click to remove) │
│ • Status: "Save as Draft" or "Publish" │
│ │
│ Actions: │
│ ┌─────────────────────┐ ┌───────────────┐ │
│ │ "Skip — Not FAQ- │ │ "Save FAQ" │ │
│ │ worthy" (resolves │ │ (creates FAQ │ │
│ │ ticket without FAQ) │ │ then resolves│ │
│ └─────────────────────┘ │ ticket) │ │
│ └───────────────┘ │
└────────────────────────────────────────────────┘


┌────────────────────────────────────────────────┐
│ Ticket status updated (RESOLVED or CLOSED) │
│ FAQ entry created (if not skipped) │
│ Audit log entry added │
│ Toast notification shown │
└────────────────────────────────────────────────┘

3e. Admin Overview & Agent Management

┌──────────────────┐     ┌────────────────────────────────────────────────┐
│ Super Admin │ │ /admin/support — 3 Tabs │
│ navigates to │────▶│ │
│ Admin > Support │ │ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
└──────────────────┘ │ │ Overview │ │ Tickets │ │ Agents │ │
│ │ │ │ │ │ │ │
│ │ Stat │ │ All │ │ List agents │ │
│ │ cards: │ │ tickets │ │ + "Add │ │
│ │ Open, │ │ with │ │ Agent" │ │
│ │ InProg, │ │ admin │ │ (user │ │
│ │ Waiting, │ │ actions │ │ search + │ │
│ │ Resolved │ │ │ │ assign │ │
│ │ │ │ │ │ role) │ │
│ │ Agent │ │ │ │ "Remove │ │
│ │ perf │ │ │ │ Agent" │ │
│ │ table │ │ │ │ (confirm) │ │
│ └──────────┘ └──────────┘ └──────────────┘ │
└────────────────────────────────────────────────┘

3f. Navigation Flow — Who Goes Where

┌────────────────────────────────────────────────────────────────────────────┐
│ Profile Dropdown Menu │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Admin Panel (super admin only → /admin) │ │
│ │ Support Portal (support agent + super admin → /support-portal│ │
│ │ ──────────────────────────────────────────── │ │
│ │ Team → /company/team/members │ │
│ │ Profile → /userprofile │ │
│ │ Settings → /userprofile?tab=account │ │
│ │ Developers Tools (super admin only → /devtools) │ │
│ │ Sign out │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Sidebar Footer: "Need help?" button │ │
│ │ → Opens SupportDrawer (modal) with FAQs, My Tickets, New Ticket │ │
│ │ → Footer link: "Open full Support Center" → /support │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────────────────┘

Access Matrix:
┌──────────────────────┬──────────────┬───────────────┬──────────────┐
│ Route │ Regular User │ Support Agent │ Super Admin │
├──────────────────────┼──────────────┼───────────────┼──────────────┤
│ SupportDrawer │ Yes │ Yes │ Yes │
│ /support │ Yes │ Yes │ Yes │
│ /support-portal │ No │ Yes │ Yes │
│ /admin/support │ No │ No │ Yes │
└──────────────────────┴──────────────┴───────────────┴──────────────┘

4. Access Control & Roles

New Role: isSupportAgent

A new boolean computed field on the User type. On the backend this should be determined by checking if the user has a "support" role in a global roles table (distinct from company-scoped roles).

Frontend changes made:

  • lib/types/user.ts — Added isSupportAgent?: boolean to User type
  • graphql/operations/user/queries.ts — Field commented out in WHO_AM_I query (waiting for backend)
  • contexts/UserAuthProvider.tsxisSupportAgent exposed in auth context, defaults to false until backend returns it

Backend needs to:

  1. Create a UserRole table (global, not company-scoped)
  2. Add a support role entry
  3. Add isSupportAgent computed field to the User GraphQL type
  4. Return isSupportAgent in the whoAmI query response

Route Guards

GuardFileLogic
SupportPortalGuardcomponents/support/layout/SupportPortalGuard.tsxuser.isSupportAgent || user.isSuperAdmin — redirects to / otherwise
AdminGuard (updated)components/admin/layout/AdminGuard.tsxAdded "support" to AdminSection type — super admin only

5. Frontend Implementation — Complete File Map

New Files Created

# Types & Data
lib/types/support.ts — All TypeScript interfaces, enums, display helpers
lib/hooks/useSupport.ts — All React hooks for support system

# GraphQL Operations
graphql/operations/schema/support/fragments.ts — 6 fragments (ticketUser, ticket, attachment, message, audit, faq)
graphql/operations/support/queries.ts — 11 queries (user, support, admin, faq)
graphql/operations/support/mutations.ts — 16 mutations (user, support, admin, faq, AI)
graphql/operations/support/subscriptions.ts — 2 subscriptions (user ticket update, support event)

# User-Facing Components
components/support/SupportDrawer.tsx — Modal with 3 tabs: FAQs, My Tickets, New Ticket
components/support/NewTicketForm.tsx — Ticket creation form
components/support/TicketList.tsx — Ticket list with loading skeleton
components/support/TicketConversation.tsx — Message thread with agent masking, context-aware alignment, initial description display
components/support/TicketStatusChip.tsx — Status badge (TWChip)
components/support/TicketPriorityChip.tsx — Priority badge (TWChip)
components/support/FaqCard.tsx — Expandable FAQ card with tags display and helpful vote
components/support/FaqList.tsx — Searchable FAQ list with debounced search (300ms), category filters

# Support Portal Components
components/support/layout/SupportPortalGuard.tsx — Route guard (isSupportAgent || isSuperAdmin)
components/support/layout/SupportPortalLayout.tsx — Flex layout with sidebar
components/support/layout/SupportPortalSidebar.tsx — Navigation: Dashboard, Tickets, FAQs
components/support/portal/SupportDashboard.tsx — Stats cards and metrics
components/support/portal/TicketQueue.tsx — Filterable ticket table with "My Tickets" support (assignedToMe query param)
components/support/portal/TicketDetailView.tsx — Full ticket view with conversation + audit
components/support/portal/TicketAuditLogView.tsx — Timeline of audit entries
components/support/portal/TicketResolveFaqModal.tsx — FAQ creation modal on resolve/close with AI

# Admin Components
components/admin/support/AdminSupportOverview.tsx — Overview stats + agent performance table
components/admin/support/AdminSupportAgents.tsx — Agent list with add/remove functionality

# Route Pages
app/(shared)/support/page.tsx — Full support page (user-facing)
app/(shared)/support/[ticketId]/page.tsx — Individual ticket page
app/support-portal/layout.tsx — Portal layout wrapper
app/support-portal/page.tsx — Portal dashboard
app/support-portal/tickets/page.tsx — Ticket queue page
app/support-portal/tickets/[ticketId]/page.tsx — Ticket detail page
app/support-portal/faqs/page.tsx — FAQ management page with create/edit/delete, debounced search
app/admin/support/page.tsx — Admin support page (3 tabs)

# Documentation
DOCS/SUPPORT_SYSTEM.md — Backend specification (1636 lines)
DOCS/SUPPORT_SYSTEM_COMPLETE.md — This document

Modified Existing Files

FileChange
lib/types/user.tsAdded isSupportAgent?: boolean
graphql/operations/user/queries.tsAdded commented isSupportAgent field in WHO_AM_I
contexts/UserAuthProvider.tsxExposed isSupportAgent in auth context
graphql/operations/schema/index.tsAdded export * from './support/fragments'
components/navigation/Sidebar.tsxReplaced mailto with SupportDrawer trigger + unread badge
components/navigation/UserProfile.tsxAdded "Support Portal" link in profile dropdown (under Admin Panel)
components/profile/Sidebar.tsxReplaced mailto with SupportDrawer trigger
components/admin/layout/AdminGuard.tsxAdded "support" to AdminSection type + permissions
components/admin/layout/AdminSidebar.tsxAdded Support nav item under "Engagement" group
components/admin/layout/AdminLayout.tsxAdded page title mapping for /admin/support

6. Frontend Components Detail

SupportDrawer (User-Facing Modal)

Trigger: "Need help?" button in sidebar footer Tabs:

  1. FAQs (shown first) — Searchable FAQ list (debounced 300ms) with category filters, expandable cards showing tags, helpful voting, "Still need help?" button switches to New Ticket tab
  2. My Tickets — List of user's tickets with status chips, click to open conversation
  3. New Ticket — Form: category (select), priority (select), subject (input), description (textarea)

Footer: "Open full Support Center" link → navigates to /support

Agent Identity Masking: In conversation view, all support replies show "AiQlick Support" with a generic avatar — individual agent names are never visible to users.

Message Alignment: Message alignment is context-aware. In user views, user messages are right-aligned (own messages) and support messages are left-aligned. In the support portal, this is reversed — support agent messages are right-aligned and user messages are left-aligned.

Ticket Description: The initial ticket description is rendered as the first message in the conversation thread (before any messages), showing the ticket creator's avatar and a "Description" chip.

TicketDetailView (Support Portal)

Layout: Two-panel — main content (left) + metadata sidebar (right)

Left panel tabs:

  • Conversation — Full message thread including initial ticket description, internal notes (amber background), and context-aware message alignment (support messages right-aligned in portal view)
  • Audit Log — Timeline of all actions with actor identity

Internal Note Toggle: Checkbox below conversation — "Send as internal note (not visible to user)"

Right sidebar:

  • Status, Priority, Category selectors (TWSelect)
  • Assigned To selector + "Assign to Me" button (hidden when ticket is already assigned to the current user)
  • Ticket metadata (created by, dates)
  • "Resolve Ticket" → opens FAQ creation modal (hidden when status is RESOLVED or CLOSED)
  • "Close Ticket" → opens FAQ creation modal (hidden when status is CLOSED)

TicketResolveFaqModal (FAQ from Resolved Ticket)

Triggered by: "Resolve Ticket" or "Close Ticket" in TicketDetailView

Features:

  • "Use AI to Summarize" button — Calls generateFaqSuggestion mutation, pre-fills question/answer/tags
  • Question input (pre-filled from ticket subject)
  • Answer textarea (editable)
  • Category selector (auto-set from ticket category)
  • Tags input (Enter to add, click to remove chips)
  • FAQ Status: "Save as Draft" or "Publish Immediately"
  • "Skip — Not FAQ-worthy" button (resolves/closes without creating FAQ)
  • "Save FAQ" button (creates FAQ entry, then updates ticket status)

SupportPortalSidebar

Navigation items: Dashboard, All Tickets, My Tickets, FAQs

  • "My Tickets" navigates to /support-portal/tickets?assignedToMe=true — the TicketQueue reads this search param and passes assignedToId: user.id to the backend query, filtering to only tickets assigned to the current agent. The page title dynamically shows "My Tickets" vs "All Tickets".

Footer links:

  • "Admin Panel" (super admin only)
  • "Back to App" → /company/dashboard or /jobseeker

7. GraphQL API Contract

Fragments

FragmentTypeUsed In
TicketUserFieldsTicketUserEmbedded user data (createdBy, assignedTo, sender, actor)
SupportTicketFieldsSupportTicketOutputAll ticket views (list + detail, includes createdBy/assignedTo)
TicketAttachmentFieldsTicketAttachmentOutputMessage attachments
TicketMessageFieldsTicketMessageOutputConversations (includes sender, attachments)
TicketAuditLogFieldsTicketAuditLogOutputAudit views (includes actor)
FaqEntryFieldsFaqEntryOutputFAQ lists and detail

Queries (11 total)

User-Facing:

QueryVariablesReturnsAuth
myTicketsMyTicketsInput?PaginatedTicketsAuthenticated user
myTicketDetailticketId: ID!{ ticket, messages, auditLog }Ticket owner only
myUnreadTicketCount{ count: Int }Authenticated user

Support Portal:

QueryVariablesReturnsAuth
supportTicketsSupportTicketsInput!PaginatedTicketsSupport agent or super admin
supportTicketDetailticketId: ID!{ ticket, messages, auditLog }Support agent or super admin
supportDashboardStatsSupportDashboardStatsSupport agent or super admin

Admin:

QueryVariablesReturnsAuth
adminSupportOverviewAdminSupportOverviewSuper admin
adminListSupportAgents[SupportAgent]Super admin
adminSupportAuditLogAdminSupportAuditLogInput?PaginatedAuditLogSuper admin

FAQ:

QueryVariablesReturnsAuth
publishedFaqsPublishedFaqsInput?PaginatedFaqsAny authenticated user
supportFaqsSupportFaqsInput?PaginatedFaqsSupport agent or super admin

Mutations (16 total)

User-Facing (4):

MutationVariablesReturnsSide Effects
createSupportTicketCreateSupportTicketInput!SupportTicketCreates audit log (CREATED), sets status OPEN
sendTicketMessageSendTicketMessageInput!TicketMessageCreates audit log (MESSAGE_SENT), updates lastActivityAt
closeMyTicketticketId: ID!SupportTicketCreates audit log (CLOSED), sets closedAt
reopenMyTicketticketId: ID!SupportTicketCreates audit log (REOPENED), sets status OPEN

Support Portal (6):

MutationVariablesReturnsSide Effects
assignTicketticketId: ID!, agentId: ID!SupportTicketAudit log (ASSIGNED), status → IN_PROGRESS
updateTicketStatusticketId: ID!, status: TicketStatus!SupportTicketAudit log (STATUS_CHANGED), set resolvedAt/closedAt
updateTicketPriorityticketId: ID!, priority: TicketPriority!SupportTicketAudit log (PRIORITY_CHANGED)
updateTicketCategoryticketId: ID!, category: TicketCategory!SupportTicketAudit log (CATEGORY_CHANGED)
sendSupportReplySendSupportReplyInput!TicketMessageAudit log (MESSAGE_SENT), senderRole=SUPPORT, set firstResponseAt if first
addInternalNoteAddInternalNoteInput!TicketMessageAudit log (INTERNAL_NOTE_ADDED), isInternalNote=true

Admin (2):

MutationVariablesReturnsSide Effects
adminAssignSupportRoleuserId: ID!{ success, message }Adds "support" role to user
adminRevokeSupportRoleuserId: ID!{ success, message }Removes "support" role from user

FAQ (4):

MutationVariablesReturnsSide Effects
createFaqEntryCreateFaqInput!FaqEntryLinks to sourceTicketId if provided
updateFaqEntryid: ID!, UpdateFaqInput!FaqEntry
deleteFaqEntryid: ID!{ success, message }Admin only
markFaqHelpfulid: ID!{ id, helpfulCount }Increments helpfulCount

AI (1):

MutationVariablesReturnsNotes
generateFaqSuggestionticketId: ID!{ question, answer, tags }AI-powered; reads ticket conversation, generates Q&A summary

Subscriptions (2)

SubscriptionVariablesPayloadAuth
myTicketUpdatedticketId: ID?{ ticket, newMessage?, type }Authenticated user (own tickets only)
supportTicketEvent{ ticket, type }Support agent or super admin

Input Types

input CreateSupportTicketInput {
subject: String!
description: String!
category: TicketCategory!
priority: TicketPriority # defaults to MEDIUM
}

input SendTicketMessageInput {
ticketId: ID!
content: String!
}

input SendSupportReplyInput {
ticketId: ID!
content: String!
}

input AddInternalNoteInput {
ticketId: ID!
content: String!
}

input SupportTicketsInput {
status: TicketStatus
priority: TicketPriority
category: TicketCategory
assignedToId: ID
search: String
sortBy: TicketSortBy # CREATED_AT | LAST_ACTIVITY | PRIORITY
sortOrder: SortOrder # ASC | DESC
page: Int
limit: Int
}

input MyTicketsInput {
status: TicketStatus
page: Int
limit: Int
}

input CreateFaqInput {
question: String!
answer: String!
category: TicketCategory!
tags: [String!]
sourceTicketId: ID
status: FaqStatus # DRAFT | PUBLISHED
}

input UpdateFaqInput {
question: String
answer: String
category: TicketCategory
tags: [String!]
status: FaqStatus
}

input PublishedFaqsInput {
category: TicketCategory
search: String
page: Int
limit: Int
}

input SupportFaqsInput {
status: FaqStatus
category: TicketCategory
search: String
page: Int
limit: Int
}

8. Ticket Lifecycle

Status Flow Diagram

                         ┌──────────────────┐
│ User creates │
│ ticket │
└────────┬─────────┘


┌──────────────────┐
┌─────────│ OPEN │◄────────────────────────┐
│ └────────┬─────────┘ │
│ │ │
│ Agent assigns Customer replies
│ │ to RESOLVED ticket
│ ▼ │
│ ┌──────────────────┐ │
│ │ IN_PROGRESS │◄──────────────┐ │
│ └────────┬─────────┘ │ │
│ │ │ │
│ Agent replies Customer replies │
│ │ │ │
│ ▼ │ │
│ ┌──────────────────┐ │ │
│ │ WAITING_ON_ │───────────────┘ │
│ │ CUSTOMER │ │
│ └────────┬─────────┘ │
│ │ │
│ Agent resolves │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ RESOLVED │──────────────────────────┘
│ └────────┬─────────┘
│ │
│ Agent/User closes
│ or auto-close (7 days)
│ │
│ ▼
│ ┌──────────────────┐
└─────────▶│ CLOSED │
└──────────────────┘

Status Transition Table

FromToTriggered ByAudit Action
OPENUser creates ticketCREATED
OPENIN_PROGRESSAgent assigns ticketASSIGNED
IN_PROGRESSWAITING_ON_CUSTOMERAgent repliesSTATUS_CHANGED
WAITING_ON_CUSTOMERIN_PROGRESSCustomer repliesSTATUS_CHANGED
IN_PROGRESSRESOLVEDAgent resolvesRESOLVED
WAITING_ON_CUSTOMERRESOLVEDAgent resolvesRESOLVED
OPENRESOLVEDAgent resolvesRESOLVED
RESOLVEDOPENCustomer replies to resolvedREOPENED
RESOLVEDCLOSEDAgent/User closesCLOSED
OPENCLOSEDAgent/User closesCLOSED
IN_PROGRESSCLOSEDAgent closesCLOSED
RESOLVEDCLOSEDAuto-close after 7 daysCLOSED
CLOSEDOPENUser reopensREOPENED

Automatic Status Changes (Backend Logic)

TriggerAction
Agent sends first replySet firstResponseAt if null
Agent sends replyStatus → WAITING_ON_CUSTOMER
User replies to WAITING_ON_CUSTOMERStatus → IN_PROGRESS
User replies to RESOLVEDStatus → OPEN (reopened)
Status changes to RESOLVEDSet resolvedAt
Status changes to CLOSEDSet closedAt
7 days after RESOLVED with no activityAuto-close → CLOSED

9. FAQ System Flow

Overview

┌──────────────────────────────────────────────────────────────────────────┐
│ FAQ Lifecycle │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌────────────┐ ┌───────────┐ │
│ │ Ticket │ │ Agent clicks │ │ FAQ Modal │ │ FAQ Entry │ │
│ │ Resolved/ │───▶│ Resolve or │───▶│ opens with │───▶│ created │ │
│ │ Closed │ │ Close button │ │ AI option │ │ as DRAFT │ │
│ └─────────────┘ └──────────────┘ └────────────┘ │ or PUB │ │
│ └─────┬─────┘ │
│ │ │
│ ┌───────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ FAQ appears in: │ │
│ │ • SupportDrawer FAQs tab (PUBLISHED only) │ │
│ │ • /support page FAQs tab (PUBLISHED only) │ │
│ │ • /support-portal/faqs (ALL statuses — support team) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ User Interaction: │
│ • Search FAQs by keyword (debounced 300ms) │
│ • Filter by category │
│ • Expand to read answer │
│ • View tags on each FAQ card │
│ • "Was this helpful?" thumbs up → markFaqHelpful mutation │
│ • "Still need help?" → switches to New Ticket tab │
└──────────────────────────────────────────────────────────────────────────┘

AI FAQ Suggestion Flow

Agent clicks "Use AI to Summarize"


generateFaqSuggestion(ticketId) mutation


Backend reads all ticket messages


AI service summarizes conversation into:
• question: distilled user question
• answer: clear resolution summary
• tags: relevant keywords


Frontend pre-fills the FAQ form fields
Agent reviews, edits, and saves

The frontend calls the generateFaqSuggestion mutation. If the backend AI service is unavailable, the hook shows a warning toast and the agent can write the FAQ manually.


10. Backend Implementation Steps

Step 1: Database Schema

Create these tables in PostgreSQL (full DDL in DOCS/SUPPORT_SYSTEM.md):

  1. SupportTicket — Main ticket table with status, priority, category, assignment
  2. TicketMessage — Messages within tickets (user, support, system roles)
  3. TicketAttachment — File attachments on messages (S3 integration)
  4. TicketAuditLog — Audit trail of all actions
  5. FaqEntry — FAQ knowledge base entries
  6. UserRole (if not exists) — Global roles table for "support" role

Also:

  • Ticket number sequence: CREATE SEQUENCE support_ticket_seq START 1;
  • Full-text search index on ticket subject + FAQ question/answer
  • Indexes for common query patterns (status, assignedTo, createdBy, category)

Step 2: GraphQL Type Definitions

Add these to the GraphQL schema SDL:

Enums: TicketCategory, TicketPriority, TicketStatus, TicketMessageSenderRole, TicketAuditAction, FaqStatus, TicketSortBy, SortOrder

Types: SupportTicket, TicketMessage, TicketAttachment, TicketAuditLog, FaqEntry, SupportAgent, SupportDashboardStats, AdminSupportOverview, AgentPerformance, PaginatedTickets, PaginatedFaqs, TicketDetail

Inputs: CreateSupportTicketInput, SendTicketMessageInput, SendSupportReplyInput, AddInternalNoteInput, SupportTicketsInput, MyTicketsInput, CreateFaqInput, UpdateFaqInput, AdminSupportAuditLogInput

See Section 7 of this document for the complete list of queries, mutations, and subscriptions.

Step 3: Resolvers — User-Facing

ResolverKey Logic
myTicketsFilter by createdById = currentUser.id, paginate
myTicketDetailVerify createdById = currentUser.id, return ticket + messages (exclude isInternalNote=true)
myUnreadTicketCountCount tickets with messages newer than user's last read timestamp
createSupportTicketGenerate ticket number (SUP-XXXXX), set status OPEN, create CREATED audit log
sendTicketMessageVerify user owns ticket, create message with senderRole=USER, update lastActivityAt
closeMyTicketVerify user owns ticket, set status CLOSED, closedAt, create CLOSED audit log
reopenMyTicketVerify user owns ticket, set status OPEN, create REOPENED audit log

Step 4: Resolvers — Support Portal

ResolverKey Logic
supportTicketsAuth: isSupportAgent || isSuperAdmin. Full filter/sort/search/paginate
supportTicketDetailReturn ticket + ALL messages (including internal notes) + audit log
supportDashboardStatsAggregate counts by status, averages for response/resolution time
assignTicketSet assignedToId, status → IN_PROGRESS, create ASSIGNED audit log
updateTicketStatusUpdate status, set resolvedAt/closedAt as appropriate, create STATUS_CHANGED audit log
sendSupportReplyCreate message with senderRole=SUPPORT, set firstResponseAt if first, status → WAITING_ON_CUSTOMER
addInternalNoteCreate message with isInternalNote=true, senderRole=SUPPORT, create INTERNAL_NOTE_ADDED audit log

Step 5: Resolvers — Admin

ResolverKey Logic
adminSupportOverviewDashboard stats + agent performance metrics (assigned, resolved, avg times)
adminListSupportAgentsList all users with "support" role, include ticket counts
adminSupportAuditLogGlobal audit log, filterable by ticket/agent/action/date
adminAssignSupportRoleAdd "support" role to user in UserRole table
adminRevokeSupportRoleRemove "support" role from user

Step 6: Resolvers — FAQ

ResolverKey Logic
publishedFaqsFilter by status=PUBLISHED, support search on question/answer/tags
supportFaqsAll statuses, auth: support agent or super admin
createFaqEntryCreate entry, link sourceTicketId if provided
updateFaqEntryAuth: creator or super admin
deleteFaqEntryAuth: super admin only
markFaqHelpfulIncrement helpfulCount (consider de-duplication via FaqVote table)
generateFaqSuggestionCall AI service to summarize ticket conversation into Q&A format

Step 7: Subscriptions

SubscriptionImplementation
myTicketUpdatedPubSub: publish on any ticket update where user is ticket creator. Filter by ticketId if provided
supportTicketEventPubSub: publish on new ticket created, new message, status change. Auth: support agent or super admin

Step 8: Audit Log Integration

Every mutation that modifies a ticket should create an audit log entry:

await prisma.ticketAuditLog.create({
data: {
ticketId,
actorId: currentUser.id,
action: 'STATUS_CHANGED',
previousValue: oldStatus,
newValue: newStatus,
metadata: { reason: '...' }
}
})

Step 9: Auto-Close Cron Job

Set up a scheduled job (cron or similar) to auto-close RESOLVED tickets after 7 days:

UPDATE "SupportTicket"
SET "status" = 'CLOSED',
"closedAt" = NOW(),
"updatedAt" = NOW()
WHERE "status" = 'RESOLVED'
AND "lastActivityAt" < NOW() - INTERVAL '7 days';

Step 10: WHO_AM_I Integration

Add isSupportAgent to the User type resolver:

// In User type resolvers
isSupportAgent: async (user, _, context) => {
const role = await prisma.userRole.findFirst({
where: { userId: user.id, role: 'support' }
})
return !!role
}

Then uncomment the field in the frontend's WHO_AM_I query (graphql/operations/user/queries.ts).


11. Modified Existing Files

Summary of All Frontend Changes to Existing Files

FileWhat ChangedWhy
lib/types/user.tsAdded isSupportAgent?: booleanNew role for support agents
graphql/operations/user/queries.tsAdded commented isSupportAgent fieldWaiting for backend to add field
contexts/UserAuthProvider.tsxAdded isSupportAgent to context type and valueExpose new role to all components
graphql/operations/schema/index.tsAdded export * from './support/fragments'Register support fragments
components/navigation/Sidebar.tsxReplaced mailto link with SupportDrawer + unread badgeNew "Need help?" interaction
components/profile/Sidebar.tsxReplaced mailto link with SupportDrawer + unread badgeSame for mobile sidebar
components/navigation/UserProfile.tsxAdded "Support Portal" link in profile dropdownNavigation for support agents
components/admin/layout/AdminGuard.tsxAdded "support" to AdminSection type + permissionsAdmin panel integration
components/admin/layout/AdminSidebar.tsxAdded Support nav item + SupportIcon SVGAdmin panel navigation
components/admin/layout/AdminLayout.tsxAdded page title for /admin/supportPage header mapping

12. Checklist — What's Done vs What's Needed

Frontend (DONE)

  • Types, interfaces, enums for entire support system
  • GraphQL fragments, queries, mutations, subscriptions defined
  • React hooks for all operations (queries, mutations, admin, FAQ)
  • SupportDrawer modal (FAQs, My Tickets, New Ticket tabs)
  • "Open full Support Center" link in drawer
  • /support full page (user-facing)
  • /support/[ticketId] individual ticket page
  • Ticket conversation with agent identity masking
  • Internal notes (visible only to support team, amber background)
  • FAQ cards with search, category filters, helpful voting
  • Support Portal guard (isSupportAgent || isSuperAdmin)
  • Support Portal layout with sidebar navigation
  • Support Portal dashboard with stats
  • Ticket queue with filters (status, priority, category, search)
  • Ticket detail view with conversation + audit log tabs
  • Ticket resolve/close → FAQ creation modal with AI option
  • FAQ management page in support portal with create/edit/delete modals
  • FAQ tags displayed in published FAQ cards and admin table
  • FAQ search debounced (300ms) in both user and admin views
  • Ticket description displayed as initial message in conversation thread
  • Message alignment context-aware (user vs support portal views)
  • "My Tickets" filters by assignedToId using URL search params
  • "Assign to Me" button hidden when already assigned to current user
  • "Resolve/Close" buttons conditionally rendered based on ticket status
  • TWSelect dropdown toggle fix (close on re-click)
  • Admin support overview with stats + agent performance
  • Admin agent management (add/remove support role)
  • "Support Portal" link in profile dropdown (under Admin Panel)
  • "Need help?" button with unread badge in sidebar
  • Back to App → /company/dashboard
  • Admin section registration (guard, sidebar, layout)
  • Backend design document (1636 lines)
  • Build passes with yarn run build

Backend (TODO)

  • Database: Create SupportTicket, TicketMessage, TicketAttachment, TicketAuditLog, FaqEntry tables
  • Database: Create UserRole table (if not exists) for global "support" role
  • Database: Ticket number sequence and indexes
  • GraphQL SDL: Add all enums, types, inputs, queries, mutations, subscriptions
  • Resolvers: User-facing (myTickets, createSupportTicket, sendTicketMessage, closeMyTicket, reopenMyTicket)
  • Resolvers: Support portal (supportTickets, assignTicket, updateStatus, sendSupportReply, addInternalNote)
  • Resolvers: Admin (adminSupportOverview, adminListSupportAgents, adminAssignSupportRole)
  • Resolvers: FAQ (publishedFaqs, createFaqEntry, updateFaqEntry, markFaqHelpful)
  • Resolvers: AI (generateFaqSuggestion — integrate with AI service)
  • WHO_AM_I: Add isSupportAgent computed field to User type
  • Audit Log: Implement audit log creation for every ticket mutation
  • Subscriptions: myTicketUpdated, supportTicketEvent via PubSub
  • Auto-close: Cron job to close RESOLVED tickets after 7 days
  • Notifications: In-app notifications for new tickets, replies, assignments
  • File Attachments: S3 integration for ticket message attachments

Frontend — Post-Backend (TODO)

  • Uncomment isSupportAgent in WHO_AM_I query
  • Test all flows end-to-end with real backend
  • Add file attachment upload support (Dropzone in NewTicketForm)
  • Wire up real-time subscriptions (WebSocket)
  • Add notification badge for support agents (new tickets)
  • Add email notifications (optional)

Appendix: Enum Values Reference

TicketCategory

GENERAL | BILLING | TECHNICAL | ACCOUNT | FEATURE_REQUEST | BUG_REPORT

TicketPriority

LOW | MEDIUM | HIGH | URGENT

TicketStatus

OPEN | IN_PROGRESS | WAITING_ON_CUSTOMER | RESOLVED | CLOSED

TicketMessageSenderRole

USER | SUPPORT | SYSTEM

TicketAuditAction

CREATED | STATUS_CHANGED | PRIORITY_CHANGED | ASSIGNED | UNASSIGNED | REASSIGNED | MESSAGE_SENT | INTERNAL_NOTE_ADDED | ATTACHMENT_ADDED | CATEGORY_CHANGED | RESOLVED | CLOSED | REOPENED

FaqStatus

DRAFT | PUBLISHED | ARCHIVED