Skip to content

Filter admin CPU and GPU brands server-side#432

Merged
Producdevity merged 4 commits into
stagingfrom
fix/admin-brand-filtering
Jun 5, 2026
Merged

Filter admin CPU and GPU brands server-side#432
Producdevity merged 4 commits into
stagingfrom
fix/admin-brand-filtering

Conversation

@Producdevity

@Producdevity Producdevity commented Jun 5, 2026

Copy link
Copy Markdown
Owner

Description

Adds a category filter to the device brands query so CPU and GPU admin pages can request relevant brands from the server instead of hard-coding client-side brand name allowlists.

Fixes #396

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update
  • Refactor
  • Other (please describe):

How Has This Been Tested?

  • Local build
  • Lint
  • Typecheck
  • Unit tests
  • Manual testing

Checks run:

  • ./node_modules/.bin/vitest run src/server/repositories/device-brands.repository.test.ts
  • ./node_modules/.bin/eslint src/schemas/deviceBrand.ts src/server/repositories/device-brands.repository.ts src/server/repositories/device-brands.repository.test.ts src/server/api/routers/mobile/deviceBrands.ts src/app/admin/cpus/page.tsx src/app/admin/gpus/page.tsx
  • ./node_modules/.bin/tsc --noEmit
  • ./node_modules/.bin/eslint . (completed with existing React Compiler warnings, 0 errors)

Screenshots (if applicable)

N/A

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have made corresponding changes to the documentation
  • I have checked that all checks (lint, typecheck, test) pass

Notes for reviewers

pnpm test src/server/repositories/device-brands.repository.test.ts could not run directly in the temp worktree because the pnpm wrapper performs a dependency-status install and requires a local .env.local/DATABASE_URL for Prisma postinstall. The same test passed through the local Vitest binary after generating/copying ignored local artifacts for verification.

Summary by CodeRabbit

  • New Features

    • Brand filters on CPU and GPU admin pages now show only manufacturers relevant to the selected device category and populate options from the updated brand source.
  • Tests

    • Added tests covering category-specific brand filtering, item counts, and default pagination behavior.
  • Documentation

    • Added a TypeScript/code-quality guideline advising when to avoid object destructuring.

@vercel

vercel Bot commented Jun 5, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
emuready Ready Ready Preview, Comment Jun 5, 2026 11:39am

Request Review

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6d9dcf0d-2303-420b-b3ca-d136e3777ef4

📥 Commits

Reviewing files that changed from the base of the PR and between f14080d and ba375bb.

📒 Files selected for processing (2)
  • AGENTS.md
  • src/server/repositories/device-brands.repository.ts
✅ Files skipped from review due to trivial changes (1)
  • AGENTS.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/server/repositories/device-brands.repository.ts

Walkthrough

Server-side category filtering for device brands: new DeviceBrandCategory in schema; repository buildWhereClause, list/count, and byIdWithCounts; router now uses the repository; Vitest repository tests added; admin CPU/GPU pages request category-scoped brands and consume brandsQuery.data directly.

Changes

Device Brand Category Filtering

Layer / File(s) Summary
Brand category schema and input contract
src/schemas/deviceBrand.ts
Adds DeviceBrandCategory enum (cpu,gpu) and an optional category field on GetDeviceBrandsSchema.
Repository core: where-clause, list/count
src/server/repositories/device-brands.repository.ts
Introduces buildWhereClause(filters) used by list() and count(), and adjusts pagination/sort destructuring to use the built where clause.
Repository by-id include with counts
src/server/repositories/device-brands.repository.ts
Adds byIdWithCounts(id) returning a brand with related device counts via the repository include selection.
Repository tests (Vitest) for filtering and defaults
src/server/repositories/device-brands.repository.test.ts
Mocks Prisma and asserts list()/count()/byIdWithCounts() forward CPU/GPU relational filters, use insensitive name.contains, and apply the repository default take.
Router delegates to repository
src/server/api/routers/mobile/deviceBrands.ts
Mobile brands router instantiates DeviceBrandsRepository and calls repository.list(input ?? {}) and repository.byIdWithCounts(input.id) instead of inlining Prisma queries.
Admin CPU/GPU pages: pass category and use query results
src/app/admin/cpus/page.tsx, src/app/admin/gpus/page.tsx
Pages pass category: 'cpu' / category: 'gpu' to api.deviceBrands.get.useQuery and use brandsQuery.data directly for Autocomplete items, removing temporary client-side brand filters.
Agents docs: TypeScript code-quality note
AGENTS.md
Adds guideline discouraging object destructuring when it reduces clarity or is used only once.

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: moving brand filtering from client-side to server-side for admin CPU and GPU pages.
Description check ✅ Passed The PR description is well-structured, follows the template, clearly states the objective (fixes #396), and documents testing with lint, typecheck, and unit tests performed.
Linked Issues check ✅ Passed The changes fully satisfy #396 requirements: server-side category filter added to brands schema/router/repository, client-side hard-coded filtering removed from CPU/GPU pages, and brands now fetched server-side with category parameter.
Out of Scope Changes check ✅ Passed All changes directly address #396 objectives. The AGENTS.md guideline addition is tangentially related but does not interfere with the primary scope of server-side brand filtering.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/admin-brand-filtering
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix/admin-brand-filtering

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/server/api/routers/mobile/deviceBrands.ts (1)

11-42: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Router should delegate to DeviceBrandsRepository instead of building Prisma queries directly.

This router constructs the where clause inline and queries ctx.prisma.deviceBrand.findMany directly. The DeviceBrandsRepository.list() method (reviewed in this same PR) implements identical filtering logic via buildWhereClause(). The main deviceBrandsRouter correctly delegates to the repository.

This violates the coding guideline: "Do not put raw Prisma queries or business logic in routers" and "Routers in src/server/api/routers/ are thin orchestration layers that handle auth context, schema-validated input, repository/service calls, and response formatting."

Additionally, lines 15-16 use string literals 'cpu' and 'gpu' for comparisons, which may violate the guideline: "do not use string literals for enum values in application code (routers)."

♻️ Refactor to use repository
  get: mobilePublicProcedure.input(GetDeviceBrandsSchema).query(async ({ ctx, input }) => {
-   const { search, category, limit, sortField, sortDirection } = input ?? {}
-
-   const where: Prisma.DeviceBrandWhereInput = {
-     ...(search && { name: { contains: search, mode: 'insensitive' } }),
-     ...(category === 'cpu' && { cpus: { some: {} } }),
-     ...(category === 'gpu' && { gpus: { some: {} } }),
-   }
-
-   const orderBy: Prisma.DeviceBrandOrderByWithRelationInput[] = []
-
-   if (sortField && sortDirection) {
-     switch (sortField) {
-       case 'name':
-         orderBy.push({ name: sortDirection })
-         break
-       case 'devicesCount':
-         orderBy.push({ devices: { _count: sortDirection } })
-         break
-     }
-   }
-
-   // Default ordering if no sort specified
-   if (!orderBy.length) {
-     orderBy.push({ name: 'asc' })
-   }
-
-   return ctx.prisma.deviceBrand.findMany({
-     where,
-     include: { _count: { select: { devices: true } } },
-     orderBy,
-     take: limit,
-   })
+   const repository = new DeviceBrandsRepository(ctx.prisma)
+   return repository.list(input ?? {})
  }),

You'll need to add the import:

+import { DeviceBrandsRepository } from '`@/server/repositories/device-brands.repository`'

As per coding guidelines, routers in src/server/api/routers/ are thin orchestration layers that delegate to repositories, and do not put raw Prisma queries or business logic in routers.

🤖 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/mobile/deviceBrands.ts` around lines 11 - 42, The
router is building Prisma queries inline; replace that logic by delegating to
the repository: import and call DeviceBrandsRepository.list (which uses
buildWhereClause) instead of constructing where/orderBy and calling
ctx.prisma.deviceBrand.findMany directly; pass the validated input and ctx (or
ctx.prisma) as the repository expects, remove the inline where/orderBy/take
logic, and ensure you stop using string literals 'cpu'/'gpu' by using the shared
enum/type used by DeviceBrandsRepository for the category field so the router
relies entirely on the repository for query semantics.
🤖 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 `@src/server/api/routers/mobile/deviceBrands.ts`:
- Around line 11-42: The router is building Prisma queries inline; replace that
logic by delegating to the repository: import and call
DeviceBrandsRepository.list (which uses buildWhereClause) instead of
constructing where/orderBy and calling ctx.prisma.deviceBrand.findMany directly;
pass the validated input and ctx (or ctx.prisma) as the repository expects,
remove the inline where/orderBy/take logic, and ensure you stop using string
literals 'cpu'/'gpu' by using the shared enum/type used by
DeviceBrandsRepository for the category field so the router relies entirely on
the repository for query semantics.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 17d1b250-781c-4a55-b16e-c4744f46a83f

📥 Commits

Reviewing files that changed from the base of the PR and between c0cdca3 and b1ff876.

📒 Files selected for processing (6)
  • src/app/admin/cpus/page.tsx
  • src/app/admin/gpus/page.tsx
  • src/schemas/deviceBrand.ts
  • src/server/api/routers/mobile/deviceBrands.ts
  • src/server/repositories/device-brands.repository.test.ts
  • src/server/repositories/device-brands.repository.ts

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/server/api/routers/mobile/deviceBrands.ts (1)

18-25: 💤 Low value

Consider delegating getById to the repository for consistency.

The get procedure now delegates to DeviceBrandsRepository, but getById still uses raw Prisma. For consistency and maintainability, consider adding a repository method for this lookup.

♻️ Proposed refactor

In the repository, byId already exists (line 54). Use it here:

  getById: mobilePublicProcedure.input(GetDeviceBrandByIdSchema).query(async ({ ctx, input }) => {
-    const brand = await ctx.prisma.deviceBrand.findUnique({
-      where: { id: input.id },
-      include: { _count: { select: { devices: true } } },
-    })
-
-    return brand || ResourceError.deviceBrand.notFound()
+    const repository = new DeviceBrandsRepository(ctx.prisma)
+    const brand = await repository.byId(input.id)
+    if (!brand) throw ResourceError.deviceBrand.notFound()
+    
+    // If counts are needed, add a repository method or include here
+    return brand
  }),

Note: The current byId in the repository does not include _count. If counts are required, extend the repository method to accept an optional include parameter.

Based on learnings, routers in src/server/api/routers/ are thin orchestration layers that delegate to repositories, and raw Prisma queries should not be placed in routers.

🤖 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/mobile/deviceBrands.ts` around lines 18 - 25, The
getById procedure in the router directly uses ctx.prisma instead of delegating
to DeviceBrandsRepository; change getById to call DeviceBrandsRepository.byId
(or extend DeviceBrandsRepository.byId to accept an optional include parameter
to request _count of devices) and return the repository result or
ResourceError.deviceBrand.notFound(); ensure you update the router to import/use
DeviceBrandsRepository and, if needed, add an include argument to byId so the
devices _count is preserved.
🤖 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/server/api/routers/mobile/deviceBrands.ts`:
- Around line 11-12: The handler in mobileDeviceBrandsRouter.get turns an
omitted input into {} and calls DeviceBrandsRepository.list with options {
defaultLimit: undefined }, which prevents the repository's default limit from
being applied and yields an unbounded query; change the call site in
mobileDeviceBrandsRouter.get so you do not override the repository's default
(e.g. pass the original undefined input through or omit the
options/defaultLimit) so DeviceBrandsRepository.list can use its defaultLimit
and GetDeviceBrandsSchema's default limit is respected.

---

Nitpick comments:
In `@src/server/api/routers/mobile/deviceBrands.ts`:
- Around line 18-25: The getById procedure in the router directly uses
ctx.prisma instead of delegating to DeviceBrandsRepository; change getById to
call DeviceBrandsRepository.byId (or extend DeviceBrandsRepository.byId to
accept an optional include parameter to request _count of devices) and return
the repository result or ResourceError.deviceBrand.notFound(); ensure you update
the router to import/use DeviceBrandsRepository and, if needed, add an include
argument to byId so the devices _count is preserved.
🪄 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: f0e28a66-76e4-4fbb-95a5-f0859103ec69

📥 Commits

Reviewing files that changed from the base of the PR and between b1ff876 and a10d82f.

📒 Files selected for processing (3)
  • src/server/api/routers/mobile/deviceBrands.ts
  • src/server/repositories/device-brands.repository.test.ts
  • src/server/repositories/device-brands.repository.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/server/repositories/device-brands.repository.test.ts

Comment thread src/server/api/routers/mobile/deviceBrands.ts Outdated
@Producdevity Producdevity merged commit 5b70a20 into staging Jun 5, 2026
8 checks passed
@Producdevity Producdevity deleted the fix/admin-brand-filtering branch June 5, 2026 11:59
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.

refactor: fix temporary brand filtering on admin CPU and GPU pages

1 participant