Migrate from tailwind-sidebar to syncfusion sidebar component
This commit is contained in:
3429
dashboard/package-lock.json
generated
3429
dashboard/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,7 @@
|
|||||||
"@syncfusion/ej2-navigations": "^30.2.7",
|
"@syncfusion/ej2-navigations": "^30.2.7",
|
||||||
"@syncfusion/ej2-notifications": "^30.2.4",
|
"@syncfusion/ej2-notifications": "^30.2.4",
|
||||||
"@syncfusion/ej2-popups": "^30.2.4",
|
"@syncfusion/ej2-popups": "^30.2.4",
|
||||||
"@syncfusion/ej2-react-buttons": "^30.1.37",
|
"@syncfusion/ej2-react-buttons": "^30.2.4",
|
||||||
"@syncfusion/ej2-react-calendars": "^30.1.37",
|
"@syncfusion/ej2-react-calendars": "^30.1.37",
|
||||||
"@syncfusion/ej2-react-dropdowns": "^30.1.37",
|
"@syncfusion/ej2-react-dropdowns": "^30.1.37",
|
||||||
"@syncfusion/ej2-react-filemanager": "^30.1.38",
|
"@syncfusion/ej2-react-filemanager": "^30.1.38",
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"@syncfusion/ej2-react-inputs": "^30.1.38",
|
"@syncfusion/ej2-react-inputs": "^30.1.38",
|
||||||
"@syncfusion/ej2-react-kanban": "^30.1.37",
|
"@syncfusion/ej2-react-kanban": "^30.1.37",
|
||||||
"@syncfusion/ej2-react-layouts": "^30.1.40",
|
"@syncfusion/ej2-react-layouts": "^30.1.40",
|
||||||
"@syncfusion/ej2-react-navigations": "^30.1.39",
|
"@syncfusion/ej2-react-navigations": "^30.2.7",
|
||||||
"@syncfusion/ej2-react-notifications": "^30.1.37",
|
"@syncfusion/ej2-react-notifications": "^30.1.37",
|
||||||
"@syncfusion/ej2-react-popups": "^30.1.37",
|
"@syncfusion/ej2-react-popups": "^30.1.37",
|
||||||
"@syncfusion/ej2-react-schedule": "^30.1.37",
|
"@syncfusion/ej2-react-schedule": "^30.1.37",
|
||||||
|
|||||||
5067
dashboard/pnpm-lock.yaml
generated
5067
dashboard/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"appName": "Infoscreen-Management",
|
"appName": "Infoscreen-Management",
|
||||||
"version": "2025.1.0-alpha.3",
|
"version": "2025.1.0-alpha.4",
|
||||||
"copyright": "© 2025 Third-Age-Applications",
|
"copyright": "© 2025 Third-Age-Applications",
|
||||||
"supportContact": "support@third-age-applications.com",
|
"supportContact": "support@third-age-applications.com",
|
||||||
"description": "Eine zentrale Verwaltungsoberfläche für digitale Informationsbildschirme.",
|
"description": "Eine zentrale Verwaltungsoberfläche für digitale Informationsbildschirme.",
|
||||||
|
|||||||
@@ -24,25 +24,51 @@ body {
|
|||||||
--sidebar-bg: #e5d8c7;
|
--sidebar-bg: #e5d8c7;
|
||||||
--sidebar-fg: #78591c;
|
--sidebar-fg: #78591c;
|
||||||
--sidebar-border: #d6c3a6;
|
--sidebar-border: #d6c3a6;
|
||||||
|
--sidebar-text: #000;
|
||||||
|
--sidebar-hover-bg: #d1b89b;
|
||||||
|
--sidebar-hover-text: #000;
|
||||||
|
--sidebar-active-bg: #cda76b;
|
||||||
|
--sidebar-active-text: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Layout-Container für Sidebar und Content */
|
/* Layout-Container für Sidebar und Content */
|
||||||
.layout-container {
|
.layout-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
position: relative; /* Wichtig für die absolute Positionierung des Inhalts */
|
|
||||||
height: 100vh; /* Feste Höhe auf die des Viewports setzen */
|
height: 100vh; /* Feste Höhe auf die des Viewports setzen */
|
||||||
overflow: hidden; /* Verhindert, dass der Scrollbalken den gesamten Container betrifft */
|
overflow: hidden; /* Verhindert, dass der Scrollbalken den gesamten Container betrifft */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sidebar fixieren, keine Scrollbalken */
|
/* Sidebar fixieren, keine Scrollbalken, volle Höhe */
|
||||||
.sidebar-theme {
|
.sidebar-theme {
|
||||||
background-color: var(--sidebar-bg);
|
background-color: var(--sidebar-bg);
|
||||||
color: var(--sidebar-fg);
|
color: var(--sidebar-text);
|
||||||
font-size: 1.15rem;
|
font-size: 1.15rem;
|
||||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
|
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
overflow: hidden;
|
|
||||||
z-index: 10; /* Stellt sicher, dass die Sidebar über dem Inhalt ist */
|
z-index: 10; /* Stellt sicher, dass die Sidebar über dem Inhalt ist */
|
||||||
|
height: 100vh; /* Volle Browser-Höhe */
|
||||||
|
min-height: 100vh; /* Mindesthöhe für volle Browser-Höhe */
|
||||||
|
max-height: 100vh; /* Maximale Höhe begrenzen */
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sicherstelle vertikale Anordnung der Navigation und Footer am Ende */
|
||||||
|
.sidebar-theme nav {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
flex: 1 1 auto !important;
|
||||||
|
overflow-y: auto !important;
|
||||||
|
min-height: 0 !important; /* Ermöglicht Flex-Shrinking */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer-Bereich am unteren Ende fixieren */
|
||||||
|
.sidebar-theme > div:last-child {
|
||||||
|
margin-top: auto !important;
|
||||||
|
flex-shrink: 0 !important;
|
||||||
|
min-height: auto !important;
|
||||||
|
padding-bottom: 0.5rem !important; /* Zusätzlicher Abstand vom unteren Rand */
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-theme .sidebar-link {
|
.sidebar-theme .sidebar-link {
|
||||||
@@ -50,6 +76,9 @@ body {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
display: flex !important;
|
||||||
|
width: 100% !important;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-theme .sidebar-logout {
|
.sidebar-theme .sidebar-logout {
|
||||||
@@ -58,38 +87,32 @@ body {
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: 1.15rem;
|
font-size: 1.15rem;
|
||||||
|
display: flex !important;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-theme .sidebar-btn,
|
|
||||||
.sidebar-theme .sidebar-link,
|
|
||||||
.sidebar-theme .sidebar-logout {
|
.sidebar-link:hover,
|
||||||
background-color: var(--sidebar-bg);
|
.sidebar-logout:hover {
|
||||||
color: var(--sidebar-fg);
|
background-color: var(--sidebar-hover-bg);
|
||||||
transition: background 0.2s, color 0.2s;
|
color: var(--sidebar-hover-text);
|
||||||
font-weight: 500;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-theme .sidebar-btn:hover,
|
.sidebar-link.active {
|
||||||
.sidebar-theme .sidebar-link:hover,
|
background-color: var(--sidebar-active-bg);
|
||||||
.sidebar-theme .sidebar-logout:hover {
|
color: var(--sidebar-active-text);
|
||||||
background-color: var(--sidebar-fg);
|
font-weight: bold;
|
||||||
color: var(--sidebar-bg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* === START: ROBUSTES ABSOLUTE-POSITIONING-LAYOUT === */
|
/* === START: SYNCFUSION-KOMPATIBLES LAYOUT === */
|
||||||
|
|
||||||
/* Der Inhaltsbereich wird absolut positioniert, um den Rest des Bildschirms auszufüllen */
|
/* Der Inhaltsbereich arbeitet mit Syncfusion's natürlichem Layout */
|
||||||
.content-area {
|
.content-area {
|
||||||
position: absolute;
|
|
||||||
inset: 0 0 0 16rem; /* Shorthand für top, right, bottom, left */
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
transition: inset-inline-start 0.3s ease-in-out; /* Animiert die 'left' Eigenschaft */
|
flex: 1;
|
||||||
}
|
min-width: 0; /* Verhindert Flex-Item-Overflow */
|
||||||
|
|
||||||
/* Anpassung für die eingeklappte Sidebar */
|
|
||||||
.content-area.collapsed {
|
|
||||||
left: 5rem; /* Breite der eingeklappten Sidebar (w-20) */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-header {
|
.content-header {
|
||||||
@@ -103,7 +126,7 @@ body {
|
|||||||
background-color: #f3f4f6;
|
background-color: #f3f4f6;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* === ENDE: ROBUSTES ABSOLUTE-POSITIONING-LAYOUT === */
|
/* === ENDE: SYNCFUSION-KOMPATIBLES LAYOUT === */
|
||||||
|
|
||||||
|
|
||||||
/* Kanban-Karten im Sidebar-Style */
|
/* Kanban-Karten im Sidebar-Style */
|
||||||
@@ -156,4 +179,97 @@ body {
|
|||||||
padding-bottom: 8px;
|
padding-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Entfernt - Syncfusion verwaltet das Layout selbst */
|
||||||
|
|
||||||
|
/* Grundlegende Sidebar-Styles - Syncfusion-kompatibel */
|
||||||
|
#sidebar .sidebar-link,
|
||||||
|
#sidebar .sidebar-logout {
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
gap: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar .sidebar-link svg,
|
||||||
|
#sidebar .sidebar-logout svg {
|
||||||
|
flex-shrink: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text standardmäßig IMMER sichtbar */
|
||||||
|
#sidebar .sidebar-link .sidebar-text,
|
||||||
|
#sidebar .sidebar-logout .sidebar-text {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
display: inline-block !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
transition: opacity 0.3s, transform 0.3s !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar .sidebar-link:hover,
|
||||||
|
#sidebar .sidebar-logout:hover {
|
||||||
|
background-color: var(--sidebar-hover-bg) !important;
|
||||||
|
color: var(--sidebar-hover-text) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Expanded state - Text sichtbar (Standard) */
|
||||||
|
#sidebar .sidebar-theme.expanded .sidebar-link,
|
||||||
|
#sidebar .sidebar-theme.expanded .sidebar-logout {
|
||||||
|
justify-content: flex-start !important;
|
||||||
|
padding: 12px 24px !important;
|
||||||
|
gap: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar .sidebar-theme.expanded .sidebar-text {
|
||||||
|
display: inline-block !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar .sidebar-theme.expanded .sidebar-link svg,
|
||||||
|
#sidebar .sidebar-theme.expanded .sidebar-logout svg {
|
||||||
|
margin-right: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Collapsed state - nur Icons */
|
||||||
|
#sidebar .sidebar-theme.collapsed .sidebar-link,
|
||||||
|
#sidebar .sidebar-theme.collapsed .sidebar-logout {
|
||||||
|
justify-content: center !important;
|
||||||
|
padding: 12px 8px !important;
|
||||||
|
gap: 0 !important;
|
||||||
|
position: relative !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar .sidebar-theme.collapsed .sidebar-text {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar .sidebar-theme.collapsed .sidebar-link svg,
|
||||||
|
#sidebar .sidebar-theme.collapsed .sidebar-logout svg {
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Syncfusion TooltipComponent wird jetzt verwendet - CSS-Tooltips entfernt */
|
||||||
|
|
||||||
|
/* Logo und Versionsnummer im collapsed state ausblenden */
|
||||||
|
|
||||||
|
@keyframes fade-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-50%) translateX(-5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(-50%) translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Logo und Versionsnummer im collapsed state ausblenden */
|
||||||
|
#sidebar .sidebar-theme.collapsed img {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar .sidebar-theme.collapsed .version-info {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { BrowserRouter as Router, Routes, Route, Link, Outlet } from 'react-router-dom';
|
import { BrowserRouter as Router, Routes, Route, Link, Outlet } from 'react-router-dom';
|
||||||
|
import { SidebarComponent } from '@syncfusion/ej2-react-navigations';
|
||||||
|
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
|
||||||
|
import { TooltipComponent } from '@syncfusion/ej2-react-popups';
|
||||||
import logo from './assets/logo.png';
|
import logo from './assets/logo.png';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
|
|
||||||
@@ -49,8 +52,9 @@ import Logout from './logout';
|
|||||||
// const ENV = import.meta.env.VITE_ENV || 'development';
|
// const ENV = import.meta.env.VITE_ENV || 'development';
|
||||||
|
|
||||||
const Layout: React.FC = () => {
|
const Layout: React.FC = () => {
|
||||||
const [collapsed, setCollapsed] = useState(false);
|
|
||||||
const [version, setVersion] = useState('');
|
const [version, setVersion] = useState('');
|
||||||
|
const [isCollapsed, setIsCollapsed] = useState(false);
|
||||||
|
let sidebarRef: SidebarComponent | null;
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetch('/program-info.json')
|
fetch('/program-info.json')
|
||||||
@@ -59,84 +63,236 @@ const Layout: React.FC = () => {
|
|||||||
.catch(err => console.error('Failed to load version info:', err));
|
.catch(err => console.error('Failed to load version info:', err));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
const toggleSidebar = () => {
|
||||||
<div className="layout-container">
|
if (sidebarRef) {
|
||||||
{/* Sidebar */}
|
sidebarRef.toggle();
|
||||||
<aside
|
}
|
||||||
className={`sidebar-theme flex flex-col transition-all duration-300 ${collapsed ? 'w-20' : 'w-64'}`}
|
};
|
||||||
|
|
||||||
|
const onSidebarChange = () => {
|
||||||
|
// Syncfusion unterscheidet zwischen isOpen (true/false) und dem Dock-Modus
|
||||||
|
// Im Dock-Modus ist isOpen=true, aber die Sidebar ist kollabiert
|
||||||
|
const sidebar = sidebarRef?.element;
|
||||||
|
if (sidebar) {
|
||||||
|
const currentWidth = sidebar.style.width;
|
||||||
|
const newCollapsedState = currentWidth === '60px' || currentWidth.includes('60');
|
||||||
|
|
||||||
|
setIsCollapsed(newCollapsedState);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const sidebarTemplate = () => (
|
||||||
|
<div
|
||||||
|
className={`sidebar-theme ${isCollapsed ? 'collapsed' : 'expanded'}`}
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
height: '100vh',
|
||||||
|
minHeight: '100vh',
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="h-20 flex items-center justify-center border-b"
|
|
||||||
style={{ borderColor: 'var(--sidebar-border)' }}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={logo}
|
|
||||||
alt="Logo"
|
|
||||||
className="h-12"
|
|
||||||
style={{ display: collapsed ? 'none' : 'block' }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
className="sidebar-btn p-2 focus:outline-none transition-colors"
|
|
||||||
onClick={() => setCollapsed(!collapsed)}
|
|
||||||
aria-label={collapsed ? 'Sidebar ausklappen' : 'Sidebar einklappen'}
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<span style={{ fontSize: 20 }}>{collapsed ? '▶' : '◀'}</span>
|
|
||||||
</button>
|
|
||||||
<nav className="flex-1 mt-4">
|
|
||||||
{sidebarItems.map(item => {
|
|
||||||
const Icon = item.icon;
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
key={item.path}
|
|
||||||
to={item.path}
|
|
||||||
className="sidebar-link flex items-center gap-3 px-6 py-3 transition-colors no-underline"
|
|
||||||
title={collapsed ? item.name : undefined}
|
|
||||||
>
|
|
||||||
<Icon size={22} />
|
|
||||||
{!collapsed && item.name}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</nav>
|
|
||||||
{/* Abmelden-Button und Version immer ganz unten */}
|
|
||||||
<div className="mt-auto mb-2">
|
|
||||||
<Link
|
|
||||||
to="/logout"
|
|
||||||
className="sidebar-logout flex items-center gap-3 px-6 py-3 w-full transition-colors no-underline"
|
|
||||||
title={collapsed ? 'Abmelden' : undefined}
|
|
||||||
>
|
|
||||||
<LogOut size={22} />
|
|
||||||
{!collapsed && 'Abmelden'}
|
|
||||||
</Link>
|
|
||||||
{!collapsed && version && (
|
|
||||||
<div className="px-6 pt-2 text-xs text-center opacity-70">Version {version}</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
{/* Main Content */}
|
|
||||||
<div className={`content-area ${collapsed ? 'collapsed' : ''}`}>
|
|
||||||
{/* Header */}
|
|
||||||
<header
|
|
||||||
className="content-header flex items-center px-8 shadow"
|
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: '#e5d8c7',
|
borderColor: 'var(--sidebar-border)',
|
||||||
color: '#78591c',
|
height: '68px',
|
||||||
height: 'calc(48px + 20px)',
|
flexShrink: 0,
|
||||||
fontSize: '1.15rem',
|
display: 'flex',
|
||||||
fontFamily:
|
alignItems: 'center',
|
||||||
'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif',
|
justifyContent: 'center',
|
||||||
|
borderBottom: '1px solid var(--sidebar-border)',
|
||||||
|
margin: 0,
|
||||||
|
padding: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={logo}
|
src={logo}
|
||||||
alt="Logo"
|
alt="Logo"
|
||||||
className="h-12 mr-4"
|
style={{
|
||||||
style={{ marginTop: 10, marginBottom: 10 }}
|
height: '64px',
|
||||||
|
maxHeight: '60px',
|
||||||
|
display: 'block',
|
||||||
|
margin: '0 auto',
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<span className="text-2xl font-bold mr-8">Infoscreen-Management</span>
|
</div>
|
||||||
<span className="ml-auto" style={{ color: '#78591c' }}>
|
<nav
|
||||||
|
style={{
|
||||||
|
flex: '1 1 auto',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
marginTop: '1rem',
|
||||||
|
overflowY: 'auto',
|
||||||
|
minHeight: 0, // Wichtig für Flex-Shrinking
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{sidebarItems.map(item => {
|
||||||
|
const Icon = item.icon;
|
||||||
|
const linkContent = (
|
||||||
|
<Link
|
||||||
|
key={item.path}
|
||||||
|
to={item.path}
|
||||||
|
className="sidebar-link no-underline w-full"
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'flex-start',
|
||||||
|
gap: '8px',
|
||||||
|
padding: '12px 24px',
|
||||||
|
transition: 'background 0.2s, color 0.2s, justify-content 0.3s',
|
||||||
|
textDecoration: 'none',
|
||||||
|
color: 'var(--sidebar-fg)',
|
||||||
|
backgroundColor: 'var(--sidebar-bg)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon size={22} style={{ flexShrink: 0, marginRight: 0 }} />
|
||||||
|
<span className="sidebar-text" style={{ marginLeft: 0, transition: 'opacity 0.3s' }}>
|
||||||
|
{item.name}
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Syncfusion Tooltip nur im collapsed state
|
||||||
|
return isCollapsed ? (
|
||||||
|
<TooltipComponent
|
||||||
|
key={item.path}
|
||||||
|
content={item.name}
|
||||||
|
position="RightCenter"
|
||||||
|
opensOn="Hover"
|
||||||
|
showTipPointer={true}
|
||||||
|
animation={{
|
||||||
|
open: { effect: 'FadeIn', duration: 200 },
|
||||||
|
close: { effect: 'FadeOut', duration: 200 },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{linkContent}
|
||||||
|
</TooltipComponent>
|
||||||
|
) : (
|
||||||
|
linkContent
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</nav>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flexShrink: 0,
|
||||||
|
marginTop: 'auto',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
minHeight: 'auto',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{(() => {
|
||||||
|
const logoutContent = (
|
||||||
|
<Link
|
||||||
|
to="/logout"
|
||||||
|
className="sidebar-logout no-underline w-full"
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'flex-start',
|
||||||
|
gap: '8px',
|
||||||
|
padding: '12px 24px',
|
||||||
|
transition: 'background 0.2s, color 0.2s, justify-content 0.3s',
|
||||||
|
textDecoration: 'none',
|
||||||
|
color: 'var(--sidebar-fg)',
|
||||||
|
backgroundColor: 'var(--sidebar-bg)',
|
||||||
|
border: 'none',
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: '1.15rem',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LogOut size={22} style={{ flexShrink: 0, marginRight: 0 }} />
|
||||||
|
<span className="sidebar-text" style={{ marginLeft: 0, transition: 'opacity 0.3s' }}>
|
||||||
|
Abmelden
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Syncfusion Tooltip nur im collapsed state
|
||||||
|
return isCollapsed ? (
|
||||||
|
<TooltipComponent
|
||||||
|
content="Abmelden"
|
||||||
|
position="RightCenter"
|
||||||
|
opensOn="Hover"
|
||||||
|
showTipPointer={true}
|
||||||
|
animation={{
|
||||||
|
open: { effect: 'FadeIn', duration: 200 },
|
||||||
|
close: { effect: 'FadeOut', duration: 200 },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{logoutContent}
|
||||||
|
</TooltipComponent>
|
||||||
|
) : (
|
||||||
|
logoutContent
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
{version && (
|
||||||
|
<div
|
||||||
|
className="version-info px-6 py-2 text-xs text-center opacity-70 border-t"
|
||||||
|
style={{ borderColor: 'var(--sidebar-border)' }}
|
||||||
|
>
|
||||||
|
Version {version}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="layout-container">
|
||||||
|
<SidebarComponent
|
||||||
|
id="sidebar"
|
||||||
|
ref={(sidebar: SidebarComponent | null) => {
|
||||||
|
sidebarRef = sidebar;
|
||||||
|
}}
|
||||||
|
width="256px"
|
||||||
|
target=".layout-container"
|
||||||
|
isOpen={true}
|
||||||
|
closeOnDocumentClick={false}
|
||||||
|
enableGestures={false}
|
||||||
|
type="Auto"
|
||||||
|
enableDock={true}
|
||||||
|
dockSize="60px"
|
||||||
|
change={onSidebarChange}
|
||||||
|
>
|
||||||
|
{sidebarTemplate()}
|
||||||
|
</SidebarComponent>
|
||||||
|
|
||||||
|
<div className="content-area">
|
||||||
|
<header
|
||||||
|
className="content-header flex items-center shadow"
|
||||||
|
style={{
|
||||||
|
backgroundColor: '#e5d8c7',
|
||||||
|
color: '#78591c',
|
||||||
|
height: '68px', // Exakt gleiche Höhe wie Sidebar-Header
|
||||||
|
fontSize: '1.15rem',
|
||||||
|
fontFamily:
|
||||||
|
'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif',
|
||||||
|
margin: 0,
|
||||||
|
padding: '0 2rem 0 0', // Nur rechts Padding, links kein Padding
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ButtonComponent
|
||||||
|
cssClass="e-inherit"
|
||||||
|
iconCss="e-icons e-menu"
|
||||||
|
onClick={toggleSidebar}
|
||||||
|
isToggle={true}
|
||||||
|
style={{
|
||||||
|
margin: '0 1rem 0 0', // Nur rechts Margin für Abstand zum Logo
|
||||||
|
padding: '8px 12px',
|
||||||
|
minWidth: '44px',
|
||||||
|
height: '44px',
|
||||||
|
flexShrink: 0,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<img src={logo} alt="Logo" className="h-16 mr-4" style={{ maxHeight: '60px' }} />
|
||||||
|
<span className="text-2xl font-bold mr-8" style={{ color: '#78591c' }}>
|
||||||
|
Infoscreen-Management
|
||||||
|
</span>
|
||||||
|
<span className="ml-auto text-lg font-medium" style={{ color: '#78591c' }}>
|
||||||
[Organisationsname]
|
[Organisationsname]
|
||||||
</span>
|
</span>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
@tailwind base;
|
/* @tailwind base;
|
||||||
@tailwind components;
|
@tailwind components; */
|
||||||
@tailwind utilities;
|
|
||||||
|
/* @tailwind utilities; */
|
||||||
|
|
||||||
/* :root {
|
/* :root {
|
||||||
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
|||||||
@@ -3,9 +3,14 @@ import { createRoot } from 'react-dom/client';
|
|||||||
import './index.css';
|
import './index.css';
|
||||||
import App from './App.tsx';
|
import App from './App.tsx';
|
||||||
import { registerLicense } from '@syncfusion/ej2-base';
|
import { registerLicense } from '@syncfusion/ej2-base';
|
||||||
|
import '@syncfusion/ej2-base/styles/material3.css';
|
||||||
|
import '@syncfusion/ej2-navigations/styles/material3.css';
|
||||||
|
import '@syncfusion/ej2-buttons/styles/material3.css';
|
||||||
|
|
||||||
// Setze hier deinen Lizenzschlüssel ein
|
// Setze hier deinen Lizenzschlüssel ein
|
||||||
registerLicense('ORg4AjUWIQA/Gnt3VVhhQlJDfV5AQmBIYVp/TGpJfl96cVxMZVVBJAtUQF1hTH5VdENiXX1dcHxUQWNVWkd2');
|
registerLicense(
|
||||||
|
'ORg4AjUWIQA/Gnt3VVhhQlJDfV5AQmBIYVp/TGpJfl96cVxMZVVBJAtUQF1hTH5VdENiXX1dcHxUQWNVWkd2'
|
||||||
|
);
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById('root')!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
|
|||||||
@@ -4,6 +4,33 @@ import react from '@vitejs/plugin-react';
|
|||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@syncfusion/ej2-react-navigations': '@syncfusion/ej2-react-navigations/index.js',
|
||||||
|
'@syncfusion/ej2-react-buttons': '@syncfusion/ej2-react-buttons/index.js',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
optimizeDeps: {
|
||||||
|
include: [
|
||||||
|
'@syncfusion/ej2-react-navigations',
|
||||||
|
'@syncfusion/ej2-react-buttons',
|
||||||
|
'@syncfusion/ej2-base',
|
||||||
|
'@syncfusion/ej2-navigations',
|
||||||
|
'@syncfusion/ej2-buttons',
|
||||||
|
'@syncfusion/ej2-react-base',
|
||||||
|
],
|
||||||
|
force: true,
|
||||||
|
esbuildOptions: {
|
||||||
|
target: 'es2020',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
target: 'es2020',
|
||||||
|
commonjsOptions: {
|
||||||
|
include: [/node_modules/],
|
||||||
|
transformMixedEsModules: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
server: {
|
server: {
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
watch: {
|
watch: {
|
||||||
|
|||||||
Reference in New Issue
Block a user