Skip to content

Refactor segmented control, schemas, and image selector#433

Merged
Producdevity merged 5 commits into
stagingfrom
refactor/code-quality-todos
Jun 6, 2026
Merged

Refactor segmented control, schemas, and image selector#433
Producdevity merged 5 commits into
stagingfrom
refactor/code-quality-todos

Conversation

@Producdevity

@Producdevity Producdevity commented Jun 5, 2026

Copy link
Copy Markdown
Owner

Description

  • Renames the three-option toggle to SegmentedControl and updates its usage.
  • Extracts shared sort-direction schema handling, moves success-rate bar colors into badge color utilities, and replaces profile access reason literals with constants.
  • Adds IGDB as a moderator-only image provider option while the author manual-add flow keeps its existing RAWG/TheGamesDB provider choices.

Closes #410.

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Code quality improvement

Testing

  • ./node_modules/.bin/vitest run src/server/services/user-profile.service.test.ts
  • ./node_modules/.bin/vitest run src/server/services/user-profile.service.test.ts src/server/repositories/device-brands.repository.test.ts
  • ./node_modules/.bin/eslint src/app/games/[id]/components/GameEditForm.tsx src/app/games/new/NewGamePage.tsx src/components/ui/image-selectors/ImageSelectorSwitcher.tsx
  • ./node_modules/.bin/eslint .
  • ./node_modules/.bin/tsc --noEmit

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
  • My changes generate no new warnings
  • New and existing tests pass locally with my changes

@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 6:35pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Generalizes ThreeWayToggle to SegmentedControl, centralizes SortDirection into a shared schema and updates many schema modules, moves success-rate color logic to badge-colors, introduces PROFILE_ACCESS_REASONS and type, and adds IGDB as an image provider with direction-based animations.

Changes

Core Refactorings and Generalizations

Layer / File(s) Summary
SegmentedControl component generalization
src/components/ui/SegmentedControl.tsx, src/app/games/components/GameFilters.tsx, src/components/ui/index.ts
SegmentedControl replaces fixed 3-column ThreeWayToggle, now accepts variable-length options with computed grid columns. GameFilters and barrel exports updated to use new component and types.
Success rate color utility relocation
src/utils/badge-colors.ts, src/utils/vote.ts, src/components/ui/SuccessRateBar.tsx, src/components/ui/VoteButtons.tsx
getBarColor logic moved to getSuccessRateBarColor in badge-colors.ts. SuccessRateBar and VoteButtons now import and use the new helper; vote.ts no longer exports the color helper.
SortDirection schema extraction and replacements
src/schemas/common.ts, src/schemas/apiAccess.ts, src/schemas/audit.ts, src/schemas/deviceBrand.ts, src/schemas/game.ts, src/schemas/permission.ts, src/schemas/soc.ts, src/schemas/system.ts, src/schemas/user.ts, plus cpu.ts, device.ts, emulator.ts, gpu.ts, listingReport.ts, performanceScale.ts, userBan.ts, voteInvestigation.ts
Introduces shared SortDirectionSchema in common.ts and updates many schema modules to import and use it instead of local SortDirection enums; minor comment cleanups accompany these changes.
Profile access reason constants and type safety
src/server/services/user-profile.service.ts, src/server/services/user-profile.service.test.ts
Adds PROFILE_ACCESS_REASONS constant and ProfileAccessReason type; InaccessibleProfile.reason and checkProfileAccess early-return reasons updated to use constants; tests updated to assert the constant values.

IGDB Image Provider Expansion

Layer / File(s) Summary
IGDB image service and direction animation
src/components/ui/image-selectors/ImageSelectorSwitcher.tsx
Adds igdb to ImageService, replaces the earlier toggle UI with three provider buttons (RAWG.io, TheGamesDB, IGDB), computes a signed animation direction from provider index differences, and renders provider branches with AnimatePresence driven by that direction.

Estimated code review effort:
🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs:

  • Producdevity/EmuReady#432: Modifies GetDeviceBrandsSchema—this PR also changes sortDirection usage for deviceBrand schemas.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.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 and concisely summarizes the main changes: refactoring segmented control, schemas, and image selector components.
Linked Issues check ✅ Passed All six objectives from issue #410 are addressed: ThreeWayToggle renamed to SegmentedControl [#410], sortDirection schema extracted [#410], success-rate color utility moved [#410], profile access reasons refactored [#410], IGDB image source added [#410], and z.infer clarification added [#410].
Out of Scope Changes check ✅ Passed All code changes are directly aligned with the six objectives from issue #410; no out-of-scope modifications were introduced.
Description check ✅ Passed The pull request description covers the main objectives with issue reference, type of change, and test execution details, though documentation update was not completed.

✏️ 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 refactor/code-quality-todos
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch refactor/code-quality-todos

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.

Comment thread src/schemas/apiAccess.ts Outdated

export const ApiKeySortFieldSchema = z.enum(['name', 'createdAt', 'lastUsedAt', 'monthlyQuota'])
export const SortDirectionSchema = z.enum(['asc', 'desc'])
export { SortDirectionSchema }

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

just import this directly from schemas/common whereever it is needed. avoid renaming using as in the import unless it conflicts with anything

Comment thread src/schemas/audit.ts Outdated
import { AuditAction, AuditEntityType } from '@orm'

export const SortDirection = z.enum(['asc', 'desc'])
export { SortDirection }

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Comment thread src/schemas/common.ts Outdated
@@ -1,11 +1,14 @@
import { z } from 'zod'

export const SortDirection = z.enum(['asc', 'desc'])

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

maybe renaming this to SortDirectionSchema makes more sense and keeps it consistent everywhere

Comment thread src/schemas/deviceBrand.ts Outdated
Comment on lines +2 to +5
import { SortDirection } from '@/schemas/common'

export const DeviceBrandSortField = z.enum(['name', 'devicesCount'])
export const SortDirection = z.enum(['asc', 'desc'])
export { SortDirection }

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Comment thread src/schemas/game.ts Outdated
])

export const SortDirection = z.enum(['asc', 'desc'])
export { SortDirection }

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

same here https://github.com/Producdevity/EmuReady/pull/433/changes#r3362439440 do not import and reexport unless the schema requires extension of changes

Comment thread src/schemas/gpu.ts Outdated
Comment on lines +43 to +46
// Type exports for repository use
// Use z.input for types that include defaults (what you pass in)
// Use z.output for types after defaults are applied (what you get out)
// TODO: figure out why we use z.infer
// The remaining schemas do not apply defaults or transforms, so z.infer matches their parsed shape.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

get rid of slop comments

Comment thread src/schemas/permission.ts Outdated
export const PermissionSortField = z.enum(['label', 'key', 'category', 'createdAt', 'updatedAt'])

export const SortDirection = z.enum(['asc', 'desc'])
export { SortDirection }

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Comment thread src/schemas/soc.ts Outdated

export const SoCSortField = z.enum(['name', 'manufacturer', 'devicesCount'])
export const SortDirection = z.enum(['asc', 'desc'])
export { SortDirection }

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Comment thread src/schemas/system.ts Outdated

export const SystemSortField = z.enum(['name', 'key', 'gamesCount'])
export const SortDirection = z.enum(['asc', 'desc'])
export { SortDirection }

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

same here and everywhere else where this pattern is repeated. https://github.com/Producdevity/EmuReady/pull/433/changes#r3362439440

@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

Caution

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

⚠️ Outside diff range comments (1)
src/schemas/gpu.ts (1)

2-2: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Import SortDirection from the consolidated location.

This file imports SortDirection from @/schemas/soc, but the consolidation effort moved the canonical definition to @/schemas/common. Other schema modules (apiAccess.ts, system.ts, user.ts) import directly from @/schemas/common.

Update the import to match the consolidation pattern:

🔄 Recommended fix
-import { SortDirection } from '`@/schemas/soc`'
+import { SortDirection } from '`@/schemas/common`'
🤖 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/schemas/gpu.ts` at line 2, Update the import of SortDirection in
src/schemas/gpu.ts to use the consolidated canonical module; replace the
existing import from '`@/schemas/soc`' with an import from '`@/schemas/common`' so
SortDirection is imported from the same central location used by apiAccess.ts,
system.ts, and user.ts.
🧹 Nitpick comments (2)
src/components/ui/image-selectors/ImageSelectorSwitcher.tsx (1)

156-156: 💤 Low value

Remove redundant custom prop on AnimatePresence.

The custom prop on line 156 is overridden by each child motion.div's own custom value (lines 160, 177, 193), so the parent's value is never used.

♻️ Simplify by removing unused prop
-        <AnimatePresence mode="wait" custom={imageServiceDirection[selectedService]}>
+        <AnimatePresence mode="wait">
🤖 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/image-selectors/ImageSelectorSwitcher.tsx` at line 156, In
ImageSelectorSwitcher, remove the redundant custom prop on the AnimatePresence
component (the prop currently set to imageServiceDirection[selectedService])
because each child motion.div already provides its own custom value; update the
JSX by deleting the custom attribute on AnimatePresence so the per-child custom
values on the motion.div elements (the ones rendering each service) take effect
without being overridden.
src/schemas/apiAccess.ts (1)

2-9: 💤 Low value

Naming inconsistency across schema modules.

This module re-exports as SortDirectionSchema (via import alias) while other modules (system.ts, user.ts, etc.) re-export as SortDirection. The aliasing maintains backwards compatibility with existing imports, but creates a naming discrepancy across the codebase.

Consider renaming the re-export to SortDirection for consistency with other schema modules, and update any downstream imports of SortDirectionSchema from this module.

🤖 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/schemas/apiAccess.ts` around lines 2 - 9, Rename the re-exported
SortDirectionSchema to match the codebase convention by exporting it as
SortDirection instead of SortDirectionSchema: change the current import alias or
re-export so the symbol exported from this module is SortDirection (referencing
the existing SortDirectionSchema identifier), and then update any downstream
imports that currently import SortDirectionSchema from this module to import
SortDirection to maintain consistency with other schema modules (e.g.,
system.ts, user.ts).
🤖 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/image-selectors/ImageSelectorSwitcher.tsx`:
- Around line 29-33: The current imageServiceDirection mapping is wrong because
it encodes absolute positions instead of transition direction; modify
ImageSelectorSwitcher to compute the transition direction dynamically from the
previous and new service indices (e.g., direction = newIndex - prevIndex), store
the previous index in a ref or state, and create a handler (replace direct
setSelectedService calls with handleServiceChange) that updates selectedService
and prev index and sets a numeric direction state; pass that direction into
AnimatePresence/motion via the custom prop (custom={direction}) and use that
custom value in your animation variants so enter/exit use the computed
transition direction for smooth left/right animations.

---

Outside diff comments:
In `@src/schemas/gpu.ts`:
- Line 2: Update the import of SortDirection in src/schemas/gpu.ts to use the
consolidated canonical module; replace the existing import from '`@/schemas/soc`'
with an import from '`@/schemas/common`' so SortDirection is imported from the
same central location used by apiAccess.ts, system.ts, and user.ts.

---

Nitpick comments:
In `@src/components/ui/image-selectors/ImageSelectorSwitcher.tsx`:
- Line 156: In ImageSelectorSwitcher, remove the redundant custom prop on the
AnimatePresence component (the prop currently set to
imageServiceDirection[selectedService]) because each child motion.div already
provides its own custom value; update the JSX by deleting the custom attribute
on AnimatePresence so the per-child custom values on the motion.div elements
(the ones rendering each service) take effect without being overridden.

In `@src/schemas/apiAccess.ts`:
- Around line 2-9: Rename the re-exported SortDirectionSchema to match the
codebase convention by exporting it as SortDirection instead of
SortDirectionSchema: change the current import alias or re-export so the symbol
exported from this module is SortDirection (referencing the existing
SortDirectionSchema identifier), and then update any downstream imports that
currently import SortDirectionSchema from this module to import SortDirection to
maintain consistency with other schema modules (e.g., system.ts, user.ts).
🪄 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: 8986b048-b4af-4618-8be5-f7c15825431b

📥 Commits

Reviewing files that changed from the base of the PR and between 7abf8b0 and af46a87.

📒 Files selected for processing (20)
  • src/app/games/components/GameFilters.tsx
  • src/components/ui/SegmentedControl.tsx
  • src/components/ui/SuccessRateBar.tsx
  • src/components/ui/VoteButtons.tsx
  • src/components/ui/image-selectors/ImageSelectorSwitcher.tsx
  • src/components/ui/index.ts
  • src/schemas/apiAccess.ts
  • src/schemas/audit.ts
  • src/schemas/common.ts
  • src/schemas/deviceBrand.ts
  • src/schemas/game.ts
  • src/schemas/gpu.ts
  • src/schemas/permission.ts
  • src/schemas/soc.ts
  • src/schemas/system.ts
  • src/schemas/user.ts
  • src/server/services/user-profile.service.test.ts
  • src/server/services/user-profile.service.ts
  • src/utils/badge-colors.ts
  • src/utils/vote.ts
💤 Files with no reviewable changes (1)
  • src/utils/vote.ts

Comment thread src/components/ui/image-selectors/ImageSelectorSwitcher.tsx Outdated
Comment thread src/utils/badge-colors.ts
return permissionCategoryVariantMap[permissionCategory] || 'default'
}

export function getSuccessRateBarColor(rate: number): string {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

this was called something in a useMemo, i don't think that is needed for this small function

@Producdevity Producdevity changed the title Resolve code quality TODOs Refactor segmented control, schemas, and image selector Jun 5, 2026

@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/components/ui/image-selectors/ImageSelectorSwitcher.tsx (1)

57-130: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Expose selected state on provider buttons for assistive tech.

The segmented buttons visually indicate selection, but they don’t expose state semantically. Add aria-pressed (or tab semantics) so screen readers can identify the active provider (e.g., Line 57, Line 80, Line 106).

Suggested patch
             <button
               type="button"
               onClick={() => handleServiceChange('rawg')}
+              aria-pressed={selectedService === 'rawg'}
               className={cn(
@@
             <button
               type="button"
               onClick={() => handleServiceChange('tgdb')}
+              aria-pressed={selectedService === 'tgdb'}
               className={cn(
@@
             <button
               type="button"
               onClick={() => handleServiceChange('igdb')}
+              aria-pressed={selectedService === 'igdb'}
               className={cn(
🤖 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/image-selectors/ImageSelectorSwitcher.tsx` around lines 57
- 130, The provider buttons in ImageSelectorSwitcher.tsx do not expose their
selected state to assistive tech; update each button (the ones using onClick={()
=> handleServiceChange('rawg'|'tgdb'|'igdb')} and reading selectedService) to
include aria-pressed={selectedService === 'rawg'|'tgdb'|'igdb'} so screen
readers can detect the active provider; ensure the attribute value is a boolean
expression matching the current selectedService for each respective button.
🤖 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/components/ui/image-selectors/ImageSelectorSwitcher.tsx`:
- Around line 57-130: The provider buttons in ImageSelectorSwitcher.tsx do not
expose their selected state to assistive tech; update each button (the ones
using onClick={() => handleServiceChange('rawg'|'tgdb'|'igdb')} and reading
selectedService) to include aria-pressed={selectedService ===
'rawg'|'tgdb'|'igdb'} so screen readers can detect the active provider; ensure
the attribute value is a boolean expression matching the current selectedService
for each respective button.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 28010aa7-2c2d-45d4-a27a-2394fa01d097

📥 Commits

Reviewing files that changed from the base of the PR and between af46a87 and d3b4060.

📒 Files selected for processing (19)
  • src/components/ui/SuccessRateBar.tsx
  • src/components/ui/image-selectors/ImageSelectorSwitcher.tsx
  • src/schemas/apiAccess.ts
  • src/schemas/audit.ts
  • src/schemas/common.ts
  • src/schemas/cpu.ts
  • src/schemas/device.ts
  • src/schemas/deviceBrand.ts
  • src/schemas/emulator.ts
  • src/schemas/game.ts
  • src/schemas/gpu.ts
  • src/schemas/listingReport.ts
  • src/schemas/performanceScale.ts
  • src/schemas/permission.ts
  • src/schemas/soc.ts
  • src/schemas/system.ts
  • src/schemas/user.ts
  • src/schemas/userBan.ts
  • src/schemas/voteInvestigation.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/schemas/common.ts

@Producdevity Producdevity merged commit 3fab7da into staging Jun 6, 2026
8 checks passed
@Producdevity Producdevity deleted the refactor/code-quality-todos branch June 6, 2026 08:05
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: misc code quality improvements (ThreeWayToggle naming, z.infer clarification, sortDirection extraction, vote utility move)

1 participant