feat: 어드민 대학 지원 정보 관리 기능 추가#558
Conversation
|
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 41 minutes and 51 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이번 PR은 admin 앱에 세 가지 주요 기능을 신규로 추가합니다.
추가로 사이드바에 세 메뉴 항목 등록, Vite에 Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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 |
| "linkifyjs": "^4.3.2", | ||
| "lucide-react": "^0.479.0", | ||
| "next": "^16.2.6", | ||
| "next": "^16.2.9", |
There was a problem hiding this comment.
정확한 원인은 기억이 잘 나지 않는데, 개발 서버 실행이 실패해서 이것저것 만지다가 들어간 거 같습니다.
롤백해도 어드민 정상 동작하는 거 확인했고, 롤백했습니다 !
There was a problem hiding this comment.
제가 ai 셋업할때 어드민은 어드민만 수정하게 좀 유도를 스킬화 해놓은것 같은데
"그게 잘 안작동했나?"하는 생각이 들어서 그랬습니다
혹시 하네스 설정 해보고 싶은거 있으면 추가해도 좋습니다 같이 얘기해도 좋고요
There was a problem hiding this comment.
아 그런가요 ? 그렇다면 그 부분 관련해서 저도 한 번 살펴보겠습니다 !! 어드민 쪽 웹 만질 일이 많을 거 같아서요
작업 당시에는 별도의 스킬이 호출되거나 그러진 않았던 거로 기억합니다
| @@ -0,0 +1,133 @@ | |||
| export const COUNTRY_CODE_BY_NAME: Record<string, string> = { | |||
There was a problem hiding this comment.
따로 빼도 좋을것 같아요 상위에 constant 만들어서!
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8cf689fa07
ℹ️ 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".
| toast.error("마크다운 헤더를 파싱할 수 없습니다. 형식을 확인해주세요."); | ||
| return; | ||
| } | ||
| const auto = buildAutoMappings(headers, fieldsQuery.data?.languageTestTypes ?? []); |
There was a problem hiding this comment.
Disable parsing until field metadata is loaded
When an admin clicks 파싱 before fieldsQuery has returned, this falls back to an empty languageTestTypes list, so headers such as TOEIC/TOEFL are never written to columnMappings. The later query success does not recompute mappings and the language rows are rendered read-only, so the import can proceed without mapping those score columns and the bulk import loses/ignores the language-test data from that paste.
Useful? React with 👍 / 👎.
| for (const row of rows) { | ||
| for (const [field, cell] of Object.entries(row.cellsByField)) { | ||
| const rules = FIELD_RULES[field]; | ||
| if (!rules) continue; | ||
| const message = validateCell(cell.value, rules); |
There was a problem hiding this comment.
Validate missing required import fields
In cases where a required column is not mapped, or a row lacks a cell for that mapped column, it is absent from row.cellsByField, so this loop never applies any required-field rule and clientCellErrors remains empty. A table like the placeholder can be previewed with required columns shown as — and still enable the final 추가 button, causing the server import to fail with only the generic mutation error instead of blocking the invalid preview.
Useful? React with 👍 / 👎.
| it("contains only required system fields", () => { | ||
| expect(UNIV_APPLY_INFO_FIELDS.every((field) => field.required)).toBe(true); |
There was a problem hiding this comment.
Fix the contradictory field test
This assertion currently fails because UNIV_APPLY_INFO_FIELDS also includes universityHomepageUrl with required: false, so any run of the admin Vitest suite will fail before exercising the import logic. Either keep this list required-only or update the test to allow the optional homepage field.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (2)
apps/admin/src/components/features/home-universities/HomeUniversitiesPageContent.tsx (2)
114-121: ⚡ Quick win접근성 개선: 폼 입력 필드에 aria-label 추가 권장
현재 생성 폼의
Input컴포넌트들에 명시적인 레이블이 없습니다. 다음을 권장합니다:
- 대학명 입력 (Line 114):
aria-label="대학명"추가- 최대 지망 수 입력 (Line 115):
aria-label="최대 지망 수"추가참고로
TermsPageContent.tsxLine 87에서는aria-label="학기 이름"을 이미 사용하고 있어, 일관성을 맞추면 스크린 리더 사용자에게 더 나은 경험을 제공할 수 있습니다.♻️ 제안 수정안
- <Input value={name} onChange={(e) => setName(e.target.value)} placeholder="대학명" /> + <Input value={name} onChange={(e) => setName(e.target.value)} placeholder="대학명" aria-label="대학명" /> <Input value={maxChoiceCount} onChange={(e) => setMaxChoiceCount(e.target.value)} placeholder="최대 지망 수" type="number" min={1} + aria-label="최대 지망 수" />🤖 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/home-universities/HomeUniversitiesPageContent.tsx` around lines 114 - 121, The Input components in the home universities form lack explicit accessibility labels for screen readers. Add aria-label attributes to both Input components to improve accessibility: add aria-label="대학명" to the Input component that has placeholder "대학명" and the setName handler, and add aria-label="최대 지망 수" to the Input component that has placeholder "최대 지망 수" and the setMaxChoiceCount handler with type="number". This will make the form more accessible to screen reader users and maintain consistency with the pattern already used in TermsPageContent.tsx.
90-93: ⚖️ Poor tradeoff선택적 개선: 확인 모달 고려
현재
window.confirm을 사용하여 삭제를 확인하고 있습니다. 기능적으로 문제는 없으나, 다음을 고려해 보세요:
- 브라우저 기본 confirm 대화상자는 스타일링이 불가능하고 UX가 일관되지 않을 수 있습니다
- 커스텀 모달 컴포넌트로 교체하면 브랜딩과 접근성을 개선할 수 있습니다
다만 관리자 도구에서는 현재 구현도 충분히 실용적이므로, 우선순위에 따라 결정하시면 됩니다.
🤖 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/home-universities/HomeUniversitiesPageContent.tsx` around lines 90 - 93, The handleDelete function currently uses window.confirm for deletion confirmation, which cannot be styled and provides inconsistent UX. Consider replacing the window.confirm call with a custom confirmation modal component that matches your application's design system and provides better accessibility. This would involve creating or using an existing modal component in your project, passing the university name and ID to it, and calling deleteMutation.mutate(id) only after the user confirms in the custom modal instead of in the confirm callback.
🤖 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/univApplyInfoFields.test.ts`:
- Around line 5-7: The test assertion in "contains only required system fields"
currently expects all fields in UNIV_APPLY_INFO_FIELDS to have required: true,
but the actual constant definition contains fields with required: false, causing
the test to fail. Update the test to properly validate the field structure by
checking that fields are correctly categorized as either required or optional,
rather than asserting that all fields must be required. This can be done by
splitting the test logic to separately validate required fields and optional
fields within UNIV_APPLY_INFO_FIELDS, or by adjusting the assertion to match the
actual schema structure where some fields have required: false.
In
`@apps/admin/src/components/features/univ-apply-infos/UnivApplyInfosPageContent.tsx`:
- Around line 419-423: The Button component's disabled condition does not
prevent users from importing when preview data is empty. Add a check for
previewRows.length === 0 to the disabled prop alongside the existing conditions
for importMutation.isPending and clientCellErrors.size > 0. Additionally, add a
guard condition at the beginning of the handleConfirmImport function to return
early if previewRows is empty, preventing any empty import requests from being
processed.
In
`@apps/admin/src/components/features/univ-apply-infos/univApplyInfoValidation.ts`:
- Around line 23-59: The FIELD_RULES object is missing required field validation
for multiple fields that should be mandatory. Add a type "required" rule with an
appropriate error message to all fields that must not be empty (such as
universityKoreanName should serve as a template). Additionally, verify that the
validation implementation in the code around lines 93-105 properly checks for
empty, null, and undefined values when processing "required" type rules to
ensure invalid rows with missing required fields are caught before import.
In `@apps/admin/src/lib/api/admin.ts`:
- Around line 76-84: The TermCreatePayload interface uses a "name" field while
the TermResponse interface uses a "label" field for the same concept, creating
inconsistency in the API contract. To improve clarity and maintainability,
standardize the field name across both interfaces. Choose either "label" or
"name" and update both TermCreatePayload and TermResponse to use the same field
name consistently. Since TermsPageContent.tsx already references term.label,
aligning TermCreatePayload to use "label" instead of "name" would be the
recommended approach to maintain consistency throughout the codebase.
In `@apps/admin/vite.config.ts`:
- Around line 7-14: The hardcoded path in vinextNavShim does not work correctly
in workspace environments with varying module layouts. Replace the hardcoded
node_modules/vinext/dist/shims/navigation.js path with
require.resolve('vinext/dist/shims/navigation.js') to dynamically resolve the
actual location of the navigation shim. This ensures the vite configuration
works consistently across development, build, and CI environments regardless of
dependency installation method.
---
Nitpick comments:
In
`@apps/admin/src/components/features/home-universities/HomeUniversitiesPageContent.tsx`:
- Around line 114-121: The Input components in the home universities form lack
explicit accessibility labels for screen readers. Add aria-label attributes to
both Input components to improve accessibility: add aria-label="대학명" to the
Input component that has placeholder "대학명" and the setName handler, and add
aria-label="최대 지망 수" to the Input component that has placeholder "최대 지망 수" and
the setMaxChoiceCount handler with type="number". This will make the form more
accessible to screen reader users and maintain consistency with the pattern
already used in TermsPageContent.tsx.
- Around line 90-93: The handleDelete function currently uses window.confirm for
deletion confirmation, which cannot be styled and provides inconsistent UX.
Consider replacing the window.confirm call with a custom confirmation modal
component that matches your application's design system and provides better
accessibility. This would involve creating or using an existing modal component
in your project, passing the university name and ID to it, and calling
deleteMutation.mutate(id) only after the user confirms in the custom modal
instead of in the confirm callback.
🪄 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: d367e552-f503-41e1-bbd4-8df0779d3f00
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (19)
.gitignoreapps/admin/src/app/home-universities/page.tsxapps/admin/src/app/terms/page.tsxapps/admin/src/app/univ-apply-infos/page.tsxapps/admin/src/components/features/home-universities/HomeUniversitiesPageContent.tsxapps/admin/src/components/features/terms/TermsPageContent.tsxapps/admin/src/components/features/terms/termValidation.test.tsapps/admin/src/components/features/terms/termValidation.tsapps/admin/src/components/features/univ-apply-infos/UnivApplyInfosPageContent.tsxapps/admin/src/components/features/univ-apply-infos/countryCodeAliases.tsapps/admin/src/components/features/univ-apply-infos/countryCodeConstants.tsapps/admin/src/components/features/univ-apply-infos/univApplyInfoFields.test.tsapps/admin/src/components/features/univ-apply-infos/univApplyInfoFields.tsapps/admin/src/components/features/univ-apply-infos/univApplyInfoPreview.tsapps/admin/src/components/features/univ-apply-infos/univApplyInfoValidation.tsapps/admin/src/components/layout/AdminSidebar.tsxapps/admin/src/lib/api/admin.tsapps/admin/vite.config.tsapps/web/package.json
| const vinextNavShim = fileURLToPath(new URL("node_modules/vinext/dist/shims/navigation.js", import.meta.url)); | ||
|
|
||
| const config = defineConfig({ | ||
| resolve: { | ||
| alias: { | ||
| "@": fileURLToPath(new URL("./src", import.meta.url)), | ||
| "next/navigation": vinextNavShim, | ||
| "next/navigation.js": vinextNavShim, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
node --input-type=module - <<'NODE'
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
try {
console.log(require.resolve('vinext/dist/shims/navigation.js'));
} catch (err) {
console.error('vinext shim could not be resolved through package resolution');
process.exit(1);
}
NODERepository: solid-connection/solid-connect-web
Length of output: 139
🏁 Script executed:
cat -n apps/admin/vite.config.ts | head -30Repository: solid-connection/solid-connect-web
Length of output: 896
🏁 Script executed:
fd -type f -name "package.json" | xargs -I {} grep -l "vinext" {} 2>/dev/null || echo "No vinext found in package.json files"Repository: solid-connection/solid-connect-web
Length of output: 249
🏁 Script executed:
find . -name "package.json" -type f | head -10 | xargs grep -l "vinext" 2>/dev/null || echo "No vinext dependency found"Repository: solid-connection/solid-connect-web
Length of output: 104
🏁 Script executed:
cat package.json | grep -A 10 "vinext" || cat apps/admin/package.json | grep -A 10 "vinext" || echo "Checking root and app package.json"Repository: solid-connection/solid-connect-web
Length of output: 771
Vinext 경로를 동적으로 해석하도록 변경해주세요.
현재 코드에서 node_modules/vinext/dist/shims/navigation.js를 하드코딩한 경로는 워크스페이스 환경에서 올바르게 해석되지 않습니다. 아래와 같이 개선해야 합니다:
-
패키지 해석기 활용
- Node의
require.resolve()를 사용하여 경로를 동적으로 가져오기 - 이렇게 하면 pnpm 호이스팅, 중첩 설치 등 다양한 모듈 배치에 대응 가능
- Node의
-
개선 예시
require.resolve('vinext/dist/shims/navigation.js')로 현재 하드코딩 대체- 또는 vinext의 공식 API가 있다면 그 방식 따르기
-
안정성 확보
- 개발 환경, 빌드 환경, CI 환경 모두에서 일관되게 작동
- 의존성 설치 방식이 변경되어도 영향받지 않음
🤖 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/vite.config.ts` around lines 7 - 14, The hardcoded path in
vinextNavShim does not work correctly in workspace environments with varying
module layouts. Replace the hardcoded
node_modules/vinext/dist/shims/navigation.js path with
require.resolve('vinext/dist/shims/navigation.js') to dynamically resolve the
actual location of the navigation shim. This ensures the vite configuration
works consistently across development, build, and CI environments regardless of
dependency installation method.
작업 내용
failedRows의존 UI를 정리하고 관련 검증 테스트를 업데이트했습니다.검증