snooze vor expired, contracts, display snoozed contracts if an item is missing, un snooze implemented, fixed invoice upload bug

This commit is contained in:
2026-02-08 13:08:58 +01:00
parent 3a9fcc5ec9
commit efe8ac25cb
39 changed files with 1369 additions and 800 deletions
@@ -12,7 +12,7 @@ import Badge from '../../components/ui/Badge';
import Input from '../../components/ui/Input';
import Modal from '../../components/ui/Modal';
import FileUpload from '../../components/ui/FileUpload';
import { Edit, Trash2, Copy, Eye, EyeOff, ArrowLeft, ArrowRight, Download, ExternalLink, Plus, ChevronDown, ChevronUp, Gauge, CheckCircle, Circle, ClipboardList, MessageSquare, Calculator, Info, X } from 'lucide-react';
import { Edit, Trash2, Copy, Eye, EyeOff, ArrowLeft, ArrowRight, Download, ExternalLink, Plus, ChevronDown, ChevronUp, Gauge, CheckCircle, Circle, ClipboardList, MessageSquare, Calculator, Info, X, BellOff } from 'lucide-react';
import { calculateConsumption, calculateCosts } from '../../utils/energyCalculations';
import CopyButton, { CopyableBlock } from '../../components/ui/CopyButton';
import type { ContractType, ContractStatus, SimCard, MeterReading, ContractTask, ContractTaskSubtask } from '../../types';
@@ -1243,6 +1243,9 @@ export default function ContractDetail() {
// Status-Info Modal
const [showStatusInfo, setShowStatusInfo] = useState(false);
// Un-Snooze Bestätigungsmodal
const [showUnsnoozeConfirm, setShowUnsnoozeConfirm] = useState(false);
const { data, isLoading } = useQuery({
queryKey: ['contract', id],
queryFn: () => contractApi.getById(contractId),
@@ -1270,6 +1273,20 @@ export default function ContractDetail() {
},
});
// Un-Snooze Mutation
const unsnoozeMutation = useMutation({
mutationFn: () => contractApi.snooze(contractId, {}),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['contract', id] });
queryClient.invalidateQueries({ queryKey: ['contract-cockpit'] });
setShowUnsnoozeConfirm(false);
},
onError: (error) => {
console.error('Un-Snooze Fehler:', error);
alert(`Fehler beim Aufheben der Zurückstellung: ${error instanceof Error ? error.message : 'Unbekannter Fehler'}`);
},
});
// Mutation für Kündigungsbestätigungsdatum
const updateCancellationDateMutation = useMutation({
mutationFn: (date: string | null) => {
@@ -1446,6 +1463,22 @@ export default function ContractDetail() {
>
<Info className="w-4 h-4" />
</button>
{/* Snooze-Hinweis wenn nextReviewDate in der Zukunft */}
{c.nextReviewDate && new Date(c.nextReviewDate) > new Date() && (
<div className="flex items-center gap-1 px-2 py-1 bg-amber-100 text-amber-800 rounded-full text-xs">
<BellOff className="w-3 h-3" />
<span>Zurückgestellt bis {new Date(c.nextReviewDate).toLocaleDateString('de-DE')}</span>
{hasPermission('contracts:update') && (
<button
onClick={() => setShowUnsnoozeConfirm(true)}
className="ml-1 p-0.5 hover:bg-amber-200 rounded"
title="Zurückstellung aufheben"
>
<X className="w-3 h-3" />
</button>
)}
</div>
)}
</div>
{c.customer && (
<p className="text-gray-500 ml-10">
@@ -2647,6 +2680,34 @@ export default function ContractDetail() {
{/* Status-Info Modal */}
<StatusInfoModal isOpen={showStatusInfo} onClose={() => setShowStatusInfo(false)} />
{/* Un-Snooze Bestätigungsmodal */}
<Modal
isOpen={showUnsnoozeConfirm}
onClose={() => setShowUnsnoozeConfirm(false)}
title="Zurückstellung aufheben?"
>
<div className="space-y-4">
<p className="text-gray-700">
Möchten Sie die Zurückstellung für diesen Vertrag wirklich aufheben?
</p>
<p className="text-sm text-gray-500">
Der Vertrag wird danach wieder im Cockpit angezeigt, wenn Fristen anstehen oder abgelaufen sind.
</p>
<div className="flex justify-end gap-3 pt-4">
<Button variant="secondary" onClick={() => setShowUnsnoozeConfirm(false)}>
Abbrechen
</Button>
<Button
variant="danger"
onClick={() => unsnoozeMutation.mutate()}
disabled={unsnoozeMutation.isPending}
>
{unsnoozeMutation.isPending ? 'Wird aufgehoben...' : 'Ja, aufheben'}
</Button>
</div>
</div>
</Modal>
</div>
);
}