Skip to content

feat: 매장 운영 업무 스케줄 구현 (#11) - #12

Merged
seongjinss555 merged 6 commits into
developfrom
feat/#11/store-operation-work-schedule
Jul 7, 2026
Merged

feat: 매장 운영 업무 스케줄 구현 (#11)#12
seongjinss555 merged 6 commits into
developfrom
feat/#11/store-operation-work-schedule

Conversation

@seongjinss555

@seongjinss555 seongjinss555 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Pull Request

작업 내용

  • 매장 운영 도메인용 워크스페이스 shell, 사이드바, 헤더 구현
  • Mock 데이터 기반 업무 스케줄 화면 구현
  • 근무 유형 설정 UI와 요일별 스케줄 변경 기능 구현
  • 랜딩 히어로 배경 및 이미지 경로 정리
  • PR 템플릿에 실행화면 섹션 추가

작업 결과

  • /workspaces/test/work-schedule에서 매장 운영 업무 스케줄 화면 확인 가능
  • 워크스페이스 내부 페이지에 공통 사이드바/헤더 적용
  • 업무 스케줄 셀 클릭 시 근무 유형 순환 변경
  • 근무 유형 추가, 삭제, 순서 변경, 시간 설정, 휴무 설정 가능
  • 랜딩 히어로에 /images/landing/bg.svg 배경 적용

변경 사항

Added

  • widgets/workspace-shell 공통 워크스페이스 shell
  • views/store-operation/work-schedule 업무 스케줄 view
  • features/manage-work-schedule 업무 스케줄 관리 feature
  • entities/work-schedule, entities/workspace, entities/workspace-member mock entity
  • public/images/header, public/images/landing 이미지 리소스

Changed

  • 랜딩 헤더 로고 이미지 적용
  • 랜딩 히어로 배경 이미지 적용
  • 랜딩 이미지 경로를 /images/landing/* 기준으로 정리
  • PR 템플릿에 실행화면 섹션 추가

Fixed

  • 주말 컬럼까지 스케줄 셀이 채워지도록 보강
  • 최신 develop의 src/shared/lib/utils 경로 기준으로 import 정리

실행화면

  • 홈 화면 herosection, logo 수정
스크린샷 2026-07-06 오후 5 34 11 - 스케줄 표 스크린샷 2026-07-06 오후 5 04 27 - 스케줄 표 수정 스크린샷 2026-07-06 오후 5 20 01

테스트

  • 로컬 실행 확인
  • 주요 시나리오 확인
  • 영향 범위 확인
  • npm run lint
  • npm run typecheck
  • npm run build

리뷰 체크리스트

  • PR base branch가 올바릅니다. (feature/* -> develop, 배포 시 develop 또는 release/* -> main)
  • 브랜치명이 Type/#issue-number/description 형식을 따릅니다.
  • 커밋 메시지가 컨벤션을 따릅니다.
  • 불필요한 console.log, 주석, 임시 코드를 제거했습니다.
  • 타입 에러와 린트 에러를 확인했습니다.
  • CodeRabbit 1차 리뷰를 확인했습니다.
  • CodeRabbit 리뷰 반영 후 Discord에 공유했습니다.
  • 최소 1명 이상의 approve 후 merge합니다.

리뷰 요청사항

  • FSD 계층 분리(app, views, features, entities, widgets)가 적절한지 확인 부탁드립니다.
  • 실제 Supabase 연동 전 Mock 구조가 확장 가능한지 확인 부탁드립니다.
  • 워크스페이스 shell이 매장 운영 도메인 기준으로 충분한지 확인 부탁드립니다.
  • src/widgets/workspace-shell/model/workspace-navigation.ts 해당 경로에서 사이드바 수정, 연동 하시면 됩니다

관련 이슈

Closes #11
Refs #2

Summary by CodeRabbit

  • New Features
    • 워크스페이스 공통 헤더/사이드바와 워크스페이스별 화면 라우팅을 추가했습니다.
    • 근무 일정 관리 화면(7일 매트릭스, 셀 클릭으로 근무 유형 전환), 요일별 집계 및 근무자 표시를 추가했습니다.
    • 근무 유형 설정(추가/수정/삭제/순서 이동)과 범례를 제공합니다.
  • Documentation
    • PR 템플릿에 “실행화면” 섹션을 추가했습니다.
  • UI 개선
    • 랜딩 로고/배경 및 CTA 이동 경로를 업데이트했습니다.
  • Chores
    • 개발 환경 포맷팅/린트 설정을 정리했습니다.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5f6f60b5-405a-45d8-8871-6d2ed9f15601

📥 Commits

Reviewing files that changed from the base of the PR and between 250128f and aaa5058.

📒 Files selected for processing (2)
  • .vscode/settings.json
  • src/features/manage-work-schedule/ui/WorkScheduleBoard.tsx

📝 Walkthrough

Walkthrough

워크스페이스 공통 shell과 업무 스케줄 화면을 추가하고, 근무 일정 도메인 타입/목업/유틸/상태/UI를 연결했습니다. 또한 랜딩 헤더와 히어로/스텝 이미지 경로를 정리하고 PR 템플릿과 VS Code 설정을 갱신했습니다.

Changes

워크스페이스 shell 및 업무 스케줄 기능

Layer / File(s) Summary
워크스페이스/멤버/근무일정 도메인 타입 및 목업
src/entities/workspace/..., src/entities/workspace-member/..., src/entities/work-schedule/model/*, src/entities/work-schedule/index.ts
Workspace, WorkspaceMember, WorkScheduleConfig/Entry/Option 타입과 목업 데이터, 요일 데이터를 정의하고 배럴로 재export합니다.
근무 일정 계산 유틸 함수
src/entities/work-schedule/lib/*
기본/다음 shift 옵션 조회, 요일별 카운트 집계, 초기 스케줄 생성, 요일별 근무자 조회 함수를 구현합니다.
근무 일정 클라이언트 상태 훅
src/features/manage-work-schedule/model/use-work-schedule-state.ts
초기 스케줄 보완, 셀 순환(cycleCell), shift 옵션 일괄 치환(replaceShiftOption) 로직을 구현합니다.
근무 일정 보드/셀/설정 패널 UI
src/features/manage-work-schedule/ui/*, src/features/manage-work-schedule/index.ts
WorkScheduleBoard가 상태 훅과 연동해 표를 렌더링하고, shift 추가/수정/삭제/순서변경 패널과 배지/범례 컴포넌트를 제공합니다.
업무 스케줄 뷰 및 라우트
src/views/store-operation/work-schedule/*, src/app/workspaces/[workspaceId]/work-schedule/page.tsx, src/app/workspaces/[workspaceId]/layout.tsx
WorkScheduleView가 목업 데이터로 초기 스케줄을 만들어 보드를 렌더링하고, page/layout이 workspaceId를 전달합니다.
워크스페이스 공통 shell
src/widgets/workspace-shell/*
매장 운영용 네비게이션 항목과 Sidebar/Header/Shell 컴포넌트를 구현하고 공개 API로 export합니다.

Estimated code review effort: 3 (Moderate) | ~45 minutes

랜딩 이미지 경로 및 헤더 정리

Layer / File(s) Summary
랜딩 이미지/링크 및 PR 템플릿
src/widgets/landing/landing-header/..., src/widgets/landing/landing-hero/..., src/widgets/landing/landing-steps/..., .github/pull_request_template.md
LandingHeader 로고를 Image로 교체하고 CTA를 /signUp으로 변경하며, HeroSection/StepsSection 이미지 경로를 새 폴더 구조로 업데이트하고 PR 템플릿에 실행화면 항목을 추가합니다.

Estimated code review effort: 2 (Simple) | ~10 minutes

편집기 기본 설정

Layer / File(s) Summary
VS Code settings
.vscode/settings.json
저장 시 포맷팅, Prettier 기본 포매터, ESLint 자동 수정, import 모듈 지정자 설정을 추가합니다.

Estimated code review effort: 1 (Trivial) | ~2 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant WorkScheduleCell
  participant WorkScheduleBoard
  participant useWorkScheduleState

  User->>WorkScheduleCell: 근무 유형 셀 클릭
  WorkScheduleCell->>WorkScheduleBoard: onCycle 호출
  WorkScheduleBoard->>useWorkScheduleState: cycleCell(userId, weekday)
  useWorkScheduleState->>useWorkScheduleState: getNextWorkShiftOption으로 shiftOptionId 갱신
  useWorkScheduleState-->>WorkScheduleBoard: 갱신된 schedule 반환
  WorkScheduleBoard-->>User: 변경된 근무 유형 렌더링
Loading

Possibly related PRs

  • TeampleRun/syncly#3: 랜딩 헤더, 히어로, 스텝 섹션의 마크업과 리소스 경로를 함께 수정한 코드 변경이 겹칩니다.

Suggested reviewers: Kwon812, JiWoongE, 0011810

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning vscode 설정 파일과 LandingHeader의 CTA 이동 변경은 #11의 명시 범위를 벗어난 추가 수정입니다. 편집기 설정과 CTA 변경은 별도 PR로 분리하고, 이슈와 직접 관련된 변경만 남기세요.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 매장 운영 업무 스케줄 구현이라는 핵심 변경을 간결하게 요약합니다.
Description check ✅ Passed 필수 섹션을 대부분 채워 템플릿을 잘 따르며, 작업 내용과 결과, 테스트, 이슈도 포함되어 있습니다.
Linked Issues check ✅ Passed 워크스페이스 shell, 목업 스케줄 화면, 셀 순환 변경, 근무 유형 관리, 랜딩 리소스 정리가 #11 요구사항과 일치합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#11/store-operation-work-schedule

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

@seongjinss555
seongjinss555 force-pushed the feat/#11/store-operation-work-schedule branch from 597b332 to 250128f Compare July 7, 2026 01:46

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

🤖 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/entities/work-schedule/lib/get-next-work-shift-option.ts`:
- Around line 9-17: Add an empty-array guard in getNextWorkShiftOption so it
never returns shifts[nextIndex] when shifts is empty, since that breaks the
WorkShiftOption contract. Check shifts before using findIndex/currentIndex and
decide on a safe fallback or explicit error path, and apply the same fix to
getDefaultWorkShiftOption because it has the same empty-shifts issue. Use the
symbols getNextWorkShiftOption and getDefaultWorkShiftOption to locate both
helpers.

In `@src/entities/work-schedule/model/mock-work-schedule-config.ts`:
- Around line 22-29: The mock shift config uses an invalid endTime value of
24:00 for the shift-close entry, which the WorkShiftSettingsPanel time input
cannot display or edit. Update the shift-close object in
mock-work-schedule-config so endTime uses 23:59 or a proper midnight-end
representation consistent with the rest of the schedule model, keeping the
name/id values unchanged.

In `@src/features/manage-work-schedule/ui/WorkScheduleBoard.tsx`:
- Around line 97-104: The settings toggle button in WorkScheduleBoard is missing
the expanded/collapsed state for assistive tech. Update the button that uses
setIsSettingsOpen and controls isSettingsOpen to include aria-expanded bound to
that state, so screen readers can announce whether the settings panel is open or
closed.
- Line 25: `scheduleConfig` is initialized from the `config` prop in
`WorkScheduleBoard`, but it never updates when `config` changes later. Add
synchronization in `WorkScheduleBoard` (using the existing
`useState`/`useEffect` flow around `scheduleConfig` and `setScheduleConfig`) so
local state is refreshed whenever the `config` prop changes, keeping the board
in sync with updated parent data.
- Around line 127-179: The member-by-weekday rendering in WorkScheduleBoard is
doing repeated linear searches with schedule.find and scheduleConfig.shifts.find
inside members.map/weekdays.map, creating O(n·m) lookup cost. Pre-index schedule
by userId+weekday and shifts by id before the render loop, then have the cell
rendering read from those Maps instead of searching each time. If possible,
reuse the same indexed data in countSchedulesByWeekday and any related helpers
to avoid duplicate scans and keep the board render efficient.
- Around line 58-71: In handleDeleteShift inside WorkScheduleBoard, remove the
nested replaceShiftOption call from the setScheduleConfig updater so the updater
stays pure and side-effect free. Compute the updated shifts and fallback shift
first, then perform replaceShiftOption outside the updater (using the same
shiftId and fallbackShift.id) before or after setting the new schedule state.
Keep the logic localized to handleDeleteShift and any related
setScheduleConfig/replaceShiftOption usage so React retries do not re-run the
side effect.

In `@src/features/manage-work-schedule/ui/WorkScheduleCell.tsx`:
- Line 16: The aria-label on WorkScheduleCell is static and does not announce
the current shift name, so update the button/accessibility label in
WorkScheduleCell to include shift.name along with the change action. Use the
existing shift object in the cell rendering and replace the current fixed label
with a dynamic one that describes the current 근무 유형 and the click action in a
single accessible string.

In `@src/features/manage-work-schedule/ui/WorkShiftBadge.tsx`:
- Around line 4-11: The `colorClassName` mapping in `WorkShiftBadge` uses a much
lighter `text-slate-400` for the `slate` option, which makes that badge less
readable than the other variants. Update the `slate` entry to use a darker text
color consistent with the rest of the map, such as `text-slate-700`, and keep
the change localized to the `colorClassName` record.

In `@src/features/manage-work-schedule/ui/WorkShiftSettingsPanel.tsx`:
- Around line 45-50: `WorkShiftSettingsPanel`의 `config.shifts.map` 안에서 사용하는 고정폭
그리드가 가로 스크롤 없이 렌더링되어 좁은 화면에서 잘릴 수 있습니다. 각 shift row를 `overflow-x-auto`가 적용된 래퍼로
감싸고, 그 내부 그리드에 `min-w-[...]` 같은 최소 폭을 부여해 `WorkScheduleBoard`의 스크롤 처리 방식과 일관되게
수정하세요. `WorkShiftSettingsPanel`과 `config.shifts.map` 위치를 기준으로 적용하면 됩니다.

In `@src/widgets/landing/landing-header/ui/LandingHeader.tsx`:
- Line 37: The header logo in LandingHeader is rendered at the default 100x100
size without any display-size constraint and also lacks priority for an
above-the-fold image. Update the Image usage in LandingHeader to explicitly
limit the rendered size with a className or equivalent styling so it matches the
intended small logo appearance, and add priority so the logo is preloaded for
better LCP. Make the change at the Image element used for the Syncly logo.
- Line 47: The CTA in LandingHeader currently points to a non-existent /signUp
route, so update the href to a valid authentication entry point or add the
missing signup route. Check the LandingHeader component and any related
sign-up/navigation constants to ensure the link matches an existing page under
src/app and does not resolve to 404.

In `@src/widgets/landing/landing-hero/ui/HeroSection.tsx`:
- Around line 16-23: The landing hero currently gives priority to both the
background image and the hero mockup, which can cause competing preloads in
Next.js. Update the HeroSection image setup so only the true LCP candidate keeps
priority, and remove priority from the other Image instance (e.g., the
background or the hero-bg render in HeroSection) to avoid preloading contention.

In `@src/widgets/workspace-shell/model/workspace-navigation.ts`:
- Around line 29-52: The commented navigation presets in workspace-navigation.ts
use the same exported name, causing a future collision if both are enabled;
rename one of the placeholders so the “사이드 프로젝트” and “팀 프로젝트” variants have
distinct identifiers. Update the duplicate declaration around
sideProjectNavigationItems to a purpose-specific name, and consider removing or
moving these dead TODO blocks into a tracked task instead of leaving large
commented code in the file.
🪄 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: 3332362e-cd85-4b89-92c3-463f436847fe

📥 Commits

Reviewing files that changed from the base of the PR and between ccf6b02 and 250128f.

⛔ Files ignored due to path filters (7)
  • public/images/header/logo.svg is excluded by !**/*.svg, !public/**
  • public/images/landing/avatar-1.png is excluded by !**/*.png, !public/**
  • public/images/landing/avatar-2.png is excluded by !**/*.png, !public/**
  • public/images/landing/avatar-3.png is excluded by !**/*.png, !public/**
  • public/images/landing/bg.svg is excluded by !**/*.svg, !public/**
  • public/images/landing/hero-bg.svg is excluded by !**/*.svg, !public/**
  • public/landing/hero-bg.png is excluded by !**/*.png, !public/**
📒 Files selected for processing (36)
  • .github/pull_request_template.md
  • src/app/workspaces/[workspaceId]/layout.tsx
  • src/app/workspaces/[workspaceId]/work-schedule/page.tsx
  • src/entities/work-schedule/index.ts
  • src/entities/work-schedule/lib/count-schedules-by-weekday.ts
  • src/entities/work-schedule/lib/create-initial-work-schedule.ts
  • src/entities/work-schedule/lib/get-default-work-shift-option.ts
  • src/entities/work-schedule/lib/get-next-work-shift-option.ts
  • src/entities/work-schedule/lib/get-work-members-by-weekday.ts
  • src/entities/work-schedule/model/mock-work-schedule-config.ts
  • src/entities/work-schedule/model/weekdays.ts
  • src/entities/work-schedule/model/work-schedule.types.ts
  • src/entities/workspace-member/index.ts
  • src/entities/workspace-member/model/mock-current-workspace-member.ts
  • src/entities/workspace-member/model/mock-workspace-members.ts
  • src/entities/workspace-member/model/workspace-member.types.ts
  • src/entities/workspace/index.ts
  • src/entities/workspace/model/mock-workspace.ts
  • src/entities/workspace/model/workspace.types.ts
  • src/features/manage-work-schedule/index.ts
  • src/features/manage-work-schedule/model/use-work-schedule-state.ts
  • src/features/manage-work-schedule/ui/WorkScheduleBoard.tsx
  • src/features/manage-work-schedule/ui/WorkScheduleCell.tsx
  • src/features/manage-work-schedule/ui/WorkShiftBadge.tsx
  • src/features/manage-work-schedule/ui/WorkShiftLegend.tsx
  • src/features/manage-work-schedule/ui/WorkShiftSettingsPanel.tsx
  • src/views/store-operation/work-schedule/index.ts
  • src/views/store-operation/work-schedule/ui/WorkScheduleView.tsx
  • src/widgets/landing/landing-header/ui/LandingHeader.tsx
  • src/widgets/landing/landing-hero/ui/HeroSection.tsx
  • src/widgets/landing/landing-steps/ui/StepsSection.tsx
  • src/widgets/workspace-shell/index.ts
  • src/widgets/workspace-shell/model/workspace-navigation.ts
  • src/widgets/workspace-shell/ui/WorkspaceHeader.tsx
  • src/widgets/workspace-shell/ui/WorkspaceShell.tsx
  • src/widgets/workspace-shell/ui/WorkspaceSidebar.tsx

Comment on lines +9 to +17
export function getNextWorkShiftOption({
shifts,
currentShiftOptionId,
}: GetNextWorkShiftOptionParams): WorkShiftOption {
const currentIndex = shifts.findIndex((shift) => shift.id === currentShiftOptionId);
const nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % shifts.length;

return shifts[nextIndex];
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== target file ==\n'
cat -n src/entities/work-schedule/lib/get-next-work-shift-option.ts

printf '\n== related helper search ==\n'
rg -n "getDefaultWorkShiftOption|type WorkShiftOption|interface WorkShiftOption|WorkShiftOption" src/entities/work-schedule -S

printf '\n== related files list ==\n'
fd -a "work-shift-option|work-schedule" src/entities/work-schedule

Repository: TeampleRun/syncly

Length of output: 2867


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== work-schedule.types ==\n'
cat -n src/entities/work-schedule/model/work-schedule.types.ts

printf '\n== get-default-work-shift-option ==\n'
cat -n src/entities/work-schedule/lib/get-default-work-shift-option.ts

printf '\n== create-initial-work-schedule ==\n'
cat -n src/entities/work-schedule/lib/create-initial-work-schedule.ts

Repository: TeampleRun/syncly

Length of output: 2375


shifts 가드를 추가하세요. shifts가 비어 있으면 shifts[nextIndex]undefined가 되어 WorkShiftOption 반환 계약을 깨뜨립니다. getDefaultWorkShiftOption도 같은 문제가 있으니 두 헬퍼 모두 빈 배열을 먼저 처리해야 합니다.

🤖 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/entities/work-schedule/lib/get-next-work-shift-option.ts` around lines 9
- 17, Add an empty-array guard in getNextWorkShiftOption so it never returns
shifts[nextIndex] when shifts is empty, since that breaks the WorkShiftOption
contract. Check shifts before using findIndex/currentIndex and decide on a safe
fallback or explicit error path, and apply the same fix to
getDefaultWorkShiftOption because it has the same empty-shifts issue. Use the
symbols getNextWorkShiftOption and getDefaultWorkShiftOption to locate both
helpers.

Comment on lines +22 to +29
{
id: 'shift-close',
name: '마감',
startTime: '19:00',
endTime: '24:00',
color: 'amber',
isOff: 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.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

date-fns parse "24:00" invalid time HH:mm

💡 Result:

In date-fns, the handling of "24:00" depends on the parsing function used [1][2][3]. For the parse function (which requires a format string), the "HH" token supports values from 00 to 23 [4][5]. Consequently, passing "24:00" with a format string like "HH:mm" will result in an "Invalid Date" because 24 is outside the supported range for that token [4][5]. For the parseISO function, the behavior is different because it is designed to follow the ISO 8601 standard [2][3]. According to ISO 8601, "24:00" is a valid representation of the end of a calendar day (equivalent to 00:00 of the following day) [2]. date-fns explicitly includes logic in parseISO to handle "24:00" correctly by treating it as midnight of the next day [2][3][6]. In summary: - parse(..., "HH:mm",...) treats 24 as invalid [4][5]. - parseISO(...) accepts "24:00" as a valid ISO 8601 time string [2][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## file outline\n'
ast-grep outline src/entities/work-schedule/model/mock-work-schedule-config.ts --view expanded || true

printf '\n## file contents\n'
cat -n src/entities/work-schedule/model/mock-work-schedule-config.ts | sed -n '1,120p'

printf '\n## search for endTime usage and time parsing\n'
rg -n "endTime|startTime|24:00|parseISO|parse\\(|format\\(|HH:mm|time" src | sed -n '1,220p'

Repository: TeampleRun/syncly

Length of output: 4027


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## related files\n'
fd -a ".*work-schedule.*|.*schedule.*" src || true

Repository: TeampleRun/syncly

Length of output: 1043


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## related files by exact name\n'
git ls-files 'src/**' | rg 'work-schedule|schedule' || true

Repository: TeampleRun/syncly

Length of output: 1270


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## work-schedule types\n'
cat -n src/entities/work-schedule/model/work-schedule.types.ts | sed -n '1,120p'

printf '\n## settings panel\n'
cat -n src/features/manage-work-schedule/ui/WorkShiftSettingsPanel.tsx | sed -n '1,220p'

printf '\n## board and legend\n'
cat -n src/features/manage-work-schedule/ui/WorkScheduleBoard.tsx | sed -n '1,220p'
printf '\n---\n'
cat -n src/features/manage-work-schedule/ui/WorkShiftLegend.tsx | sed -n '1,220p'

printf '\n## create initial config\n'
cat -n src/entities/work-schedule/lib/create-initial-work-schedule.ts | sed -n '1,220p'

printf '\n## find any time validation/parsing libraries around schedule\n'
rg -n "parseISO|parse\\(|isValid|HH:mm|type=\"time\"|endTime|startTime" src/entities src/features src/views | sed -n '1,240p'

Repository: TeampleRun/syncly

Length of output: 21847


🌐 Web query:

HTML input type=time 24:00 validity

💡 Result:

In HTML, the value 24:00 is not a valid time string for an element [1]. According to the HTML specification, a valid time string uses a 24-hour clock where the hour must be in the range 00 through 23 [1]. The value 00:00 represents midnight at the beginning of a day, and there is no representation of "end of day" as 24:00 within the standard time input format [1][2]. If you attempt to set the value attribute or the min/max attributes to 24:00, it will not be recognized as a valid time [3][4]. Consequently, if a browser performs client-side validation on an , it will treat 24:00 as invalid [4]. To handle intervals that include midnight or the end of a day, developers typically use a checkbox to indicate "entire day" or alternative input methods (such as text inputs with custom validation) [2][5].

Citations:


24:00 대신 23:59 또는 자정 종료 표현을 써 주세요.
WorkShiftSettingsPaneltype="time" 입력은 24:00을 허용하지 않아, 이 값은 수정 UI에서 제대로 표시·편집되지 않습니다.

🤖 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/entities/work-schedule/model/mock-work-schedule-config.ts` around lines
22 - 29, The mock shift config uses an invalid endTime value of 24:00 for the
shift-close entry, which the WorkShiftSettingsPanel time input cannot display or
edit. Update the shift-close object in mock-work-schedule-config so endTime uses
23:59 or a proper midnight-end representation consistent with the rest of the
schedule model, keeping the name/id values unchanged.

}

export function WorkScheduleBoard({ members, config, initialSchedule }: WorkScheduleBoardProps) {
const [scheduleConfig, setScheduleConfig] = useState(config);

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

scheduleConfigconfig prop 변경에 동기화되지 않습니다.

useState(config)는 최초 마운트 시점의 config만 반영하고, 이후 부모가 config prop을 갱신해도 로컬 상태는 그대로입니다. 현재는 mock 데이터라 문제가 없지만, PR 목표에 언급된 "Supabase 연동으로 확장"을 고려하면 서버에서 새 config를 내려줄 때 화면이 갱신되지 않는 문제로 이어질 수 있습니다.

🤖 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/features/manage-work-schedule/ui/WorkScheduleBoard.tsx` at line 25,
`scheduleConfig` is initialized from the `config` prop in `WorkScheduleBoard`,
but it never updates when `config` changes later. Add synchronization in
`WorkScheduleBoard` (using the existing `useState`/`useEffect` flow around
`scheduleConfig` and `setScheduleConfig`) so local state is refreshed whenever
the `config` prop changes, keeping the board in sync with updated parent data.

Comment thread src/features/manage-work-schedule/ui/WorkScheduleBoard.tsx
Comment on lines +97 to +104
<button
type="button"
onClick={() => setIsSettingsOpen((current) => !current)}
className="h-9 rounded-lg border border-slate-200 bg-white px-3 text-sm font-semibold text-slate-700 shadow-sm hover:bg-slate-50"
>
근무 유형 설정
</button>
</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

설정 토글 버튼에 aria-expanded 누락.

isSettingsOpen 상태로 패널을 여닫는데, 버튼에 aria-expanded={isSettingsOpen}이 없어 스크린 리더 사용자가 패널의 펼침/접힘 상태를 알기 어렵습니다.

🤖 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/features/manage-work-schedule/ui/WorkScheduleBoard.tsx` around lines 97 -
104, The settings toggle button in WorkScheduleBoard is missing the
expanded/collapsed state for assistive tech. Update the button that uses
setIsSettingsOpen and controls isSettingsOpen to include aria-expanded bound to
that state, so screen readers can announce whether the settings panel is open or
closed.

Comment on lines +45 to +50
<div className="space-y-3">
{config.shifts.map((shift, index) => (
<div
key={shift.id}
className="grid grid-cols-[260px_170px_170px_140px_90px_190px] items-center gap-3 rounded-xl border border-slate-100 bg-slate-50 p-3"
>

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 | 🟡 Minor | ⚡ Quick win

고정폭 그리드에 가로 스크롤 래퍼가 없습니다.

각 행이 grid-cols-[260px_170px_170px_140px_90px_190px]로 최소 폭이 1000px 이상 필요한데, WorkScheduleBoard의 일정표(116-117행, overflow-x-auto+min-w-[900px])와 달리 이 패널은 스크롤 컨테이너로 감싸져 있지 않습니다. 좁은 뷰포트에서 좌우가 잘리거나 페이지 전체 가로 스크롤을 유발할 수 있습니다.

🤖 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/features/manage-work-schedule/ui/WorkShiftSettingsPanel.tsx` around lines
45 - 50, `WorkShiftSettingsPanel`의 `config.shifts.map` 안에서 사용하는 고정폭 그리드가 가로 스크롤
없이 렌더링되어 좁은 화면에서 잘릴 수 있습니다. 각 shift row를 `overflow-x-auto`가 적용된 래퍼로 감싸고, 그 내부
그리드에 `min-w-[...]` 같은 최소 폭을 부여해 `WorkScheduleBoard`의 스크롤 처리 방식과 일관되게 수정하세요.
`WorkShiftSettingsPanel`과 `config.shifts.map` 위치를 기준으로 적용하면 됩니다.

<Boxes className="size-4 text-white" />
</div>
<span className="text-brand-ink text-lg font-extrabold tracking-[-0.45px]">Syncly</span>
<Image src="/images/header/logo.svg" alt="Syncly" width={100} height={100} />

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 | 🔵 Trivial | ⚡ Quick win

로고 이미지 크기 제어 및 priority 누락

width={100} height={100}만 지정되고 별도 className/스타일로 렌더 크기를 제한하지 않아, 헤더 로고가 디자인 이미지(작은 크기)보다 훨씬 크게 표시될 위험이 있습니다. 또한 헤더 로고는 above-the-fold 요소인데 priority가 지정되지 않아 LCP 최적화 혜택을 받지 못합니다.

🖼️ 제안 수정
-          <Image src="/images/header/logo.svg" alt="Syncly" width={100} height={100} />
+          <Image
+            src="/images/header/logo.svg"
+            alt="Syncly"
+            width={100}
+            height={100}
+            priority
+            className="h-8 w-auto"
+          />
📝 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.

Suggested change
<Image src="/images/header/logo.svg" alt="Syncly" width={100} height={100} />
<Image
src="/images/header/logo.svg"
alt="Syncly"
width={100}
height={100}
priority
className="h-8 w-auto"
/>
🤖 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/widgets/landing/landing-header/ui/LandingHeader.tsx` at line 37, The
header logo in LandingHeader is rendered at the default 100x100 size without any
display-size constraint and also lacks priority for an above-the-fold image.
Update the Image usage in LandingHeader to explicitly limit the rendered size
with a className or equivalent styling so it matches the intended small logo
appearance, and add priority so the logo is preloaded for better LCP. Make the
change at the Image element used for the Syncly logo.

</Link>
<Link
href="/login"
href="/signUp"

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 | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: signUp / sign-up 라우트 폴더 존재 여부 확인
fd -i 'page.tsx' src/app | rg -i 'sign'

Repository: TeampleRun/syncly

Length of output: 155


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== app routes =="
find src/app -maxdepth 3 -name 'page.tsx' | sort

echo
echo "== sign related paths =="
find src/app -maxdepth 4 \( -iname '*sign*' -o -iname '*login*' -o -iname '*work-schedule*' \) | sort

echo
echo "== LandingHeader =="
sed -n '1,140p' src/widgets/landing/landing-header/ui/LandingHeader.tsx

echo
echo "== signUp link usages =="
rg -n 'href="/signUp"|/signUp|sign-up' src

Repository: TeampleRun/syncly

Length of output: 2297


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== route tree under src/app =="
find src/app -maxdepth 4 -type f | sort

echo
echo "== any sign/signup route directories =="
find src/app -maxdepth 5 -type d \( -iname '*sign*' -o -iname '*login*' -o -iname '*auth*' \) | sort

echo
echo "== route-like link usages =="
rg -n 'href="/[^"]+"' src/widgets src/views src/app | head -n 200

Repository: TeampleRun/syncly

Length of output: 762


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== all signUp-like occurrences =="
rg -n -i 'signUp|sign-up' .

echo
echo "== all app route segments =="
find src/app -mindepth 1 -maxdepth 3 \( -type d -o -type f \) | sort

Repository: TeampleRun/syncly

Length of output: 526


/signUp 링크가 현재 라우트와 맞지 않습니다

src/appsignUp/sign-up 페이지가 없어 이 CTA는 404로 이어집니다. 실제 회원가입 라우트를 추가하거나 기존 인증 진입점으로 연결해 주세요.

🤖 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/widgets/landing/landing-header/ui/LandingHeader.tsx` at line 47, The CTA
in LandingHeader currently points to a non-existent /signUp route, so update the
href to a valid authentication entry point or add the missing signup route.
Check the LandingHeader component and any related sign-up/navigation constants
to ensure the link matches an existing page under src/app and does not resolve
to 404.

Comment on lines +16 to +23
<Image
src="/images/landing/bg.svg"
alt=""
fill
priority
sizes="100vw"
className="pointer-events-none absolute inset-0 z-0 object-cover object-center"
/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

두 이미지 모두 priority 지정 시 LCP 프리로드 경합 가능성

배경 레이어(bg.svg)와 히어로 목업 이미지(hero-bg.svg) 모두 priority가 설정되어 있습니다. Next.js는 priority 이미지를 프리로드하는데, 다수의 이미지에 동시에 적용하면 실제 LCP 요소(주로 배경 또는 텍스트)의 로딩 우선순위가 분산되어 오히려 성능이 저하될 수 있습니다. 두 이미지 중 실제 LCP 후보(주로 배경)에만 priority를 유지하는 것을 검토해 주세요.

Also applies to: 30-37

🤖 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/widgets/landing/landing-hero/ui/HeroSection.tsx` around lines 16 - 23,
The landing hero currently gives priority to both the background image and the
hero mockup, which can cause competing preloads in Next.js. Update the
HeroSection image setup so only the true LCP candidate keeps priority, and
remove priority from the other Image instance (e.g., the background or the
hero-bg render in HeroSection) to avoid preloading contention.

Comment on lines +29 to +52
// TODO: 사이드 프로젝트 도메인을 만들 때 아래 메뉴를 별도 purpose 전용 사이드바로 연결합니다.
// export const sideProjectNavigationItems: WorkspaceNavigationItem[] = [
// { label: '대시보드', href: 'dashboard', icon: LayoutDashboard },
// { label: '스프린트 보드', href: 'sprint-board', icon: Rocket },
// { label: '캘린더', href: 'calendar', icon: Calendar },
// { label: '회의록', href: 'meeting-notes', icon: FileText },
// { label: '자료실', href: 'files', icon: FileBox },
// { label: '채팅', href: 'chat', icon: MessageSquare },
// { label: '진행률 차트', href: 'progress-chart', icon: BarChart3 },
// { label: '설정', href: 'settings', icon: Settings },
// ];

// TODO: 팀 프로젝트 도메인을 만들 때 아래 메뉴를 별도 purpose 전용 사이드바로 연결합니다.
// export const sideProjectNavigationItems: WorkspaceNavigationItem[] = [
// { label: '대시보드', href: 'dashboard', icon: LayoutDashboard },
// { label: '프로젝트 관리', href: 'sprint-board', icon: Rocket },
// { label: '캘린더', href: 'calendar', icon: Calendar },
// { label: '공지', href: 'notices', icon: Bell },
// { label: '회의록', href: 'meeting-notes', icon: FileText },
// { label: '자료실', href: 'files', icon: FileBox },
// { label: '채팅', href: 'chat', icon: MessageSquare },
// { label: '진행률 차트', href: 'progress-chart', icon: BarChart3 },
// { label: '설정', href: 'settings', icon: Settings },
// ];

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

주석 처리된 코드 블록 내 변수명 중복.

두 블록 모두 sideProjectNavigationItems로 선언되어 있습니다. 주석에 따르면 첫 번째는 "사이드 프로젝트", 두 번째는 "팀 프로젝트" 도메인용이므로 이름이 달라야 합니다. 현재는 죽은 코드라 영향이 없지만, 나중에 그대로 주석 해제하면 이름 충돌이 발생합니다. 또한 사용하지 않는 대량의 주석 코드는 별도 이슈/TODO로 추적하고 파일에서 제거하는 것을 권장합니다.

♻️ 제안: 두 번째 블록 변수명 수정
 // TODO: 팀 프로젝트 도메인을 만들 때 아래 메뉴를 별도 purpose 전용 사이드바로 연결합니다.
-// export const sideProjectNavigationItems: WorkspaceNavigationItem[] = [
+// export const teamProjectNavigationItems: WorkspaceNavigationItem[] = [
📝 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.

Suggested change
// TODO: 사이드 프로젝트 도메인을 만들 때 아래 메뉴를 별도 purpose 전용 사이드바로 연결합니다.
// export const sideProjectNavigationItems: WorkspaceNavigationItem[] = [
// { label: '대시보드', href: 'dashboard', icon: LayoutDashboard },
// { label: '스프린트 보드', href: 'sprint-board', icon: Rocket },
// { label: '캘린더', href: 'calendar', icon: Calendar },
// { label: '회의록', href: 'meeting-notes', icon: FileText },
// { label: '자료실', href: 'files', icon: FileBox },
// { label: '채팅', href: 'chat', icon: MessageSquare },
// { label: '진행률 차트', href: 'progress-chart', icon: BarChart3 },
// { label: '설정', href: 'settings', icon: Settings },
// ];
// TODO: 팀 프로젝트 도메인을 만들 때 아래 메뉴를 별도 purpose 전용 사이드바로 연결합니다.
// export const sideProjectNavigationItems: WorkspaceNavigationItem[] = [
// { label: '대시보드', href: 'dashboard', icon: LayoutDashboard },
// { label: '프로젝트 관리', href: 'sprint-board', icon: Rocket },
// { label: '캘린더', href: 'calendar', icon: Calendar },
// { label: '공지', href: 'notices', icon: Bell },
// { label: '회의록', href: 'meeting-notes', icon: FileText },
// { label: '자료실', href: 'files', icon: FileBox },
// { label: '채팅', href: 'chat', icon: MessageSquare },
// { label: '진행률 차트', href: 'progress-chart', icon: BarChart3 },
// { label: '설정', href: 'settings', icon: Settings },
// ];
// TODO: 사이드 프로젝트 도메인을 만들 때 아래 메뉴를 별도 purpose 전용 사이드바로 연결합니다.
// export const sideProjectNavigationItems: WorkspaceNavigationItem[] = [
// { label: '대시보드', href: 'dashboard', icon: LayoutDashboard },
// { label: '스프린트 보드', href: 'sprint-board', icon: Rocket },
// { label: '캘린더', href: 'calendar', icon: Calendar },
// { label: '회의록', href: 'meeting-notes', icon: FileText },
// { label: '자료실', href: 'files', icon: FileBox },
// { label: '채팅', href: 'chat', icon: MessageSquare },
// { label: '진행률 차트', href: 'progress-chart', icon: BarChart3 },
// { label: '설정', href: 'settings', icon: Settings },
// ];
// TODO: 팀 프로젝트 도메인을 만들 때 아래 메뉴를 별도 purpose 전용 사이드바로 연결합니다.
// export const teamProjectNavigationItems: WorkspaceNavigationItem[] = [
// { label: '대시보드', href: 'dashboard', icon: LayoutDashboard },
// { label: '프로젝트 관리', href: 'sprint-board', icon: Rocket },
// { label: '캘린더', href: 'calendar', icon: Calendar },
// { label: '공지', href: 'notices', icon: Bell },
// { label: '회의록', href: 'meeting-notes', icon: FileText },
// { label: '자료실', href: 'files', icon: FileBox },
// { label: '채팅', href: 'chat', icon: MessageSquare },
// { label: '진행률 차트', href: 'progress-chart', icon: BarChart3 },
// { label: '설정', href: 'settings', icon: Settings },
// ];
🤖 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/widgets/workspace-shell/model/workspace-navigation.ts` around lines 29 -
52, The commented navigation presets in workspace-navigation.ts use the same
exported name, causing a future collision if both are enabled; rename one of the
placeholders so the “사이드 프로젝트” and “팀 프로젝트” variants have distinct identifiers.
Update the duplicate declaration around sideProjectNavigationItems to a
purpose-specific name, and consider removing or moving these dead TODO blocks
into a tracked task instead of leaving large commented code in the file.

@JiWoongE JiWoongE 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.

수고하셨습니다~

@Kwon812 Kwon812 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.

고생하셨습니다~

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.

feat: 매장 운영 업무 스케줄 구현

3 participants