fix(frontend): Loading and removal of members of organization
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Client['organization']['listMembers']>[0]
|
||||
export type ListMembersResponse = Awaited<ReturnType<Client['organization']['listMembers']>>
|
||||
export type ListMembersQuery = NonNullable<ListMembersOptions>['query']
|
||||
|
||||
// Extended invitation type with additional organization and inviter details
|
||||
export type CustomInvitation =
|
||||
@@ -45,14 +48,21 @@ const user = ref<InferUserFromClient<ClientOptions> | null>(null)
|
||||
const sessionFetching = import.meta.server ? ref(false) : ref(false)
|
||||
const jwt = ref<string | null>(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<string, unknown>
|
||||
logo?: string | null
|
||||
}[]
|
||||
>([])
|
||||
const selectedOrganization = ref<{
|
||||
id: string
|
||||
name: string
|
||||
createdAt: Date
|
||||
slug: string
|
||||
metadata?: any
|
||||
metadata?: Record<string, unknown>
|
||||
logo?: string | null
|
||||
} | null>(null)
|
||||
const activeMember = ref<{ role: string } | null>(null)
|
||||
|
||||
@@ -56,9 +56,9 @@
|
||||
<!-- Members -->
|
||||
<div class="flex-1">
|
||||
<p class="font-medium mb-2">Members</p>
|
||||
<div v-if="activeOrganization?.members" class="space-y-2">
|
||||
<div v-if="activeOrganizationMembers" class="space-y-2">
|
||||
<div
|
||||
v-for="member in activeOrganization.members"
|
||||
v-for="member in activeOrganizationMembers"
|
||||
:key="member.id"
|
||||
class="flex justify-between items-center"
|
||||
>
|
||||
@@ -70,7 +70,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="user && canRemove({ role: 'owner' }, member)">
|
||||
<UButton size="xs" color="error" @click="organization.removeMember({ memberIdOrEmail: member.id })">
|
||||
<UButton size="xs" color="error" @click="organizationStore.removeMember(member.id)">
|
||||
{{ member.user.id === user.id ? 'Leave' : 'Remove' }}
|
||||
</UButton>
|
||||
</div>
|
||||
@@ -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<string[]>([])
|
||||
|
||||
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()
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -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<ActiveOrganization | null>(null)
|
||||
const organizations = ref<Organization[]>([])
|
||||
const invitations = ref<Invitation[]>([])
|
||||
const activeOrganizationMembers = ref<ListMembersResponse>([])
|
||||
|
||||
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<NonNullable<ListMembersQuery>, '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,
|
||||
|
||||
Reference in New Issue
Block a user