diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 7c3a806..c7e4ba7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -2,7 +2,7 @@ name: SolSolHigh-FE Deploy on: push: - branches: develop + branches: feat/#57-main-menus jobs: deploy: @@ -17,4 +17,4 @@ jobs: password: ${{ secrets.SSH_PASSWORD }} script: | cd ~/solsol-high - ./static.sh develop + ./static.sh feat/#57-main-menus diff --git a/ssh-web/public/assets/images/common/addphoto.png b/ssh-web/public/assets/images/common/addphoto.png new file mode 100644 index 0000000..9e17b04 Binary files /dev/null and b/ssh-web/public/assets/images/common/addphoto.png differ diff --git a/ssh-web/public/assets/images/common/defaultImg.webp b/ssh-web/public/assets/images/common/defaultImg.webp new file mode 100644 index 0000000..dbb9a5e Binary files /dev/null and b/ssh-web/public/assets/images/common/defaultImg.webp differ diff --git a/ssh-web/public/assets/images/common/promiseImg.png b/ssh-web/public/assets/images/common/promiseImg.png new file mode 100644 index 0000000..7e30863 Binary files /dev/null and b/ssh-web/public/assets/images/common/promiseImg.png differ diff --git a/ssh-web/src/components/atoms/ProfileIcon.zip b/ssh-web/src/components/atoms/ProfileIcon.zip deleted file mode 100644 index c89f65f..0000000 Binary files a/ssh-web/src/components/atoms/ProfileIcon.zip and /dev/null differ diff --git a/ssh-web/src/components/atoms/ProfileIcon/ProfileIcon.styles.ts b/ssh-web/src/components/atoms/ProfileIcon/ProfileIcon.styles.ts deleted file mode 100644 index f55365f..0000000 --- a/ssh-web/src/components/atoms/ProfileIcon/ProfileIcon.styles.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { tv } from 'tailwind-variants'; - -export const profileIconStyles = tv({ - variants: { - color: { - primary: 'text-primary-500', - secondary: 'text-secondary-500', - danger: 'text-danger-500', - warning: 'text-warning-500', - dark: 'text-black', - light: 'text-white', - }, - size: { - xs: 'text-xs', - sm: 'text-sm', - md: 'text-base', - lg: 'text-lg', - xl: 'text-xl', - }, - weight: { - light: 'font-light', - regular: 'font-normal', - medium: 'font-medium', - semibold: 'font-semibold', - bold: 'font-bold', - }, - }, -}); diff --git a/ssh-web/src/components/atoms/ProfileIcon/ProfileIcon.types.ts b/ssh-web/src/components/atoms/ProfileIcon/ProfileIcon.types.ts deleted file mode 100644 index 3b3199b..0000000 --- a/ssh-web/src/components/atoms/ProfileIcon/ProfileIcon.types.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { TColor } from '../../../themes/themeBase'; - -export type TProfileIconSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'; - -export type TProfileIconWeight = - | 'light' - | 'regular' - | 'medium' - | 'semibold' - | 'bold'; - -export interface ProfileIconProps extends React.ComponentProps<'div'> { - children: string; - size?: TProfileIconSize; - weight?: TProfileIconWeight; - color?: TColor; - classNameStyles?: string; -} diff --git a/ssh-web/src/components/atoms/ProfileIcon/abc.png b/ssh-web/src/components/atoms/ProfileIcon/abc.png deleted file mode 100644 index bd90ccf..0000000 Binary files a/ssh-web/src/components/atoms/ProfileIcon/abc.png and /dev/null differ diff --git a/ssh-web/src/components/atoms/ProfileIcon/index.stories.tsx b/ssh-web/src/components/atoms/ProfileIcon/index.stories.tsx deleted file mode 100644 index 70d5697..0000000 --- a/ssh-web/src/components/atoms/ProfileIcon/index.stories.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; - -import { ProfileIcon } from './index'; - -const meta = { - component: ProfileIcon, -} satisfies Meta; - -export default meta; - -type Story = StoryObj; - -export const Default: Story = { - args: { - children: '글자', - size: 'md', - color: 'primary', - weight: 'regular', - }, -}; diff --git a/ssh-web/src/components/atoms/ProfileIcon/index.tsx b/ssh-web/src/components/atoms/ProfileIcon/index.tsx deleted file mode 100644 index 007e1cf..0000000 --- a/ssh-web/src/components/atoms/ProfileIcon/index.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import { ProfileIconProps } from './ProfileIcon.types'; -import { profileIconStyles } from './ProfileIcon.styles'; - -export const ProfileIcon = ({ - children, - size = 'md', - weight = 'regular', - classNameStyles, -}: ProfileIconProps) => { - const profileIconStyle = profileIconStyles({ - size, - weight, - }); - - return ( -
- -
{children}
-
- ); -}; diff --git a/ssh-web/src/components/molecules/AddPromiseCard/AddPromiseCard.types.tsx b/ssh-web/src/components/molecules/AddPromiseCard/AddPromiseCard.types.tsx new file mode 100644 index 0000000..3760e67 --- /dev/null +++ b/ssh-web/src/components/molecules/AddPromiseCard/AddPromiseCard.types.tsx @@ -0,0 +1,3 @@ +export interface AddPromiseCardProps { + handleModal: () => void; +} diff --git a/ssh-web/src/components/molecules/AddPromiseCard/index.tsx b/ssh-web/src/components/molecules/AddPromiseCard/index.tsx new file mode 100644 index 0000000..f035fd4 --- /dev/null +++ b/ssh-web/src/components/molecules/AddPromiseCard/index.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { Typography } from '../../atoms/Typography'; +import { AddPromiseCardProps } from './AddPromiseCard.types'; + +export const AddPromiseCard = ({ handleModal }: AddPromiseCardProps) => { + return ( +
+
+
+ + +
+
+ + 약속 요청하기 + +
+
+
+ ); +}; diff --git a/ssh-web/src/components/molecules/PromiseItem/PromiseItem.styles.tsx b/ssh-web/src/components/molecules/PromiseItem/PromiseItem.styles.tsx new file mode 100644 index 0000000..c910544 --- /dev/null +++ b/ssh-web/src/components/molecules/PromiseItem/PromiseItem.styles.tsx @@ -0,0 +1,12 @@ +import { tv } from 'tailwind-variants'; + +export const cardStyle = tv({ + base: 'flex flex-col p-4 bg-primary-100 items-center rounded-lg shadow-md relative hover:bg-primary-200 transition duration-300', + variants: { + size: { + M: 'w-32 h-48', + T: 'w-32 h-48', + D: 'w-32 h-48', + }, + }, +}); diff --git a/ssh-web/src/components/molecules/PromiseItem/PromiseItem.types.tsx b/ssh-web/src/components/molecules/PromiseItem/PromiseItem.types.tsx new file mode 100644 index 0000000..b308b5e --- /dev/null +++ b/ssh-web/src/components/molecules/PromiseItem/PromiseItem.types.tsx @@ -0,0 +1,7 @@ +import { IPromiseLogs } from '../../../interfaces/promiseTicketInterface'; + +export interface PromiseItemProps { + handleModal: (log: IPromiseLogs) => void; + isConfirm: boolean; + log: IPromiseLogs; +} diff --git a/ssh-web/src/components/molecules/PromiseItem/index.tsx b/ssh-web/src/components/molecules/PromiseItem/index.tsx new file mode 100644 index 0000000..5527d24 --- /dev/null +++ b/ssh-web/src/components/molecules/PromiseItem/index.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { useRecoilValue } from 'recoil'; +import { EResize } from '../../../themes/themeBase'; +import { Typography } from '../../atoms/Typography'; +import { cardStyle } from './PromiseItem.styles'; +import { PromiseItemProps } from './PromiseItem.types'; +import stemp from './stemp.png'; +import { resizeState } from '../../../atoms/resize'; +import defaultImg from './../../organisms/PromiseDetailModal/promiseImg.png'; + +export const PromiseItem = ({ + handleModal, + isConfirm, + log, +}: PromiseItemProps) => { + const size = useRecoilValue(resizeState); + return ( +
+
{ + handleModal(log); + }} + > +
+ {' '} + {isConfirm && ( + <> +
+ + + )} + +
+ + {log.description} + +
+
+ ); +}; diff --git a/ssh-web/src/components/molecules/PromiseItem/stemp.png b/ssh-web/src/components/molecules/PromiseItem/stemp.png new file mode 100644 index 0000000..9171057 Binary files /dev/null and b/ssh-web/src/components/molecules/PromiseItem/stemp.png differ diff --git a/ssh-web/src/components/molecules/QuizModal/ModalContent.tsx b/ssh-web/src/components/molecules/QuizModal/ModalContent.tsx index 2c4c38b..e05a5ca 100644 --- a/ssh-web/src/components/molecules/QuizModal/ModalContent.tsx +++ b/ssh-web/src/components/molecules/QuizModal/ModalContent.tsx @@ -18,9 +18,12 @@ export const ModalContent = ({ const [startY, setStartY] = useState(null); const [moveY, setMoveY] = useState(0); const modalRef = useRef(null); + const headerRef = useRef(null); const handleTouchStart = (e: React.TouchEvent) => { - setStartY(e.touches[0].clientY); + if (headerRef.current && headerRef.current.contains(e.target as Node)) { + setStartY(e.touches[0].clientY); + } }; const handleTouchMove = (e: React.TouchEvent) => { @@ -48,6 +51,7 @@ export const ModalContent = ({ } setMoveY(0); } + setStartY(null); }; const backgroundColors = { @@ -56,9 +60,9 @@ export const ModalContent = ({ }; const modalStyles = { - M: `fixed px-4 pt-8 bottom-0 rounded-t-3xl z-20 w-full mob:h-[81%] ${backgroundColors[color]} transition-transform overflow-auto`, + M: `fixed px-4 pt-0 bottom-0 rounded-t-3xl z-20 w-full mob:h-[90%] ${backgroundColors[color]} transition-transform`, T: `absolute px-[5%] py-[2%] max-w-[700px] min-w-[430px] z-20 top-12 left-1/2 -translate-x-1/2 w-2/3 h-max min-h-[calc(100vh-24rem)] flex justify-center items-center rounded-xl ${backgroundColors[color]}`, - D: `absolute px-[5%] py-[2%] max-w-[700px] min-w-[430px] z-20 top-10 left-1/2 -translate-x-1/2 w-2/3 h-max min-h-[calc(100vh-12rem)] flex justify-center items-center rounded-xl ${backgroundColors[color]}`, + D: `absolute px-[5%] py-[2%] max-w-[700px] min-w-[430px] z-20 top-10 left-1/2 -translate-x-1/2 w-2/3 h-max min-h-[calc(100vh-10rem)] flex justify-center items-center rounded-xl ${backgroundColors[color]}`, }; return ( @@ -69,10 +73,14 @@ export const ModalContent = ({ onTouchMove={handleTouchMove} onTouchEnd={handleTouchEnd} > - {size === EResize.M && ( -
- )} - {size !== EResize.M && } +
+ {size === EResize.M && ( +
+
+
+ )} + {size !== EResize.M && } +
{children}
); diff --git a/ssh-web/src/components/organisms/AddPromiseModal/AddPromiseModal.types.tsx b/ssh-web/src/components/organisms/AddPromiseModal/AddPromiseModal.types.tsx new file mode 100644 index 0000000..5181293 --- /dev/null +++ b/ssh-web/src/components/organisms/AddPromiseModal/AddPromiseModal.types.tsx @@ -0,0 +1,4 @@ +export interface AddPromiseModalProps { + countTicket: number; + onUpload: () => void; +} diff --git a/ssh-web/src/components/organisms/AddPromiseModal/index.tsx b/ssh-web/src/components/organisms/AddPromiseModal/index.tsx new file mode 100644 index 0000000..ff329e8 --- /dev/null +++ b/ssh-web/src/components/organisms/AddPromiseModal/index.tsx @@ -0,0 +1,75 @@ +import React from 'react'; +import { Button } from '../../atoms/Button'; +import { Typography } from '../../atoms/Typography'; +import { AddPromiseModalProps } from './AddPromiseModal.types'; + +export const AddPromiseModal = ({ + countTicket, + onUpload, +}: AddPromiseModalProps) => { + if (countTicket === 0) { + return ( +
+ + 보유한 약속권이 없어요! + + +
+ ); + } + + return ( +
+
+ + + 약속 + + + + 하기를 부모님과{' '} + + 약속 + + 해요 + + +
+
+ ); +}; diff --git a/ssh-web/src/components/organisms/ConfirmPromiseModal/ConfirmPromiseModal.types.tsx b/ssh-web/src/components/organisms/ConfirmPromiseModal/ConfirmPromiseModal.types.tsx new file mode 100644 index 0000000..9df176f --- /dev/null +++ b/ssh-web/src/components/organisms/ConfirmPromiseModal/ConfirmPromiseModal.types.tsx @@ -0,0 +1,6 @@ +import { IPromiseLogs } from '../../../interfaces/promiseTicketInterface'; + +export interface ConfirmPromiseModalProps { + log: IPromiseLogs | null; + onUpload: (id: number) => void; +} diff --git a/ssh-web/src/components/organisms/ConfirmPromiseModal/index.tsx b/ssh-web/src/components/organisms/ConfirmPromiseModal/index.tsx new file mode 100644 index 0000000..354978c --- /dev/null +++ b/ssh-web/src/components/organisms/ConfirmPromiseModal/index.tsx @@ -0,0 +1,98 @@ +import React, { ChangeEvent, useState } from 'react'; +import { useRecoilValue } from 'recoil'; +import { resizeState } from '../../../atoms/resize'; +import { EResize } from '../../../themes/themeBase'; +import { Button } from '../../atoms/Button'; +import { Typography } from '../../atoms/Typography'; +import { ConfirmPromiseModalProps } from './ConfirmPromiseModal.types'; +import { MdCancel } from 'react-icons/md'; +import { MdAddCircle } from 'react-icons/md'; + +export const ConfirmPromiseModal = ({ + log, + onUpload, +}: ConfirmPromiseModalProps) => { + const [uploadImgUrl, setUploadImgUrl] = useState(null); + const size = useRecoilValue(resizeState); + + const onChangeImageUpload = (e: ChangeEvent) => { + const file = e.target.files ? e.target.files[0] : null; + if (file) { + setUploadImgUrl(file); + } + }; + + const handleDelete = () => { + setUploadImgUrl(null); + }; + + return ( +
+
+ {uploadImgUrl ? ( +
handleDelete()}> + uploaded + +
+ ) : ( + + )} + + 약속 + + + {log?.description} + + + 아이가 약속을 기다리고 있어요 + + +
+
+ ); +}; diff --git a/ssh-web/src/components/organisms/PromiseDetailModal/PromiseDetailModal.styles.tsx b/ssh-web/src/components/organisms/PromiseDetailModal/PromiseDetailModal.styles.tsx new file mode 100644 index 0000000..68befb9 --- /dev/null +++ b/ssh-web/src/components/organisms/PromiseDetailModal/PromiseDetailModal.styles.tsx @@ -0,0 +1,12 @@ +import { tv } from 'tailwind-variants'; + +export const stempStyles = tv({ + base: 'absolute opacity-70', + variants: { + size: { + M: 'w-48 h-48 left-[60%] bottom-[-20%]', + T: 'w-48 h-48 left-[67%] bottom-[-24%]', + D: 'w-48 h-48 left-[67%] bottom-[-24%]', + }, + }, +}); diff --git a/ssh-web/src/components/organisms/PromiseDetailModal/PromiseDetailModal.types.tsx b/ssh-web/src/components/organisms/PromiseDetailModal/PromiseDetailModal.types.tsx new file mode 100644 index 0000000..1d98e92 --- /dev/null +++ b/ssh-web/src/components/organisms/PromiseDetailModal/PromiseDetailModal.types.tsx @@ -0,0 +1,7 @@ +import { IPromiseLogs } from '../../../interfaces/promiseTicketInterface'; + +export interface PromiseDetailModalProps { + log: IPromiseLogs | null; + isParent: boolean; + closeModal: () => void; +} diff --git a/ssh-web/src/components/organisms/PromiseDetailModal/index.tsx b/ssh-web/src/components/organisms/PromiseDetailModal/index.tsx new file mode 100644 index 0000000..e962ac2 --- /dev/null +++ b/ssh-web/src/components/organisms/PromiseDetailModal/index.tsx @@ -0,0 +1,79 @@ +import React from 'react'; +import dayjs from 'dayjs'; +import { IPromiseLogs } from '../../../interfaces/promiseTicketInterface'; +import { Button } from '../../atoms/Button'; +import { Typography } from '../../atoms/Typography'; +import { PromiseDetailModalProps } from './PromiseDetailModal.types'; +import stemp from '../../molecules/PromiseItem/stemp.png'; +import { stempStyles } from './PromiseDetailModal.styles'; +import { EResize } from '../../../themes/themeBase'; +import { resizeState } from '../../../atoms/resize'; +import { useRecoilValue } from 'recoil'; +import defaultImg from './promiseImg.png'; +export const PromiseDetailModal = ({ + log, + isParent, + closeModal, +}: PromiseDetailModalProps) => { + const isConfirm = log?.usedAt; + const size = useRecoilValue(resizeState); + + // 날짜 변환 + const formattedDate = isConfirm + ? dayjs(log.usedAt).format('YYYY년 MM월 DD일') + : null; + + return ( +
+
+
+ + {isConfirm && } +
+ + 약속 + + {formattedDate && ( + + {formattedDate} + + )} + + {log?.description} + + {!isParent && !isConfirm && ( + + 부모님의 약속을 기다리고있어요 + + )} + + {!isParent && isConfirm && ( + + 부모님이 약속을 지켜주셨어요! + + )} + +
+
+ ); +}; diff --git a/ssh-web/src/components/organisms/PromiseDetailModal/promiseImg.png b/ssh-web/src/components/organisms/PromiseDetailModal/promiseImg.png new file mode 100644 index 0000000..7e30863 Binary files /dev/null and b/ssh-web/src/components/organisms/PromiseDetailModal/promiseImg.png differ diff --git a/ssh-web/src/components/organisms/QuizLogsDetailModal/QuizLogsDetailModal.types.tsx b/ssh-web/src/components/organisms/QuizLogsDetailModal/QuizLogsDetailModal.types.tsx new file mode 100644 index 0000000..a7b3fe9 --- /dev/null +++ b/ssh-web/src/components/organisms/QuizLogsDetailModal/QuizLogsDetailModal.types.tsx @@ -0,0 +1,7 @@ +import { IQuizLogResponseList } from '../../../interfaces/quizInterface'; + +export interface QuizLogsDetailModalProps { + quizLogs: IQuizLogResponseList; + openQuizId: number; + toggleQuizDetail: (id: number) => void; +} diff --git a/ssh-web/src/components/organisms/QuizLogsDetailModal/index.tsx b/ssh-web/src/components/organisms/QuizLogsDetailModal/index.tsx index 9182267..9c29106 100644 --- a/ssh-web/src/components/organisms/QuizLogsDetailModal/index.tsx +++ b/ssh-web/src/components/organisms/QuizLogsDetailModal/index.tsx @@ -1,15 +1,9 @@ import React from 'react'; -import { IQuizLogResponseList } from '../../../interfaces/quizInterface'; import { Button } from '../../atoms/Button'; import { Typography } from '../../../components/atoms/Typography'; import dayjs from 'dayjs'; import { QuizDetail } from '../../molecules/QuizDetail'; - -interface QuizLogsDetailModalProps { - quizLogs: IQuizLogResponseList; - openQuizId: number; - toggleQuizDetail: (id: number) => void; -} +import { QuizLogsDetailModalProps } from './QuizLogsDetailModal.types'; export const QuizLogsDetailModal: React.FC = ({ quizLogs, @@ -24,15 +18,15 @@ export const QuizLogsDetailModal: React.FC = ({ {dayjs(item.correctedAt).format('YYYY년 M월 D일')} = ({ const today = new Date(); return ( -
-
+
+
{dayjs(today).format('YYYY년 M월 D일')} diff --git a/ssh-web/src/components/organisms/QuizTab/styles.tsx b/ssh-web/src/components/organisms/QuizTab/styles.tsx new file mode 100644 index 0000000..e317fa5 --- /dev/null +++ b/ssh-web/src/components/organisms/QuizTab/styles.tsx @@ -0,0 +1,12 @@ +import { tv } from 'tailwind-variants'; + +export const Card = tv({ + base: 'flex items-center justify-center w-full h-auto', + variants: { + size: { + M: '', + T: 'flex-col', + D: 'p-8 h-[48rem]', + }, + }, +}); diff --git a/ssh-web/src/interfaces/promiseTicketInterface.ts b/ssh-web/src/interfaces/promiseTicketInterface.ts new file mode 100644 index 0000000..ac50b33 --- /dev/null +++ b/ssh-web/src/interfaces/promiseTicketInterface.ts @@ -0,0 +1,10 @@ +export interface IPromiseLogs { + id: number; + imageUrl: string; + publishAt: string; + requestedAt?: string; + usedAt?: string; + description: string; +} + +export type IPromiseLogsList = IPromiseLogs[]; diff --git a/ssh-web/src/mocks/index.ts b/ssh-web/src/mocks/index.ts index a739723..0925f4f 100644 --- a/ssh-web/src/mocks/index.ts +++ b/ssh-web/src/mocks/index.ts @@ -30,6 +30,132 @@ mock.onGet(`/${REQUEST_DOMAINS.auth}/examples`).reply(() => { }); }); // ========== Test Domain ========== +// ========== 약속권 도메인 ========== + +//자식이 자신의 약속권 개수 조회 +mock.onGet(`/api/promise-tickets/count`).reply(() => { + return new Promise((resolve) => { + setTimeout(() => { + resolve([ + 200, + { + count: 3, + }, + ]); + }, 500); + }); +}); + +mock.onGet(`/api/promise-tickets?page=0&size=5`).reply(() => { + return new Promise((resolve) => { + setTimeout(() => { + resolve([ + 200, + { + totalPages: 1, + totalElements: 1, + size: 5, + content: [ + { + id: 1, + imageUrl: + 'https://media.tenor.com/Kd1AutC90hsAAAAj/pepe-thumbs-up.gif', + publishedAt: '2024-08-12T12:00:00', + requestedAt: '2024-08-15T12:00:00', + description: '엄마랑 놀이공원 놀러가고 시포', + }, + { + id: 2, + imageUrl: + 'https://media.tenor.com/Kd1AutC90hsAAAAj/pepe-thumbs-up.gif', + publishedAt: '2024-08-12T12:00:00', + requestedAt: '2024-08-15T12:00:00', + description: '아빠랑 놀이공원 놀러가고 시포', + }, + { + id: 3, + imageUrl: + 'https://media.tenor.com/Kd1AutC90hsAAAAj/pepe-thumbs-up.gif', + publishedAt: '2024-08-12T12:00:00', + requestedAt: '2024-08-15T12:00:00', + usedAt: '2024-08-18T12:00:00', + description: '할머니랑 놀이공원 놀러가고 시포', + }, + { + id: 4, + imageUrl: + 'https://media.tenor.com/Kd1AutC90hsAAAAj/pepe-thumbs-up.gif', + publishedAt: '2024-08-12T12:00:00', + requestedAt: '2024-08-15T12:00:00', + usedAt: '2024-08-18T12:00:00', + description: '할이버지랑 놀이공원 놀러가고 시포', + }, + { + id: 5, + imageUrl: + 'https://media.tenor.com/Kd1AutC90hsAAAAj/pepe-thumbs-up.gif', + publishedAt: '2024-08-12T12:00:00', + requestedAt: '2024-08-15T12:00:00', + usedAt: '2024-08-18T12:00:00', + description: '누나랑 놀이공원 놀러가고 시포', + }, + { + id: 6, + imageUrl: + 'https://media.tenor.com/Kd1AutC90hsAAAAj/pepe-thumbs-up.gif', + publishedAt: '2024-08-12T12:00:00', + requestedAt: '2024-08-15T12:00:00', + usedAt: '2024-08-18T12:00:00', + description: '형이랑 놀이공원 놀러가고 시포', + }, + { + id: 7, + imageUrl: + 'https://media.tenor.com/Kd1AutC90hsAAAAj/pepe-thumbs-up.gif', + publishedAt: '2024-08-12T12:00:00', + requestedAt: '2024-08-15T12:00:00', + usedAt: '2024-08-18T12:00:00', + description: '친구랑 놀이공원 놀러가고 시포', + }, + { + id: 8, + imageUrl: + 'https://media.tenor.com/Kd1AutC90hsAAAAj/pepe-thumbs-up.gif', + publishedAt: '2024-08-12T12:00:00', + requestedAt: '2024-08-15T12:00:00', + usedAt: '2024-08-18T12:00:00', + description: '혼자 놀이공원 놀러가고 시포', + }, + ], + number: 0, + sort: { + empty: true, + sorted: false, + unsorted: true, + }, + first: true, + last: true, + numberOfElements: 1, + pageable: { + pageNumber: 0, + pageSize: 5, + sort: { + empty: true, + sorted: false, + unsorted: true, + }, + offset: 0, + paged: true, + unpaged: false, + }, + empty: false, + }, + ]); + }, 500); + }); +}); + +// ========== 약속권 도메인 ========== // ========== 퀴즈 도메인 ========== //오늘자 퀴즈 조회 diff --git a/ssh-web/src/pages/Promise/index.tsx b/ssh-web/src/pages/Promise/index.tsx new file mode 100644 index 0000000..7674e0c --- /dev/null +++ b/ssh-web/src/pages/Promise/index.tsx @@ -0,0 +1,162 @@ +import React, { useEffect, useState } from 'react'; +import { + containerStyles, + contentStyles, + gridStyles, + layoutStyles, +} from './styles'; +import { EResize } from '../../themes/themeBase'; +import { resizeState } from '../../atoms/resize'; +import { useRecoilValue } from 'recoil'; +import { Typography } from '../../components/atoms/Typography'; +import { Modal } from '../../components/molecules/QuizModal'; +import { Mascot } from '../../components/molecules/Mascot'; +import { AddPromiseModal } from '../../components/organisms/AddPromiseModal'; +import { AddPromiseCard } from '../../components/molecules/AddPromiseCard'; +import { PromiseItem } from '../../components/molecules/PromiseItem'; +import { api } from '../../apis/interceptors'; +import { + IPromiseLogs, + IPromiseLogsList, +} from '../../interfaces/promiseTicketInterface'; +import { PromiseDetailModal } from '../../components/organisms/PromiseDetailModal'; +import { ConfirmPromiseModal } from '../../components/organisms/ConfirmPromiseModal'; +import { ChangeChild } from '../../components/molecules/ChangeChild'; +import { HiOutlineTicket } from 'react-icons/hi'; + +export const PromiseTicket = () => { + const [isOpen, setIsOpen] = useState(false); + const [isDetailModal, setIsDetailModal] = useState(false); + const [promiseLogs, setPromiseLogs] = useState([]); + const [countTicket, setCountTicket] = useState(0); + const [selectedPromise, setSelectedPromise] = useState( + null, + ); + + // isParent를 50% 확률로 true 또는 false로 설정 + const [isParent] = useState(() => Math.random() >= 0.5); + + const size = useRecoilValue(resizeState); + const isConfirm = selectedPromise?.usedAt; + + useEffect(() => { + api.get(`/api/promise-tickets/count`).then((response) => { + setCountTicket(response.data.count); + }); + }, []); + + useEffect(() => { + api.get(`/api/promise-tickets?page=0&size=5`).then((response) => { + setPromiseLogs(response.data.content); + console.log(response.data.content); + }); + }, []); + + const handleDetailModal = (log: IPromiseLogs) => { + setIsOpen(true); + setIsDetailModal(true); + setSelectedPromise(log); + }; + + const handleAddModal = () => { + setIsOpen(true); + setIsDetailModal(false); + }; + + //todo + const handleConfirmUpload = (id: number) => { + setIsOpen(false); + }; + + //todo + const handlePromiseUpload = () => { + setIsOpen(false); + }; + + const handleCloseModal = () => { + setIsOpen(false); + }; + + const renderModalContent = () => { + if (isDetailModal) { + if (isConfirm || !isParent) { + return ( + + ); + } else { + return ( + + ); + } + } else { + return ( + + ); + } + }; + + return ( + <> + + {renderModalContent()} + +
+ {size === EResize.D && ( + + )} +
+
+ + 약속권 + + {isParent && } +
+
+ + 보유한 약속권 + +
+ + {countTicket} + + + 개 + + + +
+
+
+ {!isParent && } + {promiseLogs.map((log) => ( + + ))} +
+
+
+ + ); +}; diff --git a/ssh-web/src/pages/Promise/styles.ts b/ssh-web/src/pages/Promise/styles.ts new file mode 100644 index 0000000..c65251d --- /dev/null +++ b/ssh-web/src/pages/Promise/styles.ts @@ -0,0 +1,45 @@ +import { tv } from 'tailwind-variants'; + +export const layoutStyles = tv({ + base: 'flex items-center justify-center w-full h-auto ', + variants: { + size: { + M: '', + T: 'flex-col', + D: '', + }, + }, +}); + +export const containerStyles = tv({ + base: 'bg-white flex flex-col items-center w-full overflow-auto', + variants: { + size: { + M: 'h-full p-4 ', + T: 'h-full p-6 ', + D: 'rounded-2xl px-4 max-w-[48rem] h-[48rem]', + }, + }, +}); + +export const contentStyles = tv({ + base: 'flex justify-around items-center w-full my-4 p-4 bg-primary-400 rounded-lg max-w-[90%]', + variants: { + size: { + M: '', + T: '', + D: '', + }, + }, +}); + +export const gridStyles = tv({ + base: 'grid ', + variants: { + size: { + M: 'grid-cols-2 gap-[15vw]', + T: 'grid-cols-4 gap-[8vw]', + D: 'grid-cols-4 gap-12', + }, + }, +}); diff --git a/ssh-web/src/pages/QuizMain/index.tsx b/ssh-web/src/pages/QuizMain/index.tsx index 7b7bb46..ffa8f78 100644 --- a/ssh-web/src/pages/QuizMain/index.tsx +++ b/ssh-web/src/pages/QuizMain/index.tsx @@ -1,31 +1,25 @@ import React, { useEffect, useMemo, useState } from 'react'; -import { ToggleTab } from '../../components/atoms/ToggleTab'; -import { Typography } from '../../components/atoms/Typography'; -import { resizeState } from '../../atoms/resize'; -import { EResize } from '../../themes/themeBase'; import { useRecoilValue } from 'recoil'; import { api } from '../../apis/interceptors'; -import { AvatarWithLabel } from '../../components/molecules/AvatarWithLabel'; -import { Button } from '../../components/atoms/Button'; -import { Main, containerStyles } from './styles'; -import { QuizTab } from '../../components/organisms/QuizTab'; +import { resizeState } from '../../atoms/resize'; +import { ToggleTab } from '../../components/atoms/ToggleTab'; +import { Typography } from '../../components/atoms/Typography'; +import { ChangeChild } from '../../components/molecules/ChangeChild'; +import { Mascot } from '../../components/molecules/Mascot'; +import { Modal } from '../../components/molecules/QuizModal'; import { KeywordEditModal, KeywordsTab, } from '../../components/organisms/QuizKeywordTab'; +import { QuizLogsDetailModal } from '../../components/organisms/QuizLogsDetailModal'; +import { QuizTab } from '../../components/organisms/QuizTab'; import { IKeywordResponseList, - IStrickResponseList, -} from '../../interfaces/quizInterface'; -import { - IQuizLogResponse, IQuizLogResponseList, + IStrickResponseList, } from '../../interfaces/quizInterface'; -import { Modal } from '../../components/molecules/QuizModal'; -import { QuizLogsDetailModal } from '../../components/organisms/QuizLogsDetailModal'; -import { ChangeChild } from '../../components/molecules/ChangeChild'; -import { mascotWrapperStyles } from '../QuizSolving/styles'; -import { Mascot } from '../../components/molecules/Mascot'; +import { EResize } from '../../themes/themeBase'; +import { containerStyles } from './styles'; const labels = ['쏠쏠 퀴즈', '키워드 및 내역']; @@ -71,8 +65,10 @@ export const QuizMain: React.FC = () => { const [isKeywordModal, setIsKeywordModal] = useState(false); const size = useRecoilValue(resizeState); + //todo const onRemoveKeyword = () => {}; + //todo const onAddKeyword = () => {}; const onClose = () => { @@ -135,7 +131,7 @@ export const QuizMain: React.FC = () => { }, [quizLogs]); // quizLogs가 업데이트될 때마다 실행 return ( -
+
{isKeywordModal ? ( { /> )} - {size === EResize.D && ( - - )} -
-
+ +
+
쏠쏠 퀴즈
-
+
{ @@ -175,7 +173,7 @@ export const QuizMain: React.FC = () => { />
-
+
{activeTab === 0 ? ( { const [selectedOption, setSelectedOption] = useState<'O' | 'X' | null>(null); diff --git a/ssh-web/src/utils/router.tsx b/ssh-web/src/utils/router.tsx index f5cd283..8619f7b 100644 --- a/ssh-web/src/utils/router.tsx +++ b/ssh-web/src/utils/router.tsx @@ -9,6 +9,7 @@ import { QuizSolving } from '../pages/QuizSolving'; import { Mission } from '../pages/Mission'; import { Information } from '../pages/Information'; import { Manage } from '../pages/Information/Manage'; +import { PromiseTicket } from '../pages/Promise'; import { Request } from '../pages/Information/Request'; import { Home } from '../pages/Home'; @@ -78,6 +79,10 @@ export const router = createBrowserRouter([ path: '/manage', element: , }, + { + path: '/promise', + element: , + }, { path: '/request', element: ,