feat: platform admin member management — invite, remove, role update
Add member management to the platform admin organisation detail page: - Backend: invite (creates invitation or directly adds existing user), remove member, update member role endpoints on AdminOrganisationController - Backend: show endpoint now returns members alongside organisation data - Frontend: members table with inline role editing, invite dialog, remove confirmation dialog on /platform/organisations/[id] - Tests: 7 new tests covering happy paths and edge cases (self-removal, existing member, non-super_admin denied) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,11 +4,15 @@ import { apiClient } from '@/lib/axios'
|
||||
import type {
|
||||
ActivityLogEntry,
|
||||
AdminOrganisation,
|
||||
AdminOrganisationDetail,
|
||||
AdminOrganisationMember,
|
||||
AdminUser,
|
||||
ImpersonationResponse,
|
||||
InviteMemberPayload,
|
||||
PlatformStats,
|
||||
UpdateAdminOrganisationPayload,
|
||||
UpdateAdminUserPayload,
|
||||
UpdateMemberRolePayload,
|
||||
} from '@/types/admin'
|
||||
|
||||
interface ApiResponse<T> {
|
||||
@@ -47,7 +51,7 @@ export function useAdminOrganisation(id: Ref<string>) {
|
||||
return useQuery({
|
||||
queryKey: ['admin', 'organisations', id],
|
||||
queryFn: async () => {
|
||||
const { data } = await apiClient.get<ApiResponse<AdminOrganisation>>(
|
||||
const { data } = await apiClient.get<ApiResponse<AdminOrganisationDetail>>(
|
||||
`/admin/organisations/${id.value}`,
|
||||
)
|
||||
return data.data
|
||||
@@ -86,6 +90,55 @@ export function useDeleteAdminOrganisation() {
|
||||
})
|
||||
}
|
||||
|
||||
// ─── Organisation Members ──────────────────────────────────
|
||||
|
||||
export function useInviteOrganisationMember() {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({ organisationId, payload }: { organisationId: string; payload: InviteMemberPayload }) => {
|
||||
const { data } = await apiClient.post<ApiResponse<AdminOrganisationMember | null>>(
|
||||
`/admin/organisations/${organisationId}/invite`,
|
||||
payload,
|
||||
)
|
||||
return data
|
||||
},
|
||||
onSuccess: (_data, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['admin', 'organisations', variables.organisationId] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useRemoveOrganisationMember() {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({ organisationId, userId }: { organisationId: string; userId: string }) => {
|
||||
await apiClient.delete(`/admin/organisations/${organisationId}/members/${userId}`)
|
||||
},
|
||||
onSuccess: (_data, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['admin', 'organisations', variables.organisationId] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useUpdateOrganisationMemberRole() {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({ organisationId, userId, payload }: { organisationId: string; userId: string; payload: UpdateMemberRolePayload }) => {
|
||||
const { data } = await apiClient.put<ApiResponse<AdminOrganisationMember>>(
|
||||
`/admin/organisations/${organisationId}/members/${userId}`,
|
||||
payload,
|
||||
)
|
||||
return data.data
|
||||
},
|
||||
onSuccess: (_data, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['admin', 'organisations', variables.organisationId] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// ─── Users ──────────────────────────────────────────────────
|
||||
|
||||
export function useAdminUsers(params: Ref<Record<string, string | number | undefined>>) {
|
||||
|
||||
Reference in New Issue
Block a user