fix: admin organisation limits and usage UI

This commit is contained in:
Catalin Pit
2026-06-22 17:50:49 +03:00
parent 2f24a8eab2
commit 8403d6cdca
6 changed files with 222 additions and 219 deletions
@@ -1,4 +1,6 @@
import { QUOTA_WARNING_THRESHOLD } from './get-quota-alert-kind';
import { isQuotaExceeded, isQuotaNearing, QUOTA_WARNING_THRESHOLD } from '../../universal/quota-usage';
export { QUOTA_WARNING_THRESHOLD };
export type QuotaFlags = {
isDocumentQuotaExceeded: boolean;
@@ -22,39 +24,6 @@ type ComputeQuotaFlagsOptions = {
};
};
/**
* A quota of `null` means unlimited (never exceeded). A quota of `0` means
* blocked (always exceeded). Otherwise usage `>=` quota is exceeded.
*/
const isQuotaExceeded = (quota: number | null, usage: number): boolean => {
if (quota === null) {
return false;
}
if (quota === 0) {
return true;
}
return usage >= quota;
};
/**
* A counter is "nearing" its quota once usage reaches the warning threshold
* (80% of the quota, rounded up) but has not yet been exceeded. Nearing and
* exceeded are mutually exclusive per counter.
*/
const isQuotaNearing = (quota: number | null, usage: number): boolean => {
if (quota === null || quota === 0) {
return false;
}
if (isQuotaExceeded(quota, usage)) {
return false;
}
return usage >= Math.ceil(quota * QUOTA_WARNING_THRESHOLD);
};
export const computeQuotaFlags = ({ quotas, usage }: ComputeQuotaFlagsOptions): QuotaFlags => {
return {
isDocumentQuotaExceeded: isQuotaExceeded(quotas.documentQuota, usage?.documentCount ?? 0),
@@ -1,4 +1,6 @@
export const QUOTA_WARNING_THRESHOLD = 0.8;
import { QUOTA_WARNING_THRESHOLD } from '../../universal/quota-usage';
export { QUOTA_WARNING_THRESHOLD };
export type QuotaAlertKind = 'quota' | 'quotaNearing';
+31 -8
View File
@@ -6,14 +6,37 @@ import { z } from 'zod';
*
* Example: "5m", "1h", "1d"
*/
export const ZRateLimitWindowSchema = z.string().regex(/^\d+[smhd]$/);
export const RATE_LIMIT_WINDOW_REGEX = /^\d+[smhd]$/;
export const ZRateLimitArraySchema = z.array(
z.object({
window: ZRateLimitWindowSchema,
max: z.number().int().positive(),
}),
);
export const ZRateLimitWindowSchema = z.string().trim().regex(RATE_LIMIT_WINDOW_REGEX, {
message: 'Use a duration with a unit, e.g. 5m, 1h, or 24h',
});
export const ZRateLimitArraySchema = z
.array(
z.object({
window: ZRateLimitWindowSchema,
max: z.number().int().positive(),
}),
)
.superRefine((entries, ctx) => {
const windows = new Map<string, number>();
entries.forEach((entry, index) => {
const window = entry.window.trim();
const duplicateIndex = windows.get(window);
if (duplicateIndex !== undefined) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Use a unique window for each rate limit',
path: [index, 'window'],
});
}
windows.set(window, index);
});
});
export type TRateLimitArray = z.infer<typeof ZRateLimitArraySchema>;
@@ -52,7 +75,7 @@ export const ZClaimFlagsSchema = z.object({
signingReminders: z.boolean().optional(),
cscQesSigning: z.boolean().optional(),
/**
* Controls whether an organisation is prevented from sending emails.
*