Organization writes were superadmin-only, so standing up a tenant needed an out-of-band superadmin. Creating one is now self-service, and an admin manages the org they belong to. - POST /api/orgs is open to any authenticated user. A creator who isn't a superadmin must have no organization yet (a single-valued membership relation means a second one would abandon the first), and is promoted to the new org's admin and first member in the same request. If that promotion fails the org is rolled back, so it is never left stranded with nobody able to administer it. Superadmins still create tenants without joining them. - PATCH/DELETE are manager-gated and scope an admin to their own org. An admin deletes theirs only as its sole member: they are detached and demoted to a plain user before the record goes, so the org is empty when it is removed. Other members still block deletion with a 409. - /api/me now carries organization + organizationName, which the clients need to tell "no org yet" from "org you administer". The panel, Web App (new OrgManager.vue in Settings) and Phone App (new _OrganizationSection) all mirror the server's gates rather than re-deciding them. The Phone App cached its role at login and gates the Users tab on it, so AuthService.adoptRole refreshes that from the profile instead of making a freshly promoted admin sign in again. Covered by orgs_test.go, which drives the real handler + middleware chain against a stand-in PocketBase: promotion, the already-a-member refusal, superadmin staying unattached, the rollback, own-org scoping, the detach-and-demote, and the blocking-member 409. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
248 lines
9.2 KiB
JSON
248 lines
9.2 KiB
JSON
{
|
|
"common": {
|
|
"save": "Gem",
|
|
"saving": "Gemmer…",
|
|
"saved": "Gemt ✓",
|
|
"cancel": "Annuller",
|
|
"remove": "Fjern",
|
|
"retry": "Prøv igen",
|
|
"required": "Påkrævet",
|
|
"confirm": "Bekræft",
|
|
"empty": "—"
|
|
},
|
|
"nav": {
|
|
"garage": "Garage",
|
|
"charging": "Opladning",
|
|
"settings": "Indstillinger",
|
|
"users": "Brugere"
|
|
},
|
|
"login": {
|
|
"tagline": "Styr på din bil.",
|
|
"email": "E-mail",
|
|
"password": "Adgangskode",
|
|
"showPassword": "Vis adgangskode",
|
|
"hidePassword": "Skjul adgangskode",
|
|
"submit": "Log ind",
|
|
"submitting": "Logger ind…",
|
|
"or": "eller",
|
|
"signInWithFace": "Log ind med ansigtsgenkendelse",
|
|
"signInWithFingerprint": "Log ind med fingeraftryk",
|
|
"signInWithBiometrics": "Log ind med biometri",
|
|
"enrollTitle": "Aktivér biometrisk login?",
|
|
"enrollBody": "Næste gang kan du logge ind med {method} i stedet for at skrive din adgangskode.",
|
|
"enrollNotNow": "Ikke nu",
|
|
"enrollEnable": "Aktivér",
|
|
"methodFace": "ansigtsgenkendelse",
|
|
"methodFingerprint": "dit fingeraftryk",
|
|
"methodBiometrics": "biometri",
|
|
"bioReasonSignInAs": "Log ind som {email}",
|
|
"bioReasonSignIn": "Log ind på DriverVault",
|
|
"savedInvalid": "Det gemte login er ikke længere gyldigt. Log ind med din adgangskode.",
|
|
"serverSettings": "Serverindstillinger",
|
|
"apiServerUrl": "API-serverens adresse",
|
|
"leaveBlank": "Lad feltet stå tomt for at bruge standarden.",
|
|
"reset": "Nulstil",
|
|
"savedNote": "Gemt ✓",
|
|
"resetNote": "Nulstillet ✓"
|
|
},
|
|
"lock": {
|
|
"title": "DriverVault er låst",
|
|
"reason": "Lås DriverVault op",
|
|
"unlock": "Lås op",
|
|
"unlocking": "Låser op…",
|
|
"usePassword": "Brug adgangskode i stedet"
|
|
},
|
|
"dashboard": {
|
|
"eyebrow": "DINE BILER",
|
|
"title": "Garage",
|
|
"addCar": "Tilføj bil",
|
|
"lightMode": "Lys tilstand",
|
|
"darkMode": "Mørk tilstand",
|
|
"logOut": "Log ud",
|
|
"empty": "Ingen biler endnu.",
|
|
"shared": "Delt",
|
|
"sharedReadOnly": "Delt · skrivebeskyttet",
|
|
"serviceLife": "SERVICEINTERVAL BRUGT",
|
|
"lastService": "Seneste service",
|
|
"currentOdometer": "Nuværende kilometerstand",
|
|
"nextDue": "Næste service",
|
|
"nextDueKm": "Næste service (km)",
|
|
"serviceRecords": {
|
|
"one": "{n} servicepost",
|
|
"other": "{n} serviceposter"
|
|
}
|
|
},
|
|
"charging": {
|
|
"eyebrow": "OPLADNING OG KORT",
|
|
"title": "Ladere i nærheden",
|
|
"tabs": {
|
|
"public": "Offentlige ladere",
|
|
"home": "Hjemmeladere"
|
|
},
|
|
"liveMap": "Live-kort",
|
|
"youAreHere": "Du er her",
|
|
"session": {
|
|
"chargingNow": "Oplader nu",
|
|
"rate": "Effekt",
|
|
"added": "Tilføjet",
|
|
"cost": "Pris",
|
|
"done": "Færdig",
|
|
"stop": "Stop opladning",
|
|
"idle": "Oplader ikke",
|
|
"idleHint": "Tilslut ved en station for at starte en session."
|
|
},
|
|
"stations": {
|
|
"heading": "I nærheden",
|
|
"homeHeading": "Dine ladere",
|
|
"noControlHint": "Aktivér Own eller Proxy CSMS under Indstillinger → Integrationer for at styre din hjemmelader her.",
|
|
"count": {
|
|
"one": "{n} station",
|
|
"other": "{n} stationer"
|
|
},
|
|
"free": "{avail} af {total} ledige",
|
|
"full": "Optaget",
|
|
"offPeak": "uden for spidsbelastning",
|
|
"homeCharger": "Hjemmelader"
|
|
}
|
|
},
|
|
"settings": {
|
|
"tabs": {
|
|
"personal": "Personlige indstillinger",
|
|
"integrations": "Integrationer"
|
|
},
|
|
"title": "Indstillinger",
|
|
"account": {
|
|
"title": "Konto",
|
|
"name": "Navn",
|
|
"nameSaved": "Navn gemt.",
|
|
"email": "E-mail",
|
|
"verified": "Bekræftet",
|
|
"notVerified": "Ikke bekræftet",
|
|
"sending": "Sender…",
|
|
"resendVerification": "Send bekræftelsesmail igen",
|
|
"verificationRequested": "Bekræftelsesmail anmodet.",
|
|
"changePassword": "Skift adgangskode",
|
|
"currentPassword": "Nuværende adgangskode",
|
|
"newPassword": "Ny adgangskode (mindst 8)",
|
|
"confirmNewPassword": "Bekræft ny adgangskode",
|
|
"tooShort": "Den nye adgangskode skal være på mindst 8 tegn.",
|
|
"mismatch": "Den nye adgangskode og bekræftelsen stemmer ikke overens.",
|
|
"passwordUpdated": "Adgangskode opdateret.",
|
|
"updating": "Opdaterer…",
|
|
"updatePassword": "Opdater adgangskode"
|
|
},
|
|
"appearance": {
|
|
"title": "Udseende",
|
|
"theme": "Tema",
|
|
"themeLight": "Lyst",
|
|
"themeDark": "Mørkt",
|
|
"themeSystem": "System",
|
|
"language": "Sprog",
|
|
"languageHint": "Appens tekst samt navne på måneder og dage.",
|
|
"languageFallbackHint": "Dette sprog er endnu ikke oversat — appens tekst forbliver på engelsk.",
|
|
"region": "Region",
|
|
"regionHint": "Tal- og valutaformat.",
|
|
"currency": "Valuta",
|
|
"currencyHint": "Eksempel: {example}. Beløb er kun til visning — intet omregnes.",
|
|
"dateFormat": "Datoformat",
|
|
"dateHint": "Eksempel: {example}",
|
|
"fontSize": "Skriftstørrelse",
|
|
"fontSmall": "Lille",
|
|
"fontMedium": "Mellem",
|
|
"fontLarge": "Stor"
|
|
},
|
|
"profile": {
|
|
"title": "Profil",
|
|
"working": "Arbejder…",
|
|
"uploadPhoto": "Upload billede",
|
|
"remove": "Fjern",
|
|
"bio": "Om mig",
|
|
"bioHint": "En kort note, som andre i din husstand kan se.",
|
|
"bioSaved": "Beskrivelse gemt.",
|
|
"saveBio": "Gem beskrivelse"
|
|
},
|
|
"security": {
|
|
"title": "Sikkerhed",
|
|
"checkingDevice": "Tjekker enhed…",
|
|
"biometricSignIn": "Biometrisk login",
|
|
"biometricAvailable": "Log ind med {method} i stedet for din adgangskode.",
|
|
"biometricUnavailable": "Der er ikke registreret biometri på denne enhed.",
|
|
"methodFaceOrFingerprint": "ansigt eller fingeraftryk",
|
|
"methodFace": "ansigtsgenkendelse",
|
|
"methodFingerprint": "fingeraftryk",
|
|
"methodBiometrics": "biometri",
|
|
"turnedOff": "Biometrisk login slået fra",
|
|
"enabled": "Biometrisk login aktiveret",
|
|
"couldNotEnable": "Kunne ikke aktivere: {error}",
|
|
"confirmPassword": "Bekræft din adgangskode",
|
|
"password": "Adgangskode"
|
|
},
|
|
"org": {
|
|
"title": "Organisation",
|
|
"titleAll": "Organisationer",
|
|
"subtitleOwn": "Den organisation du administrerer",
|
|
"subtitleNone": "Opret en for at administrere dit eget team",
|
|
"subtitleAll": "Enheder, som brugere tilhører",
|
|
"nameLabel": "Organisationsnavn",
|
|
"namePlaceholder": "Acme Fleet",
|
|
"newOrg": "Ny organisation",
|
|
"create": "Opret organisation",
|
|
"createHint": "Du bliver dens administrator og kan derefter tilføje og administrere brugere.",
|
|
"rename": "Omdøb",
|
|
"delete": "Slet",
|
|
"deleteTitle": "Slet organisation?",
|
|
"empty": "Ingen organisationer endnu.",
|
|
"confirmDelete": "Slet organisationen „{name}“? Dette kan ikke fortrydes.",
|
|
"confirmDeleteOwn": "Slet din organisation „{name}“? Du fjernes fra den og bliver en almindelig bruger. Dette kan ikke fortrydes."
|
|
},
|
|
"privacy": {
|
|
"title": "Privatliv og sikkerhed",
|
|
"body": "Tofaktorgodkendelse er ikke tilgængelig endnu. Sessioner bygger på tokens udstedt af serveren, som udløber af sig selv, så at logge ud afslutter kun sessionen på denne enhed. For at logge alle enheder ud skal du skifte din adgangskode ovenfor.",
|
|
"signOut": "Log ud"
|
|
},
|
|
"danger": {
|
|
"title": "Farezone",
|
|
"body": "Sletning af din konto fjerner dit login og din profil. Det sletter ikke husstandens delte biler eller servicehistorik. Der er 3 dages betænkningstid, før sletningen er endelig, og du kan annullere når som helst inden da.",
|
|
"deleteAccount": "Slet min konto",
|
|
"typeToConfirm": "Skriv {email} for at bekræfte",
|
|
"typeEmailPrompt": "Skriv din e-mail for at bekræfte.",
|
|
"requesting": "Anmoder…",
|
|
"requestDeletion": "Anmod om sletning",
|
|
"requestedOn": "Sletning af konto anmodet den {date}. {tail}",
|
|
"cooldownPassed": "Betænkningstiden er udløbet. Du kan nu gennemføre sletningen.",
|
|
"canStillCancel": "Du kan stadig annullere — det bliver permanent efter 3 dages betænkningstid.",
|
|
"cancelRequest": "Annuller anmodning om sletning",
|
|
"permanentlyDelete": "Slet permanent",
|
|
"finalizeTitle": "Slet konto?",
|
|
"finalizeBody": "Dette sletter din konto permanent. Det kan ikke fortrydes.",
|
|
"delete": "Slet"
|
|
}
|
|
},
|
|
"status": {
|
|
"noData": "Ingen data",
|
|
"serviceOverdueDays": "Service overskredet med {days} d",
|
|
"dueInDays": "Forfalder om {days} d",
|
|
"okDays": "OK · {days} d",
|
|
"noKm": "Ingen km",
|
|
"serviceOverdueKm": "Service overskredet med {km} km",
|
|
"inKm": "Om {km} km",
|
|
"kmLeft": "{km} km tilbage",
|
|
"expiredAgo": "Udløb for {days} d siden",
|
|
"expiresToday": "Udløber i dag",
|
|
"renewInDays": "Forny om {days} d",
|
|
"validDays": "Gyldig · {days} d",
|
|
"noExpiry": "Udløber ikke",
|
|
"done": "Færdig",
|
|
"noTrigger": "Ingen udløser",
|
|
"overdue": "Overskredet",
|
|
"overdueBy": "Overskredet {parts}",
|
|
"dueIn": "Forfalder om {parts}",
|
|
"upcoming": "Kommende",
|
|
"today": "i dag",
|
|
"days": "{days} d",
|
|
"km": "{km} km",
|
|
"warrantyExpiredAgo": "Garanti udløb for {days} d siden",
|
|
"warrantyEndsIn": "Garanti slutter om {days} d",
|
|
"underWarranty": "Under garanti · {days} d"
|
|
}
|
|
} |