Skip to content

Commit

Permalink
feat : 감정 선택 위젯 작성, 페이지 연결 (#104)
Browse files Browse the repository at this point in the history
* feat : 감정 선택 위젯 작성 후 페이지와 연결

* style : 페이지 스타일 수정
  • Loading branch information
cmlim0070 authored Nov 1, 2024
1 parent fe633c2 commit ef75d92
Show file tree
Hide file tree
Showing 18 changed files with 276 additions and 95 deletions.
2 changes: 1 addition & 1 deletion src/features/diary-write/condition/model/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ export type Condition = (typeof CONDITIONS)[number];

export interface ConditionButtonGroupProps {
selectedCondition: Condition | null;
onChange: (condition: Condition) => void;
onConditionChange: (condition: Condition) => void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const meta = {
options: [...CONDITIONS, null],
description: '현재 선택되어있는 기분',
},
onChange: {
onConditionChange: {
description: '기분 버튼 클릭시 호출 : 부모 컴포넌트로 선택된 기분 전달'
}
}
Expand All @@ -29,7 +29,7 @@ type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
selectedCondition: null,
onChange: (condition) => {
onConditionChange: (condition) => {
console.log(condition);
},
},
Expand All @@ -39,7 +39,7 @@ export const Default: Story = {
export const Selected: Story = {
args: {
selectedCondition: '보통',
onChange: (condition) => {
onConditionChange: (condition) => {
console.log(condition);
},
},
Expand All @@ -48,7 +48,7 @@ export const Selected: Story = {
export const Interactive: Story = {
args: {
selectedCondition: null,
onChange: (condition) => {
onConditionChange: (condition) => {
alert(`${condition}`);
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ConditionButton } from '../../../../entities/condition';
// condition으로 네이밍 변경 해야합니다
export const ConditionButtonGroup = ({
selectedCondition, // 현재 선택되어있는 기분 상태
onChange // 부모 컴포넌트로 선택된 기분 전달
onConditionChange // 부모 컴포넌트로 선택된 기분 전달
}: ConditionButtonGroupProps) => {
return (
<Container>
Expand All @@ -15,7 +15,7 @@ export const ConditionButtonGroup = ({
<ConditionButton
key={condition}
isActive={selectedCondition === condition}
onClick={() => onChange(condition)}
onClick={() => onConditionChange(condition)}
>
{condition}
</ConditionButton>
Expand Down
1 change: 1 addition & 0 deletions src/features/diary-write/emotion/ui/EmotionButtonGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import EmotionButtonList from '@/shared/EmotionButtonList/ui/EmotionButtonList';
import { KeywordButton } from '@/entities/KeywordButton/ui/KeywordButton';
import { Emotions } from '@/shared/model/EmotionEnum';

// TODO - 초기값이 undefined로 들어오는데 null로 들어오도록 변경
export const EmotionButtonGroup: React.FC<EmotionButtonGroupProps> = ({
initialKeywords = [null, null, null, null, null],
onKeywordsChange
Expand Down
4 changes: 3 additions & 1 deletion src/features/diary-write/musicList/model/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export interface MusicItem {

export interface MusicCardListProps {
type: string; // 리스트 타입

responseMusicList: MusicItem[];
// onChange: (music: MusicItem) => void;
selectedMusic: MusicItem | null;
onChange: (music: MusicItem | null) => void;
}
21 changes: 12 additions & 9 deletions src/features/diary-write/musicList/ui/MusicCardList.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,26 +28,29 @@ const sampleMusicList: MusicItem[] = [
}
];

export const GPTRecommend: Story = {
export const GptSearchMode: Story = {
args: {
type: SEARCH_TYPE.GPT,
type: 'gptSearch',
responseMusicList: sampleMusicList,
// onChange: (item: MusicItem) => console.log('Selected:', item)
selectedMusic: null, // 추가
onChange: (music) => console.log('Selected music:', music) // 추가
}
};

export const UserSearch: Story = {
export const UserSearchMode: Story = {
args: {
type: SEARCH_TYPE.USER,
type: 'userSearch',
responseMusicList: sampleMusicList,
// onChange: (item: MusicItem) => console.log('Selected:', item)
selectedMusic: null, // 추가
onChange: (music) => console.log('Selected music:', music) // 추가
}
};

export const Empty: Story = {
export const EmptyList: Story = {
args: {
type: SEARCH_TYPE.USER,
type: 'userSearch',
responseMusicList: [],
// onChange: (item: MusicItem) => console.log('Selected:', item)
selectedMusic: null, // 추가
onChange: (music) => console.log('Selected music:', music) // 추가
}
};
16 changes: 8 additions & 8 deletions src/features/diary-write/musicList/ui/MusicCardList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import { EmptyMusicCard, MusicCard } from '../../../../entities/music';
import useMusicStore from '@/entities/music/model/useMusicStore';

export const MusicCardList = ({
responseMusicList
// onChange
responseMusicList,
selectedMusic,
onChange
}: MusicCardListProps) => {
const [nowPlaying, setNowPaying] = useState<string | null>(null);
const { selectedMusic, setSelectedMusic, clearSelectedMusic } =
useMusicStore();

/**
* iframe에 비디오 아이디를 셋팅합니다.
Expand All @@ -27,14 +26,15 @@ export const MusicCardList = ({
};

/**
* 사용자가 선택한 음악 정보를 셋팅합니다.
* 사용자가 선택한 음악 정보를 리스트로 전달
* @param item :MusicItem 클릭한 카드 컴포넌트 음악 정보
*/
const handleClick = (item: MusicItem) => {
if (item === selectedMusic) {
clearSelectedMusic();
const isAlreadySelected = selectedMusic?.youtubeId === item.youtubeId;
if (isAlreadySelected) {
onChange(null);
} else {
setSelectedMusic(item);
onChange(item);
}
};

Expand Down
7 changes: 7 additions & 0 deletions src/pages/DiaryWritePage/model/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { EmotionType } from '@/shared/model/moodTypes';

export interface MoodDataType {
mood: EmotionType;
emotion: string | null;
subEmotion: (string | null)[];
}
4 changes: 3 additions & 1 deletion src/pages/DiaryWritePage/ui/DiaryWritePage.styled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import styled from 'styled-components';
export const Container = styled.div`
display: flex;
flex-direction: column;
align-items: center;
`;

export const Section = styled.div`
display: flex;
flex-direction: column;
align-items: center;
padding: 0 200px 0 200px;
width: 960px;
gap: 200px;
`;
79 changes: 53 additions & 26 deletions src/pages/DiaryWritePage/ui/DiaryWritePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,26 @@ import { Container, Section } from './DiaryWritePage.styled';
import { SelectEmotionContainer } from '@/widgets/select-emotion';
import { WriteDiaryContainer } from '@/widgets/write-diary';
import { fetchGptRecommend } from '@/entities/music/api/fetchGptRecommend';
import { gptAnswerType, gptQueryParamsType } from '@/entities/music/model/type';
import {
gptAnswerType,
gptQueryParamsType,
MusicItem
} from '@/entities/music/model/type';
import { EmotionType } from '@/shared/model/moodTypes';
import { Emotions } from '@/shared/model/EmotionEnum';
import { MoodDataType } from '../model/type';

// TODO - 리팩토링 (로직 분리)
export const DiaryWritePage = () => {
// 테스트 데이터
const testdiary: gptQueryParamsType = {
mood: '매우 나쁨',
emotion: '슬픔',
subemotion: ['슬픔'],
// 테스트 다이러리
const diary = {
title: '우울해',
content: '너무 우울해서 빵샀어'
};
// const testdata = '뉴진스 supernatural';

const [diaryData, setDiaryData] = useState();
const [emotionData, setEmotionData] = useState();
// const [diaryData, setDiaryData] = useState();
const [emotionData, setEmotionData] = useState<MoodDataType | null>(null);
const [musicData, setMusicData] = useState<MusicItem | null>(null);

const [gptRecommendMusicList, setGptRecommendMusicList] =
useState<gptAnswerType>([]);
Expand All @@ -29,31 +34,53 @@ export const DiaryWritePage = () => {
// setDiaryData(diaryData);
// };

// // 감정 데이터가 넘어오면 셋팅
// const handleEmotionSelect = (emotionData) => {
// setEmotionData(emotionData);
// };
const handleMoodSelect = (moodState: MoodDataType) => {
console.log('감정 데이터 셋팅 : ', moodState);
setEmotionData(moodState);
};

// const checkAndFetchRecommendations = async () => {
// const recommendations = await fetchGptRecommend(testdiary);
// setGptRecommendMusicList(recommendations);
// };
const handleMusicSelect = (music: MusicItem | null) => {
console.log('음악 데이터 셋팅 : ', music);
setMusicData(music);
};

const testFunction = async () => {
const recommendations = await fetchGptRecommend(testdiary);
setGptRecommendMusicList(recommendations);
// TODO - diary 파라미터 넣어야함
const createGptQuery = (mood: MoodDataType) => {
const gptQuery: gptQueryParamsType = {
title: '우울해',
content: '너무 우울해서 빵샀어',
...(mood?.mood && { mood: mood.mood }),
...(mood?.emotion && { emotion: mood.emotion }),
...(mood?.subEmotion && {
subemotion: mood.subEmotion.filter(
(item): item is string => item !== null
)
})
};
return gptQuery;
};

const testFunction = async (mood: MoodDataType | null) => {
console.log('테스트 함수 실행됨');
if (mood === null) {
console.log('감정 선택을 먼저 완료해주세요');
} else {
const gptQuery = createGptQuery(mood);
const recommendations = await fetchGptRecommend(gptQuery);
setGptRecommendMusicList(recommendations);
}
};

return (
<Container>
<Header />
<Section>
{/* <WriteDiaryContainer onDiarySubmit={handleDiarySubmit} />
<SelectEmotionContainer onEmotionSelect={handleEmotionSelect} /> */}
<button type="button" onClick={() => testFunction()}>
테스트 버튼
</button>
{/* <WriteDiaryContainer onDiarySubmit={handleDiarySubmit} /> */}
<SelectEmotionContainer
onMoodSelect={handleMoodSelect}
onNext={testFunction}
/>
<SelectMusicContainer
onMusicSelect={handleMusicSelect}
gptRecommendMusicList={gptRecommendMusicList}
/>
</Section>
Expand Down
66 changes: 33 additions & 33 deletions src/shared/EmotionButtonList/ui/EmotionButtonList.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
// import React from 'react';
// import type { Meta, StoryObj } from '@storybook/react';
// import EmotionButtonList from './EmotionButtonList';
// import { Emotions } from '../../../shared/model/EmotionEnum';
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import EmotionButtonList from './EmotionButtonList';
import { Emotions } from '../../../shared/model/EmotionEnum';

// const meta: Meta<typeof EmotionButtonList> = {
// component: EmotionButtonList,
// title: 'Shared/EmotionButtonList',
// tags: ['autodocs'],
// argTypes: {
// isPrimary: {
// control: { type: 'boolean' },
// description: '대표 감정 : true, 서브 감정 : false ',
// },
// maxSelections: {
// control: { type: 'number' },
// description: '사용자가 선택할 수 있는 최대 감정 버튼 개수',
// },
// initialSelectedEmotions: {
// control: {
// type: 'array',
// options: Object.values(Emotions),
// },
// description: '초기 선택된 감정 목록입니다.\n\n' +
// '배열의 크기가 maxSelections 수량보다 클 경우, 앞에서부터 maxSelections 수량만큼 설정됩니다.',
// },
// onSelectionChange: {
// description: '선택된 감정 목록이 변경될 때 호출되는 콜백 함수',
// action: 'onSelectionChange',
// },
// },
// };
const meta: Meta<typeof EmotionButtonList> = {
component: EmotionButtonList,
title: 'Shared/EmotionButtonList',
tags: ['autodocs'],
argTypes: {
isPrimary: {
control: { type: 'boolean' },
description: '대표 감정 : true, 서브 감정 : false ',
},
maxSelections: {
control: { type: 'number' },
description: '사용자가 선택할 수 있는 최대 감정 버튼 개수',
},
initialSelectedEmotions: {
control: {
type: 'select',
options: Object.values(Emotions),
},
description: '초기 선택된 감정 목록입니다.\n\n' +
'배열의 크기가 maxSelections 수량보다 클 경우, 앞에서부터 maxSelections 수량만큼 설정됩니다.',
},
onSelectionChange: {
description: '선택된 감정 목록이 변경될 때 호출되는 콜백 함수',
action: 'onSelectionChange',
},
},
};

// export default meta;
export default meta;

// type Story = StoryObj<typeof EmotionButtonList>;
type Story = StoryObj<typeof EmotionButtonList>;

// export const Default: Story = {
// args: {
Expand Down
6 changes: 6 additions & 0 deletions src/shared/EmotionButtonList/ui/EmotionButtonList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ interface EmotionListProps {
* 대표 감정 모드와 서브 감정 모드를 지원하며, 초기 선택된 감정을 설정하고 최대 선택 가능 수를 제한할 수 있습니다.
*/

/* ***********************************************************
TODO - 해결
SelectEmotionContainer.tsx:27 Warning: Cannot update a component (`SelectEmotionContainer`) while rendering a different component (`EmotionList`). To locate the bad setState() call inside `EmotionList`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render
at EmotionList
********************************************************** */

const EmotionList: React.FC<EmotionListProps> = ({
isPrimary = true,
maxSelections,
Expand Down
8 changes: 7 additions & 1 deletion src/shared/model/moodTypes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
type EmotionType = '좋음' | '나쁨' | '보통' | '매우 좋음' | '매우 나쁨' | null;
export type EmotionType =
| '좋음'
| '나쁨'
| '보통'
| '매우 좋음'
| '매우 나쁨'
| null;

export interface DailyEmotionType {
day: 'Mon' | 'Tue' | 'Wed' | 'Thu' | 'Fri' | 'Sat' | 'Sun';
Expand Down
Loading

0 comments on commit ef75d92

Please sign in to comment.