diff --git a/src/views/dashboards/superUserDashboard/system/CronOperations.vue b/src/views/dashboards/superUserDashboard/system/CronOperations.vue
index 54e8ddbf..655e6147 100644
--- a/src/views/dashboards/superUserDashboard/system/CronOperations.vue
+++ b/src/views/dashboards/superUserDashboard/system/CronOperations.vue
@@ -155,9 +155,9 @@ async function deployWorkers() {
try {
await deployCronWorkers({});
await loadWorkers();
- queuedMessage.value = t(
- updatingDeployment ? "cron.messages.worker_update_queued" : "cron.messages.worker_deploy_queued"
- );
+ queuedMessage.value = updatingDeployment
+ ? t("cron.messages.worker_update_queued")
+ : t("cron.messages.worker_deploy_queued");
} catch (error) {
errorMessage.value = parseError(error);
} finally {
diff --git a/src/views/dashboards/superUserDashboard/system/SystemSecurity.vue b/src/views/dashboards/superUserDashboard/system/SystemSecurity.vue
index b279eeac..5d1fcbb0 100644
--- a/src/views/dashboards/superUserDashboard/system/SystemSecurity.vue
+++ b/src/views/dashboards/superUserDashboard/system/SystemSecurity.vue
@@ -62,6 +62,20 @@ const selectedIncident = ref(null);
const incidentNote = ref("");
const incidentFilters = ref({ status: "open", search: "" });
const firewallDraft = ref(defaultFirewallDraft());
+const securityRuleLabels = {
+ bookings_created: () => t("security.rules.bookings_created"),
+ failed_login_attempts: () => t("security.rules.failed_login_attempts"),
+ requests_per_customer: () => t("security.rules.requests_per_customer"),
+ requests_per_ip: () => t("security.rules.requests_per_ip"),
+ vehicles_created: () => t("security.rules.vehicles_created"),
+};
+const securityRuleDescriptions = {
+ bookings_created: () => t("security.rule_descriptions.bookings_created"),
+ failed_login_attempts: () => t("security.rule_descriptions.failed_login_attempts"),
+ requests_per_customer: () => t("security.rule_descriptions.requests_per_customer"),
+ requests_per_ip: () => t("security.rule_descriptions.requests_per_ip"),
+ vehicles_created: () => t("security.rule_descriptions.vehicles_created"),
+};
const responseData = (response, fallback) => response?.data?.data ?? fallback;
const parseError = (error) =>
@@ -108,9 +122,7 @@ function normalizeRuleDraft(rule) {
enabled: Boolean(rule.enabled),
threshold_count: Number(rule.threshold_count || 1),
window_seconds: Number(rule.window_seconds || 60),
- exemptionsText: Array.isArray(rule.exempt_permission_nodes)
- ? rule.exempt_permission_nodes.join(", ")
- : "",
+ exemptionsText: Array.isArray(rule.exempt_permission_nodes) ? rule.exempt_permission_nodes.join(", ") : "",
};
}
@@ -136,15 +148,11 @@ function formatDate(value) {
}
function ruleLabel(rule) {
- const key = `security.rules.${rule.rule_key}`;
- const value = t(key);
- return value === key ? rule.label || rule.rule_key : value;
+ return securityRuleLabels[rule.rule_key]?.() || rule.label || rule.rule_key;
}
function ruleDescription(rule) {
- const key = `security.rule_descriptions.${rule.rule_key}`;
- const value = t(key);
- return value === key ? rule.description || "" : value;
+ return securityRuleDescriptions[rule.rule_key]?.() || rule.description || "";
}
function statusClass(status) {
@@ -468,8 +476,12 @@ onMounted(() => {
| {{ incident.title }} |
- {{ incident.status }} |
- {{ incident.severity }} |
+
+ {{ incident.status }}
+ |
+
+ {{ incident.severity }}
+ |
{{ formatDate(incident.last_seen_at) }} |
@@ -481,13 +493,7 @@ onMounted(() => {
{{ t("security.firewall.management") }}
-
+
{{ t("security.actions.refresh") }}
@@ -602,7 +608,12 @@ onMounted(() => {
|
-
+
{{ rule.action }}
{{ t("security.firewall.disabled") }}
@@ -709,13 +720,7 @@ onMounted(() => {
{{ t("security.incidents.management") }}
-
+
{{ t("security.actions.refresh") }}
@@ -761,8 +766,12 @@ onMounted(() => {
@click="selectIncident(incident)"
>
{{ incident.title }} |
- {{ incident.status }} |
- {{ incident.severity }} |
+
+ {{ incident.status }}
+ |
+
+ {{ incident.severity }}
+ |
{{ incident.occurrence_count }} |
{{ formatDate(incident.last_seen_at) }} |
|
@@ -799,7 +808,9 @@ onMounted(() => {
{{ t("security.incidents.route") }}
- {{ selectedIncident.route_path || selectedIncident.route_template || t("security.empty_value") }}
+
+ {{ selectedIncident.route_path || selectedIncident.route_template || t("security.empty_value") }}
+
diff --git a/tests/e2e/support/bookingFlow.ts b/tests/e2e/support/bookingFlow.ts
index e1e70a0c..5d43b742 100644
--- a/tests/e2e/support/bookingFlow.ts
+++ b/tests/e2e/support/bookingFlow.ts
@@ -90,6 +90,35 @@ async function clickDesktopNext(page: Page) {
throw new Error("Could not find a visible desktop next-step button in the booking flow.");
}
+async function isDesktopProductSelectionVisible(page: Page) {
+ const productSelectionLocators = [
+ page.locator('[data-testid^="pos-product-card-"]:visible').first(),
+ page.getByTestId("pos-product-categories-loading"),
+ page.getByTestId("pos-products-loading"),
+ page.locator('[data-testid^="pos-product-category-tab-"]:visible').first(),
+ page.locator('[data-testid="pos-product-category-select"]:visible').first(),
+ ];
+
+ for (const locator of productSelectionLocators) {
+ if (await locator.isVisible().catch(() => false)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+async function waitForDesktopProductSelection(page: Page) {
+ return expect
+ .poll(async () => isDesktopProductSelectionVisible(page), {
+ intervals: [50, 100, 250],
+ timeout: 1_500,
+ })
+ .toBe(true)
+ .then(() => true)
+ .catch(() => false);
+}
+
async function ensureVehicleInput(page: Page) {
const reg1Input = page.locator('input[id="reg1-input"]:visible').first();
@@ -139,6 +168,15 @@ async function ensureVehicleTypeSelection(page: Page) {
.toBeGreaterThan(0);
}
+async function confirmVehicleRegistrationInput(page: Page, reg1Input: ReturnType) {
+ if (!(await reg1Input.isVisible().catch(() => false))) {
+ return true;
+ }
+
+ await reg1Input.press("Enter");
+ return !(await reg1Input.isVisible().catch(() => false));
+}
+
export async function fillBookingField(page: Page, toggleTestId: string, inputTestId: string, value: string) {
const input = page.locator(`[data-testid="${inputTestId}"]:visible`).first();
const toggle = page.locator(`[data-testid="${toggleTestId}"]:visible`).first();
@@ -188,7 +226,7 @@ export async function goToBookingProductSelectionStepWithOptions(
const reg1Input = await ensureVehicleInput(page);
await reg1Input.fill(registrationNumber);
- await reg1Input.press("Enter");
+ const vehicleInputClosed = await confirmVehicleRegistrationInput(page, reg1Input);
if (!mobileWizard) {
await ensureVehicleTypeSelection(page);
@@ -197,7 +235,7 @@ export async function goToBookingProductSelectionStepWithOptions(
if (mobileWizard) {
await expect(page.getByTestId("booking-mobile-next")).toBeEnabled();
await page.getByTestId("booking-mobile-next").click();
- } else {
+ } else if (!vehicleInputClosed && !(await waitForDesktopProductSelection(page))) {
await clickDesktopNext(page);
}