Skip to content

✨리크루팅 product engineering 세미나 정보 추가 - #196

Merged
honeyyyoon merged 2 commits into
mainfrom
feat/v3/add_seminar_description
Aug 9, 2026
Merged

honeyyyoon merged 2 commits into
mainfrom
feat/v3/add_seminar_description

Conversation

@SeohyunLilyChoi

Copy link
Copy Markdown
Contributor

요약

리크루팅 페이지에 Product Engineering 세미나 정보를 추가하였습니다.

변경 내역

체크리스트

  • pre-commit 통과
  • PR Assignees 추가
  • PR Labels 추가

기타 질문 및 공유 사항 (Optional)

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

관리자 인증과 리크루팅 제출 조회 API를 추가했습니다. 관리자 메뉴와 리크루팅 결과 목록·상세 라우트를 구현했습니다. PRODUCT_ENGINEERING 세미나 유형과 모집 정보를 추가했습니다.

Changes

관리자 리크루팅 결과 조회

Layer / File(s) Summary
리크루팅 계약과 제출 API
src/apis/recruiting/recruiting.types.ts, src/apis/recruiting/recruiting.api.ts
RecruitingSummarygeneration을 추가했습니다. RecruitingSubmission 타입과 관리자용 모집·제출 조회 API를 추가했습니다.
관리자 접근 검증과 데이터 로더
src/apis/utility.ts, src/pages/Loader/RecruitingResultLoader.ts
토큰 갱신 실패 시 현재 URL을 로그인 redirect 파라미터로 전달합니다. 401·403 판별과 관리자 접근 검증을 추가했습니다.
관리자 라우트와 메뉴 화면
src/shared/routes/constants.ts, src/main.tsx, src/pages/Admin.tsx
관리자 및 결과 목록·상세 라우트를 등록했습니다. 관리자 메뉴와 비활성화된 사전등록 메일 메뉴를 표시합니다.
리크루팅 결과 목록과 상세 화면
src/pages/RecruitingResult.tsx
유형 필터, 모집 상태별 카드, 지원자 테이블, 점수, 에세이 및 코딩 답안을 표시합니다. 로딩·빈 결과·권한·오류 상태를 처리합니다.

PRODUCT_ENGINEERING 모집

Layer / File(s) Summary
PRODUCT_ENGINEERING 세미나 정보와 표시
src/apis/seminar/seminar.types.ts, src/features/recruiting/Position/PositionTab.tsx, src/features/recruiting/Position/utils.tsx
PRODUCT_ENGINEERING 세미나 유형과 Rookie 모집 정보를 추가했습니다. 영문 라벨을 Product Engineering으로 표시합니다.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested reviewers: honeyyyoon

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning 요약은 작성했지만 변경 내역이 비어 있고 체크리스트 항목이 완료되지 않아 필수 정보가 부족합니다. 관리자 페이지와 채용 결과 기능을 포함한 상세 변경 내역을 작성하고, pre-commit·Assignees·Labels 상태를 확인해 체크리스트를 갱신하세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed 제목은 Product Engineering 세미나 정보 추가라는 PR의 주요 목적을 명확히 설명합니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/v3/add_seminar_description

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 `@src/apis/utility.ts`:
- Around line 39-41: Update the 401 handling in withTokenRefresh so a 401
response after token refresh also removes the invalid token and redirects to the
login path with the current URL as redirect. Ensure this response is handled
before isForbiddenError classification so it does not reach the forbidden loader
or component flow.

In `@src/pages/Loader/RecruitingResultLoader.ts`:
- Around line 29-39: Update the authorization flow around the recruiting
loader’s probeTarget and adminSubmissionQuery check to use a dedicated
admin-only or explicit permission endpoint instead of the unauthenticated
recruiting list. Deny access when no verification target exists, when the
permission request fails for any reason, or when isForbiddenError detects
insufficient privileges; only return true after an explicit successful
authorization response.

In `@src/pages/RecruitingResult.tsx`:
- Around line 141-145: Update the BodyRow expansion interaction to use a
keyboard-focusable button inside ExpandCell instead of relying on the
non-focusable tr click handler. Move the toggle action to that button, set
aria-expanded, and connect aria-controls to the associated detail row or content
identifier; remove or separate the row-wide onClick to avoid duplicate toggles.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 73977ee0-b7df-4653-bc56-61c7062e5af8

📥 Commits

Reviewing files that changed from the base of the PR and between a2add3d and 7ef144a.

📒 Files selected for processing (11)
  • src/apis/recruiting/recruiting.api.ts
  • src/apis/recruiting/recruiting.types.ts
  • src/apis/seminar/seminar.types.ts
  • src/apis/utility.ts
  • src/features/recruiting/Position/PositionTab.tsx
  • src/features/recruiting/Position/utils.tsx
  • src/main.tsx
  • src/pages/Admin.tsx
  • src/pages/Loader/RecruitingResultLoader.ts
  • src/pages/RecruitingResult.tsx
  • src/shared/routes/constants.ts

Comment thread src/apis/utility.ts
Comment on lines +39 to 41
const from = `${window.location.pathname}${window.location.search}${window.location.hash}`;
window.location.href = `/login?redirect=${encodeURIComponent(from)}`;
return Promise.reject(res);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files:"
git ls-files | ag '(^|/)utility\.ts$|package\.json$' || true

echo
echo "src/apis/utility.ts outline:"
ast-grep outline src/apis/utility.ts 2>/dev/null || true

echo
echo "src/apis/utility.ts lines 1-120:"
if [ -f src/apis/utility.ts ]; then
  cat -n src/apis/utility.ts | sed -n '1,140p'
fi

echo
echo "Search isForbiddenError and withTokenRefresh usages:"
rg -n "isForbiddenError|withTokenRefresh|tokenRefresh|refreshToken|accessToken|login\\?redirect|window\\.location\\.href" src package.json || true

Repository: wafflestudio/wacruit-web

Length of output: 7779


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Search response handling candidates:"
rg -n "isForbiddenError|withTokenRefresh|if \\(" src/apis/utility.ts src -g '*.ts' 2>/dev/null | sed -n '1,200p' || true

python3 - <<'PY'
import pathlib, re
for p in pathlib.Path('src').rglob('*.ts'):
    if str(p) == 'src/apis/utility.ts':
        print('FOUND src/apis/utility.ts')
        txt = p.read_text()
        for i,line in enumerate(txt.splitlines(),1):
            if 'function' in line or 'const' in line or 'isForbiddenError' in line or 'withTokenRefresh' in line or '401' in line:
                print(f"{i}: {line}")
PY

Repository: wafflestudio/wacruit-web

Length of output: 7265


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "RecruitingResultLoader lines 1-90:"
cat -n src/pages/Loader/RecruitingResultLoader.ts | sed -n '1,110p'
echo
echo "RecruitingResult relevant lines 80-230:"
cat -n src/pages/RecruitingResult.tsx | sed -n '85,230p'
echo
echo "RecruitItem loader/error handling lines 35-55:"
cat -n src/components/recruit/RecruitItem.tsx | sed -n '35,55p'
echo
echo "DashboardLoader relevant lines 55-75:"
cat -n src/pages/Loader/DashboardLoader.ts | sed -n '55,75p'

Repository: wafflestudio/wacruit-web

Length of output: 10050


재시도 401도 로그인 경로로 처리하십시오.

withTokenRefresh가 토큰 재갱신 후 반환한 응답이 401이면 isForbiddenError로 분류되어 로더에서는 forbidden: true, 화면에서는 Forbidden 컴포넌트로 처리됩니다. 유효하지 않은 세션 사용자는 로그인 대신 권한 없음 화면을 보게 됩니다. 토큰을 삭제하고 로그인 페이지로 이동하는 조치가 필요합니다.

🤖 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 `@src/apis/utility.ts` around lines 39 - 41, Update the 401 handling in
withTokenRefresh so a 401 response after token refresh also removes the invalid
token and redirects to the login path with the current URL as redirect. Ensure
this response is handled before isForbiddenError classification so it does not
reach the forbidden loader or component flow.

Comment on lines +29 to +39
const probeTarget = recruitings[0];

if (!probeTarget) return true;

try {
await queryClient.fetchQuery(adminSubmissionQuery(probeTarget.id));
return true;
} catch (error) {
if (isForbiddenError(error)) return false;

return true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 4 '/v1/recruitings|/submission|checkAdminAccess|adminSubmissionQuery' src
fd -HI -t f . | rg -i '(openapi|swagger|api.*doc|backend)'

Repository: wafflestudio/wacruit-web

Length of output: 9971


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Loader outline =="
ast-grep outline src/pages/Loader/RecruitingResultLoader.ts --view expanded || true

echo "== RecruitingResultLoader.ts =="
cat -n src/pages/Loader/RecruitingResultLoader.ts | sed -n '1,120p'

echo "== recruiting.api.ts =="
cat -n src/apis/recruiting/recruiting.api.ts | sed -n '1,80p'

echo "== utility api request implementation search =="
rg -n -C 3 'function getRequest|const getRequest|getRequest<|function postRequest|postConfig|needAuth|true' src/apis/utility src/apis -g '*.ts'

echo "== files matching utility =="
fd -HI 'utility\.ts$' src/apis
fd -HI 'axios.*\.ts$' src/apis
fd -HI '*.api\.ts$' src/apis | sed -n '1,50p'

Repository: wafflestudio/wacruit-web

Length of output: 19456


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== src/apis/utility.ts relevant section =="
cat -n src/apis/utility.ts | sed -n '1,70p'

echo "== behavioral probe: current checkAdminAccess outcomes =="
python3 - <<'PY'
def is_forbidden_error(error):
    return isinstance(error, list) and error[0] in (401, 403)

def check_admin_access(recruitings, submission_status):
    probe_target = recruitings[0] if recruitings else None
    if not probe_target:
        return True, "empty_list"
    # line 37: isForbiddenError returns false for 500/network-like
    if is_forbidden_error([submission_status]):
        return False, "forbidden"
    return True, "success_or_other_error"

for status in [200, 401, 403, 500, "network_error"]:
    print(status, check_admin_access([1], status))
print("no recruitings", check_admin_access([], 200))
PY

Repository: wafflestudio/wacruit-web

Length of output: 2728


권한 검증을 실패한 경우에 관리자 화면을 거부하십시오.

/v1/recruitings는 인증 없는 일반 목록 조회와 같은 엔드포인트를 사용합니다. 목록이 비어 있으면 Line 31이 권한을 허용하고, 제출 조회가 500 또는 네트워크 오류를 반환하면 Line 39도 권한을 허용합니다. 관리자 전용 API 또는 명시적인 권한 확인 API로 검증하십시오. 검증 대상이 없거나 검증 요청이 실패하면 관리자 화면 접근을 거부해야 합니다.

🤖 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 `@src/pages/Loader/RecruitingResultLoader.ts` around lines 29 - 39, Update the
authorization flow around the recruiting loader’s probeTarget and
adminSubmissionQuery check to use a dedicated admin-only or explicit permission
endpoint instead of the unauthenticated recruiting list. Deny access when no
verification target exists, when the permission request fails for any reason, or
when isForbiddenError detects insufficient privileges; only return true after an
explicit successful authorization response.

Comment on lines +141 to +145
<BodyRow
$expandable={hasDetail}
onClick={() => hasDetail && setExpanded((prev) => !prev)}
aria-expanded={hasDetail ? expanded : undefined}
>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Find files =="
fd -a 'RecruitingResult\.tsx|RecruitingResult' . | sed 's#^\./##'

echo "== Relevant file outline/sections =="
if [ -f src/pages/RecruitingResult.tsx ]; then
  wc -l src/pages/RecruitingResult.tsx
  sed -n '1,220p' src/pages/RecruitingResult.tsx | cat -n
fi

echo "== Search BodyRow ExpandCell usages =="
rg -n "BodyRow|ExpandCell|aria-expanded|aria-controls|expanded|setExpanded|hasDetail" src/pages/RecruitingResult.tsx src || true

Repository: wafflestudio/wacruit-web

Length of output: 9811


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Find files =="
fd -a 'RecruitingResult\.tsx|RecruitingResult' . | sed 's#^\./##'

echo "== Relevant file outline/sections =="
if [ -f src/pages/RecjuitingResult.tsx ]; then
  wc -l src/pages/RecruitingResult.tsx
  sed -n '1,220p' src/pages/RecruitingResult.tsx | cat -n
fi

echo "== Search BodyRow ExpandCell usages =="
rg -n "BodyRow|ExpandCell|aria-expanded|aria-controls|expanded|setExpanded|hasDetail" src/pages/RecruitingResult.tsx src || true

Repository: wafflestudio/wacruit-web

Length of output: 2009


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== BodyRow and ExpandCell definitions =="
sed -n '690,735p' src/pages/RecruitingResult.tsx | cat -n

echo "== SubmissionList table structure =="
sed -n '220,380p' src/pages/RecruitingResult.tsx | cat -n

echo "== Programmatic semantic check for current source =="
python3 - <<'PY'
from pathlib import Path
import re
p = Path("src/pages/RecruitingResult.tsx")
s = p.read_text()
checks = {
    "SubmissionRow uses BodyRow for the interactive row": bool(re.search(r"<BodyRow[\s\S]*?onClick=\{\([^)]*\) => hasDetail && setExpanded", s)),
    "BodyRow is styled.tr": bool(re.search(r"const BodyRow\s*=\s*styled\.tr<", s)),
    "aria-expanded is set on BodyRow/tr": bool(re.search(r"aria-expanded=\{hasDetail \? expanded : undefined\}", s)),
    "Expanded rows are nested trs with DetailSection/CodeBlock": "expanded &&" in s and "<tr>" in s[s.find("& expanded &&"):s.find("& expanded &&")+600] and "<DetailSection>" in s[s.find("& expanded &&"):s.find("& expanded &&")+900],
    "No explicit ExpandCell button for hasDetail": not bool(re.search(r"<ExpandCell>[\s\S]*?hasDetail && .*<button|rbutton", s[s.find("<ExpandCell>"):s.find("</ExpandCell>")+s.find("</ExpandCell>")]),
}
for name, value in checks.items():
    print(f"{name}: {value}")
PY

Repository: wafflestudio/wacruit-web

Length of output: 7890


상세 보기 버튼으로 키보드 조작을 제공하십시오.

현재 BodyRowstyled.tr이며 onClickaria-expanded만 설정되어 있습니다. tr은 포커스 가능한 조작성 요소가 아니므로 키보드 사용자는 상세 답변과 코드를 열거나 닫을 수 없습니다.

ExpandCell 안에 키보드 조작이 가능한 button을 두십시오. 버튼에 aria-expanded와 상세 행/콘텐츠에 대한 aria-controls를 연결하고, 행 전체 클릭 동작은 제거하거나 버튼 클릭과 분리하십시오.

🤖 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 `@src/pages/RecruitingResult.tsx` around lines 141 - 145, Update the BodyRow
expansion interaction to use a keyboard-focusable button inside ExpandCell
instead of relying on the non-focusable tr click handler. Move the toggle action
to that button, set aria-expanded, and connect aria-controls to the associated
detail row or content identifier; remove or separate the row-wide onClick to
avoid duplicate toggles.

@honeyyyoon
honeyyyoon merged commit d84e742 into main Aug 9, 2026
2 checks passed
@honeyyyoon
honeyyyoon deleted the feat/v3/add_seminar_description branch August 9, 2026 13:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants