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
8 changes: 4 additions & 4 deletions back/timetables.json
Original file line number Diff line number Diff line change
Expand Up @@ -2963,7 +2963,7 @@
{
"subject": "Chemical Engineering Capstone Design(1)",
"time": "Thu(10 ~ 12)",
"room": "Changjo Hall (Graduate School)(008)-202"
"room": "Changjo Hall(008)-202"
},
{
"subject": "Nano Materials",
Expand Down Expand Up @@ -3265,12 +3265,12 @@
{
"subject": "Food Microbiology and Fermentation Laboratory",
"time": "Mon(1 ~ 4)",
"room": "Changjo Hall (Graduate School)(008)-206"
"room": "Changjo Hall(008)-206"
},
{
"subject": "Food Microbiology and Fermentation Laboratory",
"time": "Mon(6 ~ 9)",
"room": "Changjo Hall (Graduate School)(008)-206"
"room": "Changjo Hall(008)-206"
},
{
"subject": "Co-peratiove Education Program(3)",
Expand Down Expand Up @@ -3350,7 +3350,7 @@
{
"subject": "Food Biochemistry Laboratory",
"time": "Wed(6 ~ 9)",
"room": "Changjo Hall (Graduate School)(008)-101"
"room": "Changjo Hall(008)-101"
},
{
"subject": "Food Process Engineering 2",
Expand Down
8 changes: 4 additions & 4 deletions front/public/timetables.json
Original file line number Diff line number Diff line change
Expand Up @@ -2963,7 +2963,7 @@
{
"subject": "Chemical Engineering Capstone Design(1)",
"time": "Thu(10 ~ 12)",
"room": "Changjo Hall (Graduate School)(008)-202"
"room": "Changjo Hall(008)-202"
},
{
"subject": "Nano Materials",
Expand Down Expand Up @@ -3265,12 +3265,12 @@
{
"subject": "Food Microbiology and Fermentation Laboratory",
"time": "Mon(1 ~ 4)",
"room": "Changjo Hall (Graduate School)(008)-206"
"room": "Changjo Hall(008)-206"
},
{
"subject": "Food Microbiology and Fermentation Laboratory",
"time": "Mon(6 ~ 9)",
"room": "Changjo Hall (Graduate School)(008)-206"
"room": "Changjo Hall(008)-206"
},
{
"subject": "Co-peratiove Education Program(3)",
Expand Down Expand Up @@ -3350,7 +3350,7 @@
{
"subject": "Food Biochemistry Laboratory",
"time": "Wed(6 ~ 9)",
"room": "Changjo Hall (Graduate School)(008)-101"
"room": "Changjo Hall(008)-101"
},
{
"subject": "Food Process Engineering 2",
Expand Down
2 changes: 1 addition & 1 deletion front/src/components/BuildingCard.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import '../styles/ReservePage.css'; // ReservePage와 동일한 스타일 사용
import '../styles/ReservePage.css';
import FavoriteButton from './FavoriteButton';

const BuildingCard = ({
Expand Down
4 changes: 2 additions & 2 deletions front/src/components/Header.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import logo from '../assets/starlogo.png';
import '../styles/Header.css'; // CSS 분리 후 import
import '../styles/Header.css';

const Header = () => {
const navigate = useNavigate();
Expand All @@ -25,7 +25,7 @@ const Header = () => {
onClick={() => navigate('/')}
/>

{/* 모바일메뉴 */}
{/* mobilemenu */}
<div className="menu" onClick={() => setMenuOpen(!menuOpen)}>☰</div>

<nav className={`nav ${menuOpen ? 'open' : ''}`}>
Expand Down
2 changes: 0 additions & 2 deletions front/src/components/HotspotCard.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
// src/components/HotspotCard.js
import React from 'react';
import '../styles/HotspotCard.css';

const HotspotCard = ({ rank, building, onReserveClick }) => {
const rankLabel = ['1st', '2nd', '3rd'][rank - 1];

// 등수에 따라 클래스 이름 분기
const rankClass =
rank === 1 ? 'first' :
rank === 2 ? 'second' :
Expand Down
2 changes: 1 addition & 1 deletion front/src/components/PurposeModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const PurposeModal = ({ onClose, onSubmit }) => {
}

onSubmit({
peopleCount: Number(peopleCount), // 숫자로 백엔드에 전송
peopleCount: Number(peopleCount),
purpose,
});

Expand Down
1 change: 0 additions & 1 deletion front/src/components/RoomSelectModal.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// src/components/RoomSelectModal.js
import React from 'react';
import Modal from './Modal';

Expand Down
42 changes: 7 additions & 35 deletions front/src/pages/HotspotPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const HotspotPage = () => {
const fetchData = async (category) => {
try {
let response;

if (category === 'Auditorium Size / Large Hall') {
response = await axios.get('https://star-isih.onrender.com/api/analytics/popular-buildings/by-large-group');
} else if (category === 'Study Friendly') {
Expand Down Expand Up @@ -57,27 +58,8 @@ const HotspotPage = () => {

setHotspots(matched);
} catch (err) {
console.error('🔥 Fallback to mock data due to error:', err);
setHotspots([
{
id: '32',
rank: 1,
name: 'Frontier Hall',
image: getBuildingImage('32'),
},
{
id: '2',
rank: 2,
name: 'Dasan Hall',
image: getBuildingImage('2'),
},
{
id: '2',
rank: 3,
name: 'Dasan Hall',
image: getBuildingImage('2'),
},
]);
console.error('[Hotspot Fetch Error] Failed to load hotspot data:', err);
setHotspots([]); // fallback 제거: 빈 배열로 처리
}
};

Expand All @@ -90,7 +72,7 @@ const HotspotPage = () => {
const res = await axios.get(`https://star-isih.onrender.com/api/buildings/rooms?buildingNo=${building.id}`);
const availableRooms = res.data.rooms.map(room => ({
room: `Room ${room}`,
time: '8:00 - 17:50',
time: '08:00 - 17:50',
}));

setModalBuilding({
Expand All @@ -100,18 +82,8 @@ const HotspotPage = () => {
availableRooms,
});
} catch (err) {
console.error('Failed to fetch rooms:', err);
const mockRooms = [
{ room: 'Room 101', time: '08:00 - 09:50' },
{ room: 'Room 202', time: '10:00 - 11:50' },
];

setModalBuilding({
id: building.id,
name: building.name,
image: building.image,
availableRooms: mockRooms,
});
console.error('[Room Fetch Error] Failed to fetch available rooms:', err);
alert('Failed to load available rooms. Please try again later.');
}
};

Expand Down Expand Up @@ -169,4 +141,4 @@ const HotspotPage = () => {
);
};

export default HotspotPage;
export default HotspotPage;
39 changes: 21 additions & 18 deletions front/src/pages/LoginPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import signstudentnumber from '../assets/signprofile.png';
const LoginPage = () => {
const navigate = useNavigate();
const [form, setForm] = useState({ studentNumber: '', password: '' });
const [errorMessage, setErrorMessage] = useState('');

const handleChange = (e) => {
setForm({ ...form, [e.target.name]: e.target.value });
Expand All @@ -20,27 +21,28 @@ const LoginPage = () => {
const res = await axios.post('https://star-isih.onrender.com/api/users/login', form);
localStorage.setItem('token', res.data.token);
localStorage.setItem('user', JSON.stringify(res.data.user));
navigate('/'); // ✅ 메인 페이지로 이동
navigate('/'); // ✅ redirect to main
} catch (err) {
console.warn('⚠️ 백엔드 로그인 실패 - mock 처리로 우회');
console.error('[Login Error]', err);

const mockUser = {
name: '테스트유저',
studentNumber: form.studentNumber || '23100000',
major: 'ITM',
favorites: ['프론티어관', '다산관'],
};

localStorage.setItem('token', 'mock-token');
localStorage.setItem('user', JSON.stringify(mockUser));
alert('⚠️ 서버 미연결 상태 - mock 로그인 처리됨');
navigate('/');
if (err.response) {
if (err.response.status === 400) {
setErrorMessage('User not found. Please check your student number.');
} else if (err.response.status === 401) {
setErrorMessage('Incorrect password. Please try again.');
} else {
setErrorMessage('Login failed. Please try again later.');
}
} else {
setErrorMessage('Network error. Please check your internet connection.');
}
}
};

const handleSubmit = (e) => {
e.preventDefault(); // ⛔ 새로고침 방지
handleLogin(); // ✅ 로그인 실행
e.preventDefault();
setErrorMessage('');
handleLogin();
};

return (
Expand All @@ -56,7 +58,6 @@ const LoginPage = () => {
</h1>
</div>

{/* ✅ form으로 감싸고 onSubmit 적용 */}
<form className="login-box" onSubmit={handleSubmit}>
<h2 className="login-label">Log in</h2>

Expand Down Expand Up @@ -84,8 +85,10 @@ const LoginPage = () => {
/>
</div>

{errorMessage && <p className="error-message">{errorMessage}</p>}

<div className="login-buttons">
<button type="submit">Log in</button> {/* ✅ 기본 로그인 버튼 */}
<button type="submit">Log in</button>
<button type="button" onClick={() => navigate('/signup')}>Sign in</button>
</div>
</form>
Expand All @@ -95,4 +98,4 @@ const LoginPage = () => {
);
};

export default LoginPage;
export default LoginPage;
55 changes: 18 additions & 37 deletions front/src/pages/MyReservationPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,32 @@ import Footer from '../components/Footer';
import Modal from '../components/Modal';
import '../styles/MyReservationPage.css';

// ✅ 이미지 경로 매핑
const getBuildingImage = (number) => {
try {
return require(`../assets/buildings img/${number}.png`);
} catch {
return require('../assets/buildings img/2.png'); // Default: Dasan Hall
return require('../assets/buildings img/2.png');
}
};

const MyReservationPage = () => {
const [reservations, setReservations] = useState([]);
const [buildings, setBuildings] = useState([]); // 건물 번호 <-> 이름 매핑용
const [buildings, setBuildings] = useState([]);
const [showModal, setShowModal] = useState(false);
const [modalStep, setModalStep] = useState('confirm');
const [selectedReservation, setSelectedReservation] = useState(null);
const [error, setError] = useState('');

// 🧭 건물 리스트 받아오기
const fetchBuildings = async () => {
try {
const res = await axios.get('https://star-isih.onrender.com/api/buildings');
setBuildings(res.data.buildings || []);
} catch (err) {
console.warn('⚠️ 건물 정보 fetch 실패, fallback mock 사용');
setBuildings([
{ buildingNo: 32, buildingName: 'Frontier Hall' },
{ buildingNo: 2, buildingName: 'Dasan Hall' }
]);
console.error('[Error] Failed to load buildings:', err);
setError('Failed to load building information. Please try again later.');
}
};

// 🗓️ 예약 정보 불러오기
const fetchReservations = async () => {
try {
const token = localStorage.getItem('token');
Expand All @@ -44,25 +39,8 @@ const MyReservationPage = () => {
});
setReservations(res.data);
} catch (err) {
console.warn('⚠️ 예약 정보 fetch 실패, mock 사용');
setReservations([
{
_id: 'mock1',
building: 'Frontier Hall',
room: '107',
date: '2024-06-05',
startTime: '08:00',
endTime: '10:50'
},
{
_id: 'mock2',
building: 'Dasan Hall',
room: '201',
date: '2024-06-06',
startTime: '09:00',
endTime: '09:50'
}
]);
console.error('[Error] Failed to fetch reservations:', err);
setError('Failed to load reservations. Please try again later.');
}
};

Expand All @@ -88,11 +66,11 @@ const MyReservationPage = () => {
await axios.delete(`https://star-isih.onrender.com/api/reservations/${selectedReservation._id}`, {
headers: { Authorization: `Bearer ${token}` }
});
setReservations(reservations.filter(r => r._id !== selectedReservation._id));
setReservations(prev => prev.filter(r => r._id !== selectedReservation._id));
setModalStep('success');
} catch (err) {
console.error('❌ 예약 취소 실패:', err);
alert('Reservation cancel failed.');
console.error('[Error] Cancel failed:', err);
alert('Failed to cancel reservation. Please try again.');
closeModal();
}
};
Expand All @@ -108,6 +86,9 @@ const MyReservationPage = () => {
<Header />
<main className="my-reservation-content">
<h2>My reservation ({reservations.length})</h2>

{error && <p className="error-message">{error}</p>}

<div className="reservation-list">
{reservations.map((res) => {
const buildingNo = getBuildingNo(res.building);
Expand Down Expand Up @@ -141,17 +122,17 @@ const MyReservationPage = () => {
- {selectedReservation.startTime} ~ {selectedReservation.endTime}, {selectedReservation.date}
</div>
</div>
<p style={{ marginTop: '24px' }}>Are you sure to cancel your reservation?</p>
<p style={{ marginTop: '24px' }}>Are you sure you want to cancel this reservation?</p>
<div className="modal-buttons">
<button onClick={confirmCancel}>Yes!</button>
<button onClick={closeModal}>No!</button>
<button onClick={confirmCancel}>Yes</button>
<button onClick={closeModal}>No</button>
</div>
</div>
) : (
<div>
<p>Reservation canceled successfully!</p>
<div className="modal-buttons">
<button onClick={closeModal}>Check Reservation</button>
<button onClick={closeModal}>OK</button>
</div>
</div>
)}
Expand All @@ -163,4 +144,4 @@ const MyReservationPage = () => {
);
};

export default MyReservationPage;
export default MyReservationPage;
Loading
Loading