Danvas
Danvas
DashboardSupportWelcome

👤 USER DOCS

User Guides

Getting Started

Getting StartedDashboard & OnboardingApp Settings

Tutorials

Tutorial: Setting Up Shift Tasks & ClosersTutorial: Managing Incidents in the InboxTutorial: Tracking Compliance & Sync StatusTutorial: Operational Workflows with the AI AssistantTutorial: Building & Deploying Custom Checklists

Daily Operations (Staff)

Shift Workspace & TasksService Day SetupDaily Line-UpStaff Service Day ReportsForms

Communication & Chat

Messages & AnnouncementsUnified Operations InboxAI Assistant

Manager & Admin Guides

Daily Line-Up SetupStaff SchedulingManaging LocationsNPS and Guest FeedbackCouponsContacts and Guest HistoryManager CloseoutsDaily Line-Up & ComplianceAnalyticsIncident ReportingWhistleblower Concerns & FeedbackAdmin Tools

⚙️ DEVELOPER DOCS

Getting Started

Getting StartedDevelopmentDeployment Guide

Architecture

Architecture OverviewExplanation: AI Integration & Tenant SecurityExplanation: Dynamic Forms Engine DesignExplanation: Compliance Ledger DesignExplanation: Live Sync & Data FreshnessData FlowArchitecture Decision Records

Core Domain

Core DomainDatabase ReferenceLocations DomainAuth & RBACScheduling DomainReports DomainIncidents DomainUnified Operations InboxLive Sync Data FreshnessToast Sync PipelineNotifications DomainCoupons and Guest NPSAudit Log & Compliance ArchitectureDesign Audit FindingsAI Chat IntegrationAnalytics & Tips Integration

Frontend

Frontend ArchitectureFormsLoading SkeletonsComponentsPWA & Offline ShellScreenshots

API Reference

API Reference

Endpoints

POS Sales APIOptimization Data APISchedule Shifts APIEmployee Export APIReports APIIncidents APIAI Chat APIPush Notifications APIWebhooks APICron API

Contributing

ContributingCode Examples

Security

Security & Compliance

Release Notes

What's New

Incidents Domain

Technical lifecycle, ownership, and audit trail of operational incidents

Overview

The incidents domain tracks safety and operational issues end-to-end: filing, ownership, status workflow, escalation, commenting, and notification fan-out. Every mutation is captured in the same database transaction as the change, so the audit log never drifts from the data.

Database Schema

Incidents (incidents)

The primary record for an operational issue. Defined in packages/database/src/schema/reports.ts.

ColumnTypeDescription
idtextPrimary key (UUID)
userIdtextThe staff member who reported the incident (FK → users.clerkId)
teamIdtextConfigured logical tenant identifier; no persisted teams table
locationIdtextThe reporting location (FK → locations.id, non-null)
typetextInjury, Near Miss, Spill / Slip Hazard, Complaint, Maintenance, or Stockout (from INCIDENT_TYPES)
severitytextlow, medium, high, or critical (from INCIDENT_SEVERITIES)
priorityinteger1 (critical) / 2 (high) / 3 (default) — auto-derived from severity, overridable from the UI
isInventoryAlertbooleantrue for Stockout reports; bypasses the priority gate for Slack notification
reopenedbooleantrue once a resolved incident is moved back to investigating
statustextreported, investigating, deferred, or resolved (from INCIDENT_STATUSES)
visibilitytextmanager_only (default) or employee_visible (from INCIDENT_VISIBILITIES)
ownerUserIdtextThe staff member who owns resolution (nullable; admin/manager only)
ownedAttimestampWhen ownership was assigned
ownedBytextThe staff member who assigned the owner (audit trail)
datedateIncident date (default: today)
shifttextLunch, Dinner, or Late Night
notestextFree-form description (1–5000 chars)
mediaUrlstext[]Attachments (HEIC auto-converted before upload)
resolutionNotestextRequired when transitioning to resolved
resolvedBytextFK → users.clerkId
resolvedAttimestampSet on resolve, cleared on reopen
deferredReasontextRequired when transitioning to deferred
deferredBytextFK → users.clerkId
deferredAttimestampSet on defer, cleared on move back to investigating
contactIdtextOptional FK → contacts.id for the associated guest
createdAt / updatedAttimestampAuto-set / auto-updated

Incident Comments (incident_comments)

ColumnTypeDescription
incidentIdtextFK → incidents.id (cascade)
userIdtextThe commenter's Clerk ID
teamIdtextTenant scope
authorNametextCached display name (so deletes don't blank history)
contenttextComment text (1–2000 chars)
createdAttimestampComment timestamp

Incident Escalations (incident_escalations)

ColumnTypeDescription
incidentIdtextFK → incidents.id
escalatedBytextFK → users.clerkId
notestextReason for the escalation (required)
createdAttimestampEscalation timestamp

Status Workflow

Defined in apps/app/src/features/incidents/workflow.ts:

FromAllowed toNotes required?
reportedinvestigatingNo
reporteddeferredYes — deferral reason
investigatingresolvedYes — resolution notes
investigatingdeferredYes — reason
deferredinvestigatingNo
resolvedinvestigatingYes — reopen reason (sets reopened = true)

normalizeIncidentStatus translates the legacy in_progress value to investigating so older records still surface cleanly.

Permissions

apps/app/src/features/incidents/permissions.ts is the single source of truth for incident access control.

ActionRule
canReadIncidenthasLocationAccess AND (admin, reporter, employee_visible, or isManagementAtLocation)
canManageIncidentisManagementAtLocation (admin or manager at the location)
canCommentOnIncidentcanReadIncident (anyone with read access)
canEscalateIncidentcanManageIncident
canPublishIncidentcanManageIncident (controls visibility flips)
canOwnIncidentAtLocationadmin/manager role AND the user is in the incident's location scope

Visibility filtering is layered: getIncidents adds an OR clause for userId = self or (if member) visibility = employee_visible, so a non-management user only sees their own reports plus anything marked employee-visible.

Server Actions

All exported from apps/app/src/app/(authenticated)/incidents/actions.ts (split into crud.ts, status.ts, comments.ts, assignment.ts):

ActionAuthPurpose
createIncidentmember (rate-limited)Validate, redact, persist, fan out notification
getIncidentsmember (keyset-paginated)Cursor-based, location-scoped
getIncidentByIdmember (visibility-checked)Full detail incl. comments, escalations, source messages, state changes
updateIncidentStatusmanagementTransition with required notes; transactional audit log
escalateIncidentmanagementInsert incident_escalations row, bump priority, urgent Slack notification
addIncidentCommentmember (read access)Insert comment, audit-log incident.comment_added
assignIncidentOwnermanagementSet ownerUserId to any management user at the location
updateIncidentVisibilitymanagementToggle manager_only ↔ employee_visible
updateIncidentPrioritymanagementOverride the auto-derived priority

Rate limits (limitIncidentMutation in actions/shared.ts): one mutation per incident per ~5 seconds, scoped by (action, locationId).

Notification Pipeline

createIncident, updateIncidentStatus, and escalateIncident enqueue jobs into the notification_queue table (defined in packages/database/src/schema/notifications.ts). The worker (/api/notifications + packages/notifications/incident.ts) processes the queue with:

  • Slack — buildIncidentBlocks from apps/slack-bot/lib/templates/incident.ts posts to the location's incidents Slack channel. Includes Acknowledge / Escalate / View Details buttons.
  • Dedup — per (incidentId, eventType, channel) keys. Repeated enqueues for the same event collapse to a single delivery.

The full set of event types: created, status-changed, escalated, reopened. addIncidentComment does not currently fan out (only the in-app record is added).

Audit Logging

Every incident mutation writes an audit row inside the same database transaction (runAuditedTransaction), so the log can never drift from the data:

ActionWhen
incident.createdOn first submit
incident.status_changedOn every status transition (with previousValue / newValue)
incident.escalatedOn every escalation, with the new priority
incident.comment_addedOn every comment
incident.owner_changedWhen ownership is reassigned
incident.priority_changedWhen the priority is manually overridden
incident.visibility_changedWhen the visibility flag is toggled

The incident detail page reconstructs a "Status History" timeline from the incident.status_changed audit rows, surfacing reopen events with an ember-colored "Reopened" badge.

Source Messages

A message can be converted into an incident via createIncidentFromMessage (in apps/app/src/app/(authenticated)/messages/actions.ts). The new incident:

  • Inherits the message's mediaUrls, location, and audit author.
  • Gets its notes pre-populated with the original message body.
  • Writes a message_incident_links row (messageId, incidentId) so the cross-link survives deletion.
  • Auto-flips the message's state to in_progress if it was new.

The reverse view is in getIncidentById — it joins through messageIncidentLinks to list the source messages on the incident detail.

Related

Incident Reporting Guide

Messages Guide

Anonymous Feedback Guide

Database Schema

Notifications Domain

Reports Domain

Current technical overview of staff and manager report workflows

Unified Operations Inbox

Technical architecture of the Master-Detail operations inbox and messaging feed

On this page

OverviewDatabase SchemaIncidents (incidents)Incident Comments (incident_comments)Incident Escalations (incident_escalations)Status WorkflowPermissionsServer ActionsNotification PipelineAudit LoggingSource MessagesRelated