Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ToastContainer, Bounce } from 'react-toastify';
import Home from './page/home';
import Login from './page/login';
import Event from './page/event';
import Coupon from './page/coupon';
import NotFound from './page/notfound';

const App: React.FC = () => {
Expand All @@ -12,6 +13,7 @@ const App: React.FC = () => {
<Route path='/' element={<Home />} />
<Route path='/login' element={<Login />} />
<Route path='/event' element={<Event />} />
<Route path='/coupon' element={<Coupon />} />
<Route path='*' element={<NotFound />} />
</Routes>
<ToastContainer
Expand All @@ -31,4 +33,4 @@ const App: React.FC = () => {
);
};

export default App;
export default App;
6 changes: 6 additions & 0 deletions src/api/coupon/delete-coupon-code.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { requestApi } from './request';
import type { ApiResult } from './types';

export async function deleteCouponCode(codeCouponId: number): Promise<ApiResult<null>> {
return requestApi<null>(`/admin/coupons/code/${codeCouponId}`, { method: 'DELETE' });
}
6 changes: 6 additions & 0 deletions src/api/coupon/delete-coupon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { requestApi } from './request';
import type { ApiResult } from './types';

export async function deleteCoupon(couponId: number): Promise<ApiResult<null>> {
return requestApi<null>(`/admin/coupons/${couponId}`, { method: 'DELETE' });
}
6 changes: 6 additions & 0 deletions src/api/coupon/delete-issued-coupon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { requestApi } from './request';
import type { ApiResult } from './types';

export async function deleteIssuedCoupon(issuedCouponId: number): Promise<ApiResult<null>> {
return requestApi<null>(`/admin/coupons/issued/${issuedCouponId}`, { method: 'DELETE' });
}
6 changes: 6 additions & 0 deletions src/api/coupon/get-admin-members.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { requestApi } from './request';
import type { AdminMemberSummary, ApiResult } from './types';

export async function getAdminMembers(): Promise<ApiResult<AdminMemberSummary[]>> {
return requestApi<AdminMemberSummary[]>('/admin/members', { method: 'GET' });
}
6 changes: 6 additions & 0 deletions src/api/coupon/get-coupon-codes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { requestApi } from './request';
import type { AdminCouponCode, ApiResult } from './types';

export async function getCouponCodes(): Promise<ApiResult<AdminCouponCode[]>> {
return requestApi<AdminCouponCode[]>('/admin/coupons/code', { method: 'GET' });
}
6 changes: 6 additions & 0 deletions src/api/coupon/get-coupons.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { requestApi } from './request';
import type { AdminCoupon, ApiResult } from './types';

export async function getCoupons(): Promise<ApiResult<AdminCoupon[]>> {
return requestApi<AdminCoupon[]>('/admin/coupons', { method: 'GET' });
}
6 changes: 6 additions & 0 deletions src/api/coupon/get-issued-coupons.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { requestApi } from './request';
import type { AdminIssuedCoupon, ApiResult } from './types';

export async function getIssuedCoupons(): Promise<ApiResult<AdminIssuedCoupon[]>> {
return requestApi<AdminIssuedCoupon[]>('/admin/coupons/issued', { method: 'GET' });
}
9 changes: 9 additions & 0 deletions src/api/coupon/patch-coupon-name.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { requestApi } from './request';
import type { AdminCoupon, ApiResult } from './types';

export async function updateCouponName(couponId: number, couponName: string): Promise<ApiResult<AdminCoupon>> {
return requestApi<AdminCoupon>(`/admin/coupons/${couponId}/name`, {
method: 'PATCH',
body: JSON.stringify({ couponName }),
});
}
9 changes: 9 additions & 0 deletions src/api/coupon/post-coupon-code.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { requestApi } from './request';
import type { AdminCouponCode, ApiResult } from './types';

export async function createCouponCode(couponId: number, description: string | null): Promise<ApiResult<AdminCouponCode>> {
return requestApi<AdminCouponCode>('/admin/coupons/code', {
method: 'POST',
body: JSON.stringify({ couponId, description }),
});
}
9 changes: 9 additions & 0 deletions src/api/coupon/post-coupon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { requestApi } from './request';
import type { AdminCoupon, ApiResult } from './types';

export async function createCoupon(couponName: string, discountAmount: number): Promise<ApiResult<AdminCoupon>> {
return requestApi<AdminCoupon>('/admin/coupons', {
method: 'POST',
body: JSON.stringify({ couponName, discountAmount }),
});
}
9 changes: 9 additions & 0 deletions src/api/coupon/post-issued-coupons.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { requestApi } from './request';
import type { AdminIssuedCoupon, ApiResult } from './types';

export async function createIssuedCoupons(couponId: number, memberIds: number[]): Promise<ApiResult<AdminIssuedCoupon[]>> {
return requestApi<AdminIssuedCoupon[]>('/admin/coupons/issued', {
method: 'POST',
body: JSON.stringify({ couponId, memberIds }),
});
}
39 changes: 39 additions & 0 deletions src/api/coupon/request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { ApiResult } from './types';

const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;

export async function requestApi<T>(path: string, init?: RequestInit): Promise<ApiResult<T>> {
try {
const initHeaders = init?.headers ?? {};
const response = await fetch(`${API_BASE_URL}${path}`, {
...init,
credentials: 'include',
headers: {
accept: 'application/json',
'Content-Type': 'application/json',
...initHeaders,
},
});

const contentType = response.headers.get('content-type') || '';
const canParseJson = contentType.includes('application/json');

if (response.ok) {
if (response.status === 204 || !canParseJson) {
return { ok: true, status: response.status };
}
const data = (await response.json()) as T;
return { ok: true, status: response.status, data };
}

if (!canParseJson) {
return { ok: false, status: response.status };
}

const error = (await response.json()) as { name?: string };
return { ok: false, status: response.status, errorName: error.name };
} catch (error) {
console.error('[Coupon API Error]', error);
return { ok: false, status: 0 };
}
}
48 changes: 48 additions & 0 deletions src/api/coupon/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
export interface AdminCoupon {
couponId: number;
couponName: string;
discountAmount: number;
createdAt: string;
updatedAt: string;
}

export interface AdminCouponCode {
codeCouponId: number;
couponId: number;
couponName: string;
code: string;
description: string | null;
isValid: boolean;
issuedCouponId: number | null;
usedAt: string | null;
createdAt: string;
}

export interface AdminIssuedCoupon {
issuedCouponId: number;
couponId: number;
couponName: string;
discountAmount: number;
memberId: number;
memberName: string;
memberEmail: string;
isValid: boolean;
paymentId: number | null;
usedAt: string | null;
createdAt: string;
}

export interface AdminMemberSummary {
memberId: number;
studentId: string | null;
name: string;
email: string;
role: string;
}

export interface ApiResult<T> {
ok: boolean;
status: number;
data?: T;
errorName?: string;
}
Loading