feat: initial commit web-cad-neu with docker-compose, frontend and backend

This commit is contained in:
2026-06-26 10:50:24 +02:00
commit 4ec76fe406
102 changed files with 25722 additions and 0 deletions
+88
View File
@@ -0,0 +1,88 @@
import React, { useState } from 'react';
import type { KICopilotProps } from '../types/ui.types';
const defaultSuggestions = [
{ id: 's1', icon: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>, label: '5 Reihen à 22 Stühle anlegen' },
{ id: 's2', icon: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>, label: 'Optimale Bestuhlung vorschlagen' },
{ id: 's3', icon: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>, label: 'Überlappende Stühle finden' },
];
const defaultMessages = [
{ id: 'm1', role: 'user' as const, content: 'Lege 5 Reihen mit je 22 Stühlen parallel zur Bühne an, Abstand 1m.' },
{ id: 'm2', role: 'assistant' as const, content: <span>Ich erstelle <strong>110 Stühle</strong> in 5 Reihen (Y=2,5m/3,5m/4,5m/5,5m/6,5m; X-Start=2,5m; 22 Stühle × 0,5m + 0,1m Abstand). Los geht's! <em style={{ color: 'var(--color-ki)' }}>● function_call: placeSeating(rows=5, cols=22, gap=1.0)</em></span> },
];
const KICopilot: React.FC<KICopilotProps> = ({ messages, suggestions, onSend, onSuggestionClick, loading }) => {
const [input, setInput] = useState('');
const allMessages = messages.length > 0 ? messages : defaultMessages;
const allSuggestions = suggestions.length > 0 ? suggestions : defaultSuggestions;
const handleSend = () => {
if (input.trim()) {
onSend(input.trim());
setInput('');
}
};
return (
<>
<div className="ki-header">
<div className="ki-avatar">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/></svg>
</div>
<div>
<div className="ki-title">KI Copilot</div>
<div style={{ fontSize: '11px', color: 'var(--color-text-muted)' }}>Powered by Claude</div>
</div>
<span className="ki-status">Online</span>
</div>
<div className="ki-suggestions">
<div className="ki-suggestion-title">Schnell-Aktionen</div>
{allSuggestions.map((s) => (
<button key={s.id} className="ki-chip" onClick={() => onSuggestionClick(s)}>
{s.icon}
{s.label}
</button>
))}
</div>
<div className="ki-chat">
{allMessages.map((msg) => (
<div key={msg.id} className={`ki-msg ${msg.role}`}>
<div className="ki-msg-bubble">{msg.content}</div>
</div>
))}
{loading && (
<div className="ki-msg assistant">
<div className="ki-msg-bubble" style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
<span className="ki-typing-dot" />
<span className="ki-typing-dot" />
<span className="ki-typing-dot" />
</div>
</div>
)}
</div>
<div className="ki-input-wrap">
<input
className="ki-input"
type="text"
placeholder="Frage den KI Copilot..."
aria-label="KI Eingabe"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => { if (e.key === 'Enter') handleSend(); }}
/>
<button className="ki-voice-btn" title="Spracheingabe" aria-label="Spracheingabe starten">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/></svg>
</button>
<button className="ki-send-btn" title="Senden" aria-label="Senden" onClick={handleSend} disabled={loading}>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
</button>
</div>
</>
);
};
export default KICopilot;