diff --git a/API Server/.env.example b/API Server/.env.example index 8b3b802..54ba636 100644 --- a/API Server/.env.example +++ b/API Server/.env.example @@ -21,11 +21,11 @@ POCKETBASE_ADMIN_PASSWORD= # CORS allowed origins for browser clients (comma separated, or * for any). # Native mobile apps are not subject to CORS. Editable at runtime from the panel # (Web App section), which writes the change back into this file. -CORS_ALLOW_ORIGINS=http://localhost:5173 +CORS_ALLOW_ORIGINS=http://localhost:8090 # Web App address, probed by GET /api/status and shown on the panel. Editable at # runtime from the panel (Web App section). -WEBAPP_URL=http://localhost:5173 +WEBAPP_URL=http://localhost:8090 # PocketBase auth collection holding app users (default: users). AUTH_USERS_COLLECTION=users diff --git a/API Server/README.md b/API Server/README.md index 4fbaea8..9c385af 100644 --- a/API Server/README.md +++ b/API Server/README.md @@ -211,7 +211,7 @@ Copy `.env.example` to `.env` and fill in. Summary: | `POCKETBASE_URL` | `http://10.2.1.10:8027` | PocketBase base URL | | `POCKETBASE_ADMIN_EMAIL` / `_PASSWORD` | — | superuser service account | | `CORS_ALLOW_ORIGINS` | `*` | comma-separated browser origins | -| `WEBAPP_URL` | `http://localhost:5173` | probed by `/api/status` | +| `WEBAPP_URL` | `http://localhost:8090` | probed by `/api/status` | | `AUTH_USERS_COLLECTION` | `users` | PocketBase auth collection | | `PLUGINS_FILE` | `plugins.json` | plugin state store | diff --git a/API Server/internal/api/dist/assets/index-CL6eq5qV.js b/API Server/internal/api/dist/assets/index-frPmZHwa.js similarity index 99% rename from API Server/internal/api/dist/assets/index-CL6eq5qV.js rename to API Server/internal/api/dist/assets/index-frPmZHwa.js index a241407..dcae07e 100644 --- a/API Server/internal/api/dist/assets/index-CL6eq5qV.js +++ b/API Server/internal/api/dist/assets/index-frPmZHwa.js @@ -14,4 +14,4 @@ * @vue/runtime-dom v3.5.39 * (c) 2018-present Yuxi (Evan) You and Vue contributors * @license MIT -**/let on;const Jn=typeof window<"u"&&window.trustedTypes;if(Jn)try{on=Jn.createPolicy("vue",{createHTML:e=>e})}catch{}const _o=on?e=>on.createHTML(e):e=>e,xl="http://www.w3.org/2000/svg",kl="http://www.w3.org/1998/Math/MathML",qe=typeof document<"u"?document:null,Yn=qe&&qe.createElement("template"),Sl={insert:(e,t,s)=>{t.insertBefore(e,s||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,s,n)=>{const i=t==="svg"?qe.createElementNS(xl,e):t==="mathml"?qe.createElementNS(kl,e):s?qe.createElement(e,{is:s}):qe.createElement(e);return e==="select"&&n&&n.multiple!=null&&i.setAttribute("multiple",n.multiple),i},createText:e=>qe.createTextNode(e),createComment:e=>qe.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>qe.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,s,n,i,o){const r=s?s.previousSibling:t.lastChild;if(i&&(i===o||i.nextSibling))for(;t.insertBefore(i.cloneNode(!0),s),!(i===o||!(i=i.nextSibling)););else{Yn.innerHTML=_o(n==="svg"?`${e}`:n==="mathml"?`${e}`:e);const l=Yn.content;if(n==="svg"||n==="mathml"){const a=l.firstChild;for(;a.firstChild;)l.appendChild(a.firstChild);l.removeChild(a)}t.insertBefore(l,s)}return[r?r.nextSibling:t.firstChild,s?s.previousSibling:t.lastChild]}},Tl=Symbol("_vtc");function El(e,t,s){const n=e[Tl];n&&(t=(t?[t,...n]:[...n]).join(" ")),t==null?e.removeAttribute("class"):s?e.setAttribute("class",t):e.className=t}const Xn=Symbol("_vod"),Cl=Symbol("_vsh"),Pl=Symbol(""),$l=/(?:^|;)\s*display\s*:/;function Al(e,t,s){const n=e.style,i=ne(s);let o=!1;if(s&&!i){if(t)if(ne(t))for(const r of t.split(";")){const l=r.slice(0,r.indexOf(":")).trim();s[l]==null&&Lt(n,l,"")}else for(const r in t)s[r]==null&&Lt(n,r,"");for(const r in s){r==="display"&&(o=!0);const l=s[r];l!=null?Rl(e,r,!ne(t)&&t?t[r]:void 0,l)||Lt(n,r,l):Lt(n,r,"")}}else if(i){if(t!==s){const r=n[Pl];r&&(s+=";"+r),n.cssText=s,o=$l.test(s)}}else t&&e.removeAttribute("style");Xn in e&&(e[Xn]=o?n.display:"",e[Cl]&&(n.display="none"))}const Qn=/\s*!important$/;function Lt(e,t,s){if(L(s))s.forEach(n=>Lt(e,t,n));else if(s==null&&(s=""),t.startsWith("--"))e.setProperty(t,s);else{const n=Ol(e,t);Qn.test(s)?e.setProperty(ct(n),s.replace(Qn,""),"important"):e[n]=s}}const ei=["Webkit","Moz","ms"],Ws={};function Ol(e,t){const s=Ws[t];if(s)return s;let n=Ce(t);if(n!=="filter"&&n in e)return Ws[t]=n;n=_i(n);for(let i=0;iGs||(Dl.then(()=>Gs=0),Gs=Date.now());function Ll(e,t){const s=n=>{if(!n._vts)n._vts=Date.now();else if(n._vts<=s.attached)return;const i=s.value;if(L(i)){const o=n.stopImmediatePropagation;n.stopImmediatePropagation=()=>{o.call(n),n._stopped=!0};const r=i.slice(),l=[n];for(let a=0;ae.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,Fl=(e,t,s,n,i,o)=>{const r=i==="svg";t==="class"?El(e,n,r):t==="style"?Al(e,s,n):ks(t)?Ss(t)||Ul(e,t,s,n,o):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):Bl(e,t,n,r))?(ni(e,t,n),!e.tagName.includes("-")&&(t==="value"||t==="checked"||t==="selected")&&si(e,t,n,r,o,t!=="value")):e._isVueCE&&(Hl(e,t)||e._def.__asyncLoader&&(/[A-Z]/.test(t)||!ne(n)))?ni(e,Ce(t),n,o,t):(t==="true-value"?e._trueValue=n:t==="false-value"&&(e._falseValue=n),si(e,t,n,r))};function Bl(e,t,s,n){if(n)return!!(t==="innerHTML"||t==="textContent"||t in e&&oi(t)&&H(s));if(t==="spellcheck"||t==="draggable"||t==="translate"||t==="autocorrect"||t==="sandbox"&&e.tagName==="IFRAME"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA")return!1;if(t==="width"||t==="height"){const i=e.tagName;if(i==="IMG"||i==="VIDEO"||i==="CANVAS"||i==="SOURCE")return!1}return oi(t)&&ne(s)?!1:t in e}function Hl(e,t){const s=e._def.props;if(!s)return!1;const n=Ce(t);return Array.isArray(s)?s.some(i=>Ce(i)===n):Object.keys(s).some(i=>Ce(i)===n)}const at=e=>{const t=e.props["onUpdate:modelValue"]||!1;return L(t)?s=>fs(t,s):t};function Vl(e){e.target.composing=!0}function ri(e){const t=e.target;t.composing&&(t.composing=!1,t.dispatchEvent(new Event("input")))}const Ee=Symbol("_assign");function li(e,t,s){return t&&(e=e.trim()),s&&(e=Es(e)),e}const pe={created(e,{modifiers:{lazy:t,trim:s,number:n}},i){e[Ee]=at(i);const o=n||i.props&&i.props.type==="number";Xe(e,t?"change":"input",r=>{r.target.composing||e[Ee](li(e.value,s,o))}),(s||o)&&Xe(e,"change",()=>{e.value=li(e.value,s,o)}),t||(Xe(e,"compositionstart",Vl),Xe(e,"compositionend",ri),Xe(e,"change",ri))},mounted(e,{value:t}){e.value=t??""},beforeUpdate(e,{value:t,oldValue:s,modifiers:{lazy:n,trim:i,number:o}},r){if(e[Ee]=at(r),e.composing)return;const l=(o||e.type==="number")&&!/^0\d/.test(e.value)?Es(e.value):e.value,a=t??"";if(l===a)return;const p=e.getRootNode();(p instanceof Document||p instanceof ShadowRoot)&&p.activeElement===e&&e.type!=="range"&&(n&&t===s||i&&e.value.trim()===a)||(e.value=a)}},Kl={deep:!0,created(e,t,s){e[Ee]=at(s),Xe(e,"change",()=>{const n=e._modelValue,i=$t(e),o=e.checked,r=e[Ee];if(L(n)){const l=dn(n,i),a=l!==-1;if(o&&!a)r(n.concat(i));else if(!o&&a){const p=[...n];p.splice(l,1),r(p)}}else if(Rt(n)){const l=new Set(n);o?l.add(i):l.delete(i),r(l)}else r(yo(e,o))})},mounted:ai,beforeUpdate(e,t,s){e[Ee]=at(s),ai(e,t,s)}};function ai(e,{value:t,oldValue:s},n){e._modelValue=t;let i;if(L(t))i=dn(t,n.props.value)>-1;else if(Rt(t))i=t.has(n.props.value);else{if(t===s)return;i=rt(t,yo(e,!0))}e.checked!==i&&(e.checked=i)}const Wl={created(e,{value:t},s){e.checked=rt(t,s.props.value),e[Ee]=at(s),Xe(e,"change",()=>{e[Ee]($t(e))})},beforeUpdate(e,{value:t,oldValue:s},n){e[Ee]=at(n),t!==s&&(e.checked=rt(t,n.props.value))}},xs={deep:!0,created(e,{value:t,modifiers:{number:s}},n){const i=Rt(t);Xe(e,"change",()=>{const o=Array.prototype.filter.call(e.options,r=>r.selected).map(r=>s?Es($t(r)):$t(r));e[Ee](e.multiple?i?new Set(o):o:o[0]),e._assigning=!0,Li(()=>{e._assigning=!1})}),e[Ee]=at(n)},mounted(e,{value:t}){ci(e,t)},beforeUpdate(e,t,s){e[Ee]=at(s)},updated(e,{value:t}){e._assigning||ci(e,t)}};function ci(e,t){const s=e.multiple,n=L(t);if(!(s&&!n&&!Rt(t))){for(let i=0,o=e.options.length;iString(p)===String(l)):r.selected=dn(t,l)>-1}else r.selected=t.has(l);else if(rt($t(r),t)){e.selectedIndex!==i&&(e.selectedIndex=i);return}}!s&&e.selectedIndex!==-1&&(e.selectedIndex=-1)}}function $t(e){return"_value"in e?e._value:e.value}function yo(e,t){const s=t?"_trueValue":"_falseValue";return s in e?e[s]:t}const Gl={created(e,t,s){ds(e,t,s,null,"created")},mounted(e,t,s){ds(e,t,s,null,"mounted")},beforeUpdate(e,t,s,n){ds(e,t,s,n,"beforeUpdate")},updated(e,t,s,n){ds(e,t,s,n,"updated")}};function ql(e,t){switch(e){case"SELECT":return xs;case"TEXTAREA":return pe;default:switch(t){case"checkbox":return Kl;case"radio":return Wl;default:return pe}}}function ds(e,t,s,n,i){const r=ql(e.tagName,s.props&&s.props.type)[i];r&&r(e,t,s,n)}const Zl=["ctrl","shift","alt","meta"],Jl={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&e.button!==0,middle:e=>"button"in e&&e.button!==1,right:e=>"button"in e&&e.button!==2,exact:(e,t)=>Zl.some(s=>e[`${s}Key`]&&!t.includes(s))},Yl=(e,t)=>{if(!e)return e;const s=e._withMods||(e._withMods={}),n=t.join(".");return s[n]||(s[n]=((i,...o)=>{for(let r=0;r{const s=e._withKeys||(e._withKeys={}),n=t.join(".");return s[n]||(s[n]=(i=>{if(!("key"in i))return;const o=ct(i.key);if(t.some(r=>r===o||Xl[r]===o))return e(i)}))},ea=he({patchProp:Fl},Sl);let ui;function ta(){return ui||(ui=nl(ea))}const sa=((...e)=>{const t=ta().createApp(...e),{mount:s}=t;return t.mount=n=>{const i=ia(n);if(!i)return;const o=t._component;!H(o)&&!o.render&&!o.template&&(o.template=i.innerHTML),i.nodeType===1&&(i.textContent="");const r=s(i,!1,na(i));return i instanceof Element&&(i.removeAttribute("v-cloak"),i.setAttribute("data-v-app","")),r},t});function na(e){if(e instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&e instanceof MathMLElement)return"mathml"}function ia(e){return ne(e)?document.querySelector(e):e}const wo="dh-panel-theme";function oa(){var e;try{const t=localStorage.getItem(wo);if(t==="dark"||t==="light")return t}catch{}return(e=window.matchMedia)!=null&&e.call(window,"(prefers-color-scheme: dark)").matches?"dark":"light"}const Et=V(oa());function xo(e){Et.value=e,document.documentElement.classList.toggle("dark",e==="dark");try{localStorage.setItem(wo,e)}catch{}}function di(){xo(Et.value==="dark"?"light":"dark")}xo(Et.value);const ra={apiServer:"API server",switchToLight:"Switch to light theme",switchToDark:"Switch to dark theme",theme:"Theme",signOut:"Sign out",loading:"Loading…",standardUserNote:"Signed in as a standard user — management sections need an admin role",footer:"DriverVault — car maintenance & service tracker."},la={overview:"Overview",users:"Users",orgs:"Organizations",pocketbase:"PocketBase",webapp:"Web App",plugins:"Plugins",api:"API"},aa={title:"Sign in",subtitle:"Superadmin console for the DriverVault API Server.",email:"Email",password:"Password",submit:"Sign in",submitting:"Signing in…",invalid:"Invalid email or password.",failed:"Could not sign in.",authNote:"Authenticates against PocketBase through this server"},ca={title:"Status",unreachable:"unreachable",checking:"Checking…",apiServer:"API Server",pocketBase:"PocketBase",webApp:"Web App",thisProcess:"this process"},ua={create:"Create",save:"Save",cancel:"Cancel",edit:"Edit",delete:"Delete",rename:"Rename",empty:"—",name:"Name",email:"Email",role:"Role",organization:"Organization"},da={title:"Users",allOrgs:"All organizations",yourOrg:"Your organization",newUser:"New user",password:"Password",passwordUnchanged:"Password (blank = unchanged)",passwordPlaceholder:"min 8 characters",orgNone:"— none —",you:"you",empty:"No users.",confirmDelete:"Delete {email}? This cannot be undone."},fa={title:"Organizations",subtitle:"Tenants users belong to",newOrg:"New organization",namePlaceholder:"Acme Fleet",empty:"No organizations yet.",colId:"ID",confirmDelete:'Delete the organization "{name}"?'},pa={title:"PocketBase",subtitle:"Database connection used by every endpoint",connected:"connected",noSuperuser:"no superuser",unreachable:"unreachable",baseUrl:"Base URL",superuserEmail:"Superuser email",superuserPassword:"Superuser password",passwordUnchanged:"unchanged",passwordNotSet:"not set",saveApply:"Save & apply",testConnection:"Test connection",persistedEnv:"persisted to .env",testOk:"Connection OK — superuser authenticated.",testReachableNoAuth:"PocketBase is reachable, but the service account did not authenticate.",testUnreachable:"PocketBase is not reachable at that address.",savedNotice:"Saved. The server is now using this PocketBase."},ha={title:"Web App",subtitle:"Address probed by the status check, and who may call this API from a browser",reachable:"reachable",unreachable:"unreachable",baseUrl:"Base URL",allowedOrigins:"Allowed origins",originsHint:"Comma separated, or {star} for any. Native mobile apps are not subject to CORS.",saveApply:"Save & apply",testConnection:"Test connection",persistedEnv:"persisted to .env",testOk:"Web App is reachable.",testFailed:"Web App did not answer its health check at that address.",savedNotice:"Saved. New origins apply to the next request."},ga={title:"Plugins",subtitle:"Third-party service integrations",registerExternal:"Register external",cancelRegister:"Cancel",name:"Name",baseUrl:"Base URL",provider:"Provider",register:"Register",empty:"No plugins yet. Register an external one above, or compile a built-in connector.",builtin:"builtin",enabled:"enabled",disabled:"disabled",health:"Health",configure:"Configure",close:"Close",noConfig:"This plugin takes no configuration.",capabilities:"Capabilities",saved:"Saved.",checking:"Checking…",save:"Save",saveEnable:"Save & enable",disable:"Disable",remove:"Remove",builtinNote:"Built-in plugins can be disabled but not removed",confirmRemove:'Remove the external plugin "{name}"? Its saved config is deleted.'},ma={colEndpoint:"Endpoint",colDescription:"Description",authNone:"No auth",authBearer:"Bearer token",authManager:"Admin / superadmin",authSuperadmin:"Superadmin",groupPublic:"Public",groupIdentity:"Identity",groupCars:"Cars",groupService:"Service records",groupParts:"Parts",groupAccount:"Account",groupManagement:"Management",groupSuperadmin:"Superadmin"},ba={app:ra,sections:la,login:aa,status:ca,common:ua,users:da,orgs:fa,pocketbase:pa,webapp:ha,plugins:ga,api:ma},va={apiServer:"Serwer API",switchToLight:"Przełącz na motyw jasny",switchToDark:"Przełącz na motyw ciemny",theme:"Motyw",signOut:"Wyloguj się",loading:"Ładowanie…",standardUserNote:"Zalogowano jako zwykły użytkownik — sekcje zarządzania wymagają roli administratora",footer:"DriverVault — rejestr serwisu i konserwacji samochodu."},_a={overview:"Przegląd",users:"Użytkownicy",orgs:"Organizacje",pocketbase:"PocketBase",webapp:"Aplikacja webowa",plugins:"Wtyczki",api:"API"},ya={title:"Zaloguj się",subtitle:"Konsola superadministratora serwera API DriverVault.",email:"E-mail",password:"Hasło",submit:"Zaloguj się",submitting:"Logowanie…",invalid:"Nieprawidłowy e-mail lub hasło.",failed:"Nie udało się zalogować.",authNote:"Uwierzytelnia w PocketBase za pośrednictwem tego serwera"},wa={title:"Status",unreachable:"niedostępny",checking:"Sprawdzanie…",apiServer:"Serwer API",pocketBase:"PocketBase",webApp:"Aplikacja webowa",thisProcess:"ten proces"},xa={create:"Utwórz",save:"Zapisz",cancel:"Anuluj",edit:"Edytuj",delete:"Usuń",rename:"Zmień nazwę",empty:"—",name:"Nazwa",email:"E-mail",role:"Rola",organization:"Organizacja"},ka={title:"Użytkownicy",allOrgs:"Wszystkie organizacje",yourOrg:"Twoja organizacja",newUser:"Nowy użytkownik",password:"Hasło",passwordUnchanged:"Hasło (puste = bez zmian)",passwordPlaceholder:"min. 8 znaków",orgNone:"— brak —",you:"Ty",empty:"Brak użytkowników.",confirmDelete:"Usunąć {email}? Tej operacji nie można cofnąć."},Sa={title:"Organizacje",subtitle:"Podmioty, do których należą użytkownicy",newOrg:"Nowa organizacja",namePlaceholder:"Acme Fleet",empty:"Brak organizacji.",colId:"ID",confirmDelete:"Usunąć organizację „{name}”?"},Ta={title:"PocketBase",subtitle:"Połączenie z bazą danych używane przez każdy punkt końcowy",connected:"połączono",noSuperuser:"brak superużytkownika",unreachable:"niedostępny",baseUrl:"Adres bazowy",superuserEmail:"E-mail superużytkownika",superuserPassword:"Hasło superużytkownika",passwordUnchanged:"bez zmian",passwordNotSet:"nie ustawiono",saveApply:"Zapisz i zastosuj",testConnection:"Testuj połączenie",persistedEnv:"zapisano w .env",testOk:"Połączenie OK — superużytkownik uwierzytelniony.",testReachableNoAuth:"PocketBase jest dostępny, ale konto usługowe nie zostało uwierzytelnione.",testUnreachable:"PocketBase jest niedostępny pod tym adresem.",savedNotice:"Zapisano. Serwer korzysta teraz z tego PocketBase."},Ea={title:"Aplikacja webowa",subtitle:"Adres sprawdzany podczas kontroli statusu oraz kto może wywoływać to API z przeglądarki",reachable:"dostępna",unreachable:"niedostępna",baseUrl:"Adres bazowy",allowedOrigins:"Dozwolone źródła",originsHint:"Oddzielone przecinkami lub {star} dla dowolnego. Natywne aplikacje mobilne nie podlegają CORS.",saveApply:"Zapisz i zastosuj",testConnection:"Testuj połączenie",persistedEnv:"zapisano w .env",testOk:"Aplikacja webowa jest dostępna.",testFailed:"Aplikacja webowa nie odpowiedziała na kontrolę stanu pod tym adresem.",savedNotice:"Zapisano. Nowe źródła obowiązują od następnego żądania."},Ca={title:"Wtyczki",subtitle:"Integracje z usługami zewnętrznymi",registerExternal:"Zarejestruj zewnętrzną",cancelRegister:"Anuluj",name:"Nazwa",baseUrl:"Adres bazowy",provider:"Dostawca",register:"Zarejestruj",empty:"Brak wtyczek. Zarejestruj zewnętrzną powyżej lub skompiluj wbudowany łącznik.",builtin:"wbudowana",enabled:"włączona",disabled:"wyłączona",health:"Stan",configure:"Konfiguruj",close:"Zamknij",noConfig:"Ta wtyczka nie wymaga konfiguracji.",capabilities:"Możliwości",saved:"Zapisano.",checking:"Sprawdzanie…",save:"Zapisz",saveEnable:"Zapisz i włącz",disable:"Wyłącz",remove:"Usuń",builtinNote:"Wtyczki wbudowane można wyłączyć, ale nie usunąć",confirmRemove:"Usunąć zewnętrzną wtyczkę „{name}”? Jej zapisana konfiguracja zostanie usunięta."},Pa={colEndpoint:"Punkt końcowy",colDescription:"Opis",authNone:"Bez uwierzytelniania",authBearer:"Token Bearer",authManager:"Administrator / superadministrator",authSuperadmin:"Superadministrator",groupPublic:"Publiczne",groupIdentity:"Tożsamość",groupCars:"Samochody",groupService:"Wpisy serwisowe",groupParts:"Części",groupAccount:"Konto",groupManagement:"Zarządzanie",groupSuperadmin:"Superadministrator"},$a={app:va,sections:_a,login:ya,status:wa,common:xa,users:ka,orgs:Sa,pocketbase:Ta,webapp:Ea,plugins:Ca,api:Pa},Aa={apiServer:"API-server",switchToLight:"Skift til lyst tema",switchToDark:"Skift til mørkt tema",theme:"Tema",signOut:"Log ud",loading:"Indlæser…",standardUserNote:"Logget ind som almindelig bruger — administrationssektioner kræver en administratorrolle",footer:"DriverVault — bilservice- og vedligeholdelsesregister."},Oa={overview:"Oversigt",users:"Brugere",orgs:"Organisationer",pocketbase:"PocketBase",webapp:"Webapp",plugins:"Plugins",api:"API"},Ra={title:"Log ind",subtitle:"Superadmin-konsol til DriverVault API-serveren.",email:"E-mail",password:"Adgangskode",submit:"Log ind",submitting:"Logger ind…",invalid:"Ugyldig e-mail eller adgangskode.",failed:"Kunne ikke logge ind.",authNote:"Godkender mod PocketBase gennem denne server"},Ia={title:"Status",unreachable:"utilgængelig",checking:"Tjekker…",apiServer:"API-server",pocketBase:"PocketBase",webApp:"Webapp",thisProcess:"denne proces"},Ua={create:"Opret",save:"Gem",cancel:"Annuller",edit:"Rediger",delete:"Slet",rename:"Omdøb",empty:"—",name:"Navn",email:"E-mail",role:"Rolle",organization:"Organisation"},ja={title:"Brugere",allOrgs:"Alle organisationer",yourOrg:"Din organisation",newUser:"Ny bruger",password:"Adgangskode",passwordUnchanged:"Adgangskode (tom = uændret)",passwordPlaceholder:"mindst 8 tegn",orgNone:"— ingen —",you:"dig",empty:"Ingen brugere.",confirmDelete:"Slet {email}? Dette kan ikke fortrydes."},Ma={title:"Organisationer",subtitle:"Enheder, som brugere tilhører",newOrg:"Ny organisation",namePlaceholder:"Acme Fleet",empty:"Ingen organisationer endnu.",colId:"ID",confirmDelete:'Slet organisationen "{name}"?'},Na={title:"PocketBase",subtitle:"Databaseforbindelse brugt af hvert endpoint",connected:"forbundet",noSuperuser:"ingen superbruger",unreachable:"utilgængelig",baseUrl:"Basis-URL",superuserEmail:"Superbrugerens e-mail",superuserPassword:"Superbrugerens adgangskode",passwordUnchanged:"uændret",passwordNotSet:"ikke angivet",saveApply:"Gem og anvend",testConnection:"Test forbindelse",persistedEnv:"gemt i .env",testOk:"Forbindelse OK — superbruger godkendt.",testReachableNoAuth:"PocketBase er tilgængelig, men servicekontoen blev ikke godkendt.",testUnreachable:"PocketBase er ikke tilgængelig på den adresse.",savedNotice:"Gemt. Serveren bruger nu denne PocketBase."},Da={title:"Webapp",subtitle:"Adressen, der tjekkes ved statuskontrol, og hvem der må kalde dette API fra en browser",reachable:"tilgængelig",unreachable:"utilgængelig",baseUrl:"Basis-URL",allowedOrigins:"Tilladte oprindelser",originsHint:"Kommasepareret, eller {star} for enhver. Native mobilapps er ikke underlagt CORS.",saveApply:"Gem og anvend",testConnection:"Test forbindelse",persistedEnv:"gemt i .env",testOk:"Webappen er tilgængelig.",testFailed:"Webappen svarede ikke på sit helbredstjek på den adresse.",savedNotice:"Gemt. Nye oprindelser gælder fra næste anmodning."},za={title:"Plugins",subtitle:"Integrationer med tredjepartstjenester",registerExternal:"Registrér ekstern",cancelRegister:"Annuller",name:"Navn",baseUrl:"Basis-URL",provider:"Udbyder",register:"Registrér",empty:"Ingen plugins endnu. Registrér et eksternt ovenfor, eller kompilér et indbygget stik.",builtin:"indbygget",enabled:"aktiveret",disabled:"deaktiveret",health:"Helbred",configure:"Konfigurer",close:"Luk",noConfig:"Dette plugin kræver ingen konfiguration.",capabilities:"Funktioner",saved:"Gemt.",checking:"Tjekker…",save:"Gem",saveEnable:"Gem og aktivér",disable:"Deaktiver",remove:"Fjern",builtinNote:"Indbyggede plugins kan deaktiveres, men ikke fjernes",confirmRemove:'Fjern det eksterne plugin "{name}"? Dets gemte konfiguration slettes.'},La={colEndpoint:"Endpoint",colDescription:"Beskrivelse",authNone:"Ingen godkendelse",authBearer:"Bearer-token",authManager:"Admin / superadmin",authSuperadmin:"Superadmin",groupPublic:"Offentlig",groupIdentity:"Identitet",groupCars:"Biler",groupService:"Serviceposter",groupParts:"Reservedele",groupAccount:"Konto",groupManagement:"Administration",groupSuperadmin:"Superadmin"},Fa={app:Aa,sections:Oa,login:Ra,status:Ia,common:Ua,users:ja,orgs:Ma,pocketbase:Na,webapp:Da,plugins:za,api:La},ko="dh-panel-lang",So="en",At={en:ba,pl:$a,da:Fa},Ba=Object.keys(At);function Ha(){try{const t=localStorage.getItem(ko);if(t&&At[t])return t}catch{}const e=(navigator.language||"en").split("-")[0];return At[e]?e:So}const Sn=V(Ha());function Va(e){if(At[e]){Sn.value=e;try{localStorage.setItem(ko,e)}catch{}}}function fi(e,t){return t.split(".").reduce((s,n)=>s==null?void 0:s[n],e)}function Ka(e,t){return t?e.replace(/\{(\w+)\}/g,(s,n)=>t[n]==null?s:String(t[n])):e}function Wa(e,t,s){let n="other";try{n=new Intl.PluralRules(s).select(t)}catch{n=t===1?"one":"other"}return e[n]??e.other??e.one}function h(e,t){const s=Sn.value;let n=fi(At[s],e);if(n==null&&(n=fi(At[So],e)),n==null)return e;if(typeof n=="object"){if((t==null?void 0:t.n)==null)return e;n=Wa(n,t.n,s)}return typeof n!="string"?e:Ka(n,t)}function pi(e,t,s){const i=h(e,{...s,[t]:"\0"}),o=i.indexOf("\0");return o===-1?{before:i,after:""}:{before:i.slice(0,o),after:i.slice(o+1)}}const rn="dh-panel-token";function Ga(){try{return localStorage.getItem(rn)||""}catch{return""}}const Ot=V(Ga()),de=V(null),bt=Qe(()=>{var e;return((e=de.value)==null?void 0:e.role)==="superadmin"}),hi=Qe(()=>{var e,t;return((e=de.value)==null?void 0:e.role)==="admin"||((t=de.value)==null?void 0:t.role)==="superadmin"});function To(e){Ot.value=e;try{e?localStorage.setItem(rn,e):localStorage.removeItem(rn)}catch{}}class qa extends Error{constructor(t,s,n){super(t),this.status=s,this.body=n}}function Za(e,t){if(!e||typeof e!="object")return`HTTP ${t}`;if(e.error)return e.error;const s=Object.entries(e.data||{}).map(([n,i])=>`${n}: ${(i==null?void 0:i.message)||i}`).filter(Boolean);return s.length?s.join("; "):e.message||`HTTP ${t}`}async function se(e,{method:t="GET",body:s,auth:n=!0}={}){const i={};s!==void 0&&(i["Content-Type"]="application/json"),n&&Ot.value&&(i.Authorization=Ot.value);const o=await fetch(e,{method:t,headers:i,body:s===void 0?void 0:JSON.stringify(s)}),r=await o.text();let l=null;try{l=r?JSON.parse(r):null}catch{l=null}if(!o.ok)throw o.status===401&&n&&Tn(),new qa(Za(l,o.status),o.status,l);return l}async function Ja(e,t){const s=await se("/api/auth/login",{method:"POST",body:{email:e,password:t},auth:!1});return To(s.token),await Eo(),de.value}async function Eo(){return de.value=await se("/api/identity"),de.value}function Tn(){To(""),de.value=null}async function Ya(){if(!Ot.value)return null;try{return await Eo()}catch{return Tn(),null}}const Xa={class:"mx-auto flex w-full max-w-sm flex-col gap-5 pt-24"},Qa={class:"dh-card p-6"},ec={class:"text-lg font-bold tracking-[-0.02em] text-strong"},tc={class:"mt-1 mb-5 text-sm text-body"},sc={class:"dh-label",for:"login-email"},nc={class:"dh-label",for:"login-password"},ic={key:0,class:"rounded-control bg-danger-soft px-3 py-2 text-xs text-danger"},oc=["disabled"],rc={class:"eyebrow text-center"},lc={__name:"LoginView",emits:["authenticated"],setup(e,{emit:t}){const s=t,n=V(""),i=V(""),o=V(""),r=V(!1),l=Qe(()=>n.value.trim()!==""&&i.value!==""&&!r.value);async function a(){if(l.value){o.value="",r.value=!0;try{const p=await Ja(n.value.trim(),i.value);s("authenticated",p)}catch(p){o.value=p.status===400||p.status===404?h("login.invalid"):p.message||h("login.failed"),i.value=""}finally{r.value=!1}}}return(p,u)=>(k(),E("div",Xa,[d("div",Qa,[d("h1",ec,b(g(h)("login.title")),1),d("p",tc,b(g(h)("login.subtitle")),1),d("form",{class:"flex flex-col gap-4",onSubmit:Yl(a,["prevent"])},[d("div",null,[d("label",sc,b(g(h)("login.email")),1),re(d("input",{id:"login-email","onUpdate:modelValue":u[0]||(u[0]=m=>n.value=m),class:"dh-input",type:"email",autocomplete:"username",autofocus:"",placeholder:"you@example.com"},null,512),[[pe,n.value]])]),d("div",null,[d("label",nc,b(g(h)("login.password")),1),re(d("input",{id:"login-password","onUpdate:modelValue":u[1]||(u[1]=m=>i.value=m),class:"dh-input",type:"password",autocomplete:"current-password",placeholder:"••••••••"},null,512),[[pe,i.value]])]),o.value?(k(),E("p",ic,b(o.value),1)):B("",!0),d("button",{class:"dh-btn w-full",type:"submit",disabled:!l.value},b(r.value?g(h)("login.submitting"):g(h)("login.submit")),9,oc)],32)]),d("p",rc,b(g(h)("login.authNote")),1)]))}},ac={class:"dh-card overflow-hidden"},cc={class:"flex items-center justify-between border-b border-subtle px-5 py-4"},uc={class:"text-base font-bold tracking-[-0.02em] text-strong"},dc={key:0,class:"dh-pill bg-danger-soft text-danger"},fc={key:0,class:"px-5 py-4 text-sm text-danger"},pc={key:1,class:"w-full text-left text-sm"},hc={class:"px-5 py-3 font-medium text-strong"},gc={class:"data px-5 py-3 text-xs text-muted"},mc={class:"data px-5 py-3 text-right text-xs text-muted"},bc={class:"px-5 py-3 text-right"},vc={key:2,class:"px-5 py-4 text-sm text-muted"},_c={__name:"StatusCard",setup(e){const t=V(null),s=V("");let n=null;async function i(){try{t.value=await se("/api/status",{auth:!1}),s.value=""}catch(l){t.value=null,s.value=l.message||h("status.unreachable")}}const o=Qe(()=>{var l,a,p;return[{key:"apiServer",label:h("status.apiServer"),h:(l=t.value)==null?void 0:l.apiServer},{key:"pocketBase",label:h("status.pocketBase"),h:(a=t.value)==null?void 0:a.pocketBase},{key:"webApp",label:h("status.webApp"),h:(p=t.value)==null?void 0:p.webApp}]});ut(()=>{i(),n=setInterval(i,1e4)}),wn(()=>clearInterval(n));const r=l=>l==="ok"?"bg-success-soft text-success":"bg-danger-soft text-danger";return(l,a)=>(k(),E("div",ac,[d("div",cc,[d("div",uc,b(g(h)("status.title")),1),s.value?(k(),E("span",dc,[a[0]||(a[0]=d("span",{class:"h-1.5 w-1.5 rounded-full bg-current"},null,-1)),Te(b(g(h)("status.unreachable")),1)])):B("",!0)]),s.value?(k(),E("div",fc,b(s.value),1)):t.value?(k(),E("table",pc,[d("tbody",null,[(k(!0),E(ee,null,ke(o.value,p=>(k(),E("tr",{key:p.key,class:"border-t border-subtle first:border-t-0"},[d("td",hc,b(p.label),1),d("td",gc,b(p.h.url||g(h)("status.thisProcess")),1),d("td",mc,b(p.h.latencyMs!=null?p.h.latencyMs+"ms":g(h)("common.empty")),1),d("td",bc,[d("span",{class:$e(["dh-pill",r(p.h.status)])},[a[1]||(a[1]=d("span",{class:"h-1.5 w-1.5 rounded-full bg-current"},null,-1)),Te(b(p.h.status),1)],2)])]))),128))])])):(k(),E("div",vc,b(g(h)("status.checking")),1))]))}},yc={class:"dh-card overflow-hidden"},wc={class:"flex items-center justify-between border-b border-subtle px-5 py-4"},xc={class:"text-base font-bold tracking-[-0.02em] text-strong"},kc={class:"mt-0.5 text-xs text-muted"},Sc={class:"flex flex-col gap-4 px-5 py-4"},Tc={class:"dh-label",for:"pb-url"},Ec={class:"grid gap-4 sm:grid-cols-2"},Cc={class:"dh-label",for:"pb-email"},Pc={class:"dh-label",for:"pb-password"},$c=["placeholder"],Ac={key:0,class:"data text-xs text-muted"},Oc={key:1,class:"rounded-control bg-info-soft px-3 py-2 text-xs text-info"},Rc={key:2,class:"rounded-control bg-danger-soft px-3 py-2 text-xs text-danger"},Ic={class:"flex items-center gap-2"},Uc=["disabled"],jc=["disabled"],Mc={class:"eyebrow"},Nc={__name:"PocketBaseCard",setup(e){const t=V(null),s=V({url:"",adminEmail:"",adminPassword:""}),n=V(null),i=V(""),o=V(""),r=V(!1);async function l(){try{t.value=await se("/api/admin/pb-config"),s.value={url:t.value.url,adminEmail:t.value.adminEmail,adminPassword:""},n.value=t.value.probe}catch(u){i.value=u.message}}ut(l);async function a(){i.value="",o.value="",r.value=!0;try{n.value=await se("/api/admin/pb-config/test",{method:"POST",body:s.value}),o.value=n.value.superuser?h("pocketbase.testOk"):n.value.reachable?h("pocketbase.testReachableNoAuth"):h("pocketbase.testUnreachable")}catch(u){i.value=u.message}finally{r.value=!1}}async function p(){i.value="",o.value="",r.value=!0;try{const u=await se("/api/admin/pb-config",{method:"PUT",body:s.value});t.value=u.config,n.value=u.config.probe,s.value.adminPassword="",o.value=u.warning||h("pocketbase.savedNotice")}catch(u){i.value=u.message}finally{r.value=!1}}return(u,m)=>{var y,A;return k(),E("div",yc,[d("div",wc,[d("div",null,[d("div",xc,b(g(h)("pocketbase.title")),1),d("p",kc,b(g(h)("pocketbase.subtitle")),1)]),n.value?(k(),E("span",{key:0,class:$e(["dh-pill",n.value.superuser?"bg-success-soft text-success":n.value.reachable?"bg-warning-soft text-warning":"bg-danger-soft text-danger"])},[m[3]||(m[3]=d("span",{class:"h-1.5 w-1.5 rounded-full bg-current"},null,-1)),Te(" "+b(n.value.superuser?g(h)("pocketbase.connected"):n.value.reachable?g(h)("pocketbase.noSuperuser"):g(h)("pocketbase.unreachable")),1)],2)):B("",!0)]),d("div",Sc,[d("div",null,[d("label",Tc,b(g(h)("pocketbase.baseUrl")),1),re(d("input",{id:"pb-url","onUpdate:modelValue":m[0]||(m[0]=D=>s.value.url=D),class:"dh-input",placeholder:"http://10.2.1.10:8027"},null,512),[[pe,s.value.url]])]),d("div",Ec,[d("div",null,[d("label",Cc,b(g(h)("pocketbase.superuserEmail")),1),re(d("input",{id:"pb-email","onUpdate:modelValue":m[1]||(m[1]=D=>s.value.adminEmail=D),class:"dh-input",autocomplete:"off"},null,512),[[pe,s.value.adminEmail]])]),d("div",null,[d("label",Pc,b(g(h)("pocketbase.superuserPassword")),1),re(d("input",{id:"pb-password","onUpdate:modelValue":m[2]||(m[2]=D=>s.value.adminPassword=D),class:"dh-input",type:"password",autocomplete:"new-password",placeholder:(y=t.value)!=null&&y.adminConfigured?g(h)("pocketbase.passwordUnchanged"):g(h)("pocketbase.passwordNotSet")},null,8,$c),[[pe,s.value.adminPassword]])])]),(A=n.value)!=null&&A.detail?(k(),E("p",Ac,b(n.value.detail),1)):B("",!0),o.value?(k(),E("p",Oc,b(o.value),1)):B("",!0),i.value?(k(),E("p",Rc,b(i.value),1)):B("",!0),d("div",Ic,[d("button",{class:"dh-btn",disabled:r.value,onClick:p},b(g(h)("pocketbase.saveApply")),9,Uc),d("button",{class:"dh-btn-ghost",disabled:r.value,onClick:a},b(g(h)("pocketbase.testConnection")),9,jc),m[4]||(m[4]=d("span",{class:"flex-1"},null,-1)),d("span",Mc,b(g(h)("pocketbase.persistedEnv")),1)])])])}}},Dc={class:"dh-card overflow-hidden"},zc={class:"flex items-center justify-between border-b border-subtle px-5 py-4"},Lc={class:"text-base font-bold tracking-[-0.02em] text-strong"},Fc={class:"mt-0.5 text-xs text-muted"},Bc={class:"flex flex-col gap-4 px-5 py-4"},Hc={class:"dh-label",for:"web-url"},Vc={class:"dh-label",for:"web-origins"},Kc={class:"mt-1 text-xs text-muted"},Wc={key:0,class:"data text-xs text-muted"},Gc={key:1,class:"rounded-control bg-info-soft px-3 py-2 text-xs text-info"},qc={key:2,class:"rounded-control bg-danger-soft px-3 py-2 text-xs text-danger"},Zc={class:"flex items-center gap-2"},Jc=["disabled"],Yc=["disabled"],Xc={class:"eyebrow"},Qc={__name:"WebAppCard",setup(e){const t=V(null),s=V({url:"",origins:""}),n=V(null),i=V(""),o=V(""),r=V(!1),l=y=>y.split(",").map(A=>A.trim()).filter(Boolean);function a(y){t.value=y,s.value={url:y.url,origins:(y.allowOrigins||[]).join(", ")},n.value=y.probe}async function p(){try{a(await se("/api/admin/webapp-config"))}catch(y){i.value=y.message}}ut(p);async function u(){i.value="",o.value="",r.value=!0;try{n.value=await se("/api/admin/webapp-config/test",{method:"POST",body:{url:s.value.url}}),o.value=n.value.status==="ok"?h("webapp.testOk"):h("webapp.testFailed")}catch(y){i.value=y.message}finally{r.value=!1}}async function m(){i.value="",o.value="",r.value=!0;try{const y=await se("/api/admin/webapp-config",{method:"PUT",body:{url:s.value.url,allowOrigins:l(s.value.origins)}});a(y.config),o.value=y.warning||h("webapp.savedNotice")}catch(y){i.value=y.message}finally{r.value=!1}}return(y,A)=>{var D;return k(),E("div",Dc,[d("div",zc,[d("div",null,[d("div",Lc,b(g(h)("webapp.title")),1),d("p",Fc,b(g(h)("webapp.subtitle")),1)]),n.value?(k(),E("span",{key:0,class:$e(["dh-pill",n.value.status==="ok"?"bg-success-soft text-success":"bg-danger-soft text-danger"])},[A[2]||(A[2]=d("span",{class:"h-1.5 w-1.5 rounded-full bg-current"},null,-1)),Te(" "+b(n.value.status==="ok"?g(h)("webapp.reachable"):g(h)("webapp.unreachable")),1)],2)):B("",!0)]),d("div",Bc,[d("div",null,[d("label",Hc,b(g(h)("webapp.baseUrl")),1),re(d("input",{id:"web-url","onUpdate:modelValue":A[0]||(A[0]=C=>s.value.url=C),class:"dh-input",placeholder:"http://localhost:5173"},null,512),[[pe,s.value.url]])]),d("div",null,[d("label",Vc,b(g(h)("webapp.allowedOrigins")),1),re(d("input",{id:"web-origins","onUpdate:modelValue":A[1]||(A[1]=C=>s.value.origins=C),class:"dh-input",placeholder:"http://localhost:5173, https://app.example.com"},null,512),[[pe,s.value.origins]]),d("p",Kc,[Te(b(g(pi)("webapp.originsHint","star").before),1),A[3]||(A[3]=d("span",{class:"data"},"*",-1)),Te(b(g(pi)("webapp.originsHint","star").after),1)])]),(D=n.value)!=null&&D.error?(k(),E("p",Wc,b(n.value.error),1)):B("",!0),o.value?(k(),E("p",Gc,b(o.value),1)):B("",!0),i.value?(k(),E("p",qc,b(i.value),1)):B("",!0),d("div",Zc,[d("button",{class:"dh-btn",disabled:r.value,onClick:m},b(g(h)("webapp.saveApply")),9,Jc),d("button",{class:"dh-btn-ghost",disabled:r.value,onClick:u},b(g(h)("webapp.testConnection")),9,Yc),A[4]||(A[4]=d("span",{class:"flex-1"},null,-1)),d("span",Xc,b(g(h)("webapp.persistedEnv")),1)])])])}}},eu={class:"dh-card overflow-hidden"},tu={class:"flex items-center justify-between border-b border-subtle px-5 py-4"},su={class:"text-base font-bold tracking-[-0.02em] text-strong"},nu={class:"mt-0.5 text-xs text-muted"},iu={key:0,class:"border-b border-subtle bg-sunken px-5 py-4"},ou={class:"grid gap-3 sm:grid-cols-3"},ru={class:"dh-label"},lu={class:"dh-label"},au={class:"dh-label"},cu=["disabled"],uu={key:1,class:"border-b border-subtle px-5 py-3 text-xs text-danger"},du={key:2,class:"px-5 py-6 text-center text-sm text-muted"},fu={class:"flex items-center gap-3 px-5 py-3"},pu=["onClick"],hu={class:"font-semibold text-strong"},gu={class:"dh-pill bg-sunken text-muted"},mu={key:0,class:"text-xs text-muted"},bu=["disabled","onClick"],vu=["disabled","onClick"],_u={key:0,class:"bg-sunken px-5 py-4"},yu={key:0,class:"data mb-3 text-xs text-muted"},wu={key:1,class:"grid gap-3 sm:grid-cols-2"},xu={class:"dh-label"},ku={key:0,class:"text-danger"},Su=["onUpdate:modelValue"],Tu=["value"],Eu=["onUpdate:modelValue","type","placeholder"],Cu={key:2,class:"mt-1 text-xs text-muted"},Pu={key:2,class:"text-xs text-muted"},$u={key:3,class:"mt-4"},Au={class:"eyebrow mb-1.5"},Ou={class:"data flex flex-col gap-1 text-xs text-muted"},Ru={class:"text-strong"},Iu={key:0},Uu={key:1},ju={key:4,class:"data mt-3 text-xs text-body"},Mu={class:"mt-4 flex items-center gap-2"},Nu=["disabled","onClick"],Du=["disabled","onClick"],zu=["disabled","onClick"],Lu={key:5,class:"eyebrow mt-2"},Fu={__name:"PluginsCard",setup(e){const t=V([]),s=V(""),n=V(!1),i=V(null),o=Jt({}),r=Jt({}),l=V(!1),a=V({name:"",baseURL:"",provider:""});async function p(){try{const P=await se("/api/admin/plugins");t.value=P.plugins||[],s.value=""}catch(P){s.value=P.message}}ut(p);function u(P){var $;if(i.value===P.name){i.value=null;return}const T={};for(const j of P.configFields||[])T[j.key]=(($=P.config)==null?void 0:$[j.key])??"";o[P.name]=T,i.value=P.name}async function m(P,T){n.value=!0,r[P.name]="";try{const $=await se(`/api/admin/plugins/${encodeURIComponent(P.name)}`,{method:"PUT",body:{enabled:T,config:o[P.name]??{}}});r[P.name]=$.warning||h("plugins.saved"),await p()}catch($){r[P.name]=$.message}finally{n.value=!1}}async function y(P){n.value=!0,r[P.name]=h("plugins.checking");try{const T=await se(`/api/admin/plugins/${encodeURIComponent(P.name)}/health`,{method:"POST"});r[P.name]=`${T.health.status}${T.health.detail?" — "+T.health.detail:""}`,await p()}catch(T){r[P.name]=T.message}finally{n.value=!1}}async function A(P){if(confirm(h("plugins.confirmRemove",{name:P.name}))){n.value=!0;try{await se(`/api/admin/plugins/${encodeURIComponent(P.name)}`,{method:"DELETE"}),i.value===P.name&&(i.value=null),await p()}catch(T){r[P.name]=T.message}finally{n.value=!1}}}async function D(){n.value=!0,s.value="";try{await se("/api/admin/plugins",{method:"POST",body:a.value}),a.value={name:"",baseURL:"",provider:""},l.value=!1,await p()}catch(P){s.value=P.message}finally{n.value=!1}}const C=P=>P==="ok"?"bg-success-soft text-success":P==="degraded"?"bg-warning-soft text-warning":"bg-danger-soft text-danger";return(P,T)=>(k(),E("div",eu,[d("div",tu,[d("div",null,[d("div",su,b(g(h)("plugins.title")),1),d("p",nu,b(g(h)("plugins.subtitle")),1)]),d("button",{class:"dh-btn-ghost",onClick:T[0]||(T[0]=$=>l.value=!l.value)},b(l.value?g(h)("plugins.cancelRegister"):g(h)("plugins.registerExternal")),1)]),l.value?(k(),E("div",iu,[d("div",ou,[d("div",null,[d("label",ru,b(g(h)("plugins.name")),1),re(d("input",{"onUpdate:modelValue":T[1]||(T[1]=$=>a.value.name=$),class:"dh-input",placeholder:"acme-parts"},null,512),[[pe,a.value.name]])]),d("div",null,[d("label",lu,b(g(h)("plugins.baseUrl")),1),re(d("input",{"onUpdate:modelValue":T[2]||(T[2]=$=>a.value.baseURL=$),class:"dh-input",placeholder:"http://127.0.0.1:9100"},null,512),[[pe,a.value.baseURL]])]),d("div",null,[d("label",au,b(g(h)("plugins.provider")),1),re(d("input",{"onUpdate:modelValue":T[3]||(T[3]=$=>a.value.provider=$),class:"dh-input",placeholder:"ACME Corp"},null,512),[[pe,a.value.provider]])])]),d("button",{class:"dh-btn mt-3",disabled:n.value||!a.value.name||!a.value.baseURL,onClick:D},b(g(h)("plugins.register")),9,cu)])):B("",!0),s.value?(k(),E("p",uu,b(s.value),1)):B("",!0),t.value.length?B("",!0):(k(),E("p",du,b(g(h)("plugins.empty")),1)),(k(!0),E(ee,null,ke(t.value,$=>(k(),E("div",{key:$.name,class:"border-t border-subtle first:border-t-0"},[d("div",fu,[d("button",{class:"flex flex-1 items-center gap-3 text-left",onClick:j=>u($)},[d("span",hu,b($.name),1),d("span",gu,b($.kind||g(h)("plugins.builtin")),1),$.provider?(k(),E("span",mu,b($.provider),1)):B("",!0),$.health?(k(),E("span",{key:1,class:$e(["dh-pill",C($.health.status)])},[T[4]||(T[4]=d("span",{class:"h-1.5 w-1.5 rounded-full bg-current"},null,-1)),Te(b($.health.status),1)],2)):B("",!0)],8,pu),d("span",{class:$e(["dh-pill",$.enabled?"bg-success-soft text-success":"bg-sunken text-muted"])},b($.enabled?g(h)("plugins.enabled"):g(h)("plugins.disabled")),3),d("button",{class:"dh-btn-ghost",disabled:n.value,onClick:j=>y($)},b(g(h)("plugins.health")),9,bu),d("button",{class:"dh-btn-ghost",disabled:n.value,onClick:j=>u($)},b(i.value===$.name?g(h)("plugins.close"):g(h)("plugins.configure")),9,vu)]),i.value===$.name?(k(),E("div",_u,[$.baseURL?(k(),E("div",yu,b($.baseURL),1)):B("",!0),($.configFields||[]).length?(k(),E("div",wu,[(k(!0),E(ee,null,ke($.configFields,j=>(k(),E("div",{key:j.key},[d("label",xu,[Te(b(j.label||j.key),1),j.required?(k(),E("span",ku," *")):B("",!0)]),j.type==="select"?re((k(),E("select",{key:0,"onUpdate:modelValue":M=>o[$.name][j.key]=M,class:"dh-select"},[(k(!0),E(ee,null,ke(j.options||[],M=>(k(),E("option",{key:M.value,value:M.value},b(M.label||M.value),9,Tu))),128))],8,Su)),[[xs,o[$.name][j.key]]]):re((k(),E("input",{key:1,"onUpdate:modelValue":M=>o[$.name][j.key]=M,class:"dh-input",type:j.type==="password"?"password":j.type==="number"?"number":"text",placeholder:j.default||"",autocomplete:"off"},null,8,Eu)),[[Gl,o[$.name][j.key]]]),j.help?(k(),E("p",Cu,b(j.help),1)):B("",!0)]))),128))])):(k(),E("p",Pu,b(g(h)("plugins.noConfig")),1)),($.capabilities||[]).length?(k(),E("div",$u,[d("div",Au,b(g(h)("plugins.capabilities")),1),d("ul",Ou,[(k(!0),E(ee,null,ke($.capabilities,j=>(k(),E("li",{key:j.id},[d("span",Ru,b(j.id),1),j.method||j.endpoint?(k(),E("span",Iu," — "+b(j.method)+" "+b(j.endpoint),1)):B("",!0),j.description?(k(),E("span",Uu," · "+b(j.description),1)):B("",!0)]))),128))])])):B("",!0),r[$.name]?(k(),E("p",ju,b(r[$.name]),1)):B("",!0),d("div",Mu,[d("button",{class:"dh-btn",disabled:n.value,onClick:j=>m($,!0)},b($.enabled?g(h)("plugins.save"):g(h)("plugins.saveEnable")),9,Nu),$.enabled?(k(),E("button",{key:0,class:"dh-btn-ghost",disabled:n.value,onClick:j=>m($,!1)},b(g(h)("plugins.disable")),9,Du)):B("",!0),T[5]||(T[5]=d("span",{class:"flex-1"},null,-1)),$.kind==="external"?(k(),E("button",{key:1,class:"dh-btn-danger",disabled:n.value,onClick:j=>A($)},b(g(h)("plugins.remove")),9,zu)):B("",!0)]),$.kind!=="external"?(k(),E("p",Lu,b(g(h)("plugins.builtinNote")),1)):B("",!0)])):B("",!0)]))),128))]))}},Bu={class:"dh-card overflow-hidden"},Hu={class:"flex items-center justify-between border-b border-subtle px-5 py-4"},Vu={class:"text-base font-bold tracking-[-0.02em] text-strong"},Ku={class:"mt-0.5 text-xs text-muted"},Wu={key:0,class:"border-b border-subtle px-5 py-3 text-xs text-danger"},Gu={key:1,class:"border-b border-subtle bg-sunken px-5 py-4"},qu={class:"grid gap-3 sm:grid-cols-2"},Zu={class:"dh-label"},Ju={class:"dh-label"},Yu={class:"dh-label"},Xu=["placeholder"],Qu={class:"dh-label"},ed=["value"],td={key:0},sd={class:"dh-label"},nd={value:""},id=["value"],od={class:"mt-3 flex items-center gap-2"},rd=["disabled"],ld=["disabled"],ad={key:2,class:"px-5 py-6 text-center text-sm text-muted"},cd={key:3,class:"w-full text-left text-sm"},ud={class:"[&>th]:eyebrow [&>th]:px-5 [&>th]:py-2.5 [&>th]:font-medium"},dd={class:"data px-5 py-2.5 text-xs text-strong"},fd={key:0,class:"eyebrow ml-1"},pd={class:"px-5 py-2.5 text-body"},hd={class:"px-5 py-2.5 text-body"},gd={class:"px-5 py-2.5"},md={class:"px-5 py-2.5 text-right whitespace-nowrap"},bd=["onClick"],vd=["disabled","onClick"],_d={__name:"UsersCard",setup(e){const t=V([]),s=V([]),n=V(""),i=V(!1),o=V(null),r=V({}),l=Qe(()=>bt.value?["user","admin","superadmin"]:["user","admin"]);async function a(){try{const[C,P]=await Promise.all([se("/api/users"),se("/api/orgs")]);t.value=C.users||[],s.value=P.organizations||[],n.value=""}catch(C){n.value=C.message}}ut(a);function p(){var C;o.value="new",r.value={email:"",name:"",password:"",role:"user",organization:bt.value?"":((C=de.value)==null?void 0:C.organization)||""}}function u(C){o.value=C.id,r.value={email:C.email,name:C.name||"",password:"",role:C.role,organization:C.organization||""}}function m(){o.value=null,n.value=""}async function y(){i.value=!0,n.value="";try{if(o.value==="new")await se("/api/users",{method:"POST",body:r.value});else{const C={...r.value};C.password||delete C.password,await se(`/api/users/${o.value}`,{method:"PATCH",body:C})}o.value=null,await a()}catch(C){n.value=C.message}finally{i.value=!1}}async function A(C){if(confirm(h("users.confirmDelete",{email:C.email}))){i.value=!0,n.value="";try{await se(`/api/users/${C.id}`,{method:"DELETE"}),await a()}catch(P){n.value=P.message}finally{i.value=!1}}}const D=C=>C==="superadmin"?"bg-info-soft text-info":C==="admin"?"bg-warning-soft text-warning":"bg-sunken text-muted";return(C,P)=>(k(),E("div",Bu,[d("div",Hu,[d("div",null,[d("div",Vu,b(g(h)("users.title")),1),d("p",Ku,b(g(bt)?g(h)("users.allOrgs"):g(h)("users.yourOrg")),1)]),d("button",{class:"dh-btn",onClick:p},b(g(h)("users.newUser")),1)]),n.value?(k(),E("p",Wu,b(n.value),1)):B("",!0),o.value?(k(),E("div",Gu,[d("div",qu,[d("div",null,[d("label",Zu,b(g(h)("common.email")),1),re(d("input",{"onUpdate:modelValue":P[0]||(P[0]=T=>r.value.email=T),class:"dh-input",type:"email",autocomplete:"off"},null,512),[[pe,r.value.email]])]),d("div",null,[d("label",Ju,b(g(h)("common.name")),1),re(d("input",{"onUpdate:modelValue":P[1]||(P[1]=T=>r.value.name=T),class:"dh-input",autocomplete:"off"},null,512),[[pe,r.value.name]])]),d("div",null,[d("label",Yu,b(o.value==="new"?g(h)("users.password"):g(h)("users.passwordUnchanged")),1),re(d("input",{"onUpdate:modelValue":P[2]||(P[2]=T=>r.value.password=T),class:"dh-input",type:"password",autocomplete:"new-password",placeholder:g(h)("users.passwordPlaceholder")},null,8,Xu),[[pe,r.value.password]])]),d("div",null,[d("label",Qu,b(g(h)("common.role")),1),re(d("select",{"onUpdate:modelValue":P[3]||(P[3]=T=>r.value.role=T),class:"dh-select"},[(k(!0),E(ee,null,ke(l.value,T=>(k(),E("option",{key:T,value:T},b(T),9,ed))),128))],512),[[xs,r.value.role]])]),g(bt)?(k(),E("div",td,[d("label",sd,b(g(h)("common.organization")),1),re(d("select",{"onUpdate:modelValue":P[4]||(P[4]=T=>r.value.organization=T),class:"dh-select"},[d("option",nd,b(g(h)("users.orgNone")),1),(k(!0),E(ee,null,ke(s.value,T=>(k(),E("option",{key:T.id,value:T.id},b(T.name),9,id))),128))],512),[[xs,r.value.organization]])])):B("",!0)]),d("div",od,[d("button",{class:"dh-btn",disabled:i.value,onClick:y},b(o.value==="new"?g(h)("common.create"):g(h)("common.save")),9,rd),d("button",{class:"dh-btn-ghost",disabled:i.value,onClick:m},b(g(h)("common.cancel")),9,ld)])])):B("",!0),t.value.length?(k(),E("table",cd,[d("thead",null,[d("tr",ud,[d("th",null,b(g(h)("common.email")),1),d("th",null,b(g(h)("common.name")),1),d("th",null,b(g(h)("common.organization")),1),d("th",null,b(g(h)("common.role")),1),P[5]||(P[5]=d("th",null,null,-1))])]),d("tbody",null,[(k(!0),E(ee,null,ke(t.value,T=>{var $,j;return k(),E("tr",{key:T.id,class:"border-t border-subtle transition-colors hover:bg-sunken"},[d("td",dd,[Te(b(T.email)+" ",1),T.id===(($=g(de))==null?void 0:$.id)?(k(),E("span",fd,b(g(h)("users.you")),1)):B("",!0)]),d("td",pd,b(T.name||g(h)("common.empty")),1),d("td",hd,b(T.organizationName||g(h)("common.empty")),1),d("td",gd,[d("span",{class:$e(["dh-pill",D(T.role)])},b(T.role),3)]),d("td",md,[d("button",{class:"dh-btn-ghost",onClick:M=>u(T)},b(g(h)("common.edit")),9,bd),T.id!==((j=g(de))==null?void 0:j.id)?(k(),E("button",{key:0,class:"dh-btn-danger ml-1.5",disabled:i.value,onClick:M=>A(T)},b(g(h)("common.delete")),9,vd)):B("",!0)])])}),128))])])):(k(),E("p",ad,b(g(h)("users.empty")),1))]))}},yd={class:"dh-card overflow-hidden"},wd={class:"flex items-center justify-between border-b border-subtle px-5 py-4"},xd={class:"text-base font-bold tracking-[-0.02em] text-strong"},kd={class:"mt-0.5 text-xs text-muted"},Sd={key:0,class:"border-b border-subtle px-5 py-3 text-xs text-danger"},Td={key:1,class:"border-b border-subtle bg-sunken px-5 py-4"},Ed={class:"dh-label"},Cd=["placeholder"],Pd={class:"mt-3 flex items-center gap-2"},$d=["disabled"],Ad=["disabled"],Od={key:2,class:"px-5 py-6 text-center text-sm text-muted"},Rd={key:3,class:"w-full text-left text-sm"},Id={class:"[&>th]:eyebrow [&>th]:px-5 [&>th]:py-2.5 [&>th]:font-medium"},Ud={class:"px-5 py-2.5 font-medium text-strong"},jd={class:"data px-5 py-2.5 text-xs text-muted"},Md={class:"px-5 py-2.5 text-right whitespace-nowrap"},Nd=["onClick"],Dd=["disabled","onClick"],zd={__name:"OrgsCard",setup(e){const t=V([]),s=V(""),n=V(!1),i=V(null),o=V("");async function r(){try{const y=await se("/api/orgs");t.value=y.organizations||[],s.value=""}catch(y){s.value=y.message}}ut(r);function l(){i.value="new",o.value=""}function a(y){i.value=y.id,o.value=y.name}function p(){i.value=null,s.value=""}async function u(){n.value=!0,s.value="";try{i.value==="new"?await se("/api/orgs",{method:"POST",body:{name:o.value}}):await se(`/api/orgs/${i.value}`,{method:"PATCH",body:{name:o.value}}),i.value=null,await r()}catch(y){s.value=y.message}finally{n.value=!1}}async function m(y){if(confirm(h("orgs.confirmDelete",{name:y.name}))){n.value=!0,s.value="";try{await se(`/api/orgs/${y.id}`,{method:"DELETE"}),await r()}catch(A){s.value=A.message}finally{n.value=!1}}}return(y,A)=>(k(),E("div",yd,[d("div",wd,[d("div",null,[d("div",xd,b(g(h)("orgs.title")),1),d("p",kd,b(g(h)("orgs.subtitle")),1)]),g(bt)?(k(),E("button",{key:0,class:"dh-btn",onClick:l},b(g(h)("orgs.newOrg")),1)):B("",!0)]),s.value?(k(),E("p",Sd,b(s.value),1)):B("",!0),i.value?(k(),E("div",Td,[d("label",Ed,b(g(h)("common.name")),1),re(d("input",{"onUpdate:modelValue":A[0]||(A[0]=D=>o.value=D),class:"dh-input",placeholder:g(h)("orgs.namePlaceholder"),onKeyup:Ql(u,["enter"])},null,40,Cd),[[pe,o.value]]),d("div",Pd,[d("button",{class:"dh-btn",disabled:n.value||!o.value.trim(),onClick:u},b(i.value==="new"?g(h)("common.create"):g(h)("common.save")),9,$d),d("button",{class:"dh-btn-ghost",disabled:n.value,onClick:p},b(g(h)("common.cancel")),9,Ad)])])):B("",!0),t.value.length?(k(),E("table",Rd,[d("thead",null,[d("tr",Id,[d("th",null,b(g(h)("common.name")),1),d("th",null,b(g(h)("orgs.colId")),1),A[1]||(A[1]=d("th",null,null,-1))])]),d("tbody",null,[(k(!0),E(ee,null,ke(t.value,D=>(k(),E("tr",{key:D.id,class:"border-t border-subtle transition-colors hover:bg-sunken"},[d("td",Ud,b(D.name),1),d("td",jd,b(D.id),1),d("td",Md,[g(bt)?(k(),E(ee,{key:0},[d("button",{class:"dh-btn-ghost",onClick:C=>a(D)},b(g(h)("common.rename")),9,Nd),d("button",{class:"dh-btn-danger ml-1.5",disabled:n.value,onClick:C=>m(D)},b(g(h)("common.delete")),9,Dd)],64)):B("",!0)])]))),128))])])):(k(),E("p",Od,b(g(h)("orgs.empty")),1))]))}},Ld={class:"dh-card overflow-hidden"},Fd={class:"flex items-center justify-between border-b border-subtle px-5 py-4"},Bd={class:"text-base font-bold tracking-[-0.02em] text-strong"},Hd={class:"eyebrow"},Vd={class:"w-full text-left text-sm"},Kd={class:"[&>th]:eyebrow [&>th]:px-5 [&>th]:py-2.5 [&>th]:font-medium"},Wd={class:"data border-t border-subtle px-5 py-2.5 text-xs whitespace-nowrap"},Gd={class:"text-strong"},qd={class:"border-t border-subtle px-5 py-2.5 text-body"},nt={__name:"EndpointTable",props:{title:String,auth:String,endpoints:Array},setup(e){const t={GET:"text-success",POST:"text-brandtext",PATCH:"text-warning",DELETE:"text-danger"};return(s,n)=>(k(),E("div",Ld,[d("div",Fd,[d("div",Bd,b(e.title),1),d("span",Hd,b(e.auth),1)]),d("table",Vd,[d("thead",null,[d("tr",Kd,[d("th",null,b(g(h)("api.colEndpoint")),1),d("th",null,b(g(h)("api.colDescription")),1)])]),d("tbody",null,[(k(!0),E(ee,null,ke(e.endpoints,i=>(k(),E("tr",{key:i.method+i.path,class:"transition-colors hover:bg-sunken"},[d("td",Wd,[d("span",{class:$e(["font-semibold",t[i.method]])},b(i.method),3),d("span",Gd,b(i.path),1)]),d("td",qd,b(i.desc),1)]))),128))])])]))}},Zd={class:"mx-auto flex max-w-4xl flex-col gap-6 px-6 pt-12 pb-16"},Jd={class:"flex items-center gap-3"},Yd={class:"inline-flex items-center gap-2.5 select-none"},Xd={class:"h-8 w-8 shrink-0",viewBox:"0 0 48 48",fill:"none","aria-hidden":"true"},Qd={transform:"translate(7 0) skewX(-13)"},ef=["fill"],tf=["fill"],sf=["fill"],nf={class:"eyebrow mt-1.5"},of={key:0,class:"data hidden text-xs text-muted sm:inline"},rf={key:0},lf={key:1,class:"dh-pill bg-info-soft text-info"},af=["value","aria-label"],cf=["value"],uf=["title"],df={key:0,xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"1.75",class:"h-4 w-4"},ff={key:1,xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"1.75",class:"h-4 w-4"},pf={key:0,class:"eyebrow py-16 text-center"},hf={class:"flex flex-wrap gap-1.5"},gf=["onClick"],mf={key:7,class:"eyebrow text-center"},bf={class:"eyebrow text-center"},vf={__name:"App",setup(e){const t=V(!0),s=V("overview");ut(async()=>{await Ya(),t.value=!1});const n=Qe(()=>{const C=[{id:"overview",label:h("sections.overview")}];return hi.value&&C.push({id:"users",label:h("sections.users")},{id:"orgs",label:h("sections.orgs")}),bt.value&&C.push({id:"pocketbase",label:h("sections.pocketbase")},{id:"webapp",label:h("sections.webapp")},{id:"plugins",label:h("sections.plugins")}),C.push({id:"api",label:h("sections.api")}),C}),i=Qe(()=>Ba.map(C=>{let P=C;try{P=new Intl.DisplayNames([C],{type:"language"}).of(C)||C}catch{}return{code:C,label:P.charAt(0).toUpperCase()+P.slice(1)}}));function o(){Tn(),s.value="overview"}const r=Qe(()=>Et.value==="dark"?["#60a5fa","#93c5fd","#ffffff"]:["var(--brand-700)","var(--brand-500)","var(--brand-400)"]),l=[{method:"GET",path:"/api/health",desc:"Liveness probe (no auth)"},{method:"GET",path:"/api/status",desc:"Health of PocketBase + Web App"},{method:"POST",path:"/api/auth/login",desc:"Exchange email + password for a PocketBase token"},{method:"GET",path:"/api/auth/validate",desc:"Check whether a token is still valid"}],a=[{method:"GET",path:"/api/auth/me",desc:"Identity of the bearer token"},{method:"GET",path:"/api/identity",desc:"Identity incl. role + organization"}],p=[{method:"GET",path:"/api/cars",desc:"List owned + shared cars"},{method:"POST",path:"/api/cars",desc:"Create a car"},{method:"GET",path:"/api/cars/{id}",desc:"Fetch one car"},{method:"PATCH",path:"/api/cars/{id}",desc:"Update a car"},{method:"DELETE",path:"/api/cars/{id}",desc:"Delete a car (owner only)"},{method:"GET",path:"/api/cars/{id}/service-records",desc:"A car's service history"},{method:"GET",path:"/api/cars/{id}/parts",desc:"A car's parts catalog"},{method:"GET",path:"/api/cars/{id}/shares",desc:"Who a car is shared with (owner)"},{method:"POST",path:"/api/cars/{id}/shares",desc:"Share a car by email (owner)"},{method:"DELETE",path:"/api/cars/{id}/shares/{userId}",desc:"Revoke a share (owner)"}],u=[{method:"GET",path:"/api/service-records",desc:"List service records"},{method:"POST",path:"/api/service-records",desc:"Log a service record"},{method:"GET",path:"/api/service-records/{id}",desc:"Fetch one record"},{method:"PATCH",path:"/api/service-records/{id}",desc:"Update a record"},{method:"DELETE",path:"/api/service-records/{id}",desc:"Delete a record"}],m=[{method:"GET",path:"/api/parts",desc:"List parts"},{method:"POST",path:"/api/parts",desc:"Add a part"},{method:"GET",path:"/api/parts/{id}",desc:"Fetch one part"},{method:"PATCH",path:"/api/parts/{id}",desc:"Update a part"},{method:"DELETE",path:"/api/parts/{id}",desc:"Delete a part"}],y=[{method:"GET",path:"/api/me",desc:"Current user profile"},{method:"PATCH",path:"/api/me",desc:"Update profile"},{method:"POST",path:"/api/me/password",desc:"Change password"},{method:"POST",path:"/api/me/avatar",desc:"Upload avatar"},{method:"GET",path:"/api/me/avatar",desc:"Fetch avatar"},{method:"DELETE",path:"/api/me/avatar",desc:"Remove avatar"},{method:"POST",path:"/api/me/verify/request",desc:"Request email verification"},{method:"GET",path:"/api/me/export",desc:"Export your data"},{method:"POST",path:"/api/me/import",desc:"Import data"},{method:"POST",path:"/api/me/delete",desc:"Request account deletion"},{method:"POST",path:"/api/me/delete/cancel",desc:"Cancel deletion request"},{method:"DELETE",path:"/api/me",desc:"Finalize account deletion"}],A=[{method:"GET",path:"/api/users",desc:"List users (scoped by role)"},{method:"POST",path:"/api/users",desc:"Create a user"},{method:"PATCH",path:"/api/users/{id}",desc:"Update email / name / role / org / password"},{method:"DELETE",path:"/api/users/{id}",desc:"Delete a user"},{method:"GET",path:"/api/orgs",desc:"List organizations"},{method:"POST",path:"/api/orgs",desc:"Create an organization (superadmin)"},{method:"PATCH",path:"/api/orgs/{id}",desc:"Rename an organization (superadmin)"},{method:"DELETE",path:"/api/orgs/{id}",desc:"Delete an organization (superadmin)"}],D=[{method:"GET",path:"/api/admin/pb-config",desc:"PocketBase connection + live probe"},{method:"POST",path:"/api/admin/pb-config/test",desc:"Probe a candidate connection"},{method:"PUT",path:"/api/admin/pb-config",desc:"Apply + persist a connection"},{method:"GET",path:"/api/admin/webapp-config",desc:"Web App URL + CORS origins, with a live probe"},{method:"POST",path:"/api/admin/webapp-config/test",desc:"Probe a candidate Web App address"},{method:"PUT",path:"/api/admin/webapp-config",desc:"Apply + persist Web App settings"},{method:"GET",path:"/api/admin/plugins",desc:"List plugins (secrets masked)"},{method:"POST",path:"/api/admin/plugins",desc:"Register an external plugin"},{method:"GET",path:"/api/admin/plugins/{name}",desc:"Fetch one plugin"},{method:"PUT",path:"/api/admin/plugins/{name}",desc:"Enable/disable + configure"},{method:"DELETE",path:"/api/admin/plugins/{name}",desc:"Remove an external plugin"},{method:"POST",path:"/api/admin/plugins/{name}/health",desc:"Run a health check now"}];return(C,P)=>(k(),E("div",Zd,[d("div",Jd,[d("span",Yd,[(k(),E("svg",Xd,[d("g",Qd,[d("rect",{x:"9",y:"16",width:"6",height:"16",rx:"3",fill:r.value[0]},null,8,ef),d("rect",{x:"19",y:"12",width:"6",height:"24",rx:"3",fill:r.value[1]},null,8,tf),d("rect",{x:"29",y:"8",width:"6",height:"32",rx:"3",fill:r.value[2]},null,8,sf)])])),P[2]||(P[2]=d("span",{class:"text-2xl leading-none font-extrabold tracking-[-0.03em] italic"},[d("span",{class:"text-strong"},"Driver"),d("span",{class:"text-brandtext"},"Vault")],-1))]),d("span",nf,b(g(h)("app.apiServer")),1),P[5]||(P[5]=d("div",{class:"flex-1"},null,-1)),g(de)?(k(),E("span",of,[Te(b(g(de).email),1),g(de).organizationName?(k(),E("span",rf," · "+b(g(de).organizationName),1)):B("",!0)])):B("",!0),g(de)?(k(),E("span",lf,b(g(de).role),1)):B("",!0),d("select",{class:"dh-select !w-auto !py-1 !text-xs",value:g(Sn),"aria-label":g(h)("sections.overview"),onChange:P[0]||(P[0]=T=>g(Va)(T.target.value))},[(k(!0),E(ee,null,ke(i.value,T=>(k(),E("option",{key:T.code,value:T.code},b(T.label),9,cf))),128))],40,af),d("button",{class:"dh-btn-ghost",title:g(Et)==="dark"?g(h)("app.switchToLight"):g(h)("app.switchToDark"),onClick:P[1]||(P[1]=(...T)=>g(di)&&g(di)(...T))},[g(Et)==="dark"?(k(),E("svg",df,[...P[3]||(P[3]=[d("circle",{cx:"12",cy:"12",r:"4"},null,-1),d("path",{"stroke-linecap":"round",d:"M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"},null,-1)])])):(k(),E("svg",ff,[...P[4]||(P[4]=[d("path",{"stroke-linecap":"round","stroke-linejoin":"round",d:"M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8Z"},null,-1)])])),Te(" "+b(g(h)("app.theme")),1)],8,uf),g(Ot)?(k(),E("button",{key:2,class:"dh-btn-ghost",onClick:o},b(g(h)("app.signOut")),1)):B("",!0)]),t.value?(k(),E("p",pf,b(g(h)("app.loading")),1)):g(Ot)?(k(),E(ee,{key:2},[d("nav",hf,[(k(!0),E(ee,null,ke(n.value,T=>(k(),E("button",{key:T.id,class:$e(["dh-btn-ghost",s.value===T.id?"border-accent text-brandtext":""]),onClick:$=>s.value=T.id},b(T.label),11,gf))),128))]),s.value==="overview"?(k(),it(_c,{key:0})):s.value==="users"?(k(),it(_d,{key:1})):s.value==="orgs"?(k(),it(zd,{key:2})):s.value==="pocketbase"?(k(),it(Nc,{key:3})):s.value==="webapp"?(k(),it(Qc,{key:4})):s.value==="plugins"?(k(),it(Fu,{key:5})):s.value==="api"?(k(),E(ee,{key:6},[ce(nt,{title:g(h)("api.groupPublic"),auth:g(h)("api.authNone"),endpoints:l},null,8,["title","auth"]),ce(nt,{title:g(h)("api.groupIdentity"),auth:g(h)("api.authBearer"),endpoints:a},null,8,["title","auth"]),ce(nt,{title:g(h)("api.groupCars"),auth:g(h)("api.authBearer"),endpoints:p},null,8,["title","auth"]),ce(nt,{title:g(h)("api.groupService"),auth:g(h)("api.authBearer"),endpoints:u},null,8,["title","auth"]),ce(nt,{title:g(h)("api.groupParts"),auth:g(h)("api.authBearer"),endpoints:m},null,8,["title","auth"]),ce(nt,{title:g(h)("api.groupAccount"),auth:g(h)("api.authBearer"),endpoints:y},null,8,["title","auth"]),ce(nt,{title:g(h)("api.groupManagement"),auth:g(h)("api.authManager"),endpoints:A},null,8,["title","auth"]),ce(nt,{title:g(h)("api.groupSuperadmin"),auth:g(h)("api.authSuperadmin"),endpoints:D},null,8,["title","auth"])],64)):B("",!0),!g(hi)&&s.value==="overview"?(k(),E("p",mf,b(g(h)("app.standardUserNote")),1)):B("",!0)],64)):(k(),it(lc,{key:1})),d("p",bf,b(g(h)("app.footer")),1)]))}};sa(vf).mount("#app"); +**/let on;const Jn=typeof window<"u"&&window.trustedTypes;if(Jn)try{on=Jn.createPolicy("vue",{createHTML:e=>e})}catch{}const _o=on?e=>on.createHTML(e):e=>e,xl="http://www.w3.org/2000/svg",kl="http://www.w3.org/1998/Math/MathML",qe=typeof document<"u"?document:null,Yn=qe&&qe.createElement("template"),Sl={insert:(e,t,s)=>{t.insertBefore(e,s||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,s,n)=>{const i=t==="svg"?qe.createElementNS(xl,e):t==="mathml"?qe.createElementNS(kl,e):s?qe.createElement(e,{is:s}):qe.createElement(e);return e==="select"&&n&&n.multiple!=null&&i.setAttribute("multiple",n.multiple),i},createText:e=>qe.createTextNode(e),createComment:e=>qe.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>qe.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,s,n,i,o){const r=s?s.previousSibling:t.lastChild;if(i&&(i===o||i.nextSibling))for(;t.insertBefore(i.cloneNode(!0),s),!(i===o||!(i=i.nextSibling)););else{Yn.innerHTML=_o(n==="svg"?`${e}`:n==="mathml"?`${e}`:e);const l=Yn.content;if(n==="svg"||n==="mathml"){const a=l.firstChild;for(;a.firstChild;)l.appendChild(a.firstChild);l.removeChild(a)}t.insertBefore(l,s)}return[r?r.nextSibling:t.firstChild,s?s.previousSibling:t.lastChild]}},Tl=Symbol("_vtc");function El(e,t,s){const n=e[Tl];n&&(t=(t?[t,...n]:[...n]).join(" ")),t==null?e.removeAttribute("class"):s?e.setAttribute("class",t):e.className=t}const Xn=Symbol("_vod"),Cl=Symbol("_vsh"),Pl=Symbol(""),$l=/(?:^|;)\s*display\s*:/;function Al(e,t,s){const n=e.style,i=ne(s);let o=!1;if(s&&!i){if(t)if(ne(t))for(const r of t.split(";")){const l=r.slice(0,r.indexOf(":")).trim();s[l]==null&&Lt(n,l,"")}else for(const r in t)s[r]==null&&Lt(n,r,"");for(const r in s){r==="display"&&(o=!0);const l=s[r];l!=null?Rl(e,r,!ne(t)&&t?t[r]:void 0,l)||Lt(n,r,l):Lt(n,r,"")}}else if(i){if(t!==s){const r=n[Pl];r&&(s+=";"+r),n.cssText=s,o=$l.test(s)}}else t&&e.removeAttribute("style");Xn in e&&(e[Xn]=o?n.display:"",e[Cl]&&(n.display="none"))}const Qn=/\s*!important$/;function Lt(e,t,s){if(L(s))s.forEach(n=>Lt(e,t,n));else if(s==null&&(s=""),t.startsWith("--"))e.setProperty(t,s);else{const n=Ol(e,t);Qn.test(s)?e.setProperty(ct(n),s.replace(Qn,""),"important"):e[n]=s}}const ei=["Webkit","Moz","ms"],Ws={};function Ol(e,t){const s=Ws[t];if(s)return s;let n=Ce(t);if(n!=="filter"&&n in e)return Ws[t]=n;n=_i(n);for(let i=0;iGs||(Dl.then(()=>Gs=0),Gs=Date.now());function Ll(e,t){const s=n=>{if(!n._vts)n._vts=Date.now();else if(n._vts<=s.attached)return;const i=s.value;if(L(i)){const o=n.stopImmediatePropagation;n.stopImmediatePropagation=()=>{o.call(n),n._stopped=!0};const r=i.slice(),l=[n];for(let a=0;ae.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,Fl=(e,t,s,n,i,o)=>{const r=i==="svg";t==="class"?El(e,n,r):t==="style"?Al(e,s,n):ks(t)?Ss(t)||Ul(e,t,s,n,o):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):Bl(e,t,n,r))?(ni(e,t,n),!e.tagName.includes("-")&&(t==="value"||t==="checked"||t==="selected")&&si(e,t,n,r,o,t!=="value")):e._isVueCE&&(Hl(e,t)||e._def.__asyncLoader&&(/[A-Z]/.test(t)||!ne(n)))?ni(e,Ce(t),n,o,t):(t==="true-value"?e._trueValue=n:t==="false-value"&&(e._falseValue=n),si(e,t,n,r))};function Bl(e,t,s,n){if(n)return!!(t==="innerHTML"||t==="textContent"||t in e&&oi(t)&&H(s));if(t==="spellcheck"||t==="draggable"||t==="translate"||t==="autocorrect"||t==="sandbox"&&e.tagName==="IFRAME"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA")return!1;if(t==="width"||t==="height"){const i=e.tagName;if(i==="IMG"||i==="VIDEO"||i==="CANVAS"||i==="SOURCE")return!1}return oi(t)&&ne(s)?!1:t in e}function Hl(e,t){const s=e._def.props;if(!s)return!1;const n=Ce(t);return Array.isArray(s)?s.some(i=>Ce(i)===n):Object.keys(s).some(i=>Ce(i)===n)}const at=e=>{const t=e.props["onUpdate:modelValue"]||!1;return L(t)?s=>fs(t,s):t};function Vl(e){e.target.composing=!0}function ri(e){const t=e.target;t.composing&&(t.composing=!1,t.dispatchEvent(new Event("input")))}const Ee=Symbol("_assign");function li(e,t,s){return t&&(e=e.trim()),s&&(e=Es(e)),e}const pe={created(e,{modifiers:{lazy:t,trim:s,number:n}},i){e[Ee]=at(i);const o=n||i.props&&i.props.type==="number";Xe(e,t?"change":"input",r=>{r.target.composing||e[Ee](li(e.value,s,o))}),(s||o)&&Xe(e,"change",()=>{e.value=li(e.value,s,o)}),t||(Xe(e,"compositionstart",Vl),Xe(e,"compositionend",ri),Xe(e,"change",ri))},mounted(e,{value:t}){e.value=t??""},beforeUpdate(e,{value:t,oldValue:s,modifiers:{lazy:n,trim:i,number:o}},r){if(e[Ee]=at(r),e.composing)return;const l=(o||e.type==="number")&&!/^0\d/.test(e.value)?Es(e.value):e.value,a=t??"";if(l===a)return;const p=e.getRootNode();(p instanceof Document||p instanceof ShadowRoot)&&p.activeElement===e&&e.type!=="range"&&(n&&t===s||i&&e.value.trim()===a)||(e.value=a)}},Kl={deep:!0,created(e,t,s){e[Ee]=at(s),Xe(e,"change",()=>{const n=e._modelValue,i=$t(e),o=e.checked,r=e[Ee];if(L(n)){const l=dn(n,i),a=l!==-1;if(o&&!a)r(n.concat(i));else if(!o&&a){const p=[...n];p.splice(l,1),r(p)}}else if(Rt(n)){const l=new Set(n);o?l.add(i):l.delete(i),r(l)}else r(yo(e,o))})},mounted:ai,beforeUpdate(e,t,s){e[Ee]=at(s),ai(e,t,s)}};function ai(e,{value:t,oldValue:s},n){e._modelValue=t;let i;if(L(t))i=dn(t,n.props.value)>-1;else if(Rt(t))i=t.has(n.props.value);else{if(t===s)return;i=rt(t,yo(e,!0))}e.checked!==i&&(e.checked=i)}const Wl={created(e,{value:t},s){e.checked=rt(t,s.props.value),e[Ee]=at(s),Xe(e,"change",()=>{e[Ee]($t(e))})},beforeUpdate(e,{value:t,oldValue:s},n){e[Ee]=at(n),t!==s&&(e.checked=rt(t,n.props.value))}},xs={deep:!0,created(e,{value:t,modifiers:{number:s}},n){const i=Rt(t);Xe(e,"change",()=>{const o=Array.prototype.filter.call(e.options,r=>r.selected).map(r=>s?Es($t(r)):$t(r));e[Ee](e.multiple?i?new Set(o):o:o[0]),e._assigning=!0,Li(()=>{e._assigning=!1})}),e[Ee]=at(n)},mounted(e,{value:t}){ci(e,t)},beforeUpdate(e,t,s){e[Ee]=at(s)},updated(e,{value:t}){e._assigning||ci(e,t)}};function ci(e,t){const s=e.multiple,n=L(t);if(!(s&&!n&&!Rt(t))){for(let i=0,o=e.options.length;iString(p)===String(l)):r.selected=dn(t,l)>-1}else r.selected=t.has(l);else if(rt($t(r),t)){e.selectedIndex!==i&&(e.selectedIndex=i);return}}!s&&e.selectedIndex!==-1&&(e.selectedIndex=-1)}}function $t(e){return"_value"in e?e._value:e.value}function yo(e,t){const s=t?"_trueValue":"_falseValue";return s in e?e[s]:t}const Gl={created(e,t,s){ds(e,t,s,null,"created")},mounted(e,t,s){ds(e,t,s,null,"mounted")},beforeUpdate(e,t,s,n){ds(e,t,s,n,"beforeUpdate")},updated(e,t,s,n){ds(e,t,s,n,"updated")}};function ql(e,t){switch(e){case"SELECT":return xs;case"TEXTAREA":return pe;default:switch(t){case"checkbox":return Kl;case"radio":return Wl;default:return pe}}}function ds(e,t,s,n,i){const r=ql(e.tagName,s.props&&s.props.type)[i];r&&r(e,t,s,n)}const Zl=["ctrl","shift","alt","meta"],Jl={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&e.button!==0,middle:e=>"button"in e&&e.button!==1,right:e=>"button"in e&&e.button!==2,exact:(e,t)=>Zl.some(s=>e[`${s}Key`]&&!t.includes(s))},Yl=(e,t)=>{if(!e)return e;const s=e._withMods||(e._withMods={}),n=t.join(".");return s[n]||(s[n]=((i,...o)=>{for(let r=0;r{const s=e._withKeys||(e._withKeys={}),n=t.join(".");return s[n]||(s[n]=(i=>{if(!("key"in i))return;const o=ct(i.key);if(t.some(r=>r===o||Xl[r]===o))return e(i)}))},ea=he({patchProp:Fl},Sl);let ui;function ta(){return ui||(ui=nl(ea))}const sa=((...e)=>{const t=ta().createApp(...e),{mount:s}=t;return t.mount=n=>{const i=ia(n);if(!i)return;const o=t._component;!H(o)&&!o.render&&!o.template&&(o.template=i.innerHTML),i.nodeType===1&&(i.textContent="");const r=s(i,!1,na(i));return i instanceof Element&&(i.removeAttribute("v-cloak"),i.setAttribute("data-v-app","")),r},t});function na(e){if(e instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&e instanceof MathMLElement)return"mathml"}function ia(e){return ne(e)?document.querySelector(e):e}const wo="dh-panel-theme";function oa(){var e;try{const t=localStorage.getItem(wo);if(t==="dark"||t==="light")return t}catch{}return(e=window.matchMedia)!=null&&e.call(window,"(prefers-color-scheme: dark)").matches?"dark":"light"}const Et=V(oa());function xo(e){Et.value=e,document.documentElement.classList.toggle("dark",e==="dark");try{localStorage.setItem(wo,e)}catch{}}function di(){xo(Et.value==="dark"?"light":"dark")}xo(Et.value);const ra={apiServer:"API server",switchToLight:"Switch to light theme",switchToDark:"Switch to dark theme",theme:"Theme",signOut:"Sign out",loading:"Loading…",standardUserNote:"Signed in as a standard user — management sections need an admin role",footer:"DriverVault — car maintenance & service tracker."},la={overview:"Overview",users:"Users",orgs:"Organizations",pocketbase:"PocketBase",webapp:"Web App",plugins:"Plugins",api:"API"},aa={title:"Sign in",subtitle:"Superadmin console for the DriverVault API Server.",email:"Email",password:"Password",submit:"Sign in",submitting:"Signing in…",invalid:"Invalid email or password.",failed:"Could not sign in.",authNote:"Authenticates against PocketBase through this server"},ca={title:"Status",unreachable:"unreachable",checking:"Checking…",apiServer:"API Server",pocketBase:"PocketBase",webApp:"Web App",thisProcess:"this process"},ua={create:"Create",save:"Save",cancel:"Cancel",edit:"Edit",delete:"Delete",rename:"Rename",empty:"—",name:"Name",email:"Email",role:"Role",organization:"Organization"},da={title:"Users",allOrgs:"All organizations",yourOrg:"Your organization",newUser:"New user",password:"Password",passwordUnchanged:"Password (blank = unchanged)",passwordPlaceholder:"min 8 characters",orgNone:"— none —",you:"you",empty:"No users.",confirmDelete:"Delete {email}? This cannot be undone."},fa={title:"Organizations",subtitle:"Tenants users belong to",newOrg:"New organization",namePlaceholder:"Acme Fleet",empty:"No organizations yet.",colId:"ID",confirmDelete:'Delete the organization "{name}"?'},pa={title:"PocketBase",subtitle:"Database connection used by every endpoint",connected:"connected",noSuperuser:"no superuser",unreachable:"unreachable",baseUrl:"Base URL",superuserEmail:"Superuser email",superuserPassword:"Superuser password",passwordUnchanged:"unchanged",passwordNotSet:"not set",saveApply:"Save & apply",testConnection:"Test connection",persistedEnv:"persisted to .env",testOk:"Connection OK — superuser authenticated.",testReachableNoAuth:"PocketBase is reachable, but the service account did not authenticate.",testUnreachable:"PocketBase is not reachable at that address.",savedNotice:"Saved. The server is now using this PocketBase."},ha={title:"Web App",subtitle:"Address probed by the status check, and who may call this API from a browser",reachable:"reachable",unreachable:"unreachable",baseUrl:"Base URL",allowedOrigins:"Allowed origins",originsHint:"Comma separated, or {star} for any. Native mobile apps are not subject to CORS.",saveApply:"Save & apply",testConnection:"Test connection",persistedEnv:"persisted to .env",testOk:"Web App is reachable.",testFailed:"Web App did not answer its health check at that address.",savedNotice:"Saved. New origins apply to the next request."},ga={title:"Plugins",subtitle:"Third-party service integrations",registerExternal:"Register external",cancelRegister:"Cancel",name:"Name",baseUrl:"Base URL",provider:"Provider",register:"Register",empty:"No plugins yet. Register an external one above, or compile a built-in connector.",builtin:"builtin",enabled:"enabled",disabled:"disabled",health:"Health",configure:"Configure",close:"Close",noConfig:"This plugin takes no configuration.",capabilities:"Capabilities",saved:"Saved.",checking:"Checking…",save:"Save",saveEnable:"Save & enable",disable:"Disable",remove:"Remove",builtinNote:"Built-in plugins can be disabled but not removed",confirmRemove:'Remove the external plugin "{name}"? Its saved config is deleted.'},ma={colEndpoint:"Endpoint",colDescription:"Description",authNone:"No auth",authBearer:"Bearer token",authManager:"Admin / superadmin",authSuperadmin:"Superadmin",groupPublic:"Public",groupIdentity:"Identity",groupCars:"Cars",groupService:"Service records",groupParts:"Parts",groupAccount:"Account",groupManagement:"Management",groupSuperadmin:"Superadmin"},ba={app:ra,sections:la,login:aa,status:ca,common:ua,users:da,orgs:fa,pocketbase:pa,webapp:ha,plugins:ga,api:ma},va={apiServer:"Serwer API",switchToLight:"Przełącz na motyw jasny",switchToDark:"Przełącz na motyw ciemny",theme:"Motyw",signOut:"Wyloguj się",loading:"Ładowanie…",standardUserNote:"Zalogowano jako zwykły użytkownik — sekcje zarządzania wymagają roli administratora",footer:"DriverVault — rejestr serwisu i konserwacji samochodu."},_a={overview:"Przegląd",users:"Użytkownicy",orgs:"Organizacje",pocketbase:"PocketBase",webapp:"Aplikacja webowa",plugins:"Wtyczki",api:"API"},ya={title:"Zaloguj się",subtitle:"Konsola superadministratora serwera API DriverVault.",email:"E-mail",password:"Hasło",submit:"Zaloguj się",submitting:"Logowanie…",invalid:"Nieprawidłowy e-mail lub hasło.",failed:"Nie udało się zalogować.",authNote:"Uwierzytelnia w PocketBase za pośrednictwem tego serwera"},wa={title:"Status",unreachable:"niedostępny",checking:"Sprawdzanie…",apiServer:"Serwer API",pocketBase:"PocketBase",webApp:"Aplikacja webowa",thisProcess:"ten proces"},xa={create:"Utwórz",save:"Zapisz",cancel:"Anuluj",edit:"Edytuj",delete:"Usuń",rename:"Zmień nazwę",empty:"—",name:"Nazwa",email:"E-mail",role:"Rola",organization:"Organizacja"},ka={title:"Użytkownicy",allOrgs:"Wszystkie organizacje",yourOrg:"Twoja organizacja",newUser:"Nowy użytkownik",password:"Hasło",passwordUnchanged:"Hasło (puste = bez zmian)",passwordPlaceholder:"min. 8 znaków",orgNone:"— brak —",you:"Ty",empty:"Brak użytkowników.",confirmDelete:"Usunąć {email}? Tej operacji nie można cofnąć."},Sa={title:"Organizacje",subtitle:"Podmioty, do których należą użytkownicy",newOrg:"Nowa organizacja",namePlaceholder:"Acme Fleet",empty:"Brak organizacji.",colId:"ID",confirmDelete:"Usunąć organizację „{name}”?"},Ta={title:"PocketBase",subtitle:"Połączenie z bazą danych używane przez każdy punkt końcowy",connected:"połączono",noSuperuser:"brak superużytkownika",unreachable:"niedostępny",baseUrl:"Adres bazowy",superuserEmail:"E-mail superużytkownika",superuserPassword:"Hasło superużytkownika",passwordUnchanged:"bez zmian",passwordNotSet:"nie ustawiono",saveApply:"Zapisz i zastosuj",testConnection:"Testuj połączenie",persistedEnv:"zapisano w .env",testOk:"Połączenie OK — superużytkownik uwierzytelniony.",testReachableNoAuth:"PocketBase jest dostępny, ale konto usługowe nie zostało uwierzytelnione.",testUnreachable:"PocketBase jest niedostępny pod tym adresem.",savedNotice:"Zapisano. Serwer korzysta teraz z tego PocketBase."},Ea={title:"Aplikacja webowa",subtitle:"Adres sprawdzany podczas kontroli statusu oraz kto może wywoływać to API z przeglądarki",reachable:"dostępna",unreachable:"niedostępna",baseUrl:"Adres bazowy",allowedOrigins:"Dozwolone źródła",originsHint:"Oddzielone przecinkami lub {star} dla dowolnego. Natywne aplikacje mobilne nie podlegają CORS.",saveApply:"Zapisz i zastosuj",testConnection:"Testuj połączenie",persistedEnv:"zapisano w .env",testOk:"Aplikacja webowa jest dostępna.",testFailed:"Aplikacja webowa nie odpowiedziała na kontrolę stanu pod tym adresem.",savedNotice:"Zapisano. Nowe źródła obowiązują od następnego żądania."},Ca={title:"Wtyczki",subtitle:"Integracje z usługami zewnętrznymi",registerExternal:"Zarejestruj zewnętrzną",cancelRegister:"Anuluj",name:"Nazwa",baseUrl:"Adres bazowy",provider:"Dostawca",register:"Zarejestruj",empty:"Brak wtyczek. Zarejestruj zewnętrzną powyżej lub skompiluj wbudowany łącznik.",builtin:"wbudowana",enabled:"włączona",disabled:"wyłączona",health:"Stan",configure:"Konfiguruj",close:"Zamknij",noConfig:"Ta wtyczka nie wymaga konfiguracji.",capabilities:"Możliwości",saved:"Zapisano.",checking:"Sprawdzanie…",save:"Zapisz",saveEnable:"Zapisz i włącz",disable:"Wyłącz",remove:"Usuń",builtinNote:"Wtyczki wbudowane można wyłączyć, ale nie usunąć",confirmRemove:"Usunąć zewnętrzną wtyczkę „{name}”? Jej zapisana konfiguracja zostanie usunięta."},Pa={colEndpoint:"Punkt końcowy",colDescription:"Opis",authNone:"Bez uwierzytelniania",authBearer:"Token Bearer",authManager:"Administrator / superadministrator",authSuperadmin:"Superadministrator",groupPublic:"Publiczne",groupIdentity:"Tożsamość",groupCars:"Samochody",groupService:"Wpisy serwisowe",groupParts:"Części",groupAccount:"Konto",groupManagement:"Zarządzanie",groupSuperadmin:"Superadministrator"},$a={app:va,sections:_a,login:ya,status:wa,common:xa,users:ka,orgs:Sa,pocketbase:Ta,webapp:Ea,plugins:Ca,api:Pa},Aa={apiServer:"API-server",switchToLight:"Skift til lyst tema",switchToDark:"Skift til mørkt tema",theme:"Tema",signOut:"Log ud",loading:"Indlæser…",standardUserNote:"Logget ind som almindelig bruger — administrationssektioner kræver en administratorrolle",footer:"DriverVault — bilservice- og vedligeholdelsesregister."},Oa={overview:"Oversigt",users:"Brugere",orgs:"Organisationer",pocketbase:"PocketBase",webapp:"Webapp",plugins:"Plugins",api:"API"},Ra={title:"Log ind",subtitle:"Superadmin-konsol til DriverVault API-serveren.",email:"E-mail",password:"Adgangskode",submit:"Log ind",submitting:"Logger ind…",invalid:"Ugyldig e-mail eller adgangskode.",failed:"Kunne ikke logge ind.",authNote:"Godkender mod PocketBase gennem denne server"},Ia={title:"Status",unreachable:"utilgængelig",checking:"Tjekker…",apiServer:"API-server",pocketBase:"PocketBase",webApp:"Webapp",thisProcess:"denne proces"},Ua={create:"Opret",save:"Gem",cancel:"Annuller",edit:"Rediger",delete:"Slet",rename:"Omdøb",empty:"—",name:"Navn",email:"E-mail",role:"Rolle",organization:"Organisation"},ja={title:"Brugere",allOrgs:"Alle organisationer",yourOrg:"Din organisation",newUser:"Ny bruger",password:"Adgangskode",passwordUnchanged:"Adgangskode (tom = uændret)",passwordPlaceholder:"mindst 8 tegn",orgNone:"— ingen —",you:"dig",empty:"Ingen brugere.",confirmDelete:"Slet {email}? Dette kan ikke fortrydes."},Ma={title:"Organisationer",subtitle:"Enheder, som brugere tilhører",newOrg:"Ny organisation",namePlaceholder:"Acme Fleet",empty:"Ingen organisationer endnu.",colId:"ID",confirmDelete:'Slet organisationen "{name}"?'},Na={title:"PocketBase",subtitle:"Databaseforbindelse brugt af hvert endpoint",connected:"forbundet",noSuperuser:"ingen superbruger",unreachable:"utilgængelig",baseUrl:"Basis-URL",superuserEmail:"Superbrugerens e-mail",superuserPassword:"Superbrugerens adgangskode",passwordUnchanged:"uændret",passwordNotSet:"ikke angivet",saveApply:"Gem og anvend",testConnection:"Test forbindelse",persistedEnv:"gemt i .env",testOk:"Forbindelse OK — superbruger godkendt.",testReachableNoAuth:"PocketBase er tilgængelig, men servicekontoen blev ikke godkendt.",testUnreachable:"PocketBase er ikke tilgængelig på den adresse.",savedNotice:"Gemt. Serveren bruger nu denne PocketBase."},Da={title:"Webapp",subtitle:"Adressen, der tjekkes ved statuskontrol, og hvem der må kalde dette API fra en browser",reachable:"tilgængelig",unreachable:"utilgængelig",baseUrl:"Basis-URL",allowedOrigins:"Tilladte oprindelser",originsHint:"Kommasepareret, eller {star} for enhver. Native mobilapps er ikke underlagt CORS.",saveApply:"Gem og anvend",testConnection:"Test forbindelse",persistedEnv:"gemt i .env",testOk:"Webappen er tilgængelig.",testFailed:"Webappen svarede ikke på sit helbredstjek på den adresse.",savedNotice:"Gemt. Nye oprindelser gælder fra næste anmodning."},za={title:"Plugins",subtitle:"Integrationer med tredjepartstjenester",registerExternal:"Registrér ekstern",cancelRegister:"Annuller",name:"Navn",baseUrl:"Basis-URL",provider:"Udbyder",register:"Registrér",empty:"Ingen plugins endnu. Registrér et eksternt ovenfor, eller kompilér et indbygget stik.",builtin:"indbygget",enabled:"aktiveret",disabled:"deaktiveret",health:"Helbred",configure:"Konfigurer",close:"Luk",noConfig:"Dette plugin kræver ingen konfiguration.",capabilities:"Funktioner",saved:"Gemt.",checking:"Tjekker…",save:"Gem",saveEnable:"Gem og aktivér",disable:"Deaktiver",remove:"Fjern",builtinNote:"Indbyggede plugins kan deaktiveres, men ikke fjernes",confirmRemove:'Fjern det eksterne plugin "{name}"? Dets gemte konfiguration slettes.'},La={colEndpoint:"Endpoint",colDescription:"Beskrivelse",authNone:"Ingen godkendelse",authBearer:"Bearer-token",authManager:"Admin / superadmin",authSuperadmin:"Superadmin",groupPublic:"Offentlig",groupIdentity:"Identitet",groupCars:"Biler",groupService:"Serviceposter",groupParts:"Reservedele",groupAccount:"Konto",groupManagement:"Administration",groupSuperadmin:"Superadmin"},Fa={app:Aa,sections:Oa,login:Ra,status:Ia,common:Ua,users:ja,orgs:Ma,pocketbase:Na,webapp:Da,plugins:za,api:La},ko="dh-panel-lang",So="en",At={en:ba,pl:$a,da:Fa},Ba=Object.keys(At);function Ha(){try{const t=localStorage.getItem(ko);if(t&&At[t])return t}catch{}const e=(navigator.language||"en").split("-")[0];return At[e]?e:So}const Sn=V(Ha());function Va(e){if(At[e]){Sn.value=e;try{localStorage.setItem(ko,e)}catch{}}}function fi(e,t){return t.split(".").reduce((s,n)=>s==null?void 0:s[n],e)}function Ka(e,t){return t?e.replace(/\{(\w+)\}/g,(s,n)=>t[n]==null?s:String(t[n])):e}function Wa(e,t,s){let n="other";try{n=new Intl.PluralRules(s).select(t)}catch{n=t===1?"one":"other"}return e[n]??e.other??e.one}function h(e,t){const s=Sn.value;let n=fi(At[s],e);if(n==null&&(n=fi(At[So],e)),n==null)return e;if(typeof n=="object"){if((t==null?void 0:t.n)==null)return e;n=Wa(n,t.n,s)}return typeof n!="string"?e:Ka(n,t)}function pi(e,t,s){const i=h(e,{...s,[t]:"\0"}),o=i.indexOf("\0");return o===-1?{before:i,after:""}:{before:i.slice(0,o),after:i.slice(o+1)}}const rn="dh-panel-token";function Ga(){try{return localStorage.getItem(rn)||""}catch{return""}}const Ot=V(Ga()),de=V(null),bt=Qe(()=>{var e;return((e=de.value)==null?void 0:e.role)==="superadmin"}),hi=Qe(()=>{var e,t;return((e=de.value)==null?void 0:e.role)==="admin"||((t=de.value)==null?void 0:t.role)==="superadmin"});function To(e){Ot.value=e;try{e?localStorage.setItem(rn,e):localStorage.removeItem(rn)}catch{}}class qa extends Error{constructor(t,s,n){super(t),this.status=s,this.body=n}}function Za(e,t){if(!e||typeof e!="object")return`HTTP ${t}`;if(e.error)return e.error;const s=Object.entries(e.data||{}).map(([n,i])=>`${n}: ${(i==null?void 0:i.message)||i}`).filter(Boolean);return s.length?s.join("; "):e.message||`HTTP ${t}`}async function se(e,{method:t="GET",body:s,auth:n=!0}={}){const i={};s!==void 0&&(i["Content-Type"]="application/json"),n&&Ot.value&&(i.Authorization=Ot.value);const o=await fetch(e,{method:t,headers:i,body:s===void 0?void 0:JSON.stringify(s)}),r=await o.text();let l=null;try{l=r?JSON.parse(r):null}catch{l=null}if(!o.ok)throw o.status===401&&n&&Tn(),new qa(Za(l,o.status),o.status,l);return l}async function Ja(e,t){const s=await se("/api/auth/login",{method:"POST",body:{email:e,password:t},auth:!1});return To(s.token),await Eo(),de.value}async function Eo(){return de.value=await se("/api/identity"),de.value}function Tn(){To(""),de.value=null}async function Ya(){if(!Ot.value)return null;try{return await Eo()}catch{return Tn(),null}}const Xa={class:"mx-auto flex w-full max-w-sm flex-col gap-5 pt-24"},Qa={class:"dh-card p-6"},ec={class:"text-lg font-bold tracking-[-0.02em] text-strong"},tc={class:"mt-1 mb-5 text-sm text-body"},sc={class:"dh-label",for:"login-email"},nc={class:"dh-label",for:"login-password"},ic={key:0,class:"rounded-control bg-danger-soft px-3 py-2 text-xs text-danger"},oc=["disabled"],rc={class:"eyebrow text-center"},lc={__name:"LoginView",emits:["authenticated"],setup(e,{emit:t}){const s=t,n=V(""),i=V(""),o=V(""),r=V(!1),l=Qe(()=>n.value.trim()!==""&&i.value!==""&&!r.value);async function a(){if(l.value){o.value="",r.value=!0;try{const p=await Ja(n.value.trim(),i.value);s("authenticated",p)}catch(p){o.value=p.status===400||p.status===404?h("login.invalid"):p.message||h("login.failed"),i.value=""}finally{r.value=!1}}}return(p,u)=>(k(),E("div",Xa,[d("div",Qa,[d("h1",ec,b(g(h)("login.title")),1),d("p",tc,b(g(h)("login.subtitle")),1),d("form",{class:"flex flex-col gap-4",onSubmit:Yl(a,["prevent"])},[d("div",null,[d("label",sc,b(g(h)("login.email")),1),re(d("input",{id:"login-email","onUpdate:modelValue":u[0]||(u[0]=m=>n.value=m),class:"dh-input",type:"email",autocomplete:"username",autofocus:"",placeholder:"you@example.com"},null,512),[[pe,n.value]])]),d("div",null,[d("label",nc,b(g(h)("login.password")),1),re(d("input",{id:"login-password","onUpdate:modelValue":u[1]||(u[1]=m=>i.value=m),class:"dh-input",type:"password",autocomplete:"current-password",placeholder:"••••••••"},null,512),[[pe,i.value]])]),o.value?(k(),E("p",ic,b(o.value),1)):B("",!0),d("button",{class:"dh-btn w-full",type:"submit",disabled:!l.value},b(r.value?g(h)("login.submitting"):g(h)("login.submit")),9,oc)],32)]),d("p",rc,b(g(h)("login.authNote")),1)]))}},ac={class:"dh-card overflow-hidden"},cc={class:"flex items-center justify-between border-b border-subtle px-5 py-4"},uc={class:"text-base font-bold tracking-[-0.02em] text-strong"},dc={key:0,class:"dh-pill bg-danger-soft text-danger"},fc={key:0,class:"px-5 py-4 text-sm text-danger"},pc={key:1,class:"w-full text-left text-sm"},hc={class:"px-5 py-3 font-medium text-strong"},gc={class:"data px-5 py-3 text-xs text-muted"},mc={class:"data px-5 py-3 text-right text-xs text-muted"},bc={class:"px-5 py-3 text-right"},vc={key:2,class:"px-5 py-4 text-sm text-muted"},_c={__name:"StatusCard",setup(e){const t=V(null),s=V("");let n=null;async function i(){try{t.value=await se("/api/status",{auth:!1}),s.value=""}catch(l){t.value=null,s.value=l.message||h("status.unreachable")}}const o=Qe(()=>{var l,a,p;return[{key:"apiServer",label:h("status.apiServer"),h:(l=t.value)==null?void 0:l.apiServer},{key:"pocketBase",label:h("status.pocketBase"),h:(a=t.value)==null?void 0:a.pocketBase},{key:"webApp",label:h("status.webApp"),h:(p=t.value)==null?void 0:p.webApp}]});ut(()=>{i(),n=setInterval(i,1e4)}),wn(()=>clearInterval(n));const r=l=>l==="ok"?"bg-success-soft text-success":"bg-danger-soft text-danger";return(l,a)=>(k(),E("div",ac,[d("div",cc,[d("div",uc,b(g(h)("status.title")),1),s.value?(k(),E("span",dc,[a[0]||(a[0]=d("span",{class:"h-1.5 w-1.5 rounded-full bg-current"},null,-1)),Te(b(g(h)("status.unreachable")),1)])):B("",!0)]),s.value?(k(),E("div",fc,b(s.value),1)):t.value?(k(),E("table",pc,[d("tbody",null,[(k(!0),E(ee,null,ke(o.value,p=>(k(),E("tr",{key:p.key,class:"border-t border-subtle first:border-t-0"},[d("td",hc,b(p.label),1),d("td",gc,b(p.h.url||g(h)("status.thisProcess")),1),d("td",mc,b(p.h.latencyMs!=null?p.h.latencyMs+"ms":g(h)("common.empty")),1),d("td",bc,[d("span",{class:$e(["dh-pill",r(p.h.status)])},[a[1]||(a[1]=d("span",{class:"h-1.5 w-1.5 rounded-full bg-current"},null,-1)),Te(b(p.h.status),1)],2)])]))),128))])])):(k(),E("div",vc,b(g(h)("status.checking")),1))]))}},yc={class:"dh-card overflow-hidden"},wc={class:"flex items-center justify-between border-b border-subtle px-5 py-4"},xc={class:"text-base font-bold tracking-[-0.02em] text-strong"},kc={class:"mt-0.5 text-xs text-muted"},Sc={class:"flex flex-col gap-4 px-5 py-4"},Tc={class:"dh-label",for:"pb-url"},Ec={class:"grid gap-4 sm:grid-cols-2"},Cc={class:"dh-label",for:"pb-email"},Pc={class:"dh-label",for:"pb-password"},$c=["placeholder"],Ac={key:0,class:"data text-xs text-muted"},Oc={key:1,class:"rounded-control bg-info-soft px-3 py-2 text-xs text-info"},Rc={key:2,class:"rounded-control bg-danger-soft px-3 py-2 text-xs text-danger"},Ic={class:"flex items-center gap-2"},Uc=["disabled"],jc=["disabled"],Mc={class:"eyebrow"},Nc={__name:"PocketBaseCard",setup(e){const t=V(null),s=V({url:"",adminEmail:"",adminPassword:""}),n=V(null),i=V(""),o=V(""),r=V(!1);async function l(){try{t.value=await se("/api/admin/pb-config"),s.value={url:t.value.url,adminEmail:t.value.adminEmail,adminPassword:""},n.value=t.value.probe}catch(u){i.value=u.message}}ut(l);async function a(){i.value="",o.value="",r.value=!0;try{n.value=await se("/api/admin/pb-config/test",{method:"POST",body:s.value}),o.value=n.value.superuser?h("pocketbase.testOk"):n.value.reachable?h("pocketbase.testReachableNoAuth"):h("pocketbase.testUnreachable")}catch(u){i.value=u.message}finally{r.value=!1}}async function p(){i.value="",o.value="",r.value=!0;try{const u=await se("/api/admin/pb-config",{method:"PUT",body:s.value});t.value=u.config,n.value=u.config.probe,s.value.adminPassword="",o.value=u.warning||h("pocketbase.savedNotice")}catch(u){i.value=u.message}finally{r.value=!1}}return(u,m)=>{var y,A;return k(),E("div",yc,[d("div",wc,[d("div",null,[d("div",xc,b(g(h)("pocketbase.title")),1),d("p",kc,b(g(h)("pocketbase.subtitle")),1)]),n.value?(k(),E("span",{key:0,class:$e(["dh-pill",n.value.superuser?"bg-success-soft text-success":n.value.reachable?"bg-warning-soft text-warning":"bg-danger-soft text-danger"])},[m[3]||(m[3]=d("span",{class:"h-1.5 w-1.5 rounded-full bg-current"},null,-1)),Te(" "+b(n.value.superuser?g(h)("pocketbase.connected"):n.value.reachable?g(h)("pocketbase.noSuperuser"):g(h)("pocketbase.unreachable")),1)],2)):B("",!0)]),d("div",Sc,[d("div",null,[d("label",Tc,b(g(h)("pocketbase.baseUrl")),1),re(d("input",{id:"pb-url","onUpdate:modelValue":m[0]||(m[0]=D=>s.value.url=D),class:"dh-input",placeholder:"http://10.2.1.10:8027"},null,512),[[pe,s.value.url]])]),d("div",Ec,[d("div",null,[d("label",Cc,b(g(h)("pocketbase.superuserEmail")),1),re(d("input",{id:"pb-email","onUpdate:modelValue":m[1]||(m[1]=D=>s.value.adminEmail=D),class:"dh-input",autocomplete:"off"},null,512),[[pe,s.value.adminEmail]])]),d("div",null,[d("label",Pc,b(g(h)("pocketbase.superuserPassword")),1),re(d("input",{id:"pb-password","onUpdate:modelValue":m[2]||(m[2]=D=>s.value.adminPassword=D),class:"dh-input",type:"password",autocomplete:"new-password",placeholder:(y=t.value)!=null&&y.adminConfigured?g(h)("pocketbase.passwordUnchanged"):g(h)("pocketbase.passwordNotSet")},null,8,$c),[[pe,s.value.adminPassword]])])]),(A=n.value)!=null&&A.detail?(k(),E("p",Ac,b(n.value.detail),1)):B("",!0),o.value?(k(),E("p",Oc,b(o.value),1)):B("",!0),i.value?(k(),E("p",Rc,b(i.value),1)):B("",!0),d("div",Ic,[d("button",{class:"dh-btn",disabled:r.value,onClick:p},b(g(h)("pocketbase.saveApply")),9,Uc),d("button",{class:"dh-btn-ghost",disabled:r.value,onClick:a},b(g(h)("pocketbase.testConnection")),9,jc),m[4]||(m[4]=d("span",{class:"flex-1"},null,-1)),d("span",Mc,b(g(h)("pocketbase.persistedEnv")),1)])])])}}},Dc={class:"dh-card overflow-hidden"},zc={class:"flex items-center justify-between border-b border-subtle px-5 py-4"},Lc={class:"text-base font-bold tracking-[-0.02em] text-strong"},Fc={class:"mt-0.5 text-xs text-muted"},Bc={class:"flex flex-col gap-4 px-5 py-4"},Hc={class:"dh-label",for:"web-url"},Vc={class:"dh-label",for:"web-origins"},Kc={class:"mt-1 text-xs text-muted"},Wc={key:0,class:"data text-xs text-muted"},Gc={key:1,class:"rounded-control bg-info-soft px-3 py-2 text-xs text-info"},qc={key:2,class:"rounded-control bg-danger-soft px-3 py-2 text-xs text-danger"},Zc={class:"flex items-center gap-2"},Jc=["disabled"],Yc=["disabled"],Xc={class:"eyebrow"},Qc={__name:"WebAppCard",setup(e){const t=V(null),s=V({url:"",origins:""}),n=V(null),i=V(""),o=V(""),r=V(!1),l=y=>y.split(",").map(A=>A.trim()).filter(Boolean);function a(y){t.value=y,s.value={url:y.url,origins:(y.allowOrigins||[]).join(", ")},n.value=y.probe}async function p(){try{a(await se("/api/admin/webapp-config"))}catch(y){i.value=y.message}}ut(p);async function u(){i.value="",o.value="",r.value=!0;try{n.value=await se("/api/admin/webapp-config/test",{method:"POST",body:{url:s.value.url}}),o.value=n.value.status==="ok"?h("webapp.testOk"):h("webapp.testFailed")}catch(y){i.value=y.message}finally{r.value=!1}}async function m(){i.value="",o.value="",r.value=!0;try{const y=await se("/api/admin/webapp-config",{method:"PUT",body:{url:s.value.url,allowOrigins:l(s.value.origins)}});a(y.config),o.value=y.warning||h("webapp.savedNotice")}catch(y){i.value=y.message}finally{r.value=!1}}return(y,A)=>{var D;return k(),E("div",Dc,[d("div",zc,[d("div",null,[d("div",Lc,b(g(h)("webapp.title")),1),d("p",Fc,b(g(h)("webapp.subtitle")),1)]),n.value?(k(),E("span",{key:0,class:$e(["dh-pill",n.value.status==="ok"?"bg-success-soft text-success":"bg-danger-soft text-danger"])},[A[2]||(A[2]=d("span",{class:"h-1.5 w-1.5 rounded-full bg-current"},null,-1)),Te(" "+b(n.value.status==="ok"?g(h)("webapp.reachable"):g(h)("webapp.unreachable")),1)],2)):B("",!0)]),d("div",Bc,[d("div",null,[d("label",Hc,b(g(h)("webapp.baseUrl")),1),re(d("input",{id:"web-url","onUpdate:modelValue":A[0]||(A[0]=C=>s.value.url=C),class:"dh-input",placeholder:"http://localhost:8090"},null,512),[[pe,s.value.url]])]),d("div",null,[d("label",Vc,b(g(h)("webapp.allowedOrigins")),1),re(d("input",{id:"web-origins","onUpdate:modelValue":A[1]||(A[1]=C=>s.value.origins=C),class:"dh-input",placeholder:"http://localhost:8090, https://app.example.com"},null,512),[[pe,s.value.origins]]),d("p",Kc,[Te(b(g(pi)("webapp.originsHint","star").before),1),A[3]||(A[3]=d("span",{class:"data"},"*",-1)),Te(b(g(pi)("webapp.originsHint","star").after),1)])]),(D=n.value)!=null&&D.error?(k(),E("p",Wc,b(n.value.error),1)):B("",!0),o.value?(k(),E("p",Gc,b(o.value),1)):B("",!0),i.value?(k(),E("p",qc,b(i.value),1)):B("",!0),d("div",Zc,[d("button",{class:"dh-btn",disabled:r.value,onClick:m},b(g(h)("webapp.saveApply")),9,Jc),d("button",{class:"dh-btn-ghost",disabled:r.value,onClick:u},b(g(h)("webapp.testConnection")),9,Yc),A[4]||(A[4]=d("span",{class:"flex-1"},null,-1)),d("span",Xc,b(g(h)("webapp.persistedEnv")),1)])])])}}},eu={class:"dh-card overflow-hidden"},tu={class:"flex items-center justify-between border-b border-subtle px-5 py-4"},su={class:"text-base font-bold tracking-[-0.02em] text-strong"},nu={class:"mt-0.5 text-xs text-muted"},iu={key:0,class:"border-b border-subtle bg-sunken px-5 py-4"},ou={class:"grid gap-3 sm:grid-cols-3"},ru={class:"dh-label"},lu={class:"dh-label"},au={class:"dh-label"},cu=["disabled"],uu={key:1,class:"border-b border-subtle px-5 py-3 text-xs text-danger"},du={key:2,class:"px-5 py-6 text-center text-sm text-muted"},fu={class:"flex items-center gap-3 px-5 py-3"},pu=["onClick"],hu={class:"font-semibold text-strong"},gu={class:"dh-pill bg-sunken text-muted"},mu={key:0,class:"text-xs text-muted"},bu=["disabled","onClick"],vu=["disabled","onClick"],_u={key:0,class:"bg-sunken px-5 py-4"},yu={key:0,class:"data mb-3 text-xs text-muted"},wu={key:1,class:"grid gap-3 sm:grid-cols-2"},xu={class:"dh-label"},ku={key:0,class:"text-danger"},Su=["onUpdate:modelValue"],Tu=["value"],Eu=["onUpdate:modelValue","type","placeholder"],Cu={key:2,class:"mt-1 text-xs text-muted"},Pu={key:2,class:"text-xs text-muted"},$u={key:3,class:"mt-4"},Au={class:"eyebrow mb-1.5"},Ou={class:"data flex flex-col gap-1 text-xs text-muted"},Ru={class:"text-strong"},Iu={key:0},Uu={key:1},ju={key:4,class:"data mt-3 text-xs text-body"},Mu={class:"mt-4 flex items-center gap-2"},Nu=["disabled","onClick"],Du=["disabled","onClick"],zu=["disabled","onClick"],Lu={key:5,class:"eyebrow mt-2"},Fu={__name:"PluginsCard",setup(e){const t=V([]),s=V(""),n=V(!1),i=V(null),o=Jt({}),r=Jt({}),l=V(!1),a=V({name:"",baseURL:"",provider:""});async function p(){try{const P=await se("/api/admin/plugins");t.value=P.plugins||[],s.value=""}catch(P){s.value=P.message}}ut(p);function u(P){var $;if(i.value===P.name){i.value=null;return}const T={};for(const j of P.configFields||[])T[j.key]=(($=P.config)==null?void 0:$[j.key])??"";o[P.name]=T,i.value=P.name}async function m(P,T){n.value=!0,r[P.name]="";try{const $=await se(`/api/admin/plugins/${encodeURIComponent(P.name)}`,{method:"PUT",body:{enabled:T,config:o[P.name]??{}}});r[P.name]=$.warning||h("plugins.saved"),await p()}catch($){r[P.name]=$.message}finally{n.value=!1}}async function y(P){n.value=!0,r[P.name]=h("plugins.checking");try{const T=await se(`/api/admin/plugins/${encodeURIComponent(P.name)}/health`,{method:"POST"});r[P.name]=`${T.health.status}${T.health.detail?" — "+T.health.detail:""}`,await p()}catch(T){r[P.name]=T.message}finally{n.value=!1}}async function A(P){if(confirm(h("plugins.confirmRemove",{name:P.name}))){n.value=!0;try{await se(`/api/admin/plugins/${encodeURIComponent(P.name)}`,{method:"DELETE"}),i.value===P.name&&(i.value=null),await p()}catch(T){r[P.name]=T.message}finally{n.value=!1}}}async function D(){n.value=!0,s.value="";try{await se("/api/admin/plugins",{method:"POST",body:a.value}),a.value={name:"",baseURL:"",provider:""},l.value=!1,await p()}catch(P){s.value=P.message}finally{n.value=!1}}const C=P=>P==="ok"?"bg-success-soft text-success":P==="degraded"?"bg-warning-soft text-warning":"bg-danger-soft text-danger";return(P,T)=>(k(),E("div",eu,[d("div",tu,[d("div",null,[d("div",su,b(g(h)("plugins.title")),1),d("p",nu,b(g(h)("plugins.subtitle")),1)]),d("button",{class:"dh-btn-ghost",onClick:T[0]||(T[0]=$=>l.value=!l.value)},b(l.value?g(h)("plugins.cancelRegister"):g(h)("plugins.registerExternal")),1)]),l.value?(k(),E("div",iu,[d("div",ou,[d("div",null,[d("label",ru,b(g(h)("plugins.name")),1),re(d("input",{"onUpdate:modelValue":T[1]||(T[1]=$=>a.value.name=$),class:"dh-input",placeholder:"acme-parts"},null,512),[[pe,a.value.name]])]),d("div",null,[d("label",lu,b(g(h)("plugins.baseUrl")),1),re(d("input",{"onUpdate:modelValue":T[2]||(T[2]=$=>a.value.baseURL=$),class:"dh-input",placeholder:"http://127.0.0.1:9100"},null,512),[[pe,a.value.baseURL]])]),d("div",null,[d("label",au,b(g(h)("plugins.provider")),1),re(d("input",{"onUpdate:modelValue":T[3]||(T[3]=$=>a.value.provider=$),class:"dh-input",placeholder:"ACME Corp"},null,512),[[pe,a.value.provider]])])]),d("button",{class:"dh-btn mt-3",disabled:n.value||!a.value.name||!a.value.baseURL,onClick:D},b(g(h)("plugins.register")),9,cu)])):B("",!0),s.value?(k(),E("p",uu,b(s.value),1)):B("",!0),t.value.length?B("",!0):(k(),E("p",du,b(g(h)("plugins.empty")),1)),(k(!0),E(ee,null,ke(t.value,$=>(k(),E("div",{key:$.name,class:"border-t border-subtle first:border-t-0"},[d("div",fu,[d("button",{class:"flex flex-1 items-center gap-3 text-left",onClick:j=>u($)},[d("span",hu,b($.name),1),d("span",gu,b($.kind||g(h)("plugins.builtin")),1),$.provider?(k(),E("span",mu,b($.provider),1)):B("",!0),$.health?(k(),E("span",{key:1,class:$e(["dh-pill",C($.health.status)])},[T[4]||(T[4]=d("span",{class:"h-1.5 w-1.5 rounded-full bg-current"},null,-1)),Te(b($.health.status),1)],2)):B("",!0)],8,pu),d("span",{class:$e(["dh-pill",$.enabled?"bg-success-soft text-success":"bg-sunken text-muted"])},b($.enabled?g(h)("plugins.enabled"):g(h)("plugins.disabled")),3),d("button",{class:"dh-btn-ghost",disabled:n.value,onClick:j=>y($)},b(g(h)("plugins.health")),9,bu),d("button",{class:"dh-btn-ghost",disabled:n.value,onClick:j=>u($)},b(i.value===$.name?g(h)("plugins.close"):g(h)("plugins.configure")),9,vu)]),i.value===$.name?(k(),E("div",_u,[$.baseURL?(k(),E("div",yu,b($.baseURL),1)):B("",!0),($.configFields||[]).length?(k(),E("div",wu,[(k(!0),E(ee,null,ke($.configFields,j=>(k(),E("div",{key:j.key},[d("label",xu,[Te(b(j.label||j.key),1),j.required?(k(),E("span",ku," *")):B("",!0)]),j.type==="select"?re((k(),E("select",{key:0,"onUpdate:modelValue":M=>o[$.name][j.key]=M,class:"dh-select"},[(k(!0),E(ee,null,ke(j.options||[],M=>(k(),E("option",{key:M.value,value:M.value},b(M.label||M.value),9,Tu))),128))],8,Su)),[[xs,o[$.name][j.key]]]):re((k(),E("input",{key:1,"onUpdate:modelValue":M=>o[$.name][j.key]=M,class:"dh-input",type:j.type==="password"?"password":j.type==="number"?"number":"text",placeholder:j.default||"",autocomplete:"off"},null,8,Eu)),[[Gl,o[$.name][j.key]]]),j.help?(k(),E("p",Cu,b(j.help),1)):B("",!0)]))),128))])):(k(),E("p",Pu,b(g(h)("plugins.noConfig")),1)),($.capabilities||[]).length?(k(),E("div",$u,[d("div",Au,b(g(h)("plugins.capabilities")),1),d("ul",Ou,[(k(!0),E(ee,null,ke($.capabilities,j=>(k(),E("li",{key:j.id},[d("span",Ru,b(j.id),1),j.method||j.endpoint?(k(),E("span",Iu," — "+b(j.method)+" "+b(j.endpoint),1)):B("",!0),j.description?(k(),E("span",Uu," · "+b(j.description),1)):B("",!0)]))),128))])])):B("",!0),r[$.name]?(k(),E("p",ju,b(r[$.name]),1)):B("",!0),d("div",Mu,[d("button",{class:"dh-btn",disabled:n.value,onClick:j=>m($,!0)},b($.enabled?g(h)("plugins.save"):g(h)("plugins.saveEnable")),9,Nu),$.enabled?(k(),E("button",{key:0,class:"dh-btn-ghost",disabled:n.value,onClick:j=>m($,!1)},b(g(h)("plugins.disable")),9,Du)):B("",!0),T[5]||(T[5]=d("span",{class:"flex-1"},null,-1)),$.kind==="external"?(k(),E("button",{key:1,class:"dh-btn-danger",disabled:n.value,onClick:j=>A($)},b(g(h)("plugins.remove")),9,zu)):B("",!0)]),$.kind!=="external"?(k(),E("p",Lu,b(g(h)("plugins.builtinNote")),1)):B("",!0)])):B("",!0)]))),128))]))}},Bu={class:"dh-card overflow-hidden"},Hu={class:"flex items-center justify-between border-b border-subtle px-5 py-4"},Vu={class:"text-base font-bold tracking-[-0.02em] text-strong"},Ku={class:"mt-0.5 text-xs text-muted"},Wu={key:0,class:"border-b border-subtle px-5 py-3 text-xs text-danger"},Gu={key:1,class:"border-b border-subtle bg-sunken px-5 py-4"},qu={class:"grid gap-3 sm:grid-cols-2"},Zu={class:"dh-label"},Ju={class:"dh-label"},Yu={class:"dh-label"},Xu=["placeholder"],Qu={class:"dh-label"},ed=["value"],td={key:0},sd={class:"dh-label"},nd={value:""},id=["value"],od={class:"mt-3 flex items-center gap-2"},rd=["disabled"],ld=["disabled"],ad={key:2,class:"px-5 py-6 text-center text-sm text-muted"},cd={key:3,class:"w-full text-left text-sm"},ud={class:"[&>th]:eyebrow [&>th]:px-5 [&>th]:py-2.5 [&>th]:font-medium"},dd={class:"data px-5 py-2.5 text-xs text-strong"},fd={key:0,class:"eyebrow ml-1"},pd={class:"px-5 py-2.5 text-body"},hd={class:"px-5 py-2.5 text-body"},gd={class:"px-5 py-2.5"},md={class:"px-5 py-2.5 text-right whitespace-nowrap"},bd=["onClick"],vd=["disabled","onClick"],_d={__name:"UsersCard",setup(e){const t=V([]),s=V([]),n=V(""),i=V(!1),o=V(null),r=V({}),l=Qe(()=>bt.value?["user","admin","superadmin"]:["user","admin"]);async function a(){try{const[C,P]=await Promise.all([se("/api/users"),se("/api/orgs")]);t.value=C.users||[],s.value=P.organizations||[],n.value=""}catch(C){n.value=C.message}}ut(a);function p(){var C;o.value="new",r.value={email:"",name:"",password:"",role:"user",organization:bt.value?"":((C=de.value)==null?void 0:C.organization)||""}}function u(C){o.value=C.id,r.value={email:C.email,name:C.name||"",password:"",role:C.role,organization:C.organization||""}}function m(){o.value=null,n.value=""}async function y(){i.value=!0,n.value="";try{if(o.value==="new")await se("/api/users",{method:"POST",body:r.value});else{const C={...r.value};C.password||delete C.password,await se(`/api/users/${o.value}`,{method:"PATCH",body:C})}o.value=null,await a()}catch(C){n.value=C.message}finally{i.value=!1}}async function A(C){if(confirm(h("users.confirmDelete",{email:C.email}))){i.value=!0,n.value="";try{await se(`/api/users/${C.id}`,{method:"DELETE"}),await a()}catch(P){n.value=P.message}finally{i.value=!1}}}const D=C=>C==="superadmin"?"bg-info-soft text-info":C==="admin"?"bg-warning-soft text-warning":"bg-sunken text-muted";return(C,P)=>(k(),E("div",Bu,[d("div",Hu,[d("div",null,[d("div",Vu,b(g(h)("users.title")),1),d("p",Ku,b(g(bt)?g(h)("users.allOrgs"):g(h)("users.yourOrg")),1)]),d("button",{class:"dh-btn",onClick:p},b(g(h)("users.newUser")),1)]),n.value?(k(),E("p",Wu,b(n.value),1)):B("",!0),o.value?(k(),E("div",Gu,[d("div",qu,[d("div",null,[d("label",Zu,b(g(h)("common.email")),1),re(d("input",{"onUpdate:modelValue":P[0]||(P[0]=T=>r.value.email=T),class:"dh-input",type:"email",autocomplete:"off"},null,512),[[pe,r.value.email]])]),d("div",null,[d("label",Ju,b(g(h)("common.name")),1),re(d("input",{"onUpdate:modelValue":P[1]||(P[1]=T=>r.value.name=T),class:"dh-input",autocomplete:"off"},null,512),[[pe,r.value.name]])]),d("div",null,[d("label",Yu,b(o.value==="new"?g(h)("users.password"):g(h)("users.passwordUnchanged")),1),re(d("input",{"onUpdate:modelValue":P[2]||(P[2]=T=>r.value.password=T),class:"dh-input",type:"password",autocomplete:"new-password",placeholder:g(h)("users.passwordPlaceholder")},null,8,Xu),[[pe,r.value.password]])]),d("div",null,[d("label",Qu,b(g(h)("common.role")),1),re(d("select",{"onUpdate:modelValue":P[3]||(P[3]=T=>r.value.role=T),class:"dh-select"},[(k(!0),E(ee,null,ke(l.value,T=>(k(),E("option",{key:T,value:T},b(T),9,ed))),128))],512),[[xs,r.value.role]])]),g(bt)?(k(),E("div",td,[d("label",sd,b(g(h)("common.organization")),1),re(d("select",{"onUpdate:modelValue":P[4]||(P[4]=T=>r.value.organization=T),class:"dh-select"},[d("option",nd,b(g(h)("users.orgNone")),1),(k(!0),E(ee,null,ke(s.value,T=>(k(),E("option",{key:T.id,value:T.id},b(T.name),9,id))),128))],512),[[xs,r.value.organization]])])):B("",!0)]),d("div",od,[d("button",{class:"dh-btn",disabled:i.value,onClick:y},b(o.value==="new"?g(h)("common.create"):g(h)("common.save")),9,rd),d("button",{class:"dh-btn-ghost",disabled:i.value,onClick:m},b(g(h)("common.cancel")),9,ld)])])):B("",!0),t.value.length?(k(),E("table",cd,[d("thead",null,[d("tr",ud,[d("th",null,b(g(h)("common.email")),1),d("th",null,b(g(h)("common.name")),1),d("th",null,b(g(h)("common.organization")),1),d("th",null,b(g(h)("common.role")),1),P[5]||(P[5]=d("th",null,null,-1))])]),d("tbody",null,[(k(!0),E(ee,null,ke(t.value,T=>{var $,j;return k(),E("tr",{key:T.id,class:"border-t border-subtle transition-colors hover:bg-sunken"},[d("td",dd,[Te(b(T.email)+" ",1),T.id===(($=g(de))==null?void 0:$.id)?(k(),E("span",fd,b(g(h)("users.you")),1)):B("",!0)]),d("td",pd,b(T.name||g(h)("common.empty")),1),d("td",hd,b(T.organizationName||g(h)("common.empty")),1),d("td",gd,[d("span",{class:$e(["dh-pill",D(T.role)])},b(T.role),3)]),d("td",md,[d("button",{class:"dh-btn-ghost",onClick:M=>u(T)},b(g(h)("common.edit")),9,bd),T.id!==((j=g(de))==null?void 0:j.id)?(k(),E("button",{key:0,class:"dh-btn-danger ml-1.5",disabled:i.value,onClick:M=>A(T)},b(g(h)("common.delete")),9,vd)):B("",!0)])])}),128))])])):(k(),E("p",ad,b(g(h)("users.empty")),1))]))}},yd={class:"dh-card overflow-hidden"},wd={class:"flex items-center justify-between border-b border-subtle px-5 py-4"},xd={class:"text-base font-bold tracking-[-0.02em] text-strong"},kd={class:"mt-0.5 text-xs text-muted"},Sd={key:0,class:"border-b border-subtle px-5 py-3 text-xs text-danger"},Td={key:1,class:"border-b border-subtle bg-sunken px-5 py-4"},Ed={class:"dh-label"},Cd=["placeholder"],Pd={class:"mt-3 flex items-center gap-2"},$d=["disabled"],Ad=["disabled"],Od={key:2,class:"px-5 py-6 text-center text-sm text-muted"},Rd={key:3,class:"w-full text-left text-sm"},Id={class:"[&>th]:eyebrow [&>th]:px-5 [&>th]:py-2.5 [&>th]:font-medium"},Ud={class:"px-5 py-2.5 font-medium text-strong"},jd={class:"data px-5 py-2.5 text-xs text-muted"},Md={class:"px-5 py-2.5 text-right whitespace-nowrap"},Nd=["onClick"],Dd=["disabled","onClick"],zd={__name:"OrgsCard",setup(e){const t=V([]),s=V(""),n=V(!1),i=V(null),o=V("");async function r(){try{const y=await se("/api/orgs");t.value=y.organizations||[],s.value=""}catch(y){s.value=y.message}}ut(r);function l(){i.value="new",o.value=""}function a(y){i.value=y.id,o.value=y.name}function p(){i.value=null,s.value=""}async function u(){n.value=!0,s.value="";try{i.value==="new"?await se("/api/orgs",{method:"POST",body:{name:o.value}}):await se(`/api/orgs/${i.value}`,{method:"PATCH",body:{name:o.value}}),i.value=null,await r()}catch(y){s.value=y.message}finally{n.value=!1}}async function m(y){if(confirm(h("orgs.confirmDelete",{name:y.name}))){n.value=!0,s.value="";try{await se(`/api/orgs/${y.id}`,{method:"DELETE"}),await r()}catch(A){s.value=A.message}finally{n.value=!1}}}return(y,A)=>(k(),E("div",yd,[d("div",wd,[d("div",null,[d("div",xd,b(g(h)("orgs.title")),1),d("p",kd,b(g(h)("orgs.subtitle")),1)]),g(bt)?(k(),E("button",{key:0,class:"dh-btn",onClick:l},b(g(h)("orgs.newOrg")),1)):B("",!0)]),s.value?(k(),E("p",Sd,b(s.value),1)):B("",!0),i.value?(k(),E("div",Td,[d("label",Ed,b(g(h)("common.name")),1),re(d("input",{"onUpdate:modelValue":A[0]||(A[0]=D=>o.value=D),class:"dh-input",placeholder:g(h)("orgs.namePlaceholder"),onKeyup:Ql(u,["enter"])},null,40,Cd),[[pe,o.value]]),d("div",Pd,[d("button",{class:"dh-btn",disabled:n.value||!o.value.trim(),onClick:u},b(i.value==="new"?g(h)("common.create"):g(h)("common.save")),9,$d),d("button",{class:"dh-btn-ghost",disabled:n.value,onClick:p},b(g(h)("common.cancel")),9,Ad)])])):B("",!0),t.value.length?(k(),E("table",Rd,[d("thead",null,[d("tr",Id,[d("th",null,b(g(h)("common.name")),1),d("th",null,b(g(h)("orgs.colId")),1),A[1]||(A[1]=d("th",null,null,-1))])]),d("tbody",null,[(k(!0),E(ee,null,ke(t.value,D=>(k(),E("tr",{key:D.id,class:"border-t border-subtle transition-colors hover:bg-sunken"},[d("td",Ud,b(D.name),1),d("td",jd,b(D.id),1),d("td",Md,[g(bt)?(k(),E(ee,{key:0},[d("button",{class:"dh-btn-ghost",onClick:C=>a(D)},b(g(h)("common.rename")),9,Nd),d("button",{class:"dh-btn-danger ml-1.5",disabled:n.value,onClick:C=>m(D)},b(g(h)("common.delete")),9,Dd)],64)):B("",!0)])]))),128))])])):(k(),E("p",Od,b(g(h)("orgs.empty")),1))]))}},Ld={class:"dh-card overflow-hidden"},Fd={class:"flex items-center justify-between border-b border-subtle px-5 py-4"},Bd={class:"text-base font-bold tracking-[-0.02em] text-strong"},Hd={class:"eyebrow"},Vd={class:"w-full text-left text-sm"},Kd={class:"[&>th]:eyebrow [&>th]:px-5 [&>th]:py-2.5 [&>th]:font-medium"},Wd={class:"data border-t border-subtle px-5 py-2.5 text-xs whitespace-nowrap"},Gd={class:"text-strong"},qd={class:"border-t border-subtle px-5 py-2.5 text-body"},nt={__name:"EndpointTable",props:{title:String,auth:String,endpoints:Array},setup(e){const t={GET:"text-success",POST:"text-brandtext",PATCH:"text-warning",DELETE:"text-danger"};return(s,n)=>(k(),E("div",Ld,[d("div",Fd,[d("div",Bd,b(e.title),1),d("span",Hd,b(e.auth),1)]),d("table",Vd,[d("thead",null,[d("tr",Kd,[d("th",null,b(g(h)("api.colEndpoint")),1),d("th",null,b(g(h)("api.colDescription")),1)])]),d("tbody",null,[(k(!0),E(ee,null,ke(e.endpoints,i=>(k(),E("tr",{key:i.method+i.path,class:"transition-colors hover:bg-sunken"},[d("td",Wd,[d("span",{class:$e(["font-semibold",t[i.method]])},b(i.method),3),d("span",Gd,b(i.path),1)]),d("td",qd,b(i.desc),1)]))),128))])])]))}},Zd={class:"mx-auto flex max-w-4xl flex-col gap-6 px-6 pt-12 pb-16"},Jd={class:"flex items-center gap-3"},Yd={class:"inline-flex items-center gap-2.5 select-none"},Xd={class:"h-8 w-8 shrink-0",viewBox:"0 0 48 48",fill:"none","aria-hidden":"true"},Qd={transform:"translate(7 0) skewX(-13)"},ef=["fill"],tf=["fill"],sf=["fill"],nf={class:"eyebrow mt-1.5"},of={key:0,class:"data hidden text-xs text-muted sm:inline"},rf={key:0},lf={key:1,class:"dh-pill bg-info-soft text-info"},af=["value","aria-label"],cf=["value"],uf=["title"],df={key:0,xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"1.75",class:"h-4 w-4"},ff={key:1,xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"1.75",class:"h-4 w-4"},pf={key:0,class:"eyebrow py-16 text-center"},hf={class:"flex flex-wrap gap-1.5"},gf=["onClick"],mf={key:7,class:"eyebrow text-center"},bf={class:"eyebrow text-center"},vf={__name:"App",setup(e){const t=V(!0),s=V("overview");ut(async()=>{await Ya(),t.value=!1});const n=Qe(()=>{const C=[{id:"overview",label:h("sections.overview")}];return hi.value&&C.push({id:"users",label:h("sections.users")},{id:"orgs",label:h("sections.orgs")}),bt.value&&C.push({id:"pocketbase",label:h("sections.pocketbase")},{id:"webapp",label:h("sections.webapp")},{id:"plugins",label:h("sections.plugins")}),C.push({id:"api",label:h("sections.api")}),C}),i=Qe(()=>Ba.map(C=>{let P=C;try{P=new Intl.DisplayNames([C],{type:"language"}).of(C)||C}catch{}return{code:C,label:P.charAt(0).toUpperCase()+P.slice(1)}}));function o(){Tn(),s.value="overview"}const r=Qe(()=>Et.value==="dark"?["#60a5fa","#93c5fd","#ffffff"]:["var(--brand-700)","var(--brand-500)","var(--brand-400)"]),l=[{method:"GET",path:"/api/health",desc:"Liveness probe (no auth)"},{method:"GET",path:"/api/status",desc:"Health of PocketBase + Web App"},{method:"POST",path:"/api/auth/login",desc:"Exchange email + password for a PocketBase token"},{method:"GET",path:"/api/auth/validate",desc:"Check whether a token is still valid"}],a=[{method:"GET",path:"/api/auth/me",desc:"Identity of the bearer token"},{method:"GET",path:"/api/identity",desc:"Identity incl. role + organization"}],p=[{method:"GET",path:"/api/cars",desc:"List owned + shared cars"},{method:"POST",path:"/api/cars",desc:"Create a car"},{method:"GET",path:"/api/cars/{id}",desc:"Fetch one car"},{method:"PATCH",path:"/api/cars/{id}",desc:"Update a car"},{method:"DELETE",path:"/api/cars/{id}",desc:"Delete a car (owner only)"},{method:"GET",path:"/api/cars/{id}/service-records",desc:"A car's service history"},{method:"GET",path:"/api/cars/{id}/parts",desc:"A car's parts catalog"},{method:"GET",path:"/api/cars/{id}/shares",desc:"Who a car is shared with (owner)"},{method:"POST",path:"/api/cars/{id}/shares",desc:"Share a car by email (owner)"},{method:"DELETE",path:"/api/cars/{id}/shares/{userId}",desc:"Revoke a share (owner)"}],u=[{method:"GET",path:"/api/service-records",desc:"List service records"},{method:"POST",path:"/api/service-records",desc:"Log a service record"},{method:"GET",path:"/api/service-records/{id}",desc:"Fetch one record"},{method:"PATCH",path:"/api/service-records/{id}",desc:"Update a record"},{method:"DELETE",path:"/api/service-records/{id}",desc:"Delete a record"}],m=[{method:"GET",path:"/api/parts",desc:"List parts"},{method:"POST",path:"/api/parts",desc:"Add a part"},{method:"GET",path:"/api/parts/{id}",desc:"Fetch one part"},{method:"PATCH",path:"/api/parts/{id}",desc:"Update a part"},{method:"DELETE",path:"/api/parts/{id}",desc:"Delete a part"}],y=[{method:"GET",path:"/api/me",desc:"Current user profile"},{method:"PATCH",path:"/api/me",desc:"Update profile"},{method:"POST",path:"/api/me/password",desc:"Change password"},{method:"POST",path:"/api/me/avatar",desc:"Upload avatar"},{method:"GET",path:"/api/me/avatar",desc:"Fetch avatar"},{method:"DELETE",path:"/api/me/avatar",desc:"Remove avatar"},{method:"POST",path:"/api/me/verify/request",desc:"Request email verification"},{method:"GET",path:"/api/me/export",desc:"Export your data"},{method:"POST",path:"/api/me/import",desc:"Import data"},{method:"POST",path:"/api/me/delete",desc:"Request account deletion"},{method:"POST",path:"/api/me/delete/cancel",desc:"Cancel deletion request"},{method:"DELETE",path:"/api/me",desc:"Finalize account deletion"}],A=[{method:"GET",path:"/api/users",desc:"List users (scoped by role)"},{method:"POST",path:"/api/users",desc:"Create a user"},{method:"PATCH",path:"/api/users/{id}",desc:"Update email / name / role / org / password"},{method:"DELETE",path:"/api/users/{id}",desc:"Delete a user"},{method:"GET",path:"/api/orgs",desc:"List organizations"},{method:"POST",path:"/api/orgs",desc:"Create an organization (superadmin)"},{method:"PATCH",path:"/api/orgs/{id}",desc:"Rename an organization (superadmin)"},{method:"DELETE",path:"/api/orgs/{id}",desc:"Delete an organization (superadmin)"}],D=[{method:"GET",path:"/api/admin/pb-config",desc:"PocketBase connection + live probe"},{method:"POST",path:"/api/admin/pb-config/test",desc:"Probe a candidate connection"},{method:"PUT",path:"/api/admin/pb-config",desc:"Apply + persist a connection"},{method:"GET",path:"/api/admin/webapp-config",desc:"Web App URL + CORS origins, with a live probe"},{method:"POST",path:"/api/admin/webapp-config/test",desc:"Probe a candidate Web App address"},{method:"PUT",path:"/api/admin/webapp-config",desc:"Apply + persist Web App settings"},{method:"GET",path:"/api/admin/plugins",desc:"List plugins (secrets masked)"},{method:"POST",path:"/api/admin/plugins",desc:"Register an external plugin"},{method:"GET",path:"/api/admin/plugins/{name}",desc:"Fetch one plugin"},{method:"PUT",path:"/api/admin/plugins/{name}",desc:"Enable/disable + configure"},{method:"DELETE",path:"/api/admin/plugins/{name}",desc:"Remove an external plugin"},{method:"POST",path:"/api/admin/plugins/{name}/health",desc:"Run a health check now"}];return(C,P)=>(k(),E("div",Zd,[d("div",Jd,[d("span",Yd,[(k(),E("svg",Xd,[d("g",Qd,[d("rect",{x:"9",y:"16",width:"6",height:"16",rx:"3",fill:r.value[0]},null,8,ef),d("rect",{x:"19",y:"12",width:"6",height:"24",rx:"3",fill:r.value[1]},null,8,tf),d("rect",{x:"29",y:"8",width:"6",height:"32",rx:"3",fill:r.value[2]},null,8,sf)])])),P[2]||(P[2]=d("span",{class:"text-2xl leading-none font-extrabold tracking-[-0.03em] italic"},[d("span",{class:"text-strong"},"Driver"),d("span",{class:"text-brandtext"},"Vault")],-1))]),d("span",nf,b(g(h)("app.apiServer")),1),P[5]||(P[5]=d("div",{class:"flex-1"},null,-1)),g(de)?(k(),E("span",of,[Te(b(g(de).email),1),g(de).organizationName?(k(),E("span",rf," · "+b(g(de).organizationName),1)):B("",!0)])):B("",!0),g(de)?(k(),E("span",lf,b(g(de).role),1)):B("",!0),d("select",{class:"dh-select !w-auto !py-1 !text-xs",value:g(Sn),"aria-label":g(h)("sections.overview"),onChange:P[0]||(P[0]=T=>g(Va)(T.target.value))},[(k(!0),E(ee,null,ke(i.value,T=>(k(),E("option",{key:T.code,value:T.code},b(T.label),9,cf))),128))],40,af),d("button",{class:"dh-btn-ghost",title:g(Et)==="dark"?g(h)("app.switchToLight"):g(h)("app.switchToDark"),onClick:P[1]||(P[1]=(...T)=>g(di)&&g(di)(...T))},[g(Et)==="dark"?(k(),E("svg",df,[...P[3]||(P[3]=[d("circle",{cx:"12",cy:"12",r:"4"},null,-1),d("path",{"stroke-linecap":"round",d:"M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"},null,-1)])])):(k(),E("svg",ff,[...P[4]||(P[4]=[d("path",{"stroke-linecap":"round","stroke-linejoin":"round",d:"M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8Z"},null,-1)])])),Te(" "+b(g(h)("app.theme")),1)],8,uf),g(Ot)?(k(),E("button",{key:2,class:"dh-btn-ghost",onClick:o},b(g(h)("app.signOut")),1)):B("",!0)]),t.value?(k(),E("p",pf,b(g(h)("app.loading")),1)):g(Ot)?(k(),E(ee,{key:2},[d("nav",hf,[(k(!0),E(ee,null,ke(n.value,T=>(k(),E("button",{key:T.id,class:$e(["dh-btn-ghost",s.value===T.id?"border-accent text-brandtext":""]),onClick:$=>s.value=T.id},b(T.label),11,gf))),128))]),s.value==="overview"?(k(),it(_c,{key:0})):s.value==="users"?(k(),it(_d,{key:1})):s.value==="orgs"?(k(),it(zd,{key:2})):s.value==="pocketbase"?(k(),it(Nc,{key:3})):s.value==="webapp"?(k(),it(Qc,{key:4})):s.value==="plugins"?(k(),it(Fu,{key:5})):s.value==="api"?(k(),E(ee,{key:6},[ce(nt,{title:g(h)("api.groupPublic"),auth:g(h)("api.authNone"),endpoints:l},null,8,["title","auth"]),ce(nt,{title:g(h)("api.groupIdentity"),auth:g(h)("api.authBearer"),endpoints:a},null,8,["title","auth"]),ce(nt,{title:g(h)("api.groupCars"),auth:g(h)("api.authBearer"),endpoints:p},null,8,["title","auth"]),ce(nt,{title:g(h)("api.groupService"),auth:g(h)("api.authBearer"),endpoints:u},null,8,["title","auth"]),ce(nt,{title:g(h)("api.groupParts"),auth:g(h)("api.authBearer"),endpoints:m},null,8,["title","auth"]),ce(nt,{title:g(h)("api.groupAccount"),auth:g(h)("api.authBearer"),endpoints:y},null,8,["title","auth"]),ce(nt,{title:g(h)("api.groupManagement"),auth:g(h)("api.authManager"),endpoints:A},null,8,["title","auth"]),ce(nt,{title:g(h)("api.groupSuperadmin"),auth:g(h)("api.authSuperadmin"),endpoints:D},null,8,["title","auth"])],64)):B("",!0),!g(hi)&&s.value==="overview"?(k(),E("p",mf,b(g(h)("app.standardUserNote")),1)):B("",!0)],64)):(k(),it(lc,{key:1})),d("p",bf,b(g(h)("app.footer")),1)]))}};sa(vf).mount("#app"); diff --git a/API Server/internal/api/dist/index.html b/API Server/internal/api/dist/index.html index 857c41d..4188f76 100644 --- a/API Server/internal/api/dist/index.html +++ b/API Server/internal/api/dist/index.html @@ -6,7 +6,7 @@ DriverVault · API Server - + diff --git a/API Server/internal/config/config.go b/API Server/internal/config/config.go index d9f7aae..9d83c15 100644 --- a/API Server/internal/config/config.go +++ b/API Server/internal/config/config.go @@ -47,7 +47,7 @@ func Load() Config { return Config{ Addr: normalizeAddr(firstEnv("API_ADDR", "PORT"), ":8080"), PocketBaseURL: strings.TrimRight(firstEnvOr("http://10.2.1.10:8027", "POCKETBASE_URL", "PB_URL"), "/"), - WebAppURL: strings.TrimRight(getenv("WEBAPP_URL", "http://localhost:5173"), "/"), + WebAppURL: strings.TrimRight(getenv("WEBAPP_URL", "http://localhost:8090"), "/"), AllowOrigins: splitCSV(firstEnvOr("*", "CORS_ALLOW_ORIGINS", "CORS_ORIGINS")), UsersCollection: getenv("AUTH_USERS_COLLECTION", "users"), PluginsFile: getenv("PLUGINS_FILE", "plugins.json"), diff --git a/API Server/panel/src/components/WebAppCard.vue b/API Server/panel/src/components/WebAppCard.vue index 7edcd7d..66205d3 100644 --- a/API Server/panel/src/components/WebAppCard.vue +++ b/API Server/panel/src/components/WebAppCard.vue @@ -91,7 +91,7 @@ async function save() {
- +
@@ -100,7 +100,7 @@ async function save() { id="web-origins" v-model="form.origins" class="dh-input" - placeholder="http://localhost:5173, https://app.example.com" + placeholder="http://localhost:8090, https://app.example.com" />

{{ tSplit("webapp.originsHint", "star").before