Split plaintiffs from people directory#48
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
|
Warning Rate limit exceeded
To continue reviewing without waiting, purchase usage credits in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughThe PR introduces a dedicated "Plaintiffs" feature with new ChangesPlaintiffs Feature and People Directory Restructuring
Sequence DiagramsequenceDiagram
participant User
participant PeoplePage as People<br/>Directory
participant PlaintiffsPage as Plaintiffs<br/>Gallery
participant PlaintiffsManage as Plaintiffs<br/>Manage
participant DataLayer as getPeopleDirectory<br/>Data()
participant DB as Database
User->>PeoplePage: Visit /people with filters
PeoplePage->>DataLayer: getPeopleDirectoryData({role, query, page})
DataLayer->>DB: Prisma query with role/search filters
DB-->>DataLayer: Matching people records
DataLayer-->>PeoplePage: PeopleDirectoryData (paginated)
PeoplePage-->>User: Render directory cards with filters
User->>PlaintiffsPage: Visit /plaintiffs
PlaintiffsPage->>DB: getRepresentedPeopleGalleryData()
DB-->>PlaintiffsPage: Plaintiffs records
PlaintiffsPage-->>User: Render plaintiffs gallery + conversion form
User->>PlaintiffsManage: Visit /plaintiffs/manage
PlaintiffsManage->>DB: Fetch user's represented plaintiffs (auth required)
DB-->>PlaintiffsManage: Rich person data with nested relations
PlaintiffsManage-->>User: Render ManageRepresentedPeopleClient with edit/delete
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 13648d994b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (12)
packages/web/src/components/referendum/VoteCounterSplit.tsx (1)
15-20: ⚡ Quick winStale JSDoc references
/people.The prop comment still describes the link as going to
/peopleand "Invisible Graveyard", but the link now points atROUTES.plaintiffs. Worth updating so the prop name (linkMemorialToPeople) and the doc don't mislead future readers.📝 Suggested doc + prop rename
/** - * If true, wrap the memorial line in a Link to /people so people can scroll - * through the Invisible Graveyard. Default false to keep the component - * passive when memorial linkage isn't desired. + * If true, wrap the memorial line in a Link to /plaintiffs so people can + * scroll through the plaintiff roster. Default false to keep the component + * passive when memorial linkage isn't desired. */ - linkMemorialToPeople?: boolean; + linkMemorialToPlaintiffs?: boolean;Rename callers accordingly (or keep the prop name and just fix the doc if rename churn is undesired).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/web/src/components/referendum/VoteCounterSplit.tsx` around lines 15 - 20, The JSDoc for the VoteCounterSplit prop linkMemorialToPeople is stale (mentions `/people` and "Invisible Graveyard") while the implementation uses ROUTES.plaintiffs; update the doc to accurately describe that enabling this prop wraps the memorial line in a Link to ROUTES.plaintiffs and remove references to `/people`/Invisible Graveyard, and optionally rename the prop to linkMemorialToPlaintiffs (or keep the prop name but ensure all callers and tests reflect the new semantics if you choose to rename); if you rename linkMemorialToPeople to linkMemorialToPlaintiffs, update all usages across the codebase to the new prop name and adjust any related types/props interfaces accordingly.packages/web/src/lib/people-directory.server.ts (1)
137-169: 💤 Low valueName the page-size constants.
limit = 36,takeclampMath.min(Math.max(limit, 1), 60), andtake: 8for the task preview are magic numbers spread across the function. Pulling them into module-level constants (e.g.DEFAULT_PAGE_SIZE,MAX_PAGE_SIZE,TASK_PREVIEW_FETCH_SIZE) makes the bounds reviewable in one place and matches the project's "named constants for magic numbers" rule.As per coding guidelines: "Use named constants for magic numbers and cite the paper source".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/web/src/lib/people-directory.server.ts` around lines 137 - 169, Introduce module-level named constants for the page-size magic numbers and use them in getPeopleDirectoryData: add DEFAULT_PAGE_SIZE = 36, MAX_PAGE_SIZE = 60 and TASK_PREVIEW_FETCH_SIZE = 8 at the top of the module, replace the default param limit = 36 with limit = DEFAULT_PAGE_SIZE, replace the clamp Math.min(Math.max(limit, 1), 60) assigned to take with Math.min(Math.max(limit, 1), MAX_PAGE_SIZE) (or a helper clamp using DEFAULT_PAGE_SIZE/MAX_PAGE_SIZE), and replace any hardcoded take: 8 used for task preview fetches with TASK_PREVIEW_FETCH_SIZE; update references in getPeopleDirectoryData (symbols: getPeopleDirectoryData, limit, take) and the task-preview fetch site to use these new constants.packages/web/src/app/people/page.tsx (5)
17-25: 💤 Low value
ROLE_FILTERSencodes role keys as bare strings.The labels/values are already typed via
PeopleDirectoryRole, so this is mostly cosmetic, but as per coding guidelines: "Use enums instead of magic strings in TypeScript code." Centralizing the role key constants (e.g., as aconstobject exported frompeople-directory.server.tsalongsideparsePeopleDirectoryRole) makes call-site references symbolic and reduces drift if a role key is renamed.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/web/src/app/people/page.tsx` around lines 17 - 25, ROLE_FILTERS uses literal string role keys; replace those magic strings with centralized constants: export a const enum-like object or string consts (e.g., ROLE_KEYS) from people-directory.server.ts alongside parsePeopleDirectoryRole, then update ROLE_FILTERS to reference those exported symbols instead of raw strings (e.g., use ROLE_KEYS.all, ROLE_KEYS.officials, etc.) and keep the label values the same so types (PeopleDirectoryRole) still align.
142-155: 💤 Low valueRepeated indexed access on
verifiedTaskPreview[0].
person.verifiedTaskPreview[0]is read three times in this branch (the conditional, the href, and the title), each of which re-incurs thenoUncheckedIndexedAccessundefined check. Destructuring once keeps the JSX tighter and avoids a stale-index footgun if the array shape ever changes:♻️ Suggested change
- ) : person.verifiedTaskPreview[0] ? ( - <section className="mt-4 border-t border-border pt-4"> - <p className="text-xs font-black uppercase tracking-[0.14em] text-muted-foreground"> - Verified work - </p> - <Link - className="mt-2 block font-black leading-6 underline-offset-4 hover:underline" - href={`${ROUTES.tasks}/${person.verifiedTaskPreview[0].id}`} - > - {person.verifiedTaskPreview[0].title} - </Link> - </section> - ) : null} + ) : null}…and hoist the verified task above the JSX:
const topTask = person.openTaskPreview[0] ?? null; + const verifiedTask = person.verifiedTaskPreview[0] ?? null;then render with
verifiedTaskinstead of repeatedperson.verifiedTaskPreview[0].🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/web/src/app/people/page.tsx` around lines 142 - 155, Hoist the first verified task into a local variable to avoid repeated indexed access: before the JSX branch reference person.verifiedTaskPreview, create a const like verifiedTask = person.verifiedTaskPreview[0] and then replace all uses of person.verifiedTaskPreview[0] in this branch (the conditional, the Link href, and the Link text) with verifiedTask so you only perform the index access once and make the JSX clearer.
62-68: ⚡ Quick win
parsePageduplicated across three pages.The same
parsePagehelper is reproduced verbatim here, inpackages/web/src/app/plaintiffs/page.tsx(lines 99-103, inline), and inpackages/web/src/app/plaintiffs/manage/page.tsx(lines 29-35). As per coding guidelines: "Extract copy-paste code to shared functions." Consider extracting to a shared module (e.g.,@/lib/url) so the parsing rules stay aligned.♻️ Suggested extraction
// packages/web/src/lib/search-params.ts export function parsePageParam(value: string | string[] | undefined): number { const raw = Number.parseInt( Array.isArray(value) ? (value[0] ?? "1") : (value ?? "1"), 10, ); return Number.isFinite(raw) && raw > 0 ? raw : 1; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/web/src/app/people/page.tsx` around lines 62 - 68, The parsePage helper is duplicated across multiple pages (function parsePage in packages/web/src/app/people/page.tsx and identical copies in plaintiffs pages); extract it to a single shared utility (e.g., export function parsePageParam(value: string | string[] | undefined): number in a new module like packages/web/src/lib/search-params.ts) and replace the inline parsePage definitions by importing parsePageParam where used (pages: people/page.tsx, plaintiffs/page.tsx, plaintiffs/manage/page.tsx) so all callers use the same exported function and signature.
228-249: 💤 Low valueSearch form drops
pageand other filter state on submit.The
<form action={ROUTES.people}>only carries the hiddenroleinput and theqfield. When the user submits a search from page 5, the new request goes to page 1 (expected), but if any future filters are added (e.g., country, jurisdiction) they will silently be wiped on every search. Worth a brief comment near this form or a small helper that mirrors the active filter set into hidden inputs the waybuildDirectoryHrefdoes — to prevent future regressions when more filters land.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/web/src/app/people/page.tsx` around lines 228 - 249, The form at the People page (form with action={ROUTES.people}) only submits q and role and therefore drops other active filters and pagination state; update the form submission to include hidden inputs for every active filter the page currently uses (mirror the same logic buildDirectoryHref uses) so future filters like country, jurisdiction, or page are preserved (except explicitly reset page to 1 on new searches if desired); locate the form in packages/web/src/app/people/page.tsx and add hidden inputs for each filter variable (e.g., country, jurisdiction, page, etc.) or create a small helper that iterates current filter state and renders <input type="hidden" name="..." value="..."> for each to ensure all active filters are carried on submit.
86-90: 💤 Low value
tagstyped as(string | null)[]after.filter(Boolean).
Array.prototype.filter(Boolean)does not narrow the result type in TypeScript —tagswill still be(string | null)[], even though at runtime nulls are stripped. With strict mode this happens to work for.joinbut defeats the purpose of.filter. Either use a typed predicate or build the array conditionally:♻️ Suggested change
- const tags = [ - person.isPublicFigure ? "Public official" : null, - person.countryCode, - `${person.publicTaskCount} task${person.publicTaskCount === 1 ? "" : "s"}`, - ].filter(Boolean); + const tags: string[] = [ + person.isPublicFigure ? "Public official" : null, + person.countryCode ?? null, + `${person.publicTaskCount} task${person.publicTaskCount === 1 ? "" : "s"}`, + ].filter((tag): tag is string => Boolean(tag));🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/web/src/app/people/page.tsx` around lines 86 - 90, The tags array currently uses .filter(Boolean) which doesn't narrow away null in TypeScript, leaving tags typed as (string | null)[]; change construction so tags is typed as string[] by either (a) build the array conditionally (only push the "Public official" string when person.isPublicFigure is true and include person.countryCode and the task-count string unconditionally) or (b) use a typed predicate filter (e.g., a function isString(x): x is string) when filtering; update the variable declaration for tags so its inferred type is string[] and ensure all references (tags, person.isPublicFigure, person.countryCode, person.publicTaskCount) still compile.packages/web/src/app/plaintiffs/page.tsx (4)
113-132: 💤 Low valueHard-coded page size duplicated across the file.
pageSize: 24(line 115) and the thresholdfilteredCount >= 24(line 132) both encode the same constant. If the page size ever changes, the browse-tools threshold drifts silently.♻️ Extract a named constant
+const PLAINTIFFS_PAGE_SIZE = 24; @@ - pageSize: 24, + pageSize: PLAINTIFFS_PAGE_SIZE, @@ - const showBrowseTools = hasActiveBrowseState || filteredCount >= 24; + const showBrowseTools = hasActiveBrowseState || filteredCount >= PLAINTIFFS_PAGE_SIZE;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/web/src/app/plaintiffs/page.tsx` around lines 113 - 132, Extract the duplicated numeric literal 24 into a single named constant (e.g., PAGE_SIZE) and use that constant wherever the page size or threshold is referenced; specifically replace the inline pageSize: 24 in the data fetch call and the filteredCount >= 24 check used for showBrowseTools, ensuring hasActiveBrowseState and currentPage logic remain unchanged and any other occurrences of the same literal in this file (or nearby components) use PAGE_SIZE too.
23-49: ⚡ Quick winPrefer a typed enum/object for sort keys to avoid magic strings.
VALID_SORT_KEYSis a tuple of bare string literals reused acrossparseSortand the URL surface. As per coding guidelines: "Use enums instead of magic strings in TypeScript code". A const-asserted record or enum lets call sites reference the keys symbolically and avoids drift between this list andRepresentedPeopleSortKey.♻️ Suggested approach
-const VALID_SORT_KEYS: RepresentedPeopleSortKey[] = [ - "recent", - "oldest", - "alphabetical", - "died-closest-to-cure", -]; +const SORT_KEYS = { + RECENT: "recent", + OLDEST: "oldest", + ALPHABETICAL: "alphabetical", + DIED_CLOSEST_TO_CURE: "died-closest-to-cure", +} as const satisfies Record<string, RepresentedPeopleSortKey>; + +const VALID_SORT_KEYS = Object.values(SORT_KEYS);As per coding guidelines: "Use enums instead of magic strings in TypeScript code".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/web/src/app/plaintiffs/page.tsx` around lines 23 - 49, Replace the magic-string list with a typed symbol collection and use it everywhere: define a const enum or a const-record (e.g., SortKey and SORT_KEYS) whose keys/values map to the same values as RepresentedPeopleSortKey, then change VALID_SORT_KEYS to derive from that symbol collection and update parseSort to validate against SORT_KEYS (and return a member of SortKey/RepresentedPeopleSortKey), and ensure parseEnum's allowed parameter is typed from the same symbol collection so callers use the enum/record symbols instead of raw strings (refer to VALID_SORT_KEYS, parseSort, parseEnum, and RepresentedPeopleSortKey when making these replacements).
134-139: 💤 Low valueFilter loop preserves unknown query params.
Object.entries(params)will carry over any non-pagination query key (e.g.,edit, tracking params, stale keys) into pagination links. Consider whitelisting the keys that participate in filtering (sort,cause,conditionId,conflictId,country,efficacyLag) to avoid leaking unrelated state into navigation.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/web/src/app/plaintiffs/page.tsx` around lines 134 - 139, The loop in page.tsx that iterates over Object.entries(params) (the for...of block setting filterParams) currently copies any non-"page" query keys into pagination links; restrict it to a whitelist of valid filter keys (e.g., "sort", "cause", "conditionId", "conflictId", "country", "efficacyLag") before calling filterParams.set, keeping the existing logic for handling array values (use first element) and excluding empty strings and "page". Update the condition in that loop to check membership in the allowed-keys set so unrelated query params (like "edit" or tracking tags) are not propagated.
60-72: 💤 Low valueConsider
getRouteMetadatafor consistency with sibling pages.
packages/web/src/app/plaintiffs/manage/page.tsxusesgetRouteMetadata({ ...plaintiffsManageLink })while this page reconstructs the title/description manually viagetSiteMetadata. As per coding guidelines: "UsegetRouteMetadata()for page metadata instead of hardcoding titles in Next.js App Router pages and API routes." If site-aware metadata is required here (multi-tenant sites), a comment explaining the divergence helps; otherwise migrate togetRouteMetadatafor the single-source-of-truth on page titles/descriptions.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/web/src/app/plaintiffs/page.tsx` around lines 60 - 72, The current generateMetadata function builds title/description via getSiteMetadata using headers() and getSiteFromHeaders, but sibling page uses getRouteMetadata({ ...plaintiffsManageLink }); either replace the manual construction with a single-source call to getRouteMetadata({ ...plaintiffsLink }) (and use ROUTES.plaintiffs where appropriate) to match other pages, or if multi-tenant/site-aware metadata is required keep getSiteMetadata but add a clear comment above generateMetadata explaining why it diverges from getRouteMetadata (mentioning headers(), getSiteFromHeaders(), and plaintiffsLink) so reviewers know the difference.packages/web/src/app/plaintiffs/manage/page.tsx (1)
103-107: 💤 Low valueType-narrowing of
userIdafterredirect.
redirect()is typed asneverin Next.js, so TypeScript should narrowuserIdtostringbelow. With strict mode +noUncheckedIndexedAccessthis is fine, but if you ever see TS complaining aboutuserIdpossibly beingundefinedfurther down (e.g., inside thewhereclause at line 122), an explicit assertion (if (!userId) { redirect(...); return; }) makes the narrowing unambiguous.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/web/src/app/plaintiffs/manage/page.tsx` around lines 103 - 107, The check that calls redirect(getSignInPath(ROUTES.plaintiffsManage)) doesn't make the TypeScript narrowing of session?.user.id (userId) unambiguous in all compiler configurations; update the guard in the async page function around getServerSession(authOptions) so that after calling redirect(...) you also stop execution (e.g., return) or use an explicit assertion pattern (if (!userId) { redirect(...); return; }) so that userId is definitely narrowed to string for subsequent usage (e.g., the where clause that references userId).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/web/src/app/plaintiffs/manage/page.tsx`:
- Around line 197-230: The fallback value for memorial cause category uses the
literal "UNKNOWN" which can drift from the enum; update the mapping in
editablePeople so person.memorial?.causeCategory falls back to the enum constant
(PersonDeathCauseCategory.UNKNOWN) instead of the string literal, ensuring
imports include PersonDeathCauseCategory and updating the expression that
assigns causeCategory to use that enum symbol.
In `@packages/web/src/app/plaintiffs/page.tsx`:
- Around line 140-145: buildPageUrl currently returns a bare "?" when there are
no filterParams and target === 1, which leads to surprising Link navigation;
update buildPageUrl to return the current pathname (no "?" query) when qs is
empty instead of "?", e.g. obtain the pathname via Next.js usePathname() at the
top of the component and have buildPageUrl return that pathname when
next.toString() is empty, keeping the existing behavior of `?${qs}` when qs
exists; this change targets the buildPageUrl function and the filterParams
usage.
In `@packages/web/src/lib/people-directory.server.ts`:
- Around line 183-241: The assignedTasks relation selection currently returns up
to 8 rows ordered by status, so post-fetch filters like the verifiedTaskPreview
(.filter(...).slice(...)) can be empty when the 8 rows are dominated by the
other status; change the query to request two separate, status-scoped selections
instead of one shared assignedTasks window — e.g. add two separate relation
selects (or aliased includes) like assignedTasksActive (where: { status:
TaskStatus.ACTIVE }, take: 3, orderBy: { createdAt: "desc" }) and
assignedTasksVerified (where: { status: TaskStatus.VERIFIED }, take: 2, orderBy:
{ createdAt: "desc" }) and then map openTaskPreview from assignedTasksActive and
verifiedTaskPreview from assignedTasksVerified (removing the post-fetch
.filter/.slice). Ensure you keep publicAssignedTaskWhere/_count usage as needed
and update references to person.assignedTasks to the new aliased fields.
---
Nitpick comments:
In `@packages/web/src/app/people/page.tsx`:
- Around line 17-25: ROLE_FILTERS uses literal string role keys; replace those
magic strings with centralized constants: export a const enum-like object or
string consts (e.g., ROLE_KEYS) from people-directory.server.ts alongside
parsePeopleDirectoryRole, then update ROLE_FILTERS to reference those exported
symbols instead of raw strings (e.g., use ROLE_KEYS.all, ROLE_KEYS.officials,
etc.) and keep the label values the same so types (PeopleDirectoryRole) still
align.
- Around line 142-155: Hoist the first verified task into a local variable to
avoid repeated indexed access: before the JSX branch reference
person.verifiedTaskPreview, create a const like verifiedTask =
person.verifiedTaskPreview[0] and then replace all uses of
person.verifiedTaskPreview[0] in this branch (the conditional, the Link href,
and the Link text) with verifiedTask so you only perform the index access once
and make the JSX clearer.
- Around line 62-68: The parsePage helper is duplicated across multiple pages
(function parsePage in packages/web/src/app/people/page.tsx and identical copies
in plaintiffs pages); extract it to a single shared utility (e.g., export
function parsePageParam(value: string | string[] | undefined): number in a new
module like packages/web/src/lib/search-params.ts) and replace the inline
parsePage definitions by importing parsePageParam where used (pages:
people/page.tsx, plaintiffs/page.tsx, plaintiffs/manage/page.tsx) so all callers
use the same exported function and signature.
- Around line 228-249: The form at the People page (form with
action={ROUTES.people}) only submits q and role and therefore drops other active
filters and pagination state; update the form submission to include hidden
inputs for every active filter the page currently uses (mirror the same logic
buildDirectoryHref uses) so future filters like country, jurisdiction, or page
are preserved (except explicitly reset page to 1 on new searches if desired);
locate the form in packages/web/src/app/people/page.tsx and add hidden inputs
for each filter variable (e.g., country, jurisdiction, page, etc.) or create a
small helper that iterates current filter state and renders <input type="hidden"
name="..." value="..."> for each to ensure all active filters are carried on
submit.
- Around line 86-90: The tags array currently uses .filter(Boolean) which
doesn't narrow away null in TypeScript, leaving tags typed as (string | null)[];
change construction so tags is typed as string[] by either (a) build the array
conditionally (only push the "Public official" string when person.isPublicFigure
is true and include person.countryCode and the task-count string
unconditionally) or (b) use a typed predicate filter (e.g., a function
isString(x): x is string) when filtering; update the variable declaration for
tags so its inferred type is string[] and ensure all references (tags,
person.isPublicFigure, person.countryCode, person.publicTaskCount) still
compile.
In `@packages/web/src/app/plaintiffs/manage/page.tsx`:
- Around line 103-107: The check that calls
redirect(getSignInPath(ROUTES.plaintiffsManage)) doesn't make the TypeScript
narrowing of session?.user.id (userId) unambiguous in all compiler
configurations; update the guard in the async page function around
getServerSession(authOptions) so that after calling redirect(...) you also stop
execution (e.g., return) or use an explicit assertion pattern (if (!userId) {
redirect(...); return; }) so that userId is definitely narrowed to string for
subsequent usage (e.g., the where clause that references userId).
In `@packages/web/src/app/plaintiffs/page.tsx`:
- Around line 113-132: Extract the duplicated numeric literal 24 into a single
named constant (e.g., PAGE_SIZE) and use that constant wherever the page size or
threshold is referenced; specifically replace the inline pageSize: 24 in the
data fetch call and the filteredCount >= 24 check used for showBrowseTools,
ensuring hasActiveBrowseState and currentPage logic remain unchanged and any
other occurrences of the same literal in this file (or nearby components) use
PAGE_SIZE too.
- Around line 23-49: Replace the magic-string list with a typed symbol
collection and use it everywhere: define a const enum or a const-record (e.g.,
SortKey and SORT_KEYS) whose keys/values map to the same values as
RepresentedPeopleSortKey, then change VALID_SORT_KEYS to derive from that symbol
collection and update parseSort to validate against SORT_KEYS (and return a
member of SortKey/RepresentedPeopleSortKey), and ensure parseEnum's allowed
parameter is typed from the same symbol collection so callers use the
enum/record symbols instead of raw strings (refer to VALID_SORT_KEYS, parseSort,
parseEnum, and RepresentedPeopleSortKey when making these replacements).
- Around line 134-139: The loop in page.tsx that iterates over
Object.entries(params) (the for...of block setting filterParams) currently
copies any non-"page" query keys into pagination links; restrict it to a
whitelist of valid filter keys (e.g., "sort", "cause", "conditionId",
"conflictId", "country", "efficacyLag") before calling filterParams.set, keeping
the existing logic for handling array values (use first element) and excluding
empty strings and "page". Update the condition in that loop to check membership
in the allowed-keys set so unrelated query params (like "edit" or tracking tags)
are not propagated.
- Around line 60-72: The current generateMetadata function builds
title/description via getSiteMetadata using headers() and getSiteFromHeaders,
but sibling page uses getRouteMetadata({ ...plaintiffsManageLink }); either
replace the manual construction with a single-source call to getRouteMetadata({
...plaintiffsLink }) (and use ROUTES.plaintiffs where appropriate) to match
other pages, or if multi-tenant/site-aware metadata is required keep
getSiteMetadata but add a clear comment above generateMetadata explaining why it
diverges from getRouteMetadata (mentioning headers(), getSiteFromHeaders(), and
plaintiffsLink) so reviewers know the difference.
In `@packages/web/src/components/referendum/VoteCounterSplit.tsx`:
- Around line 15-20: The JSDoc for the VoteCounterSplit prop
linkMemorialToPeople is stale (mentions `/people` and "Invisible Graveyard")
while the implementation uses ROUTES.plaintiffs; update the doc to accurately
describe that enabling this prop wraps the memorial line in a Link to
ROUTES.plaintiffs and remove references to `/people`/Invisible Graveyard, and
optionally rename the prop to linkMemorialToPlaintiffs (or keep the prop name
but ensure all callers and tests reflect the new semantics if you choose to
rename); if you rename linkMemorialToPeople to linkMemorialToPlaintiffs, update
all usages across the codebase to the new prop name and adjust any related
types/props interfaces accordingly.
In `@packages/web/src/lib/people-directory.server.ts`:
- Around line 137-169: Introduce module-level named constants for the page-size
magic numbers and use them in getPeopleDirectoryData: add DEFAULT_PAGE_SIZE =
36, MAX_PAGE_SIZE = 60 and TASK_PREVIEW_FETCH_SIZE = 8 at the top of the module,
replace the default param limit = 36 with limit = DEFAULT_PAGE_SIZE, replace the
clamp Math.min(Math.max(limit, 1), 60) assigned to take with
Math.min(Math.max(limit, 1), MAX_PAGE_SIZE) (or a helper clamp using
DEFAULT_PAGE_SIZE/MAX_PAGE_SIZE), and replace any hardcoded take: 8 used for
task preview fetches with TASK_PREVIEW_FETCH_SIZE; update references in
getPeopleDirectoryData (symbols: getPeopleDirectoryData, limit, take) and the
task-preview fetch site to use these new constants.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: b48c46ba-da5b-4629-a149-ebf0b00a8865
📒 Files selected for processing (19)
packages/web/src/app/governments/[code]/page.tsxpackages/web/src/app/people/[id]/page.tsxpackages/web/src/app/people/manage/page.tsxpackages/web/src/app/people/page.tsxpackages/web/src/app/plaintiffs/manage/page.tsxpackages/web/src/app/plaintiffs/page.tsxpackages/web/src/components/landing/FinalCTASection.tsxpackages/web/src/components/landing/WhyPlaySection.tsxpackages/web/src/components/medical/medical-pages.tsxpackages/web/src/components/people/ManageRepresentedPeopleClient.tsxpackages/web/src/components/people/RepresentedPersonConversionForm.tsxpackages/web/src/components/people/RepresentedPersonForm.tsxpackages/web/src/components/referendum/VoteCounterSplit.tsxpackages/web/src/lib/__tests__/url.test.tspackages/web/src/lib/people-directory.server.tspackages/web/src/lib/routes.tspackages/web/src/lib/site-sitemap.tspackages/web/src/lib/site.tspackages/web/src/lib/url.ts
Summary
/plaintiffsand/plaintiffs/managefor the Humanity v. Government plaintiff intake, gallery, and management flow./peopleas a searchable coordination directory for humans with assigned public tasks, role filters, profile/task links, and bottom pagination./people/manageto/plaintiffs/manageand points memorial/plaintiff CTAs at/plaintiffs.Validation
pnpm --filter @optimitron/web run typecheck:fastpnpm --filter @optimitron/web exec vitest run src/lib/__tests__/url.test.ts src/config/__tests__/site-variant-ui.test.tspackages/web/output/playwright/route-split-audit/review.htmlScreenshot Notes
/people,/people?role=legal&q=law,/plaintiffs,/plaintiffs/manage, and/people/[id]./peopleis intentionally now a coordination directory;/plaintiffskeeps the treaty-for-someone-who-can't conversion flow.Summary by CodeRabbit
Release Notes