Conversation
There was a problem hiding this comment.
Pull request overview
Adds superadmin-facing controls for managing trial accounts by introducing a per-invitation trialDays duration and a superadmin action to clear a user’s trial.
Changes:
- Add
trialDaysto invitations, defaulting toDEFAULT_TRIAL_PERIOD_DAYSfor trial invites. - Allow superadmins to create non-trial invites by default, or create trial invites with custom duration.
- Add a superadmin “clear trial” control in the dashboard (server mutation + UI).
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/server/trpc/routers/invitation.test.ts | Expands invitation creation tests to cover trialDays defaults and superadmin custom trials. |
| tests/unit/server/trpc/routers/confirmInvite.test.ts | Adds coverage for custom trial durations affecting trialEndsAt. |
| src/server/trpc/routers/user.ts | Adds user.clearTrial superadmin mutation. |
| src/server/trpc/routers/invitation.ts | Extends invitation creation input and persists isTrial/trialDays. |
| src/server/trpc/routers/auth.ts | Computes trialEndsAt based on invitation trial duration. |
| src/server/services/database/schema.ts | Updates DB schema typings for Invitation.trialDays. |
| src/server/repositories/User.ts | Adds clearUserTrial() repository method. |
| src/server/models/User.ts | Adjusts trialEndsAt column type to allow null writes. |
| src/server/models/Invitation.ts | Adds trialDays to Kysely invitation table typing. |
| src/models/Invitation.ts | Extends shared Zod invitation schema with trialDays. |
| src/app/(private)/(dashboards)/superadmin/page.tsx | Adds “Trial” column with “Upgrade” action that clears trial. |
| src/app/(private)/(dashboards)/invite-organisation/CreateInvitationModal.tsx | Adds superadmin-only trial checkbox + duration input, sending isTrial/trialDays. |
| migrations/1776353491107_invitation_trial_days.ts | Adds trial_days column to invitation table. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| .updateTable("user") | ||
| .where("id", "=", id) | ||
| .set({ trialEndsAt: null }) | ||
| .returningAll() |
There was a problem hiding this comment.
clearUserTrial() uses returningAll(). Even though password hashes are stripped by the SuperJSON serializer, returning the entire row is still more data than needed for this action. Consider returning only the fields needed by callers (or returning nothing) to minimise accidental data exposure and reduce payload size.
| .returningAll() | |
| .returning(["id", "trialEndsAt"]) |
| clearTrial: superadminProcedure | ||
| .input(z.object({ userId: z.string().uuid() })) | ||
| .mutation(async ({ input }) => { | ||
| return clearUserTrial(input.userId); |
There was a problem hiding this comment.
clearTrial currently returns the full updated user record from the repository, but the caller (UI) doesn’t use the response. Consider returning void (or a minimal { id, trialEndsAt }) to keep the API surface small and avoid unnecessary data transfer.
| return clearUserTrial(input.userId); | |
| await clearUserTrial(input.userId); |
| isTrial: z.boolean().optional(), | ||
| trialDays: z.number().int().min(1).optional(), | ||
| }) |
There was a problem hiding this comment.
trialDays can be provided while isTrial is false/omitted (superadmin default), and it will be silently ignored. To prevent client mistakes and make the API easier to use, consider a zod refinement that rejects trialDays unless isTrial === true (and possibly rejects isTrial === true with trialDays missing if you want it explicit).
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
No description provided.