feat: add create organisation button and dialog on platform page
Add "Nieuwe organisatie" button to the platform organisations list page. Dialog with name field (auto-generates slug) and slug field. Uses the existing POST /organisations endpoint. On success, navigates to the new organisation's detail page. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import type {
|
||||
AdminOrganisationDetail,
|
||||
AdminOrganisationMember,
|
||||
AdminUser,
|
||||
CreateOrganisationPayload,
|
||||
ImpersonationResponse,
|
||||
InviteMemberPayload,
|
||||
PlatformStats,
|
||||
@@ -77,6 +78,23 @@ export function useUpdateAdminOrganisation() {
|
||||
})
|
||||
}
|
||||
|
||||
export function useCreateOrganisation() {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (payload: CreateOrganisationPayload) => {
|
||||
const { data } = await apiClient.post<ApiResponse<AdminOrganisation>>(
|
||||
'/organisations',
|
||||
payload,
|
||||
)
|
||||
return data.data
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['admin', 'organisations'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useDeleteAdminOrganisation() {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useAdminOrganisations } from '@/composables/api/useAdmin'
|
||||
import type { AdminOrganisation, BillingStatus } from '@/types/admin'
|
||||
import { useAdminOrganisations, useCreateOrganisation } from '@/composables/api/useAdmin'
|
||||
import type { AdminOrganisation, BillingStatus, CreateOrganisationPayload } from '@/types/admin'
|
||||
|
||||
definePage({
|
||||
meta: {
|
||||
@@ -76,6 +76,44 @@ function onUpdateOptions(options: { page: number; itemsPerPage: number; sortBy:
|
||||
sortDirection.value = options.sortBy[0].order as 'asc' | 'desc'
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Create organisation ────────────────────────────────────
|
||||
const isCreateDialogOpen = ref(false)
|
||||
const createForm = ref<CreateOrganisationPayload>({ name: '', slug: '' })
|
||||
const createError = ref('')
|
||||
const { mutate: createOrganisation, isPending: isCreating } = useCreateOrganisation()
|
||||
|
||||
function slugify(text: string): string {
|
||||
return text
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/^-+|-+$/g, '')
|
||||
}
|
||||
|
||||
function openCreateDialog() {
|
||||
createForm.value = { name: '', slug: '' }
|
||||
createError.value = ''
|
||||
isCreateDialogOpen.value = true
|
||||
}
|
||||
|
||||
function onNameInput(name: string) {
|
||||
createForm.value.name = name
|
||||
createForm.value.slug = slugify(name)
|
||||
}
|
||||
|
||||
function submitCreate() {
|
||||
createError.value = ''
|
||||
createOrganisation(createForm.value, {
|
||||
onSuccess: (org) => {
|
||||
isCreateDialogOpen.value = false
|
||||
router.push({ name: 'platform-organisations-id', params: { id: org.id } })
|
||||
},
|
||||
onError: (err: unknown) => {
|
||||
const error = err as { response?: { data?: { message?: string } } }
|
||||
createError.value = error.response?.data?.message ?? 'Er is een fout opgetreden.'
|
||||
},
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -89,6 +127,12 @@ function onUpdateOptions(options: { page: number; itemsPerPage: number; sortBy:
|
||||
Alle organisaties op het platform
|
||||
</p>
|
||||
</div>
|
||||
<VBtn
|
||||
prepend-icon="tabler-plus"
|
||||
@click="openCreateDialog"
|
||||
>
|
||||
Nieuwe organisatie
|
||||
</VBtn>
|
||||
</div>
|
||||
|
||||
<!-- Error -->
|
||||
@@ -174,5 +218,58 @@ function onUpdateOptions(options: { page: number; itemsPerPage: number; sortBy:
|
||||
</template>
|
||||
</VDataTableServer>
|
||||
</VCard>
|
||||
|
||||
<!-- Create Dialog -->
|
||||
<VDialog
|
||||
v-model="isCreateDialogOpen"
|
||||
max-width="500"
|
||||
>
|
||||
<VCard title="Nieuwe organisatie">
|
||||
<VCardText>
|
||||
<VAlert
|
||||
v-if="createError"
|
||||
type="error"
|
||||
variant="tonal"
|
||||
class="mb-4"
|
||||
density="comfortable"
|
||||
>
|
||||
{{ createError }}
|
||||
</VAlert>
|
||||
<VRow>
|
||||
<VCol cols="12">
|
||||
<AppTextField
|
||||
:model-value="createForm.name"
|
||||
label="Naam"
|
||||
@update:model-value="onNameInput"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12">
|
||||
<AppTextField
|
||||
v-model="createForm.slug"
|
||||
label="Slug"
|
||||
hint="Wordt automatisch gegenereerd"
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</VCardText>
|
||||
<VCardActions>
|
||||
<VSpacer />
|
||||
<VBtn
|
||||
variant="tonal"
|
||||
@click="isCreateDialogOpen = false"
|
||||
>
|
||||
Annuleren
|
||||
</VBtn>
|
||||
<VBtn
|
||||
color="primary"
|
||||
:loading="isCreating"
|
||||
:disabled="!createForm.name || !createForm.slug"
|
||||
@click="submitCreate"
|
||||
>
|
||||
Aanmaken
|
||||
</VBtn>
|
||||
</VCardActions>
|
||||
</VCard>
|
||||
</VDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -96,6 +96,12 @@ export interface UpdateMemberRolePayload {
|
||||
role: string
|
||||
}
|
||||
|
||||
export interface CreateOrganisationPayload {
|
||||
name: string
|
||||
slug: string
|
||||
billing_status?: BillingStatus
|
||||
}
|
||||
|
||||
export interface UpdateAdminOrganisationPayload {
|
||||
name?: string
|
||||
slug?: string
|
||||
|
||||
Reference in New Issue
Block a user