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:
@@ -1,14 +1,56 @@
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import { Outlet, Link } from 'react-router-dom';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useAuth } from '../../context/AuthContext';
|
||||
import { gdprApi } from '../../services/api';
|
||||
import Sidebar from './Sidebar';
|
||||
import ScrollToTopButton from '../ScrollToTopButton';
|
||||
import { AlertTriangle, ArrowRight } from 'lucide-react';
|
||||
|
||||
function ConsentBanner() {
|
||||
const { user, isCustomerPortal } = useAuth();
|
||||
|
||||
const { data } = useQuery({
|
||||
queryKey: ['my-consent-status'],
|
||||
queryFn: () => gdprApi.getMyConsentStatus(),
|
||||
enabled: isCustomerPortal && !!user?.customerId,
|
||||
staleTime: 30_000,
|
||||
});
|
||||
|
||||
if (!isCustomerPortal || !data?.data) return null;
|
||||
|
||||
if (data.data.hasConsent) return null;
|
||||
|
||||
return (
|
||||
<div className="bg-amber-50 border-b border-amber-200 px-6 py-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<AlertTriangle className="w-5 h-5 text-amber-600 flex-shrink-0" />
|
||||
<p className="text-sm text-amber-800">
|
||||
Bitte stimmen Sie unserer Datenschutzerklärung zu, damit wir Sie beraten können.
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
to="/privacy"
|
||||
className="flex items-center gap-1 text-sm font-medium text-amber-700 hover:text-amber-900 whitespace-nowrap"
|
||||
>
|
||||
Jetzt zustimmen
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Layout() {
|
||||
return (
|
||||
<div className="flex min-h-screen">
|
||||
<Sidebar />
|
||||
<main className="flex-1 p-8 overflow-auto">
|
||||
<Outlet />
|
||||
</main>
|
||||
<div className="flex-1 flex flex-col overflow-auto">
|
||||
<ConsentBanner />
|
||||
<main className="flex-1 p-8">
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
<ScrollToTopButton />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useAuth } from '../../context/AuthContext';
|
||||
import { gdprApi } from '../../services/api';
|
||||
import {
|
||||
LayoutDashboard,
|
||||
Users,
|
||||
@@ -11,17 +13,35 @@ import {
|
||||
ClipboardList,
|
||||
MessageSquare,
|
||||
AlertCircle,
|
||||
Shield,
|
||||
FileCheck,
|
||||
UserCircle,
|
||||
Gauge,
|
||||
} from 'lucide-react';
|
||||
|
||||
export default function Sidebar() {
|
||||
const { user, logout, hasPermission, isCustomer, developerMode } = useAuth();
|
||||
const { user, logout, hasPermission, isCustomer, isCustomerPortal, developerMode } = useAuth();
|
||||
|
||||
// Prüfe ob Vollmachten vorhanden sind (nur für Portal-Kunden)
|
||||
const { data: authData } = useQuery({
|
||||
queryKey: ['my-authorizations'],
|
||||
queryFn: () => gdprApi.getMyAuthorizations(),
|
||||
enabled: isCustomerPortal,
|
||||
staleTime: 60_000,
|
||||
});
|
||||
|
||||
const hasAuthorizations = (authData?.data?.length ?? 0) > 0;
|
||||
|
||||
const navItems = [
|
||||
{ to: '/', icon: LayoutDashboard, label: 'Dashboard', show: true, end: true },
|
||||
{ to: '/my-profile', icon: UserCircle, label: 'Meine Daten', show: isCustomerPortal },
|
||||
{ to: '/customers', icon: Users, label: 'Kunden', show: hasPermission('customers:read') && !isCustomer },
|
||||
{ to: '/contracts', icon: FileText, label: 'Verträge', show: hasPermission('contracts:read'), end: true },
|
||||
{ to: '/contracts/cockpit', icon: AlertCircle, label: 'Vertrags-Cockpit', show: hasPermission('contracts:read') && !isCustomer },
|
||||
{ to: '/tasks', icon: isCustomer ? MessageSquare : ClipboardList, label: isCustomer ? 'Support-Anfragen' : 'Aufgaben', show: hasPermission('contracts:read') },
|
||||
{ to: '/my-meters', icon: Gauge, label: 'Zählerstände', show: isCustomerPortal },
|
||||
{ to: '/privacy', icon: Shield, label: 'Datenschutz', show: isCustomerPortal },
|
||||
{ to: '/authorizations', icon: FileCheck, label: 'Vollmachten', show: isCustomerPortal && hasAuthorizations },
|
||||
];
|
||||
|
||||
const developerItems = [
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ReactNode, useState } from 'react';
|
||||
import { ReactNode, useState, useEffect } from 'react';
|
||||
|
||||
interface Tab {
|
||||
id: string;
|
||||
@@ -9,10 +9,24 @@ interface Tab {
|
||||
interface TabsProps {
|
||||
tabs: Tab[];
|
||||
defaultTab?: string;
|
||||
activeTab?: string;
|
||||
onTabChange?: (tabId: string) => void;
|
||||
}
|
||||
|
||||
export default function Tabs({ tabs, defaultTab }: TabsProps) {
|
||||
const [activeTab, setActiveTab] = useState(defaultTab || tabs[0]?.id);
|
||||
export default function Tabs({ tabs, defaultTab, activeTab: controlledTab, onTabChange }: TabsProps) {
|
||||
const [internalTab, setInternalTab] = useState(defaultTab || tabs[0]?.id);
|
||||
const activeTab = controlledTab ?? internalTab;
|
||||
|
||||
useEffect(() => {
|
||||
if (controlledTab !== undefined) {
|
||||
setInternalTab(controlledTab);
|
||||
}
|
||||
}, [controlledTab]);
|
||||
|
||||
const handleTabChange = (tabId: string) => {
|
||||
setInternalTab(tabId);
|
||||
onTabChange?.(tabId);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -21,7 +35,7 @@ export default function Tabs({ tabs, defaultTab }: TabsProps) {
|
||||
{tabs.map((tab) => (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
onClick={() => handleTabChange(tab.id)}
|
||||
className={`py-4 px-1 border-b-2 font-medium text-sm whitespace-nowrap ${
|
||||
activeTab === tab.id
|
||||
? 'border-blue-500 text-blue-600'
|
||||
|
||||
Reference in New Issue
Block a user