diff --git a/cmd/archipulse/ui/src/routes/WorkspaceSettings.svelte b/cmd/archipulse/ui/src/routes/WorkspaceSettings.svelte index 38ee2df..778d6cf 100644 --- a/cmd/archipulse/ui/src/routes/WorkspaceSettings.svelte +++ b/cmd/archipulse/ui/src/routes/WorkspaceSettings.svelte @@ -4,40 +4,36 @@ import { api } from '../lib/api.js'; import { user } from '../lib/auth.js'; - export let params = {}; + const { params = {} } = $props(); - $: wsId = params.wsId; + const wsId = $derived(params.wsId); // ── Tabs ────────────────────────────────────────────────────────────────── - let tab = 'members'; // 'general' | 'members' + let tab = $state('members'); // ── Workspace general ───────────────────────────────────────────────────── - let ws = null; - let wsLoading = true; - let wsSaving = false; - let wsError = null; - let wsSuccess = false; - let wsForm = { name: '', purpose: '', description: '' }; + let ws = $state(null); + let wsLoading = $state(true); + let wsSaving = $state(false); + let wsError = $state(null); + let wsSuccess = $state(false); + let wsForm = $state({ name: '', purpose: '', description: '' }); const PURPOSES = ['as-is', 'to-be', 'initiative', 'other']; // ── Members ─────────────────────────────────────────────────────────────── - let members = []; - let membersLoading = true; - let membersError = null; + let members = $state([]); + let membersLoading = $state(true); + let membersError = $state(null); - // Add member form - let addEmail = ''; - let addRole = 'viewer'; - let addSearching = false; - let addError = null; - let addSuccess = false; + let addEmail = $state(''); + let addRole = $state('viewer'); + let addSearching = $state(false); + let addError = $state(null); + let addSuccess = $state(false); - // Inline role updating - let updatingRole = {}; // userId → true while in flight - - // Removing - let removingMember = {}; // userId → true while in flight + let updatingRole = $state({}); + let removingMember = $state({}); // ── Load ────────────────────────────────────────────────────────────────── onMount(async () => { @@ -109,9 +105,7 @@ try { await api.put('/workspaces/' + wsId + '/members/' + member.user_id, { role: newRole }); member.role = newRole; - members = [...members]; } catch (e) { - // silently revert — reload to sync await loadMembers(); } updatingRole[member.user_id] = false; @@ -138,17 +132,16 @@ viewer: 'bg-gray-100 text-gray-600 border-gray-200', }; - $: currentUserId = $user?.id; - $: myRole = members.find(m => m.user_id === currentUserId)?.role; - $: canManage = $user?.org_role === 'admin' || myRole === 'owner'; - $: isOwner = myRole === 'owner'; + const currentUserId = $derived($user?.id); + const canManage = $derived($user?.org_role === 'admin' || ws?.caller_role === 'owner'); + const isOwner = $derived(ws?.caller_role === 'owner'); // ── Delete workspace ────────────────────────────────────────────────────── - let deleteConfirm = ''; - let deleting = false; - let deleteError = null; + let deleteConfirm = $state(''); + let deleting = $state(false); + let deleteError = $state(null); - $: deleteReady = ws && deleteConfirm.trim() === ws.name.trim(); + const deleteReady = $derived(ws && deleteConfirm.trim() === ws.name.trim()); async function deleteWorkspace() { if (!deleteReady || deleting) return; @@ -176,7 +169,7 @@ {/each} @@ -222,7 +215,7 @@ {addSearching ? '…' : 'Add'} @@ -353,7 +346,7 @@ @@ -418,7 +411,7 @@ ? 'bg-destructive text-destructive-foreground hover:bg-destructive/90' : 'bg-muted text-muted-foreground cursor-not-allowed opacity-50'}" disabled={!deleteReady || deleting} - on:click={deleteWorkspace} + onclick={deleteWorkspace} > {deleting ? 'Deleting…' : 'Delete workspace'} diff --git a/internal/api/workspace_handler.go b/internal/api/workspace_handler.go index 31d1ad2..d7a0e44 100644 --- a/internal/api/workspace_handler.go +++ b/internal/api/workspace_handler.go @@ -35,6 +35,11 @@ func (h *workspaceHandler) list(w http.ResponseWriter, r *http.Request) { respondJSON(w, http.StatusOK, wss) } +type workspaceResponse struct { + *workspace.Workspace + CallerRole string `json:"caller_role,omitempty"` +} + func (h *workspaceHandler) get(w http.ResponseWriter, r *http.Request) { id, err := parseUUID(r, "id") if err != nil { @@ -50,7 +55,13 @@ func (h *workspaceHandler) get(w http.ResponseWriter, r *http.Request) { respondError(w, http.StatusInternalServerError, err) return } - respondJSON(w, http.StatusOK, ws) + resp := workspaceResponse{Workspace: ws} + if claims := auth.ClaimsFromCtx(r.Context()); claims != nil { + if role, err := h.svc.Enforcer.WorkspaceRole(claims.UserID, id.String()); err == nil { + resp.CallerRole = role + } + } + respondJSON(w, http.StatusOK, resp) } func (h *workspaceHandler) create(w http.ResponseWriter, r *http.Request) {