From 087b8f002ff849a8a2d44c2fc0776731830f8145 Mon Sep 17 00:00:00 2001 From: cory Date: Sat, 30 May 2026 06:34:20 +0000 Subject: [PATCH] feat: replace Activate All with Deactivate All button and remove Assign Access modal --- src/lib/api.ts | 18 ++---- src/pages/org/AccessPage.tsx | 86 ------------------------- src/pages/org/NetworkManagementPage.tsx | 55 +++++++--------- 3 files changed, 32 insertions(+), 127 deletions(-) diff --git a/src/lib/api.ts b/src/lib/api.ts index f65c158..ebb92cd 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -1584,17 +1584,6 @@ export const api = { { method: "POST" }, true, requestConfig, ), - assignAccess: (orgId: string, data: { - target_user_id: string; - portal_network_id: string; - justification?: string; - }, requestConfig?: RequestConfig) => - request<{ approval: UserNetworkApproval }>( - `/organizations/${orgId}/approvals/assign`, - { method: "POST", body: JSON.stringify(data) }, - true, requestConfig, - ), - // ── Memberships ──────────────────────────────────────────────────────────── listMemberships: (orgId: string, requestConfig?: RequestConfig) => request<{ memberships: DeviceNetworkMembership[]; count: number }>( @@ -1694,6 +1683,13 @@ export const api = { true, requestConfig, ), + networkKillSwitch: (orgId: string, networkId: string, data?: { reason?: string }, requestConfig?: RequestConfig) => + request<{ message: string; count: number }>( + `/organizations/${orgId}/networks/${networkId}/kill-switch`, + { method: "POST", body: data ? JSON.stringify(data) : "{}" }, + true, requestConfig, + ), + // ── ZeroTier Controller (org-scoped admin) ───────────────────────────────── getZtStatus: (orgId: string, requestConfig?: RequestConfig) => request<{ status: Record }>( diff --git a/src/pages/org/AccessPage.tsx b/src/pages/org/AccessPage.tsx index f9b8478..a36ec2c 100644 --- a/src/pages/org/AccessPage.tsx +++ b/src/pages/org/AccessPage.tsx @@ -11,7 +11,6 @@ import { Loader2, Search, MoreHorizontal, - UserPlus, Trash2, RefreshCw, Skull, @@ -131,13 +130,6 @@ export default function AccessPage() { const [isApproving, setIsApproving] = useState(false); const [rejectConfirmId, setRejectConfirmId] = useState(null); - const [showAssign, setShowAssign] = useState(false); - const [assignUserId, setAssignUserId] = useState(""); - const [assignNetworkId, setAssignNetworkId] = useState(""); - const [assignJustification, setAssignJustification] = useState(""); - const [isAssigning, setIsAssigning] = useState(false); - const [assignError, setAssignError] = useState(null); - const [showKillSwitch, setShowKillSwitch] = useState(false); const [killTargetUserId, setKillTargetUserId] = useState(""); const [killScope, setKillScope] = useState<"organization" | "global">("organization"); @@ -252,29 +244,6 @@ export default function AccessPage() { } }; - const handleAssign = async () => { - if (!orgId) return; - setAssignError(null); - if (!assignUserId) { setAssignError("Please select a user."); return; } - if (!assignNetworkId) { setAssignError("Please select a network."); return; } - setIsAssigning(true); - try { - await api.zerotier.assignAccess(orgId, { - target_user_id: assignUserId, - portal_network_id: assignNetworkId, - justification: assignJustification.trim() || undefined, - }); - toast({ title: "Access assigned", description: "The user can now register devices for this network." }); - setShowAssign(false); - setAssignUserId(""); setAssignNetworkId(""); setAssignJustification(""); - fetchData(); - } catch (err) { - setAssignError(err instanceof ApiError ? err.message : "Failed to assign access."); - } finally { - setIsAssigning(false); - } - }; - const handleKillSwitch = async () => { if (!orgId) return; setKillError(null); @@ -436,9 +405,6 @@ export default function AccessPage() { {networks.map((n) => {n.name})} - @@ -906,58 +872,6 @@ export default function AccessPage() { - {/* Assign Access Dialog */} - { if (!open) setShowAssign(false); }}> - - - Assign Network Access - Grant a user direct access to a network without a request. - -
-
- - -
-
- - -
-
- - setAssignJustification(e.target.value)} - /> -
- {assignError &&

{assignError}

} -
- - - - -
-
- {/* Kill Switch Dialog */} { if (!open) setShowKillSwitch(false); }}> diff --git a/src/pages/org/NetworkManagementPage.tsx b/src/pages/org/NetworkManagementPage.tsx index eca3d3e..6d8509a 100644 --- a/src/pages/org/NetworkManagementPage.tsx +++ b/src/pages/org/NetworkManagementPage.tsx @@ -131,7 +131,8 @@ export default function NetworkManagementPage() { const [confirmRemoveUser, setConfirmRemoveUser] = useState(null); const [showActivateDialog, setShowActivateDialog] = useState(null); const [activateLifetime, setActivateLifetime] = useState("480"); - const [activatingAll, setActivatingAll] = useState(false); + const [deactivatingAll, setDeactivatingAll] = useState(false); + const [deactivateReason, setDeactivateReason] = useState(""); // Add New Membership dialog state const [isAddDialogOpen, setIsAddDialogOpen] = useState(false); @@ -296,19 +297,19 @@ export default function NetworkManagementPage() { } }; - const handleActivateAll = async () => { + const handleDeactivateAll = async () => { if (!orgId) return; - setActivatingAll(true); + setDeactivatingAll(true); try { - const lifetime = parseInt(activateLifetime); - const res = await api.zerotier.activateAllMemberships(orgId, lifetime); - toast({ title: "All memberships activated", description: `${res.count} memberships activated for ${lifetime} minutes.` }); + const res = await api.zerotier.networkKillSwitch(orgId, networkId!, { reason: deactivateReason.trim() || undefined }); + toast({ title: "All memberships deactivated", description: `${res.count} memberships deactivated.` }); + setDeactivateReason(""); const result = await api.zerotier.getNetworkMembers(orgId, networkId!); setMembers(result.memberships || []); } catch (err) { - toast({ variant: "destructive", title: "Failed to activate all", description: err instanceof ApiError ? err.message : "Something went wrong." }); + toast({ variant: "destructive", title: "Failed to deactivate all", description: err instanceof ApiError ? err.message : "Something went wrong." }); } finally { - setActivatingAll(false); + setDeactivatingAll(false); } }; @@ -653,33 +654,27 @@ export default function NetworkManagementPage() { - {/* Activate All section */} - {members.filter(m => m.status === "approved" && !m.active).length > 0 && ( - + {/* Deactivate All section */} + {members.filter(m => m.active).length > 0 && ( + -
+
- + - {members.filter(m => m.active).length} active,{" "} - {members.filter(m => m.status === "approved" && !m.active).length} ready to activate + {members.filter(m => m.active).length} active {members.filter(m => m.active).length === 1 ? "membership" : "memberships"}
-
-
- Duration: - setActivateLifetime(e.target.value)} - className="h-8 w-20 text-xs" - placeholder="480" - /> - min -
-