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:
2026-04-15 01:27:40 +02:00
parent 66e4167c03
commit 2933d957a6
3 changed files with 123 additions and 2 deletions

View File

@@ -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()

View File

@@ -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>

View File

@@ -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