Programmers Frontend UX — Platform Segment Toggle + useProgrammersSearch + AddProblemModal Genericization

Date
ImpactHigh

Key Decisions

Mirror `useBojSearch` interface exactly

Same hook signature and return shape allows AddProblemModal to switch hooks without conditional rendering complexity.

Local platform state in modal

Platform preference is ephemeral (per-session modal context). No global state or URL param needed.

`SegmentControl` reuse

Existing segment control component used — no new UI component created.

`PlatformProblemInfo` union type

BojProblemInfo | ProgrammersProblemInfo union handles both in ProblemResultCard. Type narrowing via sourcePlatform discriminant.

Background

Sprint 95 completed backend infrastructure (Gateway /api/external/programmers/* + 373-item JSON bundle). Sprint 96 delivers the user-visible frontend UX — the "platform toggle" that allows users to switch between BOJ and Programmers problem search within the existing AddProblemModal.

Goals

  1. Add platform segment toggle (BOJ | Programmers) to AddProblemModal
  2. Implement useProgrammersSearch hook (mirrors useBojSearch interface)
  3. Genericize AddProblemModal to support both platforms without code duplication
  4. Zero regression on existing BOJ search flow

Work Summary

CommitAgentContent
c1d2e3farchitectuseProgrammersSearch hook — fetches Gateway /api/external/programmers/search
g4h5i6jheraldAddProblemModal platform toggle UI + i18n keys
k7l8m9ngatekeeperRegression tests — BOJ flow + Programmers flow + toggle switching

Changes

useProgrammersSearch Hook

TypeScript
// frontend/src/hooks/useProgrammersSearch.ts
export function useProgrammersSearch(query: string, page: number) {
  return useSWR(
    query ? ['/api/external/programmers/search', query, page] : null,
    ([url, q, p]) => fetch(`${url}?query=${q}&page=${p}`).then(r => r.json()),
  );
}

Interface mirrors useBojSearch — same return shape ({ data, isLoading, error }).

AddProblemModal Genericization

Platform state added as local state:

TypeScript
const [platform, setPlatform] = useState<'BOJ' | 'PROGRAMMERS'>('BOJ');

Segment toggle rendered above search input:

TSX
<SegmentControl
  options={[
    { value: 'BOJ', label: t('platformBoj') },
    { value: 'PROGRAMMERS', label: t('platformProgrammers') },
  ]}
  value={platform}
  onChange={setPlatform}
/>

Search hook switched based on platform state. Result card rendering unified via ProblemResultCard generic component accepting PlatformProblemInfo union type.

i18n Keys Added

  • messages/ko/problems.json: platformBoj, platformProgrammers, searchPlaceholderProgrammers
  • messages/en/problems.json: same keys

Verification

ItemResult
BOJ search flow (no toggle change)✅ 0 regression
Programmers search — returns results✅ Gateway search endpoint called
Platform toggle — switches hook✅ Query reset on toggle
tsc --noEmit frontend✅ 0 errors
ESLint frontend✅ 0 errors
Test suite✅ PASS

Decisions

  • Mirror useBojSearch interface exactly: Same hook signature and return shape allows AddProblemModal to switch hooks without conditional rendering complexity.
  • Local platform state in modal: Platform preference is ephemeral (per-session modal context). No global state or URL param needed.
  • SegmentControl reuse: Existing segment control component used — no new UI component created.
  • PlatformProblemInfo union type: BojProblemInfo | ProgrammersProblemInfo union handles both in ProblemResultCard. Type narrowing via sourcePlatform discriminant.

Carried Over (Sprint 97)

  • tags display: Programmers problems have empty tags array — Sprint 97 tags crawler will backfill
  • GitHub Worker formatPlatform(): 'programmers' → 'PROGRAMMERS' case + prg_ filename prefix
  • AI feedback prompt sourcePlatform injection
  • Submission pipeline sourcePlatform MQ event field