diff --git a/legalconsenthub/composables/organization/useOrganizationApi.ts b/legalconsenthub/composables/organization/useOrganizationApi.ts index dcd27d5..fdb9884 100644 --- a/legalconsenthub/composables/organization/useOrganizationApi.ts +++ b/legalconsenthub/composables/organization/useOrganizationApi.ts @@ -1,4 +1,5 @@ import type { LegalRole } from '~/server/utils/permissions' +import type { ListMembersOptions } from '~/composables/useAuth' export function useOrganizationApi() { const { organization } = useAuth() @@ -23,6 +24,10 @@ export function useOrganizationApi() { return organization.inviteMember({ email, role }) } + async function removeMember(memberIdOrEmail: string) { + return organization.removeMember({ memberIdOrEmail }) + } + async function acceptInvitation(invitationId: string) { return organization.acceptInvitation({ invitationId }) } @@ -47,17 +52,23 @@ export function useOrganizationApi() { return organization.setActive({ organizationId }) } + async function listMembers(options?: ListMembersOptions) { + return organization.listMembers(options) + } + return { createOrganization, deleteOrganization, getInvitation, listInvitations, inviteMember, + removeMember, acceptInvitation, cancelSentInvitation, rejectInvitation, loadOrganizations, checkSlugAvailability, - setActiveOrganization + setActiveOrganization, + listMembers } } diff --git a/legalconsenthub/composables/useAuth.ts b/legalconsenthub/composables/useAuth.ts index f228a12..6366739 100644 --- a/legalconsenthub/composables/useAuth.ts +++ b/legalconsenthub/composables/useAuth.ts @@ -5,7 +5,6 @@ import { createAuthClient } from 'better-auth/vue' import type { InferSessionFromClient, InferUserFromClient, ClientOptions } from 'better-auth/client' import { organizationClient, jwtClient } from 'better-auth/client/plugins' import type { RouteLocationRaw } from 'vue-router' -import type { RouteLocationNormalizedLoaded } from '#vue-router' import { accessControl, employerRole, @@ -29,6 +28,10 @@ export type User = Session['user'] export type ActiveOrganization = Client['$Infer']['ActiveOrganization'] export type Organization = Client['$Infer']['Organization'] export type Invitation = Client['$Infer']['Invitation'] +export type Member = Client['$Infer']['Member'] +export type ListMembersOptions = Parameters[0] +export type ListMembersResponse = Awaited> +export type ListMembersQuery = NonNullable['query'] // Extended invitation type with additional organization and inviter details export type CustomInvitation = @@ -45,14 +48,21 @@ const user = ref | null>(null) const sessionFetching = import.meta.server ? ref(false) : ref(false) const jwt = ref(null) const organizations = ref< - { id: string; name: string; createdAt: Date; slug: string; metadata?: any; logo?: string | null }[] + { + id: string + name: string + createdAt: Date + slug: string + metadata?: Record + logo?: string | null + }[] >([]) const selectedOrganization = ref<{ id: string name: string createdAt: Date slug: string - metadata?: any + metadata?: Record logo?: string | null } | null>(null) const activeMember = ref<{ role: string } | null>(null) diff --git a/legalconsenthub/pages/administration.vue b/legalconsenthub/pages/administration.vue index 345aadf..e74ede5 100644 --- a/legalconsenthub/pages/administration.vue +++ b/legalconsenthub/pages/administration.vue @@ -56,9 +56,9 @@

Members

-
+
@@ -70,7 +70,7 @@
- + {{ member.user.id === user.id ? 'Leave' : 'Remove' }}
@@ -140,15 +140,14 @@ import { useClipboard } from '@vueuse/core' import type { Invitation } from 'better-auth/plugins' const { copy, copied } = useClipboard() -const { organization, user } = useAuth() +const { user } = useAuth() const organizationStore = useOrganizationStore() -const { deleteOrganization: betterAuthDeleteOrganization, loadOrganizations } = organizationStore -const { activeOrganization, organizations, invitations } = storeToRefs(organizationStore) +const { activeOrganization, activeOrganizationMembers, organizations, invitations } = storeToRefs(organizationStore) const isRevoking = ref([]) onMounted(async () => { - await loadOrganizations() + await organizationStore.loadOrganizations() }) const labeledOrganizations = computed(() => organizations.value.map((org) => ({ label: org.name, value: org.id }))) @@ -187,6 +186,6 @@ async function deleteOrganization() { if (!confirmed) return - await betterAuthDeleteOrganization() + await organizationStore.deleteOrganization() } diff --git a/legalconsenthub/stores/useOrganizationStore.ts b/legalconsenthub/stores/useOrganizationStore.ts index 00895ac..72f5877 100644 --- a/legalconsenthub/stores/useOrganizationStore.ts +++ b/legalconsenthub/stores/useOrganizationStore.ts @@ -1,4 +1,11 @@ -import type { ActiveOrganization, Organization, Invitation } from '~/composables/useAuth' +import type { + ActiveOrganization, + Organization, + Invitation, + ListMembersOptions, + ListMembersResponse, + ListMembersQuery +} from '~/composables/useAuth' import { useOrganizationApi } from '~/composables/organization/useOrganizationApi' import type { LegalRole } from '~/server/utils/permissions' @@ -6,6 +13,7 @@ export const useOrganizationStore = defineStore('Organization', () => { const activeOrganization = ref(null) const organizations = ref([]) const invitations = ref([]) + const activeOrganizationMembers = ref([]) const organizationApi = useOrganizationApi() const toast = useToast() @@ -87,6 +95,19 @@ export const useOrganizationStore = defineStore('Organization', () => { } } + async function removeMember(memberId: string) { + try { + await organizationApi.removeMember(memberId) + activeOrganizationMembers.value = activeOrganizationMembers.value.filter( + (member: Member) => member.id !== memberId + ) + toast.add({ title: 'Member removed successfully', color: 'success' }) + } catch (e) { + toast.add({ title: 'Error removing member', color: 'error' }) + console.error('Error removing member:', e) + } + } + async function acceptInvitation(invitationId: string) { try { await organizationApi.acceptInvitation(invitationId) @@ -157,14 +178,36 @@ export const useOrganizationStore = defineStore('Organization', () => { const { data: invitationsToSet } = await organizationApi.listInvitations(activeOrganizationToSet?.id) invitations.value = invitationsToSet ?? [] + + await loadMembers() } catch (e) { toast.add({ title: 'Error setting active organizations', color: 'error' }) console.error('Error setting active organizations:', e) } } + async function loadMembers(options?: Omit, 'organizationId'>) { + try { + if (!activeOrganization.value?.id) return null + + const memberOptions: ListMembersOptions = { + query: { + organizationId: activeOrganization.value.id, + ...options + } + } + + const { data: response } = await organizationApi.listMembers(memberOptions) + activeOrganizationMembers.value = response?.members ?? [] + } catch (e) { + toast.add({ title: 'Error getting members', color: 'error' }) + console.error('Error getting members:', e) + } + } + return { activeOrganization, + activeOrganizationMembers, organizations, createOrganization, deleteOrganization, @@ -172,6 +215,7 @@ export const useOrganizationStore = defineStore('Organization', () => { getInvitation, loadInvitations, inviteMember, + removeMember, acceptInvitation, rejectInvitation, cancelSentInvitation,