Compare commits

..
Author SHA1 Message Date
Jeppe Bundgaard 2f671024ab Add mocks for department daily report components and update Vitest configuration for SCSS 2026-06-02 17:41:59 +02:00
Jeppe Bundgaard 605a7ca433 Refactor department weather component to support multi-day hourly mode and update related tests 2026-06-02 17:26:17 +02:00
Jeppe Bundgaard 5170139a32 Switch CI to self-hosted runners
Updated all GitHub Actions workflows to use self-hosted runners instead of `ubuntu-latest`. This change ensures better control over the CI environment and aligns with internal infrastructure requirements.
2026-06-02 10:36:18 +02:00
Jeppe Bundgaard 90952a68b7 Refactor SubusersTable to use ActionSettingsWheelButton for row actions 2026-06-02 09:23:49 +02:00
Jeppe Bundgaard 1cc14d7b41 Merge remote-tracking branch 'origin/master' 2026-06-02 08:36:42 +02:00
Jeppe B aa4d6576f7 Merge pull request #108 from copenhagentruckwash/fix-page-crash-issues
Fix wash start recovery from API failures
2026-06-02 08:35:28 +02:00
Jeppe B 2a46984f9c Merge pull request #109 from copenhagentruckwash/fix-playwright-exit-code-error
Stabilize release sidebar E2E coverage
2026-06-02 08:34:47 +02:00
Jeppe Bundgaard 4a9c721c55 Remove debug message for beta frontend in build output 2026-06-02 08:12:50 +02:00
Jeppe B 7be9995c87 Stabilize release sidebar e2e coverage 2026-06-02 08:10:51 +02:00
Jeppe B 85529de3f8 Fix wash start recovery from API failures 2026-06-02 08:07:03 +02:00
Jeppe B 114ca9d956 Merge pull request #107 from copenhagentruckwash/investigate-and-fix-failing-tests
Resolve failing frontend CI: fix merge conflicts, restore edge cache & tests
2026-06-02 07:15:41 +02:00
Jeppe B 29f858685a Fix self-serve wash smoke restore fixture 2026-06-02 07:15:26 +02:00
Jeppe B 4112faf682 Fix edge gateway broker config e2e 2026-06-02 02:38:37 +02:00
Jeppe B a13b73c414 Harden edge gateway service parsing 2026-06-02 02:06:32 +02:00
Jeppe B 667e93af8f Resolve failing frontend checks 2026-06-02 01:13:28 +02:00
Jeppe B 54a888026e Merge pull request #104 from copenhagentruckwash/propose-fix-for-price-tampering-vulnerability
Fix trusted booking prices in POS hydration
2026-06-02 00:48:56 +02:00
Jeppe B a0fb02baa1 Merge pull request #106 from copenhagentruckwash/fix-booking-delete-access-permission
Fix booking delete action visibility
2026-06-02 00:47:38 +02:00
Jeppe B c445b932db Fix booking delete action visibility 2026-06-02 00:47:19 +02:00
copilot-swe-agent[bot] ffccb1683b Merge origin/master and resolve POS pricing conflict 2026-06-01 22:44:56 +00:00
Jeppe B 0d590fc9d6 Merge pull request #105 from copenhagentruckwash/fix-postinstall-script-vulnerability
Remove unsafe postinstall hook
2026-06-02 00:40:46 +02:00
Jeppe B 1d4910f947 Remove unsafe postinstall hook 2026-06-02 00:40:34 +02:00
Jeppe B 7e457ed961 Fix trusted booking prices in POS hydration 2026-06-02 00:39:55 +02:00
Jeppe B 34722c6985 Merge pull request #103 from copenhagentruckwash/fix-vulnerability-in-pos-order-pricing
Fix POS booking price hydration
2026-06-02 00:39:37 +02:00
Jeppe B c5a13b50ab Fix POS booking price hydration 2026-06-02 00:39:15 +02:00
Jeppe B 387bd50281 Merge pull request #102 from copenhagentruckwash/fix-unsandboxed-blob-attachment-previews
Sanitize and sandbox order attachment previews to prevent XSS
2026-06-02 00:38:15 +02:00
Jeppe B 81016fd898 Fix unsafe order attachment previews 2026-06-02 00:38:03 +02:00
Jeppe B 39fc1612ac Merge pull request #101 from copenhagentruckwash/fix-duplicate-stripe-invoice-submission-issue
Prevent duplicate Stripe invoice queue submissions
2026-06-02 00:30:18 +02:00
Jeppe B 7ab12a3fff Prevent duplicate Stripe invoice queue submissions 2026-06-02 00:29:53 +02:00
Jeppe B b4d869c469 Merge pull request #100 from copenhagentruckwash/fix-autosave-to-prevent-infinite-retry-loop
Fix autosave failed-save retry loop
2026-06-02 00:28:52 +02:00
Jeppe B 799b667828 Fix autosave failed-save retry loop 2026-06-02 00:28:39 +02:00
Jeppe B ae3513edf3 Merge pull request #99 from copenhagentruckwash/fix-repeated-booking-prefetch-flooding-api
Avoid duplicate booking prefetch by honoring exact-plate cache
2026-06-02 00:27:37 +02:00
Jeppe B 6db1861b78 Fix repeated booking prefetch requests 2026-06-02 00:27:25 +02:00
Jeppe B a8e839ba18 Merge pull request #98 from copenhagentruckwash/fix-mobile-pos-orders-completion-issue
Fix mobile POS order completion
2026-06-02 00:27:07 +02:00
Jeppe B b224cf67d1 Fix mobile POS order completion 2026-06-02 00:26:53 +02:00
Jeppe B 222237ab34 Merge pull request #96 from copenhagentruckwash/fix-edge-gateway-localstorage-vulnerability
Harden edge gateway workspace cache
2026-06-02 00:16:07 +02:00
copilot-swe-agent[bot] 433cfe521e Planning merge conflict resolution 2026-06-01 22:14:58 +00:00
Jeppe B 1e4a776fa5 Merge pull request #97 from copenhagentruckwash/fix-auto-selection-of-customer-suggestions
Fix stale POS customer suggestion auto-selection
2026-06-02 00:13:59 +02:00
Jeppe B e88a424a7d Fix stale POS customer suggestion auto-selection 2026-06-02 00:13:46 +02:00
Jeppe B 9450c7902d Harden edge gateway workspace cache 2026-06-02 00:08:20 +02:00
Jeppe B 75b5883afe Merge pull request #95 from copenhagentruckwash/fix-stripe-email-invoice-payment-misclassification
Fix Stripe email invoice paid validation
2026-06-02 00:07:49 +02:00
Jeppe B c26f0184d9 Fix Stripe email invoice paid validation 2026-06-02 00:07:35 +02:00
Jeppe B cb74b4bbe7 Merge pull request #93 from copenhagentruckwash/propose-fix-for-cached-gateway-details-leak
Scope edge gateway cache to session and prevent auth-blind cached fallbacks
2026-06-01 23:48:26 +02:00
Jeppe B 326eca656f Fix edge gateway cache authorization fallback 2026-06-01 23:48:17 +02:00
Jeppe B f0a5058b68 Merge pull request #92 from copenhagentruckwash/propose-fix-for-self-hosted-ci-vulnerability
Use GitHub-hosted runners for tests workflow
2026-06-01 23:46:15 +02:00
copilot-swe-agent[bot] d39cd36995 Merge remote-tracking branch 'origin/master' into propose-fix-for-self-hosted-ci-vulnerability
# Conflicts:
#	.github/workflows/tests.yml
2026-06-01 21:43:04 +00:00
Jeppe B 0548018b5c Use GitHub-hosted runners for tests workflow 2026-06-01 23:40:30 +02:00
Jeppe B 699e10856e Merge pull request #91 from copenhagentruckwash/propose-fix-for-self-hosted-runner-vulnerability
Fix CI runner exposure for pull requests
2026-06-01 23:40:09 +02:00
Jeppe B 36b399cb2f Fix CI runner exposure for pull requests 2026-06-01 23:40:00 +02:00
Jeppe B f613d58727 Merge pull request #90 from copenhagentruckwash/propose-fix-for-product-catalog-vulnerability
Restrict vehicle type product edits to superusers
2026-06-01 23:35:14 +02:00
Jeppe B feccfada77 Restrict vehicle type product edits to superusers 2026-06-01 23:35:04 +02:00
Jeppe B 4127e66114 Merge pull request #89 from copenhagentruckwash/fix-unvalidated-attachment-download-links
Validate self-serve attachment download links
2026-06-01 23:34:45 +02:00
Jeppe B b2a9b59399 Validate self-serve attachment download links 2026-06-01 23:34:34 +02:00
Jeppe B 6ffed9c2f2 Merge pull request #88 from copenhagentruckwash/fix-lane-toggle-guard-permission-issue
Fix lane toggle permission guards
2026-06-01 23:34:14 +02:00
Jeppe B 53e193d8d1 Fix lane toggle permission guards 2026-06-01 23:34:03 +02:00
Jeppe B 7835691bc9 Merge pull request #87 from copenhagentruckwash/fix-websocket-token-exposure-vulnerability
Secure edge gateway WebSocket session tokens
2026-06-01 23:33:44 +02:00
Jeppe B 91715541ec Secure edge gateway websocket sessions 2026-06-01 23:33:34 +02:00
Jeppe B e3f9e99129 Merge pull request #86 from copenhagentruckwash/fix-stored-xss-in-relay-options
Fix select option HTML escaping
2026-06-01 23:33:20 +02:00
Jeppe B 4634cbad89 Fix select option HTML escaping 2026-06-01 23:33:10 +02:00
Jeppe B 5db2a36da0 Merge pull request #85 from copenhagentruckwash/propose-fix-for-relay-failure-issue
Rollback wash start when relay activation fails
2026-06-01 22:53:44 +02:00
Jeppe B cd7b273da4 Rollback wash start when relay activation fails 2026-06-01 22:53:34 +02:00
Jeppe B cbbad1a6e2 Merge pull request #84 from copenhagentruckwash/fix-pos-url-order-reassignment-vulnerability
Prevent implicit POS order department changes
2026-06-01 22:44:24 +02:00
Jeppe B 9661d4efa7 Prevent implicit POS order department changes 2026-06-01 22:44:14 +02:00
Jeppe B 31762c23c5 Merge pull request #83 from copenhagentruckwash/fix-unescaped-order-delete-reasons
Fix protected order delete reason escaping
2026-06-01 22:43:55 +02:00
Jeppe B c71a6b4d14 Fix protected order delete reason escaping 2026-06-01 22:43:45 +02:00
Jeppe B e8ddb33f21 Merge pull request #82 from copenhagentruckwash/fix-vulnerability-in-edge-broker-config
Secure edge gateway broker configuration
2026-06-01 22:28:28 +02:00
Jeppe B ea368d19bb Secure edge gateway broker configuration 2026-06-01 22:28:18 +02:00
Jeppe B 7ab50b6290 Merge pull request #81 from copenhagentruckwash/fix-unredacted-broker-diagnostics-exposure
Redact edge gateway diagnostic secrets
2026-06-01 22:27:33 +02:00
Jeppe B fd7cfbd3c3 Redact edge gateway diagnostics 2026-06-01 22:27:19 +02:00
Jeppe B 90725af10a Merge pull request #80 from copenhagentruckwash/fix-ungated-destructive-actions-in-ui
Gate self-serve studio gateway actions and require confirmation for destructive operations
2026-06-01 22:26:58 +02:00
Jeppe B d63a62a921 Gate self-serve gateway actions 2026-06-01 22:26:44 +02:00
Jeppe B 4bc5481941 Merge pull request #79 from copenhagentruckwash/propose-fix-for-deferred-start-relay-issue
Require successful machine relay activation before marking wash in progress
2026-06-01 22:15:16 +02:00
Jeppe B 9feede3b0d Require machine relay activation before wash progress 2026-06-01 22:15:02 +02:00
Jeppe B 5aad9eda8d Merge pull request #78 from copenhagentruckwash/fix-raw-edge-log-context-exposure
Redact edge gateway log context secrets
2026-06-01 22:13:42 +02:00
Jeppe B 91b15faba6 Redact edge gateway log context secrets 2026-06-01 22:13:32 +02:00
Jeppe B 98c1c61cbd Merge pull request #77 from copenhagentruckwash/fix-active-wash-endpoint-information-leak
Scope active-wash restore to authenticated customer (my-active-wash)
2026-06-01 22:06:25 +02:00
Jeppe B 4463a5326a Fix scoped active wash restoration 2026-06-01 22:06:14 +02:00
Jeppe B bdb23513a4 Merge pull request #76 from copenhagentruckwash/propose-fix-for-security-vulnerability
Harden self-serve vehicle condition mutations
2026-06-01 22:05:37 +02:00
Jeppe B ee40f16232 Harden self-serve vehicle condition mutations 2026-06-01 22:05:27 +02:00
Jeppe B a76944b00c Merge pull request #75 from copenhagentruckwash/fix-sessionstorage-vulnerability-in-invoicing-period
Secure privileged session caches
2026-06-01 21:52:53 +02:00
Jeppe B cf074ff893 Secure privileged session caches 2026-06-01 21:52:42 +02:00
Jeppe B 712eb19db5 Merge pull request #74 from copenhagentruckwash/propose-fix-for-release-manager-token-leak
Restrict release manager control API origins
2026-06-01 21:52:20 +02:00
Jeppe B de9bc9b170 Restrict release manager control API origins 2026-06-01 21:52:09 +02:00
Jeppe B 95e2404307 Merge pull request #73 from copenhagentruckwash/fix-unredacted-deployment-failures-exposure
Redact release deployment failure diagnostics
2026-06-01 21:27:42 +02:00
Jeppe B 2c01feb3be Redact release deployment failure diagnostics 2026-06-01 21:27:28 +02:00
Jeppe B f416e0d462 Merge pull request #72 from copenhagentruckwash/fix-telemetry-that-leaks-passwords
Redact serialized request bodies and omit failed-request bodies when capture is disabled
2026-06-01 21:26:45 +02:00
Jeppe B 7f1308c8ce Redact release timeline failed request bodies 2026-06-01 21:26:32 +02:00
Jeppe B fd8d45e437 Merge pull request #71 from copenhagentruckwash/fix-vulnerabilities-in-release-runtime-urls
Harden release runtime URL trust boundaries
2026-06-01 21:25:51 +02:00
copilot-swe-agent[bot] 20390342d5 Merge origin/master and resolve release timeline spec conflict 2026-06-01 19:21:12 +00:00
Jeppe B c47bf229f0 Harden release runtime URL trust boundaries 2026-06-01 21:14:39 +02:00
Jeppe B 3616e197e1 Merge pull request #70 from copenhagentruckwash/propose-fix-for-isolated-stack-vulnerability
Prevent active isolated stack reuse during release deploys
2026-06-01 21:12:26 +02:00
Jeppe B 3d8544de21 Prevent isolated stack deployment reuse 2026-06-01 21:11:17 +02:00
Jeppe B 504abb7a15 Merge pull request #69 from copenhagentruckwash/fix-release-subject-autocomplete-exposure
Guard release assignment subject search
2026-06-01 21:11:02 +02:00
Jeppe B 37021533b9 Guard release assignment search by manage permission 2026-06-01 21:10:51 +02:00
Jeppe B 59eab84f03 Merge pull request #68 from copenhagentruckwash/fix-cross-origin-framing-vulnerability-in-nginx
Add anti-framing headers to Coolify Nginx frontend
2026-06-01 21:10:26 +02:00
Jeppe B e67033711c Add anti-framing headers to Coolify Nginx config 2026-06-01 21:10:14 +02:00
Jeppe B 85fd00f020 Merge pull request #67 from copenhagentruckwash/fix-metadata-exposure-for-low-privileged-users
Restrict release runtime metadata inspector
2026-06-01 21:09:54 +02:00
Jeppe B 4f064a1625 Restrict release runtime metadata inspector 2026-06-01 21:09:42 +02:00
Jeppe B 3bf7ada226 Merge pull request #66 from copenhagentruckwash/fix-tls-verification-in-dev-proxy
Secure Vite API proxy TLS by default
2026-06-01 21:09:15 +02:00
Jeppe B 3b90e17a1e Secure Vite API proxy TLS by default 2026-06-01 21:09:05 +02:00
Jeppe B c1fb5b72ee Merge pull request #65 from copenhagentruckwash/fix-privilege-escalation-in-daily-report
Guard department lane toggles by wash-lane permission
2026-06-01 21:08:43 +02:00
Jeppe B 29da26cc08 Guard department lane toggles by permission 2026-06-01 21:08:32 +02:00
Jeppe B 807fc09b1e Merge pull request #64 from copenhagentruckwash/fix-dgnvask-toggle-issue
Fix Døgnvask toggle state with configuration warnings
2026-06-01 21:08:03 +02:00
Jeppe B 528e4994db Fix Dognvask warning toggle state 2026-06-01 21:07:53 +02:00
Jeppe B c339122f4f Merge pull request #63 from copenhagentruckwash/fix-release-gate-binding-to-build
Bind frontend release gate to build id
2026-06-01 21:07:33 +02:00
Jeppe B feeef46354 Bind frontend release gate to build id 2026-06-01 21:07:23 +02:00
Jeppe B 066bd14554 Merge pull request #62 from copenhagentruckwash/fix-active-wash-refresh-vulnerability
Harden active-wash refresh ownership check
2026-06-01 21:06:53 +02:00
Jeppe B bffaa2ab88 Fix active wash refresh ownership check 2026-06-01 21:06:39 +02:00
Jeppe B 31495ea67b Merge pull request #61 from copenhagentruckwash/fix-ftp-deploy-password-exposure
Secure lftp release upload credentials
2026-06-01 21:02:13 +02:00
Jeppe B 8f2533f1b2 Secure lftp release upload credentials 2026-06-01 21:02:01 +02:00
Jeppe B 13b0742deb Merge pull request #60 from copenhagentruckwash/fix-cropping-issue-in-path-editor
Fix self-serve path editor answer switch layout
2026-06-01 20:45:04 +02:00
Jeppe B e2b2d50e5e Fix self-serve path editor answer switch layout 2026-06-01 20:19:35 +02:00
Jeppe B 55d61e37f3 Merge pull request #59 from copenhagentruckwash/fix-e2e-tests-in-i18n-v2-catalog
Fix i18n v2 catalog integrity and split Release Manager source fragments
2026-06-01 19:45:57 +02:00
Jeppe B fa19f5a704 Fix i18n v2 catalog integrity 2026-06-01 19:44:50 +02:00
Jeppe Bundgaard b995031255 Enable "Selv vask" button in MobileFooter.vue 2026-06-01 19:43:57 +02:00
Jeppe Bundgaard d096444ca0 Remove unused lane unavailable warning message from SelfServeLaneStep.vue 2026-06-01 19:41:16 +02:00
Jeppe B 10358d4f53 Merge pull request #58 from copenhagentruckwash/fix-self-serve-path-editor-for-all-departments
Fix self-serve path editor vehicle scope
2026-06-01 18:32:11 +02:00
Jeppe B 78f41b8380 Fix self-serve path editor vehicle scope 2026-06-01 18:31:53 +02:00
Jeppe B 90d4bc7c04 Merge pull request #57 from copenhagentruckwash/fix-bugs-in-start-wash-flow
Harden customer wash start flow
2026-06-01 18:01:29 +02:00
Jeppe B b59c17f120 Harden customer wash start flow 2026-06-01 17:54:42 +02:00
Jeppe Bundgaard c279e7fd38 Merge remote-tracking branch 'origin/master' 2026-06-01 17:23:06 +02:00
Jeppe Bundgaard 8a4fcb3e62 Remove unused return messages and cleanup peer dependency flags in package-lock.json 2026-06-01 17:22:33 +02:00
Jeppe B 680ef7088b Merge pull request #56 from copenhagentruckwash/investigate-playwright-exit-code-1-issue
Stabilize POS email notification E2E setup
2026-06-01 17:20:54 +02:00
Jeppe B 83935af973 Stabilize POS email notification E2E setup 2026-06-01 16:45:40 +02:00
Jeppe B 37973f8fcb Add x-api-coverage happy path flag to listOrders operation 2026-06-01 13:31:48 +02:00
Jeppe B acf19c281a Add email notification actions for booking confirmations and wash certificates 2026-06-01 13:24:29 +02:00
Jeppe B 683fce32da Fix POS step 1 customer lookup loop 2026-06-01 12:41:08 +02:00
Jeppe B 4c9ba7c30d Add unit tests for Playwright full-slice ownership 2026-06-01 12:28:42 +02:00
Jeppe B ca1f3ab477 Merge pull request #55 from copenhagentruckwash/codex/frontend-release-gate-coolify-20260530
Fix frontend release gate for Coolify deploys
2026-05-31 10:22:29 +02:00
175 changed files with 9427 additions and 4180 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ on:
jobs:
upload-qodana-config:
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64, default]
steps:
- name: Checkout repository
+1 -1
View File
@@ -9,7 +9,7 @@ on:
jobs:
upload-qodana-config:
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64, default]
steps:
- name: Checkout repository
+2 -1
View File
@@ -23,6 +23,7 @@ jobs:
PLAYWRIGHT_RELEASE_API_BASE_URL: https://api-v2.truckwash.io
PLAYWRIGHT_RELEASE_API_PING_PATHS: /ping,/master/api/ping,/canary/api/ping,/stable/api/ping
RELEASE_BUILD_ID: ${{ github.run_id }}-${{ github.run_attempt }}
RELEASE_EXPECTED_BUILD_ID: ${{ github.run_id }}-${{ github.run_attempt }}
RELEASE_EXPECTED_COMMIT: ${{ github.sha }}
RELEASE_WAIT_INITIAL_SECONDS: 45
RELEASE_WAIT_TIMEOUT_SECONDS: 600
@@ -98,7 +99,7 @@ jobs:
-X POST "$RELEASE_MANAGER_GATE_URL" \
-H "Authorization: Bearer $RELEASE_MANAGER_GATE_TOKEN" \
-H "Content-Type: application/json" \
--data "{\"environment_url\":\"$RELEASE_BASE_URL\",\"channel_slug\":\"stable\",\"app\":\"frontend\",\"repository\":\"$RELEASE_REPOSITORY\",\"branch\":\"$RELEASE_BRANCH\",\"expected_commit\":\"$RELEASE_EXPECTED_COMMIT\",\"workflow_url\":\"$RELEASE_WORKFLOW_URL\",\"auto_sync\":true,\"wait_timeout_seconds\":300,\"poll_interval_seconds\":10,\"required_checks\":[\"static_artifact\",\"api_gateway\"]}"
--data "{\"environment_url\":\"$RELEASE_BASE_URL\",\"channel_slug\":\"stable\",\"app\":\"frontend\",\"repository\":\"$RELEASE_REPOSITORY\",\"branch\":\"$RELEASE_BRANCH\",\"expected_commit\":\"$RELEASE_EXPECTED_COMMIT\",\"build_id\":\"$RELEASE_EXPECTED_BUILD_ID\",\"workflow_url\":\"$RELEASE_WORKFLOW_URL\",\"auto_sync\":true,\"wait_timeout_seconds\":300,\"poll_interval_seconds\":10,\"required_checks\":[\"static_artifact\",\"api_gateway\"]}"
env:
RELEASE_MANAGER_GATE_URL: ${{ secrets.RELEASE_MANAGER_GATE_URL || 'https://api.truckwash.io/release/gate/test-runs' }}
RELEASE_MANAGER_GATE_TOKEN: ${{ secrets.RELEASE_MANAGER_GATE_TOKEN }}
+1 -1
View File
@@ -16,7 +16,7 @@ concurrency:
jobs:
format-tests:
# Match the labels exposed by the Coolify-managed GitHub runner.
# CI runs on the repository's self-hosted runner pool.
runs-on: [self-hosted, Linux, X64, default]
steps:
- name: Checkout repository
+8
View File
@@ -38,6 +38,14 @@ To use another remote API route:
$env:VITE_API_PROXY_BASE_PATH="/canary/api"; npm run dev
```
TLS certificate validation is enabled for proxied HTTPS APIs by default. If you
are using a trusted local HTTPS API with a self-signed certificate, you can opt
out explicitly:
```powershell
$env:VITE_API_PROXY_TARGET="https://local-api.test"; $env:VITE_API_PROXY_SECURE="false"; npm run dev
```
For compatible local gateways that expect the `/api` prefix to be preserved:
```powershell
+13
View File
@@ -5,18 +5,27 @@ server {
root /usr/share/nginx/html;
index index.html;
add_header Content-Security-Policy "frame-ancestors 'self'" always;
add_header X-Frame-Options "SAMEORIGIN" always;
location ~ ^/(release-entry|release-manifest)\.json$ {
add_header Cache-Control "no-store";
add_header Content-Security-Policy "frame-ancestors 'self'" always;
add_header X-Frame-Options "SAMEORIGIN" always;
try_files $uri =404;
}
location ~ ^/(master|beta|canary|internal)/frontend/(release-entry|release-manifest)\.json$ {
add_header Cache-Control "no-store";
add_header Content-Security-Policy "frame-ancestors 'self'" always;
add_header X-Frame-Options "SAMEORIGIN" always;
try_files /$2.json =404;
}
location ~ ^/(?:.+/)?(?<static_asset_path>(?:assets|resources|favicons|icons|img|sounds|\.well-known)/.+)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Content-Security-Policy "frame-ancestors 'self'" always;
add_header X-Frame-Options "SAMEORIGIN" always;
try_files /$static_asset_path =404;
}
@@ -30,11 +39,15 @@ server {
location /assets/ {
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Content-Security-Policy "frame-ancestors 'self'" always;
add_header X-Frame-Options "SAMEORIGIN" always;
try_files $uri =404;
}
location ~ ^/(master|beta|canary|internal)/frontend/assets/ {
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Content-Security-Policy "frame-ancestors 'self'" always;
add_header X-Frame-Options "SAMEORIGIN" always;
rewrite ^/(master|beta|canary|internal)/frontend/(.*)$ /$2 break;
try_files $uri =404;
}
+115 -19
View File
@@ -3056,6 +3056,8 @@ paths:
get:
tags:
- Orders
x-api-coverage:
happy: true
summary: List orders
description: Retrieve a paginated list of orders
operationId: listOrders
@@ -4622,7 +4624,7 @@ paths:
tags:
- Self-Serve
summary: Add vehicle condition
description: Add a new vehicle condition (answer to a question). Customers can only add conditions for their own vehicles.
description: Add a new vehicle condition (answer to a question). Customers can only add conditions for their own vehicles. This answer mutation does not activate machines or synchronize live relay state; hardware changes are handled only by the explicit wash start flow.
operationId: addSelfserveVehicleCondition
requestBody:
required: true
@@ -4657,14 +4659,6 @@ paths:
type: integer
nullable: true
description: Alias for vehicle_type.
activate_machine:
type: boolean
default: true
description: Whether the session synchronization may enable the machine relay. User wash-start saves answers with false.
sync_relay_state:
type: boolean
default: true
description: Whether the answer mutation should synchronize live relay state.
responses:
'200':
description: Successfully added vehicle condition
@@ -4681,7 +4675,7 @@ paths:
tags:
- Self-Serve
summary: Update vehicle condition
description: Update an existing vehicle condition. Customers can only update conditions for their own vehicles.
description: Update an existing vehicle condition. Customers can only update conditions for their own vehicles. This answer mutation does not activate machines or synchronize live relay state; hardware changes are handled only by the explicit wash start flow.
operationId: updateSelfserveVehicleCondition
parameters:
- name: id
@@ -4717,14 +4711,6 @@ paths:
type: integer
nullable: true
description: Alias for vehicle_type.
activate_machine:
type: boolean
default: true
description: Whether the session synchronization may enable the machine relay.
sync_relay_state:
type: boolean
default: true
description: Whether the mutation should synchronize live relay state.
responses:
'200':
description: Successfully updated vehicle condition
@@ -5400,7 +5386,7 @@ paths:
application/json:
schema:
type: object
required: [department, gateway_id, action]
required: [department, gateway_id, action, confirm]
properties:
department: { type: integer }
gateway_id: { type: integer }
@@ -8702,6 +8688,116 @@ paths:
schema:
$ref: '#/components/schemas/SelfServeLaneStatus'
/modules/self-serve/lane/wash/my-active-wash:
get:
tags:
- Modules
summary: Get the authenticated customer's active self-serve wash
description: |
Returns the current authenticated customer's open self-serve wash session,
if one exists. Regular customers must only receive their own active wash
details from this endpoint.
operationId: getMyActiveSelfServeWash
responses:
'200':
description: Authenticated customer's active wash details resolved
content:
application/json:
schema:
type: object
properties:
lane_id:
type: integer
nullable: true
in_progress:
type: boolean
session:
type: object
nullable: true
properties:
id:
type: integer
lane_id:
type: integer
nullable: true
department_id:
type: integer
nullable: true
status:
type: string
reg:
type: string
customer_number:
type: integer
nullable: true
vehicle_id:
type: integer
nullable: true
vehicle_type_id:
type: integer
nullable: true
included_minutes:
type: integer
nullable: true
machine_type_id:
type: integer
nullable: true
machine_relay_enabled:
type: boolean
machine_relay_enabled_at:
type: string
nullable: true
machine_start_triggered:
type: boolean
machine_start_triggered_at:
type: string
nullable: true
wash_started_at:
type: string
nullable: true
created_at:
type: string
updated_at:
type: string
nullable: true
customer:
type: object
nullable: true
properties:
id:
type: integer
nullable: true
customer_number:
type: integer
nullable: true
display_name:
type: string
nullable: true
email:
type: string
nullable: true
phone_country_code:
type: integer
nullable: true
phone:
type: string
nullable: true
vehicle:
type: object
nullable: true
properties:
id:
type: integer
customer_id:
type: integer
type:
type: integer
reg:
type: string
reference:
type: string
nullable: true
/modules/self-serve/lane/wash/in-progress:
get:
tags:
+44 -45
View File
@@ -7,7 +7,6 @@
"": {
"name": "truckwashdashboardsfrontend",
"version": "0.0.0",
"hasInstallScript": true,
"dependencies": {
"@azure/msal-browser": "^4.12.0",
"@bubblewrap/cli": "^1.23.0",
@@ -42,6 +41,7 @@
"dayspan-vuetify": "^0.4.0",
"flow": "^0.2.3",
"html2canvas": "^1.4.1",
"pinia": "^3.0.4",
"qrcode": "^1.5.4",
"sass": "^1.81.1",
"sweetalert2": "11.22.4",
@@ -233,7 +233,6 @@
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
@@ -1931,6 +1930,16 @@
}
}
},
"node_modules/@bubblewrap/core/node_modules/inquirer/node_modules/@types/node": {
"version": "25.9.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz",
"integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==",
"optional": true,
"peer": true,
"dependencies": {
"undici-types": ">=7.24.0 <7.24.7"
}
},
"node_modules/@bubblewrap/core/node_modules/mute-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz",
@@ -1954,7 +1963,8 @@
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
"integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
"license": "MIT",
"optional": true
"optional": true,
"peer": true
},
"node_modules/@bubblewrap/validator": {
"version": "1.22.0",
@@ -2077,7 +2087,6 @@
}
],
"license": "MIT",
"peer": true,
"engines": {
"node": ">=20.19.0"
},
@@ -2126,7 +2135,6 @@
}
],
"license": "MIT",
"peer": true,
"engines": {
"node": ">=20.19.0"
}
@@ -2564,7 +2572,6 @@
"resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.20.tgz",
"integrity": "sha512-1cukXLlePFiJ8YKXn/4tMKsy0etxYLCkXk8nUCFi11nRONF2Ba2CD5b21/ovtOO2tL6afTJfwmc1ed3HG7eB1g==",
"license": "MIT",
"peer": true,
"dependencies": {
"preact": "~10.12.1"
}
@@ -2808,7 +2815,6 @@
"resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.22.12.tgz",
"integrity": "sha512-xcmww1O/JFP2MrlGUMd3Q78S3Qu6W3mYTXYuIqFq33EorgYHV/HqymHfXy9GjiCJ7OI+7lWx6nYFOzU7M4rd1Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jimp/core": "^0.22.12"
}
@@ -2845,7 +2851,6 @@
"resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.22.12.tgz",
"integrity": "sha512-xslz2ZoFZOPLY8EZ4dC29m168BtDx95D6K80TzgUi8gqT7LY6CsajWO0FAxDwHz6h0eomHMfyGX0stspBrTKnQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jimp/utils": "^0.22.12"
},
@@ -2858,7 +2863,6 @@
"resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.22.12.tgz",
"integrity": "sha512-S0vJADTuh1Q9F+cXAwFPlrKWzDj2F9t/9JAbUvaaDuivpyWuImEKXVz5PUZw2NbpuSHjwssbTpOZ8F13iJX4uw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jimp/utils": "^0.22.12"
},
@@ -2883,7 +2887,6 @@
"resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.22.12.tgz",
"integrity": "sha512-xImhTE5BpS8xa+mAN6j4sMRWaUgUDLoaGHhJhpC+r7SKKErYDR0WQV4yCE4gP+N0gozD0F3Ka1LUSaMXrn7ZIA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jimp/utils": "^0.22.12",
"tinycolor2": "^1.6.0"
@@ -2927,7 +2930,6 @@
"resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.22.12.tgz",
"integrity": "sha512-FNuUN0OVzRCozx8XSgP9MyLGMxNHHJMFt+LJuFjn1mu3k0VQxrzqbN06yIl46TVejhyAhcq5gLzqmSCHvlcBVw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jimp/utils": "^0.22.12"
},
@@ -3051,7 +3053,6 @@
"resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.22.12.tgz",
"integrity": "sha512-3NyTPlPbTnGKDIbaBgQ3HbE6wXbAlFfxHVERmrbqAi8R3r6fQPxpCauA8UVDnieg5eo04D0T8nnnNIX//i/sXg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jimp/utils": "^0.22.12"
},
@@ -3064,7 +3065,6 @@
"resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.22.12.tgz",
"integrity": "sha512-9YNEt7BPAFfTls2FGfKBVgwwLUuKqy+E8bDGGEsOqHtbuhbshVGxN2WMZaD4gh5IDWvR+emmmPPWGgaYNYt1gA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jimp/utils": "^0.22.12"
},
@@ -3080,7 +3080,6 @@
"resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.22.12.tgz",
"integrity": "sha512-dghs92qM6MhHj0HrV2qAwKPMklQtjNpoYgAB94ysYpsXslhRTiPisueSIELRwZGEr0J0VUxpUY7HgJwlSIgGZw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jimp/utils": "^0.22.12"
},
@@ -5071,7 +5070,6 @@
"version": "7.7.9",
"resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz",
"integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/devtools-shared": "^7.7.9",
@@ -5087,7 +5085,6 @@
"version": "7.7.9",
"resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz",
"integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==",
"dev": true,
"license": "MIT",
"dependencies": {
"rfdc": "^1.4.1"
@@ -5343,7 +5340,6 @@
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"devOptional": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -5378,7 +5374,6 @@
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@@ -5472,8 +5467,7 @@
"version": "5.10.4",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-5.10.4.tgz",
"integrity": "sha512-gt0VUqZ2+mr25ScbUcKZgJr96jKYm4vjOcxEWCEh/E5F4dWqhyo3dBhPRvNNnkKiWxkMd2cBwj3ZYH3rK39fkA==",
"license": "SEE LICENSE IN LICENSE",
"peer": true
"license": "SEE LICENSE IN LICENSE"
},
"node_modules/aria-query": {
"version": "5.3.1",
@@ -5597,7 +5591,6 @@
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz",
"integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"follow-redirects": "^1.15.11",
"form-data": "^4.0.5",
@@ -5743,7 +5736,6 @@
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz",
"integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/antfu"
@@ -5821,7 +5813,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -5906,8 +5897,7 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/bulma/-/bulma-1.0.4.tgz",
"integrity": "sha512-Ffb6YGXDiZYX3cqvSbHWqQ8+LkX6tVoTcZuVB3lm93sbAVXlO0D6QlOTMnV6g18gILpAXqkG2z9hf9z4hCjz2g==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/bulma-block-list": {
"version": "1.1.0",
@@ -6165,7 +6155,6 @@
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
"integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@kurkle/color": "^0.3.0"
},
@@ -6434,7 +6423,6 @@
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz",
"integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-what": "^5.2.0"
@@ -6588,7 +6576,6 @@
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
"license": "ISC",
"peer": true,
"engines": {
"node": ">=12"
}
@@ -6777,7 +6764,6 @@
"integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==",
"deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.",
"license": "MIT",
"peer": true,
"dependencies": {
"@vue/compiler-sfc": "2.7.16",
"csstype": "^3.1.0"
@@ -8335,8 +8321,7 @@
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz",
"integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==",
"license": "Apache-2.0",
"peer": true
"license": "Apache-2.0"
},
"node_modules/ieee754": {
"version": "1.2.1",
@@ -9000,7 +8985,6 @@
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz",
"integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
@@ -9583,7 +9567,6 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
"dev": true,
"license": "MIT"
},
"node_modules/mkdirp": {
@@ -10121,7 +10104,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
"dev": true,
"license": "MIT"
},
"node_modules/performance-now": {
@@ -10183,6 +10165,34 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pinia": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz",
"integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
"dependencies": {
"@vue/devtools-api": "^7.7.7"
},
"funding": {
"url": "https://github.com/sponsors/posva"
},
"peerDependencies": {
"typescript": ">=4.5.0",
"vue": "^3.5.11"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/pinia/node_modules/@vue/devtools-api": {
"version": "7.7.9",
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz",
"integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==",
"dependencies": {
"@vue/devtools-kit": "^7.7.9"
}
},
"node_modules/pixelmatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz",
@@ -10403,7 +10413,6 @@
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz",
"integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
"license": "MIT",
"peer": true,
"dependencies": {
"dijkstrajs": "^1.0.1",
"pngjs": "^5.0.0",
@@ -10741,7 +10750,6 @@
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
"dev": true,
"license": "MIT"
},
"node_modules/rgbcolor": {
@@ -10758,7 +10766,6 @@
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz",
"integrity": "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/estree": "1.0.8"
},
@@ -10942,7 +10949,6 @@
"resolved": "https://registry.npmjs.org/sass/-/sass-1.98.0.tgz",
"integrity": "sha512-+4N/u9dZ4PrgzGgPlKnaaRQx64RO0JBKs9sDhQ2pLgN6JQZ25uPQZKQYaBJU48Kd5BxgXoJ4e09Dq7nMcOUW3A==",
"license": "MIT",
"peer": true,
"dependencies": {
"chokidar": "^4.0.0",
"immutable": "^5.1.5",
@@ -10964,7 +10970,6 @@
"integrity": "sha512-Do7u6iRb6K+lrllcTkB1BXcHwOxcKe3rEfOF/GcCLE2w3WpddakRAosJOHFUR37DpsvimQXEt5abs3NzUjEIqg==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@bufbuild/protobuf": "^2.5.0",
"colorjs.io": "^0.5.0",
@@ -11662,7 +11667,6 @@
"version": "14.0.1",
"resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
"integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@@ -11934,7 +11938,6 @@
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz",
"integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==",
"dev": true,
"license": "MIT",
"dependencies": {
"copy-anything": "^4"
@@ -12730,7 +12733,6 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.11.tgz",
"integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==",
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.5.0",
@@ -14123,7 +14125,6 @@
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.21.3",
"postcss": "^8.4.43",
@@ -14183,7 +14184,6 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.31.tgz",
"integrity": "sha512-iV/sU9SzOlmA/0tygSmjkEN6Jbs3nPoIPFhCMLD2STrjgOU8DX7ZtzMhg4ahVwf5Rp9KoFzcXeB1ZrVbLBp5/Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"@vue/compiler-dom": "3.5.31",
"@vue/compiler-sfc": "3.5.31",
@@ -14871,7 +14871,6 @@
"integrity": "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"rollup": "dist/bin/rollup"
},
+1 -1
View File
@@ -11,7 +11,6 @@
"format:tests:commit": "node scripts/pre-commit-format-tests.mjs",
"format:tests:check": "prettier --check \"tests/**/*.{js,ts}\"",
"prepare": "node scripts/prepare-husky.mjs",
"postinstall": "node scripts/postinstall-sync-playwright-root-links.mjs",
"preview": "vite preview",
"preview:prod": "npm run build && npm run preview -- --host 127.0.0.1 --port 4173",
"text:fix-encoding": "node scripts/text-encoding.mjs fix",
@@ -87,6 +86,7 @@
"dayspan-vuetify": "^0.4.0",
"flow": "^0.2.3",
"html2canvas": "^1.4.1",
"pinia": "^3.0.4",
"qrcode": "^1.5.4",
"sass": "^1.81.1",
"sweetalert2": "11.22.4",
@@ -1,22 +0,0 @@
import fs from "node:fs";
import path from "node:path";
import { spawnSync } from "node:child_process";
const helperScriptPath = path.resolve(process.cwd(), "..", "scripts", "sync-playwright-root-links.mjs");
if (!fs.existsSync(helperScriptPath)) {
console.log(
`Skipping root Playwright link sync: helper script not found at ${helperScriptPath}.`
);
process.exit(0);
}
const result = spawnSync(process.execPath, [helperScriptPath], {
stdio: "inherit",
});
if (typeof result.status === "number") {
process.exit(result.status);
}
process.exit(1);
+35 -3
View File
@@ -23,20 +23,52 @@ if [[ -z "$DEPLOY_URL" ]]; then
echo "Set RELEASE_DEPLOY_URL or RELEASE_DEPLOY_HOST, RELEASE_DEPLOY_USER, and RELEASE_DEPLOY_PASSWORD." >&2
exit 1
fi
DEPLOY_URL="ftp://${USER_NAME}:${PASSWORD}@${HOST}"
DEPLOY_URL="ftp://${HOST}"
elif [[ -n "$USER_NAME" || -n "$PASSWORD" ]]; then
if [[ -z "$USER_NAME" || -z "$PASSWORD" ]]; then
echo "Set both RELEASE_DEPLOY_USER and RELEASE_DEPLOY_PASSWORD when providing deploy credentials separately." >&2
exit 1
fi
fi
lftp_quote() {
local value="${1//\'/\'\\\'\'}"
printf "'%s'" "$value"
}
run_lftp() {
local transfer_command="$1"
{
printf 'set ftp:ssl-allow true\n'
printf 'set ftp:ssl-force true\n'
printf 'set ftp:ssl-protect-data true\n'
printf 'set net:max-retries 3\n'
printf 'set net:timeout 20\n'
if [[ -n "$USER_NAME" && -n "$PASSWORD" ]]; then
printf 'open -u %s,%s %s\n' "$(lftp_quote "$USER_NAME")" "$(lftp_quote "$PASSWORD")" "$(lftp_quote "$DEPLOY_URL")"
else
printf 'open %s\n' "$(lftp_quote "$DEPLOY_URL")"
fi
printf 'cd %s\n' "$(lftp_quote "$REMOTE_ROOT")"
printf '%s\n' "$transfer_command"
printf 'bye\n'
} | lftp -f /dev/stdin
}
upload_file() {
local source_file="$1"
local remote_file="$2"
if [[ -f "$source_file" ]]; then
lftp "$DEPLOY_URL" -e "set ftp:ssl-allow true; set net:max-retries 3; set net:timeout 20; cd \"$REMOTE_ROOT\"; put -O \"$(dirname "$remote_file")\" \"$source_file\" -o \"$(basename "$remote_file")\"; bye"
run_lftp "put -O $(lftp_quote "$(dirname "$remote_file")") $(lftp_quote "$source_file") -o $(lftp_quote "$(basename "$remote_file")")"
fi
}
for directory in assets resources favicons icons img sounds .well-known; do
if [[ -d "$DIST_DIR/$directory" ]]; then
lftp "$DEPLOY_URL" -e "set ftp:ssl-allow true; set net:max-retries 3; set net:timeout 20; cd \"$REMOTE_ROOT\"; mirror -R --only-newer --parallel=4 \"$DIST_DIR/$directory\" \"$directory\"; bye"
run_lftp "mirror -R --only-newer --parallel=4 $(lftp_quote "$DIST_DIR/$directory") $(lftp_quote "$directory")"
fi
done
+1 -1
View File
@@ -120,7 +120,7 @@ async function verifyShell(baseUrl, shellPath) {
async function verifyRelease(baseUrl) {
const expectedCommit = process.env.RELEASE_EXPECTED_COMMIT || process.env.GITHUB_SHA || "";
const expectedBuildId = process.env.RELEASE_EXPECTED_BUILD_ID || "";
const expectedBuildId = process.env.RELEASE_EXPECTED_BUILD_ID || process.env.RELEASE_BUILD_ID || "";
const manifest = await fetchJson(baseUrl, "release-manifest.json");
const releaseEntry = await fetchJson(baseUrl, "release-entry.json");
+42 -8
View File
@@ -1,25 +1,34 @@
import { execFile, spawn } from "node:child_process";
import fs from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
const workingDirectory = process.cwd();
const playwrightCliPath = path.join(workingDirectory, "node_modules", "@playwright", "test", "cli.js");
const execFileAsync = promisify(execFile);
const roles = ["customer", "subuser", "admin", "superuser"];
export const roles = ["customer", "subuser", "admin", "superuser"];
const listEntryPattern = /^\s+\[[^\]]+\]\s+\s+(.+?):(\d+):(\d+)\s+\s+(.+)\s*$/u;
const ownedFilesByRole = {
export const ownedFilesByRole = {
customer: [
"auth.smoke.spec.js",
"booking-selfserve.smoke.spec.js",
"connectivityIssue.spec.ts",
"example.spec.ts",
"guest-book-wash-mobile.spec.ts",
"i18n-catalog-switch.spec.ts",
"i18n-v2-integrity.spec.ts",
"i18n.smoke.spec.ts",
"i18n.views.spec.ts",
"navigation.smoke.spec.js",
"qr-new-customer-layout.spec.ts",
"release-bootstrap.spec.js",
"release-channel-switched.spec.js",
"release-channel-unavailable.spec.js",
"release-update-widget.spec.js",
"self-serve-wash.spec.js",
"session-release-runtime.spec.ts",
"user-orders.spec.ts",
"userBookings.spec.ts",
"userBookWash.spec.ts",
@@ -53,6 +62,7 @@ const ownedFilesByRole = {
"assign-draft-order-modal-layout.spec.ts",
"change-invoice-collection.spec.ts",
"change-customer.spec.ts",
"default-mobile-redirect.spec.ts",
"economic-queue-workflow.spec.js",
"pos-customer-rules.spec.js",
"pos-desktop-card-payments.spec.js",
@@ -62,18 +72,23 @@ const ownedFilesByRole = {
"pos.visual.spec.js",
],
superuser: [
"coolify-infrastructure.spec.js",
"edge-gateways.fleet-outline.spec.js",
"edge-gateways.routes.spec.js",
"edge-gateways.smoke.spec.js",
"edge-gateways.visual.spec.js",
"errorReports.spec.ts",
"failover-config.source.spec.ts",
"invoice-distribution.smoke.spec.js",
"invoice-transfer-monitor.spec.ts",
"invoice-transfer-queue-history.spec.js",
"invoicing-period.smoke.spec.js",
"issue-repro-duplicates-date.spec.js",
"release-manager.spec.js",
"self-serve-sessions.spec.js",
"self-serve-studio-audit-navigation.spec.js",
"self-serve-studio-flow.spec.js",
"session-bootstrap.spec.ts",
"superuser-bookings.spec.ts",
"superuser-customer-complaints.spec.ts",
"superuser-customers-mass-import.spec.ts",
@@ -84,12 +99,13 @@ const ownedFilesByRole = {
"superuser-drafts.spec.ts",
"superuser-products-layout.spec.ts",
"superuser-system-status.smoke.spec.js",
"superuser-users.spec.ts",
"superuser-vehicles.smoke.spec.js",
"workfeed-config.smoke.spec.js",
],
};
const titleRules = [
export const titleRules = [
{ role: "customer", file: "userAuth.spec.ts", patterns: [/\[AUTH\]\[User\]/u] },
{ role: "subuser", file: "userAuth.spec.ts", patterns: [/\[AUTH\]\[Subuser\]/u] },
{ role: "admin", file: "userAuth.spec.ts", patterns: [/\[AUTH\]\[Operator\]/u] },
@@ -116,6 +132,11 @@ const titleRules = [
file: "subuser-management.spec.ts",
patterns: [/^authorized subuser managers/i, /^subuser self-service/i, /^subusers without /i],
},
{
role: "superuser",
file: "subuser-management.spec.ts",
patterns: [/^superusers can list and invite chauffeurs/i],
},
{ role: "subuser", file: "userProfileVisibility.spec.ts", patterns: [/\[PROFILE\]\[Subuser\]\[Visibility\]/u] },
];
@@ -200,7 +221,7 @@ function toBaseName(filePath) {
return filePath.split(/[\\/]/u).pop() || filePath;
}
function parseListedTests(listOutput) {
export function parseListedTests(listOutput) {
return listOutput
.split(/\r?\n/u)
.map((line) => {
@@ -222,7 +243,7 @@ function parseListedTests(listOutput) {
.filter(Boolean);
}
function classifyTest(testEntry) {
export function classifyTest(testEntry) {
const matches = new Set();
const directOwner = ownedFileToRole.get(testEntry.fileName);
@@ -309,8 +330,8 @@ async function runPlaywright(project, testListPath, forwardedArgs) {
});
}
async function main() {
const { options, forwardedArgs } = parseCliArgs(process.argv.slice(2));
export async function main(argv = process.argv.slice(2)) {
const { options, forwardedArgs } = parseCliArgs(argv);
validateOptions(options, forwardedArgs);
const listOutput = await listProjectTests(options.project, forwardedArgs);
@@ -342,4 +363,17 @@ async function main() {
await runPlaywright(options.project, testListPath, forwardedArgs);
}
await main();
async function isDirectRun() {
if (!process.argv[1]) {
return false;
}
const currentPath = await fs.realpath(fileURLToPath(import.meta.url));
const invokedPath = await fs.realpath(process.argv[1]).catch(() => path.resolve(process.argv[1]));
return currentPath === invokedPath;
}
if (await isDirectRun()) {
await main();
}
@@ -63,6 +63,10 @@ const props = defineProps({
type: String,
default: null,
},
reg_3: {
type: String,
default: null,
},
order_booking_id: {
type: Number,
default: null,
@@ -93,6 +97,10 @@ const props = defineProps({
type: Boolean,
default: false,
},
allowBookingDeletion: {
type: Boolean,
default: false,
},
department_lane_id: {
type: Number,
default: null,
@@ -917,6 +925,15 @@ const getAttachmentLabel = (attachment) => {
);
};
const isWashCertificateAttachment = (attachment) => {
const marker = String(getAttachmentOtherPayload(attachment) || "").trim().toUpperCase();
if (marker === "WASH_CERTIFICATE") {
return true;
}
return /(?:^|[/\\])wash[_-]?certificate.*\.pdf$/i.test(getAttachmentLabel(attachment));
};
const getAttachmentExtension = (attachment) => {
const match = String(getAttachmentLabel(attachment))
.toLowerCase()
@@ -1045,6 +1062,10 @@ const activeAttachmentPreviewSource = computed(() => {
return previewSourcesById.value[activeAttachment.value.id] ?? null;
});
const hasWashCertificateAttachment = computed(() =>
attachmentsFromOrder.value.some((attachment) => isWashCertificateAttachment(attachment))
);
const hasCachedPreviewSource = (attachmentId) => {
return Object.prototype.hasOwnProperty.call(previewSourcesById.value, attachmentId);
};
@@ -1609,6 +1630,48 @@ const showCompleteOrderBookingConfirmation = async () => {
}
};
const showEmailNotificationActionResult = async (requestAction, successKey, errorKey) => {
try {
await requestAction();
await Swal.fire({
title: t(successKey),
icon: "success",
showConfirmButton: false,
timer: 2000,
heightAuto: false,
});
} catch (error) {
console.error(error);
await Swal.fire({
title: t("common.error"),
text: [t(errorKey), SessionUser.functions.parseErrorMessage?.(error)].filter(Boolean).join(": "),
icon: "error",
heightAuto: false,
});
}
};
const resendBookingConfirmation = () =>
showEmailNotificationActionResult(
() => SessionUser.objects.order_bookings.functions.resendBookingConfirmation(props.order_booking_id),
"admin.pos.settings_wheel.resend_booking_confirmation_success",
"admin.pos.settings_wheel.resend_booking_confirmation_error"
);
const resendBookingCompletionConfirmation = () =>
showEmailNotificationActionResult(
() => SessionUser.objects.order_bookings.functions.resendBookingCompletionConfirmation(props.order_booking_id),
"admin.pos.settings_wheel.resend_booking_completion_confirmation_success",
"admin.pos.settings_wheel.resend_booking_completion_confirmation_error"
);
const resendWashCertificate = () =>
showEmailNotificationActionResult(
() => SessionUser.objects.orders.functions.resendWashCertificate(props.order_id),
"admin.pos.settings_wheel.resend_wash_certificate_success",
"admin.pos.settings_wheel.resend_wash_certificate_error"
);
const getAvailableInvoiceCollectionsForCustomer = async (customerNumber) => {
const normalizedCustomerNumber = normalizePositiveInteger(customerNumber);
if (!normalizedCustomerNumber) {
@@ -1693,6 +1756,11 @@ const acceptSelfServeWashDraft = async () => {
}
};
const canDeleteOrderBooking = computed(() =>
!props.order_id &&
(props.allowBookingDeletion || SessionUser.canAccessAdmin() || SessionUser.canAccessSuperUser())
);
const flatBuiltInMenuSections = computed(() => {
const sections = [];
@@ -1747,7 +1815,7 @@ const flatBuiltInMenuSections = computed(() => {
),
})
: null,
!props.order_id
canDeleteOrderBooking.value
? buildMenuAction("booking-delete", {
icon: "fas fa-trash-alt",
label: t("admin.pos.settings_wheel.delete_booking"),
@@ -1835,6 +1903,40 @@ const flatBuiltInMenuSections = computed(() => {
}
}
if (props.order_booking_id || hasWashCertificateAttachment.value) {
const emailNotificationsSection = buildMenuSection(
"email-notifications",
t("admin.pos.settings_wheel.email_notifications_section"),
[
props.order_booking_id
? buildMenuAction("email-notifications-resend-booking-confirmation", {
icon: "fas fa-envelope",
label: t("admin.pos.settings_wheel.resend_booking_confirmation"),
clickAction: resendBookingConfirmation,
})
: null,
props.order_booking_id && hasWashCertificateAttachment.value
? buildMenuAction("email-notifications-resend-booking-completion-confirmation", {
icon: "fas fa-envelope-open-text",
label: t("admin.pos.settings_wheel.resend_booking_completion_confirmation"),
clickAction: resendBookingCompletionConfirmation,
})
: null,
!props.order_booking_id && hasWashCertificateAttachment.value
? buildMenuAction("email-notifications-resend-wash-certificate", {
icon: "fas fa-file-pdf",
label: t("admin.pos.settings_wheel.resend_wash_certificate"),
clickAction: resendWashCertificate,
})
: null,
]
);
if (emailNotificationsSection) {
sections.push(emailNotificationsSection);
}
}
if (props.invoice_collection_id && SessionUser.canAccessSuperUser()) {
const invoiceCollectionLinkSection = buildMenuSection(
"invoice-collection-link",
@@ -2136,10 +2238,10 @@ const flatBuiltInMenuSections = computed(() => {
}
}
if (props.reg_1 || props.reg_2) {
if (props.reg_1 || props.reg_2 || props.reg_3) {
const vehicleSection = buildMenuSection(
"vehicle",
props.reg_1 && props.reg_2
[props.reg_1, props.reg_2, props.reg_3].filter(Boolean).length > 1
? SessionUser.objects.vehicles.meta.labels.multiple
: SessionUser.objects.vehicles.meta.labels.single,
[
@@ -2157,6 +2259,13 @@ const flatBuiltInMenuSections = computed(() => {
clickAction: () => SessionUser.functions.redirectTo.superUser("/vehicles/" + props.reg_2, true),
})
: null,
props.reg_3 && SessionUser.canAccessSuperUser()
? buildMenuAction("vehicle-reg-3", {
icon: "fas fa-car",
label: t("admin.pos.settings_wheel.view_vehicle_new_tab", { reg: props.reg_3 }),
clickAction: () => SessionUser.functions.redirectTo.superUser("/vehicles/" + props.reg_3, true),
})
: null,
]
);
@@ -15,6 +15,7 @@ import {
selectPreferredStripeTerminalReaderId,
STRIPE_TERMINAL_STATUS,
} from "@/components/displays/department/pos/displays/stripeTerminalReaders.js";
import { normalizeStripeInvoice } from "@/components/displays/department/pos/displays/stripeEmailInvoice.js";
const POLLING_INTERVAL_MS = 5000;
const STRIPE_TERMINAL_SETUP_REQUIRED_CODE = 'stripe_terminal_setup_required';
@@ -87,29 +88,6 @@ const selectedTaxRate = ref(1);
const paymentIntent = computed(() => StripeModule.paymentIntents.paymentIntent.value);
const isTerminalPaymentCaptured = computed(() => StripeModule.paymentIntents.isPaymentIntentAmountReceived(paymentIntent.value));
const normalizeStripeInvoice = (value) => {
if (!value || typeof value !== 'object' || Array.isArray(value) || Object.keys(value).length === 0) {
return null;
}
const invoiceId = value.invoice_id || value.id || null;
if (!invoiceId) {
return null;
}
return {
id: value.id ?? props.order_id,
invoice_id: invoiceId,
customer_id: value.customer_id ?? null,
url: value.url || value.hosted_invoice_url || null,
created_at: value.created_at || null,
paid: Boolean(value.paid),
status: value.status || 'unknown',
amount_due: Number(value.amount_due ?? 0),
amount_paid: Number(value.amount_paid ?? 0),
};
};
const hasStripeEmailInvoice = computed(() => stripeInvoice.value !== null);
const isStripeEmailInvoicePaid = computed(() => stripeInvoice.value?.paid === true);
const isStripeEmailInvoiceTerminalState = computed(() => {
@@ -202,7 +180,7 @@ const loadStripeInvoiceState = async () => {
try {
const response = await getOrder(props.order_id, true);
const nextInvoice = normalizeStripeInvoice(response?.data?.includes?.stripeModuleOrders);
const nextInvoice = normalizeStripeInvoice(response?.data?.includes?.stripeModuleOrders, props.order_id);
stripeInvoice.value = nextInvoice;
if (nextInvoice) {
emailPanelState.value = 'tracking';
@@ -0,0 +1,60 @@
const STRIPE_INVOICE_PAID_STATUS = 'paid';
export const parseStripeInvoicePaidFlag = (value) => {
if (typeof value === 'boolean') {
return value;
}
if (typeof value === 'number') {
return value === 1;
}
if (typeof value === 'string') {
const normalizedValue = value.trim().toLowerCase();
if (['true', '1'].includes(normalizedValue)) {
return true;
}
if (['false', '0', ''].includes(normalizedValue)) {
return false;
}
}
return false;
};
const toFiniteNumber = (value, fallback = 0) => {
const parsedValue = Number(value ?? fallback);
return Number.isFinite(parsedValue) ? parsedValue : fallback;
};
export const normalizeStripeInvoice = (value, fallbackOrderId = null) => {
if (!value || typeof value !== 'object' || Array.isArray(value) || Object.keys(value).length === 0) {
return null;
}
const invoiceId = value.invoice_id || value.id || null;
if (!invoiceId) {
return null;
}
const status = String(value.status || 'unknown').toLowerCase();
const amountDue = toFiniteNumber(value.amount_due);
const amountPaid = toFiniteNumber(value.amount_paid);
const hasCoveredAmountDue = amountDue <= 0 || amountPaid >= amountDue;
const isPaid = status === STRIPE_INVOICE_PAID_STATUS
&& parseStripeInvoicePaidFlag(value.paid)
&& hasCoveredAmountDue;
return {
id: value.id ?? fallbackOrderId,
invoice_id: invoiceId,
customer_id: value.customer_id ?? null,
url: value.url || value.hosted_invoice_url || null,
created_at: value.created_at || null,
paid: isPaid,
status,
amount_due: amountDue,
amount_paid: amountPaid,
};
};
@@ -10,6 +10,16 @@ import { SessionUser } from "@/components/session/token/SessionUser.vue";
const acceptedOrderAttachmentFileTypes = "image/*,application/pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx";
const imageExtensions = [".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".svg"];
const officeExtensions = [".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx"];
const safePreviewBlobTypesByKind = {
image: {
fallback: "image/png",
allowed: new Set(["image/png", "image/jpeg", "image/gif", "image/webp", "image/bmp", "image/svg+xml"]),
},
document: {
fallback: "application/pdf",
allowed: new Set(["application/pdf"]),
},
};
const props = defineProps({
order: {
@@ -324,7 +334,21 @@ const hasCachedPreviewSource = (attachmentId) => {
return Object.prototype.hasOwnProperty.call(previewSourcesById.value, attachmentId);
};
const createEmbeddablePreviewUrl = async (downloadLink) => {
const createSafePreviewBlob = (fileBlob, previewKind) => {
const previewBlobTypes = safePreviewBlobTypesByKind[previewKind];
if (!previewBlobTypes) {
return null;
}
const normalizedBlobType = String(fileBlob.type || "").toLowerCase();
const safeBlobType = previewBlobTypes.allowed.has(normalizedBlobType)
? normalizedBlobType
: previewBlobTypes.fallback;
return new Blob([fileBlob], { type: safeBlobType });
};
const createEmbeddablePreviewUrl = async (downloadLink, previewKind) => {
if (!downloadLink) {
return null;
}
@@ -340,7 +364,12 @@ const createEmbeddablePreviewUrl = async (downloadLink) => {
return null;
}
const objectUrl = URL.createObjectURL(fileBlob);
const safePreviewBlob = createSafePreviewBlob(fileBlob, previewKind);
if (!safePreviewBlob) {
return null;
}
const objectUrl = URL.createObjectURL(safePreviewBlob);
generatedObjectUrls.add(objectUrl);
return objectUrl;
} catch (error) {
@@ -372,7 +401,7 @@ const ensurePreviewSource = async (attachment) => {
attachment.id,
false
);
const previewSource = await createEmbeddablePreviewUrl(downloadLink);
const previewSource = await createEmbeddablePreviewUrl(downloadLink, previewKind);
previewSourcesById.value = {
...previewSourcesById.value,
[attachment.id]: previewSource,
@@ -517,6 +546,7 @@ const toggleDropdown = async () => {
:src="activePreviewSource"
class="order-attachments-preview-panel__document"
title="Attachment preview"
sandbox
></iframe>
<a
v-else-if="activePreviewKind === 'link'"
@@ -1263,6 +1263,9 @@ const formatCashierName = (order) => {
v-bind:customer_number="order.customer_id"
v-bind:department_id="order.department_id"
v-bind:reg_1="order.reg_1"
v-bind:reg_2="order.reg_2"
v-bind:reg_3="order.reg_3"
v-bind:order_booking_id="order.booking_id"
:refreshFunction="loadList"
@deleted="loadList()"
@flag-created="emitFlagCreated"
@@ -1506,6 +1509,9 @@ const formatCashierName = (order) => {
v-bind:customer_number="order.customer_id"
v-bind:department_id="order.department_id"
v-bind:reg_1="order.reg_1"
v-bind:reg_2="order.reg_2"
v-bind:reg_3="order.reg_3"
v-bind:order_booking_id="order.booking_id"
:refreshFunction="loadList"
@deleted="loadList()"
@flag-created="emitFlagCreated"
@@ -92,6 +92,7 @@ const duplicateDetailsExpanded = ref(false);
const pendingNextResolution = ref(false);
const isDesktopLastWashCopying = ref(false);
let desktopStep1CoordinationPromise = Promise.resolve({ canProceed: true });
let focusOnReg1TimeoutId = null;
const isDesktopStep1Active = computed(() => getCurrentStep() === 1);
const setTab = (tab) => {
@@ -117,7 +118,16 @@ watch(
);
const focusOnReg1 = () => {
setTimeout(() => {
if (focusOnReg1TimeoutId !== null) {
clearTimeout(focusOnReg1TimeoutId);
}
focusOnReg1TimeoutId = setTimeout(() => {
focusOnReg1TimeoutId = null;
if (typeof document === "undefined") {
return;
}
const reg1Input = document.getElementById("reg_1");
if (reg1Input) {
reg1Input.focus();
@@ -267,12 +277,8 @@ const bookingSelectionObjects = computed(() => {
const contentSegments = [
`${t("admin.pos.order_booking_selector.customer_label")}: ${booking?.customer_name || t("admin.pos.not_found")}`,
`${t("admin.pos.order_booking_selector.plates_label")}: ${plateText || t("admin.pos.not_found")}`,
`${t("common.reference")}: ${
getOrderBookingReferenceValue(booking) || t("admin.pos.not_found")
}`,
`${t("common.services")}: ${
getOrderBookingServiceText(booking) || t("admin.pos.not_found")
}`,
`${t("common.reference")}: ${getOrderBookingReferenceValue(booking) || t("admin.pos.not_found")}`,
`${t("common.services")}: ${getOrderBookingServiceText(booking) || t("admin.pos.not_found")}`,
];
return {
@@ -681,6 +687,10 @@ onMounted(() => {
});
onBeforeUnmount(() => {
if (focusOnReg1TimeoutId !== null) {
clearTimeout(focusOnReg1TimeoutId);
focusOnReg1TimeoutId = null;
}
clearDesktopStep1PreflightHandler(handleDesktopStep1Preflight);
});
@@ -752,11 +762,7 @@ watch(
<ButtonsBox class="pos-actions pos-actions--stacked">
<Cancel tabindex="2" class="is-fullwidth" />
</ButtonsBox>
<div
v-if="shouldShowActionRailControls"
class="pos-shell-actions__rail"
data-testid="pos-step-1-action-rail"
>
<div v-if="shouldShowActionRailControls" class="pos-shell-actions__rail" data-testid="pos-step-1-action-rail">
<PosDesktopDuplicateWarning
v-if="shouldShowDuplicateWarningInActionRail"
:title="t('admin.pos.warning')"
@@ -299,14 +299,12 @@ const getSelectedBookingId = () => {
const completeStep2Order = async ({
bookingSafetySeal = null,
markOrderCompleted = false,
}: {
bookingSafetySeal?: string | null;
markOrderCompleted?: boolean;
} = {}) => {
await finalizeCurrentMobileOrder({
bookingSafetySeal,
markOrderCompleted,
markOrderCompleted: true,
});
popups.select("completed_transaction", {
message: `Order #${order_id.value} successfully created.`,
@@ -327,14 +325,12 @@ const step2 = async () => {
const resolvedSafetySeal = getResolvedMobileSafetySeal();
const hasResolvedSafetySeal = isNonEmptyString(resolvedSafetySeal);
const requiresBookingCompletionPopup = Boolean(selectedBookingId && hasWashCertificateInBasket);
const shouldMarkOrderAsCompleted = !selectedBookingId && hasWashCertificateInBasket;
try {
if (requiresBookingCompletionPopup && hasResolvedSafetySeal) {
syncMobileSafetySealState(resolvedSafetySeal);
await completeStep2Order({
bookingSafetySeal: resolvedSafetySeal,
markOrderCompleted: false,
});
return;
}
@@ -344,7 +340,6 @@ const step2 = async () => {
syncMobileSafetySealState(safetySeal);
await completeStep2Order({
bookingSafetySeal: String(safetySeal ?? ""),
markOrderCompleted: false,
});
};
@@ -358,9 +353,7 @@ const step2 = async () => {
return;
}
await completeStep2Order({
markOrderCompleted: shouldMarkOrderAsCompleted,
});
await completeStep2Order();
} catch (error: any) {
errors.value.push(error);
console.warn("An error occurred while completing the mobile order:", error);
@@ -3,6 +3,45 @@ export const XLVASK_USAGE_AMOUNT_CACHE_TTL_MS = 10 * 60 * 1000;
const CACHE_PREFIX = "xlvask-usage-amount:";
const memoryCache = new Map();
const getStorageValue = (key) => {
try {
if (typeof window === "undefined" || !window.localStorage) {
return "";
}
return window.localStorage.getItem(key) || "";
} catch {
return "";
}
};
const hashCacheScopePart = (value) => {
let hash = 5381;
for (let index = 0; index < value.length; index += 1) {
hash = ((hash << 5) + hash) ^ value.charCodeAt(index);
}
return (hash >>> 0).toString(36);
};
const getAuthenticatedCacheScope = () => {
const token = getStorageValue("token");
if (!token) {
return "";
}
return [
hashCacheScopePart(token),
getStorageValue("is_subuser") === "true" ? "subuser" : "user",
getStorageValue("selected_customer_number"),
]
.map((part) => encodeURIComponent(String(part ?? "")))
.join("|");
};
const getScopedCacheKey = (cacheKey) => {
const scope = getAuthenticatedCacheScope();
return scope ? `${scope}:${cacheKey}` : "";
};
const safeSessionStorage = () => {
try {
if (typeof window === "undefined" || !window.sessionStorage) {
@@ -15,9 +54,7 @@ const safeSessionStorage = () => {
};
const normalizeUsageLogId = (objectOrId) => {
const value = typeof objectOrId === "object"
? objectOrId?.usage_log_id ?? objectOrId?.id
: objectOrId;
const value = typeof objectOrId === "object" ? objectOrId?.usage_log_id ?? objectOrId?.id : objectOrId;
const parsed = Number.parseInt(String(value ?? ""), 10);
return Number.isInteger(parsed) && parsed > 0 ? parsed : 0;
};
@@ -34,17 +71,18 @@ export const buildXlvaskUsageAmountCacheKey = (objectOrId) => {
export const getCachedXlvaskUsageAmount = (objectOrId) => {
const cacheKey = buildXlvaskUsageAmountCacheKey(objectOrId);
if (!cacheKey) {
const scopedCacheKey = cacheKey ? getScopedCacheKey(cacheKey) : "";
if (!scopedCacheKey) {
return null;
}
const memoryEntry = memoryCache.get(cacheKey);
const memoryEntry = memoryCache.get(scopedCacheKey);
if (isFreshEntry(memoryEntry)) {
return memoryEntry.data;
}
if (memoryEntry) {
memoryCache.delete(cacheKey);
memoryCache.delete(scopedCacheKey);
}
const storage = safeSessionStorage();
@@ -52,14 +90,14 @@ export const getCachedXlvaskUsageAmount = (objectOrId) => {
return null;
}
const storageKey = `${CACHE_PREFIX}${cacheKey}`;
const storageKey = `${CACHE_PREFIX}${scopedCacheKey}`;
try {
const parsed = JSON.parse(storage.getItem(storageKey) || "null");
if (!isFreshEntry(parsed)) {
storage.removeItem(storageKey);
return null;
}
memoryCache.set(cacheKey, parsed);
memoryCache.set(scopedCacheKey, parsed);
return parsed.data;
} catch {
storage.removeItem(storageKey);
@@ -69,7 +107,8 @@ export const getCachedXlvaskUsageAmount = (objectOrId) => {
export const setCachedXlvaskUsageAmount = (objectOrId, data) => {
const cacheKey = buildXlvaskUsageAmountCacheKey(objectOrId);
if (!cacheKey) {
const scopedCacheKey = cacheKey ? getScopedCacheKey(cacheKey) : "";
if (!scopedCacheKey) {
return;
}
@@ -77,7 +116,7 @@ export const setCachedXlvaskUsageAmount = (objectOrId, data) => {
storedAt: Date.now(),
data,
};
memoryCache.set(cacheKey, entry);
memoryCache.set(scopedCacheKey, entry);
const storage = safeSessionStorage();
if (!storage) {
@@ -85,7 +124,7 @@ export const setCachedXlvaskUsageAmount = (objectOrId, data) => {
}
try {
storage.setItem(`${CACHE_PREFIX}${cacheKey}`, JSON.stringify(entry));
storage.setItem(`${CACHE_PREFIX}${scopedCacheKey}`, JSON.stringify(entry));
} catch {
// Best-effort cache. Quota errors should not block XL Vask usage rows.
}
@@ -94,11 +133,12 @@ export const setCachedXlvaskUsageAmount = (objectOrId, data) => {
export const clearCachedXlvaskUsageAmount = (objectOrId = null) => {
const storage = safeSessionStorage();
const cacheKey = objectOrId === null ? "" : buildXlvaskUsageAmountCacheKey(objectOrId);
const scopedCacheKey = cacheKey ? getScopedCacheKey(cacheKey) : "";
if (cacheKey) {
memoryCache.delete(cacheKey);
if (scopedCacheKey) {
memoryCache.delete(scopedCacheKey);
try {
storage?.removeItem(`${CACHE_PREFIX}${cacheKey}`);
storage?.removeItem(`${CACHE_PREFIX}${scopedCacheKey}`);
} catch {
// Best-effort cache cleanup.
}
@@ -0,0 +1,100 @@
<script setup lang="ts">
import { ref, watch } from "vue";
import { BSkeleton } from "buefy";
const props = defineProps<{
src: string | null;
alt?: string;
}>();
const emit = defineEmits<{
(e: "error"): void;
(e: "load"): void;
}>();
const isLoading = ref(false);
watch(
() => props.src,
(src) => {
isLoading.value = !!src;
},
{ immediate: true }
);
const onLoad = () => {
isLoading.value = false;
emit("load");
};
const onError = () => {
isLoading.value = false;
emit("error");
};
</script>
<template>
<div
v-if="src"
class="self-serve-dynamic-image-frame mb-4"
:class="{ 'is-loading': isLoading }"
data-testid="self-serve-dynamic-image-frame"
>
<b-skeleton
v-if="isLoading"
class="self-serve-dynamic-image-skeleton"
width="100%"
height="100%"
data-testid="self-serve-dynamic-image-skeleton"
/>
<img
:key="src"
:src="src"
:alt="alt || 'Machine status'"
data-testid="self-serve-dynamic-image"
class="self-serve-dynamic-image"
:class="{ 'is-loading': isLoading }"
@load="onLoad"
@error="onError"
/>
</div>
</template>
<style scoped>
.self-serve-dynamic-image-frame {
position: relative;
width: 100%;
max-width: 100%;
aspect-ratio: 16 / 9;
margin-left: auto;
margin-right: auto;
overflow: hidden;
}
@media screen and (min-width: 769px) {
.self-serve-dynamic-image-frame {
max-width: 640px;
}
}
.self-serve-dynamic-image-skeleton {
position: absolute;
inset: 0;
overflow: hidden;
border-radius: 4px;
}
.self-serve-dynamic-image {
width: 100%;
height: 100%;
border-radius: 4px;
display: block;
object-fit: contain;
transition: opacity 120ms ease;
}
.self-serve-dynamic-image.is-loading {
opacity: 0;
}
</style>
@@ -1,7 +1,8 @@
<script setup lang="ts">
import { computed } from "vue";
import { BIcon, BMessage, BRadioButton } from "buefy";
defineProps<{
const props = defineProps<{
lanes: Array<any>;
selectedLaneId: number | string | null;
washType: string;
@@ -14,6 +15,8 @@ const emit = defineEmits<{
(e: "update:selectedLaneId", value: number): void;
(e: "update:washType", value: string): void;
}>();
const isSelectedMachineAvailable = computed(() => props.isMachineAvailable(props.selectedLaneId));
</script>
<template>
@@ -92,16 +95,16 @@ const emit = defineEmits<{
:model-value="washType"
native-value="Machine"
type="is-link"
:disabled="!isMachineAvailable(selectedLaneId)"
:title="!isMachineAvailable(selectedLaneId) ? $t('self_wash.machine_unavailable_for_lane') : null"
:aria-label="!isMachineAvailable(selectedLaneId) ? $t('self_wash.machine_unavailable_for_lane') : null"
:disabled="!isSelectedMachineAvailable"
:title="!isSelectedMachineAvailable ? $t('self_wash.unavailable') : null"
:aria-label="!isSelectedMachineAvailable ? $t('self_wash.unavailable') : null"
data-testid="self-serve-wash-type-machine"
@update:model-value="emit('update:washType', 'Machine')"
@input="emit('update:washType', 'Machine')"
>
<span>
<span>{{ $t("self_wash.machine") }}<br /></span>
<span v-if="!isMachineAvailable(selectedLaneId)">
<span v-if="!isSelectedMachineAvailable">
<small>
<b-icon icon="times-circle" type="is-danger" pack="fas" class="mr-1" />
{{ $t("self_wash.unavailable") }}
@@ -117,15 +120,6 @@ const emit = defineEmits<{
</b-radio-button>
</div>
</div>
<b-message
v-if="selectedLaneId && !isMachineAvailable(selectedLaneId)"
type="is-warning"
has-icon
:closable="false"
data-testid="self-serve-machine-unavailable-guidance"
>
{{ $t("self_wash.machine_unavailable_for_lane") }}
</b-message>
</section>
</div>
</template>
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { ref, watch } from "vue";
import { BIcon, BSkeleton } from "buefy";
import { BIcon } from "buefy";
import SelfServeDynamicImageFrame from "@/components/displays/selfServe/SelfServeDynamicImageFrame.vue";
import SelfServeTaskList from "@/components/displays/selfServe/SelfServeTaskList.vue";
const props = defineProps<{
@@ -18,12 +18,6 @@ const emit = defineEmits<{
(e: "clear-dynamic-image"): void;
}>();
const isDynamicImageLoading = ref(false);
watch(() => props.dynamicImageUrl, (dynamicImageUrl) => {
isDynamicImageLoading.value = !!dynamicImageUrl;
}, { immediate: true });
const emitToggleTask = (taskId: number, value: boolean) => {
emit("toggle-task", taskId, value);
};
@@ -32,12 +26,7 @@ const emitDownloadAttachment = (taskId: number, attachmentId: number | undefined
emit("download-attachment", taskId, attachmentId, attachment);
};
const onDynamicImageLoad = () => {
isDynamicImageLoading.value = false;
};
const onDynamicImageError = () => {
isDynamicImageLoading.value = false;
emit("clear-dynamic-image");
};
</script>
@@ -58,30 +47,11 @@ const onDynamicImageError = () => {
<span class="ml-2">{{ $t("self_wash.loading_data") }}</span>
</div>
<div
v-if="dynamicImageUrl"
class="self-serve-dynamic-image-frame mb-4"
:class="{ 'is-loading': isDynamicImageLoading }"
data-testid="self-serve-dynamic-image-frame"
>
<b-skeleton
v-if="isDynamicImageLoading"
class="self-serve-dynamic-image-skeleton"
width="100%"
height="100%"
data-testid="self-serve-dynamic-image-skeleton"
/>
<img
:key="dynamicImageUrl"
:src="dynamicImageUrl"
alt="Machine status"
data-testid="self-serve-dynamic-image"
class="self-serve-dynamic-image"
:class="{ 'is-loading': isDynamicImageLoading }"
@load="onDynamicImageLoad"
@error="onDynamicImageError"
/>
</div>
<SelfServeDynamicImageFrame
:src="dynamicImageUrl"
alt="Machine status"
@error="onDynamicImageError"
/>
<div v-show="allVisibleQuestionsAnswered && !editAnswers" class="notification is-info is-light mb-4">
<h1 class="title has-text-centered mb-2" v-if="activeTasks.length > 0">{{ $t("self_wash.start_machine") }}</h1>
<h1 class="title has-text-centered mb-2" v-else>{{ $t("self_wash.questions_answered") }}</h1>
@@ -95,41 +65,3 @@ const onDynamicImageError = () => {
</div>
</div>
</template>
<style scoped>
.self-serve-dynamic-image-frame {
position: relative;
width: 100%;
max-width: 100%;
aspect-ratio: 16 / 9;
margin-left: auto;
margin-right: auto;
overflow: hidden;
}
@media screen and (min-width: 769px) {
.self-serve-dynamic-image-frame {
max-width: 640px;
}
}
.self-serve-dynamic-image-skeleton {
position: absolute;
inset: 0;
overflow: hidden;
border-radius: 4px;
}
.self-serve-dynamic-image {
width: 100%;
height: 100%;
border-radius: 4px;
display: block;
object-fit: contain;
transition: opacity 120ms ease;
}
.self-serve-dynamic-image.is-loading {
opacity: 0;
}
</style>
@@ -2,6 +2,8 @@
import Swal from "sweetalert2";
import { usePaginatedListInstance } from "@/components/pagination/paginatedList.vue";
import { SessionUser } from "@/components/session/token/SessionUser.vue";
import ActionSettingsWheelButton from "@/components/displays/buttons/ActionSettingsWheelButton.vue";
import ActionSettingsWheelItem from "@/components/displays/buttons/ActionSettingsWheelItem.vue";
const props = defineProps({
objects: {
@@ -23,6 +25,8 @@ const canDisableAccess = () =>
const canResendInvite = (subuser) =>
(props.showCustomer ? SessionUser.canAccessSuperUser() : SessionUser.canAccessUser() || SessionUser.hasPermission("SUBUSERS_EDIT"))
&& Boolean(subuser?.can_resend_invite ?? subuser?.setup_required);
const hasRowActions = (subuser) =>
(canEditPermissions() && subuser?.grant_id) || canResendInvite(subuser) || (canDisableAccess() && subuser?.grant_id);
const formatDateTime = (dateString) => {
if (!dateString) {
@@ -241,14 +245,6 @@ const onResendInvite = async (subuser) => {
<td>
<div>{{ subuser.grant_note || "-" }}</div>
<button
v-if="canEditPermissions() && subuser.grant_id"
class="button is-text is-small px-0 mt-1"
type="button"
@click="onEditNote(subuser)"
>
Redigér note
</button>
</td>
<td>{{ formatDateTime(subuser.created_at) }}</td>
@@ -256,36 +252,42 @@ const onResendInvite = async (subuser) => {
<td>
<div class="buttons is-justify-content-flex-end action-buttons">
<button
v-if="canEditPermissions() && subuser.grant_id"
class="button is-small"
type="button"
:data-testid="`subuser-permissions-${subuser.id}`"
@click="onEditPermissions(subuser)"
>
Tilladelser
</button>
<ActionSettingsWheelButton v-if="hasRowActions(subuser)" display-actions-directly>
<template #actions>
<ActionSettingsWheelItem
v-if="canEditPermissions() && subuser.grant_id"
icon="fas fa-pen"
label="Redigér note"
:click-action="() => onEditNote(subuser)"
:test-id="`subuser-note-${subuser.id}`"
/>
<button
v-if="canResendInvite(subuser)"
class="button is-small"
type="button"
:data-testid="`subuser-resend-${subuser.id}`"
@click="onResendInvite(subuser)"
>
Gensend
</button>
<ActionSettingsWheelItem
v-if="canEditPermissions() && subuser.grant_id"
icon="fas fa-user-shield"
label="Tilladelser"
:click-action="() => onEditPermissions(subuser)"
:test-id="`subuser-permissions-${subuser.id}`"
/>
<button
v-if="canDisableAccess() && subuser.grant_id"
class="button is-small"
:class="subuser.grant_enabled ? 'is-danger is-light' : 'is-success is-light'"
type="button"
:data-testid="`subuser-toggle-${subuser.id}`"
@click="onToggleEnabled(subuser, !subuser.grant_enabled)"
>
{{ subuser.grant_enabled ? "Deaktivér" : "Aktivér" }}
</button>
<ActionSettingsWheelItem
v-if="canResendInvite(subuser)"
icon="fas fa-paper-plane"
label="Gensend"
:click-action="() => onResendInvite(subuser)"
:test-id="`subuser-resend-${subuser.id}`"
/>
<ActionSettingsWheelItem
v-if="canDisableAccess() && subuser.grant_id"
:icon="subuser.grant_enabled ? 'fas fa-ban' : 'fas fa-check'"
:label="subuser.grant_enabled ? 'Deaktivér' : 'Aktivér'"
:template="subuser.grant_enabled ? 'danger' : 'success'"
:click-action="() => onToggleEnabled(subuser, !subuser.grant_enabled)"
:test-id="`subuser-toggle-${subuser.id}`"
/>
</template>
</ActionSettingsWheelButton>
</div>
</td>
</tr>
@@ -10,6 +10,7 @@ import LoadButtonWhileAwait from "@/components/request/LoadButtonWhileAwait.vue"
import { SessionUser } from "@/components/session/token/SessionUser.vue";
import TwoFactorVerify from "@/components/forms/auth/TwoFactorVerify.vue";
import { isPasskeySupported, authenticateWithPasskey } from "@/services/PasskeyAuthService.js";
import { clearEdgeGatewayWorkspaceCache } from "@/services/edgeGateways.js";
const employees = ref([]);
const { t } = useI18n();
@@ -52,6 +53,7 @@ const login = async () => {
}
// Save the token in the local storage
clearEdgeGatewayWorkspaceCache();
localStorage.setItem('token', data.token);
// Redirect to the dashboard
window.location.href = '/admin';
@@ -72,6 +74,7 @@ const loginWithPasskey = async () => {
const result = await authenticateWithPasskey('employee', null, recaptchaToken);
if (result.token) {
clearEdgeGatewayWorkspaceCache();
localStorage.setItem('token', result.token);
window.location.href = '/admin';
return;
+3
View File
@@ -9,6 +9,7 @@ import { parseError, clearErrors, addError, getError } from "@/components/reques
import LoadButtonWhileAwait from "@/components/request/LoadButtonWhileAwait.vue";
import { isPasskeySupported, authenticateWithPasskey } from "@/services/PasskeyAuthService.js";
import TwoFactorVerify from "@/components/forms/auth/TwoFactorVerify.vue";
import { clearEdgeGatewayWorkspaceCache } from "@/services/edgeGateways.js";
const { t } = useI18n();
@@ -68,6 +69,7 @@ const login = async () => {
}
// Save the token in the local storage
clearEdgeGatewayWorkspaceCache();
localStorage.setItem('token', data.token);
// Set the success message
successMessage.value = "Du er nu logget ind!"
@@ -103,6 +105,7 @@ const loginWithPasskey = async () => {
const result = await authenticateWithPasskey('user', customerNum, recaptchaToken);
console.log('Passkey authentication result:', result);
if (result.token) {
clearEdgeGatewayWorkspaceCache();
localStorage.setItem('token', result.token);
successMessage.value = "Du er nu logget ind!";
setTimeout(() => {
@@ -9,6 +9,7 @@ import { SessionUser } from "@/components/session/token/SessionUser.vue";
import { isPasskeySupported, authenticateWithPasskey } from "@/services/PasskeyAuthService.js";
import { getSubuserPasswordPolicyError } from "@/services/subuserPasswordPolicy.js";
import TwoFactorVerify from "@/components/forms/auth/TwoFactorVerify.vue";
import { clearEdgeGatewayWorkspaceCache } from "@/services/edgeGateways.js";
const { t } = useI18n();
@@ -69,6 +70,7 @@ const login = async () => {
// Save the session token
if (data.session) {
clearEdgeGatewayWorkspaceCache();
localStorage.setItem('token', data.session);
localStorage.setItem('is_subuser', 'true');
window.location.reload();
@@ -97,6 +99,7 @@ const loginWithPasskey = async () => {
// Subuser login returns 'session' token, user login returns 'token'
const sessionToken = result.session || result.token;
if (sessionToken) {
clearEdgeGatewayWorkspaceCache();
localStorage.setItem('token', sessionToken);
localStorage.setItem('is_subuser', 'true');
// Reload the page to update the UI
@@ -5,6 +5,7 @@ import { useI18n } from 'vue-i18n';
import { verify2FA } from "@/services/TwoFactorAuthService.js";
import { parseError, getError, clearErrors } from "@/components/request/HandleGlobalError.vue";
import LoadButtonWhileAwait from "@/components/request/LoadButtonWhileAwait.vue";
import { clearEdgeGatewayWorkspaceCache } from "@/services/edgeGateways.js";
const { t } = useI18n();
const router = useRouter();
@@ -43,9 +44,11 @@ const verify = async () => {
// Handle the response based on user type
if (props.userType === 'subuser' && result.session) {
clearEdgeGatewayWorkspaceCache();
localStorage.setItem('token', result.session);
localStorage.setItem('is_subuser', 'true');
} else if (result.token) {
clearEdgeGatewayWorkspaceCache();
localStorage.setItem('token', result.token);
localStorage.removeItem('is_subuser');
}
@@ -217,6 +217,44 @@ const getVehicleReferenceValue = () => {
return String(vehicleObject.value?.reference ?? "").trim();
};
const getVehicleStateKey = (vehicle) => {
if (!vehicle) {
return "";
}
return [
vehicle?.id ?? "",
normalizePlateValue(vehicle?.reg),
normalizeCustomerNumber(vehicle?.customer_id) ?? "",
vehicle?.type ?? "",
vehicle?.booking_id ?? "",
String(vehicle?.reference ?? "").trim(),
].join("|");
};
const getBookingStateKey = (booking) => {
if (!booking) {
return "";
}
return [
booking?.id ?? "",
getBookingReg1Value(booking),
getBookingReg2Value(booking),
getBookingCustomerNumber(booking) ?? "",
String(booking?.reference_number ?? booking?.reference ?? "").trim(),
String(booking?.po ?? "").trim(),
].join("|");
};
const getBookingMatchesStateKey = (matches) => {
if (!Array.isArray(matches) || matches.length === 0) {
return "";
}
return matches.map((booking) => getBookingStateKey(booking)).join("||");
};
const setVehicleObject = (emittedVehicleObject, options = {}) => {
const normalizedOptions = {
preserveManualReference: true,
@@ -224,17 +262,26 @@ const setVehicleObject = (emittedVehicleObject, options = {}) => {
...options,
};
vehicleObject.value = emittedVehicleObject;
const emittedVehiclePlate = normalizePlateValue(vehicleObject.value?.reg || reg_1.value);
const emittedVehiclePlate = normalizePlateValue(emittedVehicleObject?.reg || reg_1.value);
if (
emittedVehiclePlate &&
skippedDesktopBookingVehiclePlate.value === emittedVehiclePlate &&
!isBookingMarkedVehicle(vehicleObject.value)
!isBookingMarkedVehicle(emittedVehicleObject)
) {
skippedDesktopBookingVehiclePlate.value = "";
}
const currentVehicleKey = getVehicleStateKey(vehicleObject.value);
const nextVehicleKey = getVehicleStateKey(emittedVehicleObject);
if (currentVehicleKey === nextVehicleKey) {
if (normalizedOptions.nextSelectionSource) {
setSelectionSource(normalizedOptions.nextSelectionSource);
}
return;
}
vehicleObject.value = emittedVehicleObject;
const matchedVehicleCustomerNumber = normalizeCustomerNumber(vehicleObject.value?.customer_id);
const selectedCustomerNumber = normalizeCustomerNumber(customer_id.value);
@@ -274,6 +321,10 @@ const setBookingObject = (emittedBookingObject, options = {}) => {
...options,
};
if (getBookingStateKey(bookingObject.value) === getBookingStateKey(emittedBookingObject)) {
return;
}
bookingObject.value = emittedBookingObject;
if (emittedBookingObject && emittedBookingObject.reference_number) {
@@ -284,7 +335,12 @@ const setBookingObject = (emittedBookingObject, options = {}) => {
};
const setBookingMatches = (emittedBookingMatches) => {
bookingMatches.value = Array.isArray(emittedBookingMatches) ? emittedBookingMatches : [];
const nextBookingMatches = Array.isArray(emittedBookingMatches) ? emittedBookingMatches : [];
if (getBookingMatchesStateKey(bookingMatches.value) === getBookingMatchesStateKey(nextBookingMatches)) {
return;
}
bookingMatches.value = nextBookingMatches;
};
const mergeBookingMatchDetails = (booking) => {
@@ -416,6 +416,21 @@ const shouldPreserveCustomerSelection = (plateValue) => {
};
let pendingVehicleCustomerLookup = null;
const lastAutoSyncedVehicleKey = ref("");
const getVehicleAutoSyncKey = (vehicle, plateOverride = null) => {
if (!vehicle) {
return "";
}
return [
resolveBookingPlate(vehicle, plateOverride),
vehicle?.id ?? "",
normalizeCustomerNumber(vehicle?.customer_id) ?? "",
normalizeCustomerNumber(customer_id.value) ?? "",
].join("|");
};
const syncMatchedVehicleSelection = (vehicle, options = {}) => {
const normalizedOptions = {
plateOverride: null,
@@ -590,6 +605,7 @@ watch(reg_1, (newValue) => {
// Define the search ID for this input change
const search_id = register_new_search();
console.log("reg_1 changed:", newValue);
lastAutoSyncedVehicleKey.value = "";
const shouldKeepCustomerSelection = shouldPreserveCustomerSelection(newValue);
selectedDropdownItem.value = -1;
// Check if the new value is empty, if so, clear the vehicles_matching array
@@ -847,11 +863,17 @@ watch(vehicles_matching, (newValue) => {
const currentValue = reg_1.value;
const vehicle = newValue.find((vehicle) => vehicle.reg === currentValue);
if (vehicle) {
syncMatchedVehicleSelection(vehicle);
const nextAutoSyncKey = getVehicleAutoSyncKey(vehicle, currentValue);
if (lastAutoSyncedVehicleKey.value !== nextAutoSyncKey) {
lastAutoSyncedVehicleKey.value = nextAutoSyncKey;
syncMatchedVehicleSelection(vehicle);
}
} else if (getBookingMatchesForSelection(null, currentValue).length > 0) {
lastAutoSyncedVehicleKey.value = "";
clearCustomerConflict();
emitVehicleObject(null, currentValue);
} else {
lastAutoSyncedVehicleKey.value = "";
clearCustomerConflict();
}
// Check if the selectedDropdownItem index is valid
@@ -52,6 +52,7 @@ const customer_suggestions = ref([
const isSettingCustomer = ref(false);
const isSettingCustomerToInteger = ref(0);
const isSettingCustomerStartTime = ref(null);
let customerSuggestionsRequestId = 0;
const isSettingCustomerTo = (customer_number) => {
return isSettingCustomerToInteger.value === parseInt(customer_number);
@@ -88,10 +89,17 @@ const getCustomerSuggestions = () => {
if (!props.reg_1) {
return;
}
const requestedReg1 = props.reg_1;
const requestId = ++customerSuggestionsRequestId;
SessionUser.request("/department/vehicle/customer-suggestions", "GET", {
reg_1: props.reg_1,
reg_1: requestedReg1,
})
.then((response) => {
if (requestId !== customerSuggestionsRequestId || requestedReg1 !== props.reg_1) {
return;
}
// Assuming the response contains an array of customer suggestions
console.log("Customer suggestions:", response.data.data);
let suggestions = [];
@@ -126,6 +134,7 @@ watch(
if (newValue) {
getCustomerSuggestions();
} else {
customerSuggestionsRequestId += 1;
customer_suggestions.value = [];
}
}
+17 -6
View File
@@ -118,6 +118,7 @@ const impersonatedUserRoleId = computed(() => {
const canGrantMissingPermissions = computed(() =>
hasSuperuserToken.value && !SessionUser.isSubuser.value && impersonatedUserRoleId.value !== null
);
const canInspectReleaseRuntime = computed(() => hasSuperuserToken.value || SessionUser.canAccessAdmin?.() === true);
const userDetailRows = computed(() => {
if (SessionUser.isSubuser.value) {
@@ -284,7 +285,12 @@ const resolvePingUrl = () => {
};
const activeApiUrl = computed(() => getReleaseRuntimeApiBaseUrl());
const releaseSessionSummary = computed(() => buildReleaseSessionSummary());
const activeApiUrlLabel = computed(() =>
canInspectReleaseRuntime.value ? activeApiUrl.value : "Restricted to release operators"
);
const releaseSessionSummary = computed(() =>
buildReleaseSessionSummary(undefined, { includeInfrastructureDetails: canInspectReleaseRuntime.value })
);
const measurePingLatency = async () => {
if (typeof fetch !== "function") {
@@ -646,9 +652,10 @@ onBeforeUnmount(() => {
</aside>
<aside class="request-queue-progress__side request-queue-progress__side--runtime" data-testid="request-queue-runtime-box">
<div class="request-queue-progress__section-title">Session release</div>
<div class="request-queue-progress__section-content request-queue-progress__section-content--release">
<ul class="request-queue-progress__meta-list">
<template v-if="canInspectReleaseRuntime">
<div class="request-queue-progress__section-title">Session release</div>
<div class="request-queue-progress__section-content request-queue-progress__section-content--release">
<ul class="request-queue-progress__meta-list">
<li class="request-queue-progress__meta-item">
<span class="request-queue-progress__meta-label">Channel</span>
<span
@@ -771,11 +778,15 @@ onBeforeUnmount(() => {
</li>
</ul>
<div class="request-queue-progress__subsection-title">Runtime details</div>
</div>
</template>
<div class="request-queue-progress__section-title">Runtime details</div>
<div class="request-queue-progress__section-content request-queue-progress__section-content--release">
<ul class="request-queue-progress__meta-list">
<li class="request-queue-progress__meta-item">
<span class="request-queue-progress__meta-label">API URL</span>
<span class="request-queue-progress__meta-value" :title="activeApiUrl">{{ activeApiUrl }}</span>
<span class="request-queue-progress__meta-value" :title="activeApiUrlLabel">{{ activeApiUrlLabel }}</span>
</li>
<li class="request-queue-progress__meta-item">
<span class="request-queue-progress__meta-label">Current host</span>
+8 -5
View File
@@ -48,10 +48,13 @@ export const getDepartmentDescription = (id) => {
export const getDepartmentsGuest = async (queryParams = {}) => {
isLoading.value = true;
const queryString = new URLSearchParams(queryParams).toString();
const request = await unauthenticatedRequest("/guest/departments" + (queryString ? `?${queryString}` : ""), "get");
departments.value = sortByDepartmentPriorityOrder(request.data.data || []);
isLoading.value = false;
return departments.value;
try {
const queryString = new URLSearchParams(queryParams).toString();
const request = await unauthenticatedRequest("/guest/departments" + (queryString ? `?${queryString}` : ""), "get");
departments.value = sortByDepartmentPriorityOrder(request.data.data || []);
return departments.value;
} finally {
isLoading.value = false;
}
};
</script>
+2
View File
@@ -2,6 +2,7 @@
import { ref } from 'vue'
import axios from 'axios'
import {API_URL} from "@/config.js";
import { clearEdgeGatewayWorkspaceCache } from "@/services/edgeGateways.js";
export const currentDepartment = ref(null);
export const selectDepartment = (department) => {
@@ -31,6 +32,7 @@ export const exitSession = () => {
}
});
clearEdgeGatewayWorkspaceCache();
localStorage.removeItem('token');
return result;
};
@@ -2,6 +2,7 @@
import axios from 'axios'
import { enqueueRequest } from "@/services/requestQueue.js";
import { buildCurrentReleaseHeaders, resolveReleaseApiUrl } from "@/services/releaseTimeline.js";
import { isTrustedReleaseUrl } from "@/services/releaseTrust.js";
/**
* Get the selected customer number for X-Customer-Number header (used by subusers)
@@ -18,22 +19,24 @@ export const authenticatedRequest = (url, method, data, catchCallable = null, th
//throw new Error('No token was found, unable to make authenticated request');
}
const requestUrl = resolveReleaseApiUrl(url);
const canSendCredentials = isTrustedReleaseUrl(requestUrl);
// Build headers
const headers = {
...buildCurrentReleaseHeaders(),
};
if (token && token.length > 0) {
if (canSendCredentials && token && token.length > 0) {
headers.Authorization = `Bearer ${token}`;
}
// Add X-Customer-Number header if subuser has selected a grant
const isSubuser = localStorage.getItem('is_subuser') === 'true';
const selectedCustomerNumber = getSelectedCustomerNumber();
if (isSubuser && selectedCustomerNumber) {
if (canSendCredentials && isSubuser && selectedCustomerNumber) {
headers['X-Customer-Number'] = selectedCustomerNumber;
}
const requestUrl = resolveReleaseApiUrl(url);
return enqueueRequest(
() => axios({
method,
+21 -1
View File
@@ -58,6 +58,12 @@ import {
releaseChannelRuntimeRequestParams,
setReleaseChannelSwitchNoticePrincipal,
} from "@/services/releaseChannelAvailability.js";
import { clearCachedXlvaskUsageAmount } from "@/components/displays/department/pos/sync/xlvaskUsageAmountCache.js";
import { clearEdgeGatewayWorkspaceCache } from "@/services/edgeGateways.js";
import {
clearPeriodCache,
clearSelfWashCountsCache,
} from "@/views/dashboards/superUserDashboard/InvoicingBillingPeriod/imports/InvoicingBillingPeriodImportPaging.js";
const normalizePositiveInteger = (value) => {
const parsedValue = Number.parseInt(String(value ?? ""), 10);
@@ -98,7 +104,16 @@ const hydrateSessionFromStorage = () => {
return true;
};
const clearPrivilegedSessionCaches = () => {
clearPeriodCache();
clearSelfWashCountsCache();
clearCachedXlvaskUsageAmount();
clearEdgeGatewayWorkspaceCache();
};
const clearStoredSession = () => {
clearPrivilegedSessionCaches();
if (typeof window === "undefined") {
return;
}
@@ -115,7 +130,8 @@ const applyReleaseRuntimeConfig = (runtime) => {
const normalizedRuntime = runtime || {};
configureReleaseRuntime(normalizedRuntime);
reconcileSelectedReleaseChannel(normalizedRuntime);
const runtimeUrls = normalizedRuntime.urls && typeof normalizedRuntime.urls === "object" ? normalizedRuntime.urls : {};
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 =
@@ -377,6 +393,7 @@ export const authenticateUser = async (customer_number, password) => {
password,
})
.then((response) => {
clearPrivilegedSessionCaches();
SessionUser.token.value = response.data.token;
SessionUser.authenticated.value = true;
localStorage.setItem("token", response.data.token);
@@ -423,6 +440,7 @@ export const authenticateSubuser = async (credentials) => {
return await authenticatedRequest("/subusers/auth/password", "POST", requestBody)
.then((response) => {
clearPrivilegedSessionCaches();
SessionUser.token.value = response.data.data.session;
SessionUser.authenticated.value = true;
SessionUser.isSubuser.value = true;
@@ -669,6 +687,7 @@ export const SessionUser = {
* @param {number|null} customerNumber - The billing_customer_number to select, or null to clear
*/
selectGrant: (customerNumber) => {
clearEdgeGatewayWorkspaceCache();
if (customerNumber === null) {
localStorage.removeItem("selected_customer_number");
SessionUser.subuser.selectedGrantCustomerNumber.value = null;
@@ -886,6 +905,7 @@ export const SessionUser = {
* @param {string} token
*/
setToken: (token) => {
clearPrivilegedSessionCaches();
SessionUser.token.value = token;
localStorage.setItem("token", token);
// Reload the window to make sure the user's session is initiated
@@ -7,6 +7,13 @@ import { getSystemUserIds } from "@/components/session/token/SessionUser/Objects
// Helper function to get i18n translation
const t = (key) => i18n.global.t(key);
const escapeHtml = (value = "") => String(value)
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll("\"", "&quot;")
.replaceAll("'", "&#39;");
const normalizeDateTimeLocalValue = (value) => {
if (value === null || value === undefined || value === '') {
return '';
@@ -765,9 +772,11 @@ export const ObjectsGlobal = {
console.log(response);
// How long are the list of options
for (let i = 0; i < response.length; i++) {
var tmp = response[i];
const tmp = response[i];
const optionValue = tmp?.id ?? "";
const optionLabel = tmp?.name ? tmp.name : optionValue;
console.log(tmp);
html += `<option value="${tmp.id}" ${(value == tmp.id || (value === null && tmp.id === 0)) ? 'selected' : ''}>${tmp.name ? tmp.name : tmp.id}</option>`;
html += `<option value="${escapeHtml(optionValue)}" ${(value == optionValue || (value === null && optionValue === 0)) ? 'selected' : ''}>${escapeHtml(optionLabel)}</option>`;
}
});
html += `</select>
@@ -356,6 +356,20 @@ export const OrderBookings = {
{ safety_seal: normalizeCompletionSafetySeal(safetySeal) },
onAfterComplete
);
},
resendBookingConfirmation: async (id) => {
return SessionUser.request(
OrderBookings.meta.endpoint + "/booking-confirmation/resend",
"POST",
{ id: parseInt(id, 10) }
).then((response) => response.data.data);
},
resendBookingCompletionConfirmation: async (id) => {
return SessionUser.request(
OrderBookings.meta.endpoint + "/completion-confirmation/resend",
"POST",
{ id: parseInt(id, 10) }
).then((response) => response.data.data);
}
},
/**
@@ -19,6 +19,14 @@ const getProtectedDeletePayload = (error) => {
return payload;
};
const escapeHtml = (value) => String(value ?? "").replace(/[&<>"']/g, (character) => ({
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
"\"": "&quot;",
"'": "&#39;",
})[character]);
const formatDeleteProtectionReason = (reason) => {
switch (reason) {
case "completed":
@@ -28,22 +36,23 @@ const formatDeleteProtectionReason = (reason) => {
case "attachments":
return "ordren har vedhæftninger";
default:
return String(reason || "").replace(/_/g, " ");
return escapeHtml(String(reason || "").replace(/_/g, " "));
}
};
const showProtectedOrderDeleteConfirmation = async (id, protectionPayload, onAfterSubmit = null) => {
const orderId = String(id);
const escapedOrderId = escapeHtml(orderId);
const reasons = Array.isArray(protectionPayload?.protected_reasons)
? protectionPayload.protected_reasons.map(formatDeleteProtectionReason).filter(Boolean)
: [];
const reasonText = reasons.length > 0 ? reasons.join(", ") : "ordren indeholder gemte data";
const result = await Swal.fire({
title: `Bekræft sletning af ordre #${orderId}`,
titleText: `Bekræft sletning af ordre #${orderId}`,
html: `
<p>Ordren kan stadig slettes, men kræver ekstra bekræftelse fordi ${reasonText}.</p>
<p>Skriv <strong>${orderId}</strong> for at slette ordren.</p>
<p>Skriv <strong>${escapedOrderId}</strong> for at slette ordren.</p>
`,
input: "text",
inputAttributes: {
@@ -924,6 +933,13 @@ const assignDraftOrderCustomer = async ({
console.error(error);
});
},
resendWashCertificate: async (id) => {
return SessionUser.request(
Orders.meta.endpoint + "/wash-certificate/resend",
"POST",
{ id: parseInt(id, 10) }
).then((response) => response.data.data);
},
showAttachWashCertificateForm(order_id, onAfterSubmit = null) {
const normalizedOrderId = Number.parseInt(order_id, 10);
if (!Number.isInteger(normalizedOrderId) || normalizedOrderId <= 0) {
@@ -127,6 +127,13 @@ export const SelfServeVehicleConditions = {
}
},
add: async (department, lane, customer_id, reg, question, value, options = {}) => {
const safeOptions = {};
const normalizedVehicleType = parseInt(options.vehicle_type ?? options.vehicle_type_id);
if (!Number.isNaN(normalizedVehicleType) && normalizedVehicleType > 0) {
safeOptions.vehicle_type = normalizedVehicleType;
}
return ObjectsGlobal.add.object(SelfServeVehicleConditions.meta.endpoint, {
department: parseInt(department),
lane: parseInt(lane),
@@ -134,7 +141,9 @@ export const SelfServeVehicleConditions = {
reg: reg,
question: parseInt(question),
value: value === "true" || value === true,
...options
...safeOptions,
activate_machine: false,
sync_relay_state: false
});
},
set: {
@@ -17,13 +17,15 @@ const normalizeEntries = (entries) => {
return [];
};
const SENSITIVE_WRITE_ONLY_KEYS = new Set(["broker_shared_secret"]);
const toConfigPayload = (entries) => {
const payload = {};
normalizeEntries(entries).forEach((entry) => {
if (!entry || !entry.variable) {
return;
}
payload[entry.variable] = entry.value;
payload[entry.variable] = SENSITIVE_WRITE_ONLY_KEYS.has(entry.variable) ? "" : entry.value;
});
return payload;
};
@@ -42,6 +44,11 @@ export const Config = {
test_broker: async (payload) => testEdgeGatewayBrokerConfig(payload),
set: async (variable, value) => {
const currentConfig = await Config.get_config();
SENSITIVE_WRITE_ONLY_KEYS.forEach((key) => {
if (key !== variable) {
delete currentConfig[key];
}
});
return Config.set_config({
...currentConfig,
[variable]: value,
@@ -73,7 +80,6 @@ export const Config = {
set: async (value) => Config.set("broker_auth_mode", value),
},
broker_shared_secret: {
get: async () => Config.get("broker_shared_secret"),
set: async (value) => Config.set("broker_shared_secret", value),
},
},
@@ -1,6 +1,7 @@
<script>
import { authenticatedRequest } from "@/components/session/authenticatedRequest.vue";
import { parseError } from "@/components/request/HandleGlobalError.vue";
import { clearEdgeGatewayWorkspaceCache } from "@/services/edgeGateways.js";
import { DatabaseSystemObject } from "@/components/session/token/superUser/systemDatabase.vue";
import { Cron } from "@/components/session/token/superUser/cron.vue";
import { Economic } from "@/components/session/token/superUser/modules/Economic/Economic.vue";
@@ -106,6 +107,7 @@ export const SuperUserObject = {
const currentToken = localStorage.getItem("token");
localStorage.setItem("superuser_token", currentToken);
// Set the token in the local storage
clearEdgeGatewayWorkspaceCache();
localStorage.setItem("token", response.data.data.token);
// Redirect the user to the dashboard
window.location = "/";
@@ -143,6 +145,7 @@ export const SuperUserObject = {
// Get the superuser token
const superuserToken = localStorage.getItem("superuser_token");
// Set the token in the local storage
clearEdgeGatewayWorkspaceCache();
localStorage.setItem("token", superuserToken);
// Clear the superuser token
localStorage.removeItem("superuser_token");
+68 -25
View File
@@ -320,20 +320,26 @@ export const ensureCurrentOrderDepartment = async (targetDepartmentId = null) =>
}
try {
let currentDepartmentId = null;
try {
currentDepartmentId = await getCurrentOrderDepartmentId(normalizedOrderId);
} catch (error) {
currentDepartmentId = null;
}
const currentDepartmentId = await getCurrentOrderDepartmentId(normalizedOrderId);
if (currentDepartmentId === normalizedDepartmentId) {
department_id.value = normalizedDepartmentId;
return true;
}
await SessionUser.objects.orders.set.department_id(normalizedOrderId, normalizedDepartmentId);
department_id.value = normalizedDepartmentId;
return true;
parseError(
{
response: {
data: {
data: {
message: "Ordren tilhører ikke den valgte afdeling",
},
message: "Ordren tilhører ikke den valgte afdeling",
},
},
},
"stepError"
);
return false;
} catch (error) {
parseError(error, "stepError");
return false;
@@ -727,9 +733,9 @@ export const restoreStoredPosOrderId = async (
if (selectedDepartmentId && toPositiveInteger(storedOrder?.department_id) !== selectedDepartmentId) {
if (options.syncDepartment === true) {
order_id.value = storedOrderId;
const didSyncDepartment = await ensureCurrentOrderDepartment(selectedDepartmentId);
if (!didSyncDepartment) {
throw new Error("Stored order department could not be changed to current department");
const isCurrentDepartment = await ensureCurrentOrderDepartment(selectedDepartmentId);
if (!isCurrentDepartment) {
throw new Error("Stored order department does not match current department");
}
} else {
throw new Error("Stored order department does not match current department");
@@ -1188,6 +1194,7 @@ export const selectScan = (scan) => {
};
const cached_customer_names = [];
const activeCustomerSelectionRequests = new Map();
/** Get the customer's name */
export const getCustomerName = async (customerNumber) => {
@@ -1216,23 +1223,62 @@ export const getCustomerName = async (customerNumber) => {
/** Search, then select customer by customer number */
export const searchAndSelectCustomer = async (customerNumber, options = {}) => {
const normalizedOptions = {
forceRefresh: false,
...options,
};
const normalizedCustomerNumber = resolveCustomerNumber(customerNumber);
if (!normalizedCustomerNumber) {
return null;
}
const selectedCustomerNumber = getSelectedCustomerNumber();
const hasSelectedCustomerData =
customer_data.value &&
typeof customer_data.value === "object" &&
!Array.isArray(customer_data.value) &&
Object.keys(customer_data.value).length > 0;
if (
!normalizedOptions.forceRefresh &&
selectedCustomerNumber === normalizedCustomerNumber &&
hasSelectedCustomerData
) {
return customer_data.value;
}
if (!normalizedOptions.forceRefresh && activeCustomerSelectionRequests.has(normalizedCustomerNumber)) {
return await activeCustomerSelectionRequests.get(normalizedCustomerNumber);
}
// Get the customer data
return await authenticatedRequest(`/users/customer?customer_number=${customerNumber}`, "GET")
const request = authenticatedRequest(`/users/customer?customer_number=${normalizedCustomerNumber}`, "GET")
.then((response) => {
console.log(response);
const responseData = response?.data?.data ?? {};
const rawEconomicCustomer = responseData.economic_customer ?? null;
const normalizedCustomer = normalizeCustomerRecord(rawEconomicCustomer, {
...responseData,
customerNumber: resolveCustomerNumber(rawEconomicCustomer) ?? resolveCustomerNumber(customerNumber),
customerNumber: resolveCustomerNumber(rawEconomicCustomer) ?? normalizedCustomerNumber,
});
selectCustomer(normalizedCustomer, options);
selectCustomer(normalizedCustomer, normalizedOptions);
return normalizedCustomer;
})
.catch((error) => {
console.error(error);
return null;
})
.finally(() => {
if (activeCustomerSelectionRequests.get(normalizedCustomerNumber) === request) {
activeCustomerSelectionRequests.delete(normalizedCustomerNumber);
}
});
if (!normalizedOptions.forceRefresh) {
activeCustomerSelectionRequests.set(normalizedCustomerNumber, request);
}
return await request;
};
/** Get order items */
@@ -1972,18 +2018,14 @@ export const getVehiclePlateBookings = (vehiclePlate) => {
return sortPendingOrderBookings(matchingBookings);
};
export const ensureVehiclePlateBookingsLoaded = async (vehiclePlate, options = {}) => {
const normalizedOptions = {
force: false,
...options,
};
export const ensureVehiclePlateBookingsLoaded = async (vehiclePlate, _options = {}) => {
const normalizedVehiclePlate = normalizeVehiclePlateForBookingSelection(vehiclePlate);
const currentDepartmentKey = syncPendingBookingsDepartmentState();
if (!normalizedVehiclePlate || !department_id.value) {
return [];
}
if (!normalizedOptions.force && loadedPendingBookingPlates.has(normalizedVehiclePlate)) {
if (loadedPendingBookingPlates.has(normalizedVehiclePlate)) {
return getVehiclePlateBookings(normalizedVehiclePlate);
}
@@ -2127,7 +2169,7 @@ const getFirstAvailableWashProduct = async () => {
}
};
const hydrateSelectedOrderBookingForDesktop = async () => {
export const hydrateSelectedOrderBookingForDesktop = async () => {
const normalizedOrderId = toPositiveInteger(order_id.value);
const normalizedBookingId = toPositiveInteger(selectedOrderBookingId.value);
@@ -2210,8 +2252,7 @@ const hydrateSelectedOrderBookingForDesktop = async () => {
}
}
const forcedPrimaryPrice =
typeof primaryRawItem?.price === "number" ? Number(primaryRawItem.price) : primaryProduct.price ?? null;
const forcedPrimaryPrice = primaryProduct.price ?? null;
const primaryItemResponse = await createOrderItem(
normalizedOrderId,
primaryProduct.id,
@@ -2229,13 +2270,15 @@ const hydrateSelectedOrderBookingForDesktop = async () => {
continue;
}
const secondaryProduct = await fetchOrderBookingProductWithPricing(secondaryProductId);
await createOrderItem(
normalizedOrderId,
secondaryProductId,
Math.max(1, Number.parseInt(String(bookingItem?.quantity ?? 1), 10) || 1),
relatedPrimaryItemId,
String(bookingItem?.notes ?? "").trim() || null,
typeof bookingItem?.price === "number" ? Number(bookingItem.price) : null
secondaryProduct?.price ?? null
);
}
@@ -26,7 +26,7 @@ const pages = {
to: "/user/wash/start",
label: "Selv vask",
icon: "tint",
disabled: !IS_DEV,
disabled: false,
},
bookings: {
to: "/user/bookings",
+7 -5
View File
@@ -64,6 +64,7 @@ export const useOrderMetadataAutosave = ({
const requestedValue = normalizeValue(draft.value);
const sequence = ++nextSequence;
let saveCompleted = false;
isSaving.value = true;
try {
@@ -83,6 +84,8 @@ export const useOrderMetadataAutosave = ({
await onSaved(savedValue, requestedValue);
}
saveCompleted = true;
return true;
} catch (error) {
if (typeof onError === "function") {
@@ -95,12 +98,11 @@ export const useOrderMetadataAutosave = ({
} finally {
isSaving.value = false;
if (saveAgainAfterCurrentRequest || isDirty.value) {
saveAgainAfterCurrentRequest = false;
const shouldSaveAgain = saveAgainAfterCurrentRequest;
saveAgainAfterCurrentRequest = false;
if (isDirty.value) {
void persist();
}
if (saveCompleted && (shouldSaveAgain || isDirty.value) && isDirty.value) {
void persist();
}
}
};
+72 -6
View File
@@ -1,5 +1,7 @@
import { computed, ref } from "vue";
import { createPinia, defineStore, getActivePinia, setActivePinia, storeToRefs } from "pinia";
import { SessionUser } from "@/components/session/token/SessionUser.vue";
import { safeAttachmentDownloadLink } from "@/services/attachmentDownloadLinks.js";
const normalizeBooleanAnswer = (value) => {
if (value === true || value === false) {
@@ -270,7 +272,26 @@ const mergeByNumericId = (existingItems, incomingItems) => {
return merged;
};
export function useSelfServeLogic() {
let selfServeLogicStoreInstance = 0;
let fallbackSelfServePinia = null;
const ensureSelfServePinia = () => {
if (typeof document === "undefined") {
fallbackSelfServePinia ||= createPinia();
setActivePinia(fallbackSelfServePinia);
return fallbackSelfServePinia;
}
const activePinia = getActivePinia();
if (activePinia) {
return activePinia;
}
fallbackSelfServePinia ||= createPinia();
setActivePinia(fallbackSelfServePinia);
return fallbackSelfServePinia;
};
const createSelfServeLogicStore = (storeId) => defineStore(storeId, () => {
const loading = ref(false);
const requestError = ref(null);
const preview = ref(null);
@@ -526,7 +547,7 @@ export function useSelfServeLogic() {
};
const downloadAttachment = async (taskId, attachmentId, attachment = null) => {
const existingDownloadLink = attachment?.download_link || null;
const existingDownloadLink = safeAttachmentDownloadLink(attachment?.download_link);
if (existingDownloadLink) {
window.open(existingDownloadLink, "_blank", "noopener");
return;
@@ -534,10 +555,9 @@ export function useSelfServeLogic() {
try {
const response = await SessionUser.objects.self_serve_tasks.attachments.download(taskId, attachmentId);
if (response?.data?.download_link) {
window.open(response.data.download_link, "_blank", "noopener");
} else if (response?.download_link) {
window.open(response.download_link, "_blank", "noopener");
const downloadLink = safeAttachmentDownloadLink(response?.data?.download_link || response?.download_link);
if (downloadLink) {
window.open(downloadLink, "_blank", "noopener");
}
} catch (error) {
console.error("Error downloading attachment:", error);
@@ -1075,4 +1095,50 @@ export function useSelfServeLogic() {
answerQuestion,
removeAnswer,
};
});
export function useSelfServeLogic(options = {}) {
const pinia = ensureSelfServePinia();
const explicitStoreId = typeof options === "string" ? options : options?.storeId;
const storeId = explicitStoreId || `selfServeLogic:${++selfServeLogicStoreInstance}`;
const store = createSelfServeLogicStore(storeId)(pinia);
const refs = storeToRefs(store);
const {
fetchSelfServeData,
fetchWashSummary,
syncVehicleAnswer,
clearVehicleAnswers,
evaluateRule,
evaluateCondition,
isQuestionVisible,
isTaskActive,
isServiceAllowed,
updateLaneAllowedServices,
enableMachineRelay,
isImage,
downloadAttachment,
reset,
answerQuestion,
removeAnswer,
} = store;
return {
...refs,
fetchSelfServeData,
fetchWashSummary,
syncVehicleAnswer,
clearVehicleAnswers,
evaluateRule,
evaluateCondition,
isQuestionVisible,
isTaskActive,
isServiceAllowed,
updateLaneAllowedServices,
enableMachineRelay,
isImage,
downloadAttachment,
reset,
answerQuestion,
removeAnswer,
};
}
+63 -34
View File
@@ -2,12 +2,17 @@ import { computed, nextTick, onUnmounted, ref, watch } from "vue";
import { getDepartmentsGuest } from "@/components/pagination/departmentTabs.vue";
import { locations } from "@/components/displays/department/pos/steps/mobile/objects/PosDepartmentStepMobileFlow.vue";
const isLaneSelfServeEnabled = (lane) => !(
lane?.selfserve_enabled === false
|| lane?.selfserve_enabled === 0
|| lane?.selfserve_enabled === "0"
|| ["false", "off", "no"].includes(String(lane?.selfserve_enabled ?? "").trim().toLowerCase())
);
const isLaneSelfServeEnabled = (lane) =>
!(
lane?.selfserve_enabled === false ||
lane?.selfserve_enabled === 0 ||
lane?.selfserve_enabled === "0" ||
["false", "off", "no"].includes(
String(lane?.selfserve_enabled ?? "")
.trim()
.toLowerCase()
)
);
const toDepartmentViewModel = (department, distance = null) => ({
id: department.id,
@@ -18,12 +23,22 @@ const toDepartmentViewModel = (department, distance = null) => ({
self_serve_enabled: department.self_serve_enabled,
});
const extractDepartmentFetchErrorMessage = (error) => {
const candidates = [
error?.response?.data?.data?.message,
error?.response?.data?.message,
error?.response?.data?.error,
error?.message,
];
return (
candidates.find((candidate) => typeof candidate === "string" && candidate.trim() !== "") ||
"Kunne ikke hente vaskeafdelinger. Prøv igen."
);
};
export function useWashDepartments(options = {}) {
const {
includeLanes = false,
refreshIntervalMs = 0,
canAccessSuperUser = () => false,
} = options;
const { includeLanes = false, refreshIntervalMs = 0, canAccessSuperUser = () => false } = options;
const guestDepartments = ref([]);
const nearestDepartment = ref(null);
@@ -31,6 +46,7 @@ export function useWashDepartments(options = {}) {
const forceNearestDepartmentEvaluationId = ref(0);
const isSearchingDepartments = ref(false);
const lastDepartmentFetchTime = ref(null);
const departmentFetchError = ref(null);
let refreshInterval = null;
@@ -57,13 +73,18 @@ export function useWashDepartments(options = {}) {
return nearestDepartment.value.self_serve_enabled === true;
});
const buildGuestDepartmentParams = () => (
includeLanes ? { include_lanes: true } : {}
);
const buildGuestDepartmentParams = () => (includeLanes ? { include_lanes: true } : {});
const fetchDepartments = async () => {
guestDepartments.value = await getDepartmentsGuest(buildGuestDepartmentParams());
lastDepartmentFetchTime.value = Date.now();
try {
guestDepartments.value = await getDepartmentsGuest(buildGuestDepartmentParams());
lastDepartmentFetchTime.value = Date.now();
departmentFetchError.value = null;
} catch (error) {
departmentFetchError.value = extractDepartmentFetchErrorMessage(error);
console.warn("Failed to fetch wash departments:", error);
}
evaluateLocationDepartments(locations.location.value);
return guestDepartments.value;
};
@@ -74,19 +95,15 @@ export function useWashDepartments(options = {}) {
}
const currentTime = Date.now();
if (
!lastDepartmentFetchTime.value
|| (currentTime - lastDepartmentFetchTime.value) >= refreshIntervalMs
) {
if (!lastDepartmentFetchTime.value || currentTime - lastDepartmentFetchTime.value >= refreshIntervalMs) {
return fetchDepartments();
}
return guestDepartments.value;
};
const getForcedDepartment = () => guestDepartments.value.find(
(department) => department.id === forceNearestDepartmentEvaluationId.value
);
const getForcedDepartment = () =>
guestDepartments.value.find((department) => department.id === forceNearestDepartmentEvaluationId.value);
const evaluateLocationDepartments = (locationValue = locations.location.value) => {
if (isForcingNearestDepartment.value && forceNearestDepartmentEvaluationId.value) {
@@ -191,19 +208,30 @@ export function useWashDepartments(options = {}) {
}
};
watch(() => guestDepartments.value, () => {
evaluateLocationDepartments(locations.location.value);
}, { deep: true });
watch(() => locations.location.value, (newValue) => {
evaluateLocationDepartments(newValue);
}, { deep: true });
watch(() => isSearchingDepartments.value, (isSearching) => {
if (!isSearching) {
watch(
() => guestDepartments.value,
() => {
evaluateLocationDepartments(locations.location.value);
},
{ deep: true }
);
watch(
() => locations.location.value,
(newValue) => {
evaluateLocationDepartments(newValue);
},
{ deep: true }
);
watch(
() => isSearchingDepartments.value,
(isSearching) => {
if (!isSearching) {
evaluateLocationDepartments(locations.location.value);
}
}
});
);
onUnmounted(() => {
stopAutoRefresh();
@@ -216,6 +244,7 @@ export function useWashDepartments(options = {}) {
forceNearestDepartmentEvaluationId,
isSearchingDepartments,
lastDepartmentFetchTime,
departmentFetchError,
availableProductIds,
doesCurrentDepartmentSelectionHaveSelfServeEnabled,
fetchDepartments,
+47 -12
View File
@@ -170,10 +170,15 @@ export function useWashSessionActions(options) {
return null;
}
return await executeSelfServeCommand(resolvedLaneId, command, {
customer_number: null,
license_plate: null,
}, options);
return await executeSelfServeCommand(
resolvedLaneId,
command,
{
customer_number: null,
license_plate: null,
},
options
);
} finally {
loadingRef.value = false;
}
@@ -185,6 +190,24 @@ export function useWashSessionActions(options) {
const openPropertyExitGate = async (laneId = null, options = {}) =>
executePropertyGateCommand("OPEN_PROPERTY_EXIT_GATE", laneId, openingPropertyExitGate, options);
const rollbackStartedWash = async (laneId) => {
const stopResponse = await executeSelfServeCommand(
laneId,
"STOP",
{
customer_number: null,
license_plate: null,
},
{ suppressAlert: true }
);
if (!stopResponse) {
console.error(`Failed to roll back started wash on lane ${laneId}:`, lastCommandErrorMessage.value);
}
return stopResponse;
};
const onStartWash = async (laneId, licensePlate, customerNumber, targetStep = steps.WASH_IN_PROGRESS) => {
if (isStartingWash.value) {
return false;
@@ -227,12 +250,31 @@ export function useWashSessionActions(options) {
const startResponse = await executeSelfServeCommand(laneId, "START", {
customer_number: parseInt(customerNumber),
license_plate: licensePlate.trim().toUpperCase(),
defer_relay_side_effects: true,
defer_relay_side_effects: false,
});
if (!startResponse) {
return false;
}
if (radioWashType.value === "Machine" && isServiceAllowed("MACHINE")) {
let machineRelayResponse = null;
try {
machineRelayResponse = await enableMachineRelay(laneId);
} catch (error) {
console.error("Error enabling machine relay after wash start:", error);
await rollbackStartedWash(laneId);
alertFn(extractCommandErrorMessage(error, "Kunne ikke starte maskinen. Prøv igen."));
return false;
}
const machineRelaySuccessValue = machineRelayResponse?.data?.success ?? machineRelayResponse?.success;
if (machineRelaySuccessValue === false) {
await rollbackStartedWash(laneId);
alertFn(extractCommandErrorMessage(machineRelayResponse, "Kunne ikke starte maskinen. Prøv igen."));
return false;
}
}
washLaneId.value = laneId;
washStartTime.value = Date.now();
completedDurationMs.value = null;
@@ -246,13 +288,6 @@ export function useWashSessionActions(options) {
reg: licensePlate.trim().toUpperCase(),
});
if (radioWashType.value === "Machine" && isServiceAllowed("MACHINE")) {
try {
await enableMachineRelay(laneId);
} catch (error) {
console.error("Error enabling machine relay after wash start:", error);
}
}
return true;
} catch (error) {
console.error("Error starting self-serve wash:", error);
+11
View File
@@ -29,6 +29,7 @@ const normalizeReleaseSource = (value) => {
};
export const RELEASE_SOURCE_ENV = String(import.meta.env.VITE_RELEASE_SOURCE || "").trim().toLowerCase();
export const RELEASE_SOURCE = normalizeReleaseSource(RELEASE_SOURCE_ENV);
export const DEFAULT_LEGACY_API_URL = "https://api.truckwash.io";
export const DEFAULT_PUBLIC_GATEWAY_API_URL = "https://api-v2.truckwash.io";
export const DEFAULT_STABLE_API_URL = `${DEFAULT_PUBLIC_GATEWAY_API_URL}/master/api`;
@@ -50,6 +51,16 @@ export const RELEASE_MANAGER_CONTROL_API_FALLBACK_URLS = String(
.split(",")
.map((url) => normalizeApiUrl(url.trim()))
.filter(Boolean);
export const RELEASE_TRUSTED_ORIGINS = String(import.meta.env.VITE_RELEASE_TRUSTED_ORIGINS || "")
.split(",")
.map((origin) => origin.trim().replace(/\/+$/, ""))
.filter(Boolean);
export const SELF_SERVE_ATTACHMENT_TRUSTED_ORIGINS = String(
import.meta.env.VITE_SELF_SERVE_ATTACHMENT_TRUSTED_ORIGINS || ""
)
.split(",")
.map((origin) => origin.trim().replace(/\/+$/, ""))
.filter(Boolean);
// Allowed origins
export const ALLOWED_ORIGINS = [
@@ -36,7 +36,73 @@ const entryContext = (entry = {}) => {
return context && typeof context === "object" && !Array.isArray(context) ? context : {};
};
const formatDetailValue = (value) => {
const REDACTED_CONTEXT_VALUE = "[REDACTED]";
const SENSITIVE_CONTEXT_KEY_PATTERN = /(?:token|secret|password|credential|api[_-]?key|authorization|auth|signature)/i;
const SENSITIVE_CONTEXT_QUERY_PATTERN = SENSITIVE_CONTEXT_KEY_PATTERN;
const redactSensitiveUrlParams = (value) => {
const text = String(value || "");
if (!text) {
return text;
}
const redactParams = (url) => {
let redacted = false;
url.searchParams.forEach((_paramValue, key) => {
if (SENSITIVE_CONTEXT_QUERY_PATTERN.test(key)) {
url.searchParams.set(key, REDACTED_CONTEXT_VALUE);
redacted = true;
}
});
return redacted ? url.toString() : text;
};
try {
return redactParams(new URL(text));
} catch {
return text.replace(/([?&])([^=&#]+)=([^&#]*)/g, (match, separator, rawKey) => {
let key = rawKey;
try {
key = decodeURIComponent(rawKey.replace(/\+/g, " "));
} catch {
key = rawKey;
}
return SENSITIVE_CONTEXT_QUERY_PATTERN.test(key) ? `${separator}${rawKey}=${REDACTED_CONTEXT_VALUE}` : match;
});
}
};
const redactEdgeGatewayLogContext = (value, seen = new WeakSet()) => {
if (value === null || typeof value === "undefined") {
return value;
}
if (typeof value === "string") {
return redactSensitiveUrlParams(value);
}
if (typeof value !== "object") {
return value;
}
if (seen.has(value)) {
return "[Circular]";
}
seen.add(value);
if (Array.isArray(value)) {
return value.map((item) => redactEdgeGatewayLogContext(item, seen));
}
return Object.fromEntries(
Object.entries(value).map(([key, item]) => [
key,
SENSITIVE_CONTEXT_KEY_PATTERN.test(key) ? REDACTED_CONTEXT_VALUE : redactEdgeGatewayLogContext(item, seen),
])
);
};
const formatDetailValue = (value, { redactContext = false } = {}) => {
if (value === null || typeof value === "undefined" || value === "") {
return "None";
}
@@ -44,13 +110,14 @@ const formatDetailValue = (value) => {
return value ? "yes" : "no";
}
if (typeof value === "object") {
const displayValue = redactContext ? redactEdgeGatewayLogContext(value) : value;
try {
return JSON.stringify(value, null, 2);
return JSON.stringify(displayValue, null, 2);
} catch {
return String(value);
return String(displayValue);
}
}
return String(value);
return redactContext && typeof value === "string" ? redactSensitiveUrlParams(value) : String(value);
};
const detailRow = (label, value) => ({
@@ -61,7 +128,7 @@ const detailRow = (label, value) => ({
const contextDetailRow = (value) => ({
label: "Context",
value: formatDetailValue(value),
value: formatDetailValue(value, { redactContext: true }),
multiline: true,
actionsOnly: true,
});
@@ -208,7 +275,9 @@ const relayActorLabel = (entry = {}) => {
};
const normalizeRelayRole = (role = "") => {
const normalized = String(role || "").trim().toUpperCase();
const normalized = String(role || "")
.trim()
.toUpperCase();
const aliases = {
ENTRANCE: "ENTRY",
IN: "ENTRY",
@@ -260,7 +329,8 @@ const firstRelayText = (values = []) => {
const relayDisplayName = (entry = {}) => {
const context = entryContext(entry);
const actionContext = context.action_context && typeof context.action_context === "object" ? context.action_context : {};
const actionContext =
context.action_context && typeof context.action_context === "object" ? context.action_context : {};
const binding = context.binding && typeof context.binding === "object" ? context.binding : {};
const request = relayRequestPayload(entry);
return firstRelayText([
@@ -284,7 +354,8 @@ const relayIdentifier = (entry = {}) => {
const relaySignalLabel = (entry = {}) => {
const context = entryContext(entry);
const actionContext = context.action_context && typeof context.action_context === "object" ? context.action_context : {};
const actionContext =
context.action_context && typeof context.action_context === "object" ? context.action_context : {};
const signal = context.signal && typeof context.signal === "object" ? context.signal : {};
const role = normalizeRelayRole(context.relay_role || actionContext.relay_role || actionContext.role);
const action = String(context.action || signal.command_type || "RELAY").toUpperCase();
@@ -2,6 +2,7 @@
import { computed, onMounted, onUnmounted, ref, watch } from "vue";
import {
cancelEdgeGatewayOperation,
clearEdgeGatewayWorkspaceCache,
createEdgeGatewayInstallToken,
createEdgeGatewayOperation,
deleteEdgeGateway,
@@ -12,16 +13,18 @@ import {
getEdgeGatewayStatistics,
getEdgeGatewayTasks,
listEdgeGatewayDepartments,
isEdgeGatewayAuthorizationError,
listEdgeGateways,
peekCachedEdgeGateway,
peekCachedEdgeGatewayDepartments,
peekCachedEdgeGatewayList,
removeEdgeGatewayCache,
rotateEdgeGatewayCredentials,
saveEdgeGatewayBindings,
setDepartmentGatewayCutover,
updateEdgeGateway,
} from "@/services/edgeGateways.js";
import { normalizeEdgeGatewayError, normalizeGatewayWebSocketClose } from "@/features/edgeGateways/edgeGatewayErrors.js";
import { normalizeEdgeGatewayError, normalizeGatewayWebSocketClose, redactEdgeGatewayDiagnostic } from "@/features/edgeGateways/edgeGatewayErrors.js";
import EdgeGatewayOverviewPage from "@/features/edgeGateways/EdgeGatewayOverviewPage.vue";
import EdgeGatewayInventoryPage from "@/features/edgeGateways/EdgeGatewayInventoryPage.vue";
import EdgeGatewayTasksPage from "@/features/edgeGateways/EdgeGatewayTasksPage.vue";
@@ -183,7 +186,7 @@ const terminalGatewayBrokerDiagnostics = (gateway = selectedGatewayView.value) =
if (value === null || value === undefined || value === "") {
return;
}
lines.push(`${label}: ${value}`);
lines.push(`${label}: ${redactEdgeGatewayDiagnostic(value)}`);
};
push("Gateway ID", gateway?.id);
@@ -1215,6 +1218,15 @@ const refreshSelected = async (gatewayId = activeGatewayId.value, { forceRefresh
}
return gateway;
} catch (error) {
if (isEdgeGatewayAuthorizationError(error)) {
removeEdgeGatewayCache(gatewayId);
unavailableGatewayId.value = String(gatewayId);
selectedGateway.value = null;
resetGatewayViewSnapshots();
fail(error);
return null;
}
const fallbackGateway = findLocalGatewaySnapshot(gatewayId);
if (fallbackGateway) {
setSelectedGatewaySnapshot(fallbackGateway);
@@ -1561,6 +1573,16 @@ const load = async () => {
departments.value = unwrap(departmentsResponse, []);
await syncSelection();
} catch (error) {
if (isEdgeGatewayAuthorizationError(error)) {
clearEdgeGatewayWorkspaceCache();
gateways.value = [];
fleetUsage.value = buildFleetUsageFromRows([]);
if (activeGatewayId.value) {
unavailableGatewayId.value = String(activeGatewayId.value);
}
selectedGateway.value = null;
resetGatewayViewSnapshots();
}
fail(error);
} finally {
loading.value.init = false;
@@ -1,4 +1,6 @@
<script setup>
import { redactEdgeGatewayDiagnostic } from "@/features/edgeGateways/edgeGatewayErrors.js";
const props = defineProps({
status: {
type: String,
@@ -44,7 +46,10 @@ const shortcuts = [
{ id: "docker ps", label: "docker ps" },
];
const diagnosticsText = () => (Array.isArray(props.diagnostics) ? props.diagnostics.join("\n") : "");
const diagnosticsText = () =>
Array.isArray(props.diagnostics)
? props.diagnostics.map((entry) => redactEdgeGatewayDiagnostic(entry)).join("\n")
: "";
const copyDiagnostics = async () => {
const text = diagnosticsText();
@@ -0,0 +1,79 @@
import { RELEASE_TRUSTED_ORIGINS } from "@/config.js";
const splitOriginList = (value) =>
String(value || "")
.split(",")
.map((origin) => origin.trim().replace(/\/+$/, ""))
.filter(Boolean);
export const ALLOWED_PUBLIC_BROKER_PROTOCOLS = Object.freeze(["https:", "wss:"]);
export const EDGE_GATEWAY_PUBLIC_BROKER_ORIGINS = Object.freeze([
"https://api.truckwash.io:4433",
"wss://api.truckwash.io:4433",
...RELEASE_TRUSTED_ORIGINS,
...splitOriginList(import.meta.env.VITE_EDGE_GATEWAY_PUBLIC_BROKER_ORIGINS),
]);
export const canUseStubBrokerAuthMode = () => import.meta.env.DEV || import.meta.env.MODE === "test";
export const sanitizeBrokerAuthMode = (value) => {
if (value === "stub" && canUseStubBrokerAuthMode()) {
return "stub";
}
return "manager";
};
export const redactBrokerSharedSecret = () => "";
export const allowedPublicBrokerOrigins = () => {
const origins = new Set(EDGE_GATEWAY_PUBLIC_BROKER_ORIGINS);
if (typeof window !== "undefined" && window.location?.origin) {
const currentOrigin = window.location.origin.replace(/\/+$/, "");
origins.add(currentOrigin);
if (window.location.hostname) {
const host = window.location.port
? `${window.location.hostname}:${window.location.port}`
: window.location.hostname;
origins.add(`https://${host}`);
origins.add(`wss://${host}`);
}
}
return origins;
};
export const validatePublicBrokerUrl = (value) => {
const normalized = String(value || "").trim();
if (!normalized) {
return { ok: true, value: "" };
}
let parsed;
try {
parsed = new URL(normalized);
} catch (_error) {
return {
ok: false,
message: "Public broker URL must be an absolute HTTPS or WSS URL on an approved origin.",
};
}
if (!ALLOWED_PUBLIC_BROKER_PROTOCOLS.includes(parsed.protocol)) {
return {
ok: false,
message: "Public broker URL must use HTTPS or WSS.",
};
}
if (!allowedPublicBrokerOrigins().has(parsed.origin)) {
return {
ok: false,
message: "Public broker URL origin is not approved for browser edge gateway sessions.",
};
}
return { ok: true, value: parsed.href };
};
+58 -9
View File
@@ -55,6 +55,36 @@ const WEBSOCKET_CLOSE_MESSAGES = {
socket_error: "Terminal websocket connection failed.",
};
const SENSITIVE_DIAGNOSTIC_KEYS = new Set([
"accesstoken",
"agenttoken",
"apikey",
"authorization",
"auth",
"clientsecret",
"jwt",
"key",
"password",
"secret",
"signature",
"token",
]);
const normalizeDiagnosticKey = (key) =>
String(key || "")
.replace(/[^a-z0-9]/gi, "")
.toLowerCase();
const isSensitiveDiagnosticKey = (key) => SENSITIVE_DIAGNOSTIC_KEYS.has(normalizeDiagnosticKey(key));
const redactSensitiveAssignments = (value) =>
String(value)
.replace(/\b(authorization|auth)\s*[:=]\s*(?:Bearer|Basic)\s+[^\s,;&]+/gi, (_match, key) => `${key}=***`)
.replace(
/([?&;,\s]?(access[_-]?token|agent[_-]?token|api[_-]?key|authorization|auth|client[_-]?secret|jwt|key|password|secret|signature|token)\s*[:=]\s*)("[^"\s,;&]*"|'[^'\s,;&]*'|[^\s,;&]+)/gi,
(_match, prefix) => `${prefix}***`
);
export function redactEdgeGatewayUrl(value) {
if (!value) {
return null;
@@ -62,17 +92,33 @@ export function redactEdgeGatewayUrl(value) {
try {
const url = new URL(String(value), typeof window !== "undefined" ? window.location.origin : "http://localhost");
["token", "agentToken", "agent_token"].forEach((key) => {
if (url.searchParams.has(key)) {
if (url.username) {
url.username = "***";
}
if (url.password) {
url.password = "***";
}
Array.from(url.searchParams.keys()).forEach((key) => {
if (isSensitiveDiagnosticKey(key)) {
url.searchParams.set(key, "***");
}
});
return url.toString();
} catch (_error) {
return String(value).replace(/([?&](?:token|agentToken|agent_token)=)[^&]+/gi, "$1***");
return redactSensitiveAssignments(String(value));
}
}
export function redactEdgeGatewayDiagnostic(value) {
if (value === null || value === undefined) {
return value;
}
return redactSensitiveAssignments(
String(value).replace(/\b(?:wss?|https?|mqtts?):\/\/[^\s<>"')]+/gi, (match) => redactEdgeGatewayUrl(match))
);
}
const formatDiagnosticLines = (diagnostics, prefix = "Diagnostic") => {
if (!diagnostics || typeof diagnostics !== "object" || Array.isArray(diagnostics)) {
return [];
@@ -83,20 +129,23 @@ const formatDiagnosticLines = (diagnostics, prefix = "Diagnostic") => {
if (value === null || value === undefined || value === "") {
return;
}
lines.push(`${label}: ${value}`);
lines.push(`${label}: ${redactEdgeGatewayDiagnostic(value)}`);
};
push(`${prefix} code`, diagnostics.reason_code || diagnostics.error_code);
push("Broker URL", diagnostics.broker_url);
push("WebSocket URL", redactEdgeGatewayUrl(diagnostics.ws_url));
push("WebSocket URL", diagnostics.ws_url);
push("Derived URL warning", diagnostics.derived_warning);
if (diagnostics.broker_presence && typeof diagnostics.broker_presence === "object") {
push("Broker connected", diagnostics.broker_presence.connected === true ? "yes" : "no");
push("Broker connection", diagnostics.broker_presence.connection_id);
push("Broker last seen", diagnostics.broker_presence.last_seen_at);
push("Broker age", Number.isFinite(Number(diagnostics.broker_presence.age_seconds))
? `${Number(diagnostics.broker_presence.age_seconds)}s`
: null);
push(
"Broker age",
Number.isFinite(Number(diagnostics.broker_presence.age_seconds))
? `${Number(diagnostics.broker_presence.age_seconds)}s`
: null
);
push("Broker last error", diagnostics.broker_presence.last_error);
push("Broker disconnect reason", diagnostics.broker_presence.disconnect_reason);
}
@@ -180,7 +229,7 @@ export function normalizeGatewayWebSocketClose(event = {}, context = {}) {
details.push(`WebSocket code: ${code}`);
}
if (reason) {
details.push(`Close reason: ${reason}`);
details.push(`Close reason: ${redactEdgeGatewayDiagnostic(reason)}`);
}
if (wasClean !== null) {
details.push(`Clean close: ${wasClean ? "yes" : "no"}`);
@@ -10,6 +10,66 @@ const MOCK_SHELL_PROMPT = "edge@truckwash:/opt/truckwash-edge-agent$ ";
const isMockSocketUrl = (value) => String(value || "").startsWith(MOCK_SOCKET_PREFIX);
const SENSITIVE_SOCKET_QUERY_KEYS = new Set([
"access_token",
"auth",
"authorization",
"bearer",
"jwt",
"session",
"session_token",
"token",
]);
const getBrowserLocation = () => (typeof window !== "undefined" && window.location ? window.location : null);
const getConfiguredTrustedSocketOrigins = () =>
String(import.meta.env?.VITE_EDGE_GATEWAY_WS_ALLOWED_ORIGINS || "")
.split(",")
.map((entry) => entry.trim())
.filter(Boolean);
const toSocketOrigin = (value) => {
const location = getBrowserLocation();
const base = location?.origin || "http://localhost";
const url = new URL(value, base);
if (url.protocol === "https:") {
url.protocol = "wss:";
} else if (url.protocol === "http:") {
url.protocol = "ws:";
}
return url.origin;
};
const isLocalSocketHost = (hostname) => ["localhost", "127.0.0.1", "::1", "[::1]"].includes(String(hostname || ""));
const getTrustedSocketOrigins = () => {
const origins = new Set();
const location = getBrowserLocation();
if (location?.origin) {
origins.add(toSocketOrigin(location.origin));
}
getConfiguredTrustedSocketOrigins().forEach((origin) => origins.add(toSocketOrigin(origin)));
return origins;
};
const assertTrustedSocketUrl = (url) => {
if (!["ws:", "wss:"].includes(url.protocol)) {
throw new Error("Unsupported websocket URL protocol");
}
if (url.protocol !== "wss:" && !isLocalSocketHost(url.hostname)) {
throw new Error("Gateway websocket URL must use wss");
}
const trustedOrigins = getTrustedSocketOrigins();
if (trustedOrigins.size > 0 && !trustedOrigins.has(url.origin)) {
throw new Error("Gateway websocket URL origin is not trusted");
}
};
const toNativeSocketUrl = (socketUrl, query = {}) => {
if (!socketUrl) {
throw new Error("Missing websocket URL");
@@ -19,16 +79,48 @@ const toNativeSocketUrl = (socketUrl, query = {}) => {
return socketUrl;
}
const url = new URL(socketUrl, typeof window !== "undefined" ? window.location.origin : "http://localhost");
const url = new URL(socketUrl, getBrowserLocation()?.origin || "http://localhost");
assertTrustedSocketUrl(url);
Array.from(url.searchParams.keys()).forEach((key) => {
if (SENSITIVE_SOCKET_QUERY_KEYS.has(String(key).toLowerCase())) {
url.searchParams.delete(key);
}
});
Object.entries(query || {}).forEach(([key, value]) => {
if (value === null || value === undefined || value === "") {
return;
}
if (SENSITIVE_SOCKET_QUERY_KEYS.has(String(key).toLowerCase())) {
return;
}
url.searchParams.set(key, String(value));
});
return url.toString();
};
const createSessionAuthenticationPayload = (session, gatewayId, kind) => {
if (!session?.token) {
return null;
}
return {
type: "AUTH",
gatewayId: String(gatewayId || session.gateway_id || ""),
token: String(session.token),
sessionId: session.id || session.session_id || null,
kind,
};
};
const sendSessionAuthentication = (send, session, gatewayId, kind) => {
const authPayload = createSessionAuthenticationPayload(session, gatewayId, kind);
if (authPayload) {
send(authPayload);
}
};
const safeJsonParse = (payload) => {
if (payload === null || payload === undefined || payload === "") {
return null;
@@ -292,10 +384,10 @@ export async function createGatewayStreamClient(gatewayId, scopes = [], handlers
const session = unwrapEdgeGatewayResponse(response, {});
const socketUrl = toNativeSocketUrl(session.ws_url, {
gatewayId,
token: session.token,
});
const connection = createConnection(socketUrl, session, "stream", handlers, ({ send }) => {
sendSessionAuthentication(send, session, gatewayId, "stream");
send({
type: "SUBSCRIBE",
gatewayId: String(gatewayId),
@@ -318,9 +410,10 @@ export async function createGatewayShellClient(gatewayId, options = {}, handlers
const session = unwrapEdgeGatewayResponse(response, {});
const socketUrl = toNativeSocketUrl(session.ws_url, {
gatewayId,
token: session.token,
});
const connection = createConnection(socketUrl, session, "shell", handlers);
const connection = createConnection(socketUrl, session, "shell", handlers, ({ send }) => {
sendSessionAuthentication(send, session, gatewayId, "shell");
});
return {
session,
+74 -57
View File
@@ -112,7 +112,7 @@
"api": "API",
"app": "app",
"appen": "appen",
"ar": "år",
"ar": "@:{'words.generated.years'}",
"are": "are",
"arsag": "årsag",
"at": "at",
@@ -679,7 +679,7 @@
"nøgle": "nøgle",
"nogle": "nogle",
"nok": "nok",
"nøkkel": "nøgle",
"nøkkel": "@:{'words.generated.nøgle'}",
"nøkkelen": "nøglen",
"not": "not",
"note": "note",
@@ -1599,7 +1599,25 @@
"view_lane_new_tab": "@:{'words.generated.abn'} @:{'words.generated.bane'} @:{'words.generated.i'} @:{'words.generated.ny'} @:{'words.generated.fane'}",
"view_lane_setup_new_tab": "@:{'words.generated.abn'} baneopsætning @:{'words.generated.i'} @:{'words.generated.ny'} @:{'words.generated.fane'}",
"view_order_new_tab": "@:{'words.generated.abn'} @:{'words.generated.ordre'} @:{'words.generated.i'} @:{'words.generated.ny'} @:{'words.generated.fane'}",
"view_vehicle_new_tab": "?@:{'words.generated.bn'} @:{'words.generated.k'}?@:{'words.generated.ret'}?j {reg} @:{'words.generated.i'} @:{'words.generated.ny'} @:{'words.generated.fane'}"
"view_vehicle_new_tab": "?@:{'words.generated.bn'} @:{'words.generated.k'}?@:{'words.generated.ret'}?j {reg} @:{'words.generated.i'} @:{'words.generated.ny'} @:{'words.generated.fane'}",
"accept_self_serve_wash": "Godkend selvbetjent vask",
"accept_self_serve_wash_confirm": "Flyt denne kladde til kunde #{customerNumber} med selvbetjeningsdetaljerne?",
"accept_self_serve_wash_success": "Den selvbetjente vask blev godkendt.",
"email_notifications_section": "E-mail notifikationer",
"resend_booking_completion_confirmation": "(Gen)send bekræftelse for færdig booking",
"resend_booking_completion_confirmation_error": "Bekræftelsen for færdig booking kunne ikke sendes",
"resend_booking_completion_confirmation_success": "Bekræftelsen for færdig booking er sendt",
"resend_booking_confirmation": "(Gen)send bookingbekræftelse",
"resend_booking_confirmation_error": "Bookingbekræftelsen kunne ikke sendes",
"resend_booking_confirmation_success": "Bookingbekræftelsen er sendt",
"resend_wash_certificate": "(Gen)send vaskecertifikat",
"resend_wash_certificate_error": "Vaskecertifikatet kunne ikke sendes",
"resend_wash_certificate_success": "Vaskecertifikatet er sendt",
"self_serve_customer": "Kunde",
"self_serve_driver": "Chauffør",
"self_serve_elapsed": "Tid",
"self_serve_wash_attachment": "Selvbetjent vask",
"self_serve_wash_attachment_for_customer": "Selvbetjent vask for kunde #{customerNumber}"
},
"show_details": "@:{'words.generated.vis'} @:{'words.generated.detaljer'}",
"show_order_details": "@:{'words.generated.vis'} @:{'words.generated.ordredetaljer'}",
@@ -2439,6 +2457,59 @@
"username_desc": "@.capitalize:{'words.generated.brugernavnet'} @:{'words.generated.bruges'} @:{'words.generated.til'} @:{'words.generated.at'} @:{'words.generated.autentificere'} @:{'words.generated.api'} @:{'words.generated.forbindelsen'}."
},
"release_manager": {
"integrations": {
"title": "GitHub og Coolify",
"subtitle": "Knyt GitHub-repositories og branches til kanalspecifikke Coolify-services.",
"choose_repository": "Vælg repository",
"add_health_url": "tilføj health-URL",
"github_token_not_configured": "GitHub-token er ikke konfigureret; private repositories kan ikke vises.",
"open_github_token_settings": "Åbn GitHub token-indstillinger",
"channel": "Kanal",
"app": "App",
"repository": "Repository",
"repository_message": "Eksempel: truckwash/backend-php",
"repository_placeholder": "ejer/repo",
"branch": "Branch",
"branch_message": "Eksempel: release/canary",
"branch_placeholder": "branch",
"latest_prefix": "Seneste: {commit}",
"coolify_instance": "Coolify-instans",
"coolify_instance_message": "Lad feltet være tomt for manuelle deployment-poster.",
"no_coolify_instance": "Ingen Coolify-instans",
"coolify_instance_placeholder": "Coolify-instans-id",
"coolify_service_uuid": "Coolify service-UUID",
"coolify_service_uuid_message": "Vælg en eksisterende service eller indsæt en UUID.",
"coolify_service_uuid_placeholder": "Coolify service-UUID",
"health_url": "Health-URL",
"health_url_message": "Eksempel: https://api-canary.example.test/ping",
"health_url_placeholder": "Health-URL",
"ssl_domain": "Load balancer-domæne",
"ssl_domain_message": "Skal være et DNS-domæne, der routes til Coolify load balanceren. Eksempel: api-v2.truckwash.io",
"ssl_domain_placeholder": "Load balancer-domæne",
"https_domain_for_coolify": "Domæne kontrolleret af load balanceren",
"endpoint_mode": "Endpoint mode",
"endpoint_mode_message": "Use automatic gateway routing or manually override the public host and port.",
"endpoint_mode_auto": "Auto",
"endpoint_mode_manual": "Manual",
"manual_endpoint_host": "Manual host",
"manual_endpoint_host_message": "Public host shown in release previews when manual mode is selected.",
"manual_endpoint_host_placeholder": "api-v2.truckwash.io",
"manual_endpoint_port": "Manual public port",
"manual_endpoint_port_message": "Optional public port from 1 to 65535.",
"app_port": "App port",
"app_port_message": "Internal container port exposed to Coolify routing.",
"auto_gateway_endpoint": "Automatic gateway endpoint",
"pending_automatic_endpoint": "Automatic endpoint resolution pending",
"auto_deploy": "Auto deploy",
"auto_deploy_tooltip": "Opret automatisk deployments, når dette mål ændres.",
"coolify_ssl": "Coolify SSL",
"coolify_ssl_tooltip": "Bed Coolify om at oprette HTTPS for det konfigurerede domæne.",
"deploy_now": "Deploy nu",
"deploy_now_tooltip": "Start en deployment straks efter målet gemmes.",
"create_missing_service": "Opret manglende service",
"create_missing_service_tooltip": "Tillad Release Manager at oprette en manglende Coolify-service.",
"test_access_tooltip": "Bekræft, at den konfigurerede GitHub-token kan læse dette repository og branch."
},
"title": "Release Manager",
"subtitle": "Gradvise API- og Front-End-udgivelser",
"channel_names": {
@@ -2884,59 +2955,6 @@
"severity": "Alvorlighed",
"any_severity": "Alle alvorligheder",
"no_timeline_events": "Ingen timeline-events matcher de aktuelle filtre."
},
"integrations": {
"title": "GitHub og Coolify",
"subtitle": "Knyt GitHub-repositories og branches til kanalspecifikke Coolify-services.",
"choose_repository": "Vælg repository",
"add_health_url": "tilføj health-URL",
"github_token_not_configured": "GitHub-token er ikke konfigureret; private repositories kan ikke vises.",
"open_github_token_settings": "Åbn GitHub token-indstillinger",
"channel": "Kanal",
"app": "App",
"repository": "Repository",
"repository_message": "Eksempel: truckwash/backend-php",
"repository_placeholder": "ejer/repo",
"branch": "Branch",
"branch_message": "Eksempel: release/canary",
"branch_placeholder": "branch",
"latest_prefix": "Seneste: {commit}",
"coolify_instance": "Coolify-instans",
"coolify_instance_message": "Lad feltet være tomt for manuelle deployment-poster.",
"no_coolify_instance": "Ingen Coolify-instans",
"coolify_instance_placeholder": "Coolify-instans-id",
"coolify_service_uuid": "Coolify service-UUID",
"coolify_service_uuid_message": "Vælg en eksisterende service eller indsæt en UUID.",
"coolify_service_uuid_placeholder": "Coolify service-UUID",
"health_url": "Health-URL",
"health_url_message": "Eksempel: https://api-canary.example.test/ping",
"health_url_placeholder": "Health-URL",
"ssl_domain": "Load balancer-domæne",
"ssl_domain_message": "Skal være et DNS-domæne, der routes til Coolify load balanceren. Eksempel: api-v2.truckwash.io",
"ssl_domain_placeholder": "Load balancer-domæne",
"https_domain_for_coolify": "Domæne kontrolleret af load balanceren",
"endpoint_mode": "Endpoint mode",
"endpoint_mode_message": "Use automatic gateway routing or manually override the public host and port.",
"endpoint_mode_auto": "Auto",
"endpoint_mode_manual": "Manual",
"manual_endpoint_host": "Manual host",
"manual_endpoint_host_message": "Public host shown in release previews when manual mode is selected.",
"manual_endpoint_host_placeholder": "api-v2.truckwash.io",
"manual_endpoint_port": "Manual public port",
"manual_endpoint_port_message": "Optional public port from 1 to 65535.",
"app_port": "App port",
"app_port_message": "Internal container port exposed to Coolify routing.",
"auto_gateway_endpoint": "Automatic gateway endpoint",
"pending_automatic_endpoint": "Automatic endpoint resolution pending",
"auto_deploy": "Auto deploy",
"auto_deploy_tooltip": "Opret automatisk deployments, når dette mål ændres.",
"coolify_ssl": "Coolify SSL",
"coolify_ssl_tooltip": "Bed Coolify om at oprette HTTPS for det konfigurerede domæne.",
"deploy_now": "Deploy nu",
"deploy_now_tooltip": "Start en deployment straks efter målet gemmes.",
"create_missing_service": "Opret manglende service",
"create_missing_service_tooltip": "Tillad Release Manager at oprette en manglende Coolify-service.",
"test_access_tooltip": "Bekræft, at den konfigurerede GitHub-token kan læse dette repository og branch."
}
}
},
@@ -4577,7 +4595,6 @@
"loading_vehicle_types": "@.capitalize:{'words.generated.indlæser'} @:{'words.generated.tilgængelige'} @:{'words.generated.køretøjstyper'}...",
"lane_unavailable": "Vaskebanen er ikke tilgængelig",
"machine": "@.capitalize:{'words.generated.maskine'}",
"machine_unavailable_for_lane": "Maskinvask er ikke tilgængelig for den valgte bane. Vælg manuel vask eller en anden ledig bane.",
"no_departments_found": "@:{'templates.generated.compat.departments.no_departments'}",
"occupied": "Optaget",
"open_property_access_gate": "@:{'words.generated.abn'} adgangsport",
+91 -57
View File
@@ -1061,7 +1061,7 @@
"uhrzeit": "Uhrzeit",
"um": "um",
"umbenannt": "umbenannt",
"umbenennen": "umbenennen",
"umbenennen": "@:{'words.generated.rename'}",
"umsatz": "Umsatz",
"umweltgeb": "Umweltgeb",
"unabhangig": "unabhängig",
@@ -1709,7 +1709,25 @@
"view_lane_new_tab": "@:{'words.generated.spur'} @:{'words.generated.in'} @:{'words.generated.neuem'} @:{'words.generated.tab'} @:{'words.generated.anzeigen'}",
"view_lane_setup_new_tab": "Spurkonfiguration @:{'words.generated.in'} @:{'words.generated.neuem'} @:{'words.generated.tab'} @:{'words.generated.anzeigen'}",
"view_order_new_tab": "@:{'words.generated.auftrag'} @:{'words.generated.in'} @:{'words.generated.neuem'} @:{'words.generated.tab'} @:{'words.generated.anzeigen'}",
"view_vehicle_new_tab": "@:{'words.generated.fahrzeug'} {reg} @:{'words.generated.in'} @:{'words.generated.neuem'} @:{'words.generated.tab'} @:{'words.generated.anzeigen'}"
"view_vehicle_new_tab": "@:{'words.generated.fahrzeug'} {reg} @:{'words.generated.in'} @:{'words.generated.neuem'} @:{'words.generated.tab'} @:{'words.generated.anzeigen'}",
"accept_self_serve_wash": "Self-Service-Wäsche akzeptieren",
"accept_self_serve_wash_confirm": "Diesen Entwurf mit den Self-Service-Details zu Kunde #{customerNumber} verschieben?",
"accept_self_serve_wash_success": "Die Self-Service-Wäsche wurde akzeptiert.",
"email_notifications_section": "E-Mail-Benachrichtigungen",
"resend_booking_completion_confirmation": "(Re)send booking completion confirmation",
"resend_booking_completion_confirmation_error": "The booking completion confirmation could not be sent",
"resend_booking_completion_confirmation_success": "The booking completion confirmation has been sent",
"resend_booking_confirmation": "(Re)send booking confirmation",
"resend_booking_confirmation_error": "The booking confirmation could not be sent",
"resend_booking_confirmation_success": "The booking confirmation has been sent",
"resend_wash_certificate": "(Re)send wash certificate",
"resend_wash_certificate_error": "The wash certificate could not be sent",
"resend_wash_certificate_success": "The wash certificate has been sent",
"self_serve_customer": "Kunde",
"self_serve_driver": "Fahrer",
"self_serve_elapsed": "Zeit",
"self_serve_wash_attachment": "Self-Service-Wäsche",
"self_serve_wash_attachment_for_customer": "Self-Service-Wäsche für Kunde #{customerNumber}"
},
"show_details": "@.capitalize:{'words.generated.details'} @:{'words.generated.anzeigen'}",
"show_order_details": "@:{'words.generated.auftragsdetails'} @:{'words.generated.anzeigen'}",
@@ -2549,6 +2567,59 @@
"username_desc": "@.capitalize:{'words.generated.der'} @:{'words.generated.benutzername'} @:{'words.generated.wird'} @:{'words.generated.zur'} @:{'words.generated.authentifizierung'} @:{'words.generated.der'} @:{'words.generated.api'}-@:{'words.generated.verbindung'} @:{'words.generated.verwendet'}."
},
"release_manager": {
"integrations": {
"title": "GitHub und Coolify",
"subtitle": "GitHub-Repositories und Branches kanalspezifischen Coolify-Services zuordnen.",
"choose_repository": "Repository auswählen",
"add_health_url": "Health-URL hinzufügen",
"github_token_not_configured": "GitHub-Token ist nicht konfiguriert; private Repositories können nicht aufgelistet werden.",
"open_github_token_settings": "GitHub-Token-Einstellungen öffnen",
"channel": "Channel",
"app": "App",
"repository": "Repository",
"repository_message": "Example: truckwash/backend-php",
"repository_placeholder": "owner/repo",
"branch": "Branch",
"branch_message": "Example: release/canary",
"branch_placeholder": "branch",
"latest_prefix": "Neueste: {commit}",
"coolify_instance": "Coolify-Instanz",
"coolify_instance_message": "Leer lassen für manuelle Deployment-Einträge.",
"no_coolify_instance": "Keine Coolify-Instanz",
"coolify_instance_placeholder": "Coolify-Instanz-ID",
"coolify_service_uuid": "Coolify service UUID",
"coolify_service_uuid_message": "Bestehenden Service auswählen oder UUID einfügen.",
"coolify_service_uuid_placeholder": "Coolify service UUID",
"health_url": "Health URL",
"health_url_message": "Example: https://api-canary.example.test/ping",
"health_url_placeholder": "Health URL",
"ssl_domain": "Load-Balancer-Domain",
"ssl_domain_message": "Muss eine DNS-Domain sein, die zum Coolify Load Balancer geroutet wird. Beispiel: api-v2.truckwash.io",
"ssl_domain_placeholder": "Load-Balancer-Domain",
"https_domain_for_coolify": "Domain unter Kontrolle des Load Balancers",
"endpoint_mode": "Endpoint mode",
"endpoint_mode_message": "Use automatic gateway routing or manually override the public host and port.",
"endpoint_mode_auto": "Auto",
"endpoint_mode_manual": "Manual",
"manual_endpoint_host": "Manual host",
"manual_endpoint_host_message": "Public host shown in release previews when manual mode is selected.",
"manual_endpoint_host_placeholder": "api-v2.truckwash.io",
"manual_endpoint_port": "Manual public port",
"manual_endpoint_port_message": "Optional public port from 1 to 65535.",
"app_port": "App port",
"app_port_message": "Internal container port exposed to Coolify routing.",
"auto_gateway_endpoint": "Automatic gateway endpoint",
"pending_automatic_endpoint": "Automatic endpoint resolution pending",
"auto_deploy": "Auto-Deploy",
"auto_deploy_tooltip": "Automatisch Deployments erstellen, wenn sich dieses Ziel ändert.",
"coolify_ssl": "Coolify SSL",
"coolify_ssl_tooltip": "Coolify anweisen, HTTPS für die konfigurierte Domain bereitzustellen.",
"deploy_now": "Jetzt deployen",
"deploy_now_tooltip": "Deployment direkt nach dem Speichern des Ziels starten.",
"create_missing_service": "Fehlenden Service erstellen",
"create_missing_service_tooltip": "Release Manager erlauben, einen fehlenden Coolify-Service zu erstellen.",
"test_access_tooltip": "Prüfen, ob der konfigurierte GitHub-Token dieses Repository und den Branch lesen kann."
},
"title": "Release Manager",
"subtitle": "Schrittweise API- und Front-End-Releases",
"channel_names": {
@@ -2744,7 +2815,8 @@
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"use_local_frontend": "Lokales Frontend verwenden"
"use_local_frontend": "Lokales Frontend verwenden",
"switch_error": "Release channel could not be switched. The previous channel is still active."
},
"common": {
"yes": "ja",
@@ -2768,7 +2840,8 @@
"save_target": "Ziel speichern",
"delete": "Löschen",
"back": "Zurück",
"next": "Weiter"
"next": "Weiter",
"publish_release": "Publish release"
},
"tables": {
"channel": "Kanal",
@@ -2978,58 +3051,20 @@
"any_severity": "Beliebiger Schweregrad",
"no_timeline_events": "Keine Timeline-Events entsprechen den aktuellen Filtern."
},
"integrations": {
"title": "GitHub und Coolify",
"subtitle": "GitHub-Repositories und Branches kanalspezifischen Coolify-Services zuordnen.",
"choose_repository": "Repository auswählen",
"add_health_url": "Health-URL hinzufügen",
"github_token_not_configured": "GitHub-Token ist nicht konfiguriert; private Repositories können nicht aufgelistet werden.",
"open_github_token_settings": "GitHub-Token-Einstellungen öffnen",
"channel": "Channel",
"app": "App",
"repository": "Repository",
"repository_message": "Example: truckwash/backend-php",
"repository_placeholder": "owner/repo",
"branch": "Branch",
"branch_message": "Example: release/canary",
"branch_placeholder": "branch",
"latest_prefix": "Neueste: {commit}",
"coolify_instance": "Coolify-Instanz",
"coolify_instance_message": "Leer lassen für manuelle Deployment-Einträge.",
"no_coolify_instance": "Keine Coolify-Instanz",
"coolify_instance_placeholder": "Coolify-Instanz-ID",
"coolify_service_uuid": "Coolify service UUID",
"coolify_service_uuid_message": "Bestehenden Service auswählen oder UUID einfügen.",
"coolify_service_uuid_placeholder": "Coolify service UUID",
"health_url": "Health URL",
"health_url_message": "Example: https://api-canary.example.test/ping",
"health_url_placeholder": "Health URL",
"ssl_domain": "Load-Balancer-Domain",
"ssl_domain_message": "Muss eine DNS-Domain sein, die zum Coolify Load Balancer geroutet wird. Beispiel: api-v2.truckwash.io",
"ssl_domain_placeholder": "Load-Balancer-Domain",
"https_domain_for_coolify": "Domain unter Kontrolle des Load Balancers",
"endpoint_mode": "Endpoint mode",
"endpoint_mode_message": "Use automatic gateway routing or manually override the public host and port.",
"endpoint_mode_auto": "Auto",
"endpoint_mode_manual": "Manual",
"manual_endpoint_host": "Manual host",
"manual_endpoint_host_message": "Public host shown in release previews when manual mode is selected.",
"manual_endpoint_host_placeholder": "api-v2.truckwash.io",
"manual_endpoint_port": "Manual public port",
"manual_endpoint_port_message": "Optional public port from 1 to 65535.",
"app_port": "App port",
"app_port_message": "Internal container port exposed to Coolify routing.",
"auto_gateway_endpoint": "Automatic gateway endpoint",
"pending_automatic_endpoint": "Automatic endpoint resolution pending",
"auto_deploy": "Auto-Deploy",
"auto_deploy_tooltip": "Automatisch Deployments erstellen, wenn sich dieses Ziel ändert.",
"coolify_ssl": "Coolify SSL",
"coolify_ssl_tooltip": "Coolify anweisen, HTTPS für die konfigurierte Domain bereitzustellen.",
"deploy_now": "Jetzt deployen",
"deploy_now_tooltip": "Deployment direkt nach dem Speichern des Ziels starten.",
"create_missing_service": "Fehlenden Service erstellen",
"create_missing_service_tooltip": "Release Manager erlauben, einen fehlenden Coolify-Service zu erstellen.",
"test_access_tooltip": "Prüfen, ob der konfigurierte GitHub-Token dieses Repository und den Branch lesen kann."
"status": {
"confirm_issue_action": "Run this Release Manager action? It can change deployment state and will be audited.",
"choose_bundle_prompt": "Enter the bundle id to set for this channel.",
"impact": "Impact",
"cause": "Cause",
"automated_fix": "Automated fix",
"manual_fallback": "Manual fallback",
"related_deployment": "Related deployment",
"recent_result": "Recent result",
"no_automated_action": "No automated action available.",
"deployment": "Deployment",
"target": "Target",
"coolify_target": "Coolify target",
"service_set": "Service set"
}
}
},
@@ -4670,7 +4705,6 @@
"loading_vehicle_types": "@.capitalize:{'words.generated.verf'}?@:{'words.generated.gbare'} @:{'words.generated.fahrzeugtypen'} @:{'words.generated.werden'} @:{'words.generated.geladen'}...",
"lane_unavailable": "Waschspur nicht verfugbar",
"machine": "@:{'words.generated.maschine'}",
"machine_unavailable_for_lane": "Maschinenwasche ist fur die ausgewahlte Spur nicht verfugbar. Wahlen Sie manuelle Wasche oder eine andere freie Spur.",
"no_departments_found": "@:{'templates.generated.compat.departments.no_departments'}",
"occupied": "Belegt",
"open_property_access_gate": "Grundstueckszugangstor @:{'words.generated.oeffnen'}",
+72 -55
View File
@@ -1433,7 +1433,25 @@
"view_lane_new_tab": "@.capitalize:{'words.generated.view'} @:{'words.generated.lane'} @:{'words.generated.in'} @:{'words.generated.new'} @:{'words.generated.tab'}",
"view_lane_setup_new_tab": "@.capitalize:{'words.generated.view'} @:{'words.generated.lane'} @:{'words.generated.setup'} @:{'words.generated.in'} @:{'words.generated.new'} @:{'words.generated.tab'}",
"view_order_new_tab": "@.capitalize:{'words.generated.view'} @:{'words.generated.order'} @:{'words.generated.in'} @:{'words.generated.new'} @:{'words.generated.tab'}",
"view_vehicle_new_tab": "@.capitalize:{'words.generated.view'} @:{'words.generated.vehicle'} {reg} @:{'words.generated.in'} @:{'words.generated.new'} @:{'words.generated.tab'}"
"view_vehicle_new_tab": "@.capitalize:{'words.generated.view'} @:{'words.generated.vehicle'} {reg} @:{'words.generated.in'} @:{'words.generated.new'} @:{'words.generated.tab'}",
"accept_self_serve_wash": "Accept self-serve wash",
"accept_self_serve_wash_confirm": "Move this draft to customer #{customerNumber} with the self-serve details?",
"accept_self_serve_wash_success": "The self-serve wash was accepted.",
"email_notifications_section": "Email notifications",
"resend_booking_completion_confirmation": "(Re)send booking completion confirmation",
"resend_booking_completion_confirmation_error": "The booking completion confirmation could not be sent",
"resend_booking_completion_confirmation_success": "The booking completion confirmation has been sent",
"resend_booking_confirmation": "(Re)send booking confirmation",
"resend_booking_confirmation_error": "The booking confirmation could not be sent",
"resend_booking_confirmation_success": "The booking confirmation has been sent",
"resend_wash_certificate": "(Re)send wash certificate",
"resend_wash_certificate_error": "The wash certificate could not be sent",
"resend_wash_certificate_success": "The wash certificate has been sent",
"self_serve_customer": "Customer",
"self_serve_driver": "Driver",
"self_serve_elapsed": "Elapsed",
"self_serve_wash_attachment": "Self-serve wash",
"self_serve_wash_attachment_for_customer": "Self-serve wash for customer #{customerNumber}"
},
"show_details": "@.capitalize:{'words.generated.show'} @:{'words.generated.details'}",
"show_order_details": "@.capitalize:{'words.generated.show'} @:{'words.generated.order'} @:{'words.generated.details'}",
@@ -2273,6 +2291,59 @@
"username_desc": "@.capitalize:{'words.replication.article.host_mention'} @:{'words.generated.username'} @:{'words.generated.is'} @:{'words.generated.used'} @:{'words.generated.to'} @:{'words.generated.authenticate'} @:{'words.replication.article.host_mention'} @:{'words.generated.api'} @:{'words.generated.connection'}."
},
"release_manager": {
"integrations": {
"title": "GitHub and Coolify",
"subtitle": "Map GitHub repositories and branches to channel-specific Coolify services.",
"choose_repository": "Choose repository",
"add_health_url": "add health URL",
"github_token_not_configured": "GitHub token is not configured; private repositories cannot be listed.",
"open_github_token_settings": "Open GitHub token settings",
"channel": "Channel",
"app": "App",
"repository": "Repository",
"repository_message": "Example: truckwash/backend-php",
"repository_placeholder": "owner/repo",
"branch": "Branch",
"branch_message": "Example: release/canary",
"branch_placeholder": "branch",
"latest_prefix": "Latest: {commit}",
"coolify_instance": "Coolify instance",
"coolify_instance_message": "Leave empty for manual deployment records.",
"no_coolify_instance": "No Coolify instance",
"coolify_instance_placeholder": "Coolify instance id",
"coolify_service_uuid": "Coolify service UUID",
"coolify_service_uuid_message": "Select an existing service or paste a UUID.",
"coolify_service_uuid_placeholder": "Coolify service UUID",
"health_url": "Health URL",
"health_url_message": "Example: https://api-canary.example.test/ping",
"health_url_placeholder": "Health URL",
"ssl_domain": "Load balancer domain",
"ssl_domain_message": "Must be a DNS domain routed to the Coolify load balancer. Example: api-v2.truckwash.io",
"ssl_domain_placeholder": "Load balancer domain",
"https_domain_for_coolify": "Domain controlled by the load balancer",
"endpoint_mode": "Endpoint mode",
"endpoint_mode_message": "Use automatic gateway routing or manually override the public host and port.",
"endpoint_mode_auto": "Auto",
"endpoint_mode_manual": "Manual",
"manual_endpoint_host": "Manual host",
"manual_endpoint_host_message": "Public host shown in release previews when manual mode is selected.",
"manual_endpoint_host_placeholder": "api-v2.truckwash.io",
"manual_endpoint_port": "Manual public port",
"manual_endpoint_port_message": "Optional public port from 1 to 65535.",
"app_port": "App port",
"app_port_message": "Internal container port exposed to Coolify routing.",
"auto_gateway_endpoint": "Automatic gateway endpoint",
"pending_automatic_endpoint": "Automatic endpoint resolution pending",
"auto_deploy": "Auto deploy",
"auto_deploy_tooltip": "Automatically create deployments when this target changes.",
"coolify_ssl": "Coolify SSL",
"coolify_ssl_tooltip": "Ask Coolify to provision HTTPS for the configured domain.",
"deploy_now": "Deploy now",
"deploy_now_tooltip": "Start a deployment immediately after saving the target.",
"create_missing_service": "Create missing service",
"create_missing_service_tooltip": "Allow Release Manager to create a missing Coolify service.",
"test_access_tooltip": "Verify the configured GitHub token can read this repository and branch."
},
"title": "Release Manager",
"subtitle": "Gradual API and Front-End releases",
"channel_names": {
@@ -2718,59 +2789,6 @@
"severity": "Severity",
"any_severity": "Any severity",
"no_timeline_events": "No timeline events match the current filters."
},
"integrations": {
"title": "GitHub and Coolify",
"subtitle": "Map GitHub repositories and branches to channel-specific Coolify services.",
"choose_repository": "Choose repository",
"add_health_url": "add health URL",
"github_token_not_configured": "GitHub token is not configured; private repositories cannot be listed.",
"open_github_token_settings": "Open GitHub token settings",
"channel": "Channel",
"app": "App",
"repository": "Repository",
"repository_message": "Example: truckwash/backend-php",
"repository_placeholder": "owner/repo",
"branch": "Branch",
"branch_message": "Example: release/canary",
"branch_placeholder": "branch",
"latest_prefix": "Latest: {commit}",
"coolify_instance": "Coolify instance",
"coolify_instance_message": "Leave empty for manual deployment records.",
"no_coolify_instance": "No Coolify instance",
"coolify_instance_placeholder": "Coolify instance id",
"coolify_service_uuid": "Coolify service UUID",
"coolify_service_uuid_message": "Select an existing service or paste a UUID.",
"coolify_service_uuid_placeholder": "Coolify service UUID",
"health_url": "Health URL",
"health_url_message": "Example: https://api-canary.example.test/ping",
"health_url_placeholder": "Health URL",
"ssl_domain": "Load balancer domain",
"ssl_domain_message": "Must be a DNS domain routed to the Coolify load balancer. Example: api-v2.truckwash.io",
"ssl_domain_placeholder": "Load balancer domain",
"https_domain_for_coolify": "Domain controlled by the load balancer",
"endpoint_mode": "Endpoint mode",
"endpoint_mode_message": "Use automatic gateway routing or manually override the public host and port.",
"endpoint_mode_auto": "Auto",
"endpoint_mode_manual": "Manual",
"manual_endpoint_host": "Manual host",
"manual_endpoint_host_message": "Public host shown in release previews when manual mode is selected.",
"manual_endpoint_host_placeholder": "api-v2.truckwash.io",
"manual_endpoint_port": "Manual public port",
"manual_endpoint_port_message": "Optional public port from 1 to 65535.",
"app_port": "App port",
"app_port_message": "Internal container port exposed to Coolify routing.",
"auto_gateway_endpoint": "Automatic gateway endpoint",
"pending_automatic_endpoint": "Automatic endpoint resolution pending",
"auto_deploy": "Auto deploy",
"auto_deploy_tooltip": "Automatically create deployments when this target changes.",
"coolify_ssl": "Coolify SSL",
"coolify_ssl_tooltip": "Ask Coolify to provision HTTPS for the configured domain.",
"deploy_now": "Deploy now",
"deploy_now_tooltip": "Start a deployment immediately after saving the target.",
"create_missing_service": "Create missing service",
"create_missing_service_tooltip": "Allow Release Manager to create a missing Coolify service.",
"test_access_tooltip": "Verify the configured GitHub token can read this repository and branch."
}
}
},
@@ -4411,7 +4429,6 @@
"loading_vehicle_types": "@.capitalize:{'words.generated.loading'} @:{'words.generated.available'} @:{'words.generated.vehicle'} @:{'words.generated.types'}...",
"lane_unavailable": "Lane unavailable",
"machine": "@.capitalize:{'words.generated.machine'}",
"machine_unavailable_for_lane": "Machine wash is unavailable for the selected lane. Choose manual wash or another available lane.",
"no_departments_found": "@:{'templates.generated.compat.departments.no_departments'}",
"occupied": "Occupied",
"open_property_access_gate": "@.capitalize:{'words.generated.open'} @:{'words.generated.property'} @:{'words.generated.access'} @:{'words.generated.gate'}",
+88 -54
View File
@@ -549,7 +549,25 @@
"view_lane_new_tab": "@:{'templates.generated.compat.admin.pos.settings_wheel.view_lane_new_tab'}",
"view_lane_setup_new_tab": "@:{'templates.generated.compat.admin.pos.settings_wheel.view_lane_setup_new_tab'}",
"view_order_new_tab": "@:{'templates.generated.compat.admin.pos.settings_wheel.view_order_new_tab'}",
"view_vehicle_new_tab": "@:{'templates.generated.compat.admin.pos.settings_wheel.view_vehicle_new_tab'}"
"view_vehicle_new_tab": "@:{'templates.generated.compat.admin.pos.settings_wheel.view_vehicle_new_tab'}",
"accept_self_serve_wash": "@:{'templates.generated.compat.admin.pos.settings_wheel.accept_self_serve_wash'}",
"accept_self_serve_wash_confirm": "@:{'templates.generated.compat.admin.pos.settings_wheel.accept_self_serve_wash_confirm'}",
"accept_self_serve_wash_success": "@:{'templates.generated.compat.admin.pos.settings_wheel.accept_self_serve_wash_success'}",
"email_notifications_section": "@:{'templates.generated.compat.admin.pos.settings_wheel.email_notifications_section'}",
"resend_booking_completion_confirmation": "@:{'templates.generated.compat.admin.pos.settings_wheel.resend_booking_completion_confirmation'}",
"resend_booking_completion_confirmation_error": "@:{'templates.generated.compat.admin.pos.settings_wheel.resend_booking_completion_confirmation_error'}",
"resend_booking_completion_confirmation_success": "@:{'templates.generated.compat.admin.pos.settings_wheel.resend_booking_completion_confirmation_success'}",
"resend_booking_confirmation": "@:{'templates.generated.compat.admin.pos.settings_wheel.resend_booking_confirmation'}",
"resend_booking_confirmation_error": "@:{'templates.generated.compat.admin.pos.settings_wheel.resend_booking_confirmation_error'}",
"resend_booking_confirmation_success": "@:{'templates.generated.compat.admin.pos.settings_wheel.resend_booking_confirmation_success'}",
"resend_wash_certificate": "@:{'templates.generated.compat.admin.pos.settings_wheel.resend_wash_certificate'}",
"resend_wash_certificate_error": "@:{'templates.generated.compat.admin.pos.settings_wheel.resend_wash_certificate_error'}",
"resend_wash_certificate_success": "@:{'templates.generated.compat.admin.pos.settings_wheel.resend_wash_certificate_success'}",
"self_serve_customer": "@:{'templates.generated.compat.admin.pos.settings_wheel.self_serve_customer'}",
"self_serve_driver": "@:{'templates.generated.compat.admin.pos.settings_wheel.self_serve_driver'}",
"self_serve_elapsed": "@:{'templates.generated.compat.admin.pos.settings_wheel.self_serve_elapsed'}",
"self_serve_wash_attachment": "@:{'templates.generated.compat.admin.pos.settings_wheel.self_serve_wash_attachment'}",
"self_serve_wash_attachment_for_customer": "@:{'templates.generated.compat.admin.pos.settings_wheel.self_serve_wash_attachment_for_customer'}"
},
"show_details": "@:{'templates.generated.compat.admin.pos.show_details'}",
"show_order_details": "@:{'templates.generated.compat.admin.pos.show_order_details'}",
@@ -1498,6 +1516,59 @@
"username_desc": "@:{'templates.generated.compat.configuration.xlvask.username_desc'}"
},
"release_manager": {
"integrations": {
"title": "@:{'templates.generated.compat.configuration.release_manager.integrations.title'}",
"subtitle": "@:{'templates.generated.compat.configuration.release_manager.integrations.subtitle'}",
"choose_repository": "@:{'templates.generated.compat.configuration.release_manager.integrations.choose_repository'}",
"add_health_url": "@:{'templates.generated.compat.configuration.release_manager.integrations.add_health_url'}",
"github_token_not_configured": "@:{'templates.generated.compat.configuration.release_manager.integrations.github_token_not_configured'}",
"open_github_token_settings": "@:{'templates.generated.compat.configuration.release_manager.integrations.open_github_token_settings'}",
"channel": "@:{'templates.generated.compat.configuration.release_manager.integrations.channel'}",
"app": "@:{'templates.generated.compat.configuration.release_manager.integrations.app'}",
"repository": "@:{'templates.generated.compat.configuration.release_manager.integrations.repository'}",
"repository_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.repository_message'}",
"repository_placeholder": "@:{'templates.generated.compat.configuration.release_manager.integrations.repository_placeholder'}",
"branch": "@:{'templates.generated.compat.configuration.release_manager.integrations.branch'}",
"branch_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.branch_message'}",
"branch_placeholder": "@:{'templates.generated.compat.configuration.release_manager.integrations.branch_placeholder'}",
"latest_prefix": "@:{'templates.generated.compat.configuration.release_manager.integrations.latest_prefix'}",
"coolify_instance": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_instance'}",
"coolify_instance_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_instance_message'}",
"no_coolify_instance": "@:{'templates.generated.compat.configuration.release_manager.integrations.no_coolify_instance'}",
"coolify_instance_placeholder": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_instance_placeholder'}",
"coolify_service_uuid": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_service_uuid'}",
"coolify_service_uuid_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_service_uuid_message'}",
"coolify_service_uuid_placeholder": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_service_uuid_placeholder'}",
"health_url": "@:{'templates.generated.compat.configuration.release_manager.integrations.health_url'}",
"health_url_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.health_url_message'}",
"health_url_placeholder": "@:{'templates.generated.compat.configuration.release_manager.integrations.health_url_placeholder'}",
"ssl_domain": "@:{'templates.generated.compat.configuration.release_manager.integrations.ssl_domain'}",
"ssl_domain_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.ssl_domain_message'}",
"ssl_domain_placeholder": "@:{'templates.generated.compat.configuration.release_manager.integrations.ssl_domain_placeholder'}",
"https_domain_for_coolify": "@:{'templates.generated.compat.configuration.release_manager.integrations.https_domain_for_coolify'}",
"endpoint_mode": "@:{'templates.generated.compat.configuration.release_manager.integrations.endpoint_mode'}",
"endpoint_mode_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.endpoint_mode_message'}",
"endpoint_mode_auto": "@:{'templates.generated.compat.configuration.release_manager.integrations.endpoint_mode_auto'}",
"endpoint_mode_manual": "@:{'templates.generated.compat.configuration.release_manager.integrations.endpoint_mode_manual'}",
"manual_endpoint_host": "@:{'templates.generated.compat.configuration.release_manager.integrations.manual_endpoint_host'}",
"manual_endpoint_host_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.manual_endpoint_host_message'}",
"manual_endpoint_host_placeholder": "@:{'templates.generated.compat.configuration.release_manager.integrations.manual_endpoint_host_placeholder'}",
"manual_endpoint_port": "@:{'templates.generated.compat.configuration.release_manager.integrations.manual_endpoint_port'}",
"manual_endpoint_port_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.manual_endpoint_port_message'}",
"app_port": "@:{'templates.generated.compat.configuration.release_manager.integrations.app_port'}",
"app_port_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.app_port_message'}",
"auto_gateway_endpoint": "@:{'templates.generated.compat.configuration.release_manager.integrations.auto_gateway_endpoint'}",
"pending_automatic_endpoint": "@:{'templates.generated.compat.configuration.release_manager.integrations.pending_automatic_endpoint'}",
"auto_deploy": "@:{'templates.generated.compat.configuration.release_manager.integrations.auto_deploy'}",
"auto_deploy_tooltip": "@:{'templates.generated.compat.configuration.release_manager.integrations.auto_deploy_tooltip'}",
"coolify_ssl": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_ssl'}",
"coolify_ssl_tooltip": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_ssl_tooltip'}",
"deploy_now": "@:{'templates.generated.compat.configuration.release_manager.integrations.deploy_now'}",
"deploy_now_tooltip": "@:{'templates.generated.compat.configuration.release_manager.integrations.deploy_now_tooltip'}",
"create_missing_service": "@:{'templates.generated.compat.configuration.release_manager.integrations.create_missing_service'}",
"create_missing_service_tooltip": "@:{'templates.generated.compat.configuration.release_manager.integrations.create_missing_service_tooltip'}",
"test_access_tooltip": "@:{'templates.generated.compat.configuration.release_manager.integrations.test_access_tooltip'}"
},
"title": "@:{'templates.generated.compat.configuration.release_manager.title'}",
"subtitle": "@:{'templates.generated.compat.configuration.release_manager.subtitle'}",
"channel_names": {
@@ -1910,58 +1981,22 @@
"any_severity": "@:{'templates.generated.compat.configuration.release_manager.replay.any_severity'}",
"no_timeline_events": "@:{'templates.generated.compat.configuration.release_manager.replay.no_timeline_events'}"
},
"integrations": {
"title": "@:{'templates.generated.compat.configuration.release_manager.integrations.title'}",
"subtitle": "@:{'templates.generated.compat.configuration.release_manager.integrations.subtitle'}",
"choose_repository": "@:{'templates.generated.compat.configuration.release_manager.integrations.choose_repository'}",
"add_health_url": "@:{'templates.generated.compat.configuration.release_manager.integrations.add_health_url'}",
"github_token_not_configured": "@:{'templates.generated.compat.configuration.release_manager.integrations.github_token_not_configured'}",
"open_github_token_settings": "@:{'templates.generated.compat.configuration.release_manager.integrations.open_github_token_settings'}",
"channel": "@:{'templates.generated.compat.configuration.release_manager.integrations.channel'}",
"app": "@:{'templates.generated.compat.configuration.release_manager.integrations.app'}",
"repository": "@:{'templates.generated.compat.configuration.release_manager.integrations.repository'}",
"repository_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.repository_message'}",
"repository_placeholder": "@:{'templates.generated.compat.configuration.release_manager.integrations.repository_placeholder'}",
"branch": "@:{'templates.generated.compat.configuration.release_manager.integrations.branch'}",
"branch_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.branch_message'}",
"branch_placeholder": "@:{'templates.generated.compat.configuration.release_manager.integrations.branch_placeholder'}",
"latest_prefix": "@:{'templates.generated.compat.configuration.release_manager.integrations.latest_prefix'}",
"coolify_instance": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_instance'}",
"coolify_instance_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_instance_message'}",
"no_coolify_instance": "@:{'templates.generated.compat.configuration.release_manager.integrations.no_coolify_instance'}",
"coolify_instance_placeholder": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_instance_placeholder'}",
"coolify_service_uuid": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_service_uuid'}",
"coolify_service_uuid_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_service_uuid_message'}",
"coolify_service_uuid_placeholder": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_service_uuid_placeholder'}",
"health_url": "@:{'templates.generated.compat.configuration.release_manager.integrations.health_url'}",
"health_url_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.health_url_message'}",
"health_url_placeholder": "@:{'templates.generated.compat.configuration.release_manager.integrations.health_url_placeholder'}",
"ssl_domain": "@:{'templates.generated.compat.configuration.release_manager.integrations.ssl_domain'}",
"ssl_domain_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.ssl_domain_message'}",
"ssl_domain_placeholder": "@:{'templates.generated.compat.configuration.release_manager.integrations.ssl_domain_placeholder'}",
"https_domain_for_coolify": "@:{'templates.generated.compat.configuration.release_manager.integrations.https_domain_for_coolify'}",
"endpoint_mode": "@:{'templates.generated.compat.configuration.release_manager.integrations.endpoint_mode'}",
"endpoint_mode_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.endpoint_mode_message'}",
"endpoint_mode_auto": "@:{'templates.generated.compat.configuration.release_manager.integrations.endpoint_mode_auto'}",
"endpoint_mode_manual": "@:{'templates.generated.compat.configuration.release_manager.integrations.endpoint_mode_manual'}",
"manual_endpoint_host": "@:{'templates.generated.compat.configuration.release_manager.integrations.manual_endpoint_host'}",
"manual_endpoint_host_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.manual_endpoint_host_message'}",
"manual_endpoint_host_placeholder": "@:{'templates.generated.compat.configuration.release_manager.integrations.manual_endpoint_host_placeholder'}",
"manual_endpoint_port": "@:{'templates.generated.compat.configuration.release_manager.integrations.manual_endpoint_port'}",
"manual_endpoint_port_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.manual_endpoint_port_message'}",
"app_port": "@:{'templates.generated.compat.configuration.release_manager.integrations.app_port'}",
"app_port_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.app_port_message'}",
"auto_gateway_endpoint": "@:{'templates.generated.compat.configuration.release_manager.integrations.auto_gateway_endpoint'}",
"pending_automatic_endpoint": "@:{'templates.generated.compat.configuration.release_manager.integrations.pending_automatic_endpoint'}",
"auto_deploy": "@:{'templates.generated.compat.configuration.release_manager.integrations.auto_deploy'}",
"auto_deploy_tooltip": "@:{'templates.generated.compat.configuration.release_manager.integrations.auto_deploy_tooltip'}",
"coolify_ssl": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_ssl'}",
"coolify_ssl_tooltip": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_ssl_tooltip'}",
"deploy_now": "@:{'templates.generated.compat.configuration.release_manager.integrations.deploy_now'}",
"deploy_now_tooltip": "@:{'templates.generated.compat.configuration.release_manager.integrations.deploy_now_tooltip'}",
"create_missing_service": "@:{'templates.generated.compat.configuration.release_manager.integrations.create_missing_service'}",
"create_missing_service_tooltip": "@:{'templates.generated.compat.configuration.release_manager.integrations.create_missing_service_tooltip'}",
"test_access_tooltip": "@:{'templates.generated.compat.configuration.release_manager.integrations.test_access_tooltip'}"
"loading": {
"assignments": "@:{'templates.generated.compat.configuration.release_manager.loading.assignments'}",
"channels": "@:{'templates.generated.compat.configuration.release_manager.loading.channels'}",
"channels_detail": "@:{'templates.generated.compat.configuration.release_manager.loading.channels_detail'}",
"data_services": "@:{'templates.generated.compat.configuration.release_manager.loading.data_services'}",
"deployments": "@:{'templates.generated.compat.configuration.release_manager.loading.deployments'}",
"github_repositories": "@:{'templates.generated.compat.configuration.release_manager.loading.github_repositories'}",
"integrations": "@:{'templates.generated.compat.configuration.release_manager.loading.integrations'}",
"operations": "@:{'templates.generated.compat.configuration.release_manager.loading.operations'}",
"refreshing_summary": "@:{'templates.generated.compat.configuration.release_manager.loading.refreshing_summary'}",
"refreshing_timeline": "@:{'templates.generated.compat.configuration.release_manager.loading.refreshing_timeline'}",
"settings": "@:{'templates.generated.compat.configuration.release_manager.loading.settings'}",
"summary": "@:{'templates.generated.compat.configuration.release_manager.loading.summary'}",
"summary_detail": "@:{'templates.generated.compat.configuration.release_manager.loading.summary_detail'}",
"timeline_events": "@:{'templates.generated.compat.configuration.release_manager.loading.timeline_events'}",
"timeline_sessions": "@:{'templates.generated.compat.configuration.release_manager.loading.timeline_sessions'}"
}
}
},
@@ -4189,7 +4224,6 @@
"loading_vehicle_types": "@:{'templates.generated.compat.self_wash.loading_vehicle_types'}",
"lane_unavailable": "@:{'templates.generated.compat.self_wash.lane_unavailable'}",
"machine": "@:{'templates.generated.compat.self_wash.machine'}",
"machine_unavailable_for_lane": "@:{'templates.generated.compat.self_wash.machine_unavailable_for_lane'}",
"no_departments_found": "@:{'templates.generated.compat.self_wash.no_departments_found'}",
"no_vehicles_found": "@:vehicles.no_vehicles",
"occupied": "@:{'templates.generated.compat.self_wash.occupied'}",
+90 -56
View File
@@ -1710,7 +1710,25 @@
"view_lane_new_tab": "@.capitalize:{'words.generated.se'} kjørefelt @:{'words.generated.i'} @:{'words.generated.ny'} @:{'words.generated.fane'}",
"view_lane_setup_new_tab": "@.capitalize:{'words.generated.se'} kjørefeltoppsett @:{'words.generated.i'} @:{'words.generated.ny'} @:{'words.generated.fane'}",
"view_order_new_tab": "@.capitalize:{'words.generated.se'} @:{'words.generated.ordre'} @:{'words.generated.i'} @:{'words.generated.ny'} @:{'words.generated.fane'}",
"view_vehicle_new_tab": "@.capitalize:{'words.generated.se'} @:{'words.generated.kjøretøy'} {reg} @:{'words.generated.i'} @:{'words.generated.ny'} @:{'words.generated.fane'}"
"view_vehicle_new_tab": "@.capitalize:{'words.generated.se'} @:{'words.generated.kjøretøy'} {reg} @:{'words.generated.i'} @:{'words.generated.ny'} @:{'words.generated.fane'}",
"accept_self_serve_wash": "Godta selvbetjent vask",
"accept_self_serve_wash_confirm": "Flytt denne kladden til kunde #{customerNumber} med selvbetjeningsdetaljene?",
"accept_self_serve_wash_success": "Den selvbetjente vasken ble godtatt.",
"email_notifications_section": "E-postvarsler",
"resend_booking_completion_confirmation": "(Re)send booking completion confirmation",
"resend_booking_completion_confirmation_error": "The booking completion confirmation could not be sent",
"resend_booking_completion_confirmation_success": "The booking completion confirmation has been sent",
"resend_booking_confirmation": "(Re)send booking confirmation",
"resend_booking_confirmation_error": "The booking confirmation could not be sent",
"resend_booking_confirmation_success": "The booking confirmation has been sent",
"resend_wash_certificate": "(Re)send wash certificate",
"resend_wash_certificate_error": "The wash certificate could not be sent",
"resend_wash_certificate_success": "The wash certificate has been sent",
"self_serve_customer": "Kunde",
"self_serve_driver": "Sjåfør",
"self_serve_elapsed": "Tid",
"self_serve_wash_attachment": "Selvbetjent vask",
"self_serve_wash_attachment_for_customer": "Selvbetjent vask for kunde #{customerNumber}"
},
"show_details": "@:{'words.generated.vis'} @:{'words.generated.detaljer'}",
"show_order_details": "@:{'words.generated.vis'} @:{'words.generated.bestillingsdetaljer'}",
@@ -2550,6 +2568,59 @@
"username_desc": "@:{'words.generated.brukernavnet'} @:{'words.generated.brukes'} @:{'words.generated.til'} @:{'words.generated.a'} @:{'words.generated.autentisere'} @:{'words.generated.api'}-@:{'words.generated.tilkoblingen'}."
},
"release_manager": {
"integrations": {
"title": "GitHub og Coolify",
"subtitle": "Knyt GitHub-repositories og branches til kanalspecifikke Coolify-services.",
"choose_repository": "Velg repository",
"add_health_url": "legg til health-URL",
"github_token_not_configured": "GitHub-token er ikke konfigurert; private repositories kan ikke vises.",
"open_github_token_settings": "Åpne GitHub token-innstillinger",
"channel": "Kanal",
"app": "App",
"repository": "Repository",
"repository_message": "Eksempel: truckwash/backend-php",
"repository_placeholder": "ejer/repo",
"branch": "Branch",
"branch_message": "Eksempel: release/canary",
"branch_placeholder": "branch",
"latest_prefix": "Seneste: {commit}",
"coolify_instance": "Coolify-instans",
"coolify_instance_message": "Lad feltet være tomt for manuelle deployment-poster.",
"no_coolify_instance": "Ingen Coolify-instans",
"coolify_instance_placeholder": "Coolify-instans-id",
"coolify_service_uuid": "Coolify service-UUID",
"coolify_service_uuid_message": "Vælg en eksisterende service eller indsæt en UUID.",
"coolify_service_uuid_placeholder": "Coolify service-UUID",
"health_url": "Health-URL",
"health_url_message": "Eksempel: https://api-canary.example.test/ping",
"health_url_placeholder": "Health-URL",
"ssl_domain": "Load balancer-domene",
"ssl_domain_message": "Må være et DNS-domene som routes til Coolify load balanceren. Eksempel: api-v2.truckwash.io",
"ssl_domain_placeholder": "Load balancer-domene",
"https_domain_for_coolify": "Domene kontrollert av load balanceren",
"endpoint_mode": "Endpoint mode",
"endpoint_mode_message": "Use automatic gateway routing or manually override the public host and port.",
"endpoint_mode_auto": "Auto",
"endpoint_mode_manual": "Manual",
"manual_endpoint_host": "Manual host",
"manual_endpoint_host_message": "Public host shown in release previews when manual mode is selected.",
"manual_endpoint_host_placeholder": "api-v2.truckwash.io",
"manual_endpoint_port": "Manual public port",
"manual_endpoint_port_message": "Optional public port from 1 to 65535.",
"app_port": "App port",
"app_port_message": "Internal container port exposed to Coolify routing.",
"auto_gateway_endpoint": "Automatic gateway endpoint",
"pending_automatic_endpoint": "Automatic endpoint resolution pending",
"auto_deploy": "Auto deploy",
"auto_deploy_tooltip": "Opret automatisk deployments, når dette mål ændres.",
"coolify_ssl": "Coolify SSL",
"coolify_ssl_tooltip": "Bed Coolify om at oprette HTTPS for det konfigurerede domæne.",
"deploy_now": "Deploy nå",
"deploy_now_tooltip": "Start en deployment straks efter målet gemmes.",
"create_missing_service": "Opprett manglende service",
"create_missing_service_tooltip": "Tillad Release Manager at oprette en manglende Coolify-service.",
"test_access_tooltip": "Bekræft, at den konfigurerede GitHub-token kan læse dette repository og branch."
},
"title": "Release Manager",
"subtitle": "Gradvise API- og Front-End-utgivelser",
"channel_names": {
@@ -2745,7 +2816,8 @@
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"use_local_frontend": "Bruk lokal frontend"
"use_local_frontend": "Bruk lokal frontend",
"switch_error": "Release channel could not be switched. The previous channel is still active."
},
"common": {
"yes": "ja",
@@ -2769,7 +2841,8 @@
"save_target": "Lagre mål",
"delete": "Slett",
"back": "Tilbake",
"next": "Neste"
"next": "Neste",
"publish_release": "Publish release"
},
"tables": {
"channel": "Kanal",
@@ -2979,58 +3052,20 @@
"any_severity": "Alle alvorligheder",
"no_timeline_events": "Ingen timeline-events matcher de aktuelle filtre."
},
"integrations": {
"title": "GitHub og Coolify",
"subtitle": "Knyt GitHub-repositories og branches til kanalspecifikke Coolify-services.",
"choose_repository": "Velg repository",
"add_health_url": "legg til health-URL",
"github_token_not_configured": "GitHub-token er ikke konfigurert; private repositories kan ikke vises.",
"open_github_token_settings": "Åpne GitHub token-innstillinger",
"channel": "Kanal",
"app": "App",
"repository": "Repository",
"repository_message": "Eksempel: truckwash/backend-php",
"repository_placeholder": "ejer/repo",
"branch": "Branch",
"branch_message": "Eksempel: release/canary",
"branch_placeholder": "branch",
"latest_prefix": "Seneste: {commit}",
"coolify_instance": "Coolify-instans",
"coolify_instance_message": "Lad feltet være tomt for manuelle deployment-poster.",
"no_coolify_instance": "Ingen Coolify-instans",
"coolify_instance_placeholder": "Coolify-instans-id",
"coolify_service_uuid": "Coolify service-UUID",
"coolify_service_uuid_message": "Vælg en eksisterende service eller indsæt en UUID.",
"coolify_service_uuid_placeholder": "Coolify service-UUID",
"health_url": "Health-URL",
"health_url_message": "Eksempel: https://api-canary.example.test/ping",
"health_url_placeholder": "Health-URL",
"ssl_domain": "Load balancer-domene",
"ssl_domain_message": "Må være et DNS-domene som routes til Coolify load balanceren. Eksempel: api-v2.truckwash.io",
"ssl_domain_placeholder": "Load balancer-domene",
"https_domain_for_coolify": "Domene kontrollert av load balanceren",
"endpoint_mode": "Endpoint mode",
"endpoint_mode_message": "Use automatic gateway routing or manually override the public host and port.",
"endpoint_mode_auto": "Auto",
"endpoint_mode_manual": "Manual",
"manual_endpoint_host": "Manual host",
"manual_endpoint_host_message": "Public host shown in release previews when manual mode is selected.",
"manual_endpoint_host_placeholder": "api-v2.truckwash.io",
"manual_endpoint_port": "Manual public port",
"manual_endpoint_port_message": "Optional public port from 1 to 65535.",
"app_port": "App port",
"app_port_message": "Internal container port exposed to Coolify routing.",
"auto_gateway_endpoint": "Automatic gateway endpoint",
"pending_automatic_endpoint": "Automatic endpoint resolution pending",
"auto_deploy": "Auto deploy",
"auto_deploy_tooltip": "Opret automatisk deployments, når dette mål ændres.",
"coolify_ssl": "Coolify SSL",
"coolify_ssl_tooltip": "Bed Coolify om at oprette HTTPS for det konfigurerede domæne.",
"deploy_now": "Deploy nå",
"deploy_now_tooltip": "Start en deployment straks efter målet gemmes.",
"create_missing_service": "Opprett manglende service",
"create_missing_service_tooltip": "Tillad Release Manager at oprette en manglende Coolify-service.",
"test_access_tooltip": "Bekræft, at den konfigurerede GitHub-token kan læse dette repository og branch."
"status": {
"confirm_issue_action": "Run this Release Manager action? It can change deployment state and will be audited.",
"choose_bundle_prompt": "Enter the bundle id to set for this channel.",
"impact": "Impact",
"cause": "Cause",
"automated_fix": "Automated fix",
"manual_fallback": "Manual fallback",
"related_deployment": "Related deployment",
"recent_result": "Recent result",
"no_automated_action": "No automated action available.",
"deployment": "Deployment",
"target": "Target",
"coolify_target": "Coolify target",
"service_set": "Service set"
}
}
},
@@ -4671,7 +4706,6 @@
"loading_vehicle_types": "@.capitalize:{'words.generated.laster'} @:{'words.generated.inn'} @:{'words.generated.tilgjengelige'} @:{'words.generated.kjøretøytyper'}...",
"lane_unavailable": "Vaskebanen er ikke tilgjengelig",
"machine": "@.capitalize:{'words.generated.maskin'}",
"machine_unavailable_for_lane": "Maskinvask er ikke tilgjengelig for valgt bane. Velg manuell vask eller en annen ledig bane.",
"no_departments_found": "@:{'templates.generated.compat.departments.no_departments'}",
"occupied": "Okkupert",
"open_property_access_gate": "@:{'words.generated.aapne'} @:{'words.generated.eiendommens'} inngangsport",
+90 -56
View File
@@ -1760,7 +1760,25 @@
"view_lane_new_tab": "@.capitalize:{'words.generated.view'} @:{'words.generated.lane'} @:{'words.generated.in'} @:{'words.generated.new'} @:{'words.generated.tab'}",
"view_lane_setup_new_tab": "@.capitalize:{'words.generated.view'} @:{'words.generated.lane'} @:{'words.generated.setup'} @:{'words.generated.in'} @:{'words.generated.new'} @:{'words.generated.tab'}",
"view_order_new_tab": "@.capitalize:{'words.generated.view'} @:{'words.generated.order'} @:{'words.generated.in'} @:{'words.generated.new'} @:{'words.generated.tab'}",
"view_vehicle_new_tab": "@.capitalize:{'words.generated.visa'} @:{'words.generated.fordon'} {reg} @:{'words.generated.i'} @:{'words.generated.ny'} @:{'words.generated.flik'}"
"view_vehicle_new_tab": "@.capitalize:{'words.generated.visa'} @:{'words.generated.fordon'} {reg} @:{'words.generated.i'} @:{'words.generated.ny'} @:{'words.generated.flik'}",
"accept_self_serve_wash": "Godkänn självbetjäningstvätt",
"accept_self_serve_wash_confirm": "Flytta detta utkast till kund #{customerNumber} med självbetjäningsdetaljerna?",
"accept_self_serve_wash_success": "Självbetjäningstvätten godkändes.",
"email_notifications_section": "E-postaviseringar",
"resend_booking_completion_confirmation": "(Re)send booking completion confirmation",
"resend_booking_completion_confirmation_error": "The booking completion confirmation could not be sent",
"resend_booking_completion_confirmation_success": "The booking completion confirmation has been sent",
"resend_booking_confirmation": "(Re)send booking confirmation",
"resend_booking_confirmation_error": "The booking confirmation could not be sent",
"resend_booking_confirmation_success": "The booking confirmation has been sent",
"resend_wash_certificate": "(Re)send wash certificate",
"resend_wash_certificate_error": "The wash certificate could not be sent",
"resend_wash_certificate_success": "The wash certificate has been sent",
"self_serve_customer": "Kund",
"self_serve_driver": "Förare",
"self_serve_elapsed": "Tid",
"self_serve_wash_attachment": "Självbetjäningstvätt",
"self_serve_wash_attachment_for_customer": "Självbetjäningstvätt för kund #{customerNumber}"
},
"show_details": "@:{'words.generated.show'} @:{'words.generated.details'}",
"show_order_details": "@:{'words.generated.show'} @:{'words.generated.order'} @:{'words.generated.details'}",
@@ -2600,6 +2618,59 @@
"username_desc": "Användarnamnet @:{'words.generated.anvands'} @:{'words.generated.for'} @:{'words.generated.att'} @:{'words.generated.autentisera'} @:{'words.generated.api'}-@:{'words.generated.anslutningen'}."
},
"release_manager": {
"integrations": {
"title": "GitHub och Coolify",
"subtitle": "Koppla GitHub-repositories och branches till kanalspecifika Coolify-tjänster.",
"choose_repository": "Välj repository",
"add_health_url": "lägg till health-URL",
"github_token_not_configured": "GitHub-token er ikke konfigureret; private repositories kan ikke vises.",
"open_github_token_settings": "Öppna GitHub token-inställningar",
"channel": "Kanal",
"app": "App",
"repository": "Repository",
"repository_message": "Eksempel: truckwash/backend-php",
"repository_placeholder": "ejer/repo",
"branch": "Branch",
"branch_message": "Eksempel: release/canary",
"branch_placeholder": "branch",
"latest_prefix": "Seneste: {commit}",
"coolify_instance": "Coolify-instans",
"coolify_instance_message": "Lad feltet være tomt for manuelle deployment-poster.",
"no_coolify_instance": "Ingen Coolify-instans",
"coolify_instance_placeholder": "Coolify-instans-id",
"coolify_service_uuid": "Coolify service-UUID",
"coolify_service_uuid_message": "Vælg en eksisterende service eller indsæt en UUID.",
"coolify_service_uuid_placeholder": "Coolify service-UUID",
"health_url": "Health-URL",
"health_url_message": "Eksempel: https://api-canary.example.test/ping",
"health_url_placeholder": "Health-URL",
"ssl_domain": "Load balancer-domän",
"ssl_domain_message": "Måste vara en DNS-domän som routas till Coolify load balancern. Exempel: api-v2.truckwash.io",
"ssl_domain_placeholder": "Load balancer-domän",
"https_domain_for_coolify": "Domän som kontrolleras av load balancern",
"endpoint_mode": "Endpoint mode",
"endpoint_mode_message": "Use automatic gateway routing or manually override the public host and port.",
"endpoint_mode_auto": "Auto",
"endpoint_mode_manual": "Manual",
"manual_endpoint_host": "Manual host",
"manual_endpoint_host_message": "Public host shown in release previews when manual mode is selected.",
"manual_endpoint_host_placeholder": "api-v2.truckwash.io",
"manual_endpoint_port": "Manual public port",
"manual_endpoint_port_message": "Optional public port from 1 to 65535.",
"app_port": "App port",
"app_port_message": "Internal container port exposed to Coolify routing.",
"auto_gateway_endpoint": "Automatic gateway endpoint",
"pending_automatic_endpoint": "Automatic endpoint resolution pending",
"auto_deploy": "Auto deploy",
"auto_deploy_tooltip": "Opret automatisk deployments, når dette mål ændres.",
"coolify_ssl": "Coolify SSL",
"coolify_ssl_tooltip": "Bed Coolify om at oprette HTTPS for det konfigurerede domæne.",
"deploy_now": "Deploy nu",
"deploy_now_tooltip": "Start en deployment straks efter målet gemmes.",
"create_missing_service": "Skapa saknad tjänst",
"create_missing_service_tooltip": "Tillad Release Manager at oprette en manglende Coolify-service.",
"test_access_tooltip": "Bekræft, at den konfigurerede GitHub-token kan læse dette repository og branch."
},
"title": "Release Manager",
"subtitle": "Gradvisa API- och Front-End-versioner",
"channel_names": {
@@ -2795,7 +2866,8 @@
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"use_local_frontend": "Använd lokal frontend"
"use_local_frontend": "Använd lokal frontend",
"switch_error": "Release channel could not be switched. The previous channel is still active."
},
"common": {
"yes": "ja",
@@ -2819,7 +2891,8 @@
"save_target": "Spara mål",
"delete": "Radera",
"back": "Tillbaka",
"next": "Nästa"
"next": "Nästa",
"publish_release": "Publish release"
},
"tables": {
"channel": "Kanal",
@@ -3029,58 +3102,20 @@
"any_severity": "Alla allvarlighetsgrader",
"no_timeline_events": "Ingen timeline-events matcher de aktuelle filtre."
},
"integrations": {
"title": "GitHub och Coolify",
"subtitle": "Koppla GitHub-repositories och branches till kanalspecifika Coolify-tjänster.",
"choose_repository": "Välj repository",
"add_health_url": "lägg till health-URL",
"github_token_not_configured": "GitHub-token er ikke konfigureret; private repositories kan ikke vises.",
"open_github_token_settings": "Öppna GitHub token-inställningar",
"channel": "Kanal",
"app": "App",
"repository": "Repository",
"repository_message": "Eksempel: truckwash/backend-php",
"repository_placeholder": "ejer/repo",
"branch": "Branch",
"branch_message": "Eksempel: release/canary",
"branch_placeholder": "branch",
"latest_prefix": "Seneste: {commit}",
"coolify_instance": "Coolify-instans",
"coolify_instance_message": "Lad feltet være tomt for manuelle deployment-poster.",
"no_coolify_instance": "Ingen Coolify-instans",
"coolify_instance_placeholder": "Coolify-instans-id",
"coolify_service_uuid": "Coolify service-UUID",
"coolify_service_uuid_message": "Vælg en eksisterende service eller indsæt en UUID.",
"coolify_service_uuid_placeholder": "Coolify service-UUID",
"health_url": "Health-URL",
"health_url_message": "Eksempel: https://api-canary.example.test/ping",
"health_url_placeholder": "Health-URL",
"ssl_domain": "Load balancer-domän",
"ssl_domain_message": "Måste vara en DNS-domän som routas till Coolify load balancern. Exempel: api-v2.truckwash.io",
"ssl_domain_placeholder": "Load balancer-domän",
"https_domain_for_coolify": "Domän som kontrolleras av load balancern",
"endpoint_mode": "Endpoint mode",
"endpoint_mode_message": "Use automatic gateway routing or manually override the public host and port.",
"endpoint_mode_auto": "Auto",
"endpoint_mode_manual": "Manual",
"manual_endpoint_host": "Manual host",
"manual_endpoint_host_message": "Public host shown in release previews when manual mode is selected.",
"manual_endpoint_host_placeholder": "api-v2.truckwash.io",
"manual_endpoint_port": "Manual public port",
"manual_endpoint_port_message": "Optional public port from 1 to 65535.",
"app_port": "App port",
"app_port_message": "Internal container port exposed to Coolify routing.",
"auto_gateway_endpoint": "Automatic gateway endpoint",
"pending_automatic_endpoint": "Automatic endpoint resolution pending",
"auto_deploy": "Auto deploy",
"auto_deploy_tooltip": "Opret automatisk deployments, når dette mål ændres.",
"coolify_ssl": "Coolify SSL",
"coolify_ssl_tooltip": "Bed Coolify om at oprette HTTPS for det konfigurerede domæne.",
"deploy_now": "Deploy nu",
"deploy_now_tooltip": "Start en deployment straks efter målet gemmes.",
"create_missing_service": "Skapa saknad tjänst",
"create_missing_service_tooltip": "Tillad Release Manager at oprette en manglende Coolify-service.",
"test_access_tooltip": "Bekræft, at den konfigurerede GitHub-token kan læse dette repository og branch."
"status": {
"confirm_issue_action": "Run this Release Manager action? It can change deployment state and will be audited.",
"choose_bundle_prompt": "Enter the bundle id to set for this channel.",
"impact": "Impact",
"cause": "Cause",
"automated_fix": "Automated fix",
"manual_fallback": "Manual fallback",
"related_deployment": "Related deployment",
"recent_result": "Recent result",
"no_automated_action": "No automated action available.",
"deployment": "Deployment",
"target": "Target",
"coolify_target": "Coolify target",
"service_set": "Service set"
}
}
},
@@ -4721,7 +4756,6 @@
"loading_vehicle_types": "@.capitalize:{'words.generated.laser'} @:{'words.generated.in'} @:{'words.generated.tillgangliga'} @:{'words.generated.fordonstyper'}...",
"lane_unavailable": "Tvattbanan ar inte tillganglig",
"machine": "@.capitalize:{'words.generated.machine'}",
"machine_unavailable_for_lane": "Maskintvatt ar inte tillganglig for vald bana. Valj manuell tvatt eller en annan ledig bana.",
"no_departments_found": "@.capitalize:{'words.generated.inga'} @:{'words.generated.avdelningar'} @:{'words.generated.hittades'}",
"occupied": "Upptagen",
"open_property_access_gate": "@:{'words.generated.oeppna'} @:{'words.generated.fastighetens'} ingangsgrind",
+10 -1
View File
@@ -600,10 +600,20 @@
"delete_user": "Slet bruger",
"download_invoice": "Download faktura",
"edit_permissions": "Rediger tilladelser",
"email_notifications_section": "E-mail notifikationer",
"enter_password": "Indtast adgangskode",
"error": "Fejl",
"error_changing_password": "Fejl ved ændring af adgangskode",
"error_downloading_attachment": "Fejl ved download af vedhæftet fil",
"resend_booking_confirmation": "(Gen)send bookingbekræftelse",
"resend_booking_confirmation_error": "Bookingbekræftelsen kunne ikke sendes",
"resend_booking_confirmation_success": "Bookingbekræftelsen er sendt",
"resend_booking_completion_confirmation": "(Gen)send bekræftelse for færdig booking",
"resend_booking_completion_confirmation_error": "Bekræftelsen for færdig booking kunne ikke sendes",
"resend_booking_completion_confirmation_success": "Bekræftelsen for færdig booking er sendt",
"resend_wash_certificate": "(Gen)send vaskecertifikat",
"resend_wash_certificate_error": "Vaskecertifikatet kunne ikke sendes",
"resend_wash_certificate_success": "Vaskecertifikatet er sendt",
"link_copied": "Link kopieret",
"login_as_customer": "Log ind som kunde",
"login_as_user": "Log ind som bruger",
@@ -3653,7 +3663,6 @@
"loading_vehicle_types": "Indlæser tilgængelige køretøjstyper...",
"lane_unavailable": "Vaskebanen er ikke tilgængelig",
"machine": "Maskine",
"machine_unavailable_for_lane": "Maskinvask er ikke tilgængelig for den valgte bane. Vælg manuel vask eller en anden ledig bane.",
"vehicle_step_missing_customer_number": "Indtast kundenummer for at fortsætte.",
"vehicle_step_missing_department": "Vent på, at nærmeste afdeling er indlæst, før du fortsætter.",
"vehicle_step_missing_registration": "Indtast registreringsnummer for at fortsætte.",
+10 -1
View File
@@ -600,10 +600,20 @@
"delete_user": "Benutzer l?schen",
"download_invoice": "Rechnung herunterladen",
"edit_permissions": "Berechtigungen bearbeiten",
"email_notifications_section": "E-Mail-Benachrichtigungen",
"enter_password": "Passwort eingeben",
"error": "Fehler",
"error_changing_password": "Beim ?ndern des Passworts ist ein Fehler aufgetreten.",
"error_downloading_attachment": "Beim Herunterladen der angeh?ngten Datei ist ein Fehler aufgetreten.",
"resend_booking_confirmation": "(Re)send booking confirmation",
"resend_booking_confirmation_error": "The booking confirmation could not be sent",
"resend_booking_confirmation_success": "The booking confirmation has been sent",
"resend_booking_completion_confirmation": "(Re)send booking completion confirmation",
"resend_booking_completion_confirmation_error": "The booking completion confirmation could not be sent",
"resend_booking_completion_confirmation_success": "The booking completion confirmation has been sent",
"resend_wash_certificate": "(Re)send wash certificate",
"resend_wash_certificate_error": "The wash certificate could not be sent",
"resend_wash_certificate_success": "The wash certificate has been sent",
"link_copied": "Link in die Zwischenablage kopiert",
"login_as_customer": "Als Kunde anmelden",
"login_as_user": "Als Benutzer anmelden",
@@ -3653,7 +3663,6 @@
"loading_vehicle_types": "Verf?gbare Fahrzeugtypen werden geladen...",
"lane_unavailable": "Waschspur nicht verfugbar",
"machine": "Maschine",
"machine_unavailable_for_lane": "Maschinenwasche ist fur die ausgewahlte Spur nicht verfugbar. Wahlen Sie manuelle Wasche oder eine andere freie Spur.",
"vehicle_step_missing_customer_number": "Geben Sie Ihre Kundennummer ein, um fortzufahren.",
"vehicle_step_missing_department": "Warten Sie, bis die nachste Abteilung geladen ist, bevor Sie fortfahren.",
"vehicle_step_missing_registration": "Geben Sie das Kennzeichen ein, um fortzufahren.",
+10 -1
View File
@@ -600,10 +600,20 @@
"delete_user": "Delete user",
"download_invoice": "Download invoice",
"edit_permissions": "Edit permissions",
"email_notifications_section": "Email notifications",
"enter_password": "Enter password",
"error": "Error",
"error_changing_password": "An error occurred while changing the password.",
"error_downloading_attachment": "An error occurred while downloading the attached file.",
"resend_booking_confirmation": "(Re)send booking confirmation",
"resend_booking_confirmation_error": "The booking confirmation could not be sent",
"resend_booking_confirmation_success": "The booking confirmation has been sent",
"resend_booking_completion_confirmation": "(Re)send booking completion confirmation",
"resend_booking_completion_confirmation_error": "The booking completion confirmation could not be sent",
"resend_booking_completion_confirmation_success": "The booking completion confirmation has been sent",
"resend_wash_certificate": "(Re)send wash certificate",
"resend_wash_certificate_error": "The wash certificate could not be sent",
"resend_wash_certificate_success": "The wash certificate has been sent",
"link_copied": "Link copied to clipboard",
"login_as_customer": "Log in as customer",
"login_as_user": "Log in as user",
@@ -3652,7 +3662,6 @@
"loading_vehicle_types": "Loading available vehicle types...",
"lane_unavailable": "Lane unavailable",
"machine": "Machine",
"machine_unavailable_for_lane": "Machine wash is unavailable for the selected lane. Choose manual wash or another available lane.",
"vehicle_step_missing_customer_number": "Enter your customer number to continue.",
"vehicle_step_missing_department": "Wait for the nearest department to load before continuing.",
"vehicle_step_missing_registration": "Enter your registration number to continue.",
+10 -1
View File
@@ -600,10 +600,20 @@
"delete_user": "Delete user",
"download_invoice": "Last ned faktura",
"edit_permissions": "Rediger tillatelser",
"email_notifications_section": "E-postvarsler",
"enter_password": "Skriv inn passord",
"error": "Feil",
"error_changing_password": "Det oppstod en feil under endring av passord.",
"error_downloading_attachment": "Det oppsto en feil under nedlasting av den vedlagte filen.",
"resend_booking_confirmation": "(Re)send booking confirmation",
"resend_booking_confirmation_error": "The booking confirmation could not be sent",
"resend_booking_confirmation_success": "The booking confirmation has been sent",
"resend_booking_completion_confirmation": "(Re)send booking completion confirmation",
"resend_booking_completion_confirmation_error": "The booking completion confirmation could not be sent",
"resend_booking_completion_confirmation_success": "The booking completion confirmation has been sent",
"resend_wash_certificate": "(Re)send wash certificate",
"resend_wash_certificate_error": "The wash certificate could not be sent",
"resend_wash_certificate_success": "The wash certificate has been sent",
"link_copied": "Linken er kopiert til utklippstavlen",
"login_as_customer": "Logg inn som kunde",
"login_as_user": "Logg inn som bruker",
@@ -3653,7 +3663,6 @@
"loading_vehicle_types": "Laster inn tilgjengelige kjøretøytyper...",
"lane_unavailable": "Vaskebanen er ikke tilgjengelig",
"machine": "Maskin",
"machine_unavailable_for_lane": "Maskinvask er ikke tilgjengelig for valgt bane. Velg manuell vask eller en annen ledig bane.",
"vehicle_step_missing_customer_number": "Skriv inn kundenummer for a fortsette.",
"vehicle_step_missing_department": "Vent til naermeste avdeling er lastet inn for du fortsetter.",
"vehicle_step_missing_registration": "Skriv inn registreringsnummer for a fortsette.",
+10 -1
View File
@@ -600,10 +600,20 @@
"delete_user": "Delete user",
"download_invoice": "Ladda ner faktura",
"edit_permissions": "Edit permissions",
"email_notifications_section": "E-postaviseringar",
"enter_password": "Enter password",
"error": "Fel",
"error_changing_password": "Ett fel uppstod när lösenordet ändrades.",
"error_downloading_attachment": "Ett fel uppstod när den bifogade filen laddades ner.",
"resend_booking_confirmation": "(Re)send booking confirmation",
"resend_booking_confirmation_error": "The booking confirmation could not be sent",
"resend_booking_confirmation_success": "The booking confirmation has been sent",
"resend_booking_completion_confirmation": "(Re)send booking completion confirmation",
"resend_booking_completion_confirmation_error": "The booking completion confirmation could not be sent",
"resend_booking_completion_confirmation_success": "The booking completion confirmation has been sent",
"resend_wash_certificate": "(Re)send wash certificate",
"resend_wash_certificate_error": "The wash certificate could not be sent",
"resend_wash_certificate_success": "The wash certificate has been sent",
"link_copied": "Link copied to clipboard",
"login_as_customer": "Log in as customer",
"login_as_user": "Log in as user",
@@ -3653,7 +3663,6 @@
"loading_vehicle_types": "Läser in tillgängliga fordonstyper...",
"lane_unavailable": "Tvattbanan ar inte tillganglig",
"machine": "Machine",
"machine_unavailable_for_lane": "Maskintvatt ar inte tillganglig for vald bana. Valj manuell tvatt eller en annan ledig bana.",
"vehicle_step_missing_customer_number": "Ange kundnummer for att fortsatta.",
"vehicle_step_missing_department": "Vanta tills narmaste avdelning har laddats innan du fortsatter.",
"vehicle_step_missing_registration": "Ange registreringsnummer for att fortsatta.",
@@ -0,0 +1,28 @@
{
"compat": {
"admin": {
"pos": {
"settings_wheel": {
"accept_self_serve_wash": "Godkend selvbetjent vask",
"accept_self_serve_wash_confirm": "Flyt denne kladde til kunde #{customerNumber} med selvbetjeningsdetaljerne?",
"accept_self_serve_wash_success": "Den selvbetjente vask blev godkendt.",
"email_notifications_section": "E-mail notifikationer",
"resend_booking_completion_confirmation": "(Gen)send bekræftelse for færdig booking",
"resend_booking_completion_confirmation_error": "Bekræftelsen for færdig booking kunne ikke sendes",
"resend_booking_completion_confirmation_success": "Bekræftelsen for færdig booking er sendt",
"resend_booking_confirmation": "(Gen)send bookingbekræftelse",
"resend_booking_confirmation_error": "Bookingbekræftelsen kunne ikke sendes",
"resend_booking_confirmation_success": "Bookingbekræftelsen er sendt",
"resend_wash_certificate": "(Gen)send vaskecertifikat",
"resend_wash_certificate_error": "Vaskecertifikatet kunne ikke sendes",
"resend_wash_certificate_success": "Vaskecertifikatet er sendt",
"self_serve_customer": "Kunde",
"self_serve_driver": "Chauffør",
"self_serve_elapsed": "Tid",
"self_serve_wash_attachment": "Selvbetjent vask",
"self_serve_wash_attachment_for_customer": "Selvbetjent vask for kunde #{customerNumber}"
}
}
}
}
}
@@ -298,507 +298,6 @@
"subtitle": "@.capitalize:{'terms.glossary.konfiguration'} @:{'terms.glossary.af'} @:{'terms.glossary.xlvask'} @:{'terms.glossary.integrationen'}",
"title": "@:{'terms.glossary.xlvask'} @:{'terms.glossary.konfiguration'}",
"username_desc": "@.capitalize:{'terms.glossary.brugernavnet'} @:{'terms.glossary.bruges'} @:{'terms.glossary.til'} @:{'terms.glossary.at'} @:{'terms.glossary.autentificere'} @:{'terms.glossary.api'} @:{'terms.glossary.forbindelsen'}."
},
"release_manager": {
"title": "Release Manager",
"subtitle": "Gradvise API- og Front-End-udgivelser",
"channel_names": {
"stable": "Stabil",
"canary": "Canary",
"internal": "Intern"
},
"channel_descriptions": {
"stable": "Standard produktionskanal.",
"canary": "Tidlig produktionsvalideringskanal.",
"internal": "Intern kanal til medarbejdere og superbruger-validering."
},
"control_api": {
"title": "Kontrol-API",
"tooltip": "Release Manager-handlinger sendes til denne API. Brug produktions-API'en, medmindre du tester en staging-backend.",
"example": "Eksempel: https://api.truckwash.io:4433",
"endpoint_label": "API-endpoint",
"endpoint_message": "Skift kun dette, når Release Manager-endpoints er tilgængelige på mål-API'en.",
"endpoint_aria": "Release Manager kontrol-API URL",
"use_tooltip": "Indlæs release-data fra denne API",
"use": "Brug",
"reset_tooltip": "Vend tilbage til standard kontrol-API",
"reset": "Nulstil",
"known_endpoints": "Kendte kontrol-API-endpoints"
},
"tabs": {
"overview": {
"label": "Oversigt",
"description": "Kanalstatus, opsætningsfremdrift og seneste release-tilstand."
},
"channels": {
"label": "Kanaler",
"description": "Opret stabile, canary- og målrettede kanaler med rollout-grænser."
},
"assignments": {
"label": "Tildelinger",
"description": "Fastgør brugere, subbrugere eller kunder til en bestemt release-kanal."
},
"deployments": {
"label": "Udrulninger",
"description": "Start udrulninger og promover vellykkede builds."
},
"replay": {
"label": "Replay",
"description": "Aktivér målrettet opsamling og søg i release-tidslinjehændelser."
},
"integrations": {
"label": "Integrationer",
"description": "Forbind GitHub-repositories, branches og Coolify-services."
},
"settings": {
"label": "Indstillinger",
"description": "Konfigurer Release Managers GitHub-adgang og webhook-indstillinger."
}
},
"loading": {
"summary": "Indl\u00e6ser Release Manager-data...",
"refreshing_summary": "Opdaterer Release Manager-data...",
"summary_detail": "Release-status, kanaler, deployments og service-health indl\u00e6ses.",
"channels": "Indl\u00e6ser release-kanaler...",
"channels_detail": "Kanalbaner og release-afh\u00e6ngigheder indl\u00e6ses.",
"assignments": "Indl\u00e6ser release-tildelinger...",
"deployments": "Indl\u00e6ser release-deployments...",
"operations": "Indl\u00e6ser release-operationer...",
"data_services": "Indl\u00e6ser release-datatjenester...",
"integrations": "Indl\u00e6ser release-integrationer...",
"settings": "Indl\u00e6ser Release Manager-indstillinger...",
"github_repositories": "Indl\u00e6ser GitHub-repositories...",
"timeline_sessions": "Indl\u00e6ser release-tidslinjesessioner...",
"timeline_events": "Indl\u00e6ser release-tidslinjeh\u00e6ndelser...",
"refreshing_timeline": "Opdaterer release-tidslinje..."
},
"setup": {
"channels": "Kanaler",
"targets": "GitHub- og Coolify-mål",
"assignments": "Pilot-tildelinger",
"deployments": "Første udrulning",
"replay": "Replay-opsamling"
},
"stats": {
"channels": "Kanaler",
"targets": "Mål",
"deployments": "Udrulninger",
"timeline_events": "Tidslinjehændelser"
},
"overview": {
"title": "Oversigt",
"subtitle": "Aktuel kanalstatus, release-aktivitet og modulstatus.",
"guided_setup": "Guidet opsætning",
"next_step": "Næste: {step}",
"ready": "Release Manager er klar til daglig drift.",
"add_suggested_channel": "Tilføj foreslået kanal",
"module_health_empty": "Modul-health snapshots vises, når probes er blevet registreret."
},
"status": {
"confirm_issue_action": "Koer denne Release Manager-handling? Den kan aendre deployment-tilstand og bliver auditeret.",
"choose_bundle_prompt": "Indtast bundle-id'et, der skal saettes for denne kanal.",
"impact": "Konsekvens",
"cause": "Aarsag",
"automated_fix": "Automatisk rettelse",
"manual_fallback": "Manuel fallback",
"related_deployment": "Relateret deployment",
"recent_result": "Seneste resultat",
"no_automated_action": "Ingen automatisk handling tilgængelig.",
"deployment": "Deployment",
"target": "Maal",
"coolify_target": "Coolify-maal",
"service_set": "Service set"
},
"settings": {
"title": "Release Manager-indstillinger",
"subtitle": "Konfigurer GitHub-tokenet, der bruges til private repositories og branch-opslag.",
"github_token": "GitHub-token",
"github_api_url": "GitHub API-URL",
"webhook_secret": "Webhook-hemmelighed",
"github_webhook_secret": "GitHub webhook-hemmelighed",
"configured": "Konfigureret",
"not_configured": "Ikke konfigureret",
"loaded_from": "Indlæst fra {variable}",
"set_below": "Angiv {variable} nedenfor",
"private_repositories_prefix": "Private repositories læses med serverens miljøvariabel",
"private_repositories_or": "eller modulets konfigurationsvariabel",
"github_token_message": "Eksempel: github_pat_... med adgang til de private repositories, Release Manager udruller.",
"github_token_placeholder": "Lad feltet være tomt for at beholde det eksisterende token",
"github_api_url_message": "Konfigureret i ReleaseManager.github_api_url. Brug https://api.github.com medmindre GitHub Enterprise bruges.",
"webhook_secret_message": "Valgfrit; lad feltet være tomt for at beholde den eksisterende hemmelighed.",
"webhook_secret_placeholder": "Webhook HMAC-hemmelighed",
"save": "Gem indstillinger",
"back_to_integrations": "Tilbage til integrationer",
"guide": {
"steps": {
"github_token": "GitHub-token",
"github_api_url": "GitHub API-URL",
"github_webhook_secret": "GitHub webhook secret",
"review": "Gennemgang"
},
"github_token_title": "Tilføj GitHub-token",
"github_token_description": "Release Manager bruger denne token til at vise private repositories, branches og commits, før et deployment-mål gemmes eller testes.",
"configure_this_in": "Konfigurer dette i",
"server_environment_variable": "Serverens miljøvariabel",
"takes_precedence": "har forrang, når den er sat.",
"recommended_access": "Anbefalet adgang",
"recommended_access_description": "Fine-grained GitHub-token med Contents: read-adgang til hvert release-repository.",
"github_api_url_title": "Tilføj GitHub API-URL",
"github_api_url_description": "Brug GitHub Cloud som standard, eller peg på REST API-base-URLen for en GitHub Enterprise Server-installation.",
"github_api_url_invalid": "Indtast en fuld HTTP- eller HTTPS-URL.",
"github_cloud": "GitHub Cloud",
"enterprise_example": "Enterprise-eksempel",
"github_webhook_secret_title": "Tilføj GitHub webhook secret",
"github_webhook_secret_description": "Brug samme secret i GitHub repository-webhooks, så Release Manager kan verificere deployment-events, før de håndteres.",
"github_webhook_url": "GitHub webhook-URL:",
"store_shared_secret_in": "Gem den delte secret i",
"review_title": "Gennemgå GitHub-opsætning",
"review_description": "Gem værdierne, og brug derefter Integrationer til at vælge et privat repository, branch og specifik commit eller seneste commit før adgang testes.",
"ready": "Klar",
"needs_value": "Mangler værdi"
}
},
"channel_unavailable": {
"kicker": "Release Manager",
"title": "Release-kanalen er ikke klar",
"summary_prefix": "Din konto er tildelt",
"summary_suffix": ", men kanalen mangler påkrævet release-konfiguration.",
"release_bundle": "Release-bundle",
"frontend_version": "Frontend-version",
"api_version": "API-version",
"frontend_base_url": "Frontend-URL",
"api_base_url": "API-URL",
"frontend_entry": "Frontend-entry",
"release_runtime": "Release-runtime",
"checking_again": "Tjekker igen om {seconds}s",
"refresh_error": "Release-status kunne ikke opdateres. Det næste automatiske tjek prøver igen.",
"ignore": "Ignorer de næste 5 minutter",
"check_again": "Tjek igen",
"logout": "Log ud",
"base_image": "Basis-image"
},
"channel_switched": {
"kicker": "Release Manager",
"title": "Du er nu på {channel}",
"summary_prefix": "Din konto er blevet tildelt release-kanalen",
"summary_suffix": "Denne enhed husker, at du har set denne besked.",
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"frontend_version": "Frontend-version",
"api_version": "API-version",
"assigned_channel": "Tildelt kanal",
"current_app_image": "Nuværende app-image",
"current_api": "Nuværende API",
"base_image": "Basis-image",
"continue": "Fortsæt"
},
"channel_selector": {
"title": "Release-kanal",
"subtitle": "Vælg hvilken tildelt release-kanal denne enhed skal bruge.",
"sidebar_title": "Release",
"default": "Standard",
"ready": "Klar",
"unavailable": "Ikke klar",
"release_bundle": "Release-bundle",
"frontend_version": "Frontend-version",
"api_version": "API-version",
"frontend_base_url": "Frontend-URL",
"api_base_url": "API-URL",
"frontend_entry": "Frontend-entry",
"release_runtime": "Release-runtime",
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"use_local_frontend": "Brug lokal frontend",
"switch_error": "Release-kanalen kunne ikke skiftes. Den forrige kanal er stadig aktiv."
},
"common": {
"yes": "ja",
"no": "nej",
"off": "fra"
},
"actions": {
"edit": "Rediger",
"rollback": "Rul tilbage",
"update": "Opdater",
"create": "Opret",
"publish_release": "Publicer release",
"clear": "Ryd",
"assign": "Tildel",
"remove": "Fjern",
"test_access": "Test adgang",
"deploy": "Deploy",
"redeploy": "Redeploy",
"promote": "Promover",
"enable": "Aktiver",
"search": "Søg",
"save_target": "Gem mål",
"delete": "Slet",
"back": "Tilbage",
"next": "Næste"
},
"tables": {
"channel": "Kanal",
"rollout": "Udrulning",
"frontend": "Frontend",
"api": "API",
"replay": "Replay",
"actions": "Handlinger",
"module": "Modul",
"status": "Status",
"checked": "Kontrolleret",
"subject": "Emne",
"reason": "Årsag",
"expires": "Udløber",
"deployment": "Deployment",
"source": "Kilde",
"started": "Startet",
"target": "Mål",
"coolify": "Coolify",
"auto": "Auto"
},
"values": {
"app": {
"frontend": "frontend",
"api": "api"
},
"capture_level": {
"metadata": "metadata",
"full_redacted": "fuldt redigeret"
},
"subject_type": {
"user": "bruger",
"subuser": "medarbejder",
"customer": "kunde",
"channel": "kanal"
},
"severity": {
"error": "fejl",
"warning": "advarsel",
"info": "info"
},
"status": {
"ok": "ok",
"active": "aktiv",
"deployed": "deployet",
"stable": "stabil",
"enabled": "aktiveret",
"queued": "i kø",
"deploying": "deploying",
"metadata": "metadata",
"not_configured": "ikke konfigureret",
"warning": "advarsel",
"failed": "fejlet",
"down": "nede",
"error": "fejl",
"unknown": "ukendt"
}
},
"autocomplete": {
"suggested_value": "Foreslået værdi",
"suggestion": "Forslag",
"use_typed_value": "Brug indtastet værdi",
"custom": "Tilpasset",
"api_url": "API-URL",
"frontend_url": "Frontend-URL",
"known_release_url": "Kendt release-URL",
"deploy_here_with_coolify": "Deploy {app} her med Coolify",
"prepare_coolify_target": "Forbered et Coolify-mål med HTTPS/SSL aktiveret",
"coolify_ssl": "Coolify SSL",
"github_repository": "GitHub-repository",
"owner_repository": "ejer/repository",
"git_branch": "Git-branch",
"deployment_branch": "Deployment-branch",
"github_commit": "GitHub-commit",
"git_commit": "Git-commit",
"use_specific_commit_sha": "Brug specifik commit-SHA",
"custom_commit": "Tilpasset commit",
"health_url": "Health-URL",
"release_health_check": "Release health check",
"coolify_service": "Coolify-service",
"service_uuid": "Service-UUID",
"known_coolify_service_uuid": "Kendt Coolify service-UUID",
"control_api": "Kontrol-API",
"release_manager_api_endpoint": "Release Manager API-endpoint",
"module": "Modul",
"timeline_module_key": "Timeline-modulnøgle"
},
"errors": {
"github_repositories": "Kunne ikke indlæse GitHub-repositories.",
"github_branches": "Kunne ikke indlæse GitHub-branches.",
"repository_branch_required": "Repository og branch er påkrævet før GitHub-adgang kan testes.",
"github_access_failed": "Test af GitHub repository-adgang fejlede."
},
"channels": {
"title": "Kanaler",
"subtitle": "Opret og juster gradvise release-kanaler.",
"rollout_percent": "{percent}% udrulning",
"already_configured": "Allerede konfigureret",
"slug": "Slug",
"slug_message": "Eksempel: canary",
"slug_placeholder": "slug",
"name": "Navn",
"name_message": "Vises til operatører, når de gennemgår en kanal.",
"name_placeholder": "Navn",
"rollout": "Udrulning",
"rollout_message": "0-100 procent af ikke-tildelt trafik.",
"frontend_url": "Frontend-URL",
"frontend_url_message": "Eksempel: https://canary.example.test",
"frontend_url_placeholder": "Frontend-URL",
"no_matching_release_url": "Ingen matchende release-URL.",
"api_url": "API-URL",
"api_url_message": "Eksempel: https://api-canary.example.test",
"api_url_placeholder": "API-URL",
"no_matching_api_url": "Ingen matchende API-URL.",
"replay_capture": "Replay capture",
"replay_capture_message": "Metadata er sikrere til brede kanaler.",
"retention_days": "Opbevaringsdage",
"retention_days_message": "1-365 dage.",
"enabled": "Aktiveret",
"default_channel": "Standardkanal",
"replay_enabled": "Replay aktiveret",
"frontend_chip": "Frontend: {url}",
"api_chip": "API: {url}"
},
"assignments": {
"title": "Tildelinger",
"subtitle": "Fastgør brugere, medarbejdere eller kunder til release-kanaler.",
"subject": "Emne",
"subject_message": "Søg efter brugere, medarbejdere eller kunder, eller skriv et manuelt emne som user:42.",
"subject_placeholder": "Søg eller skriv emne",
"subject_empty": "Ingen emner fundet",
"group_users": "Brugere",
"group_subusers": "Medarbejdere",
"group_customers": "Kunder",
"group_manual": "Manuel",
"manual_user": "Bruger #{id}",
"manual_subuser": "Medarbejder #{id}",
"manual_customer": "Kunde #{id}",
"manual_description": "Brug indtastet værdi {subject}",
"subject_type": "Emnetype",
"subject_type_message": "Vælg identitetstypen, der skal fastgøres.",
"customer": "Kunde",
"customer_message": "Søg på kundenummer eller navn, og vælg derefter den præcise kunde.",
"customer_placeholder": "Søg kunde",
"subject_id": "Emne-ID",
"subject_id_message": "Eksempel: bruger-id 42",
"subject_id_placeholder": "Emne-id",
"channel": "Kanal",
"channel_message": "Fastgjorte emner springer procentudrulningen over.",
"reason": "Årsag",
"reason_message": "Eksempel: canary-validering for kundesupport.",
"reason_placeholder": "Årsag",
"expires": "Udløber",
"expires_message": "Lad feltet være tomt for ingen automatisk udløb."
},
"deployments": {
"title": "Deployments",
"subtitle": "Start, promover og inspicer release-deployments.",
"manual_deployment_record": "Manuel deployment-post",
"target": "Mål",
"target_message": "Vælg et gemt mål, eller behold manuelle felter redigerbare.",
"select_target_or_manual": "Vælg mål eller manuel",
"channel": "Kanal",
"app": "App",
"repository": "Repository",
"repository_message": "Eksempel: truckwash/front-end-vue",
"repository_placeholder": "ejer/repo",
"branch": "Branch",
"branch_message": "Eksempel: master",
"branch_placeholder": "branch",
"commit_selection": "Commit-valg",
"commit_selection_message": "Seneste slås op som branchens head med den konfigurerede GitHub-token.",
"latest_from_branch": "Seneste fra branch",
"specific_commit": "Specifik commit",
"specific_commit_message": "Eksempel: feedface1234",
"commit_sha_placeholder": "commit sha",
"version_label": "Versionslabel",
"version_label_message": "Eksempel: api-2026.05.19",
"version_label_placeholder": "versionslabel",
"commit_prefix": "Commit: {commit}",
"test_access_tooltip": "Bekræft token-adgang til det valgte repository, branch og commit."
},
"replay": {
"title": "Replay",
"subtitle": "Aktiver målrettet capture og gennemgå fejltimelines trin for trin.",
"capture_channel": "Capture {channel}",
"target_type": "Måltype",
"target": "Mål",
"target_message": "Eksempel: bruger-id 42 eller kanal-slug canary.",
"target_placeholder": "Mål-id eller kanal-slug",
"channel_scope": "Kanalafgrænsning",
"channel_scope_message": "Valgfrit; begrænser replay capture til en kanal.",
"no_channel_scope": "Ingen kanalafgrænsning",
"capture_level": "Capture-niveau",
"expires": "Udløber",
"expires_message": "Lad feltet være tomt for manuel deaktivering.",
"trace_id": "Trace-ID",
"trace_id_message": "Eksempel: trace-canary-1",
"trace_id_placeholder": "Trace-id",
"channel": "Kanal",
"channel_placeholder": "Kanal",
"principal_id": "Principal-ID",
"principal_id_placeholder": "Principal-id",
"module": "Modul",
"module_placeholder": "Modul",
"severity": "Alvorlighed",
"any_severity": "Alle alvorligheder",
"no_timeline_events": "Ingen timeline-events matcher de aktuelle filtre."
},
"integrations": {
"title": "GitHub og Coolify",
"subtitle": "Knyt GitHub-repositories og branches til kanalspecifikke Coolify-services.",
"choose_repository": "Vælg repository",
"add_health_url": "tilføj health-URL",
"github_token_not_configured": "GitHub-token er ikke konfigureret; private repositories kan ikke vises.",
"open_github_token_settings": "Åbn GitHub token-indstillinger",
"channel": "Kanal",
"app": "App",
"repository": "Repository",
"repository_message": "Eksempel: truckwash/backend-php",
"repository_placeholder": "ejer/repo",
"branch": "Branch",
"branch_message": "Eksempel: release/canary",
"branch_placeholder": "branch",
"latest_prefix": "Seneste: {commit}",
"coolify_instance": "Coolify-instans",
"coolify_instance_message": "Lad feltet være tomt for manuelle deployment-poster.",
"no_coolify_instance": "Ingen Coolify-instans",
"coolify_instance_placeholder": "Coolify-instans-id",
"coolify_service_uuid": "Coolify service-UUID",
"coolify_service_uuid_message": "Vælg en eksisterende service eller indsæt en UUID.",
"coolify_service_uuid_placeholder": "Coolify service-UUID",
"health_url": "Health-URL",
"health_url_message": "Eksempel: https://api-canary.example.test/ping",
"health_url_placeholder": "Health-URL",
"ssl_domain": "Load balancer-domæne",
"ssl_domain_message": "Skal være et DNS-domæne, der routes til Coolify load balanceren. Eksempel: api-v2.truckwash.io",
"ssl_domain_placeholder": "Load balancer-domæne",
"https_domain_for_coolify": "Domæne kontrolleret af load balanceren",
"endpoint_mode": "Endpoint mode",
"endpoint_mode_message": "Use automatic gateway routing or manually override the public host and port.",
"endpoint_mode_auto": "Auto",
"endpoint_mode_manual": "Manual",
"manual_endpoint_host": "Manual host",
"manual_endpoint_host_message": "Public host shown in release previews when manual mode is selected.",
"manual_endpoint_host_placeholder": "api-v2.truckwash.io",
"manual_endpoint_port": "Manual public port",
"manual_endpoint_port_message": "Optional public port from 1 to 65535.",
"app_port": "App port",
"app_port_message": "Internal container port exposed to Coolify routing.",
"auto_gateway_endpoint": "Automatic gateway endpoint",
"pending_automatic_endpoint": "Automatic endpoint resolution pending",
"auto_deploy": "Auto deploy",
"auto_deploy_tooltip": "Opret automatisk deployments, når dette mål ændres.",
"coolify_ssl": "Coolify SSL",
"coolify_ssl_tooltip": "Bed Coolify om at oprette HTTPS for det konfigurerede domæne.",
"deploy_now": "Deploy nu",
"deploy_now_tooltip": "Start en deployment straks efter målet gemmes.",
"create_missing_service": "Opret manglende service",
"create_missing_service_tooltip": "Tillad Release Manager at oprette en manglende Coolify-service.",
"test_access_tooltip": "Bekræft, at den konfigurerede GitHub-token kan læse dette repository og branch."
}
}
}
}
@@ -0,0 +1,61 @@
{
"compat": {
"configuration": {
"release_manager": {
"integrations": {
"title": "GitHub og Coolify",
"subtitle": "Knyt GitHub-repositories og branches til kanalspecifikke Coolify-services.",
"choose_repository": "Vælg repository",
"add_health_url": "tilføj health-URL",
"github_token_not_configured": "GitHub-token er ikke konfigureret; private repositories kan ikke vises.",
"open_github_token_settings": "Åbn GitHub token-indstillinger",
"channel": "Kanal",
"app": "App",
"repository": "Repository",
"repository_message": "Eksempel: truckwash/backend-php",
"repository_placeholder": "ejer/repo",
"branch": "Branch",
"branch_message": "Eksempel: release/canary",
"branch_placeholder": "branch",
"latest_prefix": "Seneste: {commit}",
"coolify_instance": "Coolify-instans",
"coolify_instance_message": "Lad feltet være tomt for manuelle deployment-poster.",
"no_coolify_instance": "Ingen Coolify-instans",
"coolify_instance_placeholder": "Coolify-instans-id",
"coolify_service_uuid": "Coolify service-UUID",
"coolify_service_uuid_message": "Vælg en eksisterende service eller indsæt en UUID.",
"coolify_service_uuid_placeholder": "Coolify service-UUID",
"health_url": "Health-URL",
"health_url_message": "Eksempel: https://api-canary.example.test/ping",
"health_url_placeholder": "Health-URL",
"ssl_domain": "Load balancer-domæne",
"ssl_domain_message": "Skal være et DNS-domæne, der routes til Coolify load balanceren. Eksempel: api-v2.truckwash.io",
"ssl_domain_placeholder": "Load balancer-domæne",
"https_domain_for_coolify": "Domæne kontrolleret af load balanceren",
"endpoint_mode": "Endpoint mode",
"endpoint_mode_message": "Use automatic gateway routing or manually override the public host and port.",
"endpoint_mode_auto": "Auto",
"endpoint_mode_manual": "Manual",
"manual_endpoint_host": "Manual host",
"manual_endpoint_host_message": "Public host shown in release previews when manual mode is selected.",
"manual_endpoint_host_placeholder": "api-v2.truckwash.io",
"manual_endpoint_port": "Manual public port",
"manual_endpoint_port_message": "Optional public port from 1 to 65535.",
"app_port": "App port",
"app_port_message": "Internal container port exposed to Coolify routing.",
"auto_gateway_endpoint": "Automatic gateway endpoint",
"pending_automatic_endpoint": "Automatic endpoint resolution pending",
"auto_deploy": "Auto deploy",
"auto_deploy_tooltip": "Opret automatisk deployments, når dette mål ændres.",
"coolify_ssl": "Coolify SSL",
"coolify_ssl_tooltip": "Bed Coolify om at oprette HTTPS for det konfigurerede domæne.",
"deploy_now": "Deploy nu",
"deploy_now_tooltip": "Start en deployment straks efter målet gemmes.",
"create_missing_service": "Opret manglende service",
"create_missing_service_tooltip": "Tillad Release Manager at oprette en manglende Coolify-service.",
"test_access_tooltip": "Bekræft, at den konfigurerede GitHub-token kan læse dette repository og branch."
}
}
}
}
}
@@ -0,0 +1,454 @@
{
"compat": {
"configuration": {
"release_manager": {
"title": "Release Manager",
"subtitle": "Gradvise API- og Front-End-udgivelser",
"channel_names": {
"stable": "Stabil",
"canary": "Canary",
"internal": "Intern"
},
"channel_descriptions": {
"stable": "Standard produktionskanal.",
"canary": "Tidlig produktionsvalideringskanal.",
"internal": "Intern kanal til medarbejdere og superbruger-validering."
},
"control_api": {
"title": "Kontrol-API",
"tooltip": "Release Manager-handlinger sendes til denne API. Brug produktions-API'en, medmindre du tester en staging-backend.",
"example": "Eksempel: https://api.truckwash.io:4433",
"endpoint_label": "API-endpoint",
"endpoint_message": "Skift kun dette, når Release Manager-endpoints er tilgængelige på mål-API'en.",
"endpoint_aria": "Release Manager kontrol-API URL",
"use_tooltip": "Indlæs release-data fra denne API",
"use": "Brug",
"reset_tooltip": "Vend tilbage til standard kontrol-API",
"reset": "Nulstil",
"known_endpoints": "Kendte kontrol-API-endpoints"
},
"tabs": {
"overview": {
"label": "Oversigt",
"description": "Kanalstatus, opsætningsfremdrift og seneste release-tilstand."
},
"channels": {
"label": "Kanaler",
"description": "Opret stabile, canary- og målrettede kanaler med rollout-grænser."
},
"assignments": {
"label": "Tildelinger",
"description": "Fastgør brugere, subbrugere eller kunder til en bestemt release-kanal."
},
"deployments": {
"label": "Udrulninger",
"description": "Start udrulninger og promover vellykkede builds."
},
"replay": {
"label": "Replay",
"description": "Aktivér målrettet opsamling og søg i release-tidslinjehændelser."
},
"integrations": {
"label": "Integrationer",
"description": "Forbind GitHub-repositories, branches og Coolify-services."
},
"settings": {
"label": "Indstillinger",
"description": "Konfigurer Release Managers GitHub-adgang og webhook-indstillinger."
}
},
"loading": {
"summary": "Indlæser Release Manager-data...",
"refreshing_summary": "Opdaterer Release Manager-data...",
"summary_detail": "Release-status, kanaler, deployments og service-health indlæses.",
"channels": "Indlæser release-kanaler...",
"channels_detail": "Kanalbaner og release-afhængigheder indlæses.",
"assignments": "Indlæser release-tildelinger...",
"deployments": "Indlæser release-deployments...",
"operations": "Indlæser release-operationer...",
"data_services": "Indlæser release-datatjenester...",
"integrations": "Indlæser release-integrationer...",
"settings": "Indlæser Release Manager-indstillinger...",
"github_repositories": "Indlæser GitHub-repositories...",
"timeline_sessions": "Indlæser release-tidslinjesessioner...",
"timeline_events": "Indlæser release-tidslinjehændelser...",
"refreshing_timeline": "Opdaterer release-tidslinje..."
},
"setup": {
"channels": "Kanaler",
"targets": "GitHub- og Coolify-mål",
"assignments": "Pilot-tildelinger",
"deployments": "Første udrulning",
"replay": "Replay-opsamling"
},
"stats": {
"channels": "Kanaler",
"targets": "Mål",
"deployments": "Udrulninger",
"timeline_events": "Tidslinjehændelser"
},
"overview": {
"title": "Oversigt",
"subtitle": "Aktuel kanalstatus, release-aktivitet og modulstatus.",
"guided_setup": "Guidet opsætning",
"next_step": "Næste: {step}",
"ready": "Release Manager er klar til daglig drift.",
"add_suggested_channel": "Tilføj foreslået kanal",
"module_health_empty": "Modul-health snapshots vises, når probes er blevet registreret."
},
"status": {
"confirm_issue_action": "Koer denne Release Manager-handling? Den kan aendre deployment-tilstand og bliver auditeret.",
"choose_bundle_prompt": "Indtast bundle-id'et, der skal saettes for denne kanal.",
"impact": "Konsekvens",
"cause": "Aarsag",
"automated_fix": "Automatisk rettelse",
"manual_fallback": "Manuel fallback",
"related_deployment": "Relateret deployment",
"recent_result": "Seneste resultat",
"no_automated_action": "Ingen automatisk handling tilgængelig.",
"deployment": "Deployment",
"target": "Maal",
"coolify_target": "Coolify-maal",
"service_set": "Service set"
},
"settings": {
"title": "Release Manager-indstillinger",
"subtitle": "Konfigurer GitHub-tokenet, der bruges til private repositories og branch-opslag.",
"github_token": "GitHub-token",
"github_api_url": "GitHub API-URL",
"webhook_secret": "Webhook-hemmelighed",
"github_webhook_secret": "GitHub webhook-hemmelighed",
"configured": "Konfigureret",
"not_configured": "Ikke konfigureret",
"loaded_from": "Indlæst fra {variable}",
"set_below": "Angiv {variable} nedenfor",
"private_repositories_prefix": "Private repositories læses med serverens miljøvariabel",
"private_repositories_or": "eller modulets konfigurationsvariabel",
"github_token_message": "Eksempel: github_pat_... med adgang til de private repositories, Release Manager udruller.",
"github_token_placeholder": "Lad feltet være tomt for at beholde det eksisterende token",
"github_api_url_message": "Konfigureret i ReleaseManager.github_api_url. Brug https://api.github.com medmindre GitHub Enterprise bruges.",
"webhook_secret_message": "Valgfrit; lad feltet være tomt for at beholde den eksisterende hemmelighed.",
"webhook_secret_placeholder": "Webhook HMAC-hemmelighed",
"save": "Gem indstillinger",
"back_to_integrations": "Tilbage til integrationer",
"guide": {
"steps": {
"github_token": "GitHub-token",
"github_api_url": "GitHub API-URL",
"github_webhook_secret": "GitHub webhook secret",
"review": "Gennemgang"
},
"github_token_title": "Tilføj GitHub-token",
"github_token_description": "Release Manager bruger denne token til at vise private repositories, branches og commits, før et deployment-mål gemmes eller testes.",
"configure_this_in": "Konfigurer dette i",
"server_environment_variable": "Serverens miljøvariabel",
"takes_precedence": "har forrang, når den er sat.",
"recommended_access": "Anbefalet adgang",
"recommended_access_description": "Fine-grained GitHub-token med Contents: read-adgang til hvert release-repository.",
"github_api_url_title": "Tilføj GitHub API-URL",
"github_api_url_description": "Brug GitHub Cloud som standard, eller peg på REST API-base-URLen for en GitHub Enterprise Server-installation.",
"github_api_url_invalid": "Indtast en fuld HTTP- eller HTTPS-URL.",
"github_cloud": "GitHub Cloud",
"enterprise_example": "Enterprise-eksempel",
"github_webhook_secret_title": "Tilføj GitHub webhook secret",
"github_webhook_secret_description": "Brug samme secret i GitHub repository-webhooks, så Release Manager kan verificere deployment-events, før de håndteres.",
"github_webhook_url": "GitHub webhook-URL:",
"store_shared_secret_in": "Gem den delte secret i",
"review_title": "Gennemgå GitHub-opsætning",
"review_description": "Gem værdierne, og brug derefter Integrationer til at vælge et privat repository, branch og specifik commit eller seneste commit før adgang testes.",
"ready": "Klar",
"needs_value": "Mangler værdi"
}
},
"channel_unavailable": {
"kicker": "Release Manager",
"title": "Release-kanalen er ikke klar",
"summary_prefix": "Din konto er tildelt",
"summary_suffix": ", men kanalen mangler påkrævet release-konfiguration.",
"release_bundle": "Release-bundle",
"frontend_version": "Frontend-version",
"api_version": "API-version",
"frontend_base_url": "Frontend-URL",
"api_base_url": "API-URL",
"frontend_entry": "Frontend-entry",
"release_runtime": "Release-runtime",
"checking_again": "Tjekker igen om {seconds}s",
"refresh_error": "Release-status kunne ikke opdateres. Det næste automatiske tjek prøver igen.",
"ignore": "Ignorer de næste 5 minutter",
"check_again": "Tjek igen",
"logout": "Log ud",
"base_image": "Basis-image"
},
"channel_switched": {
"kicker": "Release Manager",
"title": "Du er nu på {channel}",
"summary_prefix": "Din konto er blevet tildelt release-kanalen",
"summary_suffix": "Denne enhed husker, at du har set denne besked.",
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"frontend_version": "Frontend-version",
"api_version": "API-version",
"assigned_channel": "Tildelt kanal",
"current_app_image": "Nuværende app-image",
"current_api": "Nuværende API",
"base_image": "Basis-image",
"continue": "Fortsæt"
},
"channel_selector": {
"title": "Release-kanal",
"subtitle": "Vælg hvilken tildelt release-kanal denne enhed skal bruge.",
"sidebar_title": "Release",
"default": "Standard",
"ready": "Klar",
"unavailable": "Ikke klar",
"release_bundle": "Release-bundle",
"frontend_version": "Frontend-version",
"api_version": "API-version",
"frontend_base_url": "Frontend-URL",
"api_base_url": "API-URL",
"frontend_entry": "Frontend-entry",
"release_runtime": "Release-runtime",
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"use_local_frontend": "Brug lokal frontend",
"switch_error": "Release-kanalen kunne ikke skiftes. Den forrige kanal er stadig aktiv."
},
"common": {
"yes": "ja",
"no": "nej",
"off": "fra"
},
"actions": {
"edit": "Rediger",
"rollback": "Rul tilbage",
"update": "Opdater",
"create": "Opret",
"publish_release": "Publicer release",
"clear": "Ryd",
"assign": "Tildel",
"remove": "Fjern",
"test_access": "Test adgang",
"deploy": "Deploy",
"redeploy": "Redeploy",
"promote": "Promover",
"enable": "Aktiver",
"search": "Søg",
"save_target": "Gem mål",
"delete": "Slet",
"back": "Tilbage",
"next": "Næste"
},
"tables": {
"channel": "Kanal",
"rollout": "Udrulning",
"frontend": "Frontend",
"api": "API",
"replay": "Replay",
"actions": "Handlinger",
"module": "Modul",
"status": "Status",
"checked": "Kontrolleret",
"subject": "Emne",
"reason": "Årsag",
"expires": "Udløber",
"deployment": "Deployment",
"source": "Kilde",
"started": "Startet",
"target": "Mål",
"coolify": "Coolify",
"auto": "Auto"
},
"values": {
"app": {
"frontend": "frontend",
"api": "api"
},
"capture_level": {
"metadata": "metadata",
"full_redacted": "fuldt redigeret"
},
"subject_type": {
"user": "bruger",
"subuser": "medarbejder",
"customer": "kunde",
"channel": "kanal"
},
"severity": {
"error": "fejl",
"warning": "advarsel",
"info": "info"
},
"status": {
"ok": "ok",
"active": "aktiv",
"deployed": "deployet",
"stable": "stabil",
"enabled": "aktiveret",
"queued": "i kø",
"deploying": "deploying",
"metadata": "metadata",
"not_configured": "ikke konfigureret",
"warning": "advarsel",
"failed": "fejlet",
"down": "nede",
"error": "fejl",
"unknown": "ukendt"
}
},
"autocomplete": {
"suggested_value": "Foreslået værdi",
"suggestion": "Forslag",
"use_typed_value": "Brug indtastet værdi",
"custom": "Tilpasset",
"api_url": "API-URL",
"frontend_url": "Frontend-URL",
"known_release_url": "Kendt release-URL",
"deploy_here_with_coolify": "Deploy {app} her med Coolify",
"prepare_coolify_target": "Forbered et Coolify-mål med HTTPS/SSL aktiveret",
"coolify_ssl": "Coolify SSL",
"github_repository": "GitHub-repository",
"owner_repository": "ejer/repository",
"git_branch": "Git-branch",
"deployment_branch": "Deployment-branch",
"github_commit": "GitHub-commit",
"git_commit": "Git-commit",
"use_specific_commit_sha": "Brug specifik commit-SHA",
"custom_commit": "Tilpasset commit",
"health_url": "Health-URL",
"release_health_check": "Release health check",
"coolify_service": "Coolify-service",
"service_uuid": "Service-UUID",
"known_coolify_service_uuid": "Kendt Coolify service-UUID",
"control_api": "Kontrol-API",
"release_manager_api_endpoint": "Release Manager API-endpoint",
"module": "Modul",
"timeline_module_key": "Timeline-modulnøgle"
},
"errors": {
"github_repositories": "Kunne ikke indlæse GitHub-repositories.",
"github_branches": "Kunne ikke indlæse GitHub-branches.",
"repository_branch_required": "Repository og branch er påkrævet før GitHub-adgang kan testes.",
"github_access_failed": "Test af GitHub repository-adgang fejlede."
},
"channels": {
"title": "Kanaler",
"subtitle": "Opret og juster gradvise release-kanaler.",
"rollout_percent": "{percent}% udrulning",
"already_configured": "Allerede konfigureret",
"slug": "Slug",
"slug_message": "Eksempel: canary",
"slug_placeholder": "slug",
"name": "Navn",
"name_message": "Vises til operatører, når de gennemgår en kanal.",
"name_placeholder": "Navn",
"rollout": "Udrulning",
"rollout_message": "0-100 procent af ikke-tildelt trafik.",
"frontend_url": "Frontend-URL",
"frontend_url_message": "Eksempel: https://canary.example.test",
"frontend_url_placeholder": "Frontend-URL",
"no_matching_release_url": "Ingen matchende release-URL.",
"api_url": "API-URL",
"api_url_message": "Eksempel: https://api-canary.example.test",
"api_url_placeholder": "API-URL",
"no_matching_api_url": "Ingen matchende API-URL.",
"replay_capture": "Replay capture",
"replay_capture_message": "Metadata er sikrere til brede kanaler.",
"retention_days": "Opbevaringsdage",
"retention_days_message": "1-365 dage.",
"enabled": "Aktiveret",
"default_channel": "Standardkanal",
"replay_enabled": "Replay aktiveret",
"frontend_chip": "Frontend: {url}",
"api_chip": "API: {url}"
},
"assignments": {
"title": "Tildelinger",
"subtitle": "Fastgør brugere, medarbejdere eller kunder til release-kanaler.",
"subject": "Emne",
"subject_message": "Søg efter brugere, medarbejdere eller kunder, eller skriv et manuelt emne som user:42.",
"subject_placeholder": "Søg eller skriv emne",
"subject_empty": "Ingen emner fundet",
"group_users": "Brugere",
"group_subusers": "Medarbejdere",
"group_customers": "Kunder",
"group_manual": "Manuel",
"manual_user": "Bruger #{id}",
"manual_subuser": "Medarbejder #{id}",
"manual_customer": "Kunde #{id}",
"manual_description": "Brug indtastet værdi {subject}",
"subject_type": "Emnetype",
"subject_type_message": "Vælg identitetstypen, der skal fastgøres.",
"customer": "Kunde",
"customer_message": "Søg på kundenummer eller navn, og vælg derefter den præcise kunde.",
"customer_placeholder": "Søg kunde",
"subject_id": "Emne-ID",
"subject_id_message": "Eksempel: bruger-id 42",
"subject_id_placeholder": "Emne-id",
"channel": "Kanal",
"channel_message": "Fastgjorte emner springer procentudrulningen over.",
"reason": "Årsag",
"reason_message": "Eksempel: canary-validering for kundesupport.",
"reason_placeholder": "Årsag",
"expires": "Udløber",
"expires_message": "Lad feltet være tomt for ingen automatisk udløb."
},
"deployments": {
"title": "Deployments",
"subtitle": "Start, promover og inspicer release-deployments.",
"manual_deployment_record": "Manuel deployment-post",
"target": "Mål",
"target_message": "Vælg et gemt mål, eller behold manuelle felter redigerbare.",
"select_target_or_manual": "Vælg mål eller manuel",
"channel": "Kanal",
"app": "App",
"repository": "Repository",
"repository_message": "Eksempel: truckwash/front-end-vue",
"repository_placeholder": "ejer/repo",
"branch": "Branch",
"branch_message": "Eksempel: master",
"branch_placeholder": "branch",
"commit_selection": "Commit-valg",
"commit_selection_message": "Seneste slås op som branchens head med den konfigurerede GitHub-token.",
"latest_from_branch": "Seneste fra branch",
"specific_commit": "Specifik commit",
"specific_commit_message": "Eksempel: feedface1234",
"commit_sha_placeholder": "commit sha",
"version_label": "Versionslabel",
"version_label_message": "Eksempel: api-2026.05.19",
"version_label_placeholder": "versionslabel",
"commit_prefix": "Commit: {commit}",
"test_access_tooltip": "Bekræft token-adgang til det valgte repository, branch og commit."
},
"replay": {
"title": "Replay",
"subtitle": "Aktiver målrettet capture og gennemgå fejltimelines trin for trin.",
"capture_channel": "Capture {channel}",
"target_type": "Måltype",
"target": "Mål",
"target_message": "Eksempel: bruger-id 42 eller kanal-slug canary.",
"target_placeholder": "Mål-id eller kanal-slug",
"channel_scope": "Kanalafgrænsning",
"channel_scope_message": "Valgfrit; begrænser replay capture til en kanal.",
"no_channel_scope": "Ingen kanalafgrænsning",
"capture_level": "Capture-niveau",
"expires": "Udløber",
"expires_message": "Lad feltet være tomt for manuel deaktivering.",
"trace_id": "Trace-ID",
"trace_id_message": "Eksempel: trace-canary-1",
"trace_id_placeholder": "Trace-id",
"channel": "Kanal",
"channel_placeholder": "Kanal",
"principal_id": "Principal-ID",
"principal_id_placeholder": "Principal-id",
"module": "Modul",
"module_placeholder": "Modul",
"severity": "Alvorlighed",
"any_severity": "Alle alvorligheder",
"no_timeline_events": "Ingen timeline-events matcher de aktuelle filtre."
}
}
}
}
}
@@ -18,7 +18,6 @@
"loading_vehicle_types": "@.capitalize:{'terms.glossary.indlæser'} @:{'terms.glossary.tilgængelige'} @:{'terms.glossary.køretøjstyper'}...",
"lane_unavailable": "Vaskebanen er ikke tilgængelig",
"machine": "@.capitalize:{'terms.glossary.maskine'}",
"machine_unavailable_for_lane": "Maskinvask er ikke tilgængelig for den valgte bane. Vælg manuel vask eller en anden ledig bane.",
"no_departments_found": "@:{'phrases.compat.departments.no_departments'}",
"occupied": "Optaget",
"open_property_access_gate": "@:{'terms.glossary.abn'} adgangsport",
+1 -1
View File
@@ -83,7 +83,7 @@
"api": "API",
"app": "app",
"appen": "appen",
"ar": "år",
"ar": "@:{'terms.glossary.years'}",
"are": "are",
"arsag": "årsag",
"at": "at",
+1 -1
View File
@@ -18,7 +18,7 @@
"nøgle": "nøgle",
"nogle": "nogle",
"nok": "nok",
"nøkkel": "nøgle",
"nøkkel": "@:{'terms.glossary.nøgle'}",
"nøkkelen": "nøglen",
"not": "not",
"note": "note",
@@ -0,0 +1,28 @@
{
"compat": {
"admin": {
"pos": {
"settings_wheel": {
"accept_self_serve_wash": "Self-Service-Wäsche akzeptieren",
"accept_self_serve_wash_confirm": "Diesen Entwurf mit den Self-Service-Details zu Kunde #{customerNumber} verschieben?",
"accept_self_serve_wash_success": "Die Self-Service-Wäsche wurde akzeptiert.",
"email_notifications_section": "E-Mail-Benachrichtigungen",
"resend_booking_completion_confirmation": "(Re)send booking completion confirmation",
"resend_booking_completion_confirmation_error": "The booking completion confirmation could not be sent",
"resend_booking_completion_confirmation_success": "The booking completion confirmation has been sent",
"resend_booking_confirmation": "(Re)send booking confirmation",
"resend_booking_confirmation_error": "The booking confirmation could not be sent",
"resend_booking_confirmation_success": "The booking confirmation has been sent",
"resend_wash_certificate": "(Re)send wash certificate",
"resend_wash_certificate_error": "The wash certificate could not be sent",
"resend_wash_certificate_success": "The wash certificate has been sent",
"self_serve_customer": "Kunde",
"self_serve_driver": "Fahrer",
"self_serve_elapsed": "Zeit",
"self_serve_wash_attachment": "Self-Service-Wäsche",
"self_serve_wash_attachment_for_customer": "Self-Service-Wäsche für Kunde #{customerNumber}"
}
}
}
}
}
@@ -298,490 +298,6 @@
"subtitle": "@:{'terms.glossary.konfiguration'} @:{'terms.glossary.der'} @:{'terms.glossary.xlvask'}-@.capitalize:{'terms.glossary.integration'}",
"title": "@:{'terms.glossary.xlvask'}-@:{'terms.glossary.konfiguration'}",
"username_desc": "@.capitalize:{'terms.glossary.der'} @:{'terms.glossary.benutzername'} @:{'terms.glossary.wird'} @:{'terms.glossary.zur'} @:{'terms.glossary.authentifizierung'} @:{'terms.glossary.der'} @:{'terms.glossary.api'}-@:{'terms.glossary.verbindung'} @:{'terms.glossary.verwendet'}."
},
"release_manager": {
"title": "Release Manager",
"subtitle": "Schrittweise API- und Front-End-Releases",
"channel_names": {
"stable": "Stabil",
"canary": "Canary",
"internal": "Intern"
},
"channel_descriptions": {
"stable": "Standard-Produktionskanal.",
"canary": "Früher Produktionsvalidierungskanal.",
"internal": "Interner Kanal für Mitarbeitende und Superuser-Validierung."
},
"control_api": {
"title": "Kontroll-API",
"tooltip": "Release-Manager-Aktionen werden an diese API gesendet. Verwende die Produktions-Kontroll-API, außer du testest ein Staging-Backend.",
"example": "Beispiel: https://api.truckwash.io:4433",
"endpoint_label": "API-Endpunkt",
"endpoint_message": "Ändere dies nur, wenn Release-Manager-Endpunkte auf der Ziel-API verfügbar sind.",
"endpoint_aria": "Release Manager Kontroll-API-URL",
"use_tooltip": "Release-Daten von dieser API laden",
"use": "Verwenden",
"reset_tooltip": "Zur Standard-Kontroll-API zurückkehren",
"reset": "Zurücksetzen",
"known_endpoints": "Bekannte Kontroll-API-Endpunkte"
},
"tabs": {
"overview": {
"label": "Übersicht",
"description": "Kanalzustand, Einrichtungsfortschritt und aktueller Release-Status."
},
"channels": {
"label": "Kanäle",
"description": "Stabile, Canary- und Zielkanäle mit Rollout-Grenzen erstellen."
},
"assignments": {
"label": "Zuweisungen",
"description": "Benutzer, Unterbenutzer oder Kunden einem bestimmten Release-Kanal zuweisen."
},
"deployments": {
"label": "Bereitstellungen",
"description": "Bereitstellungen starten und erfolgreiche Builds promoten."
},
"replay": {
"label": "Replay",
"description": "Gezielte Erfassung aktivieren und Release-Timeline-Ereignisse durchsuchen."
},
"integrations": {
"label": "Integrationen",
"description": "GitHub-Repositories, Branches und Coolify-Dienste verbinden."
},
"settings": {
"label": "Einstellungen",
"description": "GitHub-Zugriff und Webhook-Einstellungen des Release Managers konfigurieren."
}
},
"loading": {
"summary": "Release Manager-Daten werden geladen...",
"refreshing_summary": "Release Manager-Daten werden aktualisiert...",
"summary_detail": "Release-Status, Kanale, Deployments und Servicezustand werden geladen.",
"channels": "Release-Kanale werden geladen...",
"channels_detail": "Kanalansichten und Release-Abhangigkeiten werden geladen.",
"assignments": "Release-Zuweisungen werden geladen...",
"deployments": "Release-Deployments werden geladen...",
"operations": "Release-Operationen werden geladen...",
"data_services": "Release-Datendienste werden geladen...",
"integrations": "Release-Integrationen werden geladen...",
"settings": "Release Manager-Einstellungen werden geladen...",
"github_repositories": "GitHub-Repositories werden geladen...",
"timeline_sessions": "Release-Timeline-Sessions werden geladen...",
"timeline_events": "Release-Timeline-Ereignisse werden geladen...",
"refreshing_timeline": "Release-Timeline wird aktualisiert..."
},
"setup": {
"channels": "Kanäle",
"targets": "GitHub- und Coolify-Ziele",
"assignments": "Pilot-Zuweisungen",
"deployments": "Erste Bereitstellung",
"replay": "Replay-Erfassung"
},
"stats": {
"channels": "Kanäle",
"targets": "Ziele",
"deployments": "Bereitstellungen",
"timeline_events": "Timeline-Ereignisse"
},
"overview": {
"title": "Übersicht",
"subtitle": "Aktueller Kanalzustand, Release-Aktivität und Modulstatus.",
"guided_setup": "Geführte Einrichtung",
"next_step": "Nächster Schritt: {step}",
"ready": "Release Manager ist für den täglichen Betrieb bereit.",
"add_suggested_channel": "Vorgeschlagenen Kanal hinzufügen",
"module_health_empty": "Modul-Health-Snapshots erscheinen, nachdem Probes aufgezeichnet wurden."
},
"settings": {
"title": "Release-Manager-Einstellungen",
"subtitle": "GitHub-Token für private Repositories und Branch-Abfragen konfigurieren.",
"github_token": "GitHub-Token",
"github_api_url": "GitHub-API-URL",
"webhook_secret": "Webhook-Secret",
"github_webhook_secret": "GitHub-Webhook-Secret",
"configured": "Konfiguriert",
"not_configured": "Nicht konfiguriert",
"loaded_from": "Geladen aus {variable}",
"set_below": "{variable} unten festlegen",
"private_repositories_prefix": "Private Repositories werden mit der Server-Umgebungsvariable gelesen",
"private_repositories_or": "oder der Modul-Konfigurationsvariable",
"github_token_message": "Beispiel: github_pat_... mit Zugriff auf die privaten Repositories, die Release Manager bereitstellt.",
"github_token_placeholder": "Leer lassen, um das vorhandene Token zu behalten",
"github_api_url_message": "Konfiguriert in ReleaseManager.github_api_url. https://api.github.com verwenden, sofern GitHub Enterprise nicht genutzt wird.",
"webhook_secret_message": "Optional; leer lassen, um das vorhandene Secret zu behalten.",
"webhook_secret_placeholder": "Webhook-HMAC-Secret",
"save": "Einstellungen speichern",
"back_to_integrations": "Zurück zu Integrationen",
"guide": {
"steps": {
"github_token": "GitHub-Token",
"github_api_url": "GitHub API-URL",
"github_webhook_secret": "GitHub Webhook Secret",
"review": "Prüfung"
},
"github_token_title": "GitHub-Token hinzufügen",
"github_token_description": "Release Manager verwendet diesen Token, um private Repositories, Branches und Commits aufzulisten, bevor ein Deployment-Ziel gespeichert oder getestet wird.",
"configure_this_in": "Dies konfigurieren in",
"server_environment_variable": "Die Server-Umgebungsvariable",
"takes_precedence": "hat Vorrang, wenn sie gesetzt ist.",
"recommended_access": "Empfohlener Zugriff",
"recommended_access_description": "Fine-grained GitHub-Token mit Contents: read-Zugriff für jedes Release-Repository.",
"github_api_url_title": "GitHub API-URL hinzufügen",
"github_api_url_description": "GitHub Cloud standardmäßig verwenden oder auf die REST-API-Basis-URL einer GitHub Enterprise Server-Installation zeigen.",
"github_api_url_invalid": "Vollständige HTTP- oder HTTPS-URL eingeben.",
"github_cloud": "GitHub Cloud",
"enterprise_example": "Enterprise-Beispiel",
"github_webhook_secret_title": "GitHub Webhook Secret hinzufügen",
"github_webhook_secret_description": "Dasselbe Secret in GitHub-Repository-Webhooks verwenden, damit Release Manager Deployment-Events vor der Verarbeitung verifizieren kann.",
"github_webhook_url": "GitHub Webhook-URL:",
"store_shared_secret_in": "Geteiltes Secret speichern in",
"review_title": "GitHub-Konfiguration prüfen",
"review_description": "Werte speichern und dann unter Integrationen ein privates Repository, einen Branch und einen bestimmten Commit oder den neuesten Commit auswählen, bevor der Zugriff getestet wird.",
"ready": "Bereit",
"needs_value": "Wert fehlt"
}
},
"channel_unavailable": {
"kicker": "Release Manager",
"title": "Release-Kanal ist nicht bereit",
"summary_prefix": "Dein Konto ist zugewiesen zu",
"summary_suffix": ", aber diesem Kanal fehlt erforderliche Release-Konfiguration.",
"release_bundle": "Release-Bundle",
"frontend_version": "Frontend-Version",
"api_version": "API-Version",
"frontend_base_url": "Frontend-URL",
"api_base_url": "API-URL",
"frontend_entry": "Frontend-Einstiegspunkt",
"release_runtime": "Release-Laufzeit",
"checking_again": "Erneute Prüfung in {seconds}s",
"refresh_error": "Release-Status konnte nicht aktualisiert werden. Die nächste automatische Prüfung versucht es erneut.",
"ignore": "Nächste 5 Minuten ignorieren",
"check_again": "Erneut prüfen",
"logout": "Abmelden",
"base_image": "Basis-Image"
},
"channel_switched": {
"kicker": "Release Manager",
"title": "Du bist jetzt auf {channel}",
"summary_prefix": "Dein Konto wurde dem Release-Kanal",
"summary_suffix": "zugewiesen. Dieses Gerät merkt sich, dass du diesen Hinweis gesehen hast.",
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"frontend_version": "Frontend-Version",
"api_version": "API-Version",
"assigned_channel": "Zugewiesener Kanal",
"current_app_image": "Aktuelles App-Image",
"current_api": "Aktuelle API",
"base_image": "Basis-Image",
"continue": "Fortfahren"
},
"channel_selector": {
"title": "Release-Kanal",
"subtitle": "Wählen Sie, welchen zugewiesenen Release-Kanal dieses Gerät verwenden soll.",
"sidebar_title": "Release",
"default": "Standard",
"ready": "Bereit",
"unavailable": "Nicht bereit",
"release_bundle": "Release-Bundle",
"frontend_version": "Frontend-Version",
"api_version": "API-Version",
"frontend_base_url": "Frontend-URL",
"api_base_url": "API-URL",
"frontend_entry": "Frontend-Einstiegspunkt",
"release_runtime": "Release-Laufzeit",
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"use_local_frontend": "Lokales Frontend verwenden"
},
"common": {
"yes": "ja",
"no": "nein",
"off": "aus"
},
"actions": {
"edit": "Bearbeiten",
"rollback": "Zurückrollen",
"update": "Aktualisieren",
"create": "Erstellen",
"clear": "Leeren",
"assign": "Zuweisen",
"remove": "Entfernen",
"test_access": "Zugriff testen",
"deploy": "Deployen",
"redeploy": "Erneut deployen",
"promote": "Promoten",
"enable": "Aktivieren",
"search": "Suchen",
"save_target": "Ziel speichern",
"delete": "Löschen",
"back": "Zurück",
"next": "Weiter"
},
"tables": {
"channel": "Kanal",
"rollout": "Rollout",
"frontend": "Frontend",
"api": "API",
"replay": "Replay",
"actions": "Aktionen",
"module": "Module",
"status": "Status",
"checked": "Geprüft",
"subject": "Subjekt",
"reason": "Grund",
"expires": "Läuft ab",
"deployment": "Deployment",
"source": "Quelle",
"started": "Gestartet",
"target": "Ziel",
"coolify": "Coolify",
"auto": "Auto"
},
"values": {
"app": {
"frontend": "Frontend",
"api": "API"
},
"capture_level": {
"metadata": "Metadaten",
"full_redacted": "vollständig redigiert"
},
"subject_type": {
"user": "Benutzer",
"subuser": "Mitarbeiter",
"customer": "Kunde",
"channel": "Kanal"
},
"severity": {
"error": "Fehler",
"warning": "Warnung",
"info": "Info"
},
"status": {
"ok": "ok",
"active": "aktiv",
"deployed": "deployt",
"stable": "stabil",
"enabled": "aktiviert",
"queued": "in Warteschlange",
"deploying": "deploying",
"metadata": "Metadaten",
"not_configured": "nicht konfiguriert",
"warning": "Warnung",
"failed": "fehlgeschlagen",
"down": "down",
"error": "Fehler",
"unknown": "unbekannt"
}
},
"autocomplete": {
"suggested_value": "Vorgeschlagener Wert",
"suggestion": "Vorschlag",
"use_typed_value": "Eingegebenen Wert verwenden",
"custom": "Benutzerdefiniert",
"api_url": "API URL",
"frontend_url": "Frontend URL",
"known_release_url": "Bekannte Release-URL",
"deploy_here_with_coolify": "{app} hier mit Coolify deployen",
"prepare_coolify_target": "Coolify-Ziel mit aktiviertem HTTPS/SSL vorbereiten",
"coolify_ssl": "Coolify SSL",
"github_repository": "GitHub-Repository",
"owner_repository": "owner/repository",
"git_branch": "Git branch",
"deployment_branch": "Deployment branch",
"github_commit": "GitHub commit",
"git_commit": "Git commit",
"use_specific_commit_sha": "Bestimmte Commit-SHA verwenden",
"custom_commit": "Benutzerdefinierter Commit",
"health_url": "Health URL",
"release_health_check": "Release-Health-Check",
"coolify_service": "Coolify service",
"service_uuid": "Service UUID",
"known_coolify_service_uuid": "Known Coolify service UUID",
"control_api": "Kontroll-API",
"release_manager_api_endpoint": "Release Manager API-Endpunkt",
"module": "Module",
"timeline_module_key": "Timeline-Modulschlüssel"
},
"errors": {
"github_repositories": "GitHub-Repositories konnten nicht geladen werden.",
"github_branches": "GitHub-Branches konnten nicht geladen werden.",
"repository_branch_required": "Repository und Branch sind erforderlich, bevor GitHub-Zugriff getestet werden kann.",
"github_access_failed": "GitHub-Repository-Zugriffstest ist fehlgeschlagen."
},
"channels": {
"title": "Kanäle",
"subtitle": "Graduale Release-Kanäle erstellen und anpassen.",
"rollout_percent": "{percent}% rollout",
"already_configured": "Bereits konfiguriert",
"slug": "Slug",
"slug_message": "Example: canary",
"slug_placeholder": "slug",
"name": "Name",
"name_message": "Wird Operatoren bei der Kanalprüfung angezeigt.",
"name_placeholder": "Name",
"rollout": "Rollout",
"rollout_message": "0-100 Prozent des nicht zugewiesenen Traffics.",
"frontend_url": "Frontend URL",
"frontend_url_message": "Example: https://canary.example.test",
"frontend_url_placeholder": "Frontend URL",
"no_matching_release_url": "Keine passende Release-URL.",
"api_url": "API URL",
"api_url_message": "Example: https://api-canary.example.test",
"api_url_placeholder": "API URL",
"no_matching_api_url": "Keine passende API-URL.",
"replay_capture": "Replay-Erfassung",
"replay_capture_message": "Metadaten sind für breite Kanäle sicherer.",
"retention_days": "Aufbewahrungstage",
"retention_days_message": "1-365 Tage.",
"enabled": "Aktiviert",
"default_channel": "Standardkanal",
"replay_enabled": "Replay aktiviert",
"frontend_chip": "Frontend: {url}",
"api_chip": "API: {url}"
},
"assignments": {
"title": "Zuweisungen",
"subtitle": "Benutzer, Mitarbeiter oder Kunden an Release-Kanäle binden.",
"subject": "Subjekt",
"subject_message": "Benutzer, Subuser oder Kunden suchen oder ein manuelles Subjekt wie user:42 eingeben.",
"subject_placeholder": "Subjekt suchen oder eingeben",
"subject_empty": "Keine Subjekte gefunden",
"group_users": "Benutzer",
"group_subusers": "Subuser",
"group_customers": "Kunden",
"group_manual": "Manuell",
"manual_user": "Benutzer #{id}",
"manual_subuser": "Subuser #{id}",
"manual_customer": "Kunde #{id}",
"manual_description": "Eingegebenen Wert {subject} verwenden",
"subject_type": "Subjekttyp",
"subject_type_message": "Identitätstyp zum Binden auswählen.",
"customer": "Customer",
"customer_message": "Nach Kundennummer oder Name suchen und dann den genauen Kunden auswählen.",
"customer_placeholder": "Kunde suchen",
"subject_id": "Subjekt-ID",
"subject_id_message": "Beispiel: Benutzer-ID 42",
"subject_id_placeholder": "Subjekt-ID",
"channel": "Channel",
"channel_message": "Gebundene Subjekte umgehen den prozentualen Rollout.",
"reason": "Grund",
"reason_message": "Beispiel: Canary-Validierung für Kundensupport.",
"reason_placeholder": "Grund",
"expires": "Läuft ab",
"expires_message": "Leer lassen für kein automatisches Ablaufdatum."
},
"deployments": {
"title": "Deployments",
"subtitle": "Release-Deployments starten, promoten und prüfen.",
"manual_deployment_record": "Manueller Deployment-Eintrag",
"target": "Ziel",
"target_message": "Gespeichertes Ziel wählen oder manuelle Felder editierbar lassen.",
"select_target_or_manual": "Ziel oder manuell auswählen",
"channel": "Channel",
"app": "App",
"repository": "Repository",
"repository_message": "Example: truckwash/front-end-vue",
"repository_placeholder": "owner/repo",
"branch": "Branch",
"branch_message": "Example: master",
"branch_placeholder": "branch",
"commit_selection": "Commit-Auswahl",
"commit_selection_message": "Neueste Version wird mit dem konfigurierten GitHub-Token als Branch-Head aufgelöst.",
"latest_from_branch": "Neuester vom Branch",
"specific_commit": "Bestimmter Commit",
"specific_commit_message": "Example: feedface1234",
"commit_sha_placeholder": "commit sha",
"version_label": "Versionslabel",
"version_label_message": "Example: api-2026.05.19",
"version_label_placeholder": "Versionslabel",
"commit_prefix": "Commit: {commit}",
"test_access_tooltip": "Token-Zugriff auf das ausgewählte Repository, den Branch und Commit prüfen."
},
"replay": {
"title": "Replay",
"subtitle": "Gezielte Erfassung aktivieren und Fehler-Timelines Schritt für Schritt prüfen.",
"capture_channel": "{channel} erfassen",
"target_type": "Zieltyp",
"target": "Ziel",
"target_message": "Beispiel: Benutzer-ID 42 oder Kanal-Slug canary.",
"target_placeholder": "Ziel-ID oder Kanal-Slug",
"channel_scope": "Kanalbereich",
"channel_scope_message": "Optional; begrenzt Replay-Erfassung auf einen Kanal.",
"no_channel_scope": "Kein Kanalbereich",
"capture_level": "Erfassungsstufe",
"expires": "Läuft ab",
"expires_message": "Leer lassen für manuelle Deaktivierung.",
"trace_id": "Trace ID",
"trace_id_message": "Example: trace-canary-1",
"trace_id_placeholder": "Trace id",
"channel": "Channel",
"channel_placeholder": "Channel",
"principal_id": "Principal ID",
"principal_id_placeholder": "Principal-ID",
"module": "Module",
"module_placeholder": "Module",
"severity": "Schweregrad",
"any_severity": "Beliebiger Schweregrad",
"no_timeline_events": "Keine Timeline-Events entsprechen den aktuellen Filtern."
},
"integrations": {
"title": "GitHub und Coolify",
"subtitle": "GitHub-Repositories und Branches kanalspezifischen Coolify-Services zuordnen.",
"choose_repository": "Repository auswählen",
"add_health_url": "Health-URL hinzufügen",
"github_token_not_configured": "GitHub-Token ist nicht konfiguriert; private Repositories können nicht aufgelistet werden.",
"open_github_token_settings": "GitHub-Token-Einstellungen öffnen",
"channel": "Channel",
"app": "App",
"repository": "Repository",
"repository_message": "Example: truckwash/backend-php",
"repository_placeholder": "owner/repo",
"branch": "Branch",
"branch_message": "Example: release/canary",
"branch_placeholder": "branch",
"latest_prefix": "Neueste: {commit}",
"coolify_instance": "Coolify-Instanz",
"coolify_instance_message": "Leer lassen für manuelle Deployment-Einträge.",
"no_coolify_instance": "Keine Coolify-Instanz",
"coolify_instance_placeholder": "Coolify-Instanz-ID",
"coolify_service_uuid": "Coolify service UUID",
"coolify_service_uuid_message": "Bestehenden Service auswählen oder UUID einfügen.",
"coolify_service_uuid_placeholder": "Coolify service UUID",
"health_url": "Health URL",
"health_url_message": "Example: https://api-canary.example.test/ping",
"health_url_placeholder": "Health URL",
"ssl_domain": "Load-Balancer-Domain",
"ssl_domain_message": "Muss eine DNS-Domain sein, die zum Coolify Load Balancer geroutet wird. Beispiel: api-v2.truckwash.io",
"ssl_domain_placeholder": "Load-Balancer-Domain",
"https_domain_for_coolify": "Domain unter Kontrolle des Load Balancers",
"endpoint_mode": "Endpoint mode",
"endpoint_mode_message": "Use automatic gateway routing or manually override the public host and port.",
"endpoint_mode_auto": "Auto",
"endpoint_mode_manual": "Manual",
"manual_endpoint_host": "Manual host",
"manual_endpoint_host_message": "Public host shown in release previews when manual mode is selected.",
"manual_endpoint_host_placeholder": "api-v2.truckwash.io",
"manual_endpoint_port": "Manual public port",
"manual_endpoint_port_message": "Optional public port from 1 to 65535.",
"app_port": "App port",
"app_port_message": "Internal container port exposed to Coolify routing.",
"auto_gateway_endpoint": "Automatic gateway endpoint",
"pending_automatic_endpoint": "Automatic endpoint resolution pending",
"auto_deploy": "Auto-Deploy",
"auto_deploy_tooltip": "Automatisch Deployments erstellen, wenn sich dieses Ziel ändert.",
"coolify_ssl": "Coolify SSL",
"coolify_ssl_tooltip": "Coolify anweisen, HTTPS für die konfigurierte Domain bereitzustellen.",
"deploy_now": "Jetzt deployen",
"deploy_now_tooltip": "Deployment direkt nach dem Speichern des Ziels starten.",
"create_missing_service": "Fehlenden Service erstellen",
"create_missing_service_tooltip": "Release Manager erlauben, einen fehlenden Coolify-Service zu erstellen.",
"test_access_tooltip": "Prüfen, ob der konfigurierte GitHub-Token dieses Repository und den Branch lesen kann."
}
}
}
}
@@ -0,0 +1,61 @@
{
"compat": {
"configuration": {
"release_manager": {
"integrations": {
"title": "GitHub und Coolify",
"subtitle": "GitHub-Repositories und Branches kanalspezifischen Coolify-Services zuordnen.",
"choose_repository": "Repository auswählen",
"add_health_url": "Health-URL hinzufügen",
"github_token_not_configured": "GitHub-Token ist nicht konfiguriert; private Repositories können nicht aufgelistet werden.",
"open_github_token_settings": "GitHub-Token-Einstellungen öffnen",
"channel": "Channel",
"app": "App",
"repository": "Repository",
"repository_message": "Example: truckwash/backend-php",
"repository_placeholder": "owner/repo",
"branch": "Branch",
"branch_message": "Example: release/canary",
"branch_placeholder": "branch",
"latest_prefix": "Neueste: {commit}",
"coolify_instance": "Coolify-Instanz",
"coolify_instance_message": "Leer lassen für manuelle Deployment-Einträge.",
"no_coolify_instance": "Keine Coolify-Instanz",
"coolify_instance_placeholder": "Coolify-Instanz-ID",
"coolify_service_uuid": "Coolify service UUID",
"coolify_service_uuid_message": "Bestehenden Service auswählen oder UUID einfügen.",
"coolify_service_uuid_placeholder": "Coolify service UUID",
"health_url": "Health URL",
"health_url_message": "Example: https://api-canary.example.test/ping",
"health_url_placeholder": "Health URL",
"ssl_domain": "Load-Balancer-Domain",
"ssl_domain_message": "Muss eine DNS-Domain sein, die zum Coolify Load Balancer geroutet wird. Beispiel: api-v2.truckwash.io",
"ssl_domain_placeholder": "Load-Balancer-Domain",
"https_domain_for_coolify": "Domain unter Kontrolle des Load Balancers",
"endpoint_mode": "Endpoint mode",
"endpoint_mode_message": "Use automatic gateway routing or manually override the public host and port.",
"endpoint_mode_auto": "Auto",
"endpoint_mode_manual": "Manual",
"manual_endpoint_host": "Manual host",
"manual_endpoint_host_message": "Public host shown in release previews when manual mode is selected.",
"manual_endpoint_host_placeholder": "api-v2.truckwash.io",
"manual_endpoint_port": "Manual public port",
"manual_endpoint_port_message": "Optional public port from 1 to 65535.",
"app_port": "App port",
"app_port_message": "Internal container port exposed to Coolify routing.",
"auto_gateway_endpoint": "Automatic gateway endpoint",
"pending_automatic_endpoint": "Automatic endpoint resolution pending",
"auto_deploy": "Auto-Deploy",
"auto_deploy_tooltip": "Automatisch Deployments erstellen, wenn sich dieses Ziel ändert.",
"coolify_ssl": "Coolify SSL",
"coolify_ssl_tooltip": "Coolify anweisen, HTTPS für die konfigurierte Domain bereitzustellen.",
"deploy_now": "Jetzt deployen",
"deploy_now_tooltip": "Deployment direkt nach dem Speichern des Ziels starten.",
"create_missing_service": "Fehlenden Service erstellen",
"create_missing_service_tooltip": "Release Manager erlauben, einen fehlenden Coolify-Service zu erstellen.",
"test_access_tooltip": "Prüfen, ob der konfigurierte GitHub-Token dieses Repository und den Branch lesen kann."
}
}
}
}
}
@@ -0,0 +1,454 @@
{
"compat": {
"configuration": {
"release_manager": {
"title": "Release Manager",
"subtitle": "Schrittweise API- und Front-End-Releases",
"channel_names": {
"stable": "Stabil",
"canary": "Canary",
"internal": "Intern"
},
"channel_descriptions": {
"stable": "Standard-Produktionskanal.",
"canary": "Früher Produktionsvalidierungskanal.",
"internal": "Interner Kanal für Mitarbeitende und Superuser-Validierung."
},
"control_api": {
"title": "Kontroll-API",
"tooltip": "Release-Manager-Aktionen werden an diese API gesendet. Verwende die Produktions-Kontroll-API, außer du testest ein Staging-Backend.",
"example": "Beispiel: https://api.truckwash.io:4433",
"endpoint_label": "API-Endpunkt",
"endpoint_message": "Ändere dies nur, wenn Release-Manager-Endpunkte auf der Ziel-API verfügbar sind.",
"endpoint_aria": "Release Manager Kontroll-API-URL",
"use_tooltip": "Release-Daten von dieser API laden",
"use": "Verwenden",
"reset_tooltip": "Zur Standard-Kontroll-API zurückkehren",
"reset": "Zurücksetzen",
"known_endpoints": "Bekannte Kontroll-API-Endpunkte"
},
"tabs": {
"overview": {
"label": "Übersicht",
"description": "Kanalzustand, Einrichtungsfortschritt und aktueller Release-Status."
},
"channels": {
"label": "Kanäle",
"description": "Stabile, Canary- und Zielkanäle mit Rollout-Grenzen erstellen."
},
"assignments": {
"label": "Zuweisungen",
"description": "Benutzer, Unterbenutzer oder Kunden einem bestimmten Release-Kanal zuweisen."
},
"deployments": {
"label": "Bereitstellungen",
"description": "Bereitstellungen starten und erfolgreiche Builds promoten."
},
"replay": {
"label": "Replay",
"description": "Gezielte Erfassung aktivieren und Release-Timeline-Ereignisse durchsuchen."
},
"integrations": {
"label": "Integrationen",
"description": "GitHub-Repositories, Branches und Coolify-Dienste verbinden."
},
"settings": {
"label": "Einstellungen",
"description": "GitHub-Zugriff und Webhook-Einstellungen des Release Managers konfigurieren."
}
},
"loading": {
"summary": "Release Manager-Daten werden geladen...",
"refreshing_summary": "Release Manager-Daten werden aktualisiert...",
"summary_detail": "Release-Status, Kanale, Deployments und Servicezustand werden geladen.",
"channels": "Release-Kanale werden geladen...",
"channels_detail": "Kanalansichten und Release-Abhangigkeiten werden geladen.",
"assignments": "Release-Zuweisungen werden geladen...",
"deployments": "Release-Deployments werden geladen...",
"operations": "Release-Operationen werden geladen...",
"data_services": "Release-Datendienste werden geladen...",
"integrations": "Release-Integrationen werden geladen...",
"settings": "Release Manager-Einstellungen werden geladen...",
"github_repositories": "GitHub-Repositories werden geladen...",
"timeline_sessions": "Release-Timeline-Sessions werden geladen...",
"timeline_events": "Release-Timeline-Ereignisse werden geladen...",
"refreshing_timeline": "Release-Timeline wird aktualisiert..."
},
"setup": {
"channels": "Kanäle",
"targets": "GitHub- und Coolify-Ziele",
"assignments": "Pilot-Zuweisungen",
"deployments": "Erste Bereitstellung",
"replay": "Replay-Erfassung"
},
"stats": {
"channels": "Kanäle",
"targets": "Ziele",
"deployments": "Bereitstellungen",
"timeline_events": "Timeline-Ereignisse"
},
"overview": {
"title": "Übersicht",
"subtitle": "Aktueller Kanalzustand, Release-Aktivität und Modulstatus.",
"guided_setup": "Geführte Einrichtung",
"next_step": "Nächster Schritt: {step}",
"ready": "Release Manager ist für den täglichen Betrieb bereit.",
"add_suggested_channel": "Vorgeschlagenen Kanal hinzufügen",
"module_health_empty": "Modul-Health-Snapshots erscheinen, nachdem Probes aufgezeichnet wurden."
},
"settings": {
"title": "Release-Manager-Einstellungen",
"subtitle": "GitHub-Token für private Repositories und Branch-Abfragen konfigurieren.",
"github_token": "GitHub-Token",
"github_api_url": "GitHub-API-URL",
"webhook_secret": "Webhook-Secret",
"github_webhook_secret": "GitHub-Webhook-Secret",
"configured": "Konfiguriert",
"not_configured": "Nicht konfiguriert",
"loaded_from": "Geladen aus {variable}",
"set_below": "{variable} unten festlegen",
"private_repositories_prefix": "Private Repositories werden mit der Server-Umgebungsvariable gelesen",
"private_repositories_or": "oder der Modul-Konfigurationsvariable",
"github_token_message": "Beispiel: github_pat_... mit Zugriff auf die privaten Repositories, die Release Manager bereitstellt.",
"github_token_placeholder": "Leer lassen, um das vorhandene Token zu behalten",
"github_api_url_message": "Konfiguriert in ReleaseManager.github_api_url. https://api.github.com verwenden, sofern GitHub Enterprise nicht genutzt wird.",
"webhook_secret_message": "Optional; leer lassen, um das vorhandene Secret zu behalten.",
"webhook_secret_placeholder": "Webhook-HMAC-Secret",
"save": "Einstellungen speichern",
"back_to_integrations": "Zurück zu Integrationen",
"guide": {
"steps": {
"github_token": "GitHub-Token",
"github_api_url": "GitHub API-URL",
"github_webhook_secret": "GitHub Webhook Secret",
"review": "Prüfung"
},
"github_token_title": "GitHub-Token hinzufügen",
"github_token_description": "Release Manager verwendet diesen Token, um private Repositories, Branches und Commits aufzulisten, bevor ein Deployment-Ziel gespeichert oder getestet wird.",
"configure_this_in": "Dies konfigurieren in",
"server_environment_variable": "Die Server-Umgebungsvariable",
"takes_precedence": "hat Vorrang, wenn sie gesetzt ist.",
"recommended_access": "Empfohlener Zugriff",
"recommended_access_description": "Fine-grained GitHub-Token mit Contents: read-Zugriff für jedes Release-Repository.",
"github_api_url_title": "GitHub API-URL hinzufügen",
"github_api_url_description": "GitHub Cloud standardmäßig verwenden oder auf die REST-API-Basis-URL einer GitHub Enterprise Server-Installation zeigen.",
"github_api_url_invalid": "Vollständige HTTP- oder HTTPS-URL eingeben.",
"github_cloud": "GitHub Cloud",
"enterprise_example": "Enterprise-Beispiel",
"github_webhook_secret_title": "GitHub Webhook Secret hinzufügen",
"github_webhook_secret_description": "Dasselbe Secret in GitHub-Repository-Webhooks verwenden, damit Release Manager Deployment-Events vor der Verarbeitung verifizieren kann.",
"github_webhook_url": "GitHub Webhook-URL:",
"store_shared_secret_in": "Geteiltes Secret speichern in",
"review_title": "GitHub-Konfiguration prüfen",
"review_description": "Werte speichern und dann unter Integrationen ein privates Repository, einen Branch und einen bestimmten Commit oder den neuesten Commit auswählen, bevor der Zugriff getestet wird.",
"ready": "Bereit",
"needs_value": "Wert fehlt"
}
},
"channel_unavailable": {
"kicker": "Release Manager",
"title": "Release-Kanal ist nicht bereit",
"summary_prefix": "Dein Konto ist zugewiesen zu",
"summary_suffix": ", aber diesem Kanal fehlt erforderliche Release-Konfiguration.",
"release_bundle": "Release-Bundle",
"frontend_version": "Frontend-Version",
"api_version": "API-Version",
"frontend_base_url": "Frontend-URL",
"api_base_url": "API-URL",
"frontend_entry": "Frontend-Einstiegspunkt",
"release_runtime": "Release-Laufzeit",
"checking_again": "Erneute Prüfung in {seconds}s",
"refresh_error": "Release-Status konnte nicht aktualisiert werden. Die nächste automatische Prüfung versucht es erneut.",
"ignore": "Nächste 5 Minuten ignorieren",
"check_again": "Erneut prüfen",
"logout": "Abmelden",
"base_image": "Basis-Image"
},
"channel_switched": {
"kicker": "Release Manager",
"title": "Du bist jetzt auf {channel}",
"summary_prefix": "Dein Konto wurde dem Release-Kanal",
"summary_suffix": "zugewiesen. Dieses Gerät merkt sich, dass du diesen Hinweis gesehen hast.",
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"frontend_version": "Frontend-Version",
"api_version": "API-Version",
"assigned_channel": "Zugewiesener Kanal",
"current_app_image": "Aktuelles App-Image",
"current_api": "Aktuelle API",
"base_image": "Basis-Image",
"continue": "Fortfahren"
},
"channel_selector": {
"title": "Release-Kanal",
"subtitle": "Wählen Sie, welchen zugewiesenen Release-Kanal dieses Gerät verwenden soll.",
"sidebar_title": "Release",
"default": "Standard",
"ready": "Bereit",
"unavailable": "Nicht bereit",
"release_bundle": "Release-Bundle",
"frontend_version": "Frontend-Version",
"api_version": "API-Version",
"frontend_base_url": "Frontend-URL",
"api_base_url": "API-URL",
"frontend_entry": "Frontend-Einstiegspunkt",
"release_runtime": "Release-Laufzeit",
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"use_local_frontend": "Lokales Frontend verwenden",
"switch_error": "Release channel could not be switched. The previous channel is still active."
},
"common": {
"yes": "ja",
"no": "nein",
"off": "aus"
},
"actions": {
"edit": "Bearbeiten",
"rollback": "Zurückrollen",
"update": "Aktualisieren",
"create": "Erstellen",
"clear": "Leeren",
"assign": "Zuweisen",
"remove": "Entfernen",
"test_access": "Zugriff testen",
"deploy": "Deployen",
"redeploy": "Erneut deployen",
"promote": "Promoten",
"enable": "Aktivieren",
"search": "Suchen",
"save_target": "Ziel speichern",
"delete": "Löschen",
"back": "Zurück",
"next": "Weiter",
"publish_release": "Publish release"
},
"tables": {
"channel": "Kanal",
"rollout": "Rollout",
"frontend": "Frontend",
"api": "API",
"replay": "Replay",
"actions": "Aktionen",
"module": "Module",
"status": "Status",
"checked": "Geprüft",
"subject": "Subjekt",
"reason": "Grund",
"expires": "Läuft ab",
"deployment": "Deployment",
"source": "Quelle",
"started": "Gestartet",
"target": "Ziel",
"coolify": "Coolify",
"auto": "Auto"
},
"values": {
"app": {
"frontend": "Frontend",
"api": "API"
},
"capture_level": {
"metadata": "Metadaten",
"full_redacted": "vollständig redigiert"
},
"subject_type": {
"user": "Benutzer",
"subuser": "Mitarbeiter",
"customer": "Kunde",
"channel": "Kanal"
},
"severity": {
"error": "Fehler",
"warning": "Warnung",
"info": "Info"
},
"status": {
"ok": "ok",
"active": "aktiv",
"deployed": "deployt",
"stable": "stabil",
"enabled": "aktiviert",
"queued": "in Warteschlange",
"deploying": "deploying",
"metadata": "Metadaten",
"not_configured": "nicht konfiguriert",
"warning": "Warnung",
"failed": "fehlgeschlagen",
"down": "down",
"error": "Fehler",
"unknown": "unbekannt"
}
},
"autocomplete": {
"suggested_value": "Vorgeschlagener Wert",
"suggestion": "Vorschlag",
"use_typed_value": "Eingegebenen Wert verwenden",
"custom": "Benutzerdefiniert",
"api_url": "API URL",
"frontend_url": "Frontend URL",
"known_release_url": "Bekannte Release-URL",
"deploy_here_with_coolify": "{app} hier mit Coolify deployen",
"prepare_coolify_target": "Coolify-Ziel mit aktiviertem HTTPS/SSL vorbereiten",
"coolify_ssl": "Coolify SSL",
"github_repository": "GitHub-Repository",
"owner_repository": "owner/repository",
"git_branch": "Git branch",
"deployment_branch": "Deployment branch",
"github_commit": "GitHub commit",
"git_commit": "Git commit",
"use_specific_commit_sha": "Bestimmte Commit-SHA verwenden",
"custom_commit": "Benutzerdefinierter Commit",
"health_url": "Health URL",
"release_health_check": "Release-Health-Check",
"coolify_service": "Coolify service",
"service_uuid": "Service UUID",
"known_coolify_service_uuid": "Known Coolify service UUID",
"control_api": "Kontroll-API",
"release_manager_api_endpoint": "Release Manager API-Endpunkt",
"module": "Module",
"timeline_module_key": "Timeline-Modulschlüssel"
},
"errors": {
"github_repositories": "GitHub-Repositories konnten nicht geladen werden.",
"github_branches": "GitHub-Branches konnten nicht geladen werden.",
"repository_branch_required": "Repository und Branch sind erforderlich, bevor GitHub-Zugriff getestet werden kann.",
"github_access_failed": "GitHub-Repository-Zugriffstest ist fehlgeschlagen."
},
"channels": {
"title": "Kanäle",
"subtitle": "Graduale Release-Kanäle erstellen und anpassen.",
"rollout_percent": "{percent}% rollout",
"already_configured": "Bereits konfiguriert",
"slug": "Slug",
"slug_message": "Example: canary",
"slug_placeholder": "slug",
"name": "Name",
"name_message": "Wird Operatoren bei der Kanalprüfung angezeigt.",
"name_placeholder": "Name",
"rollout": "Rollout",
"rollout_message": "0-100 Prozent des nicht zugewiesenen Traffics.",
"frontend_url": "Frontend URL",
"frontend_url_message": "Example: https://canary.example.test",
"frontend_url_placeholder": "Frontend URL",
"no_matching_release_url": "Keine passende Release-URL.",
"api_url": "API URL",
"api_url_message": "Example: https://api-canary.example.test",
"api_url_placeholder": "API URL",
"no_matching_api_url": "Keine passende API-URL.",
"replay_capture": "Replay-Erfassung",
"replay_capture_message": "Metadaten sind für breite Kanäle sicherer.",
"retention_days": "Aufbewahrungstage",
"retention_days_message": "1-365 Tage.",
"enabled": "Aktiviert",
"default_channel": "Standardkanal",
"replay_enabled": "Replay aktiviert",
"frontend_chip": "Frontend: {url}",
"api_chip": "API: {url}"
},
"assignments": {
"title": "Zuweisungen",
"subtitle": "Benutzer, Mitarbeiter oder Kunden an Release-Kanäle binden.",
"subject": "Subjekt",
"subject_message": "Benutzer, Subuser oder Kunden suchen oder ein manuelles Subjekt wie user:42 eingeben.",
"subject_placeholder": "Subjekt suchen oder eingeben",
"subject_empty": "Keine Subjekte gefunden",
"group_users": "Benutzer",
"group_subusers": "Subuser",
"group_customers": "Kunden",
"group_manual": "Manuell",
"manual_user": "Benutzer #{id}",
"manual_subuser": "Subuser #{id}",
"manual_customer": "Kunde #{id}",
"manual_description": "Eingegebenen Wert {subject} verwenden",
"subject_type": "Subjekttyp",
"subject_type_message": "Identitätstyp zum Binden auswählen.",
"customer": "Customer",
"customer_message": "Nach Kundennummer oder Name suchen und dann den genauen Kunden auswählen.",
"customer_placeholder": "Kunde suchen",
"subject_id": "Subjekt-ID",
"subject_id_message": "Beispiel: Benutzer-ID 42",
"subject_id_placeholder": "Subjekt-ID",
"channel": "Channel",
"channel_message": "Gebundene Subjekte umgehen den prozentualen Rollout.",
"reason": "Grund",
"reason_message": "Beispiel: Canary-Validierung für Kundensupport.",
"reason_placeholder": "Grund",
"expires": "Läuft ab",
"expires_message": "Leer lassen für kein automatisches Ablaufdatum."
},
"deployments": {
"title": "Deployments",
"subtitle": "Release-Deployments starten, promoten und prüfen.",
"manual_deployment_record": "Manueller Deployment-Eintrag",
"target": "Ziel",
"target_message": "Gespeichertes Ziel wählen oder manuelle Felder editierbar lassen.",
"select_target_or_manual": "Ziel oder manuell auswählen",
"channel": "Channel",
"app": "App",
"repository": "Repository",
"repository_message": "Example: truckwash/front-end-vue",
"repository_placeholder": "owner/repo",
"branch": "Branch",
"branch_message": "Example: master",
"branch_placeholder": "branch",
"commit_selection": "Commit-Auswahl",
"commit_selection_message": "Neueste Version wird mit dem konfigurierten GitHub-Token als Branch-Head aufgelöst.",
"latest_from_branch": "Neuester vom Branch",
"specific_commit": "Bestimmter Commit",
"specific_commit_message": "Example: feedface1234",
"commit_sha_placeholder": "commit sha",
"version_label": "Versionslabel",
"version_label_message": "Example: api-2026.05.19",
"version_label_placeholder": "Versionslabel",
"commit_prefix": "Commit: {commit}",
"test_access_tooltip": "Token-Zugriff auf das ausgewählte Repository, den Branch und Commit prüfen."
},
"replay": {
"title": "Replay",
"subtitle": "Gezielte Erfassung aktivieren und Fehler-Timelines Schritt für Schritt prüfen.",
"capture_channel": "{channel} erfassen",
"target_type": "Zieltyp",
"target": "Ziel",
"target_message": "Beispiel: Benutzer-ID 42 oder Kanal-Slug canary.",
"target_placeholder": "Ziel-ID oder Kanal-Slug",
"channel_scope": "Kanalbereich",
"channel_scope_message": "Optional; begrenzt Replay-Erfassung auf einen Kanal.",
"no_channel_scope": "Kein Kanalbereich",
"capture_level": "Erfassungsstufe",
"expires": "Läuft ab",
"expires_message": "Leer lassen für manuelle Deaktivierung.",
"trace_id": "Trace ID",
"trace_id_message": "Example: trace-canary-1",
"trace_id_placeholder": "Trace id",
"channel": "Channel",
"channel_placeholder": "Channel",
"principal_id": "Principal ID",
"principal_id_placeholder": "Principal-ID",
"module": "Module",
"module_placeholder": "Module",
"severity": "Schweregrad",
"any_severity": "Beliebiger Schweregrad",
"no_timeline_events": "Keine Timeline-Events entsprechen den aktuellen Filtern."
},
"status": {
"confirm_issue_action": "Run this Release Manager action? It can change deployment state and will be audited.",
"choose_bundle_prompt": "Enter the bundle id to set for this channel.",
"impact": "Impact",
"cause": "Cause",
"automated_fix": "Automated fix",
"manual_fallback": "Manual fallback",
"related_deployment": "Related deployment",
"recent_result": "Recent result",
"no_automated_action": "No automated action available.",
"deployment": "Deployment",
"target": "Target",
"coolify_target": "Coolify target",
"service_set": "Service set"
}
}
}
}
}
@@ -18,7 +18,6 @@
"loading_vehicle_types": "@.capitalize:{'terms.glossary.verf'}?@:{'terms.glossary.gbare'} @:{'terms.glossary.fahrzeugtypen'} @:{'terms.glossary.werden'} @:{'terms.glossary.geladen'}...",
"lane_unavailable": "Waschspur nicht verfugbar",
"machine": "@:{'terms.glossary.maschine'}",
"machine_unavailable_for_lane": "Maschinenwasche ist fur die ausgewahlte Spur nicht verfugbar. Wahlen Sie manuelle Wasche oder eine andere freie Spur.",
"no_departments_found": "@:{'phrases.compat.departments.no_departments'}",
"occupied": "Belegt",
"open_property_access_gate": "Grundstueckszugangstor @:{'terms.glossary.oeffnen'}",
+1 -1
View File
@@ -8,7 +8,7 @@
"uhrzeit": "Uhrzeit",
"um": "um",
"umbenannt": "umbenannt",
"umbenennen": "umbenennen",
"umbenennen": "@:{'terms.glossary.rename'}",
"umsatz": "Umsatz",
"umweltgeb": "Umweltgeb",
"unabhangig": "unabhängig",
@@ -0,0 +1,28 @@
{
"compat": {
"admin": {
"pos": {
"settings_wheel": {
"accept_self_serve_wash": "Accept self-serve wash",
"accept_self_serve_wash_confirm": "Move this draft to customer #{customerNumber} with the self-serve details?",
"accept_self_serve_wash_success": "The self-serve wash was accepted.",
"email_notifications_section": "Email notifications",
"resend_booking_completion_confirmation": "(Re)send booking completion confirmation",
"resend_booking_completion_confirmation_error": "The booking completion confirmation could not be sent",
"resend_booking_completion_confirmation_success": "The booking completion confirmation has been sent",
"resend_booking_confirmation": "(Re)send booking confirmation",
"resend_booking_confirmation_error": "The booking confirmation could not be sent",
"resend_booking_confirmation_success": "The booking confirmation has been sent",
"resend_wash_certificate": "(Re)send wash certificate",
"resend_wash_certificate_error": "The wash certificate could not be sent",
"resend_wash_certificate_success": "The wash certificate has been sent",
"self_serve_customer": "Customer",
"self_serve_driver": "Driver",
"self_serve_elapsed": "Elapsed",
"self_serve_wash_attachment": "Self-serve wash",
"self_serve_wash_attachment_for_customer": "Self-serve wash for customer #{customerNumber}"
}
}
}
}
}
@@ -298,507 +298,6 @@
"subtitle": "@.capitalize:{'terms.glossary.configuration'} @:{'terms.glossary.of'} @:{'terms.replication.article.host_mention'} @:{'terms.glossary.xlvask'} @:{'terms.glossary.integration'}",
"title": "@:{'terms.glossary.xlvask'} @:{'terms.glossary.configuration'}",
"username_desc": "@.capitalize:{'terms.replication.article.host_mention'} @:{'terms.glossary.username'} @:{'terms.glossary.is'} @:{'terms.glossary.used'} @:{'terms.glossary.to'} @:{'terms.glossary.authenticate'} @:{'terms.replication.article.host_mention'} @:{'terms.glossary.api'} @:{'terms.glossary.connection'}."
},
"release_manager": {
"title": "Release Manager",
"subtitle": "Gradual API and Front-End releases",
"channel_names": {
"stable": "Stable",
"canary": "Canary",
"internal": "Internal"
},
"channel_descriptions": {
"stable": "Default production channel.",
"canary": "Early production validation channel.",
"internal": "Internal staff and superuser validation channel."
},
"control_api": {
"title": "Control API",
"tooltip": "Release Manager actions are sent to this API. Use the production control API unless testing a staged backend.",
"example": "Example: https://api.truckwash.io:4433",
"endpoint_label": "API endpoint",
"endpoint_message": "Switch this only when Release Manager endpoints are available on the target API.",
"endpoint_aria": "Release Manager control API URL",
"use_tooltip": "Load release data from this API",
"use": "Use",
"reset_tooltip": "Return to the default control API",
"reset": "Reset",
"known_endpoints": "Known control API endpoints"
},
"tabs": {
"overview": {
"label": "Overview",
"description": "Channel health, setup progress, and the latest release state."
},
"channels": {
"label": "Channels",
"description": "Create stable, canary, and targeted channels with rollout limits."
},
"assignments": {
"label": "Assignments",
"description": "Pin users, subusers, or customers to a specific release channel."
},
"deployments": {
"label": "Deployments",
"description": "Trigger deployments and promote successful builds."
},
"replay": {
"label": "Replay",
"description": "Enable targeted capture and search release timeline events."
},
"integrations": {
"label": "Integrations",
"description": "Connect GitHub repositories, branches, and Coolify services."
},
"settings": {
"label": "Settings",
"description": "Configure Release Manager GitHub access and webhook settings."
}
},
"loading": {
"summary": "Loading release manager data...",
"refreshing_summary": "Refreshing release manager data...",
"summary_detail": "Release status, channels, deployments, and service health are being loaded.",
"channels": "Loading release channels...",
"channels_detail": "Channel lanes and release dependencies are being loaded.",
"assignments": "Loading release assignments...",
"deployments": "Loading release deployments...",
"operations": "Loading release operations...",
"data_services": "Loading release data services...",
"integrations": "Loading release integrations...",
"settings": "Loading Release Manager settings...",
"github_repositories": "Loading GitHub repositories...",
"timeline_sessions": "Loading release timeline sessions...",
"timeline_events": "Loading release timeline events...",
"refreshing_timeline": "Refreshing release timeline..."
},
"setup": {
"channels": "Channels",
"targets": "GitHub and Coolify targets",
"assignments": "Pilot assignments",
"deployments": "First deployment",
"replay": "Replay capture"
},
"stats": {
"channels": "Channels",
"targets": "Targets",
"deployments": "Deployments",
"timeline_events": "Timeline events"
},
"overview": {
"title": "Overview",
"subtitle": "Current channel health, release activity, and module status.",
"guided_setup": "Guided setup",
"next_step": "Next: {step}",
"ready": "Release Manager is ready for day-to-day operations.",
"add_suggested_channel": "Add suggested channel",
"module_health_empty": "Module health snapshots appear after probes have been recorded."
},
"status": {
"confirm_issue_action": "Run this Release Manager action? It can change deployment state and will be audited.",
"choose_bundle_prompt": "Enter the bundle id to set for this channel.",
"impact": "Impact",
"cause": "Cause",
"automated_fix": "Automated fix",
"manual_fallback": "Manual fallback",
"related_deployment": "Related deployment",
"recent_result": "Recent result",
"no_automated_action": "No automated action available.",
"deployment": "Deployment",
"target": "Target",
"coolify_target": "Coolify target",
"service_set": "Service set"
},
"settings": {
"title": "Release Manager Settings",
"subtitle": "Configure the GitHub token used for private repositories and branch lookups.",
"github_token": "GitHub token",
"github_api_url": "GitHub API URL",
"webhook_secret": "Webhook secret",
"github_webhook_secret": "GitHub webhook secret",
"configured": "Configured",
"not_configured": "Not configured",
"loaded_from": "Loaded from {variable}",
"set_below": "Set {variable} below",
"private_repositories_prefix": "Private repositories are read with the server environment variable",
"private_repositories_or": "or the module config variable",
"github_token_message": "Example: github_pat_... with access to the private repositories Release Manager deploys.",
"github_token_placeholder": "Leave blank to keep the existing token",
"github_api_url_message": "Configured in ReleaseManager.github_api_url. Use https://api.github.com unless using GitHub Enterprise.",
"webhook_secret_message": "Optional; leave blank to keep the existing secret.",
"webhook_secret_placeholder": "Webhook HMAC secret",
"save": "Save settings",
"back_to_integrations": "Back to integrations",
"guide": {
"steps": {
"github_token": "GitHub token",
"github_api_url": "GitHub API URL",
"github_webhook_secret": "GitHub webhook secret",
"review": "Review"
},
"github_token_title": "Add GitHub token",
"github_token_description": "Release Manager uses this token to list private repositories, branches, and commits before a deployment target is saved or tested.",
"configure_this_in": "Configure this in",
"server_environment_variable": "The server environment variable",
"takes_precedence": "takes precedence when it is set.",
"recommended_access": "Recommended access",
"recommended_access_description": "Fine-grained GitHub token with Contents: read access for each release repository.",
"github_api_url_title": "Add GitHub API URL",
"github_api_url_description": "Use GitHub Cloud by default, or point this at the REST API base URL for a GitHub Enterprise Server installation.",
"github_api_url_invalid": "Enter a full HTTP or HTTPS URL.",
"github_cloud": "GitHub Cloud",
"enterprise_example": "Enterprise example",
"github_webhook_secret_title": "Add GitHub webhook secret",
"github_webhook_secret_description": "Use the same secret in GitHub repository webhooks so Release Manager can verify deployment events before handling them.",
"github_webhook_url": "GitHub webhook URL:",
"store_shared_secret_in": "Store the shared secret in",
"review_title": "Review GitHub setup",
"review_description": "Save the values, then use Integrations to select a private repository, branch, and specific commit or latest commit before testing access.",
"ready": "Ready",
"needs_value": "Needs value"
}
},
"channel_unavailable": {
"kicker": "Release Manager",
"title": "Release channel is not ready",
"summary_prefix": "Your account is assigned to",
"summary_suffix": ", but that channel is missing required release configuration.",
"release_bundle": "Release bundle",
"frontend_version": "Frontend version",
"api_version": "API version",
"frontend_base_url": "Frontend URL",
"api_base_url": "API URL",
"frontend_entry": "Frontend entry",
"release_runtime": "Release runtime",
"checking_again": "Checking again in {seconds}s",
"refresh_error": "Release status could not be refreshed. The next automatic check will try again.",
"ignore": "Ignore next 5 minutes",
"check_again": "Check again",
"logout": "Logout",
"base_image": "Base image"
},
"channel_switched": {
"kicker": "Release Manager",
"title": "You are now on {channel}",
"summary_prefix": "Your account has been assigned to the",
"summary_suffix": "release channel. This device will remember that you have seen this notice.",
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"frontend_version": "Frontend version",
"api_version": "API version",
"assigned_channel": "Assigned channel",
"current_app_image": "Current app image",
"current_api": "Current API",
"base_image": "Base image",
"continue": "Continue"
},
"channel_selector": {
"title": "Release channel",
"subtitle": "Choose which assigned release channel this device should use.",
"sidebar_title": "Release",
"default": "Default",
"ready": "Ready",
"unavailable": "Not ready",
"release_bundle": "Release bundle",
"frontend_version": "Frontend version",
"api_version": "API version",
"frontend_base_url": "Frontend URL",
"api_base_url": "API URL",
"frontend_entry": "Frontend entry",
"release_runtime": "Release runtime",
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"use_local_frontend": "Use local frontend",
"switch_error": "Release channel could not be switched. The previous channel is still active."
},
"common": {
"yes": "yes",
"no": "no",
"off": "off"
},
"actions": {
"edit": "Edit",
"rollback": "Rollback",
"update": "Update",
"create": "Create",
"publish_release": "Publish release",
"clear": "Clear",
"assign": "Assign",
"remove": "Remove",
"test_access": "Test access",
"deploy": "Deploy",
"redeploy": "Redeploy",
"promote": "Promote",
"enable": "Enable",
"search": "Search",
"save_target": "Save target",
"delete": "Delete",
"back": "Back",
"next": "Next"
},
"tables": {
"channel": "Channel",
"rollout": "Rollout",
"frontend": "Frontend",
"api": "API",
"replay": "Replay",
"actions": "Actions",
"module": "Module",
"status": "Status",
"checked": "Checked",
"subject": "Subject",
"reason": "Reason",
"expires": "Expires",
"deployment": "Deployment",
"source": "Source",
"started": "Started",
"target": "Target",
"coolify": "Coolify",
"auto": "Auto"
},
"values": {
"app": {
"frontend": "frontend",
"api": "api"
},
"capture_level": {
"metadata": "metadata",
"full_redacted": "full_redacted"
},
"subject_type": {
"user": "user",
"subuser": "subuser",
"customer": "customer",
"channel": "channel"
},
"severity": {
"error": "error",
"warning": "warning",
"info": "info"
},
"status": {
"ok": "ok",
"active": "active",
"deployed": "deployed",
"stable": "stable",
"enabled": "enabled",
"queued": "queued",
"deploying": "deploying",
"metadata": "metadata",
"not_configured": "not configured",
"warning": "warning",
"failed": "failed",
"down": "down",
"error": "error",
"unknown": "unknown"
}
},
"autocomplete": {
"suggested_value": "Suggested value",
"suggestion": "Suggestion",
"use_typed_value": "Use typed value",
"custom": "Custom",
"api_url": "API URL",
"frontend_url": "Frontend URL",
"known_release_url": "Known release URL",
"deploy_here_with_coolify": "Deploy {app} here with Coolify",
"prepare_coolify_target": "Prepare a Coolify target with HTTPS/SSL enabled",
"coolify_ssl": "Coolify SSL",
"github_repository": "GitHub repository",
"owner_repository": "owner/repository",
"git_branch": "Git branch",
"deployment_branch": "Deployment branch",
"github_commit": "GitHub commit",
"git_commit": "Git commit",
"use_specific_commit_sha": "Use specific commit SHA",
"custom_commit": "Custom commit",
"health_url": "Health URL",
"release_health_check": "Release health check",
"coolify_service": "Coolify service",
"service_uuid": "Service UUID",
"known_coolify_service_uuid": "Known Coolify service UUID",
"control_api": "Control API",
"release_manager_api_endpoint": "Release Manager API endpoint",
"module": "Module",
"timeline_module_key": "Timeline module key"
},
"errors": {
"github_repositories": "Could not load GitHub repositories.",
"github_branches": "Could not load GitHub branches.",
"repository_branch_required": "Repository and branch are required before testing GitHub access.",
"github_access_failed": "GitHub repository access test failed."
},
"channels": {
"title": "Channels",
"subtitle": "Create and tune gradual release channels.",
"rollout_percent": "{percent}% rollout",
"already_configured": "Already configured",
"slug": "Slug",
"slug_message": "Example: canary",
"slug_placeholder": "slug",
"name": "Name",
"name_message": "Shown to operators when reviewing a channel.",
"name_placeholder": "Name",
"rollout": "Rollout",
"rollout_message": "0-100 percent of unassigned traffic.",
"frontend_url": "Frontend URL",
"frontend_url_message": "Example: https://canary.example.test",
"frontend_url_placeholder": "Frontend URL",
"no_matching_release_url": "No matching release URL.",
"api_url": "API URL",
"api_url_message": "Example: https://api-canary.example.test",
"api_url_placeholder": "API URL",
"no_matching_api_url": "No matching API URL.",
"replay_capture": "Replay capture",
"replay_capture_message": "Metadata is safer for broad channels.",
"retention_days": "Retention days",
"retention_days_message": "1-365 days.",
"enabled": "Enabled",
"default_channel": "Default channel",
"replay_enabled": "Replay enabled",
"frontend_chip": "Frontend: {url}",
"api_chip": "API: {url}"
},
"assignments": {
"title": "Assignments",
"subtitle": "Pin users, subusers, or customers to release channels.",
"subject": "Subject",
"subject_message": "Search users, subusers, or customers, or type a manual subject like user:42.",
"subject_placeholder": "Search or type subject",
"subject_empty": "No subjects found",
"group_users": "Users",
"group_subusers": "Subusers",
"group_customers": "Customers",
"group_manual": "Manual",
"manual_user": "User #{id}",
"manual_subuser": "Subuser #{id}",
"manual_customer": "Customer #{id}",
"manual_description": "Use typed value {subject}",
"subject_type": "Subject type",
"subject_type_message": "Choose the identity type to pin.",
"customer": "Customer",
"customer_message": "Search by customer number or name, then select the exact customer.",
"customer_placeholder": "Search customer",
"subject_id": "Subject ID",
"subject_id_message": "Example: user id 42",
"subject_id_placeholder": "Subject id",
"channel": "Channel",
"channel_message": "Pinned subjects bypass percentage rollout.",
"reason": "Reason",
"reason_message": "Example: canary validation for customer support.",
"reason_placeholder": "Reason",
"expires": "Expires",
"expires_message": "Leave blank for no automatic expiry."
},
"deployments": {
"title": "Deployments",
"subtitle": "Trigger, promote, and inspect release deployments.",
"manual_deployment_record": "Manual deployment record",
"target": "Target",
"target_message": "Pick a saved target or keep manual fields editable.",
"select_target_or_manual": "Select target or manual",
"channel": "Channel",
"app": "App",
"repository": "Repository",
"repository_message": "Example: truckwash/front-end-vue",
"repository_placeholder": "owner/repo",
"branch": "Branch",
"branch_message": "Example: master",
"branch_placeholder": "branch",
"commit_selection": "Commit selection",
"commit_selection_message": "Latest resolves to the branch head with the configured GitHub token.",
"latest_from_branch": "Latest from branch",
"specific_commit": "Specific commit",
"specific_commit_message": "Example: feedface1234",
"commit_sha_placeholder": "commit sha",
"version_label": "Version label",
"version_label_message": "Example: api-2026.05.19",
"version_label_placeholder": "version label",
"commit_prefix": "Commit: {commit}",
"test_access_tooltip": "Verify token access to the selected repository, branch, and commit."
},
"replay": {
"title": "Replay",
"subtitle": "Enable targeted capture and step through failure timelines.",
"capture_channel": "Capture {channel}",
"target_type": "Target type",
"target": "Target",
"target_message": "Example: user id 42 or channel slug canary.",
"target_placeholder": "Target id or channel slug",
"channel_scope": "Channel scope",
"channel_scope_message": "Optional; limits replay capture to a channel.",
"no_channel_scope": "No channel scope",
"capture_level": "Capture level",
"expires": "Expires",
"expires_message": "Leave blank for manual disable.",
"trace_id": "Trace ID",
"trace_id_message": "Example: trace-canary-1",
"trace_id_placeholder": "Trace id",
"channel": "Channel",
"channel_placeholder": "Channel",
"principal_id": "Principal ID",
"principal_id_placeholder": "Principal id",
"module": "Module",
"module_placeholder": "Module",
"severity": "Severity",
"any_severity": "Any severity",
"no_timeline_events": "No timeline events match the current filters."
},
"integrations": {
"title": "GitHub and Coolify",
"subtitle": "Map GitHub repositories and branches to channel-specific Coolify services.",
"choose_repository": "Choose repository",
"add_health_url": "add health URL",
"github_token_not_configured": "GitHub token is not configured; private repositories cannot be listed.",
"open_github_token_settings": "Open GitHub token settings",
"channel": "Channel",
"app": "App",
"repository": "Repository",
"repository_message": "Example: truckwash/backend-php",
"repository_placeholder": "owner/repo",
"branch": "Branch",
"branch_message": "Example: release/canary",
"branch_placeholder": "branch",
"latest_prefix": "Latest: {commit}",
"coolify_instance": "Coolify instance",
"coolify_instance_message": "Leave empty for manual deployment records.",
"no_coolify_instance": "No Coolify instance",
"coolify_instance_placeholder": "Coolify instance id",
"coolify_service_uuid": "Coolify service UUID",
"coolify_service_uuid_message": "Select an existing service or paste a UUID.",
"coolify_service_uuid_placeholder": "Coolify service UUID",
"health_url": "Health URL",
"health_url_message": "Example: https://api-canary.example.test/ping",
"health_url_placeholder": "Health URL",
"ssl_domain": "Load balancer domain",
"ssl_domain_message": "Must be a DNS domain routed to the Coolify load balancer. Example: api-v2.truckwash.io",
"ssl_domain_placeholder": "Load balancer domain",
"https_domain_for_coolify": "Domain controlled by the load balancer",
"endpoint_mode": "Endpoint mode",
"endpoint_mode_message": "Use automatic gateway routing or manually override the public host and port.",
"endpoint_mode_auto": "Auto",
"endpoint_mode_manual": "Manual",
"manual_endpoint_host": "Manual host",
"manual_endpoint_host_message": "Public host shown in release previews when manual mode is selected.",
"manual_endpoint_host_placeholder": "api-v2.truckwash.io",
"manual_endpoint_port": "Manual public port",
"manual_endpoint_port_message": "Optional public port from 1 to 65535.",
"app_port": "App port",
"app_port_message": "Internal container port exposed to Coolify routing.",
"auto_gateway_endpoint": "Automatic gateway endpoint",
"pending_automatic_endpoint": "Automatic endpoint resolution pending",
"auto_deploy": "Auto deploy",
"auto_deploy_tooltip": "Automatically create deployments when this target changes.",
"coolify_ssl": "Coolify SSL",
"coolify_ssl_tooltip": "Ask Coolify to provision HTTPS for the configured domain.",
"deploy_now": "Deploy now",
"deploy_now_tooltip": "Start a deployment immediately after saving the target.",
"create_missing_service": "Create missing service",
"create_missing_service_tooltip": "Allow Release Manager to create a missing Coolify service.",
"test_access_tooltip": "Verify the configured GitHub token can read this repository and branch."
}
}
}
}
@@ -0,0 +1,61 @@
{
"compat": {
"configuration": {
"release_manager": {
"integrations": {
"title": "GitHub and Coolify",
"subtitle": "Map GitHub repositories and branches to channel-specific Coolify services.",
"choose_repository": "Choose repository",
"add_health_url": "add health URL",
"github_token_not_configured": "GitHub token is not configured; private repositories cannot be listed.",
"open_github_token_settings": "Open GitHub token settings",
"channel": "Channel",
"app": "App",
"repository": "Repository",
"repository_message": "Example: truckwash/backend-php",
"repository_placeholder": "owner/repo",
"branch": "Branch",
"branch_message": "Example: release/canary",
"branch_placeholder": "branch",
"latest_prefix": "Latest: {commit}",
"coolify_instance": "Coolify instance",
"coolify_instance_message": "Leave empty for manual deployment records.",
"no_coolify_instance": "No Coolify instance",
"coolify_instance_placeholder": "Coolify instance id",
"coolify_service_uuid": "Coolify service UUID",
"coolify_service_uuid_message": "Select an existing service or paste a UUID.",
"coolify_service_uuid_placeholder": "Coolify service UUID",
"health_url": "Health URL",
"health_url_message": "Example: https://api-canary.example.test/ping",
"health_url_placeholder": "Health URL",
"ssl_domain": "Load balancer domain",
"ssl_domain_message": "Must be a DNS domain routed to the Coolify load balancer. Example: api-v2.truckwash.io",
"ssl_domain_placeholder": "Load balancer domain",
"https_domain_for_coolify": "Domain controlled by the load balancer",
"endpoint_mode": "Endpoint mode",
"endpoint_mode_message": "Use automatic gateway routing or manually override the public host and port.",
"endpoint_mode_auto": "Auto",
"endpoint_mode_manual": "Manual",
"manual_endpoint_host": "Manual host",
"manual_endpoint_host_message": "Public host shown in release previews when manual mode is selected.",
"manual_endpoint_host_placeholder": "api-v2.truckwash.io",
"manual_endpoint_port": "Manual public port",
"manual_endpoint_port_message": "Optional public port from 1 to 65535.",
"app_port": "App port",
"app_port_message": "Internal container port exposed to Coolify routing.",
"auto_gateway_endpoint": "Automatic gateway endpoint",
"pending_automatic_endpoint": "Automatic endpoint resolution pending",
"auto_deploy": "Auto deploy",
"auto_deploy_tooltip": "Automatically create deployments when this target changes.",
"coolify_ssl": "Coolify SSL",
"coolify_ssl_tooltip": "Ask Coolify to provision HTTPS for the configured domain.",
"deploy_now": "Deploy now",
"deploy_now_tooltip": "Start a deployment immediately after saving the target.",
"create_missing_service": "Create missing service",
"create_missing_service_tooltip": "Allow Release Manager to create a missing Coolify service.",
"test_access_tooltip": "Verify the configured GitHub token can read this repository and branch."
}
}
}
}
}
@@ -0,0 +1,454 @@
{
"compat": {
"configuration": {
"release_manager": {
"title": "Release Manager",
"subtitle": "Gradual API and Front-End releases",
"channel_names": {
"stable": "Stable",
"canary": "Canary",
"internal": "Internal"
},
"channel_descriptions": {
"stable": "Default production channel.",
"canary": "Early production validation channel.",
"internal": "Internal staff and superuser validation channel."
},
"control_api": {
"title": "Control API",
"tooltip": "Release Manager actions are sent to this API. Use the production control API unless testing a staged backend.",
"example": "Example: https://api.truckwash.io:4433",
"endpoint_label": "API endpoint",
"endpoint_message": "Switch this only when Release Manager endpoints are available on the target API.",
"endpoint_aria": "Release Manager control API URL",
"use_tooltip": "Load release data from this API",
"use": "Use",
"reset_tooltip": "Return to the default control API",
"reset": "Reset",
"known_endpoints": "Known control API endpoints"
},
"tabs": {
"overview": {
"label": "Overview",
"description": "Channel health, setup progress, and the latest release state."
},
"channels": {
"label": "Channels",
"description": "Create stable, canary, and targeted channels with rollout limits."
},
"assignments": {
"label": "Assignments",
"description": "Pin users, subusers, or customers to a specific release channel."
},
"deployments": {
"label": "Deployments",
"description": "Trigger deployments and promote successful builds."
},
"replay": {
"label": "Replay",
"description": "Enable targeted capture and search release timeline events."
},
"integrations": {
"label": "Integrations",
"description": "Connect GitHub repositories, branches, and Coolify services."
},
"settings": {
"label": "Settings",
"description": "Configure Release Manager GitHub access and webhook settings."
}
},
"loading": {
"summary": "Loading release manager data...",
"refreshing_summary": "Refreshing release manager data...",
"summary_detail": "Release status, channels, deployments, and service health are being loaded.",
"channels": "Loading release channels...",
"channels_detail": "Channel lanes and release dependencies are being loaded.",
"assignments": "Loading release assignments...",
"deployments": "Loading release deployments...",
"operations": "Loading release operations...",
"data_services": "Loading release data services...",
"integrations": "Loading release integrations...",
"settings": "Loading Release Manager settings...",
"github_repositories": "Loading GitHub repositories...",
"timeline_sessions": "Loading release timeline sessions...",
"timeline_events": "Loading release timeline events...",
"refreshing_timeline": "Refreshing release timeline..."
},
"setup": {
"channels": "Channels",
"targets": "GitHub and Coolify targets",
"assignments": "Pilot assignments",
"deployments": "First deployment",
"replay": "Replay capture"
},
"stats": {
"channels": "Channels",
"targets": "Targets",
"deployments": "Deployments",
"timeline_events": "Timeline events"
},
"overview": {
"title": "Overview",
"subtitle": "Current channel health, release activity, and module status.",
"guided_setup": "Guided setup",
"next_step": "Next: {step}",
"ready": "Release Manager is ready for day-to-day operations.",
"add_suggested_channel": "Add suggested channel",
"module_health_empty": "Module health snapshots appear after probes have been recorded."
},
"status": {
"confirm_issue_action": "Run this Release Manager action? It can change deployment state and will be audited.",
"choose_bundle_prompt": "Enter the bundle id to set for this channel.",
"impact": "Impact",
"cause": "Cause",
"automated_fix": "Automated fix",
"manual_fallback": "Manual fallback",
"related_deployment": "Related deployment",
"recent_result": "Recent result",
"no_automated_action": "No automated action available.",
"deployment": "Deployment",
"target": "Target",
"coolify_target": "Coolify target",
"service_set": "Service set"
},
"settings": {
"title": "Release Manager Settings",
"subtitle": "Configure the GitHub token used for private repositories and branch lookups.",
"github_token": "GitHub token",
"github_api_url": "GitHub API URL",
"webhook_secret": "Webhook secret",
"github_webhook_secret": "GitHub webhook secret",
"configured": "Configured",
"not_configured": "Not configured",
"loaded_from": "Loaded from {variable}",
"set_below": "Set {variable} below",
"private_repositories_prefix": "Private repositories are read with the server environment variable",
"private_repositories_or": "or the module config variable",
"github_token_message": "Example: github_pat_... with access to the private repositories Release Manager deploys.",
"github_token_placeholder": "Leave blank to keep the existing token",
"github_api_url_message": "Configured in ReleaseManager.github_api_url. Use https://api.github.com unless using GitHub Enterprise.",
"webhook_secret_message": "Optional; leave blank to keep the existing secret.",
"webhook_secret_placeholder": "Webhook HMAC secret",
"save": "Save settings",
"back_to_integrations": "Back to integrations",
"guide": {
"steps": {
"github_token": "GitHub token",
"github_api_url": "GitHub API URL",
"github_webhook_secret": "GitHub webhook secret",
"review": "Review"
},
"github_token_title": "Add GitHub token",
"github_token_description": "Release Manager uses this token to list private repositories, branches, and commits before a deployment target is saved or tested.",
"configure_this_in": "Configure this in",
"server_environment_variable": "The server environment variable",
"takes_precedence": "takes precedence when it is set.",
"recommended_access": "Recommended access",
"recommended_access_description": "Fine-grained GitHub token with Contents: read access for each release repository.",
"github_api_url_title": "Add GitHub API URL",
"github_api_url_description": "Use GitHub Cloud by default, or point this at the REST API base URL for a GitHub Enterprise Server installation.",
"github_api_url_invalid": "Enter a full HTTP or HTTPS URL.",
"github_cloud": "GitHub Cloud",
"enterprise_example": "Enterprise example",
"github_webhook_secret_title": "Add GitHub webhook secret",
"github_webhook_secret_description": "Use the same secret in GitHub repository webhooks so Release Manager can verify deployment events before handling them.",
"github_webhook_url": "GitHub webhook URL:",
"store_shared_secret_in": "Store the shared secret in",
"review_title": "Review GitHub setup",
"review_description": "Save the values, then use Integrations to select a private repository, branch, and specific commit or latest commit before testing access.",
"ready": "Ready",
"needs_value": "Needs value"
}
},
"channel_unavailable": {
"kicker": "Release Manager",
"title": "Release channel is not ready",
"summary_prefix": "Your account is assigned to",
"summary_suffix": ", but that channel is missing required release configuration.",
"release_bundle": "Release bundle",
"frontend_version": "Frontend version",
"api_version": "API version",
"frontend_base_url": "Frontend URL",
"api_base_url": "API URL",
"frontend_entry": "Frontend entry",
"release_runtime": "Release runtime",
"checking_again": "Checking again in {seconds}s",
"refresh_error": "Release status could not be refreshed. The next automatic check will try again.",
"ignore": "Ignore next 5 minutes",
"check_again": "Check again",
"logout": "Logout",
"base_image": "Base image"
},
"channel_switched": {
"kicker": "Release Manager",
"title": "You are now on {channel}",
"summary_prefix": "Your account has been assigned to the",
"summary_suffix": "release channel. This device will remember that you have seen this notice.",
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"frontend_version": "Frontend version",
"api_version": "API version",
"assigned_channel": "Assigned channel",
"current_app_image": "Current app image",
"current_api": "Current API",
"base_image": "Base image",
"continue": "Continue"
},
"channel_selector": {
"title": "Release channel",
"subtitle": "Choose which assigned release channel this device should use.",
"sidebar_title": "Release",
"default": "Default",
"ready": "Ready",
"unavailable": "Not ready",
"release_bundle": "Release bundle",
"frontend_version": "Frontend version",
"api_version": "API version",
"frontend_base_url": "Frontend URL",
"api_base_url": "API URL",
"frontend_entry": "Frontend entry",
"release_runtime": "Release runtime",
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"use_local_frontend": "Use local frontend",
"switch_error": "Release channel could not be switched. The previous channel is still active."
},
"common": {
"yes": "yes",
"no": "no",
"off": "off"
},
"actions": {
"edit": "Edit",
"rollback": "Rollback",
"update": "Update",
"create": "Create",
"publish_release": "Publish release",
"clear": "Clear",
"assign": "Assign",
"remove": "Remove",
"test_access": "Test access",
"deploy": "Deploy",
"redeploy": "Redeploy",
"promote": "Promote",
"enable": "Enable",
"search": "Search",
"save_target": "Save target",
"delete": "Delete",
"back": "Back",
"next": "Next"
},
"tables": {
"channel": "Channel",
"rollout": "Rollout",
"frontend": "Frontend",
"api": "API",
"replay": "Replay",
"actions": "Actions",
"module": "Module",
"status": "Status",
"checked": "Checked",
"subject": "Subject",
"reason": "Reason",
"expires": "Expires",
"deployment": "Deployment",
"source": "Source",
"started": "Started",
"target": "Target",
"coolify": "Coolify",
"auto": "Auto"
},
"values": {
"app": {
"frontend": "frontend",
"api": "api"
},
"capture_level": {
"metadata": "metadata",
"full_redacted": "full_redacted"
},
"subject_type": {
"user": "user",
"subuser": "subuser",
"customer": "customer",
"channel": "channel"
},
"severity": {
"error": "error",
"warning": "warning",
"info": "info"
},
"status": {
"ok": "ok",
"active": "active",
"deployed": "deployed",
"stable": "stable",
"enabled": "enabled",
"queued": "queued",
"deploying": "deploying",
"metadata": "metadata",
"not_configured": "not configured",
"warning": "warning",
"failed": "failed",
"down": "down",
"error": "error",
"unknown": "unknown"
}
},
"autocomplete": {
"suggested_value": "Suggested value",
"suggestion": "Suggestion",
"use_typed_value": "Use typed value",
"custom": "Custom",
"api_url": "API URL",
"frontend_url": "Frontend URL",
"known_release_url": "Known release URL",
"deploy_here_with_coolify": "Deploy {app} here with Coolify",
"prepare_coolify_target": "Prepare a Coolify target with HTTPS/SSL enabled",
"coolify_ssl": "Coolify SSL",
"github_repository": "GitHub repository",
"owner_repository": "owner/repository",
"git_branch": "Git branch",
"deployment_branch": "Deployment branch",
"github_commit": "GitHub commit",
"git_commit": "Git commit",
"use_specific_commit_sha": "Use specific commit SHA",
"custom_commit": "Custom commit",
"health_url": "Health URL",
"release_health_check": "Release health check",
"coolify_service": "Coolify service",
"service_uuid": "Service UUID",
"known_coolify_service_uuid": "Known Coolify service UUID",
"control_api": "Control API",
"release_manager_api_endpoint": "Release Manager API endpoint",
"module": "Module",
"timeline_module_key": "Timeline module key"
},
"errors": {
"github_repositories": "Could not load GitHub repositories.",
"github_branches": "Could not load GitHub branches.",
"repository_branch_required": "Repository and branch are required before testing GitHub access.",
"github_access_failed": "GitHub repository access test failed."
},
"channels": {
"title": "Channels",
"subtitle": "Create and tune gradual release channels.",
"rollout_percent": "{percent}% rollout",
"already_configured": "Already configured",
"slug": "Slug",
"slug_message": "Example: canary",
"slug_placeholder": "slug",
"name": "Name",
"name_message": "Shown to operators when reviewing a channel.",
"name_placeholder": "Name",
"rollout": "Rollout",
"rollout_message": "0-100 percent of unassigned traffic.",
"frontend_url": "Frontend URL",
"frontend_url_message": "Example: https://canary.example.test",
"frontend_url_placeholder": "Frontend URL",
"no_matching_release_url": "No matching release URL.",
"api_url": "API URL",
"api_url_message": "Example: https://api-canary.example.test",
"api_url_placeholder": "API URL",
"no_matching_api_url": "No matching API URL.",
"replay_capture": "Replay capture",
"replay_capture_message": "Metadata is safer for broad channels.",
"retention_days": "Retention days",
"retention_days_message": "1-365 days.",
"enabled": "Enabled",
"default_channel": "Default channel",
"replay_enabled": "Replay enabled",
"frontend_chip": "Frontend: {url}",
"api_chip": "API: {url}"
},
"assignments": {
"title": "Assignments",
"subtitle": "Pin users, subusers, or customers to release channels.",
"subject": "Subject",
"subject_message": "Search users, subusers, or customers, or type a manual subject like user:42.",
"subject_placeholder": "Search or type subject",
"subject_empty": "No subjects found",
"group_users": "Users",
"group_subusers": "Subusers",
"group_customers": "Customers",
"group_manual": "Manual",
"manual_user": "User #{id}",
"manual_subuser": "Subuser #{id}",
"manual_customer": "Customer #{id}",
"manual_description": "Use typed value {subject}",
"subject_type": "Subject type",
"subject_type_message": "Choose the identity type to pin.",
"customer": "Customer",
"customer_message": "Search by customer number or name, then select the exact customer.",
"customer_placeholder": "Search customer",
"subject_id": "Subject ID",
"subject_id_message": "Example: user id 42",
"subject_id_placeholder": "Subject id",
"channel": "Channel",
"channel_message": "Pinned subjects bypass percentage rollout.",
"reason": "Reason",
"reason_message": "Example: canary validation for customer support.",
"reason_placeholder": "Reason",
"expires": "Expires",
"expires_message": "Leave blank for no automatic expiry."
},
"deployments": {
"title": "Deployments",
"subtitle": "Trigger, promote, and inspect release deployments.",
"manual_deployment_record": "Manual deployment record",
"target": "Target",
"target_message": "Pick a saved target or keep manual fields editable.",
"select_target_or_manual": "Select target or manual",
"channel": "Channel",
"app": "App",
"repository": "Repository",
"repository_message": "Example: truckwash/front-end-vue",
"repository_placeholder": "owner/repo",
"branch": "Branch",
"branch_message": "Example: master",
"branch_placeholder": "branch",
"commit_selection": "Commit selection",
"commit_selection_message": "Latest resolves to the branch head with the configured GitHub token.",
"latest_from_branch": "Latest from branch",
"specific_commit": "Specific commit",
"specific_commit_message": "Example: feedface1234",
"commit_sha_placeholder": "commit sha",
"version_label": "Version label",
"version_label_message": "Example: api-2026.05.19",
"version_label_placeholder": "version label",
"commit_prefix": "Commit: {commit}",
"test_access_tooltip": "Verify token access to the selected repository, branch, and commit."
},
"replay": {
"title": "Replay",
"subtitle": "Enable targeted capture and step through failure timelines.",
"capture_channel": "Capture {channel}",
"target_type": "Target type",
"target": "Target",
"target_message": "Example: user id 42 or channel slug canary.",
"target_placeholder": "Target id or channel slug",
"channel_scope": "Channel scope",
"channel_scope_message": "Optional; limits replay capture to a channel.",
"no_channel_scope": "No channel scope",
"capture_level": "Capture level",
"expires": "Expires",
"expires_message": "Leave blank for manual disable.",
"trace_id": "Trace ID",
"trace_id_message": "Example: trace-canary-1",
"trace_id_placeholder": "Trace id",
"channel": "Channel",
"channel_placeholder": "Channel",
"principal_id": "Principal ID",
"principal_id_placeholder": "Principal id",
"module": "Module",
"module_placeholder": "Module",
"severity": "Severity",
"any_severity": "Any severity",
"no_timeline_events": "No timeline events match the current filters."
}
}
}
}
}
@@ -18,7 +18,6 @@
"loading_vehicle_types": "@.capitalize:{'terms.glossary.loading'} @:{'terms.glossary.available'} @:{'terms.glossary.vehicle'} @:{'terms.glossary.types'}...",
"lane_unavailable": "Lane unavailable",
"machine": "@.capitalize:{'terms.glossary.machine'}",
"machine_unavailable_for_lane": "Machine wash is unavailable for the selected lane. Choose manual wash or another available lane.",
"no_departments_found": "@:{'phrases.compat.departments.no_departments'}",
"occupied": "Occupied",
"open_property_access_gate": "@.capitalize:{'terms.glossary.open'} @:{'terms.glossary.property'} @:{'terms.glossary.access'} @:{'terms.glossary.gate'}",
@@ -98,7 +98,25 @@
"view_lane_new_tab": "@:{'phrases.compat.admin.pos.settings_wheel.view_lane_new_tab'}",
"view_lane_setup_new_tab": "@:{'phrases.compat.admin.pos.settings_wheel.view_lane_setup_new_tab'}",
"view_order_new_tab": "@:{'phrases.compat.admin.pos.settings_wheel.view_order_new_tab'}",
"view_vehicle_new_tab": "@:{'phrases.compat.admin.pos.settings_wheel.view_vehicle_new_tab'}"
"view_vehicle_new_tab": "@:{'phrases.compat.admin.pos.settings_wheel.view_vehicle_new_tab'}",
"accept_self_serve_wash": "@:{'phrases.compat.admin.pos.settings_wheel.accept_self_serve_wash'}",
"accept_self_serve_wash_confirm": "@:{'phrases.compat.admin.pos.settings_wheel.accept_self_serve_wash_confirm'}",
"accept_self_serve_wash_success": "@:{'phrases.compat.admin.pos.settings_wheel.accept_self_serve_wash_success'}",
"email_notifications_section": "@:{'phrases.compat.admin.pos.settings_wheel.email_notifications_section'}",
"resend_booking_completion_confirmation": "@:{'phrases.compat.admin.pos.settings_wheel.resend_booking_completion_confirmation'}",
"resend_booking_completion_confirmation_error": "@:{'phrases.compat.admin.pos.settings_wheel.resend_booking_completion_confirmation_error'}",
"resend_booking_completion_confirmation_success": "@:{'phrases.compat.admin.pos.settings_wheel.resend_booking_completion_confirmation_success'}",
"resend_booking_confirmation": "@:{'phrases.compat.admin.pos.settings_wheel.resend_booking_confirmation'}",
"resend_booking_confirmation_error": "@:{'phrases.compat.admin.pos.settings_wheel.resend_booking_confirmation_error'}",
"resend_booking_confirmation_success": "@:{'phrases.compat.admin.pos.settings_wheel.resend_booking_confirmation_success'}",
"resend_wash_certificate": "@:{'phrases.compat.admin.pos.settings_wheel.resend_wash_certificate'}",
"resend_wash_certificate_error": "@:{'phrases.compat.admin.pos.settings_wheel.resend_wash_certificate_error'}",
"resend_wash_certificate_success": "@:{'phrases.compat.admin.pos.settings_wheel.resend_wash_certificate_success'}",
"self_serve_customer": "@:{'phrases.compat.admin.pos.settings_wheel.self_serve_customer'}",
"self_serve_driver": "@:{'phrases.compat.admin.pos.settings_wheel.self_serve_driver'}",
"self_serve_elapsed": "@:{'phrases.compat.admin.pos.settings_wheel.self_serve_elapsed'}",
"self_serve_wash_attachment": "@:{'phrases.compat.admin.pos.settings_wheel.self_serve_wash_attachment'}",
"self_serve_wash_attachment_for_customer": "@:{'phrases.compat.admin.pos.settings_wheel.self_serve_wash_attachment_for_customer'}"
},
"show_details": "@:{'phrases.compat.admin.pos.show_details'}",
"show_order_details": "@:{'phrases.compat.admin.pos.show_order_details'}",
@@ -351,473 +351,6 @@
"title": "@:{'phrases.compat.configuration.xlvask.title'}",
"username": "@:auth.username",
"username_desc": "@:{'phrases.compat.configuration.xlvask.username_desc'}"
},
"release_manager": {
"title": "@:{'phrases.compat.configuration.release_manager.title'}",
"subtitle": "@:{'phrases.compat.configuration.release_manager.subtitle'}",
"channel_names": {
"stable": "@:{'phrases.compat.configuration.release_manager.channel_names.stable'}",
"canary": "@:{'phrases.compat.configuration.release_manager.channel_names.canary'}",
"internal": "@:{'phrases.compat.configuration.release_manager.channel_names.internal'}"
},
"channel_descriptions": {
"stable": "@:{'phrases.compat.configuration.release_manager.channel_descriptions.stable'}",
"canary": "@:{'phrases.compat.configuration.release_manager.channel_descriptions.canary'}",
"internal": "@:{'phrases.compat.configuration.release_manager.channel_descriptions.internal'}"
},
"control_api": {
"title": "@:{'phrases.compat.configuration.release_manager.control_api.title'}",
"tooltip": "@:{'phrases.compat.configuration.release_manager.control_api.tooltip'}",
"example": "@:{'phrases.compat.configuration.release_manager.control_api.example'}",
"endpoint_label": "@:{'phrases.compat.configuration.release_manager.control_api.endpoint_label'}",
"endpoint_message": "@:{'phrases.compat.configuration.release_manager.control_api.endpoint_message'}",
"endpoint_aria": "@:{'phrases.compat.configuration.release_manager.control_api.endpoint_aria'}",
"use_tooltip": "@:{'phrases.compat.configuration.release_manager.control_api.use_tooltip'}",
"use": "@:{'phrases.compat.configuration.release_manager.control_api.use'}",
"reset_tooltip": "@:{'phrases.compat.configuration.release_manager.control_api.reset_tooltip'}",
"reset": "@:{'phrases.compat.configuration.release_manager.control_api.reset'}",
"known_endpoints": "@:{'phrases.compat.configuration.release_manager.control_api.known_endpoints'}"
},
"tabs": {
"overview": {
"label": "@:{'phrases.compat.configuration.release_manager.tabs.overview.label'}",
"description": "@:{'phrases.compat.configuration.release_manager.tabs.overview.description'}"
},
"channels": {
"label": "@:{'phrases.compat.configuration.release_manager.tabs.channels.label'}",
"description": "@:{'phrases.compat.configuration.release_manager.tabs.channels.description'}"
},
"assignments": {
"label": "@:{'phrases.compat.configuration.release_manager.tabs.assignments.label'}",
"description": "@:{'phrases.compat.configuration.release_manager.tabs.assignments.description'}"
},
"deployments": {
"label": "@:{'phrases.compat.configuration.release_manager.tabs.deployments.label'}",
"description": "@:{'phrases.compat.configuration.release_manager.tabs.deployments.description'}"
},
"replay": {
"label": "@:{'phrases.compat.configuration.release_manager.tabs.replay.label'}",
"description": "@:{'phrases.compat.configuration.release_manager.tabs.replay.description'}"
},
"integrations": {
"label": "@:{'phrases.compat.configuration.release_manager.tabs.integrations.label'}",
"description": "@:{'phrases.compat.configuration.release_manager.tabs.integrations.description'}"
},
"settings": {
"label": "@:{'phrases.compat.configuration.release_manager.tabs.settings.label'}",
"description": "@:{'phrases.compat.configuration.release_manager.tabs.settings.description'}"
}
},
"setup": {
"channels": "@:{'phrases.compat.configuration.release_manager.setup.channels'}",
"targets": "@:{'phrases.compat.configuration.release_manager.setup.targets'}",
"assignments": "@:{'phrases.compat.configuration.release_manager.setup.assignments'}",
"deployments": "@:{'phrases.compat.configuration.release_manager.setup.deployments'}",
"replay": "@:{'phrases.compat.configuration.release_manager.setup.replay'}"
},
"stats": {
"channels": "@:{'phrases.compat.configuration.release_manager.stats.channels'}",
"targets": "@:{'phrases.compat.configuration.release_manager.stats.targets'}",
"deployments": "@:{'phrases.compat.configuration.release_manager.stats.deployments'}",
"timeline_events": "@:{'phrases.compat.configuration.release_manager.stats.timeline_events'}"
},
"overview": {
"title": "@:{'phrases.compat.configuration.release_manager.overview.title'}",
"subtitle": "@:{'phrases.compat.configuration.release_manager.overview.subtitle'}",
"guided_setup": "@:{'phrases.compat.configuration.release_manager.overview.guided_setup'}",
"next_step": "@:{'phrases.compat.configuration.release_manager.overview.next_step'}",
"ready": "@:{'phrases.compat.configuration.release_manager.overview.ready'}",
"add_suggested_channel": "@:{'phrases.compat.configuration.release_manager.overview.add_suggested_channel'}",
"module_health_empty": "@:{'phrases.compat.configuration.release_manager.overview.module_health_empty'}"
},
"settings": {
"title": "@:{'phrases.compat.configuration.release_manager.settings.title'}",
"subtitle": "@:{'phrases.compat.configuration.release_manager.settings.subtitle'}",
"github_token": "@:{'phrases.compat.configuration.release_manager.settings.github_token'}",
"github_api_url": "@:{'phrases.compat.configuration.release_manager.settings.github_api_url'}",
"webhook_secret": "@:{'phrases.compat.configuration.release_manager.settings.webhook_secret'}",
"github_webhook_secret": "@:{'phrases.compat.configuration.release_manager.settings.github_webhook_secret'}",
"configured": "@:{'phrases.compat.configuration.release_manager.settings.configured'}",
"not_configured": "@:{'phrases.compat.configuration.release_manager.settings.not_configured'}",
"loaded_from": "@:{'phrases.compat.configuration.release_manager.settings.loaded_from'}",
"set_below": "@:{'phrases.compat.configuration.release_manager.settings.set_below'}",
"private_repositories_prefix": "@:{'phrases.compat.configuration.release_manager.settings.private_repositories_prefix'}",
"private_repositories_or": "@:{'phrases.compat.configuration.release_manager.settings.private_repositories_or'}",
"github_token_message": "@:{'phrases.compat.configuration.release_manager.settings.github_token_message'}",
"github_token_placeholder": "@:{'phrases.compat.configuration.release_manager.settings.github_token_placeholder'}",
"github_api_url_message": "@:{'phrases.compat.configuration.release_manager.settings.github_api_url_message'}",
"webhook_secret_message": "@:{'phrases.compat.configuration.release_manager.settings.webhook_secret_message'}",
"webhook_secret_placeholder": "@:{'phrases.compat.configuration.release_manager.settings.webhook_secret_placeholder'}",
"save": "@:{'phrases.compat.configuration.release_manager.settings.save'}",
"back_to_integrations": "@:{'phrases.compat.configuration.release_manager.settings.back_to_integrations'}",
"guide": {
"steps": {
"github_token": "@:{'phrases.compat.configuration.release_manager.settings.guide.steps.github_token'}",
"github_api_url": "@:{'phrases.compat.configuration.release_manager.settings.guide.steps.github_api_url'}",
"github_webhook_secret": "@:{'phrases.compat.configuration.release_manager.settings.guide.steps.github_webhook_secret'}",
"review": "@:{'phrases.compat.configuration.release_manager.settings.guide.steps.review'}"
},
"github_token_title": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_token_title'}",
"github_token_description": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_token_description'}",
"configure_this_in": "@:{'phrases.compat.configuration.release_manager.settings.guide.configure_this_in'}",
"server_environment_variable": "@:{'phrases.compat.configuration.release_manager.settings.guide.server_environment_variable'}",
"takes_precedence": "@:{'phrases.compat.configuration.release_manager.settings.guide.takes_precedence'}",
"recommended_access": "@:{'phrases.compat.configuration.release_manager.settings.guide.recommended_access'}",
"recommended_access_description": "@:{'phrases.compat.configuration.release_manager.settings.guide.recommended_access_description'}",
"github_api_url_title": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_api_url_title'}",
"github_api_url_description": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_api_url_description'}",
"github_api_url_invalid": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_api_url_invalid'}",
"github_cloud": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_cloud'}",
"enterprise_example": "@:{'phrases.compat.configuration.release_manager.settings.guide.enterprise_example'}",
"github_webhook_secret_title": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_webhook_secret_title'}",
"github_webhook_secret_description": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_webhook_secret_description'}",
"github_webhook_url": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_webhook_url'}",
"store_shared_secret_in": "@:{'phrases.compat.configuration.release_manager.settings.guide.store_shared_secret_in'}",
"review_title": "@:{'phrases.compat.configuration.release_manager.settings.guide.review_title'}",
"review_description": "@:{'phrases.compat.configuration.release_manager.settings.guide.review_description'}",
"ready": "@:{'phrases.compat.configuration.release_manager.settings.guide.ready'}",
"needs_value": "@:{'phrases.compat.configuration.release_manager.settings.guide.needs_value'}"
}
},
"channel_unavailable": {
"kicker": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.kicker'}",
"title": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.title'}",
"summary_prefix": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.summary_prefix'}",
"summary_suffix": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.summary_suffix'}",
"release_bundle": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.release_bundle'}",
"frontend_version": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.frontend_version'}",
"api_version": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.api_version'}",
"frontend_base_url": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.frontend_base_url'}",
"api_base_url": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.api_base_url'}",
"frontend_entry": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.frontend_entry'}",
"release_runtime": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.release_runtime'}",
"checking_again": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.checking_again'}",
"refresh_error": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.refresh_error'}",
"ignore": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.ignore'}",
"check_again": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.check_again'}",
"logout": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.logout'}",
"base_image": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.base_image'}"
},
"channel_switched": {
"kicker": "@:{'phrases.compat.configuration.release_manager.channel_switched.kicker'}",
"title": "@:{'phrases.compat.configuration.release_manager.channel_switched.title'}",
"summary_prefix": "@:{'phrases.compat.configuration.release_manager.channel_switched.summary_prefix'}",
"summary_suffix": "@:{'phrases.compat.configuration.release_manager.channel_switched.summary_suffix'}",
"bundle": "@:{'phrases.compat.configuration.release_manager.channel_switched.bundle'}",
"frontend": "@:{'phrases.compat.configuration.release_manager.channel_switched.frontend'}",
"api": "@:{'phrases.compat.configuration.release_manager.channel_switched.api'}",
"frontend_version": "@:{'phrases.compat.configuration.release_manager.channel_switched.frontend_version'}",
"api_version": "@:{'phrases.compat.configuration.release_manager.channel_switched.api_version'}",
"assigned_channel": "@:{'phrases.compat.configuration.release_manager.channel_switched.assigned_channel'}",
"current_app_image": "@:{'phrases.compat.configuration.release_manager.channel_switched.current_app_image'}",
"current_api": "@:{'phrases.compat.configuration.release_manager.channel_switched.current_api'}",
"base_image": "@:{'phrases.compat.configuration.release_manager.channel_switched.base_image'}",
"continue": "@:{'phrases.compat.configuration.release_manager.channel_switched.continue'}"
},
"channel_selector": {
"title": "@:{'phrases.compat.configuration.release_manager.channel_selector.title'}",
"subtitle": "@:{'phrases.compat.configuration.release_manager.channel_selector.subtitle'}",
"sidebar_title": "@:{'phrases.compat.configuration.release_manager.channel_selector.sidebar_title'}",
"default": "@:{'phrases.compat.configuration.release_manager.channel_selector.default'}",
"ready": "@:{'phrases.compat.configuration.release_manager.channel_selector.ready'}",
"unavailable": "@:{'phrases.compat.configuration.release_manager.channel_selector.unavailable'}",
"release_bundle": "@:{'phrases.compat.configuration.release_manager.channel_selector.release_bundle'}",
"frontend_version": "@:{'phrases.compat.configuration.release_manager.channel_selector.frontend_version'}",
"api_version": "@:{'phrases.compat.configuration.release_manager.channel_selector.api_version'}",
"frontend_base_url": "@:{'phrases.compat.configuration.release_manager.channel_selector.frontend_base_url'}",
"api_base_url": "@:{'phrases.compat.configuration.release_manager.channel_selector.api_base_url'}",
"frontend_entry": "@:{'phrases.compat.configuration.release_manager.channel_selector.frontend_entry'}",
"release_runtime": "@:{'phrases.compat.configuration.release_manager.channel_selector.release_runtime'}",
"bundle": "@:{'phrases.compat.configuration.release_manager.channel_selector.bundle'}",
"frontend": "@:{'phrases.compat.configuration.release_manager.channel_selector.frontend'}",
"api": "@:{'phrases.compat.configuration.release_manager.channel_selector.api'}",
"use_local_frontend": "@:{'phrases.compat.configuration.release_manager.channel_selector.use_local_frontend'}"
},
"common": {
"yes": "@:{'phrases.compat.configuration.release_manager.common.yes'}",
"no": "@:{'phrases.compat.configuration.release_manager.common.no'}",
"off": "@:{'phrases.compat.configuration.release_manager.common.off'}"
},
"actions": {
"edit": "@:{'phrases.compat.configuration.release_manager.actions.edit'}",
"rollback": "@:{'phrases.compat.configuration.release_manager.actions.rollback'}",
"update": "@:{'phrases.compat.configuration.release_manager.actions.update'}",
"create": "@:{'phrases.compat.configuration.release_manager.actions.create'}",
"clear": "@:{'phrases.compat.configuration.release_manager.actions.clear'}",
"assign": "@:{'phrases.compat.configuration.release_manager.actions.assign'}",
"remove": "@:{'phrases.compat.configuration.release_manager.actions.remove'}",
"test_access": "@:{'phrases.compat.configuration.release_manager.actions.test_access'}",
"deploy": "@:{'phrases.compat.configuration.release_manager.actions.deploy'}",
"redeploy": "@:{'phrases.compat.configuration.release_manager.actions.redeploy'}",
"promote": "@:{'phrases.compat.configuration.release_manager.actions.promote'}",
"enable": "@:{'phrases.compat.configuration.release_manager.actions.enable'}",
"search": "@:{'phrases.compat.configuration.release_manager.actions.search'}",
"save_target": "@:{'phrases.compat.configuration.release_manager.actions.save_target'}",
"delete": "@:{'phrases.compat.configuration.release_manager.actions.delete'}",
"back": "@:{'phrases.compat.configuration.release_manager.actions.back'}",
"next": "@:{'phrases.compat.configuration.release_manager.actions.next'}"
},
"tables": {
"channel": "@:{'phrases.compat.configuration.release_manager.tables.channel'}",
"rollout": "@:{'phrases.compat.configuration.release_manager.tables.rollout'}",
"frontend": "@:{'phrases.compat.configuration.release_manager.tables.frontend'}",
"api": "@:{'phrases.compat.configuration.release_manager.tables.api'}",
"replay": "@:{'phrases.compat.configuration.release_manager.tables.replay'}",
"actions": "@:{'phrases.compat.configuration.release_manager.tables.actions'}",
"module": "@:{'phrases.compat.configuration.release_manager.tables.module'}",
"status": "@:{'phrases.compat.configuration.release_manager.tables.status'}",
"checked": "@:{'phrases.compat.configuration.release_manager.tables.checked'}",
"subject": "@:{'phrases.compat.configuration.release_manager.tables.subject'}",
"reason": "@:{'phrases.compat.configuration.release_manager.tables.reason'}",
"expires": "@:{'phrases.compat.configuration.release_manager.tables.expires'}",
"deployment": "@:{'phrases.compat.configuration.release_manager.tables.deployment'}",
"source": "@:{'phrases.compat.configuration.release_manager.tables.source'}",
"started": "@:{'phrases.compat.configuration.release_manager.tables.started'}",
"target": "@:{'phrases.compat.configuration.release_manager.tables.target'}",
"coolify": "@:{'phrases.compat.configuration.release_manager.tables.coolify'}",
"auto": "@:{'phrases.compat.configuration.release_manager.tables.auto'}"
},
"values": {
"app": {
"frontend": "@:{'phrases.compat.configuration.release_manager.values.app.frontend'}",
"api": "@:{'phrases.compat.configuration.release_manager.values.app.api'}"
},
"capture_level": {
"metadata": "@:{'phrases.compat.configuration.release_manager.values.capture_level.metadata'}",
"full_redacted": "@:{'phrases.compat.configuration.release_manager.values.capture_level.full_redacted'}"
},
"subject_type": {
"user": "@:{'phrases.compat.configuration.release_manager.values.subject_type.user'}",
"subuser": "@:{'phrases.compat.configuration.release_manager.values.subject_type.subuser'}",
"customer": "@:{'phrases.compat.configuration.release_manager.values.subject_type.customer'}",
"channel": "@:{'phrases.compat.configuration.release_manager.values.subject_type.channel'}"
},
"severity": {
"error": "@:{'phrases.compat.configuration.release_manager.values.severity.error'}",
"warning": "@:{'phrases.compat.configuration.release_manager.values.severity.warning'}",
"info": "@:{'phrases.compat.configuration.release_manager.values.severity.info'}"
},
"status": {
"ok": "@:{'phrases.compat.configuration.release_manager.values.status.ok'}",
"active": "@:{'phrases.compat.configuration.release_manager.values.status.active'}",
"deployed": "@:{'phrases.compat.configuration.release_manager.values.status.deployed'}",
"stable": "@:{'phrases.compat.configuration.release_manager.values.status.stable'}",
"enabled": "@:{'phrases.compat.configuration.release_manager.values.status.enabled'}",
"queued": "@:{'phrases.compat.configuration.release_manager.values.status.queued'}",
"deploying": "@:{'phrases.compat.configuration.release_manager.values.status.deploying'}",
"metadata": "@:{'phrases.compat.configuration.release_manager.values.status.metadata'}",
"not_configured": "@:{'phrases.compat.configuration.release_manager.values.status.not_configured'}",
"warning": "@:{'phrases.compat.configuration.release_manager.values.status.warning'}",
"failed": "@:{'phrases.compat.configuration.release_manager.values.status.failed'}",
"down": "@:{'phrases.compat.configuration.release_manager.values.status.down'}",
"error": "@:{'phrases.compat.configuration.release_manager.values.status.error'}",
"unknown": "@:{'phrases.compat.configuration.release_manager.values.status.unknown'}"
}
},
"autocomplete": {
"suggested_value": "@:{'phrases.compat.configuration.release_manager.autocomplete.suggested_value'}",
"suggestion": "@:{'phrases.compat.configuration.release_manager.autocomplete.suggestion'}",
"use_typed_value": "@:{'phrases.compat.configuration.release_manager.autocomplete.use_typed_value'}",
"custom": "@:{'phrases.compat.configuration.release_manager.autocomplete.custom'}",
"api_url": "@:{'phrases.compat.configuration.release_manager.autocomplete.api_url'}",
"frontend_url": "@:{'phrases.compat.configuration.release_manager.autocomplete.frontend_url'}",
"known_release_url": "@:{'phrases.compat.configuration.release_manager.autocomplete.known_release_url'}",
"deploy_here_with_coolify": "@:{'phrases.compat.configuration.release_manager.autocomplete.deploy_here_with_coolify'}",
"prepare_coolify_target": "@:{'phrases.compat.configuration.release_manager.autocomplete.prepare_coolify_target'}",
"coolify_ssl": "@:{'phrases.compat.configuration.release_manager.autocomplete.coolify_ssl'}",
"github_repository": "@:{'phrases.compat.configuration.release_manager.autocomplete.github_repository'}",
"owner_repository": "@:{'phrases.compat.configuration.release_manager.autocomplete.owner_repository'}",
"git_branch": "@:{'phrases.compat.configuration.release_manager.autocomplete.git_branch'}",
"deployment_branch": "@:{'phrases.compat.configuration.release_manager.autocomplete.deployment_branch'}",
"github_commit": "@:{'phrases.compat.configuration.release_manager.autocomplete.github_commit'}",
"git_commit": "@:{'phrases.compat.configuration.release_manager.autocomplete.git_commit'}",
"use_specific_commit_sha": "@:{'phrases.compat.configuration.release_manager.autocomplete.use_specific_commit_sha'}",
"custom_commit": "@:{'phrases.compat.configuration.release_manager.autocomplete.custom_commit'}",
"health_url": "@:{'phrases.compat.configuration.release_manager.autocomplete.health_url'}",
"release_health_check": "@:{'phrases.compat.configuration.release_manager.autocomplete.release_health_check'}",
"coolify_service": "@:{'phrases.compat.configuration.release_manager.autocomplete.coolify_service'}",
"service_uuid": "@:{'phrases.compat.configuration.release_manager.autocomplete.service_uuid'}",
"known_coolify_service_uuid": "@:{'phrases.compat.configuration.release_manager.autocomplete.known_coolify_service_uuid'}",
"control_api": "@:{'phrases.compat.configuration.release_manager.autocomplete.control_api'}",
"release_manager_api_endpoint": "@:{'phrases.compat.configuration.release_manager.autocomplete.release_manager_api_endpoint'}",
"module": "@:{'phrases.compat.configuration.release_manager.autocomplete.module'}",
"timeline_module_key": "@:{'phrases.compat.configuration.release_manager.autocomplete.timeline_module_key'}"
},
"errors": {
"github_repositories": "@:{'phrases.compat.configuration.release_manager.errors.github_repositories'}",
"github_branches": "@:{'phrases.compat.configuration.release_manager.errors.github_branches'}",
"repository_branch_required": "@:{'phrases.compat.configuration.release_manager.errors.repository_branch_required'}",
"github_access_failed": "@:{'phrases.compat.configuration.release_manager.errors.github_access_failed'}"
},
"channels": {
"title": "@:{'phrases.compat.configuration.release_manager.channels.title'}",
"subtitle": "@:{'phrases.compat.configuration.release_manager.channels.subtitle'}",
"rollout_percent": "@:{'phrases.compat.configuration.release_manager.channels.rollout_percent'}",
"already_configured": "@:{'phrases.compat.configuration.release_manager.channels.already_configured'}",
"slug": "@:{'phrases.compat.configuration.release_manager.channels.slug'}",
"slug_message": "@:{'phrases.compat.configuration.release_manager.channels.slug_message'}",
"slug_placeholder": "@:{'phrases.compat.configuration.release_manager.channels.slug_placeholder'}",
"name": "@:{'phrases.compat.configuration.release_manager.channels.name'}",
"name_message": "@:{'phrases.compat.configuration.release_manager.channels.name_message'}",
"name_placeholder": "@:{'phrases.compat.configuration.release_manager.channels.name_placeholder'}",
"rollout": "@:{'phrases.compat.configuration.release_manager.channels.rollout'}",
"rollout_message": "@:{'phrases.compat.configuration.release_manager.channels.rollout_message'}",
"frontend_url": "@:{'phrases.compat.configuration.release_manager.channels.frontend_url'}",
"frontend_url_message": "@:{'phrases.compat.configuration.release_manager.channels.frontend_url_message'}",
"frontend_url_placeholder": "@:{'phrases.compat.configuration.release_manager.channels.frontend_url_placeholder'}",
"no_matching_release_url": "@:{'phrases.compat.configuration.release_manager.channels.no_matching_release_url'}",
"api_url": "@:{'phrases.compat.configuration.release_manager.channels.api_url'}",
"api_url_message": "@:{'phrases.compat.configuration.release_manager.channels.api_url_message'}",
"api_url_placeholder": "@:{'phrases.compat.configuration.release_manager.channels.api_url_placeholder'}",
"no_matching_api_url": "@:{'phrases.compat.configuration.release_manager.channels.no_matching_api_url'}",
"replay_capture": "@:{'phrases.compat.configuration.release_manager.channels.replay_capture'}",
"replay_capture_message": "@:{'phrases.compat.configuration.release_manager.channels.replay_capture_message'}",
"retention_days": "@:{'phrases.compat.configuration.release_manager.channels.retention_days'}",
"retention_days_message": "@:{'phrases.compat.configuration.release_manager.channels.retention_days_message'}",
"enabled": "@:{'phrases.compat.configuration.release_manager.channels.enabled'}",
"default_channel": "@:{'phrases.compat.configuration.release_manager.channels.default_channel'}",
"replay_enabled": "@:{'phrases.compat.configuration.release_manager.channels.replay_enabled'}",
"frontend_chip": "@:{'phrases.compat.configuration.release_manager.channels.frontend_chip'}",
"api_chip": "@:{'phrases.compat.configuration.release_manager.channels.api_chip'}"
},
"assignments": {
"title": "@:{'phrases.compat.configuration.release_manager.assignments.title'}",
"subtitle": "@:{'phrases.compat.configuration.release_manager.assignments.subtitle'}",
"subject": "@:{'phrases.compat.configuration.release_manager.assignments.subject'}",
"subject_message": "@:{'phrases.compat.configuration.release_manager.assignments.subject_message'}",
"subject_placeholder": "@:{'phrases.compat.configuration.release_manager.assignments.subject_placeholder'}",
"subject_empty": "@:{'phrases.compat.configuration.release_manager.assignments.subject_empty'}",
"group_users": "@:{'phrases.compat.configuration.release_manager.assignments.group_users'}",
"group_subusers": "@:{'phrases.compat.configuration.release_manager.assignments.group_subusers'}",
"group_customers": "@:{'phrases.compat.configuration.release_manager.assignments.group_customers'}",
"group_manual": "@:{'phrases.compat.configuration.release_manager.assignments.group_manual'}",
"manual_user": "@:{'phrases.compat.configuration.release_manager.assignments.manual_user'}",
"manual_subuser": "@:{'phrases.compat.configuration.release_manager.assignments.manual_subuser'}",
"manual_customer": "@:{'phrases.compat.configuration.release_manager.assignments.manual_customer'}",
"manual_description": "@:{'phrases.compat.configuration.release_manager.assignments.manual_description'}",
"subject_type": "@:{'phrases.compat.configuration.release_manager.assignments.subject_type'}",
"subject_type_message": "@:{'phrases.compat.configuration.release_manager.assignments.subject_type_message'}",
"customer": "@:{'phrases.compat.configuration.release_manager.assignments.customer'}",
"customer_message": "@:{'phrases.compat.configuration.release_manager.assignments.customer_message'}",
"customer_placeholder": "@:{'phrases.compat.configuration.release_manager.assignments.customer_placeholder'}",
"subject_id": "@:{'phrases.compat.configuration.release_manager.assignments.subject_id'}",
"subject_id_message": "@:{'phrases.compat.configuration.release_manager.assignments.subject_id_message'}",
"subject_id_placeholder": "@:{'phrases.compat.configuration.release_manager.assignments.subject_id_placeholder'}",
"channel": "@:{'phrases.compat.configuration.release_manager.assignments.channel'}",
"channel_message": "@:{'phrases.compat.configuration.release_manager.assignments.channel_message'}",
"reason": "@:{'phrases.compat.configuration.release_manager.assignments.reason'}",
"reason_message": "@:{'phrases.compat.configuration.release_manager.assignments.reason_message'}",
"reason_placeholder": "@:{'phrases.compat.configuration.release_manager.assignments.reason_placeholder'}",
"expires": "@:{'phrases.compat.configuration.release_manager.assignments.expires'}",
"expires_message": "@:{'phrases.compat.configuration.release_manager.assignments.expires_message'}"
},
"deployments": {
"title": "@:{'phrases.compat.configuration.release_manager.deployments.title'}",
"subtitle": "@:{'phrases.compat.configuration.release_manager.deployments.subtitle'}",
"manual_deployment_record": "@:{'phrases.compat.configuration.release_manager.deployments.manual_deployment_record'}",
"target": "@:{'phrases.compat.configuration.release_manager.deployments.target'}",
"target_message": "@:{'phrases.compat.configuration.release_manager.deployments.target_message'}",
"select_target_or_manual": "@:{'phrases.compat.configuration.release_manager.deployments.select_target_or_manual'}",
"channel": "@:{'phrases.compat.configuration.release_manager.deployments.channel'}",
"app": "@:{'phrases.compat.configuration.release_manager.deployments.app'}",
"repository": "@:{'phrases.compat.configuration.release_manager.deployments.repository'}",
"repository_message": "@:{'phrases.compat.configuration.release_manager.deployments.repository_message'}",
"repository_placeholder": "@:{'phrases.compat.configuration.release_manager.deployments.repository_placeholder'}",
"branch": "@:{'phrases.compat.configuration.release_manager.deployments.branch'}",
"branch_message": "@:{'phrases.compat.configuration.release_manager.deployments.branch_message'}",
"branch_placeholder": "@:{'phrases.compat.configuration.release_manager.deployments.branch_placeholder'}",
"commit_selection": "@:{'phrases.compat.configuration.release_manager.deployments.commit_selection'}",
"commit_selection_message": "@:{'phrases.compat.configuration.release_manager.deployments.commit_selection_message'}",
"latest_from_branch": "@:{'phrases.compat.configuration.release_manager.deployments.latest_from_branch'}",
"specific_commit": "@:{'phrases.compat.configuration.release_manager.deployments.specific_commit'}",
"specific_commit_message": "@:{'phrases.compat.configuration.release_manager.deployments.specific_commit_message'}",
"commit_sha_placeholder": "@:{'phrases.compat.configuration.release_manager.deployments.commit_sha_placeholder'}",
"version_label": "@:{'phrases.compat.configuration.release_manager.deployments.version_label'}",
"version_label_message": "@:{'phrases.compat.configuration.release_manager.deployments.version_label_message'}",
"version_label_placeholder": "@:{'phrases.compat.configuration.release_manager.deployments.version_label_placeholder'}",
"commit_prefix": "@:{'phrases.compat.configuration.release_manager.deployments.commit_prefix'}",
"test_access_tooltip": "@:{'phrases.compat.configuration.release_manager.deployments.test_access_tooltip'}"
},
"replay": {
"title": "@:{'phrases.compat.configuration.release_manager.replay.title'}",
"subtitle": "@:{'phrases.compat.configuration.release_manager.replay.subtitle'}",
"capture_channel": "@:{'phrases.compat.configuration.release_manager.replay.capture_channel'}",
"target_type": "@:{'phrases.compat.configuration.release_manager.replay.target_type'}",
"target": "@:{'phrases.compat.configuration.release_manager.replay.target'}",
"target_message": "@:{'phrases.compat.configuration.release_manager.replay.target_message'}",
"target_placeholder": "@:{'phrases.compat.configuration.release_manager.replay.target_placeholder'}",
"channel_scope": "@:{'phrases.compat.configuration.release_manager.replay.channel_scope'}",
"channel_scope_message": "@:{'phrases.compat.configuration.release_manager.replay.channel_scope_message'}",
"no_channel_scope": "@:{'phrases.compat.configuration.release_manager.replay.no_channel_scope'}",
"capture_level": "@:{'phrases.compat.configuration.release_manager.replay.capture_level'}",
"expires": "@:{'phrases.compat.configuration.release_manager.replay.expires'}",
"expires_message": "@:{'phrases.compat.configuration.release_manager.replay.expires_message'}",
"trace_id": "@:{'phrases.compat.configuration.release_manager.replay.trace_id'}",
"trace_id_message": "@:{'phrases.compat.configuration.release_manager.replay.trace_id_message'}",
"trace_id_placeholder": "@:{'phrases.compat.configuration.release_manager.replay.trace_id_placeholder'}",
"channel": "@:{'phrases.compat.configuration.release_manager.replay.channel'}",
"channel_placeholder": "@:{'phrases.compat.configuration.release_manager.replay.channel_placeholder'}",
"principal_id": "@:{'phrases.compat.configuration.release_manager.replay.principal_id'}",
"principal_id_placeholder": "@:{'phrases.compat.configuration.release_manager.replay.principal_id_placeholder'}",
"module": "@:{'phrases.compat.configuration.release_manager.replay.module'}",
"module_placeholder": "@:{'phrases.compat.configuration.release_manager.replay.module_placeholder'}",
"severity": "@:{'phrases.compat.configuration.release_manager.replay.severity'}",
"any_severity": "@:{'phrases.compat.configuration.release_manager.replay.any_severity'}",
"no_timeline_events": "@:{'phrases.compat.configuration.release_manager.replay.no_timeline_events'}"
},
"integrations": {
"title": "@:{'phrases.compat.configuration.release_manager.integrations.title'}",
"subtitle": "@:{'phrases.compat.configuration.release_manager.integrations.subtitle'}",
"choose_repository": "@:{'phrases.compat.configuration.release_manager.integrations.choose_repository'}",
"add_health_url": "@:{'phrases.compat.configuration.release_manager.integrations.add_health_url'}",
"github_token_not_configured": "@:{'phrases.compat.configuration.release_manager.integrations.github_token_not_configured'}",
"open_github_token_settings": "@:{'phrases.compat.configuration.release_manager.integrations.open_github_token_settings'}",
"channel": "@:{'phrases.compat.configuration.release_manager.integrations.channel'}",
"app": "@:{'phrases.compat.configuration.release_manager.integrations.app'}",
"repository": "@:{'phrases.compat.configuration.release_manager.integrations.repository'}",
"repository_message": "@:{'phrases.compat.configuration.release_manager.integrations.repository_message'}",
"repository_placeholder": "@:{'phrases.compat.configuration.release_manager.integrations.repository_placeholder'}",
"branch": "@:{'phrases.compat.configuration.release_manager.integrations.branch'}",
"branch_message": "@:{'phrases.compat.configuration.release_manager.integrations.branch_message'}",
"branch_placeholder": "@:{'phrases.compat.configuration.release_manager.integrations.branch_placeholder'}",
"latest_prefix": "@:{'phrases.compat.configuration.release_manager.integrations.latest_prefix'}",
"coolify_instance": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_instance'}",
"coolify_instance_message": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_instance_message'}",
"no_coolify_instance": "@:{'phrases.compat.configuration.release_manager.integrations.no_coolify_instance'}",
"coolify_instance_placeholder": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_instance_placeholder'}",
"coolify_service_uuid": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_service_uuid'}",
"coolify_service_uuid_message": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_service_uuid_message'}",
"coolify_service_uuid_placeholder": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_service_uuid_placeholder'}",
"health_url": "@:{'phrases.compat.configuration.release_manager.integrations.health_url'}",
"health_url_message": "@:{'phrases.compat.configuration.release_manager.integrations.health_url_message'}",
"health_url_placeholder": "@:{'phrases.compat.configuration.release_manager.integrations.health_url_placeholder'}",
"ssl_domain": "@:{'phrases.compat.configuration.release_manager.integrations.ssl_domain'}",
"ssl_domain_message": "@:{'phrases.compat.configuration.release_manager.integrations.ssl_domain_message'}",
"ssl_domain_placeholder": "@:{'phrases.compat.configuration.release_manager.integrations.ssl_domain_placeholder'}",
"https_domain_for_coolify": "@:{'phrases.compat.configuration.release_manager.integrations.https_domain_for_coolify'}",
"endpoint_mode": "@:{'phrases.compat.configuration.release_manager.integrations.endpoint_mode'}",
"endpoint_mode_message": "@:{'phrases.compat.configuration.release_manager.integrations.endpoint_mode_message'}",
"endpoint_mode_auto": "@:{'phrases.compat.configuration.release_manager.integrations.endpoint_mode_auto'}",
"endpoint_mode_manual": "@:{'phrases.compat.configuration.release_manager.integrations.endpoint_mode_manual'}",
"manual_endpoint_host": "@:{'phrases.compat.configuration.release_manager.integrations.manual_endpoint_host'}",
"manual_endpoint_host_message": "@:{'phrases.compat.configuration.release_manager.integrations.manual_endpoint_host_message'}",
"manual_endpoint_host_placeholder": "@:{'phrases.compat.configuration.release_manager.integrations.manual_endpoint_host_placeholder'}",
"manual_endpoint_port": "@:{'phrases.compat.configuration.release_manager.integrations.manual_endpoint_port'}",
"manual_endpoint_port_message": "@:{'phrases.compat.configuration.release_manager.integrations.manual_endpoint_port_message'}",
"app_port": "@:{'phrases.compat.configuration.release_manager.integrations.app_port'}",
"app_port_message": "@:{'phrases.compat.configuration.release_manager.integrations.app_port_message'}",
"auto_gateway_endpoint": "@:{'phrases.compat.configuration.release_manager.integrations.auto_gateway_endpoint'}",
"pending_automatic_endpoint": "@:{'phrases.compat.configuration.release_manager.integrations.pending_automatic_endpoint'}",
"auto_deploy": "@:{'phrases.compat.configuration.release_manager.integrations.auto_deploy'}",
"auto_deploy_tooltip": "@:{'phrases.compat.configuration.release_manager.integrations.auto_deploy_tooltip'}",
"coolify_ssl": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_ssl'}",
"coolify_ssl_tooltip": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_ssl_tooltip'}",
"deploy_now": "@:{'phrases.compat.configuration.release_manager.integrations.deploy_now'}",
"deploy_now_tooltip": "@:{'phrases.compat.configuration.release_manager.integrations.deploy_now_tooltip'}",
"create_missing_service": "@:{'phrases.compat.configuration.release_manager.integrations.create_missing_service'}",
"create_missing_service_tooltip": "@:{'phrases.compat.configuration.release_manager.integrations.create_missing_service_tooltip'}",
"test_access_tooltip": "@:{'phrases.compat.configuration.release_manager.integrations.test_access_tooltip'}"
}
}
}
}
@@ -0,0 +1,59 @@
{
"configuration": {
"release_manager": {
"integrations": {
"title": "@:{'phrases.compat.configuration.release_manager.integrations.title'}",
"subtitle": "@:{'phrases.compat.configuration.release_manager.integrations.subtitle'}",
"choose_repository": "@:{'phrases.compat.configuration.release_manager.integrations.choose_repository'}",
"add_health_url": "@:{'phrases.compat.configuration.release_manager.integrations.add_health_url'}",
"github_token_not_configured": "@:{'phrases.compat.configuration.release_manager.integrations.github_token_not_configured'}",
"open_github_token_settings": "@:{'phrases.compat.configuration.release_manager.integrations.open_github_token_settings'}",
"channel": "@:{'phrases.compat.configuration.release_manager.integrations.channel'}",
"app": "@:{'phrases.compat.configuration.release_manager.integrations.app'}",
"repository": "@:{'phrases.compat.configuration.release_manager.integrations.repository'}",
"repository_message": "@:{'phrases.compat.configuration.release_manager.integrations.repository_message'}",
"repository_placeholder": "@:{'phrases.compat.configuration.release_manager.integrations.repository_placeholder'}",
"branch": "@:{'phrases.compat.configuration.release_manager.integrations.branch'}",
"branch_message": "@:{'phrases.compat.configuration.release_manager.integrations.branch_message'}",
"branch_placeholder": "@:{'phrases.compat.configuration.release_manager.integrations.branch_placeholder'}",
"latest_prefix": "@:{'phrases.compat.configuration.release_manager.integrations.latest_prefix'}",
"coolify_instance": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_instance'}",
"coolify_instance_message": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_instance_message'}",
"no_coolify_instance": "@:{'phrases.compat.configuration.release_manager.integrations.no_coolify_instance'}",
"coolify_instance_placeholder": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_instance_placeholder'}",
"coolify_service_uuid": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_service_uuid'}",
"coolify_service_uuid_message": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_service_uuid_message'}",
"coolify_service_uuid_placeholder": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_service_uuid_placeholder'}",
"health_url": "@:{'phrases.compat.configuration.release_manager.integrations.health_url'}",
"health_url_message": "@:{'phrases.compat.configuration.release_manager.integrations.health_url_message'}",
"health_url_placeholder": "@:{'phrases.compat.configuration.release_manager.integrations.health_url_placeholder'}",
"ssl_domain": "@:{'phrases.compat.configuration.release_manager.integrations.ssl_domain'}",
"ssl_domain_message": "@:{'phrases.compat.configuration.release_manager.integrations.ssl_domain_message'}",
"ssl_domain_placeholder": "@:{'phrases.compat.configuration.release_manager.integrations.ssl_domain_placeholder'}",
"https_domain_for_coolify": "@:{'phrases.compat.configuration.release_manager.integrations.https_domain_for_coolify'}",
"endpoint_mode": "@:{'phrases.compat.configuration.release_manager.integrations.endpoint_mode'}",
"endpoint_mode_message": "@:{'phrases.compat.configuration.release_manager.integrations.endpoint_mode_message'}",
"endpoint_mode_auto": "@:{'phrases.compat.configuration.release_manager.integrations.endpoint_mode_auto'}",
"endpoint_mode_manual": "@:{'phrases.compat.configuration.release_manager.integrations.endpoint_mode_manual'}",
"manual_endpoint_host": "@:{'phrases.compat.configuration.release_manager.integrations.manual_endpoint_host'}",
"manual_endpoint_host_message": "@:{'phrases.compat.configuration.release_manager.integrations.manual_endpoint_host_message'}",
"manual_endpoint_host_placeholder": "@:{'phrases.compat.configuration.release_manager.integrations.manual_endpoint_host_placeholder'}",
"manual_endpoint_port": "@:{'phrases.compat.configuration.release_manager.integrations.manual_endpoint_port'}",
"manual_endpoint_port_message": "@:{'phrases.compat.configuration.release_manager.integrations.manual_endpoint_port_message'}",
"app_port": "@:{'phrases.compat.configuration.release_manager.integrations.app_port'}",
"app_port_message": "@:{'phrases.compat.configuration.release_manager.integrations.app_port_message'}",
"auto_gateway_endpoint": "@:{'phrases.compat.configuration.release_manager.integrations.auto_gateway_endpoint'}",
"pending_automatic_endpoint": "@:{'phrases.compat.configuration.release_manager.integrations.pending_automatic_endpoint'}",
"auto_deploy": "@:{'phrases.compat.configuration.release_manager.integrations.auto_deploy'}",
"auto_deploy_tooltip": "@:{'phrases.compat.configuration.release_manager.integrations.auto_deploy_tooltip'}",
"coolify_ssl": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_ssl'}",
"coolify_ssl_tooltip": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_ssl_tooltip'}",
"deploy_now": "@:{'phrases.compat.configuration.release_manager.integrations.deploy_now'}",
"deploy_now_tooltip": "@:{'phrases.compat.configuration.release_manager.integrations.deploy_now_tooltip'}",
"create_missing_service": "@:{'phrases.compat.configuration.release_manager.integrations.create_missing_service'}",
"create_missing_service_tooltip": "@:{'phrases.compat.configuration.release_manager.integrations.create_missing_service_tooltip'}",
"test_access_tooltip": "@:{'phrases.compat.configuration.release_manager.integrations.test_access_tooltip'}"
}
}
}
}
@@ -0,0 +1,435 @@
{
"configuration": {
"release_manager": {
"title": "@:{'phrases.compat.configuration.release_manager.title'}",
"subtitle": "@:{'phrases.compat.configuration.release_manager.subtitle'}",
"channel_names": {
"stable": "@:{'phrases.compat.configuration.release_manager.channel_names.stable'}",
"canary": "@:{'phrases.compat.configuration.release_manager.channel_names.canary'}",
"internal": "@:{'phrases.compat.configuration.release_manager.channel_names.internal'}"
},
"channel_descriptions": {
"stable": "@:{'phrases.compat.configuration.release_manager.channel_descriptions.stable'}",
"canary": "@:{'phrases.compat.configuration.release_manager.channel_descriptions.canary'}",
"internal": "@:{'phrases.compat.configuration.release_manager.channel_descriptions.internal'}"
},
"control_api": {
"title": "@:{'phrases.compat.configuration.release_manager.control_api.title'}",
"tooltip": "@:{'phrases.compat.configuration.release_manager.control_api.tooltip'}",
"example": "@:{'phrases.compat.configuration.release_manager.control_api.example'}",
"endpoint_label": "@:{'phrases.compat.configuration.release_manager.control_api.endpoint_label'}",
"endpoint_message": "@:{'phrases.compat.configuration.release_manager.control_api.endpoint_message'}",
"endpoint_aria": "@:{'phrases.compat.configuration.release_manager.control_api.endpoint_aria'}",
"use_tooltip": "@:{'phrases.compat.configuration.release_manager.control_api.use_tooltip'}",
"use": "@:{'phrases.compat.configuration.release_manager.control_api.use'}",
"reset_tooltip": "@:{'phrases.compat.configuration.release_manager.control_api.reset_tooltip'}",
"reset": "@:{'phrases.compat.configuration.release_manager.control_api.reset'}",
"known_endpoints": "@:{'phrases.compat.configuration.release_manager.control_api.known_endpoints'}"
},
"tabs": {
"overview": {
"label": "@:{'phrases.compat.configuration.release_manager.tabs.overview.label'}",
"description": "@:{'phrases.compat.configuration.release_manager.tabs.overview.description'}"
},
"channels": {
"label": "@:{'phrases.compat.configuration.release_manager.tabs.channels.label'}",
"description": "@:{'phrases.compat.configuration.release_manager.tabs.channels.description'}"
},
"assignments": {
"label": "@:{'phrases.compat.configuration.release_manager.tabs.assignments.label'}",
"description": "@:{'phrases.compat.configuration.release_manager.tabs.assignments.description'}"
},
"deployments": {
"label": "@:{'phrases.compat.configuration.release_manager.tabs.deployments.label'}",
"description": "@:{'phrases.compat.configuration.release_manager.tabs.deployments.description'}"
},
"replay": {
"label": "@:{'phrases.compat.configuration.release_manager.tabs.replay.label'}",
"description": "@:{'phrases.compat.configuration.release_manager.tabs.replay.description'}"
},
"integrations": {
"label": "@:{'phrases.compat.configuration.release_manager.tabs.integrations.label'}",
"description": "@:{'phrases.compat.configuration.release_manager.tabs.integrations.description'}"
},
"settings": {
"label": "@:{'phrases.compat.configuration.release_manager.tabs.settings.label'}",
"description": "@:{'phrases.compat.configuration.release_manager.tabs.settings.description'}"
}
},
"setup": {
"channels": "@:{'phrases.compat.configuration.release_manager.setup.channels'}",
"targets": "@:{'phrases.compat.configuration.release_manager.setup.targets'}",
"assignments": "@:{'phrases.compat.configuration.release_manager.setup.assignments'}",
"deployments": "@:{'phrases.compat.configuration.release_manager.setup.deployments'}",
"replay": "@:{'phrases.compat.configuration.release_manager.setup.replay'}"
},
"stats": {
"channels": "@:{'phrases.compat.configuration.release_manager.stats.channels'}",
"targets": "@:{'phrases.compat.configuration.release_manager.stats.targets'}",
"deployments": "@:{'phrases.compat.configuration.release_manager.stats.deployments'}",
"timeline_events": "@:{'phrases.compat.configuration.release_manager.stats.timeline_events'}"
},
"overview": {
"title": "@:{'phrases.compat.configuration.release_manager.overview.title'}",
"subtitle": "@:{'phrases.compat.configuration.release_manager.overview.subtitle'}",
"guided_setup": "@:{'phrases.compat.configuration.release_manager.overview.guided_setup'}",
"next_step": "@:{'phrases.compat.configuration.release_manager.overview.next_step'}",
"ready": "@:{'phrases.compat.configuration.release_manager.overview.ready'}",
"add_suggested_channel": "@:{'phrases.compat.configuration.release_manager.overview.add_suggested_channel'}",
"module_health_empty": "@:{'phrases.compat.configuration.release_manager.overview.module_health_empty'}"
},
"settings": {
"title": "@:{'phrases.compat.configuration.release_manager.settings.title'}",
"subtitle": "@:{'phrases.compat.configuration.release_manager.settings.subtitle'}",
"github_token": "@:{'phrases.compat.configuration.release_manager.settings.github_token'}",
"github_api_url": "@:{'phrases.compat.configuration.release_manager.settings.github_api_url'}",
"webhook_secret": "@:{'phrases.compat.configuration.release_manager.settings.webhook_secret'}",
"github_webhook_secret": "@:{'phrases.compat.configuration.release_manager.settings.github_webhook_secret'}",
"configured": "@:{'phrases.compat.configuration.release_manager.settings.configured'}",
"not_configured": "@:{'phrases.compat.configuration.release_manager.settings.not_configured'}",
"loaded_from": "@:{'phrases.compat.configuration.release_manager.settings.loaded_from'}",
"set_below": "@:{'phrases.compat.configuration.release_manager.settings.set_below'}",
"private_repositories_prefix": "@:{'phrases.compat.configuration.release_manager.settings.private_repositories_prefix'}",
"private_repositories_or": "@:{'phrases.compat.configuration.release_manager.settings.private_repositories_or'}",
"github_token_message": "@:{'phrases.compat.configuration.release_manager.settings.github_token_message'}",
"github_token_placeholder": "@:{'phrases.compat.configuration.release_manager.settings.github_token_placeholder'}",
"github_api_url_message": "@:{'phrases.compat.configuration.release_manager.settings.github_api_url_message'}",
"webhook_secret_message": "@:{'phrases.compat.configuration.release_manager.settings.webhook_secret_message'}",
"webhook_secret_placeholder": "@:{'phrases.compat.configuration.release_manager.settings.webhook_secret_placeholder'}",
"save": "@:{'phrases.compat.configuration.release_manager.settings.save'}",
"back_to_integrations": "@:{'phrases.compat.configuration.release_manager.settings.back_to_integrations'}",
"guide": {
"steps": {
"github_token": "@:{'phrases.compat.configuration.release_manager.settings.guide.steps.github_token'}",
"github_api_url": "@:{'phrases.compat.configuration.release_manager.settings.guide.steps.github_api_url'}",
"github_webhook_secret": "@:{'phrases.compat.configuration.release_manager.settings.guide.steps.github_webhook_secret'}",
"review": "@:{'phrases.compat.configuration.release_manager.settings.guide.steps.review'}"
},
"github_token_title": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_token_title'}",
"github_token_description": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_token_description'}",
"configure_this_in": "@:{'phrases.compat.configuration.release_manager.settings.guide.configure_this_in'}",
"server_environment_variable": "@:{'phrases.compat.configuration.release_manager.settings.guide.server_environment_variable'}",
"takes_precedence": "@:{'phrases.compat.configuration.release_manager.settings.guide.takes_precedence'}",
"recommended_access": "@:{'phrases.compat.configuration.release_manager.settings.guide.recommended_access'}",
"recommended_access_description": "@:{'phrases.compat.configuration.release_manager.settings.guide.recommended_access_description'}",
"github_api_url_title": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_api_url_title'}",
"github_api_url_description": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_api_url_description'}",
"github_api_url_invalid": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_api_url_invalid'}",
"github_cloud": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_cloud'}",
"enterprise_example": "@:{'phrases.compat.configuration.release_manager.settings.guide.enterprise_example'}",
"github_webhook_secret_title": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_webhook_secret_title'}",
"github_webhook_secret_description": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_webhook_secret_description'}",
"github_webhook_url": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_webhook_url'}",
"store_shared_secret_in": "@:{'phrases.compat.configuration.release_manager.settings.guide.store_shared_secret_in'}",
"review_title": "@:{'phrases.compat.configuration.release_manager.settings.guide.review_title'}",
"review_description": "@:{'phrases.compat.configuration.release_manager.settings.guide.review_description'}",
"ready": "@:{'phrases.compat.configuration.release_manager.settings.guide.ready'}",
"needs_value": "@:{'phrases.compat.configuration.release_manager.settings.guide.needs_value'}"
}
},
"channel_unavailable": {
"kicker": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.kicker'}",
"title": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.title'}",
"summary_prefix": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.summary_prefix'}",
"summary_suffix": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.summary_suffix'}",
"release_bundle": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.release_bundle'}",
"frontend_version": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.frontend_version'}",
"api_version": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.api_version'}",
"frontend_base_url": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.frontend_base_url'}",
"api_base_url": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.api_base_url'}",
"frontend_entry": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.frontend_entry'}",
"release_runtime": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.release_runtime'}",
"checking_again": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.checking_again'}",
"refresh_error": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.refresh_error'}",
"ignore": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.ignore'}",
"check_again": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.check_again'}",
"logout": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.logout'}",
"base_image": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.base_image'}"
},
"channel_switched": {
"kicker": "@:{'phrases.compat.configuration.release_manager.channel_switched.kicker'}",
"title": "@:{'phrases.compat.configuration.release_manager.channel_switched.title'}",
"summary_prefix": "@:{'phrases.compat.configuration.release_manager.channel_switched.summary_prefix'}",
"summary_suffix": "@:{'phrases.compat.configuration.release_manager.channel_switched.summary_suffix'}",
"bundle": "@:{'phrases.compat.configuration.release_manager.channel_switched.bundle'}",
"frontend": "@:{'phrases.compat.configuration.release_manager.channel_switched.frontend'}",
"api": "@:{'phrases.compat.configuration.release_manager.channel_switched.api'}",
"frontend_version": "@:{'phrases.compat.configuration.release_manager.channel_switched.frontend_version'}",
"api_version": "@:{'phrases.compat.configuration.release_manager.channel_switched.api_version'}",
"assigned_channel": "@:{'phrases.compat.configuration.release_manager.channel_switched.assigned_channel'}",
"current_app_image": "@:{'phrases.compat.configuration.release_manager.channel_switched.current_app_image'}",
"current_api": "@:{'phrases.compat.configuration.release_manager.channel_switched.current_api'}",
"base_image": "@:{'phrases.compat.configuration.release_manager.channel_switched.base_image'}",
"continue": "@:{'phrases.compat.configuration.release_manager.channel_switched.continue'}"
},
"channel_selector": {
"title": "@:{'phrases.compat.configuration.release_manager.channel_selector.title'}",
"subtitle": "@:{'phrases.compat.configuration.release_manager.channel_selector.subtitle'}",
"sidebar_title": "@:{'phrases.compat.configuration.release_manager.channel_selector.sidebar_title'}",
"default": "@:{'phrases.compat.configuration.release_manager.channel_selector.default'}",
"ready": "@:{'phrases.compat.configuration.release_manager.channel_selector.ready'}",
"unavailable": "@:{'phrases.compat.configuration.release_manager.channel_selector.unavailable'}",
"release_bundle": "@:{'phrases.compat.configuration.release_manager.channel_selector.release_bundle'}",
"frontend_version": "@:{'phrases.compat.configuration.release_manager.channel_selector.frontend_version'}",
"api_version": "@:{'phrases.compat.configuration.release_manager.channel_selector.api_version'}",
"frontend_base_url": "@:{'phrases.compat.configuration.release_manager.channel_selector.frontend_base_url'}",
"api_base_url": "@:{'phrases.compat.configuration.release_manager.channel_selector.api_base_url'}",
"frontend_entry": "@:{'phrases.compat.configuration.release_manager.channel_selector.frontend_entry'}",
"release_runtime": "@:{'phrases.compat.configuration.release_manager.channel_selector.release_runtime'}",
"bundle": "@:{'phrases.compat.configuration.release_manager.channel_selector.bundle'}",
"frontend": "@:{'phrases.compat.configuration.release_manager.channel_selector.frontend'}",
"api": "@:{'phrases.compat.configuration.release_manager.channel_selector.api'}",
"use_local_frontend": "@:{'phrases.compat.configuration.release_manager.channel_selector.use_local_frontend'}"
},
"common": {
"yes": "@:{'phrases.compat.configuration.release_manager.common.yes'}",
"no": "@:{'phrases.compat.configuration.release_manager.common.no'}",
"off": "@:{'phrases.compat.configuration.release_manager.common.off'}"
},
"actions": {
"edit": "@:{'phrases.compat.configuration.release_manager.actions.edit'}",
"rollback": "@:{'phrases.compat.configuration.release_manager.actions.rollback'}",
"update": "@:{'phrases.compat.configuration.release_manager.actions.update'}",
"create": "@:{'phrases.compat.configuration.release_manager.actions.create'}",
"clear": "@:{'phrases.compat.configuration.release_manager.actions.clear'}",
"assign": "@:{'phrases.compat.configuration.release_manager.actions.assign'}",
"remove": "@:{'phrases.compat.configuration.release_manager.actions.remove'}",
"test_access": "@:{'phrases.compat.configuration.release_manager.actions.test_access'}",
"deploy": "@:{'phrases.compat.configuration.release_manager.actions.deploy'}",
"redeploy": "@:{'phrases.compat.configuration.release_manager.actions.redeploy'}",
"promote": "@:{'phrases.compat.configuration.release_manager.actions.promote'}",
"enable": "@:{'phrases.compat.configuration.release_manager.actions.enable'}",
"search": "@:{'phrases.compat.configuration.release_manager.actions.search'}",
"save_target": "@:{'phrases.compat.configuration.release_manager.actions.save_target'}",
"delete": "@:{'phrases.compat.configuration.release_manager.actions.delete'}",
"back": "@:{'phrases.compat.configuration.release_manager.actions.back'}",
"next": "@:{'phrases.compat.configuration.release_manager.actions.next'}"
},
"tables": {
"channel": "@:{'phrases.compat.configuration.release_manager.tables.channel'}",
"rollout": "@:{'phrases.compat.configuration.release_manager.tables.rollout'}",
"frontend": "@:{'phrases.compat.configuration.release_manager.tables.frontend'}",
"api": "@:{'phrases.compat.configuration.release_manager.tables.api'}",
"replay": "@:{'phrases.compat.configuration.release_manager.tables.replay'}",
"actions": "@:{'phrases.compat.configuration.release_manager.tables.actions'}",
"module": "@:{'phrases.compat.configuration.release_manager.tables.module'}",
"status": "@:{'phrases.compat.configuration.release_manager.tables.status'}",
"checked": "@:{'phrases.compat.configuration.release_manager.tables.checked'}",
"subject": "@:{'phrases.compat.configuration.release_manager.tables.subject'}",
"reason": "@:{'phrases.compat.configuration.release_manager.tables.reason'}",
"expires": "@:{'phrases.compat.configuration.release_manager.tables.expires'}",
"deployment": "@:{'phrases.compat.configuration.release_manager.tables.deployment'}",
"source": "@:{'phrases.compat.configuration.release_manager.tables.source'}",
"started": "@:{'phrases.compat.configuration.release_manager.tables.started'}",
"target": "@:{'phrases.compat.configuration.release_manager.tables.target'}",
"coolify": "@:{'phrases.compat.configuration.release_manager.tables.coolify'}",
"auto": "@:{'phrases.compat.configuration.release_manager.tables.auto'}"
},
"values": {
"app": {
"frontend": "@:{'phrases.compat.configuration.release_manager.values.app.frontend'}",
"api": "@:{'phrases.compat.configuration.release_manager.values.app.api'}"
},
"capture_level": {
"metadata": "@:{'phrases.compat.configuration.release_manager.values.capture_level.metadata'}",
"full_redacted": "@:{'phrases.compat.configuration.release_manager.values.capture_level.full_redacted'}"
},
"subject_type": {
"user": "@:{'phrases.compat.configuration.release_manager.values.subject_type.user'}",
"subuser": "@:{'phrases.compat.configuration.release_manager.values.subject_type.subuser'}",
"customer": "@:{'phrases.compat.configuration.release_manager.values.subject_type.customer'}",
"channel": "@:{'phrases.compat.configuration.release_manager.values.subject_type.channel'}"
},
"severity": {
"error": "@:{'phrases.compat.configuration.release_manager.values.severity.error'}",
"warning": "@:{'phrases.compat.configuration.release_manager.values.severity.warning'}",
"info": "@:{'phrases.compat.configuration.release_manager.values.severity.info'}"
},
"status": {
"ok": "@:{'phrases.compat.configuration.release_manager.values.status.ok'}",
"active": "@:{'phrases.compat.configuration.release_manager.values.status.active'}",
"deployed": "@:{'phrases.compat.configuration.release_manager.values.status.deployed'}",
"stable": "@:{'phrases.compat.configuration.release_manager.values.status.stable'}",
"enabled": "@:{'phrases.compat.configuration.release_manager.values.status.enabled'}",
"queued": "@:{'phrases.compat.configuration.release_manager.values.status.queued'}",
"deploying": "@:{'phrases.compat.configuration.release_manager.values.status.deploying'}",
"metadata": "@:{'phrases.compat.configuration.release_manager.values.status.metadata'}",
"not_configured": "@:{'phrases.compat.configuration.release_manager.values.status.not_configured'}",
"warning": "@:{'phrases.compat.configuration.release_manager.values.status.warning'}",
"failed": "@:{'phrases.compat.configuration.release_manager.values.status.failed'}",
"down": "@:{'phrases.compat.configuration.release_manager.values.status.down'}",
"error": "@:{'phrases.compat.configuration.release_manager.values.status.error'}",
"unknown": "@:{'phrases.compat.configuration.release_manager.values.status.unknown'}"
}
},
"autocomplete": {
"suggested_value": "@:{'phrases.compat.configuration.release_manager.autocomplete.suggested_value'}",
"suggestion": "@:{'phrases.compat.configuration.release_manager.autocomplete.suggestion'}",
"use_typed_value": "@:{'phrases.compat.configuration.release_manager.autocomplete.use_typed_value'}",
"custom": "@:{'phrases.compat.configuration.release_manager.autocomplete.custom'}",
"api_url": "@:{'phrases.compat.configuration.release_manager.autocomplete.api_url'}",
"frontend_url": "@:{'phrases.compat.configuration.release_manager.autocomplete.frontend_url'}",
"known_release_url": "@:{'phrases.compat.configuration.release_manager.autocomplete.known_release_url'}",
"deploy_here_with_coolify": "@:{'phrases.compat.configuration.release_manager.autocomplete.deploy_here_with_coolify'}",
"prepare_coolify_target": "@:{'phrases.compat.configuration.release_manager.autocomplete.prepare_coolify_target'}",
"coolify_ssl": "@:{'phrases.compat.configuration.release_manager.autocomplete.coolify_ssl'}",
"github_repository": "@:{'phrases.compat.configuration.release_manager.autocomplete.github_repository'}",
"owner_repository": "@:{'phrases.compat.configuration.release_manager.autocomplete.owner_repository'}",
"git_branch": "@:{'phrases.compat.configuration.release_manager.autocomplete.git_branch'}",
"deployment_branch": "@:{'phrases.compat.configuration.release_manager.autocomplete.deployment_branch'}",
"github_commit": "@:{'phrases.compat.configuration.release_manager.autocomplete.github_commit'}",
"git_commit": "@:{'phrases.compat.configuration.release_manager.autocomplete.git_commit'}",
"use_specific_commit_sha": "@:{'phrases.compat.configuration.release_manager.autocomplete.use_specific_commit_sha'}",
"custom_commit": "@:{'phrases.compat.configuration.release_manager.autocomplete.custom_commit'}",
"health_url": "@:{'phrases.compat.configuration.release_manager.autocomplete.health_url'}",
"release_health_check": "@:{'phrases.compat.configuration.release_manager.autocomplete.release_health_check'}",
"coolify_service": "@:{'phrases.compat.configuration.release_manager.autocomplete.coolify_service'}",
"service_uuid": "@:{'phrases.compat.configuration.release_manager.autocomplete.service_uuid'}",
"known_coolify_service_uuid": "@:{'phrases.compat.configuration.release_manager.autocomplete.known_coolify_service_uuid'}",
"control_api": "@:{'phrases.compat.configuration.release_manager.autocomplete.control_api'}",
"release_manager_api_endpoint": "@:{'phrases.compat.configuration.release_manager.autocomplete.release_manager_api_endpoint'}",
"module": "@:{'phrases.compat.configuration.release_manager.autocomplete.module'}",
"timeline_module_key": "@:{'phrases.compat.configuration.release_manager.autocomplete.timeline_module_key'}"
},
"errors": {
"github_repositories": "@:{'phrases.compat.configuration.release_manager.errors.github_repositories'}",
"github_branches": "@:{'phrases.compat.configuration.release_manager.errors.github_branches'}",
"repository_branch_required": "@:{'phrases.compat.configuration.release_manager.errors.repository_branch_required'}",
"github_access_failed": "@:{'phrases.compat.configuration.release_manager.errors.github_access_failed'}"
},
"channels": {
"title": "@:{'phrases.compat.configuration.release_manager.channels.title'}",
"subtitle": "@:{'phrases.compat.configuration.release_manager.channels.subtitle'}",
"rollout_percent": "@:{'phrases.compat.configuration.release_manager.channels.rollout_percent'}",
"already_configured": "@:{'phrases.compat.configuration.release_manager.channels.already_configured'}",
"slug": "@:{'phrases.compat.configuration.release_manager.channels.slug'}",
"slug_message": "@:{'phrases.compat.configuration.release_manager.channels.slug_message'}",
"slug_placeholder": "@:{'phrases.compat.configuration.release_manager.channels.slug_placeholder'}",
"name": "@:{'phrases.compat.configuration.release_manager.channels.name'}",
"name_message": "@:{'phrases.compat.configuration.release_manager.channels.name_message'}",
"name_placeholder": "@:{'phrases.compat.configuration.release_manager.channels.name_placeholder'}",
"rollout": "@:{'phrases.compat.configuration.release_manager.channels.rollout'}",
"rollout_message": "@:{'phrases.compat.configuration.release_manager.channels.rollout_message'}",
"frontend_url": "@:{'phrases.compat.configuration.release_manager.channels.frontend_url'}",
"frontend_url_message": "@:{'phrases.compat.configuration.release_manager.channels.frontend_url_message'}",
"frontend_url_placeholder": "@:{'phrases.compat.configuration.release_manager.channels.frontend_url_placeholder'}",
"no_matching_release_url": "@:{'phrases.compat.configuration.release_manager.channels.no_matching_release_url'}",
"api_url": "@:{'phrases.compat.configuration.release_manager.channels.api_url'}",
"api_url_message": "@:{'phrases.compat.configuration.release_manager.channels.api_url_message'}",
"api_url_placeholder": "@:{'phrases.compat.configuration.release_manager.channels.api_url_placeholder'}",
"no_matching_api_url": "@:{'phrases.compat.configuration.release_manager.channels.no_matching_api_url'}",
"replay_capture": "@:{'phrases.compat.configuration.release_manager.channels.replay_capture'}",
"replay_capture_message": "@:{'phrases.compat.configuration.release_manager.channels.replay_capture_message'}",
"retention_days": "@:{'phrases.compat.configuration.release_manager.channels.retention_days'}",
"retention_days_message": "@:{'phrases.compat.configuration.release_manager.channels.retention_days_message'}",
"enabled": "@:{'phrases.compat.configuration.release_manager.channels.enabled'}",
"default_channel": "@:{'phrases.compat.configuration.release_manager.channels.default_channel'}",
"replay_enabled": "@:{'phrases.compat.configuration.release_manager.channels.replay_enabled'}",
"frontend_chip": "@:{'phrases.compat.configuration.release_manager.channels.frontend_chip'}",
"api_chip": "@:{'phrases.compat.configuration.release_manager.channels.api_chip'}"
},
"assignments": {
"title": "@:{'phrases.compat.configuration.release_manager.assignments.title'}",
"subtitle": "@:{'phrases.compat.configuration.release_manager.assignments.subtitle'}",
"subject": "@:{'phrases.compat.configuration.release_manager.assignments.subject'}",
"subject_message": "@:{'phrases.compat.configuration.release_manager.assignments.subject_message'}",
"subject_placeholder": "@:{'phrases.compat.configuration.release_manager.assignments.subject_placeholder'}",
"subject_empty": "@:{'phrases.compat.configuration.release_manager.assignments.subject_empty'}",
"group_users": "@:{'phrases.compat.configuration.release_manager.assignments.group_users'}",
"group_subusers": "@:{'phrases.compat.configuration.release_manager.assignments.group_subusers'}",
"group_customers": "@:{'phrases.compat.configuration.release_manager.assignments.group_customers'}",
"group_manual": "@:{'phrases.compat.configuration.release_manager.assignments.group_manual'}",
"manual_user": "@:{'phrases.compat.configuration.release_manager.assignments.manual_user'}",
"manual_subuser": "@:{'phrases.compat.configuration.release_manager.assignments.manual_subuser'}",
"manual_customer": "@:{'phrases.compat.configuration.release_manager.assignments.manual_customer'}",
"manual_description": "@:{'phrases.compat.configuration.release_manager.assignments.manual_description'}",
"subject_type": "@:{'phrases.compat.configuration.release_manager.assignments.subject_type'}",
"subject_type_message": "@:{'phrases.compat.configuration.release_manager.assignments.subject_type_message'}",
"customer": "@:{'phrases.compat.configuration.release_manager.assignments.customer'}",
"customer_message": "@:{'phrases.compat.configuration.release_manager.assignments.customer_message'}",
"customer_placeholder": "@:{'phrases.compat.configuration.release_manager.assignments.customer_placeholder'}",
"subject_id": "@:{'phrases.compat.configuration.release_manager.assignments.subject_id'}",
"subject_id_message": "@:{'phrases.compat.configuration.release_manager.assignments.subject_id_message'}",
"subject_id_placeholder": "@:{'phrases.compat.configuration.release_manager.assignments.subject_id_placeholder'}",
"channel": "@:{'phrases.compat.configuration.release_manager.assignments.channel'}",
"channel_message": "@:{'phrases.compat.configuration.release_manager.assignments.channel_message'}",
"reason": "@:{'phrases.compat.configuration.release_manager.assignments.reason'}",
"reason_message": "@:{'phrases.compat.configuration.release_manager.assignments.reason_message'}",
"reason_placeholder": "@:{'phrases.compat.configuration.release_manager.assignments.reason_placeholder'}",
"expires": "@:{'phrases.compat.configuration.release_manager.assignments.expires'}",
"expires_message": "@:{'phrases.compat.configuration.release_manager.assignments.expires_message'}"
},
"deployments": {
"title": "@:{'phrases.compat.configuration.release_manager.deployments.title'}",
"subtitle": "@:{'phrases.compat.configuration.release_manager.deployments.subtitle'}",
"manual_deployment_record": "@:{'phrases.compat.configuration.release_manager.deployments.manual_deployment_record'}",
"target": "@:{'phrases.compat.configuration.release_manager.deployments.target'}",
"target_message": "@:{'phrases.compat.configuration.release_manager.deployments.target_message'}",
"select_target_or_manual": "@:{'phrases.compat.configuration.release_manager.deployments.select_target_or_manual'}",
"channel": "@:{'phrases.compat.configuration.release_manager.deployments.channel'}",
"app": "@:{'phrases.compat.configuration.release_manager.deployments.app'}",
"repository": "@:{'phrases.compat.configuration.release_manager.deployments.repository'}",
"repository_message": "@:{'phrases.compat.configuration.release_manager.deployments.repository_message'}",
"repository_placeholder": "@:{'phrases.compat.configuration.release_manager.deployments.repository_placeholder'}",
"branch": "@:{'phrases.compat.configuration.release_manager.deployments.branch'}",
"branch_message": "@:{'phrases.compat.configuration.release_manager.deployments.branch_message'}",
"branch_placeholder": "@:{'phrases.compat.configuration.release_manager.deployments.branch_placeholder'}",
"commit_selection": "@:{'phrases.compat.configuration.release_manager.deployments.commit_selection'}",
"commit_selection_message": "@:{'phrases.compat.configuration.release_manager.deployments.commit_selection_message'}",
"latest_from_branch": "@:{'phrases.compat.configuration.release_manager.deployments.latest_from_branch'}",
"specific_commit": "@:{'phrases.compat.configuration.release_manager.deployments.specific_commit'}",
"specific_commit_message": "@:{'phrases.compat.configuration.release_manager.deployments.specific_commit_message'}",
"commit_sha_placeholder": "@:{'phrases.compat.configuration.release_manager.deployments.commit_sha_placeholder'}",
"version_label": "@:{'phrases.compat.configuration.release_manager.deployments.version_label'}",
"version_label_message": "@:{'phrases.compat.configuration.release_manager.deployments.version_label_message'}",
"version_label_placeholder": "@:{'phrases.compat.configuration.release_manager.deployments.version_label_placeholder'}",
"commit_prefix": "@:{'phrases.compat.configuration.release_manager.deployments.commit_prefix'}",
"test_access_tooltip": "@:{'phrases.compat.configuration.release_manager.deployments.test_access_tooltip'}"
},
"replay": {
"title": "@:{'phrases.compat.configuration.release_manager.replay.title'}",
"subtitle": "@:{'phrases.compat.configuration.release_manager.replay.subtitle'}",
"capture_channel": "@:{'phrases.compat.configuration.release_manager.replay.capture_channel'}",
"target_type": "@:{'phrases.compat.configuration.release_manager.replay.target_type'}",
"target": "@:{'phrases.compat.configuration.release_manager.replay.target'}",
"target_message": "@:{'phrases.compat.configuration.release_manager.replay.target_message'}",
"target_placeholder": "@:{'phrases.compat.configuration.release_manager.replay.target_placeholder'}",
"channel_scope": "@:{'phrases.compat.configuration.release_manager.replay.channel_scope'}",
"channel_scope_message": "@:{'phrases.compat.configuration.release_manager.replay.channel_scope_message'}",
"no_channel_scope": "@:{'phrases.compat.configuration.release_manager.replay.no_channel_scope'}",
"capture_level": "@:{'phrases.compat.configuration.release_manager.replay.capture_level'}",
"expires": "@:{'phrases.compat.configuration.release_manager.replay.expires'}",
"expires_message": "@:{'phrases.compat.configuration.release_manager.replay.expires_message'}",
"trace_id": "@:{'phrases.compat.configuration.release_manager.replay.trace_id'}",
"trace_id_message": "@:{'phrases.compat.configuration.release_manager.replay.trace_id_message'}",
"trace_id_placeholder": "@:{'phrases.compat.configuration.release_manager.replay.trace_id_placeholder'}",
"channel": "@:{'phrases.compat.configuration.release_manager.replay.channel'}",
"channel_placeholder": "@:{'phrases.compat.configuration.release_manager.replay.channel_placeholder'}",
"principal_id": "@:{'phrases.compat.configuration.release_manager.replay.principal_id'}",
"principal_id_placeholder": "@:{'phrases.compat.configuration.release_manager.replay.principal_id_placeholder'}",
"module": "@:{'phrases.compat.configuration.release_manager.replay.module'}",
"module_placeholder": "@:{'phrases.compat.configuration.release_manager.replay.module_placeholder'}",
"severity": "@:{'phrases.compat.configuration.release_manager.replay.severity'}",
"any_severity": "@:{'phrases.compat.configuration.release_manager.replay.any_severity'}",
"no_timeline_events": "@:{'phrases.compat.configuration.release_manager.replay.no_timeline_events'}"
},
"loading": {
"assignments": "@:{'phrases.compat.configuration.release_manager.loading.assignments'}",
"channels": "@:{'phrases.compat.configuration.release_manager.loading.channels'}",
"channels_detail": "@:{'phrases.compat.configuration.release_manager.loading.channels_detail'}",
"data_services": "@:{'phrases.compat.configuration.release_manager.loading.data_services'}",
"deployments": "@:{'phrases.compat.configuration.release_manager.loading.deployments'}",
"github_repositories": "@:{'phrases.compat.configuration.release_manager.loading.github_repositories'}",
"integrations": "@:{'phrases.compat.configuration.release_manager.loading.integrations'}",
"operations": "@:{'phrases.compat.configuration.release_manager.loading.operations'}",
"refreshing_summary": "@:{'phrases.compat.configuration.release_manager.loading.refreshing_summary'}",
"refreshing_timeline": "@:{'phrases.compat.configuration.release_manager.loading.refreshing_timeline'}",
"settings": "@:{'phrases.compat.configuration.release_manager.loading.settings'}",
"summary": "@:{'phrases.compat.configuration.release_manager.loading.summary'}",
"summary_detail": "@:{'phrases.compat.configuration.release_manager.loading.summary_detail'}",
"timeline_events": "@:{'phrases.compat.configuration.release_manager.loading.timeline_events'}",
"timeline_sessions": "@:{'phrases.compat.configuration.release_manager.loading.timeline_sessions'}"
}
}
}
}
@@ -21,7 +21,6 @@
"loading_vehicle_types": "@:{'phrases.compat.self_wash.loading_vehicle_types'}",
"lane_unavailable": "@:{'phrases.compat.self_wash.lane_unavailable'}",
"machine": "@:{'phrases.compat.self_wash.machine'}",
"machine_unavailable_for_lane": "@:{'phrases.compat.self_wash.machine_unavailable_for_lane'}",
"no_departments_found": "@:{'phrases.compat.self_wash.no_departments_found'}",
"no_vehicles_found": "@:vehicles.no_vehicles",
"occupied": "@:{'phrases.compat.self_wash.occupied'}",
@@ -0,0 +1,28 @@
{
"compat": {
"admin": {
"pos": {
"settings_wheel": {
"accept_self_serve_wash": "Godta selvbetjent vask",
"accept_self_serve_wash_confirm": "Flytt denne kladden til kunde #{customerNumber} med selvbetjeningsdetaljene?",
"accept_self_serve_wash_success": "Den selvbetjente vasken ble godtatt.",
"email_notifications_section": "E-postvarsler",
"resend_booking_completion_confirmation": "(Re)send booking completion confirmation",
"resend_booking_completion_confirmation_error": "The booking completion confirmation could not be sent",
"resend_booking_completion_confirmation_success": "The booking completion confirmation has been sent",
"resend_booking_confirmation": "(Re)send booking confirmation",
"resend_booking_confirmation_error": "The booking confirmation could not be sent",
"resend_booking_confirmation_success": "The booking confirmation has been sent",
"resend_wash_certificate": "(Re)send wash certificate",
"resend_wash_certificate_error": "The wash certificate could not be sent",
"resend_wash_certificate_success": "The wash certificate has been sent",
"self_serve_customer": "Kunde",
"self_serve_driver": "Sjåfør",
"self_serve_elapsed": "Tid",
"self_serve_wash_attachment": "Selvbetjent vask",
"self_serve_wash_attachment_for_customer": "Selvbetjent vask for kunde #{customerNumber}"
}
}
}
}
}
@@ -298,490 +298,6 @@
"subtitle": "@.capitalize:{'terms.glossary.konfigurasjon'} @:{'terms.glossary.av'} @:{'terms.glossary.xlvask'}-@:{'terms.glossary.integrasjonen'}",
"title": "@:{'terms.glossary.xlvask'}-@:{'terms.glossary.konfigurasjon'}",
"username_desc": "@:{'terms.glossary.brukernavnet'} @:{'terms.glossary.brukes'} @:{'terms.glossary.til'} @:{'terms.glossary.a'} @:{'terms.glossary.autentisere'} @:{'terms.glossary.api'}-@:{'terms.glossary.tilkoblingen'}."
},
"release_manager": {
"title": "Release Manager",
"subtitle": "Gradvise API- og Front-End-utgivelser",
"channel_names": {
"stable": "Stabil",
"canary": "Canary",
"internal": "Intern"
},
"channel_descriptions": {
"stable": "Standard produksjonskanal.",
"canary": "Tidlig produksjonsvalideringskanal.",
"internal": "Intern kanal for ansatte og superbrukervalidering."
},
"control_api": {
"title": "Kontroll-API",
"tooltip": "Release Manager-handlinger sendes til denne API-en. Bruk produksjons-API-en med mindre du tester en staging-backend.",
"example": "Eksempel: https://api.truckwash.io:4433",
"endpoint_label": "API-endepunkt",
"endpoint_message": "Endre dette bare når Release Manager-endepunkter er tilgjengelige på mål-API-en.",
"endpoint_aria": "Release Manager kontroll-API URL",
"use_tooltip": "Last release-data fra denne API-en",
"use": "Bruk",
"reset_tooltip": "Gå tilbake til standard kontroll-API",
"reset": "Tilbakestill",
"known_endpoints": "Kjente kontroll-API-endepunkter"
},
"tabs": {
"overview": {
"label": "Oversikt",
"description": "Kanalhelse, oppsettsfremdrift og siste release-status."
},
"channels": {
"label": "Kanaler",
"description": "Opprett stabile, canary- og målrettede kanaler med rollout-grenser."
},
"assignments": {
"label": "Tildelinger",
"description": "Knytt brukere, underbrukere eller kunder til en bestemt release-kanal."
},
"deployments": {
"label": "Utrullinger",
"description": "Start utrullinger og promoter vellykkede bygg."
},
"replay": {
"label": "Replay",
"description": "Aktiver målrettet innsamling og søk i release-tidslinjehendelser."
},
"integrations": {
"label": "Integrasjoner",
"description": "Koble GitHub-repositories, branches og Coolify-tjenester."
},
"settings": {
"label": "Innstillinger",
"description": "Konfigurer Release Managers GitHub-tilgang og webhook-innstillinger."
}
},
"loading": {
"summary": "Laster Release Manager-data...",
"refreshing_summary": "Oppdaterer Release Manager-data...",
"summary_detail": "Release-status, kanaler, deployments og servicehelse lastes.",
"channels": "Laster release-kanaler...",
"channels_detail": "Kanalvisninger og release-avhengigheter lastes.",
"assignments": "Laster release-tildelinger...",
"deployments": "Laster release-deployments...",
"operations": "Laster release-operasjoner...",
"data_services": "Laster release-datatjenester...",
"integrations": "Laster release-integrasjoner...",
"settings": "Laster Release Manager-innstillinger...",
"github_repositories": "Laster GitHub-repositories...",
"timeline_sessions": "Laster release-tidslinje-sesjoner...",
"timeline_events": "Laster release-tidslinjehendelser...",
"refreshing_timeline": "Oppdaterer release-tidslinje..."
},
"setup": {
"channels": "Kanaler",
"targets": "GitHub- og Coolify-mål",
"assignments": "Pilottildelinger",
"deployments": "Første utrulling",
"replay": "Replay-innsamling"
},
"stats": {
"channels": "Kanaler",
"targets": "Mål",
"deployments": "Utrullinger",
"timeline_events": "Tidslinjehendelser"
},
"overview": {
"title": "Oversikt",
"subtitle": "Aktuell kanalhelse, release-aktivitet og modulstatus.",
"guided_setup": "Veiledet oppsett",
"next_step": "Neste: {step}",
"ready": "Release Manager er klar for daglig drift.",
"add_suggested_channel": "Legg til foreslått kanal",
"module_health_empty": "Modul-health snapshots vises når probes er registrert."
},
"settings": {
"title": "Release Manager-innstillinger",
"subtitle": "Konfigurer GitHub-tokenet som brukes for private repositories og branch-oppslag.",
"github_token": "GitHub-token",
"github_api_url": "GitHub API-URL",
"webhook_secret": "Webhook-hemmelighet",
"github_webhook_secret": "GitHub webhook-hemmelighet",
"configured": "Konfigurert",
"not_configured": "Ikke konfigurert",
"loaded_from": "Lastet fra {variable}",
"set_below": "Angi {variable} nedenfor",
"private_repositories_prefix": "Private repositories leses med serverens miljøvariabel",
"private_repositories_or": "eller modulens konfigurasjonsvariabel",
"github_token_message": "Eksempel: github_pat_... med tilgang til de private repositories Release Manager ruller ut.",
"github_token_placeholder": "La stå tomt for å beholde eksisterende token",
"github_api_url_message": "Konfigurert i ReleaseManager.github_api_url. Bruk https://api.github.com med mindre GitHub Enterprise brukes.",
"webhook_secret_message": "Valgfritt; la stå tomt for å beholde eksisterende hemmelighet.",
"webhook_secret_placeholder": "Webhook HMAC-hemmelighet",
"save": "Lagre innstillinger",
"back_to_integrations": "Tilbake til integrasjoner",
"guide": {
"steps": {
"github_token": "GitHub-token",
"github_api_url": "GitHub API-URL",
"github_webhook_secret": "GitHub webhook secret",
"review": "Gennemgang"
},
"github_token_title": "Legg til GitHub-token",
"github_token_description": "Release Manager bruger denne token til at vise private repositories, branches og commits, før et deployment-mål gemmes eller testes.",
"configure_this_in": "Konfigurer dette i",
"server_environment_variable": "Serverens miljøvariabel",
"takes_precedence": "har forrang når den er satt.",
"recommended_access": "Anbefalt tilgang",
"recommended_access_description": "Fine-grained GitHub-token med Contents: read-adgang til hvert release-repository.",
"github_api_url_title": "Legg til GitHub API-URL",
"github_api_url_description": "Brug GitHub Cloud som standard, eller peg på REST API-base-URLen for en GitHub Enterprise Server-installation.",
"github_api_url_invalid": "Skriv inn en full HTTP- eller HTTPS-URL.",
"github_cloud": "GitHub Cloud",
"enterprise_example": "Enterprise-eksempel",
"github_webhook_secret_title": "Legg til GitHub webhook secret",
"github_webhook_secret_description": "Brug samme secret i GitHub repository-webhooks, så Release Manager kan verificere deployment-events, før de håndteres.",
"github_webhook_url": "GitHub webhook-URL:",
"store_shared_secret_in": "Lagre den delte secreten i",
"review_title": "Gå gjennom GitHub-oppsett",
"review_description": "Gem værdierne, og brug derefter Integrationer til at vælge et privat repository, branch og specifik commit eller seneste commit før adgang testes.",
"ready": "Klar",
"needs_value": "Mangler værdi"
}
},
"channel_unavailable": {
"kicker": "Release Manager",
"title": "Release-kanalen er ikke klar",
"summary_prefix": "Kontoen din er tildelt",
"summary_suffix": ", men kanalen mangler påkrevd release-konfigurasjon.",
"release_bundle": "Release-bundle",
"frontend_version": "Frontend-versjon",
"api_version": "API-versjon",
"frontend_base_url": "Frontend-URL",
"api_base_url": "API-URL",
"frontend_entry": "Frontend-entry",
"release_runtime": "Release-runtime",
"checking_again": "Sjekker igjen om {seconds}s",
"refresh_error": "Release-status kunne ikke oppdateres. Neste automatiske sjekk prøver igjen.",
"ignore": "Ignorer de neste 5 minuttene",
"check_again": "Sjekk igjen",
"logout": "Logg ut",
"base_image": "Basis-image"
},
"channel_switched": {
"kicker": "Release Manager",
"title": "Du er nå på {channel}",
"summary_prefix": "Kontoen din er tildelt release-kanalen",
"summary_suffix": "Denne enheten husker at du har sett denne meldingen.",
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"frontend_version": "Frontend-versjon",
"api_version": "API-versjon",
"assigned_channel": "Tildelt kanal",
"current_app_image": "Nåværende app-image",
"current_api": "Nåværende API",
"base_image": "Basis-image",
"continue": "Fortsett"
},
"channel_selector": {
"title": "Release-kanal",
"subtitle": "Velg hvilken tildelt release-kanal denne enheten skal bruke.",
"sidebar_title": "Release",
"default": "Standard",
"ready": "Klar",
"unavailable": "Ikke klar",
"release_bundle": "Release-bundle",
"frontend_version": "Frontend-versjon",
"api_version": "API-versjon",
"frontend_base_url": "Frontend-URL",
"api_base_url": "API-URL",
"frontend_entry": "Frontend-entry",
"release_runtime": "Release-runtime",
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"use_local_frontend": "Bruk lokal frontend"
},
"common": {
"yes": "ja",
"no": "nei",
"off": "av"
},
"actions": {
"edit": "Rediger",
"rollback": "Rull tilbake",
"update": "Opdater",
"create": "Opprett",
"clear": "Tøm",
"assign": "Tildel",
"remove": "Fjern",
"test_access": "Test tilgang",
"deploy": "Deploy",
"redeploy": "Redeploy",
"promote": "Promoter",
"enable": "Aktiver",
"search": "Søk",
"save_target": "Lagre mål",
"delete": "Slett",
"back": "Tilbake",
"next": "Neste"
},
"tables": {
"channel": "Kanal",
"rollout": "Udrulning",
"frontend": "Frontend",
"api": "API",
"replay": "Replay",
"actions": "Handlinger",
"module": "Modul",
"status": "Status",
"checked": "Kontrollert",
"subject": "Emne",
"reason": "Årsag",
"expires": "Utløper",
"deployment": "Deployment",
"source": "Kilde",
"started": "Startet",
"target": "Mål",
"coolify": "Coolify",
"auto": "Auto"
},
"values": {
"app": {
"frontend": "frontend",
"api": "api"
},
"capture_level": {
"metadata": "metadata",
"full_redacted": "fullt redigert"
},
"subject_type": {
"user": "bruker",
"subuser": "medarbeider",
"customer": "kunde",
"channel": "kanal"
},
"severity": {
"error": "feil",
"warning": "advarsel",
"info": "info"
},
"status": {
"ok": "ok",
"active": "aktiv",
"deployed": "deployet",
"stable": "stabil",
"enabled": "aktivert",
"queued": "i kø",
"deploying": "deploying",
"metadata": "metadata",
"not_configured": "ikke konfigurert",
"warning": "advarsel",
"failed": "feilet",
"down": "nede",
"error": "feil",
"unknown": "ukjent"
}
},
"autocomplete": {
"suggested_value": "Foreslået værdi",
"suggestion": "Forslag",
"use_typed_value": "Brug indtastet værdi",
"custom": "Tilpasset",
"api_url": "API-URL",
"frontend_url": "Frontend-URL",
"known_release_url": "Kendt release-URL",
"deploy_here_with_coolify": "Deploy {app} her med Coolify",
"prepare_coolify_target": "Forbered et Coolify-mål med HTTPS/SSL aktiveret",
"coolify_ssl": "Coolify SSL",
"github_repository": "GitHub-repository",
"owner_repository": "ejer/repository",
"git_branch": "Git-branch",
"deployment_branch": "Deployment-branch",
"github_commit": "GitHub-commit",
"git_commit": "Git-commit",
"use_specific_commit_sha": "Brug specifik commit-SHA",
"custom_commit": "Tilpasset commit",
"health_url": "Health-URL",
"release_health_check": "Release health check",
"coolify_service": "Coolify-service",
"service_uuid": "Service-UUID",
"known_coolify_service_uuid": "Kendt Coolify service-UUID",
"control_api": "Kontrol-API",
"release_manager_api_endpoint": "Release Manager API-endpoint",
"module": "Modul",
"timeline_module_key": "Timeline-modulnøgle"
},
"errors": {
"github_repositories": "Kunne ikke indlæse GitHub-repositories.",
"github_branches": "Kunne ikke indlæse GitHub-branches.",
"repository_branch_required": "Repository og branch er påkrævet før GitHub-adgang kan testes.",
"github_access_failed": "Test af GitHub repository-adgang fejlede."
},
"channels": {
"title": "Kanaler",
"subtitle": "Opprett og juster gradvise release-kanaler.",
"rollout_percent": "{percent}% udrulning",
"already_configured": "Allerede konfigurert",
"slug": "Slug",
"slug_message": "Eksempel: canary",
"slug_placeholder": "slug",
"name": "Navn",
"name_message": "Vises til operatører når de gjennomgår en kanal.",
"name_placeholder": "Navn",
"rollout": "Udrulning",
"rollout_message": "0-100 prosent av ikke-tildelt trafikk.",
"frontend_url": "Frontend-URL",
"frontend_url_message": "Eksempel: https://canary.example.test",
"frontend_url_placeholder": "Frontend-URL",
"no_matching_release_url": "Ingen matchende release-URL.",
"api_url": "API-URL",
"api_url_message": "Eksempel: https://api-canary.example.test",
"api_url_placeholder": "API-URL",
"no_matching_api_url": "Ingen matchende API-URL.",
"replay_capture": "Replay capture",
"replay_capture_message": "Metadata er sikrere til brede kanaler.",
"retention_days": "Oppbevaringsdager",
"retention_days_message": "1-365 dage.",
"enabled": "Aktivert",
"default_channel": "Standardkanal",
"replay_enabled": "Replay aktivert",
"frontend_chip": "Frontend: {url}",
"api_chip": "API: {url}"
},
"assignments": {
"title": "Tildelinger",
"subtitle": "Fest brukere, medarbeidere eller kunder til release-kanaler.",
"subject": "Emne",
"subject_message": "Søk etter brukere, medarbeidere eller kunder, eller skriv et manuelt emne som user:42.",
"subject_placeholder": "Søk eller skriv emne",
"subject_empty": "Ingen emner funnet",
"group_users": "Brukere",
"group_subusers": "Medarbeidere",
"group_customers": "Kunder",
"group_manual": "Manuell",
"manual_user": "Bruker #{id}",
"manual_subuser": "Medarbeider #{id}",
"manual_customer": "Kunde #{id}",
"manual_description": "Bruk inntastet verdi {subject}",
"subject_type": "Emnetype",
"subject_type_message": "Vælg identitetstypen, der skal fastgøres.",
"customer": "Kunde",
"customer_message": "Søk på kundenummer eller navn, og velg deretter nøyaktig kunde.",
"customer_placeholder": "Søk kunde",
"subject_id": "Emne-ID",
"subject_id_message": "Eksempel: bruker-id 42",
"subject_id_placeholder": "Emne-id",
"channel": "Kanal",
"channel_message": "Festede emner hopper over prosentutrullingen.",
"reason": "Årsag",
"reason_message": "Eksempel: canary-validering for kundesupport.",
"reason_placeholder": "Årsag",
"expires": "Utløper",
"expires_message": "Lad feltet være tomt for ingen automatisk udløb."
},
"deployments": {
"title": "Deployments",
"subtitle": "Start, promoter og inspiser release-deployments.",
"manual_deployment_record": "Manuel deployment-post",
"target": "Mål",
"target_message": "Vælg et gemt mål, eller behold manuelle felter redigerbare.",
"select_target_or_manual": "Velg mål eller manuelt",
"channel": "Kanal",
"app": "App",
"repository": "Repository",
"repository_message": "Eksempel: truckwash/front-end-vue",
"repository_placeholder": "ejer/repo",
"branch": "Branch",
"branch_message": "Eksempel: master",
"branch_placeholder": "branch",
"commit_selection": "Commit-valg",
"commit_selection_message": "Seneste slås op som branchens head med den konfigurerede GitHub-token.",
"latest_from_branch": "Siste fra branch",
"specific_commit": "Spesifikk commit",
"specific_commit_message": "Eksempel: feedface1234",
"commit_sha_placeholder": "commit sha",
"version_label": "Versjonsetikett",
"version_label_message": "Eksempel: api-2026.05.19",
"version_label_placeholder": "versionslabel",
"commit_prefix": "Commit: {commit}",
"test_access_tooltip": "Bekræft token-adgang til det valgte repository, branch og commit."
},
"replay": {
"title": "Replay",
"subtitle": "Aktiver målrettet capture og gennemgå fejltimelines trin for trin.",
"capture_channel": "Capture {channel}",
"target_type": "Måltype",
"target": "Mål",
"target_message": "Eksempel: bruker-id 42 eller kanal-slug canary.",
"target_placeholder": "Mål-id eller kanal-slug",
"channel_scope": "Kanalavgrensning",
"channel_scope_message": "Valgfrit; begrænser replay capture til en kanal.",
"no_channel_scope": "Ingen kanalavgrensning",
"capture_level": "Capture-niveau",
"expires": "Utløper",
"expires_message": "Lad feltet være tomt for manuel deaktivering.",
"trace_id": "Trace-ID",
"trace_id_message": "Eksempel: trace-canary-1",
"trace_id_placeholder": "Trace-id",
"channel": "Kanal",
"channel_placeholder": "Kanal",
"principal_id": "Principal-ID",
"principal_id_placeholder": "Principal-id",
"module": "Modul",
"module_placeholder": "Modul",
"severity": "Alvorlighet",
"any_severity": "Alle alvorligheder",
"no_timeline_events": "Ingen timeline-events matcher de aktuelle filtre."
},
"integrations": {
"title": "GitHub og Coolify",
"subtitle": "Knyt GitHub-repositories og branches til kanalspecifikke Coolify-services.",
"choose_repository": "Velg repository",
"add_health_url": "legg til health-URL",
"github_token_not_configured": "GitHub-token er ikke konfigurert; private repositories kan ikke vises.",
"open_github_token_settings": "Åpne GitHub token-innstillinger",
"channel": "Kanal",
"app": "App",
"repository": "Repository",
"repository_message": "Eksempel: truckwash/backend-php",
"repository_placeholder": "ejer/repo",
"branch": "Branch",
"branch_message": "Eksempel: release/canary",
"branch_placeholder": "branch",
"latest_prefix": "Seneste: {commit}",
"coolify_instance": "Coolify-instans",
"coolify_instance_message": "Lad feltet være tomt for manuelle deployment-poster.",
"no_coolify_instance": "Ingen Coolify-instans",
"coolify_instance_placeholder": "Coolify-instans-id",
"coolify_service_uuid": "Coolify service-UUID",
"coolify_service_uuid_message": "Vælg en eksisterende service eller indsæt en UUID.",
"coolify_service_uuid_placeholder": "Coolify service-UUID",
"health_url": "Health-URL",
"health_url_message": "Eksempel: https://api-canary.example.test/ping",
"health_url_placeholder": "Health-URL",
"ssl_domain": "Load balancer-domene",
"ssl_domain_message": "Må være et DNS-domene som routes til Coolify load balanceren. Eksempel: api-v2.truckwash.io",
"ssl_domain_placeholder": "Load balancer-domene",
"https_domain_for_coolify": "Domene kontrollert av load balanceren",
"endpoint_mode": "Endpoint mode",
"endpoint_mode_message": "Use automatic gateway routing or manually override the public host and port.",
"endpoint_mode_auto": "Auto",
"endpoint_mode_manual": "Manual",
"manual_endpoint_host": "Manual host",
"manual_endpoint_host_message": "Public host shown in release previews when manual mode is selected.",
"manual_endpoint_host_placeholder": "api-v2.truckwash.io",
"manual_endpoint_port": "Manual public port",
"manual_endpoint_port_message": "Optional public port from 1 to 65535.",
"app_port": "App port",
"app_port_message": "Internal container port exposed to Coolify routing.",
"auto_gateway_endpoint": "Automatic gateway endpoint",
"pending_automatic_endpoint": "Automatic endpoint resolution pending",
"auto_deploy": "Auto deploy",
"auto_deploy_tooltip": "Opret automatisk deployments, når dette mål ændres.",
"coolify_ssl": "Coolify SSL",
"coolify_ssl_tooltip": "Bed Coolify om at oprette HTTPS for det konfigurerede domæne.",
"deploy_now": "Deploy nå",
"deploy_now_tooltip": "Start en deployment straks efter målet gemmes.",
"create_missing_service": "Opprett manglende service",
"create_missing_service_tooltip": "Tillad Release Manager at oprette en manglende Coolify-service.",
"test_access_tooltip": "Bekræft, at den konfigurerede GitHub-token kan læse dette repository og branch."
}
}
}
}
@@ -0,0 +1,61 @@
{
"compat": {
"configuration": {
"release_manager": {
"integrations": {
"title": "GitHub og Coolify",
"subtitle": "Knyt GitHub-repositories og branches til kanalspecifikke Coolify-services.",
"choose_repository": "Velg repository",
"add_health_url": "legg til health-URL",
"github_token_not_configured": "GitHub-token er ikke konfigurert; private repositories kan ikke vises.",
"open_github_token_settings": "Åpne GitHub token-innstillinger",
"channel": "Kanal",
"app": "App",
"repository": "Repository",
"repository_message": "Eksempel: truckwash/backend-php",
"repository_placeholder": "ejer/repo",
"branch": "Branch",
"branch_message": "Eksempel: release/canary",
"branch_placeholder": "branch",
"latest_prefix": "Seneste: {commit}",
"coolify_instance": "Coolify-instans",
"coolify_instance_message": "Lad feltet være tomt for manuelle deployment-poster.",
"no_coolify_instance": "Ingen Coolify-instans",
"coolify_instance_placeholder": "Coolify-instans-id",
"coolify_service_uuid": "Coolify service-UUID",
"coolify_service_uuid_message": "Vælg en eksisterende service eller indsæt en UUID.",
"coolify_service_uuid_placeholder": "Coolify service-UUID",
"health_url": "Health-URL",
"health_url_message": "Eksempel: https://api-canary.example.test/ping",
"health_url_placeholder": "Health-URL",
"ssl_domain": "Load balancer-domene",
"ssl_domain_message": "Må være et DNS-domene som routes til Coolify load balanceren. Eksempel: api-v2.truckwash.io",
"ssl_domain_placeholder": "Load balancer-domene",
"https_domain_for_coolify": "Domene kontrollert av load balanceren",
"endpoint_mode": "Endpoint mode",
"endpoint_mode_message": "Use automatic gateway routing or manually override the public host and port.",
"endpoint_mode_auto": "Auto",
"endpoint_mode_manual": "Manual",
"manual_endpoint_host": "Manual host",
"manual_endpoint_host_message": "Public host shown in release previews when manual mode is selected.",
"manual_endpoint_host_placeholder": "api-v2.truckwash.io",
"manual_endpoint_port": "Manual public port",
"manual_endpoint_port_message": "Optional public port from 1 to 65535.",
"app_port": "App port",
"app_port_message": "Internal container port exposed to Coolify routing.",
"auto_gateway_endpoint": "Automatic gateway endpoint",
"pending_automatic_endpoint": "Automatic endpoint resolution pending",
"auto_deploy": "Auto deploy",
"auto_deploy_tooltip": "Opret automatisk deployments, når dette mål ændres.",
"coolify_ssl": "Coolify SSL",
"coolify_ssl_tooltip": "Bed Coolify om at oprette HTTPS for det konfigurerede domæne.",
"deploy_now": "Deploy nå",
"deploy_now_tooltip": "Start en deployment straks efter målet gemmes.",
"create_missing_service": "Opprett manglende service",
"create_missing_service_tooltip": "Tillad Release Manager at oprette en manglende Coolify-service.",
"test_access_tooltip": "Bekræft, at den konfigurerede GitHub-token kan læse dette repository og branch."
}
}
}
}
}
@@ -0,0 +1,454 @@
{
"compat": {
"configuration": {
"release_manager": {
"title": "Release Manager",
"subtitle": "Gradvise API- og Front-End-utgivelser",
"channel_names": {
"stable": "Stabil",
"canary": "Canary",
"internal": "Intern"
},
"channel_descriptions": {
"stable": "Standard produksjonskanal.",
"canary": "Tidlig produksjonsvalideringskanal.",
"internal": "Intern kanal for ansatte og superbrukervalidering."
},
"control_api": {
"title": "Kontroll-API",
"tooltip": "Release Manager-handlinger sendes til denne API-en. Bruk produksjons-API-en med mindre du tester en staging-backend.",
"example": "Eksempel: https://api.truckwash.io:4433",
"endpoint_label": "API-endepunkt",
"endpoint_message": "Endre dette bare når Release Manager-endepunkter er tilgjengelige på mål-API-en.",
"endpoint_aria": "Release Manager kontroll-API URL",
"use_tooltip": "Last release-data fra denne API-en",
"use": "Bruk",
"reset_tooltip": "Gå tilbake til standard kontroll-API",
"reset": "Tilbakestill",
"known_endpoints": "Kjente kontroll-API-endepunkter"
},
"tabs": {
"overview": {
"label": "Oversikt",
"description": "Kanalhelse, oppsettsfremdrift og siste release-status."
},
"channels": {
"label": "Kanaler",
"description": "Opprett stabile, canary- og målrettede kanaler med rollout-grenser."
},
"assignments": {
"label": "Tildelinger",
"description": "Knytt brukere, underbrukere eller kunder til en bestemt release-kanal."
},
"deployments": {
"label": "Utrullinger",
"description": "Start utrullinger og promoter vellykkede bygg."
},
"replay": {
"label": "Replay",
"description": "Aktiver målrettet innsamling og søk i release-tidslinjehendelser."
},
"integrations": {
"label": "Integrasjoner",
"description": "Koble GitHub-repositories, branches og Coolify-tjenester."
},
"settings": {
"label": "Innstillinger",
"description": "Konfigurer Release Managers GitHub-tilgang og webhook-innstillinger."
}
},
"loading": {
"summary": "Laster Release Manager-data...",
"refreshing_summary": "Oppdaterer Release Manager-data...",
"summary_detail": "Release-status, kanaler, deployments og servicehelse lastes.",
"channels": "Laster release-kanaler...",
"channels_detail": "Kanalvisninger og release-avhengigheter lastes.",
"assignments": "Laster release-tildelinger...",
"deployments": "Laster release-deployments...",
"operations": "Laster release-operasjoner...",
"data_services": "Laster release-datatjenester...",
"integrations": "Laster release-integrasjoner...",
"settings": "Laster Release Manager-innstillinger...",
"github_repositories": "Laster GitHub-repositories...",
"timeline_sessions": "Laster release-tidslinje-sesjoner...",
"timeline_events": "Laster release-tidslinjehendelser...",
"refreshing_timeline": "Oppdaterer release-tidslinje..."
},
"setup": {
"channels": "Kanaler",
"targets": "GitHub- og Coolify-mål",
"assignments": "Pilottildelinger",
"deployments": "Første utrulling",
"replay": "Replay-innsamling"
},
"stats": {
"channels": "Kanaler",
"targets": "Mål",
"deployments": "Utrullinger",
"timeline_events": "Tidslinjehendelser"
},
"overview": {
"title": "Oversikt",
"subtitle": "Aktuell kanalhelse, release-aktivitet og modulstatus.",
"guided_setup": "Veiledet oppsett",
"next_step": "Neste: {step}",
"ready": "Release Manager er klar for daglig drift.",
"add_suggested_channel": "Legg til foreslått kanal",
"module_health_empty": "Modul-health snapshots vises når probes er registrert."
},
"settings": {
"title": "Release Manager-innstillinger",
"subtitle": "Konfigurer GitHub-tokenet som brukes for private repositories og branch-oppslag.",
"github_token": "GitHub-token",
"github_api_url": "GitHub API-URL",
"webhook_secret": "Webhook-hemmelighet",
"github_webhook_secret": "GitHub webhook-hemmelighet",
"configured": "Konfigurert",
"not_configured": "Ikke konfigurert",
"loaded_from": "Lastet fra {variable}",
"set_below": "Angi {variable} nedenfor",
"private_repositories_prefix": "Private repositories leses med serverens miljøvariabel",
"private_repositories_or": "eller modulens konfigurasjonsvariabel",
"github_token_message": "Eksempel: github_pat_... med tilgang til de private repositories Release Manager ruller ut.",
"github_token_placeholder": "La stå tomt for å beholde eksisterende token",
"github_api_url_message": "Konfigurert i ReleaseManager.github_api_url. Bruk https://api.github.com med mindre GitHub Enterprise brukes.",
"webhook_secret_message": "Valgfritt; la stå tomt for å beholde eksisterende hemmelighet.",
"webhook_secret_placeholder": "Webhook HMAC-hemmelighet",
"save": "Lagre innstillinger",
"back_to_integrations": "Tilbake til integrasjoner",
"guide": {
"steps": {
"github_token": "GitHub-token",
"github_api_url": "GitHub API-URL",
"github_webhook_secret": "GitHub webhook secret",
"review": "Gennemgang"
},
"github_token_title": "Legg til GitHub-token",
"github_token_description": "Release Manager bruger denne token til at vise private repositories, branches og commits, før et deployment-mål gemmes eller testes.",
"configure_this_in": "Konfigurer dette i",
"server_environment_variable": "Serverens miljøvariabel",
"takes_precedence": "har forrang når den er satt.",
"recommended_access": "Anbefalt tilgang",
"recommended_access_description": "Fine-grained GitHub-token med Contents: read-adgang til hvert release-repository.",
"github_api_url_title": "Legg til GitHub API-URL",
"github_api_url_description": "Brug GitHub Cloud som standard, eller peg på REST API-base-URLen for en GitHub Enterprise Server-installation.",
"github_api_url_invalid": "Skriv inn en full HTTP- eller HTTPS-URL.",
"github_cloud": "GitHub Cloud",
"enterprise_example": "Enterprise-eksempel",
"github_webhook_secret_title": "Legg til GitHub webhook secret",
"github_webhook_secret_description": "Brug samme secret i GitHub repository-webhooks, så Release Manager kan verificere deployment-events, før de håndteres.",
"github_webhook_url": "GitHub webhook-URL:",
"store_shared_secret_in": "Lagre den delte secreten i",
"review_title": "Gå gjennom GitHub-oppsett",
"review_description": "Gem værdierne, og brug derefter Integrationer til at vælge et privat repository, branch og specifik commit eller seneste commit før adgang testes.",
"ready": "Klar",
"needs_value": "Mangler værdi"
}
},
"channel_unavailable": {
"kicker": "Release Manager",
"title": "Release-kanalen er ikke klar",
"summary_prefix": "Kontoen din er tildelt",
"summary_suffix": ", men kanalen mangler påkrevd release-konfigurasjon.",
"release_bundle": "Release-bundle",
"frontend_version": "Frontend-versjon",
"api_version": "API-versjon",
"frontend_base_url": "Frontend-URL",
"api_base_url": "API-URL",
"frontend_entry": "Frontend-entry",
"release_runtime": "Release-runtime",
"checking_again": "Sjekker igjen om {seconds}s",
"refresh_error": "Release-status kunne ikke oppdateres. Neste automatiske sjekk prøver igjen.",
"ignore": "Ignorer de neste 5 minuttene",
"check_again": "Sjekk igjen",
"logout": "Logg ut",
"base_image": "Basis-image"
},
"channel_switched": {
"kicker": "Release Manager",
"title": "Du er nå på {channel}",
"summary_prefix": "Kontoen din er tildelt release-kanalen",
"summary_suffix": "Denne enheten husker at du har sett denne meldingen.",
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"frontend_version": "Frontend-versjon",
"api_version": "API-versjon",
"assigned_channel": "Tildelt kanal",
"current_app_image": "Nåværende app-image",
"current_api": "Nåværende API",
"base_image": "Basis-image",
"continue": "Fortsett"
},
"channel_selector": {
"title": "Release-kanal",
"subtitle": "Velg hvilken tildelt release-kanal denne enheten skal bruke.",
"sidebar_title": "Release",
"default": "Standard",
"ready": "Klar",
"unavailable": "Ikke klar",
"release_bundle": "Release-bundle",
"frontend_version": "Frontend-versjon",
"api_version": "API-versjon",
"frontend_base_url": "Frontend-URL",
"api_base_url": "API-URL",
"frontend_entry": "Frontend-entry",
"release_runtime": "Release-runtime",
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"use_local_frontend": "Bruk lokal frontend",
"switch_error": "Release channel could not be switched. The previous channel is still active."
},
"common": {
"yes": "ja",
"no": "nei",
"off": "av"
},
"actions": {
"edit": "Rediger",
"rollback": "Rull tilbake",
"update": "Opdater",
"create": "Opprett",
"clear": "Tøm",
"assign": "Tildel",
"remove": "Fjern",
"test_access": "Test tilgang",
"deploy": "Deploy",
"redeploy": "Redeploy",
"promote": "Promoter",
"enable": "Aktiver",
"search": "Søk",
"save_target": "Lagre mål",
"delete": "Slett",
"back": "Tilbake",
"next": "Neste",
"publish_release": "Publish release"
},
"tables": {
"channel": "Kanal",
"rollout": "Udrulning",
"frontend": "Frontend",
"api": "API",
"replay": "Replay",
"actions": "Handlinger",
"module": "Modul",
"status": "Status",
"checked": "Kontrollert",
"subject": "Emne",
"reason": "Årsag",
"expires": "Utløper",
"deployment": "Deployment",
"source": "Kilde",
"started": "Startet",
"target": "Mål",
"coolify": "Coolify",
"auto": "Auto"
},
"values": {
"app": {
"frontend": "frontend",
"api": "api"
},
"capture_level": {
"metadata": "metadata",
"full_redacted": "fullt redigert"
},
"subject_type": {
"user": "bruker",
"subuser": "medarbeider",
"customer": "kunde",
"channel": "kanal"
},
"severity": {
"error": "feil",
"warning": "advarsel",
"info": "info"
},
"status": {
"ok": "ok",
"active": "aktiv",
"deployed": "deployet",
"stable": "stabil",
"enabled": "aktivert",
"queued": "i kø",
"deploying": "deploying",
"metadata": "metadata",
"not_configured": "ikke konfigurert",
"warning": "advarsel",
"failed": "feilet",
"down": "nede",
"error": "feil",
"unknown": "ukjent"
}
},
"autocomplete": {
"suggested_value": "Foreslået værdi",
"suggestion": "Forslag",
"use_typed_value": "Brug indtastet værdi",
"custom": "Tilpasset",
"api_url": "API-URL",
"frontend_url": "Frontend-URL",
"known_release_url": "Kendt release-URL",
"deploy_here_with_coolify": "Deploy {app} her med Coolify",
"prepare_coolify_target": "Forbered et Coolify-mål med HTTPS/SSL aktiveret",
"coolify_ssl": "Coolify SSL",
"github_repository": "GitHub-repository",
"owner_repository": "ejer/repository",
"git_branch": "Git-branch",
"deployment_branch": "Deployment-branch",
"github_commit": "GitHub-commit",
"git_commit": "Git-commit",
"use_specific_commit_sha": "Brug specifik commit-SHA",
"custom_commit": "Tilpasset commit",
"health_url": "Health-URL",
"release_health_check": "Release health check",
"coolify_service": "Coolify-service",
"service_uuid": "Service-UUID",
"known_coolify_service_uuid": "Kendt Coolify service-UUID",
"control_api": "Kontrol-API",
"release_manager_api_endpoint": "Release Manager API-endpoint",
"module": "Modul",
"timeline_module_key": "Timeline-modulnøgle"
},
"errors": {
"github_repositories": "Kunne ikke indlæse GitHub-repositories.",
"github_branches": "Kunne ikke indlæse GitHub-branches.",
"repository_branch_required": "Repository og branch er påkrævet før GitHub-adgang kan testes.",
"github_access_failed": "Test af GitHub repository-adgang fejlede."
},
"channels": {
"title": "Kanaler",
"subtitle": "Opprett og juster gradvise release-kanaler.",
"rollout_percent": "{percent}% udrulning",
"already_configured": "Allerede konfigurert",
"slug": "Slug",
"slug_message": "Eksempel: canary",
"slug_placeholder": "slug",
"name": "Navn",
"name_message": "Vises til operatører når de gjennomgår en kanal.",
"name_placeholder": "Navn",
"rollout": "Udrulning",
"rollout_message": "0-100 prosent av ikke-tildelt trafikk.",
"frontend_url": "Frontend-URL",
"frontend_url_message": "Eksempel: https://canary.example.test",
"frontend_url_placeholder": "Frontend-URL",
"no_matching_release_url": "Ingen matchende release-URL.",
"api_url": "API-URL",
"api_url_message": "Eksempel: https://api-canary.example.test",
"api_url_placeholder": "API-URL",
"no_matching_api_url": "Ingen matchende API-URL.",
"replay_capture": "Replay capture",
"replay_capture_message": "Metadata er sikrere til brede kanaler.",
"retention_days": "Oppbevaringsdager",
"retention_days_message": "1-365 dage.",
"enabled": "Aktivert",
"default_channel": "Standardkanal",
"replay_enabled": "Replay aktivert",
"frontend_chip": "Frontend: {url}",
"api_chip": "API: {url}"
},
"assignments": {
"title": "Tildelinger",
"subtitle": "Fest brukere, medarbeidere eller kunder til release-kanaler.",
"subject": "Emne",
"subject_message": "Søk etter brukere, medarbeidere eller kunder, eller skriv et manuelt emne som user:42.",
"subject_placeholder": "Søk eller skriv emne",
"subject_empty": "Ingen emner funnet",
"group_users": "Brukere",
"group_subusers": "Medarbeidere",
"group_customers": "Kunder",
"group_manual": "Manuell",
"manual_user": "Bruker #{id}",
"manual_subuser": "Medarbeider #{id}",
"manual_customer": "Kunde #{id}",
"manual_description": "Bruk inntastet verdi {subject}",
"subject_type": "Emnetype",
"subject_type_message": "Vælg identitetstypen, der skal fastgøres.",
"customer": "Kunde",
"customer_message": "Søk på kundenummer eller navn, og velg deretter nøyaktig kunde.",
"customer_placeholder": "Søk kunde",
"subject_id": "Emne-ID",
"subject_id_message": "Eksempel: bruker-id 42",
"subject_id_placeholder": "Emne-id",
"channel": "Kanal",
"channel_message": "Festede emner hopper over prosentutrullingen.",
"reason": "Årsag",
"reason_message": "Eksempel: canary-validering for kundesupport.",
"reason_placeholder": "Årsag",
"expires": "Utløper",
"expires_message": "Lad feltet være tomt for ingen automatisk udløb."
},
"deployments": {
"title": "Deployments",
"subtitle": "Start, promoter og inspiser release-deployments.",
"manual_deployment_record": "Manuel deployment-post",
"target": "Mål",
"target_message": "Vælg et gemt mål, eller behold manuelle felter redigerbare.",
"select_target_or_manual": "Velg mål eller manuelt",
"channel": "Kanal",
"app": "App",
"repository": "Repository",
"repository_message": "Eksempel: truckwash/front-end-vue",
"repository_placeholder": "ejer/repo",
"branch": "Branch",
"branch_message": "Eksempel: master",
"branch_placeholder": "branch",
"commit_selection": "Commit-valg",
"commit_selection_message": "Seneste slås op som branchens head med den konfigurerede GitHub-token.",
"latest_from_branch": "Siste fra branch",
"specific_commit": "Spesifikk commit",
"specific_commit_message": "Eksempel: feedface1234",
"commit_sha_placeholder": "commit sha",
"version_label": "Versjonsetikett",
"version_label_message": "Eksempel: api-2026.05.19",
"version_label_placeholder": "versionslabel",
"commit_prefix": "Commit: {commit}",
"test_access_tooltip": "Bekræft token-adgang til det valgte repository, branch og commit."
},
"replay": {
"title": "Replay",
"subtitle": "Aktiver målrettet capture og gennemgå fejltimelines trin for trin.",
"capture_channel": "Capture {channel}",
"target_type": "Måltype",
"target": "Mål",
"target_message": "Eksempel: bruker-id 42 eller kanal-slug canary.",
"target_placeholder": "Mål-id eller kanal-slug",
"channel_scope": "Kanalavgrensning",
"channel_scope_message": "Valgfrit; begrænser replay capture til en kanal.",
"no_channel_scope": "Ingen kanalavgrensning",
"capture_level": "Capture-niveau",
"expires": "Utløper",
"expires_message": "Lad feltet være tomt for manuel deaktivering.",
"trace_id": "Trace-ID",
"trace_id_message": "Eksempel: trace-canary-1",
"trace_id_placeholder": "Trace-id",
"channel": "Kanal",
"channel_placeholder": "Kanal",
"principal_id": "Principal-ID",
"principal_id_placeholder": "Principal-id",
"module": "Modul",
"module_placeholder": "Modul",
"severity": "Alvorlighet",
"any_severity": "Alle alvorligheder",
"no_timeline_events": "Ingen timeline-events matcher de aktuelle filtre."
},
"status": {
"confirm_issue_action": "Run this Release Manager action? It can change deployment state and will be audited.",
"choose_bundle_prompt": "Enter the bundle id to set for this channel.",
"impact": "Impact",
"cause": "Cause",
"automated_fix": "Automated fix",
"manual_fallback": "Manual fallback",
"related_deployment": "Related deployment",
"recent_result": "Recent result",
"no_automated_action": "No automated action available.",
"deployment": "Deployment",
"target": "Target",
"coolify_target": "Coolify target",
"service_set": "Service set"
}
}
}
}
}
@@ -18,7 +18,6 @@
"loading_vehicle_types": "@.capitalize:{'terms.glossary.laster'} @:{'terms.glossary.inn'} @:{'terms.glossary.tilgjengelige'} @:{'terms.glossary.kjøretøytyper'}...",
"lane_unavailable": "Vaskebanen er ikke tilgjengelig",
"machine": "@.capitalize:{'terms.glossary.maskin'}",
"machine_unavailable_for_lane": "Maskinvask er ikke tilgjengelig for valgt bane. Velg manuell vask eller en annen ledig bane.",
"no_departments_found": "@:{'phrases.compat.departments.no_departments'}",
"occupied": "Okkupert",
"open_property_access_gate": "@:{'terms.glossary.aapne'} @:{'terms.glossary.eiendommens'} inngangsport",
@@ -0,0 +1,28 @@
{
"compat": {
"admin": {
"pos": {
"settings_wheel": {
"accept_self_serve_wash": "Godkänn självbetjäningstvätt",
"accept_self_serve_wash_confirm": "Flytta detta utkast till kund #{customerNumber} med självbetjäningsdetaljerna?",
"accept_self_serve_wash_success": "Självbetjäningstvätten godkändes.",
"email_notifications_section": "E-postaviseringar",
"resend_booking_completion_confirmation": "(Re)send booking completion confirmation",
"resend_booking_completion_confirmation_error": "The booking completion confirmation could not be sent",
"resend_booking_completion_confirmation_success": "The booking completion confirmation has been sent",
"resend_booking_confirmation": "(Re)send booking confirmation",
"resend_booking_confirmation_error": "The booking confirmation could not be sent",
"resend_booking_confirmation_success": "The booking confirmation has been sent",
"resend_wash_certificate": "(Re)send wash certificate",
"resend_wash_certificate_error": "The wash certificate could not be sent",
"resend_wash_certificate_success": "The wash certificate has been sent",
"self_serve_customer": "Kund",
"self_serve_driver": "Förare",
"self_serve_elapsed": "Tid",
"self_serve_wash_attachment": "Självbetjäningstvätt",
"self_serve_wash_attachment_for_customer": "Självbetjäningstvätt för kund #{customerNumber}"
}
}
}
}
}
@@ -298,490 +298,6 @@
"subtitle": "@.capitalize:{'terms.glossary.konfiguration'} @:{'terms.glossary.af'} @:{'terms.glossary.xlvask'} @:{'terms.glossary.integrationen'}",
"title": "@:{'terms.glossary.xlvask'} @:{'terms.glossary.konfiguration'}",
"username_desc": "Användarnamnet @:{'terms.glossary.anvands'} @:{'terms.glossary.for'} @:{'terms.glossary.att'} @:{'terms.glossary.autentisera'} @:{'terms.glossary.api'}-@:{'terms.glossary.anslutningen'}."
},
"release_manager": {
"title": "Release Manager",
"subtitle": "Gradvisa API- och Front-End-versioner",
"channel_names": {
"stable": "Stabil",
"canary": "Canary",
"internal": "Intern"
},
"channel_descriptions": {
"stable": "Standardkanal för produktion.",
"canary": "Tidig produktionsvalideringskanal.",
"internal": "Intern kanal för personal och superanvändarvalidering."
},
"control_api": {
"title": "Kontroll-API",
"tooltip": "Release Manager-åtgärder skickas till detta API. Använd produktions-API:t om du inte testar en staging-backend.",
"example": "Exempel: https://api.truckwash.io:4433",
"endpoint_label": "API-endpoint",
"endpoint_message": "Ändra detta endast när Release Manager-endpoints finns på mål-API:t.",
"endpoint_aria": "Release Manager kontroll-API URL",
"use_tooltip": "Läs in release-data från detta API",
"use": "Använd",
"reset_tooltip": "Gå tillbaka till standard kontroll-API",
"reset": "Återställ",
"known_endpoints": "Kända kontroll-API-endpoints"
},
"tabs": {
"overview": {
"label": "Översikt",
"description": "Kanalhälsa, installationsstatus och senaste release-läge."
},
"channels": {
"label": "Kanaler",
"description": "Skapa stabila, canary- och riktade kanaler med rollout-gränser."
},
"assignments": {
"label": "Tilldelningar",
"description": "Koppla användare, underanvändare eller kunder till en specifik release-kanal."
},
"deployments": {
"label": "Utrullningar",
"description": "Starta utrullningar och promovera lyckade byggen."
},
"replay": {
"label": "Replay",
"description": "Aktivera riktad insamling och sök i release-tidslinjehändelser."
},
"integrations": {
"label": "Integrationer",
"description": "Anslut GitHub-repositories, branches och Coolify-tjänster."
},
"settings": {
"label": "Inställningar",
"description": "Konfigurera Release Managers GitHub-åtkomst och webhook-inställningar."
}
},
"loading": {
"summary": "Laddar Release Manager-data...",
"refreshing_summary": "Uppdaterar Release Manager-data...",
"summary_detail": "Release-status, kanaler, deployments och servicehalsa laddas.",
"channels": "Laddar release-kanaler...",
"channels_detail": "Kanalvyer och release-beroenden laddas.",
"assignments": "Laddar release-tilldelningar...",
"deployments": "Laddar release-deployments...",
"operations": "Laddar release-operationer...",
"data_services": "Laddar release-datatjanster...",
"integrations": "Laddar release-integrationer...",
"settings": "Laddar Release Manager-installningar...",
"github_repositories": "Laddar GitHub-repositories...",
"timeline_sessions": "Laddar release-tidslinjesessioner...",
"timeline_events": "Laddar release-tidslinjehandelser...",
"refreshing_timeline": "Uppdaterar release-tidslinje..."
},
"setup": {
"channels": "Kanaler",
"targets": "GitHub- och Coolify-mål",
"assignments": "Pilottilldelningar",
"deployments": "Första utrullningen",
"replay": "Replay-insamling"
},
"stats": {
"channels": "Kanaler",
"targets": "Mål",
"deployments": "Utrullningar",
"timeline_events": "Tidslinjehändelser"
},
"overview": {
"title": "Översikt",
"subtitle": "Aktuell kanalhälsa, release-aktivitet och modulstatus.",
"guided_setup": "Guidad installation",
"next_step": "Nästa: {step}",
"ready": "Release Manager är klar för daglig drift.",
"add_suggested_channel": "Lägg till föreslagen kanal",
"module_health_empty": "Modulhälsosnapshots visas när probes har registrerats."
},
"settings": {
"title": "Release Manager-inställningar",
"subtitle": "Konfigurera GitHub-token som används för privata repositories och branch-uppslag.",
"github_token": "GitHub-token",
"github_api_url": "GitHub API-URL",
"webhook_secret": "Webhook-hemlighet",
"github_webhook_secret": "GitHub webhook-hemlighet",
"configured": "Konfigurerad",
"not_configured": "Inte konfigurerad",
"loaded_from": "Inläst från {variable}",
"set_below": "Ange {variable} nedan",
"private_repositories_prefix": "Privata repositories läses med serverns miljövariabel",
"private_repositories_or": "eller modulens konfigurationsvariabel",
"github_token_message": "Exempel: github_pat_... med åtkomst till de privata repositories som Release Manager distribuerar.",
"github_token_placeholder": "Lämna tomt för att behålla befintlig token",
"github_api_url_message": "Konfigurerad i ReleaseManager.github_api_url. Använd https://api.github.com om du inte använder GitHub Enterprise.",
"webhook_secret_message": "Valfritt; lämna tomt för att behålla befintlig hemlighet.",
"webhook_secret_placeholder": "Webhook HMAC-hemlighet",
"save": "Spara inställningar",
"back_to_integrations": "Tillbaka till integrationer",
"guide": {
"steps": {
"github_token": "GitHub-token",
"github_api_url": "GitHub API-URL",
"github_webhook_secret": "GitHub webhook secret",
"review": "Gennemgang"
},
"github_token_title": "Lägg till GitHub-token",
"github_token_description": "Release Manager använder denna token för att lista privata repositories, branches och commits innan ett deployment-mål sparas eller testas.",
"configure_this_in": "Konfigurera detta i",
"server_environment_variable": "Serverns miljövariabel",
"takes_precedence": "har företräde när den är satt.",
"recommended_access": "Rekommenderad åtkomst",
"recommended_access_description": "Fine-grained GitHub-token med Contents: read-åtkomst till varje release-repository.",
"github_api_url_title": "Lägg till GitHub API-URL",
"github_api_url_description": "Brug GitHub Cloud som standard, eller peg på REST API-base-URLen for en GitHub Enterprise Server-installation.",
"github_api_url_invalid": "Ange en fullständig HTTP- eller HTTPS-URL.",
"github_cloud": "GitHub Cloud",
"enterprise_example": "Enterprise-exempel",
"github_webhook_secret_title": "Lägg till GitHub webhook secret",
"github_webhook_secret_description": "Brug samme secret i GitHub repository-webhooks, så Release Manager kan verificere deployment-events, før de håndteres.",
"github_webhook_url": "GitHub webhook-URL:",
"store_shared_secret_in": "Spara den delade secret i",
"review_title": "Granska GitHub-konfiguration",
"review_description": "Gem værdierne, og brug derefter Integrationer til at vælge et privat repository, branch og specifik commit eller seneste commit før adgang testes.",
"ready": "Klar",
"needs_value": "Saknar värde"
}
},
"channel_unavailable": {
"kicker": "Release Manager",
"title": "Release-kanalen är inte klar",
"summary_prefix": "Ditt konto är tilldelat",
"summary_suffix": ", men kanalen saknar obligatorisk release-konfiguration.",
"release_bundle": "Release-bundle",
"frontend_version": "Frontend-version",
"api_version": "API-version",
"frontend_base_url": "Frontend-URL",
"api_base_url": "API-URL",
"frontend_entry": "Frontend-entry",
"release_runtime": "Release-runtime",
"checking_again": "Kontrollerar igen om {seconds}s",
"refresh_error": "Release-status kunde inte uppdateras. Nästa automatiska kontroll försöker igen.",
"ignore": "Ignorera de kommande 5 minuterna",
"check_again": "Kontrollera igen",
"logout": "Logga ut",
"base_image": "Bas-image"
},
"channel_switched": {
"kicker": "Release Manager",
"title": "Du är nu på {channel}",
"summary_prefix": "Ditt konto har tilldelats release-kanalen",
"summary_suffix": "Den här enheten kommer ihåg att du har sett detta meddelande.",
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"frontend_version": "Frontend-version",
"api_version": "API-version",
"assigned_channel": "Tilldelad kanal",
"current_app_image": "Nuvarande app-image",
"current_api": "Nuvarande API",
"base_image": "Bas-image",
"continue": "Fortsätt"
},
"channel_selector": {
"title": "Release-kanal",
"subtitle": "Välj vilken tilldelad release-kanal den här enheten ska använda.",
"sidebar_title": "Release",
"default": "Standard",
"ready": "Klar",
"unavailable": "Inte klar",
"release_bundle": "Release-bundle",
"frontend_version": "Frontend-version",
"api_version": "API-version",
"frontend_base_url": "Frontend-URL",
"api_base_url": "API-URL",
"frontend_entry": "Frontend-entry",
"release_runtime": "Release-runtime",
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"use_local_frontend": "Använd lokal frontend"
},
"common": {
"yes": "ja",
"no": "nej",
"off": "av"
},
"actions": {
"edit": "Redigera",
"rollback": "Rulla tillbaka",
"update": "Uppdatera",
"create": "Skapa",
"clear": "Rensa",
"assign": "Tilldela",
"remove": "Ta bort",
"test_access": "Testa åtkomst",
"deploy": "Deploy",
"redeploy": "Redeploy",
"promote": "Promota",
"enable": "Aktivera",
"search": "Sök",
"save_target": "Spara mål",
"delete": "Radera",
"back": "Tillbaka",
"next": "Nästa"
},
"tables": {
"channel": "Kanal",
"rollout": "Udrulning",
"frontend": "Frontend",
"api": "API",
"replay": "Replay",
"actions": "Åtgärder",
"module": "Modul",
"status": "Status",
"checked": "Kontrollerad",
"subject": "Emne",
"reason": "Orsak",
"expires": "Upphör",
"deployment": "Deployment",
"source": "Källa",
"started": "Startet",
"target": "Mål",
"coolify": "Coolify",
"auto": "Auto"
},
"values": {
"app": {
"frontend": "frontend",
"api": "api"
},
"capture_level": {
"metadata": "metadata",
"full_redacted": "helt redigerad"
},
"subject_type": {
"user": "användare",
"subuser": "medarbetare",
"customer": "kund",
"channel": "kanal"
},
"severity": {
"error": "fel",
"warning": "varning",
"info": "info"
},
"status": {
"ok": "ok",
"active": "aktiv",
"deployed": "deployad",
"stable": "stabil",
"enabled": "aktiverad",
"queued": "i kö",
"deploying": "deployar",
"metadata": "metadata",
"not_configured": "inte konfigurerad",
"warning": "varning",
"failed": "misslyckad",
"down": "nere",
"error": "fel",
"unknown": "okänd"
}
},
"autocomplete": {
"suggested_value": "Foreslået værdi",
"suggestion": "Forslag",
"use_typed_value": "Brug indtastet værdi",
"custom": "Tilpasset",
"api_url": "API-URL",
"frontend_url": "Frontend-URL",
"known_release_url": "Kendt release-URL",
"deploy_here_with_coolify": "Deploy {app} her med Coolify",
"prepare_coolify_target": "Forbered et Coolify-mål med HTTPS/SSL aktiveret",
"coolify_ssl": "Coolify SSL",
"github_repository": "GitHub-repository",
"owner_repository": "ejer/repository",
"git_branch": "Git-branch",
"deployment_branch": "Deployment-branch",
"github_commit": "GitHub-commit",
"git_commit": "Git-commit",
"use_specific_commit_sha": "Brug specifik commit-SHA",
"custom_commit": "Tilpasset commit",
"health_url": "Health-URL",
"release_health_check": "Release health check",
"coolify_service": "Coolify-service",
"service_uuid": "Service-UUID",
"known_coolify_service_uuid": "Kendt Coolify service-UUID",
"control_api": "Kontrol-API",
"release_manager_api_endpoint": "Release Manager API-endpoint",
"module": "Modul",
"timeline_module_key": "Timeline-modulnøgle"
},
"errors": {
"github_repositories": "Kunne ikke indlæse GitHub-repositories.",
"github_branches": "Kunne ikke indlæse GitHub-branches.",
"repository_branch_required": "Repository og branch er påkrævet før GitHub-adgang kan testes.",
"github_access_failed": "Test af GitHub repository-adgang fejlede."
},
"channels": {
"title": "Kanaler",
"subtitle": "Skapa och justera gradvisa release-kanaler.",
"rollout_percent": "{percent}% utrullning",
"already_configured": "Redan konfigurerad",
"slug": "Slug",
"slug_message": "Eksempel: canary",
"slug_placeholder": "slug",
"name": "Namn",
"name_message": "Visas för operatörer när de granskar en kanal.",
"name_placeholder": "Namn",
"rollout": "Utrullning",
"rollout_message": "0-100 procent av otilldelad trafik.",
"frontend_url": "Frontend-URL",
"frontend_url_message": "Eksempel: https://canary.example.test",
"frontend_url_placeholder": "Frontend-URL",
"no_matching_release_url": "Ingen matchande release-URL.",
"api_url": "API-URL",
"api_url_message": "Eksempel: https://api-canary.example.test",
"api_url_placeholder": "API-URL",
"no_matching_api_url": "Ingen matchande API-URL.",
"replay_capture": "Replay capture",
"replay_capture_message": "Metadata är säkrare för breda kanaler.",
"retention_days": "Lagringsdagar",
"retention_days_message": "1-365 dagar.",
"enabled": "Aktiverad",
"default_channel": "Standardkanal",
"replay_enabled": "Replay aktiverad",
"frontend_chip": "Frontend: {url}",
"api_chip": "API: {url}"
},
"assignments": {
"title": "Tilldelningar",
"subtitle": "Fäst användare, medarbetare eller kunder till release-kanaler.",
"subject": "Ämne",
"subject_message": "Sök efter användare, medarbetare eller kunder, eller skriv ett manuellt ämne som user:42.",
"subject_placeholder": "Sök eller skriv ämne",
"subject_empty": "Inga ämnen hittades",
"group_users": "Användare",
"group_subusers": "Medarbetare",
"group_customers": "Kunder",
"group_manual": "Manuell",
"manual_user": "Användare #{id}",
"manual_subuser": "Medarbetare #{id}",
"manual_customer": "Kund #{id}",
"manual_description": "Använd inmatat värde {subject}",
"subject_type": "Ämnestyp",
"subject_type_message": "Välj identitetstypen som ska fästas.",
"customer": "Kunde",
"customer_message": "Sök på kundnummer eller namn och välj sedan exakt kund.",
"customer_placeholder": "Sök kund",
"subject_id": "Ämnes-ID",
"subject_id_message": "Exempel: användar-id 42",
"subject_id_placeholder": "Ämnes-id",
"channel": "Kanal",
"channel_message": "Fästa ämnen hoppar över procentutrullningen.",
"reason": "Orsak",
"reason_message": "Exempel: canary-validering för kundsupport.",
"reason_placeholder": "Orsak",
"expires": "Upphör",
"expires_message": "Lämna tomt för ingen automatisk upphörning."
},
"deployments": {
"title": "Deployments",
"subtitle": "Starta, promota och inspektera release-deployments.",
"manual_deployment_record": "Manuel deployment-post",
"target": "Mål",
"target_message": "Välj ett sparat mål eller behåll manuella fält redigerbara.",
"select_target_or_manual": "Vælg mål eller manuel",
"channel": "Kanal",
"app": "App",
"repository": "Repository",
"repository_message": "Eksempel: truckwash/front-end-vue",
"repository_placeholder": "ejer/repo",
"branch": "Branch",
"branch_message": "Eksempel: master",
"branch_placeholder": "branch",
"commit_selection": "Commit-val",
"commit_selection_message": "Senaste slås upp som branchens head med konfigurerad GitHub-token.",
"latest_from_branch": "Senaste från branch",
"specific_commit": "Specifik commit",
"specific_commit_message": "Eksempel: feedface1234",
"commit_sha_placeholder": "commit sha",
"version_label": "Versionsetikett",
"version_label_message": "Eksempel: api-2026.05.19",
"version_label_placeholder": "versionsetikett",
"commit_prefix": "Commit: {commit}",
"test_access_tooltip": "Bekræft token-adgang til det valgte repository, branch og commit."
},
"replay": {
"title": "Replay",
"subtitle": "Aktivera riktad capture och gå igenom feltimelines steg för steg.",
"capture_channel": "Capture {channel}",
"target_type": "Måltype",
"target": "Mål",
"target_message": "Exempel: användar-id 42 eller kanal-slug canary.",
"target_placeholder": "Mål-id eller kanal-slug",
"channel_scope": "Kanalavgränsning",
"channel_scope_message": "Valgfrit; begrænser replay capture til en kanal.",
"no_channel_scope": "Ingen kanalavgränsning",
"capture_level": "Capture-niveau",
"expires": "Upphör",
"expires_message": "Lad feltet være tomt for manuel deaktivering.",
"trace_id": "Trace-ID",
"trace_id_message": "Eksempel: trace-canary-1",
"trace_id_placeholder": "Trace-id",
"channel": "Kanal",
"channel_placeholder": "Kanal",
"principal_id": "Principal-ID",
"principal_id_placeholder": "Principal-id",
"module": "Modul",
"module_placeholder": "Modul",
"severity": "Allvarlighetsgrad",
"any_severity": "Alla allvarlighetsgrader",
"no_timeline_events": "Ingen timeline-events matcher de aktuelle filtre."
},
"integrations": {
"title": "GitHub och Coolify",
"subtitle": "Koppla GitHub-repositories och branches till kanalspecifika Coolify-tjänster.",
"choose_repository": "Välj repository",
"add_health_url": "lägg till health-URL",
"github_token_not_configured": "GitHub-token er ikke konfigureret; private repositories kan ikke vises.",
"open_github_token_settings": "Öppna GitHub token-inställningar",
"channel": "Kanal",
"app": "App",
"repository": "Repository",
"repository_message": "Eksempel: truckwash/backend-php",
"repository_placeholder": "ejer/repo",
"branch": "Branch",
"branch_message": "Eksempel: release/canary",
"branch_placeholder": "branch",
"latest_prefix": "Seneste: {commit}",
"coolify_instance": "Coolify-instans",
"coolify_instance_message": "Lad feltet være tomt for manuelle deployment-poster.",
"no_coolify_instance": "Ingen Coolify-instans",
"coolify_instance_placeholder": "Coolify-instans-id",
"coolify_service_uuid": "Coolify service-UUID",
"coolify_service_uuid_message": "Vælg en eksisterende service eller indsæt en UUID.",
"coolify_service_uuid_placeholder": "Coolify service-UUID",
"health_url": "Health-URL",
"health_url_message": "Eksempel: https://api-canary.example.test/ping",
"health_url_placeholder": "Health-URL",
"ssl_domain": "Load balancer-domän",
"ssl_domain_message": "Måste vara en DNS-domän som routas till Coolify load balancern. Exempel: api-v2.truckwash.io",
"ssl_domain_placeholder": "Load balancer-domän",
"https_domain_for_coolify": "Domän som kontrolleras av load balancern",
"endpoint_mode": "Endpoint mode",
"endpoint_mode_message": "Use automatic gateway routing or manually override the public host and port.",
"endpoint_mode_auto": "Auto",
"endpoint_mode_manual": "Manual",
"manual_endpoint_host": "Manual host",
"manual_endpoint_host_message": "Public host shown in release previews when manual mode is selected.",
"manual_endpoint_host_placeholder": "api-v2.truckwash.io",
"manual_endpoint_port": "Manual public port",
"manual_endpoint_port_message": "Optional public port from 1 to 65535.",
"app_port": "App port",
"app_port_message": "Internal container port exposed to Coolify routing.",
"auto_gateway_endpoint": "Automatic gateway endpoint",
"pending_automatic_endpoint": "Automatic endpoint resolution pending",
"auto_deploy": "Auto deploy",
"auto_deploy_tooltip": "Opret automatisk deployments, når dette mål ændres.",
"coolify_ssl": "Coolify SSL",
"coolify_ssl_tooltip": "Bed Coolify om at oprette HTTPS for det konfigurerede domæne.",
"deploy_now": "Deploy nu",
"deploy_now_tooltip": "Start en deployment straks efter målet gemmes.",
"create_missing_service": "Skapa saknad tjänst",
"create_missing_service_tooltip": "Tillad Release Manager at oprette en manglende Coolify-service.",
"test_access_tooltip": "Bekræft, at den konfigurerede GitHub-token kan læse dette repository og branch."
}
}
}
}
@@ -0,0 +1,61 @@
{
"compat": {
"configuration": {
"release_manager": {
"integrations": {
"title": "GitHub och Coolify",
"subtitle": "Koppla GitHub-repositories och branches till kanalspecifika Coolify-tjänster.",
"choose_repository": "Välj repository",
"add_health_url": "lägg till health-URL",
"github_token_not_configured": "GitHub-token er ikke konfigureret; private repositories kan ikke vises.",
"open_github_token_settings": "Öppna GitHub token-inställningar",
"channel": "Kanal",
"app": "App",
"repository": "Repository",
"repository_message": "Eksempel: truckwash/backend-php",
"repository_placeholder": "ejer/repo",
"branch": "Branch",
"branch_message": "Eksempel: release/canary",
"branch_placeholder": "branch",
"latest_prefix": "Seneste: {commit}",
"coolify_instance": "Coolify-instans",
"coolify_instance_message": "Lad feltet være tomt for manuelle deployment-poster.",
"no_coolify_instance": "Ingen Coolify-instans",
"coolify_instance_placeholder": "Coolify-instans-id",
"coolify_service_uuid": "Coolify service-UUID",
"coolify_service_uuid_message": "Vælg en eksisterende service eller indsæt en UUID.",
"coolify_service_uuid_placeholder": "Coolify service-UUID",
"health_url": "Health-URL",
"health_url_message": "Eksempel: https://api-canary.example.test/ping",
"health_url_placeholder": "Health-URL",
"ssl_domain": "Load balancer-domän",
"ssl_domain_message": "Måste vara en DNS-domän som routas till Coolify load balancern. Exempel: api-v2.truckwash.io",
"ssl_domain_placeholder": "Load balancer-domän",
"https_domain_for_coolify": "Domän som kontrolleras av load balancern",
"endpoint_mode": "Endpoint mode",
"endpoint_mode_message": "Use automatic gateway routing or manually override the public host and port.",
"endpoint_mode_auto": "Auto",
"endpoint_mode_manual": "Manual",
"manual_endpoint_host": "Manual host",
"manual_endpoint_host_message": "Public host shown in release previews when manual mode is selected.",
"manual_endpoint_host_placeholder": "api-v2.truckwash.io",
"manual_endpoint_port": "Manual public port",
"manual_endpoint_port_message": "Optional public port from 1 to 65535.",
"app_port": "App port",
"app_port_message": "Internal container port exposed to Coolify routing.",
"auto_gateway_endpoint": "Automatic gateway endpoint",
"pending_automatic_endpoint": "Automatic endpoint resolution pending",
"auto_deploy": "Auto deploy",
"auto_deploy_tooltip": "Opret automatisk deployments, når dette mål ændres.",
"coolify_ssl": "Coolify SSL",
"coolify_ssl_tooltip": "Bed Coolify om at oprette HTTPS for det konfigurerede domæne.",
"deploy_now": "Deploy nu",
"deploy_now_tooltip": "Start en deployment straks efter målet gemmes.",
"create_missing_service": "Skapa saknad tjänst",
"create_missing_service_tooltip": "Tillad Release Manager at oprette en manglende Coolify-service.",
"test_access_tooltip": "Bekræft, at den konfigurerede GitHub-token kan læse dette repository og branch."
}
}
}
}
}
@@ -0,0 +1,454 @@
{
"compat": {
"configuration": {
"release_manager": {
"title": "Release Manager",
"subtitle": "Gradvisa API- och Front-End-versioner",
"channel_names": {
"stable": "Stabil",
"canary": "Canary",
"internal": "Intern"
},
"channel_descriptions": {
"stable": "Standardkanal för produktion.",
"canary": "Tidig produktionsvalideringskanal.",
"internal": "Intern kanal för personal och superanvändarvalidering."
},
"control_api": {
"title": "Kontroll-API",
"tooltip": "Release Manager-åtgärder skickas till detta API. Använd produktions-API:t om du inte testar en staging-backend.",
"example": "Exempel: https://api.truckwash.io:4433",
"endpoint_label": "API-endpoint",
"endpoint_message": "Ändra detta endast när Release Manager-endpoints finns på mål-API:t.",
"endpoint_aria": "Release Manager kontroll-API URL",
"use_tooltip": "Läs in release-data från detta API",
"use": "Använd",
"reset_tooltip": "Gå tillbaka till standard kontroll-API",
"reset": "Återställ",
"known_endpoints": "Kända kontroll-API-endpoints"
},
"tabs": {
"overview": {
"label": "Översikt",
"description": "Kanalhälsa, installationsstatus och senaste release-läge."
},
"channels": {
"label": "Kanaler",
"description": "Skapa stabila, canary- och riktade kanaler med rollout-gränser."
},
"assignments": {
"label": "Tilldelningar",
"description": "Koppla användare, underanvändare eller kunder till en specifik release-kanal."
},
"deployments": {
"label": "Utrullningar",
"description": "Starta utrullningar och promovera lyckade byggen."
},
"replay": {
"label": "Replay",
"description": "Aktivera riktad insamling och sök i release-tidslinjehändelser."
},
"integrations": {
"label": "Integrationer",
"description": "Anslut GitHub-repositories, branches och Coolify-tjänster."
},
"settings": {
"label": "Inställningar",
"description": "Konfigurera Release Managers GitHub-åtkomst och webhook-inställningar."
}
},
"loading": {
"summary": "Laddar Release Manager-data...",
"refreshing_summary": "Uppdaterar Release Manager-data...",
"summary_detail": "Release-status, kanaler, deployments och servicehalsa laddas.",
"channels": "Laddar release-kanaler...",
"channels_detail": "Kanalvyer och release-beroenden laddas.",
"assignments": "Laddar release-tilldelningar...",
"deployments": "Laddar release-deployments...",
"operations": "Laddar release-operationer...",
"data_services": "Laddar release-datatjanster...",
"integrations": "Laddar release-integrationer...",
"settings": "Laddar Release Manager-installningar...",
"github_repositories": "Laddar GitHub-repositories...",
"timeline_sessions": "Laddar release-tidslinjesessioner...",
"timeline_events": "Laddar release-tidslinjehandelser...",
"refreshing_timeline": "Uppdaterar release-tidslinje..."
},
"setup": {
"channels": "Kanaler",
"targets": "GitHub- och Coolify-mål",
"assignments": "Pilottilldelningar",
"deployments": "Första utrullningen",
"replay": "Replay-insamling"
},
"stats": {
"channels": "Kanaler",
"targets": "Mål",
"deployments": "Utrullningar",
"timeline_events": "Tidslinjehändelser"
},
"overview": {
"title": "Översikt",
"subtitle": "Aktuell kanalhälsa, release-aktivitet och modulstatus.",
"guided_setup": "Guidad installation",
"next_step": "Nästa: {step}",
"ready": "Release Manager är klar för daglig drift.",
"add_suggested_channel": "Lägg till föreslagen kanal",
"module_health_empty": "Modulhälsosnapshots visas när probes har registrerats."
},
"settings": {
"title": "Release Manager-inställningar",
"subtitle": "Konfigurera GitHub-token som används för privata repositories och branch-uppslag.",
"github_token": "GitHub-token",
"github_api_url": "GitHub API-URL",
"webhook_secret": "Webhook-hemlighet",
"github_webhook_secret": "GitHub webhook-hemlighet",
"configured": "Konfigurerad",
"not_configured": "Inte konfigurerad",
"loaded_from": "Inläst från {variable}",
"set_below": "Ange {variable} nedan",
"private_repositories_prefix": "Privata repositories läses med serverns miljövariabel",
"private_repositories_or": "eller modulens konfigurationsvariabel",
"github_token_message": "Exempel: github_pat_... med åtkomst till de privata repositories som Release Manager distribuerar.",
"github_token_placeholder": "Lämna tomt för att behålla befintlig token",
"github_api_url_message": "Konfigurerad i ReleaseManager.github_api_url. Använd https://api.github.com om du inte använder GitHub Enterprise.",
"webhook_secret_message": "Valfritt; lämna tomt för att behålla befintlig hemlighet.",
"webhook_secret_placeholder": "Webhook HMAC-hemlighet",
"save": "Spara inställningar",
"back_to_integrations": "Tillbaka till integrationer",
"guide": {
"steps": {
"github_token": "GitHub-token",
"github_api_url": "GitHub API-URL",
"github_webhook_secret": "GitHub webhook secret",
"review": "Gennemgang"
},
"github_token_title": "Lägg till GitHub-token",
"github_token_description": "Release Manager använder denna token för att lista privata repositories, branches och commits innan ett deployment-mål sparas eller testas.",
"configure_this_in": "Konfigurera detta i",
"server_environment_variable": "Serverns miljövariabel",
"takes_precedence": "har företräde när den är satt.",
"recommended_access": "Rekommenderad åtkomst",
"recommended_access_description": "Fine-grained GitHub-token med Contents: read-åtkomst till varje release-repository.",
"github_api_url_title": "Lägg till GitHub API-URL",
"github_api_url_description": "Brug GitHub Cloud som standard, eller peg på REST API-base-URLen for en GitHub Enterprise Server-installation.",
"github_api_url_invalid": "Ange en fullständig HTTP- eller HTTPS-URL.",
"github_cloud": "GitHub Cloud",
"enterprise_example": "Enterprise-exempel",
"github_webhook_secret_title": "Lägg till GitHub webhook secret",
"github_webhook_secret_description": "Brug samme secret i GitHub repository-webhooks, så Release Manager kan verificere deployment-events, før de håndteres.",
"github_webhook_url": "GitHub webhook-URL:",
"store_shared_secret_in": "Spara den delade secret i",
"review_title": "Granska GitHub-konfiguration",
"review_description": "Gem værdierne, og brug derefter Integrationer til at vælge et privat repository, branch og specifik commit eller seneste commit før adgang testes.",
"ready": "Klar",
"needs_value": "Saknar värde"
}
},
"channel_unavailable": {
"kicker": "Release Manager",
"title": "Release-kanalen är inte klar",
"summary_prefix": "Ditt konto är tilldelat",
"summary_suffix": ", men kanalen saknar obligatorisk release-konfiguration.",
"release_bundle": "Release-bundle",
"frontend_version": "Frontend-version",
"api_version": "API-version",
"frontend_base_url": "Frontend-URL",
"api_base_url": "API-URL",
"frontend_entry": "Frontend-entry",
"release_runtime": "Release-runtime",
"checking_again": "Kontrollerar igen om {seconds}s",
"refresh_error": "Release-status kunde inte uppdateras. Nästa automatiska kontroll försöker igen.",
"ignore": "Ignorera de kommande 5 minuterna",
"check_again": "Kontrollera igen",
"logout": "Logga ut",
"base_image": "Bas-image"
},
"channel_switched": {
"kicker": "Release Manager",
"title": "Du är nu på {channel}",
"summary_prefix": "Ditt konto har tilldelats release-kanalen",
"summary_suffix": "Den här enheten kommer ihåg att du har sett detta meddelande.",
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"frontend_version": "Frontend-version",
"api_version": "API-version",
"assigned_channel": "Tilldelad kanal",
"current_app_image": "Nuvarande app-image",
"current_api": "Nuvarande API",
"base_image": "Bas-image",
"continue": "Fortsätt"
},
"channel_selector": {
"title": "Release-kanal",
"subtitle": "Välj vilken tilldelad release-kanal den här enheten ska använda.",
"sidebar_title": "Release",
"default": "Standard",
"ready": "Klar",
"unavailable": "Inte klar",
"release_bundle": "Release-bundle",
"frontend_version": "Frontend-version",
"api_version": "API-version",
"frontend_base_url": "Frontend-URL",
"api_base_url": "API-URL",
"frontend_entry": "Frontend-entry",
"release_runtime": "Release-runtime",
"bundle": "Bundle",
"frontend": "Frontend",
"api": "API",
"use_local_frontend": "Använd lokal frontend",
"switch_error": "Release channel could not be switched. The previous channel is still active."
},
"common": {
"yes": "ja",
"no": "nej",
"off": "av"
},
"actions": {
"edit": "Redigera",
"rollback": "Rulla tillbaka",
"update": "Uppdatera",
"create": "Skapa",
"clear": "Rensa",
"assign": "Tilldela",
"remove": "Ta bort",
"test_access": "Testa åtkomst",
"deploy": "Deploy",
"redeploy": "Redeploy",
"promote": "Promota",
"enable": "Aktivera",
"search": "Sök",
"save_target": "Spara mål",
"delete": "Radera",
"back": "Tillbaka",
"next": "Nästa",
"publish_release": "Publish release"
},
"tables": {
"channel": "Kanal",
"rollout": "Udrulning",
"frontend": "Frontend",
"api": "API",
"replay": "Replay",
"actions": "Åtgärder",
"module": "Modul",
"status": "Status",
"checked": "Kontrollerad",
"subject": "Emne",
"reason": "Orsak",
"expires": "Upphör",
"deployment": "Deployment",
"source": "Källa",
"started": "Startet",
"target": "Mål",
"coolify": "Coolify",
"auto": "Auto"
},
"values": {
"app": {
"frontend": "frontend",
"api": "api"
},
"capture_level": {
"metadata": "metadata",
"full_redacted": "helt redigerad"
},
"subject_type": {
"user": "användare",
"subuser": "medarbetare",
"customer": "kund",
"channel": "kanal"
},
"severity": {
"error": "fel",
"warning": "varning",
"info": "info"
},
"status": {
"ok": "ok",
"active": "aktiv",
"deployed": "deployad",
"stable": "stabil",
"enabled": "aktiverad",
"queued": "i kö",
"deploying": "deployar",
"metadata": "metadata",
"not_configured": "inte konfigurerad",
"warning": "varning",
"failed": "misslyckad",
"down": "nere",
"error": "fel",
"unknown": "okänd"
}
},
"autocomplete": {
"suggested_value": "Foreslået værdi",
"suggestion": "Forslag",
"use_typed_value": "Brug indtastet værdi",
"custom": "Tilpasset",
"api_url": "API-URL",
"frontend_url": "Frontend-URL",
"known_release_url": "Kendt release-URL",
"deploy_here_with_coolify": "Deploy {app} her med Coolify",
"prepare_coolify_target": "Forbered et Coolify-mål med HTTPS/SSL aktiveret",
"coolify_ssl": "Coolify SSL",
"github_repository": "GitHub-repository",
"owner_repository": "ejer/repository",
"git_branch": "Git-branch",
"deployment_branch": "Deployment-branch",
"github_commit": "GitHub-commit",
"git_commit": "Git-commit",
"use_specific_commit_sha": "Brug specifik commit-SHA",
"custom_commit": "Tilpasset commit",
"health_url": "Health-URL",
"release_health_check": "Release health check",
"coolify_service": "Coolify-service",
"service_uuid": "Service-UUID",
"known_coolify_service_uuid": "Kendt Coolify service-UUID",
"control_api": "Kontrol-API",
"release_manager_api_endpoint": "Release Manager API-endpoint",
"module": "Modul",
"timeline_module_key": "Timeline-modulnøgle"
},
"errors": {
"github_repositories": "Kunne ikke indlæse GitHub-repositories.",
"github_branches": "Kunne ikke indlæse GitHub-branches.",
"repository_branch_required": "Repository og branch er påkrævet før GitHub-adgang kan testes.",
"github_access_failed": "Test af GitHub repository-adgang fejlede."
},
"channels": {
"title": "Kanaler",
"subtitle": "Skapa och justera gradvisa release-kanaler.",
"rollout_percent": "{percent}% utrullning",
"already_configured": "Redan konfigurerad",
"slug": "Slug",
"slug_message": "Eksempel: canary",
"slug_placeholder": "slug",
"name": "Namn",
"name_message": "Visas för operatörer när de granskar en kanal.",
"name_placeholder": "Namn",
"rollout": "Utrullning",
"rollout_message": "0-100 procent av otilldelad trafik.",
"frontend_url": "Frontend-URL",
"frontend_url_message": "Eksempel: https://canary.example.test",
"frontend_url_placeholder": "Frontend-URL",
"no_matching_release_url": "Ingen matchande release-URL.",
"api_url": "API-URL",
"api_url_message": "Eksempel: https://api-canary.example.test",
"api_url_placeholder": "API-URL",
"no_matching_api_url": "Ingen matchande API-URL.",
"replay_capture": "Replay capture",
"replay_capture_message": "Metadata är säkrare för breda kanaler.",
"retention_days": "Lagringsdagar",
"retention_days_message": "1-365 dagar.",
"enabled": "Aktiverad",
"default_channel": "Standardkanal",
"replay_enabled": "Replay aktiverad",
"frontend_chip": "Frontend: {url}",
"api_chip": "API: {url}"
},
"assignments": {
"title": "Tilldelningar",
"subtitle": "Fäst användare, medarbetare eller kunder till release-kanaler.",
"subject": "Ämne",
"subject_message": "Sök efter användare, medarbetare eller kunder, eller skriv ett manuellt ämne som user:42.",
"subject_placeholder": "Sök eller skriv ämne",
"subject_empty": "Inga ämnen hittades",
"group_users": "Användare",
"group_subusers": "Medarbetare",
"group_customers": "Kunder",
"group_manual": "Manuell",
"manual_user": "Användare #{id}",
"manual_subuser": "Medarbetare #{id}",
"manual_customer": "Kund #{id}",
"manual_description": "Använd inmatat värde {subject}",
"subject_type": "Ämnestyp",
"subject_type_message": "Välj identitetstypen som ska fästas.",
"customer": "Kunde",
"customer_message": "Sök på kundnummer eller namn och välj sedan exakt kund.",
"customer_placeholder": "Sök kund",
"subject_id": "Ämnes-ID",
"subject_id_message": "Exempel: användar-id 42",
"subject_id_placeholder": "Ämnes-id",
"channel": "Kanal",
"channel_message": "Fästa ämnen hoppar över procentutrullningen.",
"reason": "Orsak",
"reason_message": "Exempel: canary-validering för kundsupport.",
"reason_placeholder": "Orsak",
"expires": "Upphör",
"expires_message": "Lämna tomt för ingen automatisk upphörning."
},
"deployments": {
"title": "Deployments",
"subtitle": "Starta, promota och inspektera release-deployments.",
"manual_deployment_record": "Manuel deployment-post",
"target": "Mål",
"target_message": "Välj ett sparat mål eller behåll manuella fält redigerbara.",
"select_target_or_manual": "Vælg mål eller manuel",
"channel": "Kanal",
"app": "App",
"repository": "Repository",
"repository_message": "Eksempel: truckwash/front-end-vue",
"repository_placeholder": "ejer/repo",
"branch": "Branch",
"branch_message": "Eksempel: master",
"branch_placeholder": "branch",
"commit_selection": "Commit-val",
"commit_selection_message": "Senaste slås upp som branchens head med konfigurerad GitHub-token.",
"latest_from_branch": "Senaste från branch",
"specific_commit": "Specifik commit",
"specific_commit_message": "Eksempel: feedface1234",
"commit_sha_placeholder": "commit sha",
"version_label": "Versionsetikett",
"version_label_message": "Eksempel: api-2026.05.19",
"version_label_placeholder": "versionsetikett",
"commit_prefix": "Commit: {commit}",
"test_access_tooltip": "Bekræft token-adgang til det valgte repository, branch og commit."
},
"replay": {
"title": "Replay",
"subtitle": "Aktivera riktad capture och gå igenom feltimelines steg för steg.",
"capture_channel": "Capture {channel}",
"target_type": "Måltype",
"target": "Mål",
"target_message": "Exempel: användar-id 42 eller kanal-slug canary.",
"target_placeholder": "Mål-id eller kanal-slug",
"channel_scope": "Kanalavgränsning",
"channel_scope_message": "Valgfrit; begrænser replay capture til en kanal.",
"no_channel_scope": "Ingen kanalavgränsning",
"capture_level": "Capture-niveau",
"expires": "Upphör",
"expires_message": "Lad feltet være tomt for manuel deaktivering.",
"trace_id": "Trace-ID",
"trace_id_message": "Eksempel: trace-canary-1",
"trace_id_placeholder": "Trace-id",
"channel": "Kanal",
"channel_placeholder": "Kanal",
"principal_id": "Principal-ID",
"principal_id_placeholder": "Principal-id",
"module": "Modul",
"module_placeholder": "Modul",
"severity": "Allvarlighetsgrad",
"any_severity": "Alla allvarlighetsgrader",
"no_timeline_events": "Ingen timeline-events matcher de aktuelle filtre."
},
"status": {
"confirm_issue_action": "Run this Release Manager action? It can change deployment state and will be audited.",
"choose_bundle_prompt": "Enter the bundle id to set for this channel.",
"impact": "Impact",
"cause": "Cause",
"automated_fix": "Automated fix",
"manual_fallback": "Manual fallback",
"related_deployment": "Related deployment",
"recent_result": "Recent result",
"no_automated_action": "No automated action available.",
"deployment": "Deployment",
"target": "Target",
"coolify_target": "Coolify target",
"service_set": "Service set"
}
}
}
}
}
@@ -18,7 +18,6 @@
"loading_vehicle_types": "@.capitalize:{'terms.glossary.laser'} @:{'terms.glossary.in'} @:{'terms.glossary.tillgangliga'} @:{'terms.glossary.fordonstyper'}...",
"lane_unavailable": "Tvattbanan ar inte tillganglig",
"machine": "@.capitalize:{'terms.glossary.machine'}",
"machine_unavailable_for_lane": "Maskintvatt ar inte tillganglig for vald bana. Valj manuell tvatt eller en annan ledig bana.",
"no_departments_found": "@.capitalize:{'terms.glossary.inga'} @:{'terms.glossary.avdelningar'} @:{'terms.glossary.hittades'}",
"occupied": "Upptagen",
"open_property_access_gate": "@:{'terms.glossary.oeppna'} @:{'terms.glossary.fastighetens'} ingangsgrind",

Some files were not shown because too many files have changed in this diff Show More