From bce8340750036d474634b67ad7772fa8373307d2 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Wed, 17 Dec 2025 20:26:37 +0000 Subject: [PATCH] feat: migrate useUserAccess to myAccessAtom in team page --- .../dashboard/[search_space_id]/team/page.tsx | 13 +- surfsense_web/hooks/use-rbac.ts | 131 ------------------ 2 files changed, 11 insertions(+), 133 deletions(-) diff --git a/surfsense_web/app/dashboard/[search_space_id]/team/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/team/page.tsx index 9a7e53e7f..82f2f96e6 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/team/page.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/team/page.tsx @@ -47,6 +47,7 @@ import { useParams, useRouter } from "next/navigation"; import { useCallback, useMemo, useState } from "react"; import { toast } from "sonner"; import { updateMemberMutationAtom, deleteMemberMutationAtom } from "@/atoms/members/members-mutation.atoms"; +import { myAccessAtom } from "@/atoms/members/members-query.atoms"; import { createInviteMutationAtom, deleteInviteMutationAtom } from '@/atoms/invites/invites-mutation.atoms'; import type { CreateInviteRequest, DeleteInviteRequest } from '@/contracts/types/invites.types'; import type { UpdateMembershipRequest, DeleteMembershipRequest, Membership } from "@/contracts/types/members.types"; @@ -118,7 +119,6 @@ import type { import { type InviteCreate, type Member, - useUserAccess, } from "@/hooks/use-rbac"; import { rolesApiService } from "@/lib/apis/roles-api.service"; import { cacheKeys } from "@/lib/query-client/cache-keys"; @@ -154,7 +154,16 @@ export default function TeamManagementPage() { const searchSpaceId = Number(params.search_space_id); const [activeTab, setActiveTab] = useState("members"); - const { access, loading: accessLoading, hasPermission } = useUserAccess(searchSpaceId); + const { data: access = null, isLoading: accessLoading } = useAtomValue(myAccessAtom); + + const hasPermission = useCallback( + (permission: string) => { + if (!access) return false; + if (access.is_owner) return true; + return access.permissions?.includes(permission) ?? false; + }, + [access] + ); const { data: membersData = [], isLoading: membersLoading, refetch: fetchMembers } = useAtomValue(membersAtom); const members = membersData as Member[]; diff --git a/surfsense_web/hooks/use-rbac.ts b/surfsense_web/hooks/use-rbac.ts index fa619407a..e7f85b87e 100644 --- a/surfsense_web/hooks/use-rbac.ts +++ b/surfsense_web/hooks/use-rbac.ts @@ -218,137 +218,6 @@ export function useMembers(searchSpaceId: number) { // ============ Roles Hook ============ -export function useInvites(searchSpaceId: number) { - const [invites, setInvites] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - const fetchInvites = useCallback(async () => { - if (!searchSpaceId) return; - - try { - setLoading(true); - const response = await authenticatedFetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces/${searchSpaceId}/invites`, - { method: "GET" } - ); - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.detail || "Failed to fetch invites"); - } - - const data = await response.json(); - setInvites(data); - setError(null); - return data; - } catch (err: any) { - setError(err.message || "Failed to fetch invites"); - console.error("Error fetching invites:", err); - } finally { - setLoading(false); - } - }, [searchSpaceId]); - - useEffect(() => { - fetchInvites(); - }, [fetchInvites]); - - const createInvite = useCallback( - async (inviteData: InviteCreate) => { - try { - const response = await authenticatedFetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces/${searchSpaceId}/invites`, - { - headers: { "Content-Type": "application/json" }, - method: "POST", - body: JSON.stringify(inviteData), - } - ); - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.detail || "Failed to create invite"); - } - - const newInvite = await response.json(); - setInvites((prev) => [...prev, newInvite]); - toast.success("Invite created successfully"); - return newInvite; - } catch (err: any) { - toast.error(err.message || "Failed to create invite"); - throw err; - } - }, - [searchSpaceId] - ); - - const updateInvite = useCallback( - async (inviteId: number, inviteData: InviteUpdate) => { - try { - const response = await authenticatedFetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces/${searchSpaceId}/invites/${inviteId}`, - { - headers: { "Content-Type": "application/json" }, - method: "PUT", - body: JSON.stringify(inviteData), - } - ); - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.detail || "Failed to update invite"); - } - - const updatedInvite = await response.json(); - setInvites((prev) => prev.map((i) => (i.id === inviteId ? updatedInvite : i))); - toast.success("Invite updated successfully"); - return updatedInvite; - } catch (err: any) { - toast.error(err.message || "Failed to update invite"); - throw err; - } - }, - [searchSpaceId] - ); - - const revokeInvite = useCallback( - async (inviteId: number) => { - try { - const response = await authenticatedFetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces/${searchSpaceId}/invites/${inviteId}`, - { method: "DELETE" } - ); - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.detail || "Failed to revoke invite"); - } - - setInvites((prev) => prev.filter((i) => i.id !== inviteId)); - toast.success("Invite revoked successfully"); - return true; - } catch (err: any) { - toast.error(err.message || "Failed to revoke invite"); - return false; - } - }, - [searchSpaceId] - ); - - return { - invites, - loading, - error, - fetchInvites, - createInvite, - updateInvite, - revokeInvite, - }; -} - -// ============ Permissions Hook ============ - export function useUserAccess(searchSpaceId: number) { const [access, setAccess] = useState(null); const [loading, setLoading] = useState(true);