Added E-Mail Sending
This commit is contained in:
@@ -1,72 +1,83 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="p-3 mt-3 editor">
|
||||||
<div v-if="editor">
|
<div v-if="editor">
|
||||||
<InputGroup class="mx-3 mt-3">
|
<InputGroup class="mb-3">
|
||||||
<UButton
|
<UButton
|
||||||
@click="editor.chain().focus().toggleBold().run()"
|
@click="editor.chain().focus().toggleBold().run()"
|
||||||
:disabled="!editor.can().chain().focus().toggleBold().run()"
|
:disabled="!editor.can().chain().focus().toggleBold().run()"
|
||||||
:class="{ 'is-active': editor.isActive('bold') }"
|
:class="{ 'is-active': editor.isActive('bold') }"
|
||||||
icon="i-heroicons-bold"
|
icon="i-heroicons-bold"
|
||||||
|
variant="outline"
|
||||||
/>
|
/>
|
||||||
<UButton
|
<UButton
|
||||||
@click="editor.chain().focus().toggleItalic().run()"
|
@click="editor.chain().focus().toggleItalic().run()"
|
||||||
:disabled="!editor.can().chain().focus().toggleItalic().run()"
|
:disabled="!editor.can().chain().focus().toggleItalic().run()"
|
||||||
:class="{ 'is-active': editor.isActive('italic') }"
|
:class="{ 'is-active': editor.isActive('italic') }"
|
||||||
icon="i-heroicons-italic"
|
icon="i-heroicons-italic"
|
||||||
|
variant="outline"
|
||||||
/>
|
/>
|
||||||
<UButton
|
<UButton
|
||||||
@click="editor.chain().focus().toggleStrike().run()"
|
@click="editor.chain().focus().toggleStrike().run()"
|
||||||
:disabled="!editor.can().chain().focus().toggleStrike().run()"
|
:disabled="!editor.can().chain().focus().toggleStrike().run()"
|
||||||
:class="{ 'is-active': editor.isActive('strike') }"
|
:class="{ 'is-active': editor.isActive('strike') }"
|
||||||
icon="i-mdi-format-strikethrough"
|
icon="i-mdi-format-strikethrough"
|
||||||
|
variant="outline"
|
||||||
/>
|
/>
|
||||||
<UButton
|
<!--<UButton
|
||||||
@click="editor.chain().focus().toggleCode().run()"
|
@click="editor.chain().focus().toggleCode().run()"
|
||||||
:disabled="!editor.can().chain().focus().toggleCode().run()"
|
:disabled="!editor.can().chain().focus().toggleCode().run()"
|
||||||
:class="{ 'is-active': editor.isActive('code') }"
|
:class="{ 'is-active': editor.isActive('code') }"
|
||||||
icon="i-heroicons-code-bracket"
|
icon="i-heroicons-code-bracket"
|
||||||
|
variant="outline"
|
||||||
/>
|
/>
|
||||||
<!-- <UButton @click="editor.chain().focus().unsetAllMarks().run()">
|
<UButton @click="editor.chain().focus().unsetAllMarks().run()">
|
||||||
clear marks
|
clear marks
|
||||||
</UButton>
|
</UButton>
|
||||||
<UButton @click="editor.chain().focus().clearNodes().run()">
|
<UButton @click="editor.chain().focus().clearNodes().run()">
|
||||||
clear nodes
|
clear nodes
|
||||||
</UButton>-->
|
</UButton>
|
||||||
<UButton
|
<UButton
|
||||||
@click="editor.chain().focus().setParagraph().run()"
|
@click="editor.chain().focus().setParagraph().run()"
|
||||||
:class="{ 'is-active': editor.isActive('paragraph') }"
|
:class="{ 'is-active': editor.isActive('paragraph') }"
|
||||||
|
variant="outline"
|
||||||
>
|
>
|
||||||
paragraph
|
paragraph
|
||||||
</UButton>
|
</UButton>-->
|
||||||
<UButton
|
<UButton
|
||||||
@click="editor.chain().focus().toggleHeading({ level: 1 }).run()"
|
@click="editor.chain().focus().toggleHeading({ level: 1 }).run()"
|
||||||
:class="{ 'is-active': editor.isActive('heading', { level: 1 }) }"
|
:class="{ 'is-active': editor.isActive('heading', { level: 1 }) }"
|
||||||
|
variant="outline"
|
||||||
>h1</UButton>
|
>h1</UButton>
|
||||||
<UButton
|
<UButton
|
||||||
@click="editor.chain().focus().toggleHeading({ level: 2 }).run()"
|
@click="editor.chain().focus().toggleHeading({ level: 2 }).run()"
|
||||||
:class="{ 'is-active': editor.isActive('heading', { level: 2 }) }"
|
:class="{ 'is-active': editor.isActive('heading', { level: 2 }) }"
|
||||||
|
variant="outline"
|
||||||
>h2</UButton>
|
>h2</UButton>
|
||||||
<UButton
|
<UButton
|
||||||
@click="editor.chain().focus().toggleHeading({ level: 3 }).run()"
|
@click="editor.chain().focus().toggleHeading({ level: 3 }).run()"
|
||||||
:class="{ 'is-active': editor.isActive('heading', { level: 3 }) }"
|
:class="{ 'is-active': editor.isActive('heading', { level: 3 }) }"
|
||||||
|
variant="outline"
|
||||||
>
|
>
|
||||||
h3
|
h3
|
||||||
</UButton>
|
</UButton>
|
||||||
<UButton
|
<UButton
|
||||||
@click="editor.chain().focus().toggleHeading({ level: 4 }).run()"
|
@click="editor.chain().focus().toggleHeading({ level: 4 }).run()"
|
||||||
:class="{ 'is-active': editor.isActive('heading', { level: 4 }) }"
|
:class="{ 'is-active': editor.isActive('heading', { level: 4 }) }"
|
||||||
|
variant="outline"
|
||||||
>
|
>
|
||||||
h4
|
h4
|
||||||
</UButton>
|
</UButton>
|
||||||
<UButton
|
<UButton
|
||||||
@click="editor.chain().focus().toggleHeading({ level: 5 }).run()"
|
@click="editor.chain().focus().toggleHeading({ level: 5 }).run()"
|
||||||
:class="{ 'is-active': editor.isActive('heading', { level: 5 }) }"
|
:class="{ 'is-active': editor.isActive('heading', { level: 5 }) }"
|
||||||
|
variant="outline"
|
||||||
>
|
>
|
||||||
h5
|
h5
|
||||||
</UButton>
|
</UButton>
|
||||||
<UButton
|
<UButton
|
||||||
@click="editor.chain().focus().toggleHeading({ level: 6 }).run()"
|
@click="editor.chain().focus().toggleHeading({ level: 6 }).run()"
|
||||||
:class="{ 'is-active': editor.isActive('heading', { level: 6 }) }"
|
:class="{ 'is-active': editor.isActive('heading', { level: 6 }) }"
|
||||||
|
variant="outline"
|
||||||
>
|
>
|
||||||
h6
|
h6
|
||||||
</UButton>
|
</UButton>
|
||||||
@@ -74,11 +85,13 @@
|
|||||||
@click="editor.chain().focus().toggleBulletList().run()"
|
@click="editor.chain().focus().toggleBulletList().run()"
|
||||||
:class="{ 'is-active': editor.isActive('bulletList') }"
|
:class="{ 'is-active': editor.isActive('bulletList') }"
|
||||||
icon="i-heroicons-list-bullet"
|
icon="i-heroicons-list-bullet"
|
||||||
|
variant="outline"
|
||||||
/>
|
/>
|
||||||
<UButton
|
<UButton
|
||||||
@click="editor.chain().focus().toggleOrderedList().run()"
|
@click="editor.chain().focus().toggleOrderedList().run()"
|
||||||
:class="{ 'is-active': editor.isActive('orderedList') }"
|
:class="{ 'is-active': editor.isActive('orderedList') }"
|
||||||
icon="i-mdi-format-list-numbered"
|
icon="i-mdi-format-list-numbered"
|
||||||
|
variant="outline"
|
||||||
/>
|
/>
|
||||||
<!-- <UButton
|
<!-- <UButton
|
||||||
@click="editor.chain().focus().toggleCodeBlock().run()"
|
@click="editor.chain().focus().toggleCodeBlock().run()"
|
||||||
@@ -120,12 +133,40 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
const emit = defineEmits(['updateContent'])
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
preloadedContent: {
|
||||||
|
type: String,
|
||||||
|
required:false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const {preloadedContent} = props
|
||||||
|
|
||||||
const editor = useEditor({
|
const editor = useEditor({
|
||||||
content: "<p>I'm running Tiptap with Vue.js. 🎉</p>",
|
content: "<p>I'm running Tiptap with Vue.js. 🎉</p>",
|
||||||
extensions: [TiptapStarterKit],
|
extensions: [TiptapStarterKit],
|
||||||
|
onUpdate({editor}) {
|
||||||
|
console.log(editor.getJSON())
|
||||||
|
console.log(editor.getHTML())
|
||||||
|
console.log(editor.getText())
|
||||||
|
emit('updateContent',{json: editor.getJSON(),html: editor.getHTML(), text: editor.getText()})
|
||||||
|
},
|
||||||
|
onCreate({editor}) {
|
||||||
|
editor.commands.setContent(preloadedContent)
|
||||||
|
emit('updateContent',{json: editor.getJSON(),html: editor.getHTML(), text: editor.getText()})
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
unref(editor).destroy();
|
unref(editor).destroy();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.editor {
|
||||||
|
border: 1px solid #69c350;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -136,33 +136,65 @@ setupPage()
|
|||||||
v-model="openTab"
|
v-model="openTab"
|
||||||
>
|
>
|
||||||
<template #item="{item}">
|
<template #item="{item}">
|
||||||
<UCard class="mt-5">
|
<div v-if="item.label === 'Informationen'" class="flex flex-row mt-5">
|
||||||
<div v-if="item.label === 'Informationen'">
|
<div class="w-1/2 mr-5">
|
||||||
|
<UCard>
|
||||||
|
<Toolbar>
|
||||||
<div class="text-wrap mt-3">
|
<UButton
|
||||||
<p v-if="itemInfo.customer">Kunde: <nuxt-link :to="`/customers/show/${itemInfo.customer.id}`">{{itemInfo.customer ? itemInfo.customer.name : ""}}</nuxt-link></p>
|
@click="router.push(`/email/new?to=${itemInfo.email}`)"
|
||||||
<p v-if="itemInfo.vendor">Lieferant: <nuxt-link :to="`/vendors/show/${itemInfo.vendor.id}`">{{itemInfo.vendor ? itemInfo.vendor.name : ""}}</nuxt-link></p>
|
icon="i-heroicons-envelope"
|
||||||
|
:disabled="!itemInfo.email"
|
||||||
|
>
|
||||||
<p>E-Mail: {{itemInfo.email}}</p>
|
E-Mail
|
||||||
<p>Mobil: {{itemInfo.phoneMobile}}</p>
|
</UButton>
|
||||||
<p>Festnetz: {{itemInfo.phoneHome}}</p>
|
</Toolbar>
|
||||||
<p>Rolle: {{itemInfo.role}}</p>
|
<table class="w-full">
|
||||||
<p>Geburtstag: {{itemInfo.birthday ? dayjs(itemInfo.birthday).format("DD.MM.YYYY") : ""}}</p>
|
<tr>
|
||||||
<p>Notizen:<br> {{itemInfo.notes}}</p>
|
<td>Kunde: </td>
|
||||||
</div>
|
<td><nuxt-link v-if="itemInfo.customer" :to="`/customers/show/${itemInfo.customer?.id}`">{{itemInfo?.customer?.name}}</nuxt-link></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Lieferant: </td>
|
||||||
|
<td><nuxt-link v-if="itemInfo.vendor" :to="`/customers/show/${itemInfo.vendor?.id}`">{{itemInfo?.vendor?.name}}</nuxt-link></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>E-Mail:</td>
|
||||||
|
<td>{{itemInfo.email}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Mobil:</td>
|
||||||
|
<td>{{itemInfo.phoneMobile}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Festnetz:</td>
|
||||||
|
<td>{{itemInfo.phoneHome}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Rolle:</td>
|
||||||
|
<td>{{itemInfo.role}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Geburtstag:</td>
|
||||||
|
<td>{{itemInfo.birthday ? dayjs(itemInfo.birthday).format("DD.MM.YYYY") : ""}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Notizen:</td>
|
||||||
|
<td>{{itemInfo.notes}}</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
</UCard>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="item.label === 'Logbuch'">
|
<div class="w-1/2">
|
||||||
<HistoryDisplay
|
<UCard>
|
||||||
type="contact"
|
<HistoryDisplay
|
||||||
v-if="itemInfo"
|
type="contact"
|
||||||
:element-id="itemInfo.id"
|
v-if="itemInfo"
|
||||||
/>
|
:element-id="itemInfo.id"
|
||||||
|
/>
|
||||||
|
</UCard>
|
||||||
</div>
|
</div>
|
||||||
</UCard>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</UTabs>
|
</UTabs>
|
||||||
<UForm
|
<UForm
|
||||||
@@ -289,5 +321,10 @@ setupPage()
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
td {
|
||||||
|
border-bottom: 1px solid lightgrey;
|
||||||
|
vertical-align: top;
|
||||||
|
padding-bottom: 0.15em;
|
||||||
|
padding-top: 0.15em;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -9,16 +9,19 @@ defineShortcuts({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const supabase = useSupabaseClient()
|
||||||
|
|
||||||
const dataStore = useDataStore()
|
const dataStore = useDataStore()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const itemInfo = ref({})
|
const itemInfo = ref({})
|
||||||
|
const linkedDocument =ref({})
|
||||||
|
|
||||||
const setupPage = () => {
|
const setupPage = async () => {
|
||||||
if(route.params) {
|
if(route.params) {
|
||||||
if(route.params.id) itemInfo.value = dataStore.getCreatedDocumentById(Number(route.params.id))
|
if(route.params.id) itemInfo.value = await useSupabaseSelectSingle("createddocuments",route.params.id,"*")
|
||||||
|
linkedDocument.value = (await supabase.from("documents").select("id").eq("createdDocument", route.params.id).single()).data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,9 +50,17 @@ setupPage()
|
|||||||
>
|
>
|
||||||
Übernehmen
|
Übernehmen
|
||||||
</UButton>
|
</UButton>
|
||||||
|
<UButton
|
||||||
|
@click="router.push(`/email/new?loadDocuments=[${linkedDocument.id}]`)"
|
||||||
|
icon="i-heroicons-envelope"
|
||||||
|
>
|
||||||
|
E-Mail
|
||||||
|
</UButton>
|
||||||
</template>
|
</template>
|
||||||
</UDashboardToolbar>
|
</UDashboardToolbar>
|
||||||
|
|
||||||
|
{{linkedDocument}}
|
||||||
|
|
||||||
<object
|
<object
|
||||||
:data="dataStore.documents.find(i => i.createdDocument === itemInfo.id) ? dataStore.documents.find(i => i.createdDocument === itemInfo.id).url : ''"
|
:data="dataStore.documents.find(i => i.createdDocument === itemInfo.id) ? dataStore.documents.find(i => i.createdDocument === itemInfo.id).url : ''"
|
||||||
class="h-full"
|
class="h-full"
|
||||||
|
|||||||
279
pages/email/index.vue
Normal file
279
pages/email/index.vue
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
<script setup>
|
||||||
|
|
||||||
|
const supabase = useSupabaseClient()
|
||||||
|
const dataStore = useDataStore()
|
||||||
|
|
||||||
|
|
||||||
|
const selectedTab = ref(0)
|
||||||
|
const selectedMail = ref(null)
|
||||||
|
const selectedMailboxPath = ref("INBOX")
|
||||||
|
const emails = ref([])
|
||||||
|
const accountData = ref(null)
|
||||||
|
const availableAccounts = ref(null)
|
||||||
|
const selectedAccount = ref(null)
|
||||||
|
const setupPage = async () => {
|
||||||
|
availableAccounts.value = (await supabase.from("emailAccounts").select("*").contains("profiles",[dataStore.activeProfile.id])).data
|
||||||
|
console.log(availableAccounts.value)
|
||||||
|
|
||||||
|
if(availableAccounts.value.length > 0) {
|
||||||
|
selectedAccount.value = availableAccounts.value[0].id
|
||||||
|
accountData.value = await useSupabaseSelectSingle("emailAccounts", selectedAccount.value)
|
||||||
|
emails.value = await useSupabaseSelect("emailMessages", "*", "date", false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const filteredMails = computed(() => {
|
||||||
|
|
||||||
|
let temp = emails.value.filter(i => i.mailboxPath.toLowerCase() === selectedMailboxPath.value.toLowerCase())
|
||||||
|
|
||||||
|
|
||||||
|
if(selectedTab.value === 0){
|
||||||
|
return temp
|
||||||
|
} else {
|
||||||
|
return temp.filter(i => !i.seen)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return emails.value
|
||||||
|
})
|
||||||
|
|
||||||
|
const isMailPanelOpen = computed({
|
||||||
|
get() {
|
||||||
|
return !!selectedMail.value
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
if (!value) {
|
||||||
|
selectedMail.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const changeSeen = async (seen) => {
|
||||||
|
const {data,error} = await supabase.functions.invoke('emailcontrol',{
|
||||||
|
body: {
|
||||||
|
method: seen ? "makeSeen" : "makeUnseen",
|
||||||
|
emailId: selectedMail.value.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if(data) {
|
||||||
|
setupPage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setupPage()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- <UDashboardNavbar title="E-Mails">
|
||||||
|
|
||||||
|
</UDashboardNavbar>
|
||||||
|
<UDashboardToolbar>
|
||||||
|
|
||||||
|
</UDashboardToolbar>-->
|
||||||
|
<UDashboardPage
|
||||||
|
v-if="selectedAccount"
|
||||||
|
>
|
||||||
|
<UDashboardPanel
|
||||||
|
id="inbox"
|
||||||
|
:width="400"
|
||||||
|
:resizable="{ min: 300, max: 500 }"
|
||||||
|
|
||||||
|
>
|
||||||
|
<UDashboardNavbar>
|
||||||
|
<template #left>
|
||||||
|
<USelectMenu
|
||||||
|
v-if="accountData"
|
||||||
|
class="w-40"
|
||||||
|
:options="accountData.mailboxes"
|
||||||
|
option-attribute="name"
|
||||||
|
value-attribute="path"
|
||||||
|
v-model="selectedMailboxPath"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #right>
|
||||||
|
<UButton
|
||||||
|
icon="i-heroicons-arrow-path"
|
||||||
|
variant="ghost"
|
||||||
|
color="gray"
|
||||||
|
@click="setupPage"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UTabs
|
||||||
|
v-model="selectedTab"
|
||||||
|
:items="[{label: 'All'}, {label: 'Ungelesen'}]"
|
||||||
|
:ui="{ wrapper: '', list: { height: 'h-9', tab: { height: 'h-7', size: 'text-[13px]' } } }"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</UDashboardNavbar>
|
||||||
|
|
||||||
|
<!-- ~/components/inbox/InboxList.vue -->
|
||||||
|
<InboxList
|
||||||
|
v-model="selectedMail"
|
||||||
|
:mails="filteredMails"
|
||||||
|
@emailSelected=" !selectedMail.seen ? changeSeen(true): ''"
|
||||||
|
/>
|
||||||
|
</UDashboardPanel>
|
||||||
|
<UDashboardPanel
|
||||||
|
v-model="isMailPanelOpen"
|
||||||
|
collapsible
|
||||||
|
grow
|
||||||
|
side="right"
|
||||||
|
>
|
||||||
|
<template v-if="selectedMail">
|
||||||
|
<UDashboardNavbar>
|
||||||
|
<template #toggle>
|
||||||
|
<UDashboardNavbarToggle icon="i-heroicons-x-mark" />
|
||||||
|
|
||||||
|
<UDivider
|
||||||
|
orientation="vertical"
|
||||||
|
class="mx-1.5 lg:hidden"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #left>
|
||||||
|
<UTooltip text="Ungelesen">
|
||||||
|
<UButton
|
||||||
|
icon="i-heroicons-eye-slash"
|
||||||
|
color="gray"
|
||||||
|
variant="ghost"
|
||||||
|
@click="changeSeen(false)"
|
||||||
|
/>
|
||||||
|
</UTooltip>
|
||||||
|
<!-- <UTooltip text="Archive">
|
||||||
|
<UButton
|
||||||
|
icon="i-heroicons-archive-box"
|
||||||
|
color="gray"
|
||||||
|
variant="ghost"
|
||||||
|
/>
|
||||||
|
</UTooltip>
|
||||||
|
|
||||||
|
<UTooltip text="Move to junk">
|
||||||
|
<UButton
|
||||||
|
icon="i-heroicons-archive-box-x-mark"
|
||||||
|
color="gray"
|
||||||
|
variant="ghost"
|
||||||
|
/>
|
||||||
|
</UTooltip>
|
||||||
|
|
||||||
|
<UDivider
|
||||||
|
orientation="vertical"
|
||||||
|
class="mx-1.5"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UPopover :popper="{ placement: 'bottom-start' }">
|
||||||
|
<template #default="{ open }">
|
||||||
|
<UTooltip
|
||||||
|
text="Snooze"
|
||||||
|
:prevent="open"
|
||||||
|
>
|
||||||
|
<UButton
|
||||||
|
icon="i-heroicons-clock"
|
||||||
|
color="gray"
|
||||||
|
variant="ghost"
|
||||||
|
:class="[open && 'bg-gray-50 dark:bg-gray-800']"
|
||||||
|
/>
|
||||||
|
</UTooltip>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #panel="{ close }">
|
||||||
|
<DatePicker @close="close" />
|
||||||
|
</template>
|
||||||
|
</UPopover>-->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #right>
|
||||||
|
<UTooltip text="Reply">
|
||||||
|
<UButton
|
||||||
|
icon="i-heroicons-arrow-uturn-left"
|
||||||
|
color="gray"
|
||||||
|
variant="ghost"
|
||||||
|
/>
|
||||||
|
</UTooltip>
|
||||||
|
|
||||||
|
<UTooltip text="Forward">
|
||||||
|
<UButton
|
||||||
|
icon="i-heroicons-arrow-uturn-right"
|
||||||
|
color="gray"
|
||||||
|
variant="ghost"
|
||||||
|
/>
|
||||||
|
</UTooltip>
|
||||||
|
|
||||||
|
<!-- <UDivider
|
||||||
|
orientation="vertical"
|
||||||
|
class="mx-1.5"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UDropdown :items="dropdownItems">
|
||||||
|
<UButton
|
||||||
|
icon="i-heroicons-ellipsis-vertical"
|
||||||
|
color="gray"
|
||||||
|
variant="ghost"
|
||||||
|
/>
|
||||||
|
</UDropdown>-->
|
||||||
|
</template>
|
||||||
|
</UDashboardNavbar>
|
||||||
|
|
||||||
|
<!-- ~/components/inbox/InboxMail.vue -->
|
||||||
|
<InboxMail :mail="selectedMail" />
|
||||||
|
</template>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="flex-1 hidden lg:flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<UIcon
|
||||||
|
name="i-heroicons-inbox"
|
||||||
|
class="w-32 h-32 text-gray-400 dark:text-gray-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</UDashboardPanel>
|
||||||
|
</UDashboardPage>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="flex-1 flex-col hidden lg:flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<UIcon
|
||||||
|
name="i-heroicons-inbox"
|
||||||
|
class="w-32 h-32 text-gray-400 dark:text-gray-500"
|
||||||
|
/>
|
||||||
|
<span class="font-bold text-2xl">Kein E-Mail Account verfügbar</span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <UInput
|
||||||
|
placeholder="Empfänger"
|
||||||
|
variant="ghost"
|
||||||
|
class="m-2"
|
||||||
|
/>
|
||||||
|
<UInput
|
||||||
|
placeholder="Betreff"
|
||||||
|
variant="ghost"
|
||||||
|
class="m-2"
|
||||||
|
/>
|
||||||
|
<UInput
|
||||||
|
placeholder="CC"
|
||||||
|
variant="ghost"
|
||||||
|
class="m-2"
|
||||||
|
/>
|
||||||
|
<UInput
|
||||||
|
placeholder="BCC"
|
||||||
|
variant="ghost"
|
||||||
|
class="m-2"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UDivider
|
||||||
|
class="my-5"
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<Tiptap/>-->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
279
pages/email/new.vue
Normal file
279
pages/email/new.vue
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
<script setup>
|
||||||
|
|
||||||
|
const supabase = useSupabaseClient()
|
||||||
|
const dataStore = useDataStore()
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
const emailData = ref({
|
||||||
|
to:"",
|
||||||
|
cc:"",
|
||||||
|
bcc: "",
|
||||||
|
subject: "",
|
||||||
|
html: "",
|
||||||
|
text: "",
|
||||||
|
account: "",
|
||||||
|
})
|
||||||
|
|
||||||
|
const emailAccounts = ref([])
|
||||||
|
const preloadedContent = ref("")
|
||||||
|
const loadedDocuments = ref([])
|
||||||
|
const loaded = ref(false)
|
||||||
|
const setupPage = async () => {
|
||||||
|
emailAccounts.value = await useSupabaseSelect("emailAccounts")
|
||||||
|
emailData.value.account = emailAccounts.value[0].id
|
||||||
|
|
||||||
|
preloadedContent.value = `<p></p><p></p><p></p>${dataStore.activeProfile.emailSignature}`
|
||||||
|
|
||||||
|
//Check Query
|
||||||
|
if(route.query.to) emailData.value.to = route.query.to
|
||||||
|
if(route.query.cc) emailData.value.to = route.query.cc
|
||||||
|
if(route.query.bcc) emailData.value.to = route.query.bcc
|
||||||
|
if(route.query.subject) emailData.value.to = route.query.subject
|
||||||
|
|
||||||
|
|
||||||
|
if(route.query.loadDocuments) {
|
||||||
|
console.log(JSON.parse(route.query.loadDocuments))
|
||||||
|
const {data,error} = await supabase.from("documents").select('*, createdDocument(id,documentNumber,title,contact(email))').in('id',JSON.parse(route.query.loadDocuments))
|
||||||
|
|
||||||
|
if(error) console.log(error)
|
||||||
|
if(data) loadedDocuments.value = data
|
||||||
|
|
||||||
|
console.log(loadedDocuments.value)
|
||||||
|
|
||||||
|
if(loadedDocuments.value.length > 0) {
|
||||||
|
emailData.value.subject = loadedDocuments.value[0].createdDocument.title
|
||||||
|
emailData.value.to = loadedDocuments.value[0].createdDocument.contact.email
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
loaded.value = true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
setupPage()
|
||||||
|
|
||||||
|
const contentChanged = (content) => {
|
||||||
|
emailData.value.html = content.html
|
||||||
|
emailData.value.text = content.text
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedAttachments = ref([])
|
||||||
|
const renderAttachments = () => {
|
||||||
|
selectedAttachments.value = Array.from(document.getElementById("inputAttachments").files).map(i => {
|
||||||
|
return {
|
||||||
|
filename: i.name,
|
||||||
|
type: i.type
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const toBase64 = file => new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
reader.onload = () => resolve(reader.result.split(",")[1]);
|
||||||
|
reader.onerror = reject;
|
||||||
|
});
|
||||||
|
|
||||||
|
function blobToBase64(blob) {
|
||||||
|
return new Promise((resolve, _) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onloadend = () => resolve(reader.result.split(",")[1]);
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendEmail = async () => {
|
||||||
|
loaded.value = false
|
||||||
|
|
||||||
|
let body = {
|
||||||
|
...emailData.value,
|
||||||
|
attachments: []
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for await (const file of Array.from(document.getElementById("inputAttachments").files)) {
|
||||||
|
body.attachments.push({
|
||||||
|
filename: file.name,
|
||||||
|
content: await toBase64(file),
|
||||||
|
contentType: file.type,
|
||||||
|
encoding: "base64",
|
||||||
|
contentDisposition: "attachment"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for await (const doc of loadedDocuments.value) {
|
||||||
|
|
||||||
|
const {data,error} = await supabase.storage.from("files").download(doc.path)
|
||||||
|
|
||||||
|
body.attachments.push({
|
||||||
|
filename: doc.path.split("/")[doc.path.split("/").length -1],
|
||||||
|
content: await blobToBase64(data),
|
||||||
|
contentType: data.type,
|
||||||
|
encoding: "base64",
|
||||||
|
contentDisposition: "attachment"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, error } = await supabase.functions.invoke('send_email', {
|
||||||
|
body
|
||||||
|
})
|
||||||
|
if(error) {
|
||||||
|
toast.add({title: "Fehler beim Absenden der E-Mail", color: "rose"})
|
||||||
|
|
||||||
|
} else if(data) {
|
||||||
|
router.push("/")
|
||||||
|
toast.add({title: "E-Mail zum Senden eingereiht"})
|
||||||
|
}
|
||||||
|
|
||||||
|
loaded.value = true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UProgress animation="carousel" v-if="!loaded" class="mt-5 w-2/3 mx-auto"/>
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
|
<UDashboardNavbar
|
||||||
|
title="Neue E-Mail"
|
||||||
|
>
|
||||||
|
<template #right>
|
||||||
|
<UButton
|
||||||
|
@click="sendEmail"
|
||||||
|
:disabled="!emailData.to || !emailData.subject"
|
||||||
|
>
|
||||||
|
Senden
|
||||||
|
</UButton>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</UDashboardNavbar>
|
||||||
|
<UDashboardToolbar>
|
||||||
|
<div class="flex-col flex w-full">
|
||||||
|
<UFormGroup
|
||||||
|
label="Absender"
|
||||||
|
>
|
||||||
|
<USelectMenu
|
||||||
|
:options="emailAccounts"
|
||||||
|
option-attribute="emailAddress"
|
||||||
|
value-attribute="id"
|
||||||
|
v-model="emailData.account"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UInput
|
||||||
|
class="w-full my-1"
|
||||||
|
placeholder="Empfänger"
|
||||||
|
variant="ghost"
|
||||||
|
v-model="emailData.to"
|
||||||
|
/>
|
||||||
|
<UInput
|
||||||
|
class="w-full my-1"
|
||||||
|
placeholder="Kopie"
|
||||||
|
variant="ghost"
|
||||||
|
v-model="emailData.cc"
|
||||||
|
/>
|
||||||
|
<UInput
|
||||||
|
class="w-full my-1"
|
||||||
|
placeholder="Blindkopie"
|
||||||
|
variant="ghost"
|
||||||
|
v-model="emailData.bcc"
|
||||||
|
/>
|
||||||
|
<UInput
|
||||||
|
placeholder="Betreff"
|
||||||
|
class="w-full my-1"
|
||||||
|
variant="ghost"
|
||||||
|
v-model="emailData.subject"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</UDashboardToolbar>
|
||||||
|
|
||||||
|
<UDashboardPanelContent>
|
||||||
|
<div id="parentAttachments" class="flex flex-col justify-center">
|
||||||
|
<span class="font-medium mb-2 text-xl">Anhänge</span>
|
||||||
|
<!-- <UIcon
|
||||||
|
name="i-heroicons-paper-clip"
|
||||||
|
class="mx-auto w-10 h-10"
|
||||||
|
/>
|
||||||
|
<span class="text-center text-2xl">Anhänge hochladen</span>-->
|
||||||
|
<input
|
||||||
|
id="inputAttachments"
|
||||||
|
type="file"
|
||||||
|
multiple
|
||||||
|
@change="renderAttachments"
|
||||||
|
/>
|
||||||
|
<ul class="mx-5 mt-3">
|
||||||
|
<li
|
||||||
|
class="list-disc"
|
||||||
|
v-for="file in selectedAttachments"
|
||||||
|
> Datei - {{file.filename}}</li>
|
||||||
|
<li
|
||||||
|
class="list-disc"
|
||||||
|
v-for="doc in loadedDocuments"
|
||||||
|
>
|
||||||
|
<span v-if="doc.createdDocument">Dokument - {{doc.createdDocument.documentNumber}}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Tiptap
|
||||||
|
@updateContent="contentChanged"
|
||||||
|
:preloadedContent="preloadedContent"
|
||||||
|
/>
|
||||||
|
</UDashboardPanelContent>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
#parentAttachments {
|
||||||
|
border: 1px dashed #69c350;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#inputAttachments {
|
||||||
|
/*
|
||||||
|
display: none;
|
||||||
|
*/
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 100;/*
|
||||||
|
width: 100%;
|
||||||
|
height: 5%;
|
||||||
|
position: relative;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#inputAttachments::file-selector-button {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #69c350;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 5px 10px 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileListItem {
|
||||||
|
border: 1px solid #69c350;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user