mirror of
http://172.20.10.11:3000/gitadmin/INSIGHT-MVP.git
synced 2026-06-25 01:36:39 +02:00
New admin page under /crm/lexware-sync with two tabs: - Import: Search Lexware contacts and import as CRM company or contact - Export: List CRM companies/contacts and push to Lexware Office Accessible via CRM Settings page (admin-only). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
300 lines
8.8 KiB
TypeScript
300 lines
8.8 KiB
TypeScript
import { Link, Navigate } from 'react-router-dom';
|
|
import { useAuth } from '../../auth/AuthContext';
|
|
import {
|
|
useCrmSettings,
|
|
type CrmModuleKey,
|
|
} from './CrmSettingsContext';
|
|
import styles from './CrmSettingsPage.module.css';
|
|
|
|
// ============================================================
|
|
// Module definitions (UI-Beschreibung je Modul)
|
|
// ============================================================
|
|
|
|
interface ModuleDef {
|
|
key: CrmModuleKey;
|
|
name: string;
|
|
description: string;
|
|
icon: React.ReactNode;
|
|
}
|
|
|
|
const iconProps = {
|
|
width: 18,
|
|
height: 18,
|
|
viewBox: '0 0 16 16',
|
|
fill: 'none',
|
|
stroke: 'currentColor',
|
|
strokeWidth: 1.5,
|
|
strokeLinecap: 'round' as const,
|
|
strokeLinejoin: 'round' as const,
|
|
};
|
|
|
|
const MODULES: ModuleDef[] = [
|
|
{
|
|
key: 'contacts',
|
|
name: 'Kontakte',
|
|
description: 'Kontaktverwaltung (Personen & Organisationen)',
|
|
icon: (
|
|
<svg {...iconProps}>
|
|
<circle cx="8" cy="5" r="3" />
|
|
<path d="M2 14c0-2.5 2.5-4.5 6-4.5s6 2 6 4.5" />
|
|
</svg>
|
|
),
|
|
},
|
|
{
|
|
key: 'companies',
|
|
name: 'Unternehmen',
|
|
description: 'Unternehmensverwaltung mit Verknüpfung zu Kontakten & Vorgängen',
|
|
icon: (
|
|
<svg {...iconProps}>
|
|
<rect x="2" y="6" width="12" height="9" rx="1" />
|
|
<path d="M5 6V3a1 1 0 011-1h4a1 1 0 011 1v3" />
|
|
<path d="M5 9h2v2H5zM9 9h2v2H9z" />
|
|
</svg>
|
|
),
|
|
},
|
|
{
|
|
key: 'deals',
|
|
name: 'Vorgänge',
|
|
description: 'Vorgänge & Sales-Pipeline-Tracking',
|
|
icon: (
|
|
<svg {...iconProps}>
|
|
<path d="M2 2h3l2 9h6l2-6H6" />
|
|
<circle cx="7" cy="14" r="1" />
|
|
<circle cx="13" cy="14" r="1" />
|
|
</svg>
|
|
),
|
|
},
|
|
{
|
|
key: 'pipelines',
|
|
name: 'Pipelines',
|
|
description: 'Pipeline-Konfiguration & Stufen-Management',
|
|
icon: (
|
|
<svg {...iconProps}>
|
|
<rect x="1" y="2" width="14" height="3" rx="0.5" />
|
|
<rect x="1" y="7" width="10" height="3" rx="0.5" />
|
|
<rect x="1" y="12" width="6" height="3" rx="0.5" />
|
|
</svg>
|
|
),
|
|
},
|
|
{
|
|
key: 'lexware',
|
|
name: 'Lexware Office',
|
|
description: 'Lexware-Kontaktverknüpfung & Belegansicht auf Detail-Seiten',
|
|
icon: (
|
|
<svg {...iconProps}>
|
|
<rect x="1" y="3" width="14" height="10" rx="1" />
|
|
<path d="M4 7h8M4 10h5" />
|
|
<circle cx="12" cy="10" r="1" />
|
|
</svg>
|
|
),
|
|
},
|
|
];
|
|
|
|
// ============================================================
|
|
// Page Component
|
|
// ============================================================
|
|
|
|
export function CrmSettingsPage() {
|
|
const { user } = useAuth();
|
|
const { settings, toggleModule } = useCrmSettings();
|
|
|
|
// Zugriffskontrolle: nur Admins
|
|
if (
|
|
user?.role !== 'PLATFORM_ADMIN' &&
|
|
user?.role !== 'TENANT_ADMIN'
|
|
) {
|
|
return <Navigate to="/" replace />;
|
|
}
|
|
|
|
const anyDisabled = Object.values(settings.modules).some(
|
|
(m) => !m.enabled,
|
|
);
|
|
|
|
return (
|
|
<div>
|
|
{/* Zurück */}
|
|
<Link to="/" className={styles.backLink}>
|
|
<svg
|
|
width="14"
|
|
height="14"
|
|
viewBox="0 0 14 14"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="1.5"
|
|
>
|
|
<path d="M9 2L4 7l5 5" />
|
|
</svg>
|
|
Zurück zum Dashboard
|
|
</Link>
|
|
|
|
{/* Header */}
|
|
<h1 style={{ fontSize: '1.5rem', fontWeight: 600, marginBottom: '1.5rem' }}>
|
|
CRM Einstellungen
|
|
</h1>
|
|
|
|
{/* Module-Card */}
|
|
<div className={styles.card}>
|
|
<h2 className={styles.cardTitle}>Module</h2>
|
|
<p className={styles.cardDesc}>
|
|
Aktiviere oder deaktiviere einzelne CRM-Module. Deaktivierte Module
|
|
werden aus dem Menü ausgeblendet.
|
|
</p>
|
|
|
|
<div className={styles.moduleList}>
|
|
{MODULES.map((mod) => {
|
|
const enabled = settings.modules[mod.key]?.enabled ?? true;
|
|
return (
|
|
<div key={mod.key} className={styles.moduleItem}>
|
|
<div
|
|
style={{
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '0.75rem',
|
|
}}
|
|
>
|
|
<span
|
|
style={{
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
width: 36,
|
|
height: 36,
|
|
borderRadius: 'var(--radius-sm)',
|
|
background: enabled
|
|
? 'var(--color-primary-bg, #eff6ff)'
|
|
: 'var(--color-bg)',
|
|
color: enabled
|
|
? 'var(--color-primary)'
|
|
: 'var(--color-text-muted)',
|
|
transition: 'background 0.2s, color 0.2s',
|
|
}}
|
|
>
|
|
{mod.icon}
|
|
</span>
|
|
<div className={styles.moduleInfo}>
|
|
<span
|
|
className={styles.moduleName}
|
|
style={{
|
|
opacity: enabled ? 1 : 0.5,
|
|
transition: 'opacity 0.2s',
|
|
}}
|
|
>
|
|
{mod.name}
|
|
</span>
|
|
<span className={styles.moduleDesc}>{mod.description}</span>
|
|
</div>
|
|
</div>
|
|
<label className={styles.toggle}>
|
|
<input
|
|
type="checkbox"
|
|
checked={enabled}
|
|
onChange={(e) =>
|
|
toggleModule(mod.key, e.target.checked)
|
|
}
|
|
/>
|
|
<span className={styles.toggleTrack} />
|
|
</label>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
{anyDisabled && (
|
|
<div className={styles.warning}>
|
|
<svg
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 16 16"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="1.5"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
style={{ flexShrink: 0, marginTop: 1 }}
|
|
>
|
|
<path d="M8 1L1 14h14L8 1z" />
|
|
<path d="M8 6v3M8 11.5v.5" />
|
|
</svg>
|
|
<span>
|
|
Deaktivierte Module werden aus dem Menü ausgeblendet.
|
|
Bestehende Daten bleiben erhalten und sind nach Reaktivierung
|
|
wieder verfügbar.
|
|
</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Lexware Synchronisation — nur wenn Modul aktiv */}
|
|
{settings.modules.lexware?.enabled && (
|
|
<div className={styles.card}>
|
|
<h2 className={styles.cardTitle}>
|
|
<span
|
|
style={{
|
|
display: 'inline-flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
width: 24,
|
|
height: 24,
|
|
borderRadius: 4,
|
|
background: 'linear-gradient(135deg, #2563eb, #7c3aed)',
|
|
color: 'white',
|
|
fontSize: '0.5625rem',
|
|
fontWeight: 700,
|
|
letterSpacing: '0.5px',
|
|
marginRight: '0.5rem',
|
|
verticalAlign: 'middle',
|
|
}}
|
|
>
|
|
LX
|
|
</span>
|
|
Lexware Office Synchronisation
|
|
</h2>
|
|
<p className={styles.cardDesc}>
|
|
Kontakte aus Lexware Office importieren oder CRM-Daten nach Lexware
|
|
exportieren.
|
|
</p>
|
|
<Link
|
|
to="/crm/lexware-sync"
|
|
style={{
|
|
display: 'inline-flex',
|
|
alignItems: 'center',
|
|
gap: '0.5rem',
|
|
padding: '0.5rem 1rem',
|
|
background: 'var(--color-primary)',
|
|
color: 'white',
|
|
borderRadius: 'var(--radius-sm)',
|
|
textDecoration: 'none',
|
|
fontSize: '0.875rem',
|
|
fontWeight: 500,
|
|
transition: 'opacity 0.15s',
|
|
}}
|
|
>
|
|
<svg
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 16 16"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="1.5"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
>
|
|
<path d="M1 8a7 7 0 0112.9-3.8M15 8a7 7 0 01-12.9 3.8" />
|
|
<path d="M14 1v3.2h-3.2M2 15v-3.2h3.2" />
|
|
</svg>
|
|
Import / Export öffnen
|
|
</Link>
|
|
</div>
|
|
)}
|
|
|
|
{/* Platzhalter für zukünftige Einstellungen */}
|
|
<div className={styles.card} style={{ opacity: 0.6 }}>
|
|
<h2 className={styles.cardTitle}>Weitere Einstellungen</h2>
|
|
<p className={styles.cardDesc}>
|
|
Zusätzliche Konfigurationsmöglichkeiten werden in zukünftigen
|
|
Versionen verfügbar sein.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|