From a7980d59bd09f1c59be03d224a40bf21360080e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=84=B8=EB=AF=BC?= <89172499+semnil5202@users.noreply.github.com> Date: Mon, 18 Nov 2024 00:36:05 +0900 Subject: [PATCH] Feat/v2 publishing (#179) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: apply v2 navigation style and feature * feat: create notification route * refactor: change badge and tag radius value * refactor: unnecessary duplicated theme file deleted, DS 0.5.8 version applied * feat: implement notification section * feat: 알림 갯수 및 글자수에 따라 단축 표현 여부 체크하는 함수 구현 및 적용 * refactor: feed 글쓰러 가기 파트 수정 * refactor: 불필요한 Profile 부분 삭제 * refactor: 축약 Badge 가변 길이 설정토록 업데이트 및 피드에 적용 * refactor: temp commit query string sign up match page * refactor: 프로젝트 매칭 설정 페이지 뼈대 구현 및 이전 form 상태값 유지 * feat: 분야 추가하기 바텀씟 분리 및 적용 * design: 회원가입 프로젝트 매칭 설정 UI 목업 완료 * refactor: wip * API 연동 필요 - 분야 등 * 알림 API 연동 필요 --- emotion.d.ts | 8 - package-lock.json | 8 +- package.json | 2 +- .../ErrorBoundary/ApiErrorBoundary.tsx | 2 +- src/layouts/Navbar.tsx | 49 +++- src/pages/Feed/Feed.page.tsx | 31 ++- .../NewIdeaCardListSection.tsx | 9 +- .../Feed/hooks/useFilteredBottomSheetState.ts | 4 +- src/pages/FeedDetail/FeedDetail.page.tsx | 8 +- src/pages/Login/Login.tsx | 7 +- src/pages/Notification/Notification.page.tsx | 11 + .../components/AdditionalText.tsx | 28 ++ .../components/NotificationItem.tsx | 39 +++ .../containers/NotificationContainer.tsx | 23 ++ .../hooks/queries/usePollingNotification.ts | 65 +++++ src/pages/Notification/types/index.ts | 7 + src/pages/Profile/Profile.page.tsx | 2 +- .../components/MyProfile/IdeaSection.tsx | 11 +- .../Profile/components/ProfileInfoSection.tsx | 8 +- src/pages/ProfileEdit/ProfileEdit.page.tsx | 2 +- src/pages/SignUp/SignUp.page.tsx | 100 ++----- src/pages/SignUp/SignUpMatch.page.tsx | 233 ++++++++++++++++ src/pages/SignUp/utils/manageQueryString.ts | 24 ++ src/pages/Write/Write.page.tsx | 259 +++++++++--------- .../Write/components/TwoDepthBottomSheet.tsx | 77 ++++++ .../HyperLinkText/HyperLinkText.tsx | 8 +- .../components/NewIdeaCard/NewIdeaCard.tsx | 4 +- .../NewIdeaCard/NewIdeaCardContext.ts | 3 +- .../NewIdeaCard/compound/Content/Content.tsx | 75 +++-- .../NewIdeaCard/compound/Profile/Profile.tsx | 96 ------- src/router.tsx | 10 + src/styles/theme.ts | 102 ------- src/utils/parsing.ts | 28 ++ yarn.lock | 8 +- 34 files changed, 856 insertions(+), 495 deletions(-) create mode 100644 src/pages/Notification/Notification.page.tsx create mode 100644 src/pages/Notification/components/AdditionalText.tsx create mode 100644 src/pages/Notification/components/NotificationItem.tsx create mode 100644 src/pages/Notification/containers/NotificationContainer.tsx create mode 100644 src/pages/Notification/hooks/queries/usePollingNotification.ts create mode 100644 src/pages/Notification/types/index.ts create mode 100644 src/pages/SignUp/SignUpMatch.page.tsx create mode 100644 src/pages/SignUp/utils/manageQueryString.ts create mode 100644 src/pages/Write/components/TwoDepthBottomSheet.tsx delete mode 100644 src/pages/components/NewIdeaCard/compound/Profile/Profile.tsx delete mode 100644 src/styles/theme.ts create mode 100644 src/utils/parsing.ts diff --git a/emotion.d.ts b/emotion.d.ts index fb795315..89b78f68 100644 --- a/emotion.d.ts +++ b/emotion.d.ts @@ -1,12 +1,4 @@ import '@emotion/react'; -import { ColorType, FontType } from './src/styles/theme'; - -declare module '@emotion/react' { - export interface Theme { - color: ColorType; - font: FontType; - } -} declare module '@emotion/styled' { import styled from '@emotion/styled'; diff --git a/package-lock.json b/package-lock.json index 736d8574..39e17d80 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@toss/use-overlay": "^1.3.8", "axios": "^1.5.1", "browser-image-compression": "^2.0.2", - "concept-be-design-system": "^0.5.3", + "concept-be-design-system": "^0.5.11", "react": "^18.2.0", "react-dom": "^18.2.0", "react-helmet-async": "^2.0.4", @@ -2495,9 +2495,9 @@ "license": "MIT" }, "node_modules/concept-be-design-system": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/concept-be-design-system/-/concept-be-design-system-0.5.3.tgz", - "integrity": "sha512-ThU8egZDs1krKizh6YqXiTvlQcUQV46HFD1lGVlsTThrvH1m2YRkPht6NwbZ2rvsO+K5e8QGS9CM1PzZUSiJiA==", + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/concept-be-design-system/-/concept-be-design-system-0.5.11.tgz", + "integrity": "sha512-aCULAmJ36lpSQa0i8slY9c+AnqtcNDSWxxTP27BCtYZTo0ejCkOfqGENOGjo4WxEe5VSkvoiVKTe/dkdAYoPZw==", "dependencies": { "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", diff --git a/package.json b/package.json index fb8a3162..97fe7aec 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@toss/use-overlay": "^1.3.8", "axios": "^1.5.1", "browser-image-compression": "^2.0.2", - "concept-be-design-system": "^0.5.3", + "concept-be-design-system": "^0.5.11", "react": "^18.2.0", "react-dom": "^18.2.0", "react-helmet-async": "^2.0.4", diff --git a/src/components/ErrorBoundary/ApiErrorBoundary.tsx b/src/components/ErrorBoundary/ApiErrorBoundary.tsx index a1525877..df449da4 100644 --- a/src/components/ErrorBoundary/ApiErrorBoundary.tsx +++ b/src/components/ErrorBoundary/ApiErrorBoundary.tsx @@ -115,7 +115,7 @@ class ErrorBoundary extends Component { return ( <> - ; + ); } diff --git a/src/layouts/Navbar.tsx b/src/layouts/Navbar.tsx index 1958f1dd..b2e5b217 100644 --- a/src/layouts/Navbar.tsx +++ b/src/layouts/Navbar.tsx @@ -1,10 +1,14 @@ import { Navigation, - SVGNavActiveFeed, - SVGNavActiveProfile, - SVGNavFeed, - SVGNavProfile, - SVGNavWrite24, + SVGNavAlarm, + SVGNavAlarmFilled, + SVGNavEdit, + SVGNavEditFilled, + SVGNavHome, + SVGNavHomeFilled, + SVGNavUser, + SVGNavUserFilled, + Text, } from 'concept-be-design-system'; import { useNavigate, useParams } from 'react-router-dom'; @@ -15,20 +19,43 @@ const Navbar = () => { const { id: userIdFromParams } = useParams(); const { hasMatched } = useRouteMatched(); const navigate = useNavigate(); - const isShowNavigation = hasMatched('/', '/profile/:id'); + const isShowNavigation = hasMatched('/', '/profile/:id', '/notification'); return ( <> {isShowNavigation && ( - + navigate('/')}> - {location.pathname.startsWith('/feed') || location.pathname === '/' ? : } + {location.pathname.startsWith('/feed') || location.pathname === '/' ? : } + + 피드 + - navigate('/write')}> - + navigate('/write')}> + {location.pathname.startsWith('/write') || location.pathname === '/write' ? ( + + ) : ( + + )} + + 글쓰기 + + + navigate('/notification')}> + {location.pathname.startsWith('/notification') || location.pathname === '/notification' ? ( + + ) : ( + + )} + + 알림 + navigate(`/profile/${getUserId()}`)}> - {Number(userIdFromParams) === getUserId() ? : } + {Number(userIdFromParams) === getUserId() ? : } + + 내 프로필 + )} diff --git a/src/pages/Feed/Feed.page.tsx b/src/pages/Feed/Feed.page.tsx index 809dbb79..f34c3ca6 100644 --- a/src/pages/Feed/Feed.page.tsx +++ b/src/pages/Feed/Feed.page.tsx @@ -1,15 +1,15 @@ import styled from '@emotion/styled'; -import { Header, Spacer, Text, theme, SVGHeaderFilter, SVGFeedWrite40, Box } from 'concept-be-design-system'; +import { Button, Header, Spacer, SVGHeaderFilter, SVGWritePencil, Text, theme } from 'concept-be-design-system'; import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; +import SEOMeta from '../../components/SEOMeta/SEOMeta'; +import Logo from '../../layouts/Logo'; +import { useWritingInfoQuery } from '../Write/hooks/queries/useWritingInfoQuery'; import BestIdeaCardListSection from './components/BestIdeaCardListSection/BestIdeaCardListSection'; import FilterBottomSheet from './components/FilterBottomSheet/FilterBottomSheet'; import NewIdeaCardListSection from './components/NewIdeaCardListSection/NewIdeaCardListSection'; import { getUserNickname } from './utils/getUserNickname'; -import SEOMeta from '../../components/SEOMeta/SEOMeta'; -import Logo from '../../layouts/Logo'; -import { useWritingInfoQuery } from '../Write/hooks/queries/useWritingInfoQuery'; const Feed = () => { const navigate = useNavigate(); @@ -39,12 +39,9 @@ const Feed = () => { - navigate('/write')} cursor="pointer" /> - - - {getUserNickname() || 'Guest1234'} + {getUserNickname() || 'Guest'} 님, @@ -53,13 +50,21 @@ const Feed = () => { - 재밌는 아이디어를 들려주세요! + 재밌있는 아이디어가 있으신가요? - - navigate('/write')} cursor="pointer"> - {`아이디어 적으러 가기 >`} - + + + diff --git a/src/pages/Feed/components/NewIdeaCardListSection/NewIdeaCardListSection.tsx b/src/pages/Feed/components/NewIdeaCardListSection/NewIdeaCardListSection.tsx index 739e50ea..e56a35fa 100644 --- a/src/pages/Feed/components/NewIdeaCardListSection/NewIdeaCardListSection.tsx +++ b/src/pages/Feed/components/NewIdeaCardListSection/NewIdeaCardListSection.tsx @@ -2,21 +2,19 @@ import styled from '@emotion/styled'; import { SVGProfileMessageDots, Spacer, Text } from 'concept-be-design-system'; import { Fragment, Suspense, useRef } from 'react'; -import NewIdeaCardListSkeleton from './NewIdeaCardListSkeleton'; import useConfirm from '../../../../hooks/useConfirm'; import { useDeleteIdea } from '../../../components/NewIdeaCard/hooks/mutations/useDeleteIdea'; import NewIdeaCard from '../../../components/NewIdeaCard/NewIdeaCard'; -import useNavigatePage from '../../../hooks/useNavigatePage'; import EmptyTabContentSection from '../../../Profile/components/EmptyTabContentSection'; import { useFilterParams } from '../../context/filterContext'; import { useIdeasQuery } from '../../hooks/queries/useIdeasQuery'; import { useFeedInfiniteFetch } from '../../hooks/useFeedInfiniteFetch'; import { getUserNickname } from '../../utils/getUserNickname'; +import NewIdeaCardListSkeleton from './NewIdeaCardListSkeleton'; const CardList = () => { const { filterParams } = useFilterParams(); const { ideas, fetchNextPage } = useIdeasQuery(filterParams); - const { goProfilePage } = useNavigatePage(); const { deleteIdea } = useDeleteIdea(); const openConfirm = useConfirm(); const nickname = getUserNickname(); @@ -66,13 +64,12 @@ const CardList = () => { return ( {isMine ? ( - - handleDeleteIdea(idea.id)} /> + + handleDeleteIdea(idea.id)} /> ) : ( - goProfilePage(idea.memberResponse.id)} /> diff --git a/src/pages/Feed/hooks/useFilteredBottomSheetState.ts b/src/pages/Feed/hooks/useFilteredBottomSheetState.ts index 9e14fe9b..7c3dfc00 100644 --- a/src/pages/Feed/hooks/useFilteredBottomSheetState.ts +++ b/src/pages/Feed/hooks/useFilteredBottomSheetState.ts @@ -63,11 +63,11 @@ const useFilteredBottomSheetState = ({ const filteredSkillCategory1Depth = filterParams?.skillCategoryIds?.[0] !== undefined ? getSkillCategory1DepthFrom2DepthSkillId(filterParams?.skillCategoryIds?.[0]).name - : undefined ?? ''; + : ''; const filteredSkillCategory2Depth = filterParams?.skillCategoryIds?.[0] !== undefined ? get2DepthNameFrom2DepthId(filterParams?.skillCategoryIds?.[0]) - : undefined ?? ''; + : ''; return { filteredBranches, diff --git a/src/pages/FeedDetail/FeedDetail.page.tsx b/src/pages/FeedDetail/FeedDetail.page.tsx index 4d4db38a..eb86091a 100644 --- a/src/pages/FeedDetail/FeedDetail.page.tsx +++ b/src/pages/FeedDetail/FeedDetail.page.tsx @@ -92,7 +92,7 @@ const FeedDetailPage = () => { - + {introduce} @@ -118,7 +118,7 @@ const FeedDetailPage = () => { {branchList.map((badge) => ( - + {badge} ))} @@ -132,7 +132,7 @@ const FeedDetailPage = () => { {purposeList.map((badge) => ( - + {badge} ))} @@ -164,7 +164,7 @@ const FeedDetailPage = () => { {skillCategories.map((badge) => ( - + {badge} ))} diff --git a/src/pages/Login/Login.tsx b/src/pages/Login/Login.tsx index 77e3d810..aab7e8ad 100644 --- a/src/pages/Login/Login.tsx +++ b/src/pages/Login/Login.tsx @@ -1,10 +1,9 @@ import styled from '@emotion/styled'; -import { Spacer, Text, theme, SVGLoginKakao, SVGLoginNaver, SVGLoginLogo, Flex } from 'concept-be-design-system'; +import { Flex, Spacer, SVGLoginKakao, SVGLoginLogo, SVGLoginNaver, Text, theme } from 'concept-be-design-system'; import { useNavigate } from 'react-router-dom'; import SEOMeta from '../../components/SEOMeta/SEOMeta'; import { BASE_URL } from '../../constants'; -import useAlert from '../../hooks/useAlert'; const KAKAO_REQUEST_URL = `${BASE_URL}/oauth/kakao`; const NAVER_REQUEST_URL = `${BASE_URL}/oauth/naver`; @@ -32,7 +31,7 @@ const Login = () => { - 카카오 로그인 + 카카오 로그인 @@ -44,7 +43,7 @@ const Login = () => { - 네이버 로그인 + 네이버 로그인 diff --git a/src/pages/Notification/Notification.page.tsx b/src/pages/Notification/Notification.page.tsx new file mode 100644 index 00000000..0a3fea29 --- /dev/null +++ b/src/pages/Notification/Notification.page.tsx @@ -0,0 +1,11 @@ +import { NotificationContainer } from './containers/NotificationContainer'; + +const NotificationPage = () => { + return ( +
+ +
+ ); +}; + +export default NotificationPage; diff --git a/src/pages/Notification/components/AdditionalText.tsx b/src/pages/Notification/components/AdditionalText.tsx new file mode 100644 index 00000000..ef18ecc3 --- /dev/null +++ b/src/pages/Notification/components/AdditionalText.tsx @@ -0,0 +1,28 @@ +import { Text, theme } from 'concept-be-design-system'; + +interface Props { + isEmpty: boolean; +} + +export const AdditionalText = ({ isEmpty }: Props) => { + const content = isEmpty ? '아직 받은 알림이 없어요!' : '모든 알림을 확인했어요!'; + + return ( +
+ + {content} + +
+ ); +}; diff --git a/src/pages/Notification/components/NotificationItem.tsx b/src/pages/Notification/components/NotificationItem.tsx new file mode 100644 index 00000000..e63c5f54 --- /dev/null +++ b/src/pages/Notification/components/NotificationItem.tsx @@ -0,0 +1,39 @@ +import { Badge, SVGGoldBell, Text } from 'concept-be-design-system'; +import { useNavigate } from 'react-router-dom'; +import { NotificationDTO } from '../types'; + +export const NotificationItem = ({ feedId, title, createAt, badges }: Omit) => { + const navigate = useNavigate(); + + return ( +
navigate(`/feed/${feedId}`)} + > +
+ + + 나에게 꼭 맞는 알림 + +
+ + {createAt} + +
+
+ + {title} + +
+
+ {badges.map((badge, idx) => ( +
+ + {badge} + +
+ ))} +
+
+ ); +}; diff --git a/src/pages/Notification/containers/NotificationContainer.tsx b/src/pages/Notification/containers/NotificationContainer.tsx new file mode 100644 index 00000000..5f4db432 --- /dev/null +++ b/src/pages/Notification/containers/NotificationContainer.tsx @@ -0,0 +1,23 @@ +import { identifyAbbreviationBadge } from '../../../utils/parsing'; +import { AdditionalText } from '../components/AdditionalText'; +import { NotificationItem } from '../components/NotificationItem'; +import { usePollingNotification } from '../hooks/queries/usePollingNotification'; + +export const NotificationContainer = () => { + const { notifications } = usePollingNotification(); + + return ( +
+ {notifications.map(({ id, feedId, title, createAt, badges }) => ( + + ))} + +
+ ); +}; diff --git a/src/pages/Notification/hooks/queries/usePollingNotification.ts b/src/pages/Notification/hooks/queries/usePollingNotification.ts new file mode 100644 index 00000000..4b18c654 --- /dev/null +++ b/src/pages/Notification/hooks/queries/usePollingNotification.ts @@ -0,0 +1,65 @@ +import { http } from '../../../../api/http'; +import { NotificationDTO } from '../../types'; + +export const NOTIFICATION_LIST_QUERY_KEY = 'NOTIFICATION_LIST_QUERY_KEY'; + +const _getNotificationList = () => http.get('/somewhere'); + +export const usePollingNotification = () => { + // const { data: notifications, ...rest } = useSuspenseQuery({ + // queryKey: [NOTIFICATION_LIST_QUERY_KEY], + // queryFn: () => _getNotificationList(), + // refetchInterval: 3600, + // }); + + const notifications = tempData; + const rest = {}; + + return { notifications, ...rest }; +}; + +const tempData = [ + { + id: 1, + feedId: 39, + title: '글 제목입니다. 두 줄짜리 제목으로 확장합니다. 두 줄짜리 제목으로 확장합니다.', + createAt: '2022.11.18', + badges: [ + '테스트-테스트', + '매우긴놈매우긴놈매우긴놈', + '매우', + '테스트-테스트', + '테스트-테스트', + '테스트-테스트', + '테스트-테스트', + ], + }, + { + id: 2, + feedId: 39, + title: '글 제목입니다. 줄짜리 제목으로 확장합니다.', + createAt: '2022.11.18', + badges: ['테스트-테스트', '테스트', '테스트', '테스트', '테스트테스트'], + }, + { + id: 3, + feedId: 39, + title: '글 제목입니다. 두 줄짜리 제목으로 확장합니다. 두 줄짜리 제목으로 확장합니다.', + createAt: '2022.11.18', + badges: ['첫번쨰가매우길다면?첫번쨰가매우길다면길다면?', '테스트'], + }, + { + id: 4, + feedId: 39, + title: '글 제목입니다. 두 줄짜리 제목으로 확장합니다. 두 줄짜리 제목으로 확장합니다.', + createAt: '2022.11.18', + badges: ['매우긴놈매우긴놈매우긴놈매우긴놈', '테스트-테스트', '테스트-테스트', '테스트-테스트'], + }, + { + id: 5, + feedId: 39, + title: '글 제목입니다. 두 줄짜리 제목으로 확장합니다. 두 줄짜리 제목으로 확장합니다.', + createAt: '2022.11.18', + badges: ['테스트', '테스트', '테스트', '테스트', '테스트', 's테스트', 's테스트', 's테스트'], + }, +]; diff --git a/src/pages/Notification/types/index.ts b/src/pages/Notification/types/index.ts new file mode 100644 index 00000000..e4a69970 --- /dev/null +++ b/src/pages/Notification/types/index.ts @@ -0,0 +1,7 @@ +export interface NotificationDTO { + id: number; + feedId: number; + title: string; + createAt: string; + badges: string[]; +} diff --git a/src/pages/Profile/Profile.page.tsx b/src/pages/Profile/Profile.page.tsx index 54f09a40..cd44250e 100644 --- a/src/pages/Profile/Profile.page.tsx +++ b/src/pages/Profile/Profile.page.tsx @@ -1,10 +1,10 @@ import { useParams } from 'react-router-dom'; +import SEOMeta from '../../components/SEOMeta/SEOMeta'; import MyProfile from './components/MyProfile/MyProfile.page'; import OtherProfile from './components/OtherProfile/OtherProfile.page'; import { useMemberInfoQuery } from './hooks/queries/useMemberInfoQuery'; import { getUserId } from './utils/getUserId'; -import SEOMeta from '../../components/SEOMeta/SEOMeta'; const Profile = () => { const { id: userIdFromParams } = useParams(); diff --git a/src/pages/Profile/components/MyProfile/IdeaSection.tsx b/src/pages/Profile/components/MyProfile/IdeaSection.tsx index 1ff292e1..7ff41bf0 100644 --- a/src/pages/Profile/components/MyProfile/IdeaSection.tsx +++ b/src/pages/Profile/components/MyProfile/IdeaSection.tsx @@ -45,6 +45,13 @@ const IdeaSection = ({ userId }: Props) => { {ideas.map((idea) => { const isMine = true; + const profile = { + profileImageUrl: '', + nickname: '', + mainSkill: '', + isBookmarked: false, + createdAt: 'testTvalue', + }; const content = { canEdit: isMine, branches: idea.branches, @@ -61,8 +68,8 @@ const IdeaSection = ({ userId }: Props) => { return ( - - handleDeleteIdea(idea.id)} /> + + handleDeleteIdea(idea.id)} /> diff --git a/src/pages/Profile/components/ProfileInfoSection.tsx b/src/pages/Profile/components/ProfileInfoSection.tsx index 562f5fa3..7cbe7720 100644 --- a/src/pages/Profile/components/ProfileInfoSection.tsx +++ b/src/pages/Profile/components/ProfileInfoSection.tsx @@ -57,13 +57,13 @@ const ProfileInfoSection = ({ memberInfo }: Props) => { {nickname} - + {renderWorkingAndLivingPlace()}
{isMyProfile === true && navigate(`/profile-edit`)}>프로필 수정} - + {introduction}
@@ -72,7 +72,7 @@ const ProfileInfoSection = ({ memberInfo }: Props) => { {skills.map(({ skillId, skillName, level }) => ( - + {`${skillName}, ${level}`} ))} @@ -85,7 +85,7 @@ const ProfileInfoSection = ({ memberInfo }: Props) => { {joinPurposes.map((badge) => ( - + {badge} ))} diff --git a/src/pages/ProfileEdit/ProfileEdit.page.tsx b/src/pages/ProfileEdit/ProfileEdit.page.tsx index 686bddee..8c2ae89e 100644 --- a/src/pages/ProfileEdit/ProfileEdit.page.tsx +++ b/src/pages/ProfileEdit/ProfileEdit.page.tsx @@ -299,7 +299,7 @@ const ProfileEdit = () => { {selectedSkillDepths.map((skill, idx) => { return ( - + {skill.name} ); diff --git a/src/pages/SignUp/SignUp.page.tsx b/src/pages/SignUp/SignUp.page.tsx index de7cdb76..4d612c36 100644 --- a/src/pages/SignUp/SignUp.page.tsx +++ b/src/pages/SignUp/SignUp.page.tsx @@ -2,24 +2,21 @@ import styled from '@emotion/styled'; import { Box, Button, - CheckboxContainer, Dropdown, Field, Flex, Header, ImageView, PNGDefaultProfileInfo100, - SVGLoginImageWrite, Spacer, + SVGLoginImageWrite, Tag, Text, theme, - useCheckbox, useDropdown, useField, } from 'concept-be-design-system'; -import { FormEvent } from 'react'; -import { useLocation } from 'react-router-dom'; +import { useLocation, useNavigate } from 'react-router-dom'; import SEOMeta from '../../components/SEOMeta/SEOMeta.tsx'; import { NICKNAME_REG_EXP } from '../../constants/index.ts'; @@ -28,34 +25,21 @@ import { OauthMemberInfo } from '../../types/login.ts'; import useCheckDuplicateNickname from './hooks/useCheckDuplicateNickname.ts'; import useDefaultProfileImage from './hooks/useDefaultProfileImage.ts'; import useSetDetailSkills from './hooks/useSetDetailSkills.ts'; -import useSignUpMutation from './hooks/useSignUpMutation.ts'; import useSignUpQuery from './hooks/useSignUpQuery.ts'; -import useValidateUserInfo from './hooks/useValidateUserInfo.ts'; import { DropdownValue, FieldValue } from './types'; - -interface CheckboxValue { - goal: CheckboxOption[]; -} - -interface CheckboxOption { - id: number; - name: string; - checked: boolean; -} +import { generateQueryString } from './utils/manageQueryString.ts'; const SignUpPage = () => { + const navigate = useNavigate(); const openAlert = useAlert(); const { state: memberInfo }: { state: OauthMemberInfo | null } = useLocation(); - const { postSignUp } = useSignUpMutation(); - const { mainSkills, detailSkills, skillLevels, regions, purposes } = useSignUpQuery(); + + const { mainSkills, detailSkills, skillLevels, regions } = useSignUpQuery(); const { fieldValue, fieldErrorValue, setFieldErrorValue, onChangeField } = useField({ nickname: '', company: '', intro: '', }); - const { checkboxValue, selectedCheckboxId, onChangeCheckbox } = useCheckbox({ - goal: purposes, - }); const { dropdownValue, onResetDropdown, onClickDropdown } = useDropdown({ mainSkill: '', skillDepthOne: '', @@ -74,7 +58,20 @@ const SignUpPage = () => { defaultProfileImage: PNGDefaultProfileInfo100, }); - useValidateUserInfo(memberInfo); + const formData = { + nickname: fieldValue.nickname, + mainSkillId: mainSkills.find(({ name }) => dropdownValue.mainSkill === name)?.id || 0, + profileImageUrl: profileImageUrl === PNGDefaultProfileInfo100 ? null : memberInfo?.profileImageUrl || '', + skills: selectedSkillDepths.map(({ id, name }) => ({ skillId: id, level: name.split(', ')[1] })), + livingPlaceId: regions.find((place) => place.name === dropdownValue.region)?.id || 1, + workingPlace: fieldValue.company, + introduction: fieldValue.intro, + email: memberInfo?.email || '', + oauthId: memberInfo?.oauthId || '', + oauthServerType: memberInfo?.oauthServerType || '', + }; + + // useValidateUserInfo(memberInfo); useCheckDuplicateNickname({ nickname: fieldValue.nickname, setFieldErrorValue }); const validateInput = () => { @@ -90,42 +87,14 @@ const SignUpPage = () => { ]; }; - const onSubmit = (e: FormEvent) => { + const onClickNextStep = (e: React.MouseEvent) => { e.preventDefault(); - if (!fieldValue.nickname) { - openAlert({ content: '닉네임을 입력해 주세요.' }); - return; - } - - if (!dropdownValue.mainSkill) { - openAlert({ content: '대표 스킬을 선택해 주세요.' }); - return; - } + if (!fieldValue.nickname) return openAlert({ content: '닉네임은 필수 값입니다.' }); + if (!dropdownValue.mainSkill) return openAlert({ content: '대표 스킬은 필수 값입니다.' }); + if (selectedSkillDepths.length === 0) return openAlert({ content: '세부 스킬은 최소 한 개 이상 선택해주세요.' }); - if (selectedSkillDepths.length === 0) { - openAlert({ content: '세부 스킬을 하나 이상 선택해 주세요.' }); - return; - } - - if (selectedCheckboxId.goal.length === 0) { - openAlert({ content: '가입 목적을 하나 이상 선택해 주세요.' }); - return; - } - - postSignUp({ - nickname: fieldValue.nickname, - mainSkillId: mainSkills.find(({ name }) => dropdownValue.mainSkill === name)?.id || 0, - profileImageUrl: profileImageUrl === PNGDefaultProfileInfo100 ? null : memberInfo?.profileImageUrl || '', - skills: selectedSkillDepths.map(({ id, name }) => ({ skillId: id, level: name.split(', ')[1] })), - joinPurposes: selectedCheckboxId.goal, - livingPlaceId: regions.find((place) => place.name === dropdownValue.region)?.id || 1, - workingPlace: fieldValue.company, - introduction: fieldValue.intro, - email: memberInfo?.email || '', - oauthId: memberInfo?.oauthId || '', - oauthServerType: memberInfo?.oauthServerType || '', - }); + navigate(`/sign-up-match?${generateQueryString(formData)}`); }; return ( @@ -147,7 +116,7 @@ const SignUpPage = () => { - + { {selectedSkillDepths.map((skill, idx) => { return ( - + {skill.name} ); @@ -304,19 +273,6 @@ const SignUpPage = () => { - - - - - - 지역 @@ -349,7 +305,7 @@ const SignUpPage = () => { - + diff --git a/src/pages/SignUp/SignUpMatch.page.tsx b/src/pages/SignUp/SignUpMatch.page.tsx new file mode 100644 index 00000000..36d157bc --- /dev/null +++ b/src/pages/SignUp/SignUpMatch.page.tsx @@ -0,0 +1,233 @@ +import styled from '@emotion/styled'; +import { + Box, + Button, + CheckboxContainer, + Flex, + Header, + RadioContainer, + Spacer, + SVGGoldBell, + SVGRadioCheck24, + SVGRadioUncheck24, + Text, + theme, + useCheckbox, + useRadio, +} from 'concept-be-design-system'; +import { FormEvent, useState } from 'react'; +import { useLocation } from 'react-router-dom'; +import useAlert from '../../hooks/useAlert'; +import { OauthMemberInfo } from '../../types/login'; +import { + Sheet_Left, + Sheet_leftItem, + Sheet_radioDiv, + Sheet_right, + TwoDepthBottomSheet, +} from '../Write/components/TwoDepthBottomSheet'; +import { useWritingInfoQuery } from '../Write/hooks/queries/useWritingInfoQuery'; +import { get2DepthCountsBy1Depth } from '../Write/utils/get2DepthCountsBy1Depth'; +import useSignUpMutation from './hooks/useSignUpMutation'; +import { parseQueryString } from './utils/manageQueryString'; + +interface QueryStringProps { + nickname: string; + mainSkillId: number; + skills: { + skillId: number; + level: string; + }[]; + joinPurposes: number[]; + livingPlaceId: number; + workingPlace: string; + introduction: string; +} + +interface CheckboxValue { + goal: CheckboxOption[]; +} + +interface CheckboxOption { + id: number; + name: string; + checked: boolean; +} + +const SignUpMatchPage = () => { + const openAlert = useAlert(); + const { state: memberInfo }: { state: OauthMemberInfo | null } = useLocation(); + + const [isOpenBranchBottomSheet, setIsOpenBranchBottomSheet] = useState(false); + const [prevFormData, _] = useState(parseQueryString); + + const { branches, purposes, recruitmentPlaces, cooperationWays, skillCategoryResponses } = useWritingInfoQuery(); + + const { postSignUp } = useSignUpMutation(); + + const { checkboxValue, selectedCheckboxId, onChangeCheckbox } = useCheckbox({ + goal: purposes, + }); + const { radioValue, selectedRadioName, onChangeRadio } = useRadio({ + cooperationWays, + }); + + const branchBottomSheetLeftItems = [] as any; + const branchBottomSheetRightItems = [] as any; + + // useValidateUserInfo(memberInfo); + + const onSubmit = (e: FormEvent) => { + e.preventDefault(); + + if (!memberInfo) return openAlert({ content: '유저 정보가 없습니다. 잘못된 접근 방법입니다.' }); + + postSignUp({ + ...memberInfo, + ...prevFormData, + joinPurposes: selectedCheckboxId.goal, + }); + }; + + return ( + +
+ + + + + + 프로젝트 매칭 설정 + + + + + +
+ + + + + + + + + 참여하고자 하는 프로젝트 조건을 등록해주세요. + + + 딱 맞는 모집 공고가 뜨면 알려드릴게요! + + + + + + + + + + + onChangeRadio(e, 'cooperationWays')} + gap="large" + required + /> + + + + + + + 분야 + +
{ + setIsOpenBranchBottomSheet(true); + }} + > + + + + 추가하기 + + +
+
+ + setIsOpenBranchBottomSheet(false)} + > + + {branchBottomSheetLeftItems.map((item: any) => { + return ( + setSelectedTeamRecruitment1Depth(item)} + checked={selectedTeamRecruitment1Depth === item} + > + + {item} + + + + {get2DepthCountsBy1Depth(selectedSkillResponses, skillCategoryResponses)[item]} + + + ); + })} + + + {branchBottomSheetRightItems.map((item: any) => { + return ( + onClickTeamRecruitment(item)}> + + {item.name} + + {selectedSkillResponses.includes(item) ? : } + + ); + })} + + +
+ + +
+ + + + +
+
+ ); +}; + +const MainWrapper = styled.form` + background-color: ${theme.color.c1}; + height: 100%; +`; + +export default SignUpMatchPage; diff --git a/src/pages/SignUp/utils/manageQueryString.ts b/src/pages/SignUp/utils/manageQueryString.ts new file mode 100644 index 00000000..431e0cce --- /dev/null +++ b/src/pages/SignUp/utils/manageQueryString.ts @@ -0,0 +1,24 @@ +export const generateQueryString = (params: Record) => { + return Object.keys(params) + .map((key) => { + if (typeof params[key] === 'object') { + return `${key}=${JSON.stringify(params[key])}`; + } + + return `${key}=${params[key]}`; + }) + .join('&'); +}; + +export const parseQueryString = >() => { + const searchParams = new URLSearchParams(window.location.search); + const searchParamsObject = Object.fromEntries(searchParams.entries()); + + Object.keys(searchParamsObject).forEach((key) => { + if (key === 'skills') { + searchParamsObject[key] = JSON.parse(searchParamsObject[key]); + } + }); + + return Object.fromEntries(searchParams.entries()) as T; +}; diff --git a/src/pages/Write/Write.page.tsx b/src/pages/Write/Write.page.tsx index de6e68b7..902250d6 100644 --- a/src/pages/Write/Write.page.tsx +++ b/src/pages/Write/Write.page.tsx @@ -1,14 +1,11 @@ import styled from '@emotion/styled'; import { - BottomSheet, Box, CheckboxContainer, Divider, Flex, RadioContainer, - SVGAdd24, SVGCancel, - SVGHeaderCheck24, SVGRadioCheck24, SVGRadioUncheck24, Spacer, @@ -25,6 +22,13 @@ import AddImages from './components/AddImages'; import Header from './components/Header'; import RecruitmentPlaceSection from './components/RecruitmentPlaceSection'; import TitleAndIntroduceSection from './components/TitleAndIntroduceSection'; +import { + Sheet_Left, + Sheet_leftItem, + Sheet_radioDiv, + Sheet_right, + TwoDepthBottomSheet, +} from './components/TwoDepthBottomSheet'; import { usePostIdeasMutation } from './hooks/mutations/usePostIdeasMutation'; import { useWritingInfoQuery } from './hooks/queries/useWritingInfoQuery'; import { Info, PostIdeasRequest } from './types'; @@ -37,7 +41,8 @@ const WritePage = () => { const [title, setTitle] = useState(''); const [introduce, setIntroduce] = useState(''); - const [isOpenBottomSheet, setIsOpenBottomSheet] = useState(false); + const [isOpenTeamMateBottomSheet, setIsOpenTeamMateBottomSheet] = useState(false); + const [isOpenBranchBottomSheet, setIsOpenBranchBottomSheet] = useState(false); const [selectedTeamRecruitment1Depth, setSelectedTeamRecruitment1Depth] = useState(skillCategoryResponses[0].name); const [selectedSkillResponses, setSelectedSkillResponses] = useState([]); const [images, setImages] = useState([]); @@ -53,16 +58,19 @@ const WritePage = () => { recruitmentPlace: '', }); - const sheetLeftItems = skillCategoryResponses.map((item) => item.name); - const sheetRightItems = skillCategoryResponses.find((item) => item.name === selectedTeamRecruitment1Depth) - ?.skillResponses; + const branchBottomSheetLeftItems = [] as any; + const branchBottomSheetRightItems = [] as any; + const teamMateBottomSheetLeftItems = skillCategoryResponses.map((item) => item.name); + const teamMateBottomSheetRightItems = skillCategoryResponses.find( + (item) => item.name === selectedTeamRecruitment1Depth, + )?.skillResponses; const canSubmit = selectedCheckboxId.branches.length > 0 && selectedCheckboxId.purposes.length > 0 && !!selectedRadioName.cooperationWays; - if (!sheetRightItems) { + if (!teamMateBottomSheetRightItems) { console.error('sheetRightItems is null'); return null; } @@ -168,13 +176,67 @@ const WritePage = () => { - + + + 분야 + +
{ + setIsOpenBranchBottomSheet(true); + }} + > + + + + 추가하기 + + +
+
+ + setIsOpenBranchBottomSheet(false)} + > + + {branchBottomSheetLeftItems.map((item: any) => { + return ( + setSelectedTeamRecruitment1Depth(item)} + checked={selectedTeamRecruitment1Depth === item} + > + + {item} + + + + {get2DepthCountsBy1Depth(selectedSkillResponses, skillCategoryResponses)[item]} + + + ); + })} + + + {branchBottomSheetRightItems.map((item: any) => { + return ( + onClickTeamRecruitment(item)}> + + {item.name} + + {selectedSkillResponses.includes(item) ? : } + + ); + })} + +
{ -
+ 팀원 모집
{ - setIsOpenBottomSheet(true); + setIsOpenTeamMateBottomSheet(true); }} > - - - - - 팀원 추가 - - + + + + 팀원 추가 + +
-
+
@@ -239,60 +306,43 @@ const WritePage = () => { - setIsOpenBottomSheet(false)}> - - { - setIsOpenBottomSheet(false); - }} - cursor="pointer" - /> - - 팀원 선택 - - { - setIsOpenBottomSheet(false); - }} - cursor="pointer" - /> - - - - {sheetLeftItems.map((item) => { - return ( - setSelectedTeamRecruitment1Depth(item)} - checked={selectedTeamRecruitment1Depth === item} - > - - {item} - - - - {get2DepthCountsBy1Depth(selectedSkillResponses, skillCategoryResponses)[item]} - - - ); - })} - - - {sheetRightItems.map((item) => { - return ( - onClickTeamRecruitment(item)}> - - {item.name} - - {selectedSkillResponses.includes(item) ? : } - - ); - })} - - - + setIsOpenTeamMateBottomSheet(false)} + > + + {teamMateBottomSheetLeftItems.map((item) => { + return ( + setSelectedTeamRecruitment1Depth(item)} + checked={selectedTeamRecruitment1Depth === item} + > + + {item} + + + + {get2DepthCountsBy1Depth(selectedSkillResponses, skillCategoryResponses)[item]} + + + ); + })} + + + {teamMateBottomSheetRightItems.map((item) => { + return ( + onClickTeamRecruitment(item)}> + + {item.name} + + {selectedSkillResponses.includes(item) ? : } + + ); + })} + + ); @@ -311,57 +361,6 @@ const BottomWrapper = styled.div` gap: 35px; `; -const Sheet_TopBox = styled.div` - box-sizing: border-box; - display: flex; - flex-direction: row; - align-items: center; - justify-content: space-between; - width: 100%; - height: 54px; - padding: 0 22px; -`; -const Sheet_BodyBox = styled.div` - width: 100%; - display: flex; - flex-direction: row; -`; - -const Sheet_Left = styled.div` - width: 38%; - cursor: pointer; -`; - -const Sheet_leftItem = styled.div<{ checked: boolean }>` - padding: 10px 22px; - - background-color: ${({ checked }) => (checked ? '' : theme.color.bg1)}; - display: flex; - flex-direction: row; - justify-content: start; - align-items: center; - - height: 34px; -`; -const Sheet_right = styled.div` - width: 62%; - box-sizing: border-box; - padding: 0 22px; -`; - -const Sheet_radioDiv = styled.div` - width: 100%; - - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - - height: 54px; - border-bottom: 1px solid rgba(0, 0, 0, 0.1); - cursor: pointer; -`; - const TeamLabelBox = styled.div` display: flex; flex-wrap: wrap; diff --git a/src/pages/Write/components/TwoDepthBottomSheet.tsx b/src/pages/Write/components/TwoDepthBottomSheet.tsx new file mode 100644 index 00000000..f486eaa7 --- /dev/null +++ b/src/pages/Write/components/TwoDepthBottomSheet.tsx @@ -0,0 +1,77 @@ +import styled from '@emotion/styled'; +import { BottomSheet, SVGCancel, SVGHeaderCheck24, Text, theme } from 'concept-be-design-system'; +import { PropsWithChildren } from 'react'; + +interface Props { + title: string; + isOpen: boolean; + onClose: () => void; +} + +export const TwoDepthBottomSheet = ({ title, isOpen, onClose, children }: PropsWithChildren) => { + return ( + + + + + {title} + + + + {children} + + ); +}; + +const Sheet_TopBox = styled.div` + box-sizing: border-box; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + width: 100%; + height: 54px; + padding: 0 22px; +`; + +const Sheet_BodyBox = styled.div` + width: 100%; + display: flex; + flex-direction: row; +`; + +export const Sheet_Left = styled.div` + width: 38%; + cursor: pointer; +`; + +export const Sheet_leftItem = styled.div<{ checked: boolean }>` + padding: 10px 22px; + + background-color: ${({ checked }) => (checked ? '' : theme.color.bg1)}; + display: flex; + flex-direction: row; + justify-content: start; + align-items: center; + + height: 34px; +`; + +export const Sheet_right = styled.div` + width: 62%; + box-sizing: border-box; + padding: 0 22px; +`; + +export const Sheet_radioDiv = styled.div` + width: 100%; + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + + height: 54px; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + cursor: pointer; +`; diff --git a/src/pages/components/HyperLinkText/HyperLinkText.tsx b/src/pages/components/HyperLinkText/HyperLinkText.tsx index 39c4e905..2bfd0c52 100644 --- a/src/pages/components/HyperLinkText/HyperLinkText.tsx +++ b/src/pages/components/HyperLinkText/HyperLinkText.tsx @@ -1,12 +1,10 @@ import styled from '@emotion/styled'; -import { Text } from 'concept-be-design-system'; +import { Text, theme } from 'concept-be-design-system'; import { Fragment } from 'react'; -import { ColorKeyType, FontKeyType } from '../../../styles/theme'; - interface Props { - font: FontKeyType; - color: ColorKeyType; + font: keyof typeof theme.font; + color: keyof typeof theme.color; lineHeight?: string; children: string; } diff --git a/src/pages/components/NewIdeaCard/NewIdeaCard.tsx b/src/pages/components/NewIdeaCard/NewIdeaCard.tsx index b05e07eb..3417647f 100644 --- a/src/pages/components/NewIdeaCard/NewIdeaCard.tsx +++ b/src/pages/components/NewIdeaCard/NewIdeaCard.tsx @@ -5,8 +5,7 @@ import { useNavigate } from 'react-router-dom'; import Content from './compound/Content/Content'; import Footer from './compound/Footer/Footer'; -import Profile from './compound/Profile/Profile'; -import { NewIdeaCardContextType, NewIdeaCardContext } from './NewIdeaCardContext'; +import { NewIdeaCardContext, NewIdeaCardContextType } from './NewIdeaCardContext'; export interface Props extends NewIdeaCardContextType {} @@ -20,7 +19,6 @@ const NewIdeaCard = ({ id, profile, content, footer, children }: PropsWithChildr ); }; -NewIdeaCard.Profile = Profile; NewIdeaCard.Content = Content; NewIdeaCard.Footer = Footer; diff --git a/src/pages/components/NewIdeaCard/NewIdeaCardContext.ts b/src/pages/components/NewIdeaCard/NewIdeaCardContext.ts index 821b0e96..63f1d497 100644 --- a/src/pages/components/NewIdeaCard/NewIdeaCardContext.ts +++ b/src/pages/components/NewIdeaCard/NewIdeaCardContext.ts @@ -1,4 +1,5 @@ import { createContext, useContext } from 'react'; +import { identifyAbbreviationBadge } from '../../../utils/parsing'; export interface NewIdeaCardContextType { id: number; @@ -57,7 +58,7 @@ export const useContentContext = () => { const context = useNewIdeaCardContext(); const { content } = context; - return content; + return { branch: identifyAbbreviationBadge(content.branches), ...content }; }; export const useFooterContext = () => { diff --git a/src/pages/components/NewIdeaCard/compound/Content/Content.tsx b/src/pages/components/NewIdeaCard/compound/Content/Content.tsx index eecb457f..578c798f 100644 --- a/src/pages/components/NewIdeaCard/compound/Content/Content.tsx +++ b/src/pages/components/NewIdeaCard/compound/Content/Content.tsx @@ -1,31 +1,48 @@ import styled from '@emotion/styled'; -import { Badge, Flex, Spacer, Text, theme, SVGMore24 } from 'concept-be-design-system'; +import { + Badge, + Box, + Flex, + Spacer, + SVGMore24, + SVGScrap24, + SVGScrapFilled24, + Text, + theme, +} from 'concept-be-design-system'; import { MouseEventHandler, useState } from 'react'; import { useNavigate } from 'react-router-dom'; +import { identifyAbbreviationBadge } from '../../../../../utils/parsing'; +import { useDeleteBookmarkIdea } from '../../../../Feed/hooks/mutations/useDeleteBookmarkIdea'; +import { usePostBookmarkIdea } from '../../../../Feed/hooks/mutations/usePostBookmarkIdea'; +import { useContentContext, useIdeaIdContext, useProfileContext } from '../../NewIdeaCardContext'; import ContentEditDropdown from './ContentEditDropdown'; -import { useContentContext, useIdeaIdContext } from '../../NewIdeaCardContext'; type Props = { - onClickDelete?: () => void; + onClick?: () => void; }; -const Content = ({ onClickDelete }: Props) => { +const Content = ({ onClick }: Props) => { const ideaId = useIdeaIdContext(); const { canEdit, branches, title, introduce, skillCategories } = useContentContext(); + const { isBookmarked, createdAt } = useProfileContext(); + + const { postBookmarkIdea } = usePostBookmarkIdea(); + const { deleteBookmarkIdea } = useDeleteBookmarkIdea(); const [isDropdownOpen, setIsDropdownOpen] = useState(false); const isSkillCategoriesExist = skillCategories.length > 0; - const SkillCategoriesBadges = (skillCategories: string[]) => { - const badges = skillCategories.map((teamRecruitment, idx) => ( - {teamRecruitment} - )); + const bookmarkIdea: MouseEventHandler = (e) => { + e.stopPropagation(); + postBookmarkIdea(ideaId); + }; - return badges.length > 5 - ? [...badges.slice(0, 5), +{badges.length - 5} 모집중] - : badges; + const unbookmarkIdea: MouseEventHandler = (e) => { + e.stopPropagation(); + deleteBookmarkIdea(ideaId); }; const toggleDropdown: MouseEventHandler = (e) => { @@ -42,22 +59,36 @@ const Content = ({ onClickDelete }: Props) => { - - {branches.join(' / ')} - - - {title} + + + {`${createdAt?.split('T')[0]} ${createdAt.split('T')[1]?.substring(0, 5)}`} + - {canEdit && ( + {canEdit ? ( - {isDropdownOpen && } + {isDropdownOpen && } + ) : ( + + {isBookmarked ? : } + )} - + + + +
+ {branches.map((branch) => ( + + {branch} + + ))} +
+ + {introduce} @@ -66,7 +97,11 @@ const Content = ({ onClickDelete }: Props) => { - {SkillCategoriesBadges(skillCategories)} + {identifyAbbreviationBadge(skillCategories, 15).map((category) => ( + + {category} + + ))} diff --git a/src/pages/components/NewIdeaCard/compound/Profile/Profile.tsx b/src/pages/components/NewIdeaCard/compound/Profile/Profile.tsx deleted file mode 100644 index 80bb5bce..00000000 --- a/src/pages/components/NewIdeaCard/compound/Profile/Profile.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import styled from '@emotion/styled'; -import { - Box, - Flex, - ImageView, - PNGDefaultProfileInfo36, - SVGScrap24, - SVGScrapFilled24, - Spacer, - Text, - TextDivider, -} from 'concept-be-design-system'; -import { MouseEventHandler } from 'react'; - -import { useDeleteBookmarkIdea } from '../../../../Feed/hooks/mutations/useDeleteBookmarkIdea'; -import { usePostBookmarkIdea } from '../../../../Feed/hooks/mutations/usePostBookmarkIdea'; -import { formatCommentDate } from '../../../../Feed/utils/formatCommentDate'; -import { useIdeaIdContext, useProfileContext } from '../../NewIdeaCardContext'; - -type Props = { - onClickProfile?: () => void; -}; - -const Profile = ({ onClickProfile }: Props) => { - const id = useIdeaIdContext(); - const { profileImageUrl, nickname, mainSkill, isBookmarked, createdAt } = useProfileContext(); - const { postBookmarkIdea } = usePostBookmarkIdea(); - const { deleteBookmarkIdea } = useDeleteBookmarkIdea(); - - const bookmarkIdea: MouseEventHandler = (e) => { - e.stopPropagation(); - postBookmarkIdea(id); - }; - - const unbookmarkIdea: MouseEventHandler = (e) => { - e.stopPropagation(); - deleteBookmarkIdea(id); - }; - - const handleClickProfile: MouseEventHandler = (e) => { - e.stopPropagation(); - onClickProfile?.(); - }; - - return ( - - - - - - - - - {nickname} - - - - - - {mainSkill} - - - - - - {formatCommentDate(createdAt)} - - - - - {isBookmarked ? : } - - ); -}; - -export default Profile; - -const ProfileWrapper = styled.div` - display: flex; - justify-content: space-between; - align-items: center; - margin: 0 0 22px 0; -`; - -const ProfileBox = styled.div` - display: flex; - gap: 10px; -`; - -const FixedSizeText = styled(Text)` - width: max-content; -`; diff --git a/src/router.tsx b/src/router.tsx index eed2b3d2..2970f7cb 100644 --- a/src/router.tsx +++ b/src/router.tsx @@ -18,6 +18,8 @@ const Profile = lazy(() => import('./pages/Profile/Profile.page')); const ProfileEdit = lazy(() => import('./pages/ProfileEdit/ProfileEdit.page')); const SignUpPage = lazy(() => import('./pages/SignUp/SignUp.page')); const NeedAuth = lazy(() => import('./pages/NeedAuth')); +const SignUpMatchPage = lazy(() => import('./pages/SignUp/SignUpMatch.page')); +const NotificationPage = lazy(() => import('./pages/Notification/Notification.page')); interface RouteElement { path: string; @@ -91,6 +93,14 @@ const routes: RouteElement[] = [ path: '/sign-up', element: withAsyncBoundary(), }, + { + path: '/sign-up-match', + element: withAsyncBoundary(), + }, + { + path: '/notification', + element: withAsyncBoundary(), + }, ], }, ]; diff --git a/src/styles/theme.ts b/src/styles/theme.ts deleted file mode 100644 index 2a1d9c67..00000000 --- a/src/styles/theme.ts +++ /dev/null @@ -1,102 +0,0 @@ -const color = { - c1: '#5F27FF', - c2: '#0DE361', - c3: '#E84C4C', - bg1: '#F5F6F8', - b: '#000000', - t: '#666', - b2: 'rgba(0, 0, 0, 0.87)', - b4: 'rgba(0, 0, 0, 0.73)', - b6: 'rgba(0, 0, 0, 0.60)', - b9: 'rgba(0, 0, 0, 0.40)', - ba: 'rgba(0, 0, 0, 0.33)', - l1: 'rgba(0, 0, 0, 0.15)', - l2: 'rgba(0, 0, 0, 0.10)', - l3: 'rgba(0, 0, 0, 0.05)', - w1: '#FFFFFF', - w2: 'rgba(255, 255, 255, 0.90)', - disabled: 'rgba(245, 246, 248, 1)', -} as const; - -const font = { - suit12r: { - fontSize: 12, - fontWeight: 400, - }, - suit12m: { - fontSize: 12, - fontWeight: 500, - }, - suit12b: { - fontSize: 12, - fontWeight: 700, - }, - suit13m: { - fontSize: 13, - fontWeight: 500, - }, - suit14r: { - fontSize: 14, - fontWeight: 400, - }, - suit14m: { - fontSize: 14, - fontWeight: 500, - }, - suit14sm: { - fontSize: 14, - fontWeight: 600, - }, - suit14b: { - fontSize: 14, - fontWeight: 700, - }, - suit15ra: { - fontSize: 15, - fontWeight: 400, - }, - suit15rb: { - fontSize: 15, - fontWeight: 400, - }, - suit15m: { - fontSize: 15, - fontWeight: 500, - }, - suit15sb: { - fontSize: 15, - fontWeight: 600, - }, - suit16m: { - fontSize: 16, - fontWeight: 500, - }, - suit16sb: { - fontSize: 16, - fontWeight: 600, - }, - suit18sb: { - fontSize: 18, - fontWeight: 600, - }, - suit22r: { - fontSize: 22, - fontWeight: 400, - }, - suit22sb: { - fontSize: 22, - fontWeight: 600, - }, -} as const; - -export type ColorType = typeof color; -export type FontType = typeof font; -export type ColorKeyType = keyof typeof color; -export type FontKeyType = keyof typeof font; - -const theme = { - color, - font, -} as const; - -export default theme; diff --git a/src/utils/parsing.ts b/src/utils/parsing.ts new file mode 100644 index 00000000..f8d7056c --- /dev/null +++ b/src/utils/parsing.ts @@ -0,0 +1,28 @@ +export const identifyAbbreviationBadge = (badges: string[], limitWordLength?: number) => { + const limitTotalWordLength = limitWordLength || 25; + const limitOriginWordLength = limitTotalWordLength - 5; + + const totalWordLength = badges.reduce((acc, cur) => acc + cur.length, 0); + + if (totalWordLength < limitTotalWordLength) return badges; + + const firstLength = !!badges[0] ? badges[0].length : 0; + const secondLength = !!badges[1] ? badges[1].length : 0; + const thirdLength = !!badges[2] ? badges[2].length : 0; + const fourthLength = !!badges[3] ? badges[3].length : 0; + const fifthLength = !!badges[4] ? badges[4].length : 0; + + if (firstLength > limitOriginWordLength) return [badges[0], `+ ${badges.length - 1}`]; + if (firstLength + secondLength > limitOriginWordLength) return [badges[0], badges[1], `+ ${badges.length - 2}`]; + if (firstLength + secondLength + thirdLength > limitOriginWordLength) { + return [badges[0], badges[1], badges[2], `+ ${badges.length - 3}`]; + } + if ( + firstLength + secondLength + thirdLength + fourthLength > limitOriginWordLength || + firstLength + secondLength + thirdLength + fourthLength + fifthLength > limitOriginWordLength + ) { + return [badges[0], badges[1], badges[2], badges[3], `+ ${badges.length - 4}`]; + } + + return [badges[0], badges[1], badges[2], badges[3], badges[4], `+ ${badges.length - 5}`]; +}; diff --git a/yarn.lock b/yarn.lock index c56f06b7..d0fb2f1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1303,10 +1303,10 @@ concat-map@0.0.1: resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -concept-be-design-system@^0.5.3: - version "0.5.3" - resolved "https://registry.npmjs.org/concept-be-design-system/-/concept-be-design-system-0.5.3.tgz" - integrity sha512-ThU8egZDs1krKizh6YqXiTvlQcUQV46HFD1lGVlsTThrvH1m2YRkPht6NwbZ2rvsO+K5e8QGS9CM1PzZUSiJiA== +concept-be-design-system@^0.5.11: + version "0.5.11" + resolved "https://registry.npmjs.org/concept-be-design-system/-/concept-be-design-system-0.5.11.tgz" + integrity sha512-aCULAmJ36lpSQa0i8slY9c+AnqtcNDSWxxTP27BCtYZTo0ejCkOfqGENOGjo4WxEe5VSkvoiVKTe/dkdAYoPZw== dependencies: "@emotion/react" "^11.11.1" "@emotion/styled" "^11.11.0"