Skip to content

Commit

Permalink
Merge pull request #26 from School-of-Company/feature/admin-page
Browse files Browse the repository at this point in the history
🔀 어드민 페이지 제작
  • Loading branch information
Ethen1264 authored Nov 19, 2024
2 parents f00aeca + 98d3d3c commit e0373de
Show file tree
Hide file tree
Showing 25 changed files with 463 additions and 55 deletions.
8 changes: 4 additions & 4 deletions src/app/(pages)/admin/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React from 'react';
import Header from '@/widgets/layout/ui/Header';
import { Admin } from '@/views/admin';

const admin = () => {
const page = () => {
return (
<div>
<Header />
<Admin />
</div>
);
};

export default admin;
export default page;
40 changes: 40 additions & 0 deletions src/app/api/admin/[admin_id]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { AxiosError } from 'axios';
import { cookies } from 'next/headers';
import { NextRequest, NextResponse } from 'next/server';
import { apiClient } from '@/shared/libs/apiClient';

export async function PATCH(
request: NextRequest,
{ params }: { params: { admin_id: number } },
) {
const { admin_id } = params;
const cookieStore = cookies();
const accessToken = cookieStore.get('accessToken')?.value;
console.log(admin_id);
console.log(accessToken);

try {
const response = await apiClient.patch(
`/admin/${admin_id}`,
{},
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
);

return NextResponse.json(response.data);
} catch (error) {
if (error instanceof AxiosError) {
const status = error.response?.status;
const message = error.response?.data?.message || 'Unknown error';
return NextResponse.json({ error: message }, { status: status || 500 });
} else {
return NextResponse.json(
{ error: 'Internal Server Error' },
{ status: 500 },
);
}
}
}
26 changes: 26 additions & 0 deletions src/app/api/admin/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { AxiosError } from 'axios';
import { cookies } from 'next/headers';
import { NextResponse } from 'next/server';
import { apiClient } from '@/shared/libs/apiClient';

export async function GET() {
const cookieStore = cookies();
const accessToken = cookieStore.get('accessToken')?.value;

try {
const response = await apiClient.get('/admin', {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
return NextResponse.json(response.data);
} catch (error) {
const axiosError = error as AxiosError<{ message: string }>;

const status = axiosError.response?.status || 500;
const message =
axiosError.response?.data?.message || 'request signup failed';

return NextResponse.json({ error: message }, { status });
}
}
32 changes: 30 additions & 2 deletions src/app/api/expo/[expo_id]/route.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { AxiosError } from 'axios';
import { NextResponse } from 'next/server';
import { cookies } from 'next/headers';
import { NextRequest, NextResponse } from 'next/server';
import { apiClient } from '@/shared/libs/apiClient';

export async function GET(
request: Request,
request: NextRequest,
{ params }: { params: { expo_id: number } },
) {
const { expo_id } = params;
Expand All @@ -17,3 +18,30 @@ export async function GET(
return NextResponse.json({ error: message }, { status });
}
}

export async function DELETE(
request: NextRequest,
{ params }: { params: { expo_id: number } },
) {
const { expo_id } = params;
const cookieStore = cookies();
const accessToken = cookieStore.get('accessToken')?.value;
console.log(accessToken);

try {
const response = await apiClient.delete(`/expo/${expo_id}`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});

return NextResponse.json(response.data);
} catch (error) {
const axiosError = error as AxiosError<{ message: string }>;

const status = axiosError.response?.status || 500;
const message = axiosError.response?.data?.message || 'expo delete failed';

return NextResponse.json({ error: message }, { status });
}
}
1 change: 1 addition & 0 deletions src/entities/admin/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as AdminProfile } from './ui/AdminProfile';
29 changes: 29 additions & 0 deletions src/entities/admin/ui/AdminProfile/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Image from 'next/image';
import React from 'react';
import { Setting } from '@/shared/assets/icons';
import Profile from '@/shared/assets/png/Profile.png';

const ProfileInfo = ({ label, value }: { label: string; value: string }) => (
<div className="flex gap-[30px]">
<p className="w-fit text-body2 text-gray-500">{label}</p>
<p className="w-fit text-body2 text-black">{value}</p>
</div>
);

const AdminProfile = () => {
return (
<div className="mx-auto my-0 flex w-fit">
<div className="flex items-center gap-[124px] mobile:flex-col mobile:gap-[30px]">
<Image src={Profile} alt="관리자 프로필" />
<div className="space-y-[32px]">
<ProfileInfo label="이름" value="김진원" />
<ProfileInfo label="아이디" value="jin1234" />
<ProfileInfo label="이메일" value="jin12345@gmail.com" />
</div>
</div>
<Setting />
</div>
);
};

export default AdminProfile;
1 change: 1 addition & 0 deletions src/entities/signIn/api/signin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const signIn = async (data: SignUpData, router: AppRouterInstance) => {
});
if (response.status === 200) {
toast.success('로그인이 완료되었습니다.');
localStorage.setItem('accessToken', response.data.accessToken);
router.push('/');
}
} catch (error) {
Expand Down
20 changes: 20 additions & 0 deletions src/shared/assets/icons/Check.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';

const Check = () => {
return (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M21.5306 7.28055L9.53055 19.2806C9.4609 19.3503 9.37818 19.4056 9.28713 19.4433C9.19609 19.4811 9.09849 19.5005 8.99993 19.5005C8.90137 19.5005 8.80377 19.4811 8.71272 19.4433C8.62168 19.4056 8.53896 19.3503 8.4693 19.2806L3.2193 14.0306C3.07857 13.8898 2.99951 13.699 2.99951 13.4999C2.99951 13.3009 3.07857 13.11 3.2193 12.9693C3.36003 12.8286 3.55091 12.7495 3.74993 12.7495C3.94895 12.7495 4.13982 12.8286 4.28055 12.9693L8.99993 17.6896L20.4693 6.2193C20.61 6.07857 20.8009 5.99951 20.9999 5.99951C21.199 5.99951 21.3898 6.07857 21.5306 6.2193C21.6713 6.36003 21.7503 6.55091 21.7503 6.74993C21.7503 6.94895 21.6713 7.13982 21.5306 7.28055Z"
fill="#4CFF34"
/>
</svg>
);
};

export default Check;
20 changes: 20 additions & 0 deletions src/shared/assets/icons/Trash.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';

const Trash = () => {
return (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M20.25 4.5H16.5V3.75C16.5 3.15326 16.2629 2.58097 15.841 2.15901C15.419 1.73705 14.8467 1.5 14.25 1.5H9.75C9.15326 1.5 8.58097 1.73705 8.15901 2.15901C7.73705 2.58097 7.5 3.15326 7.5 3.75V4.5H3.75C3.55109 4.5 3.36032 4.57902 3.21967 4.71967C3.07902 4.86032 3 5.05109 3 5.25C3 5.44891 3.07902 5.63968 3.21967 5.78033C3.36032 5.92098 3.55109 6 3.75 6H4.5V19.5C4.5 19.8978 4.65804 20.2794 4.93934 20.5607C5.22064 20.842 5.60218 21 6 21H18C18.3978 21 18.7794 20.842 19.0607 20.5607C19.342 20.2794 19.5 19.8978 19.5 19.5V6H20.25C20.4489 6 20.6397 5.92098 20.7803 5.78033C20.921 5.63968 21 5.44891 21 5.25C21 5.05109 20.921 4.86032 20.7803 4.71967C20.6397 4.57902 20.4489 4.5 20.25 4.5ZM9 3.75C9 3.55109 9.07902 3.36032 9.21967 3.21967C9.36032 3.07902 9.55109 3 9.75 3H14.25C14.4489 3 14.6397 3.07902 14.7803 3.21967C14.921 3.36032 15 3.55109 15 3.75V4.5H9V3.75ZM18 19.5H6V6H18V19.5ZM10.5 9.75V15.75C10.5 15.9489 10.421 16.1397 10.2803 16.2803C10.1397 16.421 9.94891 16.5 9.75 16.5C9.55109 16.5 9.36032 16.421 9.21967 16.2803C9.07902 16.1397 9 15.9489 9 15.75V9.75C9 9.55109 9.07902 9.36032 9.21967 9.21967C9.36032 9.07902 9.55109 9 9.75 9C9.94891 9 10.1397 9.07902 10.2803 9.21967C10.421 9.36032 10.5 9.55109 10.5 9.75ZM15 9.75V15.75C15 15.9489 14.921 16.1397 14.7803 16.2803C14.6397 16.421 14.4489 16.5 14.25 16.5C14.0511 16.5 13.8603 16.421 13.7197 16.2803C13.579 16.1397 13.5 15.9489 13.5 15.75V9.75C13.5 9.55109 13.579 9.36032 13.7197 9.21967C13.8603 9.07902 14.0511 9 14.25 9C14.4489 9 14.6397 9.07902 14.7803 9.21967C14.921 9.36032 15 9.55109 15 9.75Z"
fill="#FF3434"
/>
</svg>
);
};

export default Trash;
4 changes: 4 additions & 0 deletions src/shared/assets/icons/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {
Program,
Plus,
Location,
Trash,
Check,
} from './index';

### EXPO
Expand Down Expand Up @@ -56,4 +58,6 @@ import {
<Program />
<Plus />
<Location />
<Trash />
<Check />
</div>
2 changes: 2 additions & 0 deletions src/shared/assets/icons/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ export { default as Eye } from './Eye';
export { default as Program } from './Program';
export { default as Plus } from './Plus';
export { default as Location } from './Location';
export { default as Trash } from './Trash';
export { default as Check } from './Check';
Binary file added src/shared/assets/png/Profile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions src/shared/model/footerActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import axios from 'axios';

export const fileActions = {
exportPDF: () => console.log('PDF 내보내기'),
exportExcel: () => console.log('Excel 내보내기'),
};

export const printActions = {
printBadge: () => console.log('명찰 출력'),
};

export const checkActions = (fetchSignupList: () => Promise<void>) => ({
CheckBadge: async (selectItem: number) => {
try {
await axios.patch(`/api/admin/${selectItem}`);
await fetchSignupList();
} catch (error) {
console.error('Failed to check signup:', error);
}
},
DeleteBadge: (selectItem: number) => console.log('삭제', selectItem),
});

export const deleteActions = (fetchExpoList: () => Promise<void>) => ({
DeleteBadge: async (selectItem: number) => {
try {
await axios.delete(`/api/expo/${selectItem}`);
await fetchExpoList();
} catch (error) {
console.error('Failed to delete expo:', error);
}
},
});
7 changes: 7 additions & 0 deletions src/shared/types/SignUp/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface SignUpItem extends Record<string, unknown> {
id: number;
name: string;
nickname: string;
email: string;
phoneNumber: string;
}
8 changes: 8 additions & 0 deletions src/shared/types/Expo/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface ExpoItem extends Record<string, unknown> {
id: number;
coverImage: string;
title: string;
description: string;
startedDay: string;
finishedDay: string;
}
52 changes: 49 additions & 3 deletions src/shared/ui/Table/TableFooter/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { cva, VariantProps } from 'class-variance-authority';
import React from 'react';
import { Check, Trash } from '@/shared/assets/icons';
import SmallButton from '@/shared/ui/SmallButton';

const tableFooterStyles = cva('flex justify-between items-center', {
Expand All @@ -8,6 +9,8 @@ const tableFooterStyles = cva('flex justify-between items-center', {
file: 'flex justify-between',
default: 'flex gap-6',
print: 'flex justify-between',
check: 'flex justify-between',
delete: 'flex justify-between',
},
},
defaultVariants: {
Expand All @@ -16,14 +19,35 @@ const tableFooterStyles = cva('flex justify-between items-center', {
});

type TableFooterProps = VariantProps<typeof tableFooterStyles> & {
num?: number;
num: number;
text?: string;
actions?: { [key: string]: (selectItem: number) => void };
selectItem: number | null;
};

const TableFooter = ({ type = 'default', num = 100 }: TableFooterProps) => {
const TableFooter = ({
text = '참가자 전체 인원',
type = 'default',
actions,
num,
selectItem,
}: TableFooterProps) => {
const handleCheckClick = () => {
if (selectItem !== null && actions?.CheckBadge) {
actions.CheckBadge(selectItem);
}
};

const handleDeleteClick = () => {
if (selectItem !== null && actions?.DeleteBadge) {
actions.DeleteBadge(selectItem);
}
};

return (
<div className={tableFooterStyles({ type })}>
<div className="flex gap-6">
<p className="text-body2 text-gray-500">참가자 전체 인원</p>
<p className="text-body2 text-gray-500">{text}</p>
<p className="text-body2 text-main-600">{num}</p>
</div>

Expand All @@ -41,6 +65,28 @@ const TableFooter = ({ type = 'default', num = 100 }: TableFooterProps) => {
<SmallButton text="명찰로 출력하기" />
</div>
)}

{type === 'check' && (
<div className="mr-5 flex items-center gap-6">
<button className="flex gap-6" onClick={handleCheckClick}>
<p className="text-body1 text-gray-400">승인</p>
<Check />
</button>
<button className="flex gap-6" onClick={handleDeleteClick}>
<p className="text-body1 text-gray-400">삭제</p>
<Trash />
</button>
</div>
)}

{type === 'delete' && (
<div className="mr-5 flex items-center gap-6">
<button className="flex gap-6" onClick={handleDeleteClick}>
<p className="text-body1 text-gray-400">삭제</p>
<Trash />
</button>
</div>
)}
</div>
);
};
Expand Down
Loading

0 comments on commit e0373de

Please sign in to comment.