- Introduced `PosDesktopOrderBookingSelectorModal.vue` to streamline booking selection for desktop POS. - Added `orderBookingDisplay.js` utility for handling booking display and reference value normalization. - Created unit test `select-vehicle-form-pos.spec.js` to validate booking selection flow and registration matching. - Implemented `run-playwright-ci-parallel.mjs` script to optimize Playwright CI with parallel testing. - Updated styles and responsiveness for enhanced booking modal UX.
127 lines
3.2 KiB
JavaScript
127 lines
3.2 KiB
JavaScript
export const OUTDATED_INSTALLATION_ROUTE_NAME = 'outdated-installation';
|
|
export const OUTDATED_GATEWAY_ROUTE_NAME = 'outdated-gateway';
|
|
|
|
const EXEMPT_ROUTE_NAMES = new Set([
|
|
OUTDATED_INSTALLATION_ROUTE_NAME,
|
|
OUTDATED_GATEWAY_ROUTE_NAME,
|
|
]);
|
|
const LOOPBACK_HOSTNAMES = new Set(['localhost', '127.0.0.1', '[::1]']);
|
|
|
|
const isLoopbackOrigin = (currentOrigin) => {
|
|
if (!currentOrigin) return false;
|
|
|
|
try {
|
|
const url = new URL(currentOrigin);
|
|
return LOOPBACK_HOSTNAMES.has(url.hostname);
|
|
} catch (_error) {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
const normalizeAllowedOrigins = (allowedOrigins = []) => {
|
|
return new Set(
|
|
allowedOrigins
|
|
.map((origin) => {
|
|
try {
|
|
return new URL(origin).origin;
|
|
} catch (_error) {
|
|
return origin;
|
|
}
|
|
})
|
|
.filter(Boolean),
|
|
);
|
|
};
|
|
|
|
export const isOriginAllowed = (currentOrigin, allowedOrigins = []) => {
|
|
if (!currentOrigin) return true;
|
|
if (isLoopbackOrigin(currentOrigin)) return true;
|
|
const allowedOriginSet = normalizeAllowedOrigins(allowedOrigins);
|
|
return allowedOriginSet.has(currentOrigin);
|
|
};
|
|
|
|
export const isStandaloneContext = (win) => {
|
|
const resolvedWindow = win || (typeof window !== 'undefined' ? window : undefined);
|
|
if (!resolvedWindow) return false;
|
|
const iosStandalone = resolvedWindow.navigator?.standalone === true;
|
|
const mediaStandalone = typeof resolvedWindow.matchMedia === 'function'
|
|
&& resolvedWindow.matchMedia('(display-mode: standalone)').matches;
|
|
return iosStandalone || mediaStandalone;
|
|
};
|
|
|
|
export const shouldBypassOriginGate = (route) => {
|
|
return EXEMPT_ROUTE_NAMES.has(route?.name);
|
|
};
|
|
|
|
export const resolveOriginMigrationRoute = ({
|
|
to,
|
|
currentOrigin,
|
|
allowedOrigins,
|
|
standalone,
|
|
}) => {
|
|
if (shouldBypassOriginGate(to)) {
|
|
return null;
|
|
}
|
|
|
|
if (isOriginAllowed(currentOrigin, allowedOrigins)) {
|
|
return null;
|
|
}
|
|
|
|
if (standalone) {
|
|
return { name: OUTDATED_INSTALLATION_ROUTE_NAME };
|
|
}
|
|
|
|
return {
|
|
name: OUTDATED_GATEWAY_ROUTE_NAME,
|
|
query: {
|
|
redirect: to?.fullPath || '/',
|
|
},
|
|
};
|
|
};
|
|
|
|
export const sanitizeRedirectPath = (redirectPath) => {
|
|
if (typeof redirectPath !== 'string') return '/';
|
|
if (!redirectPath.startsWith('/')) return '/';
|
|
return redirectPath;
|
|
};
|
|
|
|
export const buildMigrationUrl = (migrationOrigin, redirectPath) => {
|
|
const safePath = sanitizeRedirectPath(redirectPath);
|
|
try {
|
|
return new URL(safePath, migrationOrigin).toString();
|
|
} catch (_error) {
|
|
return migrationOrigin;
|
|
}
|
|
};
|
|
|
|
export const startRedirectCountdown = ({
|
|
seconds = 5,
|
|
onTick,
|
|
onRedirect,
|
|
setIntervalFn = setInterval,
|
|
clearIntervalFn = clearInterval,
|
|
setTimeoutFn = setTimeout,
|
|
clearTimeoutFn = clearTimeout,
|
|
}) => {
|
|
let remainingSeconds = seconds;
|
|
const intervalId = setIntervalFn(() => {
|
|
remainingSeconds = Math.max(remainingSeconds - 1, 0);
|
|
if (typeof onTick === 'function') {
|
|
onTick(remainingSeconds);
|
|
}
|
|
if (remainingSeconds === 0) {
|
|
clearIntervalFn(intervalId);
|
|
}
|
|
}, 1000);
|
|
|
|
const timeoutId = setTimeoutFn(() => {
|
|
if (typeof onRedirect === 'function') {
|
|
onRedirect();
|
|
}
|
|
}, seconds * 1000);
|
|
|
|
return () => {
|
|
clearIntervalFn(intervalId);
|
|
clearTimeoutFn(timeoutId);
|
|
};
|
|
};
|