Files

223 lines
9.4 KiB
PHP
Raw Permalink Normal View History

2026-06-11 19:03:29 +02:00
<?php
2026-06-12 16:05:14 +02:00
class RsvReservationsPage extends RsvAdminPage {
2026-06-11 19:03:29 +02:00
2026-06-12 16:05:14 +02:00
protected function render_content(): void {
?>
<h1>Form Submissions</h1>
2026-06-11 19:03:29 +02:00
2026-06-12 16:05:14 +02:00
<hr>
<div id="reservations_table"></div>
2026-06-11 19:03:29 +02:00
2026-06-12 16:05:14 +02:00
<script>
function rsv_fmt_utc(utc_str) {
if (!utc_str) return '';
return new Date(utc_str.replace(' ', 'T') + 'Z').toLocaleString();
}
2026-06-11 19:03:29 +02:00
2026-06-12 16:05:14 +02:00
function rsv_cell_value(value) {
if (value === null || value === undefined) return '';
if (typeof value === 'object') return JSON.stringify(value);
return String(value);
2026-06-11 19:03:29 +02:00
}
2026-06-12 16:05:14 +02:00
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);
2026-06-11 19:03:29 +02:00
}
2026-06-12 16:05:14 +02:00
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);
2026-06-11 19:03:29 +02:00
2026-06-12 16:05:14 +02:00
return table;
}
2026-06-11 19:03:29 +02:00
2026-06-12 16:05:14 +02:00
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 });
}
2026-06-11 19:03:29 +02:00
}
2026-06-12 16:05:14 +02:00
return rows;
2026-06-11 19:03:29 +02:00
}
2026-06-12 16:05:14 +02:00
function rsv_make_form_table(form_values) {
const table = document.createElement('table');
table.classList.add('wp-list-table', 'widefat', 'fixed', 'striped', 'rsv-detail-table');
2026-06-11 19:03:29 +02:00
2026-06-12 16:05:14 +02:00
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;
2026-06-11 19:03:29 +02:00
}
2026-06-12 16:05:14 +02:00
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);
}
2026-06-11 19:03:29 +02:00
2026-06-12 16:05:14 +02:00
const timetable_heading = document.createElement('h4');
timetable_heading.textContent = 'Timetable Reservations';
timetable_heading.classList.add('rsv-detail-heading');
2026-06-11 19:03:29 +02:00
2026-06-12 16:05:14 +02:00
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)]
);
}
2026-06-11 19:03:29 +02:00
2026-06-12 16:05:14 +02:00
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);
}
2026-06-11 19:03:29 +02:00
2026-06-12 16:05:14 +02:00
td.replaceChildren(form_heading, form_content, timetable_heading, timetable_content, actions);
return td;
}
2026-06-11 19:03:29 +02:00
2026-06-12 16:05:14 +02:00
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));
});
}),
2026-06-11 19:03:29 +02:00
}),
2026-06-12 16:05:14 +02:00
'form_submit_id': RsvDataGrid.column('Form Submit', false),
'is_confirmed': RsvDataGrid.column('Confirmed', false),
}
);
reservations_dt.refresh();
</script>
<?php
}
2026-06-11 19:03:29 +02:00
}