Skip to content

chore: develop 브랜치를 main으로 병합 - #106

Open
meteorqz6 wants to merge 7 commits into
mainfrom
develop
Open

chore: develop 브랜치를 main으로 병합#106
meteorqz6 wants to merge 7 commits into
mainfrom
develop

Conversation

@meteorqz6

@meteorqz6 meteorqz6 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

개요

develop 브랜치에 쌓인 대시보드, 보관함, 스케줄 추천/확정, Swagger 문서 개선 사항을 main 브랜치에 반영합니다.

관련 이슈 없음


변경 사항

  • 대시보드와 보관함에서 확정 스케줄을 서버 데이터와 연결했습니다.
  • 확정 스케줄 달력과 가능 시간 관리 화면의 표시 및 입력 흐름을 개선했습니다.
  • 스케줄 히스토리 목록, 상세, CSV 내보내기 API와 관련 문서를 갱신했습니다.
  • Swagger UI와 OpenAPI 문서 서빙을 추가하고 명세를 최신 계약에 맞췄습니다.
  • 코드 리뷰 반영으로 날짜 기반 시간 표시, 스케줄 그룹화 공통 유틸, 기간 일치 검증, 월 전체 선택, API 테스트와 문서 계약을 보강했습니다.

변경 유형

  • feat — 새로운 기능
  • fix — 버그 수정
  • refactor — 리팩터링 (기능 변경 없음)
  • test — 테스트 코드
  • perf — 성능 개선
  • docs — 문서 수정
  • chore — 빌드·설정 변경

영향 범위

  • webapps/web
  • apiapps/api
  • dbpackages/database
  • sharedpackages/shared
  • repo — 루트 설정·CI/CD·문서

테스트 방법

  1. corepack pnpm format:check
  2. corepack pnpm --filter @fragment/web lint
  3. corepack pnpm --filter @fragment/web typecheck
  4. corepack pnpm --filter @fragment/web exec vitest run src/lib/schedule-display.test.ts src/features/schedules/components/mvp-schedules-page.test.ts
  5. corepack pnpm --filter @fragment/api lint
  6. corepack pnpm --filter @fragment/api typecheck
  7. corepack pnpm --filter @fragment/api test -- schedule-history.routes.test.ts

체크리스트

  • CI (테스트 + 린트) 통과 확인
  • 관련 테스트 코드 작성 또는 업데이트
  • 상태 분류 규칙(서버/전역/로컬/폼 상태) 준수
  • 에러 처리 및 Fallback UI 확인 (해당 시)
  • API 명세 업데이트 (API 변경 시)
  • 공유 타입·스키마(packages/shared) 업데이트 (타입 변경 시)

스크린샷 / 영상

첨부하지 않았습니다.


리뷰 포인트

  • 확정 스케줄 시간 표시가 workDate 기준 익일 표시를 올바르게 처리하는지 확인해 주세요.
  • 가능 시간 화면에서 선택 기간과 저장된 기간이 다를 때 기존 가능 시간을 잘못 재사용하지 않는지 확인해 주세요.
  • OpenAPI의 스케줄 히스토리 404 응답과 최소 인원 조건 요청 스키마가 서버 계약과 맞는지 확인해 주세요.

Summary by CodeRabbit

  • New Features

    • 대시보드에서 최근 확정 스케줄과 조직 정보를 확인할 수 있게 되었고, 스케줄 이력 조회와 CSV 다운로드가 추가되었습니다.
    • OpenAPI 문서와 Swagger UI를 통해 API 문서를 바로 열람할 수 있습니다.
    • 근무 가능 기간/일정 선택 UI가 달력 기반으로 개선되고, 일정과 미충원 항목이 더 보기 쉽게 그룹화되었습니다.
  • Bug Fixes

    • CSV 다운로드, 날짜 범위 표시, 시간대 표기 방식이 개선되어 더 일관된 결과를 제공합니다.

ehlung and others added 5 commits June 30, 2026 02:44
chore: develop을 main 머지 커밋과 동기화
* feat(api): serve swagger docs

* fix(api): address swagger docs review

* fix(api): document error response examples

* test(api): assert openapi response refs resolve
* docs(api): 확정 스케줄 조회 계약 정리

* feat(api): 보관함 API 구현

* feat(api): 대시보드 API 구현

* feat(web): 확정 스케줄 API 연결 함수 추가

* feat(web): 대시보드 API 연결

* feat(web): 보관함 API 연결

* feat(web): 보관함 CSV 근무표 형식 적용

* fix: 보관함·대시보드 리뷰 반영

* docs(api): 보관함·대시보드 OpenAPI 추가

* refactor(web): 스케줄 시간 표시 util 분리
* feat(web): 가능 시간 관리 UI 개선

* feat(web): 가능 시간 드래그 선택 개선

* fix(web): 가능 시간 UI 리뷰 반영
* feat(web): improve schedule calendar and time inputs

* fix: address schedule review feedback

* fix: address remaining schedule review comments
@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
final-project-web Ready Ready Preview, Comment Jul 1, 2026 4:02am

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

대시보드/스케줄 히스토리 조회, CSV 내보내기 API 및 OpenAPI/Swagger 문서화가 백엔드에 추가되고, 웹에서는 대시보드·스케줄 히스토리 페이지가 새 API/React Query와 연동됩니다. 또한 가용성 달력(드래그 다중 선택), 조직 폼 운영시간 Select, 스케줄 페이지의 그룹형 표시가 함께 변경됩니다.

Changes

대시보드/스케줄 히스토리 백엔드 API 및 문서화

Layer / File(s) Summary
환경 변수 및 빌드 설정
.env.example, apps/api/package.json, package.json
API_PUBLIC_BASE_URL 예시 값과 swagger-ui-express/yaml 의존성, corepack pnpm 기반 빌드/타입체크 스크립트가 추가·변경됨.
OpenAPI 문서 생성 및 서빙
apps/api/src/app.ts, apps/api/src/openapi.ts, apps/api/test/docs.routes.test.ts
docs/openapi.yaml을 읽어 문서를 생성하고 /api/openapi.json, /api/docs를 서빙하며 서버 URL 정규화 및 테스트를 추가함.
대시보드 조회 API
.../dashboard/dashboard.service.ts, .../dashboard.handlers.ts, .../routes/dashboard.routes.ts, .../organization.service.ts, apps/api/test/dashboard.routes.test.ts
조직 상세 및 최신 확정 스케줄을 반환하는 getDashboard 서비스, 인증/조직 검증 라우트, 관련 테스트가 추가됨.
스케줄 히스토리 조회/CSV 내보내기 API
.../schedule-history/*, .../routes/schedule-history.routes.ts, apps/api/test/helpers/fake-prisma.ts, apps/api/test/schedule-history.routes.test.ts, packages/shared/src/schemas/schedule-history.ts
목록/상세 조회 및 CSV(BOM, 수식 인젝션 방지) 내보내기 서비스·핸들러·라우트, year/month 쿼리 제약, fake-prisma 정렬 로직과 통합 테스트가 추가됨.
OpenAPI 스펙 및 문서 갱신
docs/openapi.yaml, docs/API.md, docs/SWAGGER.md
staffing-rules, schedule-history, dashboard 경로 및 오류 응답 컴포넌트(DuplicateEmail, ScheduleConflict 등)가 추가되고 API 문서가 갱신됨.

웹 대시보드/스케줄 히스토리 프론트엔드

Layer / File(s) Summary
apiClient Blob 응답 지원
apps/web/src/lib/api-client.ts
responseType: "blob" 옵션과 오버로드가 추가되어 CSV Blob 응답을 지원함.
대시보드 API/쿼리 계층
.../dashboard/api/dashboard-api.ts, .../dashboard/queries/*
getDashboard API 래퍼와 쿼리 키, useDashboardQuery 훅이 추가됨.
스케줄 표시 유틸
apps/web/src/lib/schedule-display.ts, schedule-display.test.ts
익일 표기 포맷, 시간 범위 라벨, 근무자명 프리뷰, 시간대 그룹화 유틸과 테스트가 추가됨.
확정 스케줄 캘린더 컴포넌트
.../schedules/components/confirmed-schedule-calendar.tsx
월~일 그리드와 날짜별 확정 스케줄 미리보기/다이얼로그를 제공하는 컴포넌트가 추가됨.
대시보드 페이지 통합
.../dashboard/components/mvp-dashboard-page.tsx
useDashboardQuery, CSV 내보내기 뮤테이션, 확정 스케줄 캘린더를 사용하도록 재구성됨.
스케줄 히스토리 API/쿼리 계층
.../schedule-history/api/*, .../schedule-history/queries/*
목록/상세/CSV 내보내기 API 및 쿼리 키/훅이 추가됨.
스케줄 히스토리 페이지 통합
.../schedule-history/components/mvp-schedule-history-page.tsx
더미 데이터 대신 API 조회 결과와 확정 스케줄 캘린더를 사용하도록 변경됨.

가용성 달력, 조직 폼, 스케줄 페이지 UX 개선

Layer / File(s) Summary
가용성 페이지 달력 다이얼로그 기초
.../availability/components/mvp-availability-page.tsx
월 단위 42칸 캘린더 그리드, 날짜 포맷터, 익일 표기 유틸이 추가됨.
타임테이블 드래그 다중 선택 및 근무 기간 선택 로직
동일 파일
드래그 선택 상태, updateSlotSelections/applySlotDragRange, planning period 반영/저장 흐름이 추가됨.
가용성 페이지 UI 재구성
동일 파일
근무 기간 다이얼로그, 입력 제외 날짜, 타임테이블 렌더링이 새 상태 기준으로 재구성됨.
조직 폼 운영시간 Select 입력 전환
.../organization/components/organization-form.tsx, mvp-organization-edit-page.tsx, apps/api/test/organization.routes.test.ts
30분 단위 TIME_OPTIONS 기반 Select로 운영시간 입력이 바뀌고 30분 경계 검증 테스트가 추가됨.
스케줄 페이지 미충족 조건/일정 그룹화
.../schedules/components/mvp-schedules-page.tsx, .../schedules/utils/unfilled-conditions.ts, 테스트
미충족 조건 및 일정이 날짜/시간대 그룹 카드로 재구성되고 후보 배지가 단순화됨.

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

Related issues: 명시된 관련 이슈 정보 없음.

Related PRs: 명시된 관련 PR 정보 없음.

Suggested labels: backend, frontend, api, documentation

Suggested reviewers: 프로젝트 컨벤션과 스펙 정합성 검토를 위해 백엔드/프론트엔드 양쪽에 익숙한 리뷰어를 지정하는 것을 권장합니다.


대시보드 문 열어보니 확정 스케줄 반짝,
CSV엔 BOM 붙여 엑셀도 안 깜짝.
드래그로 슥슥 긋는 가용시간 칸칸,
셀렉트 박스 30분 단위로 딱딱.
토끼도 문서 보며 고개를 끄덕끄덕 🐰

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 실제 변경의 핵심인 develop→main 병합을 정확히 가리키며, 간결한 단일 문장입니다.
Description check ✅ Passed 템플릿의 주요 섹션을 대부분 채웠고 변경 사항, 테스트 방법, 체크리스트, 리뷰 포인트까지 포함되어 있습니다.
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
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

@meteorqz6 meteorqz6 changed the title develop 브랜치를 main으로 병합 chore: develop 브랜치를 main으로 병합 Jul 1, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 15

🤖 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/api/package.json`:
- Line 13: Update the api package’s typecheck script so it does not run against
missing or stale declarations from `@fragment/shared` and `@fragment/database`. In
the package.json script that defines typecheck, add the required workspace build
step(s) before tsc --noEmit, or otherwise ensure those dependencies are resolved
from source rather than dist/index.d.ts. Use the typecheck script and the
referenced workspace packages as the main anchors when making the change.

In `@apps/api/test/schedule-history.routes.test.ts`:
- Around line 405-406: The export CSV test in schedule-history.routes.test.ts
only checks that the content-type contains text/csv, so it can miss a regression
where the charset is dropped. Update the assertion in the GET
/schedule-history/:scheduleId/export.csv test to verify the full response header
contract, including charset=utf-8, using the response.headers["content-type"]
check alongside the existing BOM validation.
- Around line 121-136: Add a regression test in schedule-history.routes.test.ts
for the Schedule History route that sends only month without year and expects a
400 VALIDATION_ERROR response. Extend the existing rejects invalid history query
coverage in the schedule history route tests so the contract enforced by the
handler/createDateRangeFilter path is exercised when month is present alone. Use
the same test setup patterns already used in the schedule history route spec
(createFakePrisma, createApp, request, authHeader) to locate and add the new
case.

In `@apps/web/src/features/availability/components/mvp-availability-page.tsx`:
- Around line 1091-1094: Replace the inline gridTemplateColumns style in
mvp-availability-page.tsx with a Tailwind-based layout approach. Update the
component that renders the timetable grid so it uses utility classes and/or a
flex/table structure instead of injecting dynamic px values via the style prop,
keeping the same TIMETABLE_TIME_COLUMN_WIDTH and
visibleTimetableDates.length-driven layout behavior. Locate the grid container
in the availability page component and refactor that section to remove the
inline style attribute entirely.
- Around line 421-424: The availability flow is treating any existing
activePlanningPeriod as if it matches the currently selected
workStartDate/workEndDate, which can enable fetch/save for a different range.
Update the logic around hasSavedPlanningPeriod, canFetchAvailability, and the
save button gating in mvp-availability-page.tsx to also verify the selected
dates are exactly the same as the stored planning period before considering it
saved. Make the same equality check consistent in the related availability query
and save paths referenced by the comment so only an unchanged planning period is
reused.

In `@apps/web/src/features/dashboard/api/dashboard-api.ts`:
- Line 1: The DashboardResponse import in dashboard-api should follow the
project’s API type path rule instead of importing from the shared package root.
Update the import in the dashboard-api module to use the API types location
under packages/shared/src/types/api/, keeping the existing DashboardResponse
symbol but changing only the source path to match the repository’s API
request/response type convention.

In `@apps/web/src/features/dashboard/components/mvp-dashboard-page.tsx`:
- Around line 49-64: `formatAssignmentTime` and
`scheduleAssignmentToCalendarItem` are formatting ISO date-times by slicing
strings, which loses timezone-aware time and breaks next-day detection. Replace
the `slice(11, 16)`/`slice(0, 10) > workDate` logic with proper `Date`-based or
formatter-based calculation so `startsAt` and `endsAt` are rendered correctly
for `ConfirmedScheduleCalendarItem`, including midnight crossings and any `+1`
day display. If there is an existing shared schedule display utility, use that
instead to keep date and date-time handling consistent with the guidelines.

In `@apps/web/src/features/organization/components/organization-form.tsx`:
- Around line 34-48: The TIME_OPTIONS select in organization-form.tsx is
incorrectly narrowing time-of-day values to 30-minute slots, so existing
server-provided HH:MM values like 09:15 cannot be represented or reselected in
the edit form. Update the organization form time inputs to support the full
HH:MM contract used by the API and shared schema, and only keep a 30-minute
restriction if you also align the shared schema, server validation, and API docs
accordingly. Check the TIME_OPTIONS constant and the related time field handling
in the organization form so existing organization times remain faithfully
editable.

In
`@apps/web/src/features/schedule-history/components/mvp-schedule-history-page.tsx`:
- Around line 133-140: `mvp-schedule-history-page` is forcing a month selection,
which prevents year-only history lookup. Update the `effectiveSelectedMonth`
logic so an empty month remains valid instead of defaulting to the first item,
and add an explicit “전체 월” (or equivalent) option in the month Select that maps
to the empty month state. Make sure `historyItems` and any related filtering
paths continue to use `selectedYearItems` when no month is chosen, preserving
the `year`-only query flow in `isHistoryInMonth` and the UI state handling.

In `@apps/web/src/features/schedules/components/mvp-schedules-page.tsx`:
- Around line 428-475: The new utility logic in groupUnfilledConditionsByDate,
getShortageCount, getUnfilledConditionLabel, and getUnfilledConditionPreview has
branching behavior and should be covered by tests. Add focused unit tests for
these helpers to verify date grouping/sorting, shortage count clamping, and
preview truncation with hidden-count formatting, using the existing
mvp-schedules-page.tsx utility symbols as the entry point. Keep the tests
minimal but include at least one or two representative cases for each branch so
regressions are caught.
- Around line 72-78: The time-grouping logic is duplicated here and in
confirmed-schedule-calendar.tsx, and the ScheduleTimeGroup shape differs between
files, which will cause maintenance drift. Move the shared
grouping/label/preview behavior into the common schedule-display utility and
have mvp-schedules-page.tsx reuse it instead of defining its own
ScheduleTimeGroup, getTimeRangeLabel, getWorkerNamesPreview, and
groupSchedulesByTime. Keep the returned group shape consistent across both
components by aligning the local types with the shared helper’s symbols.
- Around line 1035-1044: The calendar cell rendering in the schedule page is
recalculating groupSchedulesByTime for each date inside calendarDates.map,
causing repeated work on every render. Move the per-date grouping logic out of
the cell loop in mvp-schedules-page.tsx by precomputing schedules by date and
caching the grouped result with useMemo, then read the memoized groups when
deriving visibleScheduleGroups and hiddenScheduleGroupCount.

In `@apps/web/src/lib/schedule-display.ts`:
- Around line 3-9: `formatScheduleAssignmentTimeRange` is calculating the “익일”
prefix by comparing `endsAt` only against `startsAt`, which breaks cases where
both times are on the next day relative to `workDate`. Update this function to
use `assignment.workDate` as the reference date and compare both `startsAt` and
`endsAt` against it when deciding whether to add the 익일 prefix. Keep the time
formatting logic in `formatScheduleAssignmentTimeRange` but make the
day-boundary check explicit for start/end based on `workDate`.

In `@docs/openapi.yaml`:
- Around line 1522-1569: CreateMinimumStaffingRuleRequest and
UpdateMinimumStaffingRuleRequest currently allow arbitrary extra fields because
they do not close the object schema. Add additionalProperties: false to both
request schemas in openapi.yaml, keeping minProperties: 1 only on
UpdateMinimumStaffingRuleRequest for partial updates. Make sure the change is
applied to the named schemas so generated clients and validation stay aligned
with the server contract.
- Around line 755-811: The ScheduleHistory 404 contract is too generic in the
OpenAPI spec and should be fixed to match the API docs by returning
SCHEDULE_NOT_FOUND for both getScheduleHistoryDetail and
exportScheduleHistoryCsv. Update the responses for
/schedule-history/{scheduleId} and /schedule-history/{scheduleId}/export.csv to
reference or define a dedicated 404 response tied to SCHEDULE_NOT_FOUND instead
of the shared NotFound response, so the operationId-defined endpoints only
advertise the confirmed-schedule-missing case.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 12e8358c-cb73-4010-9822-c89eda127d63

📥 Commits

Reviewing files that changed from the base of the PR and between d85941a and 8edc214.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (38)
  • .env.example
  • apps/api/package.json
  • apps/api/src/app.ts
  • apps/api/src/modules/dashboard/dashboard.handlers.ts
  • apps/api/src/modules/dashboard/dashboard.service.ts
  • apps/api/src/modules/organization/organization.service.ts
  • apps/api/src/modules/schedule-history/schedule-history.handlers.ts
  • apps/api/src/modules/schedule-history/schedule-history.service.ts
  • apps/api/src/openapi.ts
  • apps/api/src/routes/dashboard.routes.ts
  • apps/api/src/routes/index.ts
  • apps/api/src/routes/schedule-history.routes.ts
  • apps/api/test/dashboard.routes.test.ts
  • apps/api/test/docs.routes.test.ts
  • apps/api/test/helpers/fake-prisma.ts
  • apps/api/test/organization.routes.test.ts
  • apps/api/test/schedule-history.routes.test.ts
  • apps/web/src/features/availability/components/mvp-availability-page.tsx
  • apps/web/src/features/dashboard/api/dashboard-api.ts
  • apps/web/src/features/dashboard/components/mvp-dashboard-page.tsx
  • apps/web/src/features/dashboard/queries/dashboard-queries.ts
  • apps/web/src/features/dashboard/queries/dashboard-query-keys.ts
  • apps/web/src/features/organization/components/mvp-organization-edit-page.tsx
  • apps/web/src/features/organization/components/organization-form.tsx
  • apps/web/src/features/schedule-history/api/schedule-history-api.ts
  • apps/web/src/features/schedule-history/components/mvp-schedule-history-page.tsx
  • apps/web/src/features/schedule-history/queries/schedule-history-queries.ts
  • apps/web/src/features/schedule-history/queries/schedule-history-query-keys.ts
  • apps/web/src/features/schedules/components/confirmed-schedule-calendar.tsx
  • apps/web/src/features/schedules/components/mvp-schedules-page.tsx
  • apps/web/src/lib/api-client.ts
  • apps/web/src/lib/schedule-display.ts
  • docs/API.md
  • docs/SWAGGER.md
  • docs/openapi.yaml
  • package.json
  • packages/shared/src/schemas/dashboard.ts
  • packages/shared/src/schemas/schedule-history.ts

Comment thread apps/api/package.json Outdated
Comment thread apps/api/test/schedule-history.routes.test.ts
Comment thread apps/api/test/schedule-history.routes.test.ts Outdated
Comment on lines +1091 to +1094
className="grid gap-1"
style={{
gridTemplateColumns: `${TIMETABLE_TIME_COLUMN_WIDTH}px repeat(${visibleTimetableDates.length}, ${TIMETABLE_DATE_COLUMN_WIDTH}px)`,
}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

인라인 스타일을 Tailwind 기반 레이아웃으로 대체하세요.

style로 동적 px 기반 grid template을 주입하고 있어 프로젝트 스타일링 규칙을 벗어납니다. 테이블/행 flex 구조 또는 토큰화된 utility class로 동일한 레이아웃을 구성해 주세요. As per coding guidelines, apps/web/src/**/*.{css,ts,tsx}: Use Tailwind CSS utility classes only; avoid inline style attributes.

🤖 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/features/availability/components/mvp-availability-page.tsx`
around lines 1091 - 1094, Replace the inline gridTemplateColumns style in
mvp-availability-page.tsx with a Tailwind-based layout approach. Update the
component that renders the timetable grid so it uses utility classes and/or a
flex/table structure instead of injecting dynamic px values via the style prop,
keeping the same TIMETABLE_TIME_COLUMN_WIDTH and
visibleTimetableDates.length-driven layout behavior. Locate the grid container
in the availability page component and refactor that section to remove the
inline style attribute entirely.

Source: Coding guidelines

Comment on lines +428 to +475
function groupUnfilledConditionsByDate(conditions: UnfilledCondition[]): UnfilledConditionGroup[] {
const groupsByDate = new Map<string, UnfilledConditionGroup>();

conditions.forEach((condition) => {
const group = groupsByDate.get(condition.date);

if (group) {
group.conditions.push(condition);
return;
}

groupsByDate.set(condition.date, {
conditions: [condition],
date: condition.date,
dayLabel: condition.dayLabel,
});
});

return [...groupsByDate.values()]
.map((group) => ({
...group,
conditions: [...group.conditions].sort((left, right) =>
left.timeRange.localeCompare(right.timeRange),
),
}))
.sort((left, right) => left.date.localeCompare(right.date));
}

function getShortageCount(condition: UnfilledCondition) {
return Math.max(condition.requiredWorkers - condition.assignedWorkers, 0);
}

function getUnfilledConditionLabel(condition: UnfilledCondition) {
return `${condition.timeRange} ${getShortageCount(condition)}명 부족`;
}

function getUnfilledConditionPreview(conditions: UnfilledCondition[]) {
const visibleConditions = conditions.slice(0, MAX_PREVIEW_UNFILLED_CONDITIONS);
const hiddenConditionCount = conditions.length - visibleConditions.length;
const visibleLabel = visibleConditions.map(getUnfilledConditionLabel).join(" · ");

if (hiddenConditionCount <= 0) {
return visibleLabel;
}

return `${visibleLabel} · +${hiddenConditionCount}개`;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

신규 그룹핑/미리보기 유틸에 대한 테스트가 없음

groupUnfilledConditionsByDate, getShortageCount, getUnfilledConditionPreview는 날짜별 정렬, 부족 인원 계산, 숨김 개수 처리 등 분기 로직이 있는 유틸입니다. CLAUDE.md에 따르면 "복잡한 유틸 또는 hook을 추가할 때만 테스트를 작성"하도록 되어 있는데, 이번 변경에는 대응하는 테스트 파일이 보이지 않습니다. 최소한 그룹핑 순서와 부족 인원 계산 케이스 1~2개는 단위 테스트로 검증해두면 이후 회귀를 막기 좋습니다.

As per path instructions, "Web은 기본적으로 typecheck/lint를 우선하고, 복잡한 유틸 또는 hook을 추가할 때만 테스트를 작성한다."

🤖 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/features/schedules/components/mvp-schedules-page.tsx` around
lines 428 - 475, The new utility logic in groupUnfilledConditionsByDate,
getShortageCount, getUnfilledConditionLabel, and getUnfilledConditionPreview has
branching behavior and should be covered by tests. Add focused unit tests for
these helpers to verify date grouping/sorting, shortage count clamping, and
preview truncation with hidden-count formatting, using the existing
mvp-schedules-page.tsx utility symbols as the entry point. Keep the tests
minimal but include at least one or two representative cases for each branch so
regressions are caught.

Source: Path instructions

Comment thread apps/web/src/features/schedules/components/mvp-schedules-page.tsx Outdated
Comment thread apps/web/src/lib/schedule-display.ts Outdated
Comment thread docs/openapi.yaml Outdated
Comment thread docs/openapi.yaml
@meteorqz6

Copy link
Copy Markdown
Contributor Author

리뷰 반영 커밋을 푸시했습니다.

반영한 내용은 다음과 같습니다.

  • API typecheck에서 shared/database 선행 빌드를 수행하도록 수정했습니다.
  • 스케줄 히스토리 month 단독 요청 검증 테스트와 CSV 응답 헤더 검증을 추가했습니다.
  • 가능 시간 화면에서 선택 기간과 저장된 근무 기간이 정확히 일치할 때만 조회·저장·입력 UI가 열리도록 수정했습니다.
  • 확정 스케줄 시간 표시와 시간대 그룹화 로직을 공통 유틸로 정리했습니다.
  • 보관함 월 필터에 “전체 월” 선택을 추가했습니다.
  • 스케줄 화면의 날짜별 시간대 그룹을 미리 계산하도록 정리했습니다.
  • 미충족 조건 helper와 스케줄 표시 helper 테스트를 추가했습니다.
  • OpenAPI의 스케줄 히스토리 404 계약과 최소 인원 조건 요청 스키마를 보강했습니다.
  • PR 본문을 저장소 템플릿에 맞춰 한글로 보강했습니다.

로컬 검증도 완료했습니다.

  • corepack pnpm format:check
  • corepack pnpm --filter @fragment/web lint
  • corepack pnpm --filter @fragment/web typecheck
  • corepack pnpm --filter @fragment/web exec vitest run src/lib/schedule-display.test.ts src/features/schedules/components/mvp-schedules-page.test.ts
  • corepack pnpm --filter @fragment/api lint
  • corepack pnpm --filter @fragment/api typecheck
  • corepack pnpm --filter @fragment/api test -- schedule-history.routes.test.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/features/availability/components/mvp-availability-page.tsx (1)

505-512: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

저장 전 근무자/기간 전환 시 draft가 조용히 사라집니다.

hasUnsavedChanges가 있어도 근무자 또는 기간을 바꿀 수 있고, 이후 새 조회 결과가 Line 505-512에서 draftAvailability를 서버값으로 덮어써 사용자가 드래그로 입력한 변경분이 유실됩니다. 전환 전에 저장/취소를 요구해 주세요.

수정 예시
 function selectWorkDate(date: string) {
   if (isPageLoading) {
     return;
   }
+
+  if (hasUnsavedChanges) {
+    setSaveMessage("가능 시간을 저장하거나 변경 취소한 뒤 근무 기간을 변경해 주세요.");
+    return;
+  }

   if (!workStartDate || (workStartDate && workEndDate) || date < workStartDate) {
 onClick={() => {
+  if (worker.id === selectedWorkerId) {
+    return;
+  }
+
+  if (hasUnsavedChanges) {
+    setSaveMessage("가능 시간을 저장하거나 변경 취소한 뒤 다른 근무자를 선택해 주세요.");
+    return;
+  }
+
   setSelectedWorkerId(worker.id);
   setSelectedAvailabilityDate("");
   setSaveMessage("");
 }}

Also applies to: 755-772, 1011-1015

🤖 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/features/availability/components/mvp-availability-page.tsx`
around lines 505 - 512, The availability page is overwriting in-progress edits
when worker or period changes, causing unsaved draft changes to disappear.
Update the selection/change flow in mvp-availability-page.tsx so that the
handlers around hasUnsavedChanges block switching or first prompt the user to
save or discard before updating the active worker/date range. Also make sure the
availabilityResult.data useEffect that resets
savedAvailability/draftAvailability only runs when it is safe to replace the
draft, and apply the same guard to the related worker/period transition logic in
the other affected sections.
🤖 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/features/schedules/components/mvp-schedules-page.test.ts`:
- Around line 1-65: The issue is that this test is colocated with the wrong
feature component path even though it only verifies the helpers in
unfilled-conditions. Move the test to sit alongside the target module by
renaming/replacing mvp-schedules-page.test.ts with unfilled-conditions.test.ts
in the utils area, and keep the existing assertions for
groupUnfilledConditionsByDate, getShortageCount, and getUnfilledConditionPreview
unchanged.

In `@apps/web/src/features/schedules/components/mvp-schedules-page.tsx`:
- Around line 197-199: `dateTimeToTime()` currently returns only the clock time,
so schedule ranges from `assignmentToScheduleItem` and
`candidateToScheduleCandidateItem` can hide next-day ավարտs; update the
rendering path to incorporate `dayOffset` when building the displayed time
range. Reuse the same next-day formatting behavior as
`formatScheduleAssignmentTimeRange()` in `schedule-display.ts`, and make sure
the start/end labels can show the `익일` or `+1` indicator when `endsAt` crosses
midnight.

---

Outside diff comments:
In `@apps/web/src/features/availability/components/mvp-availability-page.tsx`:
- Around line 505-512: The availability page is overwriting in-progress edits
when worker or period changes, causing unsaved draft changes to disappear.
Update the selection/change flow in mvp-availability-page.tsx so that the
handlers around hasUnsavedChanges block switching or first prompt the user to
save or discard before updating the active worker/date range. Also make sure the
availabilityResult.data useEffect that resets
savedAvailability/draftAvailability only runs when it is safe to replace the
draft, and apply the same guard to the related worker/period transition logic in
the other affected sections.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b160b507-7098-40aa-b652-bab1dbd79194

📥 Commits

Reviewing files that changed from the base of the PR and between 8edc214 and 68e3724.

📒 Files selected for processing (12)
  • apps/api/package.json
  • apps/api/test/schedule-history.routes.test.ts
  • apps/web/src/features/availability/components/mvp-availability-page.tsx
  • apps/web/src/features/dashboard/components/mvp-dashboard-page.tsx
  • apps/web/src/features/schedule-history/components/mvp-schedule-history-page.tsx
  • apps/web/src/features/schedules/components/confirmed-schedule-calendar.tsx
  • apps/web/src/features/schedules/components/mvp-schedules-page.test.ts
  • apps/web/src/features/schedules/components/mvp-schedules-page.tsx
  • apps/web/src/features/schedules/utils/unfilled-conditions.ts
  • apps/web/src/lib/schedule-display.test.ts
  • apps/web/src/lib/schedule-display.ts
  • docs/openapi.yaml

Comment thread apps/web/src/features/schedules/utils/unfilled-conditions.test.ts
Comment thread apps/web/src/features/schedules/components/mvp-schedules-page.tsx
@meteorqz6

Copy link
Copy Markdown
Contributor Author

추가 리뷰 3건을 반영했습니다.

  • unfilled-conditions 테스트를 대상 유틸 옆으로 이동했습니다.
  • 스케줄 화면의 후보/배정 시간 표시에서 다음날 시간은 익일 HH:MM으로 표시하도록 수정했습니다. 편집/저장용 HH:MM 값은 그대로 분리해 유지했습니다.
  • 가능 시간 관리에서 저장하지 않은 변경분이 있을 때 근무자/근무 기간 전환과 조회 결과 덮어쓰기를 막고 안내 메시지를 표시하도록 수정했습니다.

로컬 검증:

  • corepack pnpm format:check
  • corepack pnpm --filter @fragment/web lint
  • corepack pnpm --filter @fragment/web typecheck
  • corepack pnpm --filter @fragment/web exec vitest run src/lib/schedule-display.test.ts src/features/schedules/utils/unfilled-conditions.test.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/features/availability/components/mvp-availability-page.tsx (1)

981-1076: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

근무 기간 저장 실패 메시지가 숨겨집니다.

새 기간 선택 직후에는 hasMatchingPlanningPeriodfalse라서 Line 981 이하 섹션이 렌더링되지 않습니다. 이 상태에서 savePlanningPeriod()가 실패해 saveMessage가 설정되어도 Line 1073의 메시지 영역이 숨겨져 사용자가 실패 원인을 볼 수 없습니다.

수정 예시
-      {hasMatchingPlanningPeriod ? (
+      {saveMessage ? (
+        <div className="rounded-lg border border-border bg-surface-secondary px-6 py-3 text-sm text-muted-foreground">
+          {saveMessage}
+        </div>
+      ) : null}
+
+      {hasMatchingPlanningPeriod ? (
         <section className="min-w-0 overflow-hidden rounded-xl border border-border bg-card shadow-card">
...
-          {saveMessage ? (
-            <div className="border-b border-border bg-surface-secondary px-6 py-3 text-sm text-muted-foreground">
-              {saveMessage}
-            </div>
-          ) : null}
-
🤖 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/features/availability/components/mvp-availability-page.tsx`
around lines 981 - 1076, The planning-period save error state is being hidden
because the section gated by hasMatchingPlanningPeriod disappears before
saveMessage can render. Update mvp-availability-page so savePlanningPeriod
failures can still surface the message even when the newly selected period is
not yet matching, by moving the saveMessage display outside the
hasMatchingPlanningPeriod-only block or rendering a separate always-visible
status area tied to savePlanningPeriod and saveMessage. Make sure the failure
feedback remains visible immediately after a failed save and still clears
correctly when the user retries or changes selection.
🤖 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/features/availability/components/mvp-availability-page.tsx`:
- Line 408: The synchronization logic in mvp-availability-page.tsx is reapplying
activePlanningPeriod even when the user edits workStartDate or workEndDate,
which causes the newly selected period to snap back. Update the effect around
activePlanningPeriod so it only syncs when the server-side planning period truly
changes, and exclude normal local date edits from retriggering the reset. Use
the existing symbols activePlanningPeriod, workStartDate, workEndDate, and
hasUnsavedChangesRef to keep the user’s in-progress selection intact.

---

Outside diff comments:
In `@apps/web/src/features/availability/components/mvp-availability-page.tsx`:
- Around line 981-1076: The planning-period save error state is being hidden
because the section gated by hasMatchingPlanningPeriod disappears before
saveMessage can render. Update mvp-availability-page so savePlanningPeriod
failures can still surface the message even when the newly selected period is
not yet matching, by moving the saveMessage display outside the
hasMatchingPlanningPeriod-only block or rendering a separate always-visible
status area tied to savePlanningPeriod and saveMessage. Make sure the failure
feedback remains visible immediately after a failed save and still clears
correctly when the user retries or changes selection.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 539eaf38-953a-44b7-b833-56f5f60fe080

📥 Commits

Reviewing files that changed from the base of the PR and between 68e3724 and 99d30a3.

📒 Files selected for processing (5)
  • apps/web/src/features/availability/components/mvp-availability-page.tsx
  • apps/web/src/features/schedules/components/mvp-schedules-page.tsx
  • apps/web/src/features/schedules/utils/unfilled-conditions.test.ts
  • apps/web/src/lib/schedule-display.test.ts
  • apps/web/src/lib/schedule-display.ts

visitedKeys: Set<string>;
} | null>(null);
const suppressNextCellClickRef = useRef(false);
const hasUnsavedChangesRef = useRef(false);

Copy link
Copy Markdown

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

기존 근무 기간이 있으면 새 기간 선택이 즉시 되돌아갑니다.

Line 497에서 activePlanningPeriod를 무조건 다시 주입하는데, 이 effect가 workStartDate/workEndDate 변경에도 실행됩니다. 사용자가 새 시작일을 고르면 Line 497-498이 기존 저장 기간으로 되돌려 종료일 선택을 완료할 수 없습니다. 서버 planning period가 실제로 바뀐 경우에만 동기화하세요.

수정 예시
   const suppressNextCellClickRef = useRef(false);
   const hasUnsavedChangesRef = useRef(false);
+  const lastSyncedPlanningPeriodKeyRef = useRef<string | null>(null);

   useEffect(() => {
     if (!planningPeriodQuery.data) {
       return;
     }

     if (!activePlanningPeriod) {
+      if (hasUnsavedChangesRef.current) {
+        setSaveMessage("가능 시간을 저장하거나 변경 취소한 뒤 근무 기간 변경을 반영해 주세요.");
+        return;
+      }
+
+      lastSyncedPlanningPeriodKeyRef.current = null;
       setWorkStartDate("");
       setWorkEndDate("");
       return;
     }

+    const planningPeriodKey = `${activePlanningPeriod.startDate}:${activePlanningPeriod.endDate}`;
+
+    if (lastSyncedPlanningPeriodKeyRef.current === planningPeriodKey) {
+      return;
+    }
+
     if (
       hasUnsavedChangesRef.current &&
       (activePlanningPeriod.startDate !== workStartDate ||
         activePlanningPeriod.endDate !== workEndDate)
     ) {
       setSaveMessage("가능 시간을 저장하거나 변경 취소한 뒤 근무 기간 변경을 반영해 주세요.");
       return;
     }

+    lastSyncedPlanningPeriodKeyRef.current = planningPeriodKey;
     setWorkStartDate(activePlanningPeriod.startDate);
     setWorkEndDate(activePlanningPeriod.endDate);
     setCalendarMonthDate(getMonthStartDate(activePlanningPeriod.startDate));

Also applies to: 488-500

🤖 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/features/availability/components/mvp-availability-page.tsx` at
line 408, The synchronization logic in mvp-availability-page.tsx is reapplying
activePlanningPeriod even when the user edits workStartDate or workEndDate,
which causes the newly selected period to snap back. Update the effect around
activePlanningPeriod so it only syncs when the server-side planning period truly
changes, and exclude normal local date edits from retriggering the reset. Use
the existing symbols activePlanningPeriod, workStartDate, workEndDate, and
hasUnsavedChangesRef to keep the user’s in-progress selection intact.

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