Fix mobile Stripe reader recovery action (#241)

## Summary
- keep the mobile Stripe terminal menu in normal document flow so it
cannot cover the reader recovery action
- preserve the existing desktop dropdown overlay behavior
- replace pre-existing dynamic subuser-access translations with direct
literal calls
- explicitly review Bird's bounded health-state translation signatures
in the full view-i18n gate

## Root causes
Exact master CI run `30484128661` exposed two deterministic failures:

1. All three attempts of `reader unavailable state is recoverable and
non-fatal` timed out because the open terminal dropdown intercepted
pointer events on `pos-stripe-no-readers`.
2. The full Chromium desktop/customer view-i18n gate rejected two
bounded Bird health-state signatures plus three pre-existing dynamic
subuser-access calls. The PR matrix did not run this full scanner.

## Verification
- exact Chromium mobile failing scenario: 1/1 passed
- Chromium mobile repeat stress: 5/5 passed
- exact full view-i18n scanner: 1/1 passed
- Bird configuration units: 5/5 passed
- Firefox mobile scenario: skipped by the existing project/browser gate
- ESLint: passed
- test Prettier check: passed
- `git diff --check`: passed
- Vite production build: passed
This commit is contained in:
Jeppe B
2026-08-02 09:42:47 +02:00
committed by GitHub
parent 14a0d65a01
commit 917c10c1d3
3 changed files with 17 additions and 11 deletions
@@ -506,4 +506,12 @@ onBeforeUnmount(() => {
.stripe-terminal-select--mobile .stripe-terminal-select__trigger {
min-height: 3rem;
}
.stripe-terminal-select--mobile {
flex-direction: column;
}
.stripe-terminal-select--mobile .stripe-terminal-select__menu {
position: static;
}
</style>
@@ -7,7 +7,7 @@ import logo from "@/assets/img/truckwash-banner.png";
import i18n from "@/i18n";
const route = useRoute();
const t = (key, values = undefined) => i18n.global.t(key, values);
const { t } = i18n.global;
const token = ref(String(route.query.token || ""));
const request = ref(null);
const loading = ref(true);
@@ -68,11 +68,9 @@ onMounted(() => {
<b-message v-else-if="result" :type="result === 'approved' ? 'is-success' : 'is-warning'" has-icon>
{{
t(
result === "approved"
? "subuser_access_decision.approved"
: "subuser_access_decision.denied"
)
? t("subuser_access_decision.approved")
: t("subuser_access_decision.denied")
}}
</b-message>
@@ -99,11 +97,9 @@ onMounted(() => {
@click="confirmDecision"
>
{{
t(
request.action === "approve"
? "subuser_access_decision.approve"
: "subuser_access_decision.deny"
)
? t("subuser_access_decision.approve")
: t("subuser_access_decision.deny")
}}
</b-button>
</template>
+2
View File
@@ -6,6 +6,8 @@ import { scanViewTranslationKeys, type ViewTranslationKeyUsage } from "./support
const ACTIVE_LOCALES = ["da", "en", "sv", "de", "no"] as const;
const GENERATED_LOCALES_DIRECTORY = path.join(process.cwd(), "src", "i18n", "generated");
const REVIEWED_NON_LITERAL_CALLS = new Set([
'src/views/dashboards/superUserDashboard/configuration/ConfigurationBird.vue|$t|:subtitle="$t(`configuration.bird.state.${healthState}`)"',
"src/views/dashboards/superUserDashboard/configuration/ConfigurationBird.vue|$t|{{ $t(`configuration.bird.state.${healthState}`) }}",
"src/views/backoffice/LimitedBackofficeEmployees.vue|t|return `${option.flag} +${option.value} ${t(`templates.limited_backoffice.employees.country_codes.${option.labelKey}`)}`;",
"src/views/dashboards/superUserDashboard/InvoicingBillingPeriod/components/InvoicingPeriodFlagList.vue|t|const translated = t(key, params);",
"src/views/dashboards/superUserDashboard/InvoicingBillingPeriod/components/InvoicingPeriodObjectTree.vue|t|const translated = t(key, params);",