supplychain-showcase / index.html
aidn's picture
Update index.html
c294e99 verified
<!DOCTYPE html>
<html lang="de" class="scroll-smooth">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Agentic Supply Chain Showcase | valantic & databricks</title>
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Tailwind Configuration -->
<script>
tailwind.config = {
theme: {
extend: {
colors: {
brand: {
dark: '#1c1b3d', // valantic inspired dark blue
light: '#2a2859',
accent: '#ff3621', // databricks inspired red
accentHover: '#e02814'
}
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
}
}
}
}
</script>
<!-- Lucide Icons -->
<script src="https://unpkg.com/lucide@latest"></script>
<style>
/* Custom animations */
.fade-in-up {
animation: fadeInUp 0.8s ease-out forwards;
opacity: 0;
transform: translateY(20px);
}
@keyframes fadeInUp {
to { opacity: 1; transform: translateY(0); }
}
.chat-message {
animation: popIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
opacity: 0;
transform: scale(0.95);
transform-origin: top left;
}
@keyframes popIn {
to { opacity: 1; transform: scale(1); }
}
.typing-indicator span {
animation: blink 1.4s infinite both;
height: 6px;
width: 6px;
background: #94a3b8; /* Slate-400 for light mode */
border-radius: 50%;
display: inline-block;
margin: 0 1px;
}
.typing-indicator span:nth-child(2) { animation-delay: 0.2s; }
.typing-indicator span:nth-child(3) { animation-delay: 0.4s; }
@keyframes blink {
0% { opacity: 0.2; transform: scale(0.8); }
20% { opacity: 1; transform: scale(1.2); }
100% { opacity: 0.2; transform: scale(0.8); }
}
/* Map Pin Animations */
.pin-active .ping {
animation: ping 1.5s cubic-bezier(0, 0, 0.2, 1) infinite;
}
@keyframes ping {
75%, 100% { transform: scale(2.5); opacity: 0; }
}
.delay-100 { animation-delay: 100ms; }
.delay-200 { animation-delay: 200ms; }
.delay-300 { animation-delay: 300ms; }
/* Scrollbar styling for chat */
.chat-scroll::-webkit-scrollbar { width: 6px; }
.chat-scroll::-webkit-scrollbar-track { background: transparent; }
.chat-scroll::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 10px; }
.chat-scroll::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
</style>
</head>
<body class="bg-gray-50 text-gray-800 font-sans antialiased">
<!-- Navigation -->
<nav class="fixed w-full z-50 bg-white/90 backdrop-blur-md shadow-sm">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-20 items-center">
<div class="flex items-center gap-4">
<span class="text-2xl font-extrabold text-brand-dark tracking-tight">valantic</span>
<span class="text-gray-300">|</span>
<span class="text-xl font-bold text-brand-accent tracking-tight">databricks</span>
</div>
<div class="hidden md:flex space-x-8">
<a href="#problem-loesung" class="text-gray-600 hover:text-brand-accent font-medium transition-colors">Die Challenge</a>
<a href="#showcase" class="text-gray-600 hover:text-brand-accent font-medium transition-colors">Interactive Demo</a>
<a href="#architektur" class="text-gray-600 hover:text-brand-accent font-medium transition-colors">Architektur</a>
</div>
<div class="hidden md:flex">
<a href="#showcase" class="bg-brand-accent hover:bg-brand-accentHover text-white px-6 py-2.5 rounded-full font-semibold transition-all shadow-md hover:shadow-lg">
Zum Showcase
</a>
</div>
</div>
</div>
</nav>
<!-- Section 1: Hero Area -->
<section class="relative pt-32 pb-20 lg:pt-48 lg:pb-32 overflow-hidden bg-brand-dark text-white">
<div class="absolute inset-0 z-0">
<div class="absolute inset-0 bg-gradient-to-r from-brand-dark via-brand-dark/90 to-brand-light/80 z-10"></div>
<img src="https://images.unsplash.com/photo-1586528116311-ad8dd3c8310d?q=80&w=2070&auto=format&fit=crop" alt="Supply Chain Background" class="w-full h-full object-cover opacity-50 mix-blend-overlay" />
</div>
<div class="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<div class="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-white/10 backdrop-blur-sm border border-white/20 text-sm font-medium mb-8 fade-in-up">
<i data-lucide="sparkles" class="w-4 h-4 text-brand-accent"></i>
IPAI KI-Festival 2026
</div>
<h1 class="text-4xl md:text-6xl lg:text-7xl font-extrabold tracking-tight mb-6 fade-in-up delay-100">
Proaktives Supply-Chain <br class="hidden md:block" />
Management mit <span class="text-transparent bg-clip-text bg-gradient-to-r from-brand-accent to-orange-400">Agentic AI</span>.
</h1>
<p class="mt-4 text-xl md:text-2xl text-gray-300 max-w-3xl mx-auto mb-10 fade-in-up delay-200">
Erleben Sie die nächste Stufe der Automatisierung: Hybrid Intelligence auf der Databricks Data Intelligence Platform.
</p>
<div class="flex flex-col sm:flex-row justify-center gap-4 fade-in-up delay-300">
<button onclick="document.getElementById('showcase').scrollIntoView({behavior: 'smooth'})" class="bg-brand-accent hover:bg-brand-accentHover text-white px-8 py-4 rounded-full font-bold text-lg transition-all shadow-lg hover:shadow-brand-accent/30 flex items-center justify-center gap-2 cursor-pointer">
Interaktive Demo starten
<i data-lucide="play" class="w-5 h-5"></i>
</button>
</div>
</div>
</section>
<!-- Section 2: Problem vs. Lösung -->
<section id="problem-loesung" class="py-24 bg-white">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center mb-16">
<h2 class="text-3xl md:text-4xl font-bold text-brand-dark mb-4">Statische Regeln treffen auf eine chaotische Welt</h2>
<div class="w-24 h-1 bg-brand-accent mx-auto rounded-full"></div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-12 lg:gap-24">
<div class="space-y-8">
<div class="flex items-center gap-3 mb-6">
<div class="w-12 h-12 rounded-full bg-red-100 flex items-center justify-center text-red-600"><i data-lucide="alert-triangle" class="w-6 h-6"></i></div>
<h3 class="text-2xl font-bold text-gray-900">Die Challenge</h3>
</div>
<div class="bg-gray-50 p-6 rounded-2xl border border-gray-100"><div class="flex gap-4"><i data-lucide="database" class="w-6 h-6 text-gray-500 mt-1"></i><div><h4 class="font-bold text-gray-900 mb-2">Informations-Silos</h4><p class="text-gray-600">Supply Chain Manager müssen statische Vertragsdaten und dynamische Echtzeit-Ereignisse manuell abgleichen.</p></div></div></div>
<div class="bg-gray-50 p-6 rounded-2xl border border-gray-100"><div class="flex gap-4"><i data-lucide="git-network" class="w-6 h-6 text-gray-500 mt-1"></i><div><h4 class="font-bold text-gray-900 mb-2">Hohe Komplexität</h4><p class="text-gray-600">Unterschiedliche Produkte haben unterschiedliche Toleranzgrenzen. Was für Produkt A kritisch ist, ist für B irrelevant.</p></div></div></div>
<div class="bg-gray-50 p-6 rounded-2xl border border-gray-100"><div class="flex gap-4"><i data-lucide="clock" class="w-6 h-6 text-gray-500 mt-1"></i><div><h4 class="font-bold text-gray-900 mb-2">Reaktives Handeln</h4><p class="text-gray-600">Risiken werden oft erst erkannt, wenn der Schaden an sensiblen Gütern bereits eingetreten ist.</p></div></div></div>
</div>
<div class="space-y-8">
<div class="flex items-center gap-3 mb-6">
<div class="w-12 h-12 rounded-full bg-green-100 flex items-center justify-center text-green-600"><i data-lucide="check-circle" class="w-6 h-6"></i></div>
<h3 class="text-2xl font-bold text-gray-900">Der Customer Benefit</h3>
</div>
<div class="bg-blue-50/50 p-6 rounded-2xl border border-blue-100"><div class="flex gap-4"><i data-lucide="monitor" class="w-6 h-6 text-brand-accent mt-1"></i><div><h4 class="font-bold text-gray-900 mb-2">Single Pane of Glass</h4><p class="text-gray-600">Konsolidierung von unstrukturiertem Firmenwissen und weltweiten Live-Daten in einer einzigen Schnittstelle.</p></div></div></div>
<div class="bg-blue-50/50 p-6 rounded-2xl border border-blue-100"><div class="flex gap-4"><i data-lucide="shield-check" class="w-6 h-6 text-brand-accent mt-1"></i><div><h4 class="font-bold text-gray-900 mb-2">Proaktives Risikomanagement</h4><p class="text-gray-600">Das System warnt, bevor ein Schaden entsteht ("Präventiver Alarm"), indem es Wettervorhersagen gegen Produkt-Toleranzen prüft.</p></div></div></div>
<div class="bg-blue-50/50 p-6 rounded-2xl border border-blue-100"><div class="flex gap-4"><i data-lucide="bot" class="w-6 h-6 text-brand-accent mt-1"></i><div><h4 class="font-bold text-gray-900 mb-2">Automatisierte Entscheidungsfindung</h4><p class="text-gray-600">24/7 Überwachung globaler Lieferketten. Der Agent empfiehlt direkt die korrekte Maßnahme.</p></div></div></div>
</div>
</div>
</div>
</section>
<!-- Section 3: Interactive Map & Showcase -->
<section id="showcase" class="py-24 bg-brand-dark text-white overflow-hidden">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="mb-12 text-center">
<span class="text-brand-accent font-semibold tracking-wider uppercase text-sm">Interaktive Demo</span>
<h2 class="text-3xl md:text-5xl font-bold mt-2 mb-4 text-white">Agentic Control Center</h2>
<p class="text-xl text-gray-300 max-w-3xl mx-auto">Wählen Sie ein aktives Transportszenario auf der Weltkarte, um zu sehen, wie das Multi-Agent System die Route in Echtzeit überwacht und schützt.</p>
</div>
<div class="grid grid-cols-1 xl:grid-cols-12 gap-8 items-start">
<!-- LEFT: Interactive Map (Light Mode) -->
<div class="xl:col-span-5 bg-white border border-gray-200 rounded-2xl p-6 shadow-2xl relative overflow-hidden flex flex-col h-[650px] text-gray-900">
<h3 class="font-bold text-xl mb-4 flex items-center gap-2">
<i data-lucide="globe" class="text-brand-accent"></i> Global Routes
</h3>
<!-- Map Container -->
<div class="relative flex-1 bg-slate-50 rounded-xl border border-slate-200 overflow-hidden mb-6 shadow-inner">
<!-- Generic abstract map background (Dark SVG works well on light BG with low opacity) -->
<img src="https://upload.wikimedia.org/wikipedia/commons/8/80/World_map_-_low_resolution.svg" class="absolute inset-0 w-full h-full object-contain opacity-30 p-4 pointer-events-none" alt="World Map">
<!-- Connecting Lines (SVG) -->
<svg class="absolute inset-0 w-full h-full pointer-events-none" id="map-lines">
<!-- Default line color is now slate-400 (#94a3b8) for light mode -->
<path d="M 50% 35% L 75% 65%" stroke="#94a3b8" stroke-width="2" stroke-dasharray="4" class="route-line transition-colors duration-300" id="line-singapur"></path>
<path d="M 50% 35% L 52% 20%" stroke="#94a3b8" stroke-width="2" stroke-dasharray="4" class="route-line transition-colors duration-300" id="line-oslo"></path>
<path d="M 50% 35% L 62% 48%" stroke="#94a3b8" stroke-width="2" stroke-dasharray="4" class="route-line transition-colors duration-300" id="line-dubai"></path>
</svg>
<!-- HQ Pin (Frankfurt) -->
<div class="absolute w-4 h-4 rounded-full bg-brand-dark z-20 shadow-[0_0_15px_rgba(28,27,61,0.5)]" style="top: 35%; left: 50%; transform: translate(-50%, -50%);">
<span class="absolute top-5 left-1/2 -translate-x-1/2 text-xs font-bold bg-white text-brand-dark px-2 py-0.5 rounded shadow-md border border-gray-200 whitespace-nowrap">HQ (FRA)</span>
</div>
<!-- Target Pins -->
<!-- 1. Singapur -->
<button onclick="playScenario('singapur')" id="pin-singapur" class="absolute w-5 h-5 rounded-full bg-brand-accent z-20 cursor-pointer transition-transform hover:scale-125 focus:outline-none pin-active group" style="top: 65%; left: 75%; transform: translate(-50%, -50%);">
<span class="absolute inset-0 rounded-full bg-brand-accent ping opacity-75"></span>
<span class="absolute top-6 left-1/2 -translate-x-1/2 text-xs font-bold bg-white text-gray-800 px-2 py-1 rounded shadow-md border border-gray-200 whitespace-nowrap transition-colors group-hover:text-brand-accent">Singapur</span>
</button>
<!-- 2. Oslo -->
<button onclick="playScenario('oslo')" id="pin-oslo" class="absolute w-5 h-5 rounded-full bg-gray-400 z-20 cursor-pointer transition-transform hover:scale-125 focus:outline-none group" style="top: 20%; left: 52%; transform: translate(-50%, -50%);">
<span class="absolute inset-0 rounded-full bg-brand-accent ping opacity-0"></span>
<span class="absolute top-6 left-1/2 -translate-x-1/2 text-xs font-bold bg-white text-gray-800 px-2 py-1 rounded shadow-md border border-gray-200 whitespace-nowrap transition-colors group-hover:text-brand-accent">Oslo</span>
</button>
<!-- 3. Dubai -->
<button onclick="playScenario('dubai')" id="pin-dubai" class="absolute w-5 h-5 rounded-full bg-gray-400 z-20 cursor-pointer transition-transform hover:scale-125 focus:outline-none group" style="top: 48%; left: 62%; transform: translate(-50%, -50%);">
<span class="absolute inset-0 rounded-full bg-brand-accent ping opacity-0"></span>
<span class="absolute top-6 left-1/2 -translate-x-1/2 text-xs font-bold bg-white text-gray-800 px-2 py-1 rounded shadow-md border border-gray-200 whitespace-nowrap transition-colors group-hover:text-brand-accent">Dubai</span>
</button>
</div>
<!-- Selected Scenario Info -->
<div class="bg-slate-50 rounded-xl p-5 border border-slate-200 min-h-[160px]">
<div class="flex justify-between items-start mb-2">
<h4 id="info-title" class="font-bold text-lg text-gray-900">Lade Szenario...</h4>
<span id="info-badge" class="bg-yellow-100 text-yellow-800 text-xs px-2 py-1 rounded border border-yellow-200 font-medium">Class II</span>
</div>
<p id="info-desc" class="text-sm text-gray-600 leading-relaxed mb-4">
Bitte wählen Sie eine Route aus.
</p>
<button onclick="startSimulation()" id="btn-simulate" class="w-full bg-brand-dark hover:bg-brand-light transition-colors text-white py-2 rounded-lg text-sm font-semibold flex justify-center items-center gap-2 shadow-md">
<i data-lucide="refresh-cw" class="w-4 h-4"></i> Überwachung starten
</button>
</div>
</div>
<!-- RIGHT: Visuals / Databricks UI Mockup (Light Mode) -->
<div class="xl:col-span-7 relative h-[650px]">
<!-- Outer subtle glow -->
<div class="absolute -inset-1 bg-gradient-to-r from-brand-accent to-orange-400 rounded-2xl blur opacity-15"></div>
<div class="relative bg-white rounded-2xl border border-gray-200 shadow-2xl overflow-hidden h-full flex flex-col">
<!-- Browser Header -->
<div class="bg-slate-50 px-4 py-3 flex items-center justify-between border-b border-slate-200 z-10 shrink-0">
<div class="flex items-center gap-2">
<div class="flex gap-1.5 mr-4">
<div class="w-3 h-3 rounded-full bg-red-400"></div>
<div class="w-3 h-3 rounded-full bg-yellow-400"></div>
<div class="w-3 h-3 rounded-full bg-green-400"></div>
</div>
<div class="bg-white border border-gray-200 shadow-sm text-xs text-gray-600 px-3 py-1.5 rounded-md flex items-center gap-2 font-mono">
<i data-lucide="layers" class="w-3 h-3 text-brand-accent"></i> Databricks Agent Playground
</div>
</div>
<div class="text-xs font-mono text-gray-500 flex items-center gap-2 bg-blue-50 text-blue-700 px-2 py-1 rounded-md border border-blue-100">
<i data-lucide="user-check" class="w-3 h-3"></i> Human-in-the-Loop Active
</div>
</div>
<!-- Chat Area -->
<div class="flex-1 overflow-y-auto p-6 space-y-6 chat-scroll bg-slate-50/50" id="chat-container">
<!-- Chat content will be injected here by JS -->
</div>
<!-- Typing Input Bar Mockup -->
<div class="bg-white p-4 border-t border-slate-200 shrink-0">
<div class="bg-slate-50 rounded-lg p-3 flex items-center gap-3 border border-slate-200 text-gray-400">
<i data-lucide="terminal" class="w-5 h-5"></i>
<span class="text-sm font-mono">Agentic Supervisor is standing by...</span>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Section 4: Architektur -->
<section id="architektur" class="py-24 bg-gray-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center mb-16">
<h2 class="text-3xl md:text-4xl font-bold text-brand-dark mb-4">Wie denkt das Multi-Agent System?</h2>
<p class="text-xl text-gray-600 max-w-2xl mx-auto">Die Architektur orchestriert spezialisierte Agenten, um komplexe Probleme in Sekunden zu lösen.</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-8">
<div class="bg-white p-8 rounded-2xl border border-gray-200 shadow-sm hover:shadow-xl transition-all hover:-translate-y-1 group"><div class="w-14 h-14 bg-brand-dark text-white rounded-xl flex items-center justify-center mb-6 group-hover:scale-110 transition-transform"><i data-lucide="brain-circuit" class="w-7 h-7"></i></div><h3 class="text-xl font-bold text-gray-900 mb-3">Supervisor Agent</h3><p class="text-sm text-brand-accent font-semibold mb-3">Orchestration Layer</p><p class="text-gray-600">Einsatz eines Databricks Supervisor Agent, der komplexe Anfragen zerlegt und logisch an spezialisierte Sub-Agenten delegiert.</p></div>
<div class="bg-white p-8 rounded-2xl border border-gray-200 shadow-sm hover:shadow-xl transition-all hover:-translate-y-1 group"><div class="w-14 h-14 bg-blue-100 text-blue-600 rounded-xl flex items-center justify-center mb-6 group-hover:scale-110 transition-transform"><i data-lucide="book-open-check" class="w-7 h-7"></i></div><h3 class="text-xl font-bold text-gray-900 mb-3">Knowledge Assistant</h3><p class="text-sm text-blue-600 font-semibold mb-3">"Der Anwalt" (RAG)</p><p class="text-gray-600">Zuständig für statische Daten und Compliance. Nutzt Databricks Vector Search auf dem Unity Catalog, um SLAs und Verträge zu prüfen.</p></div>
<div class="bg-white p-8 rounded-2xl border border-gray-200 shadow-sm hover:shadow-xl transition-all hover:-translate-y-1 group"><div class="w-14 h-14 bg-green-100 text-green-600 rounded-xl flex items-center justify-center mb-6 group-hover:scale-110 transition-transform"><i data-lucide="radar" class="w-7 h-7"></i></div><h3 class="text-xl font-bold text-gray-900 mb-3">MCP Client</h3><p class="text-sm text-green-600 font-semibold mb-3">"Der Scout" (Live Data)</p><p class="text-gray-600">Zuständig für dynamische Daten und Real World Context. Verbindet sich via Model Context Protocol (MCP) mit externen APIs für Live-Wetter.</p></div>
<div class="bg-white p-8 rounded-2xl border border-gray-200 shadow-sm hover:shadow-xl transition-all hover:-translate-y-1 group"><div class="w-14 h-14 bg-orange-100 text-orange-600 rounded-xl flex items-center justify-center mb-6 group-hover:scale-110 transition-transform"><i data-lucide="zap" class="w-7 h-7"></i></div><h3 class="text-xl font-bold text-gray-900 mb-3">Action & Execution</h3><p class="text-sm text-orange-600 font-semibold mb-3">"Der Macher" (Future Outlook)</p><p class="text-gray-600">Ausblick auf "Active Agents" mit schreibendem Zugriff auf Systeme wie SAP oder Jira, um Folgeprozesse autonom und sicher anzustoßen.</p></div>
</div>
</div>
</section>
<!-- Section 5: Footer -->
<section class="py-16 bg-white border-t border-gray-200">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<div class="flex flex-col md:flex-row items-center justify-center gap-8 md:gap-16 mb-10 opacity-80">
<h2 class="text-3xl font-extrabold text-brand-dark tracking-tight">valantic</h2>
<div class="hidden md:block w-px h-12 bg-gray-300"></div>
<h2 class="text-3xl font-extrabold text-gray-800 tracking-tight flex items-center gap-2">
<i data-lucide="layers" class="text-brand-accent"></i> databricks
</h2>
</div>
<p class="text-gray-500 max-w-2xl mx-auto font-medium">
Die gesamte Architektur ist vollständig in die <strong class="text-gray-800">Databricks Data Intelligence Platform</strong> integriert, um höchste Sicherheit, Transparenz und Governance durch den Unity Catalog zu gewährleisten.
</p>
<div class="mt-12 text-sm text-gray-400">
&copy; 2026 valantic GmbH. Showcase entwickelt für das IPAI KI-Festival.
</div>
</div>
</section>
<!-- JavaScript for Interactivity -->
<script>
// Initialize Icons
lucide.createIcons();
// SCENARIOS DATA (Updated styling classes for Light Mode)
const scenarios = {
singapur: {
id: 'singapur',
title: 'Route: FRA → Singapur',
badge: 'Class II (Insulin)',
badgeClass: 'bg-yellow-100 text-yellow-800 border-yellow-200',
desc: 'Eine Lieferung Insulin liegt am Hafen von Singapur. Prüfen Sie, ob Wetterbedingungen die Ware gefährden.',
userPrompt: 'Wir erwarten heute eine Insulin Lieferung (Lieferung #402) in Singapur. Sind Komplikationen zu erwarten?',
mcpInput: '"Singapur Wetter heute"',
mcpOutput: '32°C, 79-88% Luftfeuchtigkeit',
ragQuery: '"Insulin Lagerbedingungen Temperatur Transport"',
ragOutput: 'Class II (Rx), 15°C - 25°C. Critical Breach > 25°C.',
alertType: 'red',
alertTitle: 'HOHES RISIKO - KOMPLIKATIONEN ZU ERWARTEN',
alertText: 'Die Außentemperaturen in Singapur liegen heute bei <strong>32°C</strong> und damit <strong>7°C über dem maximal zulässigen Grenzwert</strong> von 25°C für Insulin.',
alertActions: [
'Ware gilt als "potenziell kompromittiert".',
'Sofortige Umlagerung in Cool House veranlassen.',
'Benachrichtigung Quality Manager (qm@logitrans-demo.com).'
]
},
oslo: {
id: 'oslo',
title: 'Route: FRA → Oslo',
badge: 'Class I (Bio)',
badgeClass: 'bg-blue-100 text-blue-800 border-blue-200',
desc: 'Blutplasma-Transport erreicht das Terminal in Oslo im Winter. Gefriergefahr muss ausgeschlossen werden.',
userPrompt: 'Wie ist der Status für den Blutplasma-Transport (Lieferung #812) am Terminal in Oslo?',
mcpInput: '"Oslo Wetter heute aktuell"',
mcpOutput: '-12°C, Schneesturm, starke Winde',
ragQuery: '"Blutplasma Class I Lagerbedingungen Minimum"',
ragOutput: 'Class I (Bio), strikt 2°C - 8°C. Darf auf keinen Fall gefrieren. Sofortige Vernichtung bei Frost.',
alertType: 'red',
alertTitle: 'GEFRIERGEFAHR DETEKTIERT',
alertText: 'Die Außentemperaturen in Oslo betragen <strong>-12°C</strong>. Das liegt <strong>14°C unter der Mindesttemperatur</strong> für Blutplasma (Class I).',
alertActions: [
'Standzeit auf dem Rollfeld strikt untersagt.',
'Direkte Verladung in beheizten Spezial-LKW (Heated Transit) erforderlich.',
'Logbuch-Eintrag für Temperatur-Audit erstellen.'
]
},
dubai: {
id: 'dubai',
title: 'Route: FRA → Dubai',
badge: 'Class III (MedTech)',
badgeClass: 'bg-green-100 text-green-800 border-green-200',
desc: 'Transport von medizinischen Implantaten nach Dubai. Prüfen Sie die Hitze-Toleranz.',
userPrompt: 'Wir haben einen Stau am Zoll in Dubai für die Implantate-Lieferung (#109). Ist die Hitze ein Problem?',
mcpInput: '"Dubai Zollhafen Temperatur"',
mcpOutput: '39°C, sonnig',
ragQuery: '"Implantate MedTech Class III Toleranz Hitze"',
ragOutput: 'Class III (MedTech), 0°C bis 40°C. Keine aktive Kühlung zwingend erforderlich.',
alertType: 'green',
alertTitle: 'ROUTE STATUS: NORMAL',
alertText: 'Die Temperaturen in Dubai (39°C) liegen <strong>innerhalb der Toleranzgrenze</strong> von max. 40°C für Class III Güter.',
alertActions: [
'Keine Notfallmaßnahmen erforderlich.',
'Logbuch-Eintrag (Log Entry only) für die Verzögerung erstellen.',
'Regulären Weitertransport genehmigen.'
]
}
};
let currentScenario = 'singapur';
let simulationInProgress = false;
// Element References
const chatContainer = document.getElementById('chat-container');
const infoTitle = document.getElementById('info-title');
const infoBadge = document.getElementById('info-badge');
const infoDesc = document.getElementById('info-desc');
const btnSimulate = document.getElementById('btn-simulate');
// Typing Indicator HTML (Light Mode)
const typingIndicatorHTML = `
<div class="flex gap-4 chat-message" id="typing-indicator">
<div class="w-8 h-8 rounded-full bg-brand-accent flex items-center justify-center shrink-0">
<i data-lucide="bot" class="w-4 h-4 text-white"></i>
</div>
<div class="bg-white border border-gray-200 shadow-sm rounded-2xl rounded-tl-none p-4 w-24">
<div class="typing-indicator"><span></span><span></span><span></span></div>
</div>
</div>
`;
function playScenario(id) {
if (simulationInProgress) return;
currentScenario = id;
// 1. Update Map UI (Pins & Lines)
['singapur', 'oslo', 'dubai'].forEach(loc => {
const btn = document.getElementById(`pin-${loc}`);
const line = document.getElementById(`line-${loc}`);
const ping = btn.querySelector('.ping');
if(loc === id) {
btn.classList.replace('bg-gray-400', 'bg-brand-accent');
btn.classList.add('pin-active');
ping.classList.replace('opacity-0', 'opacity-75');
line.setAttribute('stroke', '#ff3621'); // databricks red
line.classList.add('animate-pulse');
} else {
btn.classList.replace('bg-brand-accent', 'bg-gray-400');
btn.classList.remove('pin-active');
ping.classList.replace('opacity-75', 'opacity-0');
line.setAttribute('stroke', '#94a3b8'); // slate-400
line.classList.remove('animate-pulse');
}
});
// 2. Update Info Panel
const data = scenarios[id];
infoTitle.innerText = data.title;
infoBadge.innerText = data.badge;
infoBadge.className = `text-xs px-2 py-1 rounded border font-medium ${data.badgeClass}`;
infoDesc.innerText = data.desc;
// Reset Chat
chatContainer.innerHTML = '';
// Start simulation directly or wait for button press
btnSimulate.classList.remove('opacity-50', 'cursor-not-allowed');
btnSimulate.innerHTML = '<i data-lucide="play" class="w-4 h-4"></i> Simulation starten';
lucide.createIcons();
}
async function startSimulation() {
if(simulationInProgress) return;
simulationInProgress = true;
btnSimulate.classList.add('opacity-50', 'cursor-not-allowed');
btnSimulate.innerHTML = '<i data-lucide="loader" class="w-4 h-4 animate-spin"></i> Analysiere...';
lucide.createIcons();
chatContainer.innerHTML = '';
const data = scenarios[currentScenario];
// Helper to scroll
const scrollToBottom = () => chatContainer.scrollTop = chatContainer.scrollHeight;
// STEP 1: User Message (Light Mode)
chatContainer.innerHTML += `
<div class="flex gap-4 chat-message">
<div class="w-8 h-8 rounded-full bg-slate-200 border border-slate-300 flex items-center justify-center shrink-0">
<i data-lucide="user" class="w-4 h-4 text-slate-600"></i>
</div>
<div class="bg-white border border-gray-200 rounded-2xl rounded-tl-none px-5 py-3 text-gray-800 shadow-sm">
${data.userPrompt}
</div>
</div>
`;
lucide.createIcons();
scrollToBottom();
// Insert Typing
chatContainer.insertAdjacentHTML('beforeend', typingIndicatorHTML);
lucide.createIcons();
scrollToBottom();
// STEP 2: MCP Tool Call (Delay 1.5s)
await new Promise(r => setTimeout(r, 1500));
document.getElementById('typing-indicator').remove();
chatContainer.innerHTML += `
<div class="flex gap-4 chat-message">
<div class="w-8 h-8 rounded-full bg-brand-accent flex items-center justify-center shrink-0 shadow-md">
<i data-lucide="bot" class="w-4 h-4 text-white"></i>
</div>
<div class="w-full">
<div class="bg-white border border-gray-200 shadow-sm rounded-2xl rounded-tl-none p-4 text-gray-700">
Ich sammle die aktuellen Kontextdaten für diesen Standort.
<div class="mt-4 bg-slate-50 rounded-lg p-3 border border-slate-200 font-mono text-sm">
<div class="flex items-center gap-2 text-green-600 font-semibold mb-2">
<i data-lucide="check-circle-2" class="w-4 h-4"></i>
Tool approved: tavily_search (MCP Live Data)
</div>
<div class="text-gray-600">
<span class="text-blue-600">Input:</span> ${data.mcpInput} <br/>
<span class="text-orange-600">Output:</span> ${data.mcpOutput}
</div>
</div>
</div>
</div>
</div>
`;
lucide.createIcons();
scrollToBottom();
// Insert Typing
chatContainer.insertAdjacentHTML('beforeend', typingIndicatorHTML);
lucide.createIcons();
scrollToBottom();
// STEP 3: RAG Tool Call (Delay 1.5s)
await new Promise(r => setTimeout(r, 1500));
document.getElementById('typing-indicator').remove();
chatContainer.innerHTML += `
<div class="flex gap-4 chat-message">
<div class="w-8 h-8 rounded-full bg-brand-accent flex items-center justify-center shrink-0 opacity-0"></div>
<div class="w-full">
<div class="bg-white border border-gray-200 shadow-sm rounded-2xl p-4 text-gray-700">
<div class="bg-slate-50 rounded-lg p-3 border border-slate-200 font-mono text-sm">
<div class="flex items-center gap-2 text-purple-600 font-semibold mb-2">
<i data-lucide="book-open" class="w-4 h-4"></i>
Calling: sla-knowledge-assistant (RAG)
</div>
<div class="text-gray-600">
<span class="text-blue-600">Query:</span> ${data.ragQuery} <br/>
<span class="text-orange-600">Result:</span> ${data.ragOutput}
</div>
</div>
</div>
</div>
</div>
`;
lucide.createIcons();
scrollToBottom();
// Insert Typing
chatContainer.insertAdjacentHTML('beforeend', typingIndicatorHTML);
lucide.createIcons();
scrollToBottom();
// STEP 4: Final Syntheses (Delay 1.5s)
await new Promise(r => setTimeout(r, 1500));
document.getElementById('typing-indicator').remove();
const isAlert = data.alertType === 'red';
const iconColor = 'text-white';
const iconBg = isAlert ? 'bg-brand-accent' : 'bg-green-500';
const iconName = isAlert ? 'shield-alert' : 'shield-check';
const borderColor = isAlert ? 'border-red-200' : 'border-green-200';
const titleColor = isAlert ? 'text-red-600' : 'text-green-600';
const boxBg = isAlert ? 'bg-red-50 border-red-100' : 'bg-green-50 border-green-100';
let actionsHTML = '';
data.alertActions.forEach(act => {
actionsHTML += `<li>${act}</li>`;
});
const humanLoopQuestion = isAlert ?
'Soll ich die Benachrichtigung an den Quality Manager vorbereiten und die Notfallmaßnahmen anstoßen?' :
'Soll ich das Logbuch aktualisieren und den Transport freigeben?';
chatContainer.innerHTML += `
<div class="flex gap-4 chat-message">
<div class="w-8 h-8 rounded-full ${iconBg} flex items-center justify-center shrink-0 shadow-md">
<i data-lucide="${iconName}" class="w-4 h-4 ${iconColor}"></i>
</div>
<div class="w-full">
<div class="bg-white border ${borderColor} rounded-2xl rounded-tl-none p-5 text-gray-800 shadow-lg">
<h4 class="${titleColor} font-bold flex items-center gap-2 mb-3">
<i data-lucide="activity" class="w-5 h-5"></i>
${data.alertTitle}
</h4>
<p class="mb-3">${data.alertText}</p>
<div class="${boxBg} border rounded p-3 mb-3 text-sm">
<strong>Vorgeschlagene Eskalationsmatrix:</strong>
<ol class="list-decimal list-inside mt-2 space-y-1 text-gray-700">
${actionsHTML}
</ol>
</div>
<!-- Human in the Loop Section -->
<div class="mt-5 border-t border-gray-100 pt-4">
<p class="text-sm text-gray-600 font-medium mb-3">
<i data-lucide="user-check" class="w-4 h-4 inline mr-1 text-blue-500"></i>
${humanLoopQuestion}
</p>
<div id="human-loop-actions" class="flex flex-col sm:flex-row gap-3">
<button onclick="handleHumanDecision(true)" class="flex-1 bg-green-50 hover:bg-green-100 text-green-700 border border-green-200 px-4 py-2.5 rounded-lg text-sm font-semibold flex justify-center items-center gap-2 transition-all">
<i data-lucide="check" class="w-4 h-4"></i> Aktion freigeben
</button>
<button onclick="handleHumanDecision(false)" class="flex-1 bg-white hover:bg-gray-50 text-gray-600 border border-gray-200 px-4 py-2.5 rounded-lg text-sm font-semibold flex justify-center items-center gap-2 transition-all">
<i data-lucide="x" class="w-4 h-4"></i> Abbrechen
</button>
</div>
</div>
</div>
</div>
</div>
`;
lucide.createIcons();
scrollToBottom();
// Notice that we DO NOT set simulationInProgress = false here.
// We wait for the human decision!
btnSimulate.innerHTML = '<i data-lucide="user-check" class="w-4 h-4"></i> Warte auf Freigabe...';
lucide.createIcons();
}
async function handleHumanDecision(approved) {
// Remove buttons so they can't be clicked twice
const actionDiv = document.getElementById('human-loop-actions');
if(actionDiv) actionDiv.remove();
const data = scenarios[currentScenario];
const isAlert = data.alertType === 'red';
const scrollToBottom = () => chatContainer.scrollTop = chatContainer.scrollHeight;
// 1. User Message (The Decision)
const userActionText = approved ? 'Aktion freigeben und ausführen.' : 'Aktion abbrechen. Nur lokal protokollieren.';
const userBorderColor = approved ? 'border-green-300 bg-green-50 text-green-800' : 'border-gray-300 bg-gray-50 text-gray-700';
chatContainer.innerHTML += `
<div class="flex gap-4 chat-message mt-2">
<div class="w-8 h-8 rounded-full bg-slate-200 border border-slate-300 flex items-center justify-center shrink-0">
<i data-lucide="user" class="w-4 h-4 text-slate-600"></i>
</div>
<div class="rounded-2xl rounded-tl-none px-5 py-3 shadow-sm border ${userBorderColor} font-medium">
${userActionText}
</div>
</div>
`;
lucide.createIcons();
scrollToBottom();
// Insert Typing
chatContainer.insertAdjacentHTML('beforeend', typingIndicatorHTML);
lucide.createIcons();
scrollToBottom();
// 2. Execute Action (Delay 1.5s)
await new Promise(r => setTimeout(r, 1500));
document.getElementById('typing-indicator').remove();
let aiFinalText = '';
let toolName = '';
let toolAction = '';
if (approved) {
if (isAlert) {
aiFinalText = 'Notfallprotokoll wurde initiiert. Der Quality Manager wurde benachrichtigt und der Transport-Stopp im System hinterlegt.';
toolName = 'sap_erp_connector & outlook_mailer';
toolAction = 'Status: COMPROMISED, Mails verschickt.';
} else {
aiFinalText = 'Transportfreigabe wurde erteilt. Logbuch ist aktualisiert.';
toolName = 'logistics_db_update';
toolAction = 'Status: CLEARED, Log hinzugefügt.';
}
} else {
aiFinalText = 'Aktion durch Supervisor abgebrochen. Es wurden keine externen Systeme kontaktiert. Ein Vermerk wurde im lokalen Audit-Log gespeichert.';
toolName = 'local_audit_log';
toolAction = 'Status: OVERRIDDEN_BY_HUMAN';
}
chatContainer.innerHTML += `
<div class="flex gap-4 chat-message">
<div class="w-8 h-8 rounded-full bg-orange-500 flex items-center justify-center shrink-0 shadow-md">
<i data-lucide="zap" class="w-4 h-4 text-white"></i>
</div>
<div class="w-full">
<div class="bg-white border border-gray-200 rounded-2xl rounded-tl-none p-4 text-gray-800 shadow-sm">
${aiFinalText}
<div class="mt-4 bg-slate-50 rounded-lg p-3 border border-slate-200 font-mono text-sm">
<div class="flex items-center gap-2 text-orange-600 font-semibold mb-2">
<i data-lucide="terminal-square" class="w-4 h-4"></i>
Tool execution: ${toolName}
</div>
<div class="text-gray-600">
<span>${toolAction}</span>
</div>
</div>
</div>
</div>
</div>
`;
lucide.createIcons();
scrollToBottom();
// End of Simulation - Ready for next
simulationInProgress = false;
btnSimulate.innerHTML = '<i data-lucide="refresh-cw" class="w-4 h-4"></i> Neues Szenario starten';
btnSimulate.classList.remove('opacity-50', 'cursor-not-allowed');
lucide.createIcons();
}
// Init Default State
playScenario('singapur');
</script>
</body>
</html>