Key workflows across all DIAL user personas
Getting into DIAL — invite-only sign-up, sign-in, and joining an organization
noAuthGuard redirects already-authenticated users directly to the dashboard. There is no public sign-up form — users may only join DIAL via an invite link./invite/:token) is the only way to create a new account. The token is also the Firestore document ID in the invites collection.userProfiles document is written (linked to the invite via inviteId), and the invite's status is updated to accepted. Each invite can only be used once — already-accepted invites show an "Invalid Invite" error.organizationId after joining.
authGuard confirms authentication and the user lands on their role-appropriate dashboard — member view or admin view depending on their isAdmin flag.Creating, completing, and reviewing a DORA capability assessment
status: active and linked to the org.capabilityResponses. The AI chat panel is shown only when the enable_ai_chat feature flag is enabled.assessCapability Cloud Function sends the team's responses to Claude Sonnet. The AI returns a maturity assessment grounded in DORA research.assessmentResults.generateRecommendations produces prioritized improvement recommendations for each capability. Members can drill into any capability for deeper AI guidance.PdfExportService (jsPDF + autotable).Admin-only: setting up the org, teams, and inviting members
organizations collection.teams collection and displayed in org dashboards./invite/:token) is shared with the new member. The invite document ID is the token itself, stored in the invites collection with status: pending and an expiry timestamp. Each invite is single-use — once accepted it cannot be reused.isAdmin flag and orgId. Only admins may change a member's role, organization, or inviteId — members can only update their own safe fields (display name, preferences).generateInsights, and assessment history across all teams.Viewing assessment results, radar charts, and org health at a glance
orgInsights collection.Side-by-side radar charts comparing capability scores across teams — controlled by the enable_compare feature flag
compareGuard redirects to /dashboard if the enable_compare flag is disabled.Viewing past assessments and resuming with pre-populated responses
capabilityResponses can be copied as a starting point for a new assessment, so teams only update what has changed rather than starting from scratch.Exploring a capability in depth with Claude Sonnet — controlled by the enable_ai_chat feature flag
chatAboutRecommendations Cloud Function is invoked. The AI chat panel is only shown when the enable_ai_chat Remote Config flag is enabled; the initial greeting message is also only sent when the flag is active.Firebase Remote Config controls feature availability — no redeployment needed to toggle features
FeatureFlagService is provided at the root level. On construction it calls fetchAndActivate() against Firebase Remote Config, then updates Angular signals (enableAiReports, enableCompare, enableAiChat). Flags re-fetch every 10 seconds.*ngIf="featureFlags.enableCompare()" / *ngIf="featureFlags.enableAiReports()". Route-level compareGuard and aiReportsGuard redirect to /dashboard if a disabled route is accessed directly.true in FLAG_DEFAULTS. If Remote Config is unreachable, the app falls back to these defaults — all features remain available. Defaults are also set as remoteConfig.defaultConfig for SDK-level fallback.FLAG_DEFAULTS in feature-flag.service.ts, expose a signal, and create the matching parameter in the Firebase Remote Config console.
Profile-gated access — having a Firebase Auth account is not enough to read data
hasProfile() helper — not just isAuthenticated() — to guard most collections. A Firebase Auth account alone does not grant data access; a userProfiles document must exist for the user. Profiles are only created by invite or by an admin, making the profile the effective membership gate.isInvitedMemberProfile() rule function validates all of the following before allowing a new profile to be written: the Auth UID matches, the email matches the invite and the Firebase Auth token, the invite exists and is pending and unexpired, the organizationId matches the invite, and the role is member with isAdmin: false.uid, email, role, isAdmin, organizationId, inviteId). Only admins may change these fields. This is enforced by the changesOnlyOwnSafeFields() rule function.get is allowed; list is admin-only. Only the invited person (matching email + pending status) may flip status to accepted — and only that one field pair (status, acceptedAt) may change.