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

1028 lines
40 KiB
Vue

<script>
import {authenticatedRequest} from "@/components/session/authenticatedRequest.vue";
import {computed, ref} from "vue";
import {parseError} from "@/components/request/HandleGlobalError.vue";
import {SuperUserObject} from "@/components/session/token/superUserObject.vue";
import {AdminUserObject} from "@/components/session/token/AdminUserObject.vue";
import {EditFieldForm} from "@/components/session/token/SessionUser/EditFieldForm.vue";
import {reCAPTCHA} from "@/components/session/token/SessionUser/reCAPTCHA/reCAPTCHA.vue";
import {Categories} from "@/components/session/token/SessionUser/Objects/Categories.vue";
import {Products} from "@/components/session/token/SessionUser/Objects/Products.vue";
import {ProductOptions} from "@/components/session/token/SessionUser/Objects/ProductOptions.vue";
import {Departments} from "@/components/session/token/SessionUser/Objects/Departments.vue";
import {DepartmentCategories} from "@/components/session/token/SessionUser/Objects/DepartmentCategories.vue";
import {EconomicDepartments} from "@/components/session/token/SessionUser/Objects/economicDepartments.vue";
import {EconomicProducts} from "@/components/session/token/SessionUser/Objects/economicProducts.vue";
import {Roles} from "@/components/session/token/SessionUser/Objects/Roles.vue";
import {Permissions} from "@/components/session/token/SessionUser/Objects/Permissions.vue";
import {CollectedOrderInvoices} from "@/components/session/token/SessionUser/Objects/CollectedOrderInvoices.vue";
import {DepartmentDailyReports} from "@/components/session/token/SessionUser/Objects/DepartmentDailyReports.vue";
import {DepartmentDailyReportComplaints} from "@/components/session/token/SessionUser/Objects/DepartmentDailyReportComplaints.vue";
import {Goals} from "@/components/session/token/SessionUser/Objects/Goals.vue";
import {Notifications} from "@/components/session/token/SessionUser/Objects/Notifications.vue";
import {Bookings} from "@/components/session/token/SessionUser/Objects/Bookings.vue";
import {Orders} from "@/components/session/token/SessionUser/Objects/Orders.vue";
import {Forms} from "@/components/session/token/SessionUser/Objects/Forms.vue";
import {Branding} from "@/components/session/token/SessionUser/Objects/Brandings.vue";
import {Vehicles} from "@/components/session/token/SessionUser/Objects/Vehicles.vue";
import {DepartmentNotificationSms} from "@/components/session/token/SessionUser/Objects/DepartmentNotificationSms.vue";
import {
DepartmentTimeBookingsOpeningHours
} from "@/components/session/token/SessionUser/Objects/DepartmentTimeBookingsOpeningHours.vue";
import {
DepartmentTimeBookingsTypes
} from "@/components/session/token/SessionUser/Objects/DepartmentTimeBookingsTypes.vue";
import {
DepartmentTimeBookingsEntries
} from "@/components/session/token/SessionUser/Objects/DepartmentTimeBookingsEntries.vue";
import {DepartmentVariables} from "@/components/session/token/SessionUser/Objects/DepartmentVariables.vue";
import {XLVaskVehicleTypes} from "@/components/session/token/SessionUser/Objects/XLVaskVehicleTypes.vue";
import {OpenAI} from "@/components/session/token/superUser/modules/openAI/OpenAI.vue";
import {DepartmentLanes} from "@/components/session/token/SessionUser/Objects/DepartmentLanes.vue";
import {DepartmentGates} from "@/components/session/token/SessionUser/Objects/DepartmentGates.vue";
import {DepartmentRelays} from "@/components/session/token/SessionUser/Objects/DepartmentRelays.vue";
import {OrderBookings} from "@/components/session/token/SessionUser/Objects/OrderBookings.vue";
import {ModuleActionLogs} from "@/components/session/token/SessionUser/Objects/ModuleActionLogs.vue";
import {SelfServeQuestions} from "@/components/session/token/SessionUser/Objects/SelfServeQuestions.vue";
import {SelfServeTasks} from "@/components/session/token/SessionUser/Objects/SelfServeTasks.vue";
import {SelfServeConditions} from "@/components/session/token/SessionUser/Objects/SelfServeConditions.vue";
import {SelfServeVehicleConditions} from "@/components/session/token/SessionUser/Objects/SelfServeVehicleConditions.vue";
import {SelfServeConditionRules} from "@/components/session/token/SessionUser/Objects/SelfServeConditionRules.vue";
import {SelfServeMachineTypes} from "@/components/session/token/SessionUser/Objects/SelfServeMachineTypes.vue";
import {ObjectsGlobal} from "@/components/session/token/SessionUser/Objects/ObjectsGlobal.vue";
import Swal from "sweetalert2";
import {SubuserGrants} from "@/components/session/token/SessionUser/Objects/SubuserGrants.vue";
import {Subusers} from "@/components/session/token/SessionUser/Objects/Subusers.vue";
const normalizePositiveInteger = (value) => {
const parsedValue = Number.parseInt(String(value ?? ""), 10);
return Number.isInteger(parsedValue) && parsedValue > 0 ? parsedValue : null;
};
const getStoredSessionSnapshot = () => {
if (typeof window === "undefined") {
return null;
}
try {
const token = window.localStorage.getItem("token");
if (token === null) {
return null;
}
return {
token,
isSubuser: window.localStorage.getItem("is_subuser") === "true",
selectedCustomerNumber: normalizePositiveInteger(window.localStorage.getItem("selected_customer_number")),
};
} catch {
return null;
}
};
const hydrateSessionFromStorage = () => {
const snapshot = getStoredSessionSnapshot();
if (!snapshot) {
return false;
}
SessionUser.token.value = snapshot.token;
SessionUser.authenticated.value = true;
SessionUser.isSubuser.value = snapshot.isSubuser;
SessionUser.subuser.selectedGrantCustomerNumber.value = snapshot.selectedCustomerNumber;
return true;
};
/**
* Initiate the user session on app start
* @returns {Promise<void>}
*/
export const initiateOnAppStart = async () => {
if (hydrateSessionFromStorage()) {
if (SessionUser.isSubuser.value) {
await getSubuserSessionData();
} else {
await getSessionData();
}
}
};
/**
* Authenticate the user
* @param {string} customer_number
* @param {string} password
* @returns {Promise<void>}
*/
export const authenticateUser = async (customer_number, password) => {
return await authenticatedRequest("/auth/login", "POST", {
customer_number,
password,
})
.then((response) => {
SessionUser.token.value = response.data.token;
SessionUser.authenticated.value = true;
localStorage.setItem("token", response.data.token);
localStorage.removeItem("is_subuser");
// Reload the window to make sure the user's session is initiated
window.location.reload();
})
.catch((error) => {
parseError(error, 'auth');
console.error(error);
});
};
/**
* Authenticate a subuser with password
* Supports three authentication methods:
* - phone_country_code + phone + password
* - subuser_id + password
* - username + password
* @param {Object} credentials - The authentication credentials
* @param {number} [credentials.phone_country_code] - Phone country code (1-3 digits)
* @param {number} [credentials.phone] - Phone number (4-15 digits)
* @param {number} [credentials.subuser_id] - Subuser ID
* @param {string} [credentials.username] - Username
* @param {string} credentials.password - Password (8-255 characters)
* @returns {Promise<void>}
*/
export const authenticateSubuser = async (credentials) => {
const { phone_country_code, phone, subuser_id, username, password } = credentials;
// Build the request body based on provided credentials
let requestBody = { password };
if (phone_country_code !== undefined && phone !== undefined) {
requestBody.phone_country_code = phone_country_code;
requestBody.phone = phone;
} else if (subuser_id !== undefined) {
requestBody.subuser_id = subuser_id;
} else if (username !== undefined) {
requestBody.username = username;
} else {
throw new Error('Invalid credentials: must provide phone_country_code+phone, subuser_id, or username');
}
return await authenticatedRequest("/subusers/auth/password", "POST", requestBody)
.then((response) => {
SessionUser.token.value = response.data.data.session;
SessionUser.authenticated.value = true;
SessionUser.isSubuser.value = true;
console.warn('Subuser session initiated', response);
// Make sure the subuser's session is valid before setting the token and reloading
if (!response.data.data.session) {
throw new Error('No session token received for subuser authentication');
}
localStorage.setItem("token", response.data.data.session);
localStorage.setItem("is_subuser", "true");
// Reload the window to make sure the subuser's session is initiated
window.location.reload();
})
.catch((error) => {
parseError(error, 'auth');
console.error(error);
throw error;
});
};
/**
* Get the subuser's session data from /subusers/me
* @returns {Promise<void>}
*/
export const getSubuserSessionData = async () => {
return await authenticatedRequest("/subusers/me", "GET")
.then((response) => {
const data = response.data.data || response.data;
SessionUser.subuser.id.value = data.id;
SessionUser.subuser.username.value = data.username;
SessionUser.subuser.name.value = data.name;
SessionUser.subuser.email.value = data.email;
SessionUser.subuser.phone.country_code.value = data.phone_country_code;
SessionUser.subuser.phone.number.value = data.phone;
SessionUser.subuser.grants.value = data.grants || [];
SessionUser.subuser.created_at.value = data.created_at;
SessionUser.subuser.updated_at.value = data.updated_at;
SessionUser.subuser.suspended_at.value = data.suspended_at;
SessionUser.subuser.cached_at.value = new Date();
// Build permissions from grants
const allPermissions = [];
if (data.grants && Array.isArray(data.grants)) {
data.grants.forEach(grant => {
if (grant.permissions && Array.isArray(grant.permissions)) {
allPermissions.push(...grant.permissions);
}
});
}
SessionUser.permissions.value = [...new Set(allPermissions)];
SessionUser.initiated.value = true;
})
.catch((error) => {
parseError(error, 'auth');
console.error(error);
Swal.fire({
title: 'Fejl ved hentning af subbrugerdata',
text: 'Der opstod en fejl ved hentning af dine brugerdata. Prøv at logge ind igen.',
icon: 'error',
confirmButtonText: 'Ok'
}).then(() => {
SessionUser.auth.forceClearSession();
SessionUser.functions.redirectTo.pages.login();
});
});
};
/**
* Get the user's session data
* @returns {Promise<void>}
*/
export const getSessionData = async () => {
return await authenticatedRequest("/auth/session", "GET")
.then((response) => {
SessionUser.user.id.value = response.data.data.id;
SessionUser.user.customer_number.value = response.data.data.customer_number;
SessionUser.user.group_id.value = response.data.data.group_id;
SessionUser.user.email.value = response.data.data.email;
SessionUser.user.phone.number.value = response.data.data.phone.number;
SessionUser.user.phone.country_code.value = response.data.data.phone.country_code;
SessionUser.user.notifications.wash_certificate_email.value = response.data.data.notifications.wash_certificate_email;
SessionUser.user.notifications.email_notifications_enabled.value = response.data.data.notifications.email_notifications_enabled;
SessionUser.user.notifications.sms_notifications_enabled.value = response.data.data.notifications.sms_notifications_enabled;
SessionUser.user.created_at.value = response.data.data.created_at;
SessionUser.user.updated_at.value = response.data.data.updated_at;
SessionUser.user.display_name.value = response.data.data.display_name;
// Set the last cached time to now, this is used to determine if the user's data is outdated
SessionUser.user.cached_at.value = new Date();
SessionUser.permissions.value = response.data.data.permissions;
SessionUser.runtimeConfig.economic.transactionDraftCustomerNumber.value = normalizePositiveInteger(
response?.data?.data?.runtime_config?.economic?.transaction_draft_customer_number
);
// E-conomic data is only fetched if the array isn't empty
if (response.data.data.economic_customer.length > 0) {
SessionUser.economicData.customerNumber.value = response.data.data.economic_customer.customerNumber;
SessionUser.economicData.name.value = response.data.data.economic_customer.name;
// Set the display name to the e-conomic name if it's empty
if (SessionUser.user.display_name.value === null || SessionUser.user.display_name.value === 'Unnamed') {
SessionUser.user.display_name.value = response.data.data.economic_customer.name;
}
SessionUser.economicData.address.value = response.data.data.economic_customer.address;
SessionUser.economicData.zip.value = response.data.data.economic_customer.zip;
SessionUser.economicData.city.value = response.data.data.economic_customer.city;
SessionUser.economicData.mobilePhone.value = response.data.data.economic_customer.mobilePhone;
SessionUser.economicData.email.value = response.data.data.economic_customer.email;
SessionUser.economicData.cvr.value = response.data.data.economic_customer.corporateIdentificationNumber;
SessionUser.economicData.currency.value = response.data.data.economic_customer.currency;
SessionUser.economicData.country.value = response.data.data.economic_customer.country;
SessionUser.economicData.cached_at.value = new Date();
}
SessionUser.initiated.value = true;
})
.catch((error) => {
parseError(error, 'auth');
console.error(error);
Swal.fire({
title: 'Fejl ved hentning af brugerdata',
text: 'Der opstod en fejl ved hentning af dine brugerdata. Prøv at logge ind igen.',
icon: 'error',
confirmButtonText: 'Ok'
}).then(() => {
SessionUser.auth.forceClearSession();
SessionUser.functions.redirectTo.pages.login();
});
});
};
/**
* Destroy the user's session
* @returns {Promise<void>}
*/
export const destroySession = async () => {
return await authenticatedRequest("/auth/logout", "GET")
.then(() => {
localStorage.removeItem("token");
localStorage.removeItem("is_subuser");
SessionUser.forceRefresh();
})
.catch((error) => {
parseError(error, 'auth');
localStorage.removeItem("token");
localStorage.removeItem("is_subuser");
SessionUser.forceRefresh();
console.error(error);
}).finally(() => {
localStorage.removeItem("token");
localStorage.removeItem("is_subuser");
SessionUser.forceRefresh();
})
};
/**
* Force sign out the user
*/
export const forceClearSession = () => {
localStorage.removeItem("token");
localStorage.removeItem("is_subuser");
SessionUser.forceRefresh();
};
/**
* Define the user session variables
*/
export const SessionUser = {
user: {
id: ref(null),
customer_number: ref(null),
display_name: ref(null),
group_id: ref(null),
email: ref(null),
phone: {
number: ref(null),
country_code: ref(null),
},
created_at: ref(null),
updated_at: ref(null),
cached_at: ref(null),
notifications: {
wash_certificate_email: ref(null),
email_notifications_enabled: ref(null),
sms_notifications_enabled: ref(null),
setEmailNotificationsEnabled: (enabled) => {
return SessionUser.request("/account/notifications", "PUT", {
email_notifications_enabled: enabled,
}).then(() => {
SessionUser.user.notifications.email_notifications_enabled.value = enabled;
}).catch((error) => {
parseError(error, 'user_notifications');
console.error(error);
});
},
setSmsNotificationsEnabled: (enabled) => {
return SessionUser.request("/account/notifications", "PUT", {
sms_notifications_enabled: enabled,
}).then(() => {
SessionUser.user.notifications.sms_notifications_enabled.value = enabled;
}).catch((error) => {
parseError(error, 'user_notifications');
console.error(error);
});
},
setWashCertificateEmail: (email) => {
return SessionUser.request("/account/notifications", "PUT", {
wash_certificate_email: email,
}).then(() => {
SessionUser.user.notifications.wash_certificate_email.value = email;
}).catch((error) => {
parseError(error, 'user_notifications');
console.error(error);
});
}
}
},
/** Subuser profile data (populated when authenticated as subuser) */
subuser: {
id: ref(null),
username: ref(null),
name: ref(null),
email: ref(null),
phone: {
country_code: ref(null),
number: ref(null),
},
grants: ref([]),
created_at: ref(null),
updated_at: ref(null),
suspended_at: ref(null),
cached_at: ref(null),
/** Currently selected grant (billing_customer_number) for X-Customer-Number header */
selectedGrantCustomerNumber: ref(localStorage.getItem('selected_customer_number') ? parseInt(localStorage.getItem('selected_customer_number')) : null),
/**
* Select a grant by billing_customer_number
* @param {number|null} customerNumber - The billing_customer_number to select, or null to clear
*/
selectGrant: (customerNumber) => {
if (customerNumber === null) {
localStorage.removeItem('selected_customer_number');
SessionUser.subuser.selectedGrantCustomerNumber.value = null;
} else {
localStorage.setItem('selected_customer_number', customerNumber.toString());
SessionUser.subuser.selectedGrantCustomerNumber.value = customerNumber;
}
},
/**
* Get the currently selected grant object
* @returns {Object|null} The selected grant object or null
*/
getSelectedGrant: () => {
const selectedNum = SessionUser.subuser.selectedGrantCustomerNumber.value;
if (!selectedNum || !SessionUser.subuser.grants.value) return null;
return SessionUser.subuser.grants.value.find(g => g.billing_customer_number === selectedNum) || null;
},
},
/** Whether the current session is a subuser session */
isSubuser: ref(false),
/** The user's permissions */
permissions: ref([]),
availableVirtualPermissions: [
'has_superuser_token',
],
runtimeConfig: {
economic: {
transactionDraftCustomerNumber: ref(null),
},
},
virtualPermissions: computed(() => {
/** These permissions are NOT set by default for superusers, this should ONLY be used for superusers */
let virtualPermissions = [];
// Super user token
// Check if the user has a superuser token in the local storage
if (localStorage.getItem("superuser_token") !== null) {
// Validate the superuser token
if (localStorage.getItem("superuser_token") !== SessionUser.token.value) {
virtualPermissions.push('has_superuser_token');
}
}
return virtualPermissions;
}),
permissions_computed: computed(() => {
return [...SessionUser.permissions.value];
}),
hasPermission: (permission) => {
//console.log('Checking permission: ' + permission + ' - ' + hasPermission);
// Check if the permission is virtual
if (SessionUser.availableVirtualPermissions.values().toArray().includes(permission)) {
console.warn('Virtual permission detected: ' + permission);
return SessionUser.virtualPermissions.value.values().toArray().includes(permission);
}
// For subusers, check permissions from the selected grant only
if (SessionUser.isSubuser.value) {
const selectedGrant = SessionUser.subuser.getSelectedGrant();
if (selectedGrant && selectedGrant.permissions) {
return selectedGrant.permissions.includes(permission);
}
// If no grant is selected, fall back to all permissions
return SessionUser.permissions.value.includes(permission);
}
return (SessionUser.permissions.value.includes(permission) || SessionUser.permissions.value.includes('superuser'));
},
/** Can access shortcuts */
canAccessAdmin: () => {
return SessionUser.hasPermission('admin') || SessionUser.hasPermission('superuser');
},
canAccessSuperUser: () => {
return SessionUser.hasPermission('superuser');
},
canAccessUser: () => {
return SessionUser.hasPermission('user');
},
canAccessDepartment: (id = 0) => {
return (SessionUser.hasPermission('department_access_' + parseInt(id)) || SessionUser.canAccessSuperUser());
},
canAccessDeveloper: () => {
// If the url is localhost, we can assume that the user is a developer
return window.location.hostname === "localhost";
},
canAccessGuest: () => {
return true; // All users can access guest features
},
hasToken: () => {
return SessionUser.token.value !== null || hydrateSessionFromStorage();
},
/** Shortcuts for the user's group */
superUser: SuperUserObject,
adminUser: AdminUserObject,
/** Objects */
objects: {
categories: Categories,
products: Products,
product_options: ProductOptions,
departments: Departments,
department_categories: DepartmentCategories,
economic_departments: EconomicDepartments,
economic_products: EconomicProducts,
roles: Roles,
permissions: Permissions,
collectedOrderInvoices: CollectedOrderInvoices,
department_daily_reports: DepartmentDailyReports,
department_daily_report_complaints: DepartmentDailyReportComplaints,
goals: Goals,
notifications: Notifications,
orders: Orders,
forms: Forms,
bookings: Bookings,
Branding: Branding,
vehicles: Vehicles,
department_notification_sms: DepartmentNotificationSms,
department_time_bookings_opening_hours: DepartmentTimeBookingsOpeningHours,
department_time_bookings_types: DepartmentTimeBookingsTypes,
department_time_bookings_entries: DepartmentTimeBookingsEntries,
department_variables: DepartmentVariables,
xlvask_vehicle_types: XLVaskVehicleTypes,
openai: OpenAI,
department_lanes: DepartmentLanes,
department_gates: DepartmentGates,
department_relays: DepartmentRelays,
order_bookings: OrderBookings,
module_action_logs: ModuleActionLogs,
self_serve_questions: SelfServeQuestions,
self_serve_tasks: SelfServeTasks,
self_serve_conditions: SelfServeConditions,
self_serve_vehicle_conditions: SelfServeVehicleConditions,
self_serve_condition_rules: SelfServeConditionRules,
self_serve_machine_types: SelfServeMachineTypes,
subusers: Subusers,
subuser_grants: SubuserGrants,
global: ObjectsGlobal,
},
/** The user's token and authentication status */
token: ref(null),
authenticated: ref(false),
/** The user's e-conomic data */
economicData: {
customerNumber: ref(null),
name: ref(null),
address: ref(null),
zip: ref(null),
city: ref(null),
mobilePhone: ref(null),
email: ref(null),
cvr: ref(null),
currency: ref(null),
country: ref(null),
cached_at: ref(null),
},
/** Check if the user's data is outdated (Older than 24 hours) */
isDataOutdated: () => {
return new Date() - SessionUser.user.cached_at.value > 1000 * 60 * 60 * 24;
},
/** Check if the user's e-conomic data is outdated (Older than 24 hours) */
isEconomicDataOutdated: () => {
return new Date() - SessionUser.economicData.cached_at.value > 1000 * 60 * 60 * 24;
},
/** Check if the user has e-conomic data */
hasEconomicData: () => {
return SessionUser.economicData.customerNumber.value !== null;
},
getCustomerNumber: () => {
return SessionUser.user.customer_number.value;
},
/** Is the object fully initiated */
initiated: ref(false),
isInitiated: () => {
return SessionUser.initiated.value;
},
/**
* Check if the user has a superuser token stored (This is used when the superuser is impersonating a user)
* @returns {boolean}
*/
hasSuperUserToken: () => {
return localStorage.getItem("superuser_token") !== null;
},
/**
* Set the user's token
* @param {string} token
*/
setToken: (token) => {
SessionUser.token.value = token;
localStorage.setItem("token", token);
// Reload the window to make sure the user's session is initiated
window.location.reload();
},
/**
* Does the user have a specific attribute (Basically just a link to check if the user has permission`has_attribute_{{attribute}}`) // E.g. hasAttribute('admin') would check if the user has the 'has_attribute_key_admin' permission
* @param {string} attribute
* @returns {boolean}
*/
hasAttribute: (attribute) => {
return SessionUser.hasPermission('has_attribute_' + attribute);
},
/**
* Get the user's display name
*/
getName: () => {
// If the user is a subuser, return the subuser's name
if (SessionUser.isSubuser.value) {
return SessionUser.subuser.name.value || SessionUser.subuser.username.value || 'Unnamed';
}
return SessionUser.user.display_name.value || 'Unnamed';
},
/** Reset the user's session data (Cache) */
forceRefresh: () => {
SessionUser.user.id.value = null;
SessionUser.user.customer_number.value = null;
SessionUser.user.display_name.value = null;
SessionUser.user.group_id.value = null;
SessionUser.user.email.value = null;
SessionUser.user.phone.number.value = null;
SessionUser.user.phone.country_code.value = null;
SessionUser.user.notifications.wash_certificate_email.value = null;
SessionUser.user.notifications.email_notifications_enabled.value = null;
SessionUser.user.notifications.sms_notifications_enabled.value = null;
SessionUser.user.created_at.value = null;
SessionUser.user.updated_at.value = null;
SessionUser.user.cached_at.value = null;
SessionUser.permissions.value = [];
SessionUser.authenticated.value = false;
SessionUser.isSubuser.value = false;
// Clear subuser data
SessionUser.subuser.id.value = null;
SessionUser.subuser.username.value = null;
SessionUser.subuser.name.value = null;
SessionUser.subuser.email.value = null;
SessionUser.subuser.phone.country_code.value = null;
SessionUser.subuser.phone.number.value = null;
SessionUser.subuser.grants.value = [];
SessionUser.subuser.created_at.value = null;
SessionUser.subuser.updated_at.value = null;
SessionUser.subuser.suspended_at.value = null;
SessionUser.subuser.cached_at.value = null;
SessionUser.subuser.selectedGrantCustomerNumber.value = null;
localStorage.removeItem('selected_customer_number');
// Clear economic data
SessionUser.economicData.customerNumber.value = null;
SessionUser.economicData.name.value = null;
SessionUser.economicData.address.value = null;
SessionUser.economicData.zip.value = null;
SessionUser.economicData.city.value = null;
SessionUser.economicData.mobilePhone.value = null;
SessionUser.economicData.email.value = null;
SessionUser.economicData.cvr.value = null;
SessionUser.economicData.currency.value = null;
SessionUser.economicData.country.value = null;
SessionUser.economicData.cached_at.value = null;
SessionUser.runtimeConfig.economic.transactionDraftCustomerNumber.value = null;
getSessionData();
},
auth: {
/** Authenticate the user */
authenticate: authenticateUser,
/** Authenticate a subuser with password */
authenticateSubuser: authenticateSubuser,
/** Logout the user */
logout: destroySession,
/** Force clear the user's session WARNING: This will not destroy the user's token */
forceClearSession: forceClearSession,
/** reCAPTCHA */
reCAPTCHA: reCAPTCHA,
},
functions: {
/** Contact functions */
contact: {
onClickCallPhoneNumber: () => {
// Call the phone number
window.location.href = "tel:+4543717886";
}
},
/** Device type checks */
device: {
dimensions: {
// Using breakpoints in accordance with Bulma CSS framework
// https://bulma.io/documentation/start/responsiveness/#breakpoints
mobileMaxWidth: 768,
tabletMinWidth: 769,
tabletMaxWidth: 1023,
desktopMinWidth: 1024,
desktopMaxWidth: 1215,
widescreenMinWidth: 1216,
widescreenMaxWidth: 1407,
fullhdMinWidth: 1408,
// Custom additions
fullhdMaxWidth: 1919,
ultrawideMinWidth: 1920,
ultrawideMaxWidth: 3840,
},
isMobile: () => {
return windowInnerWidth.value < SessionUser.functions.device.dimensions.tabletMinWidth;
//return /Mobi|Android/i.test(navigator.userAgent);
},
isDesktop: () => {
return windowInnerWidth.value >= SessionUser.functions.device.dimensions.desktopMinWidth;
//return !/Mobi|Android/i.test(navigator.userAgent);
},
isTablet: () => {
return windowInnerWidth.value >= SessionUser.functions.device.dimensions.tabletMinWidth && windowInnerWidth.value <= SessionUser.functions.device.dimensions.tabletMaxWidth;
//return /Tablet|iPad/i.test(navigator.userAgent);
},
isWidescreen: () => {
return windowInnerWidth.value >= SessionUser.functions.device.dimensions.widescreenMinWidth && windowInnerWidth.value <= SessionUser.functions.device.dimensions.widescreenMaxWidth;
},
isFullHD: () => {
return windowInnerWidth.value >= SessionUser.functions.device.dimensions.fullhdMinWidth;
},
isUltraWideScreen: () => {
return windowInnerWidth.value >= SessionUser.functions.device.dimensions.ultrawideMinWidth;
},
},
/** Check if the user has a token */
hasToken: () => {
return SessionUser.token.value !== null || hydrateSessionFromStorage();
},
ucFirst: (str) => {
return str.charAt(0).toUpperCase() + str.slice(1);
},
/** Select department */
selectDepartment: (departmentId) => {
// Check if the department id is valid
if (isNaN(departmentId) || departmentId <= 0) {
console.error('Invalid department id: ' + departmentId);
return;
}
if (departmentId === 'default') {
// Redirect to the default department page
SessionUser.functions.redirectTo.department('');
return;
}
// Check if the user has access to the department
if (!SessionUser.canAccessDepartment(departmentId)) {
console.error('User does not have access to department: ' + departmentId);
return;
}
// Check if the current path is within the /admin/:departmentId route
if (!window.location.pathname.startsWith('/admin/')) {
// Redirect to the department page
SessionUser.functions.redirectTo.department(departmentId);
return;
}
// Get the router instance
// Change the department in the url (while keeping the current path)
const currentPath = window.location.pathname;
// If
// If the current path is not an admin path, redirect to the department page
const newPath = currentPath.replace(/\/admin\/\d+/, '').replace(/^\/admin\/?/, '').replace(/^\//, '');
// Redirect to the department page
SessionUser.functions.redirectTo.department(departmentId, newPath);
},
/** Show confirm logout dialog */
showConfirmLogoutDialog: () => {
Swal.fire(
{
title: 'Er du sikker?',
text: "Du vil blive logget ud af systemet",
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ja, log mig ud',
cancelButtonText: 'Annuller'
}
).then((result) => {
if (result.isConfirmed) {
SessionUser.auth.logout().then(() => {
SessionUser.auth.forceClearSession();
SessionUser.functions.redirectTo.pages.login();
});
}
});
},
/** Get the department id from the url */
getDepartmentIdFromUrl: () => {
// Avoid router import cycles by parsing the current pathname directly.
const path = window.location.pathname || '';
const match = path.match(/^\/admin\/(\d+)(?:\/|$)/);
if (!match) {
return undefined;
}
const departmentId = Number.parseInt(match[1], 10);
return Number.isInteger(departmentId) && departmentId > 0 ? departmentId : undefined;
},
/** Get the department ids the user has access to */
getAccessibleDepartments: () => {
return SessionUser.permissions.value.filter(permission => permission.includes('department_access_')).map(permission => parseInt(permission.split('_').pop()));
},
currency: {
toLocal: (value) => {
return new Intl.NumberFormat('da-DK', {style: 'currency', currency: 'DKK'}).format(value);
},
},
text: {
truncate: (text, length = 25) => {
return text.length > length ? text.substring(0, length) + '...' : text;
}
},
date: {
// Format to 2023-01-01 12:00:00
format: (date) => {
return new Date(date).toISOString().slice(0, 19).replace('T', ' ');
},
toWords: (date) => {
const options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
return new Date(date).toLocaleDateString('da-DK', options);
},
toLocal: (date) => {
return new Date(date).toLocaleString('da-DK');
},
toWordsWithTime: (date) => {
const options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit' };
return new Date(date).toLocaleDateString('da-DK', options);
},
isToday(date) {
const created_at_val = new Date(date);
const today = new Date();
return created_at_val.toDateString() === today.toDateString();
},
toLocalTimeHours: (date) => {
return new Date(date).toLocaleTimeString('da-DK', {hour: '2-digit', minute: '2-digit'});
},
getDateWeekdayNumber: (date) => {
const created_at_val = new Date(date);
const weekday = created_at_val.getDay();
return weekday === 0 ? 7 : weekday;
},
timeAgo: (date) => {
// Returns e.g. "5 minutter siden"
const now = new Date();
const past = new Date(date);
const secondsAgo = Math.floor((now - past) / 1000);
const intervals = {
year: 31536000,
month: 2592000,
week: 604800,
day: 86400,
hour: 3600,
minute: 60,
second: 1
};
for (const [unit, secondsInUnit] of Object.entries(intervals)) {
const interval = Math.floor(secondsAgo / secondsInUnit);
if (interval >= 1) {
// If the unit is singular or plural in danish
if (interval === 1) {
const unitSingularMap = {
year: 'år',
month: 'måned',
week: 'uge',
day: 'dag',
hour: 'time',
minute: 'minut',
second: 'sekund'
};
return interval + ' ' + unitSingularMap[unit] + ' siden';
} else {
const unitPluralMap = {
year: 'år',
month: 'måneder',
week: 'uger',
day: 'dage',
hour: 'timer',
minute: 'minutter',
second: 'sekunder'
};
return interval + ' ' + unitPluralMap[unit] + ' siden';
}
}
}
return 'lige nu';
},
weekdayNumbers: {
monday: 1,
tuesday: 2,
wednesday: 3,
thursday: 4,
friday: 5,
saturday: 6,
sunday: 7,
},
getDateMonthStart() {
return new Date(new Date().getFullYear(), new Date().getMonth(), 1);
},
getDateMonthEnd() {
return new Date(new Date().getFullYear(), new Date().getMonth() + 1, 0);
},
getDateMonthName(date) {
console.log(date)
return new Date(date).toLocaleString('da-DK', { month: 'long' });
},
getDateYear(date) {
return new Date(date).getFullYear();
},
getDateAtTime(date, hours, minutes, seconds) {
const newDate = new Date(date);
newDate.setHours(hours);
newDate.setMinutes(minutes);
newDate.setSeconds(seconds);
return newDate;
},
getWeekdayNameToLocal(date) {
return new Date(date).toLocaleString('da-DK', { weekday: 'long' });
}
},
/** Redirect to */
redirectTo: {
/**
* Pages to redirect to
*/
pages: {
login: () => {
window.location.href = '/login';
},
redirect: () => {
window.location.href = '/redirect';
},
},
/**
* Redirect to a department
* @note This will redirect the user to '/admin/:department(/:path)'
* @param department The department id
* @param path The path to redirect to (Optional)
* @param new_tab Open the url in a new tab
*/
department: (department, path = null, new_tab = false) => {
if (!!new_tab) {
SessionUser.functions.redirectTo.external('/admin/' + department + (path !== null ? '/' + path : ''), true);
} else {
window.location.href = '/admin/' + department + (path !== null ? '/' + path : '');
}
},
/**
* Redirect to a superuser page
* @note This will redirect the user to '/superuser(/:path)'
* @param path The path to redirect to (Optional)
* @param new_tab Open the url in a new tab
*/
superUser: (path = null, new_tab = false) => {
if (new_tab) {
window.open('/superuser' + (path !== null ? path : ''), '_blank');
return;
}
window.location.href = '/superuser' + (path !== null ? path : '');
},
/**
* Redirect to a user page
* @note This will redirect the user to '/user(/:path)'
* @param path The path to redirect to (Optional)
* @param new_tab Open the url in a new tab
*/
user: (path = null, new_tab = false) => {
if (new_tab) {
window.open('/user' + (path !== null ? path : ''), '_blank');
return;
}
window.location.href = '/user' + (path !== null ? path : '');
},
/**
* External redirect
* @param url The url to redirect to
* @param newTab Open the url in a new tab
*/
external: (url, newTab = false) => {
if (newTab) {
window.open(url, '_blank');
} else {
window.location.href = url;
}
},
},
parseErrorMessage(error) {
console.log('Parsing error message: ', error);
// Check if the error is a string
if (typeof error === 'string') {
return error;
}
// check if the response.data.data.message exists
if (error.response && error.response.data && error.response.data.data && error.response.data.data.message) {
return error.response.data.data.message;
}
// check if the response.data.message exists
if (error.response && error.response.data && error.response.data.message) {
return error.response.data.message;
}
// check if the response.data exists
if (error.response && error.response.data) {
return error.response.data;
}
// check if the response exists
if (error.response) {
return error.response;
}
},
getBusinessDaysBetweenDates(start, end, weekdays = ['MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY']) {
let count = 0;
const curDate = new Date(start);
while (curDate <= end) {
const dayOfWeek = curDate.toLocaleString('en-US', { weekday: 'long' }).toUpperCase();
if (weekdays.includes(dayOfWeek)) {
count++;
}
curDate.setDate(curDate.getDate() + 1);
}
return count;
}
},
request: authenticatedRequest,
editField: EditFieldForm,
getSessionData: getSessionData,
getSubuserSessionData: getSubuserSessionData,
initiateOnAppStart: initiateOnAppStart,
checkForUpdates() {
// Check if this is the latest version of the PWA
}
};
const windowInnerWidth = ref(window.innerWidth);
const updateWindowInnerWidth = () => {
windowInnerWidth.value = window.innerWidth;
};
window.addEventListener('resize', updateWindowInnerWidth);
export default SessionUser;
</script>