feat: 매장 운영 업무 스케줄 구현 (#11) - #12
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthrough워크스페이스 공통 shell과 업무 스케줄 화면을 추가하고, 근무 일정 도메인 타입/목업/유틸/상태/UI를 연결했습니다. 또한 랜딩 헤더와 히어로/스텝 이미지 경로를 정리하고 PR 템플릿과 VS Code 설정을 갱신했습니다. Changes워크스페이스 shell 및 업무 스케줄 기능
Estimated code review effort: 3 (Moderate) | ~45 minutes 랜딩 이미지 경로 및 헤더 정리
Estimated code review effort: 2 (Simple) | ~10 minutes 편집기 기본 설정
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: 변경된 근무 유형 렌더링
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
597b332 to
250128f
Compare
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (7)
public/images/header/logo.svgis excluded by!**/*.svg,!public/**public/images/landing/avatar-1.pngis excluded by!**/*.png,!public/**public/images/landing/avatar-2.pngis excluded by!**/*.png,!public/**public/images/landing/avatar-3.pngis excluded by!**/*.png,!public/**public/images/landing/bg.svgis excluded by!**/*.svg,!public/**public/images/landing/hero-bg.svgis excluded by!**/*.svg,!public/**public/landing/hero-bg.pngis excluded by!**/*.png,!public/**
📒 Files selected for processing (36)
.github/pull_request_template.mdsrc/app/workspaces/[workspaceId]/layout.tsxsrc/app/workspaces/[workspaceId]/work-schedule/page.tsxsrc/entities/work-schedule/index.tssrc/entities/work-schedule/lib/count-schedules-by-weekday.tssrc/entities/work-schedule/lib/create-initial-work-schedule.tssrc/entities/work-schedule/lib/get-default-work-shift-option.tssrc/entities/work-schedule/lib/get-next-work-shift-option.tssrc/entities/work-schedule/lib/get-work-members-by-weekday.tssrc/entities/work-schedule/model/mock-work-schedule-config.tssrc/entities/work-schedule/model/weekdays.tssrc/entities/work-schedule/model/work-schedule.types.tssrc/entities/workspace-member/index.tssrc/entities/workspace-member/model/mock-current-workspace-member.tssrc/entities/workspace-member/model/mock-workspace-members.tssrc/entities/workspace-member/model/workspace-member.types.tssrc/entities/workspace/index.tssrc/entities/workspace/model/mock-workspace.tssrc/entities/workspace/model/workspace.types.tssrc/features/manage-work-schedule/index.tssrc/features/manage-work-schedule/model/use-work-schedule-state.tssrc/features/manage-work-schedule/ui/WorkScheduleBoard.tsxsrc/features/manage-work-schedule/ui/WorkScheduleCell.tsxsrc/features/manage-work-schedule/ui/WorkShiftBadge.tsxsrc/features/manage-work-schedule/ui/WorkShiftLegend.tsxsrc/features/manage-work-schedule/ui/WorkShiftSettingsPanel.tsxsrc/views/store-operation/work-schedule/index.tssrc/views/store-operation/work-schedule/ui/WorkScheduleView.tsxsrc/widgets/landing/landing-header/ui/LandingHeader.tsxsrc/widgets/landing/landing-hero/ui/HeroSection.tsxsrc/widgets/landing/landing-steps/ui/StepsSection.tsxsrc/widgets/workspace-shell/index.tssrc/widgets/workspace-shell/model/workspace-navigation.tssrc/widgets/workspace-shell/ui/WorkspaceHeader.tsxsrc/widgets/workspace-shell/ui/WorkspaceShell.tsxsrc/widgets/workspace-shell/ui/WorkspaceSidebar.tsx
| 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]; | ||
| } |
There was a problem hiding this comment.
🩺 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-scheduleRepository: 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.tsRepository: 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.
| { | ||
| id: 'shift-close', | ||
| name: '마감', | ||
| startTime: '19:00', | ||
| endTime: '24:00', | ||
| color: 'amber', | ||
| isOff: false, | ||
| }, |
There was a problem hiding this comment.
🗄️ 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:
- 1: Wrong parsed date with time at 24:00:00 date-fns/date-fns#1228
- 2: Fix incorrect parsing of 24:00 in parseISO (closes #1228) date-fns/date-fns#1229
- 3: https://github.com/date-fns/date-fns/blob/main/src/parseISO/index.ts
- 4: https://docs.w3cub.com/date_fns/parse.html
- 5: https://docs.w3cub.com/date_fns/parse
- 6: fix(parseISO): reject float hours above 24 as Invalid Date date-fns/date-fns#4219
🏁 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 || trueRepository: 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' || trueRepository: 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:
- 1: https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Date_and_time_formats
- 2: https://stackoverflow.com/questions/28826946/how-to-enter-2400-in-a-html5-time-input
- 3: https://stackoverflow.com/questions/39808741/html-input-time-max-at-midnight
- 4: https://dev.w3.org/html5/spec-LC/states-of-the-type-attribute.html
- 5: https://stackoverflow.com/questions/49275567/html-time-input-validation-using-regex-dont-allow-0000-but-want-allow-2400
24:00 대신 23:59 또는 자정 종료 표현을 써 주세요.
WorkShiftSettingsPanel의 type="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); |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
scheduleConfig가 config 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.
| <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> |
There was a problem hiding this comment.
🩺 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.
| <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" | ||
| > |
There was a problem hiding this comment.
📐 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} /> |
There was a problem hiding this comment.
🎯 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.
| <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" |
There was a problem hiding this comment.
🎯 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' srcRepository: 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 200Repository: 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 \) | sortRepository: TeampleRun/syncly
Length of output: 526
/signUp 링크가 현재 라우트와 맞지 않습니다
src/app에 signUp/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.
| <Image | ||
| src="/images/landing/bg.svg" | ||
| alt="" | ||
| fill | ||
| priority | ||
| sizes="100vw" | ||
| className="pointer-events-none absolute inset-0 z-0 object-cover object-center" | ||
| /> |
There was a problem hiding this comment.
🚀 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.
| // 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 }, | ||
| // ]; |
There was a problem hiding this comment.
📐 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.
| // 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.
Pull Request
작업 내용
작업 결과
/workspaces/test/work-schedule에서 매장 운영 업무 스케줄 화면 확인 가능/images/landing/bg.svg배경 적용변경 사항
Added
widgets/workspace-shell공통 워크스페이스 shellviews/store-operation/work-schedule업무 스케줄 viewfeatures/manage-work-schedule업무 스케줄 관리 featureentities/work-schedule,entities/workspace,entities/workspace-membermock entitypublic/images/header,public/images/landing이미지 리소스Changed
/images/landing/*기준으로 정리Fixed
src/shared/lib/utils경로 기준으로 import 정리실행화면
테스트
npm run lintnpm run typechecknpm run build리뷰 체크리스트
feature/*->develop, 배포 시develop또는release/*->main)Type/#issue-number/description형식을 따릅니다.console.log, 주석, 임시 코드를 제거했습니다.리뷰 요청사항
app,views,features,entities,widgets)가 적절한지 확인 부탁드립니다.관련 이슈
Closes #11
Refs #2
Summary by CodeRabbit