mirror of
https://github.com/documenso/documenso.git
synced 2026-07-25 01:15:49 +10:00
ae07df6061
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.
47 lines
1.5 KiB
TypeScript
47 lines
1.5 KiB
TypeScript
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
|
import { DEFAULT_ANALYTICS_PERIOD, resolveAnalyticsPeriod } from '@documenso/lib/server-only/team/analytics-period';
|
|
import { getTeamById } from '@documenso/lib/server-only/team/get-team';
|
|
import { getTeamAnalytics } from '@documenso/lib/server-only/team/get-team-analytics';
|
|
import { canExecuteTeamAction } from '@documenso/lib/utils/teams';
|
|
|
|
import { authenticatedProcedure } from '../trpc';
|
|
import { ZGetTeamAnalyticsRequestSchema, ZGetTeamAnalyticsResponseSchema } from './get-team-analytics.types';
|
|
|
|
export const getTeamAnalyticsRoute = authenticatedProcedure
|
|
.input(ZGetTeamAnalyticsRequestSchema)
|
|
.output(ZGetTeamAnalyticsResponseSchema)
|
|
.query(async ({ input, ctx }) => {
|
|
const { teamId, period, timezone, senderIds } = input;
|
|
const { user } = ctx;
|
|
|
|
ctx.logger.info({
|
|
input: {
|
|
teamId,
|
|
period,
|
|
senderIds,
|
|
},
|
|
});
|
|
|
|
const team = await getTeamById({ userId: user.id, teamId });
|
|
|
|
// Analytics are restricted to team admins and managers (MANAGE_TEAM).
|
|
if (!canExecuteTeamAction('MANAGE_TEAM', team.currentTeamRole)) {
|
|
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
|
message: 'You are not allowed to view analytics for this team',
|
|
});
|
|
}
|
|
|
|
const { start, end } = resolveAnalyticsPeriod({
|
|
period: period ?? DEFAULT_ANALYTICS_PERIOD,
|
|
timezone,
|
|
});
|
|
|
|
return await getTeamAnalytics({
|
|
userId: user.id,
|
|
teamId,
|
|
periodStart: start,
|
|
periodEnd: end,
|
|
senderIds,
|
|
});
|
|
});
|