diff --git a/src/api/endpoint/coffeechat/deleteCoffeechat.ts b/src/api/endpoint/coffeechat/deleteCoffeechat.ts new file mode 100644 index 000000000..0dfb05b5a --- /dev/null +++ b/src/api/endpoint/coffeechat/deleteCoffeechat.ts @@ -0,0 +1,11 @@ +import { z } from 'zod'; + +import { createEndpoint } from '@/api/typedAxios'; + +export const deleteCoffeechat = createEndpoint({ + request: () => ({ + method: 'DELETE', + url: `api/v1/members/coffeechat/details`, + }), + serverResponseScheme: z.unknown(), +}); diff --git a/src/components/coffeechat/detail/OpenerProfile/index.tsx b/src/components/coffeechat/detail/OpenerProfile/index.tsx index 6f5053a82..a5ffce937 100644 --- a/src/components/coffeechat/detail/OpenerProfile/index.tsx +++ b/src/components/coffeechat/detail/OpenerProfile/index.tsx @@ -19,6 +19,7 @@ interface OpenerProfileProps { export default function OpenerProfile({ memberId }: OpenerProfileProps) { const { data: openerProfile } = useGetCoffeechatDetail(memberId); const { isOpen: isOpenMessageModal, onOpen: onOpenMessageModal, onClose: onCloseMessageModal } = useModalState(); + return ( <> {openerProfile && ( @@ -173,7 +174,7 @@ const OpenerProfileSection = styled.section<{ isMine: boolean }>` grid: [row1-start] 'profileImageBox profileInfoBox buttonSection' auto [row1-end]/ auto; grid-template-columns: 1fr 5fr 2fr; gap: 28px; - align-items: center; + align-items: ${({ isMine }) => (isMine ? 'flex-end' : 'center')}; justify-content: space-between; width: 100%; diff --git a/src/components/coffeechat/detail/SeemoreSelect/index.tsx b/src/components/coffeechat/detail/SeemoreSelect/index.tsx new file mode 100644 index 000000000..cf80878b8 --- /dev/null +++ b/src/components/coffeechat/detail/SeemoreSelect/index.tsx @@ -0,0 +1,232 @@ +import styled from '@emotion/styled'; +import * as Dialog from '@radix-ui/react-dialog'; +import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; +import { colors } from '@sopt-makers/colors'; +import { fonts } from '@sopt-makers/fonts'; +import { IconDotsVertical, IconEdit, IconTrash } from '@sopt-makers/icons'; +import { DialogOptionType, useDialog, useToast } from '@sopt-makers/ui'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import dynamic from 'next/dynamic'; +import { useRouter } from 'next/router'; +import { playgroundLink } from 'playground-common/export'; +import { useState } from 'react'; + +import { deleteCoffeechat } from '@/api/endpoint/coffeechat/deleteCoffeechat'; +import Responsive from '@/components/common/Responsive'; +import { MOBILE_MEDIA_QUERY } from '@/styles/mediaQuery'; + +const DropdownPortal = dynamic( + () => import('@radix-ui/react-dropdown-menu').then((r) => r.DropdownMenuPortal), + { + ssr: false, + }, +); + +const DialogPortal = dynamic( + () => import('@radix-ui/react-dialog').then((r) => r.DialogPortal), + { + ssr: false, + }, +); + +interface SeemoreSelectProp { + memberId: string; +} + +interface SeemoreContentProps { + onEdit: () => void; + onDelete: () => void; +} + +export default function SeemoreSelect({ memberId }: SeemoreSelectProp) { + const router = useRouter(); + const queryClient = useQueryClient(); + const { open: toastOpen } = useToast(); + const { open } = useDialog(); + const option: DialogOptionType = { + title: '커피챗을 삭제하시겠습니까?', + description: '새 커피챗을 다시 열 수 있지만, 작성했던 내용은 저장되지 않아요.', + type: 'danger', + typeOptions: { + cancelButtonText: '취소', + approveButtonText: '삭제하기', + buttonFunction: () => handleDelete(), + }, + }; + + const { mutate, isPending } = useMutation({ + mutationFn: () => deleteCoffeechat.request(), + }); + + const onEdit = () => { + router.push(playgroundLink.coffeechatEdit(memberId)); + }; + + const handleDelete = () => { + mutate(undefined, { + onSuccess: async () => { + // TODO: 연결 + // logSubmitEvent(''); + // queryClient.invalidateQueries('') + toastOpen({ icon: 'success', content: '커피챗이 삭제되었어요. 다음에 또 만나요!' }); + await router.push(playgroundLink.coffeechat()); + }, + }); + }; + + const onDelete = () => { + open(option); + }; + + return ( + <> + + + + + + + + ); +} + +const DropdownSeemore = ({ onEdit, onDelete }: SeemoreContentProps) => { + const [open, setOpen] = useState(false); + + return ( + + + + + + + + + + <>수정 + + + + <>삭제 + + + + + + ); +}; + +const BottomSheetSeemore = ({ onEdit, onDelete }: SeemoreContentProps) => { + const [open, setOpen] = useState(false); + + return ( + + + + + + + + + + + { + setOpen(false); + onEdit(); + }} + > + + <>수정 + + { + setOpen(false); + onDelete(); + }} + isTrash + > + + <>삭제 + + + + + + ); +}; + +const Overlay = styled.div` + position: fixed; + inset: 0; + z-index: 101; + background-color: rgb(0 0 0 / 70%); + animation: overlay-show 0.3s cubic-bezier(0.16, 1, 0.3, 1); + + @keyframes overlay-show { + from { + opacity: 0; + } + + to { + opacity: 1; + } + } +`; + +const StyledContent = styled.div` + display: flex; + flex-direction: column; + gap: 6px; + border-radius: 13px; + background-color: ${colors.gray800}; + padding: 8px; + + @media ${MOBILE_MEDIA_QUERY} { + position: fixed; + right: 16px; + bottom: 42px; + left: 16px; + z-index: 102; + border-radius: 20px; + padding: 12px 8px; + width: calc(100% - 32px); + } +`; + +const StyledItem = styled(DropdownMenu.Item)<{ isTrash?: boolean }>` + display: flex; + gap: 10px; + align-items: center; + padding: 8px 12px; + color: ${({ isTrash }) => isTrash && colors.red400}; + ${fonts.BODY_16_M}; +`; + +const StyledContentItem = styled.div<{ isTrash?: boolean }>` + display: flex; + gap: 10px; + align-items: center; + padding: 10px; + color: ${({ isTrash }) => isTrash && colors.red400}; + + ${fonts.BODY_14_M}; +`; + +const EditIcon = styled(IconEdit)` + width: 16px; + height: 16px; +`; + +const TrashIcon = styled(IconTrash)` + width: 16px; + height: 16px; + color: ${colors.red400}; +`; + +const DotsVerticalIcon = styled(IconDotsVertical)` + margin-top: 12px; + cursor: pointer; + width: 24px; + height: 24px; +`; diff --git a/src/components/coffeechat/detail/index.tsx b/src/components/coffeechat/detail/index.tsx index 0de0eb833..30f0acc0d 100644 --- a/src/components/coffeechat/detail/index.tsx +++ b/src/components/coffeechat/detail/index.tsx @@ -1,7 +1,6 @@ import styled from '@emotion/styled'; import { colors } from '@sopt-makers/colors'; import { fonts } from '@sopt-makers/fonts'; -import { IconDotsVertical } from '@sopt-makers/icons'; import { useMemo } from 'react'; import { useGetCoffeechatDetail } from '@/api/endpoint/coffeechat/getCoffeechatDetail'; @@ -9,6 +8,8 @@ import { useGetMemberOfMe } from '@/api/endpoint/members/getMemberOfMe'; import { useGetMemberProfileById } from '@/api/endpoint_LEGACY/hooks'; import CoffeechatContents from '@/components/coffeechat/detail/CoffeechatContents'; import OpenerProfile from '@/components/coffeechat/detail/OpenerProfile'; +import SeemoreSelect from '@/components/coffeechat/detail/SeemoreSelect'; +import Loading from '@/components/common/Loading'; import CoffeechatLoading from '@/components/coffeechat/Loading'; import CareerSection from '@/components/members/detail/CareerSection'; import DetailInfoSection from '@/components/members/detail/DetailinfoSection'; @@ -44,7 +45,7 @@ export default function CoffeechatDetail({ memberId }: CoffeechatDetailProp) { {openerProfile.bio} {/* TODO: 더보기 버튼 기능 구현 */} - {/* <>{openerProfile.isMine && } */} + <>{openerProfile.isMine && } @@ -98,12 +99,12 @@ const ProfilPojectSection = styled.div` const SoptActivityTitle = styled.h2` margin: 28px 0 32px; color: ${colors.white}; - ${fonts.HEADING_32_B}; + ${fonts.HEADING_28_B}; @media ${MOBILE_MEDIA_QUERY} { margin: 24px 0; - ${fonts.HEADING_28_B}; + ${fonts.HEADING_20_B}; } `; @@ -123,11 +124,6 @@ const DetailPage = styled.div` } `; -const DotsVerticalIcon = styled(IconDotsVertical)` - width: 24px; - height: 24px; -`; - const CoffeechatTitle = styled.h1` /* stylelint-disable-next-line value-no-vendor-prefix */ display: -webkit-box; @@ -149,7 +145,8 @@ const CoffeechatTitle = styled.h1` const CoffeechatHeader = styled.header` display: flex; - align-items: center; + gap: 20px; + align-items: flex-start; justify-content: space-between; margin-bottom: 24px; `; diff --git a/src/components/members/detail/GroupSection/index.tsx b/src/components/members/detail/GroupSection/index.tsx index 60631651b..14ee030dd 100644 --- a/src/components/members/detail/GroupSection/index.tsx +++ b/src/components/members/detail/GroupSection/index.tsx @@ -13,6 +13,7 @@ import { playgroundLink } from '@/constants/links'; import useEnterScreen from '@/hooks/useEnterScreen'; import { MOBILE_MEDIA_QUERY } from '@/styles/mediaQuery'; import { safeParseInt } from '@/utils'; +import { fonts } from '@sopt-makers/fonts'; interface GroupSectionProps { profile: ProfileDetail; @@ -88,11 +89,10 @@ const Container = styled.section` `; const ActivityTitle = styled.div` - line-height: 100%; - font-size: 32px; - font-weight: 700; + ${fonts.HEADING_28_B}; + @media ${MOBILE_MEDIA_QUERY} { - font-size: 22px; + ${fonts.HEADING_20_B}; } `; diff --git a/src/components/members/detail/ProjectSection/index.tsx b/src/components/members/detail/ProjectSection/index.tsx index 4b3ddff6d..21c88790f 100644 --- a/src/components/members/detail/ProjectSection/index.tsx +++ b/src/components/members/detail/ProjectSection/index.tsx @@ -1,5 +1,6 @@ import styled from '@emotion/styled'; import { colors } from '@sopt-makers/colors'; +import { fonts } from '@sopt-makers/fonts'; import Link from 'next/link'; import { playgroundLink } from 'playground-common/export'; @@ -65,11 +66,10 @@ const Container = styled.section` `; const ActivityTitle = styled.div` - line-height: 100%; - font-size: 32px; - font-weight: 700; + ${fonts.HEADING_28_B}; + @media ${MOBILE_MEDIA_QUERY} { - font-size: 22px; + ${fonts.HEADING_20_B}; } `; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index bf154114e..018c8cffe 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,6 +1,6 @@ import ProgressBar from '@badrap/bar-of-progress'; import { colors } from '@sopt-makers/colors'; -import { ToastProvider as MDSToastProvider } from '@sopt-makers/ui'; +import { DialogProvider, ToastProvider as MDSToastProvider } from '@sopt-makers/ui'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { OverlayProvider } from '@toss/use-overlay'; @@ -130,21 +130,23 @@ function MyApp({ Component, pageProps }: AppProps) { import('framer-motion').then((mod) => mod.domAnimation)}> - - - - - - - - - - - - - {DEBUG && } - - + + + + + + + + + + + + + + {DEBUG && } + + +