Skip to content

Commit 13c25ff

Browse files
committed
default
1 parent 94aebe2 commit 13c25ff

File tree

12 files changed

+917
-0
lines changed

12 files changed

+917
-0
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
.table-container table th:nth-child(1),
2+
.table-container table td:nth-child(1),
3+
.table-container table th:nth-child(2),
4+
.table-container table td:nth-child(2),
5+
.table-container table th:nth-child(3),
6+
.table-container table td:nth-child(3),
7+
.table-container table th:nth-child(4),
8+
.table-container table td:nth-child(4),
9+
.table-container table th:nth-child(5),
10+
.table-container table td:nth-child(5) {
11+
width: 20%;
12+
}
13+
14+
.table-container table td:nth-child(5) {
15+
text-align: center;
16+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
body {
2+
font-family: 'Noto Sans KR', sans-serif;
3+
}
4+
5+
/* 기본 레이아웃 */
6+
#sidebar-container {
7+
height: 100vh;
8+
background-color: #0a3711;
9+
}
10+
11+
/* 로고 관련 */
12+
.sidebar-logo {
13+
text-align: center;
14+
padding: 20px 0;
15+
}
16+
17+
.sidebar-logo img {
18+
max-width: 80%;
19+
display: block;
20+
margin: 0 auto;
21+
}
22+
23+
/* 메뉴 및 아이콘 관련 */
24+
.menu-icon {
25+
margin-right: 10px;
26+
}
27+
28+
#sidebar .nav-link {
29+
border-radius: 0;
30+
color: #333;
31+
}
32+
33+
#sidebar .nav-link.active {
34+
background-color: #f8f9fa;
35+
color: #007bff;
36+
}
37+
38+
#sidebar .nav-link:hover {
39+
background-color: #f8f9fa;
40+
}
41+
42+
/* 버튼 관련 */
43+
.btn-primary {
44+
background-color: #0a3711 !important;
45+
border-color: #0a3711 !important;
46+
}
47+
48+
.btn-primary:hover, .btn-primary:focus, .btn-primary:active {
49+
background-color: #08290f !important;
50+
border-color: #08290f !important;
51+
}
52+
53+
#content {
54+
padding: 20px;
55+
}
56+
57+
.page {
58+
min-height: 100vh;
59+
background-color: #f5f5f5;
60+
}
61+
62+
.table-header {
63+
display: flex;
64+
justify-content: right;
65+
align-items: center;
66+
margin-bottom: 20px;
67+
}
68+
69+
/* 테이블 관련 */
70+
.table-container {
71+
background-color: #fff;
72+
border: 1px solid #e0e0e0;
73+
border-radius: 5px;
74+
padding: 20px;
75+
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
76+
}
77+
78+
.table {
79+
margin-bottom: 0;
80+
width: 100%;
81+
table-layout: fixed;
82+
}
83+
84+
@media (max-width: 767px) { /* Bootstrap의 md breakpoint 이전 (일반적으로 767px 이하) */
85+
#sidebar-container {
86+
height: 100%;
87+
}
88+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.table-container table th:nth-child(1),
2+
.table-container table td:nth-child(1),
3+
.table-container table th:nth-child(2),
4+
.table-container table td:nth-child(2),
5+
.table-container table th:nth-child(3),
6+
.table-container table td:nth-child(3) {
7+
width: 20%;
8+
}
9+
10+
.table-container table td:nth-child(3) {
11+
text-align: center;
12+
}
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
let isEditing = false;
2+
const RESERVATION_API_ENDPOINT = '/reservations';
3+
const TIME_API_ENDPOINT = '/times';
4+
5+
document.addEventListener('DOMContentLoaded', () => {
6+
document.getElementById('add-reservation').addEventListener('click', addEditableRow);
7+
fetchReservations();
8+
fetchTimes();
9+
});
10+
11+
function fetchTimes() {
12+
requestReadTimes()
13+
.then(data => {
14+
const timeSelectControl = createFormControl(data);
15+
appendFormControlToDocument(timeSelectControl);
16+
})
17+
.catch(error => console.error('Error fetching time:', error));
18+
}
19+
20+
function createFormControl(timeData) {
21+
const select = document.createElement('select');
22+
select.className = 'form-control';
23+
select.id = 'time-select';
24+
25+
const defaultOption = document.createElement('option');
26+
defaultOption.textContent = "시간 선택";
27+
select.appendChild(defaultOption);
28+
29+
timeData.forEach(time => {
30+
const option = document.createElement('option');
31+
option.value = time.id;
32+
option.textContent = time.time;
33+
select.appendChild(option);
34+
});
35+
36+
return select;
37+
}
38+
39+
function appendFormControlToDocument(control) {
40+
document.body.appendChild(control);
41+
}
42+
43+
function fetchReservations() {
44+
requestRead()
45+
.then(renderReservations)
46+
.catch(error => console.error('Error fetching reservations:', error));
47+
}
48+
49+
function renderReservations(data) {
50+
const tableBody = document.getElementById('reservation-table-body');
51+
tableBody.innerHTML = '';
52+
53+
data.forEach(reservation => {
54+
const row = tableBody.insertRow();
55+
insertReservationRow(row, reservation);
56+
});
57+
}
58+
59+
function insertReservationRow(row, reservation) {
60+
['id', 'name', 'date'].forEach((field, index) => {
61+
row.insertCell(index).textContent = reservation[field];
62+
});
63+
64+
row.insertCell(3).textContent = reservation.time.time;
65+
66+
const actionCell = row.insertCell(4);
67+
actionCell.appendChild(createActionButton('삭제', 'btn-danger', deleteRow));
68+
}
69+
70+
function createActionButton(label, className, eventListener) {
71+
const button = document.createElement('button');
72+
button.textContent = label;
73+
button.classList.add('btn', className, 'mr-2');
74+
button.addEventListener('click', eventListener);
75+
return button;
76+
}
77+
78+
function addEditableRow() {
79+
80+
if (isEditing) return; // 이미 편집 중인 경우 추가하지 않음
81+
82+
const tableBody = document.getElementById('reservation-table-body');
83+
const row = tableBody.insertRow();
84+
isEditing = true;
85+
86+
createEditableFieldsFor(row);
87+
addSaveAndCancelButtonsToRow(row);
88+
}
89+
90+
function createEditableFieldsFor(row) {
91+
const nameInput = createInput('text');
92+
const dateInput = createInput('date');
93+
const timeDropdown = document.getElementById('time-select').cloneNode(true);
94+
95+
const fields = ['', nameInput, dateInput, timeDropdown];
96+
97+
fields.forEach((field, index) => {
98+
const cell = row.insertCell(index);
99+
if (typeof field === 'string') {
100+
cell.textContent = field;
101+
} else {
102+
cell.appendChild(field);
103+
}
104+
});
105+
}
106+
107+
function addSaveAndCancelButtonsToRow(row) {
108+
const actionCell = row.insertCell(4);
109+
actionCell.appendChild(createActionButton('확인', 'btn-primary', saveRow));
110+
actionCell.appendChild(createActionButton('취소', 'btn-secondary', () => {
111+
row.remove();
112+
isEditing = false;
113+
}));
114+
}
115+
116+
function createInput(type) {
117+
const input = document.createElement('input');
118+
input.type = type;
119+
input.className = 'form-control';
120+
return input;
121+
}
122+
123+
function saveRow(event) {
124+
const row = event.target.parentNode.parentNode;
125+
const nameInput = row.querySelector('input[type="text"]');
126+
const dateInput = row.querySelector('input[type="date"]');
127+
const timeSelect = row.querySelector('select');
128+
129+
const reservation = {
130+
name: nameInput.value,
131+
date: dateInput.value,
132+
time: timeSelect.value
133+
};
134+
135+
requestCreate(reservation)
136+
.then(data => updateRowWithReservationData(row, data))
137+
.catch(error => console.error('Error:', error));
138+
139+
isEditing = false; // isEditing 값을 false로 설정
140+
}
141+
142+
function updateRowWithReservationData(row, data) {
143+
const cells = row.cells;
144+
cells[0].textContent = data.id;
145+
cells[1].textContent = data.name;
146+
cells[2].textContent = data.date;
147+
cells[3].textContent = data.time.time;
148+
149+
// 버튼 변경: 삭제 버튼으로 변경
150+
cells[4].innerHTML = '';
151+
cells[4].appendChild(createActionButton('삭제', 'btn-danger', deleteRow));
152+
153+
isEditing = false;
154+
155+
// Remove the editable input fields and just show the saved data
156+
for (let i = 1; i <= 3; i++) {
157+
const inputElement = cells[i].querySelector('input');
158+
if (inputElement) {
159+
inputElement.remove();
160+
}
161+
}
162+
}
163+
164+
function deleteRow(event) {
165+
const row = event.target.closest('tr');
166+
const reservationId = row.cells[0].textContent;
167+
168+
requestDelete(reservationId)
169+
.then(() => row.remove())
170+
.catch(error => console.error('Error:', error));
171+
}
172+
173+
function requestCreate(reservation) {
174+
const requestOptions = {
175+
method: 'POST',
176+
headers: {'Content-Type': 'application/json'},
177+
body: JSON.stringify(reservation)
178+
};
179+
180+
return fetch(RESERVATION_API_ENDPOINT, requestOptions)
181+
.then(response => {
182+
if (response.status === 201) return response.json();
183+
throw new Error('Create failed');
184+
});
185+
}
186+
187+
function requestRead() {
188+
return fetch(RESERVATION_API_ENDPOINT)
189+
.then(response => {
190+
if (response.status === 200) return response.json();
191+
throw new Error('Read failed');
192+
});
193+
}
194+
195+
function requestDelete(id) {
196+
const requestOptions = {
197+
method: 'DELETE',
198+
};
199+
200+
return fetch(`${RESERVATION_API_ENDPOINT}/${id}`, requestOptions)
201+
.then(response => {
202+
if (response.status !== 204) throw new Error('Delete failed');
203+
});
204+
}
205+
206+
function requestReadTimes() {
207+
return fetch(TIME_API_ENDPOINT)
208+
.then(response => {
209+
if (response.status === 200) return response.json();
210+
throw new Error('Read failed');
211+
});
212+
}

0 commit comments

Comments
 (0)