Merge remote-tracking branch 'origin/main'

# Conflicts:
#	db/custom.db
#	src/components/FuseButton.tsx
#	src/components/SlideCommit.tsx
This commit is contained in:
Z User committed 2026-10-28 17:41:22 +00:00
commit 58521ca03a
23 files changed
+2622 -26

No files matched your search

+12 -4
View File
@@ -28,6 +28,7 @@ import {
} from '@/components/ui/dialog'
import { PageHeader } from '../primitives'
import { formatEuro, formatDate, formatDateTime, daysUntil } from '@/lib/format'
import { HoldToConfirm } from '../hold-to-confirm'
import { cn } from '@/lib/utils'
import { toast } from 'sonner'
@@ -312,10 +313,17 @@ export function ContractsView() {
<DialogFooter className="gap-2">
<Button variant="outline" onClick={() => setSelected(null)}>Schließen</Button>
{canSign && (
<Button onClick={() => sign(selected)} disabled={signing} className="gap-1.5">
{signing ? <Loader2 className="size-3.5 animate-spin" /> : <PenLine className="size-3.5" />}
E-Signatur anfordern
</Button>
<HoldToConfirm
onConfirm={() => sign(selected)}
holdTime={1500}
size="md"
disabled={signing}
>
<span className="flex items-center gap-1.5">
<PenLine className="size-3.5" />
E-Signatur anfordern
</span>
</HoldToConfirm>
)}
</DialogFooter>
</>
+23
View File
@@ -17,6 +17,7 @@ import {
Filter,
ArrowRight,
} from 'lucide-react'
import { SwipeableLeadCard } from '../swipeable-lead-card'
import { useFetch } from '@/hooks/use-fetch'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
@@ -207,6 +208,28 @@ export function LeadsView() {
<div className="space-y-1.5 p-2">
{filtered.map((l) => {
const meta = STATUS_META[l.status] ?? STATUS_META.pending
// Pending leads get SwipeRow (swipe to approve/reject/snooze)
if (l.status === 'pending') {
return (
<div key={l.id} className={cn('rounded-md border overflow-hidden', selected?.id === l.id && 'ring-1 ring-foreground/40')}>
<SwipeableLeadCard
lead={{
id: l.id,
companyName: l.companyName,
website: l.website,
city: l.city,
score: l.score,
scoreLabel: l.scoreLabel,
createdAt: l.createdAt,
}}
onApprove={(id) => { setSelectedId(id); review(id, 'approved') }}
onReject={(id) => { setSelectedId(id); review(id, 'rejected', 'Nicht zur Zielgruppe') }}
onSnooze={(id) => { setSelectedId(id); review(id, 'snoozed', 'Später erneut prüfen') }}
/>
</div>
)
}
// Non-pending leads keep the simple button card
return (
<button
key={l.id}
+8 -4
View File
@@ -6,6 +6,7 @@ import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Skeleton } from '@/components/ui/skeleton'
import { PageHeader } from '../primitives'
import { FuseCTA } from '../fuse-cta'
import { formatEuro, formatEuroCompact, formatDate } from '@/lib/format'
import { cn } from '@/lib/utils'
import { toast } from 'sonner'
@@ -76,10 +77,13 @@ export function RecurringView() {
title="Wiederkehrende Rechnungen"
description="Automatische Generierung von wiederkehrenden Rechnungen (Wartung/SLA-Verträge). Bei Mahnstufe 3: automatische Lizenz-Suspendierung (PRD §6.4)."
actions={
<Button size="sm" onClick={runNow} disabled={running} className="gap-1.5">
{running ? <Loader2 className="size-4 animate-spin" /> : <Play className="size-4" />}
Jetzt ausführen
</Button>
<FuseCTA
label="Jetzt ausführen"
doneLabel="Ausgeführt"
onCommit={runNow}
disabled={running}
size="sm"
/>
}
/>
+2 -4
View File
@@ -11,6 +11,7 @@ import { formatMinutes } from '@/lib/format'
import { cn } from '@/lib/utils'
import { toast } from 'sonner'
import { ChevronLeft, ChevronRight, Lock, Clock, TrendingUp, CheckCircle2, Calendar } from 'lucide-react'
import { SlideToLock } from '../slide-to-lock'
interface DayData {
date: string
@@ -83,10 +84,7 @@ export function TimesheetView() {
description="Wochen-basierte Zeiterfassung mit PM-Freigabe (Lock). Nach Freigabe können Einträge nicht mehr geändert werden — GoBD-konform."
actions={
!stats.allLocked && stats.totalEntries > 0 ? (
<Button size="sm" onClick={lockWeek} disabled={locking} className="gap-1.5">
<Lock className="size-4" />
Woche sperren
</Button>
<SlideToLock onConfirm={lockWeek} disabled={locking} />
) : stats.allLocked ? (
<Badge variant="outline" className="gap-1">
<CheckCircle2 className="size-3" />Gesperrt