Polish up clients ui

This commit is contained in:
RobbStarkAustria
2025-10-12 05:23:10 +00:00
parent 4d807be6f8
commit 7ab4ea14c4
7 changed files with 131 additions and 144 deletions

View File

@@ -1,5 +1,9 @@
# Copilot instructions for infoscreen_2025 # Copilot instructions for infoscreen_2025
# Purpose
These instructions tell Copilot Chat how to reason about this codebase.
Prefer explanations and refactors that align with these structures.
Use this as your shared context when proposing changes. Keep edits minimal and match existing patterns referenced below. Use this as your shared context when proposing changes. Keep edits minimal and match existing patterns referenced below.
## Big picture ## Big picture

View File

@@ -387,7 +387,7 @@ const Appointments: React.FC = () => {
return ( return (
<div> <div>
<h1 className="text-2xl font-bold mb-4">Terminmanagement</h1> <h1 style={{ fontSize: '1.5rem', fontWeight: 700, marginBottom: 16 }}>Terminmanagement</h1>
<div <div
style={{ style={{
marginBottom: 16, marginBottom: 16,

View File

@@ -15,43 +15,15 @@ import {
Edit, Edit,
} from '@syncfusion/ej2-react-grids'; } from '@syncfusion/ej2-react-grids';
import { DialogComponent } from '@syncfusion/ej2-react-popups'; import { DialogComponent } from '@syncfusion/ej2-react-popups';
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
// Raumgruppen werden dynamisch aus der API geladen // Raumgruppen werden dynamisch aus der API geladen
interface DetailsModalProps { // Details dialog renders via Syncfusion Dialog for consistent look & feel
open: boolean; function DetailsContent({ client, groupIdToName }: { client: Client; groupIdToName: Record<string | number, string> }) {
client: Client | null;
groupIdToName: Record<string | number, string>;
onClose: () => void;
}
function DetailsModal({ open, client, groupIdToName, onClose }: DetailsModalProps) {
if (!open || !client) return null;
return ( return (
<div <div className="e-card-content">
style={{ <table style={{ width: '100%', borderCollapse: 'collapse' }}>
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: 'rgba(0,0,0,0.3)',
zIndex: 1000,
}}
>
<div
style={{
background: 'white',
padding: 0,
margin: '100px auto',
maxWidth: 500,
borderRadius: 12,
boxShadow: '0 4px 24px rgba(0,0,0,0.12)',
}}
>
<div style={{ padding: 32 }}>
<h3 style={{ fontSize: '1.25rem', fontWeight: 700, marginBottom: 18 }}>Client-Details</h3>
<table style={{ width: '100%', borderCollapse: 'collapse', marginBottom: 24 }}>
<tbody> <tbody>
{Object.entries(client) {Object.entries(client)
.filter( .filter(
@@ -68,7 +40,7 @@ function DetailsModal({ open, client, groupIdToName, onClose }: DetailsModalProp
) )
.map(([key, value]) => ( .map(([key, value]) => (
<tr key={key}> <tr key={key}>
<td style={{ fontWeight: 'bold', padding: '6px 8px' }}> <td style={{ fontWeight: 600, padding: '6px 8px', width: '40%' }}>
{key === 'group_id' {key === 'group_id'
? 'Raumgruppe' ? 'Raumgruppe'
: key === 'ip' : key === 'ip'
@@ -83,39 +55,33 @@ function DetailsModal({ open, client, groupIdToName, onClose }: DetailsModalProp
? 'Modell' ? 'Modell'
: key === 'uuid' : key === 'uuid'
? 'Client-Code' ? 'Client-Code'
: key === "os_version" : key === 'os_version'
? 'Betriebssystem' ? 'Betriebssystem'
: key === 'software_version' : key === 'software_version'
? 'Clientsoftware' ? 'Clientsoftware'
: key === 'macs' : key === 'macs'
? 'MAC-Adressen' ? 'MAC-Adressen'
: key.charAt(0).toUpperCase() + key.slice(1)} : key.charAt(0).toUpperCase() + key.slice(1)}
:
</td> </td>
<td style={{ padding: '6px 8px' }}> <td style={{ padding: '6px 8px' }}>
{key === 'group_id' {key === 'group_id'
? value !== undefined ? value !== undefined
? groupIdToName[value as string | number] || value ? groupIdToName[value as string | number] || String(value)
: '' : ''
: key === 'registration_time' && value : key === 'registration_time' && value
? new Date( ? new Date(
(value as string).endsWith('Z') ? (value as string) : value + 'Z' (value as string).endsWith('Z') ? (value as string) : String(value) + 'Z'
).toLocaleString() ).toLocaleString()
: key === 'last_alive' && value : key === 'last_alive' && value
? String(value) // Wert direkt anzeigen, nicht erneut parsen ? String(value)
: key === 'macs' && typeof value === 'string'
? value.replace(/,\s*/g, ', ')
: String(value)} : String(value)}
</td> </td>
</tr> </tr>
))} ))}
</tbody> </tbody>
</table> </table>
<div style={{ textAlign: 'right' }}>
<button className="e-btn e-outline" onClick={onClose}>
Schließen
</button>
</div>
</div>
</div>
</div> </div>
); );
} }
@@ -154,31 +120,38 @@ const Clients: React.FC = () => {
// DataGrid row template für Details- und Entfernen-Button // DataGrid row template für Details- und Entfernen-Button
const detailsButtonTemplate = (props: Client) => ( const detailsButtonTemplate = (props: Client) => (
<div style={{ display: 'flex', gap: '8px' }}> <div style={{ display: 'flex', gap: 8, justifyContent: 'center' }}>
<button <ButtonComponent cssClass="e-primary" onClick={() => setDetailsClient(props)}>
className="e-btn e-primary"
onClick={() => setDetailsClient(props)}
style={{ minWidth: 80 }}
>
Details Details
</button> </ButtonComponent>
<button <ButtonComponent
className="e-btn e-danger" cssClass="e-danger"
onClick={e => { onClick={e => {
e.preventDefault();
e.stopPropagation(); e.stopPropagation();
handleDelete(props.uuid); handleDelete(props.uuid);
}} }}
style={{ minWidth: 80 }}
> >
Entfernen Entfernen
</button> </ButtonComponent>
</div> </div>
); );
return ( return (
<div> <div id="dialog-target">
<div className="flex justify-between items-center mb-4"> <div
<h2 className="text-xl font-bold">Client-Übersicht</h2> style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: 16,
gap: 12,
flexWrap: 'wrap',
}}
>
<h2 style={{ margin: 0, fontSize: '1.25rem', fontWeight: 700 }}>
Client-Übersicht
</h2>
<SetupModeButton /> <SetupModeButton />
</div> </div>
{groups.length > 0 ? ( {groups.length > 0 ? (
@@ -190,7 +163,7 @@ const Clients: React.FC = () => {
toolbar={['Search', 'Edit', 'Update', 'Cancel']} toolbar={['Search', 'Edit', 'Update', 'Cancel']}
allowSorting={true} allowSorting={true}
allowFiltering={true} allowFiltering={true}
height={400} height={420}
editSettings={{ editSettings={{
allowEditing: true, allowEditing: true,
allowAdding: false, allowAdding: false,
@@ -228,17 +201,17 @@ const Clients: React.FC = () => {
width="140" width="140"
/> />
<ColumnDirective field="uuid" headerText="UUID" allowEditing={false} width="160" /> <ColumnDirective field="uuid" headerText="UUID" allowEditing={false} width="160" />
<ColumnDirective field="ip" headerText="IP-Adresse" allowEditing={false} width="80" /> <ColumnDirective field="ip" headerText="IP-Adresse" allowEditing={false} width="100" />
<ColumnDirective <ColumnDirective
field="last_alive" field="last_alive"
headerText="Last Alive" headerText="Last Alive"
allowEditing={false} allowEditing={false}
width="120" width="150"
/> />
<ColumnDirective field="model" headerText="Model" allowEditing={true} width="120" /> <ColumnDirective field="model" headerText="Model" allowEditing={true} width="140" />
<ColumnDirective <ColumnDirective
headerText="Aktion" headerText="Aktion"
width="190" width="210"
template={detailsButtonTemplate} template={detailsButtonTemplate}
textAlign="Center" textAlign="Center"
allowEditing={false} allowEditing={false}
@@ -246,17 +219,31 @@ const Clients: React.FC = () => {
</ColumnsDirective> </ColumnsDirective>
<Inject services={[Page, Toolbar, Search, Sort, Edit]} /> <Inject services={[Page, Toolbar, Search, Sort, Edit]} />
</GridComponent> </GridComponent>
<DetailsModal
open={!!detailsClient}
client={detailsClient}
groupIdToName={groupIdToName}
onClose={() => setDetailsClient(null)}
/>
</> </>
) : ( ) : (
<div className="text-gray-500">Raumgruppen werden geladen ...</div> <span style={{ color: '#6b7280' }}>Raumgruppen werden geladen ...</span>
)} )}
{/* DialogComponent für Bestätigung */}
{/* Details-Dialog */}
{detailsClient && (
<DialogComponent
visible={!!detailsClient}
header="Client-Details"
showCloseIcon={true}
target="#dialog-target"
width="560px"
close={() => setDetailsClient(null)}
footerTemplate={() => (
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
<ButtonComponent onClick={() => setDetailsClient(null)}>{'Schließen'}</ButtonComponent>
</div>
)}
>
<DetailsContent client={detailsClient} groupIdToName={groupIdToName} />
</DialogComponent>
)}
{/* Bestätigungs-Dialog für Löschen */}
{showDialog && deleteClientId && ( {showDialog && deleteClientId && (
<DialogComponent <DialogComponent
visible={showDialog} visible={showDialog}
@@ -264,6 +251,7 @@ const Clients: React.FC = () => {
content="Möchten Sie diesen Client wirklich entfernen?" content="Möchten Sie diesen Client wirklich entfernen?"
showCloseIcon={true} showCloseIcon={true}
width="400px" width="400px"
target="#dialog-target"
buttons={[ buttons={[
{ click: confirmDelete, buttonModel: { content: 'Ja', isPrimary: true } }, { click: confirmDelete, buttonModel: { content: 'Ja', isPrimary: true } },
{ click: cancelDelete, buttonModel: { content: 'Abbrechen' } }, { click: cancelDelete, buttonModel: { content: 'Abbrechen' } },

View File

@@ -1,18 +1,21 @@
import React from 'react'; import React from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { Wrench } from 'lucide-react'; import { Wrench } from 'lucide-react';
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
const SetupModeButton: React.FC = () => { const SetupModeButton: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
return ( return (
<button <ButtonComponent
className="setupmode-btn flex items-center gap-2 px-4 py-2 bg-yellow-200 hover:bg-yellow-300 rounded" cssClass="e-warning"
onClick={() => navigate('/setup')} onClick={() => navigate('/setup')}
title="Erweiterungsmodus starten" title="Erweiterungsmodus starten"
> >
<Wrench size={18} /> <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
<Wrench size={14.4} />
Erweiterungsmodus Erweiterungsmodus
</button> </span>
</ButtonComponent>
); );
}; };

View File

@@ -171,10 +171,12 @@ const Dashboard: React.FC = () => {
return ( return (
<div> <div>
<header className="mb-8 pb-4 border-b-2 border-[#d6c3a6]"> <header style={{ marginBottom: 32, paddingBottom: 16, borderBottom: '2px solid #d6c3a6' }}>
<h2 className="text-3xl font-extrabold mb-2">Dashboard</h2> <h2 style={{ fontSize: '1.75rem', fontWeight: 800, margin: 0, marginBottom: 8 }}>Dashboard</h2>
</header> </header>
<h3 className="text-lg font-semibold mt-6 mb-4">Raumgruppen Übersicht</h3> <h3 style={{ fontSize: '1.125rem', fontWeight: 600, marginTop: 24, marginBottom: 16 }}>
Raumgruppen Übersicht
</h3>
<GridComponent <GridComponent
dataSource={groups} dataSource={groups}
allowPaging={true} allowPaging={true}

View File

@@ -335,7 +335,7 @@ const Infoscreen_groups: React.FC = () => {
return ( return (
<div id="dialog-target"> <div id="dialog-target">
<h2 className="text-xl font-bold mb-4">{de.title}</h2> <h2 style={{ fontSize: '1.25rem', fontWeight: 700, marginBottom: 16 }}>{de.title}</h2>
<div <div
style={{ style={{
display: 'flex', display: 'flex',

View File

@@ -1,10 +0,0 @@
module.exports = {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
corePlugins: {
preflight: false,
},
theme: {
extend: {},
},
plugins: [],
};