refactor(settings): restructure sidebar and move danger zone to its own tab
Drop the Algemeen tab together with the Organisatie subheader — organisatie- gegevens verhuizen naar /organisation. Voeg een GEVAARLIJK-subheader toe met een Gevaarlijke acties tab, die de bestaande platform-beheerder-notitie bevat (self-delete blijft buiten scope). Legacy ?tab=algemeen/general redirects door naar /organisation; default tab valt terug op Crowd Types. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
4
apps/app/components.d.ts
vendored
4
apps/app/components.d.ts
vendored
@@ -51,11 +51,11 @@ declare module 'vue' {
|
||||
CustomRadios: typeof import('./src/@core/components/app-form-elements/CustomRadios.vue')['default']
|
||||
CustomRadiosWithIcon: typeof import('./src/@core/components/app-form-elements/CustomRadiosWithIcon.vue')['default']
|
||||
CustomRadiosWithImage: typeof import('./src/@core/components/app-form-elements/CustomRadiosWithImage.vue')['default']
|
||||
DangerZoneTab: typeof import('./src/components/organisation/settings/DangerZoneTab.vue')['default']
|
||||
DeleteSubEventDialog: typeof import('./src/components/events/DeleteSubEventDialog.vue')['default']
|
||||
DialogCloseBtn: typeof import('./src/@core/components/DialogCloseBtn.vue')['default']
|
||||
DropZone: typeof import('./src/@core/components/DropZone.vue')['default']
|
||||
EditEventDialog: typeof import('./src/components/events/EditEventDialog.vue')['default']
|
||||
EditMemberRoleDialog: typeof import('./src/components/members/EditMemberRoleDialog.vue')['default']
|
||||
EditOrganisationDialog: typeof import('./src/components/organisations/EditOrganisationDialog.vue')['default']
|
||||
EditPersonDialog: typeof import('./src/components/persons/EditPersonDialog.vue')['default']
|
||||
EditSectionDialog: typeof import('./src/components/sections/EditSectionDialog.vue')['default']
|
||||
@@ -98,8 +98,6 @@ declare module 'vue' {
|
||||
SettingsEmailBranding: typeof import('./src/components/organisation/settings/SettingsEmailBranding.vue')['default']
|
||||
SettingsEmailLog: typeof import('./src/components/organisation/settings/SettingsEmailLog.vue')['default']
|
||||
SettingsEmailTemplates: typeof import('./src/components/organisation/settings/SettingsEmailTemplates.vue')['default']
|
||||
SettingsGeneral: typeof import('./src/components/organisation/settings/SettingsGeneral.vue')['default']
|
||||
SettingsMembers: typeof import('./src/components/organisation/settings/SettingsMembers.vue')['default']
|
||||
SettingsRegistrationFields: typeof import('./src/components/organisation/settings/SettingsRegistrationFields.vue')['default']
|
||||
SettingsTags: typeof import('./src/components/organisation/settings/SettingsTags.vue')['default']
|
||||
ShareProjectDialog: typeof import('./src/components/dialogs/ShareProjectDialog.vue')['default']
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
orgId: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VCard
|
||||
variant="outlined"
|
||||
color="error"
|
||||
>
|
||||
<VCardText>
|
||||
<h6 class="text-h6 text-error mb-2">
|
||||
Organisatie verwijderen
|
||||
</h6>
|
||||
<p class="text-body-2 mb-4">
|
||||
Verwijder deze organisatie en alle bijbehorende gegevens permanent. Deze actie kan niet ongedaan worden gemaakt.
|
||||
</p>
|
||||
<VAlert
|
||||
type="info"
|
||||
variant="tonal"
|
||||
class="mb-0"
|
||||
>
|
||||
Het verwijderen van een organisatie is momenteel niet mogelijk via de applicatie.
|
||||
Neem contact op met de platform beheerder.
|
||||
</VAlert>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</template>
|
||||
@@ -1,321 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { VForm } from 'vuetify/components/VForm'
|
||||
import { useMyOrganisation, useUpdateOrganisation } from '@/composables/api/useOrganisations'
|
||||
import { useAuthStore } from '@/stores/useAuthStore'
|
||||
import { requiredValidator } from '@core/utils/validators'
|
||||
import type { AxiosError } from 'axios'
|
||||
import type { ApiErrorResponse } from '@/types/auth'
|
||||
|
||||
defineProps<{
|
||||
orgId: string
|
||||
}>()
|
||||
|
||||
const authStore = useAuthStore()
|
||||
|
||||
const isOrgAdmin = computed(() => {
|
||||
const role = authStore.currentOrganisation?.role
|
||||
return role === 'org_admin' || authStore.isSuperAdmin
|
||||
})
|
||||
|
||||
const { data: organisation, isLoading, isError, refetch } = useMyOrganisation()
|
||||
const { mutate: updateOrganisation, isPending } = useUpdateOrganisation()
|
||||
|
||||
const name = ref('')
|
||||
const errors = ref<Record<string, string>>({})
|
||||
const refVForm = ref<VForm>()
|
||||
const showSuccess = ref(false)
|
||||
const showCopied = ref(false)
|
||||
const isDeleteDialogOpen = ref(false)
|
||||
const showDeleteNotice = ref(false)
|
||||
|
||||
watch(() => organisation.value, (org) => {
|
||||
if (org) {
|
||||
name.value = org.name
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
function onSubmit() {
|
||||
refVForm.value?.validate().then(({ valid }) => {
|
||||
if (!valid || !organisation.value) return
|
||||
|
||||
errors.value = {}
|
||||
|
||||
updateOrganisation(
|
||||
{ id: organisation.value.id, name: name.value },
|
||||
{
|
||||
onSuccess: () => {
|
||||
showSuccess.value = true
|
||||
},
|
||||
onError: (err: Error) => {
|
||||
const data = (err as AxiosError<ApiErrorResponse>).response?.data
|
||||
if (data?.errors) {
|
||||
errors.value = { name: data.errors.name?.[0] ?? '' }
|
||||
}
|
||||
else if (data?.message) {
|
||||
errors.value = { name: data.message }
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
function copySlug() {
|
||||
if (organisation.value?.slug) {
|
||||
navigator.clipboard.writeText(organisation.value.slug)
|
||||
showCopied.value = true
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Loading -->
|
||||
<VSkeletonLoader
|
||||
v-if="isLoading"
|
||||
type="card"
|
||||
/>
|
||||
|
||||
<!-- Error -->
|
||||
<VAlert
|
||||
v-else-if="isError"
|
||||
type="error"
|
||||
class="mb-4"
|
||||
>
|
||||
Kon organisatie niet laden.
|
||||
<template #append>
|
||||
<VBtn
|
||||
variant="text"
|
||||
@click="refetch()"
|
||||
>
|
||||
Opnieuw proberen
|
||||
</VBtn>
|
||||
</template>
|
||||
</VAlert>
|
||||
|
||||
<template v-else-if="organisation">
|
||||
<!-- Organisation details -->
|
||||
<VCard
|
||||
title="Organisatiegegevens"
|
||||
class="mb-6"
|
||||
>
|
||||
<VCardText>
|
||||
<VForm
|
||||
ref="refVForm"
|
||||
@submit.prevent="onSubmit"
|
||||
>
|
||||
<VRow>
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppTextField
|
||||
v-model="name"
|
||||
label="Organisatienaam"
|
||||
:rules="[requiredValidator]"
|
||||
:error-messages="errors.name"
|
||||
:readonly="!isOrgAdmin"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppTextField
|
||||
:model-value="organisation.slug"
|
||||
label="Slug"
|
||||
readonly
|
||||
>
|
||||
<template #append-inner>
|
||||
<VBtn
|
||||
icon="tabler-copy"
|
||||
variant="text"
|
||||
size="x-small"
|
||||
@click="copySlug"
|
||||
/>
|
||||
</template>
|
||||
</AppTextField>
|
||||
</VCol>
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppTextField
|
||||
label="Contactpersoon"
|
||||
disabled
|
||||
>
|
||||
<template #append-inner>
|
||||
<VTooltip text="Binnenkort beschikbaar">
|
||||
<template #activator="{ props: tooltipProps }">
|
||||
<VIcon
|
||||
v-bind="tooltipProps"
|
||||
icon="tabler-info-circle"
|
||||
size="18"
|
||||
color="disabled"
|
||||
/>
|
||||
</template>
|
||||
</VTooltip>
|
||||
</template>
|
||||
</AppTextField>
|
||||
</VCol>
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppTextField
|
||||
label="Contact e-mail"
|
||||
disabled
|
||||
>
|
||||
<template #append-inner>
|
||||
<VTooltip text="Binnenkort beschikbaar">
|
||||
<template #activator="{ props: tooltipProps }">
|
||||
<VIcon
|
||||
v-bind="tooltipProps"
|
||||
icon="tabler-info-circle"
|
||||
size="18"
|
||||
color="disabled"
|
||||
/>
|
||||
</template>
|
||||
</VTooltip>
|
||||
</template>
|
||||
</AppTextField>
|
||||
</VCol>
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppTextField
|
||||
label="Telefoon"
|
||||
disabled
|
||||
>
|
||||
<template #append-inner>
|
||||
<VTooltip text="Binnenkort beschikbaar">
|
||||
<template #activator="{ props: tooltipProps }">
|
||||
<VIcon
|
||||
v-bind="tooltipProps"
|
||||
icon="tabler-info-circle"
|
||||
size="18"
|
||||
color="disabled"
|
||||
/>
|
||||
</template>
|
||||
</VTooltip>
|
||||
</template>
|
||||
</AppTextField>
|
||||
</VCol>
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppTextField
|
||||
label="Website"
|
||||
disabled
|
||||
>
|
||||
<template #append-inner>
|
||||
<VTooltip text="Binnenkort beschikbaar">
|
||||
<template #activator="{ props: tooltipProps }">
|
||||
<VIcon
|
||||
v-bind="tooltipProps"
|
||||
icon="tabler-info-circle"
|
||||
size="18"
|
||||
color="disabled"
|
||||
/>
|
||||
</template>
|
||||
</VTooltip>
|
||||
</template>
|
||||
</AppTextField>
|
||||
</VCol>
|
||||
</VRow>
|
||||
|
||||
<div
|
||||
v-if="isOrgAdmin"
|
||||
class="d-flex justify-end mt-4"
|
||||
>
|
||||
<VBtn
|
||||
type="submit"
|
||||
:loading="isPending"
|
||||
>
|
||||
Wijzigingen opslaan
|
||||
</VBtn>
|
||||
</div>
|
||||
</VForm>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
|
||||
<!-- Danger zone -->
|
||||
<VCard
|
||||
v-if="isOrgAdmin"
|
||||
class="border-error"
|
||||
>
|
||||
<VCardText>
|
||||
<div class="d-flex align-center justify-space-between">
|
||||
<div>
|
||||
<h6 class="text-h6 text-error">
|
||||
Organisatie verwijderen
|
||||
</h6>
|
||||
<p class="text-body-2 text-medium-emphasis mb-0">
|
||||
Verwijder deze organisatie en alle bijbehorende gegevens permanent. Deze actie kan niet ongedaan worden gemaakt.
|
||||
</p>
|
||||
</div>
|
||||
<VBtn
|
||||
color="error"
|
||||
variant="tonal"
|
||||
class="ms-4"
|
||||
@click="isDeleteDialogOpen = true"
|
||||
>
|
||||
Verwijderen
|
||||
</VBtn>
|
||||
</div>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</template>
|
||||
|
||||
<!-- Delete confirmation dialog -->
|
||||
<VDialog
|
||||
v-model="isDeleteDialogOpen"
|
||||
max-width="440"
|
||||
>
|
||||
<VCard title="Organisatie verwijderen">
|
||||
<VCardText class="text-body-1">
|
||||
Het verwijderen van een organisatie is momenteel niet mogelijk via de applicatie.
|
||||
Neem contact op met de platform beheerder.
|
||||
</VCardText>
|
||||
<VCardActions>
|
||||
<VSpacer />
|
||||
<VBtn
|
||||
variant="text"
|
||||
@click="isDeleteDialogOpen = false"
|
||||
>
|
||||
Sluiten
|
||||
</VBtn>
|
||||
<VBtn
|
||||
color="error"
|
||||
@click="isDeleteDialogOpen = false; showDeleteNotice = true"
|
||||
>
|
||||
Begrepen
|
||||
</VBtn>
|
||||
</VCardActions>
|
||||
</VCard>
|
||||
</VDialog>
|
||||
|
||||
<!-- Snackbars -->
|
||||
<VSnackbar
|
||||
v-model="showSuccess"
|
||||
color="success"
|
||||
:timeout="3000"
|
||||
>
|
||||
Organisatie bijgewerkt
|
||||
</VSnackbar>
|
||||
<VSnackbar
|
||||
v-model="showCopied"
|
||||
color="info"
|
||||
:timeout="2000"
|
||||
>
|
||||
Slug gekopieerd
|
||||
</VSnackbar>
|
||||
<VSnackbar
|
||||
v-model="showDeleteNotice"
|
||||
color="info"
|
||||
:timeout="4000"
|
||||
>
|
||||
Neem contact op met de platform beheerder
|
||||
</VSnackbar>
|
||||
</template>
|
||||
@@ -1,12 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { useOrganisationStore } from '@/stores/useOrganisationStore'
|
||||
import SettingsGeneral from '@/components/organisation/settings/SettingsGeneral.vue'
|
||||
import SettingsCrowdTypes from '@/components/organisation/settings/SettingsCrowdTypes.vue'
|
||||
import SettingsTags from '@/components/organisation/settings/SettingsTags.vue'
|
||||
import SettingsRegistrationFields from '@/components/organisation/settings/SettingsRegistrationFields.vue'
|
||||
import SettingsEmailBranding from '@/components/organisation/settings/SettingsEmailBranding.vue'
|
||||
import SettingsEmailTemplates from '@/components/organisation/settings/SettingsEmailTemplates.vue'
|
||||
import SettingsEmailLog from '@/components/organisation/settings/SettingsEmailLog.vue'
|
||||
import DangerZoneTab from '@/components/organisation/settings/DangerZoneTab.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -26,12 +26,6 @@ interface SettingsGroup {
|
||||
}
|
||||
|
||||
const settingsGroups: SettingsGroup[] = [
|
||||
{
|
||||
label: 'Organisatie',
|
||||
items: [
|
||||
{ icon: 'tabler-building', title: 'Algemeen', key: 'general' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Configuratie',
|
||||
items: [
|
||||
@@ -53,14 +47,34 @@ const settingsGroups: SettingsGroup[] = [
|
||||
{ icon: 'tabler-mail-forward', title: 'Verzendlog', key: 'email-log' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Gevaarlijk',
|
||||
items: [
|
||||
{ icon: 'tabler-alert-triangle', title: 'Gevaarlijke acties', key: 'danger-zone' },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const allKeys = settingsGroups.flatMap(g => g.items.map(i => i.key))
|
||||
const defaultKey = 'crowd-types'
|
||||
const legacyGeneralKeys = ['general', 'algemeen', 'gevaarlijke-acties']
|
||||
|
||||
onMounted(() => {
|
||||
const tab = route.query.tab as string | undefined
|
||||
if (tab === 'general' || tab === 'algemeen') {
|
||||
router.replace({ path: '/organisation' })
|
||||
}
|
||||
else if (tab === 'gevaarlijke-acties') {
|
||||
router.replace({ query: { tab: 'danger-zone' } })
|
||||
}
|
||||
})
|
||||
|
||||
const activeKey = computed({
|
||||
get: () => {
|
||||
const tab = route.query.tab as string
|
||||
return allKeys.includes(tab) ? tab : 'general'
|
||||
const tab = route.query.tab as string | undefined
|
||||
if (!tab || legacyGeneralKeys.includes(tab))
|
||||
return defaultKey
|
||||
return allKeys.includes(tab) ? tab : defaultKey
|
||||
},
|
||||
set: (key: string) => {
|
||||
router.replace({ query: { tab: key } })
|
||||
@@ -68,16 +82,16 @@ const activeKey = computed({
|
||||
})
|
||||
|
||||
const componentMap: Record<string, Component> = {
|
||||
'general': SettingsGeneral,
|
||||
'crowd-types': SettingsCrowdTypes,
|
||||
'tags': SettingsTags,
|
||||
'registration-fields': SettingsRegistrationFields,
|
||||
'email-branding': SettingsEmailBranding,
|
||||
'email-templates': SettingsEmailTemplates,
|
||||
'email-log': SettingsEmailLog,
|
||||
'danger-zone': DangerZoneTab,
|
||||
}
|
||||
|
||||
const activeComponent = computed(() => componentMap[activeKey.value] ?? SettingsGeneral)
|
||||
const activeComponent = computed(() => componentMap[activeKey.value] ?? SettingsCrowdTypes)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
Reference in New Issue
Block a user