Introduced Help Tickets

This commit is contained in:
2025-02-09 23:22:40 +01:00
parent 524b4b17c8
commit 52f3eb15a7
7 changed files with 344 additions and 11 deletions

179
pages/support/[id].vue Normal file
View File

@@ -0,0 +1,179 @@
<script setup>
import {useFunctions} from "~/composables/useFunctions.js";
const supabase = useSupabaseClient()
const profileStore = useProfileStore()
const toast = useToast()
const itemInfo = ref({})
const loaded = ref(false)
const setup = async () => {
itemInfo.value = (await supabase.from("tickets").select("*, ticketmessages(*, profile(fullName, tenant, id)), created_by(*)").eq("id",useRoute().params.id).single()).data
loaded.value = true
}
setup()
const messageContent = ref("")
const addMessage = async () => {
const {data,error} = await supabase.from("ticketmessages").insert({
profile: profileStore.activeProfile.id,
content: messageContent.value,
ticket: itemInfo.value.id,
internal: false,
type: "Nachricht"
}).select().single()
if(error) {
toast.add({title: "Erstellen fehlgeschlagen", color: "rose"})
} else {
toast.add({title: "Erstellen erfolgreich"})
messageContent.value=""
setup()
await useFunctions().useSendTelegramNotification(`Neue Nachricht im Ticket ${useRoute().params.id} von ${profileStore.activeProfile.fullName}: ${data.content}`)
}
}
const showAddEntryModal = ref(false)
const addEntryData = ref({})
const addEntry = async () => {
const {data,error} = await supabase.from("ticketmessages").insert({
profile: profileStore.activeProfile.id,
content: addEntryData.value.content,
ticket: itemInfo.value.id,
internal: addEntryData.value.internal,
type: addEntryData.value.type
}).select().single()
if(error) {
toast.add({title: "Erstellen fehlgeschlagen", color: "rose"})
} else {
toast.add({title: "Erstellen erfolgreich"})
addEntryData.value = {}
setup()
showAddEntryModal.value = false
}
}
</script>
<template>
<UDashboardNavbar title="Support Ticket">
<template #badge>
<UBadge
variant="outline"
v-if="itemInfo.status === 'Offen'"
color="yellow"
>{{itemInfo.status}}</UBadge>
<UBadge
variant="outline"
v-if="itemInfo.status === 'Geschlossen'"
color="primary"
>{{itemInfo.status}}</UBadge>
</template>
<template #right>
<UButton
v-if="profileStore.currentTenant === 5"
variant="outline"
disabled
>
Ticket Schließen
</UButton>
<UButton
v-if="profileStore.currentTenant === 5"
variant="outline"
@click="showAddEntryModal = true"
>
+ Eintrag
</UButton>
<UModal v-model="showAddEntryModal">
<UCard>
<template #header>
Eintrag hinzufügen
</template>
<UFormGroup
label="Intern:"
>
<UToggle
v-model="addEntryData.internal"
/>
</UFormGroup>
<UFormGroup
label="Typ:"
>
<USelectMenu
v-model="addEntryData.type"
:options="['Nachricht','Notiz','Anruf','Externe Kommunikation']"
/>
</UFormGroup>
<UFormGroup
label="Inhalt:"
>
<UTextarea
v-model="addEntryData.content"
/>
</UFormGroup>
<template #footer>
<UButton
@click="addEntry"
>
Erstellen
</UButton>
</template>
</UCard>
</UModal>
</template>
</UDashboardNavbar>
<UDashboardPanelContent v-if="loaded">
<UAlert
v-if="profileStore.currentTenant === 5"
v-for="item in itemInfo.ticketmessages"
:description="item.content"
:avatar="{ alt: item.profile.fullName}"
:title="`${item.type} - ${item.profile.fullName}`"
class="mb-3"
:color="item.internal ? 'rose' : item.profile.tenant === 5 ? 'primary' : 'white'"
variant="outline"
/>
<UAlert
v-else
v-for="item in itemInfo.ticketmessages.filter(i => !i.internal)"
:description="item.content"
:avatar="{ alt: item.profile.fullName}"
:title="item.profile.fullName"
class="mb-3"
:color="item.profile.tenant === 5 ? 'primary' : 'white'"
variant="outline"
/>
<InputGroup>
<UInput
class="w-full mr-2"
placeholder="Neue Nachricht senden"
v-model="messageContent"
@keyup.enter="addMessage"
/>
<UButton
@click="addMessage"
>
Senden
</UButton>
</InputGroup>
</UDashboardPanelContent>
<UProgress animation="carousel" v-else class="w-3/4 mx-auto"/>
</template>
<style scoped>
</style>

73
pages/support/create.vue Normal file
View File

@@ -0,0 +1,73 @@
<script setup>
import {useFunctions} from "~/composables/useFunctions.js";
const supabase = useSupabaseClient()
const profileStore = useProfileStore()
const router = useRouter()
const itemInfo = ref({})
const createTicket = async () => {
const {data:ticketData,error:ticketError} = await supabase.from("tickets").insert({
title: itemInfo.value.title,
created_by: profileStore.activeProfile.id,
tenant: profileStore.currentTenant
}).select().single()
if(ticketError) {
console.error(ticketError)
} else {
console.log(ticketData)
const {data:messageData,error:messageError} = await supabase.from("ticketmessages").insert({
ticket: ticketData.id,
profile: profileStore.activeProfile.id,
content: itemInfo.value.content,
internal: false
})
if(messageError) {
console.log(messageError)
} else {
router.push(`/support/${ticketData.id}`)
await useFunctions().useSendTelegramNotification(`Ticket von ${profileStore.activeProfile.fullName} erstellt : ${itemInfo.value.content}`)
}
}
}
</script>
<template>
<UDashboardNavbar title="Neues Ticket erstellen">
<template #right>
<UButton
@click="createTicket"
>
Erstellen
</UButton>
</template>
</UDashboardNavbar>
<UForm class="w-2/3 mx-auto mt-5">
<UFormGroup
label="Titel:"
>
<UInput
v-model="itemInfo.title"
/>
</UFormGroup>
<UFormGroup
label="Nachricht:"
>
<UTextarea
v-model="itemInfo.content"
/>
</UFormGroup>
</UForm>
</template>
<style scoped>
</style>

56
pages/support/index.vue Normal file
View File

@@ -0,0 +1,56 @@
<script setup>
import dayjs from "dayjs";
const supabase = useSupabaseClient()
const profileStore = useProfileStore()
const router = useRouter()
const tickets = ref([])
const setup = async () => {
if(profileStore.currentTenant === 5) {
tickets.value = (await supabase.from("tickets").select("*,created_by(*), ticketmessages(*), tenant(*)")).data
} else {
tickets.value = (await supabase.from("tickets").select("*,created_by(*), ticketmessages(*)").eq("tenant",profileStore.currentTenant)).data
}
}
setup()
</script>
<template>
<UDashboardNavbar
title="Suport Tickets"
>
<template #right>
<UButton
@click="router.push('/support/create')"
>
+ Ticket
</UButton>
</template>
</UDashboardNavbar>
<UTable
:rows="tickets"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: `Keine Tickets anzuzeigen` }"
@select="(i) => router.push(`/support/${i.id}`)"
:columns="[{key:'created_at',label:'Datum'}, ...profileStore.currentTenant === 5 ? [{key:'tenant',label:'Tenant'}] : [],{key:'title',label:'Titel'},{key:'created_by',label:'Ersteller'},{key:'ticketmessages',label:'Nachrichten'}]"
>
<template #tenant-data="{ row }">
{{row.tenant.name}}
</template>
<template #created_by-data="{ row }">
{{row.created_by.fullName}}
</template>
<template #created_at-data="{ row }">
{{dayjs(row.created_at).format('DD.MM.YYYY HH:mm')}}
</template>
<template #ticketmessages-data="{ row }">
{{row.ticketmessages.length}}
</template>
</UTable>
</template>
<style scoped>
</style>