Skip to content

Feat/admin grant custom reviews - #35

Merged
devtofunmi merged 13 commits into
mainfrom
feat/admin-grant-custom-reviews
Oct 3, 2026
Merged

devtofunmi merged 13 commits into
mainfrom
feat/admin-grant-custom-reviews

Conversation

@devtofunmi

Copy link
Copy Markdown
Owner

Summary

Admins can now give custom review instructions to any Free user from /admin → Users, without changing their plan. Paid plans already include it.

Changes

  • New Custom reviews column in the users table: a switch for Free users, "included" for paid
  • billing.canCustomizeReviews: one check (paid plan or admin grant) used by settings, saving, PR reviews and scans
  • New admin route POST /api/admin/custom-reviews (re-checks on the server that the caller is an admin)
  • Revoking the grant turns the feature off but keeps the user's saved settings

Admins need to give custom review instructions to specific free users
without changing their plan. A new boolean column on workspaces,
custom_reviews_granted (not null, default false), records that grant
separately from billing, so a workspace's plan and its feature grant
stay independent.
Generated with drizzle-kit. Adds workspaces.custom_reviews_granted
(boolean, not null, default false). The change is additive and every
existing workspace starts without the grant. Already applied to the
database.
Schema snapshot generated by drizzle-kit alongside the custom reviews
grant migration, used to diff future migrations.
Adds the 0006_cultured_spirit entry so drizzle-kit migrate applies the
custom reviews grant migration.
WorkspaceBilling gains canCustomizeReviews, true when the workspace is
on a paid plan or an admin has granted the feature. Every place that
gates custom review instructions can now ask billing one question
instead of checking the plan itself.

Adds setCustomReviewsGranted(workspaceId, granted) to turn the grant
on or off.
effectiveReviewGuidance()'s second parameter was named `paid`. It's
now `enabled`, since the guidance also applies to free workspaces with
an admin grant. Behaviour is unchanged.
The settings page shows the Custom review instructions card, and the
server accepts saving it, whenever billing.canCustomizeReviews is true.
That covers paid plans and free workspaces with an admin grant.
Workspaces without either are still rejected on save.
Webhook-triggered reviews resolve the workspace's guidance with
billing.canCustomizeReviews instead of checking for a paid plan, so
admin-granted free workspaces get their instructions and minimum
severity applied.
Scans resolve the workspace's guidance with
billing.canCustomizeReviews, matching PR reviews, so admin-granted
free workspaces get their settings applied to scans too.
- AdminUserRow includes customReviewsGranted, read from
  workspaces.custom_reviews_granted, for the users table.
- setCustomReviewsGrantAsAdmin(workspaceId, granted) grants or revokes
  the feature. As with the other admin actions, the API route re-checks
  the admin before calling it.
POST /api/admin/custom-reviews with { workspaceId, granted } turns
custom review instructions on or off for a workspace. Like the other
admin routes, it re-checks on the server that the caller is an admin
(403 otherwise). It returns 400 unless workspaceId is a string and
granted is a boolean.
Regenerated by the TanStack router generator to include the new admin
API route.
Add a "Custom reviews" column to the admin users table:
- Free workspaces get a switch to grant or revoke the feature. It
  shows the current state, has a tooltip, is disabled while saving,
  and refreshes the table when the save succeeds.
- Paid workspaces show "included", since their plan already has it.
- Users without a workspace show nothing.

The table's minimum width grows to fit the new column.
@vercel

vercel Bot commented Oct 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
jargons-8yyz Ready Ready Preview Oct 3, 2026 4:46am UTC

@jargons-ai jargons-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jargons Jargons review

Found 2 issues worth a look:

🟡 Medium — Inconsistent canCustomizeReviews logic

src/server/workspace.ts:164

The canCustomizeReviews property in WorkspaceBilling is derived from plan !== 'free' || (rows[0]?.customReviewsGranted ?? false). However, in getWorkspaceSettings and updateReviewPreferences, the check !billing.canCustomizeReviews is used to determine if custom review instructions are allowed. This creates a potential inconsistency where customReviewsGranted might be true, but billing.canCustomizeReviews is still false if plan is 'free' and customReviewsGranted is also false. The logic should consistently use the canCustomizeReviews property from WorkspaceBilling after it's been calculated.

Suggestion: Ensure that billing.canCustomizeReviews is correctly calculated and consistently used across all relevant functions. The current implementation of getWorkspaceBilling correctly calculates canCustomizeReviews, so the issue is in how isPaidPlan(billing.plan) was replaced. The updateReviewPreferences function should directly use billing.canCustomizeReviews instead of re-evaluating parts of its logic.

🔵 Low — Missing updatedAt field in workspaces table update

src/server/billing.ts:79

The setCustomReviewsGranted function updates the customReviewsGranted field but does not update the updatedAt timestamp for the workspaces table. This can lead to an inaccurate updatedAt value, which might be used for auditing or caching purposes.

Suggestion: Add updatedAt: new Date() to the .set() call in setCustomReviewsGranted to ensure the updatedAt timestamp is updated whenever the customReviewsGranted field changes.

🔧 Suggested fixes: open the fix PR →

@devtofunmi
devtofunmi merged commit 919d193 into main Oct 3, 2026
2 of 3 checks passed

This branch was successfully deployed

1 active deployment
Preview — 0f4fbdb9 Deployed Oct 3, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant