Skip to content

Add is_extended_promotional filter to components and search#131

Open
yuliuyi717-ux wants to merge 1 commit intotscircuit:mainfrom
yuliuyi717-ux:codex/issue-92-extended-promotional-filter
Open

Add is_extended_promotional filter to components and search#131
yuliuyi717-ux wants to merge 1 commit intotscircuit:mainfrom
yuliuyi717-ux:codex/issue-92-extended-promotional-filter

Conversation

@yuliuyi717-ux
Copy link

Implements is_extended_promotional support for issue #92 using existing source fields (preferred + basic) without adding a new DB migration.

Proposed changes

  • add is_extended_promotional query param to:
    • /components/list
    • /api/search
  • compute is_extended_promotional as preferred = 1 AND basic = 0
  • include is_extended_promotional in API response payloads
  • add filter behavior for is_extended_promotional=true in both routes
  • add focused route tests verifying:
    • computed flag matches (preferred == 1 && basic == 0)
    • filtered results only contain extended promotional components

Proof

Local validation run:

  • bunx tsc --noEmit
  • bunx biome check routes/api/search.tsx routes/components/list.tsx tests/routes/api/search.test.ts tests/routes/components/list.test.ts
  • bun test tests/routes/components/list.test.ts
  • bun test tests/routes/api/search.test.ts -t "supports is_extended_promotional filter"

/claim #92

Comment on lines +90 to +110
const allComponents = allRes.data.components as Array<{
basic: number
preferred: number
is_extended_promotional: number
}>
const filteredComponents = filteredRes.data.components as Array<{
basic: number
preferred: number
is_extended_promotional: number
}>

for (const component of allComponents) {
const expected = component.preferred === 1 && component.basic === 0 ? 1 : 0
expect(component.is_extended_promotional).toBe(expected)
}

for (const component of filteredComponents) {
expect(component.is_extended_promotional).toBe(1)
expect(component.preferred).toBe(1)
expect(component.basic).toBe(0)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Type mismatch: tests expect numeric values (0/1) but API returns booleans. The route transforms all flags with Boolean() (line 81-93 in search.tsx), so is_extended_promotional, basic, and preferred will be true/false in responses, not 0/1. This causes test failures:

// Current (broken):
component.preferred === 1  // false === 1 → false
component.basic === 0      // true === 0 → false

// Should be:
component.preferred === true
component.basic === false
component.is_extended_promotional === true

// And type should be:
type Component = {
  basic: boolean
  preferred: boolean
  is_extended_promotional: boolean
}
Suggested change
const allComponents = allRes.data.components as Array<{
basic: number
preferred: number
is_extended_promotional: number
}>
const filteredComponents = filteredRes.data.components as Array<{
basic: number
preferred: number
is_extended_promotional: number
}>
for (const component of allComponents) {
const expected = component.preferred === 1 && component.basic === 0 ? 1 : 0
expect(component.is_extended_promotional).toBe(expected)
}
for (const component of filteredComponents) {
expect(component.is_extended_promotional).toBe(1)
expect(component.preferred).toBe(1)
expect(component.basic).toBe(0)
}
const allComponents = allRes.data.components as Array<{
basic: boolean
preferred: boolean
is_extended_promotional: boolean
}>
const filteredComponents = filteredRes.data.components as Array<{
basic: boolean
preferred: boolean
is_extended_promotional: boolean
}>
for (const component of allComponents) {
const expected = component.preferred === true && component.basic === false
expect(component.is_extended_promotional).toBe(expected)
}
for (const component of filteredComponents) {
expect(component.is_extended_promotional).toBe(true)
expect(component.preferred).toBe(true)
expect(component.basic).toBe(false)
}

Spotted by Graphite

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

This comment came from an experimental review—please leave feedback if it was helpful/unhelpful. Learn more about experimental comments here.

Comment on lines +24 to +44
const allComponents = allRes.data.components as Array<{
basic: number
preferred: number
is_extended_promotional: number
}>
const filteredComponents = filteredRes.data.components as Array<{
basic: number
preferred: number
is_extended_promotional: number
}>

for (const component of allComponents) {
const expected = component.preferred === 1 && component.basic === 0 ? 1 : 0
expect(component.is_extended_promotional).toBe(expected)
}

for (const component of filteredComponents) {
expect(component.is_extended_promotional).toBe(1)
expect(component.preferred).toBe(1)
expect(component.basic).toBe(0)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Type mismatch: tests expect numeric values (0/1) but API returns booleans. The route transforms all flags with Boolean() (line 83-94 in list.tsx), so is_extended_promotional, basic, and preferred will be true/false in responses, not 0/1. This causes test failures:

// Current (broken):
component.preferred === 1  // false === 1 → false
component.basic === 0      // true === 0 → false

// Should be:
component.preferred === true
component.basic === false
component.is_extended_promotional === true

// And type should be:
type Component = {
  basic: boolean
  preferred: boolean
  is_extended_promotional: boolean
}
Suggested change
const allComponents = allRes.data.components as Array<{
basic: number
preferred: number
is_extended_promotional: number
}>
const filteredComponents = filteredRes.data.components as Array<{
basic: number
preferred: number
is_extended_promotional: number
}>
for (const component of allComponents) {
const expected = component.preferred === 1 && component.basic === 0 ? 1 : 0
expect(component.is_extended_promotional).toBe(expected)
}
for (const component of filteredComponents) {
expect(component.is_extended_promotional).toBe(1)
expect(component.preferred).toBe(1)
expect(component.basic).toBe(0)
}
const allComponents = allRes.data.components as Array<{
basic: boolean
preferred: boolean
is_extended_promotional: boolean
}>
const filteredComponents = filteredRes.data.components as Array<{
basic: boolean
preferred: boolean
is_extended_promotional: boolean
}>
for (const component of allComponents) {
const expected = component.preferred === true && component.basic === false ? true : false
expect(component.is_extended_promotional).toBe(expected)
}
for (const component of filteredComponents) {
expect(component.is_extended_promotional).toBe(true)
expect(component.preferred).toBe(true)
expect(component.basic).toBe(false)
}

Spotted by Graphite

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

This comment came from an experimental review—please leave feedback if it was helpful/unhelpful. Learn more about experimental comments here.

@yuliuyi717-ux
Copy link
Author

Quick follow-up on this PR. I can make any adjustments immediately if there are review notes. If the current approach looks good, a merge when convenient would be appreciated.

@yuliuyi717-ux
Copy link
Author

Quick follow-up: this PR is still clean and mergeable. If you want any naming/query-shape tweaks before merge, I can update right away.

@yuliuyi717-ux yuliuyi717-ux force-pushed the codex/issue-92-extended-promotional-filter branch from e4e3cca to afa4f6d Compare March 5, 2026 05:05
Comment on lines +112 to +140
test("GET /api/search supports is_extended_promotional filter", async () => {
const { axios } = await getTestServer()
const allRes = await axios.get("/api/search?full=true&limit=200")
const filteredRes = await axios.get(
"/api/search?full=true&limit=200&is_extended_promotional=true",
)

const allComponents = allRes.data.components as Array<{
basic: number
preferred: number
is_extended_promotional: number
}>
const filteredComponents = filteredRes.data.components as Array<{
basic: number
preferred: number
is_extended_promotional: number
}>

for (const component of allComponents) {
const expected = component.preferred === 1 && component.basic === 0 ? 1 : 0
expect(component.is_extended_promotional).toBe(expected)
}

for (const component of filteredComponents) {
expect(component.is_extended_promotional).toBe(1)
expect(component.preferred).toBe(1)
expect(component.basic).toBe(0)
}
})
Copy link
Contributor

Choose a reason for hiding this comment

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

This test file violates the rule that a *.test.ts file may have AT MOST one test(...) function. The file already contains multiple test functions, and this modification adds another test function starting at line 112. According to the rule, after the first test, additional tests should be split into multiple, numbered files (e.g., search1.test.ts, search2.test.ts). To fix this, create a new file like search2.test.ts and move this new test function there.

Spotted by Graphite (based on custom rule: Custom rule)

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

@yuliuyi717-ux
Copy link
Author

Gentle follow-up on #131. The filter support is ready for review; happy to revise quickly if you want changes before merge. Thanks in advance.

@yuliuyi717-ux
Copy link
Author

Clarification to my last note: this PR adds the is_extended_promotional filter support in components and search. Happy to adjust naming or query behavior if needed.

@yuliuyi717-ux
Copy link
Author

Friendly bump on this one—I added the is_extended_promotional filter for components and search in #131. When you have a moment, I’d really appreciate a review. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant