feat: vollständige Mobile-Optimierung — Bottom-Nav, responsive Grids, CoPilot Sheet

This commit is contained in:
ATLAS Deploy 2026-07-07 21:22:42 +00:00
parent e9fa87f5a9
commit 63fed9fb9c
14 changed files with 485 additions and 118 deletions

View file

@ -1,13 +1,15 @@
/**
* AppShell 3-Spalten-Layout
* Bloomberg Terminal × Raumschiff-Brücke
*
* Layout: Sidebar (160px) | Content (flex-grow) | CoPilot (320px)
* AppShell Responsive Layout
* Desktop: Sidebar (160px) | Content | CoPilot (320px)
* Mobile: Top-Bar | Content | Bottom-Nav + Slide-up CoPilot
*/
import { useState } from 'react';
import { useState, useEffect } from 'react';
import { Sidebar } from './Sidebar';
import { BottomNav } from './BottomNav';
import { CoPilotPanel } from './CoPilotPanel';
import { CoPilotSheet } from './CoPilotSheet';
import { Sparkles, Menu } from 'lucide-react';
interface AppShellProps {
children: React.ReactNode;
@ -15,113 +17,170 @@ interface AppShellProps {
}
export function AppShell({ children, currentPath }: AppShellProps) {
const [copilotOpen, setCopilotOpen] = useState(true);
const [copilotOpen, setCopilotOpen] = useState(false);
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const [isMobile, setIsMobile] = useState(false);
useEffect(() => {
const check = () => setIsMobile(window.innerWidth < 1024);
check();
window.addEventListener('resize', check);
return () => window.removeEventListener('resize', check);
}, []);
// Desktop: Co-Pilot default open
useEffect(() => {
if (!isMobile) setCopilotOpen(true);
else setCopilotOpen(false);
}, [isMobile]);
return (
<div
className="h-screen flex overflow-hidden"
className="h-screen flex flex-col overflow-hidden"
style={{ background: 'var(--atlas-navy)' }}
>
{/* Left Sidebar Navigation */}
<Sidebar currentPath={currentPath} />
{/* Main Content Area */}
<main
className="flex-1 flex flex-col overflow-hidden"
style={{ borderLeft: '1px solid var(--atlas-border)' }}
{/* ── TOP BAR (immer sichtbar) ── */}
<div
className="flex items-center justify-between px-3 flex-shrink-0"
style={{
background: 'var(--atlas-surface)',
borderBottom: '1px solid var(--atlas-border)',
minHeight: '44px',
zIndex: 40,
}}
>
{/* Top Bar */}
<div
className="flex items-center justify-between px-4 py-2 flex-shrink-0"
style={{
background: 'var(--atlas-surface)',
borderBottom: '1px solid var(--atlas-border)',
minHeight: '44px'
}}
>
<div className="flex items-center gap-2">
<span
className="text-xs font-mono"
{/* Left: Logo + Breadcrumb */}
<div className="flex items-center gap-2">
{/* Mobile: Hamburger */}
{isMobile && (
<button
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
className="p-1 rounded mr-1"
style={{ color: 'var(--muted-foreground)' }}
>
ATLAS OS
</span>
<span style={{ color: 'var(--atlas-border)' }}></span>
<span
className="text-xs font-mono"
style={{ color: 'var(--atlas-cyan)' }}
>
Social Command Center
<Menu size={18} />
</button>
)}
<span
className="text-xs font-mono hidden sm:inline"
style={{ color: 'var(--muted-foreground)' }}
>
ATLAS OS
</span>
<span className="hidden sm:inline" style={{ color: 'var(--atlas-border)' }}></span>
<span
className="text-xs font-mono"
style={{ color: 'var(--atlas-cyan)' }}
>
Social
</span>
</div>
{/* Right: Status + Clock + CoPilot Toggle */}
<div className="flex items-center gap-2">
<div className="flex items-center gap-1.5">
<div className="atlas-status-dot online" />
<span className="text-xs hidden sm:inline" style={{ color: 'var(--muted-foreground)' }}>
Postiz
</span>
</div>
<div className="flex items-center gap-3">
{/* System Status */}
<div className="flex items-center gap-1.5">
<div className="atlas-status-dot online" />
<span className="text-xs" style={{ color: 'var(--muted-foreground)' }}>
Postiz
</span>
</div>
<div className="h-3 w-px hidden sm:block" style={{ background: 'var(--atlas-border)' }} />
<LiveClock className="hidden sm:inline" />
<div className="h-3 w-px hidden sm:block" style={{ background: 'var(--atlas-border)' }} />
{/* Co-Pilot Toggle */}
<button
onClick={() => setCopilotOpen(!copilotOpen)}
className="flex items-center gap-1.5 text-xs px-2 py-1 rounded transition-colors"
style={{
color: copilotOpen ? 'var(--atlas-cyan)' : 'var(--muted-foreground)',
background: copilotOpen ? 'oklch(0.82 0.18 210 / 0.08)' : 'transparent',
border: `1px solid ${copilotOpen ? 'oklch(0.82 0.18 210 / 0.25)' : 'var(--atlas-border)'}`,
fontFamily: 'var(--font-mono)',
}}
>
<Sparkles size={11} />
<span className="hidden sm:inline">Co-Pilot</span>
</button>
</div>
</div>
{/* ── MAIN BODY ── */}
<div className="flex flex-1 overflow-hidden">
{/* Desktop Sidebar */}
{!isMobile && (
<Sidebar currentPath={currentPath} />
)}
{/* Mobile Slide-out Sidebar */}
{isMobile && mobileMenuOpen && (
<>
<div
className="h-3 w-px"
style={{ background: 'var(--atlas-border)' }}
className="fixed inset-0 z-40"
style={{ background: 'rgba(0,0,0,0.6)', backdropFilter: 'blur(2px)' }}
onClick={() => setMobileMenuOpen(false)}
/>
{/* Live Clock */}
<LiveClock />
<div
className="h-3 w-px"
style={{ background: 'var(--atlas-border)' }}
/>
{/* Co-Pilot Toggle */}
<button
onClick={() => setCopilotOpen(!copilotOpen)}
className="text-xs px-2 py-1 rounded transition-colors"
className="fixed left-0 top-0 bottom-0 z-50 flex flex-col"
style={{
color: copilotOpen ? 'var(--atlas-cyan)' : 'var(--muted-foreground)',
background: copilotOpen ? 'oklch(0.82 0.18 210 / 0.08)' : 'transparent',
border: `1px solid ${copilotOpen ? 'oklch(0.82 0.18 210 / 0.25)' : 'var(--atlas-border)'}`,
fontFamily: 'var(--font-mono)',
width: '220px',
background: 'var(--atlas-navy)',
borderRight: '1px solid var(--atlas-border)',
animation: 'slideInLeft 0.2s cubic-bezier(0.23,1,0.32,1)',
}}
>
Co-Pilot {copilotOpen ? '▶' : '◀'}
</button>
<Sidebar currentPath={currentPath} onNavigate={() => setMobileMenuOpen(false)} />
</div>
</>
)}
{/* Content */}
<main
className="flex-1 flex flex-col overflow-hidden"
style={{ borderLeft: isMobile ? 'none' : '1px solid var(--atlas-border)' }}
>
<div
className="flex-1 overflow-auto atlas-scrollbar"
style={{ paddingBottom: isMobile ? '64px' : '0' }}
>
{children}
</div>
</div>
</main>
{/* Page Content */}
<div className="flex-1 overflow-auto atlas-scrollbar">
{children}
</div>
</main>
{/* Desktop Co-Pilot Panel */}
{!isMobile && copilotOpen && (
<CoPilotPanel onClose={() => setCopilotOpen(false)} />
)}
</div>
{/* Right Co-Pilot Panel */}
{copilotOpen && (
<CoPilotPanel onClose={() => setCopilotOpen(false)} />
{/* ── MOBILE BOTTOM NAV ── */}
{isMobile && (
<BottomNav currentPath={currentPath} />
)}
{/* ── MOBILE CO-PILOT SHEET ── */}
{isMobile && (
<CoPilotSheet open={copilotOpen} onClose={() => setCopilotOpen(false)} />
)}
</div>
);
}
function LiveClock() {
function LiveClock({ className = '' }: { className?: string }) {
const [time, setTime] = useState(new Date());
// Update every second
useState(() => {
useEffect(() => {
const interval = setInterval(() => setTime(new Date()), 1000);
return () => clearInterval(interval);
});
}, []);
return (
<span
className="text-xs"
className={`text-xs ${className}`}
style={{
fontFamily: 'var(--font-mono)',
color: 'var(--muted-foreground)'
color: 'var(--muted-foreground)',
}}
>
{time.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit', second: '2-digit' })}

View file

@ -0,0 +1,84 @@
/**
* BottomNav Mobile Bottom Navigation
* Zeigt die 5 wichtigsten Seiten als Icons
* "Mehr" öffnet das volle Menü
*/
import { Link } from 'wouter';
import {
LayoutDashboard,
Zap,
FileEdit,
Calendar,
BarChart2,
} from 'lucide-react';
interface BottomNavProps {
currentPath: string;
}
const primaryNav = [
{ label: 'War Room', path: '/', icon: LayoutDashboard },
{ label: 'Signale', path: '/signals', icon: Zap },
{ label: 'Drafts', path: '/drafts', icon: FileEdit },
{ label: 'Kalender', path: '/calendar', icon: Calendar },
{ label: 'Analytics', path: '/analytics', icon: BarChart2 },
];
export function BottomNav({ currentPath }: BottomNavProps) {
return (
<nav
className="fixed bottom-0 left-0 right-0 flex items-center justify-around"
style={{
background: 'var(--atlas-surface)',
borderTop: '1px solid var(--atlas-border)',
height: '60px',
zIndex: 50,
paddingBottom: 'env(safe-area-inset-bottom)',
}}
>
{primaryNav.map(item => {
const isActive = item.path === '/'
? currentPath === '/'
: currentPath.startsWith(item.path);
const Icon = item.icon;
return (
<Link key={item.path} href={item.path}>
<div
className="flex flex-col items-center justify-center gap-0.5 px-3 py-1 rounded-lg transition-all"
style={{
minWidth: '56px',
color: isActive ? 'var(--atlas-cyan)' : 'var(--muted-foreground)',
background: isActive ? 'oklch(0.82 0.18 210 / 0.08)' : 'transparent',
}}
>
<Icon size={20} />
<span
style={{
fontSize: '0.6rem',
fontFamily: 'var(--font-body)',
letterSpacing: '0.02em',
fontWeight: isActive ? 600 : 400,
}}
>
{item.label}
</span>
{isActive && (
<div
className="absolute bottom-0 rounded-full"
style={{
width: '4px',
height: '4px',
background: 'var(--atlas-cyan)',
boxShadow: '0 0 6px var(--atlas-cyan)',
}}
/>
)}
</div>
</Link>
);
})}
</nav>
);
}

View file

@ -0,0 +1,71 @@
/**
* CoPilotSheet Mobile Slide-up Sheet für den Co-Pilot
* Öffnet von unten als Overlay auf Mobile
*/
import { useEffect, useRef } from 'react';
import { CoPilotPanel } from './CoPilotPanel';
interface CoPilotSheetProps {
open: boolean;
onClose: () => void;
}
export function CoPilotSheet({ open, onClose }: CoPilotSheetProps) {
const overlayRef = useRef<HTMLDivElement>(null);
// Prevent body scroll when open
useEffect(() => {
if (open) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = '';
}
return () => { document.body.style.overflow = ''; };
}, [open]);
if (!open) return null;
return (
<>
{/* Backdrop */}
<div
ref={overlayRef}
className="fixed inset-0 z-50"
style={{ background: 'rgba(0,0,0,0.6)', backdropFilter: 'blur(2px)' }}
onClick={onClose}
/>
{/* Sheet */}
<div
className="fixed left-0 right-0 bottom-0 z-50 flex flex-col"
style={{
height: '85vh',
borderRadius: '16px 16px 0 0',
overflow: 'hidden',
animation: 'slideInUp 0.28s cubic-bezier(0.23,1,0.32,1)',
}}
>
{/* Drag Handle */}
<div
className="flex justify-center pt-2 pb-1 flex-shrink-0"
style={{ background: 'var(--atlas-surface)' }}
>
<div
className="rounded-full"
style={{
width: '36px',
height: '4px',
background: 'var(--atlas-border)',
}}
/>
</div>
{/* Co-Pilot Panel — full height */}
<div className="flex-1 overflow-hidden" style={{ display: 'flex', flexDirection: 'column' }}>
<CoPilotPanel onClose={onClose} />
</div>
</div>
</>
);
}

View file

@ -48,9 +48,10 @@ const systemNav: NavItem[] = [
interface SidebarProps {
currentPath: string;
onNavigate?: () => void;
}
export function Sidebar({ currentPath }: SidebarProps) {
export function Sidebar({ currentPath, onNavigate }: SidebarProps) {
return (
<aside
className="flex flex-col flex-shrink-0 overflow-hidden"
@ -89,7 +90,7 @@ export function Sidebar({ currentPath }: SidebarProps) {
<div className="mb-2">
<div className="atlas-section-header">Mission</div>
{missionNav.map(item => (
<NavLink key={item.path} item={item} currentPath={currentPath} />
<NavLink key={item.path} item={item} currentPath={currentPath} onNavigate={onNavigate} />
))}
</div>
@ -99,7 +100,7 @@ export function Sidebar({ currentPath }: SidebarProps) {
<div className="my-2">
<div className="atlas-section-header">Content</div>
{contentNav.map(item => (
<NavLink key={item.path} item={item} currentPath={currentPath} />
<NavLink key={item.path} item={item} currentPath={currentPath} onNavigate={onNavigate} />
))}
</div>
@ -108,7 +109,7 @@ export function Sidebar({ currentPath }: SidebarProps) {
{/* System Section */}
<div className="mt-2">
{systemNav.map(item => (
<NavLink key={item.path} item={item} currentPath={currentPath} />
<NavLink key={item.path} item={item} currentPath={currentPath} onNavigate={onNavigate} />
))}
</div>
</nav>
@ -170,7 +171,7 @@ export function Sidebar({ currentPath }: SidebarProps) {
);
}
function NavLink({ item, currentPath }: { item: NavItem; currentPath: string }) {
function NavLink({ item, currentPath, onNavigate }: { item: NavItem; currentPath: string; onNavigate?: () => void }) {
const isActive = item.path === '/'
? currentPath === '/'
: currentPath.startsWith(item.path);
@ -179,7 +180,7 @@ function NavLink({ item, currentPath }: { item: NavItem; currentPath: string })
return (
<Link href={item.path}>
<div className={`atlas-nav-item mx-1 ${isActive ? 'active' : ''}`}>
<div className={`atlas-nav-item mx-1 ${isActive ? 'active' : ''}`} onClick={onNavigate}>
<Icon size={14} />
<span>{item.label}</span>
</div>

View file

@ -491,3 +491,155 @@
animation: none;
}
}
/* ── Mobile Animations ── */
@keyframes slideInLeft {
from { transform: translateX(-100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
@keyframes slideInUp {
from { transform: translateY(100%); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
/* ── Mobile Responsive Overrides ── */
@media (max-width: 1023px) {
/* KPI Cards — horizontal scroll on mobile */
.atlas-kpi-grid {
display: flex !important;
overflow-x: auto !important;
gap: 0.75rem !important;
padding-bottom: 0.5rem !important;
scrollbar-width: none !important;
-ms-overflow-style: none !important;
}
.atlas-kpi-grid::-webkit-scrollbar { display: none; }
.atlas-kpi-grid > * {
flex-shrink: 0 !important;
min-width: 160px !important;
}
/* Tables — horizontal scroll */
.atlas-table-container {
overflow-x: auto !important;
-webkit-overflow-scrolling: touch !important;
}
/* Page padding — tighter on mobile */
.atlas-page {
padding: 1rem !important;
}
/* Charts — full width */
.atlas-chart-grid {
grid-template-columns: 1fr !important;
}
/* Signal Cards — single column */
.atlas-signal-grid {
grid-template-columns: 1fr !important;
}
/* Campaign Cards — single column */
.atlas-campaign-grid {
grid-template-columns: 1fr !important;
}
/* Draft Studio — stacked layout */
.atlas-draft-layout {
flex-direction: column !important;
}
.atlas-draft-editor {
width: 100% !important;
max-height: 50vh !important;
overflow-y: auto !important;
}
.atlas-draft-preview {
width: 100% !important;
}
/* Calendar — compact */
.atlas-calendar-grid {
font-size: 0.7rem !important;
}
.atlas-calendar-cell {
min-height: 60px !important;
padding: 0.25rem !important;
}
/* Co-Pilot Panel — full width on mobile (via Sheet) */
.atlas-copilot-desktop {
display: none !important;
}
/* Top bar breadcrumb — compact */
.atlas-topbar-breadcrumb {
display: none !important;
}
/* Sidebar — hidden on mobile (use BottomNav) */
.atlas-sidebar-desktop {
display: none !important;
}
}
/* ── Touch Optimizations ── */
@media (hover: none) and (pointer: coarse) {
/* Larger tap targets */
.atlas-nav-item {
min-height: 44px !important;
padding: 0.625rem 0.75rem !important;
}
button {
min-height: 36px;
}
/* Remove hover states that don't apply on touch */
.atlas-nav-item:hover {
background: transparent !important;
}
.atlas-nav-item.active {
background: oklch(0.82 0.18 210 / 0.1) !important;
}
}
/* ── KPI horizontal scroll on mobile ── */
.atlas-kpi-scroll {
scrollbar-width: none;
-ms-overflow-style: none;
}
.atlas-kpi-scroll::-webkit-scrollbar { display: none; }
.atlas-kpi-scroll > * {
min-width: 150px;
}
/* ── Tables: horizontal scroll wrapper ── */
.atlas-table-scroll {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.atlas-table-scroll table {
min-width: 600px;
}
/* ── Page padding responsive ── */
@media (max-width: 640px) {
.p-5 { padding: 0.875rem !important; }
.p-4 { padding: 0.75rem !important; }
.space-y-5 > * + * { margin-top: 0.875rem !important; }
.space-y-4 > * + * { margin-top: 0.75rem !important; }
/* Header: hide subtitle on very small screens */
.atlas-page-subtitle {
display: none;
}
/* Compact header on mobile */
.atlas-page-header {
flex-direction: column !important;
align-items: flex-start !important;
gap: 0.5rem !important;
}
.atlas-page-header > div:last-child {
flex-wrap: wrap;
}
}

View file

@ -106,7 +106,7 @@ export default function Analytics() {
</div>
{/* KPI Bar */}
<div className="grid grid-cols-5 gap-3">
<div className="grid grid-cols-2 lg:grid-cols-5 gap-3">
{[
{ label: 'Reichweite gesamt', value: '2,48 Mio.', trend: '↑ 28%', color: '#60a5fa', icon: '👥' },
{ label: 'Engagement-Rate', value: '4,32 %', trend: '↑ 15%', color: '#e1306c', icon: '❤️' },
@ -126,9 +126,9 @@ export default function Analytics() {
</div>
{/* Charts Row */}
<div className="grid grid-cols-3 gap-4">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
{/* Performance über Zeit */}
<div className="col-span-2 atlas-card p-4">
<div className="col-span-1 lg:col-span-2 atlas-card p-4">
<div className="flex items-center justify-between mb-3">
<h2 className="text-sm font-semibold" style={{ fontFamily: 'var(--font-display)', color: 'var(--foreground)' }}>Performance über Zeit</h2>
<div className="flex items-center gap-2">
@ -202,9 +202,9 @@ export default function Analytics() {
</div>
{/* Content Performance Table + Funnel */}
<div className="grid grid-cols-3 gap-4">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
{/* Table */}
<div className="col-span-2 atlas-card overflow-hidden">
<div className="col-span-1 lg:col-span-2 atlas-card overflow-hidden">
<div className="p-3 flex items-center justify-between" style={{ borderBottom: '1px solid var(--atlas-border)' }}>
<h2 className="text-sm font-semibold" style={{ fontFamily: 'var(--font-display)', color: 'var(--foreground)' }}>Content Performance</h2>
<div className="flex items-center gap-2">
@ -214,7 +214,7 @@ export default function Analytics() {
<button className="atlas-btn-ghost text-xs gap-1"><Download size={11} /> Exportieren</button>
</div>
</div>
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
<div className="atlas-table-scroll"><table style={{ width: '100%', borderCollapse: 'collapse' }}>
<thead>
<tr style={{ borderBottom: '1px solid var(--atlas-border)' }}>
{['Inhalt', 'Plattform', 'Impressionen', 'Engagement', 'Klicks', 'Umsatz'].map(h => (

View file

@ -81,7 +81,7 @@ export default function Approvals() {
</div>
{/* KPI Bar */}
<div className="grid grid-cols-4 gap-3">
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3">
{[
{ label: 'Offene Freigaben', value: '48', trend: '↑ 12% vs. gestern', color: '#a78bfa' },
{ label: 'Dringend', value: '7', trend: '↑ 75% vs. gestern', color: '#ef4444' },
@ -131,7 +131,7 @@ export default function Approvals() {
Freigaben-Queue (48)
</span>
</div>
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
<div className="atlas-table-scroll"><table style={{ width: '100%', borderCollapse: 'collapse' }}>
<thead>
<tr style={{ borderBottom: '1px solid var(--atlas-border)' }}>
{['Priorität', 'Vorschau', 'Titel', 'Plattform', 'Produkt', 'Affiliate', 'Geplant für', 'Erstellt von', 'Status', 'Aktionen'].map(h => (

View file

@ -125,7 +125,7 @@ export default function Calendar() {
</div>
{/* KPI Bar */}
<div className="grid grid-cols-4 gap-3">
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3">
{[
{ label: 'Geplante Beiträge', value: '24', trend: '↑ 14% vs. letzte Woche', color: '#60a5fa' },
{ label: 'Ausstehende Freigaben', value: '7', trend: '↑ 2 vs. letzte Woche', color: '#a78bfa' },

View file

@ -82,7 +82,7 @@ export default function Campaigns() {
</div>
{/* KPI Bar */}
<div className="grid grid-cols-4 gap-3">
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3">
{[
{ label: 'Aktive Kampagnen', value: '12', trend: '↑ 20% vs. letzte Woche', color: '#60a5fa' },
{ label: 'Geplante Assets', value: '86', trend: '↑ 16% vs. letzte Woche', color: '#a78bfa' },
@ -103,7 +103,7 @@ export default function Campaigns() {
<div className="flex items-center justify-between">
<h2 className="text-sm font-semibold" style={{ fontFamily: 'var(--font-display)', color: 'var(--foreground)' }}>Kampagnen-Übersicht</h2>
</div>
<div className="grid grid-cols-2 gap-3">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
{CAMPAIGNS.map(c => (
<div key={c.id} className="atlas-card atlas-card-hover overflow-hidden cursor-pointer">
<div className="relative" style={{ height: '100px' }}>

View file

@ -187,7 +187,7 @@ export default function Drafts() {
</div>
</div>
<div className="atlas-card p-3 space-y-2">
<div className="grid grid-cols-3 gap-2">
<div className="grid grid-cols-2 lg:grid-cols-3 gap-2">
<div>
<div className="text-xs mb-1" style={{ color: 'var(--muted-foreground)', fontSize: '0.6rem' }}>Campaign</div>
<input className="atlas-input text-xs" value="shelly-plug-s-gen3-launch" readOnly />
@ -260,7 +260,7 @@ export default function Drafts() {
{/* Freigabe Status */}
<div className="atlas-card p-3">
<div className="grid grid-cols-2 gap-4">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
<div>
<div className="text-xs mb-1" style={{ color: 'var(--muted-foreground)', fontSize: '0.6rem' }}>Status</div>
<span className="atlas-badge-cyan">Entwurf</span>

View file

@ -74,7 +74,7 @@ export default function Products() {
</div>
{/* KPI Bar */}
<div className="grid grid-cols-4 gap-3">
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3">
{[
{ label: 'Produkte aktiv', value: '156', trend: '↑ 12% vs. letzte Woche', color: '#60a5fa' },
{ label: 'Mit Affiliate-Link', value: '128', trend: '↑ 18% vs. letzte Woche', color: '#a78bfa' },
@ -124,7 +124,7 @@ export default function Products() {
{/* Product Table */}
<div className="flex-1 min-w-0">
<div className="atlas-card overflow-hidden">
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
<div className="atlas-table-scroll"><table style={{ width: '100%', borderCollapse: 'collapse' }}>
<thead>
<tr style={{ borderBottom: '1px solid var(--atlas-border)' }}>
{['Produkt', 'Kategorie', 'Preis', 'Händler', 'Verfügbarkeit', 'Content Score ⓘ', 'Affiliate', 'Aktionen'].map(h => (

View file

@ -68,7 +68,7 @@ export default function Settings() {
</div>
{/* KPI Bar */}
<div className="grid grid-cols-4 gap-3">
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3">
{[
{ label: 'Verbundene Kanäle', value: '5 / 5', sub: 'Alle Systeme verbunden', color: '#10b981' },
{ label: 'Integrationen aktiv', value: '6 / 8', sub: '75% aktiv', color: '#a78bfa' },
@ -102,7 +102,7 @@ export default function Settings() {
</div>
{/* Content Grid */}
<div className="grid grid-cols-3 gap-4">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
{/* Verbundene Kanäle */}
<div className="atlas-card p-4">
<div className="flex items-center justify-between mb-3">
@ -194,7 +194,7 @@ export default function Settings() {
<h2 className="text-sm font-semibold" style={{ fontFamily: 'var(--font-display)', color: 'var(--foreground)' }}>Branding & Assets</h2>
<button className="text-xs" style={{ color: 'var(--atlas-cyan)' }}>Bearbeiten</button>
</div>
<div className="grid grid-cols-3 gap-3 mb-3">
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 mb-3">
<div>
<div className="text-xs mb-1" style={{ color: 'var(--muted-foreground)', fontSize: '0.6rem' }}>Logo (Standard)</div>
<div className="flex items-center justify-center rounded p-2" style={{ background: 'var(--atlas-navy)', border: '1px solid var(--atlas-border)', height: '60px' }}>
@ -290,7 +290,7 @@ export default function Settings() {
<h2 className="text-sm font-semibold" style={{ fontFamily: 'var(--font-display)', color: 'var(--foreground)' }}>System-Health & Sync-Status</h2>
<button className="atlas-btn-primary text-xs">System-Logs · Alle Logs anzeigen</button>
</div>
<div className="grid grid-cols-5 gap-3">
<div className="grid grid-cols-2 lg:grid-cols-5 gap-3">
{HEALTH_ITEMS.map(item => (
<div key={item.label} className="p-3 rounded" style={{ background: 'var(--atlas-navy)', border: '1px solid var(--atlas-border)' }}>
<div className="flex items-center gap-1.5 mb-1">

View file

@ -165,7 +165,7 @@ export default function Signals() {
<div className="flex gap-4">
{/* Signal Cards Grid */}
<div className="flex-1 min-w-0">
<div className="grid grid-cols-2 gap-3">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
{filtered.map((signal, i) => (
<div
key={signal.id}

View file

@ -212,7 +212,7 @@ export default function WarRoom() {
</div>
{/* KPI Cards */}
<div className="flex gap-3">
<div className="flex gap-3 overflow-x-auto pb-1 atlas-kpi-scroll">
<KpiCard
icon={<Calendar size={16} style={{ color: '#60a5fa' }} />}
iconBg="oklch(0.6 0.15 250 / 0.15)"
@ -290,7 +290,7 @@ export default function WarRoom() {
</button>
</div>
<div className="grid grid-cols-4 gap-3">
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3">
{SIGNAL_CARDS.map((card, i) => (
<div
key={i}
@ -355,7 +355,7 @@ export default function WarRoom() {
</div>
{/* Bottom Row: Pipeline + Kanal-Status */}
<div className="grid grid-cols-2 gap-4">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
{/* Content Pipeline */}
<div className="atlas-card p-4">
<h2 className="text-sm font-semibold mb-3" style={{ fontFamily: 'var(--font-display)', color: 'var(--foreground)' }}>