Compare commits
2 Commits
5edc90bd4d
...
b4ec792cc0
| Author | SHA1 | Date | |
|---|---|---|---|
| b4ec792cc0 | |||
| 9b3f48defe |
235
frontend/components/Calculator.vue
Normal file
235
frontend/components/Calculator.vue
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
ref="el"
|
||||||
|
:style="style"
|
||||||
|
class="fixed z-[999] w-72 bg-white dark:bg-gray-900 shadow-2xl rounded-xl border border-gray-200 dark:border-gray-800 p-4 select-none touch-none"
|
||||||
|
>
|
||||||
|
<div class="flex items-center justify-between mb-4 cursor-move border-b pb-2 dark:border-gray-800">
|
||||||
|
<div class="flex items-center gap-2 text-gray-500">
|
||||||
|
<UIcon name="i-heroicons-calculator" />
|
||||||
|
<span class="text-xs font-bold uppercase tracking-wider">Kalkulator</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<UTooltip text="Verlauf">
|
||||||
|
<UButton
|
||||||
|
color="gray"
|
||||||
|
variant="ghost"
|
||||||
|
:icon="showHistory ? 'i-heroicons-clock-solid' : 'i-heroicons-clock'"
|
||||||
|
size="xs"
|
||||||
|
@click="showHistory = !showHistory"
|
||||||
|
/>
|
||||||
|
</UTooltip>
|
||||||
|
<UTooltip text="Schließen (Esc)">
|
||||||
|
<UButton
|
||||||
|
color="gray"
|
||||||
|
variant="ghost"
|
||||||
|
icon="i-heroicons-x-mark"
|
||||||
|
size="xs"
|
||||||
|
@click="store.isOpen = false"
|
||||||
|
/>
|
||||||
|
</UTooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="!showHistory">
|
||||||
|
<div
|
||||||
|
class="bg-gray-100 dark:bg-gray-800 p-3 rounded-lg mb-4 text-right border border-gray-200 dark:border-gray-700 cursor-pointer group relative"
|
||||||
|
@click="copyDisplay"
|
||||||
|
>
|
||||||
|
<div class="text-[10px] text-gray-500 h-4 font-mono uppercase tracking-tighter">
|
||||||
|
Speicher: {{ Number(store.memory).toFixed(2).replace('.', ',') }} €
|
||||||
|
</div>
|
||||||
|
<div class="text-2xl font-mono truncate tracking-tighter">{{ store.display }}</div>
|
||||||
|
|
||||||
|
<div class="absolute inset-0 flex items-center justify-center bg-primary-500/10 opacity-0 group-hover:opacity-100 transition-opacity rounded-lg">
|
||||||
|
<span class="text-[10px] font-bold text-primary-600 uppercase">
|
||||||
|
{{ copied ? 'Kopiert!' : 'Klicken zum Kopieren' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-4 gap-2">
|
||||||
|
<UTooltip text="Brutto (+19%)"><UButton color="green" variant="soft" block size="xs" @click="applyTax(19)">+19%</UButton></UTooltip>
|
||||||
|
<UTooltip text="Brutto (+7%)"><UButton color="green" variant="soft" block size="xs" @click="applyTax(7)">+7%</UButton></UTooltip>
|
||||||
|
<UTooltip text="Netto (-19%)"><UButton color="rose" variant="soft" block size="xs" @click="removeTax(19)">-19%</UButton></UTooltip>
|
||||||
|
<UTooltip text="Netto (-7%)"><UButton color="rose" variant="soft" block size="xs" @click="removeTax(7)">-7%</UButton></UTooltip>
|
||||||
|
|
||||||
|
<UTooltip text="Löschen"><UButton color="gray" variant="ghost" block @click="clear">C</UButton></UTooltip>
|
||||||
|
<UTooltip text="Speicher +"><UButton color="gray" variant="ghost" block @click="addToSum">M+</UButton></UTooltip>
|
||||||
|
<UTooltip text="Speicher Reset"><UButton color="gray" variant="ghost" block @click="store.memory = 0">MC</UButton></UTooltip>
|
||||||
|
<UButton color="primary" variant="soft" @click="setOperator('/')">/</UButton>
|
||||||
|
|
||||||
|
<UButton v-for="n in [7, 8, 9]" :key="n" color="white" @click="appendNumber(n)">{{ n }}</UButton>
|
||||||
|
<UButton color="primary" variant="soft" @click="setOperator('*')">×</UButton>
|
||||||
|
|
||||||
|
<UButton v-for="n in [4, 5, 6]" :key="n" color="white" @click="appendNumber(n)">{{ n }}</UButton>
|
||||||
|
<UButton color="primary" variant="soft" @click="setOperator('-')">-</UButton>
|
||||||
|
|
||||||
|
<UButton v-for="n in [1, 2, 3]" :key="n" color="white" @click="appendNumber(n)">{{ n }}</UButton>
|
||||||
|
<UButton color="primary" variant="soft" @click="setOperator('+')">+</UButton>
|
||||||
|
|
||||||
|
<UButton color="white" class="col-span-2" @click="appendNumber(0)">0</UButton>
|
||||||
|
<UButton color="white" @click="addComma">,</UButton>
|
||||||
|
<UButton color="primary" block @click="calculate">=</UButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="h-[270px] flex flex-col animate-in fade-in duration-200">
|
||||||
|
<div class="flex-1 overflow-y-auto space-y-2 pr-1 custom-scrollbar">
|
||||||
|
<div v-if="store.history.length === 0" class="text-center text-gray-400 text-xs mt-10 italic">
|
||||||
|
Keine Berechnungen im Verlauf
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="(item, i) in store.history" :key="i"
|
||||||
|
class="p-2 bg-gray-50 dark:bg-gray-800 rounded text-right border-l-2 border-primary-500 cursor-pointer hover:bg-primary-50 dark:hover:bg-primary-900/20 transition-colors"
|
||||||
|
@click="useHistoryItem(item.result)"
|
||||||
|
>
|
||||||
|
<div class="text-[10px] text-gray-400">{{ item.expression }} =</div>
|
||||||
|
<div class="text-sm font-bold">{{ item.result }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<UButton
|
||||||
|
color="gray"
|
||||||
|
variant="ghost"
|
||||||
|
size="xs"
|
||||||
|
block
|
||||||
|
class="mt-2"
|
||||||
|
icon="i-heroicons-trash"
|
||||||
|
@click="store.history = []"
|
||||||
|
>
|
||||||
|
Verlauf leeren
|
||||||
|
</UButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useDraggable, useClipboard } from '@vueuse/core'
|
||||||
|
import { useCalculatorStore } from '~/stores/calculator'
|
||||||
|
|
||||||
|
const store = useCalculatorStore()
|
||||||
|
const { copy, copied } = useClipboard()
|
||||||
|
|
||||||
|
const el = ref(null)
|
||||||
|
const { style } = useDraggable(el, {
|
||||||
|
initialValue: { x: window.innerWidth - 350, y: 150 },
|
||||||
|
})
|
||||||
|
|
||||||
|
const shouldResetDisplay = ref(false)
|
||||||
|
const showHistory = ref(false)
|
||||||
|
const previousValue = ref(null)
|
||||||
|
const lastOperator = ref(null)
|
||||||
|
|
||||||
|
// --- Logik ---
|
||||||
|
const appendNumber = (num) => {
|
||||||
|
if (store.display === '0' || shouldResetDisplay.value) {
|
||||||
|
store.display = String(num)
|
||||||
|
shouldResetDisplay.value = false
|
||||||
|
} else {
|
||||||
|
store.display += String(num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const addComma = () => {
|
||||||
|
if (!store.display.includes(',')) {
|
||||||
|
store.display += ','
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setOperator = (op) => {
|
||||||
|
previousValue.value = parseFloat(store.display.replace(',', '.'))
|
||||||
|
lastOperator.value = op
|
||||||
|
shouldResetDisplay.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const calculate = () => {
|
||||||
|
if (lastOperator.value === null) return
|
||||||
|
const currentVal = parseFloat(store.display.replace(',', '.'))
|
||||||
|
const prevVal = previousValue.value
|
||||||
|
let result = 0
|
||||||
|
|
||||||
|
switch (lastOperator.value) {
|
||||||
|
case '+': result = prevVal + currentVal; break
|
||||||
|
case '-': result = prevVal - currentVal; break
|
||||||
|
case '*': result = prevVal * currentVal; break
|
||||||
|
case '/': result = currentVal !== 0 ? prevVal / currentVal : 0; break
|
||||||
|
}
|
||||||
|
|
||||||
|
const expression = `${prevVal} ${lastOperator.value} ${currentVal}`
|
||||||
|
const resultString = String(Number(result.toFixed(4))).replace('.', ',')
|
||||||
|
|
||||||
|
store.addHistory(expression, resultString)
|
||||||
|
store.display = resultString
|
||||||
|
lastOperator.value = null
|
||||||
|
shouldResetDisplay.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const clear = () => {
|
||||||
|
store.display = '0'
|
||||||
|
previousValue.value = null
|
||||||
|
lastOperator.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const applyTax = (percent) => {
|
||||||
|
const current = parseFloat(store.display.replace(',', '.'))
|
||||||
|
store.display = (current * (1 + percent / 100)).toFixed(2).replace('.', ',')
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeTax = (percent) => {
|
||||||
|
const current = parseFloat(store.display.replace(',', '.'))
|
||||||
|
store.display = (current / (1 + percent / 100)).toFixed(2).replace('.', ',')
|
||||||
|
}
|
||||||
|
|
||||||
|
const addToSum = () => {
|
||||||
|
store.memory += parseFloat(store.display.replace(',', '.'))
|
||||||
|
shouldResetDisplay.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const copyDisplay = () => {
|
||||||
|
copy(store.display)
|
||||||
|
}
|
||||||
|
|
||||||
|
const useHistoryItem = (val) => {
|
||||||
|
store.display = val
|
||||||
|
showHistory.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Shortcuts ---
|
||||||
|
defineShortcuts({
|
||||||
|
'0': () => appendNumber(0),
|
||||||
|
'1': () => appendNumber(1),
|
||||||
|
'2': () => appendNumber(2),
|
||||||
|
'3': () => appendNumber(3),
|
||||||
|
'4': () => appendNumber(4),
|
||||||
|
'5': () => appendNumber(5),
|
||||||
|
'6': () => appendNumber(6),
|
||||||
|
'7': () => appendNumber(7),
|
||||||
|
'8': () => appendNumber(8),
|
||||||
|
'9': () => appendNumber(9),
|
||||||
|
'comma': addComma,
|
||||||
|
'plus': () => setOperator('+'),
|
||||||
|
'minus': () => setOperator('-'),
|
||||||
|
'enter': { usingInput: true, handler: calculate },
|
||||||
|
'backspace': () => {
|
||||||
|
store.display = store.display.length > 1 ? store.display.slice(0, -1) : '0'
|
||||||
|
},
|
||||||
|
// Escape schließt nun das Fenster via Store
|
||||||
|
'escape': {
|
||||||
|
usingInput: true,
|
||||||
|
whenever: [computed(() => store.isOpen)],
|
||||||
|
handler: () => { store.isOpen = false }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.custom-scrollbar::-webkit-scrollbar {
|
||||||
|
width: 4px;
|
||||||
|
}
|
||||||
|
.custom-scrollbar::-webkit-scrollbar-track {
|
||||||
|
@apply bg-transparent;
|
||||||
|
}
|
||||||
|
.custom-scrollbar::-webkit-scrollbar-thumb {
|
||||||
|
@apply bg-gray-200 dark:bg-gray-700 rounded-full;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,42 +1,39 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
|
const { has } = usePermission()
|
||||||
|
|
||||||
const {has} = usePermission()
|
// Lokaler State für den Taschenrechner
|
||||||
|
const showCalculator = ref(false)
|
||||||
|
|
||||||
const links = computed(() => {
|
const links = computed(() => {
|
||||||
return [
|
return [
|
||||||
...(auth.profile?.pinned_on_navigation || []).map(pin => {
|
...(auth.profile?.pinned_on_navigation || []).map(pin => {
|
||||||
if(pin.type === "external") {
|
if (pin.type === "external") {
|
||||||
return {
|
return {
|
||||||
label: pin.label,
|
label: pin.label,
|
||||||
to: pin.link,
|
to: pin.link,
|
||||||
icon: pin.icon,
|
icon: pin.icon,
|
||||||
target: "_blank",
|
target: "_blank",
|
||||||
pinned: true
|
pinned: true
|
||||||
}
|
|
||||||
}else if(pin.type === "standardEntity") {
|
|
||||||
return {
|
|
||||||
label: pin.label,
|
|
||||||
to: `/standardEntity/${pin.datatype}/show/${pin.id}`,
|
|
||||||
icon: pin.icon,
|
|
||||||
pinned: true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}),
|
} else if (pin.type === "standardEntity") {
|
||||||
|
return {
|
||||||
|
label: pin.label,
|
||||||
|
to: `/standardEntity/${pin.datatype}/show/${pin.id}`,
|
||||||
|
icon: pin.icon,
|
||||||
|
pinned: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
... false ? [{
|
|
||||||
label: "Support Tickets",
|
|
||||||
to: "/support",
|
|
||||||
icon: "i-heroicons-rectangle-stack",
|
|
||||||
}] : [],
|
|
||||||
{
|
{
|
||||||
id: 'dashboard',
|
id: 'dashboard',
|
||||||
label: "Dashboard",
|
label: "Dashboard",
|
||||||
to: "/",
|
to: "/",
|
||||||
icon: "i-heroicons-home"
|
icon: "i-heroicons-home"
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
id: 'historyitems',
|
id: 'historyitems',
|
||||||
label: "Logbuch",
|
label: "Logbuch",
|
||||||
to: "/historyitems",
|
to: "/historyitems",
|
||||||
@@ -48,31 +45,11 @@ const links = computed(() => {
|
|||||||
icon: "i-heroicons-rectangle-stack",
|
icon: "i-heroicons-rectangle-stack",
|
||||||
defaultOpen: false,
|
defaultOpen: false,
|
||||||
children: [
|
children: [
|
||||||
... has("tasks") ? [{
|
...has("tasks") ? [{
|
||||||
label: "Aufgaben",
|
label: "Aufgaben",
|
||||||
to: "/standardEntity/tasks",
|
to: "/standardEntity/tasks",
|
||||||
icon: "i-heroicons-rectangle-stack"
|
icon: "i-heroicons-rectangle-stack"
|
||||||
}] : [],
|
}] : [],
|
||||||
/*... true ? [{
|
|
||||||
label: "Plantafel",
|
|
||||||
to: "/calendar/timeline",
|
|
||||||
icon: "i-heroicons-calendar-days"
|
|
||||||
}] : [],
|
|
||||||
... true ? [{
|
|
||||||
label: "Kalender",
|
|
||||||
to: "/calendar/grid",
|
|
||||||
icon: "i-heroicons-calendar-days"
|
|
||||||
}] : [],
|
|
||||||
... true ? [{
|
|
||||||
label: "Termine",
|
|
||||||
to: "/standardEntity/events",
|
|
||||||
icon: "i-heroicons-calendar-days"
|
|
||||||
}] : [],*/
|
|
||||||
/*{
|
|
||||||
label: "Dateien",
|
|
||||||
to: "/files",
|
|
||||||
icon: "i-heroicons-document"
|
|
||||||
},*/
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -84,12 +61,12 @@ const links = computed(() => {
|
|||||||
label: "Dateien",
|
label: "Dateien",
|
||||||
to: "/files",
|
to: "/files",
|
||||||
icon: "i-heroicons-document"
|
icon: "i-heroicons-document"
|
||||||
},{
|
}, {
|
||||||
label: "Anschreiben",
|
label: "Anschreiben",
|
||||||
to: "/createdletters",
|
to: "/createdletters",
|
||||||
icon: "i-heroicons-document",
|
icon: "i-heroicons-document",
|
||||||
disabled: true
|
disabled: true
|
||||||
},{
|
}, {
|
||||||
label: "Boxen",
|
label: "Boxen",
|
||||||
to: "/standardEntity/documentboxes",
|
to: "/standardEntity/documentboxes",
|
||||||
icon: "i-heroicons-archive-box",
|
icon: "i-heroicons-archive-box",
|
||||||
@@ -113,62 +90,44 @@ const links = computed(() => {
|
|||||||
to: "/email/new",
|
to: "/email/new",
|
||||||
icon: "i-heroicons-envelope",
|
icon: "i-heroicons-envelope",
|
||||||
disabled: true
|
disabled: true
|
||||||
}/*, {
|
}
|
||||||
label: "Logbücher",
|
|
||||||
to: "/communication/historyItems",
|
|
||||||
icon: "i-heroicons-book-open"
|
|
||||||
}, {
|
|
||||||
label: "Chats",
|
|
||||||
to: "/chats",
|
|
||||||
icon: "i-heroicons-chat-bubble-left"
|
|
||||||
}*/
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
... (has("customers") || has("vendors") || has("contacts")) ? [{
|
...(has("customers") || has("vendors") || has("contacts")) ? [{
|
||||||
label: "Kontakte",
|
label: "Kontakte",
|
||||||
defaultOpen: false,
|
defaultOpen: false,
|
||||||
icon: "i-heroicons-user-group",
|
icon: "i-heroicons-user-group",
|
||||||
children: [
|
children: [
|
||||||
... has("customers") ? [{
|
...has("customers") ? [{
|
||||||
label: "Kunden",
|
label: "Kunden",
|
||||||
to: "/standardEntity/customers",
|
to: "/standardEntity/customers",
|
||||||
icon: "i-heroicons-user-group"
|
icon: "i-heroicons-user-group"
|
||||||
}] : [],
|
}] : [],
|
||||||
... has("vendors") ? [{
|
...has("vendors") ? [{
|
||||||
label: "Lieferanten",
|
label: "Lieferanten",
|
||||||
to: "/standardEntity/vendors",
|
to: "/standardEntity/vendors",
|
||||||
icon: "i-heroicons-truck"
|
icon: "i-heroicons-truck"
|
||||||
}] : [],
|
}] : [],
|
||||||
... has("contacts") ? [{
|
...has("contacts") ? [{
|
||||||
label: "Ansprechpartner",
|
label: "Ansprechpartner",
|
||||||
to: "/standardEntity/contacts",
|
to: "/standardEntity/contacts",
|
||||||
icon: "i-heroicons-user-group"
|
icon: "i-heroicons-user-group"
|
||||||
}] : [],
|
}] : [],
|
||||||
]
|
]
|
||||||
},] : [],
|
}] : [],
|
||||||
{
|
{
|
||||||
label: "Mitarbeiter",
|
label: "Mitarbeiter",
|
||||||
defaultOpen:false,
|
defaultOpen: false,
|
||||||
icon: "i-heroicons-user-group",
|
icon: "i-heroicons-user-group",
|
||||||
children: [
|
children: [
|
||||||
... true ? [{
|
...true ? [{
|
||||||
label: "Zeiten",
|
label: "Zeiten",
|
||||||
to: "/staff/time",
|
to: "/staff/time",
|
||||||
icon: "i-heroicons-clock",
|
icon: "i-heroicons-clock",
|
||||||
}] : [],
|
}] : [],
|
||||||
/*... has("absencerequests") ? [{
|
|
||||||
label: "Abwesenheiten",
|
|
||||||
to: "/standardEntity/absencerequests",
|
|
||||||
icon: "i-heroicons-document-text"
|
|
||||||
}] : [],*/
|
|
||||||
/*{
|
|
||||||
label: "Fahrten",
|
|
||||||
to: "/trackingTrips",
|
|
||||||
icon: "i-heroicons-map"
|
|
||||||
},*/
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
... [{
|
...[{
|
||||||
label: "Buchhaltung",
|
label: "Buchhaltung",
|
||||||
defaultOpen: false,
|
defaultOpen: false,
|
||||||
icon: "i-heroicons-chart-bar-square",
|
icon: "i-heroicons-chart-bar-square",
|
||||||
@@ -177,23 +136,23 @@ const links = computed(() => {
|
|||||||
label: "Ausgangsbelege",
|
label: "Ausgangsbelege",
|
||||||
to: "/createDocument",
|
to: "/createDocument",
|
||||||
icon: "i-heroicons-document-text"
|
icon: "i-heroicons-document-text"
|
||||||
},{
|
}, {
|
||||||
label: "Serienvorlagen",
|
label: "Serienvorlagen",
|
||||||
to: "/createDocument/serialInvoice",
|
to: "/createDocument/serialInvoice",
|
||||||
icon: "i-heroicons-document-text"
|
icon: "i-heroicons-document-text"
|
||||||
},{
|
}, {
|
||||||
label: "Eingangsbelege",
|
label: "Eingangsbelege",
|
||||||
to: "/incomingInvoices",
|
to: "/incomingInvoices",
|
||||||
icon: "i-heroicons-document-text",
|
icon: "i-heroicons-document-text",
|
||||||
},{
|
}, {
|
||||||
label: "Kostenstellen",
|
label: "Kostenstellen",
|
||||||
to: "/standardEntity/costcentres",
|
to: "/standardEntity/costcentres",
|
||||||
icon: "i-heroicons-document-currency-euro"
|
icon: "i-heroicons-document-currency-euro"
|
||||||
},{
|
}, {
|
||||||
label: "Buchungskonten",
|
label: "Buchungskonten",
|
||||||
to: "/accounts",
|
to: "/accounts",
|
||||||
icon: "i-heroicons-document-text",
|
icon: "i-heroicons-document-text",
|
||||||
},{
|
}, {
|
||||||
label: "zusätzliche Buchungskonten",
|
label: "zusätzliche Buchungskonten",
|
||||||
to: "/standardEntity/ownaccounts",
|
to: "/standardEntity/ownaccounts",
|
||||||
icon: "i-heroicons-document-text"
|
icon: "i-heroicons-document-text"
|
||||||
@@ -205,48 +164,39 @@ const links = computed(() => {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
}],
|
}],
|
||||||
... has("inventory") ? [{
|
...has("inventory") ? [{
|
||||||
label: "Lager",
|
label: "Lager",
|
||||||
icon: "i-heroicons-puzzle-piece",
|
icon: "i-heroicons-puzzle-piece",
|
||||||
defaultOpen: false,
|
defaultOpen: false,
|
||||||
children: [
|
children: [
|
||||||
/*{
|
...has("spaces") ? [{
|
||||||
label: "Vorgänge",
|
|
||||||
to: "/inventory",
|
|
||||||
icon: "i-heroicons-square-3-stack-3d"
|
|
||||||
},{
|
|
||||||
label: "Bestände",
|
|
||||||
to: "/inventory/stocks",
|
|
||||||
icon: "i-heroicons-square-3-stack-3d"
|
|
||||||
},*/
|
|
||||||
... has("spaces") ? [{
|
|
||||||
label: "Lagerplätze",
|
label: "Lagerplätze",
|
||||||
to: "/standardEntity/spaces",
|
to: "/standardEntity/spaces",
|
||||||
icon: "i-heroicons-square-3-stack-3d"
|
icon: "i-heroicons-square-3-stack-3d"
|
||||||
}] : [],
|
}] : [],
|
||||||
]
|
]
|
||||||
},] : [],
|
}] : [],
|
||||||
{
|
{
|
||||||
label: "Stammdaten",
|
label: "Stammdaten",
|
||||||
defaultOpen: false,
|
defaultOpen: false,
|
||||||
icon: "i-heroicons-clipboard-document",
|
icon: "i-heroicons-clipboard-document",
|
||||||
children: [
|
children: [
|
||||||
... has("products") ? [{
|
...has("products") ? [{
|
||||||
label: "Artikel",
|
label: "Artikel",
|
||||||
to: "/standardEntity/products",
|
to: "/standardEntity/products",
|
||||||
icon: "i-heroicons-puzzle-piece"
|
icon: "i-heroicons-puzzle-piece"
|
||||||
}] : [],
|
}] : [],
|
||||||
... has("productcategories") ? [{
|
...has("productcategories") ? [{
|
||||||
label: "Artikelkategorien",
|
label: "Artikelkategorien",
|
||||||
to: "/standardEntity/productcategories",
|
to: "/standardEntity/productcategories",
|
||||||
icon: "i-heroicons-puzzle-piece"
|
icon: "i-heroicons-puzzle-piece"
|
||||||
}] : [],
|
}] : [],
|
||||||
... has("services") ? [{
|
...has("services") ? [{
|
||||||
label: "Leistungen",
|
label: "Leistungen",
|
||||||
to: "/standardEntity/services",
|
to: "/standardEntity/services",
|
||||||
icon: "i-heroicons-wrench-screwdriver"
|
icon: "i-heroicons-wrench-screwdriver"
|
||||||
}] : [],
|
}] : [],
|
||||||
... has("servicecategories") ? [{
|
...has("servicecategories") ? [{
|
||||||
label: "Leistungskategorien",
|
label: "Leistungskategorien",
|
||||||
to: "/standardEntity/servicecategories",
|
to: "/standardEntity/servicecategories",
|
||||||
icon: "i-heroicons-wrench-screwdriver"
|
icon: "i-heroicons-wrench-screwdriver"
|
||||||
@@ -261,17 +211,17 @@ const links = computed(() => {
|
|||||||
to: "/standardEntity/hourrates",
|
to: "/standardEntity/hourrates",
|
||||||
icon: "i-heroicons-user-group"
|
icon: "i-heroicons-user-group"
|
||||||
},
|
},
|
||||||
... has("vehicles") ? [{
|
...has("vehicles") ? [{
|
||||||
label: "Fahrzeuge",
|
label: "Fahrzeuge",
|
||||||
to: "/standardEntity/vehicles",
|
to: "/standardEntity/vehicles",
|
||||||
icon: "i-heroicons-truck"
|
icon: "i-heroicons-truck"
|
||||||
}] : [],
|
}] : [],
|
||||||
... has("inventoryitems") ? [{
|
...has("inventoryitems") ? [{
|
||||||
label: "Inventar",
|
label: "Inventar",
|
||||||
to: "/standardEntity/inventoryitems",
|
to: "/standardEntity/inventoryitems",
|
||||||
icon: "i-heroicons-puzzle-piece"
|
icon: "i-heroicons-puzzle-piece"
|
||||||
}] : [],
|
}] : [],
|
||||||
... has("inventoryitems") ? [{
|
...has("inventoryitems") ? [{
|
||||||
label: "Inventargruppen",
|
label: "Inventargruppen",
|
||||||
to: "/standardEntity/inventoryitemgroups",
|
to: "/standardEntity/inventoryitemgroups",
|
||||||
icon: "i-heroicons-puzzle-piece"
|
icon: "i-heroicons-puzzle-piece"
|
||||||
@@ -279,26 +229,21 @@ const links = computed(() => {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
... has("projects") ? [{
|
...has("projects") ? [{
|
||||||
label: "Projekte",
|
label: "Projekte",
|
||||||
to: "/standardEntity/projects",
|
to: "/standardEntity/projects",
|
||||||
icon: "i-heroicons-clipboard-document-check"
|
icon: "i-heroicons-clipboard-document-check"
|
||||||
},] : [],
|
}] : [],
|
||||||
... has("contracts") ? [{
|
...has("contracts") ? [{
|
||||||
label: "Verträge",
|
label: "Verträge",
|
||||||
to: "/standardEntity/contracts",
|
to: "/standardEntity/contracts",
|
||||||
icon: "i-heroicons-clipboard-document"
|
icon: "i-heroicons-clipboard-document"
|
||||||
}] : [],
|
}] : [],
|
||||||
... has("plants") ? [{
|
...has("plants") ? [{
|
||||||
label: "Objekte",
|
label: "Objekte",
|
||||||
to: "/standardEntity/plants",
|
to: "/standardEntity/plants",
|
||||||
icon: "i-heroicons-clipboard-document"
|
icon: "i-heroicons-clipboard-document"
|
||||||
},] : [],
|
}] : [],
|
||||||
/*... has("checks") ? [{
|
|
||||||
label: "Überprüfungen",
|
|
||||||
to: "/standardEntity/checks",
|
|
||||||
icon: "i-heroicons-magnifying-glass"
|
|
||||||
},] : [],*/
|
|
||||||
{
|
{
|
||||||
label: "Einstellungen",
|
label: "Einstellungen",
|
||||||
defaultOpen: false,
|
defaultOpen: false,
|
||||||
@@ -308,67 +253,57 @@ const links = computed(() => {
|
|||||||
label: "Nummernkreise",
|
label: "Nummernkreise",
|
||||||
to: "/settings/numberRanges",
|
to: "/settings/numberRanges",
|
||||||
icon: "i-heroicons-clipboard-document-list",
|
icon: "i-heroicons-clipboard-document-list",
|
||||||
},/*{
|
}, {
|
||||||
label: "Rollen",
|
|
||||||
to: "/roles",
|
|
||||||
icon: "i-heroicons-key"
|
|
||||||
},*/{
|
|
||||||
label: "E-Mail Konten",
|
label: "E-Mail Konten",
|
||||||
to: "/settings/emailaccounts",
|
to: "/settings/emailaccounts",
|
||||||
icon: "i-heroicons-envelope",
|
icon: "i-heroicons-envelope",
|
||||||
},{
|
}, {
|
||||||
label: "Bankkonten",
|
label: "Bankkonten",
|
||||||
to: "/settings/banking",
|
to: "/settings/banking",
|
||||||
icon: "i-heroicons-currency-euro",
|
icon: "i-heroicons-currency-euro",
|
||||||
},{
|
}, {
|
||||||
label: "Textvorlagen",
|
label: "Textvorlagen",
|
||||||
to: "/settings/texttemplates",
|
to: "/settings/texttemplates",
|
||||||
icon: "i-heroicons-clipboard-document-list",
|
icon: "i-heroicons-clipboard-document-list",
|
||||||
},/*{
|
}, {
|
||||||
label: "Eigene Felder",
|
|
||||||
to: "/settings/ownfields",
|
|
||||||
icon: "i-heroicons-clipboard-document-list"
|
|
||||||
},*/{
|
|
||||||
label: "Firmeneinstellungen",
|
label: "Firmeneinstellungen",
|
||||||
to: "/settings/tenant",
|
to: "/settings/tenant",
|
||||||
icon: "i-heroicons-building-office",
|
icon: "i-heroicons-building-office",
|
||||||
},{
|
}, {
|
||||||
label: "Projekttypen",
|
label: "Projekttypen",
|
||||||
to: "/projecttypes",
|
to: "/projecttypes",
|
||||||
icon: "i-heroicons-clipboard-document-list",
|
icon: "i-heroicons-clipboard-document-list",
|
||||||
},{
|
}, {
|
||||||
label: "Export",
|
label: "Export",
|
||||||
to: "/export",
|
to: "/export",
|
||||||
icon: "i-heroicons-clipboard-document-list"
|
icon: "i-heroicons-clipboard-document-list"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
// nur Items mit Children → für Accordion
|
|
||||||
const accordionItems = computed(() =>
|
const accordionItems = computed(() =>
|
||||||
links.value.filter(item => Array.isArray(item.children) && item.children.length > 0)
|
links.value.filter(item => Array.isArray(item.children) && item.children.length > 0)
|
||||||
)
|
)
|
||||||
|
|
||||||
// nur Items ohne Children → als Buttons
|
|
||||||
const buttonItems = computed(() =>
|
const buttonItems = computed(() =>
|
||||||
links.value.filter(item => !item.children || item.children.length === 0)
|
links.value.filter(item => !item.children || item.children.length === 0)
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- Standalone Buttons -->
|
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<UButton
|
<UButton
|
||||||
v-for="item in buttonItems"
|
v-for="item in buttonItems"
|
||||||
:key="item.label"
|
:key="item.label"
|
||||||
:variant="item.pinned ? 'ghost' : 'ghost'"
|
variant="ghost"
|
||||||
:color="(item.to && route.path === item.to) ? 'primary' : (item.pinned ? 'amber' : 'gray')"
|
:color="(item.to && route.path === item.to) ? 'primary' : (item.pinned ? 'amber' : 'gray')"
|
||||||
:icon="item.pinned ? 'i-heroicons-star' : item.icon"
|
:icon="item.pinned ? 'i-heroicons-star' : item.icon"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
:to="item.to"
|
:to="item.to"
|
||||||
:target="item.target"
|
:target="item.target"
|
||||||
|
@click="item.click ? item.click() : null"
|
||||||
>
|
>
|
||||||
<UIcon
|
<UIcon
|
||||||
v-if="item.pinned"
|
v-if="item.pinned"
|
||||||
@@ -378,8 +313,9 @@ const buttonItems = computed(() =>
|
|||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
</UButton>
|
</UButton>
|
||||||
</div>
|
</div>
|
||||||
<UDivider/>
|
|
||||||
<!-- Accordion für die Items mit Children -->
|
<UDivider class="my-2"/>
|
||||||
|
|
||||||
<UAccordion
|
<UAccordion
|
||||||
:items="accordionItems"
|
:items="accordionItems"
|
||||||
:multiple="false"
|
:multiple="false"
|
||||||
@@ -387,7 +323,7 @@ const buttonItems = computed(() =>
|
|||||||
>
|
>
|
||||||
<template #default="{ item, open }">
|
<template #default="{ item, open }">
|
||||||
<UButton
|
<UButton
|
||||||
:variant="'ghost'"
|
variant="ghost"
|
||||||
:color="(item.children?.some(c => route.path.includes(c.to))) ? 'primary' : 'gray'"
|
:color="(item.children?.some(c => route.path.includes(c.to))) ? 'primary' : 'gray'"
|
||||||
:icon="item.icon"
|
:icon="item.icon"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
@@ -415,56 +351,13 @@ const buttonItems = computed(() =>
|
|||||||
:to="child.to"
|
:to="child.to"
|
||||||
:target="child.target"
|
:target="child.target"
|
||||||
:disabled="child.disabled"
|
:disabled="child.disabled"
|
||||||
|
@click="child.click ? child.click() : null"
|
||||||
>
|
>
|
||||||
{{ child.label }}
|
{{ child.label }}
|
||||||
</UButton>
|
</UButton>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</UAccordion>
|
</UAccordion>
|
||||||
<!-- <UAccordion
|
|
||||||
:items="links"
|
|
||||||
:multiple="false"
|
|
||||||
>
|
|
||||||
<template #default="{ item, index, open }">
|
|
||||||
<UButton
|
|
||||||
:variant="item.pinned ? 'ghost' : 'ghost'"
|
|
||||||
:color="(item.to && route.path === item.to) || (item.children?.some(c => route.path.includes(c.to))) ? 'primary' : (item.pinned ? 'amber' : 'gray')"
|
|
||||||
:icon="item.pinned ? 'i-heroicons-star' : item.icon"
|
|
||||||
class="w-full"
|
|
||||||
:to="item.to"
|
|
||||||
:target="item.target"
|
|
||||||
>
|
|
||||||
<UIcon
|
|
||||||
v-if="item.pinned"
|
|
||||||
:name="item.icon" class="w-5 h-5 me-2" />
|
|
||||||
{{ item.label }}
|
|
||||||
|
|
||||||
<template v-if="item.children" #trailing>
|
|
||||||
<UIcon
|
|
||||||
name="i-heroicons-chevron-right-20-solid"
|
|
||||||
class="w-5 h-5 ms-auto transform transition-transform duration-200"
|
|
||||||
:class="[open && 'rotate-90']"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</UButton>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #item="{ item }">
|
|
||||||
<div class="flex flex-col" v-if="item.children?.length > 0">
|
|
||||||
<UButton
|
|
||||||
v-for="child in item.children"
|
|
||||||
:key="child.label"
|
|
||||||
variant="ghost"
|
|
||||||
:color="child.to === route.path ? 'primary' : 'gray'"
|
|
||||||
:icon="child.icon"
|
|
||||||
class="ml-4"
|
|
||||||
:to="child.to"
|
|
||||||
:target="child.target"
|
|
||||||
>
|
|
||||||
{{ child.label }}
|
|
||||||
</UButton>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</UAccordion>-->
|
|
||||||
|
|
||||||
|
<Calculator v-if="showCalculator" v-model="showCalculator"/>
|
||||||
</template>
|
</template>
|
||||||
@@ -1,21 +1,20 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
|
||||||
|
|
||||||
import MainNav from "~/components/MainNav.vue";
|
import MainNav from "~/components/MainNav.vue";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import GlobalMessages from "~/components/GlobalMessages.vue";
|
import GlobalMessages from "~/components/GlobalMessages.vue";
|
||||||
import TenantDropdown from "~/components/TenantDropdown.vue";
|
import TenantDropdown from "~/components/TenantDropdown.vue";
|
||||||
import LabelPrinterButton from "~/components/LabelPrinterButton.vue";
|
import LabelPrinterButton from "~/components/LabelPrinterButton.vue";
|
||||||
|
import {useCalculatorStore} from '~/stores/calculator'
|
||||||
|
|
||||||
const dataStore = useDataStore()
|
const dataStore = useDataStore()
|
||||||
const colorMode = useColorMode()
|
const colorMode = useColorMode()
|
||||||
const { isHelpSlideoverOpen } = useDashboard()
|
const {isHelpSlideoverOpen} = useDashboard()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
const labelPrinter = useLabelPrinterStore()
|
const labelPrinter = useLabelPrinterStore()
|
||||||
|
const calculatorStore = useCalculatorStore()
|
||||||
|
|
||||||
const month = dayjs().format("MM")
|
const month = dayjs().format("MM")
|
||||||
|
|
||||||
@@ -24,91 +23,108 @@ const actions = [
|
|||||||
id: 'new-customer',
|
id: 'new-customer',
|
||||||
label: 'Kunde hinzufügen',
|
label: 'Kunde hinzufügen',
|
||||||
icon: 'i-heroicons-user-group',
|
icon: 'i-heroicons-user-group',
|
||||||
to: "/customers/create" ,
|
to: "/customers/create",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'new-vendor',
|
id: 'new-vendor',
|
||||||
label: 'Lieferant hinzufügen',
|
label: 'Lieferant hinzufügen',
|
||||||
icon: 'i-heroicons-truck',
|
icon: 'i-heroicons-truck',
|
||||||
to: "/vendors/create" ,
|
to: "/vendors/create",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'new-contact',
|
id: 'new-contact',
|
||||||
label: 'Ansprechpartner hinzufügen',
|
label: 'Ansprechpartner hinzufügen',
|
||||||
icon: 'i-heroicons-user-group',
|
icon: 'i-heroicons-user-group',
|
||||||
to: "/contacts/create" ,
|
to: "/contacts/create",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'new-task',
|
id: 'new-task',
|
||||||
label: 'Aufgabe hinzufügen',
|
label: 'Aufgabe hinzufügen',
|
||||||
icon: 'i-heroicons-rectangle-stack',
|
icon: 'i-heroicons-rectangle-stack',
|
||||||
to: "/tasks/create" ,
|
to: "/tasks/create",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'new-plant',
|
id: 'new-plant',
|
||||||
label: 'Objekt hinzufügen',
|
label: 'Objekt hinzufügen',
|
||||||
icon: 'i-heroicons-clipboard-document',
|
icon: 'i-heroicons-clipboard-document',
|
||||||
to: "/plants/create" ,
|
to: "/plants/create",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'new-product',
|
id: 'new-product',
|
||||||
label: 'Artikel hinzufügen',
|
label: 'Artikel hinzufügen',
|
||||||
icon: 'i-heroicons-puzzle-piece',
|
icon: 'i-heroicons-puzzle-piece',
|
||||||
to: "/products/create" ,
|
to: "/products/create",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'new-project',
|
id: 'new-project',
|
||||||
label: 'Projekt hinzufügen',
|
label: 'Projekt hinzufügen',
|
||||||
icon: 'i-heroicons-clipboard-document-check',
|
icon: 'i-heroicons-clipboard-document-check',
|
||||||
to: "/projects/create" ,
|
to: "/projects/create",
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
const groups = computed(() => [
|
const groups = computed(() => [
|
||||||
{
|
{
|
||||||
key: 'actions',
|
key: 'actions',
|
||||||
commands: actions
|
commands: actions
|
||||||
},{
|
}, {
|
||||||
key: "customers",
|
key: "customers",
|
||||||
label: "Kunden",
|
label: "Kunden",
|
||||||
commands: dataStore.customers.map(item => { return {id: item.id, label: item.name, to: `/customers/show/${item.id}`}})
|
commands: dataStore.customers.map(item => {
|
||||||
},{
|
return {id: item.id, label: item.name, to: `/customers/show/${item.id}`}
|
||||||
key: "vendors",
|
})
|
||||||
label: "Lieferanten",
|
}, {
|
||||||
commands: dataStore.vendors.map(item => { return {id: item.id, label: item.name, to: `/vendors/show/${item.id}`}})
|
key: "vendors",
|
||||||
},{
|
label: "Lieferanten",
|
||||||
key: "contacts",
|
commands: dataStore.vendors.map(item => {
|
||||||
label: "Ansprechpartner",
|
return {id: item.id, label: item.name, to: `/vendors/show/${item.id}`}
|
||||||
commands: dataStore.contacts.map(item => { return {id: item.id, label: item.fullName, to: `/contacts/show/${item.id}`}})
|
})
|
||||||
},{
|
}, {
|
||||||
key: "products",
|
key: "contacts",
|
||||||
label: "Artikel",
|
label: "Ansprechpartner",
|
||||||
commands: dataStore.products.map(item => { return {id: item.id, label: item.name, to: `/products/show/${item.id}`}})
|
commands: dataStore.contacts.map(item => {
|
||||||
},{
|
return {id: item.id, label: item.fullName, to: `/contacts/show/${item.id}`}
|
||||||
key: "tasks",
|
})
|
||||||
label: "Aufgaben",
|
}, {
|
||||||
commands: dataStore.tasks.map(item => { return {id: item.id, label: item.name, to: `/tasks/show/${item.id}`}})
|
key: "products",
|
||||||
},{
|
label: "Artikel",
|
||||||
key: "plants",
|
commands: dataStore.products.map(item => {
|
||||||
label: "Objekte",
|
return {id: item.id, label: item.name, to: `/products/show/${item.id}`}
|
||||||
commands: dataStore.plants.map(item => { return {id: item.id, label: item.name, to: `/plants/show/${item.id}`}})
|
})
|
||||||
},{
|
}, {
|
||||||
key: "projects",
|
key: "tasks",
|
||||||
label: "Projekte",
|
label: "Aufgaben",
|
||||||
commands: dataStore.projects.map(item => { return {id: item.id, label: item.name, to: `/projects/show/${item.id}`}})
|
commands: dataStore.tasks.map(item => {
|
||||||
}
|
return {id: item.id, label: item.name, to: `/tasks/show/${item.id}`}
|
||||||
].filter(Boolean))
|
})
|
||||||
const footerLinks = [
|
}, {
|
||||||
/*{
|
key: "plants",
|
||||||
label: 'Invite people',
|
label: "Objekte",
|
||||||
icon: 'i-heroicons-plus',
|
commands: dataStore.plants.map(item => {
|
||||||
to: '/settings/members'
|
return {id: item.id, label: item.name, to: `/plants/show/${item.id}`}
|
||||||
}, */{
|
})
|
||||||
label: 'Hilfe & Info',
|
}, {
|
||||||
icon: 'i-heroicons-question-mark-circle',
|
key: "projects",
|
||||||
click: () => isHelpSlideoverOpen.value = true
|
label: "Projekte",
|
||||||
}]
|
commands: dataStore.projects.map(item => {
|
||||||
|
return {id: item.id, label: item.name, to: `/projects/show/${item.id}`}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
].filter(Boolean))
|
||||||
|
|
||||||
|
// --- Footer Links nutzen jetzt den zentralen Calculator Store ---
|
||||||
|
const footerLinks = computed(() => [
|
||||||
|
{
|
||||||
|
label: 'Taschenrechner',
|
||||||
|
icon: 'i-heroicons-calculator',
|
||||||
|
click: () => calculatorStore.toggle()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Hilfe & Info',
|
||||||
|
icon: 'i-heroicons-question-mark-circle',
|
||||||
|
click: () => isHelpSlideoverOpen.value = true
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -130,24 +146,24 @@ const footerLinks = [
|
|||||||
v-else
|
v-else
|
||||||
/>
|
/>
|
||||||
<div class="flex justify-center mb-6">
|
<div class="flex justify-center mb-6">
|
||||||
<UIcon name="i-heroicons-exclamation-triangle-solid" class="w-16 h-16 text-yellow-500" />
|
<UIcon name="i-heroicons-exclamation-triangle-solid" class="w-16 h-16 text-yellow-500"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
||||||
Wartungsarbeiten
|
Wartungsarbeiten
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-gray-600 dark:text-gray-300 mb-8">
|
<p class="text-gray-600 dark:text-gray-300 mb-8">
|
||||||
Dieser FEDEO Mandant wird derzeit gewartet. Bitte versuche es in einigen Minuten erneut oder verwende einen anderen Mandanten.
|
Dieser FEDEO Mandant wird derzeit gewartet. Bitte versuche es in einigen Minuten erneut oder verwende einen
|
||||||
|
anderen Mandanten.
|
||||||
</p>
|
</p>
|
||||||
<div class="mx-auto text-left flex flex-row justify-between my-3" v-for="tenant in auth.tenants">
|
<div class="mx-auto text-left flex flex-row justify-between my-3" v-for="tenant in auth.tenants">
|
||||||
{{tenant.name}}
|
{{ tenant.name }}
|
||||||
<UButton
|
<UButton
|
||||||
:disabled="tenant.locked"
|
:disabled="tenant.locked"
|
||||||
@click="auth.switchTenant(tenant.id)"
|
@click="auth.switchTenant(tenant.id)"
|
||||||
>Wählen</UButton>
|
>Wählen
|
||||||
|
</UButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</UCard>
|
</UCard>
|
||||||
</UContainer>
|
</UContainer>
|
||||||
</div>
|
</div>
|
||||||
@@ -167,7 +183,7 @@ const footerLinks = [
|
|||||||
v-else
|
v-else
|
||||||
/>
|
/>
|
||||||
<div class="flex justify-center mb-6">
|
<div class="flex justify-center mb-6">
|
||||||
<UIcon name="i-heroicons-exclamation-triangle-solid" class="w-16 h-16 text-yellow-500" />
|
<UIcon name="i-heroicons-exclamation-triangle-solid" class="w-16 h-16 text-yellow-500"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
||||||
@@ -176,8 +192,6 @@ const footerLinks = [
|
|||||||
<p class="text-gray-600 dark:text-gray-300 mb-8">
|
<p class="text-gray-600 dark:text-gray-300 mb-8">
|
||||||
FEDEO wird derzeit gewartet. Bitte versuche es in einigen Minuten erneut.
|
FEDEO wird derzeit gewartet. Bitte versuche es in einigen Minuten erneut.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
</UCard>
|
</UCard>
|
||||||
</UContainer>
|
</UContainer>
|
||||||
</div>
|
</div>
|
||||||
@@ -197,32 +211,33 @@ const footerLinks = [
|
|||||||
v-else
|
v-else
|
||||||
/>
|
/>
|
||||||
<div class="flex justify-center mb-6">
|
<div class="flex justify-center mb-6">
|
||||||
<UIcon name="i-heroicons-credit-card" class="w-16 h-16 text-red-600" />
|
<UIcon name="i-heroicons-credit-card" class="w-16 h-16 text-red-600"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
||||||
Kein Aktives Abonnement für diesen Mandant.
|
Kein Aktives Abonnement für diesen Mandant.
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-gray-600 dark:text-gray-300 mb-8">
|
<p class="text-gray-600 dark:text-gray-300 mb-8">
|
||||||
Bitte wenden Sie sich an den FEDEO Support um ein Abonnement zu erhalten oder verwenden Sie einen anderen Mandanten.
|
Bitte wenden Sie sich an den FEDEO Support um ein Abonnement zu erhalten oder verwenden Sie einen anderen
|
||||||
|
Mandanten.
|
||||||
</p>
|
</p>
|
||||||
<div class="mx-auto text-left flex flex-row justify-between my-3" v-for="tenant in auth.tenants">
|
<div class="mx-auto text-left flex flex-row justify-between my-3" v-for="tenant in auth.tenants">
|
||||||
{{tenant.name}}
|
{{ tenant.name }}
|
||||||
<UButton
|
<UButton
|
||||||
:disabled="tenant.locked"
|
:disabled="tenant.locked"
|
||||||
@click="auth.switchTenant(tenant.id)"
|
@click="auth.switchTenant(tenant.id)"
|
||||||
>Wählen</UButton>
|
>Wählen
|
||||||
|
</UButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</UCard>
|
</UCard>
|
||||||
</UContainer>
|
</UContainer>
|
||||||
</div>
|
</div>
|
||||||
<UDashboardLayout class="safearea" v-else >
|
<UDashboardLayout class="safearea" v-else>
|
||||||
<UDashboardPanel :width="250" :resizable="{ min: 200, max: 300 }" collapsible>
|
<UDashboardPanel :width="250" :resizable="{ min: 200, max: 300 }" collapsible>
|
||||||
<UDashboardNavbar style="margin-top: env(safe-area-inset-top, 10px) !important;" :class="['!border-transparent']" :ui="{ left: 'flex-1' }">
|
<UDashboardNavbar style="margin-top: env(safe-area-inset-top, 10px) !important;"
|
||||||
|
:class="['!border-transparent']" :ui="{ left: 'flex-1' }">
|
||||||
<template #left>
|
<template #left>
|
||||||
<TenantDropdown class="w-full" />
|
<TenantDropdown class="w-full"/>
|
||||||
</template>
|
</template>
|
||||||
</UDashboardNavbar>
|
</UDashboardNavbar>
|
||||||
|
|
||||||
@@ -230,24 +245,17 @@ const footerLinks = [
|
|||||||
|
|
||||||
<MainNav/>
|
<MainNav/>
|
||||||
|
|
||||||
<div class="flex-1" />
|
<div class="flex-1"/>
|
||||||
|
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="flex flex-col gap-3 w-full">
|
<div class="flex flex-col gap-3 w-full">
|
||||||
|
|
||||||
|
<UColorModeButton/>
|
||||||
<UColorModeButton />
|
|
||||||
<LabelPrinterButton/>
|
<LabelPrinterButton/>
|
||||||
|
|
||||||
|
<UDashboardSidebarLinks :links="footerLinks"/>
|
||||||
|
|
||||||
|
<UDivider class="sticky bottom-0"/>
|
||||||
<!-- Footer Links -->
|
|
||||||
<UDashboardSidebarLinks :links="footerLinks" />
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<UDivider class="sticky bottom-0" />
|
|
||||||
<UserDropdown style="margin-bottom: env(safe-area-inset-bottom, 10px) !important;"/>
|
<UserDropdown style="margin-bottom: env(safe-area-inset-bottom, 10px) !important;"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -256,14 +264,14 @@ const footerLinks = [
|
|||||||
|
|
||||||
<UDashboardPage>
|
<UDashboardPage>
|
||||||
<UDashboardPanel grow>
|
<UDashboardPanel grow>
|
||||||
<slot />
|
<slot/>
|
||||||
</UDashboardPanel>
|
</UDashboardPanel>
|
||||||
</UDashboardPage>
|
</UDashboardPage>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<HelpSlideover/>
|
<HelpSlideover/>
|
||||||
|
|
||||||
|
<Calculator v-if="calculatorStore.isOpen"/>
|
||||||
|
|
||||||
</UDashboardLayout>
|
</UDashboardLayout>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -278,37 +286,32 @@ const footerLinks = [
|
|||||||
v-if="month === '12'"
|
v-if="month === '12'"
|
||||||
/>
|
/>
|
||||||
<UColorModeImage
|
<UColorModeImage
|
||||||
light="/Logo.png"
|
light="/Logo.png"
|
||||||
dark="/Logo_Dark.png"
|
dark="/Logo_Dark.png"
|
||||||
class="w-1/3 mx-auto my-10"
|
class="w-1/3 mx-auto my-10"
|
||||||
v-else
|
v-else
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div v-if="!auth.activeTenant" class="w-1/2 mx-auto text-center">
|
<div v-if="!auth.activeTenant" class="w-1/2 mx-auto text-center">
|
||||||
<!-- Tenant Selection -->
|
<h3 class="text-center font-bold text-2xl mb-5">Kein Aktiver Mandant. Bitte wählen Sie ein Mandant.</h3>
|
||||||
<h3 class="text-center font-bold text-2xl mb-5">Kein Aktiver Mandant. Bitte wählen Sie ein Mandant.</h3>
|
<div class="mx-auto w-1/2 flex flex-row justify-between my-3" v-for="tenant in auth.tenants">
|
||||||
<div class="mx-auto w-1/2 flex flex-row justify-between my-3" v-for="tenant in auth.tenants">
|
<span class="text-left">{{ tenant.name }}</span>
|
||||||
<span class="text-left">{{tenant.name}}</span>
|
|
||||||
<UButton
|
|
||||||
@click="auth.switchTenant(tenant.id)"
|
|
||||||
>Wählen</UButton>
|
|
||||||
</div>
|
|
||||||
<UButton
|
<UButton
|
||||||
variant="outline"
|
@click="auth.switchTenant(tenant.id)"
|
||||||
color="rose"
|
>Wählen
|
||||||
|
</UButton>
|
||||||
|
</div>
|
||||||
|
<UButton
|
||||||
|
variant="outline"
|
||||||
|
color="rose"
|
||||||
@click="auth.logout()"
|
@click="auth.logout()"
|
||||||
>Abmelden</UButton>
|
>Abmelden
|
||||||
|
</UButton>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<UProgress animation="carousel" class="w-3/4 mx-auto mt-10" />
|
<UProgress animation="carousel" class="w-3/4 mx-auto mt-10"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
>
|
>
|
||||||
<display-open-tasks/>
|
<display-open-tasks/>
|
||||||
</UDashboardCard>
|
</UDashboardCard>
|
||||||
<UDashboardCard
|
<!-- <UDashboardCard
|
||||||
title="Label Test"
|
title="Label Test"
|
||||||
>
|
>
|
||||||
<UButton
|
<UButton
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
>
|
>
|
||||||
Label Drucken
|
Label Drucken
|
||||||
</UButton>
|
</UButton>
|
||||||
</UDashboardCard>
|
</UDashboardCard>-->
|
||||||
</UPageGrid>
|
</UPageGrid>
|
||||||
</UDashboardPanelContent>
|
</UDashboardPanelContent>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
40
frontend/stores/calculator.ts
Normal file
40
frontend/stores/calculator.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
export const useCalculatorStore = defineStore('calculator', () => {
|
||||||
|
const tempStore = useTempStore()
|
||||||
|
|
||||||
|
// Initialisierung aus dem TempStore
|
||||||
|
const isOpen = ref(false)
|
||||||
|
const display = computed({
|
||||||
|
get: () => tempStore.settings?.calculator?.display || '0',
|
||||||
|
set: (val) => tempStore.modifySettings('calculator', { ...tempStore.settings.calculator, display: val })
|
||||||
|
})
|
||||||
|
|
||||||
|
const memory = computed({
|
||||||
|
get: () => tempStore.settings?.calculator?.memory || 0,
|
||||||
|
set: (val) => tempStore.modifySettings('calculator', { ...tempStore.settings.calculator, memory: val })
|
||||||
|
})
|
||||||
|
|
||||||
|
const history = computed({
|
||||||
|
get: () => tempStore.filters?.calculator?.history || [],
|
||||||
|
set: (val) => tempStore.modifyFilter('calculator', 'history', val)
|
||||||
|
})
|
||||||
|
|
||||||
|
function toggle() {
|
||||||
|
isOpen.value = !isOpen.value
|
||||||
|
}
|
||||||
|
|
||||||
|
function addHistory(expression: string, result: string) {
|
||||||
|
const newHistory = [{ expression, result }, ...history.value].slice(0, 10)
|
||||||
|
history.value = newHistory
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isOpen,
|
||||||
|
display,
|
||||||
|
memory,
|
||||||
|
history,
|
||||||
|
toggle,
|
||||||
|
addHistory
|
||||||
|
}
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user