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

198 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Scratchpad — Ticket Origin Badge (Internal vs Client Portal vs Inbound Email vs API)
- Plan slug: `2026-02-09-ticket-origin-badge`
- Created: `2026-02-09`
## Context Snapshot
- Existing plan `2026-02-05-ticket-response-source` already covers comment-level response source badges; this new scope is ticket creation origin.
- Ticket creation paths already pass source hints:
- MSP create: `source: 'web_app'` in `packages/tickets/src/actions/ticketActions.ts`
- Client portal create: `source: 'client_portal'` in `packages/client-portal/src/actions/client-portal-actions/client-tickets.ts`
- Inbound email create: `source: 'email'` in `shared/services/email/processInboundEmailInApp.ts` and `shared/workflow/actions/emailWorkflowActions.ts`
- API create also sets `source: 'api'` in `server/src/lib/api/services/TicketService.ts`.
- `shared/models/ticketModel.ts` accepts `CreateTicketInput.source`, but its `ticketSchema` does not currently include `source` (zod parse strips unknown keys), so source hints are not guaranteed to persist via that write path.
- `tickets.email_metadata` exists and is reliable signal for inbound-email-created tickets.
- Current ticket details UIs:
- MSP: `packages/tickets/src/components/ticket/TicketDetails.tsx`
- Client portal: `packages/client-portal/src/components/tickets/TicketDetails.tsx`
## Decisions
- (2026-02-09) Promote API to first-class ticket origin (`api`) instead of collapsing under internal.
- (2026-02-09) Add persisted `tickets.ticket_origin` to avoid relying solely on heuristics and preserve source fidelity.
- (2026-02-09) Keep `ticket_origin` as text (not DB enum) for forward compatibility with future values (for example `ai_agent`) without schema redesign.
- (2026-02-09) Origin remains ticket-details-only for this phase.
- (2026-02-09) Existing per-comment response source badges remain unchanged.
## Discoveries / Constraints
- (2026-02-09) `ITicket` in `packages/types/src/interfaces/ticket.interfaces.ts` currently has no ticket origin field.
- (2026-02-09) MSP and client portal detail queries already join creator user type:
- MSP: `u_creator.user_type as entered_by_user_type`
- Client portal: `u_creator.user_type as entered_by_user_type`
- (2026-02-09) Existing badge patterns/components (`ResponseStateBadge`, `ResponseSourceBadge`) can be reused stylistically for consistency.
- (2026-02-09) API ticket creation path already marks source as `api`, but this is not reliably persisted today due to shared model validation/schema.
## Commands / Runbooks
- (2026-02-09) Inspect plans: `ls -la ee/docs/plans`
- (2026-02-09) Read related plan: `sed -n '1,220p' ee/docs/plans/2026-02-05-ticket-response-source/PRD.md`
- (2026-02-09) Locate source hints: `rg -n "source: 'web_app'|source: 'client_portal'|source: 'email'" packages shared`
- (2026-02-09) Locate API source hint: `rg -n "source: 'api'" server/src/lib/api/services/TicketService.ts`
- (2026-02-09) Locate ticket details surfaces:
- `sed -n '1438,1495p' packages/tickets/src/components/ticket/TicketDetails.tsx`
- `sed -n '380,500p' packages/client-portal/src/components/tickets/TicketDetails.tsx`
- (2026-02-09) Scaffold plan: `python3 /Users/roberisaacs/.codex/skills/alga-plan/scripts/scaffold_plan.py "Ticket Origin Badge" --slug ticket-origin-badge`
## Links / References
- `ee/docs/plans/2026-02-05-ticket-response-source/PRD.md`
- `shared/models/ticketModel.ts`
- `packages/types/src/interfaces/ticket.interfaces.ts`
- `packages/tickets/src/actions/ticketActions.ts`
- `packages/client-portal/src/actions/client-portal-actions/client-tickets.ts`
- `shared/services/email/processInboundEmailInApp.ts`
- `shared/workflow/actions/emailWorkflowActions.ts`
- `server/src/app/api/v1/tickets/route.ts`
- `server/src/lib/api/controllers/ApiTicketController.ts`
- `server/src/lib/api/services/TicketService.ts`
- `packages/tickets/src/components/ticket/TicketDetails.tsx`
- `packages/client-portal/src/components/tickets/TicketDetails.tsx`
- `packages/tickets/src/components/ResponseSourceBadge.tsx`
- `packages/tickets/src/components/ResponseStateBadge.tsx`
## Open Questions
- Confirm label copy for internal origin: `Created Internally` vs `Created by MSP`.
- Confirm whether ticket list/table should show origin in this phase.
- Confirm whether inbound email badge should remain generic or include provider detail.
- Confirm desired fallback label for unknown future origins (`Created via Other` vs raw source string).
## Implementation Log
- (2026-02-09) **F001 completed**: Added canonical ticket origin constants in `packages/types/src/interfaces/ticket.interfaces.ts`:
- `TICKET_ORIGINS.INTERNAL = 'internal'`
- `TICKET_ORIGINS.CLIENT_PORTAL = 'client_portal'`
- `TICKET_ORIGINS.INBOUND_EMAIL = 'inbound_email'`
- (2026-02-09) Validation command: `npx vitest run packages/types/src/interfaces/barrel.test.ts` (fails due to pre-existing unrelated `tax.interfaces` barrel mismatch).
- (2026-02-09) **F002 completed**: Added shared `TicketOrigin` union type and `ITicket.ticket_origin?: TicketOrigin` in `packages/types/src/interfaces/ticket.interfaces.ts` for ticket-level origin typing across packages.
- (2026-02-09) **F003 completed**: Added shared resolver `getTicketOrigin` in `packages/tickets/src/lib/ticketOrigin.ts` and exported it via `packages/tickets/src/lib/index.ts`. Resolver precedence is explicit and deterministic: `email_metadata` -> source hint mapping -> creator user type -> internal fallback.
- (2026-02-09) **F004 completed**: `getTicketOrigin` now gives highest precedence to `email_metadata` presence and classifies as `inbound_email` before any source/user-type checks.
- (2026-02-09) **F005 completed**: Added explicit source-hint mapping in resolver (`email`, `inbound_email`, `client_portal`, `web_app`, `api`, `manual`, `worker`, `workflow`) with canonical outputs (`inbound_email`, `client_portal`, `internal`).
- (2026-02-09) **F006 completed**: Resolver falls through to creator user type (`creator_user_type` / `entered_by_user_type` / `user_type`) and classifies `client` creators as `client_portal` when no higher-priority signal applies.
- (2026-02-09) **F007 completed**: Resolver default return is `internal`, providing deterministic legacy fallback when email/source/creator signals are absent or unknown.
- (2026-02-09) **F008 completed**: Updated `packages/tickets/src/actions/ticketActions.ts#getTicketById` to:
- join creator user (`u_creator.user_type as entered_by_user_type`),
- derive `ticket_origin` via `getTicketOrigin(ticket)`,
- include `ticket_origin` on `DetailedTicket` payload returned to MSP details UI.
- (2026-02-09) **F009 completed**: Updated `packages/client-portal/src/actions/client-portal-actions/client-tickets.ts#getClientTicketDetails` to:
- join creator user type (`u_creator.user_type as entered_by_user_type`),
- derive `ticket_origin` with shared `getTicketOrigin`,
- return sanitized ticket payload with `ticket_origin` for client portal details.
- (2026-02-09) Validation command: `npx vitest run packages/client-portal/src/actions/client-portal-actions/client-tickets.responseSource.test.ts` (fails in existing test mock with `Unexpected table: tickets`; unrelated to origin derivation path).
- (2026-02-09) **F010 completed**: Added reusable `TicketOriginBadge` in `packages/tickets/src/components/TicketOriginBadge.tsx` and exported it in `packages/tickets/src/components/index.ts`. Component supports all three origin states with icon/color variants and `size`/`className` props.
- (2026-02-09) **F011 completed**: Rendered `TicketOriginBadge` in MSP ticket header (`packages/tickets/src/components/ticket/TicketDetails.tsx`) beside ticket number/response-state badge; origin is resolved with shared `getTicketOrigin`.
- (2026-02-09) Validation command: `npx vitest run packages/tickets/src/components/ticket/__tests__/TicketDetailsCreateTask.test.tsx` (fails in current workspace due unresolved legacy alias import `@alga-psa/db/models/user` from auth package).
- (2026-02-09) **F012 completed**: Rendered `TicketOriginBadge` in client portal ticket details status row (`packages/client-portal/src/components/tickets/TicketDetails.tsx`), using shared resolver + translated labels.
- (2026-02-09) **F013 completed**: Added English common locale keys under `server/public/locales/en/common.json`:
- `tickets.origin.internal`
- `tickets.origin.clientPortal`
- `tickets.origin.inboundEmail`
- (2026-02-09) **F014 completed**: Added matching English client portal locale keys under `server/public/locales/en/clientPortal.json` for `tickets.origin.internal|clientPortal|inboundEmail`.
- (2026-02-09) **F015 completed**: Final badge copy now consistently uses:
- `Created Internally`
- `Created via Client Portal`
- `Created via Inbound Email`
across component defaults + English locale keys.
- (2026-02-09) Added resolver unit tests `packages/tickets/src/lib/__tests__/ticketOrigin.test.ts` covering `T001``T010`.
- (2026-02-09) Added type-contract test `packages/types/src/interfaces/ticket.interface.typecheck.test.ts` for `T011`.
- (2026-02-09) Validation commands:
- `npx vitest run packages/tickets/src/lib/__tests__/ticketOrigin.test.ts`
- `npx vitest run packages/types/src/interfaces/ticket.interface.typecheck.test.ts`
- Note: running Vitest in parallel processes with coverage caused transient `server/coverage/.tmp/coverage-0.json` ENOENT; rerunning sequentially passed.
- (2026-02-09) Added MSP action tests `packages/tickets/src/actions/ticketActions.ticketOrigin.test.ts` for `T020``T023`.
- (2026-02-09) Added client portal action tests `packages/client-portal/src/actions/client-portal-actions/client-tickets.ticketOrigin.test.ts` for `T024``T027`.
- (2026-02-09) Validation commands:
- `npx vitest run packages/tickets/src/actions/ticketActions.ticketOrigin.test.ts`
- `npx vitest run packages/client-portal/src/actions/client-portal-actions/client-tickets.ticketOrigin.test.ts`
- Note: same transient coverage tmp-folder ENOENT can appear when running concurrent Vitest processes; sequential rerun passes.
- (2026-02-09) Added badge render contract tests `packages/tickets/src/components/TicketOriginBadge.render.test.tsx` for `T030``T033`.
- (2026-02-09) Validation command: `npx vitest run packages/tickets/src/components/TicketOriginBadge.render.test.tsx`
- (2026-02-09) Replaced blocked JSdom TicketDetails render tests (module alias resolution issue on `@alga-psa/db/models/user`) with deterministic contract tests:
- `packages/tickets/src/components/ticket/TicketDetails.originBadge.contract.test.ts` (`T040`, `T041`, `T042`, MSP side of `T061`)
- `packages/client-portal/src/components/tickets/TicketDetails.originBadge.contract.test.ts` (`T043`, `T044`, `T045`, client portal side of `T061`)
- (2026-02-09) Added locale key tests `packages/tickets/src/lib/__tests__/ticketOriginLocales.test.ts` (`T050`, `T051`).
- (2026-02-09) Added migration posture test `packages/tickets/src/lib/__tests__/ticketOriginMigration.test.ts` (`T070`).
- (2026-02-09) Added flow sanity tests `packages/tickets/src/lib/__tests__/ticketOriginFlowSanity.test.tsx` (`T080`, `T081`, `T082`).
- (2026-02-09) Regression validation reused existing comment-source suite `packages/tickets/src/components/ResponseSourceBadge.render.test.tsx` for `T060`.
- (2026-02-09) Validation commands for this batch (all passed):
- `npx vitest run --coverage.enabled=false packages/tickets/src/lib/__tests__/ticketOriginLocales.test.ts`
- `npx vitest run --coverage.enabled=false packages/tickets/src/components/ticket/TicketDetails.originBadge.contract.test.ts`
- `npx vitest run --coverage.enabled=false packages/client-portal/src/components/tickets/TicketDetails.originBadge.contract.test.ts`
- `npx vitest run --coverage.enabled=false packages/tickets/src/lib/__tests__/ticketOriginMigration.test.ts`
- `npx vitest run --coverage.enabled=false packages/tickets/src/lib/__tests__/ticketOriginFlowSanity.test.tsx`
- `npx vitest run --coverage.enabled=false packages/tickets/src/components/ResponseSourceBadge.render.test.tsx`
- (2026-02-09) **F016 completed**: Verified no regressions for comment/source and response-state badges via existing and new regression/contract tests (`T060`, `T061`).
- (2026-02-09) **F017 completed**: Confirmed migration-free MVP via resolver derivation + migration scan test (`T070`).
- (2026-02-09) **F018 completed**: Added automated coverage for resolver logic, action payloads, badge component, locales, TicketDetails surfaces, and flow sanity (`T001``T082` plan scope items now covered).
- (2026-02-09) **F001 completed (reconciliation pass)**: Added `api` to canonical `TICKET_ORIGINS` and propagated canonical origin typing updates in `@alga-psa/types`.
- (2026-02-09) **F002 completed (reconciliation pass)**: Add shared TicketOrigin type in @alga-psa/types and include api in the union
- (2026-02-09) **F003 completed (reconciliation pass)**: Add tickets.ticket_origin persisted column (text) via migration
- (2026-02-09) **F004 completed (reconciliation pass)**: Backfill existing tickets.ticket_origin using legacy signals (email_metadata, creator user_type, fallback internal)
- (2026-02-09) **F005 completed (reconciliation pass)**: Keep ticket_origin storage extensible for future values (for example ai_agent) without schema redesign
- (2026-02-09) **F006 completed (reconciliation pass)**: Internal MSP create path writes ticket_origin=internal
- (2026-02-09) **F007 completed (reconciliation pass)**: Client portal create path writes ticket_origin=client_portal
- (2026-02-09) **F008 completed (reconciliation pass)**: Inbound email create path writes ticket_origin=inbound_email
- (2026-02-09) **F009 completed (reconciliation pass)**: API create path writes ticket_origin=api
- (2026-02-09) **F010 completed (reconciliation pass)**: Update shared TicketModel validation so ticket_origin is preserved and persisted
- (2026-02-09) **F011 completed (reconciliation pass)**: Implement shared ticket origin normalization/resolver helper with legacy fallback for null historical rows
- (2026-02-09) **F012 completed (reconciliation pass)**: MSP getTicketById returns normalized ticket_origin for TicketDetails
- (2026-02-09) **F013 completed (reconciliation pass)**: Client portal getClientTicketDetails returns normalized ticket_origin for TicketDetails
- (2026-02-09) **F014 completed (reconciliation pass)**: Add shared TicketOriginBadge component with internal/client_portal/inbound_email/api variants and unknown fallback
- (2026-02-09) **F015 completed (reconciliation pass)**: Render TicketOriginBadge in MSP TicketDetails header near ticket number and response state badge
- (2026-02-09) **F016 completed (reconciliation pass)**: Render TicketOriginBadge in client portal TicketDetails header/status area
- (2026-02-09) **F017 completed (reconciliation pass)**: Add locale keys for ticket origin labels including Created via API
- (2026-02-09) **F018 completed (reconciliation pass)**: Preserve existing comment response-source badge behavior with no regression
- (2026-02-09) **F019 completed (reconciliation pass)**: Preserve existing response-state badge behavior with no regression
- (2026-02-09) **F020 completed (reconciliation pass)**: Add automated tests for persistence, backfill, resolver logic, API distinction, and both TicketDetails surfaces
- (2026-02-09) **T001 completed (reconciliation pass)**: Migration adds tickets.ticket_origin column successfully in existing DB
- (2026-02-09) **T002 completed (reconciliation pass)**: Newly inserted tickets default ticket_origin to internal when not explicitly provided
- (2026-02-09) **T003 completed (reconciliation pass)**: Backfill marks tickets with email_metadata as inbound_email
- (2026-02-09) **T004 completed (reconciliation pass)**: Backfill marks tickets created by client users as client_portal when no email_metadata
- (2026-02-09) **T005 completed (reconciliation pass)**: Backfill marks unresolved legacy tickets as internal
- (2026-02-09) **T006 completed (reconciliation pass)**: TicketOrigin typecheck accepts internal/client_portal/inbound_email/api and rejects invalid values
- (2026-02-09) **T010 completed (reconciliation pass)**: MSP server action create path persists ticket_origin=internal
- (2026-02-09) **T011 completed (reconciliation pass)**: Client portal create path persists ticket_origin=client_portal
- (2026-02-09) **T012 completed (reconciliation pass)**: Inbound email create path persists ticket_origin=inbound_email
- (2026-02-09) **T013 completed (reconciliation pass)**: API create path persists ticket_origin=api
- (2026-02-09) **T014 completed (reconciliation pass)**: Workflow/automation ticket creation without explicit origin persists internal default
- (2026-02-09) **T020 completed (reconciliation pass)**: Resolver returns stored ticket_origin when present and valid
- (2026-02-09) **T021 completed (reconciliation pass)**: Resolver maps null legacy row with email_metadata to inbound_email
- (2026-02-09) **T022 completed (reconciliation pass)**: Resolver maps null legacy row with creator user_type client to client_portal
- (2026-02-09) **T023 completed (reconciliation pass)**: Resolver maps null legacy row with no signal to internal
- (2026-02-09) **T024 completed (reconciliation pass)**: Resolver handles unknown future origin values without crashing and returns safe fallback classification
- (2026-02-09) **T030 completed (reconciliation pass)**: MSP getTicketById payload includes normalized ticket_origin for internal ticket
- (2026-02-09) **T031 completed (reconciliation pass)**: MSP getTicketById payload includes normalized ticket_origin for client_portal ticket
- (2026-02-09) **T032 completed (reconciliation pass)**: MSP getTicketById payload includes normalized ticket_origin for inbound_email ticket
- (2026-02-09) **T033 completed (reconciliation pass)**: MSP getTicketById payload includes normalized ticket_origin for api ticket
- (2026-02-09) **T034 completed (reconciliation pass)**: Client portal getClientTicketDetails payload includes normalized ticket_origin for internal ticket
- (2026-02-09) **T035 completed (reconciliation pass)**: Client portal getClientTicketDetails payload includes normalized ticket_origin for client_portal ticket
- (2026-02-09) **T036 completed (reconciliation pass)**: Client portal getClientTicketDetails payload includes normalized ticket_origin for inbound_email ticket
- (2026-02-09) **T037 completed (reconciliation pass)**: Client portal getClientTicketDetails payload includes normalized ticket_origin for api ticket
- (2026-02-09) **T040 completed (reconciliation pass)**: TicketOriginBadge renders Created Internally label and data attribute for internal
- (2026-02-09) **T041 completed (reconciliation pass)**: TicketOriginBadge renders Created via Client Portal label and data attribute for client_portal
- (2026-02-09) **T042 completed (reconciliation pass)**: TicketOriginBadge renders Created via Inbound Email label and data attribute for inbound_email
- (2026-02-09) **T043 completed (reconciliation pass)**: TicketOriginBadge renders Created via API label and data attribute for api
- (2026-02-09) **T044 completed (reconciliation pass)**: TicketOriginBadge renders safe fallback label for unknown future origin value
- (2026-02-09) **T050 completed (reconciliation pass)**: MSP TicketDetails renders API origin badge when ticket_origin=api
- (2026-02-09) **T051 completed (reconciliation pass)**: MSP TicketDetails renders all other origin badges correctly (internal/client_portal/inbound_email)
- (2026-02-09) **T052 completed (reconciliation pass)**: Client portal TicketDetails renders API origin badge when ticket_origin=api
- (2026-02-09) **T053 completed (reconciliation pass)**: Client portal TicketDetails renders all other origin badges correctly (internal/client_portal/inbound_email)
- (2026-02-09) **T060 completed (reconciliation pass)**: English common locale includes ticket origin keys for internal/client_portal/inbound_email/api/other
- (2026-02-09) **T061 completed (reconciliation pass)**: English clientPortal locale includes ticket origin keys for internal/client_portal/inbound_email/api/other
- (2026-02-09) **T070 completed (reconciliation pass)**: Existing comment ResponseSourceBadge tests continue to pass after ticket-origin changes
- (2026-02-09) **T071 completed (reconciliation pass)**: Existing response-state badge behavior remains unchanged in MSP and client portal ticket details
- (2026-02-09) **T080 completed (reconciliation pass)**: End-to-end: API-created ticket displays Created via API badge in MSP ticket details
- (2026-02-09) **T081 completed (reconciliation pass)**: End-to-end: API-created ticket displays Created via API badge in client portal ticket details when ticket is visible to the client