diff --git a/public/png/mood_BAD.png b/public/png/mood_BAD.png new file mode 100644 index 0000000..0926ec9 Binary files /dev/null and b/public/png/mood_BAD.png differ diff --git a/public/png/mood_GOOD.png b/public/png/mood_GOOD.png new file mode 100644 index 0000000..ccdb080 Binary files /dev/null and b/public/png/mood_GOOD.png differ diff --git a/public/svg/add.svg b/public/svg/add.svg new file mode 100644 index 0000000..9f98254 --- /dev/null +++ b/public/svg/add.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/features/setting/Container.tsx b/src/features/setting/Container.tsx index 35df6c2..530abf1 100644 --- a/src/features/setting/Container.tsx +++ b/src/features/setting/Container.tsx @@ -1,54 +1,34 @@ -import { Controller } from 'react-hook-form'; +import { useAtom } from 'jotai'; +import { useLayoutEffect } from 'react'; + +import { bottomBtnAtom, titleAtom } from '../shared/layout/atom'; -import './style/index.scss'; -import { colorset } from './constants'; import useSetting from './hooks/useSetting'; +import './style/index.scss'; +import PreviewSection from './components/PreviewSection'; +import SubmitForm from './components/SubmitForm'; const Container = () => { - const { register, handleSubmit, control, onSubmit } = useSetting(); + const [, setTitle] = useAtom(titleAtom); + const [, setBottomBtn] = useAtom(bottomBtnAtom); - return ( -
-
-
- Today's comment: - -
+ const { handlePreviewClick, handleSubmit, onSubmit, register, control } = + useSetting(); -
- {/* */} - ( -
- {colorset.map((color) => ( - - ))} -
- )} - /> -
+ useLayoutEffect(() => { + setTitle({ title: 'Active', back: true }); + setBottomBtn({ text: 'Save' }); + }, []); - -
+ return ( +
+ +
); }; diff --git a/src/features/setting/components/PreviewSection.tsx b/src/features/setting/components/PreviewSection.tsx new file mode 100644 index 0000000..3ae1db0 --- /dev/null +++ b/src/features/setting/components/PreviewSection.tsx @@ -0,0 +1,23 @@ +import { FunctionComponent } from 'react'; + +import { previewSubText, previewTitleText } from '../constants'; + +type PreviewSectionProps = { + handlePreviewClick: () => void; +}; + +const PreviewSection: FunctionComponent = ({ + handlePreviewClick, +}) => { + return ( +
+
{previewTitleText}
+
{previewSubText}
+ +
+ ); +}; + +export default PreviewSection; diff --git a/src/features/setting/components/SubmitForm/DailySticker.tsx b/src/features/setting/components/SubmitForm/DailySticker.tsx new file mode 100644 index 0000000..29614e7 --- /dev/null +++ b/src/features/setting/components/SubmitForm/DailySticker.tsx @@ -0,0 +1,54 @@ +import { FunctionComponent } from 'react'; +import { Controller } from 'react-hook-form'; +import { Control } from 'react-hook-form'; + +import { Inputs } from '../../types'; +import { stickerList, wordStickerText } from '../../constants'; + +import '../../style/index.scss'; + +type DailyStickerProps = { + control: Control; +}; + +const DailySticker: FunctionComponent = ({ control }) => { + return ( +
+ {wordStickerText} + ( +
+ {stickerList.map((sticker) => ( + + ))} +
+ )} + /> +
+ ); +}; + +export default DailySticker; diff --git a/src/features/setting/components/SubmitForm/DailyWord.tsx b/src/features/setting/components/SubmitForm/DailyWord.tsx new file mode 100644 index 0000000..cf61dc9 --- /dev/null +++ b/src/features/setting/components/SubmitForm/DailyWord.tsx @@ -0,0 +1,21 @@ +import { FunctionComponent } from 'react'; +import { UseFormRegister } from 'react-hook-form'; + +import { Inputs } from '../../types'; +import { wordTitleText } from '../../constants'; +import '../../style/index.scss'; + +type DailyWordProps = { + register: UseFormRegister; +}; + +const DailyWord: FunctionComponent = ({ register }) => { + return ( +
+ {wordTitleText} + +
+ ); +}; + +export default DailyWord; diff --git a/src/features/setting/components/SubmitForm/index.tsx b/src/features/setting/components/SubmitForm/index.tsx new file mode 100644 index 0000000..509a008 --- /dev/null +++ b/src/features/setting/components/SubmitForm/index.tsx @@ -0,0 +1,48 @@ +import { useAtom } from 'jotai'; +import { FunctionComponent, useEffect, useRef } from 'react'; +import { + UseFormHandleSubmit, + SubmitHandler, + UseFormRegister, + Control, +} from 'react-hook-form'; + +import { activeSaveAtom } from '../../../shared/layout/atom'; +import { Inputs } from '../../types'; + +import DailySticker from './DailySticker'; +import DailyWord from './DailyWord'; + +type SubmitFormProps = { + handleSubmit: UseFormHandleSubmit; + onSubmit: SubmitHandler; + register: UseFormRegister; + control: Control; +}; + +const SubmitForm: FunctionComponent = ({ + handleSubmit, + onSubmit, + register, + control, +}) => { + const [activeSave, setActiveSave] = useAtom(activeSaveAtom); + + const inputRef = useRef(null); + + useEffect(() => { + if (!activeSave) return; + setActiveSave(false); + inputRef.current?.click(); + }, [activeSave]); + + return ( +
+ + + + + ); +}; + +export default SubmitForm; diff --git a/src/features/setting/constants.ts b/src/features/setting/constants.ts index b4ca5bd..d957670 100644 --- a/src/features/setting/constants.ts +++ b/src/features/setting/constants.ts @@ -1 +1,9 @@ export const colorset = ['red', 'yellow', 'green']; +export const stickerList = ['mood_BAD', 'mood_GOOD']; + +export const previewTitleText = 'What is the Active Setting?'; +export const previewSubText = + 'Congratulations on winning! Press the button to get a giftCongratulations on winning! Press the button to get a giftCongratulations on winning!'; + +export const wordTitleText = 'Word of the day'; +export const wordStickerText = 'Word of the Sticker'; diff --git a/src/features/setting/hooks/useSetting.ts b/src/features/setting/hooks/useSetting.ts index f739f74..a90b551 100644 --- a/src/features/setting/hooks/useSetting.ts +++ b/src/features/setting/hooks/useSetting.ts @@ -1,21 +1,29 @@ import { useForm, SubmitHandler } from 'react-hook-form'; +import { useNavigate } from 'react-router-dom'; import { Inputs } from '../types'; +import { URL } from '../../shared/constants/url'; const useSetting = () => { const { register, handleSubmit, control } = useForm({ defaultValues: { - mude: 'green', - comment: '퇴근!', + mude: '', + comment: '', }, }); + const navigate = useNavigate(); const onSubmit: SubmitHandler = (data) => console.log(data); + const handlePreviewClick = () => { + navigate(URL.preview); + }; + return { register, handleSubmit, control, onSubmit, + handlePreviewClick, }; }; diff --git a/src/features/setting/style/index.scss b/src/features/setting/style/index.scss index c81079f..40cc527 100644 --- a/src/features/setting/style/index.scss +++ b/src/features/setting/style/index.scss @@ -1,5 +1,124 @@ .setting { &-container { width: 100%; + height: calc(100% - 159px); + background: #F7F7F7; } + + &-preview-container { + display: flex; + padding: 24px 16px; + flex-direction: column; + align-items: center; + align-self: stretch; + background-color: #FFFFFF; + } + + &-preview-title-text { + height: fit-content; + margin-bottom: 4px; + color: #191919; + text-align: center; + font-size: 24px; + font-style: normal; + font-weight: 700; + line-height: 36px; /* 150% */ + } + + &-preview-sub-text { + margin-bottom: 16px; + color: #9797AE; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 24px; /* 171.429% */ + } + + &-preview-btn { + display: flex; + width: 100%; + height: 48px; + padding: 0px 14px; + justify-content: center; + align-items: center; + align-self: stretch; + border: 1px solid #191919; + color: #191919; + background-color: #FFFFFF; + cursor: pointer; + } + + &-word-container { + height: 68px; + display: flex; + gap: 4px; + padding: 16px; + margin: 12px 0; + flex-direction: column; + align-items: flex-start; + align-self: stretch; + background: #FFFFFF; + } + + &-word-title { + color: #000; + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: 24px; /* 171.429% */ + } + + &-word-input { + display: flex; + align-items: center; + align-self: stretch; + border: 1px solid #191919; + height: 40px; + } + + &-sticker-container { + display: flex; + padding: 16px; + flex-direction: column; + align-items: flex-start; + gap: 8px; + flex: 1 0 0; + align-self: stretch; + background: #FFF; + } + + &-sticker-title { + color: #000; + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: 24px; /* 150% */ + margin-bottom: 8px; + } + + &-sticker-box { + display: flex; + height: 100%; + width: 100%; + justify-content: space-between; + align-items: flex-start; + align-content: flex-start; + gap: 8px; + flex-wrap: wrap; + + input { + appearance: none; + -webkit-appearance: none; + } + input:checked { + outline: 1px solid #000; + } + } + + &-sticker-width { + width: 48%; + img { + width: 100%; + } + } } \ No newline at end of file diff --git a/src/features/shared/layout/Container.tsx b/src/features/shared/layout/Container.tsx index 454dc1c..988a84f 100644 --- a/src/features/shared/layout/Container.tsx +++ b/src/features/shared/layout/Container.tsx @@ -4,6 +4,7 @@ import { Outlet } from 'react-router-dom'; import './layout.scss'; import useLayout from './hooks/useLayout'; import LayoutTitle from './components/Title'; +import LayoutBottomBtn from './components/BottomBtn'; const LayoutContainer: FunctionComponent = () => { useLayout(); @@ -11,6 +12,7 @@ const LayoutContainer: FunctionComponent = () => {
+
); }; diff --git a/src/features/shared/layout/atom.ts b/src/features/shared/layout/atom.ts index 95c96dd..b2c99d0 100644 --- a/src/features/shared/layout/atom.ts +++ b/src/features/shared/layout/atom.ts @@ -6,3 +6,11 @@ type TitleAtom = { }; export const titleAtom = atom({}); + +type BottomBtnAtom = { + text?: string; + add?: boolean; +}; +export const bottomBtnAtom = atom({}); + +export const activeSaveAtom = atom(false); diff --git a/src/features/shared/layout/components/BottomBtn.tsx b/src/features/shared/layout/components/BottomBtn.tsx new file mode 100644 index 0000000..39926f0 --- /dev/null +++ b/src/features/shared/layout/components/BottomBtn.tsx @@ -0,0 +1,27 @@ +import { useAtom } from 'jotai'; +import { FunctionComponent } from 'react'; + +import { activeSaveAtom, bottomBtnAtom } from '../atom'; + +import addLogo from '/svg/add.svg'; + +const LayoutBottomBtn: FunctionComponent = () => { + const [{ text, add }] = useAtom(bottomBtnAtom); + const [, setActiveSave] = useAtom(activeSaveAtom); + + if (!text) return
; + + return ( +
+
setActiveSave(true)}> +
+ {add && Add Logo} +
+
{text}
+
+
+
+ ); +}; + +export default LayoutBottomBtn; diff --git a/src/features/shared/layout/layout.scss b/src/features/shared/layout/layout.scss index 7c75d0e..03af1c6 100644 --- a/src/features/shared/layout/layout.scss +++ b/src/features/shared/layout/layout.scss @@ -3,11 +3,13 @@ .layout-container { min-width: 320px; max-width: 480px; + height: 100%; background-color: $container-color; } .layout-title { display: flex; + justify-content: space-between; align-items: center; height: 61px; padding: 8px; @@ -18,7 +20,7 @@ } .left { - width: 48px; + width: 20px; height: 48px; padding: 0px 14px; display: flex; @@ -30,8 +32,6 @@ display: flex; align-items: center; justify-content: center; - - width: 247px; height: 36px; padding: 6px 8px; gap: 10px; @@ -39,4 +39,30 @@ font-size: 24px; line-height: 24px; } +} + +.layout-bottom { + display: flex; + padding: 16px; + align-items: flex-start; + gap: 8px; + align-self: stretch; + border-top: 1px solid #E9E9F1; + background: #FFF; + position: sticky; + bottom: 0; + width: 100%; + box-sizing: border-box; +} +.layout-bottom-btn { + display: flex; + height: 48px; + padding: 0px 14px; + justify-content: center; + align-items: center; + flex: 1 0 0; + gap: 8px; + background: #191919; + color: #FFF; + cursor: pointer; } \ No newline at end of file diff --git a/src/features/shared/style/global.scss b/src/features/shared/style/global.scss index 2cce1b9..cdaa48b 100644 --- a/src/features/shared/style/global.scss +++ b/src/features/shared/style/global.scss @@ -10,3 +10,8 @@ body { min-height: 100vh; background-color: $other-area-color; } +#root { + width: 100%; + display: flex; + justify-content: center; +} \ No newline at end of file