Skip to content

Commit

Permalink
Merge pull request #208 from FinalDoubleTen/FE-95--feat/Share
Browse files Browse the repository at this point in the history
�Feat: 공유 기능 구현
  • Loading branch information
LeHiHo authored Jan 21, 2024
2 parents c4b7935 + 4dbaea7 commit c31182e
Show file tree
Hide file tree
Showing 19 changed files with 541 additions and 13 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@radix-ui/react-radio-group": "^1.1.3",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-toast": "^1.1.5",
"@radix-ui/react-toggle-group": "^1.0.4",
"@stomp/stompjs": "^7.0.0",
"@svgr/rollup": "^8.1.0",
Expand Down
35 changes: 35 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions src/api/trips.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,15 @@ export const getTripsAuthority = async (tripId: string) => {
const res = await authClient.get(`trips/${tripId}/authority`);
return res;
};

// 여정 참여 코드 조회
export const getTripsjoin = async (tripId: string) => {
const res = await authClient.get(`trips/${tripId}/join`);
return res;
};

// 여정 참여
export const postTripsjoin = async (tripId: number, joinCode: string) => {
const res = await authClient.post(`trips/${tripId}/join`, { joinCode });
return res;
};
10 changes: 8 additions & 2 deletions src/components/Auth/Login/LoginForm.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { AuthRequest } from '@/@types/auth.types';
import { SubmitHandler, useForm } from 'react-hook-form';
import { postEmailLogin } from '@api/auth';
import { useNavigate } from 'react-router-dom';
import { useLocation, useNavigate } from 'react-router-dom';
import { AxiosError } from 'axios';
import { useState } from 'react';
import SubmitBtn from '@components/common/button/SubmitBtn';
Expand All @@ -26,6 +26,8 @@ const LoginForm = () => {
});

const navigate = useNavigate();
const { state } = useLocation();

// const [userInfo, setUserInfo] = useRecoilState(UserInfoState);

const onLoginSubmit: SubmitHandler<AuthRequest> = async (data) => {
Expand All @@ -38,7 +40,11 @@ const LoginForm = () => {
if (res.data.status === 200) {
setItem('accessToken', res.data.data.tokenInfo.accessToken);
// setUserInfo(res.data.data.memberDto);
navigate('/');
if (state) {
navigate(state.prevPath);
} else {
navigate('/');
}
}
} catch (err) {
if (err instanceof AxiosError) {
Expand Down
6 changes: 4 additions & 2 deletions src/components/Plan/PlanSectionTop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { tripIdState, memberIdState } from '@recoil/socket';
import { calculateDayAndDate } from '@utils/utils';
import PlanSchedule from './PlanSchedule';


const PlanSectionTop = () => {
const navigate = useNavigate();
const tripId = useRecoilValue(tripIdState);
Expand Down Expand Up @@ -52,10 +51,13 @@ const PlanSectionTop = () => {
<div className="min-h-screen">
<BackBox
showBack={true}
showShare={true}
backHandler={() => {
navigate(-1);
}}
showShare={true}
shareHandler={() => {
navigate(`/trip/${tripId}/share`);
}}
/>
<TripRealtimeEditor />
<PlanSchedule />
Expand Down
39 changes: 39 additions & 0 deletions src/components/Share/CodeInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
interface Props {
inputCode: string;
setInputCode: React.Dispatch<React.SetStateAction<string>>;
showError: boolean;
}

const CodeInput = ({ inputCode, setInputCode, showError }: Props) => {
const onCodeChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const changeValue = e.target.value;
if (changeValue.length <= 5) {
setInputCode(e.target.value);
}
};
return (
<div className="relative flex w-full flex-col items-start px-2 ">
<div
className={`mb-10 mt-6 w-full border-b-[1.5px] border-solid ${
showError ? 'border-red' : 'border-gray3 focus-within:border-main2'
}`}>
<input
type="number"
autoFocus
maxLength={5}
placeholder="편집 초대 코드를 입력해주세요."
className="title3 mb-2 h-5 w-full bg-transparent text-gray4 outline-none placeholder:text-gray2"
onChange={onCodeChange}
value={inputCode}
/>
</div>
{showError && (
<div className="body5 absolute top-[62px] text-red">
편집 참여 코드를 다시 한번 확인해주세요.
</div>
)}
</div>
);
};

export default CodeInput;
38 changes: 38 additions & 0 deletions src/components/Share/CopyBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import CopyToast from './CopyToast';

interface Props {
title: string;
subTitle: string;
copyValue: string;
}

const CopyBox = ({ title, subTitle, copyValue }: Props) => {
const onCopyClick = () => {
navigator.clipboard.writeText(copyValue);
};

return (
<div>
<div className="mb-4 flex flex-col gap-1">
<span className="title3 text-gray7">{`${title} 복사`}</span>
<span className="body6 text-gray4">{subTitle}</span>
</div>
<div className="flex h-12 w-full items-center justify-between rounded-lg border border-solid border-gray2 px-4 py-2">
<input
className="body5 mr-4 w-full text-gray6 outline-none"
readOnly
value={copyValue}
/>
<CopyToast title={title}>
<div
onClick={onCopyClick}
className="bg-main3 body3 w-12 rounded-lg p-2 text-main1">
복사
</div>
</CopyToast>
</div>
</div>
);
};

export default CopyBox;
36 changes: 36 additions & 0 deletions src/components/Share/CopyToast.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as Toast from '@radix-ui/react-toast';
import { ReactNode, useState } from 'react';
import { ReactComponent as CircleCheckIcon } from '@assets/images/CircleCheck.svg';

interface Props {
title: string;
children: ReactNode;
}

const CopyToast = ({ title, children }: Props) => {
const [open, setOpen] = useState(false);

return (
<Toast.Provider duration={2000}>
<button
onClick={() => {
setOpen(true);
}}>
{children}
</button>

<Toast.Root
className="bg-main4 flex h-16 w-[370px] items-center rounded-lg border border-solid border-main2 px-4 py-2 shadow-md"
open={open}
onOpenChange={setOpen}>
<CircleCheckIcon fill="#29DDF6" className="mr-2" />
<Toast.Title className="body4 text-main1 [grid-area:_title]">
{`${title}가 복사되었습니다.`}
</Toast.Title>
</Toast.Root>
<Toast.Viewport className="fixed left-[50%] top-12 translate-x-[-50%]" />
</Toast.Provider>
);
};

export default CopyToast;
68 changes: 68 additions & 0 deletions src/components/Share/IsEditableModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import * as Dialog from '@radix-ui/react-dialog';
import { EditStarIcon } from '@components/common/icons/Icons';
import Alert from '@components/common/alert/Alert';
import { useLocation, useNavigate } from 'react-router-dom';
import { getItem } from '@utils/localStorageFun';

interface Props {
isEditable: boolean;
setIsEditable: React.Dispatch<React.SetStateAction<boolean>>;
}

const IsEditableModal = ({ isEditable, setIsEditable }: Props) => {
const navigate = useNavigate();
const { pathname } = useLocation();

const isLogin = getItem('accessToken');

const handleConfirm = () => {
navigate('/login', { state: { prevPath: `${pathname}/code` } });
};

return (
<Dialog.Root open={isEditable} onOpenChange={setIsEditable} modal>
<Dialog.Portal>
<Dialog.Overlay className="data-[state=open]:animate-overlayShow fixed inset-0 z-10 bg-black opacity-70" />
<Dialog.Content className="data-[state=open]:animate-contentShow fixed bottom-0 left-[50%] z-10 flex w-[412px] translate-x-[-50%] flex-col items-center rounded-t-2xl bg-white px-5 pb-8 pt-9">
<Dialog.Title className="title3 pb-2.5 text-center text-gray7">
편집 참여 코드를 입력하시면
<br />
여행 계획을 함께 편집할 수 있어요!
</Dialog.Title>
<Dialog.Description className="pb-8">
<EditStarIcon />
</Dialog.Description>
{isLogin ? (
<button
onClick={() => {
navigate('code');
}}
className="headline1 mb-4 h-14 w-full rounded-lg bg-main2 text-white outline-none">
초대코드 입력하기
</button>
) : (
<Alert
title={'로그인'}
message={
<>
편집 참여 코드 입력을 위해 로그인이 필요해요.
<br />
로그인하시겠어요?
</>
}
onConfirm={handleConfirm}>
<button className="headline1 mb-4 h-14 w-full rounded-lg bg-main2 text-white outline-none">
초대코드 입력하기
</button>
</Alert>
)}
<Dialog.Close>
<button className="body1 text-gray5">보기만 할게요</button>
</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
};

export default IsEditableModal;
59 changes: 59 additions & 0 deletions src/components/Trip/EditCodeModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { postTripsjoin } from '@api/trips';
import CodeInput from '@components/Share/CodeInput';
import Alert from '@components/common/alert/Alert';
import { useGetTripsAuthority } from '@hooks/useGetTripsAuthority';
import { useState } from 'react';
import { useParams } from 'react-router-dom';

const EditCodeModal = () => {
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const [inputCode, setInputCode] = useState<string>('');
const [showError, setShowError] = useState<boolean>(false);

const { id: tripId } = useParams();
const { tripAuthority } = useGetTripsAuthority();

const handleConfirm = async () => {
if (tripId) {
try {
const { data } = await postTripsjoin(Number(tripId), inputCode);
if (data.status === 200) {
setIsModalOpen(false);
}
} catch (err) {
setShowError(true);
setInputCode('');
console.error('참여 코드 요청 중 에러 발생', err);
}
}
};

return (
<>
{tripAuthority === 'WRITE' ? (
<button className="body3 rounded-lg border-2 border-solid border-gray2 p-2 text-gray4">
편집
</button>
) : (
<Alert
title="편집 참여 코드 입력"
content={
<CodeInput
inputCode={inputCode}
setInputCode={setInputCode}
showError={showError}
/>
}
isOpen={isModalOpen}
setIsOpen={setIsModalOpen}
onConfirm={handleConfirm}>
<button className="body3 rounded-lg border-2 border-solid border-gray2 p-2 text-gray4">
편집
</button>
</Alert>
)}
</>
);
};

export default EditCodeModal;
2 changes: 2 additions & 0 deletions src/components/Trip/TripSchedule.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { UserIcon } from '@components/common/icons/Icons';
import EditCodeModal from './EditCodeModal';

export const TripSchedule = () => {
return (
Expand All @@ -12,6 +13,7 @@ export const TripSchedule = () => {
<span className="body4 pt-[1px] text-gray4">5</span>
</div>
</div>
<EditCodeModal />
</div>
<span className="body1 text-gray4">23.12.23 - 23.12.25</span>
</>
Expand Down
Loading

0 comments on commit c31182e

Please sign in to comment.