Skip to content

Commit

Permalink
Merge pull request #8 from AmorGakCo/feat/Group#3
Browse files Browse the repository at this point in the history
Feat/group#3 그룹 관련 나머지 기능들
  • Loading branch information
phnml1 authored Dec 21, 2024
2 parents 4194e6c + 50da5d7 commit b57dd9f
Show file tree
Hide file tree
Showing 40 changed files with 713 additions and 480 deletions.
1 change: 1 addition & 0 deletions public/arrow_down.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions src/app/(afterLogin)/_lib/FetchWithAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ export const fetchWithAuth = async <T = any>(endpoint: string, options: ApiFetch
headers,
});

if (!response.ok) {
throw new Error(`API 요청 실패: ${response.statusText}`);
}
// if (!response.ok) {
// throw new Error(`API 요청 실패: ${response.statusText}`);
// }
const result = await response.json();
return result;
};
31 changes: 31 additions & 0 deletions src/app/(afterLogin)/_lib/FetchWithAuthServer.ts
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;
};
27 changes: 27 additions & 0 deletions src/app/(afterLogin)/_lib/getCurrentPosition.ts
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.'));
}
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,14 @@ export default function Location() {
});
const [center, setCenter] = useState({lng:0,lat:0});
const [marker, setMarker] = useState({lng:0,lat:0});
console.log(data);
const [address, setAddress] = useState('');
useEffect(() => {
console.log(isSuccess);
if(isSuccess) {
setCenter({lat:data?.latitude,lng:data?.longitude});
setMarker({lat:data?.latitude,lng:data?.longitude});
setAddress(data?.address)
}
},[isSuccess,data]);
console.log(center);
return (
<div className="mt-8 w-full h-[200px] bg-gray-200">
<Map // 지도를 표시할 Container
Expand Down
113 changes: 72 additions & 41 deletions src/app/(afterLogin)/group/detail/[groupId]/_component/ButtonGroup.tsx
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>
);
}
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>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default function GroupMembers({groupId}:{groupId:number}) {
className="cursor-pointer"
/>
</DialogTrigger>
<GroupMembersModal/>
<GroupMembersModal groupMembers = {data!.groupMembers}/>
</Dialog>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,103 +1,30 @@
import { Avatar, AvatarImage } from '@/components/ui/avatar';
import { DialogContent, DialogTitle } from '@/components/ui/dialog';

export default function GroupMembersModal() {
interface groupMembersType {
groupMembers: {
memberId: number;
imgUrl: string;
nickname: string;
moGakCoTemperature: number;
githubUrl: string | null;
}[]
}
export default function GroupMembersModal({groupMembers}:groupMembersType) {
return (
<>
<DialogTitle className = 'hidden'></DialogTitle>
<DialogContent className="flex flex-col gap-2 py-6 px-4 max-h-96 ">
<DialogContent className="flex flex-col gap-2 py-8 px-6 max-h-96 rounded-lg">
<div className='overflow-auto'>
<div className="flex w-full h-14 gap-4 items-center">
<Avatar>
<AvatarImage className="w-10 h-10 rounded-full" src="/coin.svg" />
</Avatar>

<div>하준숴이</div>
</div>
<div className="flex w-full h-14 gap-4 items-center">
<Avatar>
<AvatarImage className="w-10 h-10 rounded-full" src="/coin.svg" />
</Avatar>

<div>하준숴이</div>
</div>
<div className="flex w-full h-14 gap-4 items-center">
<Avatar>
<AvatarImage className="w-10 h-10 rounded-full" src="/coin.svg" />
</Avatar>

<div>하준숴이</div>
</div>
<div className="flex w-full h-14 gap-4 items-center">
<Avatar>
<AvatarImage className="w-10 h-10 rounded-full" src="/coin.svg" />
</Avatar>

<div>하준숴이</div>
</div>
<div className="flex w-full h-14 gap-4 items-center">
<Avatar>
<AvatarImage className="w-10 h-10 rounded-full" src="/coin.svg" />
</Avatar>

<div>하준숴이</div>
</div>
<div className="flex w-full h-14 gap-4 items-center">
<Avatar>
<AvatarImage className="w-10 h-10 rounded-full" src="/coin.svg" />
</Avatar>

<div>하준숴이</div>
</div>
<div className="flex w-full h-14 gap-4 items-center">
<Avatar>
<AvatarImage className="w-10 h-10 rounded-full" src="/coin.svg" />
</Avatar>

<div>하준숴이</div>
</div>
<div className="flex w-full h-14 gap-4 items-center">
<Avatar>
<AvatarImage className="w-10 h-10 rounded-full" src="/coin.svg" />
</Avatar>

<div>하준숴이</div>
</div>
<div className="flex w-full h-14 gap-4 items-center">
<Avatar>
<AvatarImage className="w-10 h-10 rounded-full" src="/coin.svg" />
</Avatar>

<div>하준숴이</div>
</div>
<div className="flex w-full h-14 gap-4 items-center">
<Avatar>
<AvatarImage className="w-10 h-10 rounded-full" src="/coin.svg" />
</Avatar>

<div>하준숴이</div>
</div>
<div className="flex w-full h-14 gap-4 items-center">
<Avatar>
<AvatarImage className="w-10 h-10 rounded-full" src="/coin.svg" />
</Avatar>

<div>하준숴이</div>
</div>
<div className="flex w-full h-14 gap-4 items-center">
<Avatar>
<AvatarImage className="w-10 h-10 rounded-full" src="/coin.svg" />
</Avatar>

<div>하준숴이</div>
</div>
<div className="flex w-full h-14 gap-4 items-center">
<Avatar>
<AvatarImage className="w-10 h-10 rounded-full" src="/coin.svg" />
</Avatar>

<div>하준숴이</div>
</div>
{groupMembers?.map((data) => {
return (<div key={data.memberId} className="flex w-full h-14 gap-4 items-center">
<Avatar>
<AvatarImage className="w-10 h-10 rounded-full" src={data.imgUrl} />
</Avatar>

<div>{data.nickname}</div>
</div>)
})}
</div>
</DialogContent>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export default function GroupTime ({groupId}:{groupId: number}) {
<div className="flex justify-between items-center w-full h-10">
<div>모임 시간</div>
<div className="font-thin text-xl">
{`${beginAtDate.getHours()}:${beginAtDate.getMinutes()}`}~
{`${endAtDate.getHours()}:${endAtDate.getMinutes()}`}
{`${beginAtDate.getHours().toString().padStart(2,'0')}:${beginAtDate.getMinutes().toString().padStart(2,'0')}`}~
{`${endAtDate.getHours().toString().padStart(2,'0')}:${endAtDate.getMinutes().toString().padStart(2,'0')}`}
</div>
</div>
)
Expand Down
Loading

0 comments on commit b57dd9f

Please sign in to comment.