refactor: replace individual Async*MultiSelect components with consolidated Async*FilterSelect components and adjust query logic for filters#375
Conversation
…idated Async*FilterSelect components and adjust query logic for filters
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughUnifies env flags and Sentry gating; adds async options endpoints and UI; updates repositories, pagination, and schemas; enhances human verification and spam detection; adds DB indexes and seeders; revises analytics, CORS, proxy, and tests/CI/tooling. ChangesEnv, Sentry, async options, repositories, HV, and data seeding
Sequence Diagram(s)sequenceDiagram
participant Client
participant TRPC as TRPC options endpoint
participant Repo
participant DB
Client->>TRPC: gpus.options({ search, limit, offset })
TRPC->>Repo: options(filters)
Repo->>DB: findMany(select: option, orderBy, skip/take)
DB-->>Repo: results (+extra row)
Repo-->>TRPC: { gpus, hasMore }
TRPC-->>Client: { gpus, hasMore }
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related issues
Possibly related PRs
✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
|
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (8)
src/app/admin/devices/components/DeviceModal.tsx (1)
23-24: 🏗️ Heavy liftReplace the 1000-item preload with real async lookup.
Line 24 still preloads a large SoC list in the modal path, which can degrade open-time performance as data grows. Implement a search-driven async fetch instead of bulk loading and remove the TODO once done.
🤖 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 `@src/app/admin/devices/components/DeviceModal.tsx` around lines 23 - 24, The modal currently preloads 1000 SoC options via the call to api.socs.options.useQuery({ limit: 1000 }) (socsQuery) which must be replaced with a search-driven async fetch: remove the hardcoded limit call, add a debounced search state in DeviceModal and call api.socs.options.useQuery or an equivalent endpoint with the search term (e.g., { q: searchTerm, limit: 50 }) when the user types, wire those results into the select component (or use an AsyncSelect pattern) to load options on demand, ensure loading/error states are handled, and remove the TODO comment once complete.sentry.edge.config.ts (1)
8-20: ⚡ Quick winTrim explanatory inline comments in this block.
Keep only comments that capture non-obvious external constraints or business invariants.
As per coding guidelines `**/*.{ts,tsx,js,jsx}`: Comments should be rare, factual, and explain only non-obvious external constraints or business invariants.Proposed cleanup
-// Only enable Sentry when explicitly configured for this deployment. if (process.env.NEXT_PUBLIC_ENABLE_SENTRY === 'true') { Sentry.init({ dsn: 'https://85ca585e45005d8786e361c3456518bf@o74828.ingest.us.sentry.io/4509717207318529', - // Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control. tracesSampleRate: 1, - // Enable logs to be sent to Sentry enableLogs: true, - // Setting this option to true will print useful information to the console while you're setting up Sentry. debug: false, }) }🤖 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 `@sentry.edge.config.ts` around lines 8 - 20, This block contains excessive explanatory inline comments around the Sentry initialization; trim comments to only those that convey non-obvious external constraints or business invariants. In the if (process.env.NEXT_PUBLIC_ENABLE_SENTRY === 'true') { ... } / Sentry.init({...}) section, remove or shorten comments for obvious configuration keys (dsn, tracesSampleRate, enableLogs, debug) and keep a single factual comment only if there is an external requirement (e.g., a deployment-level mandate about NEXT_PUBLIC_ENABLE_SENTRY or mandatory DSN handling). Ensure remaining comments are concise and factual.src/app/pc-listings/components/filters/AsyncGpuFilterSelect.test.tsx (1)
68-84: ⚡ Quick winReduce non-mock inline string literals in this test.
Move assertion/label strings to fixture-derived constants so literals are limited to mocking data.
As per coding guidelines `**/*.{test,spec}.{ts,tsx}`: Tests may use string literals only when mocking requires it.Proposed refactor
+const FILTER_LABEL = 'GPUs' +const SELECTED_GPU = { id: 'gpu-selected', modelName: 'Radeon RX 7800 XT', brandName: 'AMD' } +const OPTION_GPU = { id: 'gpu-1', modelName: 'RTX 4070', brandName: 'NVIDIA' } it('maps GPU option and selected labels', () => { - render(<AsyncGpuFilterSelect label="GPUs" value={['gpu-selected']} onChange={vi.fn()} />) + render(<AsyncGpuFilterSelect label={FILTER_LABEL} value={[SELECTED_GPU.id]} onChange={vi.fn()} />) - expect(screen.getByText('AMD Radeon RX 7800 XT')).toBeInTheDocument() - fireEvent.click(screen.getByRole('button', { name: 'GPUs multi-select' })) - expect(screen.getByText('NVIDIA RTX 4070')).toBeInTheDocument() + expect( + screen.getByText(`${SELECTED_GPU.brandName} ${SELECTED_GPU.modelName}`), + ).toBeInTheDocument() + fireEvent.click(screen.getByRole('button', { name: `${FILTER_LABEL} multi-select` })) + expect(screen.getByText(`${OPTION_GPU.brandName} ${OPTION_GPU.modelName}`)).toBeInTheDocument() })🤖 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 `@src/app/pc-listings/components/filters/AsyncGpuFilterSelect.test.tsx` around lines 68 - 84, The test in AsyncGpuFilterSelect uses inline string literals for labels and selected values; replace them with constants sourced from your test fixtures/mock data instead of hard-coded strings. Update the test to import the fixture constants (e.g., GPU_LABEL, GPU_OPTION_LABEL_NVIDIA, GPU_OPTION_LABEL_AMD, GPU_SELECTED_ID) from the module used by setupApiMocks or the API mock file, then use those constants in render(<AsyncGpuFilterSelect label={GPU_LABEL} value={[GPU_SELECTED_ID]} .../>), in the screen.getByText assertions, and in fireEvent.click(screen.getByRole('button', { name: `${GPU_LABEL} multi-select` })); ensure setupApiMocks still provides the mock data keys that the constants reference.src/app/pc-listings/components/filters/AsyncCpuFilterSelect.test.tsx (1)
80-85: ⚡ Quick winReduce non-mock string literals in the test body.
Lines 80-85 use hard-coded UI strings in test title/props/assertions. Please switch to shared constants/test fixtures so literals remain only where mocking requires them.
As per coding guidelines "Tests may use string literals only when mocking requires it".
🤖 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 `@src/app/pc-listings/components/filters/AsyncCpuFilterSelect.test.tsx` around lines 80 - 85, The test uses hard-coded UI strings; replace those literals with shared test constants/fixtures: import canonical labels and option text constants (e.g., CPU_LABEL, CPU_MULTI_SELECT_NAME, CPU_OPTION_INTEL, CPU_OPTION_AMD or equivalent) and use them in the it() title, the render label prop for AsyncCpuFilterSelect, and the assertions that call screen.getByText / getByRole; update references to AsyncCpuFilterSelect, the test case name, render(...) and the two expect(...) calls to use those imported constants so strings only remain in mocks/fixtures.src/components/popups/StopKillingGamesPopup.tsx (1)
12-12: ⚡ Quick winRemove or replace the non-actionable TODO comment.
Line 12 adds a vague TODO that does not document a concrete invariant/constraint. Please remove it or replace it with a specific tracked action.
As per coding guidelines "Comments should be rare, factual, and explain only non-obvious external constraints or business invariants".
🤖 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 `@src/components/popups/StopKillingGamesPopup.tsx` at line 12, Remove the vague TODO on line 12 in the StopKillingGamesPopup component: either delete the non-actionable comment or replace it with a specific, factual note (e.g., a one-line explanation of the invariant or a reference to a tracking ticket/PR) so the comment documents a concrete constraint or next step; update the StopKillingGamesPopup.tsx file's comment near the component declaration accordingly.src/server/api/routers/socs.ts (1)
22-22: ⚡ Quick winRemove the TODO note or convert it into a tracked issue.
Inline TODOs here add noise without documenting a business invariant or external constraint.
As per coding guidelines, “Comments should be rare, factual, and explain only non-obvious external constraints or business invariants.”
🤖 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 `@src/server/api/routers/socs.ts` at line 22, Remove the inline comment "// TODO: use paginate helpers" from src/server/api/routers/socs.ts; either delete the TODO entirely or replace it with a short factual comment linking to a created issue/PR (e.g., "See ISSUE-123 for paginate helper work") so the intent is tracked externally rather than left as inline noise. Locate the exact TODO string in the file and apply the change, ensuring no other logic is altered.src/lib/cors.test.ts (1)
5-123: ⚡ Quick winReplace repeated raw literals with named test constants.
This test now relies on many direct string literals outside mocking paths; move env keys/URLs/app-env values into constants to align with test conventions.
As per coding guidelines, “Tests may use string literals only when mocking requires it.”
🤖 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 `@src/lib/cors.test.ts` around lines 5 - 123, The tests use many repeated string literals; define named test constants (e.g., ALLOWED_ORIGIN_EMUREADY = 'https://emuready.com', LOCALHOST_URL = 'http://localhost:3000', CI_ENV = 'true', PROD_ENV = 'production', TEST_APP_ENV = 'test') at the top of the file and replace raw usages in calls to loadCors(), expectations for getOriginFromUrl, getAllowedOrigins, and isAllowedRequestOrigin (and any references to allowedOrigins or source strings) with those constants so the env keys/URLs/app-env values are centralized and follow the test convention.src/components/ui/form/async-multi-select/AsyncMultiSelect.test.tsx (1)
41-353: 🏗️ Heavy liftExtract repeated UI query strings into named constants/fixtures.
This suite hardcodes many query strings (labels/placeholders/button names). Centralizing them in shared constants keeps tests resilient and aligns with repository test rules.
As per coding guidelines, "
**/*.{test,spec}.{ts,tsx}: Tests may use string literals only when mocking requires it".🤖 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 `@src/components/ui/form/async-multi-select/AsyncMultiSelect.test.tsx` around lines 41 - 353, The tests hardcode many UI query strings (e.g., 'Devices multi-select', 'Search devices...', 'Clear search', 'Retroid Pocket 5', 'AYN Odin 2', 'Steam Deck OLED', etc.) which should be centralized; update the AsyncMultiSelect.test.tsx to import and use a shared test-strings fixture (e.g., names like SELECT_BUTTON_LABEL, SEARCH_PLACEHOLDER, CLEAR_SEARCH_LABEL, OPTION_RETROID_POCKET, OPTION_AYN_ODIN, SELECTED_STEAM_DECK) instead of literal strings, replace all occurrences in the suite (including usages in renderSelect calls and screen.getByRole/getByText/getByPlaceholderText assertions) and add/update the fixture file exporting these constants so tests remain resilient and follow the test string guideline.
🤖 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 `@prisma/seeders/pcListingsSeeder.ts`:
- Around line 1-2: Reorder the imports so external package imports come before
project-alias imports to satisfy ESLint import/order: move the line importing
ApprovalStatus, PcOs, Role, and PrismaClient from '`@orm/client`' above the line
importing calculateWilsonScore from '`@/utils/wilson-score`'. Ensure the symbols
ApprovalStatus, PcOs, Role, and PrismaClient remain imported from '`@orm/client`'
and calculateWilsonScore stays from '`@/utils/wilson-score`'.
In `@src/app/listings/components/filters/AsyncSocFilterSelect.test.tsx`:
- Around line 79-83: The test for AsyncSocFilterSelect is asserting UI text with
inline string literals ("MediaTek Dimensity 1100", "Qualcomm Snapdragon 8 Gen
2"), which violates the rule against non-mocking literals; update the test to
assert against values derived from your mocked fixture data instead (e.g. import
and use the mock SoC names/IDs used by the AsyncSocFilterSelect mock provider or
fixture object) so that render(<AsyncSocFilterSelect ... />) and subsequent
expect(...) calls reference the fixture constants rather than hardcoded strings;
locate references in this file to AsyncSocFilterSelect, the render call, and the
two expect(...) assertions and replace the literals with the corresponding
mocked fixture variables.
In `@src/app/pc-listings/new/NewPcListingPage.tsx`:
- Line 35: The import ordering is incorrect: move the named import "PcOs" from
"`@orm`" so it appears in the correct group/order before the "next/link" import in
NewPcListingPage.tsx; update the import list so "`@orm`" imports (PcOs) are
grouped with other external/module imports in the proper order expected by the
lint rule (import/order) to satisfy CI.
In `@src/app/profile/components/PcPresetModal.tsx`:
- Around line 3-10: The import order is incorrect for linting: move the named
import PcOs from '`@orm`' so it appears before the React imports (useCallback,
useState, useEffect, FormEvent) and other third-party imports; update the
top-of-file import sequence that currently includes "import { PcOs } from
'`@orm`'" and "import { useCallback, useState, useEffect, type FormEvent } from
'react'" so that PcOs/@orm is imported earlier to satisfy the project's
import-order rule.
In `@src/lib/env.test.ts`:
- Around line 21-76: The tests use many inline env key/value strings and
assertions (e.g., calls to loadEnv with NODE_ENV, NEXT_PUBLIC_APP_ENV,
NEXT_PUBLIC_ENABLE_ANALYTICS, NEXT_PUBLIC_ENABLE_KOFI_WIDGET,
NEXT_PUBLIC_ENABLE_SENTRY and assertions against env.APP_ENV, env.ENABLE_*);
refactor by introducing shared test constants/fixtures (e.g., ENV_KEYS and
ENV_FIXTURES or TEST_ENV_VALUES) exported from a test helper module and replace
the inline literals in these cases with those constants when calling loadEnv and
when asserting env properties so all env keys/expected values are centrally
maintained and reused across tests.
In `@src/lib/trust/service.test.ts`:
- Line 41: Remove all "as never" casts in src/lib/trust/service.test.ts and
replace them by passing a properly typed mock Prisma context that matches the
service's dependency shape: update the prismaCtx and prismaWithTx variables to
the exact interface expected by canUserAutoApprove and TrustService (use the
existing MockPrismaCtx type or create a narrowed mock interface), then call
canUserAutoApprove('user-1', prismaCtx) and construct new
TrustService(prismaWithTx, ...) with those typed mocks (ensure the mock
implements required methods/properties used in the tests such as the Prisma
client and transaction helpers).
In `@src/server/repositories/devices.repository.ts`:
- Around line 1-9: ESLint import/order is failing because the aliased internal
imports (e.g., TIME_CONSTANTS from '`@/utils/time`' and Prisma/ApprovalStatus or
getTrendingDevices from '`@orm/client`' / '`@orm/sql`') appear after the external
date-fns import (startOfMonth, subDays); move the internal/alias imports
(TIME_CONSTANTS and the `@orm` imports like Prisma/ApprovalStatus and
getTrendingDevices) so they come before the date-fns imports to satisfy the
rule, keeping other imports (LRUCache, HOME_PAGE_LIMITS, ResourceError,
paginate/calculateOffset, BaseRepository) in their logical groups.
In `@tests/helpers/external-services.ts`:
- Around line 9-35: The Playwright routes use exact URL strings so requests with
query strings bypass mocks; update the page.route patterns (the calls to
page.route in this file) to match query-string variants by using glob patterns
or regex (e.g., append a wildcard like '*' to the existing path patterns or use
a regex that allows trailing query strings) for the three routes handling
'_vercel/speed-insights/script.js', 'floating-chat-wrapper.css', and
'overlay-widget.js' so requests like '?v=123' are intercepted reliably.
---
Nitpick comments:
In `@sentry.edge.config.ts`:
- Around line 8-20: This block contains excessive explanatory inline comments
around the Sentry initialization; trim comments to only those that convey
non-obvious external constraints or business invariants. In the if
(process.env.NEXT_PUBLIC_ENABLE_SENTRY === 'true') { ... } / Sentry.init({...})
section, remove or shorten comments for obvious configuration keys (dsn,
tracesSampleRate, enableLogs, debug) and keep a single factual comment only if
there is an external requirement (e.g., a deployment-level mandate about
NEXT_PUBLIC_ENABLE_SENTRY or mandatory DSN handling). Ensure remaining comments
are concise and factual.
In `@src/app/admin/devices/components/DeviceModal.tsx`:
- Around line 23-24: The modal currently preloads 1000 SoC options via the call
to api.socs.options.useQuery({ limit: 1000 }) (socsQuery) which must be replaced
with a search-driven async fetch: remove the hardcoded limit call, add a
debounced search state in DeviceModal and call api.socs.options.useQuery or an
equivalent endpoint with the search term (e.g., { q: searchTerm, limit: 50 })
when the user types, wire those results into the select component (or use an
AsyncSelect pattern) to load options on demand, ensure loading/error states are
handled, and remove the TODO comment once complete.
In `@src/app/pc-listings/components/filters/AsyncCpuFilterSelect.test.tsx`:
- Around line 80-85: The test uses hard-coded UI strings; replace those literals
with shared test constants/fixtures: import canonical labels and option text
constants (e.g., CPU_LABEL, CPU_MULTI_SELECT_NAME, CPU_OPTION_INTEL,
CPU_OPTION_AMD or equivalent) and use them in the it() title, the render label
prop for AsyncCpuFilterSelect, and the assertions that call screen.getByText /
getByRole; update references to AsyncCpuFilterSelect, the test case name,
render(...) and the two expect(...) calls to use those imported constants so
strings only remain in mocks/fixtures.
In `@src/app/pc-listings/components/filters/AsyncGpuFilterSelect.test.tsx`:
- Around line 68-84: The test in AsyncGpuFilterSelect uses inline string
literals for labels and selected values; replace them with constants sourced
from your test fixtures/mock data instead of hard-coded strings. Update the test
to import the fixture constants (e.g., GPU_LABEL, GPU_OPTION_LABEL_NVIDIA,
GPU_OPTION_LABEL_AMD, GPU_SELECTED_ID) from the module used by setupApiMocks or
the API mock file, then use those constants in render(<AsyncGpuFilterSelect
label={GPU_LABEL} value={[GPU_SELECTED_ID]} .../>), in the screen.getByText
assertions, and in fireEvent.click(screen.getByRole('button', { name:
`${GPU_LABEL} multi-select` })); ensure setupApiMocks still provides the mock
data keys that the constants reference.
In `@src/components/popups/StopKillingGamesPopup.tsx`:
- Line 12: Remove the vague TODO on line 12 in the StopKillingGamesPopup
component: either delete the non-actionable comment or replace it with a
specific, factual note (e.g., a one-line explanation of the invariant or a
reference to a tracking ticket/PR) so the comment documents a concrete
constraint or next step; update the StopKillingGamesPopup.tsx file's comment
near the component declaration accordingly.
In `@src/components/ui/form/async-multi-select/AsyncMultiSelect.test.tsx`:
- Around line 41-353: The tests hardcode many UI query strings (e.g., 'Devices
multi-select', 'Search devices...', 'Clear search', 'Retroid Pocket 5', 'AYN
Odin 2', 'Steam Deck OLED', etc.) which should be centralized; update the
AsyncMultiSelect.test.tsx to import and use a shared test-strings fixture (e.g.,
names like SELECT_BUTTON_LABEL, SEARCH_PLACEHOLDER, CLEAR_SEARCH_LABEL,
OPTION_RETROID_POCKET, OPTION_AYN_ODIN, SELECTED_STEAM_DECK) instead of literal
strings, replace all occurrences in the suite (including usages in renderSelect
calls and screen.getByRole/getByText/getByPlaceholderText assertions) and
add/update the fixture file exporting these constants so tests remain resilient
and follow the test string guideline.
In `@src/lib/cors.test.ts`:
- Around line 5-123: The tests use many repeated string literals; define named
test constants (e.g., ALLOWED_ORIGIN_EMUREADY = 'https://emuready.com',
LOCALHOST_URL = 'http://localhost:3000', CI_ENV = 'true', PROD_ENV =
'production', TEST_APP_ENV = 'test') at the top of the file and replace raw
usages in calls to loadCors(), expectations for getOriginFromUrl,
getAllowedOrigins, and isAllowedRequestOrigin (and any references to
allowedOrigins or source strings) with those constants so the env
keys/URLs/app-env values are centralized and follow the test convention.
In `@src/server/api/routers/socs.ts`:
- Line 22: Remove the inline comment "// TODO: use paginate helpers" from
src/server/api/routers/socs.ts; either delete the TODO entirely or replace it
with a short factual comment linking to a created issue/PR (e.g., "See ISSUE-123
for paginate helper work") so the intent is tracked externally rather than left
as inline noise. Locate the exact TODO string in the file and apply the change,
ensuring no other logic is altered.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 9a93ba80-d89a-4690-b6e1-ef398a089291
📒 Files selected for processing (98)
.env.docker.example.env.example.env.test.example.github/workflows/e2e-tests.ymlnext.config.tsplaywright.config.tsprisma/migrations/20260526215009_add_listing_query_indexes/migration.sqlprisma/schema.prismaprisma/seed.tsprisma/seeders/azaharCustomFieldsSeeder.tsprisma/seeders/customFieldCategoryUtils.tsprisma/seeders/customFieldValueSeederUtils.tsprisma/seeders/edenCustomFieldsSeeder.tsprisma/seeders/gamenativeCustomFieldsSeeder.tsprisma/seeders/listingsSeeder.tsprisma/seeders/pcListingsSeeder.tssentry.edge.config.tssentry.server.config.tssrc/app/admin/brands/page.tsxsrc/app/admin/cpus/page.tsxsrc/app/admin/devices/components/DeviceModal.tsxsrc/app/admin/devices/page.tsxsrc/app/admin/gpus/page.tsxsrc/app/admin/listings/[id]/edit/components/ListingEditForm.tsxsrc/app/admin/socs/page.tsxsrc/app/api/proxy-image/route.tssrc/app/globals.csssrc/app/home/components/HomeTrendingDevices.tsxsrc/app/layout.tsxsrc/app/listings/ListingsPage.tsxsrc/app/listings/components/ListingsFiltersContent.tsxsrc/app/listings/components/ListingsFiltersSidebar.tsxsrc/app/listings/components/filters/AsyncDeviceFilterSelect.test.tsxsrc/app/listings/components/filters/AsyncDeviceFilterSelect.tsxsrc/app/listings/components/filters/AsyncSocFilterSelect.test.tsxsrc/app/listings/components/filters/AsyncSocFilterSelect.tsxsrc/app/listings/new/NewListingPage.tsxsrc/app/listings/shared/components/ActiveFiltersSummary.tsxsrc/app/listings/shared/components/MobileFiltersFab.tsxsrc/app/pc-listings/PcListingsPage.tsxsrc/app/pc-listings/components/PcFiltersContent.tsxsrc/app/pc-listings/components/filters/AsyncCpuFilterSelect.test.tsxsrc/app/pc-listings/components/filters/AsyncCpuFilterSelect.tsxsrc/app/pc-listings/components/filters/AsyncGpuFilterSelect.test.tsxsrc/app/pc-listings/components/filters/AsyncGpuFilterSelect.tsxsrc/app/pc-listings/new/NewPcListingPage.tsxsrc/app/profile/components/DeviceSelector.tsxsrc/app/profile/components/PcPresetModal.tsxsrc/app/profile/components/SocSelector.tsxsrc/app/v2/listings/V2ListingsPage.tsxsrc/app/v2/listings/components/ListingFilters.tsxsrc/components/footer/Footer.tsxsrc/components/footer/components/FooterBetaBadge.tsxsrc/components/popups/BetaWarningPopup.tsxsrc/components/popups/StopKillingGamesPopup.tsxsrc/components/ui/KofiWidget.tsxsrc/components/ui/form/AsyncCpuMultiSelect.tsxsrc/components/ui/form/AsyncDeviceMultiSelect.tsxsrc/components/ui/form/AsyncGpuMultiSelect.tsxsrc/components/ui/form/AsyncMultiSelect.tsxsrc/components/ui/form/AsyncSocMultiSelect.tsxsrc/components/ui/form/MultiSelect.test.tsxsrc/components/ui/form/MultiSelect.tsxsrc/components/ui/form/async-multi-select/AsyncMultiSelect.test.tsxsrc/components/ui/form/async-multi-select/AsyncMultiSelect.tsxsrc/components/ui/form/index.tssrc/instrumentation-client.test.tssrc/instrumentation-client.tssrc/lib/analytics/utils/sendAnalyticsEvent.test.tssrc/lib/analytics/utils/sendAnalyticsEvent.tssrc/lib/cors.test.tssrc/lib/cors.tssrc/lib/env.test.tssrc/lib/env.tssrc/lib/trust/service.test.tssrc/lib/trust/service.tssrc/proxy.tssrc/schemas/cpu.tssrc/schemas/device.tssrc/schemas/gpu.tssrc/schemas/soc.tssrc/sentry-config.test.tssrc/server/api/routers/cpus.tssrc/server/api/routers/devices.tssrc/server/api/routers/gpus.tssrc/server/api/routers/socs.tssrc/server/repositories/cpus.repository.tssrc/server/repositories/devices.repository.tssrc/server/repositories/gpus.repository.tssrc/server/repositories/listings.repository.tssrc/server/repositories/pc-listings.repository.tssrc/server/repositories/socs.repository.tssrc/server/utils/pagination.tstests/filtering.spec.tstests/helpers/cookie-consent.tstests/helpers/external-services.tstests/pages/ListingsPage.tstests/third-party-services.spec.ts
💤 Files with no reviewable changes (8)
- src/components/ui/form/AsyncSocMultiSelect.tsx
- src/components/ui/form/AsyncGpuMultiSelect.tsx
- src/components/ui/form/AsyncMultiSelect.tsx
- src/components/footer/components/FooterBetaBadge.tsx
- src/components/ui/form/AsyncDeviceMultiSelect.tsx
- src/components/ui/form/AsyncCpuMultiSelect.tsx
- src/components/ui/form/index.ts
- src/components/footer/Footer.tsx
|
@coderabbitai create individual GitHub issues for each TODO . When the TODO comments are related to each other group them together in 1 issue. do not make changes to this PR (do not remove the TODO comments) |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@src/components/ui/form/Autocomplete.tsx`:
- Line 590: The input's aria-activedescendant must be computed using the same id
schema as the option elements (the safeId defined as
`option-${idx}-${String(itemValue).replace(/[^A-Za-z0-9_-]/g, '_')}`) so the
active descendant points to an existing element; update the logic that currently
builds `option-${value}` (used for aria-activedescendant in the Autocomplete
component/input) to produce the same sanitized id including the option index
(use the active option's idx and itemValue or derive the id from the same helper
that creates safeId) so keyboard focus and screen-reader announcements reference
the correct element.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: f16f2685-4807-4d20-b3da-c4e1adfb0355
📒 Files selected for processing (6)
src/components/ui/form/Autocomplete.tsxsrc/features/human-verification/server/providers/turnstile.test.tssrc/features/human-verification/server/providers/turnstile.tssrc/lib/api.tsxsrc/server/utils/spam-check.test.tssrc/server/utils/spam-check.ts
✅ Files skipped from review due to trivial changes (1)
- src/lib/api.tsx
There was a problem hiding this comment.
🧹 Nitpick comments (2)
docs/ERROR_HANDLING.md (2)
21-21: ⚡ Quick winConsider consolidating the mirrored error formatter implementations.
The documentation notes that the error formatter is "mirrored in
src/server/api/mobileContext.ts". Duplicate error formatting logic creates a maintenance burden and risks divergence between web and mobile error handling.♻️ Suggested approach
Extract the error formatter into a shared utility function that both
trpc.tsandmobileContext.tscan import, ensuring consistent error serialization across both contexts.🤖 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 `@docs/ERROR_HANDLING.md` at line 21, The repo has duplicated error formatter logic between src/server/api/trpc.ts and src/server/api/mobileContext.ts; extract the common formatting into a shared utility (e.g., export a function like formatAppError or getSerializableAppError wrapper) in a new module (or existing src/lib/app-error-cause.ts), update trpc.ts and mobileContext.ts to import and call that single function, and ensure the exported function returns the same shape (data.appError) so both contexts remain behaviorally identical.
30-30: 💤 Low valueClarify the consequence of violating the plain JSON constraint.
The constraint that
causefields must be plain JSON values is stated but doesn't explain what happens if a nestedErrorinstance is passed. Adding the consequence (e.g., "will throw during serialization" or "will be omitted from the client payload") helps developers understand the severity.🤖 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 `@docs/ERROR_HANDLING.md` at line 30, The docs state that `cause` fields must be plain JSON values but don’t explain the consequence; update the `cause` documentation to explicitly say that passing a nested Error instance will break serialization (e.g., cause JSON.stringify to fail or result in non-serializable fields being stripped/omitted from the client payload) and add a short remediation: convert Error instances to plain objects (message, name, stack) before assigning to `cause`. Include the term `cause` and mention JSON serialization/JSON.stringify so readers can locate the rule and the fix.
🤖 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.
Nitpick comments:
In `@docs/ERROR_HANDLING.md`:
- Line 21: The repo has duplicated error formatter logic between
src/server/api/trpc.ts and src/server/api/mobileContext.ts; extract the common
formatting into a shared utility (e.g., export a function like formatAppError or
getSerializableAppError wrapper) in a new module (or existing
src/lib/app-error-cause.ts), update trpc.ts and mobileContext.ts to import and
call that single function, and ensure the exported function returns the same
shape (data.appError) so both contexts remain behaviorally identical.
- Line 30: The docs state that `cause` fields must be plain JSON values but
don’t explain the consequence; update the `cause` documentation to explicitly
say that passing a nested Error instance will break serialization (e.g., cause
JSON.stringify to fail or result in non-serializable fields being
stripped/omitted from the client payload) and add a short remediation: convert
Error instances to plain objects (message, name, stack) before assigning to
`cause`. Include the term `cause` and mention JSON serialization/JSON.stringify
so readers can locate the rule and the fix.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 6744feaa-f1e0-47e0-9929-7ed49fa3d343
📒 Files selected for processing (1)
docs/ERROR_HANDLING.md
…ty, add support for structured error callbacks and flexible UI options
…hecks, add development hostname support, and fix test coverage
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/server/api/routers/games.test.ts (1)
79-98: ⚡ Quick winAvoid
as neverin the caller helper.This suppresses router-context drift in a brand-new test and can let broken mocks compile unnoticed. Please type the mock context to the caller contract instead of casting it away. As per coding guidelines
**/*.{ts,tsx}: Do not use casts to hide type problems. Fix the underlying type issue.🤖 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 `@src/server/api/routers/games.test.ts` around lines 79 - 98, The helper createCaller uses an unsafe cast ("prisma: prisma as never") which hides real type mismatches; instead build and pass a properly typed context to gamesRouter.createCaller by using the router's context parameter type (e.g. type Ctx = Parameters<typeof gamesRouter.createCaller>[0]) and declare the context object with session and prisma typed as Ctx['prisma'] (or import the actual Context type used by the router) before calling gamesRouter.createCaller(context); remove the as never cast and ensure MockPrisma implements the expected prisma shape so TypeScript will validate the mock.src/features/human-verification/client/components/HumanVerificationProvider.test.tsx (1)
98-207: ⚡ Quick winAdd coverage for the new script-load failure branches.
These tests cover the ready/render paths well, but the provider now also rejects on
VERIFICATION_SCRIPT_LOAD_TIMEOUT_MSand<Script onError>. A fake-timer timeout case plus a directscriptProps[0].onError?.()case would protect the new user-visible failure modes.🤖 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 `@src/features/human-verification/client/components/HumanVerificationProvider.test.tsx` around lines 98 - 207, Add two tests to cover script-load failure branches: one that uses fake timers to advance time past VERIFICATION_SCRIPT_LOAD_TIMEOUT_MS and asserts the captured promise rejects with a script-load timeout error, and another that directly invokes scriptProps[0].onError?.() after rendering with window.turnstile unset and asserts the captured promise rejects with the script load error; place these alongside the existing tests in HumanVerificationProvider.test.tsx and reference VERIFICATION_SCRIPT_LOAD_TIMEOUT_MS, scriptProps[0].onError, and the same CaptureButton request-handling pattern to collect the promise for assertions.
🤖 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 `@src/features/human-verification/server/providers/turnstile.ts`:
- Around line 77-86: The current bypass of validateSuccessfulResponse when the
secret is in TURNSTILE_TEST_SECRET_KEYS must be limited to test environments:
update the logic around the parsed.data.success check so that the test-secret
shortcut is only used when running in a test environment (e.g., via NODE_ENV ===
'test' or an existing isTestEnvironment helper); if a test secret is encountered
outside a test environment, treat it as invalid (do not skip
validateSuccessfulResponse and return failure/error codes). Change the
conditional that references TURNSTILE_TEST_SECRET_KEYS to also check the
test-env flag (or invert to fail-closed when test key + not test env) and ensure
validateSuccessfulResponse is always invoked for non-test environments.
---
Nitpick comments:
In
`@src/features/human-verification/client/components/HumanVerificationProvider.test.tsx`:
- Around line 98-207: Add two tests to cover script-load failure branches: one
that uses fake timers to advance time past VERIFICATION_SCRIPT_LOAD_TIMEOUT_MS
and asserts the captured promise rejects with a script-load timeout error, and
another that directly invokes scriptProps[0].onError?.() after rendering with
window.turnstile unset and asserts the captured promise rejects with the script
load error; place these alongside the existing tests in
HumanVerificationProvider.test.tsx and reference
VERIFICATION_SCRIPT_LOAD_TIMEOUT_MS, scriptProps[0].onError, and the same
CaptureButton request-handling pattern to collect the promise for assertions.
In `@src/server/api/routers/games.test.ts`:
- Around line 79-98: The helper createCaller uses an unsafe cast ("prisma:
prisma as never") which hides real type mismatches; instead build and pass a
properly typed context to gamesRouter.createCaller by using the router's context
parameter type (e.g. type Ctx = Parameters<typeof gamesRouter.createCaller>[0])
and declare the context object with session and prisma typed as Ctx['prisma']
(or import the actual Context type used by the router) before calling
gamesRouter.createCaller(context); remove the as never cast and ensure
MockPrisma implements the expected prisma shape so TypeScript will validate the
mock.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 947f5127-c9f1-4e0e-ba90-7cb9d3811c25
⛔ Files ignored due to path filters (1)
public/placeholder/game.svgis excluded by!**/*.svg
📒 Files selected for processing (24)
.env.docker.example.env.example.env.test.exampledocs/ERROR_HANDLING.mdsrc/app/games/new/NewGamePage.tsxsrc/app/games/new/search/GameSearchPage.tsxsrc/app/games/new/search/v2/IGDBSearchPage.tsxsrc/app/layout.tsxsrc/components/banners/CommunitySupportBanner.tsxsrc/features/human-verification/client/components/HumanVerificationProvider.test.tsxsrc/features/human-verification/client/components/HumanVerificationProvider.tsxsrc/features/human-verification/server/providers/turnstile.test.tssrc/features/human-verification/server/providers/turnstile.tssrc/schemas/game.tssrc/schemas/listing.test.tssrc/server/api/routers/games.test.tssrc/server/api/routers/games.tssrc/server/api/routers/listings/comments.test.tssrc/server/api/routers/listings/core.test.tssrc/server/api/routers/pcListings.test.tssrc/server/utils/spam-check.test.tssrc/server/utils/spam-check.tssrc/server/utils/spamDetection.test.tssrc/server/utils/spamDetection.ts
💤 Files with no reviewable changes (2)
- src/components/banners/CommunitySupportBanner.tsx
- src/app/layout.tsx
✅ Files skipped from review due to trivial changes (1)
- docs/ERROR_HANDLING.md
🚧 Files skipped from review as they are similar to previous changes (3)
- .env.test.example
- .env.example
- .env.docker.example
| if (parsed.data.success && !TURNSTILE_TEST_SECRET_KEYS.has(secret)) { | ||
| const validationErrors = validateSuccessfulResponse({ | ||
| action: parsed.data.action, | ||
| hostname: parsed.data.hostname, | ||
| }) | ||
|
|
||
| if (validationErrors.length > 0) { | ||
| return { success: false, errorCodes: validationErrors } | ||
| } | ||
| } |
There was a problem hiding this comment.
Scope the test-secret bypass to test environments only.
Skipping validateSuccessfulResponse() solely because the secret matches Cloudflare's documented test keys means a staging or production env that is accidentally configured with a test secret also skips the new action and hostname checks. Gate this bypass behind a test-only env, or fail closed when test keys appear outside tests.
Suggested hardening
- if (parsed.data.success && !TURNSTILE_TEST_SECRET_KEYS.has(secret)) {
+ const isTestSecret = TURNSTILE_TEST_SECRET_KEYS.has(secret)
+ const allowTestSecretBypass =
+ isTestSecret &&
+ (process.env.NODE_ENV === 'test' || process.env.NEXT_PUBLIC_APP_ENV === 'test')
+
+ if (isTestSecret && !allowTestSecretBypass) {
+ return { success: false, errorCodes: ['invalid-test-secret'] }
+ }
+
+ if (parsed.data.success && !allowTestSecretBypass) {
const validationErrors = validateSuccessfulResponse({
action: parsed.data.action,
hostname: parsed.data.hostname,
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (parsed.data.success && !TURNSTILE_TEST_SECRET_KEYS.has(secret)) { | |
| const validationErrors = validateSuccessfulResponse({ | |
| action: parsed.data.action, | |
| hostname: parsed.data.hostname, | |
| }) | |
| if (validationErrors.length > 0) { | |
| return { success: false, errorCodes: validationErrors } | |
| } | |
| } | |
| const isTestSecret = TURNSTILE_TEST_SECRET_KEYS.has(secret) | |
| const allowTestSecretBypass = | |
| isTestSecret && | |
| (process.env.NODE_ENV === 'test' || process.env.NEXT_PUBLIC_APP_ENV === 'test') | |
| if (isTestSecret && !allowTestSecretBypass) { | |
| return { success: false, errorCodes: ['invalid-test-secret'] } | |
| } | |
| if (parsed.data.success && !allowTestSecretBypass) { | |
| const validationErrors = validateSuccessfulResponse({ | |
| action: parsed.data.action, | |
| hostname: parsed.data.hostname, | |
| }) | |
| if (validationErrors.length > 0) { | |
| return { success: false, errorCodes: validationErrors } | |
| } | |
| } |
🤖 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 `@src/features/human-verification/server/providers/turnstile.ts` around lines
77 - 86, The current bypass of validateSuccessfulResponse when the secret is in
TURNSTILE_TEST_SECRET_KEYS must be limited to test environments: update the
logic around the parsed.data.success check so that the test-secret shortcut is
only used when running in a test environment (e.g., via NODE_ENV === 'test' or
an existing isTestEnvironment helper); if a test secret is encountered outside a
test environment, treat it as invalid (do not skip validateSuccessfulResponse
and return failure/error codes). Change the conditional that references
TURNSTILE_TEST_SECRET_KEYS to also check the test-env flag (or invert to
fail-closed when test key + not test env) and ensure validateSuccessfulResponse
is always invoked for non-test environments.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.husky/pre-push (1)
1-4:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winAdd error handling to fail fast on first check failure.
Without
set -eor command chaining, all three commands will execute sequentially even if earlier ones fail. The hook's exit code will be determined only by the last command (pnpm test), allowing a push to proceed even ifpnpm lintorpnpm typesfail.🔧 Proposed fix
+#!/bin/sh -e + pnpm lint pnpm types pnpm testAlternatively, chain the commands:
-pnpm lint -pnpm types -pnpm test +pnpm lint && pnpm types && pnpm test🤖 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 @.husky/pre-push around lines 1 - 4, The pre-push hook runs pnpm lint, pnpm types, pnpm test sequentially but does not fail fast; update the hook to abort on the first failing command by either adding a shell fail-fast setting (e.g., set -e) at the top of the script or by chaining the commands with && so that pnpm lint && pnpm types && pnpm test will stop on first error; apply this change in the .husky/pre-push script where the pnpm lint, pnpm types, pnpm test commands are listed.
🤖 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.
Outside diff comments:
In @.husky/pre-push:
- Around line 1-4: The pre-push hook runs pnpm lint, pnpm types, pnpm test
sequentially but does not fail fast; update the hook to abort on the first
failing command by either adding a shell fail-fast setting (e.g., set -e) at the
top of the script or by chaining the commands with && so that pnpm lint && pnpm
types && pnpm test will stop on first error; apply this change in the
.husky/pre-push script where the pnpm lint, pnpm types, pnpm test commands are
listed.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: b22dc3bb-2559-4be5-9546-4968b919430a
📒 Files selected for processing (2)
.husky/pre-commit.husky/pre-push
✅ Files skipped from review due to trivial changes (1)
- .husky/pre-commit
no. Git does not run that file directly. Husky runs it through .husky/_/h |
What changed
This PR reduces the amount of work the app does for report list filters and fixes a production submit path that could time out with the current one-connection Prisma pool.
Main changes:
.optionsendpoints for Devices, SoCs, CPUs, and GPUs so filters/selectors do not need to fetch full admin list payloads with counts and relations.selectedByIds.src/components/ui/form/async-multi-select.MultiSelectshell where it was causing layout overlap.NEXT_PUBLIC_*values instead of server-only env inference.Why
The old filter paths were doing too much work for simple option feeds, and several of those results were being cached for a long time without matching invalidation. That made the app slower and made admin changes look stale.
The submit timeout fix is separate but important: production can hit
timeout exceeded when trying to connectwhen a transaction holds the only pool connection and code inside that transaction tries to query through the root Prisma client. The create path now keeps that read on the transaction client.Testing
pnpm typespnpm lint- passes with the existing warning setpnpm test- 1801 passed, 4 skippedpnpm test:e2e:ci- completed with 274 passed, 2 skipped, 1 flaky retry intests/error-handling.spec.tsthat passed on retryNotes
The Prisma migration in this PR should be reviewed as a normal schema migration. I recreated it after cleaning up the local drift from the temporary platforms branch work.
Summary by CodeRabbit
New Features
Performance
Bug Fixes