- Implement `endpoint_mode` (manual/auto) and related configuration in i18n across multiple locales. - Add new utility methods for channel rollback and runtime confirmation workflows. - Update unit and E2E tests to cover runtime refresh behavior, channel switching mechanics, and release references. - Enhance `SessionUser` and related services to improve error handling during release runtime refresh. - Add data-test attributes for frontend components to support improved test coverage.
1184 lines
46 KiB
Vue
1184 lines
46 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";
|
|
import { configureReleaseRuntime } from "@/services/releaseTimeline.js";
|
|
import { fetchReleaseRuntime } from "@/services/releaseBootstrap.js";
|
|
import {
|
|
isReleaseChannelApiAvailabilityError,
|
|
markReleaseChannelApiUnavailable,
|
|
reconcileSelectedReleaseChannel,
|
|
releaseChannelRuntimeRequestParams,
|
|
setReleaseChannelSwitchNoticePrincipal,
|
|
} from "@/services/releaseChannelAvailability.js";
|
|
|
|
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;
|
|
};
|
|
|
|
const applyReleaseRuntimeConfig = (runtime) => {
|
|
const normalizedRuntime = runtime || {};
|
|
configureReleaseRuntime(normalizedRuntime);
|
|
reconcileSelectedReleaseChannel(normalizedRuntime);
|
|
const runtimeUrls = normalizedRuntime.urls && typeof normalizedRuntime.urls === "object" ? normalizedRuntime.urls : {};
|
|
SessionUser.runtimeConfig.release.traceId.value = normalizedRuntime.trace_id || null;
|
|
SessionUser.runtimeConfig.release.channel.value = normalizedRuntime.channel || null;
|
|
SessionUser.runtimeConfig.release.availableChannels.value =
|
|
normalizedRuntime.available_channels || normalizedRuntime.availableChannels || [];
|
|
SessionUser.runtimeConfig.release.versions.value = normalizedRuntime.versions || {
|
|
frontend: null,
|
|
api: null,
|
|
bundle_id: null,
|
|
};
|
|
SessionUser.runtimeConfig.release.frontendBaseUrl.value =
|
|
normalizedRuntime.frontend_base_url || runtimeUrls.frontend_base_url || null;
|
|
SessionUser.runtimeConfig.release.apiBaseUrl.value =
|
|
normalizedRuntime.api_base_url || runtimeUrls.api_base_url || null;
|
|
SessionUser.runtimeConfig.release.availability.value = normalizedRuntime.availability || {
|
|
configured: true,
|
|
missing: [],
|
|
status: "ready",
|
|
explicit: false,
|
|
};
|
|
SessionUser.runtimeConfig.release.capturePolicy.value = normalizedRuntime.capture_policy || {
|
|
enabled: false,
|
|
capture_level: "metadata",
|
|
all_failure_metadata: true,
|
|
retention_days: 14,
|
|
};
|
|
};
|
|
|
|
export const refreshReleaseRuntime = async ({ throwOnError = false } = {}) => {
|
|
try {
|
|
const runtime = await fetchReleaseRuntime();
|
|
applyReleaseRuntimeConfig(runtime || {});
|
|
return runtime || {};
|
|
} catch (error) {
|
|
console.warn("Could not refresh release runtime", error);
|
|
if (throwOnError) {
|
|
throw error;
|
|
}
|
|
return null;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 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(async (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();
|
|
setReleaseChannelSwitchNoticePrincipal(`subuser:${data.id || data.username || data.email || "unknown"}`);
|
|
|
|
// 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)];
|
|
await refreshReleaseRuntime();
|
|
SessionUser.initiated.value = true;
|
|
})
|
|
.catch((error) => {
|
|
if (isReleaseChannelApiAvailabilityError(error)) {
|
|
console.warn("Selected release channel API is unavailable during session bootstrap.", error);
|
|
markReleaseChannelApiUnavailable();
|
|
return;
|
|
}
|
|
|
|
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", releaseChannelRuntimeRequestParams())
|
|
.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;
|
|
setReleaseChannelSwitchNoticePrincipal(
|
|
`user:${response.data.data.id || response.data.data.customer_number || response.data.data.email || "unknown"}`
|
|
);
|
|
// 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
|
|
);
|
|
SessionUser.runtimeConfig.economic.defaultDistributionDepartmentId.value = normalizePositiveInteger(
|
|
response?.data?.data?.runtime_config?.economic?.default_distribution_department_id
|
|
);
|
|
applyReleaseRuntimeConfig(response?.data?.data?.runtime_config?.release || {});
|
|
// 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) => {
|
|
if (isReleaseChannelApiAvailabilityError(error)) {
|
|
console.warn("Selected release channel API is unavailable during session bootstrap.", error);
|
|
markReleaseChannelApiUnavailable();
|
|
return;
|
|
}
|
|
|
|
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),
|
|
defaultDistributionDepartmentId: ref(null),
|
|
},
|
|
release: {
|
|
traceId: ref(null),
|
|
channel: ref(null),
|
|
availableChannels: ref([]),
|
|
versions: ref({ frontend: null, api: null }),
|
|
frontendBaseUrl: ref(null),
|
|
apiBaseUrl: ref(null),
|
|
availability: ref({
|
|
configured: true,
|
|
missing: [],
|
|
status: "ready",
|
|
}),
|
|
capturePolicy: ref({
|
|
enabled: false,
|
|
capture_level: "metadata",
|
|
all_failure_metadata: true,
|
|
retention_days: 14,
|
|
}),
|
|
},
|
|
},
|
|
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();
|
|
},
|
|
canAccessAssignedDepartment: (id = 0) => {
|
|
const departmentId = parseInt(id, 10);
|
|
if (!Number.isInteger(departmentId)) {
|
|
return false;
|
|
}
|
|
|
|
const permission = "department_access_" + departmentId;
|
|
if (SessionUser.isSubuser.value) {
|
|
const selectedGrant = SessionUser.subuser.getSelectedGrant();
|
|
if (selectedGrant && selectedGrant.permissions) {
|
|
return selectedGrant.permissions.includes(permission);
|
|
}
|
|
}
|
|
|
|
return SessionUser.permissions.value.includes(permission);
|
|
},
|
|
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;
|
|
SessionUser.runtimeConfig.economic.defaultDistributionDepartmentId.value = null;
|
|
SessionUser.runtimeConfig.release.traceId.value = null;
|
|
SessionUser.runtimeConfig.release.channel.value = null;
|
|
SessionUser.runtimeConfig.release.availableChannels.value = [];
|
|
SessionUser.runtimeConfig.release.versions.value = { frontend: null, api: null, bundle_id: null };
|
|
SessionUser.runtimeConfig.release.frontendBaseUrl.value = null;
|
|
SessionUser.runtimeConfig.release.apiBaseUrl.value = null;
|
|
SessionUser.runtimeConfig.release.availability.value = {
|
|
configured: true,
|
|
missing: [],
|
|
status: "ready",
|
|
explicit: false,
|
|
};
|
|
SessionUser.runtimeConfig.release.capturePolicy.value = {
|
|
enabled: false,
|
|
capture_level: "metadata",
|
|
all_failure_metadata: true,
|
|
retention_days: 14,
|
|
};
|
|
setReleaseChannelSwitchNoticePrincipal("");
|
|
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(/^\//, "");
|
|
const shouldPreserveCurrentQuery = newPath === "modules/pos" || newPath.startsWith("modules/pos/");
|
|
const queryString = shouldPreserveCurrentQuery ? window.location.search : "";
|
|
// Redirect to the department page
|
|
SessionUser.functions.redirectTo.department(departmentId, newPath + queryString);
|
|
},
|
|
/** 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,
|
|
refreshReleaseRuntime: refreshReleaseRuntime,
|
|
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>
|