-
Notifications
You must be signed in to change notification settings - Fork 1
[FEAT] 하루한컷 업로드 #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[FEAT] 하루한컷 업로드 #14
Changes from 32 commits
9d1b136
6fabbb4
506304b
afaa8de
91ee31c
99a9353
528d81f
10d3ab3
cf13f28
10d1bd5
48dd0e8
2a675b1
e763d5e
272b1a2
8f7b486
3f99a50
5e6ef76
95f3e7a
57e3a87
889e0aa
3b8a5a2
8a563dd
ec28cdb
0494d6c
7dfdcac
b6cc486
37bdacd
5137aac
3f510c4
df87e1e
c916ad1
c31ff2d
ce32ac7
5e656ca
612d769
be95b69
e8f45a4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check if MY_ID is used elsewhere in the codebase
rg -n "MY_ID" --type=ts --type=tsxRepository: 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.tsxRepository: 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 -50Repository: 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 -100Repository: 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 -20Repository: 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 -40Repository: 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 -50Repository: 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.jsonRepository: 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 -10Repository: IT-Cotato/12th-SimTok-FE Length of output: 92 하드코딩된 MY_ID는 개발용 모의 데이터입니다. 코드 중복 제거 및 인증 통합 준비 필요 MY_ID = 111은 모의 데이터와 함께 사용되는 개발 단계의 코드입니다. 실제로 dailyRecord.json의 모든 userId는 101~110 범위이므로, 111은 "레코드 없음" 시나리오를 의도적으로 테스트하는 것으로 확인됩니다. 다만, 다음 두 가지를 개선해야 합니다:
🤖 Prompt for AI Agents |
||
| const hasMyRecord = DailyRecordData.some(item => item.userId === MY_ID); | ||
|
|
||
| return ( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| "use client"; | ||
| import { useState } from "react"; | ||
|
|
||
| import { BackHeader } from "@/components/common/BackHeader"; | ||
| import { DailyMissionCard } from "@/components/dailyPhoto/DailyMissionCard"; | ||
| import { DailyMissionProgress } from "@/components/dailyPhoto/DailyMissionProgress"; | ||
|
|
||
| import { MISSION_STATUS } from "@/constants/missionCard"; | ||
|
|
||
| const DayStoryUpload = () => { | ||
| const [status, setStatus] = | ||
| useState<keyof typeof MISSION_STATUS>("NOT_STARTED"); | ||
| return ( | ||
| <main className="w-full bg-black"> | ||
| <BackHeader title="하루한컷" titleColor="white" /> | ||
| <DailyMissionProgress status={status} /> | ||
| <div className="mt-[120px] flex items-center justify-center"> | ||
| <DailyMissionCard status={status} setStatus={setStatus} /> | ||
| </div> | ||
| </main> | ||
| ); | ||
| }; | ||
| export default DayStoryUpload; |
| 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; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| //공유일기 글쓰기 페이지 | ||
lemoncurdyogurt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 ( | ||
|
|
@@ -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`} /> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 동적 Tailwind 클래스명이 작동하지 않습니다. Tailwind CSS의 JIT 컴파일러는 빌드 타임에 정적 분석을 통해 클래스를 생성합니다. 🔎 해결 방법방법 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 |
||
| </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 && ( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| "use client"; | ||
| import Image from "next/image"; | ||
| import { useRouter } from "next/navigation"; | ||
|
|
||
| import { useState } from "react"; | ||
|
|
||
| import { MISSION_SORT, MISSION_STATUS } from "@/constants/missionCard"; | ||
| import { WEEK_DAYS_KOR } from "@/constants/weekDays"; | ||
|
|
||
| import { useImageUpload } from "@/hooks/useImageUpload"; | ||
|
|
||
| import missionCardData from "@/mock/randomMission.json"; | ||
|
|
||
| import { getTodayIndex } from "@/utils/getCurrentDay"; | ||
|
|
||
| interface DailyMissionCardProps { | ||
| status: keyof typeof MISSION_STATUS; | ||
| setStatus: React.Dispatch<React.SetStateAction<keyof typeof MISSION_STATUS>>; | ||
| } | ||
|
|
||
| export const DailyMissionCard = ({ | ||
| status, | ||
| setStatus, | ||
| }: DailyMissionCardProps) => { | ||
| const [previewUrl, setPreviewUrl] = useState<string | null>(null); | ||
|
|
||
| const currentDayIndex = getTodayIndex(); | ||
| const currentDay = WEEK_DAYS_KOR[currentDayIndex]; // 현재 요일 | ||
|
|
||
| const subtitle = | ||
| typeof MISSION_STATUS[status].subtitle === "function" | ||
| ? `${MISSION_STATUS[status].subtitle(currentDay)}요일` | ||
| : MISSION_STATUS[status].subtitle; | ||
|
|
||
lemoncurdyogurt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| const missionKind = missionCardData[0].kind; | ||
|
|
||
| const missionIcon = MISSION_SORT.find( | ||
| item => item.sort === missionKind, | ||
| )?.icon; | ||
|
|
||
| const onSelectImage = (file: File) => { | ||
| const url = URL.createObjectURL(file); | ||
| setPreviewUrl(url); | ||
| setStatus("IMAGE_UPLOADED"); | ||
| }; | ||
|
|
||
| const { inputRef, openFilePicker, onChangeFile } = useImageUpload({ | ||
| onSelect: ({ file }) => { | ||
| onSelectImage(file); | ||
| }, | ||
| maxSizeMB: 10, | ||
| }); | ||
lemoncurdyogurt marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| const router = useRouter(); | ||
| 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> | ||
| {status === "NOT_STARTED" && missionIcon && ( | ||
| <div className="bg-neutral-11 flex h-28 w-28 items-center justify-center rounded-full"> | ||
| <Image | ||
| src={missionIcon} | ||
| alt="미션카드종류 이미지" | ||
| width={88} | ||
| height={88} | ||
| /> | ||
| </div> | ||
| )} | ||
| {status !== "NOT_STARTED" && previewUrl && ( | ||
| <div | ||
| className={`h-27 w-27 overflow-hidden rounded-full ${status === "IMAGE_CONFIRMED" ? "border border-[4px] border-white shadow-[0_0_12px_-1px_#00C362]" : ""}`} | ||
| > | ||
| <Image | ||
| src={previewUrl} | ||
| alt="하루한컷이미지" | ||
| width={108} | ||
| height={108} | ||
| className="h-full w-full object-cover" | ||
| /> | ||
| </div> | ||
| )} | ||
|
|
||
| <div className="flex flex-col items-center justify-center"> | ||
| <div className="text-sub-number p-[10px]"> | ||
| {status === "IMAGE_CONFIRMED" | ||
| ? "미션이 완료되었어요!" | ||
| : missionCardData[0].mission} | ||
| </div> | ||
| <button | ||
| className={`${status == "NOT_STARTED" ? "bg-gradient-orange" : "bg-mint-01"} text-button-sb h-[50px] w-[90px] cursor-pointer rounded-2xl text-white`} | ||
| onClick={() => { | ||
| if (status === "NOT_STARTED") openFilePicker(); | ||
| if (status === "IMAGE_UPLOADED") setStatus("IMAGE_CONFIRMED"); | ||
| if (status === "IMAGE_CONFIRMED") router.push("/day-log"); | ||
| }} | ||
| > | ||
| {MISSION_STATUS[status].buttonText} | ||
| </button> | ||
| </div> | ||
| <input | ||
| ref={inputRef} | ||
| type="file" | ||
| accept="image/*" | ||
| className="hidden" | ||
| onChange={onChangeFile} | ||
| /> | ||
| </section> | ||
| ); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| "use client"; | ||
|
|
||
| import CheckIcon from "@/assets/check.svg"; | ||
| import CrossIcon from "@/assets/close.svg"; | ||
| import QuestionIcon from "@/assets/questionMark.svg"; | ||
|
|
||
| import { MISSION_STATUS } from "@/constants/missionCard"; | ||
| import { WEEK_DAYS_KOR, WEEK_DAY_KEYS } from "@/constants/weekDays"; | ||
|
|
||
| import dailyRecordProgress from "@/mock/dailyRecordProgress.json"; | ||
|
|
||
| import { getWeekDayStatus } from "@/utils/getCurrentDay"; | ||
|
|
||
| type WeekDayKey = keyof typeof dailyRecordProgress; | ||
|
|
||
| interface DailyMissionProgressProps { | ||
| status: keyof typeof MISSION_STATUS; | ||
| } | ||
|
|
||
| export const DailyMissionProgress = ({ status }: DailyMissionProgressProps) => { | ||
| return ( | ||
| <ul className="scrollbar-hide mt-8 flex gap-[9px] overflow-x-auto px-4"> | ||
| {WEEK_DAYS_KOR.map((item, index) => { | ||
| const dayStatus = getWeekDayStatus(index); // past | today | future | ||
| const dayKey = WEEK_DAY_KEYS[index] as WeekDayKey; | ||
|
|
||
| const isCompleted = | ||
| dayStatus === "today" && status === "IMAGE_CONFIRMED" | ||
| ? true | ||
| : dailyRecordProgress[dayKey]; | ||
|
|
||
| const showCheck = | ||
| (dayStatus === "past" && isCompleted) || | ||
| (dayStatus === "today" && isCompleted); | ||
|
|
||
| const showCross = dayStatus === "past" && !isCompleted; | ||
|
|
||
| const showQuestion = | ||
| dayStatus === "future" || (dayStatus === "today" && !isCompleted); | ||
|
|
||
| return ( | ||
| <li key={item} className="h-[82px] w-[74px] shrink-0"> | ||
| <div | ||
| className={`flex h-full flex-col items-center justify-center gap-[3px] rounded-2xl border border-solid px-[10px] py-[6px] ${showQuestion ? "bg-white" : "bg-spring-01"} ${dayStatus !== "future" ? "border-mint-01" : "border-neutral-08"} `} | ||
| > | ||
| {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> | ||
| )} | ||
|
|
||
| {showCross && ( | ||
| <div className="bg-neutral-03 flex h-8 w-8 items-center justify-center rounded-full"> | ||
| <CrossIcon className="text-neutral-08 h-[13px] w-[13px]" /> | ||
| </div> | ||
| )} | ||
|
|
||
| {showQuestion && ( | ||
| <div | ||
| className={`flex h-8 w-8 items-center justify-center rounded-full ${dayStatus === "today" ? "bg-mint-01" : "bg-neutral-08"} `} | ||
| > | ||
| <QuestionIcon | ||
| className={`h-6 w-6 ${ | ||
| dayStatus === "today" ? "text-white" : "text-neutral-06" | ||
| } `} | ||
| /> | ||
| </div> | ||
| )} | ||
|
|
||
| <span | ||
| className={`text-sub1-sb ${ | ||
| dayStatus !== "future" ? "text-neutral-01" : "text-neutral-06" | ||
| } `} | ||
| > | ||
| {item} | ||
| </span> | ||
| </div> | ||
| </li> | ||
| ); | ||
| })} | ||
| </ul> | ||
| ); | ||
| }; |
Uh oh!
There was an error while loading. Please reload this page.