From 0da4bb582c01fd51a12febf738c4849d5633265f Mon Sep 17 00:00:00 2001 From: useonglee Date: Fri, 23 Feb 2024 17:04:03 +0900 Subject: [PATCH 01/10] =?UTF-8?q?feat:=20=EC=9D=BC=EC=A0=95=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EB=B0=94=ED=85=80=EC=8B=AF=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my/FloatingButton/FloatingButton.tsx | 3 +- .../components/AddScheduleBottomSheet.css.ts | 95 ++++++ .../components/AddScheduleBottomSheet.tsx | 305 ++++++++++++++++++ .../schedule/components/SearchContact.css.ts | 49 +++ .../schedule/components/SearchContact.tsx | 144 +++++++++ .../src/features/schedule/components/index.ts | 1 + .../web/src/features/schedule/constants.ts | 28 ++ .../schedule/hooks/useAddSchedule.tsx | 19 ++ services/web/src/features/schedule/store.ts | 4 + services/web/src/types/schedule.ts | 12 + services/web/src/utils/formatPhoneNumber.ts | 9 +- 11 files changed, 667 insertions(+), 2 deletions(-) create mode 100644 services/web/src/features/schedule/components/AddScheduleBottomSheet.css.ts create mode 100644 services/web/src/features/schedule/components/AddScheduleBottomSheet.tsx create mode 100644 services/web/src/features/schedule/components/SearchContact.css.ts create mode 100644 services/web/src/features/schedule/components/SearchContact.tsx create mode 100644 services/web/src/features/schedule/components/index.ts create mode 100644 services/web/src/features/schedule/constants.ts create mode 100644 services/web/src/features/schedule/hooks/useAddSchedule.tsx create mode 100644 services/web/src/features/schedule/store.ts diff --git a/services/web/src/features/my/FloatingButton/FloatingButton.tsx b/services/web/src/features/my/FloatingButton/FloatingButton.tsx index d868ee98..4c28024b 100644 --- a/services/web/src/features/my/FloatingButton/FloatingButton.tsx +++ b/services/web/src/features/my/FloatingButton/FloatingButton.tsx @@ -1,6 +1,7 @@ 'use client'; import { AddFriendBottomSheet } from '@features/contact/components'; +import { AddScheduleBottomSheet } from '@features/schedule/components'; import { BottomSheet, Button, IconButton } from '@linker/lds'; import type { FABType } from '@linker/lds/src/Button/FAB'; import { useEffect, useState } from 'react'; @@ -31,7 +32,7 @@ const FloatingButton = () => { }, []); const BottomSheetContext = { - calendar: , + calendar: , user: , }; diff --git a/services/web/src/features/schedule/components/AddScheduleBottomSheet.css.ts b/services/web/src/features/schedule/components/AddScheduleBottomSheet.css.ts new file mode 100644 index 00000000..ac825237 --- /dev/null +++ b/services/web/src/features/schedule/components/AddScheduleBottomSheet.css.ts @@ -0,0 +1,95 @@ +import { colors } from '@linker/styles'; +import { style } from '@vanilla-extract/css'; + +export const container = style({ + position: 'relative', + backgroundColor: colors.background, + height: 'calc(100% - 64px)', +}); + +export const listRow = style({ + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', +}); + +export const invitedContactCard = style([ + { + flexDirection: 'column', + }, +]); + +export const scheduleInput = style({ + width: '11.8rem !important', + height: '3.4rem !important', + padding: '0.6rem 1.2rem !important', + backgroundColor: `${colors.gray050} !important`, +}); + +export const contactAddButton = style({ + display: 'inline-flex', + alignItems: 'center', +}); + +export const divider = style({ + width: '100%', + height: 1, + margin: '1.2rem 0', + backgroundColor: colors.gray100, +}); + +export const textareaContainer = style({ + padding: '0 1.6rem', +}); + +export const textarea = style({ + height: '23.2rem !important', + padding: '0 1.6rem', +}); + +export const categoryLegend = style({ + display: 'inline-block', + width: '1.2rem', + height: '1.2rem', + borderRadius: '50%', +}); + +export const dropdown = style({ + position: 'relative', + display: 'flex', + flexDirection: 'column', +}); + +export const categoryWrapper = style({ + position: 'absolute', + top: 30, + right: -10, + zIndex: 1, + width: '16rem', + borderRadius: 8, + boxShadow: '0px 0px 6px 0px #0000001A', + backgroundColor: colors.gray000, +}); + +export const border = style({ + borderBottom: `1px solid ${colors.gray100}`, +}); + +export const categoryItem = style([ + listRow, + { + width: '100%', + padding: '1.2rem 1.6rem', + }, +]); + +export const categoryIdentifier = style({ + position: 'absolute', + zIndex: 1, + top: 0, + left: 2, + width: '0.5rem', + height: '2.8rem', + margin: '1.2rem 0.8rem 1.2rem 0.8rem', + borderRadius: '0.8rem', +}); diff --git a/services/web/src/features/schedule/components/AddScheduleBottomSheet.tsx b/services/web/src/features/schedule/components/AddScheduleBottomSheet.tsx new file mode 100644 index 00000000..f324d971 --- /dev/null +++ b/services/web/src/features/schedule/components/AddScheduleBottomSheet.tsx @@ -0,0 +1,305 @@ +/* eslint-disable max-lines */ +/* eslint-disable max-lines-per-function */ +/* eslint-disable react/jsx-no-undef */ +import { + BottomSheet, + Dropdown, + Icon, + Input, + List, + Profile, + Spacing, + TextArea, + TextButton, + Txt, +} from '@linker/lds'; +import { colors, typography } from '@linker/styles'; +import clsx from 'clsx'; +import { format } from 'date-fns'; +import { useAtom } from 'jotai'; +import { useState } from 'react'; +import { useForm, useWatch } from 'react-hook-form'; + +import formatInputDate from '@utils/formatInputDate'; + +import { + border, + categoryIdentifier, + categoryItem, + categoryLegend, + categoryWrapper, + contactAddButton, + container, + divider, + dropdown, + invitedContactCard, + listRow, + scheduleInput, + textarea, + textareaContainer, +} from './AddScheduleBottomSheet.css'; +import SearchContact from './SearchContact'; +import { contactCard, contactCardInfo, contactWrapper } from './SearchContact.css'; +import { CalendarType, Category, CategoryColor, ColorEnum } from '../constants'; +import useAddSchedule from '../hooks/useAddSchedule'; +import { selectedContactsAtom } from '../store'; + +interface InputData { + title: string; + startDateTime: string; + endDateTime: string; + description: string; +} + +const AddScheduleBottomSheet = () => { + const { + register, + handleSubmit, + control, + reset, + formState: { errors, isValid }, + } = useForm({ + defaultValues: { + startDateTime: format(new Date(), 'yyyy. MM. dd'), + endDateTime: format(new Date(), 'yyyy. MM. dd'), + }, + }); + + const formState = useWatch({ + name: ['title', 'startDateTime', 'endDateTime', 'description'], + control, + }); + + const { mutate: addSchedule } = useAddSchedule(); + + const 모든정보를_입력했는가 = formState.every((value) => value !== '') && isValid; + + const [isSearchContact, setIsSearchContact] = useState(false); + const [isOpenDropdown, setIsOpenDropdown] = useState(false); + const [category, setCategory] = useState('개인일정'); + const [categoryColor, setCategoryColor] = useState(ColorEnum['개인일정']); + + const [selectedContacts, setSelectedContacts] = useAtom(selectedContactsAtom); + + const resetFormState = () => { + reset(); + setSelectedContacts([]); + setCategory('개인일정'); + setCategoryColor(ColorEnum['개인일정']); + }; + + const handleContactSubmit = () => { + const payload = { + title: formState[0], + startDateTime: formatDate(formState[1]), + endDateTime: formatDate(formState[2]), + description: formState[3], + contactIds: selectedContacts.map((contact) => contact.id), + category, + color: categoryColor, + }; + + addSchedule(payload); + resetFormState(); + }; + + return ( + + {isSearchContact ? ( + + ) : ( + <> + + + { + resetFormState(); + }} + > + 취소 + + + + + + 저장 + + + +
+
+ + + + +
+ + 시작 + + + { + const formatted = formatInputDate(e.currentTarget.value); + + e.currentTarget.value = formatted; + }} + /> +
+
+
+ + 종료 + + + { + const formatted = formatInputDate(e.currentTarget.value); + + e.currentTarget.value = formatted; + }} + /> +
+ + + + + + 캘린더 + + setIsOpenDropdown((prev) => !prev)} + > + +
+ + {category} +
+
+ + {CalendarType.map((item, index) => ( +
  • { + setCategory(item.category); + setCategoryColor(ColorEnum[item.category]); + + setIsOpenDropdown(false); + }} + > + + {item.category} + + + +
  • + ))} +
    +
    +
    + + + +
    + + 초대받은 사람 + + setIsSearchContact(true)}> + + 추가하기 + + +
    + + {selectedContacts.length > 0 && ( +
      + {selectedContacts.map((contact) => { + return ( +
    • + + +
      + {contact.name} + + {contact.careers ?? contact.school ?? ''} + +
      +
    • + ); + })} + + +
    + )} +
    + + + +