PSA/docs/ticket_timer_flow.mmd
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

85 lines
3.1 KiB
Plaintext

%% Ticket Timer Flow: Opening a ticket to stopping on leave
%% This sequence shows current behavior with IntervalTrackingService + State Machine + UI setInterval
sequenceDiagram
autonumber
participant U as User
participant TD as TicketDetails (component)
participant HT as useTicketTimeTracking (hook)
participant SM as IntervalTrackingStateMachine
participant IS as IntervalTrackingService (IndexedDB)
participant UIT as UI Timer (setInterval 1s)
participant NAV as Router/Unmount
U->>TD: Open Ticket Details
TD->>TD: mount; derive holderId; memo IntervalTrackingService
TD->>HT: init hook (ticketId, userId, options)
HT->>SM: construct(machine); subscribe(listener)
HT->>SM: refreshLockState()
SM->>IS: isLockedByOther(ticketId, holderId)
IS-->>SM: locked? (true/false)
SM-->>HT: snapshot(state: idle|locked_by_other)
HT-->>TD: isTracking false
rect rgb(250,250,210)
note right of TD: Component-level auto-start attempt (guarded)
TD->>HT: startTracking(false)
end
alt lock acquired
SM->>IS: acquireLock(ticketId,userId,holderId,force?)
IS-->>SM: acquired = true
SM->>IS: startInterval(ticketId, number, title, userId)
IS-->>SM: intervalId
Note over SM,IS: No lock TTL or heartbeat. Simple tracking marker is set.
SM-->>HT: snapshot(state: active, intervalId)
HT-->>TD: isTracking true
TD->>IS: getOpenInterval(ticketId, userId)
IS-->>TD: open interval (startTime)
TD->>TD: seed elapsed = now - startTime (sec)
TD->>UIT: setInterval(1000) start
loop every 1s (visible)
UIT->>TD: tick +1s (elapsed++)
end
else locked by other
IS-->>SM: acquired = false
SM-->>HT: snapshot(state: locked_by_other)
HT-->>TD: isLockedByOther true
TD->>TD: show "Replace here" dialog on Start click
end
par UI lock indicator polling
TD->>HT: refreshLockState() every ~5s
HT->>SM: refreshLockState()
SM->>IS: isLockedByOther(ticketId, holderId)
IS-->>SM: locked? (true/false)
SM-->>HT: snapshot(state)
HT-->>TD: update isLockedByOther
and Page visibility
Note over HT,SM: No visibility handling. Timer is strictly mount/unmount.
end
opt User presses Pause/Stop or navigates away
TD->>HT: stopTracking()
HT->>SM: stop()
SM->>IS: endInterval(intervalId)
SM->>IS: releaseLock(ticketId, holderId)
SM-->>HT: snapshot(state: idle)
HT-->>TD: isTracking false
TD->>UIT: clearInterval()
end
opt BeforeUnload (best-effort sync)
SM->>IS: update interval end + delete lock (IndexedDB direct)
end
NAV->>TD: unmount
TD->>HT: stopTracking() in cleanup
%% Notes on past race conditions / complexity sources
%% - Dual auto-start paths: hook autoStart + component effect -> multiple start attempts
%% - visibility handling removed: tracking tied only to mount/unmount
%% - Heartbeat and lock refresh run concurrently with UI timer, impacting state
%% - Seeding UI elapsed from open interval vs. resetting locally may cause jumps if repeated
%% - IndexedDB operations (locks/intervals) add async timing and error paths