revert(frontend): Frontend auf letzten funktionierenden Stand 5680179 zurückgesetzt — i18n-Massen-Batch brach Dashboard-Shell in Produktion; Render-Loop-Fix und DSGVO-Antrags-UI liegen sicher in Historie für kontrollierten Wiedereinspiel

This commit is contained in:
Agent Zero
2026-08-27 09:14:21 +02:00
parent bea479bfad
commit 66c11d3d64
119 changed files with 925 additions and 1477 deletions
+8 -10
View File
@@ -44,11 +44,10 @@ interface TabDef {
}
function ChatPanel() {
const { t } = useTranslation();
return (
<div className="flex flex-col h-full" data-testid="chat-panel">
<div className="flex-1 overflow-y-auto p-3">
<p className="text-sm text-secondary-400 text-center py-8">{t('aISidebar.chatsidebarcomingsoon')}</p>
<p className="text-sm text-secondary-400 text-center py-8">Chat-Sidebar - Coming Soon</p>
</div>
</div>
);
@@ -104,7 +103,6 @@ export function AISidebar() {
}
const renderTabContent = () => {
const { t } = useTranslation();
if (aiSidebarTab === 'proactive') {
return (
<div className="flex flex-col h-full">
@@ -119,14 +117,14 @@ export function AISidebar() {
return (
<div className="flex flex-col h-full overflow-y-auto p-3 gap-2" data-testid="notification-list">
{notifications.length === 0 && (
<p className="text-sm text-secondary-400 text-center py-4">{t('aISidebar.keinebenachrichtigungen')}</p>
<p className="text-sm text-secondary-400 text-center py-4">Keine Benachrichtigungen</p>
)}
{notifications.map((msg, i) => (
<div key={i} className="flex items-start justify-between gap-2 px-3 py-2 rounded-lg border border-secondary-200 bg-secondary-50 hover:border-secondary-300 hover:bg-secondary-100 transition-colors text-sm text-secondary-700 group">
<span className="flex-1 leading-snug">{msg}</span>
<button
className="p-1 rounded text-secondary-300 hover:text-danger-600 hover:bg-danger-50 opacity-0 group-hover:opacity-100 transition-opacity flex-shrink-0 mt-0.5"
aria-label={t('aISidebar.benachrichtigunglöschen')}
aria-label="Benachrichtigung löschen"
onClick={() => removeNotification(i)}
>
<X className="w-3.5 h-3.5" aria-hidden="true" strokeWidth={2} />
@@ -152,13 +150,13 @@ export function AISidebar() {
return (
<div className="flex flex-col items-center justify-center h-full text-center px-4 gap-3">
<p className="text-sm text-secondary-500">
{t('aISidebar.dervollekichatistauf')}
Der volle KI-Chat ist auf der AI-Assistant-Seite verfügbar.
</p>
<Link
to="/ai-assistant"
className="inline-flex items-center px-4 py-2 rounded-md bg-primary-600 text-white text-sm font-medium hover:bg-primary-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 min-h-touch"
>
{t('aISidebar.aiassistantöffnen')}
AI Assistant öffnen
</Link>
</div>
);
@@ -192,8 +190,8 @@ export function AISidebar() {
<button
onClick={toggleAISidebar}
className="p-1.5 rounded-md text-secondary-400 hover:text-secondary-600 hover:bg-secondary-100 min-h-touch min-w-touch flex items-center justify-center"
title={t('aISidebar.einklappen')}
aria-label={t('aISidebar.kiassistenteinklappen')}
title="Einklappen"
aria-label="KI Assistent einklappen"
>
{chevronRightIcon}
</button>
@@ -212,7 +210,7 @@ export function AISidebar() {
<button
onClick={toggleAISidebar}
className="inline-flex items-center gap-1 px-2 py-1 rounded text-sm text-secondary-700 hover:bg-secondary-100 min-h-touch"
aria-label={t('aISidebar.zurück')}
aria-label="Zurück"
data-testid="ai-sidebar-back"
>
<ChevronLeft className="w-5 h-5" aria-hidden="true" strokeWidth={2} />
@@ -128,7 +128,6 @@ function MessageFeed({
messages: Message[];
loading: boolean;
}) {
const { t } = useTranslation();
const scrollRef = useRef<HTMLDivElement>(null);
useEffect(() => {
@@ -148,7 +147,7 @@ function MessageFeed({
if (messages.length === 0) {
return (
<div className="flex items-center justify-center h-full text-sm text-secondary-400">
{t('messageSidebar.keinenachrichten')}
Keine Nachrichten
</div>
);
}
@@ -206,7 +205,6 @@ function MessageInput({
onSend: (text: string) => void;
disabled: boolean;
}) {
const { t } = useTranslation();
const [text, setText] = useState('');
const handleSend = () => {
@@ -238,7 +236,7 @@ function MessageInput({
onClick={handleSend}
disabled={disabled || !text.trim()}
className="p-2 rounded-lg bg-primary-500 text-white hover:bg-primary-600 disabled:bg-secondary-200 disabled:text-secondary-400 transition-colors min-h-touch min-w-touch flex items-center justify-center"
aria-label={t('messageSidebar.senden')}
aria-label="Senden"
data-testid="message-send-btn"
>
{sendIcon}
@@ -388,7 +386,6 @@ export function MessageSidebar() {
// Determine which pinned conv to select when a quick-access button is clicked
const handleQuickAccess = (qa: QuickAccessDef) => {
const { t } = useTranslation();
setCurrentView(qa.view);
if (qa.view === 'conversations' && qa.filterPinned) {
// Find pinned conversation matching the filter by title
@@ -483,8 +480,8 @@ export function MessageSidebar() {
<button
onClick={toggleMessageSidebar}
className="p-1.5 rounded-md text-secondary-400 hover:text-secondary-600 hover:bg-secondary-100 min-h-touch min-w-touch flex items-center justify-center"
title={t('messageSidebar.einklappen')}
aria-label={t('messageSidebar.messagingeinklappen')}
title="Einklappen"
aria-label="Messaging einklappen"
>
{chevronRightIcon}
</button>
@@ -501,7 +498,7 @@ export function MessageSidebar() {
<p className="text-sm text-red-500 text-center py-2">{typeof error === "string" ? error : (error as any)?.message || "Ein Fehler ist aufgetreten"}</p>
)}
{!loading && conversations.length === 0 && !error && (
<p className="text-sm text-secondary-400 text-center py-4">{t('messageSidebar.keinekonversationen')}</p>
<p className="text-sm text-secondary-400 text-center py-4">Keine Konversationen</p>
)}
{/* Pinned conversations */}
{pinnedConversations.length > 0 && (
@@ -540,7 +537,6 @@ export function MessageSidebar() {
// ─── Main Content Area ───
const renderContent = () => {
const { t } = useTranslation();
if (currentView === 'team') {
return <TeamPanel onStartDirectChat={handleStartDirectChat} />;
}
@@ -568,7 +564,7 @@ export function MessageSidebar() {
)}
{isSystemLocked && (
<div className="p-3 border-t border-secondary-200 text-center">
<span className="text-xs text-secondary-400">{t('messageSidebar.systemkonversationschreibgeschützt')}</span>
<span className="text-xs text-secondary-400">System-Konversation (schreibgeschützt)</span>
</div>
)}
</div>
@@ -588,7 +584,7 @@ export function MessageSidebar() {
<button
onClick={toggleMessageSidebar}
className="inline-flex items-center gap-1 px-2 py-1 rounded text-sm text-secondary-700 hover:bg-secondary-100 min-h-touch"
aria-label={t('messageSidebar.zurück')}
aria-label="Zurück"
data-testid="message-sidebar-back"
>
<ChevronLeft className="w-5 h-5" aria-hidden="true" strokeWidth={2} />
@@ -1,9 +1,7 @@
import React from 'react';
import { usePluginToolbarStore, type ToolbarItem } from '@/store/pluginToolbarStore';
import { useTranslation } from 'react-i18next';
function ToolbarButton({ item }: { item: ToolbarItem }) {
const { t } = useTranslation();
return (
<button
onClick={item.onClick}
@@ -23,7 +21,6 @@ function ToolbarButton({ item }: { item: ToolbarItem }) {
}
function ToolbarSearch({ item }: { item: ToolbarItem }) {
const { t } = useTranslation();
const [expanded, setExpanded] = React.useState(false);
const [value, setValue] = React.useState('');
const inputRef = React.useRef<HTMLInputElement>(null);
@@ -77,7 +74,7 @@ function ToolbarSearch({ item }: { item: ToolbarItem }) {
<button
onClick={handleToggle}
className="ml-1 p-1 rounded hover:bg-secondary-100 text-secondary-400 hover:text-secondary-600 transition-colors"
aria-label={t('pluginToolbar.sucheschließen')}
aria-label="Suche schließen"
>
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" strokeWidth={2} viewBox="0 0 24 24">
<line x1="18" y1="6" x2="6" y2="18" />
+4 -4
View File
@@ -182,21 +182,21 @@ export function Sidebar() {
'flex flex-col transition-transform motion-safe:duration-300',
sidebarOpen ? 'translate-x-0' : '-translate-x-full md:hidden'
)}
aria-label={t('sidebar.seitenleistenavigation')}
aria-label="Seitenleiste Navigation"
data-testid="sidebar"
>
<div className="h-[58px] flex items-center gap-2 px-4 border-b border-secondary-700">
<button
onClick={() => navigate('/start')}
className="p-1.5 rounded-md text-secondary-400 hover:bg-secondary-700 hover:text-white flex items-center justify-center focus:outline-none"
aria-label={t('sidebar.zurückzurstartseite')}
title={t('sidebar.zurückzurstartseite')}
aria-label="Zurück zur Startseite"
title="Zurück zur Startseite"
>
<ArrowLeft className="w-4 h-4" />
</button>
<span className="text-xl font-bold text-white">leocrm</span>
</div>
<nav className="flex-1 overflow-y-auto py-4" aria-label={t('sidebar.hauptnavigation')}>
<nav className="flex-1 overflow-y-auto py-4" aria-label="Hauptnavigation">
<ul className="space-y-1 px-2">
{(() => {
const groups = new Map<string, typeof allMenuItems>();
@@ -27,7 +27,6 @@ import {
Workflow,
} from 'lucide-react';
import clsx from 'clsx';
import { useTranslation } from 'react-i18next';
// Curated icon map — avoids `import * as LucideIcons` which loads ALL icons
// and causes OOM in tests (ARCH-063).
@@ -70,7 +69,6 @@ function getIcon(name: string): React.ReactNode {
}
export function SortableMenuItem({ id, label, icon, isGroup }: SortableMenuItemProps) {
const { t } = useTranslation();
const {
attributes,
listeners,
@@ -101,7 +99,7 @@ export function SortableMenuItem({ id, label, icon, isGroup }: SortableMenuItemP
className="cursor-grab active:cursor-grabbing text-secondary-400 hover:text-secondary-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 rounded"
{...attributes}
{...listeners}
aria-label={t('sortableMenuItem.ziehenzumsortieren')}
aria-label="Ziehen zum Sortieren"
type="button"
>
<GripVertical className="w-4 h-4" />
+1 -1
View File
@@ -63,7 +63,7 @@ export function TopBar() {
<button
onClick={toggleSidebar}
className="p-2 rounded-md bg-primary-600 text-white hover:bg-primary-700 min-h-touch min-w-touch flex items-center justify-center focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500"
aria-label={t('topBar.seitenleisteeinausklappen')}
aria-label="Seitenleiste ein-/ausklappen"
aria-expanded={true}
>
<Menu className="w-4 h-4" aria-hidden="true" strokeWidth={2} />