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); }