import { createToast } from './bs'; import { getElements, apiPatch, hasError, getSelectedOptions } from './util'; /** * Mark each option element in the selected columns element as 'selected' so they are submitted to * the API. */ function saveTableConfig(): void { for (const element of getElements('select[name="columns"] option')) { element.selected = true; } } /** * Add columns to the table config select element. */ function addColumns(event: Event): void { for (const selectedOption of getElements('#id_available_columns > option')) { if (selectedOption.selected) { for (const selected of getElements('#id_columns')) { selected.appendChild(selectedOption.cloneNode(true)); } selectedOption.remove(); } } event.preventDefault(); } /** * Remove columns from the table config select element. */ function removeColumns(event: Event): void { for (const selectedOption of getElements('#id_columns > option')) { if (selectedOption.selected) { for (const available of getElements('#id_available_columns')) { available.appendChild(selectedOption.cloneNode(true)); } selectedOption.remove(); } } event.preventDefault(); } /** * Submit form configuration to the NetBox API. */ async function submitFormConfig( url: string, formConfig: Dict, ): Promise> { return await apiPatch(url, formConfig); } /** * Handle table config form submission. Sends the selected columns to the NetBox API to update * the user's table configuration preferences. */ function handleSubmit(event: Event): void { event.preventDefault(); const element = event.currentTarget as HTMLFormElement; // Get the API URL for submitting the form const url = element.getAttribute('data-url'); if (url == null) { const toast = createToast( 'danger', 'Error Updating Table Configuration', 'No API path defined for configuration form.', ); toast.show(); return; } // Determine if the form action is to reset the table config. const reset = document.activeElement?.getAttribute('value') === 'Reset'; // Create an array from the dot-separated config path. E.g. tables.DevicePowerOutletTable becomes // ['tables', 'DevicePowerOutletTable'] const path = element.getAttribute('data-config-root')?.split('.') ?? []; if (reset) { // If we're resetting the table config, create an empty object for this table. E.g. // tables.PlatformTable becomes {tables: PlatformTable: {}} const data = path.reduceRight>((value, key) => ({ [key]: value }), {}); // Submit the reset for configuration to the API. submitFormConfig(url, data).then(res => { if (hasError(res)) { const toast = createToast('danger', 'Error Resetting Table Configuration', res.error); toast.show(); } else { location.reload(); } }); return; } // Get all the selected options from any select element in the form. const options = getSelectedOptions(element, 'select[name=columns]'); // Create an object mapping the select element's name to all selected options for that element. const formData: Dict> = Object.assign( {}, ...options.map(opt => ({ [opt.name]: opt.options })), ); // Create an object mapping the configuration path to the select element names, which contain the // selection options. E.g. {tables: {DevicePowerOutletTable: {columns: ['label', 'type']}}} const data = path.reduceRight>((value, key) => ({ [key]: value }), formData); // Submit the resulting object to the API to update the user's preferences for this table. submitFormConfig(url, data).then(res => { if (hasError(res)) { const toast = createToast('danger', 'Error Updating Table Configuration', res.error); toast.show(); } else { location.reload(); } }); } /** * Initialize table configuration elements. */ export function initTableConfig(): void { for (const element of getElements('#save_tableconfig')) { element.addEventListener('click', saveTableConfig); } for (const element of getElements('#add_columns')) { element.addEventListener('click', addColumns); } for (const element of getElements('#remove_columns')) { element.addEventListener('click', removeColumns); } for (const element of getElements('form.userconfigform')) { element.addEventListener('submit', handleSubmit); } }