1005 lines
40 KiB
Vue
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("&", "&")
|
|
.replaceAll("<", "<")
|
|
.replaceAll(">", ">")
|
|
.replaceAll("\"", """)
|
|
.replaceAll("'", "'");
|
|
|
|
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>
|