├── src
├── assets
│ ├── .gitkeep
│ └── PretendardVariable.woff2
├── foundations
│ ├── Icon
│ │ ├── index.js
│ │ ├── Icon.styled.js
│ │ └── Icon.jsx
│ ├── Logo
│ │ ├── index.js
│ │ ├── Logo.styled.js
│ │ └── Logo.stories.jsx
│ ├── Background
│ │ ├── index.js
│ │ ├── Background.jsx
│ │ └── Background.styled.js
│ ├── IconSocial
│ │ ├── index.js
│ │ ├── IconSocial.styled.js
│ │ └── IconSocial.jsx
│ ├── Typography
│ │ ├── index.js
│ │ ├── Typography.jsx
│ │ └── Typography.styled.js
│ ├── index.js
│ └── Color
│ │ └── Color.stories.jsx
├── components
│ ├── patterns
│ │ ├── Toc
│ │ │ ├── index.js
│ │ │ ├── TocNav
│ │ │ │ ├── index.js
│ │ │ │ ├── TocNav.styled.js
│ │ │ │ └── TocNav.stories.jsx
│ │ │ ├── TocItem
│ │ │ │ ├── index.js
│ │ │ │ └── TocItem.jsx
│ │ │ ├── Toc.styled.js
│ │ │ ├── Toc.stories.jsx
│ │ │ └── Toc.jsx
│ │ ├── Card
│ │ │ └── index.js
│ │ ├── Modal
│ │ │ ├── index.js
│ │ │ └── Modal.stories.jsx
│ │ ├── Sort
│ │ │ ├── index.js
│ │ │ ├── Sort.styled.js
│ │ │ └── Sort.stories.jsx
│ │ ├── Comment
│ │ │ ├── index.js
│ │ │ └── Comment.stories.jsx
│ │ ├── Dropdown
│ │ │ ├── index.js
│ │ │ ├── DropdownSmall
│ │ │ │ ├── index.js
│ │ │ │ ├── DropdownSmall.stories.jsx
│ │ │ │ └── DropdownSmall.styled.js
│ │ │ ├── DropdownItem
│ │ │ │ ├── index.js
│ │ │ │ ├── DropdownItem.stories.jsx
│ │ │ │ ├── DropdownItem.jsx
│ │ │ │ └── DropdownItem.styled.js
│ │ │ └── Dropdown.stories.jsx
│ │ ├── FeedItem
│ │ │ └── index.js
│ │ ├── Progress
│ │ │ ├── index.js
│ │ │ ├── ProgressItem
│ │ │ │ └── index.js
│ │ │ ├── Progress.styled.js
│ │ │ └── Progress.stories.jsx
│ │ ├── Search
│ │ │ ├── index.js
│ │ │ ├── Search.stories.jsx
│ │ │ └── Search.jsx
│ │ ├── Sidebar
│ │ │ ├── index.js
│ │ │ ├── SideBar.styled.js
│ │ │ └── SideBar.stories.jsx
│ │ ├── AutoTyping
│ │ │ ├── index.js
│ │ │ └── AutoTyping.stories.jsx
│ │ ├── CardSmall
│ │ │ ├── index.js
│ │ │ └── CardSmall.stories.jsx
│ │ ├── FeedDetail
│ │ │ └── index.js
│ │ ├── SubNavbar
│ │ │ ├── index.js
│ │ │ ├── SubNavbarItem
│ │ │ │ ├── index.js
│ │ │ │ ├── SubNavbarItem.jsx
│ │ │ │ └── SubNavbarItem.stories.jsx
│ │ │ ├── SubNavbar.stories.jsx
│ │ │ └── SubNavbar.styled.js
│ │ ├── BlinkCursor
│ │ │ ├── index.js
│ │ │ └── BlinkCursor.stories.jsx
│ │ ├── CommentInput
│ │ │ ├── index.js
│ │ │ └── CommentInput.stories.jsx
│ │ ├── FeedItemSmall
│ │ │ ├── index.js
│ │ │ └── FeedItemSmall.stories.jsx
│ │ ├── ImageUploader
│ │ │ ├── index.js
│ │ │ ├── ImageUploader.stories.jsx
│ │ │ └── ImageUploader.styled.js
│ │ ├── Notification
│ │ │ ├── index.js
│ │ │ ├── NotificationItem
│ │ │ │ ├── index.js
│ │ │ │ └── NotificationItem.stories.jsx
│ │ │ └── Notification.stories.jsx
│ │ ├── AvatarUploader
│ │ │ ├── index.js
│ │ │ ├── AvatarUploader.stories.jsx
│ │ │ └── AvatarUploader.styled.js
│ │ ├── CardResponsive
│ │ │ └── index.js
│ │ ├── CardSmallCreate
│ │ │ ├── index.js
│ │ │ ├── CardSmallCreate.stories.jsx
│ │ │ └── CardSmallCreate.jsx
│ │ └── ScrollTopButton
│ │ │ ├── index.js
│ │ │ ├── ScrollTopButton.stories.jsx
│ │ │ ├── ScrollTopButton.jsx
│ │ │ └── ScrollTopButton.styled.js
│ ├── commons
│ │ ├── Avatar
│ │ │ ├── index.js
│ │ │ └── AvatarBase.jsx
│ │ ├── Badge
│ │ │ ├── index.js
│ │ │ └── Badge.jsx
│ │ ├── Button
│ │ │ ├── index.js
│ │ │ └── Button.jsx
│ │ ├── Input
│ │ │ └── index.js
│ │ ├── Radio
│ │ │ ├── index.js
│ │ │ ├── Radio.jsx
│ │ │ └── Radio.styled.js
│ │ ├── Switch
│ │ │ ├── index.js
│ │ │ └── Switch.stories.js
│ │ ├── BookMark
│ │ │ ├── index.js
│ │ │ ├── BookMark.stories.jsx
│ │ │ └── BookMark.styled.js
│ │ ├── Checkbox
│ │ │ ├── index.js
│ │ │ ├── Checkbox.stories.jsx
│ │ │ └── Checkbox.jsx
│ │ ├── ImageBox
│ │ │ ├── index.js
│ │ │ └── ImageBox.stories.jsx
│ │ ├── Selector
│ │ │ └── index.js
│ │ ├── InputImage
│ │ │ ├── index.js
│ │ │ └── InputImage.stories.jsx
│ │ ├── SideBarItem
│ │ │ └── index.js
│ │ ├── ImageBoxResponsive
│ │ │ ├── index.js
│ │ │ └── ImageBoxResponsive.stories.jsx
│ │ └── index.js
│ ├── layouts
│ │ ├── Footer
│ │ │ ├── index.js
│ │ │ └── Footer.stories.jsx
│ │ ├── Navbar
│ │ │ ├── index.js
│ │ │ ├── NavDefault
│ │ │ │ ├── index.js
│ │ │ │ └── NavDefaultItem
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── NavDefaultItem.jsx
│ │ │ ├── NavMobile
│ │ │ │ ├── index.js
│ │ │ │ ├── NavMobileItem
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── NavMobileItem.jsx
│ │ │ │ │ └── NavVerticalItem.stories.jsx
│ │ │ │ └── NavMobile.stories.jsx
│ │ │ └── Navbar.jsx
│ │ ├── CardGrid
│ │ │ ├── index.js
│ │ │ ├── CardGrid.styled.js
│ │ │ ├── CardGrid.stories.jsx
│ │ │ └── CardGrid.jsx
│ │ ├── FeedGrid
│ │ │ ├── index.js
│ │ │ ├── FeedGrid.styled.js
│ │ │ └── FeedGrid.jsx
│ │ ├── CardSlider
│ │ │ ├── index.js
│ │ │ └── CardSlider.stories.jsx
│ │ ├── CommentList
│ │ │ ├── index.js
│ │ │ ├── CommentList.styled.js
│ │ │ └── CommentList.jsx
│ │ ├── IntroSlider
│ │ │ └── index.js
│ │ ├── SignUpModal
│ │ │ ├── index.js
│ │ │ └── SignUpModal.stories.jsx
│ │ ├── CardSmallGrid
│ │ │ ├── index.js
│ │ │ ├── CardSmallGrid.styled.js
│ │ │ └── CardSmallGrid.jsx
│ │ ├── FeedSmallGrid
│ │ │ ├── index.js
│ │ │ ├── FeedSmallGrid.styled.js
│ │ │ └── FeedSmallGrid.jsx
│ │ ├── CardSmallSlider
│ │ │ ├── index.js
│ │ │ └── CardSmallSlider.styled.js
│ │ ├── FeedSmallSlider
│ │ │ └── index.js
│ │ └── index.js
│ └── index.js
├── _shared
│ ├── colors
│ │ ├── index.js
│ │ └── colors.js
│ ├── avatars
│ │ └── index.js
│ ├── shadows
│ │ ├── index.js
│ │ └── shadows.js
│ ├── icons
│ │ └── index.js
│ ├── animations
│ │ ├── index.js
│ │ ├── loadings.js
│ │ └── animations.js
│ ├── typography
│ │ ├── fontWeight.js
│ │ ├── fontFamily.js
│ │ ├── index.js
│ │ ├── fontSize.js
│ │ └── lineHeight.js
│ └── index.js
├── containers
│ ├── Common
│ │ ├── Footer
│ │ │ └── index.js
│ │ ├── Navbar
│ │ │ ├── index.js
│ │ │ ├── NavMobile
│ │ │ │ ├── index.js
│ │ │ │ └── NavMobileItem
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── NavMobileItem.jsx
│ │ │ │ │ └── NavVerticalItem.stories.jsx
│ │ │ └── NavDefault
│ │ │ │ ├── index.js
│ │ │ │ └── NavDefaultItem
│ │ │ │ ├── index.js
│ │ │ │ └── NavDefaultItem.jsx
│ │ ├── AuthModal
│ │ │ └── index.js
│ │ ├── DeleteModal
│ │ │ └── index.js
│ │ ├── ScrollToTop
│ │ │ ├── index.js
│ │ │ └── ScrollToTop.jsx
│ │ ├── SignUpModal
│ │ │ └── index.js
│ │ ├── SmallModal
│ │ │ └── index.js
│ │ ├── SubmitModal
│ │ │ └── index.js
│ │ ├── ErrorBoundary
│ │ │ ├── index.js
│ │ │ └── ErrorBoundary.jsx
│ │ ├── Notification
│ │ │ ├── index.js
│ │ │ └── NotificationItem
│ │ │ │ └── index.js
│ │ └── index.js
│ ├── MyPage
│ │ ├── MyFeed
│ │ │ ├── index.js
│ │ │ └── MyFeed.styled.js
│ │ ├── MyInfo
│ │ │ └── index.js
│ │ ├── AuthForm
│ │ │ └── index.js
│ │ ├── AuthWait
│ │ │ └── index.js
│ │ ├── MyComment
│ │ │ ├── index.js
│ │ │ └── MyComment.styled.js
│ │ ├── MyPages
│ │ │ ├── index.js
│ │ │ └── MyPages.styled.js
│ │ ├── MyStudy
│ │ │ ├── index.js
│ │ │ └── MyStudy.styled.js
│ │ ├── MyPageIntro
│ │ │ ├── index.js
│ │ │ └── MyPageIntro.jsx
│ │ ├── AuthComplete
│ │ │ ├── index.js
│ │ │ └── AuthComplete.jsx
│ │ ├── ProfileImage
│ │ │ ├── index.js
│ │ │ └── ProfileImage.styled.js
│ │ ├── DeleteAccountModal
│ │ │ └── index.js
│ │ ├── DeleteAccountCompleteModal
│ │ │ └── index.js
│ │ └── index.js
│ ├── commons
│ │ ├── Footer
│ │ │ └── index.js
│ │ ├── Navbar
│ │ │ ├── index.js
│ │ │ ├── NavDefault
│ │ │ │ ├── index.js
│ │ │ │ └── NavDefaultItem
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── NavDefaultItem.jsx
│ │ │ └── NavMobile
│ │ │ │ ├── index.js
│ │ │ │ └── NavMobileItem
│ │ │ │ ├── index.js
│ │ │ │ └── NavMobileItem.jsx
│ │ ├── AuthModal
│ │ │ └── index.js
│ │ ├── DeleteModal
│ │ │ └── index.js
│ │ ├── ScrollToTop
│ │ │ ├── index.js
│ │ │ └── ScrollToTop.jsx
│ │ ├── SignUpModal
│ │ │ └── index.js
│ │ ├── SmallModal
│ │ │ └── index.js
│ │ ├── SubmitModal
│ │ │ └── index.js
│ │ ├── Notification
│ │ │ ├── index.js
│ │ │ └── NotificationItem
│ │ │ │ └── index.js
│ │ └── index.js
│ ├── JobPage
│ │ ├── JobIntro
│ │ │ ├── index.js
│ │ │ ├── JobIntro.jsx
│ │ │ └── JobIntro.styled.js
│ │ ├── JobMain
│ │ │ ├── index.js
│ │ │ └── JobMain.styled.js
│ │ ├── JobCardGrid
│ │ │ ├── index.js
│ │ │ └── JobCardGrid.styled.js
│ │ ├── JobDetail
│ │ │ └── index.js
│ │ ├── JobSideBar
│ │ │ ├── index.js
│ │ │ └── JobSideBar.styled.js
│ │ ├── JobMainMobile
│ │ │ ├── index.js
│ │ │ └── JobMainMobile.styled.js
│ │ ├── JobNoContent
│ │ │ ├── index.js
│ │ │ ├── JobNoContent.jsx
│ │ │ └── JobNoContent.styled.js
│ │ ├── JobDetailComment
│ │ │ ├── index.js
│ │ │ └── JobDetailComment.styled.js
│ │ ├── JobDetailMobile
│ │ │ └── index.js
│ │ └── index.js
│ ├── StudyPage
│ │ ├── StudyIntro
│ │ │ ├── index.js
│ │ │ └── StudyIntro.jsx
│ │ ├── StudyMain
│ │ │ ├── index.js
│ │ │ └── StudyMain.styled.js
│ │ ├── StudyDetail
│ │ │ └── index.js
│ │ ├── StudySideBar
│ │ │ ├── index.js
│ │ │ └── StudySideBar.styled.js
│ │ ├── StudyCardGrid
│ │ │ └── index.js
│ │ ├── StudyCreateForm
│ │ │ ├── index.js
│ │ │ └── StudyCreateForm.styled.js
│ │ ├── StudyMainMobile
│ │ │ ├── index.js
│ │ │ └── StudyMainMobile.styled.js
│ │ ├── StudyNoContent
│ │ │ ├── index.js
│ │ │ └── StudyNoContent.jsx
│ │ ├── StudyUpdateForm
│ │ │ ├── index.js
│ │ │ └── StudyUpdateForm.styled.js
│ │ ├── StudyCreateDetail
│ │ │ └── index.js
│ │ ├── StudyCreateEditor
│ │ │ └── index.js
│ │ ├── StudyCreateIntro
│ │ │ ├── index.js
│ │ │ ├── StudyCreateIntro.jsx
│ │ │ └── StudyCreateIntro.styled.js
│ │ ├── StudyDetailMobile
│ │ │ └── index.js
│ │ ├── ThumbnailUploader
│ │ │ ├── index.js
│ │ │ └── ThumbnailUploader.styled.js
│ │ ├── StudyCreateSummary
│ │ │ └── index.js
│ │ ├── StudyDetailComment
│ │ │ ├── index.js
│ │ │ └── StudyDetailComment.styled.js
│ │ └── index.js
│ ├── LandingPage
│ │ ├── LandingHero
│ │ │ └── index.js
│ │ ├── LandingCTASection
│ │ │ ├── index.js
│ │ │ └── LandingCTASection.jsx
│ │ ├── LandingDetailSection
│ │ │ ├── index.js
│ │ │ └── LandingDetailSection.jsx
│ │ └── index.js
│ ├── Loading
│ │ ├── ErrorBoundary
│ │ │ ├── index.js
│ │ │ └── ErrorBoundary.jsx
│ │ ├── LoadingDetail
│ │ │ └── index.js
│ │ ├── LoadingMyPages
│ │ │ └── index.js
│ │ ├── LoadingDetailMobile
│ │ │ └── index.js
│ │ ├── LoadingCommunityDetail
│ │ │ ├── index.js
│ │ │ └── LoadingCommunityDetail.jsx
│ │ └── index.js
│ ├── MainPage
│ │ ├── MainJobSlider
│ │ │ ├── index.js
│ │ │ └── MainJobSlider.jsx
│ │ ├── MainIntroSlider
│ │ │ ├── index.js
│ │ │ └── MainIntroSlider.jsx
│ │ ├── MainStudySilder
│ │ │ ├── index.js
│ │ │ └── MainStudySilder.jsx
│ │ ├── MainCommunitySlider
│ │ │ ├── index.js
│ │ │ └── MainCommunitySlider.jsx
│ │ └── index.js
│ └── CommunityPage
│ │ ├── CommunityCreate
│ │ └── index.js
│ │ ├── CommunityDetail
│ │ └── index.js
│ │ ├── CommunityEditor
│ │ └── index.js
│ │ ├── CommunityIntro
│ │ ├── index.js
│ │ ├── CommunityIntro.jsx
│ │ └── CommunityIntro.styled.js
│ │ ├── CommunitySlider
│ │ ├── index.js
│ │ └── CommunitySlider.jsx
│ │ ├── CommunityUpdate
│ │ └── index.js
│ │ ├── CommunityFeedGrid
│ │ ├── index.js
│ │ └── CommunityFeedGrid.styled.js
│ │ ├── CommunityNoContent
│ │ ├── index.js
│ │ ├── CommunityNoContent.jsx
│ │ └── CommunityNoContent.styled.js
│ │ ├── CommunityCreateButton
│ │ ├── index.js
│ │ └── CommunityCreateButton.jsx
│ │ ├── CommunityDetailComment
│ │ ├── index.js
│ │ └── CommunityDetailComment.styled.js
│ │ └── index.js
├── pages
│ ├── MyPage
│ │ ├── index.js
│ │ └── MyPage.jsx
│ ├── MainPage
│ │ └── index.js
│ ├── LandingPage
│ │ └── index.js
│ ├── CommunityPage
│ │ ├── index.js
│ │ └── CommunityDetailPage.jsx
│ ├── JobPage
│ │ ├── JobCreatePage.jsx
│ │ └── index.js
│ ├── StudyPage
│ │ ├── index.js
│ │ ├── StudyCreatePage.jsx
│ │ └── StudyUpdatePage.jsx
│ └── CommonPage
│ │ ├── index.js
│ │ └── EmptyPage.jsx
├── router
│ ├── index.js
│ └── PrivateRoute.jsx
├── hooks
│ ├── index.js
│ ├── useGaTracker.jsx
│ └── useIntersectionObserver.jsx
├── recoil
│ ├── index.js
│ ├── community.js
│ ├── study.js
│ ├── job.js
│ ├── theme.js
│ ├── menu.js
│ └── modal.js
├── utils
│ ├── numToMillion.js
│ ├── debounce.js
│ ├── throttle.js
│ ├── parseHtml.js
│ ├── localStorageEffect.js
│ ├── getEndDate.js
│ ├── index.js
│ ├── daysLeftFromToday.js
│ └── daysFromToday.js
├── api
│ ├── deleteUser.js
│ ├── postStudy.js
│ ├── putJobScrap.js
│ ├── postCommunity.js
│ ├── putCommunityLike.js
│ ├── putStudyScrap.js
│ ├── deleteStudy.js
│ ├── putAuthorization.js
│ ├── putNickname.js
│ ├── deleteProfileImage.js
│ ├── putStudy.js
│ ├── putStudyRecruits.js
│ ├── putProfileImage.js
│ ├── postNicknameCheck.js
│ ├── putCommunity.js
│ ├── getDummyApi.js
│ ├── getCompanyList.js
│ ├── deleteCommunity.js
│ ├── postComment.js
│ ├── postScrap.js
│ ├── deleteNotification.js
│ ├── deleteComments.js
│ ├── getNotifications.js
│ ├── getRefreshToken.js
│ ├── getUserProfile.js
│ ├── getCommunityDetail.js
│ └── getJobDetail.js
├── setupTests.js
├── App.test.js
├── reportWebVitals.js
├── index.js
└── index.css
├── public
├── _redirects
├── robots.txt
├── favicon.ico
├── images
│ ├── job-intro.webp
│ ├── op-image.png
│ ├── mockup-auth.webp
│ ├── mockup-job.webp
│ ├── study-intro.webp
│ ├── mockup-study.webp
│ ├── apple-touch-icon.png
│ ├── mockup-community.webp
│ ├── Icon.svg
│ └── admin.svg
└── index.html
├── jsconfig.json
├── .eslintrc.json
├── .github
├── PULL_REQUEST_TEMPLATE.md
├── auto_assign.yml
└── workflows
│ └── chromatic.yml
├── nginx.conf
├── .storybook
├── webpack.config.js
├── main.js
└── preview.js
├── Dockerfile
├── .gitignore
└── craco.config.js
/src/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/_redirects:
--------------------------------------------------------------------------------
1 | /* /index.html 200
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Allow: /
--------------------------------------------------------------------------------
/src/foundations/Icon/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Icon";
2 |
--------------------------------------------------------------------------------
/src/foundations/Logo/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Logo";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/Toc/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Toc";
2 |
--------------------------------------------------------------------------------
/src/_shared/colors/index.js:
--------------------------------------------------------------------------------
1 | export { default as colors } from "./colors";
2 |
--------------------------------------------------------------------------------
/src/components/commons/Avatar/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Avatar";
2 |
--------------------------------------------------------------------------------
/src/components/commons/Badge/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Badge";
2 |
--------------------------------------------------------------------------------
/src/components/commons/Button/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Button";
2 |
--------------------------------------------------------------------------------
/src/components/commons/Input/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Input";
2 |
--------------------------------------------------------------------------------
/src/components/commons/Radio/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Radio";
2 |
--------------------------------------------------------------------------------
/src/components/commons/Switch/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Switch";
2 |
--------------------------------------------------------------------------------
/src/components/layouts/Footer/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Footer";
2 |
--------------------------------------------------------------------------------
/src/components/layouts/Navbar/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Navbar";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/Card/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Card";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/Modal/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Modal";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/Sort/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Sort";
2 |
--------------------------------------------------------------------------------
/src/containers/Common/Footer/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Footer";
2 |
--------------------------------------------------------------------------------
/src/containers/Common/Navbar/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Navbar";
2 |
--------------------------------------------------------------------------------
/src/containers/MyPage/MyFeed/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./MyFeed";
2 |
--------------------------------------------------------------------------------
/src/containers/MyPage/MyInfo/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./MyInfo";
2 |
--------------------------------------------------------------------------------
/src/containers/commons/Footer/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Footer";
2 |
--------------------------------------------------------------------------------
/src/containers/commons/Navbar/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Navbar";
2 |
--------------------------------------------------------------------------------
/src/pages/MyPage/index.js:
--------------------------------------------------------------------------------
1 | export { default as MyPage } from "./MyPage";
2 |
--------------------------------------------------------------------------------
/src/_shared/avatars/index.js:
--------------------------------------------------------------------------------
1 | export { default as avatars } from "./avatars";
2 |
--------------------------------------------------------------------------------
/src/_shared/shadows/index.js:
--------------------------------------------------------------------------------
1 | export { default as shadows } from "./shadows";
2 |
--------------------------------------------------------------------------------
/src/components/commons/BookMark/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./BookMark";
2 |
--------------------------------------------------------------------------------
/src/components/commons/Checkbox/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Checkbox";
2 |
--------------------------------------------------------------------------------
/src/components/commons/ImageBox/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./ImageBox";
2 |
--------------------------------------------------------------------------------
/src/components/commons/Selector/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Selector";
2 |
--------------------------------------------------------------------------------
/src/components/layouts/CardGrid/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./CardGrid";
2 |
--------------------------------------------------------------------------------
/src/components/layouts/FeedGrid/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./FeedGrid";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/Comment/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Comment";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/Dropdown/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Dropdown";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/FeedItem/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./FeedItem";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/Progress/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Progress";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/Search/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Search";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/Sidebar/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./SideBar";
2 |
--------------------------------------------------------------------------------
/src/containers/Common/AuthModal/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./AuthModal";
2 |
--------------------------------------------------------------------------------
/src/containers/JobPage/JobIntro/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./JobIntro";
2 |
--------------------------------------------------------------------------------
/src/containers/JobPage/JobMain/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./JobMain";
2 |
--------------------------------------------------------------------------------
/src/containers/MyPage/AuthForm/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./AuthForm";
2 |
--------------------------------------------------------------------------------
/src/containers/MyPage/AuthWait/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./AuthWait";
2 |
--------------------------------------------------------------------------------
/src/containers/MyPage/MyComment/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./MyComment";
2 |
--------------------------------------------------------------------------------
/src/containers/MyPage/MyPages/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./MyPages";
2 |
--------------------------------------------------------------------------------
/src/containers/MyPage/MyStudy/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./MyStudy";
2 |
--------------------------------------------------------------------------------
/src/foundations/Background/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Background";
2 |
--------------------------------------------------------------------------------
/src/foundations/IconSocial/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./IconSocial";
2 |
--------------------------------------------------------------------------------
/src/foundations/Typography/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Typography";
2 |
--------------------------------------------------------------------------------
/src/pages/MainPage/index.js:
--------------------------------------------------------------------------------
1 | export { default as MainPage } from "./MainPage";
2 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moramoram/client/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/src/components/commons/InputImage/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./InputImage";
2 |
--------------------------------------------------------------------------------
/src/components/commons/SideBarItem/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./SideBarItem";
2 |
--------------------------------------------------------------------------------
/src/components/layouts/CardSlider/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./CardSlider";
2 |
--------------------------------------------------------------------------------
/src/components/layouts/CommentList/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./CommentList";
2 |
--------------------------------------------------------------------------------
/src/components/layouts/IntroSlider/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./IntroSlider";
2 |
--------------------------------------------------------------------------------
/src/components/layouts/SignUpModal/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./SignUpModal";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/AutoTyping/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./AutoTyping";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/CardSmall/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./CardSmall";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/FeedDetail/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./FeedDetail";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/SubNavbar/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./SubNavbar";
2 |
--------------------------------------------------------------------------------
/src/containers/Common/DeleteModal/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./DeleteModal";
2 |
--------------------------------------------------------------------------------
/src/containers/Common/ScrollToTop/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./ScrollToTop";
2 |
--------------------------------------------------------------------------------
/src/containers/Common/SignUpModal/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./SignUpModal";
2 |
--------------------------------------------------------------------------------
/src/containers/Common/SmallModal/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./SmallModal";
2 |
--------------------------------------------------------------------------------
/src/containers/Common/SubmitModal/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./SubmitModal";
2 |
--------------------------------------------------------------------------------
/src/containers/JobPage/JobCardGrid/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./JobCardGrid";
2 |
--------------------------------------------------------------------------------
/src/containers/JobPage/JobDetail/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./JobDetail";
2 |
--------------------------------------------------------------------------------
/src/containers/JobPage/JobSideBar/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./JobSideBar";
2 |
--------------------------------------------------------------------------------
/src/containers/MyPage/MyPageIntro/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./MyPageIntro";
2 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyIntro/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./StudyIntro";
2 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyMain/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./StudyMain";
2 |
--------------------------------------------------------------------------------
/src/containers/commons/AuthModal/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./AuthModal";
2 |
--------------------------------------------------------------------------------
/src/containers/commons/DeleteModal/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./DeleteModal";
2 |
--------------------------------------------------------------------------------
/src/containers/commons/ScrollToTop/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./ScrollToTop";
2 |
--------------------------------------------------------------------------------
/src/containers/commons/SignUpModal/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./SignUpModal";
2 |
--------------------------------------------------------------------------------
/src/containers/commons/SmallModal/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./SmallModal";
2 |
--------------------------------------------------------------------------------
/src/containers/commons/SubmitModal/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./SubmitModal";
2 |
--------------------------------------------------------------------------------
/src/components/layouts/CardSmallGrid/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./CardSmallGrid";
2 |
--------------------------------------------------------------------------------
/src/components/layouts/FeedSmallGrid/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./FeedSmallGrid";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/BlinkCursor/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./BlinkCursor";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/CommentInput/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./CommentInput";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/FeedItemSmall/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./FeedItemSmall";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/ImageUploader/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./ImageUploader";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/Notification/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Notification";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/Toc/TocNav/index.js:
--------------------------------------------------------------------------------
1 | export { default as TocNav } from "./TocNav";
2 |
--------------------------------------------------------------------------------
/src/containers/Common/ErrorBoundary/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./ErrorBoundary";
2 |
--------------------------------------------------------------------------------
/src/containers/Common/Notification/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Notification";
2 |
--------------------------------------------------------------------------------
/src/containers/JobPage/JobMainMobile/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./JobMainMobile";
2 |
--------------------------------------------------------------------------------
/src/containers/JobPage/JobNoContent/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./JobNoContent";
2 |
--------------------------------------------------------------------------------
/src/containers/LandingPage/LandingHero/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./LandingHero";
2 |
--------------------------------------------------------------------------------
/src/containers/Loading/ErrorBoundary/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./ErrorBoundary";
2 |
--------------------------------------------------------------------------------
/src/containers/Loading/LoadingDetail/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./LoadingDetail";
2 |
--------------------------------------------------------------------------------
/src/containers/MainPage/MainJobSlider/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./MainJobSlider";
2 |
--------------------------------------------------------------------------------
/src/containers/MyPage/AuthComplete/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./AuthComplete";
2 |
--------------------------------------------------------------------------------
/src/containers/MyPage/ProfileImage/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./ProfileImage";
2 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyDetail/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./StudyDetail";
2 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudySideBar/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./StudySideBar";
2 |
--------------------------------------------------------------------------------
/src/containers/commons/Notification/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./Notification";
2 |
--------------------------------------------------------------------------------
/src/pages/LandingPage/index.js:
--------------------------------------------------------------------------------
1 | export { default as LandingPage } from "./LandingPage";
2 |
--------------------------------------------------------------------------------
/src/components/layouts/CardSmallSlider/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./CardSmallSlider";
2 |
--------------------------------------------------------------------------------
/src/components/layouts/FeedSmallSlider/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./FeedSmallSlider";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/AvatarUploader/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./AvatarUploader";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/CardResponsive/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./CardResponsive";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/CardSmallCreate/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./CardSmallCreate";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/ScrollTopButton/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./ScrollTopButton";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/Toc/TocItem/index.js:
--------------------------------------------------------------------------------
1 | export { default as TocItem } from "./TocItem";
2 |
--------------------------------------------------------------------------------
/src/containers/JobPage/JobDetailComment/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./JobDetailComment";
2 |
--------------------------------------------------------------------------------
/src/containers/JobPage/JobDetailMobile/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./JobDetailMobile";
2 |
--------------------------------------------------------------------------------
/src/containers/Loading/LoadingMyPages/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./LoadingMyPages";
2 |
--------------------------------------------------------------------------------
/src/containers/MainPage/MainIntroSlider/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./MainIntroSlider";
2 |
--------------------------------------------------------------------------------
/src/containers/MainPage/MainStudySilder/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./MainStudySilder";
2 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyCardGrid/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./StudyCardGrid";
2 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyCreateForm/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./StudyCreateForm";
2 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyMainMobile/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./StudyMainMobile";
2 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyNoContent/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./StudyNoContent";
2 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyUpdateForm/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./StudyUpdateForm";
2 |
--------------------------------------------------------------------------------
/src/components/commons/ImageBoxResponsive/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./ImageBoxResponsive";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/Dropdown/DropdownSmall/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./DropdownSmall";
2 |
--------------------------------------------------------------------------------
/src/containers/Common/Navbar/NavMobile/index.js:
--------------------------------------------------------------------------------
1 | export { default as NavMobile } from "./NavMobile";
2 |
--------------------------------------------------------------------------------
/src/containers/CommunityPage/CommunityCreate/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./CommunityCreate";
2 |
--------------------------------------------------------------------------------
/src/containers/CommunityPage/CommunityDetail/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./CommunityDetail";
2 |
--------------------------------------------------------------------------------
/src/containers/CommunityPage/CommunityEditor/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./CommunityEditor";
2 |
--------------------------------------------------------------------------------
/src/containers/CommunityPage/CommunityIntro/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./CommunityIntro";
2 |
--------------------------------------------------------------------------------
/src/containers/CommunityPage/CommunitySlider/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./CommunitySlider";
2 |
--------------------------------------------------------------------------------
/src/containers/CommunityPage/CommunityUpdate/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./CommunityUpdate";
2 |
--------------------------------------------------------------------------------
/src/containers/MyPage/DeleteAccountModal/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./DeleteAccountModal";
2 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyCreateDetail/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./StudyCreateDetail";
2 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyCreateEditor/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./StudyCreateEditor";
2 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyCreateIntro/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./StudyCreateIntro";
2 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyDetailMobile/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./StudyDetailMobile";
2 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/ThumbnailUploader/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./ThumbnailUploader";
2 |
--------------------------------------------------------------------------------
/public/images/job-intro.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moramoram/client/HEAD/public/images/job-intro.webp
--------------------------------------------------------------------------------
/public/images/op-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moramoram/client/HEAD/public/images/op-image.png
--------------------------------------------------------------------------------
/src/components/layouts/Navbar/NavDefault/index.js:
--------------------------------------------------------------------------------
1 | export { default as NavDefault } from "./NavDefault";
2 |
--------------------------------------------------------------------------------
/src/components/layouts/Navbar/NavMobile/index.js:
--------------------------------------------------------------------------------
1 | export { default as NavMobile } from "./NavMobile";
2 |
--------------------------------------------------------------------------------
/src/containers/Common/Navbar/NavDefault/index.js:
--------------------------------------------------------------------------------
1 | export { default as NavDefault } from "./NavDefault";
2 |
--------------------------------------------------------------------------------
/src/containers/CommunityPage/CommunityFeedGrid/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./CommunityFeedGrid";
2 |
--------------------------------------------------------------------------------
/src/containers/CommunityPage/CommunityNoContent/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./CommunityNoContent";
2 |
--------------------------------------------------------------------------------
/src/containers/LandingPage/LandingCTASection/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./LandingCTASection";
2 |
--------------------------------------------------------------------------------
/src/containers/Loading/LoadingDetailMobile/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./LoadingDetailMobile";
2 |
--------------------------------------------------------------------------------
/src/containers/MainPage/MainCommunitySlider/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./MainCommunitySlider";
2 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyCreateSummary/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./StudyCreateSummary";
2 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyDetailComment/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./StudyDetailComment";
2 |
--------------------------------------------------------------------------------
/src/containers/commons/Navbar/NavDefault/index.js:
--------------------------------------------------------------------------------
1 | export { default as NavDefault } from "./NavDefault";
2 |
--------------------------------------------------------------------------------
/src/containers/commons/Navbar/NavMobile/index.js:
--------------------------------------------------------------------------------
1 | export { default as NavMobile } from "./NavMobile";
2 |
--------------------------------------------------------------------------------
/public/images/mockup-auth.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moramoram/client/HEAD/public/images/mockup-auth.webp
--------------------------------------------------------------------------------
/public/images/mockup-job.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moramoram/client/HEAD/public/images/mockup-job.webp
--------------------------------------------------------------------------------
/public/images/study-intro.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moramoram/client/HEAD/public/images/study-intro.webp
--------------------------------------------------------------------------------
/src/containers/LandingPage/LandingDetailSection/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./LandingDetailSection";
2 |
--------------------------------------------------------------------------------
/src/containers/Loading/LoadingCommunityDetail/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./LoadingCommunityDetail";
2 |
--------------------------------------------------------------------------------
/public/images/mockup-study.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moramoram/client/HEAD/public/images/mockup-study.webp
--------------------------------------------------------------------------------
/src/components/index.js:
--------------------------------------------------------------------------------
1 | export * from "./commons";
2 | export * from "./patterns";
3 | export * from "./layouts";
4 |
--------------------------------------------------------------------------------
/src/components/patterns/Dropdown/DropdownItem/index.js:
--------------------------------------------------------------------------------
1 | export { default as DropdownItem } from "./DropdownItem";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/Progress/ProgressItem/index.js:
--------------------------------------------------------------------------------
1 | export { default as ProgressItem } from "./ProgressItem";
2 |
--------------------------------------------------------------------------------
/src/containers/CommunityPage/CommunityCreateButton/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./CommunityCreateButton";
2 |
--------------------------------------------------------------------------------
/src/containers/CommunityPage/CommunityDetailComment/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./CommunityDetailComment";
2 |
--------------------------------------------------------------------------------
/src/containers/MyPage/DeleteAccountCompleteModal/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./DeleteAccountCompleteModal";
2 |
--------------------------------------------------------------------------------
/public/images/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moramoram/client/HEAD/public/images/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/images/mockup-community.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moramoram/client/HEAD/public/images/mockup-community.webp
--------------------------------------------------------------------------------
/src/assets/PretendardVariable.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moramoram/client/HEAD/src/assets/PretendardVariable.woff2
--------------------------------------------------------------------------------
/src/components/patterns/SubNavbar/SubNavbarItem/index.js:
--------------------------------------------------------------------------------
1 | export { default as SubNavbarItem } from "./SubNavbarItem";
2 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "paths": { "@/*": ["./src/*"] }
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/components/layouts/Navbar/NavMobile/NavMobileItem/index.js:
--------------------------------------------------------------------------------
1 | export { default as NavMobileItem } from "./NavMobileItem";
2 |
--------------------------------------------------------------------------------
/src/containers/Common/Navbar/NavMobile/NavMobileItem/index.js:
--------------------------------------------------------------------------------
1 | export { default as NavMobileItem } from "./NavMobileItem";
2 |
--------------------------------------------------------------------------------
/src/containers/commons/Navbar/NavMobile/NavMobileItem/index.js:
--------------------------------------------------------------------------------
1 | export { default as NavMobileItem } from "./NavMobileItem";
2 |
--------------------------------------------------------------------------------
/src/_shared/icons/index.js:
--------------------------------------------------------------------------------
1 | export { default as icons } from "./basic";
2 | export { default as iconsSocial } from "./social";
3 |
--------------------------------------------------------------------------------
/src/components/layouts/Navbar/NavDefault/NavDefaultItem/index.js:
--------------------------------------------------------------------------------
1 | export { default as NavDefaultItem } from "./NavDefaultItem";
2 |
--------------------------------------------------------------------------------
/src/components/patterns/Notification/NotificationItem/index.js:
--------------------------------------------------------------------------------
1 | export { default as NotificationItem } from "./NotificationItem";
2 |
--------------------------------------------------------------------------------
/src/containers/Common/Navbar/NavDefault/NavDefaultItem/index.js:
--------------------------------------------------------------------------------
1 | export { default as NavDefaultItem } from "./NavDefaultItem";
2 |
--------------------------------------------------------------------------------
/src/containers/Common/Notification/NotificationItem/index.js:
--------------------------------------------------------------------------------
1 | export { default as NotificationItem } from "./NotificationItem";
2 |
--------------------------------------------------------------------------------
/src/containers/commons/Navbar/NavDefault/NavDefaultItem/index.js:
--------------------------------------------------------------------------------
1 | export { default as NavDefaultItem } from "./NavDefaultItem";
2 |
--------------------------------------------------------------------------------
/src/containers/commons/Notification/NotificationItem/index.js:
--------------------------------------------------------------------------------
1 | export { default as NotificationItem } from "./NotificationItem";
2 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | export { default as Router } from "./Router";
2 | export { default as PrivateRoute } from "./PrivateRoute";
3 |
--------------------------------------------------------------------------------
/src/_shared/animations/index.js:
--------------------------------------------------------------------------------
1 | export { default as animations } from "./animations";
2 | export { default as loadings } from "./loadings";
3 |
--------------------------------------------------------------------------------
/src/containers/JobPage/JobCardGrid/JobCardGrid.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 |
3 | export const FetchBox = styled.div`
4 | height: 30px;
5 | `;
6 |
--------------------------------------------------------------------------------
/src/pages/CommunityPage/index.js:
--------------------------------------------------------------------------------
1 | export { default as CommunityDetailPage } from "./CommunityDetailPage";
2 | export { default as CommunityPage } from "./CommunityPage";
3 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["react-app", "plugin:prettier/recommended"],
3 | "rules": {
4 | "prettier/prettier": ["error", { "endOfLine": "auto" }]
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## 💡 Issue
2 | - closed #
3 |
4 |
5 |
6 |
7 | ## 🔎 Overview
8 |
9 |
10 |
11 | ## 📷 Screenshot
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/_shared/shadows/shadows.js:
--------------------------------------------------------------------------------
1 | const shadows = {
2 | base: "0px 1px 3px rgba(0,0,0,0.1)",
3 | button: "0px 1px 1px rgba(0,0,0,0.05)",
4 | };
5 |
6 | export default shadows;
7 |
--------------------------------------------------------------------------------
/src/hooks/index.js:
--------------------------------------------------------------------------------
1 | export { default as useSlider } from "./useSlider";
2 | export { default as useGaTracker } from "./useGaTracker";
3 | export * from "./useIntersectionObserver";
4 |
--------------------------------------------------------------------------------
/nginx.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | location / {
4 | root /app/build;
5 | index index.html;
6 | try_files $uri $uri/ /index.html;
7 | }
8 | }
--------------------------------------------------------------------------------
/src/pages/JobPage/JobCreatePage.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const JobsCreatePage = () => {
4 | return <>JobsCreatePage>;
5 | };
6 |
7 | export default JobsCreatePage;
8 |
--------------------------------------------------------------------------------
/src/_shared/typography/fontWeight.js:
--------------------------------------------------------------------------------
1 | const fontWeight = {
2 | regular: "400",
3 | bold: "700",
4 | extrabold: "800",
5 | black: "900",
6 | };
7 |
8 | export default fontWeight;
9 |
--------------------------------------------------------------------------------
/src/containers/CommunityPage/CommunityFeedGrid/CommunityFeedGrid.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 |
3 | export const FetchBox = styled.div`
4 | height: 30px;
5 | `;
6 |
--------------------------------------------------------------------------------
/src/recoil/index.js:
--------------------------------------------------------------------------------
1 | export * from "./auth";
2 | export * from "./modal";
3 | export * from "./theme";
4 | export * from "./job";
5 | export * from "./community";
6 | export * from "./study";
7 |
--------------------------------------------------------------------------------
/src/components/patterns/Progress/Progress.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 |
3 | export const Layout = styled.div`
4 | display: flex;
5 | justify-content: space-between;
6 | `;
7 |
--------------------------------------------------------------------------------
/src/pages/JobPage/index.js:
--------------------------------------------------------------------------------
1 | export { default as JobDetailPage } from "./JobDetailPage";
2 | export { default as JobCreatePage } from "./JobCreatePage";
3 | export { default as JobPage } from "./JobPage";
4 |
--------------------------------------------------------------------------------
/src/_shared/typography/fontFamily.js:
--------------------------------------------------------------------------------
1 | const fontFamily = {
2 | sans: '"Pretendard Variable"',
3 | mono: '"SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace',
4 | };
5 |
6 | export default fontFamily;
7 |
--------------------------------------------------------------------------------
/src/_shared/index.js:
--------------------------------------------------------------------------------
1 | export * from "./animations";
2 | export * from "./avatars";
3 | export * from "./colors";
4 | export * from "./icons";
5 | export * from "./shadows";
6 | export * from "./icons";
7 | export * from "./typography";
8 |
--------------------------------------------------------------------------------
/src/foundations/IconSocial/IconSocial.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 |
3 | export const Svg = styled.svg`
4 | display: ${(props) => (props.block ? "block" : "inline-block")};
5 | vertical-align: middle;
6 | `;
7 |
--------------------------------------------------------------------------------
/src/containers/LandingPage/index.js:
--------------------------------------------------------------------------------
1 | export { default as LandingHero } from "./LandingHero";
2 | export { default as LandingCTASection } from "./LandingCTASection";
3 | export { default as LandingDetailSection } from "./LandingDetailSection";
4 |
--------------------------------------------------------------------------------
/src/foundations/Background/Background.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Layout } from "./Background.styled";
3 |
4 | const Background = ({ ...props }) => {
5 | return ;
6 | };
7 |
8 | export default Background;
9 |
--------------------------------------------------------------------------------
/src/utils/numToMillion.js:
--------------------------------------------------------------------------------
1 | const numToMillion = (num) => {
2 | if (num >= 1000000) return `${(num / 1000000).toFixed(1)}M`;
3 | if (num >= 1000) return `${(num / 1000).toFixed(1)}K`;
4 | return num;
5 | };
6 |
7 | export default numToMillion;
8 |
--------------------------------------------------------------------------------
/src/api/deleteUser.js:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from "@/utils";
2 |
3 | export const DeleteUser = async () => {
4 | const res = await axiosInstance({
5 | url: "/users",
6 | method: "delete",
7 | });
8 | return res.data;
9 | };
10 |
--------------------------------------------------------------------------------
/src/_shared/typography/index.js:
--------------------------------------------------------------------------------
1 | export { default as fontFamily } from "./fontFamily";
2 | export { default as fontSize } from "./fontSize";
3 | export { default as fontWeight } from "./fontWeight";
4 | export { default as lineHeight } from "./lineHeight";
5 |
--------------------------------------------------------------------------------
/.storybook/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | module.exports = ({ config }) => {
4 | config.resolve.alias = {
5 | ...config.resolve.alias,
6 | "@": path.resolve(__dirname, "../src"),
7 | };
8 |
9 | return config;
10 | };
11 |
--------------------------------------------------------------------------------
/src/api/postStudy.js:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from "@/utils";
2 |
3 | export const PostStudy = async (data) => {
4 | const res = await axiosInstance({
5 | url: "/studies",
6 | method: "post",
7 | data,
8 | });
9 | return res.data;
10 | };
11 |
--------------------------------------------------------------------------------
/src/api/putJobScrap.js:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from "@/utils";
2 |
3 | export const putJobScrap = async (id) => {
4 | const res = await axiosInstance({
5 | url: `/recruits/${id}/scraps`,
6 | method: "put",
7 | });
8 | return res.data;
9 | };
10 |
--------------------------------------------------------------------------------
/src/components/layouts/Footer/Footer.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Footer from ".";
3 |
4 | export default {
5 | title: "layouts/Footer",
6 | component: Footer,
7 | };
8 |
9 | export const Default = (args) => ;
10 |
--------------------------------------------------------------------------------
/src/api/postCommunity.js:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from "@/utils";
2 |
3 | export const postCommunity = async (data) => {
4 | const res = await axiosInstance({
5 | url: `/boards`,
6 | method: "post",
7 | data,
8 | });
9 | return res.data;
10 | };
11 |
--------------------------------------------------------------------------------
/src/api/putCommunityLike.js:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from "@/utils";
2 |
3 | export const putCommunityLike = async (id) => {
4 | const res = await axiosInstance({
5 | url: `/boards/${id}/likes`,
6 | method: "put",
7 | });
8 | return res.data;
9 | };
10 |
--------------------------------------------------------------------------------
/src/api/putStudyScrap.js:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from "@/utils";
2 |
3 | export const putStudyScrap = async (id) => {
4 | const res = await axiosInstance({
5 | url: `/studies/${id}/scraps`,
6 | method: "put",
7 | });
8 | return res.data;
9 | };
10 |
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import "@testing-library/jest-dom";
6 |
--------------------------------------------------------------------------------
/src/api/deleteStudy.js:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from "@/utils";
2 |
3 | export const DeleteStudy = async (studyId) => {
4 | const res = await axiosInstance({
5 | url: `/studies/${studyId}`,
6 | method: "delete",
7 | });
8 |
9 | return res.data;
10 | };
11 |
--------------------------------------------------------------------------------
/src/api/putAuthorization.js:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from "@/utils";
2 |
3 | export const PutAuthorization = async (data) => {
4 | const res = await axiosInstance({
5 | url: "/users",
6 | method: "put",
7 | data,
8 | });
9 | return res.data;
10 | };
11 |
--------------------------------------------------------------------------------
/src/api/putNickname.js:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from "@/utils";
2 |
3 | export const PutNickname = async (data) => {
4 | const res = await axiosInstance({
5 | url: "/users/nickname",
6 | method: "put",
7 | data,
8 | });
9 | return res.data;
10 | };
11 |
--------------------------------------------------------------------------------
/src/components/layouts/CommentList/CommentList.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 |
3 | export const Layout = styled.ul`
4 | padding: 2rem 0;
5 | `;
6 |
7 | export const CommentBox = styled.li`
8 | padding: 1rem 0;
9 | list-style: none;
10 | `;
11 |
--------------------------------------------------------------------------------
/src/api/deleteProfileImage.js:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from "@/utils";
2 |
3 | export const DeleteProfileImage = async () => {
4 | const res = await axiosInstance({
5 | url: "/users/profile-images",
6 | method: "delete",
7 | });
8 | return res.data;
9 | };
10 |
--------------------------------------------------------------------------------
/src/api/putStudy.js:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from "@/utils";
2 |
3 | export const PutStudy = async (studyId, data) => {
4 | const res = await axiosInstance({
5 | url: `/studies/${studyId}`,
6 | method: "put",
7 | data,
8 | });
9 | return res.data;
10 | };
11 |
--------------------------------------------------------------------------------
/src/api/putStudyRecruits.js:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from "@/utils";
2 |
3 | export const PutStudyRecruits = async (id) => {
4 | const res = await axiosInstance({
5 | url: `/studies/${id}/recruitments`,
6 | method: "put",
7 | });
8 | return res.data;
9 | };
10 |
--------------------------------------------------------------------------------
/src/pages/StudyPage/index.js:
--------------------------------------------------------------------------------
1 | export { default as StudyDetailPage } from "./StudyDetailPage";
2 | export { default as StudyCreatePage } from "./StudyCreatePage";
3 | export { default as StudyUpdatePage } from "./StudyUpdatePage";
4 | export { default as StudyPage } from "./StudyPage";
5 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from "@testing-library/react";
2 | import App from "./App";
3 |
4 | test("renders learn react link", () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/src/foundations/index.js:
--------------------------------------------------------------------------------
1 | export { default as Background } from "./Background";
2 | export { default as Icon } from "./Icon";
3 | export { default as IconSocial } from "./IconSocial";
4 | export { default as Logo } from "./Logo";
5 | export { default as Typography } from "./Typography";
6 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node as builder
2 |
3 |
4 | FROM nginx
5 |
6 | RUN mkdir /app
7 | WORKDIR /app
8 | RUN mkdir ./build
9 | ADD ./build ./build
10 | RUN rm /etc/nginx/conf.d/default.conf
11 | COPY ./nginx.conf /etc/nginx/conf.d
12 | EXPOSE 80
13 |
14 | CMD ["nginx", "-g", "daemon off;"]
--------------------------------------------------------------------------------
/src/_shared/typography/fontSize.js:
--------------------------------------------------------------------------------
1 | const fontSize = {
2 | h1: "3rem",
3 | h2: "2rem",
4 | h3: "1.5rem",
5 | h4: "1.25rem",
6 | lg: "1.125rem",
7 | p: "1rem",
8 | sm: "0.875rem",
9 | xs: "0.75rem",
10 | xxs: "0.625rem",
11 | };
12 |
13 | export default fontSize;
14 |
--------------------------------------------------------------------------------
/src/_shared/typography/lineHeight.js:
--------------------------------------------------------------------------------
1 | const lineHeight = {
2 | h1: "3.75rem",
3 | h2: "2.25rem",
4 | h3: "2rem",
5 | h4: "1.75rem",
6 | lg: "1.75rem",
7 | p: "1.5rem",
8 | sm: "1.25rem",
9 | xs: "1rem",
10 | xxs: "1rem",
11 | };
12 |
13 | export default lineHeight;
14 |
--------------------------------------------------------------------------------
/src/api/putProfileImage.js:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from "@/utils";
2 |
3 | export const PutProfileImage = async (data) => {
4 | const res = await axiosInstance({
5 | url: "/users/profile-images",
6 | method: "put",
7 | data,
8 | });
9 | return res.data;
10 | };
11 |
--------------------------------------------------------------------------------
/src/recoil/community.js:
--------------------------------------------------------------------------------
1 | import { atom } from "recoil";
2 |
3 | export const communitySearch = atom({
4 | key: "communitySearch",
5 | default: {
6 | boardType: 1,
7 | title: "",
8 | criteria: {
9 | label: "",
10 | value: "",
11 | },
12 | },
13 | });
14 |
--------------------------------------------------------------------------------
/src/utils/debounce.js:
--------------------------------------------------------------------------------
1 | const debounce = (func, timeout = 300) => {
2 | let timer;
3 | return (...args) => {
4 | clearTimeout(timer);
5 | timer = setTimeout(() => {
6 | func.apply(this, args);
7 | }, timeout);
8 | };
9 | };
10 |
11 | export default debounce;
12 |
--------------------------------------------------------------------------------
/src/api/postNicknameCheck.js:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from "@/utils";
2 |
3 | export const PostNicknameCheck = async (data) => {
4 | const res = await axiosInstance({
5 | url: "/users/nickname-check",
6 | method: "post",
7 | data,
8 | });
9 | return res.data;
10 | };
11 |
--------------------------------------------------------------------------------
/src/api/putCommunity.js:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from "@/utils";
2 |
3 | export const putCommunity = async (contentId, data) => {
4 | const res = await axiosInstance({
5 | url: `/boards/${contentId}`,
6 | method: "put",
7 | data,
8 | });
9 | return res.data;
10 | };
11 |
--------------------------------------------------------------------------------
/src/containers/Loading/LoadingCommunityDetail/LoadingCommunityDetail.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { FeedDetail } from "@/components";
3 |
4 | const FeedDetailLoading = ({ ...props }) => {
5 | return ;
6 | };
7 |
8 | export default FeedDetailLoading;
9 |
--------------------------------------------------------------------------------
/src/containers/MainPage/index.js:
--------------------------------------------------------------------------------
1 | export { default as MainCommunitySlider } from "./MainCommunitySlider";
2 | export { default as MainIntroSlider } from "./MainIntroSlider";
3 | export { default as MainJobSlider } from "./MainJobSlider";
4 | export { default as MainStudySilder } from "./MainStudySilder";
5 |
--------------------------------------------------------------------------------
/src/api/getDummyApi.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import { useQuery } from "react-query";
3 |
4 | export const fetchDummyData = async (data) => {
5 | const res = await axios.get("http://swapi.dev/api/people/1/");
6 | return res.data;
7 | };
8 |
9 | export const GetDummyApi = () => useQuery(["dummy"], fetchDummyData);
10 |
--------------------------------------------------------------------------------
/src/utils/throttle.js:
--------------------------------------------------------------------------------
1 | const throttle = (func, timeout = 300) => {
2 | let throttleCheck;
3 | return () => {
4 | if (!throttleCheck) {
5 | throttleCheck = setTimeout(() => {
6 | func();
7 | throttleCheck = false;
8 | }, timeout);
9 | }
10 | };
11 | };
12 |
13 | export default throttle;
14 |
--------------------------------------------------------------------------------
/src/components/layouts/SignUpModal/SignUpModal.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import SignUpModal from ".";
3 |
4 | export default {
5 | title: "Layouts/SignUpModal",
6 | component: SignUpModal,
7 | };
8 |
9 | export const Default = (args) => ;
10 |
11 | Default.args = {
12 | children: "개발자 계정으로 \n간편하게 시작하기",
13 | };
14 |
--------------------------------------------------------------------------------
/src/pages/CommonPage/index.js:
--------------------------------------------------------------------------------
1 | export { default as NotFoundPage } from "./NotFoundPage";
2 | export { default as ErrorPage } from "./ErrorPage";
3 | export { default as PrivacyPage } from "./PrivacyPage";
4 | export { default as TermsPage } from "./TermsPage";
5 | export { default as MadeByPage } from "./MadeByPage";
6 | export { default as LoginPage } from "./LoginPage";
7 |
--------------------------------------------------------------------------------
/src/containers/Loading/index.js:
--------------------------------------------------------------------------------
1 | export { default as ErrorBoundary } from "./ErrorBoundary";
2 | export { default as LoadingDetail } from "./LoadingDetail";
3 | export { default as LoadingDetailMobile } from "./LoadingDetailMobile";
4 | export { default as LoadingCommunityDetail } from "./LoadingCommunityDetail";
5 | export { default as LoadingMyPages } from "./LoadingMyPages";
6 |
--------------------------------------------------------------------------------
/src/pages/MyPage/MyPage.jsx:
--------------------------------------------------------------------------------
1 | import React, { Suspense } from "react";
2 | import { MyPages } from "@/containers/MyPage";
3 | import { LoadingMyPages } from "@/containers/Loading";
4 |
5 | const MyPage = () => {
6 | return (
7 | }>
8 |
9 |
10 | );
11 | };
12 |
13 | export default MyPage;
14 |
--------------------------------------------------------------------------------
/src/containers/Common/ScrollToTop/ScrollToTop.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useLocation } from "react-router-dom";
3 |
4 | const ScrollToTop = () => {
5 | const { pathname } = useLocation();
6 |
7 | useEffect(() => {
8 | window.scrollTo(0, 0);
9 | }, [pathname]);
10 |
11 | return null;
12 | };
13 |
14 | export default ScrollToTop;
15 |
--------------------------------------------------------------------------------
/src/containers/commons/ScrollToTop/ScrollToTop.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useLocation } from "react-router-dom";
3 |
4 | const ScrollToTop = () => {
5 | const { pathname } = useLocation();
6 |
7 | useEffect(() => {
8 | window.scrollTo(0, 0);
9 | }, [pathname]);
10 |
11 | return null;
12 | };
13 |
14 | export default ScrollToTop;
15 |
--------------------------------------------------------------------------------
/src/_shared/animations/loadings.js:
--------------------------------------------------------------------------------
1 | import { css } from "styled-components";
2 | import { animations } from "./";
3 |
4 | const loadings = {
5 | light: css`
6 | animation: ${animations.glowLight} 1.5s ease-in-out infinite;
7 | `,
8 | dark: css`
9 | animation: ${animations.glowDark} 1.5s ease-in-out infinite;
10 | `,
11 | };
12 |
13 | export default loadings;
14 |
--------------------------------------------------------------------------------
/src/components/patterns/Toc/Toc.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { colors } from "@/_shared";
3 |
4 | const borderColor = {
5 | dark: colors.gray700,
6 | light: colors.gray200,
7 | };
8 |
9 | export const Layout = styled.div`
10 | display: flex;
11 | width: 100%;
12 | border-bottom: 1px solid ${(props) => borderColor[props.theme]};
13 | `;
14 |
--------------------------------------------------------------------------------
/src/foundations/Icon/Icon.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 |
3 | export const Svg = styled.svg`
4 | display: ${(props) => (props.block ? "block" : "inline-block")};
5 | vertical-align: middle;
6 | shape-rendering: inherit;
7 | transform: translate3d(0, 0, 0);
8 |
9 | stroke-width: 2;
10 | stroke-linecap: round;
11 | stroke-linejoin: round;
12 | `;
13 |
--------------------------------------------------------------------------------
/src/api/getCompanyList.js:
--------------------------------------------------------------------------------
1 | import { useQuery } from "react-query";
2 | import { axiosInstance } from "@/utils";
3 |
4 | export const GetCompanyList = () =>
5 | useQuery("getCompanyList", () => getCompanyList(), { suspense: false });
6 |
7 | export const getCompanyList = async () => {
8 | const res = await axiosInstance({
9 | url: "/companies",
10 | });
11 | return res.data;
12 | };
13 |
--------------------------------------------------------------------------------
/src/components/patterns/BlinkCursor/BlinkCursor.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import BlinkCursor from ".";
4 | import { Typography } from "@/foundations";
5 |
6 | export default {
7 | title: "Patterns/BlinkCursor",
8 | component: BlinkCursor,
9 | };
10 |
11 | export const Default = () => (
12 |
13 |
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/src/containers/JobPage/JobNoContent/JobNoContent.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Layout, Title, Content } from "./JobNoContent.styled";
3 |
4 | const JobNoContent = ({ ...props }) => {
5 | return (
6 |
7 | 검색 결과가 없어요 🥺
8 |
9 |
10 | );
11 | };
12 |
13 | export default JobNoContent;
14 |
--------------------------------------------------------------------------------
/src/recoil/study.js:
--------------------------------------------------------------------------------
1 | import { atom } from "recoil";
2 |
3 | export const studySearch = atom({
4 | key: "studySearch",
5 | default: {
6 | category: 1,
7 | title: "",
8 | studyType: "",
9 | criteria: {
10 | label: "",
11 | value: "",
12 | },
13 | },
14 | });
15 |
16 | export const studyfilter = atom({
17 | key: "studyfilter",
18 | default: false,
19 | });
20 |
--------------------------------------------------------------------------------
/src/components/layouts/CardSmallSlider/CardSmallSlider.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { Link } from "react-router-dom";
3 |
4 | export const Layout = styled.div`
5 | width: 100%;
6 |
7 | .swiper-slide {
8 | width: 220px !important;
9 | flex-shrink: 0;
10 | }
11 | `;
12 |
13 | export const CardItemLink = styled(Link)`
14 | text-decoration: none;
15 | `;
16 |
--------------------------------------------------------------------------------
/src/foundations/Background/Background.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { colors } from "@/_shared";
3 |
4 | const bgColors = {
5 | dark: colors.black,
6 | blue: colors.blue100,
7 | light: colors.white,
8 | };
9 |
10 | export const Layout = styled.div`
11 | padding: 5rem;
12 | border-radius: 12px;
13 | background-color: ${(props) => bgColors[props.theme]};
14 | `;
15 |
--------------------------------------------------------------------------------
/src/recoil/job.js:
--------------------------------------------------------------------------------
1 | import { atom } from "recoil";
2 |
3 | export const jobSearch = atom({
4 | key: "jobSearch",
5 | default: {
6 | category: 1,
7 | title: "",
8 | techStack: [],
9 | job: "",
10 | criteria: {
11 | label: "",
12 | value: "",
13 | },
14 | },
15 | });
16 |
17 | export const jobFilter = atom({
18 | key: "jobFilter",
19 | default: false,
20 | });
21 |
--------------------------------------------------------------------------------
/src/api/deleteCommunity.js:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from "@/utils";
2 | import { useMutation } from "react-query";
3 |
4 | export const DeleteCommunity = () =>
5 | useMutation("deleteCommunity", deleteCommunity);
6 |
7 | export const deleteCommunity = async (boardId) => {
8 | const res = await axiosInstance({
9 | url: `/boards/${boardId}`,
10 | method: "delete",
11 | });
12 | return res.data;
13 | };
14 |
--------------------------------------------------------------------------------
/src/api/postComment.js:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from "@/utils";
2 |
3 | const commentType = {
4 | company: "company-comments",
5 | study: "study-comments",
6 | board: "board-comments",
7 | };
8 |
9 | export const postComment = async (data) => {
10 | const res = await axiosInstance({
11 | url: `/${commentType[data.type]}`,
12 | method: "post",
13 | data,
14 | });
15 | return res.data;
16 | };
17 |
--------------------------------------------------------------------------------
/src/api/postScrap.js:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from "@/utils";
2 | import { useMutation } from "react-query";
3 |
4 | export const PostScrap = () => useMutation("postScrap", postScrap);
5 |
6 | export const postScrap = async (type, status, studyId) => {
7 | const res = await axiosInstance({
8 | url: `/${type}-scraps/${studyId}`,
9 | method: status ? "delete" : "post",
10 | });
11 | return res.data;
12 | };
13 |
--------------------------------------------------------------------------------
/src/components/layouts/CardSmallGrid/CardSmallGrid.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { Link } from "react-router-dom";
3 |
4 | export const Layout = styled.div`
5 | display: flex;
6 | gap: 20px;
7 |
8 | width: 100%;
9 | overflow-x: scroll;
10 | margin: auto;
11 | align-items: center;
12 | `;
13 |
14 | export const CardItemLink = styled(Link)`
15 | text-decoration: none;
16 | `;
17 |
--------------------------------------------------------------------------------
/src/components/layouts/CardGrid/CardGrid.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { Link } from "react-router-dom";
3 |
4 | export const Layout = styled.section`
5 | display: grid;
6 | width: 100%;
7 | max-width: 940px;
8 | grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
9 | gap: 20px;
10 | `;
11 |
12 | export const CardItemLink = styled(Link)`
13 | text-decoration: none;
14 | `;
15 |
--------------------------------------------------------------------------------
/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = (onPerfEntry) => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/src/components/patterns/Modal/Modal.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Modal from "./";
3 |
4 | export default {
5 | title: "Patterns/Modal",
6 | component: Modal,
7 | };
8 |
9 | export const Default = (args) => ;
10 |
11 | Default.args = {
12 | title: "Job.ssafy로 연결됩니다",
13 | description: "연결시 Web DRM이 켜집니다.",
14 | secondary: "사양할게요",
15 | primary: "네! 들어갈게요",
16 | theme: "light",
17 | };
18 |
--------------------------------------------------------------------------------
/.github/auto_assign.yml:
--------------------------------------------------------------------------------
1 | # Set to true to add reviewers to pull requests
2 | addReviewers: true
3 |
4 | # Set to true to add assignees to pull requests
5 | addAssignees: author
6 |
7 | # A list of reviewers to be added to pull requests (GitHub user name)
8 | reviewers:
9 | - Yewonn
10 | - devpla
11 | - SonSangjoon
12 |
13 | # A number of reviewers added to the pull request
14 | # Set 0 to add all the reviewers (default: 0)
15 | numberOfReviewers: 0
16 |
--------------------------------------------------------------------------------
/src/components/commons/Avatar/AvatarBase.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { avatars } from "@/_shared";
3 |
4 | const AvatarBase = ({ avatar, ...props }) => {
5 | return (
6 |
16 | );
17 | };
18 |
19 | export default AvatarBase;
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development
18 | .env.development.local
19 | .env.production
20 | .env.production.local
21 | .env.test.local
22 |
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | /dist
28 |
--------------------------------------------------------------------------------
/src/components/patterns/AutoTyping/AutoTyping.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import AutoTyping from ".";
4 | import { Typography } from "@/foundations";
5 |
6 | export default {
7 | title: "Patterns/AutoTyping",
8 | component: AutoTyping,
9 | };
10 |
11 | export const Default = (args) => (
12 |
13 |
14 |
15 | );
16 |
17 | Default.args = {
18 | arrayRef: ["Type", "Any", "Text"],
19 | };
20 |
--------------------------------------------------------------------------------
/src/containers/CommunityPage/CommunityDetailComment/CommunityDetailComment.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { colors, fontSize, fontWeight } from "@/_shared";
3 |
4 | const titleColor = {
5 | light: colors.gray900,
6 | dark: colors.gray25,
7 | };
8 |
9 | export const Title = styled.h2`
10 | margin: 2rem 0;
11 |
12 | font-weight: ${fontWeight.bold};
13 | font-size: ${fontSize.h3};
14 | color: ${(props) => titleColor[props.theme]};
15 | `;
16 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyCreateForm/StudyCreateForm.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 |
3 | export const Layout = styled.div`
4 | width: 100%;
5 | max-width: 1280px;
6 | margin: auto;
7 | padding-bottom: 4rem;
8 | `;
9 |
10 | export const ContentBox = styled.div`
11 | padding: 20px;
12 | `;
13 |
14 | export const ButtonBox = styled.div`
15 | display: flex;
16 | justify-content: flex-end;
17 | gap: 1rem;
18 | padding-top: 2rem;
19 | `;
20 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyUpdateForm/StudyUpdateForm.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 |
3 | export const Layout = styled.div`
4 | width: 100%;
5 | max-width: 1280px;
6 | margin: auto;
7 | padding-bottom: 4rem;
8 | `;
9 |
10 | export const ContentBox = styled.div`
11 | padding: 20px;
12 | `;
13 |
14 | export const ButtonBox = styled.div`
15 | display: flex;
16 | justify-content: flex-end;
17 | gap: 1rem;
18 | padding-top: 2rem;
19 | `;
20 |
--------------------------------------------------------------------------------
/src/api/deleteNotification.js:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from "@/utils";
2 |
3 | export const DeleteNotification = async (notificationId) => {
4 | const res = await axiosInstance({
5 | url: `/notifications/${notificationId}`,
6 | method: "delete",
7 | });
8 | return res.data;
9 | };
10 |
11 | export const DeleteNotificationsAll = async () => {
12 | const res = await axiosInstance({
13 | url: `/notifications`,
14 | method: "delete",
15 | });
16 | return res.data;
17 | };
18 |
--------------------------------------------------------------------------------
/src/components/commons/BookMark/BookMark.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import BookMark from "./";
3 |
4 | export default {
5 | title: "Components/BookMark",
6 | component: BookMark,
7 | };
8 |
9 | export const Default = (args) => ;
10 |
11 | export const AllTypes = () => (
12 | <>
13 |
14 |
15 |
16 | >
17 | );
18 |
--------------------------------------------------------------------------------
/src/components/patterns/Dropdown/DropdownItem/DropdownItem.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { DropdownItem } from "./";
4 | import { Background } from "@/foundations";
5 |
6 | export default {
7 | title: "Patterns/Dropdown/DropdownItem",
8 | component: DropdownItem,
9 | };
10 |
11 | export const Default = (args) => (
12 |
13 |
14 |
15 | );
16 |
17 | Default.args = {
18 | children: "메뉴",
19 | };
20 |
--------------------------------------------------------------------------------
/src/components/layouts/FeedSmallGrid/FeedSmallGrid.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { Link } from "react-router-dom";
3 |
4 | export const Layout = styled.div`
5 | display: flex;
6 | gap: 20px;
7 | max-width: 940px;
8 | overflow-x: auto;
9 |
10 | @media screen and (max-width: 530px) {
11 | flex-direction: column;
12 | gap: 12px;
13 | }
14 | `;
15 |
16 | export const FeedItemLink = styled(Link)`
17 | text-decoration: none;
18 | width: 100%;
19 | `;
20 |
--------------------------------------------------------------------------------
/src/utils/parseHtml.js:
--------------------------------------------------------------------------------
1 | import parse from "html-react-parser";
2 | import DOMPurify from "dompurify";
3 |
4 | const parseHtml = (htmlString) => {
5 | const cleanHtmlString = DOMPurify.sanitize(htmlString);
6 | const tagDeletedHtml = cleanHtmlString.replace(/(<([^>]+)>)/gi, "");
7 | const thumbnail = cleanHtmlString.match(/ {
9 | const theme = useRecoilValue(themeState);
10 |
11 | return (
12 | <>
13 |
14 |
15 | >
16 | );
17 | };
18 |
19 | export default StudyCreatePage;
20 |
--------------------------------------------------------------------------------
/src/components/patterns/SubNavbar/SubNavbar.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import SubNavbar from "./";
3 |
4 | export default {
5 | title: "Patterns/SubNavbar",
6 | component: SubNavbar,
7 | };
8 |
9 | export const Default = (args) => ;
10 |
11 | Default.args = {
12 | data: [
13 | {
14 | id: 0,
15 | title: "마감임박",
16 | },
17 | {
18 | id: 1,
19 | title: "최신순",
20 | },
21 | {
22 | id: 2,
23 | title: "인기순",
24 | },
25 | ],
26 | };
27 |
--------------------------------------------------------------------------------
/src/recoil/theme.js:
--------------------------------------------------------------------------------
1 | import { atom } from "recoil";
2 | import { localStorageEffect } from "@/utils";
3 |
4 | const THEME = {
5 | DARK: "dark",
6 | LIGHT: "light",
7 | };
8 |
9 | const TYPE = {
10 | DEFAULT: "default",
11 | TRANSPARENT: "transparent",
12 | };
13 |
14 | export const themeState = atom({
15 | key: "themeState",
16 | default: THEME.LIGHT,
17 | effects_UNSTABLE: [localStorageEffect("theme")],
18 | });
19 |
20 | export const navTypeState = atom({
21 | key: "navTypeState",
22 | default: TYPE.DEFAULT,
23 | });
24 |
--------------------------------------------------------------------------------
/src/utils/localStorageEffect.js:
--------------------------------------------------------------------------------
1 | import { DefaultValue } from "recoil";
2 |
3 | export const localStorageEffect =
4 | (key) =>
5 | ({ setSelf, onSet }) => {
6 | const savedValue = localStorage.getItem(key);
7 | if (savedValue != null) {
8 | setSelf(JSON.parse(savedValue));
9 | }
10 | onSet((newValue) => {
11 | if (newValue instanceof DefaultValue) {
12 | localStorage.removeItem(key);
13 | } else {
14 | localStorage.setItem(key, JSON.stringify(newValue));
15 | }
16 | });
17 | };
18 |
--------------------------------------------------------------------------------
/.storybook/main.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | stories: [
3 | "../src/**/*.stories.mdx",
4 | "../src/**/*.stories.@(js|jsx|ts|tsx)",
5 | "../src/**/**/*.stories.mdx",
6 | "../src/**/**/*.stories.@(js|jsx|ts|tsx)",
7 | ],
8 | addons: [
9 | "@storybook/addon-links",
10 | "@storybook/addon-a11y",
11 | "@storybook/addon-docs",
12 | "@storybook/addon-essentials",
13 | "@storybook/preset-create-react-app",
14 | ],
15 | framework: "@storybook/react",
16 | core: {
17 | builder: "webpack5",
18 | },
19 | };
20 |
--------------------------------------------------------------------------------
/src/components/patterns/SubNavbar/SubNavbar.styled.js:
--------------------------------------------------------------------------------
1 | import styled, { css } from "styled-components";
2 |
3 | export const Layout = styled.ul`
4 | display: flex;
5 | overflow-x: scroll;
6 | margin: 0;
7 | padding: 0;
8 |
9 | ${(props) =>
10 | props.view === "default" &&
11 | css`
12 | flex-direction: column;
13 | align-items: stretch;
14 | width: 200px;
15 | `}
16 |
17 | > div {
18 | flex-shrink: 0;
19 | }
20 |
21 | scroll-behavior: smooth;
22 | ::-webkit-scrollbar {
23 | display: none;
24 | }
25 | `;
26 |
--------------------------------------------------------------------------------
/src/containers/JobPage/index.js:
--------------------------------------------------------------------------------
1 | export { default as JobCardGrid } from "./JobCardGrid";
2 | export { default as JobDetail } from "./JobDetail";
3 | export { default as JobDetailMobile } from "./JobDetailMobile";
4 | export { default as JobDetailComment } from "./JobDetailComment";
5 | export { default as JobIntro } from "./JobIntro";
6 | export { default as JobSideBar } from "./JobSideBar";
7 | export { default as JobMain } from "./JobMain";
8 | export { default as JobMainMobile } from "./JobMainMobile";
9 | export { default as JobNoContent } from "./JobNoContent";
10 |
--------------------------------------------------------------------------------
/src/components/patterns/Sidebar/SideBar.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 |
3 | export const Layout = styled.aside`
4 | display: block;
5 | border-radius: 16px;
6 | width: 400px;
7 | height: 540px;
8 |
9 | button {
10 | margin: 6px 10px;
11 | width: calc(100% - 20px);
12 | }
13 | .thumbnail {
14 | margin-bottom: 12px;
15 | }
16 | `;
17 |
18 | export const SideBarBox = styled.div`
19 | margin: 10px 0px;
20 | `;
21 |
22 | export const BadgeBox = styled.div`
23 | display: flex;
24 | gap: 5px;
25 | margin: 20px 10px;
26 | `;
27 |
--------------------------------------------------------------------------------
/src/utils/getEndDate.js:
--------------------------------------------------------------------------------
1 | const getEndDate = (value) => {
2 | const date = new Date(value);
3 | const WEEKDAY = ["일", "월", "화", "수", "목", "금", "토"];
4 |
5 | const month = (date.getMonth() + 1).toString().padStart(2, "0");
6 | const day = date.getDate().toString().padStart(2, "0");
7 | const weekday = WEEKDAY[date.getDay()];
8 | const hour = date.getHours().toString().padStart(2, "0");
9 | const minute = date.getMinutes().toString().padStart(2, "0");
10 |
11 | return `~ ${month}.${day} (${weekday}) ${hour}:${minute}`;
12 | };
13 |
14 | export default getEndDate;
15 |
--------------------------------------------------------------------------------
/src/containers/Common/ErrorBoundary/ErrorBoundary.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | class ErrorBoundary extends React.Component {
4 | constructor(props) {
5 | super(props);
6 | this.state = { hasError: false };
7 | }
8 |
9 | static getDerivedStateFromError(error) {
10 | return { hasError: true };
11 | }
12 |
13 | // componentDidCatch(error, errorInfo)
14 |
15 | render() {
16 | if (this.state.hasError) {
17 | return this.props.fallback;
18 | }
19 | return this.props.children;
20 | }
21 | }
22 |
23 | export default ErrorBoundary;
24 |
--------------------------------------------------------------------------------
/src/containers/Loading/ErrorBoundary/ErrorBoundary.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | class ErrorBoundary extends React.Component {
4 | constructor(props) {
5 | super(props);
6 | this.state = { hasError: false };
7 | }
8 |
9 | static getDerivedStateFromError(error) {
10 | return { hasError: true };
11 | }
12 |
13 | // componentDidCatch(error, errorInfo)
14 |
15 | render() {
16 | if (this.state.hasError) {
17 | return this.props.fallback;
18 | }
19 | return this.props.children;
20 | }
21 | }
22 |
23 | export default ErrorBoundary;
24 |
--------------------------------------------------------------------------------
/src/components/layouts/CardGrid/CardGrid.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import CardGrid from ".";
3 |
4 | export default {
5 | title: "layouts/CardGrid",
6 | component: CardGrid,
7 | };
8 |
9 | export const Default = (args) => ;
10 |
11 | Default.args = {
12 | theme: "light",
13 | data: new Array(20).fill({
14 | contents: {
15 | title: "주니어 프론트엔드 채용",
16 | subtitle: "싸페 디자인 시스템",
17 | highlight: "D-day",
18 | src: "",
19 | },
20 | badges: ["JavaScript", "React", "Vue.js"],
21 | id: "1",
22 | }),
23 | };
24 |
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | export { default as daysFromToday } from "./daysFromToday";
2 | export { default as daysLeftFromToday } from "./daysLeftFromToday";
3 | export { default as numToMillion } from "./numToMillion";
4 | export { default as getCroppedImg } from "./getCroppedImg";
5 | export * from "./localStorageEffect";
6 | export { default as getEndDate } from "./getEndDate";
7 | export { default as axiosInstance } from "./axiosConfig";
8 | export { default as parseHtml } from "./parseHtml";
9 | export { default as debounce } from "./debounce";
10 | export { default as throttle } from "./throttle";
11 |
--------------------------------------------------------------------------------
/src/components/commons/InputImage/InputImage.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import InputImage from "./";
4 | import { Background } from "@/foundations";
5 |
6 | export default {
7 | title: "Components/InputImage",
8 | component: InputImage,
9 | };
10 |
11 | export const Default = (args) => ;
12 |
13 | export const AllTypes = () => (
14 | <>
15 |
16 |
17 |
18 |
19 |
20 |
21 | >
22 | );
23 |
--------------------------------------------------------------------------------
/src/components/layouts/CardSlider/CardSlider.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import CardSlider from ".";
3 |
4 | export default {
5 | title: "layouts/CardSlider",
6 | component: CardSlider,
7 | };
8 |
9 | export const Default = (args) => ;
10 |
11 | Default.args = {
12 | theme: "light",
13 | data: new Array(20).fill({
14 | contents: {
15 | title: "주니어 프론트엔드 채용",
16 | subtitle: "싸페 디자인 시스템",
17 | highlight: "D-day",
18 | src: "",
19 | },
20 | badges: ["JavaScript", "React", "Vue.js"],
21 | id: "1",
22 | }),
23 | };
24 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyNoContent/StudyNoContent.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { Layout, Title, Content, ButtonLink } from "./StudyNoContent.styled";
4 | import { Button } from "@/components";
5 |
6 | const StudyNoContent = ({ ...props }) => {
7 | return (
8 |
9 | 검색 결과가 없어요 🥺
10 | 스터디를 직접 개설해볼까요?
11 |
12 |
13 |
14 |
15 | );
16 | };
17 |
18 | export default StudyNoContent;
19 |
--------------------------------------------------------------------------------
/.github/workflows/chromatic.yml:
--------------------------------------------------------------------------------
1 | # name of our action
2 | name: 'Chromatic'
3 | # the event that will trigger the action
4 | on: push
5 |
6 | # what the action will do
7 | jobs:
8 | test:
9 | # the operating system it will run on
10 | runs-on: ubuntu-latest
11 | # the list of steps that the action will go through
12 | steps:
13 | - uses: actions/checkout@v1
14 | - run: yarn
15 | - uses: chromaui/action@v1
16 | # options required to the GitHub chromatic action
17 | with:
18 | projectToken: 79f8f0336c9d
19 | token: ${{ secrets.GITHUB_TOKEN }}
--------------------------------------------------------------------------------
/src/containers/MainPage/MainIntroSlider/MainIntroSlider.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { GetJobList } from "@/api";
4 | import { IntroSlider } from "@/components";
5 |
6 | const MainIntroSlider = ({ ...props }) => {
7 | const search = {
8 | category: 2,
9 | title: "",
10 | techStack: [],
11 | job: "",
12 | criteria: {
13 | label: "",
14 | value: "",
15 | },
16 | };
17 |
18 | const { data } = GetJobList(search);
19 |
20 | return ;
21 | };
22 |
23 | export default MainIntroSlider;
24 |
--------------------------------------------------------------------------------
/src/api/deleteComments.js:
--------------------------------------------------------------------------------
1 | import { useMutation } from "react-query";
2 | import { axiosInstance } from "@/utils";
3 |
4 | const commentType = {
5 | company: "company-comments",
6 | study: "study-comments",
7 | board: "board-comments",
8 | };
9 |
10 | export const DeleteComments = (data) =>
11 | useMutation(["deleteComments", data], deleteComments(data));
12 |
13 | export const deleteComments = async (data) => {
14 | const url = `/${commentType[data.type]}/${data.id}`;
15 | const res = await axiosInstance({
16 | url: url,
17 | method: "delete",
18 | });
19 | return res.data;
20 | };
21 |
--------------------------------------------------------------------------------
/src/components/patterns/AvatarUploader/AvatarUploader.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import AvatarUploader from ".";
4 | import { Background } from "@/foundations";
5 |
6 | export default {
7 | title: "Patterns/AvatarUploader",
8 | component: AvatarUploader,
9 | };
10 |
11 | export const Default = (args) => ;
12 |
13 | export const AllTypes = () => (
14 | <>
15 |
16 |
17 |
18 |
19 |
20 |
21 | >
22 | );
23 |
--------------------------------------------------------------------------------
/src/components/patterns/Search/Search.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Search from ".";
3 | import { Background } from "@/foundations";
4 |
5 | export default {
6 | title: "Patterns/Search",
7 | component: Search,
8 | };
9 |
10 | export const Default = (args) => (
11 |
12 |
13 |
14 | );
15 |
16 | export const AllTypes = () => (
17 | <>
18 |
19 |
20 |
21 |
22 |
23 |
24 | >
25 | );
26 |
--------------------------------------------------------------------------------
/src/components/patterns/Toc/TocNav/TocNav.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { colors } from "@/_shared";
3 |
4 | const bgColors = {
5 | dark: colors.black,
6 | light: colors.white,
7 | };
8 |
9 | const borderColor = {
10 | dark: colors.gray700,
11 | light: colors.gray200,
12 | };
13 |
14 | export const Layout = styled.div`
15 | display: flex;
16 | align-items: flex-end;
17 |
18 | width: 100%;
19 | height: 60px;
20 | padding-left: 468px;
21 | border-bottom: 1px solid ${(props) => borderColor[props.theme]};
22 |
23 | background-color: ${(props) => bgColors[props.theme]};
24 | `;
25 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 |
4 | import "normalize.css/normalize.css";
5 | import "./index.css";
6 |
7 | import App from "./App";
8 | import reportWebVitals from "./reportWebVitals";
9 |
10 | ReactDOM.render(
11 |
12 |
13 | ,
14 | document.getElementById("root")
15 | );
16 |
17 | // If you want to start measuring performance in your app, pass a function
18 | // to log results (for example: reportWebVitals(console.log))
19 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
20 | reportWebVitals();
21 |
--------------------------------------------------------------------------------
/src/components/patterns/ScrollTopButton/ScrollTopButton.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import ScrollTopButton from "./";
4 | import { Background } from "@/foundations";
5 |
6 | export default {
7 | title: "Patterns/ScrollTopButton",
8 | component: ScrollTopButton,
9 | };
10 |
11 | export const Default = (props) => ;
12 |
13 | export const AllTypes = () => (
14 | <>
15 |
16 |
17 |
18 |
19 |
20 |
21 | >
22 | );
23 |
--------------------------------------------------------------------------------
/src/containers/MainPage/MainJobSlider/MainJobSlider.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { GetJobList, JobCardSelector } from "@/api";
4 | import { CardSlider } from "@/components";
5 |
6 | const MainJobSlider = ({ ...props }) => {
7 | const search = {
8 | category: 1,
9 | title: "",
10 | techStack: [],
11 | job: "",
12 | criteria: {
13 | label: "",
14 | value: "",
15 | },
16 | };
17 |
18 | const { data } = GetJobList(search);
19 | const { cardData } = JobCardSelector(data);
20 |
21 | return ;
22 | };
23 |
24 | export default MainJobSlider;
25 |
--------------------------------------------------------------------------------
/src/containers/MainPage/MainStudySilder/MainStudySilder.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { GetStudyList, StudyCardSelector } from "@/api";
4 | import { CardSlider } from "@/components";
5 |
6 | const MainStudySilder = ({ ...props }) => {
7 | const search = {
8 | category: 1,
9 | title: "",
10 | studyType: "",
11 | criteria: {
12 | label: "",
13 | value: "",
14 | },
15 | };
16 |
17 | const { data } = GetStudyList(search);
18 | const { cardData } = StudyCardSelector(data);
19 |
20 | return ;
21 | };
22 |
23 | export default MainStudySilder;
24 |
--------------------------------------------------------------------------------
/src/hooks/useGaTracker.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 | import { useLocation } from "react-router-dom";
3 | import ReactGA from "react-ga";
4 |
5 | const useGaTracker = () => {
6 | const location = useLocation();
7 | const [initialized, setInitialized] = useState(false);
8 |
9 | useEffect(() => {
10 | ReactGA.initialize(process.env.REACT_APP_GOOGLE_ANALYTICS_ID);
11 | setInitialized(true);
12 | }, []);
13 |
14 | useEffect(() => {
15 | if (initialized) {
16 | ReactGA.pageview(location.pathname + location.search);
17 | }
18 | }, [initialized, location]);
19 | };
20 |
21 | export default useGaTracker;
22 |
--------------------------------------------------------------------------------
/src/components/patterns/Dropdown/DropdownItem/DropdownItem.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout } from "./DropdownItem.styled";
5 |
6 | const THEME = {
7 | DARK: "dark",
8 | LIGHT: "light",
9 | };
10 |
11 | const DropdownItem = ({ children, ...props }) => {
12 | return {children};
13 | };
14 |
15 | DropdownItem.propTypes = {
16 | theme: PropTypes.oneOf(Object.values(THEME)),
17 | children: PropTypes.node.isRequired,
18 | };
19 |
20 | DropdownItem.defaultProps = {
21 | theme: THEME.LIGHT,
22 | children: "메뉴",
23 | };
24 |
25 | export default DropdownItem;
26 |
--------------------------------------------------------------------------------
/src/recoil/menu.js:
--------------------------------------------------------------------------------
1 | import { atom } from "recoil";
2 |
3 | export const navMenuData = atom({
4 | key: "navMenuData",
5 | default: [
6 | {
7 | name: "community",
8 | title: "커뮤니티",
9 | url: "community",
10 | },
11 | {
12 | name: "study",
13 | title: "스터디",
14 | url: "study",
15 | },
16 | {
17 | name: "job",
18 | title: "취업정보",
19 | url: "job",
20 | },
21 | ],
22 | });
23 |
24 | export const navUserData = atom({
25 | key: "navUserData",
26 | default: [
27 | {
28 | name: "mypage",
29 | title: "내 프로필",
30 | url: "mypage",
31 | },
32 | ],
33 | });
34 |
--------------------------------------------------------------------------------
/src/components/commons/BookMark/BookMark.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { colors } from "@/_shared";
3 |
4 | const iconColor = {
5 | light: {
6 | primary: colors.blue100,
7 | secondary: colors.gray300,
8 | },
9 | dark: {
10 | primary: colors.blue100,
11 | secondary: colors.gray500,
12 | },
13 | };
14 |
15 | export const Svg = styled.svg`
16 | display: ${(props) => (props.block ? "block" : "inline-block")};
17 | vertical-align: middle;
18 |
19 | path {
20 | stroke-width: ${(props) => iconColor[props.theme][props.mode]};
21 | fill: ${(props) => iconColor[props.theme][props.mode]};
22 | }
23 | `;
24 |
--------------------------------------------------------------------------------
/src/hooks/useIntersectionObserver.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 |
3 | export const useIntersectionObserver = ({ target, onIntersect, enabled }) => {
4 | useEffect(() => {
5 | const el = target?.current;
6 |
7 | if (!enabled || !el) {
8 | return;
9 | }
10 |
11 | const observer = new IntersectionObserver(
12 | (entries) =>
13 | entries.forEach((entry) => entry.isIntersecting && onIntersect()),
14 | {
15 | threshold: 1.0,
16 | }
17 | );
18 |
19 | observer.observe(el);
20 | return () => {
21 | observer.unobserve(el);
22 | };
23 | }, [target, enabled, onIntersect]);
24 | };
25 |
--------------------------------------------------------------------------------
/src/utils/daysLeftFromToday.js:
--------------------------------------------------------------------------------
1 | const daysLeftFromToday = (value) => {
2 | const now = new Date();
3 | const today =
4 | now.getTime() + now.getTimezoneOffset() * 60 * 1000 + 9 * 60 * 60 * 1000;
5 | const timeValue = new Date(value);
6 |
7 | const betweenTime = Math.floor((timeValue.getTime() - today) / 1000 / 60);
8 |
9 | if (betweenTime < 1) return false;
10 |
11 | const betweenTimeHour = Math.floor(betweenTime / 60);
12 | if (betweenTimeHour < 24) {
13 | return `D-Day`;
14 | }
15 |
16 | const betweenTimeDay = Math.floor(betweenTime / 60 / 24);
17 | return `D-${betweenTimeDay}`;
18 | };
19 |
20 | export default daysLeftFromToday;
21 |
--------------------------------------------------------------------------------
/src/components/commons/Checkbox/Checkbox.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import Checkbox from "./";
4 | import { Background } from "@/foundations";
5 |
6 | export default {
7 | title: "Components/Checkbox",
8 | component: Checkbox,
9 | };
10 |
11 | export const Default = (args) => (
12 | <>
13 |
14 |
15 |
16 | >
17 | );
18 |
19 | export const All = () => (
20 | <>
21 |
22 |
23 |
24 |
25 |
26 |
27 | >
28 | );
29 |
--------------------------------------------------------------------------------
/src/components/patterns/ImageUploader/ImageUploader.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import ImageUploader from "./";
4 | import { Background } from "@/foundations";
5 |
6 | export default {
7 | title: "Patterns/ImageUploader",
8 | component: ImageUploader,
9 | };
10 |
11 | export const Default = (args) => ;
12 |
13 | Default.args = {
14 | aspect: 2,
15 | };
16 |
17 | export const AllTypes = () => (
18 | <>
19 |
20 |
21 |
22 |
23 |
24 |
25 | >
26 | );
27 |
--------------------------------------------------------------------------------
/src/foundations/Typography/Typography.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Text } from "./Typography.styled";
5 |
6 | const TYPE = {
7 | H1: "h1",
8 | H2: "h2",
9 | H3: "h3",
10 | H4: "h4",
11 | LARGE: "large",
12 | PARAGRAPH: "paragraph",
13 | SMALL: "small",
14 | BUTTON: "button",
15 | };
16 |
17 | const Typography = ({ children, ...props }) => {
18 | return {children};
19 | };
20 |
21 | Typography.propTypes = {
22 | type: PropTypes.oneOf(Object.values(TYPE)),
23 | };
24 |
25 | Typography.defaultProps = {
26 | type: TYPE.PARAGRAPH,
27 | };
28 |
29 | export default Typography;
30 |
--------------------------------------------------------------------------------
/src/components/patterns/CommentInput/CommentInput.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import CommentInput from "./";
4 | import { Background } from "@/foundations";
5 |
6 | export default {
7 | title: "Patterns/CommentInput",
8 | component: CommentInput,
9 | };
10 |
11 | export const Default = (args) => (
12 |
13 |
14 |
15 | );
16 |
17 | export const AllTypes = () => (
18 | <>
19 |
20 |
21 |
22 |
23 |
24 |
25 | >
26 | );
27 |
--------------------------------------------------------------------------------
/src/components/patterns/ScrollTopButton/ScrollTopButton.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout } from "./ScrollTopButton.styled";
5 | import { Icon } from "@/foundations";
6 |
7 | const THEME = {
8 | DARK: "dark",
9 | LIGHT: "light",
10 | };
11 |
12 | const ScrollTopButton = ({ ...props }) => {
13 | return (
14 |
15 |
16 |
17 | );
18 | };
19 |
20 | ScrollTopButton.propTypes = {
21 | theme: PropTypes.oneOf(Object.values(THEME)),
22 | };
23 |
24 | ScrollTopButton.defaultProps = {
25 | theme: THEME.LIGHT,
26 | };
27 |
28 | export default ScrollTopButton;
29 |
--------------------------------------------------------------------------------
/src/pages/CommonPage/EmptyPage.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 |
4 | import { useRecoilValue } from "recoil";
5 | import { themeState } from "@/recoil/theme";
6 |
7 | import { colors } from "@/_shared";
8 |
9 | const EmptyPage = () => {
10 | const theme = useRecoilValue(themeState);
11 |
12 | return ;
13 | };
14 |
15 | export default EmptyPage;
16 |
17 | const bgColor = {
18 | light: colors.white,
19 | dark: colors.black,
20 | };
21 |
22 | const Layout = styled.div`
23 | display: flex;
24 | height: 100vh;
25 | width: 100vw;
26 | background-color: ${(props) => bgColor[props.theme]};
27 | `;
28 |
--------------------------------------------------------------------------------
/src/components/layouts/FeedGrid/FeedGrid.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { Link } from "react-router-dom";
3 | import { colors } from "@/_shared";
4 |
5 | const borderColor = {
6 | light: colors.gray100,
7 | dark: colors.gray900,
8 | };
9 |
10 | export const Layout = styled.section`
11 | display: flex;
12 | flex-direction: column;
13 | align-items: center;
14 | gap: 32px;
15 |
16 | width: 100%;
17 | max-width: 940px;
18 | padding-top: 32px;
19 | `;
20 |
21 | export const FeedItemLink = styled(Link)`
22 | width: 100%;
23 | padding-bottom: 32px;
24 | border-bottom: 1px solid ${(props) => borderColor[props.theme]};
25 | text-decoration: none;
26 | `;
27 |
--------------------------------------------------------------------------------
/src/components/patterns/Dropdown/Dropdown.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import Dropdown from "./";
4 | import { Background } from "@/foundations";
5 |
6 | export default {
7 | title: "Patterns/Dropdown",
8 | component: Dropdown,
9 | };
10 |
11 | export const Default = (args) => (
12 |
13 |
14 |
15 | );
16 |
17 | Default.args = {
18 | items: [
19 | {
20 | name: "mypage",
21 | title: "내 프로필",
22 | url: "mypage",
23 | },
24 | {
25 | name: "logout",
26 | title: "로그아웃",
27 | url: "logout",
28 | },
29 | ],
30 | user: "김싸피",
31 | };
32 |
--------------------------------------------------------------------------------
/src/components/patterns/ImageUploader/ImageUploader.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { Button } from "@/components";
3 |
4 | export const Layout = styled.div`
5 | display: flex;
6 | flex-direction: column;
7 | gap: 2rem;
8 | `;
9 |
10 | export const CropperBox = styled.div`
11 | position: fixed;
12 | top: 0;
13 | left: 0;
14 | right: 0;
15 | bottom: 0;
16 | z-index: 1;
17 | `;
18 |
19 | export const Submit = styled(Button)`
20 | position: absolute;
21 | left: calc(50% - 90px);
22 | bottom: 18%;
23 |
24 | width: 180px;
25 | margin: auto;
26 | `;
27 |
28 | export const ImgBox = styled.div`
29 | img {
30 | border-radius: 8px;
31 | }
32 | `;
33 |
--------------------------------------------------------------------------------
/src/api/getNotifications.js:
--------------------------------------------------------------------------------
1 | import { useQuery } from "react-query";
2 | import { axiosInstance } from "@/utils";
3 |
4 | export const GetNotificationList = () =>
5 | useQuery(["getNotificationList"], () => fetchData(), {
6 | refetchOnWindowFocus: "always",
7 | refetchOnMount: "always",
8 | refetchOnReconnect: "always",
9 | refetchInterval: 100000,
10 | });
11 |
12 | const fetchData = async () => {
13 | const res = await axiosInstance({
14 | url: "/notifications",
15 | });
16 | return res.data;
17 | };
18 |
19 | export const GetNotification = async (id) => {
20 | const res = await axiosInstance({
21 | url: `/notifications/${id}`,
22 | });
23 | return res.data;
24 | };
25 |
--------------------------------------------------------------------------------
/src/components/patterns/AvatarUploader/AvatarUploader.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 |
3 | export const Layout = styled.div`
4 | display: flex;
5 | gap: 1rem;
6 |
7 | @media screen and (max-width: 530px) {
8 | flex-direction: column;
9 | }
10 | `;
11 |
12 | export const CropperBox = styled.div`
13 | position: fixed;
14 | top: 68px;
15 | left: 0;
16 | right: 0;
17 | bottom: 0;
18 | z-index: 1;
19 |
20 | button {
21 | position: absolute;
22 | left: calc(50% - 90px);
23 | bottom: 18%;
24 |
25 | width: 180px;
26 | margin: auto;
27 | }
28 | `;
29 |
30 | export const ImgBox = styled.div`
31 | img {
32 | border-radius: 8px;
33 | }
34 | `;
35 |
--------------------------------------------------------------------------------
/src/containers/commons/index.js:
--------------------------------------------------------------------------------
1 | export { default as Footer } from "./Footer";
2 | export { default as Navbar } from "./Navbar";
3 | export { default as Notification } from "./Notification";
4 | export { default as ScrollToTop } from "./ScrollToTop";
5 | export { default as SignUpModal } from "./SignUpModal";
6 | export { default as AuthModal } from "./AuthModal";
7 | export { default as SmallModal } from "./SmallModal";
8 | export { default as SubmitModal } from "./SubmitModal";
9 | export { default as DeleteModal } from "./DeleteModal";
10 | export { default as DeleteAccountModal } from "../MyPage/DeleteAccountModal";
11 | export { default as DeleteAccountCompleteModal } from "../MyPage/DeleteAccountCompleteModal";
12 |
--------------------------------------------------------------------------------
/src/components/commons/ImageBoxResponsive/ImageBoxResponsive.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ImageBoxResponsive from "./";
3 |
4 | export default {
5 | title: "Components/ImageBoxResponsive",
6 | component: ImageBoxResponsive,
7 | };
8 |
9 | export const Default = (args) => ;
10 |
11 | export const AllTypes = () => (
12 | <>
13 |
14 |
15 |
16 |
17 |
18 | >
19 | );
20 |
--------------------------------------------------------------------------------
/src/components/commons/index.js:
--------------------------------------------------------------------------------
1 | export { default as Avatar } from "./Avatar";
2 | export { default as Badge } from "./Badge";
3 | export { default as BookMark } from "./BookMark";
4 | export { default as Button } from "./Button";
5 | export { default as Checkbox } from "./Checkbox";
6 | export { default as ImageBox } from "./ImageBox";
7 | export { default as ImageBoxResponsive } from "./ImageBoxResponsive";
8 | export { default as Input } from "./Input";
9 | export { default as InputImage } from "./InputImage";
10 | export { default as Radio } from "./Radio";
11 | export { default as Selector } from "./Selector";
12 | export { default as SideBarItem } from "./SideBarItem";
13 | export { default as Switch } from "./Switch";
14 |
--------------------------------------------------------------------------------
/src/containers/JobPage/JobMainMobile/JobMainMobile.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { SubNavbar } from "@/components";
3 |
4 | export const SubNavMobile = styled(SubNavbar)`
5 | padding: 20px 20px 0 20px;
6 | `;
7 |
8 | export const MobileCardBox = styled.div`
9 | display: flex;
10 | justify-content: center;
11 | flex-direction: column;
12 | gap: 100px;
13 | max-width: 1280px;
14 |
15 | padding: 20px;
16 | margin: auto;
17 | `;
18 |
19 | export const SearchBox = styled.div`
20 | padding: 20px 20px 0 20px;
21 | `;
22 |
23 | export const SortBox = styled.div`
24 | display: flex;
25 | justify-content: space-between;
26 | align-items: center;
27 | padding: 20px;
28 | `;
29 |
--------------------------------------------------------------------------------
/src/containers/JobPage/JobSideBar/JobSideBar.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 |
3 | export const Layout = styled.aside`
4 | position: sticky;
5 | top: 180px;
6 |
7 | display: block;
8 | border-radius: 16px;
9 | width: 400px;
10 | height: 540px;
11 |
12 | button {
13 | margin: 6px 10px;
14 | width: calc(100% - 20px);
15 | }
16 | .thumbnail {
17 | margin-bottom: 12px;
18 | }
19 | `;
20 |
21 | export const SideBarItemBox = styled.ul`
22 | margin: 10px 0px;
23 | padding: 0;
24 | `;
25 |
26 | export const BadgeBox = styled.ul`
27 | display: flex;
28 | gap: 5px;
29 | margin: 20px 10px;
30 | padding: 0;
31 |
32 | li {
33 | margin-left: 4px;
34 | }
35 | `;
36 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyMainMobile/StudyMainMobile.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { SubNavbar } from "@/components";
3 |
4 | export const SubNavMobile = styled(SubNavbar)`
5 | padding: 20px 20px 0 20px;
6 | `;
7 |
8 | export const MobileCardBox = styled.div`
9 | display: flex;
10 | justify-content: center;
11 | flex-direction: column;
12 | gap: 100px;
13 | max-width: 1280px;
14 |
15 | padding: 20px;
16 | margin: auto;
17 | `;
18 |
19 | export const SearchBox = styled.div`
20 | padding: 20px 20px 0 20px;
21 | `;
22 |
23 | export const SortBox = styled.div`
24 | display: flex;
25 | justify-content: space-between;
26 | align-items: center;
27 | padding: 20px;
28 | `;
29 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudySideBar/StudySideBar.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 |
3 | export const Layout = styled.aside`
4 | display: block;
5 | position: sticky;
6 | top: 180px;
7 |
8 | width: 400px;
9 | height: 540px;
10 | border-radius: 16px;
11 |
12 | button {
13 | margin: 6px 10px;
14 | width: calc(100% - 20px);
15 | }
16 | .thumbnail {
17 | margin-bottom: 12px;
18 | }
19 | `;
20 |
21 | export const SideBarItemBox = styled.ul`
22 | margin: 10px 0px;
23 | padding: 0;
24 | `;
25 |
26 | export const BadgeBox = styled.ul`
27 | display: flex;
28 | gap: 5px;
29 | margin: 20px 10px;
30 | padding: 0;
31 |
32 | li {
33 | margin-left: 4px;
34 | }
35 | `;
36 |
--------------------------------------------------------------------------------
/src/router/PrivateRoute.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 |
3 | import { Navigate } from "react-router-dom";
4 | import { useSetRecoilState, useRecoilValue } from "recoil";
5 | import { loginModalState, isLoginState } from "@/recoil";
6 |
7 | const PrivateRoute = ({ element: RouteComponent, fallback: fallbackUrl }) => {
8 | const isLogined = useRecoilValue(isLoginState);
9 | const setLoginModalOpen = useSetRecoilState(loginModalState);
10 |
11 | useEffect(() => {
12 | !isLogined && setLoginModalOpen("require");
13 | });
14 |
15 | if (!isLogined) {
16 | return ;
17 | }
18 |
19 | return ;
20 | };
21 |
22 | export default PrivateRoute;
23 |
--------------------------------------------------------------------------------
/public/images/Icon.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/components/layouts/index.js:
--------------------------------------------------------------------------------
1 | export { default as CardGrid } from "./CardGrid";
2 | export { default as CardSlider } from "./CardSlider";
3 | export { default as CardSmallGrid } from "./CardSmallGrid";
4 | export { default as CardSmallSlider } from "./CardSmallSlider";
5 | export { default as CommentList } from "./CommentList";
6 | export { default as FeedGrid } from "./FeedGrid";
7 | export { default as FeedSmallGrid } from "./FeedSmallGrid";
8 | export { default as FeedSmallSlider } from "./FeedSmallSlider";
9 | export { default as Footer } from "./Footer";
10 | export { default as IntroSlider } from "./IntroSlider";
11 | // export { default as Navbar } from "./Navbar";
12 | export { default as SignUpModal } from "./SignUpModal";
13 |
--------------------------------------------------------------------------------
/src/containers/CommunityPage/index.js:
--------------------------------------------------------------------------------
1 | export { default as CommunityIntro } from "./CommunityIntro";
2 | export { default as CommunityCreate } from "./CommunityCreate";
3 | export { default as CommunityCreateButton } from "./CommunityCreateButton";
4 | export { default as CommunityDetail } from "./CommunityDetail";
5 | export { default as CommunityEditor } from "./CommunityEditor";
6 | export { default as CommunityFeedGrid } from "./CommunityFeedGrid";
7 | export { default as CommunitySlider } from "./CommunitySlider";
8 | export { default as CommunityUpdate } from "./CommunityUpdate";
9 | export { default as CommunityDetailComment } from "./CommunityDetailComment";
10 | export { default as CommunityNoContent } from "./CommunityNoContent";
11 |
--------------------------------------------------------------------------------
/src/containers/MyPage/index.js:
--------------------------------------------------------------------------------
1 | export { default as AuthForm } from "./AuthForm";
2 | export { default as AuthWait } from "./AuthWait";
3 | export { default as AuthComplete } from "./AuthComplete";
4 | export { default as MyComment } from "./MyComment";
5 | export { default as MyFeed } from "./MyFeed";
6 | export { default as MyInfo } from "./MyInfo";
7 | export { default as MyPageIntro } from "./MyPageIntro";
8 | export { default as MyPages } from "./MyPages";
9 | export { default as MyStudy } from "./MyStudy";
10 | export { default as ProfileImage } from "./ProfileImage";
11 | export { default as DeleteAccountModal } from "./DeleteAccountModal";
12 | export { default as DeleteAccountCompleteModal } from "./DeleteAccountCompleteModal";
13 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/ThumbnailUploader/ThumbnailUploader.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { Button } from "@/components";
3 |
4 | export const Layout = styled.div`
5 | display: flex;
6 | flex-direction: column;
7 | gap: 2rem;
8 | `;
9 |
10 | export const CropperBox = styled.div`
11 | position: fixed;
12 | top: 0;
13 | left: 0;
14 | right: 0;
15 | bottom: 0;
16 | z-index: 1;
17 | `;
18 |
19 | export const SubmitButton = styled(Button)`
20 | position: absolute;
21 | left: calc(50% - 90px);
22 | bottom: 18%;
23 |
24 | width: 180px;
25 | margin: auto;
26 | `;
27 |
28 | export const ImgBox = styled.div`
29 | img {
30 | max-width: 100%;
31 | border-radius: 8px;
32 | }
33 | `;
34 |
--------------------------------------------------------------------------------
/src/foundations/Icon/Icon.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Svg } from "./Icon.styled";
5 | import { icons } from "@/_shared";
6 |
7 | const Icon = ({ icon, block, ...props }) => {
8 | return (
9 |
21 | );
22 | };
23 |
24 | Icon.propTypes = {
25 | icon: PropTypes.string.isRequired,
26 | block: PropTypes.bool,
27 | };
28 |
29 | Icon.defaultProps = {
30 | block: false,
31 | };
32 |
33 | export default Icon;
34 |
--------------------------------------------------------------------------------
/src/components/patterns/Dropdown/DropdownSmall/DropdownSmall.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import DropdownSmall from "./";
4 | import { Background } from "@/foundations";
5 |
6 | export default {
7 | title: "Patterns/Dropdown/DropdownSmall",
8 | component: DropdownSmall,
9 | };
10 |
11 | export const Default = (args) => (
12 |
13 |
14 |
15 | );
16 |
17 | Default.args = {
18 | items: [
19 | {
20 | name: "edit",
21 | title: "수정",
22 | onClick: () => console.log("수정"),
23 | },
24 | {
25 | name: "delete",
26 | title: "삭제",
27 | onClick: () => console.log("삭제"),
28 | },
29 | ],
30 | };
31 |
--------------------------------------------------------------------------------
/src/components/layouts/CardSmallGrid/CardSmallGrid.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout, CardItemLink } from "./CardSmallGrid.styled";
5 | import { CardSmall } from "@/components";
6 |
7 | const CardSmallGrid = ({ data, theme, isLoading, ...props }) => {
8 | return (
9 |
10 | {data.map(({ url, ...props }, idx) => {
11 | return (
12 |
13 |
14 |
15 | );
16 | })}
17 |
18 | );
19 | };
20 |
21 | CardSmallGrid.propTypes = {
22 | list: PropTypes.arrayOf(Object),
23 | };
24 |
25 | export default CardSmallGrid;
26 |
--------------------------------------------------------------------------------
/src/pages/StudyPage/StudyUpdatePage.jsx:
--------------------------------------------------------------------------------
1 | import React, { Suspense, lazy } from "react";
2 |
3 | import { useRecoilValue } from "recoil";
4 | import { themeState } from "@/recoil/theme";
5 | import EmptyPage from "@/pages/CommonPage/EmptyPage";
6 |
7 | const StudyCreateIntro = lazy(() =>
8 | import("@/containers/StudyPage/StudyCreateIntro")
9 | );
10 | const StudyUpdateForm = lazy(() =>
11 | import("@/containers/StudyPage/StudyUpdateForm")
12 | );
13 | const StudyCreatePage = () => {
14 | const theme = useRecoilValue(themeState);
15 |
16 | return (
17 | }>
18 |
19 |
20 |
21 | );
22 | };
23 |
24 | export default StudyCreatePage;
25 |
--------------------------------------------------------------------------------
/src/api/getRefreshToken.js:
--------------------------------------------------------------------------------
1 | import Axios from "axios";
2 |
3 | const axiosInstance = Axios.create({
4 | baseURL: process.env.REACT_APP_API_URL,
5 | timeout: 30000,
6 | headers: {
7 | "Content-Type": "application/json",
8 | },
9 | });
10 |
11 | axiosInstance.interceptors.request.use((config) => {
12 | const token = JSON.parse(localStorage.getItem("ssafe_token"));
13 | if (token) {
14 | config.headers.Authorization = `Bearer ${token.refreshToken}`;
15 | }
16 | return config;
17 | });
18 |
19 | export const getRefreshToken = async () => {
20 | const res = await axiosInstance({
21 | url: `/users/refresh`,
22 | method: "put",
23 | });
24 | await localStorage.setItem("ssafe_token", JSON.stringify(res.data.data));
25 | return res.data;
26 | };
27 |
--------------------------------------------------------------------------------
/src/api/getUserProfile.js:
--------------------------------------------------------------------------------
1 | import { useQuery } from "react-query";
2 | import { axiosInstance } from "@/utils";
3 |
4 | export const GetUserProfile = () =>
5 | useQuery(["getUserProfile"], () => fetchData());
6 |
7 | const fetchData = async () => {
8 | const res = await axiosInstance({
9 | url: "/users/me",
10 | });
11 | return res.data;
12 | };
13 |
14 | export const UserProfileSelector = (user) => {
15 | const profileData = {
16 | authCheck: user.authCheck,
17 | campus: user.campus,
18 | email: user.email,
19 | likeJob: user.likeJob,
20 | nickname: user.nickname,
21 | ordinal: user.ordinal,
22 | profileImg: user.profileImg,
23 | realName: user.realName,
24 | userId: user.userId,
25 | };
26 | return profileData;
27 | };
28 |
--------------------------------------------------------------------------------
/src/components/commons/Checkbox/Checkbox.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout, Icon, Label } from "./Checkbox.styled";
5 |
6 | const THEME = {
7 | LIGHT: "light",
8 | DARK: "dark",
9 | };
10 |
11 | const Checkbox = ({ theme, label, ...props }) => {
12 | return (
13 |
14 |
15 |
16 |
17 |
18 | );
19 | };
20 |
21 | Checkbox.propTypes = {
22 | theme: PropTypes.oneOf(Object.values(THEME)),
23 | label: PropTypes.string,
24 | };
25 |
26 | Checkbox.defaultProps = {
27 | theme: THEME.LIGHT,
28 | label: "Label",
29 | };
30 |
31 | export default Checkbox;
32 |
--------------------------------------------------------------------------------
/src/components/patterns/Sort/Sort.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { colors, fontSize, animations } from "@/_shared";
3 |
4 | export const Layout = styled.div`
5 | display: flex;
6 | position: relative;
7 |
8 | .dropdown {
9 | top: 2rem;
10 | left: -1.4rem;
11 | z-index: 9999;
12 | animation: ${animations.dropdown} 0.3s cubic-bezier(0.3, 0, 0, 1);
13 | }
14 | `;
15 |
16 | export const Button = styled.div`
17 | display: flex;
18 | align-items: center;
19 | color: ${colors.gray500};
20 |
21 | cursor: pointer;
22 | `;
23 |
24 | export const Text = styled.div`
25 | width: 100%;
26 | margin-left: 8px;
27 |
28 | font-size: ${fontSize.sm};
29 | color: ${colors.gray500};
30 |
31 | user-select: none;
32 | `;
33 |
--------------------------------------------------------------------------------
/src/containers/JobPage/JobIntro/JobIntro.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout, Title, SubTitle } from "./JobIntro.styled";
5 |
6 | const THEME = {
7 | LIGHT: "light",
8 | DARK: "dark",
9 | };
10 |
11 | const JobIntro = ({ ...props }) => {
12 | return (
13 | <>
14 |
15 | 채용정보
16 |
17 | 열정 가득한 당신을 원하는 회사들을 모아봤어요.
18 |
19 |
20 | >
21 | );
22 | };
23 |
24 | JobIntro.propTypes = {
25 | theme: PropTypes.oneOf(Object.values(THEME)),
26 | data: PropTypes.arrayOf(Object),
27 | };
28 |
29 | JobIntro.defaultProps = {
30 | theme: THEME.LIGHT,
31 | };
32 |
33 | export default JobIntro;
34 |
--------------------------------------------------------------------------------
/src/containers/Common/index.js:
--------------------------------------------------------------------------------
1 | export { default as Footer } from "./Footer";
2 | export { default as Navbar } from "./Navbar";
3 | export { default as Notification } from "./Notification";
4 | export { default as ScrollToTop } from "./ScrollToTop";
5 | export { default as SignUpModal } from "./SignUpModal";
6 | export { default as AuthModal } from "./AuthModal";
7 | export { default as SmallModal } from "./SmallModal";
8 | export { default as SubmitModal } from "./SubmitModal";
9 | export { default as DeleteModal } from "./DeleteModal";
10 | export { default as DeleteAccountModal } from "../MyPage/DeleteAccountModal";
11 | export { default as DeleteAccountCompleteModal } from "../MyPage/DeleteAccountCompleteModal";
12 | export { default as ErrorBoundary } from "../Common/ErrorBoundary";
13 |
--------------------------------------------------------------------------------
/src/components/patterns/Toc/TocNav/TocNav.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { TocNav } from "./";
3 |
4 | export default {
5 | title: "Patterns/Toc/Navbar",
6 | component: TocNav,
7 | };
8 |
9 | const items = [
10 | {
11 | name: "info",
12 | title: "공고",
13 | number: null,
14 | },
15 | {
16 | name: "study",
17 | title: "스터디",
18 | number: 5,
19 | },
20 | {
21 | name: "comments",
22 | title: "댓글",
23 | number: 20,
24 | },
25 | ];
26 |
27 | export const Default = (args) => ;
28 |
29 | Default.args = {
30 | items,
31 | };
32 |
33 | export const AllTypes = () => (
34 | <>
35 |
36 |
37 |
38 | >
39 | );
40 |
--------------------------------------------------------------------------------
/src/_shared/animations/animations.js:
--------------------------------------------------------------------------------
1 | import { keyframes } from "styled-components";
2 | import { colors } from "../colors";
3 |
4 | const animations = {
5 | glow: keyframes`
6 | 0%, 100% { opacity: 1; }
7 | 50% { opacity: 0; }
8 | `,
9 | glowLight: keyframes`
10 | 0%, 100% { background: ${colors.gray100} }
11 | 50% { background: ${colors.gray25} }
12 | `,
13 | glowDark: keyframes`
14 | 0%, 100% { background: ${colors.gray700}}
15 | 50% { background: ${colors.gray900}}
16 | `,
17 | dropdown: keyframes`
18 | 0% { opacity: 0; transform: translateY(-2px); }
19 | `,
20 | modal: keyframes`
21 | 0% { opacity: 0; transform: translateY(10px); }
22 | `,
23 | appear: keyframes`
24 | 0% { opacity: 0;}
25 | `,
26 | };
27 |
28 | export default animations;
29 |
--------------------------------------------------------------------------------
/src/components/commons/Radio/Radio.jsx:
--------------------------------------------------------------------------------
1 | import React, { forwardRef } from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout, Icon, Label } from "./Radio.styled";
5 |
6 | const THEME = {
7 | LIGHT: "light",
8 | DARK: "dark",
9 | };
10 |
11 | const Radio = forwardRef(({ theme, label, ...props }, inputRef) => {
12 | return (
13 |
14 |
15 |
16 |
17 |
18 | );
19 | });
20 |
21 | Radio.propTypes = {
22 | theme: PropTypes.oneOf(Object.values(THEME)),
23 | label: PropTypes.string,
24 | };
25 |
26 | Radio.defaultProps = {
27 | theme: THEME.LIGHT,
28 | label: "Label",
29 | };
30 |
31 | export default Radio;
32 |
--------------------------------------------------------------------------------
/src/containers/CommunityPage/CommunityCreateButton/CommunityCreateButton.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout, Text } from "./CommunityCreateButton.styled";
5 | import { Icon } from "@/foundations";
6 |
7 | const THEME = {
8 | LIGHT: "light",
9 | DARK: "dark",
10 | };
11 |
12 | const CommunityCreateButton = ({ placeholder, icon, ...props }) => {
13 | return (
14 |
15 |
16 | 게시글을 작성하세요
17 |
18 | );
19 | };
20 |
21 | CommunityCreateButton.propTypes = {
22 | theme: PropTypes.oneOf(Object.values(THEME)),
23 | };
24 |
25 | CommunityCreateButton.defaultProps = {
26 | theme: THEME.LIGHT,
27 | };
28 |
29 | export default CommunityCreateButton;
30 |
--------------------------------------------------------------------------------
/.storybook/preview.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { MemoryRouter } from "react-router-dom";
4 | import { RecoilRoot } from "recoil";
5 | import { GlobalStyle } from "@/_shared/global";
6 | import "normalize.css/normalize.css";
7 |
8 | // 모든 스토리에 스타일을 적용하기 위한 글로벌 decorator
9 | export const decorators = [
10 | (Story) => (
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | ),
19 | ];
20 |
21 | export const parameters = {
22 | actions: { argTypesRegex: "^on[A-Z].*" },
23 | a11y: {
24 | element: "#root",
25 | manual: false,
26 | },
27 | options: {
28 | storySort: {
29 | order: ["Foundations", "Components", "Patterns", "Containers", "*"],
30 | },
31 | },
32 | };
33 |
--------------------------------------------------------------------------------
/src/components/patterns/Comment/Comment.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import Comment from "./";
4 | import { Background } from "@/foundations";
5 |
6 | import { daysFromToday } from "@/utils";
7 |
8 | export default {
9 | title: "Patterns/Comment",
10 | component: Comment,
11 | };
12 |
13 | export const Default = (args) => ;
14 |
15 | Default.args = {
16 | username: "",
17 | src: "",
18 | created: daysFromToday("2022-01-24"),
19 | content: "Lorem ipsum dolor sit amet, consectetur adipiscing elit",
20 | };
21 |
22 | export const AllTypes = () => (
23 | <>
24 |
25 |
26 |
27 |
28 |
29 |
30 | >
31 | );
32 |
--------------------------------------------------------------------------------
/src/containers/MyPage/MyPages/MyPages.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { SubNavbar } from "@/components";
3 |
4 | export const Layout = styled.section`
5 | padding: 20px;
6 | `;
7 |
8 | export const MainBox = styled.div`
9 | display: flex;
10 | gap: 100px;
11 |
12 | max-width: 1280px;
13 | width: 100%;
14 | margin: auto;
15 | padding: 0 0 4rem 0;
16 | `;
17 |
18 | export const MobileBox = styled.div`
19 | max-width: 1280px;
20 | width: 100%;
21 | padding: 0 0 4rem 0;
22 | `;
23 |
24 | export const ContentBox = styled.section`
25 | width: 100%;
26 | margin-right: 20px;
27 | `;
28 |
29 | export const StickyNavBox = styled.nav`
30 | padding-top: 86px;
31 | `;
32 |
33 | export const StickyNav = styled(SubNavbar)`
34 | position: sticky;
35 | top: 150px;
36 | `;
37 |
--------------------------------------------------------------------------------
/src/_shared/colors/colors.js:
--------------------------------------------------------------------------------
1 | const colors = {
2 | blue50: "#A5C1F7",
3 | blue150: "#4A83EF",
4 | blue100: "#4A83EF",
5 | blue200: "#3669CD",
6 |
7 | blueOpacity50: "#4A83EF22",
8 | blueOpacity100: "#4A83EF33",
9 | blueOpacity200: "#4A83EF77",
10 | blueOpacity300: "#4A83EF9e",
11 |
12 | white: "#FFFFFF",
13 | black: "#1B1F24",
14 |
15 | gray25: "#F8F9FA",
16 | gray50: "#F1F3F5",
17 | gray100: "#E9ECEF",
18 | gray200: "#DEE2E6",
19 | gray300: "#CED4DA",
20 | gray400: "#ADB5BD",
21 | gray500: "#868E96",
22 | gray600: "#4A5056",
23 | gray700: "#3D4248",
24 | gray800: "#31363B",
25 | gray900: "#292D32",
26 |
27 | error: "#F04438",
28 | errorOpacity100: "#F8736A33",
29 | errorOpacity200: "#F04438CF",
30 |
31 | transparent: "#00000000",
32 | };
33 |
34 | export default colors;
35 |
--------------------------------------------------------------------------------
/src/components/patterns/CardSmallCreate/CardSmallCreate.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import CardSmallCreate from ".";
4 | import { Background } from "@/foundations";
5 |
6 | export default {
7 | title: "Patterns/CardSmallCreate",
8 | component: CardSmallCreate,
9 | };
10 |
11 | export const Default = (args) => ;
12 |
13 | const defaultData = {
14 | companyData: {
15 | companyName: "",
16 | },
17 | content: "스터디 만들기",
18 | };
19 |
20 | Default.args = defaultData;
21 |
22 | export const AllTypes = () => (
23 | <>
24 |
25 |
26 |
27 |
28 |
29 |
30 | >
31 | );
32 |
--------------------------------------------------------------------------------
/src/components/patterns/Dropdown/DropdownItem/DropdownItem.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { colors, fontSize, fontWeight } from "@/_shared";
3 |
4 | const textColor = {
5 | dark: colors.gray25,
6 | light: colors.gray900,
7 | };
8 |
9 | const hoverColor = {
10 | dark: colors.gray700,
11 | light: colors.gray100,
12 | };
13 |
14 | export const Layout = styled.li`
15 | display: flex;
16 | align-items: center;
17 |
18 | height: 36px;
19 | padding: 0 16px;
20 |
21 | color: ${(props) => textColor[props.theme]};
22 | font-size: ${fontSize.sm};
23 | font-weight: ${fontWeight.regular};
24 | list-style: none;
25 |
26 | cursor: pointer;
27 | user-select: none;
28 | transition: 0.2s;
29 |
30 | :hover {
31 | background-color: ${(props) => hoverColor[props.theme]};
32 | }
33 | `;
34 |
--------------------------------------------------------------------------------
/src/containers/MainPage/MainCommunitySlider/MainCommunitySlider.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { FeedSmallSlider } from "@/components";
4 | import { GetCommunityList } from "@/api";
5 |
6 | const MainCommunitySlider = ({ isLoading, ...props }) => {
7 | const search = {
8 | boardType: 1,
9 | title: "",
10 | criteria: {
11 | label: "좋아요순",
12 | value: "like",
13 | },
14 | };
15 |
16 | const { data } = GetCommunityList(search);
17 | const communityData = data.pages[0].res.map((card) => {
18 | return {
19 | ...card,
20 | writerInfo: {
21 | ...card.writerInfo,
22 | ordinal: `${card.writerInfo.ordinal}기`,
23 | },
24 | };
25 | });
26 | return ;
27 | };
28 |
29 | export default MainCommunitySlider;
30 |
--------------------------------------------------------------------------------
/src/containers/MyPage/ProfileImage/ProfileImage.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 |
3 | export const Layout = styled.div`
4 | display: flex;
5 | flex-direction: column;
6 | gap: 1rem;
7 |
8 | @media screen and (max-width: 530px) {
9 | flex-direction: column;
10 | }
11 | `;
12 |
13 | export const CropperBox = styled.div`
14 | position: fixed;
15 | top: 68px;
16 | left: 0;
17 | right: 0;
18 | bottom: 0;
19 | z-index: 1;
20 | `;
21 |
22 | export const ButtonBox = styled.div`
23 | display: flex;
24 | gap: 1rem;
25 | position: absolute;
26 | left: calc(50% - 115px);
27 | bottom: 10%;
28 | margin: auto;
29 | `;
30 |
31 | export const ImgBox = styled.div`
32 | display: flex;
33 | align-items: center;
34 | gap: 2rem;
35 |
36 | img {
37 | border-radius: 8px;
38 | }
39 | `;
40 |
--------------------------------------------------------------------------------
/craco.config.js:
--------------------------------------------------------------------------------
1 | const CracoAlias = require("craco-alias");
2 | const BundleAnalyzerPlugin =
3 | require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
4 |
5 | module.exports = {
6 | plugins: [
7 | {
8 | plugin: CracoAlias,
9 | options: {
10 | source: "jsconfig",
11 | jsConfigPath: "jsconfig.paths.json",
12 | },
13 | },
14 | ],
15 | webpack: {
16 | // 번들 확인
17 | // plugins: [new BundleAnalyzerPlugin()],
18 | optimization: {
19 | splitChunks: {
20 | cacheGroups: {
21 | // vendor 코드 (dependencies)들을 다른 chunk에 분리하기
22 | vendors: {
23 | test: /[\\/]node_modules[\\/]/i,
24 | chunks: "all",
25 | },
26 | },
27 | },
28 | // 런타임 chunk
29 | runtimeChunk: { name: "runtime" },
30 | },
31 | },
32 | };
33 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyCreateIntro/StudyCreateIntro.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout, Title, SubTitle } from "./StudyCreateIntro.styled";
5 |
6 | const THEME = {
7 | LIGHT: "light",
8 | DARK: "dark",
9 | };
10 |
11 | const StudyCreateIntro = ({ ...props }) => {
12 | return (
13 | <>
14 |
15 | 스터디 모집
16 |
17 | 스터디를 직접 개설하고 동료들을 모아보세요
18 |
19 |
20 | >
21 | );
22 | };
23 |
24 | StudyCreateIntro.propTypes = {
25 | theme: PropTypes.oneOf(Object.values(THEME)),
26 | data: PropTypes.arrayOf(Object),
27 | };
28 |
29 | StudyCreateIntro.defaultProps = {
30 | theme: THEME.LIGHT,
31 | };
32 |
33 | export default StudyCreateIntro;
34 |
--------------------------------------------------------------------------------
/src/containers/LandingPage/LandingCTASection/LandingCTASection.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { useSetRecoilState } from "recoil";
4 | import { loginModalState } from "@/recoil";
5 |
6 | import {
7 | Layout,
8 | ContentBox,
9 | Text,
10 | GetStartedBtn,
11 | } from "./LandingCTASection.styled";
12 |
13 | const LandingCTASection = ({ text, isButton, ...props }) => {
14 | const setLoginModalOpen = useSetRecoilState(loginModalState);
15 | return (
16 |
17 |
18 | {text}
19 | {isButton && (
20 | setLoginModalOpen(true)}>
21 | 시작하기
22 |
23 | )}
24 |
25 |
26 | );
27 | };
28 |
29 | export default LandingCTASection;
30 |
--------------------------------------------------------------------------------
/src/foundations/IconSocial/IconSocial.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Svg } from "./IconSocial.styled";
5 | import { iconsSocial } from "@/_shared";
6 |
7 | const SOCIAL = {
8 | GOOGLE: "google",
9 | GITHUB: "github",
10 | };
11 |
12 | const IconSocial = ({ icon, block, ...props }) => {
13 | return (
14 |
25 | );
26 | };
27 |
28 | IconSocial.propTypes = {
29 | icon: PropTypes.oneOf(Object.values(SOCIAL)),
30 | block: PropTypes.bool,
31 | };
32 |
33 | IconSocial.defaultProps = {
34 | block: false,
35 | };
36 |
37 | export default IconSocial;
38 |
--------------------------------------------------------------------------------
/src/containers/MyPage/MyComment/MyComment.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { colors, fontSize, lineHeight, fontWeight } from "@/_shared";
3 |
4 | const titleColor = {
5 | light: colors.gray900,
6 | dark: colors.gray25,
7 | };
8 |
9 | const subtitleColor = {
10 | light: colors.gray400,
11 | dark: colors.gray500,
12 | };
13 |
14 | export const Layout = styled.div`
15 | display: flex;
16 | flex-direction: column;
17 | padding-top: 86px;
18 | `;
19 |
20 | export const Title = styled.div`
21 | line-height: ${lineHeight.h2};
22 | margin-bottom: 0.5rem;
23 |
24 | font-weight: ${fontWeight.bold};
25 | font-size: ${fontSize.h3};
26 | color: ${(props) => titleColor[props.theme]};
27 | `;
28 |
29 | export const SubTitle = styled.div`
30 | font-size: ${fontSize.p};
31 | color: ${(props) => subtitleColor[props.theme]};
32 | `;
33 |
--------------------------------------------------------------------------------
/src/components/patterns/Sort/Sort.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Sort from ".";
3 | import { Background } from "@/foundations";
4 |
5 | export default {
6 | title: "Patterns/Sort",
7 | component: Sort,
8 | };
9 |
10 | const sampleData = {
11 | items: [
12 | {
13 | name: "new",
14 | title: "최신순",
15 | },
16 | {
17 | name: "popular",
18 | title: "인기순",
19 | },
20 | ],
21 | };
22 |
23 | export const Default = (args) => (
24 |
25 |
26 |
27 | );
28 |
29 | Default.args = {
30 | ...sampleData,
31 | };
32 |
33 | export const AllTypes = () => (
34 | <>
35 |
36 |
37 |
38 |
39 |
40 |
41 | >
42 | );
43 |
--------------------------------------------------------------------------------
/src/containers/CommunityPage/CommunityIntro/CommunityIntro.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout, Title, SubTitle } from "./CommunityIntro.styled";
5 | import { Button } from "@/components";
6 |
7 | const THEME = {
8 | LIGHT: "light",
9 | DARK: "dark",
10 | };
11 |
12 | const CommunityIntro = ({ ...props }) => {
13 | return (
14 | <>
15 |
16 | 커뮤니티
17 | 싸피인들끼리 자유롭게 소통하세요
18 |
19 |
20 | >
21 | );
22 | };
23 |
24 | CommunityIntro.propTypes = {
25 | theme: PropTypes.oneOf(Object.values(THEME)),
26 | data: PropTypes.arrayOf(Object),
27 | };
28 |
29 | CommunityIntro.defaultProps = {
30 | theme: THEME.LIGHT,
31 | };
32 |
33 | export default CommunityIntro;
34 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ssafé
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/components/patterns/Notification/NotificationItem/NotificationItem.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { NotificationItem } from "./";
4 | import { Background } from "@/foundations";
5 |
6 | export default {
7 | title: "Patterns/Notification/NotificationItem",
8 | component: NotificationItem,
9 | };
10 |
11 | export const Default = (args) => (
12 |
13 |
14 |
15 | );
16 |
17 | Default.args = {
18 | children: "싸피 인증이 승인되었어요. 이제 모든 서비스를 이용해보세요!",
19 | };
20 |
21 | export const AllTypes = () => (
22 | <>
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | >
32 | );
33 |
--------------------------------------------------------------------------------
/src/components/patterns/SubNavbar/SubNavbarItem/SubNavbarItem.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout } from "./SubNavbarItem.styled";
5 |
6 | const THEME = {
7 | LIGHT: "light",
8 | DARK: "dark",
9 | };
10 |
11 | const STATUS = {
12 | DEFAULT: "default",
13 | ACTIVE: "active",
14 | };
15 |
16 | const SubNavbarItem = ({ children, onClick, ...props }) => {
17 | return (
18 | onClick()} {...props}>
19 | {children}
20 |
21 | );
22 | };
23 |
24 | SubNavbarItem.propTypes = {
25 | children: PropTypes.node.isRequired,
26 | theme: PropTypes.oneOf(Object.values(THEME)),
27 | status: PropTypes.oneOf(Object.values(STATUS)),
28 | onClick: PropTypes.func,
29 | };
30 |
31 | SubNavbarItem.defaultProps = {
32 | theme: THEME.LIGHT,
33 | status: STATUS.DEFAULT,
34 | };
35 |
36 | export default SubNavbarItem;
37 |
--------------------------------------------------------------------------------
/src/components/commons/Switch/Switch.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import Switch from "./";
4 | import { Background } from "@/foundations";
5 |
6 | export default {
7 | title: "Components/Switch",
8 | component: Switch,
9 | };
10 |
11 | const onToggle = () => {
12 | // console.log("toggle");
13 | };
14 |
15 | export const Default = (args) => (
16 |
17 |
18 |
19 | );
20 |
21 | export const AllTypes = () => (
22 | <>
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | >
35 | );
36 |
--------------------------------------------------------------------------------
/src/recoil/modal.js:
--------------------------------------------------------------------------------
1 | import { atom } from "recoil";
2 |
3 | export const modalState = atom({
4 | key: "modalState",
5 | default: false,
6 | });
7 |
8 | export const smallModalState = atom({
9 | key: "smallModalState",
10 | default: false,
11 | });
12 |
13 | export const submitModalState = atom({
14 | key: "submitModalState",
15 | default: false,
16 | });
17 |
18 | export const loginModalState = atom({
19 | key: "loginModalState",
20 | default: null,
21 | });
22 |
23 | export const createModalState = atom({
24 | key: "createModalState",
25 | default: false,
26 | });
27 |
28 | export const updateModalState = atom({
29 | key: "updateModalState",
30 | default: null,
31 | });
32 |
33 | export const deleteModalState = atom({
34 | key: "deleteModalState",
35 | default: null,
36 | });
37 |
38 | export const deleteAccountModalState = atom({
39 | key: "deleteAccountModalState",
40 | default: true,
41 | });
42 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyIntro/StudyIntro.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout, Title, SubTitle, ButtonLink } from "./StudyIntro.styled";
5 | import { Button } from "@/components";
6 |
7 | const THEME = {
8 | LIGHT: "light",
9 | DARK: "dark",
10 | };
11 |
12 | const StudyIntro = ({ ...props }) => {
13 | return (
14 | <>
15 |
16 | 스터디 모집
17 | 함께 성장할 동료들을 찾아보세요.
18 |
19 |
20 |
21 |
22 | >
23 | );
24 | };
25 |
26 | StudyIntro.propTypes = {
27 | theme: PropTypes.oneOf(Object.values(THEME)),
28 | data: PropTypes.arrayOf(Object),
29 | };
30 |
31 | StudyIntro.defaultProps = {
32 | theme: THEME.LIGHT,
33 | };
34 |
35 | export default StudyIntro;
36 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: "Pretendard Variable";
3 | font-weight: 45 920;
4 | font-style: normal;
5 | font-display: swap;
6 | src: local("Pretendard Variable"),
7 | url("./assets/PretendardVariable.woff2") format("woff2-variations");
8 | }
9 |
10 | body {
11 | margin: 0;
12 | font-family: "Pretendard Variable", -apple-system, BlinkMacSystemFont,
13 | system-ui, Roboto, "Helvetica Neue", "Segoe UI", "Apple SD Gothic Neo",
14 | "Noto Sans KR", "Malgun Gothic", sans-serif;
15 | font-weight: 400;
16 | -webkit-font-smoothing: antialiased;
17 | -moz-osx-font-smoothing: grayscale;
18 | letter-spacing: -0.02rem;
19 | }
20 |
21 | div {
22 | box-sizing: border-box;
23 | }
24 |
25 | code {
26 | font-family: "Pretendard Variable", -apple-system, BlinkMacSystemFont,
27 | system-ui, Roboto, "Helvetica Neue", "Segoe UI", "Apple SD Gothic Neo",
28 | "Noto Sans KR", "Malgun Gothic", sans-serif;
29 | }
30 |
--------------------------------------------------------------------------------
/src/pages/CommunityPage/CommunityDetailPage.jsx:
--------------------------------------------------------------------------------
1 | import React, { Suspense } from "react";
2 | import styled from "styled-components";
3 |
4 | import { useRecoilValue } from "recoil";
5 | import { themeState } from "@/recoil/theme";
6 |
7 | import { LoadingCommunityDetail } from "@/containers/Loading";
8 | import { CommunityDetail } from "@/containers/CommunityPage";
9 |
10 | const CommunityDetailPage = ({ match }) => {
11 | const theme = useRecoilValue(themeState);
12 | return (
13 |
14 | }>
15 |
16 |
17 |
18 | );
19 | };
20 |
21 | export default CommunityDetailPage;
22 |
23 | const Layout = styled.div`
24 | max-width: 940px;
25 | padding: 160px 20px 20px 20px;
26 | margin: auto;
27 |
28 | @media screen and (max-width: 530px) {
29 | padding: 120px 20px 20px 20px;
30 | }
31 | `;
32 |
--------------------------------------------------------------------------------
/src/components/patterns/SubNavbar/SubNavbarItem/SubNavbarItem.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { SubNavbarItem } from "./";
3 |
4 | export default {
5 | title: "Patterns/SubNavbar/SubNavbarItem",
6 | component: SubNavbarItem,
7 | };
8 |
9 | export const Default = (args) => (
10 | 사이드바 아이템
11 | );
12 |
13 | Default.args = {};
14 |
15 | export const AllTypes = (args) => (
16 | <>
17 | Light Sub Navbar Item
18 |
19 | 사이드바 아이템
20 |
21 | 사이드바 아이템
22 |
23 |
24 | Dark Sub Navbar Item
25 |
26 |
27 |
28 | 사이드바 아이템
29 |
30 |
31 |
32 | 사이드바 아이템
33 |
34 | >
35 | );
36 |
--------------------------------------------------------------------------------
/src/components/patterns/Notification/Notification.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import Notification from ".";
4 | import { Background } from "@/foundations";
5 |
6 | export default {
7 | title: "Patterns/Notification",
8 | component: Notification,
9 | };
10 |
11 | export const Default = (args) => ;
12 |
13 | Default.args = {
14 | data: [
15 | {
16 | message:
17 | "축하드려요! ✨ \n SSAFY 인증이 완료되었어요. \n\n 재로그인 하시면 모든 서비스를 이용할 수 있어요 :)",
18 | createdDate: new Date(),
19 | status: "new",
20 | },
21 | {
22 | message: "처음 오신 것을 환영해요! ✨",
23 | createdDate: new Date(),
24 | status: "default",
25 | },
26 | ],
27 | };
28 |
29 | export const AllType = () => (
30 | <>
31 |
32 |
33 |
34 |
35 |
36 |
37 | >
38 | );
39 |
--------------------------------------------------------------------------------
/src/components/patterns/Toc/TocItem/TocItem.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Text, Number, Layout } from "./TocItem.styled";
5 |
6 | const THEME = {
7 | DARK: "dark",
8 | LIGHT: "light",
9 | };
10 |
11 | const STATUS = {
12 | DEFAULT: "default",
13 | ACTIVE: "active",
14 | };
15 |
16 | const TocItem = ({ children, number, ...props }) => {
17 | return (
18 |
19 |
20 | {children}
21 |
22 | {number}
23 |
24 | );
25 | };
26 |
27 | TocItem.propTypes = {
28 | status: PropTypes.oneOf(Object.values(STATUS)),
29 | theme: PropTypes.oneOf(Object.values(THEME)),
30 | children: PropTypes.node.isRequired,
31 | };
32 |
33 | TocItem.defaultProps = {
34 | theme: THEME.LIGHT,
35 | status: STATUS.DEFAULT,
36 | children: "메뉴",
37 | number: "n",
38 | };
39 |
40 | export default TocItem;
41 |
--------------------------------------------------------------------------------
/src/containers/MyPage/MyFeed/MyFeed.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { colors, fontSize, lineHeight, fontWeight } from "@/_shared";
3 |
4 | const titleColor = {
5 | light: colors.gray900,
6 | dark: colors.gray25,
7 | };
8 |
9 | const subtitleColor = {
10 | light: colors.gray400,
11 | dark: colors.gray500,
12 | };
13 |
14 | export const Layout = styled.div`
15 | display: flex;
16 | flex-direction: column;
17 | padding-top: 86px;
18 | `;
19 |
20 | export const Title = styled.h1`
21 | line-height: ${lineHeight.h2};
22 | margin: 0 0 0.5rem 0;
23 |
24 | font-weight: ${fontWeight.bold};
25 | font-size: ${fontSize.h3};
26 | color: ${(props) => titleColor[props.theme]};
27 | `;
28 |
29 | export const SubTitle = styled.p`
30 | margin: 0 0 3rem 0;
31 |
32 | font-size: ${fontSize.p};
33 | color: ${(props) => subtitleColor[props.theme]};
34 | `;
35 |
36 | export const FetchBox = styled.div`
37 | height: 30px;
38 | `;
39 |
--------------------------------------------------------------------------------
/src/containers/MyPage/MyStudy/MyStudy.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { colors, fontSize, lineHeight, fontWeight } from "@/_shared";
3 |
4 | const titleColor = {
5 | light: colors.gray900,
6 | dark: colors.gray25,
7 | };
8 |
9 | const subtitleColor = {
10 | light: colors.gray400,
11 | dark: colors.gray500,
12 | };
13 |
14 | export const Layout = styled.div`
15 | display: flex;
16 | flex-direction: column;
17 | padding-top: 86px;
18 | `;
19 |
20 | export const Title = styled.h1`
21 | line-height: ${lineHeight.h2};
22 | margin: 0 0 0.5rem 0;
23 |
24 | font-weight: ${fontWeight.bold};
25 | font-size: ${fontSize.h3};
26 | color: ${(props) => titleColor[props.theme]};
27 | `;
28 |
29 | export const SubTitle = styled.p`
30 | margin: 0 0 3rem 0;
31 |
32 | font-size: ${fontSize.p};
33 | color: ${(props) => subtitleColor[props.theme]};
34 | `;
35 |
36 | export const FetchBox = styled.div`
37 | height: 30px;
38 | `;
39 |
--------------------------------------------------------------------------------
/src/components/commons/Radio/Radio.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { colors, fontSize } from "@/_shared";
3 |
4 | const borderColor = {
5 | light: colors.gray300,
6 | dark: colors.gray600,
7 | };
8 |
9 | export const Layout = styled.label`
10 | display: flex;
11 | align-content: center;
12 | gap: 0.5rem;
13 |
14 | input {
15 | display: none;
16 | }
17 |
18 | input:checked + .icon {
19 | border: 5px solid ${colors.blue100};
20 | }
21 | `;
22 |
23 | export const Icon = styled.div`
24 | position: relative;
25 | flex-shrink: 0;
26 |
27 | width: 18px;
28 | height: 18px;
29 | border: 1px solid ${(props) => borderColor[props.theme]};
30 | border-radius: 50%;
31 |
32 | background-color: transparent;
33 |
34 | cursor: pointer;
35 | transition: 0.2s;
36 | `;
37 |
38 | export const Label = styled.span`
39 | color: ${colors.gray500};
40 | font-size: ${fontSize.sm};
41 | line-height: 18px;
42 | user-select: none;
43 | `;
44 |
--------------------------------------------------------------------------------
/src/components/patterns/Toc/Toc.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import Toc from "./";
4 | import { Background } from "@/foundations";
5 |
6 | export default {
7 | title: "Patterns/Toc",
8 | component: Toc,
9 | };
10 |
11 | const items = [
12 | {
13 | name: "info",
14 | title: "공고",
15 | number: null,
16 | },
17 | {
18 | name: "study",
19 | title: "스터디",
20 | number: 5,
21 | },
22 | {
23 | name: "comments",
24 | title: "댓글",
25 | number: 20,
26 | },
27 | ];
28 |
29 | export const Default = (args) => (
30 |
31 |
32 |
33 | );
34 |
35 | Default.args = {
36 | items,
37 | };
38 |
39 | export const AllTypes = () => (
40 | <>
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | >
49 | );
50 |
--------------------------------------------------------------------------------
/src/components/patterns/Dropdown/DropdownSmall/DropdownSmall.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { colors, shadows } from "@/_shared";
3 |
4 | const borderColor = {
5 | dark: colors.gray800,
6 | light: colors.gray100,
7 | };
8 |
9 | const bgColor = {
10 | dark: colors.gray900,
11 | light: colors.white,
12 | };
13 |
14 | const boxShadow = {
15 | dark: "2px 2px 4px rgba(0, 0, 0, 0.185)",
16 | light: shadows.base,
17 | };
18 |
19 | export const Layout = styled.div`
20 | display: flex;
21 | flex-direction: column;
22 | align-items: stretch;
23 | position: absolute;
24 |
25 | min-width: 100px;
26 | border-radius: 8px;
27 | border: 1px solid ${(props) => borderColor[props.theme]};
28 |
29 | background-color: ${(props) => bgColor[props.theme]};
30 | box-shadow: ${(props) => boxShadow[props.theme]};
31 | `;
32 |
33 | export const MenuBox = styled.ul`
34 | margin: 0;
35 | padding: 4px 0;
36 |
37 | > li {
38 | justify-content: center;
39 | padding: 0;
40 | }
41 | `;
42 |
--------------------------------------------------------------------------------
/src/containers/JobPage/JobMain/JobMain.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { SubNavbar } from "@/components";
3 |
4 | export const Layout = styled.section`
5 | display: flex;
6 | justify-content: space-between;
7 |
8 | max-width: 1280px;
9 | padding: 0 20px 20px 20px;
10 | margin: auto;
11 | `;
12 |
13 | export const StickyNavBox = styled.nav`
14 | padding-top: 86px;
15 | `;
16 |
17 | export const StickyNav = styled(SubNavbar)`
18 | position: sticky;
19 | top: 150px;
20 | `;
21 |
22 | export const CardGridBox = styled.div`
23 | width: calc(100% - 300px);
24 | padding-top: 80px;
25 | `;
26 |
27 | export const InputBox = styled.div`
28 | display: flex;
29 | gap: 0.5rem;
30 |
31 | max-width: 940px;
32 |
33 | > div {
34 | flex-grow: 1;
35 | }
36 | `;
37 |
38 | export const SortBox = styled.div`
39 | display: flex;
40 | justify-content: space-between;
41 | align-items: center;
42 |
43 | max-width: 940px;
44 |
45 | padding: 20px 0px 20px 0;
46 | margin-bottom: 20px;
47 | `;
48 |
--------------------------------------------------------------------------------
/src/components/patterns/FeedItemSmall/FeedItemSmall.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import FeedItemSmall from ".";
4 | import { Background } from "@/foundations";
5 |
6 | export default {
7 | title: "Patterns/FeedItemSmall",
8 | component: FeedItemSmall,
9 | };
10 |
11 | const data = {
12 | boardId: 1,
13 | colorIdx: 1,
14 | writerInfo: {
15 | nickname: "아이유",
16 | ordinal: "6기",
17 | campus: "서울",
18 | authCheck: 0,
19 | avatar:
20 | "https://image.bada.io/files//crawling/2021/04/05/bobaedream/1612930_i14788674553.jpg",
21 | },
22 | title: "더 좋은 커뮤니티를 만들기 위한 약속 🤙",
23 | category: "공지",
24 | };
25 |
26 | export const Default = (args) => ;
27 |
28 | Default.args = { ...data };
29 |
30 | export const AllTypes = () => (
31 | <>
32 |
33 |
34 |
35 |
36 |
37 |
38 | >
39 | );
40 |
--------------------------------------------------------------------------------
/src/components/patterns/Sidebar/SideBar.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Sidebar from ".";
3 |
4 | export default {
5 | title: "Patterns/Sidebar",
6 | component: Sidebar,
7 | };
8 |
9 | const sampleData = {
10 | contents: [
11 | {
12 | title: "직무",
13 | description: "프론트엔드",
14 | icon: "briefcase",
15 | },
16 | {
17 | title: "고용형태",
18 | description: "정규직",
19 | icon: "building",
20 | },
21 | {
22 | title: "경력",
23 | description: "신입",
24 | icon: "monitor",
25 | },
26 | {
27 | title: "근무위치",
28 | description: "서울 강남구",
29 | icon: "mapPin",
30 | },
31 | ],
32 | badges: ["JavaScript", "TypeScript", "Vue.js", "React", "Redux", "Svelte"],
33 | };
34 |
35 | export const Default = (args) => ;
36 |
37 | Default.args = {
38 | ...sampleData,
39 | };
40 |
41 | export const AllTypes = () => (
42 | <>
43 |
44 |
45 | >
46 | );
47 |
--------------------------------------------------------------------------------
/src/components/commons/Button/Button.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout } from "./Button.styled";
5 |
6 | const THEME = {
7 | LIGHT: "light",
8 | DARK: "dark",
9 | };
10 |
11 | const MODE = {
12 | PRIMARY: "primary",
13 | SECONDARY: "secondary",
14 | TRANSPARENT: "transparent",
15 | ACTIVE: "active",
16 | };
17 |
18 | const Button = ({ isLoading, children, ...props }) => {
19 | const buttonInner = isLoading || children;
20 |
21 | return (
22 |
23 | {buttonInner}
24 |
25 | );
26 | };
27 |
28 | Button.propTypes = {
29 | children: PropTypes.node,
30 | theme: PropTypes.oneOf(Object.values(THEME)),
31 | mode: PropTypes.oneOf(Object.values(MODE)),
32 | isLoading: PropTypes.bool,
33 | isUnclickable: PropTypes.bool,
34 | };
35 |
36 | Button.defaultProps = {
37 | theme: THEME.LIGHT,
38 | mode: MODE.PRIMARY,
39 | isLoading: false,
40 | isUnclickable: false,
41 | };
42 |
43 | export default Button;
44 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyMain/StudyMain.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { SubNavbar } from "@/components";
3 |
4 | export const Layout = styled.section`
5 | display: flex;
6 | justify-content: space-between;
7 |
8 | max-width: 1280px;
9 | padding: 0 20px 20px 20px;
10 | margin: auto;
11 | `;
12 |
13 | export const StickyNavBox = styled.nav`
14 | padding-top: 86px;
15 | `;
16 |
17 | export const StickyNav = styled(SubNavbar)`
18 | position: sticky;
19 | top: 150px;
20 | `;
21 |
22 | export const CardGridBox = styled.section`
23 | width: calc(100% - 300px);
24 | padding-top: 80px;
25 | `;
26 |
27 | export const InputBox = styled.div`
28 | display: flex;
29 | gap: 0.5rem;
30 |
31 | max-width: 940px;
32 |
33 | > div {
34 | flex-grow: 1;
35 | }
36 | `;
37 |
38 | export const SortBox = styled.div`
39 | display: flex;
40 | justify-content: space-between;
41 | align-items: center;
42 |
43 | max-width: 940px;
44 |
45 | padding: 20px 0px 20px 0;
46 | margin-bottom: 20px;
47 | `;
48 |
--------------------------------------------------------------------------------
/src/components/commons/Badge/Badge.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout, Text } from "./Badge.styled";
5 |
6 | const THEME = {
7 | DARK: "dark",
8 | LIGHT: "light",
9 | };
10 |
11 | const MODE = {
12 | PRIMARY: "primary",
13 | SECONDARY: "secondary",
14 | };
15 |
16 | const Badge = ({ children, isLoading, ...props }) => {
17 | const badgeinner = isLoading ? null : children;
18 | return (
19 |
20 |
21 | {badgeinner}
22 |
23 |
24 | );
25 | };
26 |
27 | Badge.propTypes = {
28 | children: PropTypes.node.isRequired,
29 | theme: PropTypes.oneOf(Object.values(THEME)),
30 | mode: PropTypes.oneOf(Object.values(MODE)),
31 | isBold: PropTypes.bool,
32 | isLoading: PropTypes.bool,
33 | };
34 |
35 | Badge.defaultProps = {
36 | theme: THEME.LIGHT,
37 | mode: MODE.PRIMARY,
38 | isBold: false,
39 | isLoading: false,
40 | };
41 |
42 | export default Badge;
43 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyDetailComment/StudyDetailComment.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { colors, fontSize, lineHeight, fontWeight } from "@/_shared";
3 |
4 | const titleColor = {
5 | light: colors.gray900,
6 | dark: colors.gray25,
7 | };
8 |
9 | const subtitleColor = {
10 | light: colors.gray400,
11 | dark: colors.gray500,
12 | };
13 |
14 | const borderColor = {
15 | dark: colors.gray700,
16 | light: colors.gray200,
17 | };
18 |
19 | export const CommentBox = styled.section``;
20 |
21 | export const BoxTitle = styled.h2`
22 | min-height: ${lineHeight.h3};
23 | padding: 4rem 0 0.2rem 0;
24 | margin: 0;
25 |
26 | border-top: 1px solid ${(props) => borderColor[props.theme]};
27 | color: ${(props) => titleColor[props.theme]};
28 |
29 | font-weight: ${fontWeight.bold};
30 | font-size: ${fontSize.h3};
31 | `;
32 |
33 | export const BoxDescription = styled.p`
34 | padding-bottom: 2rem;
35 | margin: 0;
36 | color: ${(props) => subtitleColor[props.theme]};
37 | font-size: ${fontSize.p};
38 | `;
39 |
--------------------------------------------------------------------------------
/src/foundations/Logo/Logo.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { colors } from "@/_shared";
3 |
4 | const symbolColors = {
5 | dark: {
6 | default: colors.blue100,
7 | transparent: colors.blue100,
8 | },
9 | blue: {
10 | default: colors.white,
11 | transparent: colors.blue100,
12 | },
13 | light: {
14 | default: colors.blue100,
15 | transparent: colors.blue100,
16 | },
17 | };
18 |
19 | const wordColors = {
20 | dark: {
21 | default: colors.gray25,
22 | transparent: colors.gray25,
23 | },
24 | blue: {
25 | default: colors.white,
26 | transparent: colors.gray25,
27 | },
28 | light: {
29 | default: colors.gray900,
30 | transparent: colors.gray25,
31 | },
32 | };
33 |
34 | export const Svg = styled.svg`
35 | display: block;
36 | `;
37 |
38 | export const Symbol = styled.path`
39 | fill: ${(props) => symbolColors[props.theme][props.type]};
40 | `;
41 |
42 | export const Wordmark = styled.path`
43 | fill: ${(props) => wordColors[props.theme][props.type]};
44 | `;
45 |
--------------------------------------------------------------------------------
/src/containers/MyPage/AuthComplete/AuthComplete.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import {
4 | Layout,
5 | LogoBox,
6 | Title,
7 | SubTitle,
8 | ButtonLink,
9 | Link,
10 | AskingForHelp,
11 | } from "./AuthComplete.styled";
12 | import { Button } from "@/components";
13 | import { Logo } from "@/foundations";
14 |
15 | const AuthComplete = ({ ...props }) => {
16 | return (
17 |
18 |
19 |
20 |
21 | 인증이 완료되었습니다
22 |
23 | {`많은 싸피인들을 만나보세요. \n 더 자유롭고 쉽게 소통하세요!`}
24 |
25 |
26 |
27 |
28 |
29 |
33 | 도움이 필요하신가요?
34 |
35 |
36 | );
37 | };
38 |
39 | export default AuthComplete;
40 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/StudyCreateIntro/StudyCreateIntro.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { colors, fontSize, fontWeight } from "@/_shared";
3 |
4 | const bgColor = {
5 | light: colors.gray25,
6 | dark: colors.gray900,
7 | };
8 |
9 | const titleColor = {
10 | light: colors.gray800,
11 | dark: colors.gray25,
12 | };
13 |
14 | const subtitleColor = {
15 | light: colors.gray400,
16 | dark: colors.gray500,
17 | };
18 |
19 | export const Layout = styled.div`
20 | display: flex;
21 | flex-direction: column;
22 | justify-content: center;
23 | align-items: center;
24 | gap: 1rem;
25 |
26 | width: 100%;
27 | height: 400px;
28 | background: ${(props) => bgColor[props.theme]};
29 | `;
30 |
31 | export const Title = styled.div`
32 | padding-top: 68px;
33 | font-weight: ${fontWeight.bold};
34 | font-size: ${fontSize.h2};
35 | color: ${(props) => titleColor[props.theme]};
36 | `;
37 |
38 | export const SubTitle = styled.div`
39 | font-size: ${fontSize.p};
40 | color: ${(props) => subtitleColor[props.theme]};
41 | `;
42 |
--------------------------------------------------------------------------------
/src/components/layouts/Navbar/NavMobile/NavMobileItem/NavMobileItem.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout, Text } from "./NavMobileItem.styled";
5 |
6 | const THEME = {
7 | DARK: "dark",
8 | LIGHT: "light",
9 | };
10 |
11 | const STATUS = {
12 | DEFAULT: "default",
13 | ACTIVE: "active",
14 | };
15 |
16 | const TYPE = {
17 | DEFAULT: "default",
18 | TRANSPARENT: "transparent",
19 | };
20 |
21 | const NavMobileItem = ({ children, ...props }) => {
22 | return (
23 |
24 | {children}
25 |
26 | );
27 | };
28 |
29 | NavMobileItem.propTypes = {
30 | theme: PropTypes.oneOf(Object.values(THEME)),
31 | status: PropTypes.oneOf(Object.values(STATUS)),
32 | type: PropTypes.oneOf(Object.values(TYPE)),
33 | children: PropTypes.node.isRequired,
34 | };
35 |
36 | NavMobileItem.defaultProps = {
37 | theme: THEME.LIGHT,
38 | status: STATUS.DEFAULT,
39 | type: TYPE.DEFAULT,
40 | children: "Menu",
41 | };
42 |
43 | export default NavMobileItem;
44 |
--------------------------------------------------------------------------------
/src/containers/Common/Navbar/NavMobile/NavMobileItem/NavMobileItem.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout, Text } from "./NavMobileItem.styled";
5 |
6 | const THEME = {
7 | DARK: "dark",
8 | LIGHT: "light",
9 | };
10 |
11 | const STATUS = {
12 | DEFAULT: "default",
13 | ACTIVE: "active",
14 | };
15 |
16 | const TYPE = {
17 | DEFAULT: "default",
18 | TRANSPARENT: "transparent",
19 | };
20 |
21 | const NavMobileItem = ({ children, ...props }) => {
22 | return (
23 |
24 | {children}
25 |
26 | );
27 | };
28 |
29 | NavMobileItem.propTypes = {
30 | theme: PropTypes.oneOf(Object.values(THEME)),
31 | status: PropTypes.oneOf(Object.values(STATUS)),
32 | type: PropTypes.oneOf(Object.values(TYPE)),
33 | children: PropTypes.node.isRequired,
34 | };
35 |
36 | NavMobileItem.defaultProps = {
37 | theme: THEME.LIGHT,
38 | status: STATUS.DEFAULT,
39 | type: TYPE.DEFAULT,
40 | children: "Menu",
41 | };
42 |
43 | export default NavMobileItem;
44 |
--------------------------------------------------------------------------------
/src/containers/CommunityPage/CommunityIntro/CommunityIntro.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { colors, fontSize, fontWeight } from "@/_shared";
3 |
4 | const titleColor = {
5 | light: colors.gray900,
6 | dark: colors.gray25,
7 | };
8 |
9 | const subtitleColor = {
10 | light: colors.gray400,
11 | dark: colors.gray500,
12 | };
13 |
14 | export const Layout = styled.section`
15 | display: flex;
16 | flex-direction: column;
17 | justify-content: center;
18 | align-items: center;
19 |
20 | width: 100%;
21 | height: 400px;
22 | background: ${colors.blueOpacity50};
23 | `;
24 |
25 | export const Title = styled.h1`
26 | padding-top: 68px;
27 | margin: 0;
28 |
29 | font-weight: ${fontWeight.bold};
30 | font-size: ${fontSize.h2};
31 | color: ${(props) => titleColor[props.theme]};
32 | user-select: none;
33 | `;
34 |
35 | export const SubTitle = styled.span`
36 | display: block;
37 | font-size: ${fontSize.p};
38 | color: ${(props) => subtitleColor[props.theme]};
39 | padding: 1rem 0 2rem 0;
40 | user-select: none;
41 | `;
42 |
--------------------------------------------------------------------------------
/src/containers/commons/Navbar/NavMobile/NavMobileItem/NavMobileItem.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout, Text } from "./NavMobileItem.styled";
5 |
6 | const THEME = {
7 | DARK: "dark",
8 | LIGHT: "light",
9 | };
10 |
11 | const STATUS = {
12 | DEFAULT: "default",
13 | ACTIVE: "active",
14 | };
15 |
16 | const TYPE = {
17 | DEFAULT: "default",
18 | TRANSPARENT: "transparent",
19 | };
20 |
21 | const NavMobileItem = ({ children, ...props }) => {
22 | return (
23 |
24 | {children}
25 |
26 | );
27 | };
28 |
29 | NavMobileItem.propTypes = {
30 | theme: PropTypes.oneOf(Object.values(THEME)),
31 | status: PropTypes.oneOf(Object.values(STATUS)),
32 | type: PropTypes.oneOf(Object.values(TYPE)),
33 | children: PropTypes.node.isRequired,
34 | };
35 |
36 | NavMobileItem.defaultProps = {
37 | theme: THEME.LIGHT,
38 | status: STATUS.DEFAULT,
39 | type: TYPE.DEFAULT,
40 | children: "Menu",
41 | };
42 |
43 | export default NavMobileItem;
44 |
--------------------------------------------------------------------------------
/src/containers/Common/Navbar/NavDefault/NavDefaultItem/NavDefaultItem.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout, Text } from "./NavDefaultItem.styled";
5 |
6 | const THEME = {
7 | DARK: "dark",
8 | LIGHT: "light",
9 | };
10 |
11 | const STATUS = {
12 | DEFAULT: "default",
13 | ACTIVE: "active",
14 | };
15 |
16 | const TYPE = {
17 | DEFAULT: "default",
18 | TRANSPARENT: "transparent",
19 | };
20 |
21 | const NavDefaultItem = ({ children, ...props }) => {
22 | return (
23 |
24 | {children}
25 |
26 | );
27 | };
28 |
29 | NavDefaultItem.propTypes = {
30 | theme: PropTypes.oneOf(Object.values(THEME)),
31 | status: PropTypes.oneOf(Object.values(STATUS)),
32 | type: PropTypes.oneOf(Object.values(TYPE)),
33 | children: PropTypes.node.isRequired,
34 | };
35 |
36 | NavDefaultItem.defaultProps = {
37 | theme: THEME.LIGHT,
38 | status: STATUS.DEFAULT,
39 | type: TYPE.DEFAULT,
40 | children: "Menu",
41 | };
42 |
43 | export default NavDefaultItem;
44 |
--------------------------------------------------------------------------------
/src/containers/CommunityPage/CommunityNoContent/CommunityNoContent.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { useRecoilValue, useSetRecoilState } from "recoil";
4 | import { isLoginState, createModalState, loginModalState } from "@/recoil";
5 |
6 | import { Layout, Title, Content } from "./CommunityNoContent.styled";
7 | import { Button } from "@/components";
8 |
9 | const CommunityNoContent = ({ ...props }) => {
10 | const isLogined = useRecoilValue(isLoginState);
11 |
12 | const setCreateModalOpen = useSetRecoilState(createModalState);
13 | const setLoginModalOpen = useSetRecoilState(loginModalState);
14 | const handleCreation = () => {
15 | !isLogined && setLoginModalOpen("require");
16 | isLogined && setCreateModalOpen(true);
17 | };
18 |
19 | return (
20 |
21 | 검색 결과가 없어요 🥺
22 | 게시글을 직접 작성해볼까요?
23 |
26 |
27 | );
28 | };
29 |
30 | export default CommunityNoContent;
31 |
--------------------------------------------------------------------------------
/src/components/layouts/FeedGrid/FeedGrid.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout, FeedItemLink } from "./FeedGrid.styled";
5 | import { FeedItem } from "@/components";
6 |
7 | const FeedGrid = ({ data, isLoading, theme, ...props }) => {
8 | const feeds = isLoading ? loadingData : data;
9 |
10 | return (
11 |
12 | {feeds.map(({ id, ...feedData }, idx) => (
13 |
14 |
15 |
16 | ))}
17 |
18 | );
19 | };
20 |
21 | FeedGrid.propTypes = {
22 | data: PropTypes.arrayOf(Object),
23 | };
24 |
25 | export default FeedGrid;
26 |
27 | const loadingData = new Array(6).fill({
28 | username: "",
29 | avatar: "",
30 | campus: "",
31 | ordinal: "",
32 | userDetail: "",
33 | created: "",
34 | title: "",
35 | content: "",
36 | thumbnail: "",
37 | likecount: "",
38 | commentcount: "",
39 | viewcount: "",
40 | });
41 |
--------------------------------------------------------------------------------
/src/components/layouts/Navbar/NavDefault/NavDefaultItem/NavDefaultItem.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout, Text } from "./NavDefaultItem.styled";
5 |
6 | const THEME = {
7 | DARK: "dark",
8 | LIGHT: "light",
9 | };
10 |
11 | const STATUS = {
12 | DEFAULT: "default",
13 | ACTIVE: "active",
14 | };
15 |
16 | const TYPE = {
17 | DEFAULT: "default",
18 | TRANSPARENT: "transparent",
19 | };
20 |
21 | const NavDefaultItem = ({ children, ...props }) => {
22 | return (
23 |
24 | {children}
25 |
26 | );
27 | };
28 |
29 | NavDefaultItem.propTypes = {
30 | theme: PropTypes.oneOf(Object.values(THEME)),
31 | status: PropTypes.oneOf(Object.values(STATUS)),
32 | type: PropTypes.oneOf(Object.values(TYPE)),
33 | children: PropTypes.node.isRequired,
34 | };
35 |
36 | NavDefaultItem.defaultProps = {
37 | theme: THEME.LIGHT,
38 | status: STATUS.DEFAULT,
39 | type: TYPE.DEFAULT,
40 | children: "Menu",
41 | };
42 |
43 | export default NavDefaultItem;
44 |
--------------------------------------------------------------------------------
/src/components/layouts/Navbar/Navbar.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { useMediaQuery } from "react-responsive";
5 |
6 | import { NavDefault } from "./NavDefault";
7 | import { NavMobile } from "./NavMobile";
8 |
9 | const THEME = {
10 | DARK: "dark",
11 | LIGHT: "light",
12 | };
13 |
14 | const TYPE = {
15 | DEFAULT: "default",
16 | TRANSPARENT: "transparent",
17 | };
18 |
19 | const Navbar = ({ ...props }) => {
20 | const isPc = useMediaQuery({ query: "(min-width:980px)" });
21 | const isMobile = useMediaQuery({ query: "(max-width:980px)" });
22 |
23 | return (
24 | <>
25 | {isPc && }
26 | {isMobile && }
27 | >
28 | );
29 | };
30 |
31 | Navbar.propTypes = {
32 | theme: PropTypes.oneOf(Object.values(THEME)),
33 | type: PropTypes.oneOf(Object.values(TYPE)),
34 | isLogin: PropTypes.bool,
35 | };
36 |
37 | Navbar.defaultProps = {
38 | theme: THEME.LIGHT,
39 | type: TYPE.DEFAULT,
40 | isLogin: true,
41 | };
42 |
43 | export default Navbar;
44 |
--------------------------------------------------------------------------------
/src/components/patterns/Toc/Toc.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout } from "./Toc.styled";
5 | import { TocItem } from "./TocItem";
6 |
7 | const THEME = {
8 | DARK: "dark",
9 | LIGHT: "light",
10 | };
11 |
12 | const Toc = ({ items, ...props }) => {
13 | const [current, setCurrent] = useState("info");
14 |
15 | return (
16 |
17 | {items.map(({ name, title, number }, index) => {
18 | return (
19 | setCurrent(name)}
21 | status={current === name ? "active" : "default"}
22 | number={number}
23 | key={index}
24 | {...props}
25 | >
26 | {title}
27 |
28 | );
29 | })}
30 |
31 | );
32 | };
33 |
34 | Toc.propTypes = {
35 | items: PropTypes.array.isRequired,
36 | theme: PropTypes.oneOf(Object.values(THEME)),
37 | };
38 |
39 | Toc.defaultProps = {
40 | theme: THEME.LIGHT,
41 | };
42 |
43 | export default Toc;
44 |
--------------------------------------------------------------------------------
/src/containers/StudyPage/index.js:
--------------------------------------------------------------------------------
1 | export { default as StudyCreateDetail } from "./StudyCreateDetail";
2 | export { default as StudyCreateEditor } from "./StudyCreateEditor";
3 | export { default as StudyCreateForm } from "./StudyCreateForm";
4 | export { default as StudyCreateIntro } from "./StudyCreateIntro";
5 | export { default as StudyCreateSummary } from "./StudyCreateSummary";
6 | export { default as StudyCardGrid } from "./StudyCardGrid";
7 | export { default as StudyIntro } from "./StudyIntro";
8 | export { default as StudyDetail } from "./StudyDetail";
9 | export { default as StudyDetailMobile } from "./StudyDetailMobile";
10 | export { default as StudyDetailComment } from "./StudyDetailComment";
11 | export { default as StudySideBar } from "./StudySideBar";
12 | export { default as StudyMain } from "./StudyMain";
13 | export { default as StudyMainMobile } from "./StudyMainMobile";
14 | export { default as StudyNoContent } from "./StudyNoContent";
15 | export { default as StudyUpdateForm } from "./StudyUpdateForm";
16 | export { default as ThumbnailUploader } from "./ThumbnailUploader";
17 |
--------------------------------------------------------------------------------
/src/containers/commons/Navbar/NavDefault/NavDefaultItem/NavDefaultItem.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout, Text } from "./NavDefaultItem.styled";
5 |
6 | const THEME = {
7 | DARK: "dark",
8 | LIGHT: "light",
9 | };
10 |
11 | const STATUS = {
12 | DEFAULT: "default",
13 | ACTIVE: "active",
14 | };
15 |
16 | const TYPE = {
17 | DEFAULT: "default",
18 | TRANSPARENT: "transparent",
19 | };
20 |
21 | const NavDefaultItem = ({ children, ...props }) => {
22 | return (
23 |
24 | {children}
25 |
26 | );
27 | };
28 |
29 | NavDefaultItem.propTypes = {
30 | theme: PropTypes.oneOf(Object.values(THEME)),
31 | status: PropTypes.oneOf(Object.values(STATUS)),
32 | type: PropTypes.oneOf(Object.values(TYPE)),
33 | children: PropTypes.node.isRequired,
34 | };
35 |
36 | NavDefaultItem.defaultProps = {
37 | theme: THEME.LIGHT,
38 | status: STATUS.DEFAULT,
39 | type: TYPE.DEFAULT,
40 | children: "Menu",
41 | };
42 |
43 | export default NavDefaultItem;
44 |
--------------------------------------------------------------------------------
/src/components/layouts/FeedSmallGrid/FeedSmallGrid.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout, FeedItemLink } from "./FeedSmallGrid.styled";
5 | import { FeedItemSmall } from "@/components";
6 |
7 | const FeedSmallGrid = ({ data, isLoading, ...props }) => {
8 | const feedData = isLoading
9 | ? new Array(3).fill({
10 | boardId: 1,
11 | })
12 | : data;
13 |
14 | return (
15 |
16 | {feedData.map(({ boardId, ...data }) => {
17 | const colorIdx = boardId % 17;
18 |
19 | return (
20 |
21 |
27 |
28 | );
29 | })}
30 |
31 | );
32 | };
33 |
34 | FeedSmallGrid.propTypes = {
35 | data: PropTypes.arrayOf(Object),
36 | isLoading: PropTypes.bool,
37 | };
38 |
39 | export default FeedSmallGrid;
40 |
--------------------------------------------------------------------------------
/src/components/patterns/ScrollTopButton/ScrollTopButton.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { colors, shadows } from "@/_shared";
3 |
4 | const bgColor = {
5 | light: colors.white,
6 | dark: colors.gray800,
7 | };
8 |
9 | const bgHoverColor = {
10 | light: colors.gray50,
11 | dark: colors.gray700,
12 | };
13 |
14 | const borderColor = {
15 | light: colors.gray200,
16 | dark: colors.gray800,
17 | };
18 |
19 | const iconColor = {
20 | light: colors.gray600,
21 | dark: colors.gray200,
22 | };
23 |
24 | export const Layout = styled.div`
25 | display: flex;
26 | justify-content: center;
27 | align-items: center;
28 |
29 | width: 40px;
30 | height: 40px;
31 | border: 1px solid ${(props) => borderColor[props.theme]};
32 | border-radius: 50%;
33 |
34 | background-color: ${(props) => bgColor[props.theme]};
35 | box-shadow: ${shadows.base};
36 | cursor: pointer;
37 |
38 | :hover {
39 | background-color: ${(props) => bgHoverColor[props.theme]};
40 | }
41 |
42 | svg {
43 | stroke: ${(props) => iconColor[props.theme]};
44 | }
45 | `;
46 |
--------------------------------------------------------------------------------
/src/components/patterns/CardSmallCreate/CardSmallCreate.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { Link } from "react-router-dom";
4 |
5 | import { Layout, TextBox, Thumbnail, Title } from "./CardSmallCreate.styled";
6 | import { Icon } from "@/foundations";
7 |
8 | const THEME = {
9 | LIGHT: "light",
10 | DARK: "dark",
11 | };
12 |
13 | const CardSmallCreate = ({ content, companyData, ...props }) => {
14 | return (
15 |
21 |
22 |
23 |
24 |
25 |
26 | {content}
27 |
28 |
29 |
30 | );
31 | };
32 |
33 | CardSmallCreate.propTypes = {
34 | theme: PropTypes.oneOf(Object.values(THEME)),
35 | };
36 |
37 | CardSmallCreate.defaultProps = {
38 | theme: THEME.LIGHT,
39 | };
40 |
41 | export default CardSmallCreate;
42 |
--------------------------------------------------------------------------------
/src/components/layouts/CommentList/CommentList.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout, CommentBox } from "./CommentList.styled";
5 | import { Comment } from "@/components";
6 |
7 | const CommentList = ({
8 | currentUser,
9 | data,
10 | theme,
11 | isLoading,
12 | dropdownItems,
13 | ...props
14 | }) => {
15 | const items = isLoading ? dummy : data;
16 | return (
17 |
18 | {items.map((props, idx) => {
19 | return (
20 |
21 |
27 |
28 | );
29 | })}
30 |
31 | );
32 | };
33 |
34 | CommentList.propTypes = {
35 | data: PropTypes.arrayOf(Object),
36 | };
37 |
38 | export default CommentList;
39 |
40 | const dummy = new Array(6).fill({
41 | username: "",
42 | src: null,
43 | created: null,
44 | content: null,
45 | });
46 |
--------------------------------------------------------------------------------
/src/foundations/Logo/Logo.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import Logo from "./";
4 | import { Background, Typography } from "@/foundations";
5 | import { colors } from "@/_shared";
6 |
7 | export default {
8 | title: "Foundations/Logo",
9 | component: Logo,
10 | };
11 |
12 | export const Default = (args) => (
13 |
14 |
15 |
16 | );
17 |
18 | export const AllTypes = () => (
19 | <>
20 |
21 |
22 | Light
23 |
24 |
25 |
26 |
27 |
28 | Blue
29 |
30 |
31 |
32 |
33 |
34 | Dark
35 |
36 |
37 |
38 | >
39 | );
40 |
--------------------------------------------------------------------------------
/src/containers/JobPage/JobIntro/JobIntro.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { colors, fontSize, lineHeight, fontWeight } from "@/_shared";
3 |
4 | const subtitleColor = {
5 | light: colors.gray400,
6 | dark: colors.gray500,
7 | };
8 |
9 | export const Layout = styled.section`
10 | display: flex;
11 | flex-direction: column;
12 | justify-content: center;
13 | align-items: center;
14 | gap: 1rem;
15 |
16 | width: 100%;
17 | height: 400px;
18 | background: url("/images/job-intro.webp");
19 | background-repeat: no-repeat;
20 | background-size: cover;
21 |
22 | overflow: hidden;
23 | `;
24 |
25 | export const Title = styled.h1`
26 | padding-top: 2rem;
27 | margin: 0;
28 |
29 | line-height: ${lineHeight.h2};
30 | font-weight: ${fontWeight.bold};
31 | font-size: ${fontSize.h2};
32 | color: ${colors.gray25};
33 |
34 | user-select: none;
35 | `;
36 |
37 | export const SubTitle = styled.span`
38 | display: block;
39 | height: ${lineHeight.h4};
40 | font-size: ${fontSize.p};
41 | color: ${(props) => subtitleColor[props.theme]};
42 | user-select: none;
43 | `;
44 |
--------------------------------------------------------------------------------
/src/containers/MyPage/MyPageIntro/MyPageIntro.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import {
5 | Layout,
6 | ContentBox,
7 | TitleBox,
8 | Title,
9 | SubTitle,
10 | } from "./MyPageIntro.styled";
11 | import { Avatar } from "@/components";
12 |
13 | const THEME = {
14 | LIGHT: "light",
15 | DARK: "dark",
16 | };
17 |
18 | const MyPageIntro = ({ userProfile, ...props }) => {
19 | const { profileImg, nickname } = userProfile;
20 |
21 | return (
22 | <>
23 |
24 |
25 |
26 |
27 | {nickname}
28 | ${nickname}님 안녕하세요!
29 |
30 |
31 |
32 | >
33 | );
34 | };
35 |
36 | MyPageIntro.propTypes = {
37 | theme: PropTypes.oneOf(Object.values(THEME)),
38 | data: PropTypes.arrayOf(Object),
39 | };
40 |
41 | MyPageIntro.defaultProps = {
42 | theme: THEME.LIGHT,
43 | };
44 |
45 | export default MyPageIntro;
46 |
--------------------------------------------------------------------------------
/src/containers/JobPage/JobDetailComment/JobDetailComment.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { colors, fontSize, fontWeight, lineHeight } from "@/_shared";
3 |
4 | const titleColor = {
5 | light: colors.gray900,
6 | dark: colors.gray25,
7 | };
8 |
9 | const subtitleColor = {
10 | light: colors.gray400,
11 | dark: colors.gray500,
12 | };
13 |
14 | const borderColor = {
15 | dark: colors.gray700,
16 | light: colors.gray200,
17 | };
18 |
19 | export const Layout = styled.section``;
20 |
21 | export const BoxTitle = styled.h2`
22 | min-height: ${lineHeight.h3};
23 | padding: 4rem 0 0.2rem 0;
24 | margin: 0;
25 |
26 | border-top: 1px solid ${(props) => borderColor[props.theme]};
27 | color: ${(props) => titleColor[props.theme]};
28 |
29 | font-size: ${fontSize.h3};
30 | line-height: ${lineHeight.h3};
31 | font-weight: ${fontWeight.bold};
32 | `;
33 |
34 | export const BoxDescription = styled.p`
35 | padding-bottom: 2rem;
36 | margin: 0;
37 |
38 | color: ${(props) => subtitleColor[props.theme]};
39 | font-size: ${fontSize.p};
40 | line-height: ${lineHeight.p};
41 | `;
42 |
--------------------------------------------------------------------------------
/src/foundations/Typography/Typography.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { fontSize, fontWeight, lineHeight } from "@/_shared";
3 |
4 | const textSize = {
5 | h1: fontSize.h1,
6 | h2: fontSize.h2,
7 | h3: fontSize.h3,
8 | h4: fontSize.h4,
9 | large: fontSize.lg,
10 | paragraph: fontSize.p,
11 | small: fontSize.sm,
12 | button: fontSize.sm,
13 | };
14 |
15 | const textHeight = {
16 | h1: lineHeight.h1,
17 | h2: lineHeight.h2,
18 | h3: lineHeight.h3,
19 | h4: lineHeight.h4,
20 | large: lineHeight.lg,
21 | paragraph: lineHeight.p,
22 | small: lineHeight.sm,
23 | button: "0.875rem",
24 | };
25 |
26 | const textWeight = {
27 | h1: fontWeight.bold,
28 | h2: fontWeight.bold,
29 | h3: fontWeight.bold,
30 | h4: fontWeight.bold,
31 | large: fontWeight.regular,
32 | paragraph: fontWeight.regular,
33 | sm: fontWeight.regular,
34 | button: fontWeight.bold,
35 | };
36 |
37 | export const Text = styled.div`
38 | font-weight: ${(props) => textWeight[props.type]};
39 | font-size: ${(props) => textSize[props.type]};
40 | line-height: ${(props) => textHeight[props.type]};
41 | `;
42 |
--------------------------------------------------------------------------------
/public/images/admin.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/containers/LandingPage/LandingDetailSection/LandingDetailSection.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import {
5 | Layout,
6 | ContentBox,
7 | Highlight,
8 | Title,
9 | ImgBox,
10 | Description,
11 | } from "./LandingDetailSection.styled";
12 |
13 | const MODE = {
14 | DEFAULT: "default",
15 | BACKGROUND: "background",
16 | };
17 |
18 | const LandingDetailSection = ({
19 | highlight,
20 | title,
21 | description,
22 | src,
23 | mode,
24 | ...props
25 | }) => {
26 | return (
27 |
28 |
29 | {highlight}
30 | {title}
31 | {description}
32 |
33 |
34 |
35 |
36 |
37 | );
38 | };
39 |
40 | LandingDetailSection.propTypes = {
41 | mode: PropTypes.oneOf(Object.values(MODE)),
42 | };
43 |
44 | LandingDetailSection.defaultProps = {
45 | mode: MODE.DEFAULT,
46 | };
47 |
48 | export default LandingDetailSection;
49 |
--------------------------------------------------------------------------------
/src/api/getCommunityDetail.js:
--------------------------------------------------------------------------------
1 | import { useQuery } from "react-query";
2 | import { axiosInstance, daysFromToday, numToMillion, parseHtml } from "@/utils";
3 |
4 | export const GetCommunityDetail = (id) =>
5 | useQuery(["getCommunityDetail", id], () => fetchData(id));
6 |
7 | const fetchData = async (id) => {
8 | const res = await axiosInstance({
9 | url: `/boards/${id}`,
10 | });
11 | return res.data;
12 | };
13 |
14 | export const CommunityDetailSelector = (data) => {
15 | const { parsedhtml } = parseHtml(data.content);
16 | const contentData = {
17 | username: data.writerInfo.nickname,
18 | userId: data.writerInfo.userId,
19 | avatar: data.writerInfo.profileImg,
20 | campus: data.writerInfo.campus,
21 | ordinal: data.writerInfo.ordinal,
22 | created: daysFromToday(data.createdDate),
23 | title: data.title,
24 | content: parsedhtml,
25 | likecount: numToMillion(data.totalLike),
26 | commentcount: numToMillion(data.totalComment),
27 | viewcount: numToMillion(data.views),
28 | likeStatus: data.likeStatus,
29 | boardType: data.boardType,
30 | };
31 | return { contentData };
32 | };
33 |
--------------------------------------------------------------------------------
/src/components/commons/ImageBox/ImageBox.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 |
4 | import ImageBox from "./";
5 | import { Typography } from "@/foundations";
6 |
7 | export default {
8 | title: "Components/ImageBox",
9 | component: ImageBox,
10 | };
11 |
12 | export const Default = (args) => ;
13 |
14 | Default.args = {
15 | size: "large",
16 | };
17 |
18 | export const AllTypes = () => (
19 | <>
20 |
21 | Size
22 |
23 |
24 |
25 |
26 |
27 | Light ImageBox
28 |
29 |
30 |
31 |
32 | Dark ImageBox
33 |
34 |
35 |
36 | >
37 | );
38 |
39 | const TypeBox = styled.div`
40 | display: flex;
41 | flex-direction: column;
42 | gap: 1rem;
43 | padding: 1rem;
44 | `;
45 |
--------------------------------------------------------------------------------
/src/api/getJobDetail.js:
--------------------------------------------------------------------------------
1 | import { useQuery } from "react-query";
2 | import { axiosInstance, daysLeftFromToday, parseHtml } from "@/utils";
3 |
4 | export const GetJobDetail = (id) =>
5 | useQuery(["getJobDetail", id], () => fetchData(id));
6 |
7 | const fetchData = async (id) => {
8 | const res = await axiosInstance({
9 | url: `/recruits/${id}`,
10 | });
11 | return res.data;
12 | };
13 |
14 | export const JobDetailSelector = (data) => {
15 | const { parsedhtml } = parseHtml(data.content);
16 | const titleData = {
17 | title: data.title,
18 | subtitle: data.company.companyName,
19 | highlight: daysLeftFromToday(data.closeDate) ? "모집중" : "모집마감",
20 | };
21 | const contentData = parsedhtml;
22 |
23 | const sidebarData = {
24 | task: data.job,
25 | type: data.empType,
26 | career: data.career,
27 | location: data.location,
28 | badges: data.techStack.split(",").filter((data) => data),
29 | scrap: data.scrapStatus,
30 | url: data.recruitUrl,
31 | src: data.company.logoImg,
32 | };
33 | const companyData = data.company;
34 | return { contentData, titleData, sidebarData, companyData };
35 | };
36 |
--------------------------------------------------------------------------------
/src/components/patterns/CardSmall/CardSmall.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 |
4 | import CardSmall from "./";
5 | import { Background } from "@/foundations";
6 |
7 | export default {
8 | title: "Patterns/CardSmall",
9 | component: CardSmall,
10 | };
11 |
12 | export const Default = (args) => ;
13 |
14 | const defaultContents = {
15 | title: "주니어 웹 개발자 채용",
16 | highlight: "모집중",
17 | src: "",
18 | };
19 |
20 | Default.args = {
21 | contents: defaultContents,
22 | };
23 |
24 | export const AllTypes = () => (
25 | <>
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | >
39 | );
40 |
41 | const TypeBox = styled.div`
42 | display: flex;
43 | flex-direction: column;
44 | gap: 4rem;
45 | padding: 1rem;
46 | `;
47 |
--------------------------------------------------------------------------------
/src/components/layouts/Navbar/NavMobile/NavMobile.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { NavMobile } from ".";
3 |
4 | export default {
5 | title: "layouts/Navbar/NavMobile",
6 | component: NavMobile,
7 | };
8 |
9 | const navData = [
10 | {
11 | name: "community",
12 | title: "커뮤니티",
13 | url: "community",
14 | },
15 | {
16 | name: "study",
17 | title: "스터디",
18 | url: "study",
19 | },
20 | {
21 | name: "jobs",
22 | title: "취업정보",
23 | url: "job",
24 | },
25 | ];
26 |
27 | const userMenuData = [
28 | {
29 | name: "profile",
30 | title: "내 프로필",
31 | url: "profile",
32 | },
33 | ];
34 |
35 | export const Default = (args) => ;
36 |
37 | Default.args = {
38 | navData,
39 | userMenuData,
40 | };
41 |
42 | export const AllTypes = () => (
43 | <>
44 |
50 |
51 |
57 | >
58 | );
59 |
--------------------------------------------------------------------------------
/src/utils/daysFromToday.js:
--------------------------------------------------------------------------------
1 | const daysFromToday = (value) => {
2 | const now = new Date();
3 | const today =
4 | now.getTime() + now.getTimezoneOffset() * 60 * 1000 + 9 * 60 * 60 * 1000;
5 | const timeValue = new Date(value);
6 |
7 | const betweenTime = Math.floor((today - timeValue.getTime()) / 1000 / 60);
8 | if (betweenTime < 1) return "방금 전";
9 | if (betweenTime < 60) {
10 | return `${betweenTime}분 전`;
11 | }
12 |
13 | const betweenTimeHour = Math.floor(betweenTime / 60);
14 | if (betweenTimeHour < 24) {
15 | return `${betweenTimeHour}시간 전`;
16 | }
17 |
18 | const betweenTimeDay = Math.floor(betweenTime / 60 / 24);
19 | if (betweenTimeDay < 7) {
20 | return `${betweenTimeDay}일 전`;
21 | }
22 |
23 | const betweenTimeDayWeek = Math.floor(betweenTime / 60 / 24 / 7);
24 | if (betweenTimeDayWeek < 4) {
25 | return `${betweenTimeDayWeek}주 전`;
26 | }
27 |
28 | const betweenTimeDayMonth = Math.floor(betweenTime / 60 / 24 / 30);
29 | if (betweenTimeDayMonth < 12) {
30 | return `${betweenTimeDayMonth}개월 전`;
31 | }
32 |
33 | return `${Math.floor(betweenTimeDay / 365)}년 전`;
34 | };
35 |
36 | export default daysFromToday;
37 |
--------------------------------------------------------------------------------
/src/components/layouts/CardGrid/CardGrid.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Layout, CardItemLink } from "./CardGrid.styled";
5 | import { CardResponsive } from "@/components";
6 |
7 | const THEME = {
8 | LIGHT: "light",
9 | DARK: "dark",
10 | };
11 |
12 | const CardGrid = ({ data, theme, isLoading, ...props }) => {
13 | const items = isLoading ? cardData : data;
14 | return (
15 |
16 | {items.map(({ id, ...props }, idx) => (
17 |
18 |
19 |
20 | ))}
21 |
22 | );
23 | };
24 |
25 | CardGrid.propTypes = {
26 | theme: PropTypes.oneOf(Object.values(THEME)),
27 | data: PropTypes.array,
28 | isLoading: PropTypes.bool,
29 | };
30 |
31 | CardGrid.defaultProps = {
32 | theme: THEME.LIGHT,
33 | };
34 |
35 | const cardData = new Array(3).fill({
36 | contents: {
37 | title: "",
38 | subtitle: "",
39 | highlight: "",
40 | src: "",
41 | },
42 | badges: ["", "", ""],
43 | id: "",
44 | });
45 |
46 | export default CardGrid;
47 |
--------------------------------------------------------------------------------
/src/foundations/Color/Color.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 |
4 | import { colors, fontSize, fontWeight } from "@/_shared";
5 |
6 | export default {
7 | title: "Foundations/Color",
8 | };
9 |
10 | export const AllTypes = () => (
11 | <>
12 |
13 | {Object.keys(colors).map((key) => (
14 |
15 |
16 | {key}
17 | {colors[key]}
18 |
19 | ))}
20 |
21 | >
22 | );
23 |
24 | const Layout = styled.div`
25 | display: flex;
26 | flex-wrap: wrap;
27 | gap: 2rem;
28 | `;
29 |
30 | const ColorBox = styled.div`
31 | display: flex;
32 | flex-direction: column;
33 | align-items: center;
34 | gap: 4px;
35 | `;
36 |
37 | const Color = styled.div`
38 | width: 120px;
39 | height: 120px;
40 | `;
41 |
42 | const Name = styled.div`
43 | color: ${colors.gray900};
44 | font-size: ${fontSize.sm};
45 | font-weight: ${fontWeight.bold};
46 | `;
47 |
48 | const Meta = styled.div`
49 | color: ${colors.gray500};
50 | font-size: ${fontSize.xs};
51 | `;
52 |
--------------------------------------------------------------------------------
/src/components/patterns/Progress/Progress.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import Progress from "./";
4 | import { Background } from "@/foundations";
5 |
6 | export default {
7 | title: "Patterns/Progress",
8 | component: Progress,
9 | argTypes: {
10 | items: {
11 | table: {
12 | disable: true,
13 | },
14 | },
15 | },
16 | };
17 |
18 | const items = [
19 | {
20 | step: 1,
21 | title: "공고 요약",
22 | description: "한 눈에 들어오게 요약해주세요",
23 | status: "default",
24 | },
25 | {
26 | step: 2,
27 | title: "상세 내용 입력",
28 | description: "공고 내용을 그대로 옮겨주세요",
29 | status: "default",
30 | },
31 | {
32 | step: 3,
33 | title: "미리보기",
34 | description: "잘 작성되었는지 확인해보세요.",
35 | status: "default",
36 | },
37 | ];
38 |
39 | export const Default = (props) => ;
40 |
41 | Default.args = {
42 | items,
43 | };
44 |
45 | export const AllTypes = () => (
46 | <>
47 |
48 |
49 |
50 |
51 |
52 |
53 | >
54 | );
55 |
--------------------------------------------------------------------------------
/src/components/patterns/Search/Search.jsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable prettier/prettier */
2 | import React, { useState } from "react";
3 | import PropTypes from "prop-types";
4 |
5 | import { Layout, Button, InputBox, Input } from "./Search.styled";
6 | import { Icon } from "@/foundations";
7 |
8 | const THEME = {
9 | LIGHT: "light",
10 | DARK: "dark",
11 | };
12 |
13 | const Search = ({ items, placeholder, value, ...props }) => {
14 | const [inputOpen, setInputOpen] = useState(!!value);
15 |
16 | return (
17 |
18 |
19 |
25 |
26 |
29 |
30 | );
31 | };
32 |
33 | Search.propTypes = {
34 | theme: PropTypes.oneOf(Object.values(THEME)),
35 | placeholder: PropTypes.string,
36 | };
37 |
38 | Search.defaultProps = {
39 | theme: THEME.LIGHT,
40 | placeholder: "검색",
41 | };
42 |
43 | export default Search;
44 |
--------------------------------------------------------------------------------
/src/containers/CommunityPage/CommunityNoContent/CommunityNoContent.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { colors, fontSize, fontWeight, lineHeight } from "@/_shared";
3 |
4 | const titleColor = {
5 | dark: colors.gray25,
6 | light: colors.gray900,
7 | };
8 |
9 | const contentColor = {
10 | dark: colors.gray400,
11 | light: colors.gray600,
12 | };
13 |
14 | export const Layout = styled.div`
15 | display: flex;
16 | flex-direction: column;
17 | align-items: center;
18 | gap: 0.5rem;
19 | width: 100%;
20 | padding: 4rem 0;
21 | `;
22 |
23 | export const Title = styled.div`
24 | font-size: ${fontSize.h4};
25 | line-height: ${lineHeight.h4};
26 | font-weight: ${fontWeight.bold};
27 | color: ${(props) => titleColor[props.theme]};
28 |
29 | @media screen and (max-width: 530px) {
30 | font-size: ${fontSize.lg};
31 | }
32 | `;
33 |
34 | export const Content = styled.div`
35 | display: -webkit-box;
36 | overflow: hidden;
37 | height: 3rem;
38 |
39 | color: ${(props) => contentColor[props.theme]};
40 | font-weight: ${fontWeight.regular};
41 | font-size: ${fontSize.p};
42 | line-height: ${lineHeight.p};
43 | text-overflow: ellipsis;
44 | `;
45 |
--------------------------------------------------------------------------------
/src/containers/CommunityPage/CommunitySlider/CommunitySlider.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { FeedSmallSlider } from "@/components/layouts";
3 |
4 | const CommunitySlider = ({ ...props }) => {
5 | return ;
6 | };
7 |
8 | const data = [
9 | {
10 | boardId: 1,
11 | writerInfo: {
12 | nickname: "관리자",
13 | ordinal: "",
14 | campus: "",
15 | authCheck: 0,
16 | avatar: "",
17 | },
18 | title: "더 좋은 커뮤니티를 만들기 위한 약속 🤙",
19 | category: "공지",
20 | },
21 | {
22 | boardId: 5,
23 | writerInfo: {
24 | nickname: "아이유",
25 | ordinal: "6기",
26 | campus: "서울",
27 | authCheck: 0,
28 | avatar:
29 | "https://image.bada.io/files//crawling/2021/04/05/bobaedream/1612930_i14788674553.jpg",
30 | },
31 | title: "겨울잠",
32 | category: "자유게시판",
33 | },
34 | {
35 | boardId: 10,
36 | writerInfo: {
37 | nickname: "익명",
38 | ordinal: null,
39 | campus: null,
40 | authCheck: 0,
41 | avatar: "",
42 | },
43 | title: "더 좋은 커뮤니티를 만들기 위한 약속 🤙",
44 | category: "익명게시판",
45 | },
46 | ];
47 |
48 | export default CommunitySlider;
49 |
--------------------------------------------------------------------------------
/src/components/layouts/Navbar/NavMobile/NavMobileItem/NavVerticalItem.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { NavMobileItem } from ".";
4 | import { Background } from "@/foundations";
5 |
6 | export default {
7 | title: "layouts/Navbar/NavMobile/NavMobileItem",
8 | component: NavMobileItem,
9 | };
10 |
11 | export const Default = (args) => (
12 |
13 |
14 |
15 | );
16 |
17 | Default.args = {
18 | children: "Menu",
19 | };
20 |
21 | export const AllTypes = () => (
22 | <>
23 | Light theme
24 |
25 |
26 |
27 | Active
28 |
29 |
30 | Default
31 |
32 |
33 |
34 | Dark theme
35 |
36 |
37 |
38 | Active
39 |
40 |
41 | Default
42 |
43 |
44 | >
45 | );
46 |
--------------------------------------------------------------------------------
/src/containers/Common/Navbar/NavMobile/NavMobileItem/NavVerticalItem.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { NavMobileItem } from ".";
4 | import { Background } from "@/foundations";
5 |
6 | export default {
7 | title: "layouts/Navbar/NavMobile/NavMobileItem",
8 | component: NavMobileItem,
9 | };
10 |
11 | export const Default = (args) => (
12 |
13 |
14 |
15 | );
16 |
17 | Default.args = {
18 | children: "Menu",
19 | };
20 |
21 | export const AllTypes = () => (
22 | <>
23 | Light theme
24 |
25 |
26 |
27 | Active
28 |
29 |
30 | Default
31 |
32 |
33 |
34 | Dark theme
35 |
36 |
37 |
38 | Active
39 |
40 |
41 | Default
42 |
43 |
44 | >
45 | );
46 |
--------------------------------------------------------------------------------
/src/containers/JobPage/JobNoContent/JobNoContent.styled.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { colors, fontSize, fontWeight, lineHeight } from "@/_shared";
3 |
4 | const titleColor = {
5 | dark: colors.gray25,
6 | light: colors.gray900,
7 | };
8 |
9 | const contentColor = {
10 | dark: colors.gray400,
11 | light: colors.gray600,
12 | };
13 |
14 | export const Layout = styled.div`
15 | display: flex;
16 | flex-direction: column;
17 | align-items: center;
18 | gap: 0.5rem;
19 |
20 | max-width: 940px;
21 | width: 100%;
22 | padding: 4rem 0;
23 | `;
24 |
25 | export const Title = styled.div`
26 | font-size: ${fontSize.h4};
27 | line-height: ${lineHeight.h4};
28 | font-weight: ${fontWeight.bold};
29 | color: ${(props) => titleColor[props.theme]};
30 |
31 | @media screen and (max-width: 530px) {
32 | font-size: ${fontSize.lg};
33 | }
34 | `;
35 |
36 | export const Content = styled.div`
37 | display: -webkit-box;
38 | overflow: hidden;
39 | height: 3rem;
40 |
41 | color: ${(props) => contentColor[props.theme]};
42 | font-weight: ${fontWeight.regular};
43 | font-size: ${fontSize.p};
44 | line-height: ${lineHeight.p};
45 | text-overflow: ellipsis;
46 | `;
47 |
--------------------------------------------------------------------------------