11<script setup lang="ts">
22import { apiClient } from ' @/api'
33import Loading from ' @/components/UIComponents/Loading.vue'
4+ import { Bars3Icon } from ' @heroicons/vue/24/outline'
45import { useAsyncState } from ' @vueuse/core'
5- import { computed , ref , useTemplateRef } from ' vue'
6+ import { computed , ref , useTemplateRef , watch } from ' vue'
7+ import draggable from ' vuedraggable'
68import FAQFormModal , { type FAQ } from ' ./FAQFormModal.vue'
9+ import { toast } from ' vue-sonner'
710
811type Props = {
912 unterveranstaltungId: string
1013}
1114
1215const { unterveranstaltungId } = defineProps <Props >()
1316
14- const { state, isLoading, execute } = useAsyncState (() => apiClient .faq .list .query ({ unterveranstaltungId }), {})
15- const hasKeys = computed (() => Object .keys (state .value ).length > 0 )
17+ const { state, isLoading, execute } = useAsyncState (
18+ async () => apiClient .faq .list .query ({ unterveranstaltungId }),
19+ {},
20+ { resetOnExecute: false }
21+ )
22+
23+ type Category = {
24+ name: string
25+ categoryId: string
26+ faqs: FAQ []
27+ }
28+ const categories = ref <Category []>([])
29+ watch (state , () => {
30+ categories .value = Object .entries (state .value ).map (([name , faqs ]) => ({
31+ name ,
32+ categoryId: faqs [0 ]?.categoryId ?? ' ' ,
33+ faqs: [... faqs ],
34+ }))
35+ })
36+
37+ const hasKeys = computed (() => categories .value .length > 0 )
1638
1739const formModal = useTemplateRef (' formModal' )
1840const editFaq = ref <FAQ >()
1941
2042function openFormModal(faq ? : FAQ ) {
21- if (faq !== undefined ) {
22- editFaq .value = faq
23- }
43+ editFaq .value = faq
2444 formModal .value ?.show ()
2545}
2646
47+ async function saveOrder() {
48+ const faqOrder = categories .value .flatMap ((cat ) =>
49+ cat .faqs .map ((faq , index ) => ({ id: faq .id , sortOrder: index , categoryId: cat .categoryId }))
50+ )
51+ const categoryOrder = categories .value .map ((cat , index ) => ({
52+ id: cat .categoryId ,
53+ sortOrder: index ,
54+ }))
55+ await apiClient .faq .reorder .mutate ({ faqOrder , categoryOrder })
56+ await execute ()
57+ toast .success (' FAQ-Reihenfolge gespeichert' )
58+ }
59+
2760defineExpose ({
2861 openFormModal ,
2962})
@@ -37,27 +70,50 @@ defineExpose({
3770 @success =" execute"
3871 />
3972
40- <Loading v-if =" isLoading" />
73+ <Loading v-if =" isLoading && !hasKeys " />
4174 <p v-else-if =" !hasKeys" >Hier wurden noch keine FAQs angelegt.</p >
4275
43- <div
44- v-for =" (list, category) in state"
76+ <draggable
4577 v-else
46- :key =" category"
47- class =" space-y-4"
78+ v-model =" categories"
79+ item-key =" categoryId"
80+ handle =" .category-handle"
81+ class =" grid grid-cols-3 gap-8 items-start"
82+ @end =" saveOrder"
4883 >
49- <p class =" text-gray-500 font-normal" >{{ category }}</p >
50- <div
51- v-for =" (faq, index) in list"
52- :key =" index"
53- class =" transition-all rounded-lg shadow hover:shadow-lg bg-primary-5 dark:bg-primary-950 p-2 select-none cursor-pointer"
54- @click =" () => openFormModal(faq)"
55- >
56- <span class =" font-bold" >{{ faq.question }}</span >
57- <br />
58- <!-- eslint-disable vue/no-v-html -->
59- <div v-html =" faq.answer" />
60- <!-- eslint-enable vue/no-v-html -->
61- </div >
62- </div >
84+ <template #item =" { element: category } " >
85+ <div class =" space-y-4 mb-6" >
86+ <div class =" flex items-center gap-2" >
87+ <Bars3Icon class =" category-handle w-5 h-5 text-gray-400 cursor-grab active:cursor-grabbing" />
88+ <p class =" text-gray-500 font-normal" >{{ category.name }}</p >
89+ </div >
90+ <draggable
91+ v-model =" category.faqs"
92+ item-key =" id"
93+ handle =" .faq-handle"
94+ group =" faqs"
95+ class =" space-y-4"
96+ @end =" saveOrder"
97+ >
98+ <template #item =" { element: faq } " >
99+ <div
100+ class =" transition-all rounded-lg shadow hover:shadow-lg bg-primary-5 dark:bg-primary-950 p-2 select-none flex items-start gap-2"
101+ >
102+ <Bars3Icon class =" faq-handle w-5 h-5 mt-0.5 text-gray-400 cursor-grab active:cursor-grabbing shrink-0" />
103+ <div
104+ class =" cursor-pointer flex-1"
105+ @click =" () => openFormModal(faq)"
106+ >
107+ <span class =" font-bold" >{{ faq.question }}</span >
108+ <br />
109+ <!-- eslint-disable vue/no-v-html -->
110+ <div v-html =" faq.answer" />
111+ <!-- eslint-enable vue/no-v-html -->
112+ </div >
113+ </div >
114+ </template >
115+ </draggable >
116+ </div >
117+ </template >
118+ </draggable >
63119</template >
0 commit comments