add programminfo.tsx and program-info.json for
information and display of program details add simple logout-page
This commit is contained in:
58
dashboard/public/program-info.json
Normal file
58
dashboard/public/program-info.json
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"appName": "Infoscreen-Management",
|
||||
"version": "2025.1.0-alpha.3",
|
||||
"copyright": "© 2025 Third-Age-Applications",
|
||||
"supportContact": "support@third-age-applications.com",
|
||||
"description": "Eine zentrale Verwaltungsoberfläche für digitale Informationsbildschirme.",
|
||||
"techStack": {
|
||||
"frontend": "React, Vite, TypeScript, Tailwind CSS",
|
||||
"backend": "Python (Flask), SQLAlchemy",
|
||||
"database": "MariaDB",
|
||||
"realtime": "Mosquitto (MQTT)",
|
||||
"containerization": "Docker"
|
||||
},
|
||||
"openSourceComponents": {
|
||||
"frontend": [
|
||||
{ "name": "React", "license": "MIT" },
|
||||
{ "name": "Vite", "license": "MIT" },
|
||||
{ "name": "Tailwind CSS", "license": "MIT" },
|
||||
{ "name": "Lucide Icons", "license": "ISC" },
|
||||
{ "name": "Syncfusion UI Components", "license": "Kommerziell / Community" }
|
||||
],
|
||||
"backend": [
|
||||
{ "name": "Flask", "license": "BSD" },
|
||||
{ "name": "SQLAlchemy", "license": "MIT" },
|
||||
{ "name": "Paho-MQTT", "license": "EPL/EDL" },
|
||||
{ "name": "Alembic", "license": "MIT" }
|
||||
]
|
||||
},
|
||||
"buildInfo": {
|
||||
"buildDate": "2025-08-30T12:00:00Z",
|
||||
"commitId": "a1b2c3d4e5f6"
|
||||
},
|
||||
"changelog": [
|
||||
{
|
||||
"version": "2025.1.0-alpha.3",
|
||||
"date": "2025-08-30",
|
||||
"changes": [
|
||||
"NEU: Programminfo-Seite mit dynamischen Daten, Build-Infos und Changelog.",
|
||||
"NEU: Logout-Funktionalität implementiert.",
|
||||
"FIX: Breite der Sidebar im eingeklappten Zustand korrigiert."
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2025.1.0-alpha.2",
|
||||
"date": "2025-08-29",
|
||||
"changes": [
|
||||
"INFO: Analyse und Anzeige der verwendeten Open-Source-Bibliotheken."
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2025.1.0-alpha.1",
|
||||
"date": "2025-08-28",
|
||||
"changes": [
|
||||
"Initiales Setup des Projekts und der Grundstruktur."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -28,7 +28,7 @@ body {
|
||||
/* Layout-Container für Sidebar und Content */
|
||||
.layout-container {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
height: 100vh; /* Feste Höhe auf die des Viewports setzen */
|
||||
overflow: hidden; /* Verhindert, dass der Scrollbalken den gesamten Container betrifft */
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ body {
|
||||
color: var(--sidebar-fg);
|
||||
font-size: 1.15rem;
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
|
||||
width: 240px; /* Feste Breite für die Sidebar */
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
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 logo from './assets/logo.png';
|
||||
import './App.css';
|
||||
|
||||
@@ -21,6 +15,7 @@ import {
|
||||
MonitorDotIcon,
|
||||
LogOut,
|
||||
Wrench,
|
||||
Info,
|
||||
} from 'lucide-react';
|
||||
import { ToastProvider } from './components/ToastProvider';
|
||||
|
||||
@@ -34,6 +29,7 @@ const sidebarItems = [
|
||||
{ name: 'Medien', path: '/medien', icon: Image },
|
||||
{ name: 'Benutzer', path: '/benutzer', icon: User },
|
||||
{ name: 'Einstellungen', path: '/einstellungen', icon: Settings },
|
||||
{ name: 'Programminfo', path: '/programminfo', icon: Info },
|
||||
];
|
||||
|
||||
// Dummy Components (können in eigene Dateien ausgelagert werden)
|
||||
@@ -46,18 +42,28 @@ import Media from './media';
|
||||
import Benutzer from './benutzer';
|
||||
import Einstellungen from './einstellungen';
|
||||
import SetupMode from './SetupMode';
|
||||
import Programminfo from './programminfo';
|
||||
import Logout from './logout';
|
||||
|
||||
// ENV aus .env holen (Platzhalter, im echten Projekt über process.env oder API)
|
||||
// const ENV = import.meta.env.VITE_ENV || 'development';
|
||||
|
||||
const Layout: React.FC = () => {
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const [version, setVersion] = useState('');
|
||||
|
||||
React.useEffect(() => {
|
||||
fetch('/program-info.json')
|
||||
.then(res => res.json())
|
||||
.then(data => setVersion(data.version))
|
||||
.catch(err => console.error('Failed to load version info:', err));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="layout-container">
|
||||
{/* Sidebar */}
|
||||
<aside
|
||||
className={`sidebar-theme flex flex-col transition-all duration-300 ${collapsed ? 'w-20' : 'w-30'}`}
|
||||
className={`sidebar-theme flex flex-col transition-all duration-300 ${collapsed ? 'w-20' : 'w-64'}`}
|
||||
>
|
||||
<div
|
||||
className="h-20 flex items-center justify-center border-b"
|
||||
@@ -94,20 +100,19 @@ const Layout: React.FC = () => {
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
{/* Abmelden-Button immer ganz unten */}
|
||||
<div className="mb-4 mt-auto">
|
||||
<button
|
||||
{/* 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}
|
||||
onClick={() => {
|
||||
// Hier ggf. Logout-Logik einfügen
|
||||
window.location.href = '/logout';
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<LogOut size={22} />
|
||||
{!collapsed && 'Abmelden'}
|
||||
</button>
|
||||
</Link>
|
||||
{!collapsed && version && (
|
||||
<div className="px-6 pt-2 text-xs text-center opacity-70">Version {version}</div>
|
||||
)}
|
||||
</div>
|
||||
</aside>
|
||||
{/* Main Content */}
|
||||
@@ -143,9 +148,7 @@ const Layout: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const App: React.FC = () => {
|
||||
|
||||
// Automatische Navigation zu /clients bei leerer Beschreibung entfernt
|
||||
|
||||
return (
|
||||
@@ -161,7 +164,9 @@ const App: React.FC = () => {
|
||||
<Route path="einstellungen" element={<Einstellungen />} />
|
||||
<Route path="clients" element={<Infoscreens />} />
|
||||
<Route path="setup" element={<SetupMode />} />
|
||||
<Route path="programminfo" element={<Programminfo />} />
|
||||
</Route>
|
||||
<Route path="/logout" element={<Logout />} />
|
||||
</Routes>
|
||||
</ToastProvider>
|
||||
);
|
||||
|
||||
12
dashboard/src/logout.tsx
Normal file
12
dashboard/src/logout.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
|
||||
const Logout: React.FC = () => (
|
||||
<div className="flex items-center justify-center h-screen">
|
||||
<div className="text-center">
|
||||
<h2 className="text-2xl font-bold mb-4">Abmeldung</h2>
|
||||
<p>Sie haben sich erfolgreich abgemeldet.</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Logout;
|
||||
168
dashboard/src/programminfo.tsx
Normal file
168
dashboard/src/programminfo.tsx
Normal file
@@ -0,0 +1,168 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
interface ProgramInfo {
|
||||
appName: string;
|
||||
version: string;
|
||||
copyright: string;
|
||||
supportContact: string;
|
||||
description: string;
|
||||
techStack: {
|
||||
[key: string]: string;
|
||||
};
|
||||
openSourceComponents: {
|
||||
frontend: { name: string; license: string }[];
|
||||
backend: { name: string; license: string }[];
|
||||
};
|
||||
buildInfo: {
|
||||
buildDate: string;
|
||||
commitId: string;
|
||||
};
|
||||
changelog: {
|
||||
version: string;
|
||||
date: string;
|
||||
changes: string[];
|
||||
}[];
|
||||
}
|
||||
|
||||
const Programminfo: React.FC = () => {
|
||||
const [info, setInfo] = useState<ProgramInfo | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetch('/program-info.json')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Netzwerk-Antwort war nicht ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => setInfo(data))
|
||||
.catch(error => {
|
||||
console.error('Fehler beim Laden der Programminformationen:', error);
|
||||
setError('Informationen konnten nicht geladen werden.');
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-xl font-bold mb-4 text-red-600">Fehler</h2>
|
||||
<p>{error}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!info) {
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-xl font-bold mb-4">Programminfo</h2>
|
||||
<p>Lade Informationen...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-2">{info.appName}</h2>
|
||||
<p className="text-gray-600">{info.description}</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
{/* Allgemeine Infos & Build */}
|
||||
<div className="bg-white p-6 rounded-lg shadow">
|
||||
<h3 className="text-xl font-semibold mb-4 border-b pb-2">Allgemein</h3>
|
||||
<div className="space-y-3">
|
||||
<p>
|
||||
<strong>Version:</strong> {info.version}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Copyright:</strong> {info.copyright}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Support:</strong>{' '}
|
||||
<a href={`mailto:${info.supportContact}`} className="text-blue-600 hover:underline">
|
||||
{info.supportContact}
|
||||
</a>
|
||||
</p>
|
||||
<hr className="my-4" />
|
||||
<h4 className="font-semibold">Build-Informationen</h4>
|
||||
<p>
|
||||
<strong>Build-Datum:</strong>{' '}
|
||||
{new Date(info.buildInfo.buildDate).toLocaleString('de-DE')}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Commit-ID:</strong>{' '}
|
||||
<span className="font-mono text-sm bg-gray-100 p-1 rounded">
|
||||
{info.buildInfo.commitId}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Technischer Stack */}
|
||||
<div className="bg-white p-6 rounded-lg shadow">
|
||||
<h3 className="text-xl font-semibold mb-4 border-b pb-2">Technologie-Stack</h3>
|
||||
<ul className="list-disc list-inside space-y-2">
|
||||
{Object.entries(info.techStack).map(([key, value]) => (
|
||||
<li key={key}>
|
||||
<span className="font-semibold capitalize">{key}:</span> {value}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Changelog */}
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold mb-4">Änderungsprotokoll (Changelog)</h3>
|
||||
<div className="space-y-6">
|
||||
{info.changelog.map(log => (
|
||||
<div key={log.version} className="bg-white p-6 rounded-lg shadow">
|
||||
<h4 className="font-bold text-lg mb-2">
|
||||
Version {log.version}{' '}
|
||||
<span className="text-sm font-normal text-gray-500">
|
||||
- {new Date(log.date).toLocaleDateString('de-DE')}
|
||||
</span>
|
||||
</h4>
|
||||
<ul className="list-disc list-inside space-y-1 text-gray-700">
|
||||
{log.changes.map((change, index) => (
|
||||
<li key={index}>{change}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Open Source Komponenten */}
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold mb-4">Verwendete Open-Source-Komponenten</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<div className="bg-white p-6 rounded-lg shadow">
|
||||
<h4 className="font-bold mb-3">Frontend</h4>
|
||||
<ul className="list-disc list-inside space-y-1">
|
||||
{info.openSourceComponents.frontend.map(item => (
|
||||
<li key={item.name}>
|
||||
{item.name} ({item.license}-Lizenz)
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="bg-white p-6 rounded-lg shadow">
|
||||
<h4 className="font-bold mb-3">Backend</h4>
|
||||
<ul className="list-disc list-inside space-y-1">
|
||||
{info.openSourceComponents.backend.map(item => (
|
||||
<li key={item.name}>
|
||||
{item.name} ({item.license}-Lizenz)
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Programminfo;
|
||||
Reference in New Issue
Block a user