Files
pleno-vue/src/components/session/token/SessionUser/Objects/ObjectsGlobal.vue
T

1005 lines
40 KiB
Vue

<script>
import Swal from "sweetalert2";
import {authenticatedRequest, unauthenticatedRequest} from "@/components/session/authenticatedRequest.vue";
import i18n from '@/i18n';
import { getSystemUserIds } from "@/components/session/token/SessionUser/Objects/systemUserIds.js";
// Helper function to get i18n translation
const t = (key) => i18n.global.t(key);
const escapeHtml = (value = "") => String(value)
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll("\"", "&quot;")
.replaceAll("'", "&#39;");
const normalizeDateTimeLocalValue = (value) => {
if (value === null || value === undefined || value === '') {
return '';
}
const normalized = String(value).trim();
if (normalized.includes('T')) {
return normalized.slice(0, 16);
}
const [datePart, timePart = ''] = normalized.split(' ');
if (!datePart || !timePart) {
return normalized;
}
return `${datePart}T${timePart.slice(0, 5)}`;
};
const sanitizeQueryParams = (params = {}) => {
if (!params || typeof params !== 'object') {
return {};
}
return Object.fromEntries(
Object.entries(params).filter(([, value]) => value !== null && value !== undefined)
);
};
const buildQueryString = (params = {}) => {
const queryParams = sanitizeQueryParams(params);
const queryString = new URLSearchParams(queryParams).toString();
return queryString ? `?${queryString}` : '';
};
/**
* The Global Objects object, which contains global functions for objects.
*/
export const ObjectsGlobal = {
icons: {
delete: 'fas fa-trash',
info: 'fas fa-info',
import: 'fas fa-file-import',
},
system_user_ids: getSystemUserIds(), // The system user ids, used by e.g. OrdersTable to highlight system orders
cache: new Map(),
clearCache: () => {
ObjectsGlobal.cache.clear();
},
// Language object now uses i18n for translations
get language() {
return {
title: (object) => {
return `Opret ${object.meta.labels.single.toLowerCase()}`;
},
field: (object, column) => {
return `Rediger ${object.columns[column].label.toLowerCase()}`;
},
get compare() { return t('global.compare'); },
get generate_wash_certificate() { return t('global.generate_wash_certificate'); },
get download_wash_certificate() { return t('global.download_wash_certificate'); },
get not_configured() { return t('global.not_configured'); },
get information() { return t('global.information'); },
get login() { return t('global.login'); },
get ready_to_wash() { return t('global.ready_to_wash'); },
get register() { return t('global.register'); },
get total_price_amount() { return t('global.total_price_amount'); },
get change_customer() { return t('global.change_customer'); },
get settings() { return t('common.settings'); },
get view_order() { return t('global.view_order'); },
get collection() { return t('global.collection'); },
get generated() { return t('global.generated'); },
get login_with_qr_code() { return t('global.login_with_qr_code'); },
get logout() { return t('common.logout'); },
get wash_multiple() { return t('global.wash_multiple'); },
get homepage() { return t('global.homepage'); },
get error_loading_transaction() { return t('global.error_loading_transaction'); },
get requires_action() { return t('global.requires_action'); },
get unknown_error() { return t('common.unknown_error'); },
get expand() { return t('global.expand'); },
get no_transactions_yet() { return t('global.no_transactions_yet'); },
get add_other_product() { return t('global.add_other_product'); },
get no_additional_items() { return t('global.no_additional_items'); },
get additional_items() { return t('global.additional_items'); },
get handheld_pending_order() { return t('global.handheld_pending_order'); },
get confirmation_needed() { return t('global.confirmation_needed'); },
get showing_of_separator() { return t('global.showing_of_separator'); },
get scanning() { return t('global.scanning'); },
get next() { return t('common.next'); },
get send_as_is() { return t('global.send_as_is'); },
get ignore_customer_arrangements() { return t('global.ignore_customer_arrangements'); },
get recommended() { return t('global.recommended'); },
get unlink() { return t('global.unlink'); },
get link() { return t('global.link'); },
get include_all_items_on_invoice() { return t('global.include_all_items_on_invoice'); },
get to_department_and_customer_defaults() { return t('global.to_department_and_customer_defaults'); },
get non_included_products() { return t('global.non_included_products'); },
get invoice_now() { return t('global.invoice_now'); },
get draft() { return t('pos.order.badges.draft'); },
get all_booked() { return t('global.all_booked'); },
get defaults() { return t('global.defaults'); },
get prices() { return t('global.prices'); },
get processing() { return t('global.processing'); },
get abnormal() { return t('global.abnormal'); },
get regret() { return t('global.regret'); },
get search() { return t('global.search'); },
get date_from() { return t('global.date_from'); },
get date_to() { return t('global.date_to'); },
get price_match() { return t('global.price_match'); },
get price_unmatch() { return t('global.price_unmatch'); },
get complete() { return t('global.complete'); },
get lane() { return t('global.lane'); },
get invoice_per_order() { return t('global.invoice_per_order'); },
get price() { return t('common.price'); },
get collapse() { return t('global.collapse'); },
get edit() { return t('global.edit'); },
get home() { return t('global.home'); },
get configuration() { return t('global.configuration'); },
get statistics() { return t('global.statistics'); },
get booked() { return t('global.booked'); },
get not_synchronized() { return t('global.not_synchronized'); },
get only_tank_cleaning() { return t('global.only_tank_cleaning'); },
get reject() { return t('global.reject'); },
get history() { return t('global.history'); },
get emphasis() { return t('global.emphasis'); },
get special_arrangements() { return t('global.special_arrangements'); },
get fixed_price_arrangements() { return t('global.fixed_price_arrangements'); },
get split() { return t('global.split'); },
get recent() { return t('global.recent'); },
get possible_duplicates() { return t('global.possible_duplicates'); },
get successful_syncs() { return t('global.successful_syncs'); },
get save() { return t('common.save'); },
get mark_as_not_duplicate() { return t('global.mark_as_not_duplicate'); },
get my() { return t('global.my'); },
get attach_to_order() { return t('global.attach_to_order'); },
get synchronized() { return t('global.synchronized'); },
get warnings() { return t('global.warnings'); },
get warning() { return t('common.warning'); },
get tank_cleaning() { return t('global.tank_cleaning'); },
get none() { return t('global.none'); },
get default() { return t('global.default'); },
get required() { return t('global.required'); },
get no_applicable_booking_types() { return t('global.no_applicable_booking_types'); },
get optional() { return t('global.optional'); },
get rows() { return t('global.rows'); },
get auto_start_on_lpr() { return t('global.auto_start_on_lpr'); },
get close() { return t('common.close'); },
get no_slots_available() { return t('global.no_slots_available'); },
get past() { return t('global.past'); },
get closed() { return t('global.closed'); },
get related_service() { return t('global.related_service'); },
get usage_log() { return t('global.usage_log'); },
get import_all() { return t('global.import_all'); },
get note() { return t('global.note'); },
get or_scan_plates() { return t('global.or_scan_plates'); },
get import() { return t('global.import'); },
get phone_country_code() { return t('global.phone_country_code'); },
get succeeded() { return t('global.succeeded'); },
get summary() { return t('global.summary'); },
get opening_hours() { return t('global.opening_hours'); },
get customers() { return t('global.customers'); },
get selected() { return t('global.selected'); },
get weekday() { return t('global.weekday'); },
get opening_hour() { return t('global.opening_hour'); },
get closing_hour() { return t('global.closing_hour'); },
get found() { return t('global.found'); },
get waiting() { return t('global.waiting'); },
get selected_multiple() { return t('global.selected_multiple'); },
get queue() { return t('global.queue'); },
get in_progress() { return t('global.in_progress'); },
get failed() { return t('global.failed'); },
get barred() { return t('global.barred'); },
get invalid() { return t('global.invalid'); },
get last_wash() { return t('global.last_wash'); },
get have_not() { return t('global.have_not'); },
get clear() { return t('common.clear'); },
get have() { return t('global.have'); },
get hide() { return t('common.hide'); },
get cancel() { return t('common.cancel'); },
get previous() { return t('global.previous'); },
get unselect() { return t('global.unselect'); },
get select() { return t('common.select'); },
get month() { return t('global.month'); },
get all() { return t('common.all'); },
get copy() { return t('global.copy'); },
get yes() { return t('common.yes'); },
get no() { return t('common.no'); },
get email() { return t('common.email'); },
get subscriptions() { return t('global.subscriptions'); },
get invoice_already_created() { return t('global.invoice_already_created'); },
get active() { return t('common.active'); },
get inactive() { return t('common.inactive'); },
get password() { return t('global.password'); },
get customer() { return t('global.customer'); },
get other_period() { return t('global.other_period'); },
get phone_number() { return t('global.phone_number'); },
get take_picture() { return t('global.take_picture'); },
get no_data() { return t('global.no_data'); },
get confirm_delete() { return t('global.confirm_delete'); },
get clear_all() { return t('global.clear_all'); },
get customer_notes() { return t('global.customer_notes'); },
get done() { return t('common.done'); },
get stop() { return t('global.stop'); },
get confirm_delete_message() { return t('global.confirm_delete_message'); },
get actions() { return t('common.actions'); },
get update() { return t('global.update'); },
get loading() { return t('global.loading'); },
get error() { return t('common.error'); },
get completed() { return t('global.completed'); },
get not_completed() { return t('global.not_completed'); },
get nothing_to_do_all_set() { return t('global.nothing_to_do_all_set'); },
get mark_as_completed() { return t('global.mark_as_completed'); },
get delete() { return t('global.delete'); },
get submit() { return t('global.submit'); },
get reset() { return t('global.reset'); },
get customer_name() { return t('global.customer_name'); },
get total() { return t('global.total'); },
get status() { return t('common.status'); },
get other() { return t('global.other'); },
get remove() { return t('common.remove'); },
get showing() { return t('global.showing'); },
get add() { return t('global.add'); },
get reload() { return t('global.reload'); },
get time() {
return {
get at_hour() { return t('global.time.at_hour'); },
get today() { return t('common.today'); },
get anytime() { return t('global.text.anytime'); },
get yesterday() { return t('global.time.yesterday'); },
get this_month() { return t('global.time.this_month'); },
get last_month() { return t('global.time.last_month'); },
get this_year() { return t('global.time.this_year'); },
get all() { return t('global.time.all'); },
get date() { return t('common.date'); },
};
},
get error_messages() {
return {
get not_array() { return t('global.error_messages.not_array'); },
get not_object() { return t('global.error_messages.not_object'); },
get not_string() { return t('global.error_messages.not_string'); },
};
},
get in() { return t('global.in'); },
get nothing_left_to_show() { return t('global.nothing_left_to_show'); },
get pending() { return t('global.pending'); },
get cancelled() { return t('global.cancelled'); },
get show_content() { return t('global.show_content'); },
get show() { return t('common.show'); },
get hide_content() { return t('global.hide_content'); },
get quantity() { return t('common.quantity'); },
get manage() { return t('global.manage'); },
get customer_number() { return t('global.customer_number'); },
get payment() {
return {
get method() { return t('global.payment.method'); },
get fixed_pricing() { return t('global.payment.fixed_pricing'); },
get status() {
return {
get pending() { return t('global.payment.status.pending'); },
get succeeded() { return t('global.payment.status.succeeded'); },
get failed() { return t('global.payment.status.failed'); },
get cancelled() { return t('global.payment.status.cancelled'); },
};
},
get type() {
return {
get card() { return t('global.payment.type.card'); },
get bank_transfer() { return t('global.payment.type.bank_transfer'); },
get cash() { return t('global.payment.type.cash'); },
get other() { return t('global.payment.type.other'); },
};
},
get payment_recieved() { return t('global.payment.payment_recieved'); },
get amount() { return t('global.payment.amount'); },
get date() { return t('common.date'); },
get reference() { return t('common.reference'); },
get transaction_id() { return t('global.payment.transaction_id'); },
get payment_intent() { return t('global.payment.payment_intent'); },
get receipt() { return t('global.payment.receipt'); },
get receipt_url() { return t('global.payment.receipt_url'); },
get discount() { return t('common.discount'); },
};
},
get invoice() { return t('common.invoice'); },
get text() {
return {
get anytime() { return t('global.text.anytime'); },
get today() { return t('common.today'); },
get yesterday() { return t('global.text.yesterday'); },
get this_week() { return t('global.text.this_week'); },
get last_7_days() { return t('global.text.last_7_days'); },
get last_week() { return t('global.text.last_week'); },
get this_month() { return t('global.text.this_month'); },
get last_month() { return t('global.text.last_month'); },
get other_month() { return t('global.text.other_month'); },
get this_year() { return t('global.text.this_year'); },
get same_week_last_year() { return t('global.text.same_week_last_year'); },
get same_month_last_year() { return t('global.text.same_month_last_year'); },
get all() { return t('global.text.all'); },
get year() { return t('common.year'); },
get month() { return t('global.text.month'); },
get search_in() { return t('global.text.search_in'); },
};
},
get capture() { return t('global.capture'); },
};
},
requestOptions: {
defaultOptions: {
authenticated: true,
},
applyOptions: (optionsObject) => {
return {
...ObjectsGlobal.requestOptions.defaultOptions,
...optionsObject
};
},
},
parse: {
boolean: (value) => {
return value === 'true' || value === '1' || value === true || value === 1;
},
number: (value) => {
return parseInt(value);
},
string: (value) => {
return value;
},
},
set: {
/**
* Set a column in an object
* @param {string} endpoint The endpoint of the object
* @param {number} id The id of the object
* @param {string} column The column to set
* @param {string} value The value to set
* @returns {Promise} The promise
*/
column: async (endpoint, id, column, value) => {
ObjectsGlobal.clearCache();
return authenticatedRequest(
endpoint,
"PUT",
{
id: id,
[column]: value,
},
(error) => {
ObjectsGlobal.errorHandler.handleError(error);
}
).then((response) => {
ObjectsGlobal.clearCache();
return response;
});
}
},
errorHandler: {
/**
* Handle an error
* @param {object} error The error object
*/
handleError: (error) => {
console.error(error);
let message = ObjectsGlobal.language.unknown_error;
if (error.response && error.response.data && error.response.data.data.message) {
message = error.response.data.data.message;
}
Swal.fire({
icon: 'error',
title: ObjectsGlobal.language.error,
text: message,
});
}
},
add: {
/**
* Add an object
* @param {string} endpoint The endpoint of the object
* @param {object} data The data (columns) of the object to add (e.g. [name = "Test", description = "Test"])
* @param {object} optionsObject The options (e.g. {authenticated: false})
* @returns {Promise} The promise
*/
object: async (endpoint, data, optionsObject = {}) => {
ObjectsGlobal.clearCache();
let options = ObjectsGlobal.requestOptions.applyOptions(optionsObject);
let promise;
if (options.authenticated) {
promise = authenticatedRequest(
endpoint,
"POST",
data
);
} else {
promise = unauthenticatedRequest(
endpoint,
"POST",
data
);
}
return promise.then((response) => {
ObjectsGlobal.clearCache();
return response;
});
}
},
delete: {
/**
* Delete an object
* @param {string} endpoint The endpoint of the object
* @param {number} id The id of the object
* @returns {Promise} The promise
*/
object: async (endpoint, id, optionsObject = {}) => {
ObjectsGlobal.clearCache();
const params = new URLSearchParams({ id: String(id) });
if (optionsObject?.confirmed === true) {
params.set("confirmed", "true");
}
return authenticatedRequest(
`${endpoint}?${params.toString()}`,
"DELETE"
).then((response) => {
ObjectsGlobal.clearCache();
return response;
});
}
},
get: {
/**
* Get an object
* @param {string} endpoint The endpoint of the object
* @param {number|array} id The id of the object, OR an array of get parameters (e.g. {id: 1, department_id: 2})
* @param {object} optionsObject The options (e.g. {authenticated: false})
* @returns {Promise} The promise
*/
object: async (endpoint, id, optionsObject = {}) => {
const requestParams = typeof id === 'object' && id !== null
? sanitizeQueryParams(id)
: {id};
const cacheKey = JSON.stringify({endpoint, id: requestParams, optionsObject});
if (ObjectsGlobal.cache.has(cacheKey)) {
return ObjectsGlobal.cache.get(cacheKey);
}
const requestEndpoint = `${endpoint}${buildQueryString(requestParams)}`;
let options = ObjectsGlobal.requestOptions.applyOptions(optionsObject);
let promise;
if (options.authenticated) {
promise = authenticatedRequest(
requestEndpoint,
"GET"
).then((response) => {
return response.data.data;
}).catch((error) => {
ObjectsGlobal.cache.delete(cacheKey);
});
} else {
promise = unauthenticatedRequest(
requestEndpoint,
"GET"
).then((response) => {
return response.data.data;
}).catch((error) => {
ObjectsGlobal.cache.delete(cacheKey);
});
}
ObjectsGlobal.cache.set(cacheKey, promise);
return promise;
},
list: async (endpoint, optionsObject = {filters: {}, pagination: {page: 1, limit: 100, order: 'id:DESC'}}) => {
return await authenticatedRequest(
endpoint,
"GET",
{
// Map the filters as: filter1:value1,filter2:value2,filter3:value3
filters: Object.entries(optionsObject.filters).flatMap(([key, value]) => {
if (Array.isArray(value)) {
return value.map(v => `${key}:${v}`);
}
return `${key}:${value}`;
}).join(','),
page: optionsObject.pagination.page,
limit: optionsObject.pagination.limit,
search: optionsObject.search || '',
order: optionsObject.pagination.order || 'id:DESC',
}).then((response) => {
return response.data.data;
}).catch((error) => {
return [];
});
},
/**
* Get all objects
* @param {string} endpoint The endpoint of the object
* @param data The filters to apply to the objects (e.g. {filters: "department_id:1"})
* @returns {Promise} The promise
*/
objects: async (endpoint, data = {}) => {
const requestData = sanitizeQueryParams(data);
if (requestData.filters && typeof requestData.filters === 'object') {
requestData.filters = Object.entries(requestData.filters).flatMap(([key, value]) => {
if (Array.isArray(value)) {
return value.map(v => `${key}:${v}`);
}
return `${key}:${value}`;
}).join(',');
}
const cacheKey = JSON.stringify({endpoint, data: requestData});
if (ObjectsGlobal.cache.has(cacheKey)) {
return ObjectsGlobal.cache.get(cacheKey);
}
const promise = authenticatedRequest(
endpoint,
"GET",
requestData
).then((response) => {
return response.data.data;
}).catch((error) => {
ObjectsGlobal.cache.delete(cacheKey);
});
ObjectsGlobal.cache.set(cacheKey, promise);
return promise;
},
/**
* Get multiple objects by their ids
* @param {string} endpoint The endpoint of the object
* @param {array} ids The ids of the objects
* @returns {Promise} The promise
*/
objectsByIds(endpoint, ids) {
return Promise.all(ids.map(id => this.object(endpoint, id)));
}
},
/**
* Show the create object form
* @param {object} object The object
* @param {function} onAfterSubmit The function to call after the form is submitted
* @param {object} lockedValues The locked values of the object (e.g. {department_id: 1})
* @returns {Promise} The promise
*/
showCreateObjectForm: async (object, onAfterSubmit = null, lockedValues = {}) => {
let html = await ObjectsGlobal.generateCreateObjectForm(object.columns, lockedValues);
return Swal.fire({
title: ObjectsGlobal.language.title(object),
html: html,
showCancelButton: true,
confirmButtonText: ObjectsGlobal.language.save,
cancelButtonText: ObjectsGlobal.language.cancel,
didOpen: () => {
const helpers = {
getFieldValue: (id) => document.getElementById(id)?.value,
setFieldValue: (id, value) => {
const el = document.getElementById(id);
if (el) el.value = value;
},
};
// Attach onchange listeners if defined in the object configuration
for (const column in object.columns) {
const config = object.columns[column].creation;
if (config && typeof config.onchange === 'function') {
const element = document.getElementById(column);
if (element) {
element.addEventListener('change', (event) => {
config.onchange(event, helpers);
});
// Trigger once on load if there's an initial value
if (element.value) {
config.onchange({ target: element }, helpers);
}
}
}
}
},
preConfirm: () => {
// For each column in the object, get the value from the input field and add it to the object
const argument_array = [];
// Add the object add arguments in the same order as the add function
for (const column in object.columns) {
if (object.columns[column].creation.required) {
const fieldValue = lockedValues[column] !== undefined
? lockedValues[column]
: document.getElementById(column)?.value;
try {
if (fieldValue === undefined || fieldValue === null || fieldValue === '') {
throw new Error('Feltet ' + object.columns[column].label + ' er påkrævet.');
}
} catch (error) {
Swal.showValidationMessage(
`${error}`
);
return false;
}
argument_array.push(fieldValue);
}
}
// Call the add function with the arguments
return object.add(...argument_array).then(() => {
if (onAfterSubmit !== null) {
return Promise.resolve(onAfterSubmit());
}
}).catch((error) => {
Swal.showValidationMessage(
`Fejl: ${error.message || error}`
);
});
}
});
},
generateCreateObjectForm: async (columns, lockedValues) => {
let html = '';
// Check if the column is a locked value
function isLockedValue(column) {
return lockedValues[column] !== undefined;
}
// If it is a locked value, set the value to the locked value
function getLockedValue(column, defaultValue = '') {
return lockedValues[column] !== undefined ? lockedValues[column] : '';
}
function getCreationDefaultValue(column, defaultValue = '') {
return columns[column].creation.default !== undefined ? columns[column].creation.default : defaultValue;
}
function isBooleanDefault(column, value) {
const defaultValue = getCreationDefaultValue(column, true);
return String(defaultValue) === String(value);
}
// Add the form fields
for (const column in columns) {
if (columns[column].creation.required) {
switch (columns[column].type) {
case 'string':
html += `<div class="field">
<label class="label has-text-black">${columns[column].label}</label>
<div class="control">
<input class="input has-background-light has-text-black" type="text" id="${column}">
</div>
</div>`;
break;
case 'number':
html += `<div class="field" style="display: ${isLockedValue(column) ? 'none' : 'block'}">
<label class="label has-text-black">${columns[column].label}</label>
<div class="control">
<input class="input has-background-light has-text-black" type="number" id="${column}" value="${getLockedValue(column)}" ${isLockedValue(column) ? 'disabled' : ''}>
</div>
</div>`;
break;
case 'boolean':
html += `<div class="field">
<label class="label has-text-black">${columns[column].label}</label>
<div class="control">
<!-- Select -->
<div class="select is-fullwidth">
<select id="${column}" class="has-background-light has-text-black">
<option value="true" ${isBooleanDefault(column, true) ? 'selected' : ''}>Ja</option>
<option value="false" ${isBooleanDefault(column, false) ? 'selected' : ''}>Nej</option>
</select>
</div>
</div>`;
break;
case 'datetime':
html += `<div class="field">
<label class="label has-text-black">${columns[column].label}</label>
<div class="control">
<input class="input has-background-light has-text-black" type="datetime-local" id="${column}">
</div>
</div>`;
break;
case 'select':
//var isLocked = lockedValues[column] !== undefined;
html += `<div class="field">
<label class="label has-text-black">${columns[column].label}</label>
<div class="control">
<!-- Select -->
<div class="select is-fullwidth">
<select id="${column}" class="has-background-light has-text-black" ${isLockedValue(column) ? 'disabled' : ''} >`;
// Check if the columns options are a function, if so, call it (and wait for it to finish). Otherwise, make the array awaitable
var awaitOptions = columns[column].options;
if (typeof columns[column].options === 'function') {
awaitOptions = columns[column].options(lockedValues);
} else {
awaitOptions = new Promise((resolve) => {
resolve(columns[column].options);
});
}
// Wait for the options to be ready
await awaitOptions.then((response) => {
// How long are the list of options
for (let i = 0; i < response.length; i++) {
var tmp = response[i];
// Check if the column exists in the locked values
//var lockedValue = lockedValues[column] === tmp.id ? 'selected' : '';
//html += `<option value="${tmp.id}" ${lockedValue}>`;
html += `<option value="${tmp.id}" ${getLockedValue(column) == tmp.id ? 'selected' : ''}>`;
html += `${tmp.name}`;
html += `</option>`;
}
});
html += `</select>
</div>
</div>`;
break;
}
}
}
// Return the form
return html;
},
async generateEditObjectFieldForm(object, column, value, options = { filters: {
//customer_id: 12345679,
//department_id: 2
}, pagination: { limit: 10, page: 1 } }) {
let html = '';
function ifNullDefault(value, defaultValue) {
return value === null ? defaultValue : value;
}
switch (object.columns[column].type) {
case 'string':
html += `<div class="field">
<label class="label has-text-black">${object.columns[column].label}</label>
<div class="control">
<input class="input has-background-light has-text-black" type="text" id="${column}" value="${ifNullDefault(value, '')}">
</div>
</div>`;
break;
case 'number':
html += `<div class="field">
<label class="label has-text-black">${object.columns[column].label}</label>
<div class="control">
<input class="input has-background-light has-text-black" type="number" id="${column}" value="${value}">
</div>
</div>`;
break;
case 'boolean':
html += `<div class="field">
<label class="label has-text-black">${object.columns[column].label}</label>
<div class="control">
<!-- Select -->
<div class="select is-fullwidth">
<select id="${column}" class="has-background-light has-text-black">
<option value="true" ${value === true ? 'selected' : ''}>${ObjectsGlobal.language.yes}</option>
<option value="false" ${value === false ? 'selected' : ''}>${ObjectsGlobal.language.no}</option>
</select>
</div>
</div>`;
break;
case 'datetime':
html += `<div class="field">
<label class="label has-text-black">${object.columns[column].label}</label>
<div class="control">
<input class="input has-background-light has-text-black" type="datetime-local" id="${column}" value="${normalizeDateTimeLocalValue(value)}">
</div>
</div>`;
break;
case 'multi-select':
html += `<div class="field">
<label class="label has-text-black">${object.columns[column].label}</label>
<div class="control" id="${column}-container">`;
await object.columns[column].options(options).then((response) => {
const currentValues = Array.isArray(value) ? value : [];
for (let i = 0; i < response.length; i++) {
const tmp = response[i];
const isChecked = currentValues.includes(tmp.id);
html += `<label class="checkbox is-block has-text-black" style="margin-bottom: 0.5rem;">
<input type="checkbox" name="${column}" value="${tmp.id}" ${isChecked ? 'checked' : ''}>
${tmp.name ? tmp.name : tmp.id}
</label>`;
}
});
html += `</div></div>`;
break;
case 'select':
html += `<div class="field">
<label class="label has-text-black">${object.columns[column].label}</label>
<div class="control">
<!-- Select -->
<div class="select is-fullwidth">
<select id="${column}" class="has-background-light has-text-black">`;
await object.columns[column].options(options).then((response) => {
console.log(response);
// How long are the list of options
for (let i = 0; i < response.length; i++) {
const tmp = response[i];
const optionValue = tmp?.id ?? "";
const optionLabel = tmp?.name ? tmp.name : optionValue;
console.log(tmp);
html += `<option value="${escapeHtml(optionValue)}" ${(value == optionValue || (value === null && optionValue === 0)) ? 'selected' : ''}>${escapeHtml(optionLabel)}</option>`;
}
});
html += `</select>
</div>
</div>`;
break;
}
return html;
},
showEditObjectFieldForm: async (object, id, column, value, onAfterSubmit = null, options = {}) => {
let html = await ObjectsGlobal.generateEditObjectFieldForm(object, column, value, options);
const isMultiSelect = object.columns[column].type === 'multi-select';
return Swal.fire({
title: ObjectsGlobal.language.field(object, column),
html: html,
showCancelButton: true,
confirmButtonText: ObjectsGlobal.language.save,
cancelButtonText: ObjectsGlobal.language.cancel,
preConfirm: () => {
try {
let newValue;
if (isMultiSelect) {
// Collect all checked checkbox values for multi-select
const checkboxes = document.querySelectorAll(`input[name="${column}"]:checked`);
newValue = Array.from(checkboxes).map(cb => cb.value);
} else {
newValue = document.getElementById(column).value;
}
// Call the set function with the arguments
return object.set[column](id, newValue).then(() => {
if (onAfterSubmit !== null) {
return Promise.resolve(onAfterSubmit());
}
}).catch((error) => {
console.error('Unable to edit object field', error);
// Show an error message
Swal.showValidationMessage(
`Fejl: ${error.message || error}`
);
});
} catch (error) {
console.error('Unable to edit object field', error);
// Show an error message
Swal.showValidationMessage(
`Fejl: ${error}`
);
}
}
});
},
showObjects: async (object, displayColumns = null, filters = {}, displayActions = false) => {
let html = '';
// Get the objects
await object.get.all().then((objects) => {
// Filter the objects
if (Object.keys(filters).length > 0) {
objects = objects.filter((object) => {
for (const filter in filters) {
if (object[filter] !== filters[filter]) {
return false;
}
}
return true;
});
}
// Add the table
html += `<table class="table is-fullwidth has-text-left has-background-light">
<thead>
<tr>`;
// Add the table headers
if (displayColumns === null) {
for (const column in object.columns) {
html += `<th class="has-text-black">${object.columns[column].label}</th>`;
}
} else {
for (const column in displayColumns) {
html += `<th class="has-text-black">${object.columns[column].label}</th>`;
}
}
if (displayActions) {
html += `<th class="has-text-black">Handlinger</th>`;
}
html += `
</tr>
</thead>
<tbody>`;
// Add the table rows
for (let i = 0; i < objects.length; i++) {
html += `<tr>`;
// Add the table columns
if (displayColumns === null) {
for (const column in object.columns) {
html += `<td class="has-text-black">${objects[i][column]}</td>`;
}
} else {
for (const column in displayColumns) {
html += `<td class="has-text-black">${objects[i][column]}</td>`;
}
}
// Add the actions column
if (displayActions) {
html += `<td>
<button class="button is-small is-info" @click="showEditObjectFieldForm(${objects[i].id}, 'name', '${objects[i].name}')">Rediger</button>
<button class="button is-small is-danger" @click="deleteObject(${objects[i].id})">Slet</button>
</td>`;
html += `</tr>`;
}
}
// If the list is empty
if (objects.length === 0) {
html += `<tr>
<td colspan="${Object.keys(object.columns).length + 1}">${ObjectsGlobal.language.no_data}</td>
</tr>`;
}
html += `</tbody>
</table>`;
});
return Swal.fire({
title: object.meta.title,
html: html,
showCancelButton: !displayActions,
showConfirmButton: displayActions,
confirmButtonText: ObjectsGlobal.language.save,
cancelButtonText: ObjectsGlobal.language.cancel,
});
},
showDeleteObjectForm: async (object, id, onAfterSubmit = null) => {
return Swal.fire({
title: `Fjern ${object.meta.labels.single}?`,
text: ObjectsGlobal.language.confirm_delete_message,
showCancelButton: true,
showConfirmButton: true,
confirmButtonText: ObjectsGlobal.language.confirm_delete,
cancelButtonText: ObjectsGlobal.language.cancel,
showLoaderOnConfirm: true,
preConfirm: () => {
return object.delete(id).then(() => {
if (onAfterSubmit !== null) {
return Promise.resolve(onAfterSubmit());
}
}).catch((error) => {
Swal.showValidationMessage(
`Fejl: ${error.message || error}`
);
});
}
});
},
showDeleteConfirmationModal: async (object, id, onAfterSubmit = null) => {
return Swal.fire({
title: `Fjern ${object.meta.labels.single}?`,
text: ObjectsGlobal.language.confirm_delete_message,
showCancelButton: true,
showConfirmButton: true,
confirmButtonText: ObjectsGlobal.language.confirm_delete,
cancelButtonText: ObjectsGlobal.language.cancel,
showLoaderOnConfirm: true,
preConfirm: () => {
return object.delete.single(id).then(() => {
if (onAfterSubmit !== null) {
onAfterSubmit();
}
}).catch((error) => {
Swal.showValidationMessage(
`Fejl: ${error.message || error}`
);
});
}
})
},
performAction: async (endpoint, id, action, data = {}, onAfterSubmit = null) => {
ObjectsGlobal.clearCache();
return authenticatedRequest(
`${endpoint}/${action}`,
"POST",
{
id: parseInt(id),
...data
}
).then((response) => {
ObjectsGlobal.clearCache();
if (onAfterSubmit !== null) {
onAfterSubmit();
}
return response;
});
},
functions: {
/**
* Format a date to a local string
* @param date
* @returns {string}
*/
formatDate: (date) => {
return new Date(date).toLocaleString('da-DK');
}
}
};
</script>