Skip to content

Commit 6b1ede0

Browse files
Merge pull request #84 from wafflestudio/fix/unauthorized-rental-prevention
Fix/unauthorized rental prevention
2 parents 908cd5c + e39fdc6 commit 6b1ede0

File tree

5 files changed

+32
-13
lines changed

5 files changed

+32
-13
lines changed

src/components/AdminFAB.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ export function AdminFAB() {
111111
<div className="apply-name">{app.name}</div>
112112
<div className="apply-details">
113113
<span>{app.email}</span>
114-
<span>학번: {app.student_id}</span>
115114
</div>
116115
</div>
117116
<div className="apply-actions">

src/contexts/AuthContext.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ interface AuthState {
1212
userName: string | null;
1313
userType: number | null;
1414
isAdmin: boolean;
15+
permission: number | null;
1516
}
1617

1718
interface AuthContextType extends AuthState {
@@ -26,12 +27,16 @@ interface AuthProviderProps {
2627
}
2728

2829
export function AuthProvider({ children }: AuthProviderProps) {
29-
const [authState, setAuthState] = useState<AuthState>(() => ({
30-
isLoggedIn: !!getAccessToken(),
31-
userName: getStoredUserName(),
32-
userType: getStoredUserType(),
33-
isAdmin: getStoredUserType() === 1,
34-
}));
30+
const [authState, setAuthState] = useState<AuthState>(() => {
31+
const userType = getStoredUserType();
32+
return {
33+
isLoggedIn: !!getAccessToken(),
34+
userName: getStoredUserName(),
35+
userType: userType,
36+
permission: userType, // userType 값을 permission으로도 활용
37+
isAdmin: userType === 1,
38+
};
39+
});
3540

3641
const refreshAuth = useCallback(() => {
3742
const token = getAccessToken();
@@ -41,6 +46,7 @@ export function AuthProvider({ children }: AuthProviderProps) {
4146
isLoggedIn: !!token,
4247
userName,
4348
userType,
49+
permission: userType,
4450
isAdmin: userType === 1,
4551
});
4652
}, []);
@@ -51,6 +57,7 @@ export function AuthProvider({ children }: AuthProviderProps) {
5157
isLoggedIn: false,
5258
userName: null,
5359
userType: null,
60+
permission: null,
5461
isAdmin: false,
5562
});
5663
}, []);

src/pages/ItemListPage.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
22
import { useParams, useNavigate } from 'react-router-dom';
33
import { getAssets, borrowItem, getPictureUrl, type Asset } from '@/api/client';
44
import '@/styles/App.css';
5+
import { useAuth } from '@/contexts/AuthContext';
56

67
const ITEMS_PER_PAGE = 10;
78

@@ -42,8 +43,14 @@ export function ItemListPage() {
4243
fetchAssets();
4344
}, [clubIdNum, isValidClubId]);
4445

46+
const { userType } = useAuth();
47+
4548
// 대여 버튼 클릭 핸들러
4649
const handleRentClick = (asset: Asset) => {
50+
if (userType === 2) {
51+
alert('동아리 관리자의 승인이 필요합니다. 승인 후 대여가 가능합니다.');
52+
return;
53+
}
4754
setSelectedAsset(asset);
4855
const defaultDate = new Date();
4956
// max_rental_days가 설정되어 있으면 그 값을, 아니면 기본 7일

src/pages/ReturnDetailPage.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export function ReturnDetailPage() {
7777
const navigate = useNavigate();
7878
const location = useLocation();
7979
const locationState = location.state as LocationState | null;
80+
const [isLocationVerified, setIsLocationVerified] = useState(false);
8081
const [isSubmitting, setIsSubmitting] = useState(false);
8182
const [isCompressing, setIsCompressing] = useState(false);
8283
const item = locationState?.item;
@@ -238,7 +239,8 @@ export function ReturnDetailPage() {
238239
}
239240

240241
try {
241-
setIsLocating(true); // 로딩 시작
242+
setIsLocating(true);
243+
setIsLocationVerified(false);
242244

243245
const clubResult = await getClub(item.clubId);
244246
if (!clubResult.success || !clubResult.data) {
@@ -251,6 +253,7 @@ export function ReturnDetailPage() {
251253

252254
if (!clubData.location_lat || !clubData.location_lng) {
253255
console.log("동아리 위치 정보가 없어 인증을 생략합니다.");
256+
setIsLocationVerified(false);
254257
setIsLocating(false);
255258
return;
256259
}
@@ -274,6 +277,7 @@ export function ReturnDetailPage() {
274277
alert(`⚠️ 거리가 너무 멉니다! (현재 거리: ${distance.toFixed(1)}m)\n15m 이내에서 다시 시도해주세요.`);
275278
} else {
276279
alert('✅ 위치 인증에 성공했습니다!');
280+
setIsLocationVerified(false);
277281
}
278282
setIsLocating(false);
279283
},
@@ -373,14 +377,11 @@ export function ReturnDetailPage() {
373377
/>
374378
<div className="card-actions" style={{ marginTop: '15px' }}>
375379
<button
376-
// 인자 없이 호출하도록 수정
377380
onClick={handleReturnItem}
378381
className="primary-btn"
379-
// isLocating을 불리언(true/false)으로 관리한다면 아래와 같이 수정
380382
disabled={isLocating}
381383
style={{
382384
width: '100%',
383-
// schedule.id 대신 item.id 사용
384385
backgroundColor: isLocating ? '#999' : '#373F47',
385386
color: 'white',
386387
padding: '12px',
@@ -398,6 +399,8 @@ export function ReturnDetailPage() {
398399
<span className="spinner" />
399400
위치 확인 중...
400401
</>
402+
) : isLocationVerified ? (
403+
'✅ 위치 인증 완료'
401404
) : (
402405
'📍 위치 인증하기'
403406
)}

src/styles/AdminDashboard.css

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
/* Club Info Banner */
88
.club-info-banner {
9-
margin-bottom: 1.5rem;
9+
margin-bottom: 2rem;
1010
padding: 1.25rem 1.5rem;
1111
background: linear-gradient(135deg, var(--primary-color), #6366f1);
1212
border-radius: 16px;
@@ -52,10 +52,13 @@
5252
.admin-tabs {
5353
display: flex;
5454
align-items: center;
55-
gap: 0.75rem;
55+
gap: 0.7rem;
5656
margin-bottom: 1.5rem;
5757
border-bottom: 1px solid var(--glass-border);
58+
59+
padding-top: 0.5rem;
5860
padding-bottom: 1rem;
61+
padding-left: 3px;
5962
overflow-x: auto;
6063
-webkit-overflow-scrolling: touch;
6164
scrollbar-width: none;

0 commit comments

Comments
 (0)