feat(team): add team analytics dashboard

Add a team document-usage dashboard at /t/:teamUrl/analytics for team admins and managers,
behind the NEXT_PUBLIC_FEATURE_TEAM_ANALYTICS_ENABLED rollout flag (enabled by default, set to
"false" to gate it off).

Backend:
- getTeamAnalytics Kysely query over team-produced documents across all folders, with exact
  COUNT(*) (no STATS_COUNT_CAP). Each metric uses its own date axis: Sent/Draft/Pending by
  createdAt, Completed by Envelope.completedAt, Declined by the DOCUMENT_RECIPIENT_REJECTED
  audit-log timestamp.
- resolveAnalyticsPeriod turns calendar presets into half-open [start, end) ranges in the
  viewer's timezone, falling back to UTC.
- team.getAnalytics tRPC route gated to ADMIN/MANAGER.

Frontend:
- Standalone /t/:teamUrl/analytics route whose loader gates the flag and role, silently
  redirecting members to documents.
- Headline metrics and compact stat tiles, a member multiselect filter, a calendar-preset
  period selector, and an empty state.
- Role- and flag-gated nav entries in the desktop and mobile navigation.

Tests:
- Unit tests for the period resolver (timezone and preset boundaries).
- Integration/E2E tests for the query semantics (date axes, audit-log decline, all-folders
  aggregation, sender attribution), access control, filters and the empty state.
This commit is contained in:
ephraimduncan
2026-05-29 13:52:29 +00:00
parent 22ceff43e3
commit ae07df6061
14 changed files with 1002 additions and 3 deletions
+9
View File
@@ -15,6 +15,15 @@ export const NEXT_PRIVATE_INTERNAL_WEBAPP_URL = () =>
export const IS_BILLING_ENABLED = () => env('NEXT_PUBLIC_FEATURE_BILLING_ENABLED') === 'true';
/**
* Team analytics dashboard rollout flag.
*
* Acts as a kill-switch: enabled by default and disabled only when the env var
* is explicitly set to "false". This keeps the feature available to all teams
* while leaving a single lever to gate it off during rollout.
*/
export const IS_TEAM_ANALYTICS_ENABLED = () => env('NEXT_PUBLIC_FEATURE_TEAM_ANALYTICS_ENABLED') !== 'false';
export const API_V2_BETA_URL = '/api/v2-beta';
export const API_V2_URL = '/api/v2';