Skip to content
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9d1b136
design: 텍스트가 있는 경우 border색 변경
Jan 6, 2026
6fabbb4
feat: 공유일기 댓글창에서 인풋필드 클릭시 댓글 페이지로 이동
Jan 6, 2026
506304b
feat: 공유일기 댓글 목데이터 추가
Jan 6, 2026
afaa8de
feat: 인풋필드 클릭 props추가
Jan 6, 2026
91ee31c
rename: chat대신 comment로 파일명 변경
Jan 6, 2026
99a9353
feat: 공유일기 댓글 타입 추가
Jan 6, 2026
528d81f
feat: 날짜 년월일로 포매팅되는 함수추가
Jan 6, 2026
10d3ab3
feat: 댓글리스트 영역 컴포넌트 분리 후, 댓글 데이터 가져오기 로직 추가
Jan 6, 2026
cf13f28
feat: 댓글 전송클릭 시, 댓글 리스트에 반영
Jan 6, 2026
10d1bd5
refactor: 인풋필드 onClick 라우팅이 아닌 onFocus로 페이지가 아닌경우에 라우팅되는것으로 변경
Jan 6, 2026
48dd0e8
chore: toss use-funnel삭제
Jan 6, 2026
2a675b1
Merge remote-tracking branch 'origin' into feat/11-shared-diary
Jan 6, 2026
e763d5e
feat: 댓글이 많을 경우에 스크롤영역 ui수정
Jan 6, 2026
272b1a2
design: 하루한컷 업로드 백헤더 text-white추가
Jan 6, 2026
8f7b486
feat: 요일 배열 추가
Jan 6, 2026
3f99a50
feat: 과거, 현재, 미래 요일을 알아내는 유틸 추가
Jan 6, 2026
5e6ef76
rename: 하루기록바뀐 링크 수정
Jan 6, 2026
95f3e7a
feat: 하루한컷 미션진행현황 피드 컴포넌트 추가
Jan 6, 2026
57e3a87
feat: 하루한컷 업로드 페이지로 이동
Jan 6, 2026
889e0aa
chore: 하루한컷업로드페이지 개발되는 동안 my_id 매직넘버 하루한컷 안올린 것으로 변경
Jan 6, 2026
3b8a5a2
feat: gradient-orange 추가 및 중복되는 코드 제거
Jan 6, 2026
8a563dd
feat: 미션카드 상태에 따른 타이틀텍스트 및 버튼 텍스트 상수처리
Jan 6, 2026
ec28cdb
feat: 미션카드 ui 추가
Jan 6, 2026
0494d6c
design: 미션텍스트와 버튼 간격 수정
Jan 6, 2026
7dfdcac
feat: 미션카드 종류의 아이콘 및 상수 추가
Jan 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
]
},
"dependencies": {
"@toss/use-funnel": "^1.4.2",
"@use-funnel/next": "^0.0.22",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

@use-funnel/next version 0.0.22 compatibility Next.js 16 React 19

💡 Result:

Short answer: No — there’s no published @use-funnel/next v0.0.22 on npm, and the package’s docs list supported React as ^16.8, so the current release is not explicitly compatible with React 19 / Next.js 16. [1][2][3]

Sources:

  • @use-funnel/next npm (latest published 0.0.21). [1]
  • @use-funnel docs — Supported React Version: ^16.8. [2]
  • Package index (Socket/npm metadata) showing latest version 0.0.21 (no 0.0.22). [3]

If you want, I can: 1) check the package’s GitHub for any unreleased v0.0.22/branches or compatibility notes, or 2) suggest a migration/workaround (shim/adapter) for using it with Next.js 16 + React 19. Which would you prefer?


🏁 Script executed:

# Check package.json for Next.js and React versions
find . -name "package.json" -type f | head -1 | xargs cat | head -50

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 192


🌐 Web query:

@toss/use-funnel vs @use-funnel/next differences API migration

💡 Result:

Summary of key differences and migration steps from @toss/use-funnel → @use-funnel (/@use-funnel/next):

What changed (high-level)

  • New package family: @use-funnel (core + adapters like @use-funnel/next). @toss/use-funnel is deprecated. [1][2]
  • No hard Next.js dependency: core is router-agnostic and supports Next App/Pages, react-router, React Native, custom routers via adapters. [3]
  • Stronger typing for per-step context: steps are defined with createFunnelSteps() so each step can have its own state type. [1][6]
  • API shape: useFunnel now takes an options object and returns a funnel controller (funnel.step, funnel.context, funnel.history, funnel.Render) rather than the old tuple + /<Funnel.Step/> components. [1][6]
  • History + state are integrated: history snapshots include state so back/forward restores prior step-specific state. [4]

Concrete migration mappings

  • Step list → createFunnelSteps

    • Before: useFunnel(['A','B'] as const)
    • After: const steps = createFunnelSteps().extends(['A','B']).build(); useFunnel({ steps, initial: { step: 'A', context: {} } }). [1]
    • This lets you pass generics for per-step context (replaces withState()). [1]
  • Components → render by step or funnel.Render

    • Before: returned with <Funnel.Step name="A">…
    • After: switch on funnel.step and render, or use funnel.Render component. [1]
  • withState() → type generics on createFunnelSteps/useFunnel

    • Before: .withState<...> HOC pattern
    • After: define state types with createFunnelSteps generics and supply initial context in useFunnel options. [1]
  • onStepChange callback → observe funnel.step

    • Before: onStepChange option passed to useFunnel
    • After: watch funnel.step in useEffect (or use funnel history hooks). [1]
  • Initial step via router query

    • Example: read router.query and pass as initial.step when calling useFunnel (adapter-specific examples in the Next adapter docs). [1]

Migration checklist (minimal)

  1. Install @use-funnel/next (or appropriate adapter). [1]
  2. Replace create of steps with createFunnelSteps().extends(...).build(). Add generics for per-step context if needed. [1]
  3. Replace useFunnel([...]) calls with useFunnel({ id?, steps, initial: { step, context } }). [6]
  4. Replace /<Funnel.Step> UI with switch(funnel.step) or funnel.Render. [1]
  5. Move any withState logic into step-specific types/initial context. [1]
  6. Replace onStepChange option with useEffect(() => {/* react to funnel.step */}, [funnel.step]). [1]
  7. Verify history behavior (back/forward) works as expected; adjust router adapter/custom router if you used non-Next routing. [4][3]

References

  • Migration guide / docs (examples & breaking changes). [1]
  • useFunnel API docs (options/returned object). [6]
  • Background / rationale and router-agnostic redesign (Toss engineering post). [3]
  • Features: history + state integration description. [4]

If you want, tell me one example file from your repo (old code) and I’ll produce the exact migrated code to @use-funnel/next.


버전 번호 확인 및 호환성 검증 필수.

npm에 게시된 @use-funnel/next의 최신 버전은 v0.0.21이며, v0.0.22는 존재하지 않습니다. 또한 공식 문서에서 지원하는 React 버전은 ^16.8로 명시되어 있어 React 19와의 명시적 호환성이 보장되지 않습니다.

@toss/use-funnel에서 @use-funnel/next로의 마이그레이션에는 다음과 같은 주요 breaking changes가 있습니다:

  • Step 정의 방식 변경 (배열 → createFunnelSteps() 사용)
  • 컴포넌트 구조 변경 (<Funnel>/<Funnel.Step>funnel.Render 또는 조건부 렌더링)
  • 상태 관리 API 변경 (withState() HOC → createFunnelSteps 제네릭 타입)
  • 콜백 패턴 변경 (onStepChangeuseEffect with funnel.step)

다음을 반드시 확인하세요:

  • 버전을 v0.0.21로 수정하거나 v0.0.22 존재 여부 재확인
  • 프로젝트의 React 및 Next.js 버전이 실제 호환되는지 검증
  • 위 API 변경 사항들이 코드에 모두 반영되었는지 검토
🤖 Prompt for AI Agents
In @package.json at line 20, The package.json entry for "@use-funnel/next"
references a non-existent v0.0.22 and may be incompatible with our React/Next
versions; change the dependency to the published v0.0.21 (or verify v0.0.22
exists) and then audit the codebase for the library's breaking changes: replace
array step definitions with createFunnelSteps(), swap any <Funnel> /
<Funnel.Step> usage to funnel.Render or conditional rendering, migrate state HOC
usage of withState() to createFunnelSteps generic types, and replace
onStepChange callbacks with useEffect reading funnel.step; also confirm our
React/Next versions match the library's supported versions before committing the
dependency change.

"axios": "^1.13.2",
"next": "16.0.10",
"react": "19.2.1",
Expand Down
2 changes: 1 addition & 1 deletion src/app/day-log/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { SharedDiaryCard } from "@/components/dailyRecord/SharedDiaryCard";
import DailyRecordData from "@/mock/dailyRecord.json";

const DailyRecordPage = () => {
const MY_ID = 101;
const MY_ID = 111;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if MY_ID is used elsewhere in the codebase
rg -n "MY_ID" --type=ts --type=tsx

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 95


🏁 Script executed:

# Examine the full context of the page.tsx file
cat -n src/app/day-log/page.tsx

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 1074


🏁 Script executed:

# Search for authentication-related patterns and user context
rg -n "useAuth|useSession|getCurrentUser|getUser|userId" --type=ts --type=tsx -A 2 | head -50

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 95


🏁 Script executed:

# Check if there's a git history showing the change from 101 to 111
git log -p --all -S "MY_ID" -- "src/app/day-log/page.tsx" 2>/dev/null | head -100

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 50


🏁 Script executed:

#!/bin/bash
# Search for MY_ID with general pattern matching
rg "MY_ID" -g "*.ts" -g "*.tsx" --type-list | head -20

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 457


🏁 Script executed:

# Try without file type restriction
rg -n "MY_ID" --include="*.ts" --include="*.tsx"

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 509


🏁 Script executed:

# Search for authentication/user context patterns
rg -n "(useAuth|useSession|getCurrentUser|getUser|userId|authContext)" --include="*.ts" --include="*.tsx" | head -40

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 509


🏁 Script executed:

# Check the mock data file to understand the structure
cat -n src/mock/dailyRecord.json | head -50

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 1352


🏁 Script executed:

# Search for other hardcoded IDs to understand the testing pattern
rg -n "= (10[0-9]|11[0-9])" --include="*.ts" --include="*.tsx" | grep -i "id\|user"

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 509


🏁 Script executed:

#!/bin/bash
# Simple search for MY_ID usage
rg "MY_ID" .

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 480


🏁 Script executed:

# Check if userId 111 exists in the full mock data file
cat src/mock/dailyRecord.json | grep -n "111"

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 50


🏁 Script executed:

# Check the full length of the mock data to see all userIds
tail -20 src/mock/dailyRecord.json

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 423


🏁 Script executed:

# Search for authentication/context patterns more simply
rg "useAuth\|useSession\|AuthContext\|UserContext" .

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 50


🏁 Script executed:

# Look at the git history of the dailyRecord file
git log --oneline src/mock/dailyRecord.json 2>/dev/null | head -10

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 92


하드코딩된 MY_ID는 개발용 모의 데이터입니다. 코드 중복 제거 및 인증 통합 준비 필요

MY_ID = 111은 모의 데이터와 함께 사용되는 개발 단계의 코드입니다. 실제로 dailyRecord.json의 모든 userId는 101~110 범위이므로, 111은 "레코드 없음" 시나리오를 의도적으로 테스트하는 것으로 확인됩니다.

다만, 다음 두 가지를 개선해야 합니다:

  1. 코드 중복: MY_ID = 111이 src/app/day-log/page.tsxsrc/components/dailyRecord/MissionFeed.tsx에서 반복되고 있습니다. 공용 상수로 추출하거나 상위 컴포넌트에서 props로 전달하는 것을 권장합니다.

  2. 프로덕션 준비: 현재 인증 시스템이 없으므로, 프로덕션 배포 전에 실제 인증 컨텍스트에서 사용자 ID를 가져오도록 리팩토링해야 합니다.

🤖 Prompt for AI Agents
In @src/app/day-log/page.tsx at line 10, The hardcoded MY_ID = 111 in
src/app/day-log/page.tsx (also duplicated in
src/components/dailyRecord/MissionFeed.tsx) is dev-only mock data; refactor by
removing the duplicate constant and either (a) extract a shared constant (e.g.,
export const DEV_MOCK_USER_ID) in a single module and import it where needed for
local testing, or (b) prefer passing userId as a prop from the parent (page.tsx)
into MissionFeed, and replace MY_ID usage with that prop; additionally prepare
for production by replacing the mock with a call to your auth/context provider
(e.g., getUserIdFromAuth or useAuth().user.id) so runtime derives the real user
id when authentication is available.

const hasMyRecord = DailyRecordData.some(item => item.userId === MY_ID);

return (
Expand Down
17 changes: 17 additions & 0 deletions src/app/day-story/upload/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { BackHeader } from "@/components/common/BackHeader";
import { DailyMissionCard } from "@/components/dailyPhoto/DailyMissionCard";
import { DailyMissionProgress } from "@/components/dailyPhoto/DailyMissionProgress";

//하루한컷 업로드 페이지
const DayStoryUpload = () => {
return (
<main className="w-full bg-black">
<BackHeader title="하루한컷" titleColor="white" />
<DailyMissionProgress />
<div className="mt-[143px] flex items-center justify-center">
<DailyMissionCard />
</div>
</main>
);
};
export default DayStoryUpload;
7 changes: 7 additions & 0 deletions src/app/shared-diary/[id]/comment/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { SharedDiaryComment } from "@/components/dailyRecord/SharedDiaryChat";

const SharedDiaryChatPage = () => {
return <SharedDiaryComment variant="page" />;
};

export default SharedDiaryChatPage;
1 change: 1 addition & 0 deletions src/app/shared-diary/upload/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//공유일기 글쓰기 페이지
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

미구현 페이지 추적 필요.

이 파일은 주석만 있고 실제 구현이 없는 플레이스홀더입니다. 사용자가 이 라우트에 접근하면 빈 페이지가 표시될 수 있습니다.

다음 중 하나를 권장합니다:

  • 페이지 구현을 완료하거나
  • 임시로 "준비 중" UI를 표시하거나
  • 라우트를 비활성화

해당 작업을 위한 이슈를 생성하거나 기본 페이지 구조를 생성하는 데 도움이 필요하신가요?

🤖 Prompt for AI Agents
In @src/app/shared-diary/upload/page.tsx at line 1, The file
src/app/shared-diary/upload/page.tsx is an empty placeholder and will render a
blank page; either implement the page component (export default async function
SharedDiaryUploadPage() { ... }) with the form and handlers, or replace the file
contents with a simple placeholder UI (e.g., a "Coming Soon / Under
Construction" React component) or disable the route by removing its export; pick
one approach, add basic accessibility and skeleton markup, and open an issue
referencing "shared-diary upload page implementation" if you want to defer full
work.

10 changes: 7 additions & 3 deletions src/assets/SendButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ import SendIcon from "@/assets/messenger.svg";

interface SendButtonProps {
hasText: boolean;
onClick: React.MouseEventHandler<HTMLButtonElement>;
}
export const SendButton = ({ hasText = false }: SendButtonProps) => {
export const SendButton = ({ hasText = false, onClick }: SendButtonProps) => {
return (
<button className="absolute top-1 right-4 rotate-[-23deg] px-[2px] py-[3px]">
<button
className="absolute top-1 right-4 rotate-[-23deg] px-[2px] py-[3px]"
onClick={onClick}
>
<SendIcon
className={`h-[18px] w-[20px] ${hasText ? "text-green-02 cursor-pointer" : "text-neutral-04"}`}
className={`text-neutral-04 h-[18px] w-[20px] ${hasText ? "cursor-pointer" : ""}`}
/>
</button>
);
Expand Down
2 changes: 1 addition & 1 deletion src/assets/left-arrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/questionMark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 11 additions & 3 deletions src/components/common/BackHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@ interface HeaderProps {
title: string;
timeAgo?: string; // 하루한컷보기에서 사용
menuIcon?: boolean; // 채팅페이지에서 사용
titleColor?: string; //하루한컷 업로드에서 사용
}

export const BackHeader = ({ title, timeAgo, menuIcon }: HeaderProps) => {
export const BackHeader = ({
title,
timeAgo,
menuIcon,
titleColor = "black",
}: HeaderProps) => {
const router = useRouter();

return (
Expand All @@ -21,9 +27,11 @@ export const BackHeader = ({ title, timeAgo, menuIcon }: HeaderProps) => {
onClick={() => router.back()}
className="absolute top-1/2 left-4 -translate-y-1/2 cursor-pointer"
>
<BackIcon className="h-6 w-6" />
<BackIcon className={`text-${titleColor} h-6 w-6`} />
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

동적 Tailwind 클래스명이 작동하지 않습니다.

Tailwind CSS의 JIT 컴파일러는 빌드 타임에 정적 분석을 통해 클래스를 생성합니다. text-${titleColor} 형태의 동적 클래스명은 인식되지 않아 스타일이 적용되지 않습니다.

🔎 해결 방법

방법 1 (권장): 조건부 렌더링 사용

-        <BackIcon className={`text-${titleColor} h-6 w-6`} />
+        <BackIcon className={`${titleColor === "white" ? "text-white" : "text-black"} h-6 w-6`} />
       <h1
-        className={`text-h1 flex w-full items-center justify-center whitespace-nowrap text-${titleColor}`}
+        className={`text-h1 flex w-full items-center justify-center whitespace-nowrap ${titleColor === "white" ? "text-white" : "text-black"}`}
       >

방법 2: safelist에 추가 (tailwind.config에서)

// tailwind.config.js
module.exports = {
  safelist: [
    'text-white',
    'text-black',
  ],
  // ...
}

방법 3: style prop 사용

-        <BackIcon className={`text-${titleColor} h-6 w-6`} />
+        <BackIcon className="h-6 w-6" style={{ color: titleColor }} />

방법 1이 타입 안전성과 Tailwind의 디자인 시스템 활용 측면에서 가장 권장됩니다.

Also applies to: 33-33

🤖 Prompt for AI Agents
In @src/components/common/BackHeader.tsx at line 30, BackHeader uses a dynamic
Tailwind class `text-${titleColor}` which JIT won't generate; replace the
dynamic string with a deterministic class mapping or conditional rendering:
create a small map or switch keyed by the titleColor prop (e.g., map titleColor
values to 'text-white', 'text-black', etc.) and apply the resulting class to
BackIcon and the other affected element instead of `text-${titleColor}`, or
alternatively render conditional classNames for known values to ensure Tailwind
generates the CSS.

</button>
<h1 className="text-h1 flex w-full items-center justify-center whitespace-nowrap text-black">
<h1
className={`text-h1 flex w-full items-center justify-center whitespace-nowrap text-${titleColor}`}
>
{title}
</h1>
{timeAgo && (
Expand Down
27 changes: 24 additions & 3 deletions src/components/common/MessageInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,40 @@ import { useState } from "react";

import { SendButton } from "@/assets/SendButton";

export const MessageInput = () => {
interface MessageInputProps {
onFocus?: React.FocusEventHandler<HTMLInputElement>;
onSend?: (message: string) => void;
}

export const MessageInput = ({ onFocus, onSend }: MessageInputProps) => {
const [text, setText] = useState("");
const [isComposing, setIsComposing] = useState(false);

const hasText = Boolean(text);
const sendMessage = () => {
if (!hasText) return;
onSend?.(text.trim());
setText("");
};

return (
<div className="relative flex-1">
<input
className="border-neutral-07 bg-neutral-11 w-full rounded-2xl border border-solid px-4 py-1 focus:outline-none"
className="border-neutral-07 bg-neutral-11 focus:border-mint-01 w-full rounded-2xl border border-solid px-4 py-1 focus:outline-none"
type="text"
value={text}
onChange={e => setText(e.target.value)}
onFocus={onFocus}
onCompositionStart={() => setIsComposing(true)}
onCompositionEnd={() => setIsComposing(false)}
onKeyDown={e => {
if (e.key === "Enter" && !isComposing) {
e.preventDefault();
sendMessage();
}
}}
/>
<SendButton hasText={hasText} />
<SendButton hasText={hasText} onClick={sendMessage} />
</div>
);
};
41 changes: 41 additions & 0 deletions src/components/dailyPhoto/DailyMissionCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"use client";
import { useState } from "react";

import { WEEK_DAYS } from "@/constants/dayToKorean";
import { MISSION_STATUS } from "@/constants/missionCard";

import { getTodayIndex } from "@/utils/getCurrentDay";

export const DailyMissionCard = () => {
const [status, setStatus] =
useState<keyof typeof MISSION_STATUS>("NOT_STARTED");
const currentDayIndex = getTodayIndex();
const currentDay = WEEK_DAYS[currentDayIndex]; // 현재 요일

const subtitle =
typeof MISSION_STATUS[status].subtitle === "function"
? `${MISSION_STATUS[status].subtitle(currentDay)}요일`
: MISSION_STATUS[status].subtitle;

Copy link
Contributor

Choose a reason for hiding this comment

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

subtitle 계산 시 요일을 붙이는 부분이 "요일"을 하드코딩하고 있어서, 나중에 subtitle 함수가 이미 요일까지 포함한 문구를 반환한다면 중복될 수 있을 것 같아요.
subtitle(currentDay) 쪽에서 요일까지 포함해서 완성된 문장을 만들어 주도록 위임하거나, 최소한 subtitle이 문자열인지 함수인지 구분하는 로직을 유틸로 분리해 두면 좋을 것 같아요

Copy link
Member Author

Choose a reason for hiding this comment

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

유틸 분리하라는게 무슨말인지 이해가 안가요ㅠ 현재 로직은 subtitle을 미션 완료 상태에 따라서 상수로 빼둔 상태이고 미션이 NOT_STARTED 상태라면 요일의 앞부분을 함수로 가져오는 방식입니다.

현재의 요일의 인덱스를 가져와서 WEEK_DAYS에서 해당 요일을 한국어로 선언되어있는 배열에서 값을 가져오는 방식입니다.

Copy link
Contributor

Choose a reason for hiding this comment

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

const subtitle =
  typeof MISSION_STATUS[status].subtitle === "function"
    ? `${MISSION_STATUS[status].subtitle(currentDay)}요일`
    : MISSION_STATUS[status].subtitle;

여기서

  • subtitle이 함수인지 문자열인지 구분하고
  • 함수면 currentDay 넣어서 문장 만들고
  • 뒤에 "요일"까지 붙이는 일
    을 한 번에 하고 있어서, 이 부분을 유틸로 분리하는게 어떠냐는 제안이었습니다.
import { WEEK_DAYS } from "@/constants/dayToKorean";
import { MISSION_STATUS } from "@/constants/missionCard";

export const getMissionSubtitle = (
  status: keyof typeof MISSION_STATUS,
  todayIndex: number,
) => {
  const currentDay = WEEK_DAYS[todayIndex];
  const rawSubtitle = MISSION_STATUS[status].subtitle;

  const text =
    typeof rawSubtitle === "function"
      ? rawSubtitle(currentDay) 
      : rawSubtitle;

  return text;
};

이런식으로

return (
<section className="flex h-[385px] w-[353px] flex-col items-center justify-center gap-5 rounded-2xl bg-white">
<div className="flex flex-col items-center justify-center">
<h3 className="text-green-01 text-h3">
{MISSION_STATUS[status].title}
</h3>
<p className="text-neutral-06 text-sub1-r -mt-[4px]">{subtitle}</p>
</div>
<div className="bg-neutral-11 h-28 w-28 rounded-full">
{/* 아이콘 영역 */}
</div>
<div className="text-sub-number p-[10px]">
오늘의 하늘을 공유해주세요.
</div>
<button
className={`${status == "NOT_STARTED" ? "bg-gradient-orange" : "bg-green-01"} text-button-sb h-[50px] w-[90px] cursor-pointer rounded-2xl text-white`}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

엄격한 동등 연산자를 사용하세요.

== 대신 ===를 사용하여 타입 안전성을 보장하세요.

🔎 수정 제안
-        className={`${status == "NOT_STARTED" ? "bg-gradient-orange" : "bg-green-01"} text-button-sb h-[50px] w-[90px] cursor-pointer rounded-2xl text-white`}
+        className={`${status === "NOT_STARTED" ? "bg-gradient-orange" : "bg-green-01"} text-button-sb h-[50px] w-[90px] cursor-pointer rounded-2xl text-white`}
📝 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
className={`${status == "NOT_STARTED" ? "bg-gradient-orange" : "bg-green-01"} text-button-sb h-[50px] w-[90px] cursor-pointer rounded-2xl text-white`}
className={`${status === "NOT_STARTED" ? "bg-gradient-orange" : "bg-green-01"} text-button-sb h-[50px] w-[90px] cursor-pointer rounded-2xl text-white`}
🤖 Prompt for AI Agents
In @src/components/dailyPhoto/DailyMissionCard.tsx at line 35, In
DailyMissionCard update the conditional className expression to use strict
equality (replace `==` with `===`) when comparing the `status` variable so the
ternary `status === "NOT_STARTED" ? "bg-gradient-orange" : "bg-green-01"` is
used, ensuring type-safe comparison in the JSX expression that builds the
className string.

>
{MISSION_STATUS[status].buttonText}
</button>
</section>
);
};
49 changes: 49 additions & 0 deletions src/components/dailyPhoto/DailyMissionProgress.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"use client";

import CheckIcon from "@/assets/check.svg";
import QuestionIcon from "@/assets/questionMark.svg";

import { WEEK_DAYS } from "@/constants/dayToKorean";

import { getWeekDayStatus } from "@/utils/getCurrentDay";

export const DailyMissionProgress = () => {
return (
<ul className="scrollbar-hide mt-8 flex gap-[9px] overflow-x-auto px-4">
{WEEK_DAYS.map((item, index) => {
const status = getWeekDayStatus(index); // past | today | future
const isTodayCompleted = false;

const showCheck =
status === "past" || (status === "today" && isTodayCompleted);

return (
<li key={item} className="h-[82px] w-[74px] shrink-0">
<div
className={`${showCheck ? "bg-spring-01" : "bg-white"} ${status != "future" ? "border-mint-01" : "border-neutral-08"} flex h-full flex-col items-center justify-center gap-[3px] rounded-2xl border border-solid px-[10px] py-[6px]`}
>
{showCheck ? (
<div className="bg-mint-01 flex h-8 w-8 items-center justify-center rounded-full">
<CheckIcon className="text-spring-01 h-6 w-6" />
</div>
) : (
<div
className={`${status === "today" ? "bg-mint-01" : "bg-neutral-08"} flex h-8 w-8 items-center justify-center rounded-full`}
>
<QuestionIcon
className={`${status === "today" ? "text-white" : "text-neutral-06"} h-6 w-6`}
/>
</div>
)}
<span
className={`text-sub1-sb ${status === "today" ? "text-neutral-01" : "text-neutral-06"}`}
>
{item}
</span>
</div>
</li>
);
})}
</ul>
);
};
45 changes: 45 additions & 0 deletions src/components/dailyRecord/CommentList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import Image from "next/image";

import { DiaryComment } from "@/types/diaryComment.type";

import { formatDate } from "@/utils/formatDate";

interface CommentListProps {
comments: DiaryComment[];
}

export const CommentList = ({ comments }: CommentListProps) => {
if (comments.length === 0) {
return (
<div className="flex flex-col items-center justify-center pt-[109px]">
<p className="text-sub1-sb text-neutral-01">아직 댓글이 없습니다</p>
<p className="text-sub2-r text-neutral-06">댓글을 남겨보세요!</p>
</div>
);
}

return (
<ul className="flex flex-col">
{comments.map(comment => (
<li key={comment.id} className="flex gap-[10px] px-4 py-[10px]">
<Image
src={comment.profileImg}
alt={comment.userName}
width={56}
height={56}
className="h-14 w-14 rounded-2xl object-cover"
/>
<div className="flex flex-1 justify-between">
<div className="flex flex-col">
<p className="text-d3 text-black">{comment.userName}</p>
<p className="text-sub1-r text-neutral-05">{comment.comment}</p>
</div>
<p className="text-body3 text-neutral-05 flex flex-col self-start">
{formatDate(comment.createdAt)}
</p>
</div>
</li>
))}
</ul>
);
};
2 changes: 1 addition & 1 deletion src/components/dailyRecord/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const Header = () => {

return (
<header className="relative mt-[13px] flex items-center justify-center px-4 py-[10px]">
<h1>하루기록</h1>
<h1 className="text-h1 text-black">하루기록</h1>
<div className="absolute right-4 flex gap-2">
{isAlarmNew ? (
<button
Expand Down
9 changes: 7 additions & 2 deletions src/components/dailyRecord/MissionFeed.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
"use client";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/navigation";

import PlusIcon from "@/assets/plus.svg";

import DailyRecordData from "@/mock/dailyRecord.json";

export const RecordMissionFeed = () => {
const MY_ID = 101;
const MY_ID = 111;

const myRecord = DailyRecordData.find(item => item.userId === MY_ID);
const router = useRouter();

// TODO: api연결시, zustand로 optimistic update로직 추가
const otherRecords = DailyRecordData.filter(
Expand Down Expand Up @@ -37,7 +39,10 @@ export const RecordMissionFeed = () => {
/>
</Link>
) : (
<div className="bg-neutral-11 flex h-[88px] w-[88px] items-center justify-center rounded-full">
<div
className="bg-neutral-11 flex h-[88px] w-[88px] items-center justify-center rounded-full"
onClick={() => router.push("/day-story/upload")}
>
<PlusIcon className="text-neutral-05 h-12 w-12" />
</div>
)}
Expand Down
56 changes: 30 additions & 26 deletions src/components/dailyRecord/MissionInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,43 @@
"use client";
import { useRouter } from "next/navigation";

import CheckIcon from "@/assets/check.svg";

interface RecordMissionInfoProps {
hasMyRecord: boolean;
}
export const RecordMissionInfo = ({ hasMyRecord }: RecordMissionInfoProps) => {
const router = useRouter();
return (
<section className="border-mint-01 bg-neutral-11 mx-4 mt-[13.5px] cursor-pointer rounded-2xl border">
<div className="flex h-[76px] items-center justify-between p-[10px]">
{hasMyRecord ? (
<>
<p className="text-sub1-sb text-neutral-07">
오늘의 챌린지 미션을 완료했어요.
</p>
<div className="flex flex-col items-center gap-[7px]">
<div className="bg-neutral-07 flex h-6 w-6 items-center justify-center rounded-full">
<CheckIcon className="h-4 w-4 text-white" />
</div>

<p className="text-sub2-r text-neutral-06 -mt-[4px] px-[6px]">
미션완료
</p>
<section className="border-mint-01 bg-neutral-11 mx-4 mt-[13.5px] rounded-2xl border">
{hasMyRecord ? (
<div className="flex h-[76px] items-center justify-between p-[10px]">
<p className="text-sub1-sb text-neutral-07">
오늘의 챌린지 미션을 완료했어요.
</p>
<div className="flex flex-col items-center gap-[7px]">
<div className="bg-neutral-07 flex h-6 w-6 items-center justify-center rounded-full">
<CheckIcon className="h-4 w-4 text-white" />
</div>
</>
) : (
<>
<p className="text-sub1-sb text-black">
오늘의 챌린지 미션이 도착했어요!

<p className="text-sub2-r text-neutral-06 -mt-[4px] px-[6px]">
미션완료
</p>
<div className="flex h-full flex-col justify-end">
<p className="text-green-01 text-sub2-r px-[6px]">시작하기</p>
</div>
</>
)}
</div>
</div>
</div>
) : (
<div
className="flex h-[76px] cursor-pointer items-center justify-between p-[10px]"
onClick={() => router.push("/day-story/upload")}
>
<p className="text-sub1-sb text-black">
오늘의 챌린지 미션이 도착했어요!
</p>
<div className="flex h-full flex-col justify-end">
<p className="text-green-01 text-sub2-r px-[6px]">시작하기</p>
</div>
</div>
)}
</section>
);
};
Loading