✨ 지원자 현황 접근 상태별 퍼널 개선 - #612
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Walkthrough
Estimated code review effort: 4 (Complex) | ~45 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 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: aa057f73cc
ℹ️ 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".
| const SCORE_PENDING_MESSAGE = "성적 승인이 아직 안되었어요! 승인 완료 후에 지원자 수 확인이 가능합니다"; | ||
|
|
||
| const ScorePendingApplicationStatusPage = () => { | ||
| const { data: universities = [], isLoading } = useUniversitySearch("", undefined, { useDefaultTermId: true }); |
There was a problem hiding this comment.
Scope restricted previews to the user's home university
When a score-pending user belongs to a home university, this request omits homeUniversityId, so the public search returns the term-wide catalog rather than that university's available exchange destinations. The application flow explicitly supplies homeUniversityId for this reason, while both restricted authenticated pages currently display and count out-of-scope universities; pass the authenticated user's home-university ID through the search options.
Useful? React with 👍 / 👎.
| const SignedInApplicationStatusPage = () => { | ||
| const { data: universities = [], isLoading } = useUniversitySearch("", undefined, { useDefaultTermId: true }); | ||
|
|
||
| return <RestrictedApplicationStatusView universities={universities} isLoading={isLoading} />; |
There was a problem hiding this comment.
Provide the missing setup action from the signed-in state
For authenticated users with no verified home university or with either score list empty, the new resolver selects this page, but it renders only a noninteractive masked preview with no link or handler to continue setup. Previously the failed applications request sent these users into /university/application/apply, whose existing flow routes school-unverified users to verification and users without scores to score registration; this change therefore leaves those users unable to advance from the status funnel unless they discover another route manually.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (7)
apps/web/src/app/university/application/_pages/ApprovedApplicationStatusPage.tsx (2)
308-310: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win병합 키(id 우선)와 렌더 키(koreanName)가 불일치합니다.
uniqueScoreSheets는getScoreSheetKey(Line 353-355)로id를 우선 사용하므로id가 다르고koreanName이 같은 항목이 함께 남을 수 있고, 그때 React key가 중복돼 아코디언 열림 상태가 엉킬 수 있습니다. 동일한 키 함수를 렌더에서도 재사용하는 편이 안전합니다.♻️ 제안 리팩터
- {scoreSheets.map((scoreSheet, index) => ( - <MobileScoreSheet key={scoreSheet.koreanName} scoreSheet={scoreSheet} defaultOpen={index === 0} /> - ))} + {scoreSheets.map((scoreSheet, index) => ( + <MobileScoreSheet key={getScoreSheetKey(scoreSheet)} scoreSheet={scoreSheet} defaultOpen={index === 0} /> + ))}🤖 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/web/src/app/university/application/_pages/ApprovedApplicationStatusPage.tsx` around lines 308 - 310, Update the scoreSheets.map render in ApprovedApplicationStatusPage to use the same getScoreSheetKey function used by uniqueScoreSheets instead of scoreSheet.koreanName for the React key, preserving id-first identity and preventing duplicate keys.
53-56: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value
allScoreSheets의 정렬은 불필요한 중복 작업입니다.
allScoreSheets는 카운트·참여자 수 집계와 필터 입력으로만 쓰이고, 최종 정렬은 Line 75에서 다시 수행됩니다. 여기서sortMode를 빼면 정렬 토글마다 병합·중복제거까지 재실행되는 것도 함께 사라집니다.♻️ 제안 리팩터
- const allScoreSheets = useMemo( - () => sortScoreSheets(uniqueScoreSheets(scoreChoices.flat()), sortMode), - [scoreChoices, sortMode], - ); + const allScoreSheets = useMemo(() => uniqueScoreSheets(scoreChoices.flat()), [scoreChoices]);🤖 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/web/src/app/university/application/_pages/ApprovedApplicationStatusPage.tsx` around lines 53 - 56, Update the allScoreSheets useMemo to only merge and deduplicate scoreChoices without calling sortScoreSheets, and remove sortMode from its dependency array. Keep the existing final sorting at the later rendering path unchanged, while preserving allScoreSheets as the input for counts, participant totals, and filtering.apps/web/src/app/university/application/_pages/GuestApplicationStatusPage.tsx (1)
9-10: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value오버레이 위치를 고정 픽셀에 의존하고 있습니다.
top-[134px],min-h-[550px],-mt-14는 배너·탭 높이나 폰트 크기가 바뀌면 바로 어긋납니다.inset-0으로 부모(main)를 덮고justify-center로 중앙 정렬하면 레이아웃 변경에 훨씬 둔감해집니다.♻️ 제안 리팩터
- <div className="absolute inset-x-0 top-[134px] z-10 flex min-h-[550px] items-center bg-white/80 px-5 backdrop-blur-[3px]"> - <div className="-mt-14 flex w-full flex-col items-center gap-4 text-center"> + <div className="absolute inset-0 z-10 flex items-center justify-center bg-white/80 px-5 backdrop-blur-[3px]"> + <div className="flex w-full flex-col items-center gap-4 text-center">🤖 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/web/src/app/university/application/_pages/GuestApplicationStatusPage.tsx` around lines 9 - 10, GuestApplicationStatusPage의 오버레이 컨테이너에서 고정 위치·높이 값인 top-[134px]와 min-h-[550px]를 제거하고 부모 main 전체를 덮도록 inset-0을 적용하세요. 내부 콘텐츠의 -mt-14도 제거하고 justify-center을 사용해 오버레이 내용을 수직 중앙 정렬하며, 기존 가로 정렬과 간격은 유지하세요.apps/web/src/app/university/application/_pages/SignedInApplicationStatusPage.tsx (1)
6-10: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win공개 대학 프리뷰 데이터 로딩이 두 페이지에 복제되어 있습니다. 동일한
useUniversitySearch("", undefined, { useDefaultTermId: true })호출이 양쪽에 있어, 한쪽 인자만 바뀌면 두 퍼널의 프리뷰가 조용히 달라집니다. 공통 훅(예:usePreviewUniversities)으로 한 번만 정의하는 편이 좋습니다.
apps/web/src/app/university/application/_pages/SignedInApplicationStatusPage.tsx#L6-L10: 직접 호출을 공통 훅 호출로 교체합니다.apps/web/src/app/university/application/_pages/ScorePendingApplicationStatusPage.tsx#L10-L24: 동일 훅을 사용하고, 이 파일에는 토스트 관련 로직만 남깁니다.🤖 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/web/src/app/university/application/_pages/SignedInApplicationStatusPage.tsx` around lines 6 - 10, 중복된 대학 프리뷰 조회를 공통 훅으로 통합하세요. apps/web/src/app/university/application/_pages/SignedInApplicationStatusPage.tsx#L6-L10에서는 useUniversitySearch 직접 호출을 공통 훅(예: usePreviewUniversities) 호출로 교체하고, apps/web/src/app/university/application/_pages/ScorePendingApplicationStatusPage.tsx#L10-L24에서도 동일 훅을 사용해 두 페이지가 같은 조회 설정을 공유하도록 하세요. ScorePendingApplicationStatusPage에는 토스트 관련 로직만 남기세요.apps/web/src/app/university/application/_components/RestrictedApplicationStatusView.tsx (1)
62-69: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value활성 탭 표기와 실제 목록 내용이 어긋납니다.
렌더되는 목록은 공개 대학 전체인데 활성 표시는 "지원자 있는 대학"입니다. 또한 승인 화면(
ApprovedApplicationStatusPage.tsxLine 224, 227)은 "N개" 접미사를 쓰는데 여기는 "N"이라 퍼널 간 문구가 달라집니다. 활성 탭을 "모든 대학"으로 옮기고 카운트 표기를 통일하면 상태 전환 시 이질감이 줄어듭니다.🤖 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/web/src/app/university/application/_components/RestrictedApplicationStatusView.tsx` around lines 62 - 69, Update RestrictedScopeTabs so the “모든 대학” tab is active, matching the displayed public-university list, and remove active from “지원자 있는 대학”. Standardize both university count labels to the “N개” format used by ApprovedApplicationStatusPage, including the total count and masked count while preserving the existing fallback behavior.apps/web/src/app/university/application/ScorePageContent.tsx (1)
34-43: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value② switch에 안전그물 하나 쳐두면 마음이 편해질 것 같아요.
현재는 리터럴 유니온 4종을 다 커버해서 타입 상 문제는 없지만, 나중에
ApplicationStatusAccess에 상태가 추가되면 조용히undefined를 반환할 수 있어요.default분기에never단언을 넣어두면 향후 실수를 컴파일 타임에 잡을 수 있습니다.🛟 제안: exhaustiveness guard
switch (access) { case "guest": return <GuestApplicationStatusPage />; case "signedIn": return <SignedInApplicationStatusPage />; case "scorePending": return <ScorePendingApplicationStatusPage />; case "approved": return <ApprovedApplicationStatusPage />; + default: { + const _exhaustiveCheck: never = access; + return _exhaustiveCheck; + } }🤖 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/web/src/app/university/application/ScorePageContent.tsx` around lines 34 - 43, Update the switch on ApplicationStatusAccess in ScorePageContent to add a default exhaustiveness guard that asserts the unhandled access value is never, ensuring newly added statuses fail at compile time instead of returning undefined.apps/web/src/app/university/application/_pages/applicationStatusAccess.ts (1)
12-34: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win② 이 함수, 테스트로 안전벨트 매어주면 든든하겠어요.
네 가지 접근 상태를 가르는 핵심 분기 로직인데 순수 함수라 테스트하기도 쉬워요.
homeUniversityId=null, 배열 중 하나만 비어있는 경우,APPROVED/PENDING/REJECTED혼합 케이스 등을 유닛 테스트로 고정해두면 이후 회귀를 빠르게 잡을 수 있습니다.🤖 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/web/src/app/university/application/_pages/applicationStatusAccess.ts` around lines 12 - 34, 대상 함수 resolveApplicationStatusAccess의 네 가지 접근 상태 분기를 고정하는 단위 테스트를 추가하세요. 비인증 사용자, homeUniversityId가 null인 경우, GPA 또는 언어 시험 배열 하나만 비어 있는 경우, APPROVED가 없는 PENDING/REJECTED 혼합 및 두 점수가 모두 승인된 경우를 각각 검증하고, 기존 반환값인 guest, signedIn, scorePending, approved를 보장하세요.
🤖 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/web/src/app/university/application/_components/RestrictedApplicationStatusView.tsx`:
- Around line 25-26: Update RestrictedApplicationStatusView so
shouldShowSkeleton depends only on isLoading, preventing an empty loaded result
from leaving the skeleton visible. Add a separate showPlaceholderRows flag for
the GuestApplicationStatusPage dummy-preview path, and render an empty-list
message for the logged-in/pending-approval path while keeping aria-busy aligned
with the loading state.
In
`@apps/web/src/app/university/application/_pages/ApprovedApplicationStatusPage.tsx`:
- Around line 50-51: Update the scoreChoices handling in
ApprovedApplicationStatusPage so loading from useGetApplicationsList does not
substitute emptyChoices before the response arrives. Keep participant counts,
tab counts, and empty-state messaging hidden or in a loading state until the
application list request completes, while preserving emptyChoices for a
completed response with no choices.
In `@apps/web/src/app/university/application/ScorePageContent.tsx`:
- Around line 18-32: Update the useGetMyGpaScore and useGetMyLanguageTestScore
queries to also consume their isError states, and handle either query failure
before calling resolveApplicationStatusAccess. Do not convert failed or
unavailable score data into empty arrays for normal access resolution; render
the existing error state or otherwise propagate a distinct failure outcome so
failed requests cannot be treated as "signedIn" or "성적 미제출".
---
Nitpick comments:
In
`@apps/web/src/app/university/application/_components/RestrictedApplicationStatusView.tsx`:
- Around line 62-69: Update RestrictedScopeTabs so the “모든 대학” tab is active,
matching the displayed public-university list, and remove active from “지원자 있는
대학”. Standardize both university count labels to the “N개” format used by
ApprovedApplicationStatusPage, including the total count and masked count while
preserving the existing fallback behavior.
In `@apps/web/src/app/university/application/_pages/applicationStatusAccess.ts`:
- Around line 12-34: 대상 함수 resolveApplicationStatusAccess의 네 가지 접근 상태 분기를 고정하는
단위 테스트를 추가하세요. 비인증 사용자, homeUniversityId가 null인 경우, GPA 또는 언어 시험 배열 하나만 비어 있는
경우, APPROVED가 없는 PENDING/REJECTED 혼합 및 두 점수가 모두 승인된 경우를 각각 검증하고, 기존 반환값인 guest,
signedIn, scorePending, approved를 보장하세요.
In
`@apps/web/src/app/university/application/_pages/ApprovedApplicationStatusPage.tsx`:
- Around line 308-310: Update the scoreSheets.map render in
ApprovedApplicationStatusPage to use the same getScoreSheetKey function used by
uniqueScoreSheets instead of scoreSheet.koreanName for the React key, preserving
id-first identity and preventing duplicate keys.
- Around line 53-56: Update the allScoreSheets useMemo to only merge and
deduplicate scoreChoices without calling sortScoreSheets, and remove sortMode
from its dependency array. Keep the existing final sorting at the later
rendering path unchanged, while preserving allScoreSheets as the input for
counts, participant totals, and filtering.
In
`@apps/web/src/app/university/application/_pages/GuestApplicationStatusPage.tsx`:
- Around line 9-10: GuestApplicationStatusPage의 오버레이 컨테이너에서 고정 위치·높이 값인
top-[134px]와 min-h-[550px]를 제거하고 부모 main 전체를 덮도록 inset-0을 적용하세요. 내부 콘텐츠의 -mt-14도
제거하고 justify-center을 사용해 오버레이 내용을 수직 중앙 정렬하며, 기존 가로 정렬과 간격은 유지하세요.
In
`@apps/web/src/app/university/application/_pages/SignedInApplicationStatusPage.tsx`:
- Around line 6-10: 중복된 대학 프리뷰 조회를 공통 훅으로 통합하세요.
apps/web/src/app/university/application/_pages/SignedInApplicationStatusPage.tsx#L6-L10에서는
useUniversitySearch 직접 호출을 공통 훅(예: usePreviewUniversities) 호출로 교체하고,
apps/web/src/app/university/application/_pages/ScorePendingApplicationStatusPage.tsx#L10-L24에서도
동일 훅을 사용해 두 페이지가 같은 조회 설정을 공유하도록 하세요. ScorePendingApplicationStatusPage에는 토스트 관련
로직만 남기세요.
In `@apps/web/src/app/university/application/ScorePageContent.tsx`:
- Around line 34-43: Update the switch on ApplicationStatusAccess in
ScorePageContent to add a default exhaustiveness guard that asserts the
unhandled access value is never, ensuring newly added statuses fail at compile
time instead of returning undefined.
🪄 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 Plus
Run ID: e6edce29-0eca-4eae-a48e-adc6d7107fcc
⛔ Files ignored due to path filters (2)
docs/screenshots/application-status/as-is-guest.jpgis excluded by!**/*.jpgdocs/screenshots/application-status/to-be-guest.jpgis excluded by!**/*.jpg
📒 Files selected for processing (8)
apps/web/src/apis/Scores/getLanguageTestList.tsapps/web/src/app/university/application/ScorePageContent.tsxapps/web/src/app/university/application/_components/RestrictedApplicationStatusView.tsxapps/web/src/app/university/application/_pages/ApprovedApplicationStatusPage.tsxapps/web/src/app/university/application/_pages/GuestApplicationStatusPage.tsxapps/web/src/app/university/application/_pages/ScorePendingApplicationStatusPage.tsxapps/web/src/app/university/application/_pages/SignedInApplicationStatusPage.tsxapps/web/src/app/university/application/_pages/applicationStatusAccess.ts
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/web/src/app/university/application/_components/RestrictedApplicationStatusView.tsx`:
- Around line 149-153: Update RestrictedUniversityEmptyState so its primary
message is neutral and does not claim that no applicants exist; use fixed
masking wording appropriate for the restricted state while preserving the
existing layout and secondary guidance text.
In `@apps/web/src/app/university/application/ScorePageContent.tsx`:
- Around line 47-50: Update the access-transition condition in ScorePageContent
so that, when shouldFetchScoreStatus is true, either score query’s isFetching
state keeps the spinner visible, even if cached data makes isLoading false.
Preserve the existing authentication and school-email verification checks.
🪄 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 Plus
Run ID: 7aa64992-647c-45c4-98c4-12c0f049951c
📒 Files selected for processing (8)
apps/web/src/apis/Scores/getGpaList.tsapps/web/src/apis/Scores/getLanguageTestList.tsapps/web/src/apis/applications/api.tsapps/web/src/app/my/school-email/_lib/returnTo.tsapps/web/src/app/university/application/ScorePageContent.tsxapps/web/src/app/university/application/_components/RestrictedApplicationStatusView.tsxapps/web/src/app/university/application/_pages/ApprovedApplicationStatusPage.tsxapps/web/src/app/university/application/_pages/GuestApplicationStatusPage.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/web/src/app/university/application/_pages/GuestApplicationStatusPage.tsx
- apps/web/src/app/university/application/_pages/ApprovedApplicationStatusPage.tsx
| const RestrictedUniversityEmptyState = () => ( | ||
| <div className="flex min-h-48 flex-col items-center justify-center rounded-lg bg-k-50 px-6 py-8 text-center"> | ||
| <p className="text-k-900 typo-sb-7">아직 지원자가 있는 대학이 없어요.</p> | ||
| <p className="mt-2 text-k-500 typo-medium-3">지원 현황이 등록되면 여기에 표시됩니다.</p> | ||
| </div> |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
1. 제한 상태에서 실제 지원 여부를 출력하지 마세요.
"아직 지원자가 있는 대학이 없어요."는 지원자가 없다는 실제 상태를 노출합니다. 제한 상태에서는 지원자 정보에 고정 마스킹 값만 사용하세요. 중립적인 빈 목록 문구로 바꾸세요.
수정 예시
- <p className="text-k-900 typo-sb-7">아직 지원자가 있는 대학이 없어요.</p>
- <p className="mt-2 text-k-500 typo-medium-3">지원 현황이 등록되면 여기에 표시됩니다.</p>
+ <p className="text-k-900 typo-sb-7">표시할 대학 정보가 없어요.</p>
+ <p className="mt-2 text-k-500 typo-medium-3">잠시 후 다시 확인해 주세요.</p>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const RestrictedUniversityEmptyState = () => ( | |
| <div className="flex min-h-48 flex-col items-center justify-center rounded-lg bg-k-50 px-6 py-8 text-center"> | |
| <p className="text-k-900 typo-sb-7">아직 지원자가 있는 대학이 없어요.</p> | |
| <p className="mt-2 text-k-500 typo-medium-3">지원 현황이 등록되면 여기에 표시됩니다.</p> | |
| </div> | |
| const RestrictedUniversityEmptyState = () => ( | |
| <div className="flex min-h-48 flex-col items-center justify-center rounded-lg bg-k-50 px-6 py-8 text-center"> | |
| <p className="text-k-900 typo-sb-7">표시할 대학 정보가 없어요.</p> | |
| <p className="mt-2 text-k-500 typo-medium-3">잠시 후 다시 확인해 주세요.</p> | |
| </div> |
🤖 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/web/src/app/university/application/_components/RestrictedApplicationStatusView.tsx`
around lines 149 - 153, Update RestrictedUniversityEmptyState so its primary
message is neutral and does not claim that no applicants exist; use fixed
masking wording appropriate for the restricted state while preserving the
existing layout and secondary guidance text.
| if ( | ||
| !isAuthInitialized || | ||
| needsSchoolEmailVerification || | ||
| (shouldFetchScoreStatus && (isGpaLoading || isLanguageTestLoading)) |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Locate ScorePageContent.tsx and related score hooks/mutation usage"
fd -a 'ScorePageContent\.tsx$|score|Score' . | sed -n '1,120p'
echo
echo "Show ScorePageContent outline and relevant lines"
file="$(fd 'ScorePageContent\.tsx$' . | head -n 1)"
if [ -n "$file" ]; then
wc -l "$file"
ast-grep outline "$file" --view expanded || true
sed -n '1,120p' "$file" | cat -n
fi
echo
echo "Search query hook definitions and usages"
rg -n "useGetMyGpaScore|useGetMyLanguageTestScore|refetchOnMount\\s*:\\s*\"always\"|score_status|needsSchoolEmailVerification|access" -S .Repository: solid-connection/solid-connect-web
Length of output: 32671
🏁 Script executed:
#!/bin/bash
set -euo pipefail
scores="apps/web/src/apis/Scores"
status_pages="apps/web/src/app/university/application/_pages"
echo "Score hook files"
for f in "$scores/getGpaList.ts" "$scores/getLanguageTestList.ts" "$scores/index.ts"; do
[ -f "$f" ] || continue
echo "--- $f"
sed -n '1,120p' "$f" | cat -n
done
echo
echo "Application access decision files"
fd '_pages' "$status_pages" -t f | sort
access_file="$(fd 'applicationStatusAccess\.(ts|tsx)$' "$status_pages" | head -n 1)"
if [ -n "$access_file" ]; then
echo "--- $access_file"
wc -l "$access_file"
ast-grep outline "$access_file" --view expanded || true
sed -n '1,240p' "$access_file" | cat -n
fi
echo
echo "Package dependency versions"
rg -n '"(`@tanstack/react-query`|`@tanstack/query-core`|`@tanstack/react-query`)' pnpm-lock.yaml package.json */package.json -S | sed -n '1,80p' || trueRepository: solid-connection/solid-connect-web
Length of output: 4650
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Package dependency versions"
rg -n '"`@tanstack/`(query-core|react-query)"|`@tanstack/`(query-core|react-query)' package.json pnpm-lock.yaml -S | sed -n '1,120p' || true
echo
echo "Behavioral probe: React Query v5 loading/fetching semantics on refetch"
node - <<'JS'
(async () => {
const { queryClientFactory } = require('`@tanstack/query-core`')
const client = queryClientFactory()
const key = ['test']
client.setQueryData(key, { value: 'stale-approval' })
client.resumePausedMutations()
const promise = client.fetchQuery({
queryKey: key,
queryFn: async () => {
await new Promise((resolve) => setTimeout(resolve, 80))
return { value: 'new-rejection' }
}
})
const state = client.getQueryState(key)
console.log(JSON.stringify({
initialStatus: state?.status,
isLoading: state?.status === 'pending' && state?.fetchStatus === 'fetching',
isFetching: state?.fetchStatus === 'fetching'
}))
return promise.catch(() => {})
})()
JS
echo
echo "Check if expected package exists in lockfile"
node - <<'JS'
const fs = require('fs')
const txt = fs.readFileSync('pnpm-lock.yaml', 'utf8', { encoding: 'utf8' })
for (const pkg of ['`@tanstack/react-query`', '`@tanstack/query-core`']) {
const lines = txt.split(/\r?\n/).filter(line => line.startsWith(' ') || line.startsWith(' ')).filter(line => line.includes(pkg))
console.log('---', pkg, 'count', lines.length)
console.log(lines.slice(0, 80).join('\n'))
}
JSRepository: solid-connection/solid-connect-web
Length of output: 1956
🌐 Web query:
TanStack Query v5 refetchOnMount always isFetching isLoading status cached data
💡 Result:
In TanStack Query v5, refetchOnMount: 'always' forces a query to refetch every time its component mounts, regardless of whether the data is considered stale [1][2]. The behavior of the status and loading indicators depends on whether the query has existing cached data: 1. isLoading and isPending: - isLoading is a derived boolean (isPending && isFetching) [1][2]. It represents a "hard loading" state, which only occurs during the initial fetch when no data is available in the cache [3][4]. - If cached data already exists, the query will be in the success status immediately upon mount, even if a refetch is triggered by refetchOnMount: 'always' [5][6]. Consequently, isLoading will be false [4]. 2. isFetching: - This flag is true whenever the query function is executing, which includes both the initial "hard" fetch and any background refetches (such as those triggered by refetchOnMount: 'always' when data is already cached) [1][4]. - Use isFetching if you want to display a non-blocking indicator (like a small spinner or "updating" text) while the background refetch happens [7][4]. 3. Status: - With cached data: The status will be 'success' [8][5]. - Without cached data: The status will be 'pending' [5][4]. In summary, if you use refetchOnMount: 'always' and data exists, your component will receive the cached data immediately (status: 'success') while simultaneously performing a background refetch (isFetching: true). If you need to distinguish between the first load and subsequent background refetches, rely on isPending for the former and isFetching for the latter [4].
Citations:
- 1: https://tanstack.com/query/v5/docs/framework/react/reference/useQuery
- 2: https://tanstack.com/query/latest/docs/framework/react/reference/useQuery
- 3: Differentiate refetching from actual loading TanStack/query#1574
- 4: https://www.carlosdubon.dev/blog/understanding-loading-states-in-tanstack-query-v5
- 5: https://tanstack.com/query/v5/docs/framework/react/guides/queries
- 6: https://tanstack.com/query/v5/docs/framework/react/guides/caching
- 7: https://tanstack.com/query/v5/docs/framework/react/guides/background-fetching-indicators
- 8: https://tanstack.com/query/v5/docs/framework/react/guides/disabling-queries
1. 성적 재검증 중에는 접근 전환을 지연하세요.
refetchOnMount: "always"는 캐시된 점수 데이터가 있으면 isLoading이 false가 됩니다. 두 점수 조회 중 하나라도 isFetching이면 approved 페이지가 렌더링되지 않도록 스피너를 유지하세요.
수정 예시
const {
data: gpaScoreData,
isLoading: isGpaLoading,
+ isFetching: isGpaFetching,
isError: isGpaError,
refetch: refetchGpa,
} = useGetMyGpaScore({ enabled: shouldFetchScoreStatus, refetchOnMount: "always" });
const {
data: languageTestScores = [],
isLoading: isLanguageTestLoading,
+ isFetching: isLanguageTestFetching,
isError: isLanguageTestError,
refetch: refetchLanguageTest,
} = useGetMyLanguageTestScore({
@@
- (shouldFetchScoreStatus && (isGpaLoading || isLanguageTestLoading))
+ (shouldFetchScoreStatus && (isGpaFetching || isLanguageTestFetching))📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if ( | |
| !isAuthInitialized || | |
| needsSchoolEmailVerification || | |
| (shouldFetchScoreStatus && (isGpaLoading || isLanguageTestLoading)) | |
| if ( | |
| !isAuthInitialized || | |
| needsSchoolEmailVerification || | |
| (shouldFetchScoreStatus && (isGpaFetching || isLanguageTestFetching)) |
🤖 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/web/src/app/university/application/ScorePageContent.tsx` around lines 47
- 50, Update the access-transition condition in ScorePageContent so that, when
shouldFetchScoreStatus is true, either score query’s isFetching state keeps the
spinner visible, even if cached data makes isLoading false. Preserve the
existing authentication and school-email verification checks.
관련 이슈
작업 내용
지원자 현황 접근 상태 분리
지원자 현황을 상위 컨테이너에서 권한 상태로 판정하고 아래 4개 페이지 컴포넌트로 분리했습니다.
제한 상태에서는 전체 지원자 API를 호출하지 않으며, 실제 지원자 수를 DOM에 넣고 CSS로 가리는 방식도 사용하지 않습니다. 화면에는 고정 마스킹 토큰만 렌더링됩니다.
Preview 응답 계약과 조회 범위
GET /applications/preview 요청에는 대학 ID를 보내지 않습니다. 서버가 인증된 사용자의 homeUniversityId를 기준으로 응답을 제한하므로, 경희대학교 사용자에게는 경희대학교에 연결된 대학만 반환됩니다.
클라이언트는 다음 공개 필드만 사용합니다.
지원자 배열, 대학별 지원자 수, 닉네임, 성적은 응답에 없습니다. 모교가 아직 등록되지 않은 사용자는 서버에서 빈 프리뷰를 받습니다.
AS-IS / TO-BE
상태별 UI
아래 이미지는 기획 기준(Figma)으로 정리한 네 상태의 UI 캡처입니다.
특이 사항
리뷰 요구사항 (선택)
검증