Limit community to single-user workspace
Deploy / deploy (push) Successful in 2m33s
CI / Rust Checks (push) Successful in 5m35s
CI / UI Checks (push) Successful in 5s
CI / Deployment Manifests (push) Successful in 2s
CI / Frontend E2E (push) Successful in 4m19s

This commit is contained in:
github-ops
2026-06-19 17:41:50 +00:00
parent ba29ac7b94
commit 58cfd86d90
23 changed files with 134 additions and 1696 deletions
+3 -168
View File
@@ -36,7 +36,7 @@
<div class="ws-setup-header">
<div class="ws-setup-title" id="page-title" data-i18n="workspace_setup.title">Workspace settings</div>
<div class="ws-setup-subtitle" id="page-subtitle" data-i18n="workspace_setup.page_subtitle">Configure your workspace identity and team members.</div>
<div class="ws-setup-subtitle" id="page-subtitle" data-i18n="workspace_setup.page_subtitle">Configure the identity of your self-hosted workspace.</div>
</div>
<!-- ── Identity ── -->
@@ -79,164 +79,6 @@
</div>
</div>
<!-- ── Members (edit mode only) ── -->
<div id="section-members" class="ws-form-section" hidden style="padding:0; overflow:hidden;">
<div style="display:flex;align-items:center;justify-content:space-between;padding:20px 24px 16px;">
<div>
<div class="ws-form-section-title" style="margin-bottom:2px;" data-i18n="workspace_setup.members.title">Team members</div>
<div style="font-size:12px;color:var(--text-muted);" id="members-count-label">4 members</div>
</div>
<button class="btn-primary" type="button" style="padding:7px 14px;font-size:13px;" onclick="toggleInviteForm()">
<svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor" style="margin-right:4px;"><path d="M7.75 2a.75.75 0 01.75.75V7h4.25a.75.75 0 010 1.5H8.5v4.25a.75.75 0 01-1.5 0V8.5H2.75a.75.75 0 010-1.5H7V2.75A.75.75 0 017.75 2z"/></svg>
<span data-i18n="workspace_setup.members.invite">Invite member</span>
</button>
</div>
<!-- Invite form -->
<div id="invite-form" hidden style="padding: 0 24px 16px; border-bottom: 1px solid var(--border-subtle);">
<div style="display:flex; gap:10px; align-items:flex-end; flex-wrap:wrap;">
<div class="form-group" style="flex:1; min-width:200px; margin-bottom:0;">
<label class="form-label" data-i18n="workspace_setup.members.email">Email address</label>
<input class="form-input" id="invite-email" type="email" placeholder="colleague@company.com" autocomplete="off">
</div>
<div class="form-group" style="width:140px; margin-bottom:0;">
<label class="form-label" data-i18n="workspace_setup.members.role">Role</label>
<select class="form-input" id="invite-role">
<option value="admin" data-i18n="workspace_setup.role.admin">Admin</option>
<option value="developer" selected data-i18n="workspace_setup.role.operator">Developer</option>
<option value="viewer" data-i18n="workspace_setup.role.viewer">Viewer</option>
</select>
</div>
<button class="btn-primary" type="button" style="padding:7px 14px;font-size:13px;" onclick="sendInvite()" data-i18n="workspace_setup.members.send_invite">Send invite</button>
<button class="btn-ghost-sm" type="button" style="padding:7px 12px;" onclick="toggleInviteForm()" data-i18n="workspace_setup.members.cancel">Cancel</button>
</div>
<div id="invite-success" hidden style="margin-top:10px; font-size:13px; color: #3fb950;" data-i18n="workspace_setup.members.invite_sent">✓ Invite sent.</div>
</div>
<!-- Members list -->
<div style="padding: 4px 24px 8px;" id="members-list">
<div class="member-row">
<div class="member-avatar" style="background:linear-gradient(135deg,#0d9488,#6366f1);">AT</div>
<div class="member-info">
<div class="member-name">Crank <span style="font-size:11px;font-weight:400;color:var(--text-muted);" data-i18n="workspace_setup.members.you">(you)</span></div>
<div class="member-email">operator@acme.com</div>
</div>
<div class="member-last-active">Today</div>
<span class="member-role-badge role-owner" data-i18n="workspace_setup.role.owner">Owner</span>
</div>
<div class="member-row">
<div class="member-avatar" style="background:linear-gradient(135deg,#7c3aed,#4f46e5);">BS</div>
<div class="member-info">
<div class="member-name">Blake Smith</div>
<div class="member-email">blake@acme.com</div>
</div>
<div class="member-last-active">2 days ago</div>
<select class="member-role-select" onchange="updateRole(this, 'blake@acme.com')">
<option value="admin" selected data-i18n="workspace_setup.role.admin">Admin</option>
<option value="developer" data-i18n="workspace_setup.role.operator">Developer</option>
<option value="viewer" data-i18n="workspace_setup.role.viewer">Viewer</option>
</select>
<button class="member-remove-btn" onclick="removeMember(this, 'Blake Smith')" data-i18n-title="workspace_setup.members.remove" title="Remove member">
<svg width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M2 4h12M5 4V3a1 1 0 011-1h4a1 1 0 011 1v1M10 8v4M6 8v4"/><path d="M3 4l1 9a1 1 0 001 1h6a1 1 0 001-1l1-9"/></svg>
</button>
</div>
<div class="member-row">
<div class="member-avatar" style="background:linear-gradient(135deg,#d29922,#f59e0b);">CL</div>
<div class="member-info">
<div class="member-name">Carol Lane</div>
<div class="member-email">carol@acme.com</div>
</div>
<div class="member-last-active">1 week ago</div>
<select class="member-role-select" onchange="updateRole(this, 'carol@acme.com')">
<option value="admin" data-i18n="workspace_setup.role.admin">Admin</option>
<option value="developer" selected data-i18n="workspace_setup.role.operator">Developer</option>
<option value="viewer" data-i18n="workspace_setup.role.viewer">Viewer</option>
</select>
<button class="member-remove-btn" onclick="removeMember(this, 'Carol Lane')" data-i18n-title="workspace_setup.members.remove" title="Remove member">
<svg width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M2 4h12M5 4V3a1 1 0 011-1h4a1 1 0 011 1v1M10 8v4M6 8v4"/><path d="M3 4l1 9a1 1 0 001 1h6a1 1 0 001-1l1-9"/></svg>
</button>
</div>
<div class="member-row">
<div class="member-avatar" style="background:linear-gradient(135deg,#0891b2,#06b6d4);">DM</div>
<div class="member-info">
<div class="member-name">Dan Morgan</div>
<div class="member-email">dan@acme.com</div>
</div>
<div class="member-last-active">Never</div>
<select class="member-role-select" onchange="updateRole(this, 'dan@acme.com')">
<option value="admin" data-i18n="workspace_setup.role.admin">Admin</option>
<option value="developer" data-i18n="workspace_setup.role.operator">Developer</option>
<option value="viewer" selected data-i18n="workspace_setup.role.viewer">Viewer</option>
</select>
<button class="member-remove-btn" onclick="removeMember(this, 'Dan Morgan')" data-i18n-title="workspace_setup.members.remove" title="Remove member">
<svg width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M2 4h12M5 4V3a1 1 0 011-1h4a1 1 0 011 1v1M10 8v4M6 8v4"/><path d="M3 4l1 9a1 1 0 001 1h6a1 1 0 001-1l1-9"/></svg>
</button>
</div>
</div>
<!-- Pending invites -->
<div id="pending-invites" hidden style="border-top: 1px solid var(--border-subtle); padding: 12px 24px 16px;">
<div style="font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.06em;color:var(--text-muted);margin-bottom:10px;">
<span data-i18n="workspace_setup.members.pending">Pending invites</span> <span id="pending-count" style="background:rgba(139,148,158,0.15);border-radius:10px;padding:1px 7px;font-size:10px;">1</span>
</div>
<div class="member-row" id="invite-john">
<div class="member-avatar" style="background:var(--bg-canvas);border:1.5px dashed var(--border);color:var(--text-muted);font-size:16px;">?</div>
<div class="member-info">
<div class="member-name">john@startup.com</div>
<div class="member-email">Invited 3 days ago · not yet accepted</div>
</div>
<div class="member-last-active"></div>
<span class="member-role-badge" style="background:rgba(139,148,158,0.12);color:var(--text-muted);border:1px solid var(--border);" data-i18n="workspace_setup.role.operator">Developer</span>
<button class="member-remove-btn" onclick="revokeInvite(this)" data-i18n-title="workspace_setup.members.revoke" title="Revoke invite">
<svg width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><line x1="2" y1="2" x2="14" y2="14"/><line x1="14" y1="2" x2="2" y2="14"/></svg>
</button>
</div>
</div>
<!-- Roles reference -->
<div style="border-top:1px solid var(--border-subtle);padding:16px 24px 20px;">
<div style="font-size:12px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.06em;margin-bottom:14px;" data-i18n="workspace_setup.roles.title">Roles &amp; permissions</div>
<div class="role-def-row">
<span class="member-role-badge role-owner" style="min-width:80px;text-align:center;" data-i18n="workspace_setup.role.owner">Owner</span>
<div class="role-def-text" data-i18n="workspace_setup.roles.owner_body">Full workspace control, billing access, can transfer or delete the workspace.</div>
</div>
<div class="role-def-row">
<span class="member-role-badge role-admin" style="min-width:80px;text-align:center;" data-i18n="workspace_setup.role.admin">Admin</span>
<div class="role-def-text" data-i18n="workspace_setup.roles.admin_body">Manage members and roles, create and edit all operations and agents, manage API keys.</div>
</div>
<div class="role-def-row">
<span class="member-role-badge role-developer" style="min-width:80px;text-align:center;" data-i18n="workspace_setup.role.operator">Developer</span>
<div class="role-def-text" data-i18n="workspace_setup.roles.developer_body">Create and edit operations and agents. Cannot manage members or workspace settings.</div>
</div>
<div class="role-def-row">
<span class="member-role-badge role-viewer" style="min-width:80px;text-align:center;" data-i18n="workspace_setup.role.viewer">Viewer</span>
<div class="role-def-text" data-i18n="workspace_setup.roles.viewer_body">Read-only access to operations, agents, and logs.</div>
</div>
</div>
</div>
<!-- ── Invite team members (create mode only) ── -->
<div id="section-invite" class="ws-form-section" hidden>
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:4px;">
<div class="ws-form-section-title" style="margin-bottom:0;"><span data-i18n="workspace_setup.invite.title">Invite team members</span> <span style="font-size:12px;font-weight:400;color:var(--text-muted);" data-i18n="workspace_setup.optional">(optional)</span></div>
<button class="btn-ghost-sm" type="button" onclick="addInviteRow()" style="font-size:12px;padding:4px 10px;" data-i18n="workspace_setup.invite.add_person">+ Add person</button>
</div>
<div class="form-hint" style="margin-bottom:14px;" data-i18n="workspace_setup.invite.later">You can invite more people later from Workspace settings.</div>
<div class="invite-rows" id="invite-rows">
<div class="invite-row">
<input class="form-input" type="email" placeholder="colleague@company.com" autocomplete="off" style="flex:1;margin-bottom:0;">
<select class="form-input" style="width:130px;margin-bottom:0;">
<option value="admin" data-i18n="workspace_setup.role.admin">Admin</option>
<option value="developer" selected data-i18n="workspace_setup.role.operator">Developer</option>
<option value="viewer" data-i18n="workspace_setup.role.viewer">Viewer</option>
</select>
<button class="invite-row-remove" onclick="removeInviteRow(this)" data-i18n-title="workspace_setup.members.remove" title="Remove">
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><line x1="1" y1="1" x2="11" y2="11"/><line x1="11" y1="1" x2="1" y2="11"/></svg>
</button>
</div>
</div>
</div>
<!-- ── Actions ── -->
<div class="ws-setup-actions">
<a href="javascript:history.back()" class="btn-ghost-sm" style="padding:9px 18px;font-size:13px;text-decoration:none;color:var(--text-secondary);" data-i18n="btn.cancel">Cancel</a>
@@ -244,7 +86,7 @@
</div>
<div class="ws-setup-footer-note" id="footer-note" hidden>
<span data-i18n="workspace_setup.create.footer">You'll be the Owner of this workspace. You can invite additional members after creation.</span>
<span data-i18n="workspace_setup.create.footer">This Community installation uses one workspace.</span>
</div>
<!-- ── Danger zone (edit mode only) ── -->
@@ -256,17 +98,10 @@
<div class="danger-zone-action">
<div class="danger-zone-text">
<div class="danger-zone-title" data-i18n="workspace_setup.danger.export_title">Export all data</div>
<div class="danger-zone-desc" data-i18n="workspace_setup.danger.export_body">Download a JSON snapshot of workspace settings, memberships, invitations, operations, agents and platform API keys.</div>
<div class="danger-zone-desc" data-i18n="workspace_setup.danger.export_body">Download a JSON snapshot of workspace settings, operations, agents, secrets, usage data and agent access keys.</div>
</div>
<button class="btn-danger" id="export-workspace-btn" type="button" data-i18n="workspace_setup.export">Export</button>
</div>
<div class="danger-zone-action">
<div class="danger-zone-text">
<div class="danger-zone-title" data-i18n="workspace_setup.danger.delete_title">Delete workspace</div>
<div class="danger-zone-desc" data-i18n="workspace_setup.danger.delete_body">Permanently deletes all operations, keys, logs and settings. This action cannot be undone. You will be logged out immediately.</div>
</div>
<button class="btn-danger" id="delete-workspace-btn" type="button" data-i18n="workspace_setup.delete">Delete workspace</button>
</div>
</div>
</div>