Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

156 lines
6.8 KiB
Markdown

# PRD — Ticket Origin Badge (Internal, Client Portal, Inbound Email, API)
- Slug: `2026-02-09-ticket-origin-badge`
- Date: `2026-02-09`
- Status: Draft
## Summary
Add a ticket-level origin badge so users can see where a ticket was created:
- `Internal (MSP)`
- `Client Portal`
- `Inbound Email`
- `API`
The badge should be visible in ticket details and should work consistently for both MSP and client portal views.
## Problem
Today, users can see ticket response state and per-comment response source, but not the ticket creation origin at a glance. This makes triage and context handoff slower, especially when teams handle mixed intake channels.
## Goals
- Show a clear ticket origin badge in ticket details.
- Support four first-class origins: `internal`, `client_portal`, `inbound_email`, `api`.
- Persist ticket origin at creation time so we do not rely only on heuristics.
- Keep a fallback resolver for legacy tickets created before origin persistence.
- Keep origin storage extensible for future values (for example `ai_agent`) without schema redesign.
## Non-goals
- No ticket list/table filtering by origin in this phase.
- No redesign of ticket details layout beyond adding the badge.
- No changes to existing per-comment `responseSource` badges in conversation.
- No analytics/reporting dashboard for ticket origin in this phase.
## Users and Primary Flows
### Flow A — MSP user creates a ticket internally
1. Internal user creates ticket from MSP app (`addTicket` / related internal flows).
2. Ticket origin resolves to `internal`.
3. Badge shows internal origin in ticket details.
### Flow B — Client creates a ticket in client portal
1. Client user creates ticket from client portal (`createClientTicket`).
2. Ticket origin resolves to `client_portal`.
3. Badge shows client portal origin in ticket details.
### Flow C — Inbound email creates a new ticket
1. Inbound email processing creates ticket (`processInboundEmailInApp` / `createTicketFromEmail`).
2. Ticket origin resolves to `inbound_email`.
3. Badge shows inbound email origin in ticket details.
### Flow D — External integration creates a ticket through API
1. API request creates ticket (`POST /api/v1/tickets` -> `ApiTicketController` -> `TicketService`).
2. Ticket origin resolves to `api`.
3. Badge shows API origin in ticket details.
## UX / UI Notes
- Badge is ticket-level (not per-comment) and indicates creation origin.
- Placement:
- MSP: in `packages/tickets/src/components/ticket/TicketDetails.tsx` header, near ticket number and response-state badge.
- Client portal: in `packages/client-portal/src/components/tickets/TicketDetails.tsx` header/status area.
- Proposed labels:
- `Created Internally`
- `Created via Client Portal`
- `Created via Inbound Email`
- `Created via API`
- Badge should always render exactly one origin value for valid tickets.
- Unknown future origin values should render safely with a generic fallback label (`Created via Other`) until a specific label is added.
## Requirements
### Functional Requirements
- Add persistent `ticket_origin` field on `tickets` table (text).
- Canonical first-class values for this phase: `internal | client_portal | inbound_email | api`.
- Add a shared normalization/helper (`normalizeTicketOrigin` / `getTicketOrigin`) used by both MSP and client portal details paths.
- Origin resolution precedence for reads:
1. If `tickets.ticket_origin` exists, use it.
2. Else use legacy fallback resolver (email metadata, creator user type, etc.).
3. Else resolve `internal`.
- Set `ticket_origin` explicitly on create paths:
- MSP app creates -> `internal`
- Client portal creates -> `client_portal`
- Inbound email creates -> `inbound_email`
- API creates -> `api`
- Ensure ticket detail actions return derived origin for UI consumption:
- `packages/tickets/src/actions/ticketActions.ts#getTicketById`
- `packages/client-portal/src/actions/client-portal-actions/client-tickets.ts#getClientTicketDetails`
- Add a shared `TicketOriginBadge` component in `@alga-psa/tickets/components`.
- Render badge in both ticket detail UIs using shared labels/translation keys.
- Keep existing comment-level response source behavior unchanged.
### Non-functional Requirements
- Backward compatible with existing tickets (legacy tickets without `ticket_origin` still resolve correctly).
- No additional heavy queries; origin derivation should be O(1) per ticket details load.
- Resolver must be deterministic and unit-testable.
## Data / API / Integrations
- Existing signals already available in current codebase:
- `tickets.email_metadata` (inbound email-created tickets)
- `tickets.entered_by` + creator `users.user_type`
- Existing create path context:
- MSP app currently passes `source: 'web_app'`
- Client portal passes `source: 'client_portal'`
- Inbound email paths pass `source: 'email'`
- API service passes `source: 'api'`
- Existing code pointers:
- Internal create: `packages/tickets/src/actions/ticketActions.ts`
- Client portal create: `packages/client-portal/src/actions/client-portal-actions/client-tickets.ts`
- Inbound email create: `shared/services/email/processInboundEmailInApp.ts`, `shared/workflow/actions/emailWorkflowActions.ts`
- API create: `server/src/app/api/v1/tickets/route.ts`, `server/src/lib/api/services/TicketService.ts`
## Security / Permissions
- No new permissions required.
- Origin badge displays metadata about ticket origin only; it does not expose sensitive payloads.
## Observability
- No new metrics/logging required for MVP.
## Rollout / Migration
- DB migration to add `tickets.ticket_origin`.
- Data backfill (migration or one-time script) for existing rows:
- `email_metadata` present -> `inbound_email`
- creator user type `client` -> `client_portal`
- otherwise -> `internal`
- Keep legacy fallback resolver in code as a safety net.
## Open Questions
1. Confirm final internal label copy: `Created Internally` vs `Created by MSP`.
2. Should this badge remain ticket-details-only, or also appear in ticket list rows now?
3. Should inbound email badge stay generic, or include provider label (`Gmail`, `Microsoft`, `IMAP`) in this phase?
4. For future origins like `ai_agent`, should we show raw value with title-case fallback or keep a strict whitelist + generic `Other` label?
5. Do we want to expose ticket origin in exports/reporting in this phase (currently no)?
## Acceptance Criteria (Definition of Done)
- New ticket created internally shows `internal` origin badge in MSP ticket details.
- New ticket created in client portal shows `client_portal` origin badge in both MSP and client portal ticket details.
- New inbound email-created ticket shows `inbound_email` origin badge in ticket details.
- New API-created ticket shows `api` origin badge in ticket details.
- Legacy tickets still render a stable origin badge using fallback rules.
- Existing comment response-source badge behavior is unchanged.