Feat/add gh waitlist#81
Conversation
Replace the email-only waitlist with an admin-reviewed access queue: - accessStatus/accessReviewedAt/accessReviewedBy/waitlistedAt on user (drizzle schema + better-auth additionalFields, server-only input) - new signups land pending; signups-paused block removed; prior email waitlist entries flagged with their original join date - approvedProcedure tRPC gate (also on org/admin procedures) behind ACCESS_GATE_ENABLED, with DB re-check for stale sessions - accessRequests admin router: list/approve/reject/bulkApprove + lazy GitHub metadata; Access Requests table in _admin - /queue status page for pending users, distinct rejected copy; useAccessGate client redirect in the app shell - packages/email: @opencoredev/email-sdk client (Resend, retries) and approval template; access/approved Inngest job with two-layer idempotency so bulk-approve retries never double-send - backfill + waitlist migration-email scripts; landing/login CTA copy swapped to request-access framing
The marketing landing page lives in the tripwire-landing repo now. The app root just routes by session: signed-in users to /home, everyone else to /login. Removes components/layout/landing (header, waitlist form, faulty-terminal shader, space-invaders easter egg). The waitlist tRPC router and table are intentionally untouched — they stay live through the email-migration claim window.
|
❌ Comp AI code review failed. Comment Posted by Comp AI Code Reviews. |
|
React Doctor found 11 new issues in 5 files · 5 errors & 6 warnings · score 40 / 100 (Critical) · 1 fixed · vs Errors
6 warnings
Reviewed by React Doctor for commit |
| .from(userTable) | ||
| .where(where) | ||
|
|
||
| const rows = await db |
There was a problem hiding this comment.
React Doctor · react-doctor/server-sequential-independent-await (warning)
This await doesn't use the previous result, so your users wait twice as long for nothing.
Fix → These two awaits don't depend on each other. Wrap them in Promise.all([...]) so they run at the same time.
| ), | ||
| }) | ||
|
|
||
| const accountAge = useMemo(() => { |
There was a problem hiding this comment.
React Doctor · react-hooks-js/preserve-manual-memoization (error)
This component misses React Compiler's automatic memoization & re-renders more than it should: Compilation Skipped: Existing memoization could not be preserved. Rewrite the flagged code so the compiler can optimize it.
Fix → React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was meta.data.accountCreatedAt, but the source dependencies were [meta.data?.accountCreatedAt]. Inferred different dependency than source.
| </div> | ||
| {row.waitlistedAt ? ( | ||
| <span | ||
| title={`On the email waitlist since ${new Date( |
There was a problem hiding this comment.
React Doctor · react-doctor/no-locale-format-in-render (warning)
This can cause a hydration mismatch because toLocaleDateString() formats with the server's locale and timezone during server rendering but the user's in the browser. Format it in a post-mount useEffect, or pass an explicit locale and timeZone.
Fix → Format locale/timezone-dependent values in a post-mount useEffect + state, or pass an explicit locale and timeZone so the server and the browser render the same text. Only runs on SSR-capable projects.
| * redirect, or COOP severed window.opener — it falls back to navigating in | ||
| * place (there's no popup to close in that case). | ||
| */ | ||
| export const Route = createFileRoute("/oauth/popup-callback")({ |
There was a problem hiding this comment.
React Doctor · react-doctor/tanstack-start-route-property-order (error)
Ordering route property "validateSearch" after "headers" breaks type inference.
Fix → Follow the route property order because TanStack Router's type inference depends on earlier properties feeding later ones.
| if (error) { | ||
| done.current = true | ||
| if (postToOpener({ type: MESSAGE_TYPE, status: "error" }, targetOrigin)) { | ||
| setView({ kind: "error" }) |
There was a problem hiding this comment.
React Doctor · react-hooks-js/set-state-in-effect (error)
This component misses React Compiler's automatic memoization & re-renders more than it should: Calling setState synchronously within an effect can trigger cascading renders. Rewrite the flagged code so the compiler can optimize it.
Fix → Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
- Update external systems with the latest state from React.
- Subscribe for updates from some external system, calling setState in a callback function when external state changes.
Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).
| ) | ||
| } | ||
|
|
||
| function Heading({ children }: { children: React.ReactNode }) { |
There was a problem hiding this comment.
React Doctor · react-doctor/no-multi-comp (warning)
This file declares several components, so each component is harder to find, test, and change.
Fix → Move secondary components into their own files so each component stays easier to find, test, and change.
| ) | ||
| } | ||
|
|
||
| function Body({ children }: { children: React.ReactNode }) { |
There was a problem hiding this comment.
React Doctor · react-doctor/no-multi-comp (warning)
This file declares several components, so each component is harder to find, test, and change.
Fix → Move secondary components into their own files so each component stays easier to find, test, and change.
| ) | ||
| } | ||
|
|
||
| function CloseButton() { |
There was a problem hiding this comment.
React Doctor · react-doctor/no-multi-comp (warning)
This file declares several components, so each component is harder to find, test, and change.
Fix → Move secondary components into their own files so each component stays easier to find, test, and change.
| * threading through the opener origin so the closer knows exactly which window | ||
| * (and origin) to notify. This is standard OAuth machinery, hence /oauth/*. | ||
| */ | ||
| export const Route = createFileRoute("/oauth/waitlist")({ |
There was a problem hiding this comment.
React Doctor · react-doctor/tanstack-start-route-property-order (error)
Ordering route property "validateSearch" after "headers" breaks type inference.
Fix → Follow the route property order because TanStack Router's type inference depends on earlier properties feeding later ones.
| import { useTRPC } from "#/integrations/trpc/react" | ||
| import { buildSeo, formatPageTitle, PRIVATE_ROUTE_HEADERS } from "#/lib/seo" | ||
|
|
||
| export const Route = createFileRoute("/queue")({ |
There was a problem hiding this comment.
React Doctor · react-doctor/tanstack-start-route-property-order (error)
Ordering route property "head" after "headers" breaks type inference.
Fix → Follow the route property order because TanStack Router's type inference depends on earlier properties feeding later ones.
Summary
Checklist
Notes for reviewers