-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8 from AmorGakCo/feat/Group#3
Feat/group#3 그룹 관련 나머지 기능들
- Loading branch information
Showing
40 changed files
with
713 additions
and
480 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { cookies } from "next/headers"; | ||
interface ApiFetchOptions extends RequestInit { | ||
headers?: Record<string, string>; | ||
} | ||
|
||
/** | ||
* AccessToken이 포함된 fetch 요청 함수 | ||
* @param endpoint - API 엔드포인트 | ||
* @param options - fetch 옵션 | ||
* @returns API 응답의 JSON 데이터 | ||
*/ | ||
export const fetchWithAuthServer = async <T = any>(endpoint: string, options: ApiFetchOptions = {}): Promise<T> => { | ||
const accessToken = cookies().get('accessToken')?.value; | ||
const headers: Record<string, string> = { | ||
'Content-Type': 'application/json', | ||
...(accessToken && { Authorization: `Bearer ${accessToken}` }), | ||
...options.headers, | ||
}; | ||
|
||
const response = await fetch(`${process.env.NEXT_PUBLIC_API_LOCATION}/api${endpoint}`, { | ||
...options, | ||
credentials: 'include', | ||
headers, | ||
}); | ||
|
||
if (!response.ok) { | ||
throw new Error(`API 요청 실패: ${response.statusText}`); | ||
} | ||
const result = await response.json(); | ||
return result; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
interface GeolocationPosition { | ||
currentLat: number; | ||
currentLon: number; | ||
} | ||
|
||
export function getCurrentPosition(): Promise<GeolocationPosition> { | ||
return new Promise((resolve, reject) => { | ||
if (navigator.geolocation) { | ||
navigator.geolocation.getCurrentPosition( | ||
(position) => { | ||
resolve({ | ||
currentLat: position.coords.latitude, | ||
currentLon: position.coords.longitude, | ||
}); | ||
}, | ||
() => { | ||
resolve({ | ||
currentLat: 0, | ||
currentLon: 0, | ||
}); | ||
} | ||
); | ||
} else { | ||
reject(new Error('Geolocation is not supported by this browser.')); | ||
} | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
113 changes: 72 additions & 41 deletions
113
src/app/(afterLogin)/group/detail/[groupId]/_component/ButtonGroup.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,50 +1,81 @@ | ||
'use client' | ||
import { GroupDetailData } from "@/app/_types/Api"; | ||
import { Button } from "@/components/ui/button"; | ||
import { useQuery } from "@tanstack/react-query"; | ||
import { fetchGroupData } from "../_lib/fetchGroupData"; | ||
import { useLeaveGroupMutation } from "../_lib/useLeaveGroupMutation"; | ||
import { useRouter } from "next/navigation"; | ||
import { useDeleteGroupMutation } from "../_lib/useDeleteGroup"; | ||
'use client'; | ||
import { GroupDetailData } from '@/app/_types/Api'; | ||
import { Button } from '@/components/ui/button'; | ||
import { useQuery } from '@tanstack/react-query'; | ||
import { fetchGroupData } from '../_lib/fetchGroupData'; | ||
import { useLeaveGroupMutation } from '../hooks/useLeaveGroupMutation'; | ||
import { useRouter } from 'next/navigation'; | ||
import { useDeleteGroupMutation } from '../hooks/useDeleteGroup'; | ||
import { fetchAuthentication } from '../_lib/fetchAuthentication'; | ||
import TardinessDialog from './TardinessDialog'; | ||
import { useState } from 'react'; | ||
|
||
export function ButtonGroup ({groupId}:{groupId: number}) { | ||
export function ButtonGroup({ groupId }: { groupId: number }) { | ||
const router = useRouter(); | ||
const [openDialog,setOpenDialog] = useState(false); | ||
const { data, error } = useQuery< | ||
GroupDetailData, // 성공 시 반환될 데이터 타입 | ||
Error, // 에러 타입 (여기서는 Error로 지정) | ||
GroupDetailData, // 캐시된 데이터를 사용할 때의 타입 (보통 첫 번째와 동일하게 사용) | ||
[string, number] // queryKey의 타입 (string과 number로 이루어진 튜플) | ||
>({ | ||
queryKey: ["groupDetail", groupId], | ||
GroupDetailData, // 성공 시 반환될 데이터 타입 | ||
Error, // 에러 타입 (여기서는 Error로 지정) | ||
GroupDetailData, // 캐시된 데이터를 사용할 때의 타입 (보통 첫 번째와 동일하게 사용) | ||
[string, number] // queryKey의 타입 (string과 number로 이루어진 튜플) | ||
>({ | ||
queryKey: ['groupDetail', groupId], | ||
queryFn: fetchGroupData, | ||
staleTime: 60 * 1000, // fresh -> stale, 5분이라는 기준 | ||
gcTime: 300 * 1000, | ||
}); | ||
const router = useRouter(); | ||
const {mutate:leaveGroup,isSuccess:isSuccessLeave} = useLeaveGroupMutation(groupId); | ||
const {mutate:deleteGroup,isError:isFailDelete} = useDeleteGroupMutation(groupId); | ||
|
||
const handleLeaveGroup = () => { | ||
leaveGroup(); | ||
if (isSuccessLeave) { | ||
router.push('/group/history'); | ||
} | ||
} | ||
const handleDeleteGroup = () => { | ||
deleteGroup(); | ||
if (!isFailDelete) { | ||
alert('그룹이 삭제되었습니다.'); | ||
router.push('/group/history'); | ||
const { mutateAsync: leaveGroup, isError: isErrorLeave } = | ||
useLeaveGroupMutation(groupId); | ||
const { mutateAsync: deleteGroup } = useDeleteGroupMutation(groupId); | ||
const handleLeaveGroup = async () => { | ||
if (!confirm(`정말로 ${data?.name} 그룹을 탈퇴하시겠습니까?`)) return; | ||
const { status } = await leaveGroup(); | ||
if (status === 'success') { | ||
alert('그룹 탈퇴에 성공했습니다!'); | ||
} else { | ||
alert('그룹 탈퇴중 오류가 발생했습니다.'); | ||
} | ||
else { | ||
alert('오류가 발생했습니다.'); | ||
}; | ||
const handleDeleteGroup = async () => { | ||
if (!confirm(`정말로 ${data?.name} 그룹을 삭제하시겠습니까?`)) return; | ||
const { status } = await deleteGroup(); | ||
if (status === 'success') { | ||
alert('그룹 삭제에 성공했습니다!'); | ||
} else { | ||
alert('그룹 삭제중 오류가 발생했습니다.'); | ||
} | ||
} | ||
return (<div className="flex flex-col gap-4"> | ||
<Button>모임 위치 인증</Button> | ||
<Button>지각 알림</Button> | ||
<Button>장소 변경 요청</Button> | ||
<Button className={`bg-[#FF2950] hover:bg-[#FF2950]/70`} onClick={() => {handleDeleteGroup()}}> | ||
{`'${data!.name}'`} 삭제 | ||
</Button> | ||
</div>) | ||
} | ||
}; | ||
return ( | ||
<div className="flex flex-col gap-4"> | ||
<Button | ||
onClick={async () => { | ||
await fetchAuthentication(groupId); | ||
}} | ||
> | ||
모임 위치 인증 | ||
</Button> | ||
<TardinessDialog open = {openDialog} setOpen = {setOpenDialog} groupId={groupId}/> | ||
<Button>장소 변경 요청</Button> | ||
{data?.isGroupHost ? ( | ||
<Button | ||
className={`bg-[#FF2950] hover:bg-[#FF2950]/70`} | ||
onClick={() => { | ||
handleDeleteGroup(); | ||
}} | ||
> | ||
{`'${data!.name}'`} 삭제 | ||
</Button> | ||
) : ( | ||
<Button | ||
className={`bg-[#FF2950] hover:bg-[#FF2950]/70`} | ||
onClick={() => { | ||
handleLeaveGroup(); | ||
}} | ||
> | ||
{`'${data!.name}'`} 탈퇴 | ||
</Button> | ||
)} | ||
</div> | ||
); | ||
} |
15 changes: 15 additions & 0 deletions
15
src/app/(afterLogin)/group/detail/[groupId]/_component/ErrorComponent.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
'use client'; | ||
import { useRouter } from 'next/navigation'; | ||
|
||
export const ErrorComponent = () => { | ||
const router = useRouter(); | ||
setTimeout(() => { | ||
router.push('/group/history'); | ||
}, 1500); | ||
return ( | ||
<div className="flex-col justify-center items-center w-full gap-4"> | ||
<div className='flex justify-center'>존재하지 않는 그룹입니다.</div> | ||
<div className='flex justify-center mt-4 text-blue-500'>그룹 목록창으로 다시 이동합니다.</div> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
113 changes: 20 additions & 93 deletions
113
src/app/(afterLogin)/group/detail/[groupId]/_component/GroupMembersModal.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.