gdpr audit implemented, email log, vollmachten, pdf delete cancel data privacy and vollmachten, removed message no id card in engergy car, and other contracts that are not telecom contracts, added insert counter for engery

This commit is contained in:
2026-03-21 11:59:53 +01:00
parent 89cf92eaf5
commit f2876f877e
1491 changed files with 265550 additions and 1292 deletions
+45 -31
View File
@@ -1,7 +1,7 @@
import { useState, useEffect } from 'react';
import { useParams, Link, useNavigate, useLocation } from 'react-router-dom';
import { useParams, Link, useNavigate } from 'react-router-dom';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { contractApi, uploadApi, meterApi, contractTaskApi, appSettingsApi } from '../../services/api';
import { contractApi, uploadApi, meterApi, contractTaskApi, appSettingsApi, gdprApi } from '../../services/api';
import { ContractEmailsSection } from '../../components/email';
import { ContractDetailModal, ContractHistorySection } from '../../components/contracts';
import InvoicesSection from '../../components/contracts/InvoicesSection';
@@ -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, BellOff } 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, Lock, Shield } 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';
@@ -1207,17 +1207,9 @@ function ContractTaskModal({
);
}
interface LocationState {
from?: 'customer' | 'contracts' | 'cockpit';
customerId?: string;
filter?: string; // Für Cockpit-Filter
}
export default function ContractDetail() {
const { id } = useParams();
const navigate = useNavigate();
const location = useLocation();
const locationState = location.state as LocationState | null;
const queryClient = useQueryClient();
const { hasPermission, isCustomer, isCustomerPortal } = useAuth();
const contractId = parseInt(id!);
@@ -1251,6 +1243,15 @@ export default function ContractDetail() {
queryFn: () => contractApi.getById(contractId),
});
// Consent-Check für den Kunden des Vertrags (nur für Mitarbeiter relevant)
const contractCustomerId = data?.data?.customerId;
const { data: consentStatusData } = useQuery({
queryKey: ['consent-status', contractCustomerId],
queryFn: () => gdprApi.checkConsentStatus(contractCustomerId!),
enabled: !!contractCustomerId && !isCustomerPortal,
});
const hasConsentApproval = isCustomerPortal || (consentStatusData?.data?.hasConsent ?? true);
const deleteMutation = useMutation({
mutationFn: () => contractApi.delete(contractId),
onSuccess: () => {
@@ -1422,6 +1423,38 @@ export default function ContractDetail() {
const c = data.data;
// Consent-Sperrung: Vertrag nicht anzeigen wenn Kunde keine Einwilligung hat
if (!hasConsentApproval) {
return (
<div>
<div className="flex items-center gap-4 mb-6">
<Button variant="ghost" size="sm" onClick={() => navigate(-1)}>
<ArrowLeft className="w-4 h-4" />
</Button>
<h1 className="text-2xl font-bold">Vertrag {c.contractNumber}</h1>
</div>
<div className="flex flex-col items-center justify-center py-16 text-center">
<div className="bg-amber-50 border border-amber-200 rounded-full p-4 mb-4">
<Lock className="w-8 h-8 text-amber-500" />
</div>
<h2 className="text-lg font-semibold text-gray-800 mb-2">
Datenschutz-Einwilligung erforderlich
</h2>
<p className="text-sm text-gray-600 mb-6 max-w-md">
Die Vertragsdaten können nicht angezeigt werden, da der Kunde der Datenschutzerklärung noch nicht zugestimmt hat.
</p>
<Link
to={`/customers/${c.customerId}?tab=consents`}
className="inline-flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors text-sm"
>
<Shield className="w-4 h-4" />
Zum Kunden: Einwilligungen / Datenschutz
</Link>
</div>
</div>
);
}
return (
<div>
<div className="flex items-center justify-between mb-6">
@@ -1430,26 +1463,7 @@ export default function ContractDetail() {
<Button
variant="ghost"
size="sm"
onClick={() => {
// Zurück zur Herkunftsseite navigieren
if (locationState?.from === 'customer' && locationState?.customerId) {
// Kam von Kundendetail -> zurück zum Kunden mit Verträge-Tab
navigate(`/customers/${locationState.customerId}?tab=contracts`);
} else if (locationState?.from === 'cockpit') {
// Kam vom Cockpit -> zurück zum Cockpit (mit Filter falls vorhanden)
const filterParam = locationState.filter ? `?filter=${locationState.filter}` : '';
navigate(`/contracts/cockpit${filterParam}`);
} else if (locationState?.from === 'contracts') {
// Kam von Vertragsliste -> zurück zur Vertragsliste (URL-Parameter bleiben erhalten)
navigate('/contracts');
} else if (c.customer) {
// Fallback: Wenn Kunde vorhanden, zum Kunden
navigate(`/customers/${c.customer.id}?tab=contracts`);
} else {
// Fallback: Zur Vertragsliste
navigate('/contracts');
}
}}
onClick={() => navigate(-1)}
>
<ArrowLeft className="w-4 h-4" />
</Button>