diff --git a/apps/app/src/components/crowd-lists/AddPersonToCrowdListDialog.vue b/apps/app/src/components/crowd-lists/AddPersonToCrowdListDialog.vue new file mode 100644 index 00000000..f4ad7df8 --- /dev/null +++ b/apps/app/src/components/crowd-lists/AddPersonToCrowdListDialog.vue @@ -0,0 +1,103 @@ + + + + + + + + Voeg een persoon toe aan {{ crowdList.name }} + + + + + + + + Annuleren + + + Toevoegen + + + + + + + {{ successName }} toegevoegd aan {{ crowdList.name }} + + diff --git a/apps/app/src/components/crowd-lists/CrowdListDetailPanel.vue b/apps/app/src/components/crowd-lists/CrowdListDetailPanel.vue new file mode 100644 index 00000000..7467327e --- /dev/null +++ b/apps/app/src/components/crowd-lists/CrowdListDetailPanel.vue @@ -0,0 +1,363 @@ + + + + + + + + + + + {{ crowdList.name }} + + + + {{ crowdList.type === CrowdListType.INTERNAL ? 'Intern' : 'Extern' }} + + + Vol + + + Auto-approve + + + + + + + + + + + + + + + + + + + + Crowd Type + {{ crowdTypeName }} + + + + + + + Ontvangende organisatie + {{ companyName }} + + + + + + + Aangemaakt op + {{ formatDate(crowdList.created_at) }} + + + + + + + + Capaciteit + + {{ crowdList.persons_count }} / {{ crowdList.max_persons }} personen + + + + + + + + + + + + + Personen ({{ crowdList.persons_count }}) + + + Toevoegen + + + + + + + + + Kon personen niet laden. + + + Opnieuw + + + + + + + + + Nog geen personen op deze lijst + + + + + + + + + {{ getInitials(person.name) }} + + + {{ person.name }} + {{ person.email }} + + + + + + + + + + + + + + + + Weet je zeker dat je {{ removingPerson?.name }} wilt verwijderen van deze lijst? + + + + + Annuleren + + + Verwijderen + + + + + + + + {{ successMessage }} + + + diff --git a/apps/app/src/components/crowd-lists/CrowdListFormDialog.vue b/apps/app/src/components/crowd-lists/CrowdListFormDialog.vue new file mode 100644 index 00000000..5ff2e5ad --- /dev/null +++ b/apps/app/src/components/crowd-lists/CrowdListFormDialog.vue @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Annuleren + + + {{ isEditMode ? 'Opslaan' : 'Aanmaken' }} + + + + + + + + {{ successMessage }} + + diff --git a/apps/app/src/components/events/EventTabsNav.vue b/apps/app/src/components/events/EventTabsNav.vue index 9516fd74..5e85bcc9 100644 --- a/apps/app/src/components/events/EventTabsNav.vue +++ b/apps/app/src/components/events/EventTabsNav.vue @@ -53,6 +53,7 @@ function formatDate(iso: string) { const baseTabs = [ { label: 'Overzicht', icon: 'tabler-layout-dashboard', route: 'events-id' }, { label: 'Personen', icon: 'tabler-users', route: 'events-id-persons' }, + { label: 'Publiekslijsten', icon: 'tabler-list', route: 'events-id-crowd-lists' }, { label: 'Secties & Shifts', icon: 'tabler-layout-grid', route: 'events-id-sections' }, { label: 'Artiesten', icon: 'tabler-music', route: 'events-id-artists' }, { label: 'Briefings', icon: 'tabler-mail', route: 'events-id-briefings' }, @@ -70,7 +71,7 @@ const programmaonderdelenLabel = computed(() => { const tabs = computed(() => { if (!event.value?.is_festival) return baseTabs - // Festival tab order: Overzicht | Programmaonderdelen | Secties & Shifts | Personen | Artiesten | Briefings | Instellingen + // Festival tab order: Overzicht | Programmaonderdelen | Secties & Shifts | Personen | Publiekslijsten | Artiesten | Briefings | Instellingen const festivalTab = { label: programmaonderdelenLabel.value, icon: 'tabler-calendar-event', @@ -80,11 +81,12 @@ const tabs = computed(() => { return [ baseTabs[0], // Overzicht festivalTab, - baseTabs[2], // Secties & Shifts + baseTabs[3], // Secties & Shifts baseTabs[1], // Personen - baseTabs[3], // Artiesten - baseTabs[4], // Briefings - baseTabs[5], // Instellingen + baseTabs[2], // Publiekslijsten + baseTabs[4], // Artiesten + baseTabs[5], // Briefings + baseTabs[6], // Instellingen ] }) diff --git a/apps/app/src/composables/api/useCrowdLists.ts b/apps/app/src/composables/api/useCrowdLists.ts new file mode 100644 index 00000000..95304e2d --- /dev/null +++ b/apps/app/src/composables/api/useCrowdLists.ts @@ -0,0 +1,121 @@ +import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query' +import type { Ref } from 'vue' +import { apiClient } from '@/lib/axios' +import type { CrowdList, CreateCrowdListDto, UpdateCrowdListDto } from '@/types/crowdList' +import type { Person } from '@/types/person' + +interface ApiResponse { + success: boolean + data: T + message?: string +} + +export function useCrowdLists(eventId: Ref) { + return useQuery({ + queryKey: ['crowd-lists', eventId], + queryFn: async () => { + const { data } = await apiClient.get<{ data: CrowdList[] }>( + `/events/${eventId.value}/crowd-lists`, + ) + + return data.data + }, + enabled: () => !!eventId.value, + }) +} + +export function useCrowdListPersons(eventId: Ref, listId: Ref) { + return useQuery({ + queryKey: ['crowd-lists', eventId, 'persons', listId], + queryFn: async () => { + const { data } = await apiClient.get<{ data: Person[] }>( + `/events/${eventId.value}/crowd-lists/${listId.value}/persons`, + ) + + return data.data + }, + enabled: () => !!eventId.value && !!listId.value, + }) +} + +export function useCreateCrowdList(eventId: Ref) { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async (payload: CreateCrowdListDto) => { + const { data } = await apiClient.post>( + `/events/${eventId.value}/crowd-lists`, + payload, + ) + + return data.data + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['crowd-lists', eventId.value] }) + }, + }) +} + +export function useUpdateCrowdList(eventId: Ref) { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async ({ id, ...payload }: UpdateCrowdListDto & { id: string }) => { + const { data } = await apiClient.put>( + `/events/${eventId.value}/crowd-lists/${id}`, + payload, + ) + + return data.data + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['crowd-lists', eventId.value] }) + }, + }) +} + +export function useDeleteCrowdList(eventId: Ref) { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async (id: string) => { + await apiClient.delete(`/events/${eventId.value}/crowd-lists/${id}`) + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['crowd-lists', eventId.value] }) + }, + }) +} + +export function useAddPersonToCrowdList(eventId: Ref) { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async ({ listId, personId }: { listId: string; personId: string }) => { + const { data } = await apiClient.post>( + `/events/${eventId.value}/crowd-lists/${listId}/persons`, + { person_id: personId }, + ) + + return data.data + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['crowd-lists', eventId.value] }) + }, + }) +} + +export function useRemovePersonFromCrowdList(eventId: Ref) { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async ({ listId, personId }: { listId: string; personId: string }) => { + await apiClient.delete( + `/events/${eventId.value}/crowd-lists/${listId}/persons/${personId}`, + ) + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['crowd-lists', eventId.value] }) + }, + }) +} diff --git a/apps/app/src/pages/events/[id]/crowd-lists/index.vue b/apps/app/src/pages/events/[id]/crowd-lists/index.vue new file mode 100644 index 00000000..374c336b --- /dev/null +++ b/apps/app/src/pages/events/[id]/crowd-lists/index.vue @@ -0,0 +1,307 @@ + + + + + + + + Publiekslijsten + + + Nieuwe lijst + + + + + + + + + Kon publiekslijsten niet laden. + + + Opnieuw proberen + + + + + + + + + Nog geen publiekslijsten voor dit evenement. + Maak je eerste lijst aan om deelnemers te organiseren. + + + Eerste lijst aanmaken + + + + + + + + + {{ item.name }} + + Vol + + + + + + + {{ item.type === CrowdListType.INTERNAL ? 'Intern' : 'Extern' }} + + + + + {{ crowdTypeMap.get(item.crowd_type_id) ?? '-' }} + + + + {{ formatPersonsCount(item) }} + + + + + + + + + {{ companyMap.get(item.recipient_company_id) ?? '-' }} + + - + + + + + + + + + + + + + + + + + + + + + + Weet je zeker dat je {{ deletingCrowdList?.name }} wilt verwijderen? + Alle personen worden van de lijst verwijderd. + + + + + Annuleren + + + Verwijderen + + + + + + + + {{ successMessage }} + + + diff --git a/apps/app/src/types/crowdList.ts b/apps/app/src/types/crowdList.ts new file mode 100644 index 00000000..1f0ac39d --- /dev/null +++ b/apps/app/src/types/crowdList.ts @@ -0,0 +1,41 @@ +export const CrowdListType = { + INTERNAL: 'internal', + EXTERNAL: 'external', +} as const +export type CrowdListType = (typeof CrowdListType)[keyof typeof CrowdListType] + +export interface CrowdList { + id: string + event_id: string + crowd_type_id: string + name: string + type: CrowdListType + recipient_company_id: string | null + auto_approve: boolean + max_persons: number | null + is_full: boolean + created_at: string + persons_count: number +} + +export interface CreateCrowdListDto { + crowd_type_id: string + name: string + type: CrowdListType + recipient_company_id?: string | null + auto_approve: boolean + max_persons?: number | null +} + +export interface UpdateCrowdListDto { + crowd_type_id?: string + name?: string + type?: CrowdListType + recipient_company_id?: string | null + auto_approve?: boolean + max_persons?: number | null +} + +export interface AddPersonToCrowdListDto { + person_id: string +}
+ Voeg een persoon toe aan {{ crowdList.name }} +
+ Nog geen personen op deze lijst +
+ Nog geen publiekslijsten voor dit evenement. + Maak je eerste lijst aan om deelnemers te organiseren. +