Skip to content

Commit

Permalink
Merge pull request #392 from depromeet/develop
Browse files Browse the repository at this point in the history
 Merge to main for v1.0.2
  • Loading branch information
leeminhee119 committed Sep 13, 2024
2 parents ed914d5 + 0bb2b9e commit 45f7cc9
Show file tree
Hide file tree
Showing 31 changed files with 862 additions and 236 deletions.
20 changes: 11 additions & 9 deletions apps/web/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import Cookies from "js-cookie";
/** API 사용 전, ENV 파일을 통해 서버 연동 설정을 해주세요 */
const API_URL = import.meta.env.VITE_API_URL as string;

export type ErrorResponse = { name: string; message: string };

const baseApi = axios.create({
baseURL: API_URL,
timeout: 5000,
Expand All @@ -21,8 +23,8 @@ const logOnDev = (message: string) => {
};

/** API 요청이 실패한 경우 호출되는 함수 */
const onError = (status: number, message: string) => {
const error = { status, message };
const onError = (status: number, message: string, data?: ErrorResponse) => {
const error = { status, message, data };
throw error;
};

Expand Down Expand Up @@ -64,32 +66,32 @@ const onErrorResponse = (error: AxiosError | Error) => {
if (axios.isAxiosError(error)) {
const { message } = error;
const { method, url } = error?.config as AxiosRequestConfig;
const { status, statusText } = error?.response as AxiosResponse;
const { status, statusText, data } = error?.response as AxiosResponse<ErrorResponse>;

logOnDev(`[API ERROR_RESPONSE ${status} | ${statusText} | ${message}] ${method?.toUpperCase()} ${url}`);

switch (status) {
case 400:
onError(status, "잘못된 요청을 했어요");
onError(status, "잘못된 요청을 했어요", data);
break;
case 401: {
onError(status, "인증을 실패했어요");
onError(status, "인증을 실패했어요", data);
break;
}
case 403: {
onError(status, "권한이 없는 상태로 접근했어요");
onError(status, "권한이 없는 상태로 접근했어요", data);
break;
}
case 404: {
onError(status, "찾을 수 없는 페이지를 요청했어요");
onError(status, "찾을 수 없는 페이지를 요청했어요", data);
break;
}
case 500: {
onError(status, "서버 오류가 발생했어요");
onError(status, "서버 오류가 발생했어요", data);
break;
}
default: {
onError(status, `기타 에러가 발생했어요 : ${error?.message}`);
onError(status, `기타 에러가 발생했어요 : ${error?.message}`, data);
}
}
} else if (error instanceof Error && error?.name === "TimoutError") {
Expand Down
14 changes: 13 additions & 1 deletion apps/web/src/app/info/PrivacyPolicyPage.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import { css } from "@emotion/react";

import { Typography } from "@/component/common/typography";
import { info } from "@/config/info";
import { DefaultLayout } from "@/layout/DefaultLayout";

export function PrivacyPolicyPage() {
return (
<DefaultLayout title="개인정보 처리방침">
<Typography variant="body16Medium">{info.privacyPolicy}</Typography>
<Typography variant="body16Medium">
<pre
css={css`
white-space: pre-wrap;
word-wrap: break-word;
overflow-wrap: break-word;
`}
>
{info.privacyPolicy}
</pre>
</Typography>
</DefaultLayout>
);
}
14 changes: 13 additions & 1 deletion apps/web/src/app/info/TermsOfServicePage.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import { css } from "@emotion/react";

import { Typography } from "@/component/common/typography";
import { info } from "@/config/info";
import { DefaultLayout } from "@/layout/DefaultLayout";

export function TermsOfServicePage() {
return (
<DefaultLayout title="이용약관">
<Typography variant="body16Medium">{info.termsOfService}</Typography>
<Typography variant="body16Medium">
<pre
css={css`
white-space: pre-wrap;
word-wrap: break-word;
overflow-wrap: break-word;
`}
>
{info.termsOfService}
</pre>
</Typography>
</DefaultLayout>
);
}
20 changes: 14 additions & 6 deletions apps/web/src/app/retrospect/analysis/RetrospectAnalysisPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Fragment } from "react";
import { Fragment, useEffect } from "react";
import { useLocation } from "react-router-dom";

import { LoadingModal } from "@/component/common/Modal/LoadingModal.tsx";
Expand All @@ -10,9 +10,10 @@ import { QuestionForm } from "@/component/retrospect/analysis/QuestionForm.tsx";
import { useGetAnalysisAnswer } from "@/hooks/api/retrospect/analysis/useGetAnalysisAnswer.ts";
import { useTabs } from "@/hooks/useTabs";
import { DualToneLayout } from "@/layout/DualToneLayout";
import { EmptyList } from "@/component/common/empty";

export const RetrospectAnalysisPage = () => {
const { title } = useLocation().state as { title: string };
const { title, defaultTab } = useLocation().state as { title: string; defaultTab: "질문" | "개별" | "분석" };

const tabMappings = {
질문: "QUESTIONS",
Expand All @@ -27,6 +28,11 @@ export const RetrospectAnalysisPage = () => {
const spaceId = queryParams.get("spaceId");
const retrospectId = queryParams.get("retrospectId");
const { data, isLoading } = useGetAnalysisAnswer({ spaceId: spaceId!, retrospectId: retrospectId! });
useEffect(() => {
if (defaultTab) {
selectTab(defaultTab);
}
}, []);
return (
<DualToneLayout
bottomTheme="gray"
Expand All @@ -38,13 +44,15 @@ export const RetrospectAnalysisPage = () => {
}
>
{isLoading && <LoadingModal />}
{
{!data || data.individuals.length === 0 ? (
<EmptyList icon={"ic_clock"} message={"제출된 회고가 없어요"} />
) : (
{
QUESTIONS: <QuestionForm data={data!} />,
INDIVIDUAL_ANALYSIS: <PersonalForm data={data!} />,
QUESTIONS: <QuestionForm data={data} />,
INDIVIDUAL_ANALYSIS: <PersonalForm data={data} />,
ANALYSIS: <AnalysisContainer spaceId={spaceId!} retrospectId={retrospectId!} hasAIAnalyzed={data?.hasAIAnalyzed} />,
}[selectedTab]
}
)}
</DualToneLayout>
);
};
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
import { useLocation } from "react-router-dom";

import { Header } from "@/component/common/header";
import { LoadingModal } from "@/component/common/Modal/LoadingModal";
import { Spacing } from "@/component/common/Spacing";
import { CardCarousel } from "@/component/retrospect/template/card/CardCarousel";
import { TemplateKey } from "@/component/retrospect/template/card/template.const";
import { useApiGetSpace } from "@/hooks/api/space/useApiGetSpace";
import { DefaultLayout } from "@/layout/DefaultLayout";
import { chooseParticle } from "@/utils/retrospect/chooseParticle";
import { createTemplateArr } from "@/utils/retrospect/createTemplateArr";

export function RecommendSearch() {
const { templateId, spaceId } = useLocation().state as { templateId: string; spaceId: string };
const TemplateArr = createTemplateArr(templateId as unknown as TemplateKey);
const { data, isLoading } = useApiGetSpace(spaceId);

if (isLoading) return <LoadingModal />;

const particle = chooseParticle(data?.name ?? "");

return (
<DefaultLayout theme="gray">
<Header title={`디프만과 어울리는\n회고 템플릿을 찾는중...`} />
<Header title={`${data?.name}${particle} 어울리는\n회고 템플릿을 찾는중...`} />
<Spacing size={13} />
<div>
<CardCarousel templateId={templateId} spaceId={spaceId} templateArr={TemplateArr} />
Expand Down
17 changes: 10 additions & 7 deletions apps/web/src/app/space/CreateDonePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useLocation, useNavigate } from "react-router-dom";
import CreateDone from "@/assets/lottie/space/create_done.json";
import { ButtonProvider, IconButton } from "@/component/common/button";
import { Icon } from "@/component/common/Icon";
import { LoadingModal } from "@/component/common/Modal/LoadingModal";
import { Spacing } from "@/component/common/Spacing";
import { Typography } from "@/component/common/typography";
import { useApiGetUser } from "@/hooks/api/auth/useApiGetUser";
Expand All @@ -20,7 +21,7 @@ import { encryptId } from "@/utils/space/cryptoKey";
export function CreateDonePage() {
const navigate = useNavigate();
const { spaceId } = useLocation().state as { spaceId: string };
const { data: spaceData } = useApiGetSpace(spaceId);
const { data: spaceData, isLoading } = useApiGetSpace(spaceId);
const [animate, setAnimate] = useState(spaceData?.category === ProjectType.Individual);
const { data: userData } = useApiGetUser();
const { toast } = useToast();
Expand All @@ -41,8 +42,8 @@ export function CreateDonePage() {
if (bridge.isWebViewBridgeAvailable) {
await bridge.sendShareToKakao({
content: {
title: `${userData.name}님이 스페이스에 초대했습니다.`,
description: "어서오세용~!!",
title: `${userData.name}님의 회고 초대장`,
description: `함께 회고해요! ${userData.name}님이 팀 레이어 스페이스에 초대했어요`,
imageUrl: "https://kr.object.ncloudstorage.com/layer-bucket/small_banner.png",
link: {
mobileWebUrl: `${window.location.protocol}//${window.location.host}/space/join/${encryptedId}`,
Expand All @@ -62,21 +63,23 @@ export function CreateDonePage() {
} else {
shareKakaoWeb(
`${window.location.protocol}//${window.location.host}/space/join/${encryptedId}`,
`${userData.name}님이 스페이스에 초대했습니다.`,
"어서오세용~!!",
`${userData.name}님의 회고 초대장`,
`함께 회고해요! ${userData.name}님이 ${spaceData?.name} 스페이스에 초대했어요`,
);
}
};

const handleCopyClipBoard = async () => {
try {
await navigator.clipboard.writeText(`${window.location.protocol}//${window.location.host}/space/join/${encryptedId}`);
toast.success("복사 성공!!");
toast.success("링크 복사가 완료되었어요!");
} catch (e) {
alert("failed");
alert("링크 복사에 실패했어요!");
}
};

if (isLoading) return <LoadingModal />;

return (
<>
{spaceData && (
Expand Down
60 changes: 32 additions & 28 deletions apps/web/src/app/space/SpaceViewPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { css } from "@emotion/react";
import { PATHS } from "@layer/shared";
import { useQueries } from "@tanstack/react-query";
import Cookies from "js-cookie";
import { Fragment, useEffect, useState } from "react";
import { Fragment, useEffect, useRef, useState } from "react";
import { useNavigate } from "react-router-dom";

import { BottomSheet } from "@/component/BottomSheet";
Expand All @@ -22,12 +22,16 @@ import { useApiOptionsGetSpaceInfo } from "@/hooks/api/space/useApiOptionsGetSpa
import { useBottomSheet } from "@/hooks/useBottomSheet";
import { useModal } from "@/hooks/useModal";
import { useRequiredParams } from "@/hooks/useRequiredParams";
import { DualToneLayout } from "@/layout/DualToneLayout";
import { DefaultLayout } from "@/layout/DefaultLayout";
import { useTestNatigate } from "@/lib/test-natigate";
import { DESIGN_TOKEN_COLOR } from "@/style/designTokens";
import { Retrospect } from "@/types/retrospect";
import { useCollisionDetection } from "@/hooks/useCollisionDetection";

export function SpaceViewPage() {
const appbarRef = useRef<HTMLDivElement>(null);
const contentRef = useRef<HTMLDivElement>(null);
const isColliding = useCollisionDetection(appbarRef, contentRef);
const navigate = useNavigate();
const appNavigate = useTestNatigate();
const memberId = Cookies.get("memberId");
Expand Down Expand Up @@ -99,32 +103,20 @@ export function SpaceViewPage() {
}

return (
<DualToneLayout
topTheme="dark"
<DefaultLayout
theme={isColliding ? "default" : "dark"}
LeftComp={
<Icon
size={2.4}
icon="ic_arrow_left"
css={css`
cursor: pointer;
`}
onClick={() => appNavigate(PATHS.home())}
color={DESIGN_TOKEN_COLOR.gray00}
/>
}
TopComp={
<>
<ActionItemListView
restrospectArr={restrospectArr ? restrospectArr : []}
isPossibleMake={doneRetrospects.length === 0}
spaceId={spaceInfo?.id}
teamActionList={teamActionList?.teamActionItemList || []}
leaderId={spaceInfo?.leader.id}
<div ref={appbarRef}>
<Icon
size={2.4}
icon="ic_arrow_left"
css={css`
cursor: pointer;
`}
onClick={() => appNavigate(PATHS.home())}
color={DESIGN_TOKEN_COLOR.gray00}
/>
<Spacing size={1.1} />
<SpaceCountView mainTemplate={spaceInfo?.formTag} memberCount={spaceInfo?.memberCount} isLeader={isLeader} />
<Spacing size={2.4} />
</>
</div>
}
title={spaceInfo?.name}
RightComp={
Expand Down Expand Up @@ -154,6 +146,18 @@ export function SpaceViewPage() {
/>
}
>
<div ref={contentRef}>
<ActionItemListView
restrospectArr={restrospectArr ? restrospectArr : []}
isPossibleMake={doneRetrospects.length === 0}
spaceId={spaceInfo?.id}
teamActionList={teamActionList?.teamActionItemList || []}
leaderId={spaceInfo?.leader.id}
/>
<Spacing size={1.1} />
<SpaceCountView mainTemplate={spaceInfo?.formTag} memberCount={spaceInfo?.memberCount} isLeader={isLeader} />
<Spacing size={2.4} />
</div>
<div
css={css`
width: calc(100% + 4rem);
Expand Down Expand Up @@ -210,7 +214,7 @@ export function SpaceViewPage() {
gap: 0.6rem;
`}
>
<Typography variant="title18Bold">완료된 회고</Typography>
<Typography variant="title18Bold">마감된 회고</Typography>
<Typography variant="title16Bold" color="gray600">
{doneRetrospects?.length}
</Typography>
Expand Down Expand Up @@ -245,6 +249,6 @@ export function SpaceViewPage() {
handler={true}
/>
<Toast />
</DualToneLayout>
</DefaultLayout>
);
}
6 changes: 5 additions & 1 deletion apps/web/src/component/common/empty/EmptyList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import { IconType } from "@/component/common/Icon/Icon";
import { Typography } from "@/component/common/typography";

type EmptyListProps = {
title?: React.ReactNode;
message: React.ReactNode;
icon: IconType;
iconSize?: number;
} & React.HTMLAttributes<HTMLDivElement>;

export function EmptyList({ message, icon, iconSize = 7.2, children, ...props }: PropsWithChildren<EmptyListProps>) {
export function EmptyList({ title, message, icon, iconSize = 7.2, children, ...props }: PropsWithChildren<EmptyListProps>) {
return (
<div
css={css`
Expand All @@ -25,6 +26,9 @@ export function EmptyList({ message, icon, iconSize = 7.2, children, ...props }:
{...props}
>
<Icon icon={icon} size={iconSize} />
<Typography variant="title18Bold" color={"gray900"}>
{title}
</Typography>
<div
css={css`
text-align: center;
Expand Down
Loading

0 comments on commit 45f7cc9

Please sign in to comment.