fix: update new quota and rates UX (#2954)

This commit is contained in:
David Nguyen
2026-06-08 14:14:22 +10:00
committed by GitHub
parent 03b5fe6117
commit 8448e333cf
22 changed files with 459 additions and 110 deletions
@@ -0,0 +1,55 @@
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { computeQuotaFlags } from '@documenso/lib/server-only/rate-limit/compute-quota-flags';
import { currentMonthlyPeriod } from '@documenso/lib/universal/monthly-period';
import { buildOrganisationWhereQuery } from '@documenso/lib/utils/organisations';
import { prisma } from '@documenso/prisma';
import { authenticatedProcedure } from '../trpc';
import {
ZGetOrganisationQuotaFlagsRequestSchema,
ZGetOrganisationQuotaFlagsResponseSchema,
} from './get-organisation-quota-flags.types';
export const getOrganisationQuotaFlagsRoute = authenticatedProcedure
.input(ZGetOrganisationQuotaFlagsRequestSchema)
.output(ZGetOrganisationQuotaFlagsResponseSchema)
.query(async ({ input, ctx }) => {
const { organisationId } = input;
const userId = ctx.user.id;
ctx.logger.info({
input: {
organisationId,
},
});
// Any member of the organisation may view quota usage flags.
const organisation = await prisma.organisation.findFirst({
where: buildOrganisationWhereQuery({
organisationId,
userId,
}),
include: {
organisationClaim: true,
monthlyStats: {
where: {
period: currentMonthlyPeriod(),
},
},
},
});
if (!organisation) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Organisation not found',
});
}
return computeQuotaFlags({
quotas: {
documentQuota: organisation.organisationClaim.documentQuota,
emailQuota: organisation.organisationClaim.emailQuota,
apiQuota: organisation.organisationClaim.apiQuota,
},
usage: organisation.monthlyStats[0] ?? undefined,
});
});
@@ -0,0 +1,17 @@
import { z } from 'zod';
export const ZGetOrganisationQuotaFlagsRequestSchema = z.object({
organisationId: z.string().describe('The ID of the organisation.'),
});
/**
* Booleans only. Raw usage counts and quota caps are intentionally never
* surfaced to the client.
*/
export const ZGetOrganisationQuotaFlagsResponseSchema = z.object({
isDocumentQuotaExceeded: z.boolean(),
isEmailQuotaExceeded: z.boolean(),
isApiQuotaExceeded: z.boolean(),
});
export type TGetOrganisationQuotaFlagsResponse = z.infer<typeof ZGetOrganisationQuotaFlagsResponseSchema>;
@@ -14,6 +14,7 @@ import { findOrganisationMemberInvitesRoute } from './find-organisation-member-i
import { findOrganisationMembersRoute } from './find-organisation-members';
import { getOrganisationRoute } from './get-organisation';
import { getOrganisationMemberInvitesRoute } from './get-organisation-member-invites';
import { getOrganisationQuotaFlagsRoute } from './get-organisation-quota-flags';
import { getOrganisationSessionRoute } from './get-organisation-session';
import { getOrganisationsRoute } from './get-organisations';
import { leaveOrganisationRoute } from './leave-organisation';
@@ -26,6 +27,7 @@ import { updateOrganisationSettingsRoute } from './update-organisation-settings'
export const organisationRouter = router({
get: getOrganisationRoute,
getMany: getOrganisationsRoute,
getQuotaFlags: getOrganisationQuotaFlagsRoute,
create: createOrganisationRoute,
update: updateOrganisationRoute,
delete: deleteOrganisationRoute,