Split Web App Settings into tabs; allow unset plugin Control mode
Web App: Settings is now two tabs — Personal settings (account, appearance, profile, privacy, advanced/danger) and Integrations (Toyota, Anker Solix). Panels use v-show so loaded state and in-flight edits survive a tab switch. Adds settings.tabs.* labels (en/pl/da). API Server panel: non-required select config fields now render a leading "Not set" option, so a superadmin can leave the Anker Solix Control mode (and Toyota Brand) unset at the global layer. Previously every option was a real value, forcing a pick that won the cascade and locked organizations and users out of choosing their own mode. The server side already treated an empty value as "abstain"; this closes the UI gap. Adds plugins.notSet label (en/pl/da) and rebuilds the embedded panel dist. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
318e9c1670
commit
8656aaaee3
10 files changed
+50
-3
No files matched your search
+1
-1
@@ -14,4 +14,4 @@
|
|||||||
* @vue/runtime-dom v3.5.39
|
* @vue/runtime-dom v3.5.39
|
||||||
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
||||||
* @license MIT
|
* @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"?`<svg>${e}</svg>`:n==="mathml"?`<math>${e}</math>`: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;i<ei.length;i++){const o=ei[i]+n;if(o in e)return Ws[t]=o}return t}function Rl(e,t,s,n){return e.tagName==="TEXTAREA"&&(t==="width"||t==="height")&&ne(n)&&s===n}const ti="http://www.w3.org/1999/xlink";function si(e,t,s,n,i,o=Do(t)){n&&t.startsWith("xlink:")?s==null?e.removeAttributeNS(ti,t.slice(6,t.length)):e.setAttributeNS(ti,t,s):s==null||o&&!wi(s)?e.removeAttribute(t):e.setAttribute(t,o?"":Ve(s)?String(s):s)}function ni(e,t,s,n,i){if(t==="innerHTML"||t==="textContent"){s!=null&&(e[t]=t==="innerHTML"?_o(s):s);return}const o=e.tagName;if(t==="value"&&o!=="PROGRESS"&&!o.includes("-")){const l=o==="OPTION"?e.getAttribute("value")||"":e.value,a=s==null?e.type==="checkbox"?"on":"":String(s);(l!==a||!("_value"in e))&&(e.value=a),s==null&&e.removeAttribute(t),e._value=s;return}let r=!1;if(s===""||s==null){const l=typeof e[t];l==="boolean"?s=wi(s):s==null&&l==="string"?(s="",r=!0):l==="number"&&(s=0,r=!0)}try{e[t]=s}catch{}r&&e.removeAttribute(i||t)}function Xe(e,t,s,n){e.addEventListener(t,s,n)}function Il(e,t,s,n){e.removeEventListener(t,s,n)}const ii=Symbol("_vei");function Ul(e,t,s,n,i=null){const o=e[ii]||(e[ii]={}),r=o[t];if(n&&r)r.value=n;else{const[l,a]=Nl(t);if(n){const p=o[t]=Ll(n,i);Xe(e,l,p,a)}else r&&(Il(e,l,r,a),o[t]=void 0)}}const jl=/(Once|Passive|Capture)$/,Ml=/^on:?(?:Once|Passive|Capture)$/;function Nl(e){let t,s;for(;(s=e.match(jl))&&!Ml.test(e);)t||(t={}),e=e.slice(0,e.length-s[1].length),t[s[1].toLowerCase()]=!0;return[e[2]===":"?e.slice(3):ct(e.slice(2)),t]}let Gs=0;const Dl=Promise.resolve(),zl=()=>Gs||(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;a<r.length&&!n._stopped;a++){const p=r[a];p&&Oe(p,t,5,l)}}else Oe(i,t,5,[n])};return s.value=e,s.attached=zl(),s}const oi=e=>e.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._fals
Line truncated
|
**/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"?`<svg>${e}</svg>`:n==="mathml"?`<math>${e}</math>`: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;i<ei.length;i++){const o=ei[i]+n;if(o in e)return Ws[t]=o}return t}function Rl(e,t,s,n){return e.tagName==="TEXTAREA"&&(t==="width"||t==="height")&&ne(n)&&s===n}const ti="http://www.w3.org/1999/xlink";function si(e,t,s,n,i,o=Do(t)){n&&t.startsWith("xlink:")?s==null?e.removeAttributeNS(ti,t.slice(6,t.length)):e.setAttributeNS(ti,t,s):s==null||o&&!wi(s)?e.removeAttribute(t):e.setAttribute(t,o?"":Ve(s)?String(s):s)}function ni(e,t,s,n,i){if(t==="innerHTML"||t==="textContent"){s!=null&&(e[t]=t==="innerHTML"?_o(s):s);return}const o=e.tagName;if(t==="value"&&o!=="PROGRESS"&&!o.includes("-")){const l=o==="OPTION"?e.getAttribute("value")||"":e.value,a=s==null?e.type==="checkbox"?"on":"":String(s);(l!==a||!("_value"in e))&&(e.value=a),s==null&&e.removeAttribute(t),e._value=s;return}let r=!1;if(s===""||s==null){const l=typeof e[t];l==="boolean"?s=wi(s):s==null&&l==="string"?(s="",r=!0):l==="number"&&(s=0,r=!0)}try{e[t]=s}catch{}r&&e.removeAttribute(i||t)}function Xe(e,t,s,n){e.addEventListener(t,s,n)}function Il(e,t,s,n){e.removeEventListener(t,s,n)}const ii=Symbol("_vei");function Ul(e,t,s,n,i=null){const o=e[ii]||(e[ii]={}),r=o[t];if(n&&r)r.value=n;else{const[l,a]=Nl(t);if(n){const p=o[t]=Ll(n,i);Xe(e,l,p,a)}else r&&(Il(e,l,r,a),o[t]=void 0)}}const jl=/(Once|Passive|Capture)$/,Ml=/^on:?(?:Once|Passive|Capture)$/;function Nl(e){let t,s;for(;(s=e.match(jl))&&!Ml.test(e);)t||(t={}),e=e.slice(0,e.length-s[1].length),t[s[1].toLowerCase()]=!0;return[e[2]===":"?e.slice(3):ct(e.slice(2)),t]}let Gs=0;const Dl=Promise.resolve(),zl=()=>Gs||(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;a<r.length&&!n._stopped;a++){const p=r[a];p&&Oe(p,t,5,l)}}else Oe(i,t,5,[n])};return s.value=e,s.attached=zl(),s}const oi=e=>e.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._fals
Line truncated
|
||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="theme-color" content="#2563eb" />
|
<meta name="theme-color" content="#2563eb" />
|
||||||
<title>DriverVault · API Server</title>
|
<title>DriverVault · API Server</title>
|
||||||
<script type="module" crossorigin src="/assets/index-frPmZHwa.js"></script>
|
<script type="module" crossorigin src="/assets/index-D-rTgBao.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-D3MeNcl7.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-D3MeNcl7.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -187,6 +187,9 @@ const healthClass = (s) =>
|
|||||||
{{ f.label || f.key }}<span v-if="f.required" class="text-danger"> *</span>
|
{{ f.label || f.key }}<span v-if="f.required" class="text-danger"> *</span>
|
||||||
</label>
|
</label>
|
||||||
<select v-if="f.type === 'select'" v-model="drafts[p.name][f.key]" class="dh-select">
|
<select v-if="f.type === 'select'" v-model="drafts[p.name][f.key]" class="dh-select">
|
||||||
|
<!-- A non-required select can be left unset (empty), so the global
|
||||||
|
layer abstains and lower layers (org / user) may choose. -->
|
||||||
|
<option v-if="!f.required" value="">{{ t("plugins.notSet") }}</option>
|
||||||
<option v-for="o in f.options || []" :key="o.value" :value="o.value">
|
<option v-for="o in f.options || []" :key="o.value" :value="o.value">
|
||||||
{{ o.label || o.value }}
|
{{ o.label || o.value }}
|
||||||
</option>
|
</option>
|
||||||
|
|||||||
@@ -133,6 +133,7 @@
|
|||||||
"configure": "Konfigurer",
|
"configure": "Konfigurer",
|
||||||
"close": "Luk",
|
"close": "Luk",
|
||||||
"noConfig": "Dette plugin kræver ingen konfiguration.",
|
"noConfig": "Dette plugin kræver ingen konfiguration.",
|
||||||
|
"notSet": "Ikke angivet — lad organisationer og brugere vælge",
|
||||||
"capabilities": "Funktioner",
|
"capabilities": "Funktioner",
|
||||||
"saved": "Gemt.",
|
"saved": "Gemt.",
|
||||||
"checking": "Tjekker…",
|
"checking": "Tjekker…",
|
||||||
|
|||||||
@@ -133,6 +133,7 @@
|
|||||||
"configure": "Configure",
|
"configure": "Configure",
|
||||||
"close": "Close",
|
"close": "Close",
|
||||||
"noConfig": "This plugin takes no configuration.",
|
"noConfig": "This plugin takes no configuration.",
|
||||||
|
"notSet": "Not set — let organizations and users choose",
|
||||||
"capabilities": "Capabilities",
|
"capabilities": "Capabilities",
|
||||||
"saved": "Saved.",
|
"saved": "Saved.",
|
||||||
"checking": "Checking…",
|
"checking": "Checking…",
|
||||||
|
|||||||
@@ -133,6 +133,7 @@
|
|||||||
"configure": "Konfiguruj",
|
"configure": "Konfiguruj",
|
||||||
"close": "Zamknij",
|
"close": "Zamknij",
|
||||||
"noConfig": "Ta wtyczka nie wymaga konfiguracji.",
|
"noConfig": "Ta wtyczka nie wymaga konfiguracji.",
|
||||||
|
"notSet": "Nie ustawiono — pozwól organizacjom i użytkownikom wybrać",
|
||||||
"capabilities": "Możliwości",
|
"capabilities": "Możliwości",
|
||||||
"saved": "Zapisano.",
|
"saved": "Zapisano.",
|
||||||
"checking": "Sprawdzanie…",
|
"checking": "Sprawdzanie…",
|
||||||
|
|||||||
@@ -134,6 +134,10 @@
|
|||||||
"eyebrow": "Konto",
|
"eyebrow": "Konto",
|
||||||
"title": "Indstillinger",
|
"title": "Indstillinger",
|
||||||
"subtitle": "Administrer din konto, udseende og dine data.",
|
"subtitle": "Administrer din konto, udseende og dine data.",
|
||||||
|
"tabs": {
|
||||||
|
"personal": "Personlige indstillinger",
|
||||||
|
"integrations": "Integrationer"
|
||||||
|
},
|
||||||
|
|
||||||
"account": {
|
"account": {
|
||||||
"title": "Konto",
|
"title": "Konto",
|
||||||
|
|||||||
@@ -152,6 +152,10 @@
|
|||||||
"eyebrow": "Account",
|
"eyebrow": "Account",
|
||||||
"title": "Settings",
|
"title": "Settings",
|
||||||
"subtitle": "Manage your account, appearance, and data.",
|
"subtitle": "Manage your account, appearance, and data.",
|
||||||
|
"tabs": {
|
||||||
|
"personal": "Personal settings",
|
||||||
|
"integrations": "Integrations"
|
||||||
|
},
|
||||||
|
|
||||||
"account": {
|
"account": {
|
||||||
"title": "Account",
|
"title": "Account",
|
||||||
|
|||||||
@@ -138,6 +138,10 @@
|
|||||||
"eyebrow": "Konto",
|
"eyebrow": "Konto",
|
||||||
"title": "Ustawienia",
|
"title": "Ustawienia",
|
||||||
"subtitle": "Zarządzaj kontem, wyglądem i danymi.",
|
"subtitle": "Zarządzaj kontem, wyglądem i danymi.",
|
||||||
|
"tabs": {
|
||||||
|
"personal": "Ustawienia osobiste",
|
||||||
|
"integrations": "Integracje"
|
||||||
|
},
|
||||||
|
|
||||||
"account": {
|
"account": {
|
||||||
"title": "Konto",
|
"title": "Konto",
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ const loading = ref(true);
|
|||||||
const loadError = ref("");
|
const loadError = ref("");
|
||||||
const profile = ref(null);
|
const profile = ref(null);
|
||||||
|
|
||||||
|
// Settings is split into two tabs: personal account settings and external
|
||||||
|
// integrations. The panels stay mounted (v-show) so their loaded state and
|
||||||
|
// in-flight edits survive a tab switch.
|
||||||
|
const activeTab = ref("personal"); // "personal" | "integrations"
|
||||||
|
|
||||||
async function load() {
|
async function load() {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
loadError.value = "";
|
loadError.value = "";
|
||||||
@@ -722,7 +727,24 @@ onBeforeUnmount(() => {
|
|||||||
<p v-if="loadError" class="mb-4 rounded-control bg-danger-soft px-4 py-3 text-sm font-medium text-danger">{{ loadError }}</p>
|
<p v-if="loadError" class="mb-4 rounded-control bg-danger-soft px-4 py-3 text-sm font-medium text-danger">{{ loadError }}</p>
|
||||||
<p v-if="loading" class="text-muted">{{ t("common.loading") }}</p>
|
<p v-if="loading" class="text-muted">{{ t("common.loading") }}</p>
|
||||||
|
|
||||||
<div v-else-if="profile" class="space-y-6">
|
<div v-else-if="profile">
|
||||||
|
<!-- Tabs: personal settings vs. integrations -->
|
||||||
|
<div class="mb-6 flex gap-2 border-b border-subtle">
|
||||||
|
<button
|
||||||
|
v-for="tab in ['personal', 'integrations']"
|
||||||
|
:key="tab"
|
||||||
|
class="-mb-px border-b-2 px-1 pb-3 text-sm font-semibold transition-colors"
|
||||||
|
:class="activeTab === tab
|
||||||
|
? 'border-accent text-strong'
|
||||||
|
: 'border-transparent text-muted hover:text-body'"
|
||||||
|
@click="activeTab = tab"
|
||||||
|
>
|
||||||
|
{{ t(`settings.tabs.${tab}`) }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Personal settings -->
|
||||||
|
<div v-show="activeTab === 'personal'" class="space-y-6">
|
||||||
<!-- Account -->
|
<!-- Account -->
|
||||||
<section class="dh-card p-6">
|
<section class="dh-card p-6">
|
||||||
<h2 class="mb-4 text-lg font-bold tracking-[-0.02em] text-strong">{{ t("settings.account.title") }}</h2>
|
<h2 class="mb-4 text-lg font-bold tracking-[-0.02em] text-strong">{{ t("settings.account.title") }}</h2>
|
||||||
@@ -891,7 +913,10 @@ onBeforeUnmount(() => {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Integrations -->
|
<!-- Integrations -->
|
||||||
|
<div v-show="activeTab === 'integrations'" class="space-y-6">
|
||||||
<section v-if="toyota || anker" class="dh-card p-6">
|
<section v-if="toyota || anker" class="dh-card p-6">
|
||||||
<h2 class="text-lg font-bold tracking-[-0.02em] text-strong">{{ t("settings.integrations.title") }}</h2>
|
<h2 class="text-lg font-bold tracking-[-0.02em] text-strong">{{ t("settings.integrations.title") }}</h2>
|
||||||
<p class="mb-4 mt-1 text-sm text-muted">{{ t("settings.integrations.subtitle") }}</p>
|
<p class="mb-4 mt-1 text-sm text-muted">{{ t("settings.integrations.subtitle") }}</p>
|
||||||
@@ -1197,7 +1222,10 @@ onBeforeUnmount(() => {
|
|||||||
<p v-if="ankerError" class="mt-2 text-sm text-danger">{{ ankerError }}</p>
|
<p v-if="ankerError" class="mt-2 text-sm text-danger">{{ ankerError }}</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Personal settings (continued) -->
|
||||||
|
<div v-show="activeTab === 'personal'" class="space-y-6">
|
||||||
<!-- Privacy & Security -->
|
<!-- Privacy & Security -->
|
||||||
<section class="dh-card p-6">
|
<section class="dh-card p-6">
|
||||||
<div class="mb-4 flex items-center justify-between">
|
<div class="mb-4 flex items-center justify-between">
|
||||||
@@ -1279,6 +1307,7 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
<p v-if="deleteError" class="mt-3 text-sm font-medium text-danger">{{ deleteError }}</p>
|
<p v-if="deleteError" class="mt-3 text-sm font-medium text-danger">{{ deleteError }}</p>
|
||||||
</section>
|
</section>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
Reference in new issue
Block a user