Skip to content
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: 편지 쓰기 모킹 API 구현 #68

Merged
merged 10 commits into from
Feb 7, 2024
5 changes: 4 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0 user-scalable=no"
/>
<link
href="https://cdn.jsdelivr.net/gh/sun-typeface/SUIT/fonts/static/woff2/SUIT.css"
rel="stylesheet"
Expand Down
12 changes: 12 additions & 0 deletions src/api/letter/apis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Letter } from '@/types/letter';
import baseInstance from '../instance';

const letterAPI = {
/** 편지 작성 */
postLetter: async (letter: Letter) => {
const { data } = await baseInstance.post('/api/letter', letter);
return data;
},
};

export default letterAPI;
Empty file added src/api/letter/queryOptions.ts
Empty file.
22 changes: 10 additions & 12 deletions src/assets/icons/ImageSquare.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import type { SVGProps } from 'react';
const SvgImageSquare = ({
width = 24,
height = 24,
fill = 'currentColor',
...props
}: SVGProps<SVGSVGElement>) => (
const SvgImageSquare = ({ ...props }: SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
width={width}
height={height}
{...props}
>
<path
fill={fill}
d="M19.5 3h-15A1.5 1.5 0 0 0 3 4.5v15A1.5 1.5 0 0 0 4.5 21h15a1.5 1.5 0 0 0 1.5-1.5v-15A1.5 1.5 0 0 0 19.5 3m-15 1.5h15v7.254L17.185 9.44a1.5 1.5 0 0 0-2.12 0L5.003 19.5H4.5zm3 4.5a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0"
/>
<g
stroke="#4F4F4F"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
>
<path d="M5 21h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2" />
<path d="M15 13a2 2 0 1 0 0 4 2 2 0 0 0 0-4M3 9l3.086 3.086a2 2 0 0 0 2.828 0L18 3" />
</g>
</svg>
);
export default SvgImageSquare;
10 changes: 7 additions & 3 deletions src/assets/icons/raw/imageSquare.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/components/Polaroid/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const sizeStyle = {
justify-content: center;
align-items: flex-start;
align-self: stretch;
height: 32.75rem;
height: 52vh;
padding: 1.25rem 1.25rem 5rem;
border-radius: 0.5rem;
background-size: cover;
Expand Down
10 changes: 10 additions & 0 deletions src/constants/letters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { WORRY_DICT, type Worry } from './users';

export const EQUAL_GENDER_DICT = [
'모두에게 보내기',
'나와 같은 성별에게 보내기',
] as const;

export type EqualGender = (typeof EQUAL_GENDER_DICT)[number];

export { WORRY_DICT, type Worry };
14 changes: 13 additions & 1 deletion src/constants/schemaLiteral.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ export const letterWrite = {
age: {
value: 2,
message: '편지를 받을 사람의 나이를 선택해주세요.',
min: 10,
max: 40,
},
content: {
min: {
Expand All @@ -17,8 +19,18 @@ export const letterWrite = {
value: 1,
message: '편지를 받을 사람의 성별를 선택해주세요.',
},
concern: {
worryType: {
value: 1,
message: '편지의 고민을 선택해주세요.',
},
image: {
maxFileSize: {
value: 5000000,
message: '사진은 5MB 이하여야 합니다.',
},
acceptType: {
list: ['image/jpeg', 'image/jpg', 'image/png', 'image/webp'],
message: '사진은 이미지 파일만 가능합니다.',
},
},
} as const;
23 changes: 23 additions & 0 deletions src/constants/users.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export const WORRY_DICT = {
WORK: '일·직장',
COURSE: '취업·진로',
RELATIONSHIP: '인간관계',
BREAK_LOVE: '이별·상실',
LOVE: '연애',
STUDY: '학업',
FAMILY: '가족',
ETC: '기타',
} as const;

export const GENDER_DICT = {
MALE: '남성',
FEMALE: '여성',
} as const;

export const ROLE_DICT = {
USER: '사용자',
} as const;

export type Worry = keyof typeof WORRY_DICT;
export type Gender = keyof typeof GENDER_DICT;
export type Role = keyof typeof ROLE_DICT;
3 changes: 2 additions & 1 deletion src/mocks/handlers/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import testHandler from './test';
import letterHandler from './letter';

const mockHandlers = [...testHandler];
const mockHandlers = [...testHandler, ...letterHandler];

export default mockHandlers;
11 changes: 11 additions & 0 deletions src/mocks/handlers/letter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { http, HttpResponse, delay } from 'msw';
import { baseURL } from '@/utils/mswUtils';

const letterHandler = [
http.post(baseURL('/api/letter'), async () => {
await delay(1000);
return HttpResponse.json();
}),
];

export default letterHandler;
25 changes: 15 additions & 10 deletions src/pages/LetterWritePage/components/LetterReceiverBottomSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,41 @@ import COLORS from '@/constants/colors';
import textStyles from '@/styles/textStyles';
import BottomSheet from '@/components/BottomSheet';
import { ArrowClockWise } from '@/assets/icons';
import { letterWrite } from '@/constants/schemaLiteral';
import { type Worry, type EqualGender } from '@/constants/letters';
import { type Inputs } from '..';
import { BottomSheetProps } from './LetterWriteContent';
import { AgeSlider, GenderSelect, ConcernSelect } from '.';
import { AgeSlider, GenderSelect, WorrySelect } from '.';

const LetterReceiverSelect = ({
isBottomSheetOpen,
toggleBottomSheet,
}: BottomSheetProps) => {
const { setValue } = useFormContext<Inputs>();

const [age, setAge] = useState([15, 40]);
const [gender, setGender] = useState('');
const [concern, setConcern] = useState('');
const [age, setAge] = useState<[number, number]>([
letterWrite.age.min,
letterWrite.age.max,
]);
const [gender, setGender] = useState<'' | EqualGender>('');
const [worryType, setWorryType] = useState<'' | Worry>('');

const [iconRotation, setIconRotation] = useState(0);
const [iconRotation, setIconRotation] = useState<number>(0);

const onCompleteButtonClick = () => {
setValue('age', age);
setValue('concern', concern);
setValue('worryType', worryType);
setValue('gender', gender);
toggleBottomSheet(false)();
};

const onRefreshIconClick = () => {
setValue('age', []);
setValue('gender', '');
setValue('concern', '');
setAge([15, 40]);
setValue('worryType', '');
setAge([letterWrite.age.min, letterWrite.age.max]);
setGender('');
setConcern('');
setWorryType('');
setIconRotation((prev) => prev + 360);
};

Expand All @@ -51,7 +56,7 @@ const LetterReceiverSelect = ({
</div>
<AgeSlider age={age} setAge={setAge} />
<GenderSelect gender={gender} setGender={setGender} />
<ConcernSelect concern={concern} setConcern={setConcern} />
<WorrySelect worryType={worryType} setWorryType={setWorryType} />
</div>
<div css={style.buttonContainer}>
<button onClick={toggleBottomSheet(false)}>닫기</button>
Expand Down
51 changes: 32 additions & 19 deletions src/pages/LetterWritePage/components/LetterReceiverContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ import { css } from '@emotion/react';
import COLORS from '@/constants/colors';
import textStyles from '@/styles/textStyles';
import { CaretDown } from '@/assets/icons';
import {
WORRY_DICT,
type Worry,
EQUAL_GENDER_DICT,
type EqualGender,
} from '@/constants/letters';
import Chip from '@/components/Chip';
import { type Inputs } from '..';

interface ReceiverContainerProps {
Expand All @@ -14,24 +21,31 @@ const ReceiverContainer = ({ onClick, isOpen }: ReceiverContainerProps) => {
const { watch } = useFormContext<Inputs>();

const age = watch('age');
const gender = watch('gender');
const concern = watch('concern');
const gender = watch('gender') as '' | EqualGender;
const worryType = watch('worryType') as '' | Worry;

return (
<div css={style.ReceiverContainer}>
<span>To.</span>
{age.length !== 0 ? (
<div onClick={onClick} css={style.ReceiverBoxSelect}>
<div>
<span>
<Chip variant="form-selected">
<span>#</span>
{age[0]}~{age[1]}
</span>
</Chip>
{gender !== '' && (
<span>
{gender === '모두에게 보내기' ? '모두에게' : '동성에게'}
</span>
<Chip variant="form-selected">
<span>#</span>
{EQUAL_GENDER_DICT[0] === gender ? '모두에게' : '동성에게'}
</Chip>
)}
{worryType !== '' && (
<Chip variant="form-selected">
<span>#</span>
{WORRY_DICT[worryType]}
</Chip>
)}
{concern !== '' && <span>{concern}</span>}
</div>
<CaretDown css={style.caretDown(isOpen)} />
</div>
Expand Down Expand Up @@ -62,9 +76,8 @@ const style = {
width: 100%;
margin-left: 0.5rem;
padding: 0.5rem 0.75rem;
border: 1px solid white;
border-radius: 0.5rem;
background: rgb(255 255 255 / 0.38);
background: rgb(204 199 190 / 0.3);
letter-spacing: -0.0035rem;
cursor: pointer;

Expand All @@ -79,9 +92,8 @@ const style = {
width: 100%;
margin-left: 0.5rem;
padding: 0.4375rem 0.75rem;
border: 1px solid white;
border-radius: 0.5rem;
background: ${COLORS.gray6};
background: rgb(204 199 190 / 0.3);
letter-spacing: -0.0035rem;
cursor: pointer;

Expand All @@ -90,14 +102,15 @@ const style = {
gap: 0.625rem;
}

span {
button {
padding-block: 0;
padding-inline: 0.5rem;
border: 1px solid ${COLORS.gray4};
border-radius: 1.25rem;
background: ${COLORS.gray5};
color: ${COLORS.gray1};
font-weight: 500;
font-size: 14px;
color: black;

${textStyles.b4m};
span {
color: ${COLORS.gray2};
}
}
`,
caretDown: (isOpen: boolean) => css`
Expand Down
11 changes: 8 additions & 3 deletions src/pages/LetterWritePage/components/LetterWriteBottom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import Button from '@/components/Button';
import { ROUTER_PATHS } from '@/router';
import { ImageSelect } from '.';

const LetterWriteBottom = () => {
interface LetterWriteBottomProps {
isPosting: boolean;
}

const LetterWriteBottom = ({ isPosting }: LetterWriteBottomProps) => {
const navigate = useNavigate();

return (
Expand All @@ -16,9 +20,10 @@ const LetterWriteBottom = () => {
variant="secondary"
size="sm"
>
뒤로
취소
</Button>
<Button type="submit" variant="primary" size="sm">
<Button disabled={isPosting} type="submit" variant="primary" size="sm">
{/** isPosting 가 true 일 때 로딩스피너 보여줄 예정 */}
보내기
</Button>
<ImageSelect />
Expand Down
6 changes: 3 additions & 3 deletions src/pages/LetterWritePage/components/PolaroidImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Inputs } from '..';

const PolaroidImage = () => {
const { setValue, watch } = useFormContext<Inputs>();
const imgUrl = URL.createObjectURL(watch('image'));
const imgUrl = URL.createObjectURL(watch('image')[0]);

const modal = useModal();
const { open, close } = modal;
Expand Down Expand Up @@ -47,7 +47,7 @@ const style = {
justify-content: center;
align-items: center;
margin-inline: 1rem;
margin-top: 3rem;
margin-top: 15vh;

div {
display: flex;
Expand All @@ -58,7 +58,7 @@ const style = {
}

button {
padding-inline: 2.5rem;
padding-inline: 3.75rem;
}
`,
};
4 changes: 2 additions & 2 deletions src/pages/LetterWritePage/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import AgeSlider from './inputs/AgeSlider';
import ConcernSelect from './inputs/ConcernSelect';
import WorrySelect from './inputs/WorrySelect';
import GenderSelect from './inputs/GenderSelect';
import LetterTextarea from './inputs/LetterTextarea';
import ImageSelect from './inputs/ImageSelect';
Expand All @@ -12,7 +12,7 @@ import LetterWriteContent from './LetterWriteContent';

export {
AgeSlider,
ConcernSelect,
WorrySelect,
GenderSelect,
LetterTextarea,
ImageSelect,
Expand Down
Loading
Loading