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
85 lines
3.1 KiB
Plaintext
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
|