f4d3972d07
Co-authored-by: Martin Slachta <martin.slachta@outlook.com> Reviewed-on: #4
223 lines
9.4 KiB
PHP
223 lines
9.4 KiB
PHP
<?php
|
|
|
|
class RsvReservationsPage extends RsvAdminPage {
|
|
|
|
protected function render_content(): void {
|
|
?>
|
|
<h1>Form Submissions</h1>
|
|
|
|
<hr>
|
|
<div id="reservations_table"></div>
|
|
|
|
<script>
|
|
function rsv_fmt_utc(utc_str) {
|
|
if (!utc_str) return '';
|
|
return new Date(utc_str.replace(' ', 'T') + 'Z').toLocaleString();
|
|
}
|
|
|
|
function rsv_cell_value(value) {
|
|
if (value === null || value === undefined) return '';
|
|
if (typeof value === 'object') return JSON.stringify(value);
|
|
return String(value);
|
|
}
|
|
|
|
function rsv_make_table(head_labels, rows_data, cell_fn) {
|
|
const table = document.createElement('table');
|
|
table.classList.add('wp-list-table', 'widefat', 'fixed', 'striped', 'rsv-detail-table');
|
|
|
|
const thead = document.createElement('thead');
|
|
const header_row = document.createElement('tr');
|
|
for (const label of head_labels) {
|
|
const th = document.createElement('th');
|
|
th.textContent = label;
|
|
header_row.appendChild(th);
|
|
}
|
|
thead.appendChild(header_row);
|
|
table.appendChild(thead);
|
|
|
|
const tbody = document.createElement('tbody');
|
|
for (const row_data of rows_data) {
|
|
const tr = document.createElement('tr');
|
|
for (const cell of cell_fn(row_data)) {
|
|
const td = document.createElement('td');
|
|
td.textContent = cell;
|
|
tr.appendChild(td);
|
|
}
|
|
tbody.appendChild(tr);
|
|
}
|
|
table.appendChild(tbody);
|
|
|
|
return table;
|
|
}
|
|
|
|
function rsv_flatten_form_entries(obj, depth) {
|
|
const rows = [];
|
|
for (const [key, val] of Object.entries(obj)) {
|
|
if (val !== null && typeof val === 'object' && !Array.isArray(val)) {
|
|
rows.push({ key, value: null, depth });
|
|
for (const child of rsv_flatten_form_entries(val, depth + 1)) rows.push(child);
|
|
} else if (Array.isArray(val)) {
|
|
rows.push({ key, value: null, depth });
|
|
val.forEach((item, i) => {
|
|
if (item !== null && typeof item === 'object') {
|
|
rows.push({ key: `[${i}]`, value: null, depth: depth + 1 });
|
|
for (const child of rsv_flatten_form_entries(item, depth + 2)) rows.push(child);
|
|
} else {
|
|
rows.push({ key: `[${i}]`, value: rsv_cell_value(item), depth: depth + 1 });
|
|
}
|
|
});
|
|
} else {
|
|
rows.push({ key, value: rsv_cell_value(val), depth });
|
|
}
|
|
}
|
|
return rows;
|
|
}
|
|
|
|
function rsv_make_form_table(form_values) {
|
|
const table = document.createElement('table');
|
|
table.classList.add('wp-list-table', 'widefat', 'fixed', 'striped', 'rsv-detail-table');
|
|
|
|
const thead = document.createElement('thead');
|
|
const header_row = document.createElement('tr');
|
|
for (const label of ['Field', 'Value']) {
|
|
const th = document.createElement('th');
|
|
th.textContent = label;
|
|
header_row.appendChild(th);
|
|
}
|
|
thead.appendChild(header_row);
|
|
table.appendChild(thead);
|
|
|
|
const tbody = document.createElement('tbody');
|
|
for (const { key, value, depth } of rsv_flatten_form_entries(form_values, 0)) {
|
|
const tr = document.createElement('tr');
|
|
|
|
const td_key = document.createElement('td');
|
|
td_key.textContent = key;
|
|
td_key.classList.add('rsv-form-key');
|
|
td_key.style.setProperty('--rsv-depth', depth);
|
|
if (value === null) td_key.classList.add('rsv-form-key--group');
|
|
|
|
const td_val = document.createElement('td');
|
|
td_val.textContent = value ?? '';
|
|
if (value === null) td_val.classList.add('rsv-form-val--null');
|
|
|
|
tr.appendChild(td_key);
|
|
tr.appendChild(td_val);
|
|
tbody.appendChild(tr);
|
|
}
|
|
table.appendChild(tbody);
|
|
|
|
return table;
|
|
}
|
|
|
|
function rsv_render_reservation_detail(dt, row, data, detail) {
|
|
const td = document.createElement('td');
|
|
td.setAttribute('colspan', 3);
|
|
td.classList.add('rsv-detail-expand');
|
|
|
|
const form_heading = document.createElement('h4');
|
|
form_heading.textContent = 'Form Submission';
|
|
form_heading.classList.add('rsv-detail-heading');
|
|
|
|
const form_values = detail.form_values ?? {};
|
|
let form_content;
|
|
if (Object.keys(form_values).length === 0) {
|
|
form_content = document.createElement('p');
|
|
form_content.textContent = 'No form values recorded.';
|
|
form_content.classList.add('rsv-detail-empty');
|
|
} else {
|
|
form_content = rsv_make_form_table(form_values);
|
|
}
|
|
|
|
const timetable_heading = document.createElement('h4');
|
|
timetable_heading.textContent = 'Timetable Reservations';
|
|
timetable_heading.classList.add('rsv-detail-heading');
|
|
|
|
const timetable_rows = detail.timetable_reservations ?? [];
|
|
let timetable_content;
|
|
if (timetable_rows.length === 0) {
|
|
timetable_content = document.createElement('p');
|
|
timetable_content.textContent = 'No timetable reservations.';
|
|
timetable_content.classList.add('rsv-detail-empty');
|
|
} else {
|
|
timetable_content = rsv_make_table(
|
|
['ID', 'Timetable', 'Start', 'End'],
|
|
timetable_rows,
|
|
r => [r.id, r.timetable_id, rsv_fmt_utc(r.start), rsv_fmt_utc(r.end)]
|
|
);
|
|
}
|
|
|
|
const actions = document.createElement('div');
|
|
actions.classList.add('rsv-detail-actions');
|
|
|
|
const close_btn = document.createElement('button');
|
|
close_btn.classList.add('button');
|
|
close_btn.textContent = 'Close';
|
|
close_btn.onclick = () => dt.refresh_row(row, data);
|
|
actions.appendChild(close_btn);
|
|
|
|
if (detail.pending_confirmation) {
|
|
const base_url = `<?= get_rest_url(null, 'reservations/v1/reservation'); ?>/${data.id}`;
|
|
|
|
const accept_btn = document.createElement('button');
|
|
accept_btn.classList.add('button', 'button-primary');
|
|
accept_btn.textContent = 'Accept';
|
|
accept_btn.onclick = () => {
|
|
accept_btn.disabled = true;
|
|
refuse_btn.disabled = true;
|
|
fetch(base_url + '/accept', { method: 'POST', credentials: 'same-origin' })
|
|
.then(() => dt.refresh())
|
|
.catch(() => { accept_btn.disabled = false; refuse_btn.disabled = false; });
|
|
};
|
|
|
|
const refuse_btn = document.createElement('button');
|
|
refuse_btn.classList.add('button', 'button-secondary', 'rsv-btn-refuse');
|
|
refuse_btn.textContent = 'Refuse';
|
|
refuse_btn.onclick = () => {
|
|
accept_btn.disabled = true;
|
|
refuse_btn.disabled = true;
|
|
fetch(base_url + '/refuse', { method: 'POST', credentials: 'same-origin' })
|
|
.then(() => dt.refresh())
|
|
.catch(() => { accept_btn.disabled = false; refuse_btn.disabled = false; });
|
|
};
|
|
|
|
actions.appendChild(accept_btn);
|
|
actions.appendChild(refuse_btn);
|
|
}
|
|
|
|
td.replaceChildren(form_heading, form_content, timetable_heading, timetable_content, actions);
|
|
return td;
|
|
}
|
|
|
|
var reservations_dt = RsvDataGrid.create_data_grid(
|
|
document.getElementById('reservations_table'),
|
|
RsvReservationResource(),
|
|
{
|
|
'id': RsvDataGrid.action_column('ID', false, {
|
|
'View': RsvDataGrid.func_action(function(dt, row, data) {
|
|
const url = `<?= get_rest_url(null, 'reservations/v1/reservation'); ?>/${data.id}`;
|
|
fetch(url, {
|
|
credentials: 'same-origin',
|
|
headers: { 'Accept': 'application/json' },
|
|
})
|
|
.then(r => r.json())
|
|
.then(detail => {
|
|
row.classList.add(
|
|
'inline-edit-row', 'inline-edit-row-post',
|
|
'quick-edit-row', 'quick-edit-row-post',
|
|
'inline-edit-post', 'inline-editor'
|
|
);
|
|
row.replaceChildren(rsv_render_reservation_detail(dt, row, data, detail));
|
|
});
|
|
}),
|
|
}),
|
|
'form_submit_id': RsvDataGrid.column('Form Submit', false),
|
|
'is_confirmed': RsvDataGrid.column('Confirmed', false),
|
|
}
|
|
);
|
|
reservations_dt.refresh();
|
|
</script>
|
|
<?php
|
|
}
|
|
}
|