mirror of
http://172.20.10.11:3000/gitadmin/INSIGHT-MVP.git
synced 2026-06-24 23:56:40 +02:00
fix(crm): ContactDetailPage Layout-Anpassungen
- Name (Unternehmen) in Klammern in der Überschrift - Grüner Punkt → "● Aktiv"/"● Inaktiv" Badge - Position als Subtitle unterhalb des Namens - Lexware-Card rechts neben Kontaktdaten (260px) - Aktivitäten als volle Breite unterhalb der Kontaktdaten Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ec9f3ea364
commit
bff4419c27
2 changed files with 148 additions and 104 deletions
|
|
@ -38,7 +38,8 @@
|
|||
.nameRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.625rem;
|
||||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.name {
|
||||
|
|
@ -48,22 +49,78 @@
|
|||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.nameCompany {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 400;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 0.9375rem;
|
||||
color: var(--color-text-muted);
|
||||
margin: 0.2rem 0 0;
|
||||
margin: 0.25rem 0 0;
|
||||
}
|
||||
|
||||
/* ---- Main layout ---- */
|
||||
.layout {
|
||||
/* ---- Status badges ---- */
|
||||
.badgeActive {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.3rem;
|
||||
padding: 0.1875rem 0.5rem;
|
||||
background: #dcfce7;
|
||||
color: #166534;
|
||||
border-radius: 9999px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.badgeActive::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: #16a34a;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.badgeInactive {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.3rem;
|
||||
padding: 0.1875rem 0.5rem;
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
border-radius: 9999px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.badgeInactive::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: #dc2626;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* ---- Top row: Kontaktdaten + Lexware ---- */
|
||||
.topRow {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr minmax(380px, 40%);
|
||||
grid-template-columns: 1fr 260px;
|
||||
gap: 1.5rem;
|
||||
align-items: start;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.layout {
|
||||
.topRow {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,11 +126,6 @@ export function ContactDetailPage() {
|
|||
const activities: Activity[] = contact.activities ?? [];
|
||||
const deals = dealsData?.data ?? [];
|
||||
|
||||
// Subtitle: "Position @ Unternehmen"
|
||||
const subtitle = [contact.position, contact.company?.name]
|
||||
.filter(Boolean)
|
||||
.join(' @ ');
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Back link */}
|
||||
|
|
@ -151,27 +146,26 @@ export function ContactDetailPage() {
|
|||
{/* ── Header ── */}
|
||||
<div className={styles.header}>
|
||||
<div className={styles.headerLeft}>
|
||||
<div>
|
||||
<div className={styles.nameRow}>
|
||||
<h1 className={styles.name}>{contactDisplayName(contact)}</h1>
|
||||
<span
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: '50%',
|
||||
background: contact.isActive
|
||||
? 'var(--color-success)'
|
||||
: 'var(--color-error)',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
title={contact.isActive ? 'Aktiv' : 'Inaktiv'}
|
||||
/>
|
||||
</div>
|
||||
{subtitle && (
|
||||
<p className={styles.subtitle}>{subtitle}</p>
|
||||
)}
|
||||
<div className={styles.nameRow}>
|
||||
<h1 className={styles.name}>
|
||||
{contactDisplayName(contact)}
|
||||
{contact.company && (
|
||||
<span className={styles.nameCompany}>
|
||||
{' '}({contact.company.name})
|
||||
</span>
|
||||
)}
|
||||
</h1>
|
||||
<span
|
||||
className={
|
||||
contact.isActive ? styles.badgeActive : styles.badgeInactive
|
||||
}
|
||||
>
|
||||
{contact.isActive ? 'Aktiv' : 'Inaktiv'}
|
||||
</span>
|
||||
</div>
|
||||
{contact.position && (
|
||||
<p className={styles.subtitle}>{contact.position}</p>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '0.5rem', flexShrink: 0 }}>
|
||||
<button
|
||||
|
|
@ -205,9 +199,8 @@ export function ContactDetailPage() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── Two-column layout ── */}
|
||||
<div className={styles.layout}>
|
||||
{/* ── Left column ── */}
|
||||
{/* ── Top row: Kontaktdaten (wide) + Lexware (small) ── */}
|
||||
<div className={styles.topRow}>
|
||||
<div>
|
||||
{/* Contact data card */}
|
||||
<div className={styles.card}>
|
||||
|
|
@ -525,82 +518,76 @@ export function ContactDetailPage() {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* Lexware Office */}
|
||||
<div style={{ marginTop: '1.5rem' }}>
|
||||
<LexwareSection
|
||||
entityType="contact"
|
||||
entityId={contact.id}
|
||||
lexwareContactId={contact.lexwareContactId ?? null}
|
||||
lexwareSyncedAt={contact.lexwareSyncedAt ?? null}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── Right column: Activities ── */}
|
||||
<div className={styles.card}>
|
||||
<div
|
||||
{/* ── Right: Lexware (compact) ── */}
|
||||
<div>
|
||||
<LexwareSection
|
||||
entityType="contact"
|
||||
entityId={contact.id}
|
||||
lexwareContactId={contact.lexwareContactId ?? null}
|
||||
lexwareSyncedAt={contact.lexwareSyncedAt ?? null}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── Aktivitäten (full width below) ── */}
|
||||
<div className={styles.card} style={{ marginTop: '1.5rem' }}>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: '1rem',
|
||||
}}
|
||||
>
|
||||
<h2 className={styles.cardTitle} style={{ margin: 0 }}>
|
||||
Aktivitäten
|
||||
</h2>
|
||||
<button
|
||||
onClick={() => setActivityOpen(true)}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: '1rem',
|
||||
padding: '0.25rem 0.625rem',
|
||||
fontSize: '0.8125rem',
|
||||
background: 'var(--color-primary)',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: 'var(--radius-sm)',
|
||||
cursor: 'pointer',
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
<h2 className={styles.cardTitle} style={{ margin: 0 }}>
|
||||
Aktivitäten
|
||||
</h2>
|
||||
<button
|
||||
onClick={() => setActivityOpen(true)}
|
||||
style={{
|
||||
padding: '0.25rem 0.625rem',
|
||||
fontSize: '0.8125rem',
|
||||
background: 'var(--color-primary)',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: 'var(--radius-sm)',
|
||||
cursor: 'pointer',
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
+ Neu
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{activities.length === 0 ? (
|
||||
<p
|
||||
style={{
|
||||
color: 'var(--color-text-muted)',
|
||||
fontSize: '0.875rem',
|
||||
}}
|
||||
>
|
||||
Keine Aktivitäten vorhanden
|
||||
</p>
|
||||
) : (
|
||||
<div className={styles.timeline}>
|
||||
{activities.map((act) => (
|
||||
<div key={act.id} className={styles.timelineItem}>
|
||||
<div className={styles.timelineIcon}>
|
||||
{activityIcon(act.type)}
|
||||
</div>
|
||||
<div className={styles.timelineContent}>
|
||||
<div className={styles.timelineSubject}>
|
||||
{act.subject}
|
||||
</div>
|
||||
<div className={styles.timelineMeta}>
|
||||
{ACTIVITY_TYPE_LABELS[act.type]} ·{' '}
|
||||
{formatDate(act.createdAt)}
|
||||
</div>
|
||||
{act.description && (
|
||||
<div className={styles.timelineDesc}>
|
||||
{act.description}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
+ Neu
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{activities.length === 0 ? (
|
||||
<p style={{ color: 'var(--color-text-muted)', fontSize: '0.875rem' }}>
|
||||
Keine Aktivitäten vorhanden
|
||||
</p>
|
||||
) : (
|
||||
<div className={styles.timeline}>
|
||||
{activities.map((act) => (
|
||||
<div key={act.id} className={styles.timelineItem}>
|
||||
<div className={styles.timelineIcon}>
|
||||
{activityIcon(act.type)}
|
||||
</div>
|
||||
<div className={styles.timelineContent}>
|
||||
<div className={styles.timelineSubject}>{act.subject}</div>
|
||||
<div className={styles.timelineMeta}>
|
||||
{ACTIVITY_TYPE_LABELS[act.type]} ·{' '}
|
||||
{formatDate(act.createdAt)}
|
||||
</div>
|
||||
{act.description && (
|
||||
<div className={styles.timelineDesc}>
|
||||
{act.description}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* ── Modals ── */}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue