@arcblock/did-connect-service 4.1.33 → 4.1.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_generated/asset-bytes.js +1 -1
- package/dist/_generated/asset-manifest.js +4 -4
- package/dist/_generated/login-bundle-string.d.ts +1 -1
- package/dist/_generated/login-bundle-string.d.ts.map +1 -1
- package/dist/_generated/login-bundle-string.js +1 -1
- package/dist/_generated/login-bundle-string.js.map +1 -1
- package/dist/assets/{admin-core.16704b2e.js → admin-core.69147606.js} +80 -2
- package/dist/assets/{admin-extra.8243c7f4.js → admin-extra.979f5e56.js} +57 -14
- package/dist/assets/{admin.7c5d5f04.css → admin.76aa0931.css} +11 -0
- package/dist/assets/{login.fb0bf7b4.js → login.566f858e.js} +1 -1
- package/dist/blocklet-service.d.ts +8 -1
- package/dist/blocklet-service.d.ts.map +1 -1
- package/dist/blocklet-service.js +6 -1
- package/dist/blocklet-service.js.map +1 -1
- package/dist/handlers/auth-handler.d.ts +7 -0
- package/dist/handlers/auth-handler.d.ts.map +1 -1
- package/dist/handlers/auth-handler.js +7 -2
- package/dist/handlers/auth-handler.js.map +1 -1
- package/dist/handlers/avatar-handler.d.ts.map +1 -1
- package/dist/handlers/avatar-handler.js +6 -0
- package/dist/handlers/avatar-handler.js.map +1 -1
- package/dist/handlers/did-connect-handler.d.ts +2 -0
- package/dist/handlers/did-connect-handler.d.ts.map +1 -1
- package/dist/handlers/did-connect-handler.js +2 -2
- package/dist/handlers/did-connect-handler.js.map +1 -1
- package/dist/handlers/email-login-handler.d.ts +2 -0
- package/dist/handlers/email-login-handler.d.ts.map +1 -1
- package/dist/handlers/email-login-handler.js +2 -2
- package/dist/handlers/email-login-handler.js.map +1 -1
- package/dist/handlers/oauth-handler.d.ts +2 -0
- package/dist/handlers/oauth-handler.d.ts.map +1 -1
- package/dist/handlers/oauth-handler.js +3 -4
- package/dist/handlers/oauth-handler.js.map +1 -1
- package/dist/handlers/passkey-handler.d.ts +1 -0
- package/dist/handlers/passkey-handler.d.ts.map +1 -1
- package/dist/handlers/passkey-handler.js +5 -4
- package/dist/handlers/passkey-handler.js.map +1 -1
- package/dist/handlers/ticket-handler.d.ts +2 -0
- package/dist/handlers/ticket-handler.d.ts.map +1 -1
- package/dist/handlers/ticket-handler.js +2 -2
- package/dist/handlers/ticket-handler.js.map +1 -1
- package/dist/identity/session-cookie.d.ts +11 -0
- package/dist/identity/session-cookie.d.ts.map +1 -0
- package/dist/identity/session-cookie.js +17 -0
- package/dist/identity/session-cookie.js.map +1 -0
- package/dist/pages/admin/scripts/dialog.d.ts +1 -1
- package/dist/pages/admin/scripts/dialog.d.ts.map +1 -1
- package/dist/pages/admin/scripts/dialog.js +29 -2
- package/dist/pages/admin/scripts/dialog.js.map +1 -1
- package/dist/pages/admin/scripts/utils.d.ts +1 -1
- package/dist/pages/admin/scripts/utils.d.ts.map +1 -1
- package/dist/pages/admin/scripts/utils.js +35 -0
- package/dist/pages/admin/scripts/utils.js.map +1 -1
- package/dist/pages/admin/styles.d.ts +1 -1
- package/dist/pages/admin/styles.d.ts.map +1 -1
- package/dist/pages/admin/styles.js +11 -0
- package/dist/pages/admin/styles.js.map +1 -1
- package/dist/pages/admin/tab-access.d.ts.map +1 -1
- package/dist/pages/admin/tab-access.js +6 -0
- package/dist/pages/admin/tab-access.js.map +1 -1
- package/dist/pages/admin/tab-appearance.d.ts.map +1 -1
- package/dist/pages/admin/tab-appearance.js +4 -0
- package/dist/pages/admin/tab-appearance.js.map +1 -1
- package/dist/pages/admin/tab-branding.d.ts.map +1 -1
- package/dist/pages/admin/tab-branding.js +4 -0
- package/dist/pages/admin/tab-branding.js.map +1 -1
- package/dist/pages/admin/tab-federation.d.ts +1 -1
- package/dist/pages/admin/tab-federation.d.ts.map +1 -1
- package/dist/pages/admin/tab-federation.js +21 -11
- package/dist/pages/admin/tab-federation.js.map +1 -1
- package/dist/pages/admin/tab-invitations.d.ts +1 -1
- package/dist/pages/admin/tab-invitations.d.ts.map +1 -1
- package/dist/pages/admin/tab-invitations.js +4 -0
- package/dist/pages/admin/tab-invitations.js.map +1 -1
- package/dist/pages/admin/tab-members.d.ts +1 -1
- package/dist/pages/admin/tab-members.d.ts.map +1 -1
- package/dist/pages/admin/tab-members.js +7 -0
- package/dist/pages/admin/tab-members.js.map +1 -1
- package/dist/pages/admin/tab-profile-accounts.d.ts.map +1 -1
- package/dist/pages/admin/tab-profile-accounts.js +10 -0
- package/dist/pages/admin/tab-profile-accounts.js.map +1 -1
- package/dist/pages/admin/tab-profile.d.ts +1 -1
- package/dist/pages/admin/tab-profile.d.ts.map +1 -1
- package/dist/pages/admin/tab-profile.js +6 -0
- package/dist/pages/admin/tab-profile.js.map +1 -1
- package/dist/pages/admin/tab-settings.d.ts.map +1 -1
- package/dist/pages/admin/tab-settings.js +11 -3
- package/dist/pages/admin/tab-settings.js.map +1 -1
- package/dist/pages/admin-instances-page.d.ts.map +1 -1
- package/dist/pages/admin-instances-page.js +5 -1
- package/dist/pages/admin-instances-page.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/assets/admin-core.1fc3260c.js +0 -1623
- package/dist/assets/admin-core.5a2d69b0.js +0 -2067
- package/dist/assets/admin-core.9fff6042.js +0 -2083
- package/dist/assets/admin-core.a13c2729.js +0 -1461
- package/dist/assets/admin-core.a63bbc69.js +0 -1464
- package/dist/assets/admin-core.c0b5af61.js +0 -1393
- package/dist/assets/admin-core.cbbdbe6d.js +0 -1465
- package/dist/assets/admin-core.e83fd379.js +0 -1397
- package/dist/assets/admin-extra.143b5774.js +0 -2543
- package/dist/assets/admin-extra.35b91f98.js +0 -2543
- package/dist/assets/admin-extra.531b187f.js +0 -2552
- package/dist/assets/admin-extra.5b309e5e.js +0 -2547
- package/dist/assets/admin-extra.7ca9c16b.js +0 -2529
- package/dist/assets/admin-extra.841b2bbe.js +0 -2530
- package/dist/assets/admin-extra.afcb3651.js +0 -2528
- package/dist/assets/admin-extra.d12f05bb.js +0 -2553
- package/dist/assets/admin.c26bb17a.css +0 -2219
- package/dist/assets/admin.dfeedbe4.css +0 -2229
- package/dist/assets/login.0bf3d739.js +0 -720
- package/dist/assets/login.4404e695.js +0 -720
- package/dist/assets/login.76c483c2.js +0 -720
- package/dist/assets/login.bb9f8b03.js +0 -720
- package/dist/assets/login.d3f05790.js +0 -720
- package/dist/assets/login.fa25f37a.js +0 -720
|
@@ -485,6 +485,10 @@ async function saveBrandingLanguages() {
|
|
|
485
485
|
else showToast(__t("branding.langUpdateFailed"), "error");
|
|
486
486
|
}
|
|
487
487
|
|
|
488
|
+
// Button-driven mutations must not double-submit (submitOnce — utils.ts).
|
|
489
|
+
// Logo uploads are driven by <input type=file> change events, not clicks.
|
|
490
|
+
saveBrandingInfo = submitOnce(saveBrandingInfo);
|
|
491
|
+
|
|
488
492
|
registerTab("branding", loadBranding);
|
|
489
493
|
`)();
|
|
490
494
|
//# sourceMappingURL=tab-branding.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tab-branding.js","sourceRoot":"","sources":["../../../src/pages/admin/tab-branding.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAIxD,oDAAoD;AACpD,MAAM,QAAQ,GAAG,weAAwe,CAAC;AAC1f,MAAM,SAAS,GAAG,wKAAwK,CAAC;AAC3L,MAAM,WAAW,GAAG,sOAAsO,CAAC;AAC3P,MAAM,YAAY,GAAG,2OAA2O,CAAC;AACjQ,MAAM,aAAa,GAAG,0KAA0K,CAAC;AACjM,MAAM,eAAe,GAAG,+NAA+N,CAAC;AACxP,MAAM,cAAc,GAAG,+NAA+N,CAAC;AACvP,MAAM,UAAU,GAAG,yRAAyR,CAAC;AAE7S,MAAM,UAAU,GAAG;IACjB,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,EAAE,mBAAmB,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,EAAE;IACvH,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,cAAc,EAAE;IAC3H,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,EAAE,mBAAmB,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE;IAChH,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IACpH,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,qBAAqB,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,gBAAgB,EAAE;IAChI,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,sBAAsB,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE;IACjI,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,uBAAuB,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE;IAClI,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,oBAAoB,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,eAAe,EAAE;CACxH,CAAC;AAEF,MAAM,SAAS,GAAG;IAChB,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE;IAC/B,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,0BAA0B,EAAE;CACjD,CAAC;AAEF,0BAA0B;AAC1B,MAAM,UAAU,GAA2B;IACzC,MAAM,EAAE,yBAAyB;IACjC,aAAa,EAAE,yBAAyB;IACxC,IAAI,EAAE,uBAAuB;IAC7B,WAAW,EAAE,uBAAuB;IACpC,OAAO,EAAE,0BAA0B;IACnC,UAAU,EAAE,sBAAsB;IAClC,iBAAiB,EAAE,6BAA6B;IAChD,kBAAkB,EAAE,8BAA8B;CACnD,CAAC;AAEF,SAAS,YAAY,CAAC,CAAM;IAC1B,OAAO,UAAU,CAAC,GAAG,CACnB,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC;yCACmB,CAAC,CAAC,IAAI;;uCAER,CAAC,CAAC,IAAI;uCACN,CAAC,CAAC,IAAI;;sDAES,CAAC,CAAC,IAAI;;iCAE3B,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;qCACR,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;;6DAEa,CAAC,CAAC,IAAI;;wCAE3B,CAAC,CAAC,eAAe,CAAC;mIACyE,CAAC,CAAC,IAAI;;WAE9H,CACR,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,CAAM;IACpC,OAAO,UAAU,CAAC;;8BAEU,CAAC,CAAC,gBAAgB,CAAC;4BACrB,CAAC,CAAC,eAAe,CAAC;;;;;uCAKP,CAAC,CAAC,sBAAsB,CAAC;0CACtB,CAAC,CAAC,uBAAuB,CAAC;;;QAG5D,SAAS,CAAC,GAAG,CACb,CAAC,CAAC,EAAE,EAAE,CAAC,wDAAwD,CAAC,CAAC,IAAI,0BAA0B,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,eAAe,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAC7L,CAAC,IAAI,CAAC,UAAU,CAAC;;;;;;;;wCAQgB,CAAC,CAAC,sBAAsB,CAAC;yDACR,CAAC,CAAC,mBAAmB,CAAC;;;;;wCAKvC,CAAC,CAAC,eAAe,CAAC,qCAAqC,CAAC,CAAC,mBAAmB,CAAC;yEAC5C,CAAC,CAAC,0BAA0B,CAAC;;;wCAG9D,CAAC,CAAC,cAAc,CAAC,qCAAqC,CAAC,CAAC,kBAAkB,CAAC;uEAC5C,CAAC,CAAC,yBAAyB,CAAC;;;;sCAI7D,CAAC,CAAC,sBAAsB,CAAC,qCAAqC,CAAC,CAAC,mBAAmB,CAAC;8DAC5D,CAAC,CAAC,0BAA0B,CAAC;;;;;;;;;uCASpD,CAAC,CAAC,wBAAwB,CAAC;0CACxB,CAAC,CAAC,yBAAyB,CAAC;;;QAG9D,YAAY,CAAC,CAAC,CAAC;;;;;;;;wCAQiB,CAAC,CAAC,2BAA2B,CAAC;yDACb,CAAC,CAAC,wBAAwB,CAAC;;;;;wCAK5C,CAAC,CAAC,yBAAyB,CAAC;oFACgB,CAAC,CAAC,oCAAoC,CAAC;;;wCAGnF,CAAC,CAAC,wBAAwB,CAAC;mFACgB,CAAC,CAAC,mCAAmC,CAAC;;;;sCAInF,CAAC,CAAC,wBAAwB,CAAC;;;;;;;;;+DASF,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;;;wCAW/C,CAAC,CAAC,uBAAuB,CAAC;yDACT,CAAC,CAAC,oBAAoB,CAAC;;;;sCAI1C,CAAC,CAAC,sBAAsB,CAAC,qCAAqC,CAAC,CAAC,0BAA0B,CAAC;gFACjD,CAAC,CAAC,iCAAiC,CAAC;;;;;;;;;;uCAU7E,CAAC,CAAC,oBAAoB,CAAC;;6EAEe,CAAC,CAAC,yBAAyB,CAAC;yDAChD,CAAC,CAAC,qBAAqB,CAAC;;;;CAIhF,CAAC;AACF,CAAC;AAED,mFAAmF;AACnF,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,eAAe,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAkE/D,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;;;;;;;;;;;;;mBAaX,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBA+GjB,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;6BA2Bf,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAkF3B,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC
|
|
1
|
+
{"version":3,"file":"tab-branding.js","sourceRoot":"","sources":["../../../src/pages/admin/tab-branding.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
type TFn = (key: string, params?: Record<string, string>) => string;
|
|
5
5
|
export declare function federationSectionHtml(t: TFn): string;
|
|
6
|
-
export declare const FEDERATION_SECTION_SCRIPT = "\n// \u2500\u2500\u2500 Federation (Unified Login) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nvar __fedData = null;\nvar __fedLoading = false;\n\nvar FED_ICON_REMOVE = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 6h18\"/><path d=\"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6\"/><path d=\"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2\"/></svg>';\nvar FED_ICON_REFRESH = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/><path d=\"M3 3v5h5\"/><path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"/><path d=\"M21 21v-5h-5\"/></svg>';\nvar FED_ICON_CHECK = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"/></svg>';\nvar FED_ICON_UNLINK = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M15 7h3a5 5 0 0 1 0 10h-3m-6 0H6a5 5 0 0 1 0-10h3\"/><line x1=\"2\" y1=\"2\" x2=\"22\" y2=\"22\"/></svg>';\nvar FED_ICON_DISABLE = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"4.93\" y1=\"4.93\" x2=\"19.07\" y2=\"19.07\"/></svg>';\nvar FED_ICON_ENABLE = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18.36 6.64a9 9 0 1 1-12.73 0\"/><line x1=\"12\" y1=\"2\" x2=\"12\" y2=\"12\"/></svg>';\n\nfunction fedIconBtn(icon, title, onclick, variant) {\n var cls = variant === \"danger\" ? \"ap-icon-btn ap-icon-btn-danger\" : variant === \"success\" ? \"ap-icon-btn ap-icon-btn-success\" : \"ap-icon-btn\";\n return '<button class=\"' + cls + '\" title=\"' + fedEscAttr(title) + '\" onclick=\"' + onclick + '\">' + icon + '</button>';\n}\n\nfunction fedNormalizeUrl(input) {\n var s = (input || \"\").trim();\n if (!s) return \"\";\n if (!/^https?:\\/\\//i.test(s)) s = \"https://\" + s;\n try { return new URL(s).origin; } catch(e) { return s; }\n}\n\nfunction fedFormatUrl(el) {\n clearTimeout(el._fedTimer);\n el._fedTimer = setTimeout(function() {\n var v = el.value.trim();\n if (!v) return;\n var norm = fedNormalizeUrl(v);\n if (norm && norm !== v && norm !== \"https://\") {\n el.value = norm;\n }\n }, 800);\n}\n\nfunction fedLoad() {\n api(\"GET\", \"/admin/federation\").then(function(data) {\n if (!data || data.error) {\n __fedData = { role: null, enabled: false, sites: [], delegation: { exists: false } };\n } else {\n __fedData = data;\n }\n fedRender();\n });\n}\n\nfunction fedRender() {\n var el = document.getElementById(\"fed-section\");\n if (!el || !__fedData) return;\n if (!__fedData.role) {\n el.innerHTML = fedRenderUnconfigured();\n } else if (__fedData.role === \"master\") {\n el.innerHTML = fedRenderMaster();\n } else {\n el.innerHTML = fedRenderMember();\n }\n}\n\nfunction fedRenderUnconfigured() {\n return '<p class=\"settings-card-desc\">' + __t(\"federation.desc\") + '</p>'\n + '<div class=\"fed-init-buttons\">'\n + '<button class=\"btn\" id=\"fed-enable-btn\" onclick=\"fedEnable()\"' + (__fedLoading ? ' disabled' : '') + '>'\n + (__fedLoading ? '<span class=\"spinner\"></span> ' : '') + __t(\"federation.enableMaster\") + '</button>'\n + '<button class=\"btn btn-secondary\" onclick=\"fedOpenJoin()\"' + (__fedLoading ? ' disabled' : '') + '>'\n + __t(\"federation.joinMember\") + '</button>'\n + '</div>';\n}\n\nfunction fedRenderMaster() {\n var html = '<div style=\"padding:12px 20px;display:flex;align-items:center;gap:8px\">'\n + '<span class=\"fed-status-badge master\">★ ' + __t(\"federation.masterSite\") + '</span>'\n + '<span style=\"font-size:13px;color:var(--text-secondary)\">' + __t(\"federation.masterDesc\") + '</span>'\n + '</div>';\n\n // Sites header with refresh button\n html += '<div style=\"padding:0 20px 8px;display:flex;justify-content:space-between;align-items:center\">'\n + '<span style=\"font-size:13px;font-weight:600;color:var(--text)\">' + __t(\"federation.sites\") + '</span>'\n + '<button class=\"ap-icon-btn\" title=\"' + __t(\"federation.refresh\") + '\" onclick=\"fedRefresh()\" id=\"fed-refresh-btn\">' + FED_ICON_REFRESH + '</button>'\n + '</div>';\n\n // Sort: master first, then approved, then pending\n var sites = (__fedData.sites || []).slice().sort(function(a, b) {\n if (a.isMaster) return -1;\n if (b.isMaster) return 1;\n if (a.status === \"pending\" && b.status !== \"pending\") return 1;\n if (b.status === \"pending\" && a.status !== \"pending\") return -1;\n return 0;\n });\n var currentOrigin = location.origin;\n for (var i = 0; i < sites.length; i++) {\n var s = sites[i];\n var star = s.isMaster ? '<span class=\"fed-star\">★</span> ' : '';\n var isMe = s.url && fedNormalizeUrl(s.url) === currentOrigin;\n var meTag = isMe ? ' <span style=\"font-size:10px;padding:1px 5px;border-radius:999px;background:var(--blue-light,#dbeafe);color:var(--blue-muted,#1d4ed8);font-weight:500\">' + __t(\"federation.currentSite\") + '</span>' : '';\n var isPending = s.status === \"pending\";\n var statusBadge = isPending\n ? ' <span style=\"font-size:11px;padding:1px 6px;border-radius:999px;background:var(--yellow-light,#fef3c7);color:var(--yellow-text,#92400e);font-weight:500\">' + __t(\"federation.pending\") + '</span>'\n : '';\n\n var actions = '';\n if (s.isMaster) {\n // Disabled placeholder buttons for alignment with member rows\n actions = '<button class=\"ap-icon-btn\" disabled style=\"opacity:0.25;cursor:default\">' + FED_ICON_DISABLE + '</button>'\n + '<button class=\"ap-icon-btn\" disabled style=\"opacity:0.25;cursor:default\">' + FED_ICON_UNLINK + '</button>';\n } else if (isPending) {\n actions = fedIconBtn(FED_ICON_CHECK, __t(\"federation.approve\"), \"fedApproveSite('\" + s.did + \"')\", \"success\")\n + fedIconBtn(FED_ICON_REMOVE, __t(\"federation.reject\"), \"fedRejectSite('\" + s.did + \"','\" + fedEscAttr(s.name) + \"')\", \"danger\");\n } else {\n // Approved member: disable/enable toggle + unbind\n if (s.enabled === false) {\n actions = fedIconBtn(FED_ICON_ENABLE, __t(\"federation.enable\"), \"fedToggleMember('\" + s.did + \"',true)\")\n + fedIconBtn(FED_ICON_UNLINK, __t(\"federation.unbind\"), \"fedUnbindSite('\" + s.did + \"','\" + fedEscAttr(s.name) + \"')\", \"danger\");\n } else {\n actions = fedIconBtn(FED_ICON_DISABLE, __t(\"federation.disable\"), \"fedToggleMember('\" + s.did + \"',false)\", \"danger\")\n + fedIconBtn(FED_ICON_UNLINK, __t(\"federation.unbind\"), \"fedUnbindSite('\" + s.did + \"','\" + fedEscAttr(s.name) + \"')\", \"danger\");\n }\n }\n // Disabled badge for approved but disabled members\n if (!isPending && !s.isMaster && s.enabled === false) {\n statusBadge = ' <span style=\"font-size:11px;padding:1px 6px;border-radius:999px;background:var(--bg-tertiary,#e5e7eb);color:var(--text-tertiary)\">' + __t(\"federation.disabled\") + '</span>';\n }\n\n html += '<div class=\"fed-site-row\">'\n + '<div class=\"fed-site-name\">' + star + '<span class=\"fed-site-label\">' + fedEscHtml(s.name) + '</span>' + meTag + statusBadge + '</div>'\n + '<div class=\"fed-site-url\" title=\"' + fedEscAttr(s.url) + '\"><a href=\"' + fedEscAttr(s.url) + '\" target=\"_blank\">' + fedEscHtml(s.url) + '</a></div>'\n + '<div class=\"fed-site-actions\">' + actions + '</div>'\n + '</div>';\n }\n\n // Disband\n html += '<div style=\"padding:16px 20px;border-top:1px solid var(--border)\">'\n + '<button class=\"btn btn-danger btn-sm\" style=\"width:auto\" onclick=\"fedDisband()\">' + __t(\"federation.disband\") + '</button>'\n + '</div>';\n\n return html;\n}\n\nfunction fedRenderMember() {\n var enabled = __fedData.enabled;\n var isPending = !enabled && __fedData.role === \"member\";\n\n // Header: badge + status + toggle (no toggle for pending)\n var badgeClass = isPending ? 'style=\"background:var(--yellow-light,#fef3c7);color:var(--yellow-text,#92400e)\"' : '';\n var badgeText = isPending ? __t(\"federation.pending\") : __t(\"federation.member\");\n var statusText = isPending ? __t(\"federation.pendingDesc\") : (enabled ? __t(\"federation.enabled\") : __t(\"federation.disabled\"));\n\n var html = '<div style=\"padding:12px 20px;display:flex;align-items:center;gap:12px;justify-content:space-between\">'\n + '<div style=\"display:flex;align-items:center;gap:8px\">'\n + '<span class=\"fed-status-badge member\" ' + badgeClass + '>' + badgeText + '</span>'\n + '<span style=\"font-size:13px;color:var(--text-secondary)\">' + statusText + '</span>'\n + '</div>';\n if (!isPending) {\n html += '<label class=\"toggle-switch\">'\n + '<input type=\"checkbox\" ' + (enabled ? 'checked' : '') + ' onchange=\"fedToggle(this.checked)\" />'\n + '<span class=\"toggle-slider\"></span>'\n + '</label>';\n }\n html += '</div>';\n\n // Master URL\n if (__fedData.masterUrl) {\n html += '<div style=\"padding:0 20px 8px;font-size:13px;color:var(--text-secondary)\">'\n + __t(\"federation.delegatedTo\") + ' <a href=\"' + fedEscAttr(__fedData.masterUrl) + '\" target=\"_blank\" style=\"color:var(--blue)\">' + fedEscHtml(__fedData.masterUrl) + '</a>'\n + '</div>';\n }\n\n // Site list \u2014 show all sites with role labels; pending sites get \"pending\" instead of \"member\"\n if (__fedData.sites && __fedData.sites.length > 0) {\n // Sort: master first, then approved, then pending\n var memberSites = __fedData.sites.slice().sort(function(a, b) {\n if (a.isMaster) return -1;\n if (b.isMaster) return 1;\n if (a.status === \"pending\" && b.status !== \"pending\") return 1;\n if (b.status === \"pending\" && a.status !== \"pending\") return -1;\n return 0;\n });\n html += '<div style=\"padding:8px 20px 4px;display:flex;justify-content:space-between;align-items:center\">'\n + '<span style=\"font-size:13px;font-weight:600;color:var(--text)\">' + __t(\"federation.sites\") + '</span>'\n + '<button class=\"ap-icon-btn\" title=\"' + __t(\"federation.refresh\") + '\" onclick=\"fedRefresh()\" id=\"fed-refresh-btn\">' + FED_ICON_REFRESH + '</button>'\n + '</div>';\n var currentOrigin = location.origin;\n for (var i = 0; i < memberSites.length; i++) {\n var s = memberSites[i];\n var star = s.isMaster ? '<span class=\"fed-star\">★</span> ' : '';\n var isMe = s.url && fedNormalizeUrl(s.url) === currentOrigin;\n var meTag = isMe ? ' <span style=\"font-size:10px;padding:1px 5px;border-radius:999px;background:var(--blue-light,#dbeafe);color:var(--blue-muted,#1d4ed8);font-weight:500\">' + __t(\"federation.currentSite\") + '</span>' : '';\n var isPendingSite = s.status === \"pending\";\n var role = s.isMaster ? __t(\"federation.master\")\n : (isPendingSite\n ? '<span style=\"color:var(--yellow-text,#92400e);font-weight:500\">' + __t(\"federation.pending\") + '</span>'\n : __t(\"federation.member\"));\n html += '<div class=\"fed-site-row\">'\n + '<div class=\"fed-site-name\">' + star + '<span class=\"fed-site-label\">' + fedEscHtml(s.name) + '</span>' + meTag + '</div>'\n + '<div class=\"fed-site-url\" title=\"' + fedEscAttr(s.url) + '\"><a href=\"' + fedEscAttr(s.url) + '\" target=\"_blank\">' + fedEscHtml(s.url) + '</a></div>'\n + '<div class=\"fed-site-role\">' + role + '</div>'\n + '</div>';\n }\n }\n\n html += '<div style=\"padding:16px 20px;border-top:1px solid var(--border)\">'\n + '<button class=\"btn btn-danger btn-sm\" style=\"width:auto\" onclick=\"fedLeave()\">' + __t(\"federation.leave\") + '</button>'\n + '</div>';\n\n return html;\n}\n\nfunction fedEscHtml(str) {\n if (!str) return '';\n return str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/\"/g,'"');\n}\nfunction fedEscAttr(str) {\n if (!str) return '';\n return str.replace(/&/g,'&').replace(/\"/g,'"').replace(/'/g,''');\n}\n\nfunction fedSetLoading(loading) {\n __fedLoading = loading;\n fedRender();\n}\n\nfunction fedEnable() {\n confirmDialog({\n title: __t(\"federation.enableConfirm\"),\n message: __t(\"federation.enableMasterDesc\"),\n confirmLabel: __t(\"federation.enableMaster\"),\n }).then(function(ok) {\n if (!ok) return;\n fedSetLoading(true);\n api(\"POST\", \"/admin/federation/enable\", { url: location.origin, name: document.title || \"App\" }).then(function(data) {\n __fedLoading = false;\n if (data && !data.error) { __fedData = data; fedRender(); showToast(__t(\"federation.enabled\"), \"success\"); }\n else { showToast(data.error || \"Failed\", \"error\"); fedRender(); }\n });\n });\n}\n\nfunction fedOpenJoin() {\n document.getElementById(\"fed-join-url\").value = \"\";\n showDialog(\"fed-join-dialog\");\n}\n\nfunction fedJoinSubmit() {\n var masterUrl = fedNormalizeUrl(document.getElementById(\"fed-join-url\").value);\n if (!masterUrl) { showToast(__t(\"federation.masterUrl\") + \" is required\", \"error\"); return; }\n\n var btn = document.getElementById(\"fed-join-btn\");\n btn.disabled = true;\n btn.innerHTML = '<span class=\"spinner\"></span>';\n\n api(\"POST\", \"/admin/federation/join\", { masterUrl: masterUrl }).then(function(data) {\n btn.disabled = false;\n btn.textContent = __t(\"federation.joinMember\");\n if (data && !data.error) {\n __fedData = data;\n fedRender();\n closeDialog(\"fed-join-dialog\");\n showToast(__t(\"federation.pendingApproval\"), \"success\");\n } else {\n showToast(data.error || __t(\"federation.targetNotEnabled\"), \"error\");\n }\n });\n}\n\nfunction fedToggle(enabled) {\n if (!enabled) {\n confirmDialog({\n title: __t(\"federation.disableTitle\"),\n message: __t(\"federation.disableDesc\") + \" \" + __t(\"federation.walletSafe\") + \" \" + __t(\"federation.sessionWarning\"),\n confirmLabel: __t(\"common.confirm\"),\n danger: true,\n }).then(function(ok) {\n if (!ok) { fedRender(); return; }\n api(\"PUT\", \"/admin/federation/toggle\", { enabled: false }).then(function(data) {\n if (data && !data.error) { __fedData = data; fedRender(); }\n });\n });\n var cb = document.querySelector(\"#fed-section input[type=checkbox]\");\n if (cb) cb.checked = true;\n } else {\n api(\"PUT\", \"/admin/federation/toggle\", { enabled: true }).then(function(data) {\n if (data && !data.error) { __fedData = data; fedRender(); }\n });\n }\n}\n\nfunction fedApproveSite(did) {\n api(\"POST\", \"/admin/federation/sites/\" + encodeURIComponent(did) + \"/approve\").then(function(data) {\n if (data && !data.error) {\n __fedData = data;\n fedRender();\n showToast(__t(\"federation.approved\"), \"success\");\n } else {\n showToast(data.error || \"Failed\", \"error\");\n }\n });\n}\n\nfunction fedRejectSite(did, name) {\n confirmDialog({\n title: __t(\"federation.rejectTitle\"),\n message: __t(\"federation.rejectDesc\"),\n confirmLabel: __t(\"federation.reject\"),\n danger: true,\n }).then(function(ok) {\n if (!ok) return;\n api(\"DELETE\", \"/admin/federation/sites/\" + encodeURIComponent(did)).then(function(data) {\n if (data && !data.error) { __fedData = data; fedRender(); showToast(__t(\"federation.reject\") + \" \\u2713\", \"success\"); }\n });\n });\n}\n\nfunction fedRefresh() {\n var btn = document.getElementById(\"fed-refresh-btn\");\n if (btn) { btn.style.opacity = \"0.5\"; btn.style.pointerEvents = \"none\"; }\n fedLoad();\n setTimeout(function() { if (btn) { btn.style.opacity = \"\"; btn.style.pointerEvents = \"\"; } }, 500);\n}\n\n// \u2500\u2500\u2500 Actions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction fedToggleMember(did, enabled) {\n if (!enabled) {\n confirmDialog({\n title: __t(\"federation.disableTitle\"),\n message: __t(\"federation.disableDesc\") + \" \" + __t(\"federation.walletSafe\") + \" \" + __t(\"federation.sessionWarning\"),\n confirmLabel: __t(\"federation.disable\"),\n danger: true,\n }).then(function(ok) {\n if (!ok) return;\n api(\"PUT\", \"/admin/federation/sites/\" + encodeURIComponent(did) + \"/toggle\", { enabled: false }).then(function(data) {\n if (data && !data.error) { __fedData = data; fedRender(); showToast(__t(\"federation.disabled\"), \"success\"); }\n });\n });\n } else {\n api(\"PUT\", \"/admin/federation/sites/\" + encodeURIComponent(did) + \"/toggle\", { enabled: true }).then(function(data) {\n if (data && !data.error) { __fedData = data; fedRender(); showToast(__t(\"federation.enabled\"), \"success\"); }\n });\n }\n}\n\nfunction fedUnbindSite(did, name) {\n confirmDialog({\n title: __t(\"federation.unbindTitle\"),\n message: __t(\"federation.unbindDesc\") + \" \" + __t(\"federation.walletSafe\") + \" \" + __t(\"federation.sessionWarning\"),\n confirmLabel: __t(\"federation.unbind\"),\n danger: true,\n }).then(function(ok) {\n if (!ok) return;\n api(\"DELETE\", \"/admin/federation/sites/\" + encodeURIComponent(did)).then(function(data) {\n if (data && !data.error) { __fedData = data; fedRender(); showToast(__t(\"federation.unbind\") + \" \\u2713\", \"success\"); }\n });\n });\n}\n\nfunction fedDisband() {\n confirmDialog({\n title: __t(\"federation.disbandTitle\"),\n message: __t(\"federation.disbandDesc\") + \" \" + __t(\"federation.sessionWarning\"),\n confirmLabel: __t(\"federation.disband\"),\n danger: true,\n }).then(function(ok) {\n if (!ok) return;\n api(\"POST\", \"/admin/federation/disband\").then(function(data) {\n if (data && !data.error) { __fedData = data; fedRender(); showToast(__t(\"federation.disband\") + \" \\u2713\", \"success\"); }\n });\n });\n}\n\nfunction fedLeave() {\n confirmDialog({\n title: __t(\"federation.leaveTitle\"),\n message: __t(\"federation.leaveDesc\") + \" \" + __t(\"federation.sessionWarning\"),\n confirmLabel: __t(\"federation.leave\"),\n danger: true,\n }).then(function(ok) {\n if (!ok) return;\n api(\"POST\", \"/admin/federation/leave\").then(function(data) {\n if (data && !data.error) { __fedData = data; fedRender(); showToast(__t(\"federation.leave\") + \" \\u2713\", \"success\"); }\n });\n });\n}\n";
|
|
6
|
+
export declare const FEDERATION_SECTION_SCRIPT = "\n// \u2500\u2500\u2500 Federation (Unified Login) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nvar __fedData = null;\nvar __fedLoading = false;\n\nvar FED_ICON_REMOVE = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 6h18\"/><path d=\"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6\"/><path d=\"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2\"/></svg>';\nvar FED_ICON_REFRESH = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/><path d=\"M3 3v5h5\"/><path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"/><path d=\"M21 21v-5h-5\"/></svg>';\nvar FED_ICON_CHECK = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"/></svg>';\nvar FED_ICON_UNLINK = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M15 7h3a5 5 0 0 1 0 10h-3m-6 0H6a5 5 0 0 1 0-10h3\"/><line x1=\"2\" y1=\"2\" x2=\"22\" y2=\"22\"/></svg>';\nvar FED_ICON_DISABLE = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"4.93\" y1=\"4.93\" x2=\"19.07\" y2=\"19.07\"/></svg>';\nvar FED_ICON_ENABLE = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18.36 6.64a9 9 0 1 1-12.73 0\"/><line x1=\"12\" y1=\"2\" x2=\"12\" y2=\"12\"/></svg>';\n\nfunction fedIconBtn(icon, title, onclick, variant) {\n var cls = variant === \"danger\" ? \"ap-icon-btn ap-icon-btn-danger\" : variant === \"success\" ? \"ap-icon-btn ap-icon-btn-success\" : \"ap-icon-btn\";\n return '<button class=\"' + cls + '\" title=\"' + fedEscAttr(title) + '\" onclick=\"' + onclick + '\">' + icon + '</button>';\n}\n\nfunction fedNormalizeUrl(input) {\n var s = (input || \"\").trim();\n if (!s) return \"\";\n if (!/^https?:\\/\\//i.test(s)) s = \"https://\" + s;\n try { return new URL(s).origin; } catch(e) { return s; }\n}\n\nfunction fedFormatUrl(el) {\n clearTimeout(el._fedTimer);\n el._fedTimer = setTimeout(function() {\n var v = el.value.trim();\n if (!v) return;\n var norm = fedNormalizeUrl(v);\n if (norm && norm !== v && norm !== \"https://\") {\n el.value = norm;\n }\n }, 800);\n}\n\nfunction fedLoad() {\n api(\"GET\", \"/admin/federation\").then(function(data) {\n if (!data || data.error) {\n __fedData = { role: null, enabled: false, sites: [], delegation: { exists: false } };\n } else {\n __fedData = data;\n }\n fedRender();\n });\n}\n\nfunction fedRender() {\n var el = document.getElementById(\"fed-section\");\n if (!el || !__fedData) return;\n if (!__fedData.role) {\n el.innerHTML = fedRenderUnconfigured();\n } else if (__fedData.role === \"master\") {\n el.innerHTML = fedRenderMaster();\n } else {\n el.innerHTML = fedRenderMember();\n }\n}\n\nfunction fedRenderUnconfigured() {\n return '<p class=\"settings-card-desc\">' + __t(\"federation.desc\") + '</p>'\n + '<div class=\"fed-init-buttons\">'\n + '<button class=\"btn\" id=\"fed-enable-btn\" onclick=\"fedEnable()\"' + (__fedLoading ? ' disabled' : '') + '>'\n + (__fedLoading ? '<span class=\"spinner\"></span> ' : '') + __t(\"federation.enableMaster\") + '</button>'\n + '<button class=\"btn btn-secondary\" onclick=\"fedOpenJoin()\"' + (__fedLoading ? ' disabled' : '') + '>'\n + __t(\"federation.joinMember\") + '</button>'\n + '</div>';\n}\n\nfunction fedRenderMaster() {\n var html = '<div style=\"padding:12px 20px;display:flex;align-items:center;gap:8px\">'\n + '<span class=\"fed-status-badge master\">★ ' + __t(\"federation.masterSite\") + '</span>'\n + '<span style=\"font-size:13px;color:var(--text-secondary)\">' + __t(\"federation.masterDesc\") + '</span>'\n + '</div>';\n\n // Sites header with refresh button\n html += '<div style=\"padding:0 20px 8px;display:flex;justify-content:space-between;align-items:center\">'\n + '<span style=\"font-size:13px;font-weight:600;color:var(--text)\">' + __t(\"federation.sites\") + '</span>'\n + '<button class=\"ap-icon-btn\" title=\"' + __t(\"federation.refresh\") + '\" onclick=\"fedRefresh()\" id=\"fed-refresh-btn\">' + FED_ICON_REFRESH + '</button>'\n + '</div>';\n\n // Sort: master first, then approved, then pending\n var sites = (__fedData.sites || []).slice().sort(function(a, b) {\n if (a.isMaster) return -1;\n if (b.isMaster) return 1;\n if (a.status === \"pending\" && b.status !== \"pending\") return 1;\n if (b.status === \"pending\" && a.status !== \"pending\") return -1;\n return 0;\n });\n var currentOrigin = location.origin;\n for (var i = 0; i < sites.length; i++) {\n var s = sites[i];\n var star = s.isMaster ? '<span class=\"fed-star\">★</span> ' : '';\n var isMe = s.url && fedNormalizeUrl(s.url) === currentOrigin;\n var meTag = isMe ? ' <span style=\"font-size:10px;padding:1px 5px;border-radius:999px;background:var(--blue-light,#dbeafe);color:var(--blue-muted,#1d4ed8);font-weight:500\">' + __t(\"federation.currentSite\") + '</span>' : '';\n var isPending = s.status === \"pending\";\n var statusBadge = isPending\n ? ' <span style=\"font-size:11px;padding:1px 6px;border-radius:999px;background:var(--yellow-light,#fef3c7);color:var(--yellow-text,#92400e);font-weight:500\">' + __t(\"federation.pending\") + '</span>'\n : '';\n\n var actions = '';\n if (s.isMaster) {\n // Disabled placeholder buttons for alignment with member rows\n actions = '<button class=\"ap-icon-btn\" disabled style=\"opacity:0.25;cursor:default\">' + FED_ICON_DISABLE + '</button>'\n + '<button class=\"ap-icon-btn\" disabled style=\"opacity:0.25;cursor:default\">' + FED_ICON_UNLINK + '</button>';\n } else if (isPending) {\n actions = fedIconBtn(FED_ICON_CHECK, __t(\"federation.approve\"), \"fedApproveSite('\" + s.did + \"')\", \"success\")\n + fedIconBtn(FED_ICON_REMOVE, __t(\"federation.reject\"), \"fedRejectSite('\" + s.did + \"','\" + fedEscAttr(s.name) + \"')\", \"danger\");\n } else {\n // Approved member: disable/enable toggle + unbind\n if (s.enabled === false) {\n actions = fedIconBtn(FED_ICON_ENABLE, __t(\"federation.enable\"), \"fedToggleMember('\" + s.did + \"',true)\")\n + fedIconBtn(FED_ICON_UNLINK, __t(\"federation.unbind\"), \"fedUnbindSite('\" + s.did + \"','\" + fedEscAttr(s.name) + \"')\", \"danger\");\n } else {\n actions = fedIconBtn(FED_ICON_DISABLE, __t(\"federation.disable\"), \"fedToggleMember('\" + s.did + \"',false)\", \"danger\")\n + fedIconBtn(FED_ICON_UNLINK, __t(\"federation.unbind\"), \"fedUnbindSite('\" + s.did + \"','\" + fedEscAttr(s.name) + \"')\", \"danger\");\n }\n }\n // Disabled badge for approved but disabled members\n if (!isPending && !s.isMaster && s.enabled === false) {\n statusBadge = ' <span style=\"font-size:11px;padding:1px 6px;border-radius:999px;background:var(--bg-tertiary,#e5e7eb);color:var(--text-tertiary)\">' + __t(\"federation.disabled\") + '</span>';\n }\n\n html += '<div class=\"fed-site-row\">'\n + '<div class=\"fed-site-name\">' + star + '<span class=\"fed-site-label\">' + fedEscHtml(s.name) + '</span>' + meTag + statusBadge + '</div>'\n + '<div class=\"fed-site-url\" title=\"' + fedEscAttr(s.url) + '\"><a href=\"' + fedEscAttr(s.url) + '\" target=\"_blank\">' + fedEscHtml(s.url) + '</a></div>'\n + '<div class=\"fed-site-actions\">' + actions + '</div>'\n + '</div>';\n }\n\n // Disband\n html += '<div style=\"padding:16px 20px;border-top:1px solid var(--border)\">'\n + '<button class=\"btn btn-danger btn-sm\" style=\"width:auto\" onclick=\"fedDisband()\">' + __t(\"federation.disband\") + '</button>'\n + '</div>';\n\n return html;\n}\n\nfunction fedRenderMember() {\n var enabled = __fedData.enabled;\n var isPending = !enabled && __fedData.role === \"member\";\n\n // Header: badge + status + toggle (no toggle for pending)\n var badgeClass = isPending ? 'style=\"background:var(--yellow-light,#fef3c7);color:var(--yellow-text,#92400e)\"' : '';\n var badgeText = isPending ? __t(\"federation.pending\") : __t(\"federation.member\");\n var statusText = isPending ? __t(\"federation.pendingDesc\") : (enabled ? __t(\"federation.enabled\") : __t(\"federation.disabled\"));\n\n var html = '<div style=\"padding:12px 20px;display:flex;align-items:center;gap:12px;justify-content:space-between\">'\n + '<div style=\"display:flex;align-items:center;gap:8px\">'\n + '<span class=\"fed-status-badge member\" ' + badgeClass + '>' + badgeText + '</span>'\n + '<span style=\"font-size:13px;color:var(--text-secondary)\">' + statusText + '</span>'\n + '</div>';\n if (!isPending) {\n html += '<label class=\"toggle-switch\">'\n + '<input type=\"checkbox\" ' + (enabled ? 'checked' : '') + ' onchange=\"fedToggle(this.checked)\" />'\n + '<span class=\"toggle-slider\"></span>'\n + '</label>';\n }\n html += '</div>';\n\n // Master URL\n if (__fedData.masterUrl) {\n html += '<div style=\"padding:0 20px 8px;font-size:13px;color:var(--text-secondary)\">'\n + __t(\"federation.delegatedTo\") + ' <a href=\"' + fedEscAttr(__fedData.masterUrl) + '\" target=\"_blank\" style=\"color:var(--blue)\">' + fedEscHtml(__fedData.masterUrl) + '</a>'\n + '</div>';\n }\n\n // Site list \u2014 show all sites with role labels; pending sites get \"pending\" instead of \"member\"\n if (__fedData.sites && __fedData.sites.length > 0) {\n // Sort: master first, then approved, then pending\n var memberSites = __fedData.sites.slice().sort(function(a, b) {\n if (a.isMaster) return -1;\n if (b.isMaster) return 1;\n if (a.status === \"pending\" && b.status !== \"pending\") return 1;\n if (b.status === \"pending\" && a.status !== \"pending\") return -1;\n return 0;\n });\n html += '<div style=\"padding:8px 20px 4px;display:flex;justify-content:space-between;align-items:center\">'\n + '<span style=\"font-size:13px;font-weight:600;color:var(--text)\">' + __t(\"federation.sites\") + '</span>'\n + '<button class=\"ap-icon-btn\" title=\"' + __t(\"federation.refresh\") + '\" onclick=\"fedRefresh()\" id=\"fed-refresh-btn\">' + FED_ICON_REFRESH + '</button>'\n + '</div>';\n var currentOrigin = location.origin;\n for (var i = 0; i < memberSites.length; i++) {\n var s = memberSites[i];\n var star = s.isMaster ? '<span class=\"fed-star\">★</span> ' : '';\n var isMe = s.url && fedNormalizeUrl(s.url) === currentOrigin;\n var meTag = isMe ? ' <span style=\"font-size:10px;padding:1px 5px;border-radius:999px;background:var(--blue-light,#dbeafe);color:var(--blue-muted,#1d4ed8);font-weight:500\">' + __t(\"federation.currentSite\") + '</span>' : '';\n var isPendingSite = s.status === \"pending\";\n var role = s.isMaster ? __t(\"federation.master\")\n : (isPendingSite\n ? '<span style=\"color:var(--yellow-text,#92400e);font-weight:500\">' + __t(\"federation.pending\") + '</span>'\n : __t(\"federation.member\"));\n html += '<div class=\"fed-site-row\">'\n + '<div class=\"fed-site-name\">' + star + '<span class=\"fed-site-label\">' + fedEscHtml(s.name) + '</span>' + meTag + '</div>'\n + '<div class=\"fed-site-url\" title=\"' + fedEscAttr(s.url) + '\"><a href=\"' + fedEscAttr(s.url) + '\" target=\"_blank\">' + fedEscHtml(s.url) + '</a></div>'\n + '<div class=\"fed-site-role\">' + role + '</div>'\n + '</div>';\n }\n }\n\n html += '<div style=\"padding:16px 20px;border-top:1px solid var(--border)\">'\n + '<button class=\"btn btn-danger btn-sm\" style=\"width:auto\" onclick=\"fedLeave()\">' + __t(\"federation.leave\") + '</button>'\n + '</div>';\n\n return html;\n}\n\nfunction fedEscHtml(str) {\n if (!str) return '';\n return str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/\"/g,'"');\n}\nfunction fedEscAttr(str) {\n if (!str) return '';\n return str.replace(/&/g,'&').replace(/\"/g,'"').replace(/'/g,''');\n}\n\nfunction fedSetLoading(loading) {\n __fedLoading = loading;\n fedRender();\n}\n\nfunction fedEnable() {\n return confirmDialog({\n title: __t(\"federation.enableConfirm\"),\n message: __t(\"federation.enableMasterDesc\"),\n confirmLabel: __t(\"federation.enableMaster\"),\n }).then(function(ok) {\n if (!ok) return;\n fedSetLoading(true);\n return api(\"POST\", \"/admin/federation/enable\", { url: location.origin, name: document.title || \"App\" }).then(function(data) {\n __fedLoading = false;\n if (data && !data.error) { __fedData = data; fedRender(); showToast(__t(\"federation.enabled\"), \"success\"); }\n else { showToast(data.error || \"Failed\", \"error\"); fedRender(); }\n });\n });\n}\n\nfunction fedOpenJoin() {\n document.getElementById(\"fed-join-url\").value = \"\";\n showDialog(\"fed-join-dialog\");\n}\n\nfunction fedJoinSubmit() {\n var masterUrl = fedNormalizeUrl(document.getElementById(\"fed-join-url\").value);\n if (!masterUrl) { showToast(__t(\"federation.masterUrl\") + \" is required\", \"error\"); return; }\n\n var btn = document.getElementById(\"fed-join-btn\");\n btn.disabled = true;\n btn.innerHTML = '<span class=\"spinner\"></span>';\n\n api(\"POST\", \"/admin/federation/join\", { masterUrl: masterUrl }).then(function(data) {\n btn.disabled = false;\n btn.textContent = __t(\"federation.joinMember\");\n if (data && !data.error) {\n __fedData = data;\n fedRender();\n closeDialog(\"fed-join-dialog\");\n showToast(__t(\"federation.pendingApproval\"), \"success\");\n } else {\n showToast(data.error || __t(\"federation.targetNotEnabled\"), \"error\");\n }\n });\n}\n\nfunction fedToggle(enabled) {\n if (!enabled) {\n confirmDialog({\n title: __t(\"federation.disableTitle\"),\n message: __t(\"federation.disableDesc\") + \" \" + __t(\"federation.walletSafe\") + \" \" + __t(\"federation.sessionWarning\"),\n confirmLabel: __t(\"common.confirm\"),\n danger: true,\n }).then(function(ok) {\n if (!ok) { fedRender(); return; }\n api(\"PUT\", \"/admin/federation/toggle\", { enabled: false }).then(function(data) {\n if (data && !data.error) { __fedData = data; fedRender(); }\n });\n });\n var cb = document.querySelector(\"#fed-section input[type=checkbox]\");\n if (cb) cb.checked = true;\n } else {\n api(\"PUT\", \"/admin/federation/toggle\", { enabled: true }).then(function(data) {\n if (data && !data.error) { __fedData = data; fedRender(); }\n });\n }\n}\n\nfunction fedApproveSite(did) {\n return api(\"POST\", \"/admin/federation/sites/\" + encodeURIComponent(did) + \"/approve\").then(function(data) {\n if (data && !data.error) {\n __fedData = data;\n fedRender();\n showToast(__t(\"federation.approved\"), \"success\");\n } else {\n showToast(data.error || \"Failed\", \"error\");\n }\n });\n}\n\nfunction fedRejectSite(did, name) {\n return confirmDialog({\n title: __t(\"federation.rejectTitle\"),\n message: __t(\"federation.rejectDesc\"),\n confirmLabel: __t(\"federation.reject\"),\n danger: true,\n }).then(function(ok) {\n if (!ok) return;\n return api(\"DELETE\", \"/admin/federation/sites/\" + encodeURIComponent(did)).then(function(data) {\n if (data && !data.error) { __fedData = data; fedRender(); showToast(__t(\"federation.reject\") + \" \\u2713\", \"success\"); }\n });\n });\n}\n\nfunction fedRefresh() {\n var btn = document.getElementById(\"fed-refresh-btn\");\n if (btn) { btn.style.opacity = \"0.5\"; btn.style.pointerEvents = \"none\"; }\n fedLoad();\n setTimeout(function() { if (btn) { btn.style.opacity = \"\"; btn.style.pointerEvents = \"\"; } }, 500);\n}\n\n// \u2500\u2500\u2500 Actions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction fedToggleMember(did, enabled) {\n if (!enabled) {\n confirmDialog({\n title: __t(\"federation.disableTitle\"),\n message: __t(\"federation.disableDesc\") + \" \" + __t(\"federation.walletSafe\") + \" \" + __t(\"federation.sessionWarning\"),\n confirmLabel: __t(\"federation.disable\"),\n danger: true,\n }).then(function(ok) {\n if (!ok) return;\n api(\"PUT\", \"/admin/federation/sites/\" + encodeURIComponent(did) + \"/toggle\", { enabled: false }).then(function(data) {\n if (data && !data.error) { __fedData = data; fedRender(); showToast(__t(\"federation.disabled\"), \"success\"); }\n });\n });\n } else {\n api(\"PUT\", \"/admin/federation/sites/\" + encodeURIComponent(did) + \"/toggle\", { enabled: true }).then(function(data) {\n if (data && !data.error) { __fedData = data; fedRender(); showToast(__t(\"federation.enabled\"), \"success\"); }\n });\n }\n}\n\nfunction fedUnbindSite(did, name) {\n return confirmDialog({\n title: __t(\"federation.unbindTitle\"),\n message: __t(\"federation.unbindDesc\") + \" \" + __t(\"federation.walletSafe\") + \" \" + __t(\"federation.sessionWarning\"),\n confirmLabel: __t(\"federation.unbind\"),\n danger: true,\n }).then(function(ok) {\n if (!ok) return;\n return api(\"DELETE\", \"/admin/federation/sites/\" + encodeURIComponent(did)).then(function(data) {\n if (data && !data.error) { __fedData = data; fedRender(); showToast(__t(\"federation.unbind\") + \" \\u2713\", \"success\"); }\n });\n });\n}\n\nfunction fedDisband() {\n return confirmDialog({\n title: __t(\"federation.disbandTitle\"),\n message: __t(\"federation.disbandDesc\") + \" \" + __t(\"federation.sessionWarning\"),\n confirmLabel: __t(\"federation.disband\"),\n danger: true,\n }).then(function(ok) {\n if (!ok) return;\n return api(\"POST\", \"/admin/federation/disband\").then(function(data) {\n if (data && !data.error) { __fedData = data; fedRender(); showToast(__t(\"federation.disband\") + \" \\u2713\", \"success\"); }\n });\n });\n}\n\nfunction fedLeave() {\n return confirmDialog({\n title: __t(\"federation.leaveTitle\"),\n message: __t(\"federation.leaveDesc\") + \" \" + __t(\"federation.sessionWarning\"),\n confirmLabel: __t(\"federation.leave\"),\n danger: true,\n }).then(function(ok) {\n if (!ok) return;\n return api(\"POST\", \"/admin/federation/leave\").then(function(data) {\n if (data && !data.error) { __fedData = data; fedRender(); showToast(__t(\"federation.leave\") + \" \\u2713\", \"success\"); }\n });\n });\n}\n\n// Button-driven mutations must not double-submit (submitOnce \u2014 utils.ts).\n// fedJoinSubmit locks its own button; fedToggle / fedToggleMember are\n// checkbox toggles and stay unwrapped (dropping a toggle would desync the UI).\nfedEnable = submitOnce(fedEnable);\nfedApproveSite = submitOnce(fedApproveSite);\nfedRejectSite = submitOnce(fedRejectSite);\nfedUnbindSite = submitOnce(fedUnbindSite);\nfedDisband = submitOnce(fedDisband);\nfedLeave = submitOnce(fedLeave);\n";
|
|
7
7
|
export {};
|
|
8
8
|
//# sourceMappingURL=tab-federation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tab-federation.d.ts","sourceRoot":"","sources":["../../../src/pages/admin/tab-federation.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC;AAEpE,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,GAAG,MAAM,CA2BpD;AAED,eAAO,MAAM,yBAAyB,
|
|
1
|
+
{"version":3,"file":"tab-federation.d.ts","sourceRoot":"","sources":["../../../src/pages/admin/tab-federation.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC;AAEpE,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,GAAG,MAAM,CA2BpD;AAED,eAAO,MAAM,yBAAyB,m2nBAyYrC,CAAC"}
|
|
@@ -252,14 +252,14 @@ function fedSetLoading(loading) {
|
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
function fedEnable() {
|
|
255
|
-
confirmDialog({
|
|
255
|
+
return confirmDialog({
|
|
256
256
|
title: __t("federation.enableConfirm"),
|
|
257
257
|
message: __t("federation.enableMasterDesc"),
|
|
258
258
|
confirmLabel: __t("federation.enableMaster"),
|
|
259
259
|
}).then(function(ok) {
|
|
260
260
|
if (!ok) return;
|
|
261
261
|
fedSetLoading(true);
|
|
262
|
-
api("POST", "/admin/federation/enable", { url: location.origin, name: document.title || "App" }).then(function(data) {
|
|
262
|
+
return api("POST", "/admin/federation/enable", { url: location.origin, name: document.title || "App" }).then(function(data) {
|
|
263
263
|
__fedLoading = false;
|
|
264
264
|
if (data && !data.error) { __fedData = data; fedRender(); showToast(__t("federation.enabled"), "success"); }
|
|
265
265
|
else { showToast(data.error || "Failed", "error"); fedRender(); }
|
|
@@ -317,7 +317,7 @@ function fedToggle(enabled) {
|
|
|
317
317
|
}
|
|
318
318
|
|
|
319
319
|
function fedApproveSite(did) {
|
|
320
|
-
api("POST", "/admin/federation/sites/" + encodeURIComponent(did) + "/approve").then(function(data) {
|
|
320
|
+
return api("POST", "/admin/federation/sites/" + encodeURIComponent(did) + "/approve").then(function(data) {
|
|
321
321
|
if (data && !data.error) {
|
|
322
322
|
__fedData = data;
|
|
323
323
|
fedRender();
|
|
@@ -329,14 +329,14 @@ function fedApproveSite(did) {
|
|
|
329
329
|
}
|
|
330
330
|
|
|
331
331
|
function fedRejectSite(did, name) {
|
|
332
|
-
confirmDialog({
|
|
332
|
+
return confirmDialog({
|
|
333
333
|
title: __t("federation.rejectTitle"),
|
|
334
334
|
message: __t("federation.rejectDesc"),
|
|
335
335
|
confirmLabel: __t("federation.reject"),
|
|
336
336
|
danger: true,
|
|
337
337
|
}).then(function(ok) {
|
|
338
338
|
if (!ok) return;
|
|
339
|
-
api("DELETE", "/admin/federation/sites/" + encodeURIComponent(did)).then(function(data) {
|
|
339
|
+
return api("DELETE", "/admin/federation/sites/" + encodeURIComponent(did)).then(function(data) {
|
|
340
340
|
if (data && !data.error) { __fedData = data; fedRender(); showToast(__t("federation.reject") + " \\u2713", "success"); }
|
|
341
341
|
});
|
|
342
342
|
});
|
|
@@ -372,45 +372,55 @@ function fedToggleMember(did, enabled) {
|
|
|
372
372
|
}
|
|
373
373
|
|
|
374
374
|
function fedUnbindSite(did, name) {
|
|
375
|
-
confirmDialog({
|
|
375
|
+
return confirmDialog({
|
|
376
376
|
title: __t("federation.unbindTitle"),
|
|
377
377
|
message: __t("federation.unbindDesc") + " " + __t("federation.walletSafe") + " " + __t("federation.sessionWarning"),
|
|
378
378
|
confirmLabel: __t("federation.unbind"),
|
|
379
379
|
danger: true,
|
|
380
380
|
}).then(function(ok) {
|
|
381
381
|
if (!ok) return;
|
|
382
|
-
api("DELETE", "/admin/federation/sites/" + encodeURIComponent(did)).then(function(data) {
|
|
382
|
+
return api("DELETE", "/admin/federation/sites/" + encodeURIComponent(did)).then(function(data) {
|
|
383
383
|
if (data && !data.error) { __fedData = data; fedRender(); showToast(__t("federation.unbind") + " \\u2713", "success"); }
|
|
384
384
|
});
|
|
385
385
|
});
|
|
386
386
|
}
|
|
387
387
|
|
|
388
388
|
function fedDisband() {
|
|
389
|
-
confirmDialog({
|
|
389
|
+
return confirmDialog({
|
|
390
390
|
title: __t("federation.disbandTitle"),
|
|
391
391
|
message: __t("federation.disbandDesc") + " " + __t("federation.sessionWarning"),
|
|
392
392
|
confirmLabel: __t("federation.disband"),
|
|
393
393
|
danger: true,
|
|
394
394
|
}).then(function(ok) {
|
|
395
395
|
if (!ok) return;
|
|
396
|
-
api("POST", "/admin/federation/disband").then(function(data) {
|
|
396
|
+
return api("POST", "/admin/federation/disband").then(function(data) {
|
|
397
397
|
if (data && !data.error) { __fedData = data; fedRender(); showToast(__t("federation.disband") + " \\u2713", "success"); }
|
|
398
398
|
});
|
|
399
399
|
});
|
|
400
400
|
}
|
|
401
401
|
|
|
402
402
|
function fedLeave() {
|
|
403
|
-
confirmDialog({
|
|
403
|
+
return confirmDialog({
|
|
404
404
|
title: __t("federation.leaveTitle"),
|
|
405
405
|
message: __t("federation.leaveDesc") + " " + __t("federation.sessionWarning"),
|
|
406
406
|
confirmLabel: __t("federation.leave"),
|
|
407
407
|
danger: true,
|
|
408
408
|
}).then(function(ok) {
|
|
409
409
|
if (!ok) return;
|
|
410
|
-
api("POST", "/admin/federation/leave").then(function(data) {
|
|
410
|
+
return api("POST", "/admin/federation/leave").then(function(data) {
|
|
411
411
|
if (data && !data.error) { __fedData = data; fedRender(); showToast(__t("federation.leave") + " \\u2713", "success"); }
|
|
412
412
|
});
|
|
413
413
|
});
|
|
414
414
|
}
|
|
415
|
+
|
|
416
|
+
// Button-driven mutations must not double-submit (submitOnce — utils.ts).
|
|
417
|
+
// fedJoinSubmit locks its own button; fedToggle / fedToggleMember are
|
|
418
|
+
// checkbox toggles and stay unwrapped (dropping a toggle would desync the UI).
|
|
419
|
+
fedEnable = submitOnce(fedEnable);
|
|
420
|
+
fedApproveSite = submitOnce(fedApproveSite);
|
|
421
|
+
fedRejectSite = submitOnce(fedRejectSite);
|
|
422
|
+
fedUnbindSite = submitOnce(fedUnbindSite);
|
|
423
|
+
fedDisband = submitOnce(fedDisband);
|
|
424
|
+
fedLeave = submitOnce(fedLeave);
|
|
415
425
|
`;
|
|
416
426
|
//# sourceMappingURL=tab-federation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tab-federation.js","sourceRoot":"","sources":["../../../src/pages/admin/tab-federation.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,UAAU,qBAAqB,CAAC,CAAM;IAC1C,OAAO,UAAU,CAAC;;;;cAIN,CAAC,CAAC,kBAAkB,CAAC;;;;;;;YAOvB,CAAC,CAAC,uBAAuB,CAAC;+DACyB,CAAC,CAAC,qBAAqB,CAAC;;oCAEnD,CAAC,CAAC,sBAAsB,CAAC;8DACC,CAAC,CAAC,iCAAiC,CAAC;;;;mFAIf,CAAC,CAAC,eAAe,CAAC;wEAC7B,CAAC,CAAC,uBAAuB,CAAC;;;;;GAK/F,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,yBAAyB,GAAG,QAAQ,CAAC
|
|
1
|
+
{"version":3,"file":"tab-federation.js","sourceRoot":"","sources":["../../../src/pages/admin/tab-federation.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,UAAU,qBAAqB,CAAC,CAAM;IAC1C,OAAO,UAAU,CAAC;;;;cAIN,CAAC,CAAC,kBAAkB,CAAC;;;;;;;YAOvB,CAAC,CAAC,uBAAuB,CAAC;+DACyB,CAAC,CAAC,qBAAqB,CAAC;;oCAEnD,CAAC,CAAC,sBAAsB,CAAC;8DACC,CAAC,CAAC,iCAAiC,CAAC;;;;mFAIf,CAAC,CAAC,eAAe,CAAC;wEAC7B,CAAC,CAAC,uBAAuB,CAAC;;;;;GAK/F,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,yBAAyB,GAAG,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyYjD,CAAC"}
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
type TFn = (key: string, params?: Record<string, string>) => string;
|
|
5
5
|
export declare function invitationsTabHtml(t: TFn): string;
|
|
6
|
-
export declare const INVITATIONS_TAB_SCRIPT = "\nvar invitationsPage = 1;\nvar invitationsPageSize = 20;\n\n\nfunction updateRemarkCounter() {\n document.getElementById(\"remark-counter\").textContent = document.getElementById(\"invite-remark\").value.length;\n}\n\nasync function loadInvitations(page) {\n invitationsPage = page || 1;\n var qs = \"?page=\" + invitationsPage + \"&pageSize=\" + invitationsPageSize;\n document.getElementById(\"invitations-table-wrap\").innerHTML = skeletonTable(6, 4);\n document.getElementById(\"invitations-pagination\").innerHTML = \"\";\n var data = await api(\"GET\", \"/invitations\" + qs);\n if (!data.ok) {\n document.getElementById(\"invitations-table-wrap\").innerHTML = '<div class=\"empty-state\"><div class=\"empty-state-icon\"><svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 8v4m0 4h.01\"/></svg></div><div class=\"empty-state-title\">' + __t(\"invitations.loadFailed\") + '</div></div>';\n return;\n }\n renderInvitationsTable(data.invitations, data.paging);\n}\n\nfunction renderInvitationsTable(invitations, paging) {\n var wrap = document.getElementById(\"invitations-table-wrap\");\n if (!invitations || invitations.length === 0) {\n wrap.innerHTML = '<div class=\"empty-state\"><div class=\"empty-state-icon\"><svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M22 12h-4l-3 9L9 3l-3 9H2\"/></svg></div><div class=\"empty-state-title\">' + __t(\"invitations.noInvitations\") + '</div><div class=\"empty-state-desc\">' + __t(\"invitations.noInvitationsHint\") + '</div></div>';\n document.getElementById(\"invitations-pagination\").innerHTML = \"\";\n return;\n }\n\n var html = '<table class=\"table\"><thead><tr><th>' + __t(\"invitations.col.role\") + '</th><th class=\"col-hide-sm\">' + __t(\"invitations.col.remark\") + '</th><th>' + __t(\"invitations.col.uses\") + '</th><th>' + __t(\"invitations.col.expires\") + '</th><th class=\"col-hide-md\">' + __t(\"invitations.col.createdBy\") + '</th><th></th></tr></thead><tbody>';\n invitations.forEach(function(inv) {\n var roleBadge = '<span class=\"badge badge-' + inv.role + '\">' + __t(\"common.\" + inv.role) + '</span>';\n var remark = inv.remark ? escapeHtml(inv.remark.length > 30 ? inv.remark.slice(0, 30) + \"...\" : inv.remark) : '<span class=\"text-muted\">\u2014</span>';\n var uses = inv.useCount + \" / \" + inv.maxUses;\n if (inv.useCount >= inv.maxUses) uses += ' <span class=\"badge badge-blocked\">' + __t(\"invitations.closed\") + '</span>';\n var expired = new Date(inv.expireAt) < new Date();\n var expireText = expired ? '<span class=\"badge badge-expired\">' + __t(\"common.expired\") + '</span>' : '<span class=\"cell-date\">' + escapeHtml(relativeTime(inv.expireAt)) + '</span>';\n var isActive = !expired && inv.status !== \"closed\" && inv.useCount < inv.maxUses;\n var inviter = inv.inviterName ? escapeHtml(inv.inviterName) : '<span class=\"text-muted\">\u2014</span>';\n\n var actions = '';\n if (isActive) {\n actions += '<button class=\"action-menu-item\" onclick=\"event.stopPropagation();copyToClipboard(\\'' + escapeHtml(inv.link) + '\\')\">' + __t(\"invitations.copyLink\") + '</button>';\n }\n var canDelete = __caller.role === \"owner\" || inv.inviterDid === __caller.did;\n if (canDelete) {\n if (actions) actions += '<div class=\"action-menu-sep\"></div>';\n actions += '<button class=\"action-menu-item danger\" onclick=\"event.stopPropagation();deleteInvitation(\\'' + escapeHtml(inv.id) + '\\')\">' + __t(\"common.delete\") + '</button>';\n }\n var actionMenu = actions ? '<td class=\"action-cell\"><button class=\"action-trigger\" onclick=\"event.stopPropagation();toggleActionMenu(this)\">\u22EF</button><div class=\"action-menu\">' + actions + '</div></td>' : '<td></td>';\n\n html += '<tr><td>' + roleBadge + '</td><td class=\"col-hide-sm\">' + remark + '</td><td>' + uses + '</td><td>' + expireText + '</td><td class=\"col-hide-md\">' + inviter + '</td>' + actionMenu + '</tr>';\n });\n html += '</tbody></table>';\n wrap.innerHTML = html;\n\n renderPagination(\"invitations-pagination\", paging, loadInvitations);\n}\n\nasync function createInvitation() {\n var btn = document.getElementById(\"create-invite-btn\");\n btn.disabled = true;\n\n var role = document.getElementById(\"invite-role\").value;\n var remark = document.getElementById(\"invite-remark\").value.trim();\n var expireHours = parseInt(document.getElementById(\"invite-expire\").value, 10);\n var maxUses = parseInt(document.getElementById(\"invite-max-uses\").value, 10) || 1;\n maxUses = Math.min(100, Math.max(1, maxUses));\n\n var data = await api(\"POST\", \"/invitations\", { role: role, remark: remark, expireHours: expireHours, maxUses: maxUses });\n btn.disabled = false;\n\n if (data.ok && data.invitation) {\n closeDialog(\"create-invitation-dialog\");\n copyToClipboard(data.invitation.link);\n showToast(__t(\"invitations.created\"), \"success\");\n // Reset form\n document.getElementById(\"invite-remark\").value = \"\";\n document.getElementById(\"invite-max-uses\").value = \"1\";\n updateRemarkCounter();\n loadInvitations(invitationsPage);\n }\n}\n\nasync function deleteInvitation(id) {\n var ok = await confirmDialog({ title: __t(\"invitations.deleteTitle\"), message: __t(\"invitations.deleteMsg\"), confirmLabel: __t(\"common.delete\"), danger: true });\n if (!ok) return;\n var data = await api(\"DELETE\", \"/invitations/\" + encodeURIComponent(id));\n if (data.ok) { showToast(__t(\"invitations.deleted\"), \"success\"); loadInvitations(invitationsPage); }\n}\n\n// Initialize role options based on caller role\nfunction initInviteRoleOptions() {\n var select = document.getElementById(\"invite-role\");\n if (__caller.role === \"owner\") {\n select.innerHTML = '<option value=\"guest\">' + __t(\"common.guest\") + '</option><option value=\"member\">' + __t(\"common.member\") + '</option><option value=\"admin\">' + __t(\"common.admin\") + '</option>';\n }\n}\n\nregisterTab(\"invitations\", function() {\n initInviteRoleOptions();\n loadInvitations(1);\n});\n";
|
|
6
|
+
export declare const INVITATIONS_TAB_SCRIPT = "\nvar invitationsPage = 1;\nvar invitationsPageSize = 20;\n\n\nfunction updateRemarkCounter() {\n document.getElementById(\"remark-counter\").textContent = document.getElementById(\"invite-remark\").value.length;\n}\n\nasync function loadInvitations(page) {\n invitationsPage = page || 1;\n var qs = \"?page=\" + invitationsPage + \"&pageSize=\" + invitationsPageSize;\n document.getElementById(\"invitations-table-wrap\").innerHTML = skeletonTable(6, 4);\n document.getElementById(\"invitations-pagination\").innerHTML = \"\";\n var data = await api(\"GET\", \"/invitations\" + qs);\n if (!data.ok) {\n document.getElementById(\"invitations-table-wrap\").innerHTML = '<div class=\"empty-state\"><div class=\"empty-state-icon\"><svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 8v4m0 4h.01\"/></svg></div><div class=\"empty-state-title\">' + __t(\"invitations.loadFailed\") + '</div></div>';\n return;\n }\n renderInvitationsTable(data.invitations, data.paging);\n}\n\nfunction renderInvitationsTable(invitations, paging) {\n var wrap = document.getElementById(\"invitations-table-wrap\");\n if (!invitations || invitations.length === 0) {\n wrap.innerHTML = '<div class=\"empty-state\"><div class=\"empty-state-icon\"><svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M22 12h-4l-3 9L9 3l-3 9H2\"/></svg></div><div class=\"empty-state-title\">' + __t(\"invitations.noInvitations\") + '</div><div class=\"empty-state-desc\">' + __t(\"invitations.noInvitationsHint\") + '</div></div>';\n document.getElementById(\"invitations-pagination\").innerHTML = \"\";\n return;\n }\n\n var html = '<table class=\"table\"><thead><tr><th>' + __t(\"invitations.col.role\") + '</th><th class=\"col-hide-sm\">' + __t(\"invitations.col.remark\") + '</th><th>' + __t(\"invitations.col.uses\") + '</th><th>' + __t(\"invitations.col.expires\") + '</th><th class=\"col-hide-md\">' + __t(\"invitations.col.createdBy\") + '</th><th></th></tr></thead><tbody>';\n invitations.forEach(function(inv) {\n var roleBadge = '<span class=\"badge badge-' + inv.role + '\">' + __t(\"common.\" + inv.role) + '</span>';\n var remark = inv.remark ? escapeHtml(inv.remark.length > 30 ? inv.remark.slice(0, 30) + \"...\" : inv.remark) : '<span class=\"text-muted\">\u2014</span>';\n var uses = inv.useCount + \" / \" + inv.maxUses;\n if (inv.useCount >= inv.maxUses) uses += ' <span class=\"badge badge-blocked\">' + __t(\"invitations.closed\") + '</span>';\n var expired = new Date(inv.expireAt) < new Date();\n var expireText = expired ? '<span class=\"badge badge-expired\">' + __t(\"common.expired\") + '</span>' : '<span class=\"cell-date\">' + escapeHtml(relativeTime(inv.expireAt)) + '</span>';\n var isActive = !expired && inv.status !== \"closed\" && inv.useCount < inv.maxUses;\n var inviter = inv.inviterName ? escapeHtml(inv.inviterName) : '<span class=\"text-muted\">\u2014</span>';\n\n var actions = '';\n if (isActive) {\n actions += '<button class=\"action-menu-item\" onclick=\"event.stopPropagation();copyToClipboard(\\'' + escapeHtml(inv.link) + '\\')\">' + __t(\"invitations.copyLink\") + '</button>';\n }\n var canDelete = __caller.role === \"owner\" || inv.inviterDid === __caller.did;\n if (canDelete) {\n if (actions) actions += '<div class=\"action-menu-sep\"></div>';\n actions += '<button class=\"action-menu-item danger\" onclick=\"event.stopPropagation();deleteInvitation(\\'' + escapeHtml(inv.id) + '\\')\">' + __t(\"common.delete\") + '</button>';\n }\n var actionMenu = actions ? '<td class=\"action-cell\"><button class=\"action-trigger\" onclick=\"event.stopPropagation();toggleActionMenu(this)\">\u22EF</button><div class=\"action-menu\">' + actions + '</div></td>' : '<td></td>';\n\n html += '<tr><td>' + roleBadge + '</td><td class=\"col-hide-sm\">' + remark + '</td><td>' + uses + '</td><td>' + expireText + '</td><td class=\"col-hide-md\">' + inviter + '</td>' + actionMenu + '</tr>';\n });\n html += '</tbody></table>';\n wrap.innerHTML = html;\n\n renderPagination(\"invitations-pagination\", paging, loadInvitations);\n}\n\nasync function createInvitation() {\n var btn = document.getElementById(\"create-invite-btn\");\n btn.disabled = true;\n\n var role = document.getElementById(\"invite-role\").value;\n var remark = document.getElementById(\"invite-remark\").value.trim();\n var expireHours = parseInt(document.getElementById(\"invite-expire\").value, 10);\n var maxUses = parseInt(document.getElementById(\"invite-max-uses\").value, 10) || 1;\n maxUses = Math.min(100, Math.max(1, maxUses));\n\n var data = await api(\"POST\", \"/invitations\", { role: role, remark: remark, expireHours: expireHours, maxUses: maxUses });\n btn.disabled = false;\n\n if (data.ok && data.invitation) {\n closeDialog(\"create-invitation-dialog\");\n copyToClipboard(data.invitation.link);\n showToast(__t(\"invitations.created\"), \"success\");\n // Reset form\n document.getElementById(\"invite-remark\").value = \"\";\n document.getElementById(\"invite-max-uses\").value = \"1\";\n updateRemarkCounter();\n loadInvitations(invitationsPage);\n }\n}\n\nasync function deleteInvitation(id) {\n var ok = await confirmDialog({ title: __t(\"invitations.deleteTitle\"), message: __t(\"invitations.deleteMsg\"), confirmLabel: __t(\"common.delete\"), danger: true });\n if (!ok) return;\n var data = await api(\"DELETE\", \"/invitations/\" + encodeURIComponent(id));\n if (data.ok) { showToast(__t(\"invitations.deleted\"), \"success\"); loadInvitations(invitationsPage); }\n}\n\n// Initialize role options based on caller role\nfunction initInviteRoleOptions() {\n var select = document.getElementById(\"invite-role\");\n if (__caller.role === \"owner\") {\n select.innerHTML = '<option value=\"guest\">' + __t(\"common.guest\") + '</option><option value=\"member\">' + __t(\"common.member\") + '</option><option value=\"admin\">' + __t(\"common.admin\") + '</option>';\n }\n}\n\n// Button-driven mutations must not double-submit (submitOnce \u2014 utils.ts).\n// createInvitation locks its own button (#create-invite-btn) already.\ndeleteInvitation = submitOnce(deleteInvitation);\n\nregisterTab(\"invitations\", function() {\n initInviteRoleOptions();\n loadInvitations(1);\n});\n";
|
|
7
7
|
export {};
|
|
8
8
|
//# sourceMappingURL=tab-invitations.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tab-invitations.d.ts","sourceRoot":"","sources":["../../../src/pages/admin/tab-invitations.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC;AAEpE,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,GAAG,GAAG,MAAM,CAmDjD;AAED,eAAO,MAAM,sBAAsB,
|
|
1
|
+
{"version":3,"file":"tab-invitations.d.ts","sourceRoot":"","sources":["../../../src/pages/admin/tab-invitations.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC;AAEpE,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,GAAG,GAAG,MAAM,CAmDjD;AAED,eAAO,MAAM,sBAAsB,i2MA4GlC,CAAC"}
|
|
@@ -153,6 +153,10 @@ function initInviteRoleOptions() {
|
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
// Button-driven mutations must not double-submit (submitOnce — utils.ts).
|
|
157
|
+
// createInvitation locks its own button (#create-invite-btn) already.
|
|
158
|
+
deleteInvitation = submitOnce(deleteInvitation);
|
|
159
|
+
|
|
156
160
|
registerTab("invitations", function() {
|
|
157
161
|
initInviteRoleOptions();
|
|
158
162
|
loadInvitations(1);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tab-invitations.js","sourceRoot":"","sources":["../../../src/pages/admin/tab-invitations.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,UAAU,kBAAkB,CAAC,CAAM;IACvC,OAAO,UAAU,CAAC;;;yDAGqC,CAAC,CAAC,mBAAmB,CAAC;oFACK,CAAC,CAAC,oBAAoB,CAAC;;4BAE/E,CAAC,CAAC,kBAAkB,CAAC;;;;;;;;;;;UAWvC,CAAC,CAAC,oBAAoB,CAAC;6BACJ,CAAC,CAAC,wBAAwB,CAAC;;kCAEtB,CAAC,CAAC,kBAAkB,CAAC;;gCAEvB,CAAC,CAAC,cAAc,CAAC;iCAChB,CAAC,CAAC,eAAe,CAAC;;;;kCAIjB,CAAC,CAAC,oBAAoB,CAAC;yFACgC,CAAC,CAAC,+BAA+B,CAAC;oEACvD,CAAC,CAAC,uBAAuB,CAAC;;;kCAG5D,CAAC,CAAC,uBAAuB,CAAC;;6BAE/B,CAAC,CAAC,kBAAkB,CAAC;uCACX,CAAC,CAAC,mBAAmB,CAAC;8BAC/B,CAAC,CAAC,oBAAoB,CAAC;;;;kCAInB,CAAC,CAAC,qBAAqB,CAAC;;;;;0FAKgC,CAAC,CAAC,eAAe,CAAC;8EAC9B,CAAC,CAAC,uBAAuB,CAAC;;;CAGvG,CAAC;AACF,CAAC;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAG,QAAQ,CAAC
|
|
1
|
+
{"version":3,"file":"tab-invitations.js","sourceRoot":"","sources":["../../../src/pages/admin/tab-invitations.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,UAAU,kBAAkB,CAAC,CAAM;IACvC,OAAO,UAAU,CAAC;;;yDAGqC,CAAC,CAAC,mBAAmB,CAAC;oFACK,CAAC,CAAC,oBAAoB,CAAC;;4BAE/E,CAAC,CAAC,kBAAkB,CAAC;;;;;;;;;;;UAWvC,CAAC,CAAC,oBAAoB,CAAC;6BACJ,CAAC,CAAC,wBAAwB,CAAC;;kCAEtB,CAAC,CAAC,kBAAkB,CAAC;;gCAEvB,CAAC,CAAC,cAAc,CAAC;iCAChB,CAAC,CAAC,eAAe,CAAC;;;;kCAIjB,CAAC,CAAC,oBAAoB,CAAC;yFACgC,CAAC,CAAC,+BAA+B,CAAC;oEACvD,CAAC,CAAC,uBAAuB,CAAC;;;kCAG5D,CAAC,CAAC,uBAAuB,CAAC;;6BAE/B,CAAC,CAAC,kBAAkB,CAAC;uCACX,CAAC,CAAC,mBAAmB,CAAC;8BAC/B,CAAC,CAAC,oBAAoB,CAAC;;;;kCAInB,CAAC,CAAC,qBAAqB,CAAC;;;;;0FAKgC,CAAC,CAAC,eAAe,CAAC;8EAC9B,CAAC,CAAC,uBAAuB,CAAC;;;CAGvG,CAAC;AACF,CAAC;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAG,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4G9C,CAAC"}
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
type TFn = (key: string, params?: Record<string, string>) => string;
|
|
5
5
|
export declare function membersTabHtml(t: TFn): string;
|
|
6
|
-
export declare const MEMBERS_TAB_SCRIPT = "\nvar membersPage = 1;\nvar membersPageSize = 20;\nvar memberSearchTimeout = null;\nvar currentMembersList = [];\nvar selectedMember = null;\nvar __membersAggregated = false;\n// Sort state: empty string = default (server decides + self-first on client).\n// Three-state click cycle: (none) \u2192 desc \u2192 asc \u2192 (none).\n// Persisted in location.search as sortBy/sortOrder so it survives refresh + share.\nvar membersSortBy = \"\";\nvar membersSortOrder = \"\";\nvar MEMBERS_SORT_VALID = [\"name\", \"role\", \"status\", \"source\", \"joined\"];\n\n/** Populate module sort state from the current URL query string. Idempotent. */\nfunction readMembersSortFromURL() {\n membersSortBy = \"\";\n membersSortOrder = \"\";\n var params = new URLSearchParams(location.search);\n var by = params.get(\"sortBy\");\n var order = params.get(\"sortOrder\");\n if (by && MEMBERS_SORT_VALID.indexOf(by) >= 0) {\n membersSortBy = by;\n membersSortOrder = (order === \"asc\" || order === \"desc\") ? order : \"desc\";\n }\n}\n\n/** Write current sort state back to location.search without reloading. */\nfunction writeMembersSortToURL() {\n var params = new URLSearchParams(location.search);\n if (membersSortBy) {\n params.set(\"sortBy\", membersSortBy);\n params.set(\"sortOrder\", membersSortOrder || \"desc\");\n } else {\n params.delete(\"sortBy\");\n params.delete(\"sortOrder\");\n }\n var qs = params.toString();\n var newUrl = location.pathname + (qs ? \"?\" + qs : \"\") + location.hash;\n history.replaceState(null, \"\", newUrl);\n}\n\n/** Cycle sort state for a column key and reload. */\nfunction toggleSort(key) {\n if (membersSortBy !== key) {\n membersSortBy = key;\n membersSortOrder = \"desc\";\n } else if (membersSortOrder === \"desc\") {\n membersSortOrder = \"asc\";\n } else {\n // Third click \u2014 back to default.\n membersSortBy = \"\";\n membersSortOrder = \"\";\n }\n writeMembersSortToURL();\n loadMembers(1);\n}\n\n/** Render a <th> with click-to-sort behavior. */\nfunction sortableTh(key, label, extraClass) {\n var isActive = membersSortBy === key;\n var arrow;\n if (isActive) {\n arrow = membersSortOrder === \"asc\"\n ? '<span class=\"sort-arrow sort-arrow-active\">\\u2191</span>'\n : '<span class=\"sort-arrow sort-arrow-active\">\\u2193</span>';\n } else {\n arrow = '<span class=\"sort-arrow\">\\u2195</span>';\n }\n var cls = \"sortable\" + (isActive ? \" sort-active\" : \"\") + (extraClass ? \" \" + extraClass : \"\");\n return '<th class=\"' + cls + '\" onclick=\"toggleSort(\\'' + key + '\\')\">' + label + arrow + '</th>';\n}\n\nfunction providerLabel(sp) {\n if (!sp) return \"\\u2014\";\n var key = \"members.providers.\" + sp;\n var val = __t(key);\n return val !== key ? val : sp;\n}\n\nfunction debouncedMemberSearch() {\n clearTimeout(memberSearchTimeout);\n memberSearchTimeout = setTimeout(function() { loadMembers(1); }, 300);\n}\n\n// Load registered instances for the filter dropdown (system admin only)\nvar __instanceNames = {};\nvar __instanceFilterInitialized = false;\nasync function initInstanceFilter() {\n if (__instanceFilterInitialized) return;\n try {\n // Best-effort: this endpoint is system-admin-only. Instance-scoped owners/admins\n // get 403 here, which is expected \u2014 fail silently (no error toast).\n var data = await api(\"GET\", \"/../admin/instances\", undefined, { silent: true });\n if (data.ok && data.instances && data.instances.length > 0) {\n __instanceFilterInitialized = true;\n var sel = document.getElementById(\"members-instance-filter\");\n data.instances.forEach(function(inst) {\n __instanceNames[inst.instanceDid] = inst.appName || inst.instanceDid.substring(0, 16);\n var opt = document.createElement(\"option\");\n opt.value = inst.instanceDid;\n opt.textContent = __instanceNames[inst.instanceDid];\n sel.appendChild(opt);\n });\n sel.style.display = \"\";\n }\n } catch(e) { /* not admin or no instances */ }\n}\n\n// Also support specific instance filter via query param\n// When ?instance=z1xxx, team handler needs to use that instanceDid\n\nasync function loadMembers(page) {\n membersPage = page || 1;\n var instanceFilter = document.getElementById(\"members-instance-filter\").value;\n var qs = \"?page=\" + membersPage + \"&pageSize=\" + membersPageSize;\n // System view (\"\") \u2192 use defaultInstanceDid so we query membership, not global users\n if (instanceFilter === \"\" && __pageData.instanceDid) {\n qs += \"&instance=\" + encodeURIComponent(__pageData.instanceDid);\n } else if (instanceFilter) {\n qs += \"&instance=\" + encodeURIComponent(instanceFilter);\n }\n var search = document.getElementById(\"members-search\").value.trim();\n if (search) qs += \"&search=\" + encodeURIComponent(search);\n var role = document.getElementById(\"members-role-filter\").value;\n if (role) qs += \"&role=\" + encodeURIComponent(role);\n var status = document.getElementById(\"members-status-filter\").value;\n if (status !== \"\") qs += \"&approved=\" + status;\n var source = document.getElementById(\"members-source-filter\").value;\n if (source) qs += \"&sourceProvider=\" + encodeURIComponent(source);\n if (membersSortBy) {\n qs += \"&sortBy=\" + encodeURIComponent(membersSortBy);\n qs += \"&sortOrder=\" + encodeURIComponent(membersSortOrder || \"desc\");\n }\n\n document.getElementById(\"members-table-wrap\").innerHTML = skeletonTable(8, 6);\n document.getElementById(\"members-pagination\").innerHTML = \"\";\n var data = await api(\"GET\", \"/members\" + qs);\n if (!data.ok) {\n document.getElementById(\"members-table-wrap\").innerHTML = '<div class=\"empty-state\"><div class=\"empty-state-icon\"><svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 8v4m0 4h.01\"/></svg></div><div class=\"empty-state-title\">' + __t(\"members.loadFailed\") + '</div></div>';\n return;\n }\n __membersAggregated = !!data.aggregated;\n // Only pin the caller to the top when using the default (server) order.\n // Under explicit sort, honor the user's intent without re-shuffling.\n if (membersSortBy) {\n currentMembersList = data.users || [];\n } else {\n currentMembersList = (data.users || []).slice().sort(function(a, b) {\n if (a.did === __caller.did) return -1;\n if (b.did === __caller.did) return 1;\n return (a.createdAt || 0) - (b.createdAt || 0);\n });\n }\n renderMembersTable(currentMembersList, data.paging);\n updateTransferSection(data.users);\n}\n\nfunction renderMembersTable(users, paging) {\n var wrap = document.getElementById(\"members-table-wrap\");\n // Aggregated (cross-instance \"All Instances\") view can't tell which instance\n // a role change should apply to, so per-row actions are disabled there \u2014 see\n // canOperate below. Surface why, instead of silently hiding the buttons.\n var aggregatedHint = __membersAggregated ? '<p class=\"text-muted\" style=\"font-size:13px;margin-bottom:12px;\">' + __t(\"members.aggregatedHint\") + '</p>' : '';\n if (!users || users.length === 0) {\n wrap.innerHTML = aggregatedHint + '<div class=\"empty-state\"><div class=\"empty-state-icon\"><svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2\"/><circle cx=\"9\" cy=\"7\" r=\"4\"/></svg></div><div class=\"empty-state-title\">' + __t(\"members.noMembers\") + '</div><div class=\"empty-state-desc\">' + __t(\"members.noMembersHint\") + '</div></div>';\n document.getElementById(\"members-pagination\").innerHTML = \"\";\n return;\n }\n\n var instanceSel = document.getElementById(\"members-instance-filter\");\n var instanceVisible = instanceSel && instanceSel.style.display !== 'none';\n var showInstance = instanceVisible && instanceSel.value === \"all\";\n var html = aggregatedHint + '<table class=\"table\"><thead><tr>';\n html += sortableTh(\"name\", __t(\"members.col.member\"));\n html += sortableTh(\"role\", __t(\"members.col.role\"));\n if (showInstance) html += '<th>' + __t(\"members.col.instance\") + '</th>';\n html += sortableTh(\"source\", __t(\"members.col.source\"), \"col-hide-sm\");\n html += sortableTh(\"status\", __t(\"members.col.status\"), \"col-hide-sm\");\n html += sortableTh(\"joined\", __t(\"members.col.joined\"), \"col-hide-md\");\n // Registered/Last Login From columns removed \u2014 domain info not meaningful in Worker context\n html += '<th></th></tr></thead><tbody>';\n users.forEach(function(u) {\n var userDid = u.did || u.user_did || '';\n var av = renderAvatar(u.avatar, u.fullName, \"sm\");\n var name = u.fullName ? escapeHtml(u.fullName) : '<span class=\"text-muted\">(unnamed)</span>';\n var did = '<did-address did=\"' + escapeHtml(userDid) + '\" compact></did-address>';\n var roleBadge = '<span class=\"badge badge-' + (u.role || \"guest\") + '\">' + __t(\"common.\" + (u.role || \"guest\")) + '</span>';\n var statusBadge = u.approved !== undefined ? (u.approved ? '<span class=\"badge badge-active\">' + __t(\"common.active\") + '</span>' : '<span class=\"badge badge-blocked\">' + __t(\"common.blocked\") + '</span>') : '<span class=\"text-muted\">\u2014</span>';\n var joined = (u.createdAt || u.joined_at) ? '<span class=\"cell-date\">' + escapeHtml(relativeTime(u.createdAt || u.joined_at)) + '</span>' : '<span class=\"text-muted\">\u2014</span>';\n html += '<tr style=\"cursor:pointer\" onclick=\"openMemberDetail(\\'' + escapeHtml(userDid) + '\\')\">';\n html += '<td><div class=\"member-identity\">' + av + '<div><div class=\"member-name\">' + name + '</div><div class=\"member-email\">' + did + '</div></div></div></td>';\n html += '<td>' + roleBadge + '</td>';\n if (showInstance) {\n if (__membersAggregated && u.instance_count > 1) {\n html += '<td><a href=\"javascript:void(0)\" onclick=\"event.stopPropagation();openMemberDetail(\\'' + escapeHtml(userDid) + '\\')\" style=\"color:var(--blue);font-size:14px;text-decoration:none\">' + u.instance_count + ' ' + __t(\"members.instances\") + '</a></td>';\n } else {\n var instDid = (__membersAggregated && u.instances && u.instances.length === 1) ? u.instances[0].instance_did : (u.instance_did || '');\n var instName = __instanceNames[instDid] || '';\n if (!instName) instName = instDid ? __t(\"members.currentInstance\") : '\u2014';\n html += '<td><div>' + escapeHtml(instName);\n if (instDid) html += '<div style=\"font-size:11px;color:var(--text-secondary);margin-top:2px\" onclick=\"event.stopPropagation()\"><did-address did=\"' + escapeHtml(instDid) + '\" compact></did-address></div>';\n html += '</div></td>';\n }\n }\n var sourceBadge = '<span class=\"text-muted\">' + escapeHtml(providerLabel(u.sourceProvider)) + '</span>';\n html += '<td class=\"col-hide-sm\">' + sourceBadge + '</td>';\n html += '<td class=\"col-hide-sm\">' + statusBadge + '</td>';\n html += '<td class=\"col-hide-md\">' + joined + '</td>';\n // Actions: only for current instance members, not cross-instance\n var canOperate = !instanceVisible || (instanceVisible && instanceSel.value === '');\n // In aggregated mode, only allow actions on users whose instance matches current system\n if (__membersAggregated) canOperate = false;\n html += '<td class=\"action-cell\">' + (canOperate ? renderMemberActions(u) : '') + '</td>';\n html += '</tr>';\n });\n html += '</tbody></table>';\n wrap.innerHTML = html;\n\n renderPagination(\"members-pagination\", paging, loadMembers);\n}\n\nfunction renderMemberActions(u) {\n var isOwner = __caller.role === \"owner\";\n var isAdmin = __caller.role === \"admin\";\n var isSelf = u.did === __caller.did;\n var ROLE_LEVEL = { owner: 3, admin: 2, member: 1, guest: 0 };\n var callerLevel = ROLE_LEVEL[__caller.role] || 0;\n var targetLevel = ROLE_LEVEL[u.role] || 0;\n // Can only act on members with lower privilege\n var canManage = !isSelf && targetLevel < callerLevel;\n var items = [];\n\n // Change role: can manage lower-level members\n if (canManage) {\n items.push('<button class=\"action-menu-item\" onclick=\"event.stopPropagation();openChangeRoleForDid(\\'' + escapeHtml(u.did) + '\\')\">' + __t(\"members.changeRole\") + '</button>');\n }\n if (canManage && u.approved) {\n items.push('<button class=\"action-menu-item\" onclick=\"event.stopPropagation();blockMember(\\'' + escapeHtml(u.did) + '\\')\">' + __t(\"members.block\") + '</button>');\n }\n if (canManage && !u.approved) {\n items.push('<button class=\"action-menu-item\" onclick=\"event.stopPropagation();unblockMember(\\'' + escapeHtml(u.did) + '\\')\">' + __t(\"members.unblock\") + '</button>');\n }\n if (items.length > 0 && canManage) {\n items.push('<div class=\"action-menu-sep\"></div>');\n }\n if (canManage) {\n items.push('<button class=\"action-menu-item danger\" onclick=\"event.stopPropagation();removeMember(\\'' + escapeHtml(u.did) + '\\')\">' + __t(\"members.remove\") + '</button>');\n }\n\n if (items.length === 0) return \"\";\n return '<button class=\"action-trigger\" onclick=\"event.stopPropagation();toggleActionMenu(this)\">\u22EF</button><div class=\"action-menu\">' + items.join(\"\") + '</div>';\n}\n\nfunction toggleActionMenu(trigger) {\n var menu = trigger.nextElementSibling;\n var wasOpen = menu.classList.contains(\"open\");\n // Close all menus first\n document.querySelectorAll(\".action-menu.open\").forEach(function(m) { m.classList.remove(\"open\"); });\n if (!wasOpen) menu.classList.toggle(\"open\");\n}\n\nasync function openMemberDetail(did) {\n var u = currentMembersList.find(function(m) { return m.did === did; });\n if (!u) return;\n selectedMember = u;\n\n // Fetch full member info (with connectedAccounts) from API.\n // Falls back to in-memory list data if the request fails.\n var detail = u;\n try {\n var resp = await api(\"GET\", \"/members/\" + encodeURIComponent(did));\n if (resp && resp.ok && resp.user) {\n // Merge: in-memory list has fields the API may omit (instances, passkeyCount in some paths).\n detail = Object.assign({}, u, resp.user);\n selectedMember = detail;\n }\n } catch (e) {}\n\n var body = document.getElementById(\"member-detail-body\");\n var html = '<div class=\"profile-header\">' + renderAvatar(detail.avatar, detail.fullName) + '<div>';\n html += '<div style=\"font-weight:500;font-size:16px;color:var(--text-white)\">' + escapeHtml(detail.fullName || \"(unnamed)\") + '</div>';\n html += '<did-address did=\"' + escapeHtml(detail.did) + '\" compact></did-address>';\n html += '</div></div>';\n\n html += '<div class=\"settings-card\" style=\"margin-top:16px\">';\n html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailRole\") + '</span><span class=\"settings-value\"><span class=\"badge badge-' + (detail.role || \"guest\") + '\">' + __t(\"common.\" + (detail.role || \"guest\")) + '</span></span></div>';\n html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailStatus\") + '</span><span class=\"settings-value\">' + (detail.approved ? '<span class=\"badge badge-active\">' + __t(\"common.active\") + '</span>' : '<span class=\"badge badge-blocked\">' + __t(\"common.blocked\") + '</span>') + '</span></div>';\n html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailSource\") + '</span><span class=\"settings-value\">' + escapeHtml(providerLabel(detail.sourceProvider)) + '</span></div>';\n if (detail.email) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailEmail\") + '</span><span class=\"settings-value\">' + escapeHtml(detail.email) + '</span></div>';\n if (detail.inviterName) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailInvitedBy\") + '</span><span class=\"settings-value\">' + escapeHtml(detail.inviterName) + '</span></div>';\n if (detail.createdAt) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailJoined\") + '</span><span class=\"settings-value\">' + escapeHtml(absoluteTime(detail.createdAt)) + '</span></div>';\n if (detail.lastLoginAt) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailLastLogin\") + '</span><span class=\"settings-value\">' + escapeHtml(absoluteTime(detail.lastLoginAt)) + '</span></div>';\n if (detail.sourceDomain) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailRegisteredFrom\") + '</span><span class=\"settings-value\">' + escapeHtml(detail.sourceDomain) + '</span></div>';\n if (detail.lastLoginDomain) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailLastLoginFrom\") + '</span><span class=\"settings-value\">' + escapeHtml(detail.lastLoginDomain) + '</span></div>';\n if (detail.passkeyCount !== undefined) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailPasskeys\") + '</span><span class=\"settings-value\">' + detail.passkeyCount + '</span></div>';\n html += '</div>';\n\n // Connected Accounts (read-only)\n if (detail.connectedAccounts && detail.connectedAccounts.length > 0) {\n html += '<div class=\"settings-card\" style=\"margin-top:16px\">';\n html += '<div class=\"settings-card-header\"><span>' + __t(\"members.detailConnectedAccounts\") + ' (' + detail.connectedAccounts.length + ')</span></div>';\n html += '<div style=\"max-height:240px;overflow-y:auto\">';\n detail.connectedAccounts.forEach(function(a) {\n var label = providerLabel(a.provider);\n var info = '';\n if (a.userInfo && a.userInfo.name) info = a.userInfo.name;\n else if (a.userInfo && a.userInfo.email) info = a.userInfo.email;\n else if (a.id) info = a.id;\n html += '<div class=\"settings-row\" style=\"gap:12px;align-items:flex-start\">';\n html += '<div style=\"flex:1;min-width:0\">';\n html += '<div style=\"font-size:14px;font-weight:500;color:var(--text)\">' + escapeHtml(label);\n if (a.isMain) html += ' <span class=\"badge badge-active\" style=\"font-size:10px;padding:1px 6px;\">' + __t(\"profile.mainAccount\") + '</span>';\n html += '</div>';\n if (info) html += '<div style=\"font-size:12px;color:var(--text-secondary);margin-top:2px\">' + escapeHtml(info) + '</div>';\n if (a.did) html += '<div style=\"font-size:11px;color:var(--text-secondary);margin-top:2px\"><did-address did=\"' + escapeHtml(a.did) + '\" compact></did-address></div>';\n html += '</div>';\n if (a.lastLoginAt) html += '<div style=\"font-size:12px;color:var(--text-secondary);flex-shrink:0\">' + escapeHtml(relativeTime(a.lastLoginAt)) + '</div>';\n html += '</div>';\n });\n html += '</div></div>';\n }\n\n // Instance memberships (aggregated mode)\n if (detail.instances && detail.instances.length > 0) {\n html += '<div class=\"settings-card\" style=\"margin-top:16px\">';\n html += '<div class=\"settings-card-header\"><span>' + __t(\"members.instanceMemberships\") + ' (' + detail.instances.length + ')</span></div>';\n html += '<div style=\"max-height:240px;overflow-y:auto\">';\n detail.instances.forEach(function(inst) {\n var instName = __instanceNames[inst.instance_did] || '';\n var instDid = inst.instance_did || '';\n html += '<div class=\"settings-row\" style=\"gap:12px\">';\n html += '<div style=\"flex:1;min-width:0\"><div style=\"font-size:14px;font-weight:500;color:var(--text)\">' + escapeHtml(instName || __t(\"members.currentInstance\")) + '</div>';\n if (instDid) html += '<div style=\"font-size:11px;color:var(--text-secondary);margin-top:2px\"><did-address did=\"' + escapeHtml(instDid) + '\" compact></did-address></div>';\n html += '</div>';\n html += '<div style=\"display:flex;align-items:center;gap:8px;flex-shrink:0\"><span class=\"badge badge-' + inst.role + '\">' + __t(\"common.\" + inst.role) + '</span>';\n if (inst.joined_at) html += '<span style=\"font-size:12px;color:var(--text-secondary)\">' + escapeHtml(relativeTime(inst.joined_at)) + '</span>';\n html += '</div></div>';\n });\n html += '</div></div>';\n }\n\n body.innerHTML = html;\n\n // Change role: mirror list's canOperate + renderMemberActions logic exactly.\n // Use post-merge detail so role/did reflect the latest API response.\n var changeRoleBtn = document.getElementById(\"member-detail-change-role\");\n var instanceSel = document.getElementById(\"members-instance-filter\");\n var instanceVisible = instanceSel && instanceSel.style.display !== 'none';\n var canOperate = !instanceVisible || (instanceVisible && instanceSel.value === '');\n if (__membersAggregated) canOperate = false;\n var isSelf = detail.did === __caller.did;\n var ROLE_LEVEL = { owner: 3, admin: 2, member: 1, guest: 0 };\n var callerLevel = ROLE_LEVEL[__caller.role] || 0;\n var targetLevel = ROLE_LEVEL[detail.role] || 0;\n var canChangeRole = canOperate && !isSelf && targetLevel < callerLevel;\n if (canChangeRole) {\n changeRoleBtn.classList.remove(\"hidden\");\n } else {\n changeRoleBtn.classList.add(\"hidden\");\n }\n\n showDialog(\"member-detail-dialog\");\n}\n\nfunction openChangeRoleForDid(did) {\n var u = currentMembersList.find(function(m) { return m.did === did; });\n if (!u) return;\n selectedMember = u;\n openChangeRoleDialog();\n}\n\nfunction openChangeRoleDialog() {\n if (!selectedMember) return;\n document.getElementById(\"change-role-title\").textContent = __t(\"members.changeRole\");\n document.getElementById(\"change-role-subtitle\").textContent = __t(\"members.changeRoleFor\", { name: selectedMember.fullName || truncateDid(selectedMember.did) });\n // Show/hide role options based on caller's level (only owner can assign admin)\n var radios = document.querySelectorAll('input[name=\"new-role\"]');\n radios.forEach(function(r) {\n r.checked = r.value === selectedMember.role;\n var label = r.closest('label');\n if (label) {\n if (r.value === 'admin' && __caller.role !== 'owner') label.style.display = 'none';\n else label.style.display = '';\n }\n });\n closeDialog(\"member-detail-dialog\");\n showDialog(\"change-role-dialog\");\n}\n\nasync function saveRoleChange() {\n if (!selectedMember) return;\n var selected = document.querySelector('input[name=\"new-role\"]:checked');\n if (!selected) return;\n var newRole = selected.value;\n var data = await api(\"PUT\", \"/members/\" + encodeURIComponent(selectedMember.did) + \"/role\", { role: newRole });\n if (data.ok) {\n showToast(__t(\"members.roleUpdated\"), \"success\");\n closeDialog(\"change-role-dialog\");\n loadMembers(membersPage);\n }\n}\n\n// name is looked up (not passed via onclick) so a member's self-set fullName\n// never has to be embedded as a JS-string literal inside an inline onclick=\n// attribute \u2014 HTML entity-escaping alone can't fully neutralize that context,\n// since the browser decodes entities before parsing onclick as script.\nasync function blockMember(did) {\n var u = currentMembersList.find(function(m) { return m.did === did; });\n var name = (u && u.fullName) || did;\n var ok = await confirmDialog({ title: __t(\"members.blockTitle\"), message: __t(\"members.blockMsg\", { name: name }), confirmLabel: __t(\"members.blockBtn\"), danger: true });\n if (!ok) return;\n var data = await api(\"PUT\", \"/members/\" + encodeURIComponent(did) + \"/approval\", { approved: false });\n if (data.ok) { showToast(__t(\"members.blockSuccess\"), \"success\"); loadMembers(membersPage); }\n}\n\nasync function unblockMember(did) {\n var u = currentMembersList.find(function(m) { return m.did === did; });\n var name = (u && u.fullName) || did;\n var ok = await confirmDialog({ title: __t(\"members.unblockTitle\"), message: __t(\"members.unblockMsg\", { name: name }), confirmLabel: __t(\"members.unblock\") });\n if (!ok) return;\n var data = await api(\"PUT\", \"/members/\" + encodeURIComponent(did) + \"/approval\", { approved: true });\n if (data.ok) { showToast(__t(\"members.unblockSuccess\"), \"success\"); loadMembers(membersPage); }\n}\n\nasync function removeMember(did) {\n var u = currentMembersList.find(function(m) { return m.did === did; });\n var name = (u && u.fullName) || did;\n var ok = await confirmDialog({ title: __t(\"members.removeTitle\"), message: __t(\"members.removeMsg\", { name: name }), confirmLabel: __t(\"members.remove\"), danger: true });\n if (!ok) return;\n var data = await api(\"DELETE\", \"/members/\" + encodeURIComponent(did));\n if (data.ok) { showToast(__t(\"members.removeSuccess\"), \"success\"); loadMembers(membersPage); }\n}\n\nfunction updateTransferSection(users) {\n var section = document.getElementById(\"ownership-transfer-section\");\n if (__caller.role !== \"owner\") {\n section.classList.add(\"hidden\");\n return;\n }\n section.classList.remove(\"hidden\");\n var select = document.getElementById(\"transfer-target\");\n var html = '<option value=\"\">' + __t(\"members.transferSelect\") + '</option>';\n users.forEach(function(u) {\n if (u.did !== __caller.did && u.approved && u.role !== \"owner\") {\n html += '<option value=\"' + escapeHtml(u.did) + '\">' + escapeHtml(u.fullName || truncateDid(u.did)) + ' (' + u.role + ')</option>';\n }\n });\n select.innerHTML = html;\n}\n\nasync function handleTransferOwnership() {\n var target = document.getElementById(\"transfer-target\").value;\n if (!target) { showToast(__t(\"members.selectMemberFirst\"), \"error\"); return; }\n var targetUser = currentMembersList.find(function(u) { return u.did === target; });\n var name = targetUser ? (targetUser.fullName || truncateDid(target)) : truncateDid(target);\n var ok = await confirmDialog({\n title: __t(\"members.transferTitle\"),\n message: __t(\"members.transferMsg\", { name: name }),\n confirmLabel: __t(\"members.transferConfirm\"),\n danger: true,\n });\n if (!ok) return;\n var data = await api(\"POST\", \"/transfer-ownership\", { targetDid: target });\n if (data.ok) {\n showToast(__t(\"members.transferSuccess\"), \"success\");\n setTimeout(function() { location.reload(); }, 1000);\n }\n}\n\n// Close action menus on outside click\ndocument.addEventListener(\"click\", function() {\n document.querySelectorAll(\".action-menu.open\").forEach(function(m) { m.classList.remove(\"open\"); });\n});\n\nregisterTab(\"members\", function() {\n initInstanceFilter().then(function() {\n // Auto-select instance from URL query (e.g., ?instance=z1xxx)\n var urlInstance = new URLSearchParams(window.location.search).get(\"instance\");\n if (urlInstance) {\n var sel = document.getElementById(\"members-instance-filter\");\n if (sel) sel.value = urlInstance;\n }\n // Restore sort state from URL (sortBy / sortOrder) on tab enter.\n readMembersSortFromURL();\n loadMembers(1);\n });\n});\n";
|
|
6
|
+
export declare const MEMBERS_TAB_SCRIPT = "\nvar membersPage = 1;\nvar membersPageSize = 20;\nvar memberSearchTimeout = null;\nvar currentMembersList = [];\nvar selectedMember = null;\nvar __membersAggregated = false;\n// Sort state: empty string = default (server decides + self-first on client).\n// Three-state click cycle: (none) \u2192 desc \u2192 asc \u2192 (none).\n// Persisted in location.search as sortBy/sortOrder so it survives refresh + share.\nvar membersSortBy = \"\";\nvar membersSortOrder = \"\";\nvar MEMBERS_SORT_VALID = [\"name\", \"role\", \"status\", \"source\", \"joined\"];\n\n/** Populate module sort state from the current URL query string. Idempotent. */\nfunction readMembersSortFromURL() {\n membersSortBy = \"\";\n membersSortOrder = \"\";\n var params = new URLSearchParams(location.search);\n var by = params.get(\"sortBy\");\n var order = params.get(\"sortOrder\");\n if (by && MEMBERS_SORT_VALID.indexOf(by) >= 0) {\n membersSortBy = by;\n membersSortOrder = (order === \"asc\" || order === \"desc\") ? order : \"desc\";\n }\n}\n\n/** Write current sort state back to location.search without reloading. */\nfunction writeMembersSortToURL() {\n var params = new URLSearchParams(location.search);\n if (membersSortBy) {\n params.set(\"sortBy\", membersSortBy);\n params.set(\"sortOrder\", membersSortOrder || \"desc\");\n } else {\n params.delete(\"sortBy\");\n params.delete(\"sortOrder\");\n }\n var qs = params.toString();\n var newUrl = location.pathname + (qs ? \"?\" + qs : \"\") + location.hash;\n history.replaceState(null, \"\", newUrl);\n}\n\n/** Cycle sort state for a column key and reload. */\nfunction toggleSort(key) {\n if (membersSortBy !== key) {\n membersSortBy = key;\n membersSortOrder = \"desc\";\n } else if (membersSortOrder === \"desc\") {\n membersSortOrder = \"asc\";\n } else {\n // Third click \u2014 back to default.\n membersSortBy = \"\";\n membersSortOrder = \"\";\n }\n writeMembersSortToURL();\n loadMembers(1);\n}\n\n/** Render a <th> with click-to-sort behavior. */\nfunction sortableTh(key, label, extraClass) {\n var isActive = membersSortBy === key;\n var arrow;\n if (isActive) {\n arrow = membersSortOrder === \"asc\"\n ? '<span class=\"sort-arrow sort-arrow-active\">\\u2191</span>'\n : '<span class=\"sort-arrow sort-arrow-active\">\\u2193</span>';\n } else {\n arrow = '<span class=\"sort-arrow\">\\u2195</span>';\n }\n var cls = \"sortable\" + (isActive ? \" sort-active\" : \"\") + (extraClass ? \" \" + extraClass : \"\");\n return '<th class=\"' + cls + '\" onclick=\"toggleSort(\\'' + key + '\\')\">' + label + arrow + '</th>';\n}\n\nfunction providerLabel(sp) {\n if (!sp) return \"\\u2014\";\n var key = \"members.providers.\" + sp;\n var val = __t(key);\n return val !== key ? val : sp;\n}\n\nfunction debouncedMemberSearch() {\n clearTimeout(memberSearchTimeout);\n memberSearchTimeout = setTimeout(function() { loadMembers(1); }, 300);\n}\n\n// Load registered instances for the filter dropdown (system admin only)\nvar __instanceNames = {};\nvar __instanceFilterInitialized = false;\nasync function initInstanceFilter() {\n if (__instanceFilterInitialized) return;\n try {\n // Best-effort: this endpoint is system-admin-only. Instance-scoped owners/admins\n // get 403 here, which is expected \u2014 fail silently (no error toast).\n var data = await api(\"GET\", \"/../admin/instances\", undefined, { silent: true });\n if (data.ok && data.instances && data.instances.length > 0) {\n __instanceFilterInitialized = true;\n var sel = document.getElementById(\"members-instance-filter\");\n data.instances.forEach(function(inst) {\n __instanceNames[inst.instanceDid] = inst.appName || inst.instanceDid.substring(0, 16);\n var opt = document.createElement(\"option\");\n opt.value = inst.instanceDid;\n opt.textContent = __instanceNames[inst.instanceDid];\n sel.appendChild(opt);\n });\n sel.style.display = \"\";\n }\n } catch(e) { /* not admin or no instances */ }\n}\n\n// Also support specific instance filter via query param\n// When ?instance=z1xxx, team handler needs to use that instanceDid\n\nasync function loadMembers(page) {\n membersPage = page || 1;\n var instanceFilter = document.getElementById(\"members-instance-filter\").value;\n var qs = \"?page=\" + membersPage + \"&pageSize=\" + membersPageSize;\n // System view (\"\") \u2192 use defaultInstanceDid so we query membership, not global users\n if (instanceFilter === \"\" && __pageData.instanceDid) {\n qs += \"&instance=\" + encodeURIComponent(__pageData.instanceDid);\n } else if (instanceFilter) {\n qs += \"&instance=\" + encodeURIComponent(instanceFilter);\n }\n var search = document.getElementById(\"members-search\").value.trim();\n if (search) qs += \"&search=\" + encodeURIComponent(search);\n var role = document.getElementById(\"members-role-filter\").value;\n if (role) qs += \"&role=\" + encodeURIComponent(role);\n var status = document.getElementById(\"members-status-filter\").value;\n if (status !== \"\") qs += \"&approved=\" + status;\n var source = document.getElementById(\"members-source-filter\").value;\n if (source) qs += \"&sourceProvider=\" + encodeURIComponent(source);\n if (membersSortBy) {\n qs += \"&sortBy=\" + encodeURIComponent(membersSortBy);\n qs += \"&sortOrder=\" + encodeURIComponent(membersSortOrder || \"desc\");\n }\n\n document.getElementById(\"members-table-wrap\").innerHTML = skeletonTable(8, 6);\n document.getElementById(\"members-pagination\").innerHTML = \"\";\n var data = await api(\"GET\", \"/members\" + qs);\n if (!data.ok) {\n document.getElementById(\"members-table-wrap\").innerHTML = '<div class=\"empty-state\"><div class=\"empty-state-icon\"><svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 8v4m0 4h.01\"/></svg></div><div class=\"empty-state-title\">' + __t(\"members.loadFailed\") + '</div></div>';\n return;\n }\n __membersAggregated = !!data.aggregated;\n // Only pin the caller to the top when using the default (server) order.\n // Under explicit sort, honor the user's intent without re-shuffling.\n if (membersSortBy) {\n currentMembersList = data.users || [];\n } else {\n currentMembersList = (data.users || []).slice().sort(function(a, b) {\n if (a.did === __caller.did) return -1;\n if (b.did === __caller.did) return 1;\n return (a.createdAt || 0) - (b.createdAt || 0);\n });\n }\n renderMembersTable(currentMembersList, data.paging);\n updateTransferSection(data.users);\n}\n\nfunction renderMembersTable(users, paging) {\n var wrap = document.getElementById(\"members-table-wrap\");\n // Aggregated (cross-instance \"All Instances\") view can't tell which instance\n // a role change should apply to, so per-row actions are disabled there \u2014 see\n // canOperate below. Surface why, instead of silently hiding the buttons.\n var aggregatedHint = __membersAggregated ? '<p class=\"text-muted\" style=\"font-size:13px;margin-bottom:12px;\">' + __t(\"members.aggregatedHint\") + '</p>' : '';\n if (!users || users.length === 0) {\n wrap.innerHTML = aggregatedHint + '<div class=\"empty-state\"><div class=\"empty-state-icon\"><svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2\"/><circle cx=\"9\" cy=\"7\" r=\"4\"/></svg></div><div class=\"empty-state-title\">' + __t(\"members.noMembers\") + '</div><div class=\"empty-state-desc\">' + __t(\"members.noMembersHint\") + '</div></div>';\n document.getElementById(\"members-pagination\").innerHTML = \"\";\n return;\n }\n\n var instanceSel = document.getElementById(\"members-instance-filter\");\n var instanceVisible = instanceSel && instanceSel.style.display !== 'none';\n var showInstance = instanceVisible && instanceSel.value === \"all\";\n var html = aggregatedHint + '<table class=\"table\"><thead><tr>';\n html += sortableTh(\"name\", __t(\"members.col.member\"));\n html += sortableTh(\"role\", __t(\"members.col.role\"));\n if (showInstance) html += '<th>' + __t(\"members.col.instance\") + '</th>';\n html += sortableTh(\"source\", __t(\"members.col.source\"), \"col-hide-sm\");\n html += sortableTh(\"status\", __t(\"members.col.status\"), \"col-hide-sm\");\n html += sortableTh(\"joined\", __t(\"members.col.joined\"), \"col-hide-md\");\n // Registered/Last Login From columns removed \u2014 domain info not meaningful in Worker context\n html += '<th></th></tr></thead><tbody>';\n users.forEach(function(u) {\n var userDid = u.did || u.user_did || '';\n var av = renderAvatar(u.avatar, u.fullName, \"sm\");\n var name = u.fullName ? escapeHtml(u.fullName) : '<span class=\"text-muted\">(unnamed)</span>';\n var did = '<did-address did=\"' + escapeHtml(userDid) + '\" compact></did-address>';\n var roleBadge = '<span class=\"badge badge-' + (u.role || \"guest\") + '\">' + __t(\"common.\" + (u.role || \"guest\")) + '</span>';\n var statusBadge = u.approved !== undefined ? (u.approved ? '<span class=\"badge badge-active\">' + __t(\"common.active\") + '</span>' : '<span class=\"badge badge-blocked\">' + __t(\"common.blocked\") + '</span>') : '<span class=\"text-muted\">\u2014</span>';\n var joined = (u.createdAt || u.joined_at) ? '<span class=\"cell-date\">' + escapeHtml(relativeTime(u.createdAt || u.joined_at)) + '</span>' : '<span class=\"text-muted\">\u2014</span>';\n html += '<tr style=\"cursor:pointer\" onclick=\"openMemberDetail(\\'' + escapeHtml(userDid) + '\\')\">';\n html += '<td><div class=\"member-identity\">' + av + '<div><div class=\"member-name\">' + name + '</div><div class=\"member-email\">' + did + '</div></div></div></td>';\n html += '<td>' + roleBadge + '</td>';\n if (showInstance) {\n if (__membersAggregated && u.instance_count > 1) {\n html += '<td><a href=\"javascript:void(0)\" onclick=\"event.stopPropagation();openMemberDetail(\\'' + escapeHtml(userDid) + '\\')\" style=\"color:var(--blue);font-size:14px;text-decoration:none\">' + u.instance_count + ' ' + __t(\"members.instances\") + '</a></td>';\n } else {\n var instDid = (__membersAggregated && u.instances && u.instances.length === 1) ? u.instances[0].instance_did : (u.instance_did || '');\n var instName = __instanceNames[instDid] || '';\n if (!instName) instName = instDid ? __t(\"members.currentInstance\") : '\u2014';\n html += '<td><div>' + escapeHtml(instName);\n if (instDid) html += '<div style=\"font-size:11px;color:var(--text-secondary);margin-top:2px\" onclick=\"event.stopPropagation()\"><did-address did=\"' + escapeHtml(instDid) + '\" compact></did-address></div>';\n html += '</div></td>';\n }\n }\n var sourceBadge = '<span class=\"text-muted\">' + escapeHtml(providerLabel(u.sourceProvider)) + '</span>';\n html += '<td class=\"col-hide-sm\">' + sourceBadge + '</td>';\n html += '<td class=\"col-hide-sm\">' + statusBadge + '</td>';\n html += '<td class=\"col-hide-md\">' + joined + '</td>';\n // Actions: only for current instance members, not cross-instance\n var canOperate = !instanceVisible || (instanceVisible && instanceSel.value === '');\n // In aggregated mode, only allow actions on users whose instance matches current system\n if (__membersAggregated) canOperate = false;\n html += '<td class=\"action-cell\">' + (canOperate ? renderMemberActions(u) : '') + '</td>';\n html += '</tr>';\n });\n html += '</tbody></table>';\n wrap.innerHTML = html;\n\n renderPagination(\"members-pagination\", paging, loadMembers);\n}\n\nfunction renderMemberActions(u) {\n var isOwner = __caller.role === \"owner\";\n var isAdmin = __caller.role === \"admin\";\n var isSelf = u.did === __caller.did;\n var ROLE_LEVEL = { owner: 3, admin: 2, member: 1, guest: 0 };\n var callerLevel = ROLE_LEVEL[__caller.role] || 0;\n var targetLevel = ROLE_LEVEL[u.role] || 0;\n // Can only act on members with lower privilege\n var canManage = !isSelf && targetLevel < callerLevel;\n var items = [];\n\n // Change role: can manage lower-level members\n if (canManage) {\n items.push('<button class=\"action-menu-item\" onclick=\"event.stopPropagation();openChangeRoleForDid(\\'' + escapeHtml(u.did) + '\\')\">' + __t(\"members.changeRole\") + '</button>');\n }\n if (canManage && u.approved) {\n items.push('<button class=\"action-menu-item\" onclick=\"event.stopPropagation();blockMember(\\'' + escapeHtml(u.did) + '\\')\">' + __t(\"members.block\") + '</button>');\n }\n if (canManage && !u.approved) {\n items.push('<button class=\"action-menu-item\" onclick=\"event.stopPropagation();unblockMember(\\'' + escapeHtml(u.did) + '\\')\">' + __t(\"members.unblock\") + '</button>');\n }\n if (items.length > 0 && canManage) {\n items.push('<div class=\"action-menu-sep\"></div>');\n }\n if (canManage) {\n items.push('<button class=\"action-menu-item danger\" onclick=\"event.stopPropagation();removeMember(\\'' + escapeHtml(u.did) + '\\')\">' + __t(\"members.remove\") + '</button>');\n }\n\n if (items.length === 0) return \"\";\n return '<button class=\"action-trigger\" onclick=\"event.stopPropagation();toggleActionMenu(this)\">\u22EF</button><div class=\"action-menu\">' + items.join(\"\") + '</div>';\n}\n\nfunction toggleActionMenu(trigger) {\n var menu = trigger.nextElementSibling;\n var wasOpen = menu.classList.contains(\"open\");\n // Close all menus first\n document.querySelectorAll(\".action-menu.open\").forEach(function(m) { m.classList.remove(\"open\"); });\n if (!wasOpen) menu.classList.toggle(\"open\");\n}\n\nasync function openMemberDetail(did) {\n var u = currentMembersList.find(function(m) { return m.did === did; });\n if (!u) return;\n selectedMember = u;\n\n // Fetch full member info (with connectedAccounts) from API.\n // Falls back to in-memory list data if the request fails.\n var detail = u;\n try {\n var resp = await api(\"GET\", \"/members/\" + encodeURIComponent(did));\n if (resp && resp.ok && resp.user) {\n // Merge: in-memory list has fields the API may omit (instances, passkeyCount in some paths).\n detail = Object.assign({}, u, resp.user);\n selectedMember = detail;\n }\n } catch (e) {}\n\n var body = document.getElementById(\"member-detail-body\");\n var html = '<div class=\"profile-header\">' + renderAvatar(detail.avatar, detail.fullName) + '<div>';\n html += '<div style=\"font-weight:500;font-size:16px;color:var(--text-white)\">' + escapeHtml(detail.fullName || \"(unnamed)\") + '</div>';\n html += '<did-address did=\"' + escapeHtml(detail.did) + '\" compact></did-address>';\n html += '</div></div>';\n\n html += '<div class=\"settings-card\" style=\"margin-top:16px\">';\n html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailRole\") + '</span><span class=\"settings-value\"><span class=\"badge badge-' + (detail.role || \"guest\") + '\">' + __t(\"common.\" + (detail.role || \"guest\")) + '</span></span></div>';\n html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailStatus\") + '</span><span class=\"settings-value\">' + (detail.approved ? '<span class=\"badge badge-active\">' + __t(\"common.active\") + '</span>' : '<span class=\"badge badge-blocked\">' + __t(\"common.blocked\") + '</span>') + '</span></div>';\n html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailSource\") + '</span><span class=\"settings-value\">' + escapeHtml(providerLabel(detail.sourceProvider)) + '</span></div>';\n if (detail.email) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailEmail\") + '</span><span class=\"settings-value\">' + escapeHtml(detail.email) + '</span></div>';\n if (detail.inviterName) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailInvitedBy\") + '</span><span class=\"settings-value\">' + escapeHtml(detail.inviterName) + '</span></div>';\n if (detail.createdAt) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailJoined\") + '</span><span class=\"settings-value\">' + escapeHtml(absoluteTime(detail.createdAt)) + '</span></div>';\n if (detail.lastLoginAt) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailLastLogin\") + '</span><span class=\"settings-value\">' + escapeHtml(absoluteTime(detail.lastLoginAt)) + '</span></div>';\n if (detail.sourceDomain) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailRegisteredFrom\") + '</span><span class=\"settings-value\">' + escapeHtml(detail.sourceDomain) + '</span></div>';\n if (detail.lastLoginDomain) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailLastLoginFrom\") + '</span><span class=\"settings-value\">' + escapeHtml(detail.lastLoginDomain) + '</span></div>';\n if (detail.passkeyCount !== undefined) html += '<div class=\"settings-row\"><span class=\"settings-label\">' + __t(\"members.detailPasskeys\") + '</span><span class=\"settings-value\">' + detail.passkeyCount + '</span></div>';\n html += '</div>';\n\n // Connected Accounts (read-only)\n if (detail.connectedAccounts && detail.connectedAccounts.length > 0) {\n html += '<div class=\"settings-card\" style=\"margin-top:16px\">';\n html += '<div class=\"settings-card-header\"><span>' + __t(\"members.detailConnectedAccounts\") + ' (' + detail.connectedAccounts.length + ')</span></div>';\n html += '<div style=\"max-height:240px;overflow-y:auto\">';\n detail.connectedAccounts.forEach(function(a) {\n var label = providerLabel(a.provider);\n var info = '';\n if (a.userInfo && a.userInfo.name) info = a.userInfo.name;\n else if (a.userInfo && a.userInfo.email) info = a.userInfo.email;\n else if (a.id) info = a.id;\n html += '<div class=\"settings-row\" style=\"gap:12px;align-items:flex-start\">';\n html += '<div style=\"flex:1;min-width:0\">';\n html += '<div style=\"font-size:14px;font-weight:500;color:var(--text)\">' + escapeHtml(label);\n if (a.isMain) html += ' <span class=\"badge badge-active\" style=\"font-size:10px;padding:1px 6px;\">' + __t(\"profile.mainAccount\") + '</span>';\n html += '</div>';\n if (info) html += '<div style=\"font-size:12px;color:var(--text-secondary);margin-top:2px\">' + escapeHtml(info) + '</div>';\n if (a.did) html += '<div style=\"font-size:11px;color:var(--text-secondary);margin-top:2px\"><did-address did=\"' + escapeHtml(a.did) + '\" compact></did-address></div>';\n html += '</div>';\n if (a.lastLoginAt) html += '<div style=\"font-size:12px;color:var(--text-secondary);flex-shrink:0\">' + escapeHtml(relativeTime(a.lastLoginAt)) + '</div>';\n html += '</div>';\n });\n html += '</div></div>';\n }\n\n // Instance memberships (aggregated mode)\n if (detail.instances && detail.instances.length > 0) {\n html += '<div class=\"settings-card\" style=\"margin-top:16px\">';\n html += '<div class=\"settings-card-header\"><span>' + __t(\"members.instanceMemberships\") + ' (' + detail.instances.length + ')</span></div>';\n html += '<div style=\"max-height:240px;overflow-y:auto\">';\n detail.instances.forEach(function(inst) {\n var instName = __instanceNames[inst.instance_did] || '';\n var instDid = inst.instance_did || '';\n html += '<div class=\"settings-row\" style=\"gap:12px\">';\n html += '<div style=\"flex:1;min-width:0\"><div style=\"font-size:14px;font-weight:500;color:var(--text)\">' + escapeHtml(instName || __t(\"members.currentInstance\")) + '</div>';\n if (instDid) html += '<div style=\"font-size:11px;color:var(--text-secondary);margin-top:2px\"><did-address did=\"' + escapeHtml(instDid) + '\" compact></did-address></div>';\n html += '</div>';\n html += '<div style=\"display:flex;align-items:center;gap:8px;flex-shrink:0\"><span class=\"badge badge-' + inst.role + '\">' + __t(\"common.\" + inst.role) + '</span>';\n if (inst.joined_at) html += '<span style=\"font-size:12px;color:var(--text-secondary)\">' + escapeHtml(relativeTime(inst.joined_at)) + '</span>';\n html += '</div></div>';\n });\n html += '</div></div>';\n }\n\n body.innerHTML = html;\n\n // Change role: mirror list's canOperate + renderMemberActions logic exactly.\n // Use post-merge detail so role/did reflect the latest API response.\n var changeRoleBtn = document.getElementById(\"member-detail-change-role\");\n var instanceSel = document.getElementById(\"members-instance-filter\");\n var instanceVisible = instanceSel && instanceSel.style.display !== 'none';\n var canOperate = !instanceVisible || (instanceVisible && instanceSel.value === '');\n if (__membersAggregated) canOperate = false;\n var isSelf = detail.did === __caller.did;\n var ROLE_LEVEL = { owner: 3, admin: 2, member: 1, guest: 0 };\n var callerLevel = ROLE_LEVEL[__caller.role] || 0;\n var targetLevel = ROLE_LEVEL[detail.role] || 0;\n var canChangeRole = canOperate && !isSelf && targetLevel < callerLevel;\n if (canChangeRole) {\n changeRoleBtn.classList.remove(\"hidden\");\n } else {\n changeRoleBtn.classList.add(\"hidden\");\n }\n\n showDialog(\"member-detail-dialog\");\n}\n\nfunction openChangeRoleForDid(did) {\n var u = currentMembersList.find(function(m) { return m.did === did; });\n if (!u) return;\n selectedMember = u;\n openChangeRoleDialog();\n}\n\nfunction openChangeRoleDialog() {\n if (!selectedMember) return;\n document.getElementById(\"change-role-title\").textContent = __t(\"members.changeRole\");\n document.getElementById(\"change-role-subtitle\").textContent = __t(\"members.changeRoleFor\", { name: selectedMember.fullName || truncateDid(selectedMember.did) });\n // Show/hide role options based on caller's level (only owner can assign admin)\n var radios = document.querySelectorAll('input[name=\"new-role\"]');\n radios.forEach(function(r) {\n r.checked = r.value === selectedMember.role;\n var label = r.closest('label');\n if (label) {\n if (r.value === 'admin' && __caller.role !== 'owner') label.style.display = 'none';\n else label.style.display = '';\n }\n });\n closeDialog(\"member-detail-dialog\");\n showDialog(\"change-role-dialog\");\n}\n\nasync function saveRoleChange() {\n if (!selectedMember) return;\n var selected = document.querySelector('input[name=\"new-role\"]:checked');\n if (!selected) return;\n var newRole = selected.value;\n var data = await api(\"PUT\", \"/members/\" + encodeURIComponent(selectedMember.did) + \"/role\", { role: newRole });\n if (data.ok) {\n showToast(__t(\"members.roleUpdated\"), \"success\");\n closeDialog(\"change-role-dialog\");\n loadMembers(membersPage);\n }\n}\n\n// name is looked up (not passed via onclick) so a member's self-set fullName\n// never has to be embedded as a JS-string literal inside an inline onclick=\n// attribute \u2014 HTML entity-escaping alone can't fully neutralize that context,\n// since the browser decodes entities before parsing onclick as script.\nasync function blockMember(did) {\n var u = currentMembersList.find(function(m) { return m.did === did; });\n var name = (u && u.fullName) || did;\n var ok = await confirmDialog({ title: __t(\"members.blockTitle\"), message: __t(\"members.blockMsg\", { name: name }), confirmLabel: __t(\"members.blockBtn\"), danger: true });\n if (!ok) return;\n var data = await api(\"PUT\", \"/members/\" + encodeURIComponent(did) + \"/approval\", { approved: false });\n if (data.ok) { showToast(__t(\"members.blockSuccess\"), \"success\"); loadMembers(membersPage); }\n}\n\nasync function unblockMember(did) {\n var u = currentMembersList.find(function(m) { return m.did === did; });\n var name = (u && u.fullName) || did;\n var ok = await confirmDialog({ title: __t(\"members.unblockTitle\"), message: __t(\"members.unblockMsg\", { name: name }), confirmLabel: __t(\"members.unblock\") });\n if (!ok) return;\n var data = await api(\"PUT\", \"/members/\" + encodeURIComponent(did) + \"/approval\", { approved: true });\n if (data.ok) { showToast(__t(\"members.unblockSuccess\"), \"success\"); loadMembers(membersPage); }\n}\n\nasync function removeMember(did) {\n var u = currentMembersList.find(function(m) { return m.did === did; });\n var name = (u && u.fullName) || did;\n var ok = await confirmDialog({ title: __t(\"members.removeTitle\"), message: __t(\"members.removeMsg\", { name: name }), confirmLabel: __t(\"members.remove\"), danger: true });\n if (!ok) return;\n var data = await api(\"DELETE\", \"/members/\" + encodeURIComponent(did));\n if (data.ok) { showToast(__t(\"members.removeSuccess\"), \"success\"); loadMembers(membersPage); }\n}\n\nfunction updateTransferSection(users) {\n var section = document.getElementById(\"ownership-transfer-section\");\n if (__caller.role !== \"owner\") {\n section.classList.add(\"hidden\");\n return;\n }\n section.classList.remove(\"hidden\");\n var select = document.getElementById(\"transfer-target\");\n var html = '<option value=\"\">' + __t(\"members.transferSelect\") + '</option>';\n users.forEach(function(u) {\n if (u.did !== __caller.did && u.approved && u.role !== \"owner\") {\n html += '<option value=\"' + escapeHtml(u.did) + '\">' + escapeHtml(u.fullName || truncateDid(u.did)) + ' (' + u.role + ')</option>';\n }\n });\n select.innerHTML = html;\n}\n\nasync function handleTransferOwnership() {\n var target = document.getElementById(\"transfer-target\").value;\n if (!target) { showToast(__t(\"members.selectMemberFirst\"), \"error\"); return; }\n var targetUser = currentMembersList.find(function(u) { return u.did === target; });\n var name = targetUser ? (targetUser.fullName || truncateDid(target)) : truncateDid(target);\n var ok = await confirmDialog({\n title: __t(\"members.transferTitle\"),\n message: __t(\"members.transferMsg\", { name: name }),\n confirmLabel: __t(\"members.transferConfirm\"),\n danger: true,\n });\n if (!ok) return;\n var data = await api(\"POST\", \"/transfer-ownership\", { targetDid: target });\n if (data.ok) {\n showToast(__t(\"members.transferSuccess\"), \"success\");\n setTimeout(function() { location.reload(); }, 1000);\n }\n}\n\n// Button-driven mutations must not double-submit (submitOnce \u2014 utils.ts).\nsaveRoleChange = submitOnce(saveRoleChange);\nblockMember = submitOnce(blockMember);\nunblockMember = submitOnce(unblockMember);\nremoveMember = submitOnce(removeMember);\nhandleTransferOwnership = submitOnce(handleTransferOwnership);\n\n// Close action menus on outside click\ndocument.addEventListener(\"click\", function() {\n document.querySelectorAll(\".action-menu.open\").forEach(function(m) { m.classList.remove(\"open\"); });\n});\n\nregisterTab(\"members\", function() {\n initInstanceFilter().then(function() {\n // Auto-select instance from URL query (e.g., ?instance=z1xxx)\n var urlInstance = new URLSearchParams(window.location.search).get(\"instance\");\n if (urlInstance) {\n var sel = document.getElementById(\"members-instance-filter\");\n if (sel) sel.value = urlInstance;\n }\n // Restore sort state from URL (sortBy / sortOrder) on tab enter.\n readMembersSortFromURL();\n loadMembers(1);\n });\n});\n";
|
|
7
7
|
export {};
|
|
8
8
|
//# sourceMappingURL=tab-members.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tab-members.d.ts","sourceRoot":"","sources":["../../../src/pages/admin/tab-members.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC;AAEpE,wBAAgB,cAAc,CAAC,CAAC,EAAE,GAAG,GAAG,MAAM,CAiG7C;AAED,eAAO,MAAM,kBAAkB,
|
|
1
|
+
{"version":3,"file":"tab-members.d.ts","sourceRoot":"","sources":["../../../src/pages/admin/tab-members.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC;AAEpE,wBAAgB,cAAc,CAAC,CAAC,EAAE,GAAG,GAAG,MAAM,CAiG7C;AAED,eAAO,MAAM,kBAAkB,ki2BA+e9B,CAAC"}
|
|
@@ -569,6 +569,13 @@ async function handleTransferOwnership() {
|
|
|
569
569
|
}
|
|
570
570
|
}
|
|
571
571
|
|
|
572
|
+
// Button-driven mutations must not double-submit (submitOnce — utils.ts).
|
|
573
|
+
saveRoleChange = submitOnce(saveRoleChange);
|
|
574
|
+
blockMember = submitOnce(blockMember);
|
|
575
|
+
unblockMember = submitOnce(unblockMember);
|
|
576
|
+
removeMember = submitOnce(removeMember);
|
|
577
|
+
handleTransferOwnership = submitOnce(handleTransferOwnership);
|
|
578
|
+
|
|
572
579
|
// Close action menus on outside click
|
|
573
580
|
document.addEventListener("click", function() {
|
|
574
581
|
document.querySelectorAll(".action-menu.open").forEach(function(m) { m.classList.remove("open"); });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tab-members.js","sourceRoot":"","sources":["../../../src/pages/admin/tab-members.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,UAAU,cAAc,CAAC,CAAM;IACnC,OAAO,UAAU,CAAC;;8BAEU,CAAC,CAAC,eAAe,CAAC;4BACpB,CAAC,CAAC,cAAc,CAAC;;;;;+EAKkC,CAAC,CAAC,gBAAgB,CAAC;;;4BAGtE,CAAC,CAAC,sBAAsB,CAAC;kCACnB,CAAC,CAAC,yBAAyB,CAAC;;;yBAGrC,CAAC,CAAC,kBAAkB,CAAC;8BAChB,CAAC,CAAC,cAAc,CAAC;8BACjB,CAAC,CAAC,cAAc,CAAC;+BAChB,CAAC,CAAC,eAAe,CAAC;8BACnB,CAAC,CAAC,cAAc,CAAC;;;yBAGtB,CAAC,CAAC,mBAAmB,CAAC;0BACrB,CAAC,CAAC,eAAe,CAAC;0BAClB,CAAC,CAAC,gBAAgB,CAAC;;;yBAGpB,CAAC,CAAC,oBAAoB,CAAC;+BACjB,CAAC,CAAC,0BAA0B,CAAC;gCAC5B,CAAC,CAAC,2BAA2B,CAAC;8BAChC,CAAC,CAAC,yBAAyB,CAAC;+BAC3B,CAAC,CAAC,0BAA0B,CAAC;+BAC7B,CAAC,CAAC,0BAA0B,CAAC;8BAC9B,CAAC,CAAC,yBAAyB,CAAC;gCAC1B,CAAC,CAAC,2BAA2B,CAAC;iCAC7B,CAAC,CAAC,4BAA4B,CAAC;8BAClC,CAAC,CAAC,yBAAyB,CAAC;;;;;;;;;;;;8EAYoB,CAAC,CAAC,kBAAkB,CAAC;;;2EAGxB,CAAC,CAAC,sBAAsB,CAAC;;;sFAGd,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;;UAUpG,CAAC,CAAC,gBAAgB,CAAC;;;;sFAIyD,CAAC,CAAC,cAAc,CAAC;iGACN,CAAC,CAAC,oBAAoB,CAAC;;;;;;;iCAOvF,CAAC,CAAC,oBAAoB,CAAC;;;;qEAIa,CAAC,CAAC,mBAAmB,CAAC;;;sEAGrB,CAAC,CAAC,oBAAoB,CAAC;;;qEAGxB,CAAC,CAAC,mBAAmB,CAAC;;;;;oFAKP,CAAC,CAAC,eAAe,CAAC;qDACjD,CAAC,CAAC,oBAAoB,CAAC;;;CAG3E,CAAC;AACF,CAAC;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAG,QAAQ,CAAC
|
|
1
|
+
{"version":3,"file":"tab-members.js","sourceRoot":"","sources":["../../../src/pages/admin/tab-members.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,UAAU,cAAc,CAAC,CAAM;IACnC,OAAO,UAAU,CAAC;;8BAEU,CAAC,CAAC,eAAe,CAAC;4BACpB,CAAC,CAAC,cAAc,CAAC;;;;;+EAKkC,CAAC,CAAC,gBAAgB,CAAC;;;4BAGtE,CAAC,CAAC,sBAAsB,CAAC;kCACnB,CAAC,CAAC,yBAAyB,CAAC;;;yBAGrC,CAAC,CAAC,kBAAkB,CAAC;8BAChB,CAAC,CAAC,cAAc,CAAC;8BACjB,CAAC,CAAC,cAAc,CAAC;+BAChB,CAAC,CAAC,eAAe,CAAC;8BACnB,CAAC,CAAC,cAAc,CAAC;;;yBAGtB,CAAC,CAAC,mBAAmB,CAAC;0BACrB,CAAC,CAAC,eAAe,CAAC;0BAClB,CAAC,CAAC,gBAAgB,CAAC;;;yBAGpB,CAAC,CAAC,oBAAoB,CAAC;+BACjB,CAAC,CAAC,0BAA0B,CAAC;gCAC5B,CAAC,CAAC,2BAA2B,CAAC;8BAChC,CAAC,CAAC,yBAAyB,CAAC;+BAC3B,CAAC,CAAC,0BAA0B,CAAC;+BAC7B,CAAC,CAAC,0BAA0B,CAAC;8BAC9B,CAAC,CAAC,yBAAyB,CAAC;gCAC1B,CAAC,CAAC,2BAA2B,CAAC;iCAC7B,CAAC,CAAC,4BAA4B,CAAC;8BAClC,CAAC,CAAC,yBAAyB,CAAC;;;;;;;;;;;;8EAYoB,CAAC,CAAC,kBAAkB,CAAC;;;2EAGxB,CAAC,CAAC,sBAAsB,CAAC;;;sFAGd,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;;UAUpG,CAAC,CAAC,gBAAgB,CAAC;;;;sFAIyD,CAAC,CAAC,cAAc,CAAC;iGACN,CAAC,CAAC,oBAAoB,CAAC;;;;;;;iCAOvF,CAAC,CAAC,oBAAoB,CAAC;;;;qEAIa,CAAC,CAAC,mBAAmB,CAAC;;;sEAGrB,CAAC,CAAC,oBAAoB,CAAC;;;qEAGxB,CAAC,CAAC,mBAAmB,CAAC;;;;;oFAKP,CAAC,CAAC,eAAe,CAAC;qDACjD,CAAC,CAAC,oBAAoB,CAAC;;;CAG3E,CAAC;AACF,CAAC;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAG,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+e1C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tab-profile-accounts.d.ts","sourceRoot":"","sources":["../../../src/pages/admin/tab-profile-accounts.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC;AAqBpE,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,GAAG,GAAG,MAAM,CAUxD;AAID,eAAO,MAAM,yBAAyB,
|
|
1
|
+
{"version":3,"file":"tab-profile-accounts.d.ts","sourceRoot":"","sources":["../../../src/pages/admin/tab-profile-accounts.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC;AAqBpE,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,GAAG,GAAG,MAAM,CAUxD;AAID,eAAO,MAAM,yBAAyB,QA8hClC,CAAC"}
|
|
@@ -1079,5 +1079,15 @@ function bufferToBase64url(buf) {
|
|
|
1079
1079
|
for (var i = 0; i < bytes.length; i++) str += String.fromCharCode(bytes[i]);
|
|
1080
1080
|
return btoa(str).replace(/\\+/g, "-").replace(/\\//g, "_").replace(/=+$/, "");
|
|
1081
1081
|
}
|
|
1082
|
+
|
|
1083
|
+
// Button-driven mutations must not double-submit (submitOnce — utils.ts).
|
|
1084
|
+
// OAuth/wallet bind flows are single-shot by construction (once-listeners,
|
|
1085
|
+
// polling stopped before the completing POST) and are not wrapped.
|
|
1086
|
+
unbindOAuth = submitOnce(unbindOAuth);
|
|
1087
|
+
unbindWallet = submitOnce(unbindWallet);
|
|
1088
|
+
addPasskey = submitOnce(addPasskey);
|
|
1089
|
+
removePasskey = submitOnce(removePasskey);
|
|
1090
|
+
openRenamePasskeyDialog = submitOnce(openRenamePasskeyDialog);
|
|
1091
|
+
startAccountMerge = submitOnce(startAccountMerge);
|
|
1082
1092
|
`)();
|
|
1083
1093
|
//# sourceMappingURL=tab-profile-accounts.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tab-profile-accounts.js","sourceRoot":"","sources":["../../../src/pages/admin/tab-profile-accounts.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,eAAe,EACf,UAAU,EACV,YAAY,EACZ,WAAW,GACZ,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAC9F,OAAO,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAIvE,uDAAuD;AACvD,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC,CAAC;AAE/D,4DAA4D;AAC5D,SAAS,gBAAgB;IACvB,MAAM,GAAG,GAA2B;QAClC,OAAO,EAAE,YAAY;QACrB,MAAM,EAAE,eAAe;QACvB,aAAa,EAAE,eAAe;QAC9B,KAAK,EAAE,UAAU;KAClB,CAAC;IACF,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QACrD,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/B,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;QACjB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,CAAM;IAC9C,OAAO,UAAU,CAAC;;;0CAGsB,CAAC,CAAC,2BAA2B,CAAC;;;;;CAKvE,CAAC;AACF,CAAC;AAED,mFAAmF;AACnF,+EAA+E;AAC/E,MAAM,CAAC,MAAM,yBAAyB,GAAG,eAAe,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC;;;;;wBAKjD,gBAAgB,EAAE;0BAChB,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC;EAC7D,uBAAuB,CAAC,QAAQ,EAAE;EAClC,0BAA0B,CAAC,QAAQ,EAAE;EACrC,2BAA2B,CAAC,QAAQ,EAAE
|
|
1
|
+
{"version":3,"file":"tab-profile-accounts.js","sourceRoot":"","sources":["../../../src/pages/admin/tab-profile-accounts.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,eAAe,EACf,UAAU,EACV,YAAY,EACZ,WAAW,GACZ,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAC9F,OAAO,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAIvE,uDAAuD;AACvD,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC,CAAC;AAE/D,4DAA4D;AAC5D,SAAS,gBAAgB;IACvB,MAAM,GAAG,GAA2B;QAClC,OAAO,EAAE,YAAY;QACrB,MAAM,EAAE,eAAe;QACvB,aAAa,EAAE,eAAe;QAC9B,KAAK,EAAE,UAAU;KAClB,CAAC;IACF,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QACrD,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/B,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;QACjB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,CAAM;IAC9C,OAAO,UAAU,CAAC;;;0CAGsB,CAAC,CAAC,2BAA2B,CAAC;;;;;CAKvE,CAAC;AACF,CAAC;AAED,mFAAmF;AACnF,+EAA+E;AAC/E,MAAM,CAAC,MAAM,yBAAyB,GAAG,eAAe,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC;;;;;wBAKjD,gBAAgB,EAAE;0BAChB,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC;EAC7D,uBAAuB,CAAC,QAAQ,EAAE;EAClC,0BAA0B,CAAC,QAAQ,EAAE;EACrC,2BAA2B,CAAC,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqhCvC,CAAC,EAAE,CAAC"}
|