diff --git a/frontend/src/pages/host/DashBoard/styles.ts b/frontend/src/pages/host/DashBoard/styles.ts
index 84c50f25..ef023e34 100644
--- a/frontend/src/pages/host/DashBoard/styles.ts
+++ b/frontend/src/pages/host/DashBoard/styles.ts
@@ -3,9 +3,16 @@ import { css } from '@emotion/react';
import theme from '@/styles/theme';
const layout = css`
- padding: 32px 48px;
display: flex;
flex-direction: column;
+
+ @media screen and (min-width: 1024px) {
+ padding: 32px 48px;
+ }
+
+ @media screen and (max-width: 1023px) {
+ padding: 32px;
+ }
`;
const contents = css`
@@ -19,27 +26,49 @@ const contents = css`
const cardWrapper = css`
display: flex;
width: 100%;
- justify-content: space-around;
gap: 20px;
- margin-bottom: 32px;
+ margin-bottom: 20px;
+
+ @media screen and (min-width: 1024px) {
+ flex-direction: row;
+ justify-content: space-around;
+ }
+
+ @media screen and (max-width: 1023px) {
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ }
`;
const linkButton = css`
width: auto;
height: 2rem;
padding: 0 12px;
- font-weight: 500;
- font-size: 1rem;
+ font-size: 0.9rem;
margin: 0 0 16px 12px;
background-color: ${theme.colors.green};
color: ${theme.colors.white};
box-shadow: 0px 0px 1px 1px ${theme.colors.shadow10};
svg {
- height: 14px;
- width: 14px;
- margin-right: 6px;
- transform: translateY(2px);
+ height: 20px;
+ width: 20px;
+ transform: translateY(3px);
+ }
+
+ @media screen and (min-width: 1024px) {
+ svg {
+ height: 16px;
+ width: 16px;
+ margin-right: 4px;
+ }
+ }
+
+ @media screen and (max-width: 1023px) {
+ span {
+ display: none;
+ }
}
`;
@@ -47,27 +76,76 @@ const slackButton = css`
width: auto;
height: 2rem;
padding: 0 12px;
- font-weight: 500;
- font-size: 1rem;
+ font-size: 0.9rem;
margin: 0 0 16px 12px;
background-color: ${theme.colors.white};
color: ${theme.colors.black};
box-shadow: 0px 0px 1px 1px ${theme.colors.shadow30};
img {
- height: 14px;
- width: 14px;
- margin-right: 6px;
+ height: 18px;
+ width: 18px;
transform: translateY(2px);
}
+
+ @media screen and (min-width: 1024px) {
+ img {
+ height: 14px;
+ width: 14px;
+ margin-right: 6px;
+ }
+ }
+
+ @media screen and (max-width: 1023px) {
+ span {
+ display: none;
+ }
+ }
`;
const buttons = css`
- width: 100%;
display: flex;
justify-content: end;
+
+ @media screen and (min-width: 1024px) {
+ width: 100%;
+ }
+
+ @media screen and (max-width: 1023px) {
+ width: 90%;
+ }
+`;
+
+const spaceDeleteButton = css`
+ width: auto;
+ height: 2rem;
+ padding: 0 12px;
+ font-size: 0.9rem;
+ margin: 0 0 16px 12px;
+ background-color: ${theme.colors.red};
+ color: ${theme.colors.white};
+
+ svg {
+ height: 24px;
+ width: 24px;
+ transform: translateY(3px);
+ }
+
+ @media screen and (min-width: 1024px) {
+ svg {
+ height: 16px;
+ width: 16px;
+ margin-right: 4px;
+ }
+ }
+
+ @media screen and (max-width: 1023px) {
+ span {
+ display: none;
+ }
+ }
`;
-const styles = { layout, contents, cardWrapper, slackButton, linkButton, buttons };
+const styles = { layout, contents, cardWrapper, slackButton, linkButton, buttons, spaceDeleteButton };
export default styles;
diff --git a/frontend/src/pages/host/DashBoard/useDashBoard.tsx b/frontend/src/pages/host/DashBoard/useDashBoard.tsx
index 72d8be26..57eebc25 100644
--- a/frontend/src/pages/host/DashBoard/useDashBoard.tsx
+++ b/frontend/src/pages/host/DashBoard/useDashBoard.tsx
@@ -1,7 +1,9 @@
-import { useQuery } from 'react-query';
+import copyUrlToClipboard from '@/utils/copyUrlToClipboard';
+import { useMutation, useQuery } from 'react-query';
import { useNavigate, useParams } from 'react-router-dom';
import SlackUrlModal from '@/components/host/SlackUrlModal';
+import SpaceDeleteModal from '@/components/host/SpaceDeleteModal';
import useModal from '@/hooks/useModal';
import useToast from '@/hooks/useToast';
@@ -16,7 +18,7 @@ import { ID } from '@/types';
const useDashBoard = () => {
const navigate = useNavigate();
- const { openModal } = useModal();
+ const { openModal, closeModal } = useModal();
const { openToast } = useToast();
const { spaceId } = useParams() as { spaceId: ID };
@@ -27,15 +29,30 @@ const useDashBoard = () => {
});
const { data: jobsData } = useQuery(['jobs', spaceId], () => apiJobs.getJobs(spaceId));
const { data: submissionData } = useQuery(['submissions', spaceId], () => apiSubmission.getSubmission(spaceId));
- const { refetch: copyEntranceLink } = useQuery(['entranceCode'], () => apiHost.getEntranceCode(), {
+ const { data: entranceCodeData } = useQuery(['entranceCode'], () => apiHost.getEntranceCode(), {
suspense: false,
+ });
+
+ const { refetch } = useQuery(['deleteSpaces'], apiSpace.getSpaces, {
enabled: false,
onSuccess: data => {
- navigator.clipboard.writeText(`${location.origin}/enter/${data.entranceCode}/pwd`);
- openToast('SUCCESS', '๊ณต๊ฐ ์
์ฅ ๋งํฌ๊ฐ ๋ณต์ฌ๋์์ต๋๋ค.');
+ const { spaces } = data;
+
+ if (spaces.length === 0) {
+ navigate('/host/manage/spaceCreate');
+ return;
+ }
+
+ const space = spaces[0];
+ navigate(`/host/manage/${space.id}`);
},
- onError: () => {
- openToast('ERROR', '์ ์ ํ ๋ค์ ์๋ํด์ฃผ์ธ์.');
+ });
+
+ const { mutate: deleteSpace } = useMutation((spaceId: ID) => apiSpace.deleteSpace(spaceId), {
+ onSuccess: () => {
+ refetch();
+ closeModal();
+ openToast('SUCCESS', '๊ณต๊ฐ์ด ์ญ์ ๋์์ต๋๋ค.');
},
});
@@ -44,7 +61,18 @@ const useDashBoard = () => {
};
const onClickLinkButton = () => {
- copyEntranceLink();
+ if (entranceCodeData) {
+ copyUrlToClipboard(`${location.origin}/enter/${entranceCodeData.entranceCode}/pwd`);
+ openToast('SUCCESS', '๊ณต๊ฐ ์
์ฅ ๋งํฌ๊ฐ ๋ณต์ฌ๋์์ต๋๋ค.');
+ return;
+ }
+ openToast('ERROR', '๋ค์ ์๋ํด์ฃผ์ธ์.');
+ };
+
+ const onClickDeleteSpace = () => {
+ openModal(
+
deleteSpace(spaceId)} />
+ );
};
return {
@@ -54,6 +82,7 @@ const useDashBoard = () => {
submissionData,
onClickSlackButton,
onClickLinkButton,
+ onClickDeleteSpace,
};
};
diff --git a/frontend/src/pages/host/Home/index.tsx b/frontend/src/pages/host/Home/index.tsx
index e3e6feca..ae4ad6e0 100644
--- a/frontend/src/pages/host/Home/index.tsx
+++ b/frontend/src/pages/host/Home/index.tsx
@@ -1,79 +1,20 @@
-import { css } from '@emotion/react';
-
import GitHubLoginButton from '@/components/common/GitHubLoginButton';
import homeCover from '@/assets/homeCover.png';
+import homeCoverWebp from '@/assets/homeCover.webp';
import logoTitle from '@/assets/logoTitle.png';
+import logoTitleWebp from '@/assets/logoTitle.webp';
-import theme from '@/styles/theme';
+import styles from './styles';
const Home: React.FC = () => {
- const isMobile = innerWidth < 600;
-
return (
<>
- b {
- font-size: 16px;
- }
- `}
- >
-

- {!isMobile && (
-
- for ๊ณต๊ฐ ๊ด๋ฆฌ์
-
- )}
+
+
-
- {isMobile && (
-
- ๋ชจ๋ฐ์ผ์ ์ต์ ํ๋์ด์์ง ์์ต๋๋ค. ๋ฐ์คํฌํ์ ์ด์ฉํด์ฃผ์ธ์.
-
- )}
-

-
- ๊ด๋ฆฌ์ ํ์ด์ง ์ด์ฉ์ ์ํด ๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค.
-
+
+
>
diff --git a/frontend/src/pages/host/Home/styles.ts b/frontend/src/pages/host/Home/styles.ts
new file mode 100644
index 00000000..9be017a5
--- /dev/null
+++ b/frontend/src/pages/host/Home/styles.ts
@@ -0,0 +1,38 @@
+import { css } from '@emotion/react';
+
+import theme from '@/styles/theme';
+
+const header = css`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100vw;
+ height: 64px;
+ background-color: ${theme.colors.white};
+ box-shadow: 0px 2px 2px 2px ${theme.colors.shadow20};
+
+ img {
+ height: 40px;
+ transform: translateY(4px);
+ aspect-ratio: auto 5 / 1;
+ }
+`;
+
+const layout = css`
+ display: flex;
+ padding: 48px 0 64px 0;
+ height: calc(100vh - 64px);
+ flex-direction: column;
+ align-items: center;
+ justify-content: space-around;
+
+ img {
+ width: 80%;
+ max-width: 420px;
+ aspect-ratio: auto 1 / 1;
+ }
+`;
+
+const styles = { header, layout };
+
+export default styles;
diff --git a/frontend/src/pages/host/JobCreate/styles.ts b/frontend/src/pages/host/JobCreate/styles.ts
index 28af5b5e..5df11450 100644
--- a/frontend/src/pages/host/JobCreate/styles.ts
+++ b/frontend/src/pages/host/JobCreate/styles.ts
@@ -11,10 +11,10 @@ const layout = css`
`;
const contents = css`
- width: 95%;
+ width: 100%;
min-height: 100%;
background-color: ${theme.colors.white};
- min-width: 360px;
+ min-width: 356px;
`;
const grid = css`
@@ -33,7 +33,7 @@ const createCard = css`
align-items: center;
width: 100%;
max-width: 480px;
- height: 360px;
+ height: 356px;
background-color: ${theme.colors.gray200};
font-size: 64px;
border-radius: 8px;
@@ -43,26 +43,6 @@ const createCard = css`
}
`;
-const createButton = css`
- background-color: ${theme.colors.green};
-`;
-
-const jobNameInput = css`
- border: none;
- border-radius: 12px;
- width: 50%;
- height: 48px;
- padding: 8px 16px;
- font-size: 28px;
- font-weight: 500;
- margin: 12px 0;
- background-color: ${theme.colors.shadow20};
-
- :focus {
- outline: 2px solid ${theme.colors.shadow30};
- }
-`;
-
-const styles = { layout, contents, grid, createCard, createButton, jobNameInput };
+const styles = { layout, contents, grid, createCard };
export default styles;
diff --git a/frontend/src/pages/host/JobUpdate/index.tsx b/frontend/src/pages/host/JobUpdate/index.tsx
index 37b44568..1aedf48e 100644
--- a/frontend/src/pages/host/JobUpdate/index.tsx
+++ b/frontend/src/pages/host/JobUpdate/index.tsx
@@ -3,8 +3,6 @@ import useJobUpdate from './useJobUpdate';
import JobControl from '@/components/host/JobControl';
import SectionCard from '@/components/host/SectionCard';
-import useSections from '@/hooks/useSections';
-
import styles from './styles';
const JobUpdate: React.FC = () => {
diff --git a/frontend/src/pages/host/JobUpdate/styles.ts b/frontend/src/pages/host/JobUpdate/styles.ts
index 28af5b5e..e77fdb68 100644
--- a/frontend/src/pages/host/JobUpdate/styles.ts
+++ b/frontend/src/pages/host/JobUpdate/styles.ts
@@ -14,7 +14,7 @@ const contents = css`
width: 95%;
min-height: 100%;
background-color: ${theme.colors.white};
- min-width: 360px;
+ min-width: 356px;
`;
const grid = css`
@@ -33,7 +33,7 @@ const createCard = css`
align-items: center;
width: 100%;
max-width: 480px;
- height: 360px;
+ height: 356px;
background-color: ${theme.colors.gray200};
font-size: 64px;
border-radius: 8px;
@@ -43,26 +43,6 @@ const createCard = css`
}
`;
-const createButton = css`
- background-color: ${theme.colors.green};
-`;
-
-const jobNameInput = css`
- border: none;
- border-radius: 12px;
- width: 50%;
- height: 48px;
- padding: 8px 16px;
- font-size: 28px;
- font-weight: 500;
- margin: 12px 0;
- background-color: ${theme.colors.shadow20};
-
- :focus {
- outline: 2px solid ${theme.colors.shadow30};
- }
-`;
-
-const styles = { layout, contents, grid, createCard, createButton, jobNameInput };
+const styles = { layout, contents, grid, createCard };
export default styles;
diff --git a/frontend/src/pages/host/Landing/index.tsx b/frontend/src/pages/host/Landing/index.tsx
new file mode 100644
index 00000000..858e9ad2
--- /dev/null
+++ b/frontend/src/pages/host/Landing/index.tsx
@@ -0,0 +1,35 @@
+import React, { useRef } from 'react';
+
+import FloatingActionButton from '@/components/host/LandingView/FloatingActionButton';
+import HeroSection from '@/components/host/LandingView/HeroSection';
+import HostViewSection1 from '@/components/host/LandingView/HostViewSection1';
+import HostViewSection2 from '@/components/host/LandingView/HostViewSection2';
+import HostViewSection3 from '@/components/host/LandingView/HostViewSection3';
+import UserViewSection1 from '@/components/host/LandingView/UserViewSection1';
+import UserViewSection2 from '@/components/host/LandingView/UserViewSection2';
+import UserViewSection3 from '@/components/host/LandingView/UserViewSection3';
+
+import useResizeScreen from '@/hooks/useResizeScreen';
+
+import styles from './styles';
+
+const Landing: React.FC = () => {
+ const mainRef = useRef
(null);
+
+ const { screenMode } = useResizeScreen();
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Landing;
diff --git a/frontend/src/pages/host/Landing/styles.ts b/frontend/src/pages/host/Landing/styles.ts
new file mode 100644
index 00000000..e8f7e94f
--- /dev/null
+++ b/frontend/src/pages/host/Landing/styles.ts
@@ -0,0 +1,12 @@
+import { css } from '@emotion/react';
+
+import theme from '@/styles/theme';
+
+const layout = css`
+ width: 100vw;
+ background-color: ${theme.colors.background};
+`;
+
+const styles = { layout };
+
+export default styles;
diff --git a/frontend/src/pages/host/PasswordUpdate/index.tsx b/frontend/src/pages/host/PasswordUpdate/index.tsx
index d42fe01c..1cea1b2c 100644
--- a/frontend/src/pages/host/PasswordUpdate/index.tsx
+++ b/frontend/src/pages/host/PasswordUpdate/index.tsx
@@ -1,6 +1,7 @@
import usePasswordUpdate from './usePasswordUpdate';
-import { FiEyeOff, FiEye } from 'react-icons/fi';
-import { RiLockPasswordLine } from 'react-icons/ri';
+import { FiEye } from '@react-icons/all-files/fi/FiEye';
+import { FiEyeOff } from '@react-icons/all-files/fi/FiEyeOff';
+import { RiLockPasswordLine } from '@react-icons/all-files/ri/RiLockPasswordLine';
import Button from '@/components/common/Button';
diff --git a/frontend/src/pages/host/PasswordUpdate/styles.ts b/frontend/src/pages/host/PasswordUpdate/styles.ts
index cb069360..c18c3568 100644
--- a/frontend/src/pages/host/PasswordUpdate/styles.ts
+++ b/frontend/src/pages/host/PasswordUpdate/styles.ts
@@ -12,7 +12,7 @@ const layout = css`
svg + span {
color: ${theme.colors.shadow50};
- font-size: 24px;
+ font-size: 16px;
margin: 24px 0 32px 0;
}
svg {
@@ -28,12 +28,12 @@ const content = css`
const inputWrapper = css`
width: 280px;
- height: 48px;
+ height: 36px;
display: flex;
justify-content: space-around;
align-items: center;
background-color: ${theme.colors.gray200};
- border: 2px solid ${theme.colors.gray400};
+ border: 1px solid ${theme.colors.gray400};
border-radius: 8px;
padding: 0 12px;
@@ -43,7 +43,7 @@ const inputWrapper = css`
background-color: ${theme.colors.gray200};
border: none;
outline: none;
- font-size: 20px;
+ font-size: 16px;
color: ${theme.colors.shadow80};
}
@@ -60,6 +60,7 @@ const inputWrapper = css`
const button = css`
margin: 0 0 0 8px;
width: auto;
+ height: 36px;
padding: 0 16px;
`;
diff --git a/frontend/src/pages/host/SpaceCreate/index.tsx b/frontend/src/pages/host/SpaceCreate/index.tsx
index e1e88296..6b0dceab 100644
--- a/frontend/src/pages/host/SpaceCreate/index.tsx
+++ b/frontend/src/pages/host/SpaceCreate/index.tsx
@@ -1,4 +1,4 @@
-import SpaceInfoCreateBox from '@/components/host/SpaceInfoCreateBox';
+import SpaceInfoCreateBox from '@/components/host/SpaceInfo/SpaceInfoCreateBox';
import styles from './styles';
diff --git a/frontend/src/pages/host/SpaceCreate/styles.ts b/frontend/src/pages/host/SpaceCreate/styles.ts
index 917b604d..221dd35c 100644
--- a/frontend/src/pages/host/SpaceCreate/styles.ts
+++ b/frontend/src/pages/host/SpaceCreate/styles.ts
@@ -1,26 +1,16 @@
import { css } from '@emotion/react';
const layout = css`
- width: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
-`;
-
-const contents = css`
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
-`;
-const text = css`
- font-size: 2rem;
- font-weight: bold;
- margin: 0;
+ @media screen and (max-width: 1023px) {
+ height: calc(100vh - 64px);
+ }
`;
-const styles = { layout, contents, text };
+const styles = { layout };
export default styles;
diff --git a/frontend/src/pages/host/SpaceUpdate/index.tsx b/frontend/src/pages/host/SpaceUpdate/index.tsx
index 0b1953d5..aa2cfb4a 100644
--- a/frontend/src/pages/host/SpaceUpdate/index.tsx
+++ b/frontend/src/pages/host/SpaceUpdate/index.tsx
@@ -1,7 +1,7 @@
import { useQuery } from 'react-query';
import { useParams } from 'react-router-dom';
-import SpaceInfoUpdateBox from '@/components/host/SpaceInfoUpdateBox';
+import SpaceInfoUpdateBox from '@/components/host/SpaceInfo/SpaceInfoUpdateBox';
import apiSpace from '@/apis/space';
diff --git a/frontend/src/pages/host/SpaceUpdate/styles.ts b/frontend/src/pages/host/SpaceUpdate/styles.ts
index a0f978eb..f52e239b 100644
--- a/frontend/src/pages/host/SpaceUpdate/styles.ts
+++ b/frontend/src/pages/host/SpaceUpdate/styles.ts
@@ -6,6 +6,10 @@ const layout = css`
display: flex;
justify-content: center;
align-items: center;
+
+ @media screen and (max-width: 1023px) {
+ height: calc(100vh - 64px);
+ }
`;
const styles = { layout };
diff --git a/frontend/src/pages/user/JobList/index.tsx b/frontend/src/pages/user/JobList/index.tsx
index 558fa219..12fb8476 100644
--- a/frontend/src/pages/user/JobList/index.tsx
+++ b/frontend/src/pages/user/JobList/index.tsx
@@ -1,8 +1,10 @@
import useJobList from './useJobList';
-import { IoIosArrowBack } from 'react-icons/io';
+import { IoIosArrowBack } from '@react-icons/all-files/io/IoIosArrowBack';
import JobCard from '@/components/user/JobCard';
+import DEFAULT_IMAGE from '@/assets/defaultSpaceImage.webp';
+
import styles from './styles';
const JobList: React.FC = () => {
@@ -10,11 +12,10 @@ const JobList: React.FC = () => {
return (
-
-
+
+
{spaceData?.name}
-
์ฒดํฌํ์ค ์
๋ฌด๋ฅผ ์ ํํด์ฃผ์ธ์.
{jobsData?.jobs.length === 0 ? (
๊ด๋ฆฌ์๊ฐ ์์ฑํ ์
๋ฌด๊ฐ ์์ด์
) : (
diff --git a/frontend/src/pages/user/JobList/styles.ts b/frontend/src/pages/user/JobList/styles.ts
index 90b1afaa..6ccf5d8a 100644
--- a/frontend/src/pages/user/JobList/styles.ts
+++ b/frontend/src/pages/user/JobList/styles.ts
@@ -26,6 +26,7 @@ const coverText = css`
text-shadow: 0 0 4px ${theme.colors.black};
background-image: linear-gradient(transparent 90%, ${theme.colors.primary} 10%);
width: fit-content;
+ margin-left: 16px;
`;
const text = css`
diff --git a/frontend/src/pages/user/Password/index.tsx b/frontend/src/pages/user/Password/index.tsx
index d2ef3907..08dfa017 100644
--- a/frontend/src/pages/user/Password/index.tsx
+++ b/frontend/src/pages/user/Password/index.tsx
@@ -3,6 +3,7 @@ import usePassword from './usePassword';
import Button from '@/components/common/Button';
import homeCover from '@/assets/homeCover.png';
+import homeCoverWebp from '@/assets/homeCover.webp';
import styles from './styles';
@@ -12,7 +13,7 @@ const Password: React.FC = () => {
return (
-

+
๋น๋ฐ๋ฒํธ๋ฅผ ์
๋ ฅํด์ฃผ์ธ์.
@@ -26,6 +27,7 @@ const Password: React.FC = () => {
{
return (
-

-
์ฌ์ฉํ์ค ๊ณต๊ฐ์ ์ ํํด์ฃผ์ธ์.
+

+
ํ์ฌ ๋จธ๋ฌด๋ฅด๋ ๊ณต๊ฐ์ ์ ํํด์ฃผ์ธ์.
{spaceData?.spaces.length === 0 ? (
๊ด๋ฆฌ์๊ฐ ์์ฑํ ๊ณต๊ฐ ์์ด์
) : (
spaceData?.spaces.map(space => (
-
+
))
)}
{}
diff --git a/frontend/src/pages/user/SpaceList/styles.ts b/frontend/src/pages/user/SpaceList/styles.ts
index 17747515..050b56e6 100644
--- a/frontend/src/pages/user/SpaceList/styles.ts
+++ b/frontend/src/pages/user/SpaceList/styles.ts
@@ -10,8 +10,9 @@ const layout = css`
`;
const logo = css`
- width: 248px;
+ width: 264px;
margin: 32px 0;
+ aspect-ratio: auto 5 / 1;
`;
const text = css`
diff --git a/frontend/src/pages/user/TaskList/index.tsx b/frontend/src/pages/user/TaskList/index.tsx
index 10efe0f6..645da54f 100644
--- a/frontend/src/pages/user/TaskList/index.tsx
+++ b/frontend/src/pages/user/TaskList/index.tsx
@@ -1,10 +1,12 @@
import useTaskList from './useTaskList';
+import { IoIosArrowBack } from '@react-icons/all-files/io/IoIosArrowBack';
import React from 'react';
-import { IoIosArrowBack } from 'react-icons/io';
import Button from '@/components/common/Button';
-import SectionInfoPreview from '@/components/user/SectionInfoPreview';
-import TaskCard from '@/components/user/TaskCard';
+import Sticky from '@/components/common/Sticky';
+import SectionCard from '@/components/user/SectionCard';
+
+import DEFAULT_IMAGE from '@/assets/defaultSpaceImage.webp';
import styles from './styles';
@@ -22,57 +24,43 @@ const TaskList: React.FC = () => {
sectionsData,
onClickSectionDetail,
onClickSectionAllCheck,
- progressBarRef,
- isActiveSticky,
} = useTaskList();
return (
-
+
-
-
-
{spaceData?.name}
-
{locationState?.jobName}
+
+
+
+
{spaceData?.name}
+
{locationState?.jobName}
+
-
+
{`${checkedCount}/${totalCount}`}
-
-
+
+
+
);
};
diff --git a/frontend/src/pages/user/TaskList/styles.ts b/frontend/src/pages/user/TaskList/styles.ts
index 5563dff7..df6acb1f 100644
--- a/frontend/src/pages/user/TaskList/styles.ts
+++ b/frontend/src/pages/user/TaskList/styles.ts
@@ -8,54 +8,36 @@ const layout = css`
width: 100%;
align-items: center;
font-size: 16px;
- padding: 0 0 32px 0;
+ padding-bottom: 32px;
`;
const contents = css`
display: flex;
flex-direction: column;
- width: 100%;
+ width: 80%;
align-items: center;
margin-top: 16px;
`;
-const location = css`
- margin: 16px 0;
- padding: 24px 16px;
- border: 1px solid ${theme.colors.shadow30};
- border-radius: 24px;
- box-shadow: 2px 2px 2px 0px ${theme.colors.shadow30};
-`;
+const header = css`
+ position: relative;
+ width: 100%;
-const locationHeader = css`
display: flex;
- justify-content: space-between;
+ justify-content: space-evenly;
align-items: center;
- padding: 0 8px;
- min-height: 48px;
-`;
-
-const locationName = css`
- font-size: 24px;
- font-weight: 600;
- margin: 0;
-`;
-
-const locationHeaderRightItems = css`
- display: flex;
- gap: 10px;
+ padding: 4em 0 3em 0;
`;
-const header = css`
- position: relative;
+const headerInfo = css`
+ max-width: 420px;
width: 100%;
display: flex;
justify-content: space-evenly;
align-items: center;
- padding: 4em 0 3em 0;
`;
-const thumbnail = (imageUrl: string | undefined) => css`
+const thumbnail = (imageUrl: string) => css`
width: 120px;
height: 120px;
background-image: url(${imageUrl});
@@ -63,29 +45,30 @@ const thumbnail = (imageUrl: string | undefined) => css`
background-repeat: no-repeat;
background-size: cover;
border-radius: 40%;
+ box-shadow: 2px 2px 6px 0px ${theme.colors.shadow60};
`;
const infoWrapper = css`
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+
p {
margin: 0;
}
- p:nth-of-type(1) {
- font-weight: 500;
- font-size: 1em;
- }
-
p:nth-of-type(2) {
- font-size: 1.5em;
+ font-size: 1.8em;
font-weight: bold;
- margin-top: 0.5em;
+ margin-top: 0.3em;
}
`;
-const arrowBackIconWrapper = css`
+const arrowBackIcon = css`
position: absolute;
- top: 20px;
- left: 20px;
+ top: 16px;
+ left: 16px;
svg {
color: ${theme.colors.black};
@@ -93,26 +76,13 @@ const arrowBackIconWrapper = css`
}
`;
-const progressBarWrapperSticky = (isSticked: boolean | undefined) => css`
- position: sticky;
- top: 0;
- padding-top: 16px;
- width: 100%;
- height: 100%;
- display: flex;
- justify-content: center;
- align-items: center;
- background-color: ${theme.colors.white};
- z-index: 1;
-
- box-shadow: ${isSticked ? `2px 2px 2px 0px ${theme.colors.shadow30}` : ''};
-`;
-
const progressBarWrapper = css`
+ background-color: ${theme.colors.white};
box-shadow: 2px 2px 4px 0px ${theme.colors.shadow40};
border: 1px solid ${theme.colors.green};
border-radius: 4px;
height: 30px;
+ max-width: 380px;
width: 80%;
position: relative;
margin-bottom: 16px;
@@ -175,38 +145,18 @@ const button = (isAllChecked: boolean) =>
background: ${isAllChecked ? theme.colors.primary : theme.colors.gray400};
`;
-const form = css`
- display: flex;
- flex-direction: column;
- align-items: center;
-`;
-
-const sectionAllCheckButton = css`
- width: 40px;
- height: 40px;
- margin: 0;
- padding: 0;
- box-shadow: 2px 2px 2px 0px ${theme.colors.shadow30};
-`;
-
const styles = {
layout,
- contents,
- location,
- locationHeader,
- locationName,
- locationHeaderRightItems,
- arrowBackIconWrapper,
header,
+ headerInfo,
thumbnail,
+ arrowBackIcon,
infoWrapper,
- progressBarWrapperSticky,
progressBarWrapper,
progressBar,
percentText,
+ contents,
button,
- sectionAllCheckButton,
- form,
};
export default styles;
diff --git a/frontend/src/pages/user/TaskList/useTaskList.tsx b/frontend/src/pages/user/TaskList/useTaskList.tsx
index 811ffccd..d6b2e791 100644
--- a/frontend/src/pages/user/TaskList/useTaskList.tsx
+++ b/frontend/src/pages/user/TaskList/useTaskList.tsx
@@ -1,13 +1,14 @@
import { EventSourcePolyfill } from 'event-source-polyfill';
-import { useEffect, useRef, useState } from 'react';
+import { useEffect, useState } from 'react';
import { useMutation, useQuery } from 'react-query';
-import { useLocation, useNavigate, useParams } from 'react-router-dom';
+import { useLocation, useParams } from 'react-router-dom';
import DetailInfoModal from '@/components/user/DetailInfoModal';
import NameModal from '@/components/user/NameModal';
import useGoPreviousPage from '@/hooks/useGoPreviousPage';
import useModal from '@/hooks/useModal';
+import useScroll from '@/hooks/useScroll';
import useSectionCheck from '@/hooks/useSectionCheck';
import useToast from '@/hooks/useToast';
@@ -17,25 +18,18 @@ import { ID, SectionType } from '@/types';
import { ApiTaskData } from '@/types/apis';
const API_URL = process.env.REACT_APP_API_URL || 'http://localhost:8080';
-const PROGRESS_BAR_DEFAULT_POSITION = 232;
const useTaskList = () => {
- const navigate = useNavigate();
-
- const { spaceId, jobId, hostId } = useParams() as { spaceId: ID; jobId: ID; hostId: ID };
+ const { spaceId, jobId } = useParams() as { spaceId: ID; jobId: ID };
const location = useLocation();
const locationState = location.state as { jobName: string };
- const { openModal } = useModal();
+ const { openModal, closeModal } = useModal();
const { openToast } = useToast();
const { goPreviousPage } = useGoPreviousPage();
- const progressBarRef = useRef
(null);
-
- const [isActiveSticky, setIsActiveSticky] = useState(false);
-
const [sectionsData, setSectionsData] = useState({
sections: [
{
@@ -77,12 +71,6 @@ const useTaskList = () => {
postSectionAllCheck(sectionId);
};
- useEffect(() => {
- const isActive = progressBarRef.current?.offsetTop! > PROGRESS_BAR_DEFAULT_POSITION;
-
- setIsActiveSticky(isActive);
- }, [progressBarRef.current?.offsetTop]);
-
useEffect(() => {
const tokenKey = sessionStorage.getItem('tokenKey');
if (!tokenKey) return;
@@ -106,9 +94,12 @@ const useTaskList = () => {
});
sse.addEventListener('submit', () => {
- navigate(`/enter/${hostId}/spaces/${spaceId}`);
+ closeModal();
openToast('SUCCESS', 'ํด๋น ์ฒดํฌ๋ฆฌ์คํธ๋ ์ ์ถ๋์์ต๋๋ค.');
+ goPreviousPage();
});
+
+ return () => sse.close();
}, []);
return {
@@ -124,8 +115,6 @@ const useTaskList = () => {
sectionsData,
onClickSectionDetail,
onClickSectionAllCheck,
- progressBarRef,
- isActiveSticky,
};
};
diff --git a/frontend/src/styles/animation.ts b/frontend/src/styles/animation.ts
index 08e8e88b..3cc404ea 100644
--- a/frontend/src/styles/animation.ts
+++ b/frontend/src/styles/animation.ts
@@ -78,6 +78,28 @@ const moveUp = keyframes`
}
`;
+const scaleUp = keyframes`
+ from {
+ transform: scale(0.7);
+ }
+ to {
+ transform: scale(1);
+ }
+`;
+
+const scaleDown = keyframes`
+ from {
+ opacity: 1;
+ transform: scale(1);
+
+ }
+ to {
+ opacity: 0;
+ transform: scale(0.7);
+
+ }
+`;
+
const customMoveUp = (y: string) => keyframes`
0% {
opacity: 0;
@@ -136,6 +158,24 @@ const wave = keyframes`
transform: skew(2deg, 2deg);
}`;
+const navigatorOpen = keyframes`
+ from {
+ transform: translateX(-100%);
+ }
+ to {
+ transform: translateX(0)
+ }
+`;
+
+const navigatorClose = keyframes`
+ from {
+ transform: translateX(0);
+ }
+ to {
+ transform: translateX(-100%)
+ }
+`;
+
const animation = {
fadeIn,
fadeOut,
@@ -149,6 +189,10 @@ const animation = {
moveRight,
moveLeft,
wave,
+ scaleUp,
+ scaleDown,
+ navigatorOpen,
+ navigatorClose,
};
export default animation;
diff --git a/frontend/src/styles/global.ts b/frontend/src/styles/global.ts
index b8aa8afa..9e24784f 100644
--- a/frontend/src/styles/global.ts
+++ b/frontend/src/styles/global.ts
@@ -7,11 +7,12 @@ const globalStyle = css`
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_2001@1.1/GmarketSansMedium.woff') format('woff');
font-weight: 0;
font-style: normal;
+ font-display: swap;
}
* {
box-sizing: border-box;
- font-family: '์ง๋ง์ผ';
+ font-family: '์ง๋ง์ผ', 'Noto Sans Korean', 'Roboto';
}
a {
@@ -19,14 +20,8 @@ const globalStyle = css`
color: ${theme.colors.black};
}
- // ๋ชจ๋ฌ์ด ์น์์๋ ๋ชจ๋ฐ์ผ ํ๊ฒฝ ์ฒ๋ผ ๋ณด์ผ ์ ์๋๋ก center ์ฒ๋ฆฌ
body {
margin: 0;
- display: flex;
- justify-content: center;
- overflow: scroll;
- overflow-x: hidden;
- background-color: ${theme.colors.background};
}
button {
@@ -39,7 +34,7 @@ const globalStyle = css`
justify-content: center;
width: 100vw;
min-height: 100vh;
- background-color: ${theme.colors.gray350};
+ background-color: ${theme.colors.white};
}
#modal {
diff --git a/frontend/src/styles/transitions.ts b/frontend/src/styles/transitions.ts
index 42ccd8af..6a80c2b6 100644
--- a/frontend/src/styles/transitions.ts
+++ b/frontend/src/styles/transitions.ts
@@ -1,8 +1,9 @@
import { css } from '@emotion/react';
+import animation from '@/styles/animation';
+
const transitions = css`
.transitions-group {
- max-width: 414px;
width: 100vw;
overflow-x: hidden;
position: relative;
@@ -62,45 +63,53 @@ const transitions = css`
// right
.right-enter {
z-index: 1;
- transform: translate3d(100%, 0, 0);
+ transform: translateX(100%);
}
.right-enter.right-enter-active {
z-index: 1;
- transform: translate3d(0, 0, 0);
- transition: all 500ms;
+ transform: translateX(0);
+ transition: transform 600ms;
}
-
.right-exit {
- z-index: 1;
- transform: translate3d(0, 0, 0);
- }
-
- .right-exit.right-exit-active {
- z-index: 1;
- transform: translate3d(0, 0, 0);
- transition: all 700ms;
+ z-index: 0;
+ transform: translateX(0);
+ transition: transform 600ms;
}
// left
.left-enter {
+ z-index: 0;
+ transform: translateX(0);
+ transition: transform 600ms;
+ }
+ .left-exit {
z-index: 1;
- transform: translate3d(0, 0, 0);
+ transform: translateX(0);
}
- .left-enter.left-enter-active {
+ .left-exit.left-exit-active {
z-index: 1;
- transform: translate3d(0, 0, 0);
- transition: all 500ms;
+ transform: translateX(100%);
+ transition: transform 600ms;
}
- .left-exit {
+ // image scale up
+ .image-scale-up-enter.image-scale-up-enter-active {
z-index: 1;
- transform: translate3d(0, 0, 0);
+ animation: ${animation.scaleUp} 500ms;
}
- .left-exit.left-exit-active {
+ .image-scale-down-exit {
+ z-index: 0;
+ }
+
+ // image scale down
+ .image-scale-up-enter {
+ z-index: 0;
+ }
+
+ .image-scale-down-exit.image-scale-down-exit-active {
z-index: 1;
- transform: translate3d(100%, 0, 0);
- transition: all 700ms;
+ animation: ${animation.scaleDown} 500ms;
}
`;
diff --git a/frontend/src/types/global.d.ts b/frontend/src/types/global.d.ts
index da7fb7f6..f3cc6b14 100644
--- a/frontend/src/types/global.d.ts
+++ b/frontend/src/types/global.d.ts
@@ -3,3 +3,4 @@ declare module '*.jpg';
declare module '*.jpeg';
declare module '*.svg';
declare module '*.gif';
+declare module '*.webp';
diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts
index 712cca9f..53ab3ca3 100644
--- a/frontend/src/types/index.ts
+++ b/frontend/src/types/index.ts
@@ -26,3 +26,23 @@ export type TaskType = {
description: string;
imageUrl: string;
};
+
+export type ScreenModeType = 'DESKTOP' | 'MOBILE';
+
+type ImagePathType = string;
+
+export type UserImageType = {
+ '160w': ImagePathType;
+ '240w': ImagePathType;
+ '320w': ImagePathType;
+ '480w': ImagePathType;
+ fallback: ImagePathType;
+};
+
+export type HostImageType = {
+ '280w': ImagePathType;
+ '360w': ImagePathType;
+ '540w': ImagePathType;
+ '800w': ImagePathType;
+ fallback: ImagePathType;
+};
diff --git a/frontend/src/utils/copyUrlToClipboard.ts b/frontend/src/utils/copyUrlToClipboard.ts
new file mode 100644
index 00000000..629576bd
--- /dev/null
+++ b/frontend/src/utils/copyUrlToClipboard.ts
@@ -0,0 +1,17 @@
+const urlToClipboard = (url: string): void => {
+ if (isSecureContext && navigator.clipboard) {
+ navigator.clipboard.writeText(url);
+ return;
+ }
+
+ const textarea = document.createElement('textarea');
+ textarea.value = url;
+ textarea.style.opacity = '0';
+ document.body.appendChild(textarea);
+ textarea.select();
+ textarea.setSelectionRange(0, textarea.value.length);
+ document.execCommand('copy');
+ document.body.removeChild(textarea);
+};
+
+export default urlToClipboard;
diff --git a/frontend/src/utils/disableReactDevTools.ts b/frontend/src/utils/disableReactDevTools.ts
new file mode 100644
index 00000000..adc0e9dc
--- /dev/null
+++ b/frontend/src/utils/disableReactDevTools.ts
@@ -0,0 +1,20 @@
+declare global {
+ interface Window {
+ __REACT_DEVTOOLS_GLOBAL_HOOK__: any;
+ }
+}
+
+export function disableReactDevTools() {
+ if (typeof window.__REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'object') {
+ return;
+ }
+
+ for (const prop in window.__REACT_DEVTOOLS_GLOBAL_HOOK__) {
+ if (prop === 'renderers') {
+ window.__REACT_DEVTOOLS_GLOBAL_HOOK__[prop] = new Map();
+ } else {
+ window.__REACT_DEVTOOLS_GLOBAL_HOOK__[prop] =
+ typeof window.__REACT_DEVTOOLS_GLOBAL_HOOK__[prop] === 'function' ? () => {} : null;
+ }
+ }
+}
diff --git a/frontend/src/utils/isNull.ts b/frontend/src/utils/isNull.ts
new file mode 100644
index 00000000..5ab457be
--- /dev/null
+++ b/frontend/src/utils/isNull.ts
@@ -0,0 +1,5 @@
+const isNull = (target: any) => {
+ return target === null;
+};
+
+export default isNull;
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
index a12e0411..72b0a4f0 100644
--- a/frontend/tsconfig.json
+++ b/frontend/tsconfig.json
@@ -4,7 +4,7 @@
"composite": true,
"jsx": "react-jsx",
"jsxImportSource": "@emotion/react",
- "module": "commonjs",
+ "module": "es6",
"moduleResolution": "node",
"resolveJsonModule": true,
"allowJs": true,
diff --git a/frontend/webpack.common.js b/frontend/webpack.common.js
new file mode 100644
index 00000000..2090bcff
--- /dev/null
+++ b/frontend/webpack.common.js
@@ -0,0 +1,40 @@
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+
+module.exports = {
+ entry: './src/index.tsx',
+ resolve: { extensions: ['.tsx', '.ts', '.jsx', '.js', '...'] },
+ plugins: [
+ new HtmlWebpackPlugin({
+ template: 'public/index.html',
+ favicon: 'src/assets/favicon.png',
+ }),
+ ],
+ module: {
+ rules: [
+ {
+ test: /\.(ts|tsx|js|jsx)?$/,
+ exclude: ['/node_modules/'],
+ use: {
+ loader: 'babel-loader',
+ },
+ },
+ {
+ test: /\.(png|jpe?g|gif|svg|webp)$/,
+ exclude: ['/node_modules/'],
+ use: {
+ loader: 'file-loader',
+ options: {
+ name: 'assets/images/[name].[ext]',
+ },
+ },
+ },
+ {
+ test: /\.(eot|ttf|woff|woff2)$/i,
+ loader: 'url-loader',
+ options: {
+ name: 'assets/fonts/[name].[ext]',
+ },
+ },
+ ],
+ },
+};
diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js
deleted file mode 100644
index 439f5361..00000000
--- a/frontend/webpack.config.js
+++ /dev/null
@@ -1,75 +0,0 @@
-const path = require('path');
-const HtmlWebpackPlugin = require('html-webpack-plugin');
-const { CleanWebpackPlugin } = require('clean-webpack-plugin');
-const Dotenv = require('dotenv-webpack');
-
-const config = {
- entry: './src/index.tsx',
- output: {
- publicPath: '/',
- path: path.resolve(__dirname, 'dist'),
- filename: 'bundle.[chunkhash].js',
- },
- devServer: {
- port: 3000,
- open: true,
- hot: true,
- historyApiFallback: true,
- },
- plugins: [
- new HtmlWebpackPlugin({
- template: 'public/index.html',
- favicon: 'src/assets/favicon.png',
- }),
- new CleanWebpackPlugin({
- cleanAfterEveryBuildPatterns: ['dist'],
- }),
- new Dotenv({
- path: path.resolve(
- process.env.NODE_ENV === 'production'
- ? `./frontend-security/.env.production`
- : process.env.NODE_ENV === 'staging'
- ? `./frontend-security/.env.staging`
- : `./frontend-security/.env.development`
- ),
- }),
- ],
- module: {
- rules: [
- {
- test: /\.(ts|tsx)?$/,
- exclude: ['/node_modules/'],
- use: {
- loader: 'babel-loader',
- },
- },
- {
- test: /\.(png|jpe?g|gif|svg)$/,
- use: {
- loader: 'file-loader',
- options: {
- name: '[path]/[name].[ext]',
- },
- },
- },
- ],
- },
- resolve: {
- extensions: ['.tsx', '.ts', '.jsx', '.js', '...'],
- },
-};
-
-module.exports = env => {
- switch (process.env.NODE_ENV) {
- case 'production':
- config.mode = 'production';
- break;
- case 'staging':
- config.mode = 'production';
- break;
- default:
- config.mode = 'development';
- break;
- }
- return config;
-};
diff --git a/frontend/webpack.dev.js b/frontend/webpack.dev.js
new file mode 100644
index 00000000..6baa2a69
--- /dev/null
+++ b/frontend/webpack.dev.js
@@ -0,0 +1,26 @@
+const path = require('path');
+const common = require('./webpack.common');
+const { merge } = require('webpack-merge');
+const Dotenv = require('dotenv-webpack');
+
+module.exports = merge(common, {
+ mode: 'development',
+ output: {
+ publicPath: '/',
+ filename: 'js/bundle.js',
+ path: path.join(__dirname, '/dist'),
+ clean: true,
+ },
+ devtool: 'source-map',
+ devServer: {
+ port: 3000,
+ open: true,
+ hot: true,
+ historyApiFallback: true,
+ },
+ plugins: [
+ new Dotenv({
+ path: path.resolve('./frontend-security/.env.development'),
+ }),
+ ],
+});
diff --git a/frontend/webpack.prod.js b/frontend/webpack.prod.js
new file mode 100644
index 00000000..9af4a04a
--- /dev/null
+++ b/frontend/webpack.prod.js
@@ -0,0 +1,31 @@
+const path = require('path');
+const common = require('./webpack.common');
+const { merge } = require('webpack-merge');
+const Dotenv = require('dotenv-webpack');
+const CompressionPlugin = require('compression-webpack-plugin');
+
+module.exports = merge(common, {
+ mode: 'production',
+ output: {
+ publicPath: '/',
+ filename: 'js/[name].[chunkhash].js',
+ path: path.join(__dirname, '/dist'),
+ clean: true,
+ },
+ devtool: false,
+ plugins: [
+ new Dotenv({
+ path: path.resolve(
+ process.env.NODE_ENV === 'production'
+ ? './frontend-security/.env.production'
+ : './frontend-security/.env.staging'
+ ),
+ }),
+ new CompressionPlugin({
+ algorithm: 'gzip',
+ test: /\.js$|\.css$/,
+ threshold: 10240,
+ minRatio: 0.8,
+ }),
+ ],
+});