Two month of work

This commit is contained in:
2026-02-17 10:35:03 +01:00
parent 0ffbeeedff
commit d9fd3d1ccb
158 changed files with 5637 additions and 1512 deletions
@@ -7,20 +7,21 @@ import draggable from 'vuedraggable';
import { cn, toCurrency } from '@/lib/utils';
import { LineItem, Product, Unit } from '@/types';
import { newLineItem, newUnit } from '@/types/index.d'
import { Table, TableCell, TableFooter, TableHead, TableHeader, TableRow, } from '@/components/ui/table';
import { Table, TableCell, TableFooter, TableHead, TableHeader, TableRow, } from '@/components/ui/crm-table';
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"
import { NumberField, NumberFieldContent, NumberFieldDecrement, NumberFieldIncrement, NumberFieldInput, } from '@/components/ui/number-field'
import { Input } from '@/components/ui/crm-input';
import { Loader2, GripVertical, Trash2, Plus, TextSelect, CheckIcon, ChevronsUpDownIcon } from 'lucide-vue-next';
import { Button } from '@/components/ui/crm-button';
import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, } from '@/components/ui/empty'
import NumberInput from '@/components/ui/number-input/NumberInput.vue';
import NumberInput from '@/components/ui/crm-number-input/NumberInput.vue';
import { GrowingTextarea } from '@/components/ui/growing-textarea';
import PlaceholderPattern from '@/components/PlaceholderPattern.vue';
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from '@/components/ui/command'
import { Popover, PopoverContent, PopoverTrigger, } from '@/components/ui/popover'
import axios, { AxiosError } from "axios";
import { toast } from "vue-sonner";
import ButtonGroup from '../ui/button-group/ButtonGroup.vue';
const DEBUG = ref(true)
@@ -194,7 +195,7 @@ const recalculatePositions = () => {
<TableRow v-if="element.isSection">
<TableCell class="handle px-1 cursor-move w-6">
<GripVertical :size="18" stroke-width="1.5" class="text-muted-foreground" />
<GripVertical :size="18" class="text-muted-foreground" />
</TableCell>
<!-- Title -->
@@ -210,7 +211,7 @@ const recalculatePositions = () => {
<TableCell class="w-8 text-right px-1">
<Button variant="ghost" size="sm" @click="deleteItem(element)"
class="has-[>svg]:px-1 text-muted-foreground hover:text-destructive">
<Trash2 :size="18" stroke-width="1.5" />
<Trash2 :size="18" />
</Button>
</TableCell>
</TableRow>
@@ -218,7 +219,7 @@ const recalculatePositions = () => {
<TableRow v-else>
<TableCell class="handle px-1 cursor-move w-6">
<GripVertical :size="18" stroke-width="1.5" class="text-muted-foreground" />
<GripVertical :size="18" class="text-muted-foreground" />
</TableCell>
<!-- Pos. -->
@@ -272,19 +273,19 @@ const recalculatePositions = () => {
<!-- Total -->
<TableCell class="w-1/8 text-right tabular-nums font-bold">{{ toCurrency(element.price * element.quantity)
}}
}}
</TableCell>
<!-- Buttons -->
<TableCell class="w-8 text-right px-1">
<Button variant="ghost" size="sm" @click="deleteItem(element)"
class="has-[>svg]:px-1 text-muted-foreground hover:text-destructive">
<Trash2 stroke-width="1.5" />
<Trash2 />
</Button>
<!-- <Button variant="ghost" size="sm" @click=""
class="has-[>svg]:px-1 text-muted-foreground">
<BetweenHorizonalEnd stroke-width="1.5"/>
<BetweenHorizonalEnd />
</Button> -->
</TableCell>
@@ -299,18 +300,18 @@ const recalculatePositions = () => {
<div class="flex gap-2 justify-center">
<Button class="mt-4" variant="ghost" @click="newItem(true)">
<Plus stroke-width="1.5" /> Neuer Abschnitt
<Plus /> Neuer Abschnitt
</Button>
<Button class="mt-4" variant="ghost" @click="newItem(false)">
<Plus stroke-width="1.5" /> Neue Zeile
<Plus /> Neue Zeile
</Button>
<Popover v-model:open="productsComboboxOpen">
<PopoverTrigger as-child>
<Button variant="ghost" role="combobox" :aria-expanded="productsComboboxOpen" class="mt-4"
:disabled="!products || products.length === 0">
<Plus stroke-width="1.5" />
<Plus />
Produkt hinzufügen
</Button>
</PopoverTrigger>
@@ -326,7 +327,7 @@ const recalculatePositions = () => {
}" class="hover:bg-accent">
<!-- Thumbnail -->
<div class="w-6 relative aspect-4/3 overflow-hidden rounded-lg">
<div class="w-6 relative aspect-4/3 overflow-hidden rounded-sm shrink-0">
<img v-if="product.image" :src="'storage/uploads/products/' + product.image"
class="size-full object-cover dark:brightness-75" loading="lazy" />
<PlaceholderPattern v-else />
@@ -348,59 +349,63 @@ const recalculatePositions = () => {
</Table>
<Loader2 v-if="props.isLoading" class="mx-auto mt-8 h-6 w-6 animate-spin text-muted-foreground"
stroke-width="1.5" />
/>
<Empty v-else-if="items.length < 1" class="py-8!">
<EmptyHeader>
<EmptyMedia variant="icon">
<TextSelect class="text-muted-foreground" stroke-width="1.5" />
<TextSelect class="text-muted-foreground" />
</EmptyMedia>
<EmptyTitle>Diese Rechnung ist leer</EmptyTitle>
<EmptyDescription>Erstelle hier deinen ersten Posten</EmptyDescription>
</EmptyHeader>
<div class="flex gap-2">
<Button variant="action" @click="newItem(true)">
<Plus stroke-width="1.5" /> Neuer Abschnitt
</Button>
<ButtonGroup>
<Button variant="action" @click="newItem(false)">
<Plus stroke-width="1.5" /> Neue Zeile
</Button>
<Button @click="newItem(true)">
<Plus /> Neuer Abschnitt
</Button>
<Popover v-model:open="productsComboboxOpen">
<PopoverTrigger as-child>
<Button variant="action" role="combobox" :aria-expanded="productsComboboxOpen"
:disabled="!products || products.length === 0">
<Plus stroke-width="1.5" />
Produkt hinzufügen
</Button>
</PopoverTrigger>
<PopoverContent class="w-50 p-0">
<Command>
<CommandInput placeholder="Produkt suchen…" />
<CommandList>
<CommandEmpty>Kein Produkt gefunden</CommandEmpty>
<CommandGroup>
<CommandItem v-for="product in products" :key="product.id" :value="product.id" @select="() => {
productsComboboxOpen = false
insertProduct(product)
}" class="hover:bg-accent">
<Button @click="newItem(false)">
<Plus /> Neue Zeile
</Button>
<!-- Thumbnail -->
<div class="w-6 relative aspect-4/3 overflow-hidden rounded-lg">
<img v-if="product.image" :src="'storage/uploads/products/' + product.image"
class="size-full object-cover dark:brightness-75" loading="lazy" />
<PlaceholderPattern v-else />
</div>
<Popover v-model:open="productsComboboxOpen">
<PopoverTrigger as-child>
<Button role="combobox" :aria-expanded="productsComboboxOpen"
:disabled="!products || products.length === 0">
<Plus />
Produkt hinzufügen
</Button>
</PopoverTrigger>
<PopoverContent class="w-50 p-0">
<Command>
<CommandInput placeholder="Produkt suchen…" />
<CommandList>
<CommandEmpty>Kein Produkt gefunden</CommandEmpty>
<CommandGroup>
<CommandItem v-for="product in products" :key="product.id" :value="product.id" @select="() => {
productsComboboxOpen = false
insertProduct(product)
}" class="hover:bg-accent">
{{ product.title }}
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
<!-- Thumbnail -->
<div class="w-6 relative aspect-4/3 overflow-hidden rounded-sm shrink-0">
<img v-if="product.image" :src="'storage/uploads/products/' + product.image"
class="size-full object-cover dark:brightness-75" loading="lazy" />
<PlaceholderPattern v-else />
</div>
{{ product.title }}
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
</ButtonGroup>
</div>