feat: 지원 대학 관리 페이지 추가 (호스트 대학 전체 컬럼 표시 포함)#567
Conversation
createMutation.onSuccess에 queryClient.invalidateQueries 호출 추가로, 생성 후 검색 결과가 즉시 갱신되도록 개선합니다. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
|
Warning Review limit reached
More reviews will be available in 42 minutes and 52 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more credits in the billing tab to continue. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
Walkthrough
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Suggested reviewers
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9baa4e4595
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (3)
apps/admin/src/components/features/univ-apply-infos/tabs/UnivApplyInfoImportTab.tsx (3)
171-188: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
mappedFieldSet와previewColumns계산을useMemo로 감싸는 것을 권장합니다.현재 매 렌더마다 다음 연산이 반복됩니다:
mappedFieldSet: Set 생성 및 Object.values 순회previewColumns: 3개의 배열 필터/맵 연산 + 스프레드 병합
columnMappings가 변경될 때만 재계산하면 충분합니다.♻️ useMemo 적용 예시
+const mappedFieldSet = useMemo( + () => new Set(Object.values(columnMappings).filter(Boolean)), + [columnMappings], +); -const mappedFieldSet = new Set(Object.values(columnMappings).filter(Boolean)); +const previewColumns = useMemo(() => [ -const previewColumns: { field: string; label: string; required: boolean; mapped: boolean }[] = [ ...UNIV_APPLY_INFO_FIELDS.filter((f) => f.required).map((f) => ({ field: f.field, label: f.label, required: true, mapped: mappedFieldSet.has(f.field), })), ...UNIV_APPLY_INFO_FIELDS.filter((f) => !f.required && mappedFieldSet.has(f.field)).map((f) => ({ field: f.field, label: f.label, required: false, mapped: true, })), ...[...mappedFieldSet] .filter((f) => !UNIV_APPLY_INFO_FIELDS.some((sf) => sf.field === f)) .map((f) => ({ field: f, label: f, required: false, mapped: true })), -]; +], [mappedFieldSet]);🤖 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 `@apps/admin/src/components/features/univ-apply-infos/tabs/UnivApplyInfoImportTab.tsx` around lines 171 - 188, The `mappedFieldSet` and `previewColumns` calculations are being performed on every render, even when their dependencies haven't changed. Wrap both the `mappedFieldSet` Set creation and the `previewColumns` array construction in a single `useMemo` hook with `columnMappings` as the dependency array. This will ensure these expensive computations involving Set creation, object value filtering, and multiple array filter/map operations only run when `columnMappings` actually changes, improving performance.
141-156: 🧹 Nitpick | 🔵 Trivial | 💤 Low value
canConfirmImport변수가 함수 정의 이후에 선언되어 있어 코드 가독성이 저하됩니다.
handleConfirmImport함수(line 142)에서 참조하는canConfirmImport가 line 195에서 정의됩니다. JavaScript 클로저 덕분에 런타임에는 문제가 없지만, 코드 흐름을 따라가기 어렵습니다.
- 현상: 함수가 아래에서 정의된 변수를 참조
- 권장: 파생 데이터(derived data)를 핸들러 정의 위로 이동하거나, 핸들러를 파생 데이터 아래로 이동
🤖 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 `@apps/admin/src/components/features/univ-apply-infos/tabs/UnivApplyInfoImportTab.tsx` around lines 141 - 156, The handleConfirmImport function references the canConfirmImport variable before it is declared, which reduces code readability despite working at runtime due to JavaScript closures. Move the canConfirmImport variable declaration to appear before the handleConfirmImport function definition so that derived data is available when the handler is defined. This improves code flow and makes the data dependencies clear at a glance.
285-316: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
nonLangHeaders필터링이 매 반복마다 중복 실행됩니다.
UNIV_APPLY_INFO_FIELDS.map()내부(line 287)에서parsedHeaders.filter(...)가 호출되어, 필드 개수만큼 동일한 필터 연산이 반복됩니다.
- 현상: O(fields × headers) 복잡도
- 개선: 루프 외부에서 한 번만 계산하여 O(headers)로 감소
♻️ 루프 외부로 이동
+const nonLangHeaders = useMemo( + () => parsedHeaders.filter((h) => !(fields?.languageTestTypes.includes(h) ?? false)), + [parsedHeaders, fields?.languageTestTypes], +); {UNIV_APPLY_INFO_FIELDS.map((f) => { const mappedHeader = Object.entries(columnMappings).find(([, v]) => v === f.field)?.[0] ?? ""; - const nonLangHeaders = parsedHeaders.filter((h) => !(fields?.languageTestTypes.includes(h) ?? false)); return (🤖 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 `@apps/admin/src/components/features/univ-apply-infos/tabs/UnivApplyInfoImportTab.tsx` around lines 285 - 316, The nonLangHeaders variable is being computed inside the UNIV_APPLY_INFO_FIELDS.map() function on line 287, causing the same filter operation to execute repeatedly for each field iteration. Move the nonLangHeaders calculation outside and above the map function so it is computed only once before the loop starts, then reference the already-computed nonLangHeaders inside the map callback. This will reduce the time complexity from O(fields × headers) to O(headers).
🤖 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
`@apps/admin/src/components/features/univ-apply-infos/tabs/HostUniversityTab.tsx`:
- Around line 152-166: The handleOpenEdit function has a race condition where
rapidly selecting different universities can cause stale responses from previous
getHostUniversity calls to overwrite the current form and modal state with
incorrect data. To fix this, implement a mechanism to cancel or ignore responses
from outdated requests, such as using an AbortController to cancel the previous
async call when handleOpenEdit is invoked for a different university ID, or by
tracking a request ID/timestamp and only updating form and modal state if the
response matches the current request. This ensures that only the response from
the most recent getHostUniversity call updates the state.
In
`@apps/admin/src/components/features/univ-apply-infos/tabs/UnivApplyInfoManageTab.tsx`:
- Around line 528-545: The select element for host university selection is
uncontrolled because it lacks a value prop binding to
createForm.hostUniversityId. This causes the visual selection to not sync with
the state, so when validation runs it checks the unupdated hostUniversityId
value and fails the required check. Fix this by adding a value prop to the
select element that is set to createForm.hostUniversityId, converting it from an
uncontrolled to a controlled component. This ensures the selected option
visually matches and syncs with the state value that the validation checks.
- Around line 38-44: The parseExtraInfo function validates that the parsed JSON
is an object but does not validate that all values within that object are
strings before casting to Record<string, string>. This can allow non-string
values (like numbers) to pass through, breaking the API contract. After
confirming the parsed value is an object and not null and not an array, add
validation to ensure every value in the object is actually a string type before
returning it. If any value is not a string, return undefined instead of casting.
Apply the same validation fix to the other locations mentioned (lines 165-168
and 185-188) where similar type assertions occur without proper value
validation.
---
Nitpick comments:
In
`@apps/admin/src/components/features/univ-apply-infos/tabs/UnivApplyInfoImportTab.tsx`:
- Around line 171-188: The `mappedFieldSet` and `previewColumns` calculations
are being performed on every render, even when their dependencies haven't
changed. Wrap both the `mappedFieldSet` Set creation and the `previewColumns`
array construction in a single `useMemo` hook with `columnMappings` as the
dependency array. This will ensure these expensive computations involving Set
creation, object value filtering, and multiple array filter/map operations only
run when `columnMappings` actually changes, improving performance.
- Around line 141-156: The handleConfirmImport function references the
canConfirmImport variable before it is declared, which reduces code readability
despite working at runtime due to JavaScript closures. Move the canConfirmImport
variable declaration to appear before the handleConfirmImport function
definition so that derived data is available when the handler is defined. This
improves code flow and makes the data dependencies clear at a glance.
- Around line 285-316: The nonLangHeaders variable is being computed inside the
UNIV_APPLY_INFO_FIELDS.map() function on line 287, causing the same filter
operation to execute repeatedly for each field iteration. Move the
nonLangHeaders calculation outside and above the map function so it is computed
only once before the loop starts, then reference the already-computed
nonLangHeaders inside the map callback. This will reduce the time complexity
from O(fields × headers) to O(headers).
🪄 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: a78a5b0a-94ba-404a-adf3-d0eb7b1a08ce
📒 Files selected for processing (7)
apps/admin/src/components/features/univ-apply-infos/UnivApplyInfosPageContent.tsxapps/admin/src/components/features/univ-apply-infos/tabs/HostUniversityTab.tsxapps/admin/src/components/features/univ-apply-infos/tabs/UnivApplyInfoImportTab.tsxapps/admin/src/components/features/univ-apply-infos/tabs/UnivApplyInfoManageTab.tsxapps/admin/src/components/layout/AdminLayout.tsxapps/admin/src/components/layout/AdminSidebar.tsxapps/admin/src/lib/api/admin.ts
…rch functionality
Summary
min-w-0추가)Test plan
🤖 Generated with Claude Code