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
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
156 lines
6.8 KiB
Markdown
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.
|