diff --git a/packages/frontend/src/shell/DashboardPage.module.css b/packages/frontend/src/shell/DashboardPage.module.css index 5593613..37f9fdb 100644 --- a/packages/frontend/src/shell/DashboardPage.module.css +++ b/packages/frontend/src/shell/DashboardPage.module.css @@ -72,8 +72,228 @@ } .homeSidebar { - width: 280px; + width: 300px; flex-shrink: 0; + display: flex; + flex-direction: column; + gap: 1rem; +} + +/* ── Kompakter Messe-Ticker ── */ + +.compactMesse { + background: var(--color-bg-card); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + padding: 0.625rem 0.75rem; + box-shadow: var(--shadow-sm); +} + +.compactMesseTitle { + font-size: 0.6875rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--color-text-muted); + margin: 0 0 0.5rem 0; +} + +.compactMesseList { + display: flex; + flex-direction: column; + gap: 2px; +} + +.messeRow { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.5rem; + width: 100%; + background: none; + border: none; + border-left: 3px solid transparent; + padding: 0.3125rem 0.5rem; + border-radius: 0 var(--radius-sm) var(--radius-sm) 0; + cursor: pointer; + text-align: left; + transition: background 0.12s; +} + +.messeRow:hover { + background: var(--color-bg-subtle); +} + +.messeRowName { + font-size: 0.8125rem; + font-weight: 500; + color: var(--color-text); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + min-width: 0; +} + +.messeRowCountdown { + font-size: 0.75rem; + color: var(--color-text-muted); + white-space: nowrap; + flex-shrink: 0; +} + +/* ── Messe-Detail-Modal ── */ + +.messeModalBackdrop { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.45); + display: flex; + align-items: center; + justify-content: center; + z-index: 500; + padding: 1rem; +} + +.messeModal { + background: var(--color-bg-card); + border-radius: var(--radius-md); + box-shadow: var(--shadow-lg); + border-top: 4px solid #3b82f6; + width: 100%; + max-width: 440px; + padding: 1.25rem 1.5rem; + display: flex; + flex-direction: column; + gap: 0.875rem; +} + +.messeModalHeader { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 0.75rem; +} + +.messeModalTitle { + font-size: 1.0625rem; + font-weight: 700; + color: var(--color-text); + margin: 0 0 0.25rem 0; + line-height: 1.3; +} + +.messeStatusChip { + display: inline-flex; + padding: 0.125rem 0.5rem; + border-radius: 999px; + font-size: 0.625rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.messeChip_upcoming { + background: #dbeafe; + color: #1e40af; +} + +.messeChip_ongoing { + background: #dcfce7; + color: #166534; +} + +.messeChip_ended { + background: var(--color-bg-subtle); + color: var(--color-text-muted); +} + +.messeModalClose { + background: none; + border: none; + font-size: 1.375rem; + line-height: 1; + color: var(--color-text-muted); + cursor: pointer; + padding: 0; + flex-shrink: 0; +} + +.messeModalClose:hover { + color: var(--color-text); +} + +.messeModalCountdown { + font-size: 1.25rem; + font-weight: 700; + color: var(--color-text); + margin: 0; +} + +.messeProgressBar { + width: 100%; + height: 6px; + background: var(--color-bg-subtle); + border-radius: 3px; + overflow: hidden; +} + +.messeProgressFill { + height: 100%; + border-radius: 3px; + transition: width 0.4s ease; +} + +.messeModalMeta { + display: flex; + flex-direction: column; + gap: 0.375rem; +} + +.messeModalMetaRow { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.875rem; + color: var(--color-text-secondary); +} + +.messeModalMetaRow svg { + flex-shrink: 0; + color: var(--color-text-muted); +} + +.messeModalDesc { + font-size: 0.875rem; + color: var(--color-text-secondary); + margin: 0; + line-height: 1.55; + padding-top: 0.25rem; + border-top: 1px solid var(--color-border); +} + +.messeModalLink { + display: inline-flex; + align-items: center; + gap: 0.25rem; + font-size: 0.875rem; + font-weight: 500; + color: var(--color-primary); + text-decoration: none; +} + +.messeModalLink:hover { + text-decoration: underline; +} + +/* dark mode Messe-Modal */ +:global([data-theme='dark']) .messeChip_upcoming { + background: rgba(59, 130, 246, 0.15); + color: #93c5fd; +} + +:global([data-theme='dark']) .messeChip_ongoing { + background: rgba(34, 197, 94, 0.15); + color: #86efac; } /* ── Platzhalter für bestehenden Home-Inhalt ── */ diff --git a/packages/frontend/src/shell/DashboardPage.tsx b/packages/frontend/src/shell/DashboardPage.tsx index 43d45ab..0171d46 100644 --- a/packages/frontend/src/shell/DashboardPage.tsx +++ b/packages/frontend/src/shell/DashboardPage.tsx @@ -1,14 +1,185 @@ import { useState } from 'react'; import { useAuth } from '../auth/AuthContext'; import { WeatherWidget } from '../components/WeatherWidget'; -import { EventCountdownTiles } from '../components/EventCountdownTiles'; import { DashboardEmailTab } from './DashboardEmailTab'; import { DashboardCalendarTab, DayAgenda } from './DashboardCalendarTab'; import { DashboardTasksTab } from './DashboardTasksTab'; -import { useIntegrations, useOffice365CalendarRange } from '../crm/hooks'; -import type { M365CalendarEvent } from '../crm/types'; +import { useIntegrations, useOffice365CalendarRange, useActiveTradeEvents } from '../crm/hooks'; +import { useEventCountdown } from '../hooks/useEventCountdown'; +import type { M365CalendarEvent, TradeEvent } from '../crm/types'; import styles from './DashboardPage.module.css'; +// ── Messe-Ticker: Hilfsfunktionen ───────────────────────────────────────────── + +function isStillRelevant(event: TradeEvent): boolean { + const endDay = new Date(event.endDate); + endDay.setHours(23, 59, 59, 999); + const cutoff = new Date(endDay.getTime() + 24 * 60 * 60 * 1000); + return new Date() < cutoff; +} + +function formatDateRange(start: string, end: string): string { + const opts: Intl.DateTimeFormatOptions = { day: '2-digit', month: '2-digit', year: 'numeric' }; + return `${new Date(start).toLocaleDateString('de-DE', opts)} – ${new Date(end).toLocaleDateString('de-DE', opts)}`; +} + +// ── Kompakte Messe-Zeile (klickbar) ────────────────────────────────────────── + +function CompactMesseRow({ + event, + onClick, +}: { + event: TradeEvent; + onClick: () => void; +}) { + const countdown = useEventCountdown(event); + const color = + countdown.status === 'upcoming' + ? '#3b82f6' + : countdown.status === 'ongoing' + ? '#22c55e' + : '#9ca3af'; + + return ( + + ); +} + +// ── Messe-Detail-Popup ──────────────────────────────────────────────────────── + +function MesseDetailModal({ + event, + onClose, +}: { + event: TradeEvent; + onClose: () => void; +}) { + const countdown = useEventCountdown(event); + const color = + countdown.status === 'upcoming' + ? '#3b82f6' + : countdown.status === 'ongoing' + ? '#22c55e' + : '#9ca3af'; + + return ( +
{event.description}
+ )} + + {event.websiteUrl && ( + + Website öffnen ↗ + + )} +Kalender wird geladen…
-+ Kalender wird geladen… +
+ :INSIGHT Platform - Sprint 1 Alpha @@ -77,7 +250,7 @@ function HomeTab({ firstName, lastName, city, role }: {