import server from "/@server/server.js" import env from "/_/code/env.js" import "/_/code/components/LoadingCircle.js" import "./DesktopSettingsSidebar.js" import "./DesktopRolesSection.js" import "./DesktopIntegrationsSection.js" css(` settings- { font-family: 'Arial'; scrollbar-width: none; -ms-overflow-style: none; } settings- input::placeholder { color: var(--headertext); opacity: 0.35; } `) class Settings extends Shadow { activeSection = "roles" loaded = false roles = [] allApps = [] selectedRoleId = null roleApps = {} confirmDeleteId = null stripeDetails = null render() { HStack(() => { DesktopSettingsSidebar(this.activeSection, (section) => { this.activeSection = section this.rerender() }) VStack(() => { if (!this.loaded) { VStack(() => LoadingCircle()) .flex(1) .justifyContent("center") .alignItems("center") } else if (this.activeSection === "roles") DesktopRolesSection( this.roles, this.allApps, this.selectedRoleId, this.roleApps, this.confirmDeleteId, (id) => { this.selectedRoleId = id; this.confirmDeleteId = null; this.rerender() }, (id) => this.deleteRole(id), (id) => { this.confirmDeleteId = id; this.rerender() }, (name) => this.createRole(name), (roleId, appId, add) => this.toggleRoleApp(roleId, appId, add) ) else if (this.activeSection === "integrations") DesktopIntegrationsSection(this.stripeDetails, () => this.handleConnectStripe()) }) .flex(1).height(100, pct).overflowY("auto") }) .height(100, pct).width(100, pct).overflow("hidden") .onAppear(async () => { if (this.loaded) return await this.loadRoles() this.stripeDetails = await server.getStripeProfile(global.currentNetwork.id) this.rerender() }) } // ── Server actions ──────────────────────────────────────────────── async loadRoles() { const [roles, apps] = await Promise.all([ server.getRoles(global.currentNetwork.id), server.getAllApps() ]) this.roles = Array.isArray(roles) ? roles : [] this.allApps = Array.isArray(apps) ? apps : [] this.selectedRoleId = this.roles[0]?.id ?? null await Promise.all(this.roles.map(async role => { const roleApps = await server.getRoleApps(role.id) this.roleApps[role.id] = new Set(Array.isArray(roleApps) ? roleApps.map(a => a.id) : []) })) this.loaded = true this.rerender() } async createRole(name) { const result = await server.createRole(name, global.currentNetwork.id) if (!result?.error && result?.role) { this.roles.push(result.role) this.roleApps[result.role.id] = new Set() this.selectedRoleId = result.role.id this.rerender() } } async deleteRole(roleId) { await server.deleteRole(roleId, global.currentNetwork.id) this.roles = this.roles.filter(r => r.id !== roleId) delete this.roleApps[roleId] this.selectedRoleId = this.roles[0]?.id ?? null this.confirmDeleteId = null this.rerender() } async toggleRoleApp(roleId, appId, add) { if (add) { await server.addRoleApp(roleId, appId) if (!this.roleApps[roleId]) this.roleApps[roleId] = new Set() this.roleApps[roleId].add(appId) } else { await server.removeRoleApp(roleId, appId) this.roleApps[roleId]?.delete(appId) } if (roleId == global.currentNetwork.role?.id) { const appName = this.allApps.find(a => a.id === appId)?.name if (appName) { if (add) { global.currentNetwork.apps.push(appName) } else { global.currentNetwork.apps = global.currentNetwork.apps.filter(a => a !== appName) } document.querySelector("app-menu")?.rerender() } } this.rerender() } handleConnectStripe() { const state = btoa(JSON.stringify({ returnTo: window.location.href, networkId: global.currentNetwork.id })) const params = new URLSearchParams({ response_type: "code", client_id: env.client_id, scope: "read_write", redirect_uri: `${env.baseURL}/stripe/onboardingcomplete`, state, }) window.location.href = `https://connect.stripe.com/oauth/authorize?${params}` } } register(Settings)