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 82f2f96e6..1b0082186 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/team/page.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/team/page.tsx @@ -49,12 +49,11 @@ 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"; +import type { DeleteInviteRequest } from '@/contracts/types/invites.types'; +import type { UpdateMembershipRequest, DeleteMembershipRequest} from "@/contracts/types/members.types"; import { permissionsAtom } from "@/atoms/permissions/permissions-query.atoms"; import { membersAtom } from "@/atoms/members/members-query.atoms"; import { invitesApiService } from '@/lib/apis/invites-api.service'; -import type { Invite } from '@/contracts/types/invites.types'; import { AlertDialog, AlertDialogAction, @@ -117,9 +116,10 @@ import type { UpdateRoleRequest, } from "@/contracts/types/roles.types"; import { - type InviteCreate, - type Member, -} from "@/hooks/use-rbac"; + type Invite, + type CreateInviteRequest, +} from "@/contracts/types/invites.types"; +import type { Membership } from "@/contracts/types/members.types"; import { rolesApiService } from "@/lib/apis/roles-api.service"; import { cacheKeys } from "@/lib/query-client/cache-keys"; import { cn } from "@/lib/utils"; @@ -165,8 +165,7 @@ export default function TeamManagementPage() { [access] ); - const { data: membersData = [], isLoading: membersLoading, refetch: fetchMembers } = useAtomValue(membersAtom); - const members = membersData as Member[]; + const { data: members = [], isLoading: membersLoading, refetch: fetchMembers } = useAtomValue(membersAtom); const { mutateAsync: createRole } = useAtomValue(createRoleMutationAtom); const { mutateAsync: updateRole } = useAtomValue(updateRoleMutationAtom); @@ -190,7 +189,7 @@ export default function TeamManagementPage() { ); const handleCreateInvite = useCallback( - async (inviteData: InviteCreate) => { + async (inviteData: CreateInviteRequest['data']) => { const request: CreateInviteRequest = { search_space_id: searchSpaceId, data: inviteData, @@ -236,7 +235,7 @@ export default function TeamManagementPage() { ); const handleUpdateMember = useCallback( - async (membershipId: number, roleId: number | null): Promise => { + async (membershipId: number, roleId: number | null): Promise => { const request: UpdateMembershipRequest = { search_space_id: searchSpaceId, membership_id: membershipId, @@ -244,7 +243,7 @@ export default function TeamManagementPage() { role_id: roleId, }, }; - return await updateMember(request) as Member; + return await updateMember(request) as Membership; }, [updateMember, searchSpaceId] ); @@ -511,10 +510,10 @@ function MembersTab({ canManageRoles, canRemove, }: { - members: Member[]; + members: Membership[]; roles: Role[]; loading: boolean; - onUpdateRole: (membershipId: number, roleId: number | null) => Promise; + onUpdateRole: (membershipId: number, roleId: number | null) => Promise; onRemoveMember: (membershipId: number) => Promise; canManageRoles: boolean; canRemove: boolean; @@ -1078,7 +1077,7 @@ function CreateInviteDialog({ searchSpaceId, }: { roles: Role[]; - onCreateInvite: (data: InviteCreate) => Promise; + onCreateInvite: (data: CreateInviteRequest['data']) => Promise; searchSpaceId: number; }) { const [open, setOpen] = useState(false); @@ -1093,7 +1092,7 @@ function CreateInviteDialog({ const handleCreate = async () => { setCreating(true); try { - const data: InviteCreate = {}; + const data: CreateInviteRequest['data'] = {}; if (name) data.name = name; if (roleId && roleId !== "default") data.role_id = Number(roleId); if (maxUses) data.max_uses = Number(maxUses); diff --git a/surfsense_web/hooks/index.ts b/surfsense_web/hooks/index.ts index db454c161..60afebc45 100644 --- a/surfsense_web/hooks/index.ts +++ b/surfsense_web/hooks/index.ts @@ -1,4 +1,3 @@ export * from "./use-debounced-value"; export * from "./use-logs"; -export * from "./use-rbac"; export * from "./use-search-source-connectors"; diff --git a/surfsense_web/hooks/use-rbac.ts b/surfsense_web/hooks/use-rbac.ts deleted file mode 100644 index 2d8e4a22d..000000000 --- a/surfsense_web/hooks/use-rbac.ts +++ /dev/null @@ -1,177 +0,0 @@ -"use client"; - -import { useCallback, useEffect, useMemo, useState } from "react"; -import { toast } from "sonner"; -import { authenticatedFetch, getBearerToken, handleUnauthorized } from "@/lib/auth-utils"; - -// ============ Types ============ - -export interface Role { - id: number; - name: string; - description: string | null; - permissions: string[]; - is_default: boolean; - is_system_role: boolean; - search_space_id: number; - created_at: string; -} - -export interface Member { - id: number; - user_id: string; - search_space_id: number; - role_id: number | null; - is_owner: boolean; - joined_at: string; - created_at: string; - role: Role | null; - user_email: string | null; -} - -export interface Invite { - id: number; - invite_code: string; - search_space_id: number; - role_id: number | null; - created_by_id: string | null; - expires_at: string | null; - max_uses: number | null; - uses_count: number; - is_active: boolean; - name: string | null; - created_at: string; - role: Role | null; -} - -export interface InviteCreate { - name?: string; - role_id?: number; - expires_at?: string; - max_uses?: number; -} - -export interface InviteUpdate { - name?: string; - role_id?: number; - expires_at?: string; - max_uses?: number; - is_active?: boolean; -} - -export interface RoleCreate { - name: string; - description?: string; - permissions: string[]; - is_default?: boolean; -} - -export interface RoleUpdate { - name?: string; - description?: string; - permissions?: string[]; - is_default?: boolean; -} - -export interface PermissionInfo { - value: string; - name: string; - category: string; -} - -export interface UserAccess { - search_space_id: number; - search_space_name: string; - is_owner: boolean; - role_name: string | null; - permissions: string[]; -} - -export interface InviteInfo { - search_space_name: string; - role_name: string | null; - is_valid: boolean; - message: string | null; -} - -// ============ Members Hook ============ - -export function useInviteInfo(inviteCode: string | null) { - const [inviteInfo, setInviteInfo] = useState(null); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - const fetchInviteInfo = useCallback(async () => { - if (!inviteCode) { - setLoading(false); - return; - } - - try { - setLoading(true); - const response = await fetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/invites/${inviteCode}/info`, - { - method: "GET", - } - ); - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.detail || "Failed to fetch invite info"); - } - - const data = await response.json(); - setInviteInfo(data); - setError(null); - return data; - } catch (err: any) { - setError(err.message || "Failed to fetch invite info"); - console.error("Error fetching invite info:", err); - } finally { - setLoading(false); - } - }, [inviteCode]); - - useEffect(() => { - fetchInviteInfo(); - }, [fetchInviteInfo]); - - const acceptInvite = useCallback(async () => { - if (!inviteCode) { - toast.error("No invite code provided"); - return null; - } - - try { - const response = await authenticatedFetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/invites/accept`, - { - headers: { "Content-Type": "application/json" }, - method: "POST", - body: JSON.stringify({ invite_code: inviteCode }), - } - ); - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.detail || "Failed to accept invite"); - } - - const data = await response.json(); - toast.success(data.message || "Successfully joined the search space"); - return data; - } catch (err: any) { - toast.error(err.message || "Failed to accept invite"); - throw err; - } - }, [inviteCode]); - - return { - inviteInfo, - loading, - error, - fetchInviteInfo, - acceptInvite, - }; -}