(#2) - forms improvements
This commit is contained in:
@@ -1,219 +1,222 @@
|
||||
<?php
|
||||
|
||||
function rsv_reservations_page(): void {
|
||||
?>
|
||||
<h1>Form Submissions</h1>
|
||||
class RsvReservationsPage extends RsvAdminPage {
|
||||
|
||||
<hr>
|
||||
<div id="reservations_table"></div>
|
||||
protected function render_content(): void {
|
||||
?>
|
||||
<h1>Form Submissions</h1>
|
||||
|
||||
<script>
|
||||
function rsv_fmt_utc(utc_str) {
|
||||
if (!utc_str) return '';
|
||||
return new Date(utc_str.replace(' ', 'T') + 'Z').toLocaleString();
|
||||
}
|
||||
<hr>
|
||||
<div id="reservations_table"></div>
|
||||
|
||||
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);
|
||||
<script>
|
||||
function rsv_fmt_utc(utc_str) {
|
||||
if (!utc_str) return '';
|
||||
return new Date(utc_str.replace(' ', 'T') + 'Z').toLocaleString();
|
||||
}
|
||||
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);
|
||||
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);
|
||||
}
|
||||
tbody.appendChild(tr);
|
||||
}
|
||||
table.appendChild(tbody);
|
||||
thead.appendChild(header_row);
|
||||
table.appendChild(thead);
|
||||
|
||||
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 });
|
||||
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);
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
table.appendChild(tbody);
|
||||
|
||||
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);
|
||||
return table;
|
||||
}
|
||||
|
||||
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));
|
||||
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 });
|
||||
}
|
||||
});
|
||||
}),
|
||||
}),
|
||||
'form_submit_id': RsvDataGrid.column('Form Submit', false),
|
||||
'is_confirmed': RsvDataGrid.column('Confirmed', false),
|
||||
} else {
|
||||
rows.push({ key, value: rsv_cell_value(val), depth });
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
);
|
||||
reservations_dt.refresh();
|
||||
</script>
|
||||
<?php
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user