-
Notifications
You must be signed in to change notification settings - Fork 0
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/#22] 인덱스 페이지 구현 #26
Changes from 7 commits
3d91be7
a67b4c7
3c40c52
21638da
af29994
a000441
7e98822
93e9190
133288f
6aaf17a
09220ba
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 |
---|---|---|
@@ -1,11 +1,31 @@ | ||
import HeaderContent from '@/shared/components/HeaderContent'; | ||
'use client'; | ||
|
||
import QuickQuestionPickerBox from '@/chat/components/QuickQuestionPickerBox'; | ||
import MainContent from '@/shared/components/MainContent'; | ||
import { css } from 'styled-components'; | ||
|
||
export default function HomePage() { | ||
return ( | ||
<> | ||
<HeaderContent>{null}</HeaderContent> | ||
<MainContent>{null}</MainContent> | ||
<MainContent> | ||
<h1 | ||
css={css` | ||
margin-top: 170px; | ||
text-align: center; | ||
${(props) => props.theme.fonts.headline2} | ||
`} | ||
> | ||
AI 타로 술사, 타로냥에게 | ||
<br /> 무엇이든 물어봐라냥 | ||
</h1> | ||
<div | ||
css={css` | ||
margin-top: 32px; | ||
`} | ||
> | ||
<QuickQuestionPickerBox /> | ||
</div> | ||
</MainContent> | ||
</> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { ColorsTypes } from '@/shared/lib/styledComponents/theme'; | ||
import { css, useTheme } from 'styled-components'; | ||
type Props = { | ||
question: string; | ||
onClick: () => void; | ||
selectedCount: number; | ||
color: keyof Pick<ColorsTypes, 'primary03' | 'primary01' | 'grey10' | 'grey60'>; | ||
}; | ||
|
||
export default function QuickQuestionPicker({ question, onClick, selectedCount, color }: Props) { | ||
const theme = useTheme(); | ||
const formattedSelectedCount = selectedCount.toLocaleString(); | ||
|
||
const componentTheme = (() => { | ||
switch (color) { | ||
case 'primary03': | ||
case 'grey60': | ||
return { | ||
backgroundColor: theme.colors[color], | ||
titleColor: theme.colors.white, | ||
captionColor: theme.colors.grey10, | ||
}; | ||
case 'primary01': | ||
case 'grey10': | ||
return { | ||
backgroundColor: theme.colors[color], | ||
titleColor: theme.colors.grey70, | ||
captionColor: theme.colors.grey60, | ||
}; | ||
|
||
default: | ||
const _exhausted: never = color; | ||
throw new Error(`처리되지 않은 색상 타입입니다. ${_exhausted}`); | ||
} | ||
})(); | ||
|
||
return ( | ||
<button | ||
type="button" | ||
onClick={onClick} | ||
css={css` | ||
min-height: 94px; | ||
padding: 8px 12px; | ||
border: none; | ||
border-radius: 8px; | ||
background-color: ${componentTheme.backgroundColor}; | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: space-between; | ||
text-align: left; | ||
cursor: pointer; | ||
`} | ||
> | ||
<p | ||
css={css` | ||
${theme.fonts.subHead3} | ||
color: ${componentTheme.titleColor}; | ||
`} | ||
> | ||
{question} | ||
</p> | ||
<p | ||
css={css` | ||
${theme.fonts.caption} | ||
color: ${componentTheme.captionColor}; | ||
`} | ||
>{`${formattedSelectedCount}명이 질문 중`}</p> | ||
</button> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { createUserKeyCookie } from '@/auth/utils/createUserKeyCookie'; | ||
import { useCreateChatRoom } from '@/chat/hooks/useCreateChatRoom'; | ||
import { useSendChatMessage } from '@/chat/hooks/useSendChatMesasge'; | ||
import { TarotQuestionRecommendListData } from '@/tarot/apis/getTarotQuestionRecommends'; | ||
import { useTarotQuestionRecommends } from '@/tarot/hooks/useTarotQuestionRecommends'; | ||
import { useRouter } from 'next/navigation'; | ||
import { css } from 'styled-components'; | ||
import QuickQuestionPicker from '../QuickQuestionPicker'; | ||
import RefreshQuickQuestionButton from '../RefreshQuickQuestionButton'; | ||
|
||
export default function QuickQuestionPickerBox() { | ||
const { data } = useTarotQuestionRecommends(); | ||
const { mutate: createChatRoom } = useCreateChatRoom(); | ||
const { mutateAsync: sendChatMessage } = useSendChatMessage(); | ||
const router = useRouter(); | ||
|
||
if (!data) return null; | ||
|
||
const adaptQuestionRecommends = (data: TarotQuestionRecommendListData) => { | ||
const colors = ['primary03', 'grey10', 'primary01', 'grey60'] as const; | ||
return data.questions.map((question, i) => ({ | ||
...question, | ||
color: colors[i], | ||
onClick: async () => { | ||
await createUserKeyCookie(); | ||
createChatRoom(undefined, { | ||
onSuccess: (data) => { | ||
sendChatMessage( | ||
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. 현재 에러 처리가 되어있지 않습니다. |
||
{ | ||
roomId: data.roomId, | ||
message: question.question, | ||
intent: 'RECOMMEND_QUESTION', | ||
referenceQuestionId: question.recommendQuestionId, | ||
}, | ||
{ | ||
onSuccess: () => { | ||
router.push(`/chats/${data.roomId}`); | ||
}, | ||
} | ||
); | ||
}, | ||
}); | ||
}, | ||
})); | ||
}; | ||
|
||
return ( | ||
<div> | ||
<div | ||
css={css` | ||
display: grid; | ||
grid-template-columns: repeat(2, 1fr); | ||
grid-template-rows: repeat(2, 1fr); | ||
gap: 8px; | ||
`} | ||
> | ||
{adaptQuestionRecommends(data).map((question) => ( | ||
<QuickQuestionPicker | ||
key={question.recommendQuestionId} | ||
question={question.question} | ||
onClick={question.onClick} | ||
selectedCount={question.referenceCount} | ||
color={question.color} | ||
/> | ||
))} | ||
</div> | ||
<div | ||
css={css` | ||
width: fit-content; | ||
margin: 12px auto 0; | ||
`} | ||
> | ||
<RefreshQuickQuestionButton /> | ||
</div> | ||
</div> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { useQueryClient } from '@tanstack/react-query'; | ||
import { css } from 'styled-components'; | ||
|
||
export default function RefreshQuickQuestionButton() { | ||
const queryClient = useQueryClient(); | ||
|
||
const handleClick = () => { | ||
queryClient.invalidateQueries({ queryKey: ['tarotQuestionRecommends'] }); | ||
}; | ||
|
||
return ( | ||
<button | ||
type="button" | ||
onClick={handleClick} | ||
css={css` | ||
padding: 5px 8px; | ||
border: none; | ||
background-color: transparent; | ||
color: ${(props) => props.theme.colors.grey60}; | ||
cursor: pointer; | ||
`} | ||
> | ||
<span | ||
css={css` | ||
${(props) => props.theme.fonts.body1}; | ||
`} | ||
> | ||
추천 질문 변경 | ||
</span> | ||
{/* TODO: 아이콘 추가 */} | ||
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. #15 에서 아이콘 컴포넌트 작업이 마무리 되면 추가하겠습니다. |
||
</button> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,15 +2,15 @@ import apiClient from '@/shared/lib/axios/apiClient'; | |
import { z } from 'zod'; | ||
|
||
export type TarotQuestionRecommendListResponse = { | ||
question: { | ||
questions: { | ||
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. 필드명을 잘못 적었는데 zod가 잡아줬습니다! ㅎㅎ 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. ㅋㅋㅋㅋ 좋습니다~! |
||
recommendQuestionId: number; | ||
question: string; | ||
referenceCount: number; | ||
}[]; | ||
}; | ||
|
||
const schema = z.object({ | ||
question: z.array( | ||
questions: z.array( | ||
z.object({ | ||
recommendQuestionId: z.number(), | ||
question: z.string(), | ||
|
@@ -19,7 +19,7 @@ const schema = z.object({ | |
), | ||
}); | ||
|
||
type TarotQuestionRecommendListData = z.infer<typeof schema>; | ||
export type TarotQuestionRecommendListData = z.infer<typeof schema>; | ||
|
||
const validate = (data: TarotQuestionRecommendListResponse): TarotQuestionRecommendListData => { | ||
const validatedData = schema.parse(data); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
타입의 마술사 이 준 근