Skip to content

feat: 보관함·대시보드 확정 스케줄 API 연결 - #103

Merged
ehlung merged 11 commits into
developfrom
codex/archive-dashboard-api-contract
Jun 30, 2026
Merged

feat: 보관함·대시보드 확정 스케줄 API 연결#103
ehlung merged 11 commits into
developfrom
codex/archive-dashboard-api-contract

Conversation

@ehlung

@ehlung ehlung commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

요약

  • 확정 스케줄 보관함 API와 대시보드 API를 구현했습니다.
  • 보관함/대시보드 프론트 API 연결과 CSV 다운로드를 연결했습니다.
  • CSV는 인쇄/공유용 근무표 형태로 내려주고 Excel 한글 깨짐 방지를 위해 UTF-8 BOM을 포함합니다.

검증

  • ./node_modules/.bin/jest --config apps/api/jest.config.js --runInBand
  • ./node_modules/.bin/tsc -p apps/web/tsconfig.json --noEmit

Summary by CodeRabbit

  • 새로운 기능
    • 대시보드 조회(조직 운영 시간/휴무, 최신 확정 스케줄 포함)와 CSV 내보내기 흐름을 추가했습니다.
    • 스케줄 히스토리 목록·상세·CSV 내보내기를 제공하고, 연/월 범위 겹침 기준 및 정렬/빈 목록 동작을 반영했습니다.
  • 버그 수정
    • 인증·조직 컨텍스트 및 요청 값 검증을 강화해 부적절한 요청에 명확한 오류를 반환합니다.
    • CSV 다운로드가 UTF-8 BOM 및 포맷 규칙을 준수하도록 개선했습니다.

@vercel

vercel Bot commented Jun 29, 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 Jun 30, 2026 1:34am

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ehlung, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 41 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

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, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9bf0c630-9e39-473d-b01a-272ff4d0a726

📥 Commits

Reviewing files that changed from the base of the PR and between 829b6fd and 4951c8e.

📒 Files selected for processing (6)
  • apps/api/test/docs.routes.test.ts
  • 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/lib/schedule-display.ts
  • docs/SWAGGER.md
  • docs/openapi.yaml

Walkthrough

대시보드(GET /dashboard)와 스케줄 히스토리(GET /schedule-history, 상세, CSV 내보내기) API를 추가하고, 웹 대시보드·스케줄 히스토리 페이지를 실제 조회/다운로드 흐름으로 전환한다. 공유 스키마, 라우터, 통합 테스트, API 문서도 함께 갱신된다.

Changes

Dashboard & Schedule History 기능 전체

Layer / File(s) Summary
공유 스키마 및 organization export
packages/shared/src/schemas/dashboard.ts, apps/api/src/modules/organization/organization.service.ts
dashboardResponseSchemaorganization 필드를 organizationDetailSchema로 교체하고, OrganizationWithBusinessHours 타입과 toOrganizationDetail 헬퍼를 export로 노출한다.
Dashboard 서비스와 핸들러
apps/api/src/modules/dashboard/dashboard.service.ts, apps/api/src/modules/dashboard/dashboard.handlers.ts
getDashboard가 조직과 최신 CONFIRMED 스케줄을 조회·매핑하고, getDashboardHandler가 조직 검증 후 200 JSON으로 응답한다.
Schedule History 서비스와 핸들러
apps/api/src/modules/schedule-history/schedule-history.service.ts, apps/api/src/modules/schedule-history/schedule-history.handlers.ts
listScheduleHistory, getScheduleHistoryDetail, exportScheduleHistoryCsv와 각 Express 핸들러를 추가해 목록·상세·CSV 응답을 구성한다.
Express 라우터 등록
apps/api/src/routes/dashboard.routes.ts, apps/api/src/routes/schedule-history.routes.ts, apps/api/src/routes/index.ts
두 라우터에 requireAuth·requireOrganization·validate 미들웨어를 적용하고 /dashboard, /schedule-history로 마운트한다.
fake-prisma 정렬·범위 처리
apps/api/test/helpers/fake-prisma.ts
matchesScheduleWhere의 범위 조건을 확장하고, findFirst·findManyconfirmedAt desc/id desc 정렬을 반영하도록 변경한다.
API 통합 테스트
apps/api/test/dashboard.routes.test.ts, apps/api/test/schedule-history.routes.test.ts
인증·조직 미존재·정상 조회·CSV·404/400 시나리오를 supertest로 검증하며, 조직·스케줄·배정 더미 헬퍼를 포함한다.
웹 API, 쿼리 키, React Query 훅
apps/web/src/lib/api-client.ts, apps/web/src/features/dashboard/api/dashboard-api.ts, apps/web/src/features/dashboard/queries/dashboard-query-keys.ts, apps/web/src/features/dashboard/queries/dashboard-queries.ts, apps/web/src/features/schedule-history/api/schedule-history-api.ts, apps/web/src/features/schedule-history/queries/schedule-history-query-keys.ts, apps/web/src/features/schedule-history/queries/schedule-history-queries.ts
apiClient에 Blob 응답 지원을 추가하고, 대시보드·스케줄 히스토리용 API 함수, 쿼리 키, React Query 훅을 추가한다.
대시보드·스케줄 히스토리 페이지 전환
apps/web/src/features/dashboard/components/mvp-dashboard-page.tsx, apps/web/src/features/schedule-history/components/mvp-schedule-history-page.tsx
목데이터와 고정 상수를 제거하고 실제 조회 결과·상세 응답·CSV 다운로드 흐름으로 전환한다.
API 문서 업데이트
docs/API.md
schedule-history 조회 규칙과 CSV 포맷, dashboard 응답 예시와 CSV 호출 경로를 문서화한다.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning 요약과 검증만 있고 템플릿의 개요, 변경 사항, 변경 유형, 영향 범위, 체크리스트 등이 대부분 누락됐습니다. 템플릿의 개요/변경 사항/변경 유형/영향 범위/테스트 방법/체크리스트를 채우고, 가능하면 리뷰 포인트와 스크린샷도 추가하세요.
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 (3 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 보관함·대시보드의 확정 스케줄 API 연결이라는 핵심 변경을 간결하게 요약합니다.
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 codex/archive-dashboard-api-contract

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.

@ehlung
ehlung changed the base branch from main to develop June 29, 2026 20:43

@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: 8

🤖 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/src/modules/schedule-history/schedule-history.service.ts`:
- Around line 79-85: The CSV escaping helper in escapeCsvCell only handles CSV
syntax and does not neutralize formula-injection payloads from user-controlled
values like workerNameSnapshot. Update escapeCsvCell so it also detects cells
starting with =, +, -, @, or those characters after leading whitespace, and
prefix them with a safe character such as apostrophe before applying the
existing quote escaping. Apply the same protection anywhere the same CSV export
logic is used, including the other workerNameSnapshot-related export path
referenced in the diff.

In `@apps/api/test/dashboard.routes.test.ts`:
- Around line 84-132: The dashboard route test currently only seeds one
organization, so it cannot catch regressions where latestConfirmedSchedule is
fetched globally instead of within the logged-in user’s organization scope.
Update the createFakePrisma setup in this test to include a second organization
with a more recent CONFIRMED schedule, then assert that
createApp("/api/dashboard") still returns only the current organization’s data
and its own latest confirmed schedule. Use the existing dashboard.routes.test
case and the latestConfirmedSchedule/organization assertions to verify the
access-control behavior.

In `@apps/api/test/schedule-history.routes.test.ts`:
- Around line 72-291: The schedule history route tests are missing
validation-failure coverage for the `validate()`-protected inputs. Add one or
two Supertest cases in `schedule-history.routes.test.ts` that hit `GET
/api/schedule-history` with an invalid `year/month` query (for example
`month=13`) and/or `GET /api/schedule-history/:scheduleId` with a non-numeric
id, and assert the response is `400` with `VALIDATION_ERROR`. Use the existing
`createApp`, `authHeader`, and route paths to locate the relevant test blocks.
- Around line 109-291: The schedule-history route tests currently only seed one
organization, so they do not catch organization-scope leakage. Update the
existing cases around createApp, createFakePrisma, and the /api/schedule-history
list/detail/export.csv requests to include a second organization with a newer
CONFIRMED schedule and a matching detail target, then assert the logged-in org
only sees its own data. Verify the foreign-org schedule is excluded from the
list and that detail/CSV calls for it return 404 with SCHEDULE_NOT_FOUND, using
the schedule-history route handlers and confirmed schedule fixtures to locate
the affected tests.

In `@apps/web/src/features/dashboard/components/mvp-dashboard-page.tsx`:
- Around line 61-63: The `formatDateTimeToTime()` helper is dropping the date
and making overnight ranges like `startsAt`/`endsAt` look like same-day shifts.
Update the time formatting logic in `mvp-dashboard-page.tsx` to preserve or
indicate next-day end times, and apply the same fix anywhere the copied helper
is used in the archive page so both views handle overnight boundaries
consistently. Use the existing schedule display code around
`formatDateTimeToTime()` and the `startsAt`/`endsAt` rendering as the place to
centralize this behavior.

In
`@apps/web/src/features/schedule-history/components/mvp-schedule-history-page.tsx`:
- Around line 25-26: The schedule history year filter is incorrectly capped by
HISTORY_YEAR_OPTIONS in mvp-schedule-history-page, which hides older CONFIRMED
records that still exist on the server. Update the availableYears/year option
logic so it is not limited to the current year minus six; instead derive options
from server-provided years or another uncapped discovery mechanism, and make
sure the filtering/export flow in this page can surface all available history
years.
- Around line 143-146: The schedule history detail query is being treated as
loading even when it is disabled, which leaves the page stuck on a loading state
for empty year/month selections. Update the loading logic in
mvp-schedule-history-page and the useScheduleHistoryDetailQuery result handling
so disabled state does not count as pending; only show loading when
selectedScheduleId is non-empty and the detail query is actually pending, or
switch the UI condition to use isFetching instead of isPending.

In `@docs/API.md`:
- Around line 579-602: The CSV export contract for GET
/schedule-history/:scheduleId/export.csv is missing the BOM requirement, which
should be documented alongside the existing UTF-8 content type. Update the API
contract in docs/API.md near the schedule history CSV export section to
explicitly state that the response includes a UTF-8 BOM, so consumers and QA can
rely on the exact file format. Keep the wording aligned with the existing
response description and CSV format notes.
🪄 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: 72330de5-a13e-48e5-96ec-fa54efed0d83

📥 Commits

Reviewing files that changed from the base of the PR and between d85941a and 355ea34.

📒 Files selected for processing (22)
  • 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/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/helpers/fake-prisma.ts
  • apps/api/test/schedule-history.routes.test.ts
  • 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/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/lib/api-client.ts
  • docs/API.md
  • packages/shared/src/schemas/dashboard.ts

Comment thread apps/api/src/modules/schedule-history/schedule-history.service.ts
Comment thread apps/api/test/dashboard.routes.test.ts
Comment thread apps/api/test/schedule-history.routes.test.ts
Comment thread apps/api/test/schedule-history.routes.test.ts Outdated
Comment thread apps/web/src/features/dashboard/components/mvp-dashboard-page.tsx Outdated
Comment thread apps/web/src/features/schedule-history/components/mvp-schedule-history-page.tsx Outdated
Comment thread docs/API.md

@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/schedule-history/api/schedule-history-api.ts (1)

12-18: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

month 단독 쿼리를 여기서 막아 주세요.

/schedule-history 계약상 monthyear와 함께 전달돼야 하는데, 현재는 query.month만 있어도 그대로 붙습니다. 호출부가 실수하면 바로 400 검증 오류로 끝나므로 이 helper에서 조합을 보정하는 편이 안전합니다.

수정 예시
   if (query.year !== undefined) {
     searchParams.set("year", String(query.year));
   }

-  if (query.month !== undefined) {
+  if (query.year !== undefined && query.month !== undefined) {
     searchParams.set("month", String(query.month));
   }

As per path instructions, docs/API.md says “month는 year와 함께 전달(조건 불일치 시 validation 흐름 유지)”.

🤖 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/schedule-history/api/schedule-history-api.ts` around
lines 12 - 18, The schedule-history query builder currently appends month
whenever query.month is present, even if year is missing. Update the helper in
schedule-history-api.ts so month is only included when year is also set, keeping
the query shape aligned with the /schedule-history contract. Use the existing
query.year and query.month checks in the same function to gate month insertion
together, so standalone month inputs are ignored rather than forwarded.

Source: Path instructions

🤖 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/schedule-history/components/mvp-schedule-history-page.tsx`:
- Around line 65-71: The `formatScheduleAssignmentTimeRange` logic is duplicated
in `mvp-schedule-history-page.tsx` and `mvp-dashboard-page.tsx`, so move this
shared time-range formatting into a common util and have both pages call that
single helper. Keep the existing `formatScheduleAssignmentTimeRange` behavior,
but replace the copied implementation with an import from the shared utility so
future fixes stay in sync.

---

Outside diff comments:
In `@apps/web/src/features/schedule-history/api/schedule-history-api.ts`:
- Around line 12-18: The schedule-history query builder currently appends month
whenever query.month is present, even if year is missing. Update the helper in
schedule-history-api.ts so month is only included when year is also set, keeping
the query shape aligned with the /schedule-history contract. Use the existing
query.year and query.month checks in the same function to gate month insertion
together, so standalone month inputs are ignored rather than forwarded.
🪄 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: bb2b7a1e-dcb4-4de6-9cea-949789b566d3

📥 Commits

Reviewing files that changed from the base of the PR and between 355ea34 and 829b6fd.

📒 Files selected for processing (9)
  • apps/api/src/modules/schedule-history/schedule-history.service.ts
  • apps/api/test/dashboard.routes.test.ts
  • apps/api/test/schedule-history.routes.test.ts
  • apps/web/src/features/dashboard/components/mvp-dashboard-page.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
  • docs/API.md
  • packages/shared/src/schemas/schedule-history.ts

Comment thread apps/web/src/features/schedule-history/components/mvp-schedule-history-page.tsx Outdated
@ehlung
ehlung merged commit 687f3f1 into develop Jun 30, 2026
4 checks passed
@ehlung
ehlung deleted the codex/archive-dashboard-api-contract branch June 30, 2026 01:35
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