This commit is contained in:
2023-12-02 13:09:23 +01:00
parent 098bc97fa4
commit 45da05c9a4
46 changed files with 2889 additions and 5793 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
.idea
/test/csvparser/data.csv
/test/imaps/.env
/test/csvparser/node_modules/

View File

@@ -23,6 +23,14 @@ services:
- "traefik.http.routers.spaces-frontend-secure.entrypoints=web-secured" #
- "traefik.http.routers.spaces-frontend-secure.tls.certresolver=mytlschallenge"
imapsync:
image: registry.gitlab.com/cmykmedia/spaces:main-IMAPSYNC
restart: always
environment:
SUPABASE_URL: "https://uwppvcxflrcsibuzsbil.supabase.co"
SUPABASE_KEY: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InV3cHB2Y3hmbHJjc2lidXpzYmlsIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTcwMDkzODE5NCwiZXhwIjoyMDE2NTE0MTk0fQ.6hOkD1J8XBkVJUm-swv0ngLQ74xrEYr28EEbo0rUrts"
INTERVAL: 30
# backend:
# image: registry.gitlab.com/cmykmedia/manordsonne:master-BACKEND
# restart: always

1
spaces/.npmrc Normal file
View File

@@ -0,0 +1 @@
@bryntum:registry=https://npm.bryntum.com

View File

@@ -1,10 +1,99 @@
<script setup>
const user = useSupabaseUser()
const router = useRouter()
const route = useRoute()
const supabase = useSupabaseClient()
const tenants = (await supabase.from("tenants").select()).data
const dataStore = useDataStore()
const {loaded} = storeToRefs(useDataStore())
const {fetchData} = dataStore
fetchData()
const navLinks = [
{
label: "Home",
link: "",
icon: 'i-heroicons-home'
},
{
label: "Aufgaben",
link: "tasks",
icon: "i-heroicons-rectangle-stack"
},
{
label: "Kunden",
link: "customers",
icon: "i-heroicons-user-group"
},
{
label: "Projekte",
link: "projects",
icon: "i-heroicons-clipboard-document-check"
},
{
label: "Zeiterfassung",
link: "timetracking",
icon: "i-heroicons-clock"
},
{
label: "Artikel",
link: "products"
},
{
label: "Dokumente",
link: "documents",
icon: "i-heroicons-document"
},
{
label: "Inventar",
link: "inventory"
}
]
const linksForBreadcrumbs = ref([])
const generateLinks = () => {
let pathSteps = route.fullPath.split("/")
let returnArr = []
pathSteps.forEach((step,index) => {
let stepLink = navLinks.find(link => link.link == step)
if(stepLink) {
returnArr[index] = {
label: stepLink.label,
icon: stepLink.icon,
to: '/' + stepLink.link
}
}
linksForBreadcrumbs.value = returnArr
})
}
watch(
() => route.path,
() => {
generateLinks()
}
)
generateLinks()
const userTenant = ref({})
if(user) userTenant.value = tenants.find(tenant => tenant.id === user.value.app_metadata.tenant)
const userDropdownItems = [
@@ -33,20 +122,39 @@ const userDropdownItems = [
<UCard id="page">
<template #header>
<div id="menu">
<router-link to="/tasks" class="mr-2"><UButton>Aufgaben</UButton></router-link>
<router-link to="/customers" class="mr-2"><UButton>Kunden</UButton></router-link>
<router-link
v-for="link in navLinks"
:to="'/' + link.link"
class="mr-2"
>
<UButton>{{link.label}}</UButton>
</router-link>
<!--<router-link to="/customers" class="mr-2"><UButton>Kunden</UButton></router-link>
<router-link to="/projects" class="mr-2"><UButton>Projekte</UButton></router-link>
-
<router-link to="/vendorinvoices" class="mr-2"><UButton>Eingangsrechnungen</UButton></router-link>
<router-link to="/timetracking" class="mr-2"><UButton>Zeiterfassung</UButton></router-link>
<router-link to="/products" class="mr-2"><UButton>Artikel</UButton></router-link>
<router-link to="/documents" class="mr-2"><UButton>Dokumente</UButton></router-link>
<router-link to="/inventory" class="mr-2"><UButton>Inventar</UButton></router-link>
<router-link to="/inventory" class="mr-2"><UButton>Inventar</UButton></router-link>-->
<UDropdown :items="userDropdownItems" :popper="{placement: 'bottom-start'}">
<UButton color="white" label="Benutzer" trailing-icon="i-heroicons-chevron-down-20-solid" />
</UDropdown>
</div>
<UBreadcrumb
class="my-3"
:links="linksForBreadcrumbs"
/>
</template>
<NuxtPage/>
<NuxtPage
v-if="loaded"
/>
<div
v-else
>
<UProgress animation="carousel" />
</div>
</UCard>
</template>

View File

@@ -1,6 +1,9 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
devtools: { enabled: true },
imports: {
dirs: ['stores']
},
modules: [
'@pinia/nuxt',
'@nuxt/ui',

7318
spaces/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -17,21 +17,17 @@
"vue-router": "^4.2.5"
},
"dependencies": {
"@huntersofbook/naive-ui-nuxt": "^1.2.0",
"@nuxt/ui": "^2.10.0",
"@nuxt/ui": "^2.11.0",
"@nuxtjs/strapi": "^1.9.3",
"@pinia/nuxt": "^0.5.1",
"@tato30/vue-pdf": "^1.8.1",
"@vicons/ionicons5": "^0.12.0",
"axios": "^1.6.2",
"buffer": "^6.0.3",
"csv-parser": "^3.0.0",
"jsprintmanager": "^6.0.3",
"neat-csv": "^7.0.0",
"nuxt-editorjs": "^1.0.4",
"papaparse": "^5.4.1",
"pinia": "^2.1.7",
"uuidv4": "^6.2.13",
"vue-pdf-embed": "^1.2.1"
"sass": "^1.69.5",
"uuidv4": "^6.2.13"
}
}

View File

@@ -0,0 +1,18 @@
<script setup>
const route = useRoute()
const supabase = useSupabaseClient()
console.log(route.params.account)
const accounts = (await supabase.from("bankAccounts").select()).data
</script>
<template>
<div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,103 @@
<script setup>
import Papa from 'papaparse'
const supabase = useSupabaseClient()
const accounts = (supabase.from("bankAccounts").select()).data
let items = ref([])
async function readSingleFile(evt) {
var f = document.getElementById('fileInput').files[0];
if (f) {
let results = []
let text = await f.text()
results = Papa.parse(text).data
console.log(results[0])
results = results.slice(1)
results = results.map(temp => {
return {
iban: temp[1],
bank: temp[3],
date: temp[4],
partnerName: temp[6],
partnerIban: temp[7],
type: temp[9],
text: temp[10],
value: temp[11],
currency: temp[12]
}
})
console.log(results)
items.value = results
parseStatements()
} else {
alert("Failed to load file");
}
}
const newStatements = ref([])
const parseStatements = async () => {
items.value.forEach(item => {
if(item.date) {
let returnObj = {
date: item.date,
partnerIban: item.partnerIban,
partnerName: item.partnerName,
text: item.text,
value: Number(item.value.replace(",",".")),
}
newStatements.value.push(returnObj)
}
})
const {data,error} = await supabase
.from("bankStatements")
.insert(newStatements.value)
.select()
console.log(data)
console.log(error)
}
</script>
<template>
<div>
<UInput
type="file"
id="fileInput"
/>
<UButton
@click="readSingleFile"
>Test</UButton>
<table>
<tr
v-for="temp in items"
>
<td>{{temp.date}}</td>
<td>{{temp.partnerName}}</td>
<td>{{temp.text}}</td>
<td>{{temp.value}}</td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,28 @@
<script setup>
/*import '@bryntum/scheduler'
import { BryntumScheduler } from '@bryntum/scheduler-vue-3'
import '@bryntum/scheduler/scheduler.material.css'
const config = reactive({
startDate: new Date(2024,0,1,6),
endDate: new Date(2024,0,1,20),
viewPreset: "hourAndDay",
rowHeight: 50,
columns: [{text: "Name", field: "name", width: 130}]
})*/
</script>
<template>
<div>
<!-- <bryntum-scheduler
:width="800"
:height="600"
start-date="2023-04-16"
end-date="2023-05-15"
></bryntum-scheduler>-->
</div>
</template>
<style scoped>
</style>

View File

@@ -1,5 +1,6 @@
<template>
<div id="main">
<!-- TODO: Kontakte erstellen und dem Kunden zuweisen -->
<div id="left">
<UButton @click="showCreateCustomer = true">+ Kunde</UButton>
@@ -57,7 +58,7 @@
<!-- TODO: Scrollcontainer -->
</UCard>
</div>
</div>
@@ -70,11 +71,7 @@ definePageMeta({
const supabase = useSupabaseClient()
//const {find,create} = useStrapi4()
//const customers = (await find('customers',{populate: "*"})).data
const customers = (await supabase.from("customers").select().order('customerNumber', {ascending: true})).data
const {customers } = storeToRefs(useDataStore())
let showCreateCustomer = ref(false)

View File

@@ -68,72 +68,37 @@
v-model="showDocumentModal"
fullscreen
>
<UCard>
<!-- <a
v-if="selectedDocument"
target="_blank"
:href="`http://localhost:1337${selectedDocument.file.data.url}`"
class="p-2"
>
Anzeigen
</a>-->
{{selectedDocument}}
<UCard class="h-full">
<embed
:src="pdfSource"
class="bigPreview mb-3"
:src="selectedDocument.url"
/>
<UBadge
v-for="tag in selectedDocument.tags"
>
{{tag}}
</UBadge>
<!-- <UFormGroup
label="Tags:"
<UFormGroup
label="Ordner ändern:"
>
<USelectMenu
v-if="selectedDocument"
multiple
searchable
searchable-placeholder="Suchen..."
:options="tags"
v-on:change="update('documents',selectedDocument.id, {tags: selectedDocument.tags})"
v-model="selectedDocument.tags"
:options="folders"
v-on:change="changeFolder"
v-model="newFolder"
value-attribute="label"
/>
</UFormGroup>
<UFormGroup
label="Status:"
class="mb-3"
>
<USelectMenu
:options="states"
v-model="selectedDocument.state"
v-on:change="update('documents',selectedDocument.id,{state: selectedDocument.state})"
/>
</UFormGroup>-->
<div>
<VuePDF
ref="vuePDFRef"
:pdf="pdf"
fit-parent
:page="page"
/>
</div>
<div class="mt-3">
<UButton @click="page = page > 1 ? page - 1 : page">
Prev
</UButton>
<span class="mx-3">{{ page }} / {{ pages }}</span>
<UButton @click="page = page < pages ? page + 1 : page">
Next
</UButton>
</div>
</UCard>
</USlideover>
<!-- TODO: Tab Height always Full -->
<UTabs
:items="folders"
orientation="vertical"
@@ -143,8 +108,8 @@
<template #item="{item}">
<div class="documentList">
<div
v-if="documentsComposed.filter(doc => doc.folder === item.label).length > 0"
v-for="document in documentsComposed.filter(doc => doc.folder === item.label)"
v-if="documents.filter(doc => doc.folder === item.label).length > 0"
v-for="document in documents.filter(doc => doc.folder === item.label)"
class="documentListItem"
>
<embed
@@ -187,25 +152,14 @@
</template>
<script setup>
definePageMeta({
middleware: "auth"
})
const supabase = useSupabaseClient()
const user = useSupabaseUser()
const documents = (await supabase.from("documents").select()).data
const documentsComposed = ref([])
async function composeDocs () {
for(const doc of documents){
let fileurl = (await supabase.storage.from('documents').createSignedUrl(doc.path,60*60)).data.signedUrl
documentsComposed.value.push({...doc, url: fileurl})
}
}
await composeDocs()
const {documents} = storeToRefs(useDataStore())
const tabOpen = ref(0)
const uploadModalOpen = ref(false)
@@ -271,10 +225,28 @@ const uploadFile = async () => {
uploadModalOpen.value = false;
}
const updateDocument = async () => {
await update('documents', selectedDocument.id, {tags: selectedDocument.tags, state: selectedDocument.state})
const changeFolder = async () => {
console.log("Change Folder")
console.log(selectedDocument.value.path)
let filename = selectedDocument.value.path.split("/")[2]
let newPath = `${user.value.app_metadata.tenant}/${newFolder.value}/${filename}`
console.log(newPath)
const { data, error } = await supabase
.storage
.from('documents')
.move(selectedDocument.value.path, newPath )
console.log(data)
console.log(error)
}
const newFolder = ref("")
const selectedDocument = ref({})
const showDocumentModal = ref(false)
@@ -290,12 +262,13 @@ const openDocument = async (document) => {
.documentList {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.documentListItem {
display:block;
width: 15vw;
height: 25vh;
height: 30vh;
padding:1em;
margin: 0.7em;
border: 1px solid lightgrey;
@@ -309,7 +282,7 @@ const openDocument = async (document) => {
.previewEmbed {
width: 100%;
height: 18vh;
height: 22vh;
overflow: hidden;
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
@@ -318,4 +291,9 @@ const openDocument = async (document) => {
.previewEmbed::-webkit-scrollbar {
display: none;
}
.bigPreview {
height: 70vh;
width: 100%;
}
</style>

View File

@@ -0,0 +1,88 @@
<script setup>
const route = useRoute()
const router = useRouter()
const supabase = useSupabaseClient()
const currentSubmission = (await supabase.from("formSubmits").select().eq('id',route.params.id)).data[0]
const form = (await supabase.from("forms").select().eq('id',currentSubmission.formType)).data[0]
const formData = ref({})
const submitted = ref(currentSubmission.submitted)
const submitForm = async () => {
submitted.value = true
console.log(formData.value)
const {data,error} = await supabase
.from("formSubmits")
.update({values: formData.value, submitted: true})
.eq('id',currentSubmission.id)
.select()
if(error) {
console.log(error)
} else if( data) {
formData.value = {}
}
}
</script>
<template>
<div>
<UForm
v-if="!submitted"
@submit="submitForm"
@reset="formData = {}"
>
<div
v-for="item in form.fields"
>
<p v-if="item.type === 'header'">{{item.label}}</p>
<UFormGroup
v-else-if="item.type.includes('Input')"
:label="item.required ? item.label + '*' : item.label"
>
<UInput
v-if="item.type === 'textInput'"
v-model="formData[item.key]"
:required="item.required"
/>
<UInput
v-else-if="item.type === 'numberInput'"
v-model="formData[item.key]"
:required="item.required"
type="number"
inputmode="numeric"
/>
</UFormGroup>
</div>
<UButton type="submit">
Abschicken
</UButton>
<UButton
type="reset"
color="rose"
class="m-2"
>
Zurücksetzen
</UButton>
</UForm>
<div v-else>
Dieses Formular wurde bereits abgeschickt. Möchten Sie erneut Daten abschicken, sprechen Sie bitte Ihren Ansprechpartner an, um das Formular freizuschalten.
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -1,5 +1,9 @@
<template>
<div>
Offene Aufgaben: {{openTasks}}<br>
Laufende Projekte:
</div>
</template>
<script setup>
@@ -7,15 +11,13 @@ definePageMeta({
middleware: "auth"
})
const {getOpenTasksCount} = useDataStore()
const openTasks = getOpenTasksCount
const supabase = useSupabaseClient()
const tasks = (await supabase.from("tasks").select()).data
const user = useSupabaseUser()
console.log(user)
console.log(tasks)
</script>
<style scoped>

View File

@@ -66,21 +66,17 @@ function checkSpaceId(spaceId) {
<div id="main">
<router-link to="/inventory/spaces"><UButton>Lagerplätze</UButton></router-link>
<!--<UInput
icon="i-heroicons-magnifying-glass-20-solid"
variant="outline"
color="primary"
placeholder="Barcode / Suche"
v-model="searchinput"
/>-->
<UButton @click="mode = 'incoming'" class="ml-3" >Wareneingang</UButton>
<UButton @click="mode = 'outgoing'" class="ml-1">Warenausgang</UButton>
<UButton @click="mode = 'change'" class="ml-1" disabled>Umlagern</UButton>
<div class="my-3">
<UButton @click="mode = 'incoming'" class="ml-3" >Wareneingang</UButton>
<UButton @click="mode = 'outgoing'" class="ml-1">Warenausgang</UButton>
<UButton @click="mode = 'change'" class="ml-1" disabled>Umlagern</UButton>
</div>
<UFormGroup
label="Artikel:"
class="mt-3"
class="mt-3 w-80"
>
<UInput
variant="outline"
@@ -92,7 +88,7 @@ function checkSpaceId(spaceId) {
<UFormGroup
label="Lagerplatz:"
class="mt-3"
class="mt-3 w-80"
><!--.map(space => {return {id: space.id, name: space.spaceNumber}}-->
<USelectMenu
:options="spaces"
@@ -106,7 +102,7 @@ function checkSpaceId(spaceId) {
<UFormGroup
label="Anzahl:"
class="mt-3"
class="mt-3 w-80"
>
<UInput
variant="outline"
@@ -132,8 +128,9 @@ function checkSpaceId(spaceId) {
<style scoped>
#main {
/*display: flex;
flex-direction: row;*/
display: flex;
flex-direction: column;
align-items: center;
}
#left {
width: 25vw;

View File

@@ -1,5 +1,4 @@
<script setup>
import {move} from "@antfu/utils";
definePageMeta({
middleware: "auth"
@@ -7,23 +6,19 @@ definePageMeta({
const supabase = useSupabaseClient()
const spaces = (await supabase.from("spaces").select()).data
const movements = (await supabase.from("movements").select()).data
const products = (await supabase.from("products").select()).data
const units = (await supabase.from("units").select()).data
const {spaces,movements,products,units} = storeToRefs(useDataStore())
const {movementsBySpace, getProductById} = useDataStore()
console.log(movements)
let selectedItem = ref({})
const showCreateSpace = ref(false)
const selectItem = (item) => {
selectedItem.value = item
spaceMovements.value = movements.filter(movement => movement.spaceId === selectedItem.value.id)
spaceMovements.value = movementsBySpace(item.id)//movements.filter(movement => movement.spaceId === selectedItem.value.id)
spaceProducts.value = []
spaceMovements.value.forEach(movement => {
if(spaceProducts.value.filter(product => product.id === movement.productId).length === 0) spaceProducts.value.push(products.find(product => product.id === movement.productId))
if(spaceProducts.value.filter(product => product.id === movement.productId).length === 0) spaceProducts.value.push(getProductById(movement.productId))
})
@@ -31,7 +26,7 @@ const selectItem = (item) => {
}
const spaceTypes = ["Regalplatz", "Kiste", "Palettenplatz"]
const spaceTypes = ["Regalplatz", "Kiste", "Palettenplatz","KFZ"]
const createSpaceData = ref({})
const createSpace = async () => {
@@ -112,11 +107,23 @@ function getSpaceProductCount(productId) {
</template>
{{selectedItem.description}}
</UCard>
<div v-if="spaceProducts.length > 0">
<p class="mt-5">Artikel in diesem Lagerplatz</p>
<p v-for="product in spaceProducts">{{product.name}} - {{getSpaceProductCount(product.id)}} {{units.find(unit => unit.id === product.unit).name}}</p>
<table>
<tr>
<th>Artikel</th>
<th>Anzahl</th>
<th>Einheit</th>
</tr>
<tr v-for="product in spaceProducts">
<td>{{product.name}}</td>
<td>{{getSpaceProductCount(product.id)}}</td>
<td>{{units.find(unit => unit.id === product.unit).name}}</td>
</tr>
</table>
</div>
</div>
</div>
@@ -138,4 +145,9 @@ function getSpaceProductCount(productId) {
width: 60vw;
padding-left: 3vw;
}
td, th {
padding: 1em;
border: 1px solid black;
}
</style>

View File

@@ -13,7 +13,8 @@ const onSubmit = async () => {
password: password.value
})
if(error) {
console.log(error)
console.log(error.toString())
alert(error.toString())
} else {
console.log("Login Successful")
router.push("/")

View File

@@ -127,13 +127,11 @@ definePageMeta({
const supabase = useSupabaseClient()
const products = (await supabase.from("products").select()).data
const units = (await supabase.from("units").select()).data
const {products,units} = storeToRefs(useDataStore())
const showCreateProduct = ref(false)
const createProductData = ref({})
let selectedItem = ref({})
const history = ref([

View File

@@ -3,9 +3,48 @@ definePageMeta({
middleware: "auth"
})
//const {find, findOne,create, update} = useStrapi4()
const supabase = useSupabaseClient()
const route = useRoute()
//let project = (await findOne('projects',route.params.id)).data
const {getProjectById, getFormSubmitsWithLabelProp} = useDataStore()
const {forms, formSubmits} = storeToRefs(useDataStore())
const currentProject = getProjectById(Number(route.params.id))
const formSubmissionsComposed = getFormSubmitsWithLabelProp
const formModalOpen = ref(false)
const newFormSubmissionData = ref({
formType: "",
values: {},
submitted: false
})
const addNewFormSubmission = async () => {
//Add Form Submission
const {data:insertData,error:insertError} = await supabase
.from("formSubmits")
.insert([newFormSubmissionData.value])
.select()
console.log(insertData)
console.log(insertError)
let projectForms = [...currentProject.forms, insertData[0].id ]
console.log(projectForms)
//Add Form ID to project.forms
const {data:updateData,error:updateError} = await supabase
.from("projects")
.update({forms: projectForms})
.eq('id',currentProject.id)
.select()
console.log(updateData)
console.log(updateError)
}
const tabItems = [
{
key: "phases",
@@ -25,6 +64,8 @@ const selectedPhase = ref({})
const changesSaved = ref(true)
const default_data = {
time: 1660335428612,
blocks: [
@@ -43,87 +84,25 @@ const default_data = {
text: "This is a nuxt3 plugin for editorjs.",
},
},
{
id: "R3o5BpI-r9",
type: "paragraph",
data: {
text: "<b>A paragraph of text:</b>&nbsp;Lorem ipsum dolor sit amet consectetur adipisicing elit. Labore perspiciatis molestias neque autem cumque provident? Laudantium ad, quisquam quos nulla amet, perferendis recusandae voluptates eligendi cupiditate consectetur veniam! Ipsum, ullam?",
},
},
{
id: "b9mkw6ZO92",
type: "header",
data: {
text: "Heading 1",
level: 1,
},
},
{
id: "P2PZsHo2lq",
type: "header",
data: {
text: "Heading 2",
level: 2,
},
},
{
id: "hHJZjkW-TO",
type: "header",
data: {
text: "Heading 3",
level: 3,
},
},
{
id: "k8EDwa0oVG",
type: "header",
data: {
text: "Heading 4",
level: 4,
},
},
{
id: "62ciFnEFjZ",
type: "header",
data: {
text: "Heading 5",
level: 5,
},
},
{
id: "YCBcKhNqib",
type: "header",
data: {
text: "Heading 6",
level: 6,
},
},
{
id: "s_J3d5U8DA",
type: "list",
data: {
style: "ordered",
items: [
"An ordered list item",
"Another ordered list item",
"One more",
],
},
},
{
id: "kMyQbO156y",
type: "list",
data: {
style: "unordered",
items: ["An unordered list item!", "In italics?", "Or bold?"],
},
},
],
version: "2.25.0",
};
const dat = ref(default_data);
const save = () => {
console.log(foo);
const newProjectDescription = ref(currentProject.description || default_data.value);
const saveProjectDescription = async () => {
//Update Project Description
const {data:updateData,error:updateError} = await supabase
.from("projects")
.update({description: newProjectDescription.value})
.eq('id',currentProject.id)
.select()
console.log(updateData)
console.log(updateError)
};
@@ -182,12 +161,68 @@ const phaseInfo = ref({
</div>
</div>
<div v-else-if="item.key === 'password'" class="space-y-3">
<div v-else-if="item.key === 'forms'" class="space-y-3">
<UButton
@click="formModalOpen = true"
>
+ Formular
</UButton>
<UModal
v-model="formModalOpen"
>
<UCard>
<template #header>
Formular hinzufügen
</template>
<UFormGroup>
<USelectMenu
:options="forms"
option-attribute="name"
value-attribute="id"
v-model="newFormSubmissionData.formType"
/>
</UFormGroup>
<template #footer>
<UButton
@click="addNewFormSubmission"
>
Hinzufügen
</UButton>
</template>
</UCard>
</UModal>
<UAccordion :items="formSubmissionsComposed">
<template #item="{item}">
<p class="my-3">Formular Link: <a :href="'https://app.spaces.software/formSubmissions/' + item.id">{{'https://app.spaces.software/formSubmissions/' + item.id}}</a></p>
<div v-if="Object.keys(item.values).length == 0">
<p>Es wurden noch keine Daten über das Formular abgegeben</p>
</div>
<table v-else>
<tr v-for="key in Object.keys(item.values)">
<td>{{key}}</td>
<td>{{ item.values[key] }}</td>
</tr>
</table>
</template>
</UAccordion>
</div>
<div v-else-if="item.key === 'description'" class="space-y-3">
<client-only><editor-js v-model="dat" /></client-only>
<UButton
:disabled="false/*newProjectDescription.time === currentProject.description.time*/"
@click="saveProjectDescription"
>
Speichern
</UButton>
<client-only><editor-js v-model="newProjectDescription" /></client-only>
</div>
{{item}}
</template>
</UTabs>
<!-- <div id="left">

View File

@@ -1,7 +1,7 @@
<template>
<div id="main">
<div id="left">
<!-- TODO: Projekt Auflistung überarbeiten, Rechte Seite wird nicht genutzt -->
<UButton @click="showCreateProject = true">+ Projekt</UButton>
<UModal v-model="showCreateProject">
<UCard>
@@ -89,8 +89,11 @@ definePageMeta({
const supabase = useSupabaseClient()
const projects = (await supabase.from("projects").select()).data
const customers = (await supabase.from("customers").select()).data
const {projects,customers} = storeToRefs(useDataStore())
const {fetchProjects} = useDataStore()
//const projects = (await supabase.from("projects").select()).data
//const customers = (await supabase.from("customers").select()).data
const showCreateProject = ref(false)
const createProjectData = ref({
@@ -114,6 +117,8 @@ const createProject = async () => {
showCreateProject.value = false
createProjectData.value = {phases: []}
fetchProjects()
}

View File

@@ -1,5 +1,6 @@
<template>
<div>
<!-- TODO: Benutzer Aufgaben zuweisen und nach diesen Filtern -->
<UModal
v-model="showCreateTask"
>
@@ -112,7 +113,7 @@
<div id="taskCatList">
<div id="catNew">
<h3>Neue Aufgaben</h3>
<div class="taskScrollList">
<div class="taskScrollList" v-if="tasks.length > 0">
<a
v-for="taskNew in tasks.filter(task => task.categorie == 'Neu')"
@click="inspectTask(taskNew)"
@@ -134,7 +135,7 @@
</div>
<div id="catInProgress">
<h3>Aufgaben in Bearbeitung</h3>
<div class="taskScrollList">
<div class="taskScrollList" v-if="tasks.length > 0">
<a
v-for="taskNew in tasks.filter(task => task.categorie == 'In Bearbeitung')"
@click="inspectTask(taskNew)"
@@ -154,7 +155,7 @@
</div>
<div id="catUrgent">
<h3>Dringende Aufgaben</h3>
<div class="taskScrollList">
<div class="taskScrollList" v-if="tasks.length > 0">
<a
v-for="taskNew in tasks.filter(task => task.categorie == 'Dringend')"
@click="inspectTask(taskNew)"
@@ -184,15 +185,12 @@ definePageMeta({
const supabase = useSupabaseClient()
const tasks = (await supabase.from("tasks").select()).data
const {tasks} = storeToRefs(useDataStore())
const {fetchTasks} = useDataStore()
let refTasks = ref([])
//const users = (await find('users',{populate: "*"}))
//const users = (await supabase.from("users").select()).data
let usersForList = []
//users.forEach(user => usersForList.push(user.username))
const usersSelected = ref([])
@@ -223,6 +221,7 @@ const createTask = async () => {
showCreateTask.value = false
createTaskData.value = {}
fetchTasks()
}
const updateTask = async () => {
@@ -260,7 +259,7 @@ const finishTask = async () => {
showTaskModal.value = false
}
filterTasks()
//filterTasks()
</script>

View File

@@ -0,0 +1,121 @@
<template>
<div id="main">
<div
class="previewDoc"
>
<embed
:src="fileurl"
>
{{documents}}
</div>
<div
class="inputData"
>
<UFormGroup label="Lieferant:" required>
<UInput />
</UFormGroup>
<UFormGroup label="Rechnungsreferenz:" required>
<UInput />
</UFormGroup>
<UFormGroup label="Rechnungsdatum:" required>
<UInput />
</UFormGroup>
<UButton @click="vendorInvoiceData.lineItems.push({})">+ Reihe</UButton>
<div v-for="lineItem in vendorInvoiceData.lineItems" class="lineItemRow">
<UFormGroup label="Text:" required>
<UInput v-model="lineItem.text"/>
</UFormGroup>
<UFormGroup label="Produkt:" required>
<UInput v-model="lineItem.productId"/>
</UFormGroup>
<UFormGroup label="Projekt:" required>
<UInput v-model="lineItem.projectId"/>
</UFormGroup>
<UFormGroup label="Anzahl:" required>
<UInput v-model="lineItem.quantity"/>
</UFormGroup>
<UFormGroup label="Einheit:" required>
<UInput v-model="lineItem.unit"/>
</UFormGroup>
<UFormGroup label="Einzelpreis:" required>
<UInput v-model="lineItem.unitPriceNet"/>
</UFormGroup>
<UFormGroup label="USt:" required>
<UInput v-model="lineItem.vat"/>
</UFormGroup>
<UFormGroup label="Rabatt:" required>
<UInput v-model="lineItem.discount"/>
</UFormGroup>
<UFormGroup label="Buchungskonto:" required>
<UInput v-model="lineItem.skrAccountId"/>
</UFormGroup>
<UFormGroup label="Positionspreis:" required>
<UInput disabled/>
</UFormGroup>
</div>
{{vendorInvoiceData}}
</div>
</div>
</template>
<script setup>
const supabase = useSupabaseClient()
const route = useRoute()
const currentVendorInvoice = (await supabase.from('vendorInvoices').select().eq('id', route.params.id)).data
const document = (await supabase.from('documents').select().eq("id",18)).data[0]
console.log(currentVendorInvoice)
console.log(document)
let fileurl = (await supabase.storage.from('documents').createSignedUrl(document.path,60*60)).data.signedUrl
let vendorInvoiceData = ref({
reference: "",
date: "",
vendorId: 0,
lineItems: []
})
</script>
<style scoped>
#main {
display: flex;
flex-direction: row;
}
.previewDoc {
width: 50vw;
min-height: 80vh;
}
.previewDoc embed {
width: 90%;
height: 100%;
}
.inputData {
}
.lineItemRow {
display: flex;
flex-direction: row;
}
</style>

View File

@@ -1,72 +0,0 @@
<template>
<div id="main">
<UFormGroup label="Lieferant:" required>
<UInput />
</UFormGroup>
<UFormGroup label="Rechnungsreferenz:" required>
<UInput />
</UFormGroup>
<UFormGroup label="Rechnungsdatum:" required>
<UInput />
</UFormGroup>
<UButton @click="vendorInvoiceData.lineItems.push({})">+ Reihe</UButton>
<div v-for="lineItem in vendorInvoiceData.lineItems" class="lineItemRow">
<UFormGroup label="Text:" required>
<UInput v-model="lineItem.text"/>
</UFormGroup>
<UFormGroup label="Produkt:" required>
<UInput v-model="lineItem.productId"/>
</UFormGroup>
<UFormGroup label="Projekt:" required>
<UInput v-model="lineItem.projectId"/>
</UFormGroup>
<UFormGroup label="Anzahl:" required>
<UInput v-model="lineItem.quantity"/>
</UFormGroup>
<UFormGroup label="Einheit:" required>
<UInput v-model="lineItem.unit"/>
</UFormGroup>
<UFormGroup label="Einzelpreis:" required>
<UInput v-model="lineItem.unitPriceNet"/>
</UFormGroup>
<UFormGroup label="USt:" required>
<UInput v-model="lineItem.vat"/>
</UFormGroup>
<UFormGroup label="Rabatt:" required>
<UInput v-model="lineItem.discount"/>
</UFormGroup>
<UFormGroup label="Buchungskonto:" required>
<UInput v-model="lineItem.skrAccountId"/>
</UFormGroup>
<UFormGroup label="Positionspreis:" required>
<UInput disabled/>
</UFormGroup>
</div>
{{vendorInvoiceData}}
</div>
</template>
<script setup>
let vendorInvoiceData = ref({
reference: "",
date: "",
vendorId: 0,
lineItems: []
})
</script>
<style scoped>
.lineItemRow {
display: flex;
flex-direction: row;
}
</style>

View File

@@ -32,7 +32,7 @@
<script setup>
import {useDataStore} from "../../store/data";
import {useDataStore} from "~/stores/data.ts";
import {storeToRefs} from "pinia"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

16
spaces/public/grid.stockholm.min.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,236 +0,0 @@
import {defineStore} from 'pinia'
import {CustomerInfo, ProjectInfo, VendorInvoice, ProductInfo} from "@/misc/interfaces";
export const useDataStore = defineStore('dataStore', {
state: () => {
return {
customers: [
{
id: 10069,
name: "NOA Service GmbH",
addresses: [
{
type: "invoice",
street: "Oldenburger Str. ",
number: "52",
zip: 26340,
city: "Zetel"
}
],
contacts: [
{
name: "Noa",
firstname: "Stefanie",
role: "Geschäftsführerin",
email: ""
}, {
name: "Kramer",
firstname: "Lena",
role: "",
email: ""
}
]
},
{
id: 10083,
name: "Specht Reepsholt e.K.",
addresses: [
{
type: "invoice",
street: "Reepsholter Hauptstr. ",
number: "17",
zip: 26446,
city: "Reepsholt"
}
],
contacts: [
{
name: "Specht",
firstname: "Oliver",
role: "",
email: ""
}
]
},
{
id: 10023,
name: "M&K Nordsonne GmbH & Co. KG",
addresses: [
{
type: "invoice",
street: "Berghamm",
number: "1",
zip: 26434,
city: "Wangerland"
}
],
contacts: [
{
name: "Mick",
firstname: "Martin",
role: "Geschäftsführer",
email: ""
}, {
name: "Obst",
firstname: "Yvonne",
role: "",
email: ""
}
]
}
] as CustomerInfo[],
projects: [
{
id: 1001,
name: "FRIOS052PV001",
customerId: 10069,
notes: ""
},
{
id: 1002,
name: "WTMRH017PV001",
customerId: 10083,
notes: ""
},
{
id: 1003,
name: "Kamerainstallation WHVAS19",
customerId: 10069,
notes: ""
}
] as ProjectInfo[],
vendorInvoices: [
{
id: 230001,
vendorId: 10001,
date: "2023-11-01",
reference: "2023-11-01-1000",
lineItems: [
{
pos: 0,
text: "Ja Solar",
projectId: 1001,
quantity: 10,
unit: "Stk",
unitPriceNet: 120.50,
vat: 19,
skrAccountId: 3400
}
]
},
{
id: 230002,
vendorId: 10001,
date: "2023-10-29",
reference: "2023-11-01-1001",
lineItems: [
{
pos: 0,
productId: 10003,
projectId: 1003,
quantity: 3,
unit: "Stk",
unitPriceNet: 96.00,
vat: 19,
skrAccountId: 3400
}
]
}
] as VendorInvoice[],
products: [
{
id:10001,
name: "STP-10SE",
manufacturer: "SMA",
purchasePriceNet: 1500.00,
profitPercentage: 20,
retailPriceNet: 1800.00,
tags: ["pv","wechselrichter"]
},
{
id:10002,
name: "RLC-810A",
manufacturer: "Reolink",
purchasePriceNet: 80.00,
profitPercentage: 20,
retailPriceNet: 96.00,
tags: ["kamera","outdoor","lan","poe"],
optionalProductIds: [10003]
},
{
id:10003,
name: "RLC-510WA",
manufacturer: "Reolink",
purchasePriceNet: 80.00,
profitPercentage: 20,
retailPriceNet: 96.00,
tags: ["kamera","outdoor","wlan"],
optionalProductIds: [10002]
}
] as ProductInfo[],
}
},
actions: {
async getData(){
const {data} = await useFetch("/api/customers")
console.log(data)
// @ts-ignore
this.customers = data
},
addCustomer(value: CustomerInfo) {
this.customers.push(value)
},
addProject(value: ProjectInfo){
this.projects.push(value)
}
},
getters: {
customerList(state){
return state.customers
},
customersForSelect(state){
return state.customers.map((customer:any) => {return {label: customer.name, value: customer.id}})
},
getCustomerById: (state) => (id:number) => state.customers.find((customer:any) => customer.id === id),
projectList(state){
return state.projects
},
getVendorInvoiceList: (state) => state.vendorInvoices,
getVendorInvoicesByProjectId: (state) => (id:number) => {
let invoices:any[] = []
state.vendorInvoices.forEach((invoice:any) => {
//Store Current Invoice Data
let temp:any = {
vendorInvoiceId: 0,
value: 0
}
//Check if Current Invoice Contains Relevant Data
if(invoice.lineItems.filter((lineItem:any) => lineItem.projectId === id).length > 0) {
console.log("in if")
temp.vendorInvoiceId = invoice.id
invoice.lineItems.filter((lineItem:any) => lineItem.projectId === id).forEach((lineItem:any) => {
console.log(temp)
temp.value = Number(temp.value) + lineItem.quantity * lineItem.unitPriceNet
console.log(lineItem)
})
temp.value = String(temp.value.toFixed(2)) + "€"
invoices.push(temp)
}
})
return invoices
},
getProductsList: (state) => state.products
}
})

92
spaces/stores/data.ts Normal file
View File

@@ -0,0 +1,92 @@
import {defineStore} from 'pinia'
import {createClient} from '@supabase/supabase-js'
const supabase = createClient('https://uwppvcxflrcsibuzsbil.supabase.co','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InV3cHB2Y3hmbHJjc2lidXpzYmlsIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDA5MzgxOTQsImV4cCI6MjAxNjUxNDE5NH0.CkxYSQH0uLfwx9GVUlO6AYMU2FMLAxGMrwEKvyPv7Oo')
// @ts-ignore
export const useDataStore = defineStore('data', {
state: () => ({
loaded: false,
customers: [] as any[],
tasks: [] as any[],
projects: [] as any[],
documents: [] as any[],
spaces: [] as any[],
units: [] as any[],
times: [] as any[],
products: [] as any[],
movements: [] as any[],
forms: [] as any[],
formSubmits: [] as any[],
contact: [] as any[]
}),
actions: {
async fetchData() {
await this.fetchCustomers()
await this.fetchTasks()
await this.fetchForms()
await this.fetchFormSubmits()
await this.fetchProducts()
await this.fetchUnits()
await this.fetchProjects()
await this.fetchDocuments()
await this.fetchMovements()
await this.fetchSpaces()
this.loaded = true
},
async fetchCustomers() {
// @ts-ignore
this.customers = (await supabase.from("customers").select().order('customerNumber', {ascending: true})).data
},
async fetchTasks() {
// @ts-ignore
this.tasks = (await supabase.from("tasks").select()).data
},
async fetchForms() {
// @ts-ignore
this.forms = (await supabase.from("forms").select()).data
},
async fetchFormSubmits() {
// @ts-ignore
this.formSubmits = (await supabase.from("formSubmits").select()).data
},
async fetchProducts() {
// @ts-ignore
this.products = (await supabase.from("products").select()).data
},
async fetchUnits() {
// @ts-ignore
this.units = (await supabase.from("units").select()).data
},
async fetchProjects() {
// @ts-ignore
this.projects = (await supabase.from("projects").select()).data
},
async fetchSpaces() {
// @ts-ignore
this.spaces = (await supabase.from("spaces").select().order('spaceNumber', {ascending: true})).data
},
async fetchMovements() {
// @ts-ignore
this.movements = (await supabase.from("movements").select()).data
},
async fetchDocuments() {
// @ts-ignore
this.documents = (await supabase.from("documents").select()).data
for(const [index,doc] of this.documents.entries()){
// @ts-ignore
this.documents[index].url = (await supabase.storage.from('documents').createSignedUrl(doc.path, 60 * 60)).data.signedUrl
}
}
},
getters: {
getOpenTasksCount: (state) => state.tasks.filter(task => task.categorie != "Erledigt").length,
movementsBySpace: (state) => (spaceId:number) => state.movements.filter(move => move.spaceId === spaceId),
getProductById: (state) => (productId:number) => state.products.find(product => product.id === productId),
getProjectById: (state) => (projectId:number) => state.projects.find(project => project.id === projectId),
getFormSubmitsWithLabelProp: (state) => (state.formSubmits.map(submit => {return{...submit, label: submit.id}}))
}
})

View File

@@ -50,7 +50,8 @@ const uploadAttachmentToSupabase = async (tenant, folder ,file ) => {
object: storageData.id,
folder: folder,
tags: ["E-Mail Anhang"],
path: storageData.path
path: storageData.path,
tenant: tenant
})
.select()