Polish up clients ui
This commit is contained in:
4
.github/copilot-instructions.md
vendored
4
.github/copilot-instructions.md
vendored
@@ -1,5 +1,9 @@
|
||||
# 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.
|
||||
|
||||
## Big picture
|
||||
|
||||
@@ -387,7 +387,7 @@ const Appointments: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold mb-4">Terminmanagement</h1>
|
||||
<h1 style={{ fontSize: '1.5rem', fontWeight: 700, marginBottom: 16 }}>Terminmanagement</h1>
|
||||
<div
|
||||
style={{
|
||||
marginBottom: 16,
|
||||
|
||||
@@ -15,43 +15,15 @@ import {
|
||||
Edit,
|
||||
} from '@syncfusion/ej2-react-grids';
|
||||
import { DialogComponent } from '@syncfusion/ej2-react-popups';
|
||||
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
|
||||
|
||||
// Raumgruppen werden dynamisch aus der API geladen
|
||||
|
||||
interface DetailsModalProps {
|
||||
open: boolean;
|
||||
client: Client | null;
|
||||
groupIdToName: Record<string | number, string>;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
function DetailsModal({ open, client, groupIdToName, onClose }: DetailsModalProps) {
|
||||
if (!open || !client) return null;
|
||||
// Details dialog renders via Syncfusion Dialog for consistent look & feel
|
||||
function DetailsContent({ client, groupIdToName }: { client: Client; groupIdToName: Record<string | number, string> }) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
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 }}>
|
||||
<div className="e-card-content">
|
||||
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
|
||||
<tbody>
|
||||
{Object.entries(client)
|
||||
.filter(
|
||||
@@ -68,7 +40,7 @@ function DetailsModal({ open, client, groupIdToName, onClose }: DetailsModalProp
|
||||
)
|
||||
.map(([key, value]) => (
|
||||
<tr key={key}>
|
||||
<td style={{ fontWeight: 'bold', padding: '6px 8px' }}>
|
||||
<td style={{ fontWeight: 600, padding: '6px 8px', width: '40%' }}>
|
||||
{key === 'group_id'
|
||||
? 'Raumgruppe'
|
||||
: key === 'ip'
|
||||
@@ -83,39 +55,33 @@ function DetailsModal({ open, client, groupIdToName, onClose }: DetailsModalProp
|
||||
? 'Modell'
|
||||
: key === 'uuid'
|
||||
? 'Client-Code'
|
||||
: key === "os_version"
|
||||
: key === 'os_version'
|
||||
? 'Betriebssystem'
|
||||
: key === 'software_version'
|
||||
? 'Clientsoftware'
|
||||
: key === 'macs'
|
||||
? 'MAC-Adressen'
|
||||
: key.charAt(0).toUpperCase() + key.slice(1)}
|
||||
:
|
||||
</td>
|
||||
<td style={{ padding: '6px 8px' }}>
|
||||
{key === 'group_id'
|
||||
? value !== undefined
|
||||
? groupIdToName[value as string | number] || value
|
||||
? groupIdToName[value as string | number] || String(value)
|
||||
: ''
|
||||
: key === 'registration_time' && value
|
||||
? new Date(
|
||||
(value as string).endsWith('Z') ? (value as string) : value + 'Z'
|
||||
(value as string).endsWith('Z') ? (value as string) : String(value) + 'Z'
|
||||
).toLocaleString()
|
||||
: 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)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
<button className="e-btn e-outline" onClick={onClose}>
|
||||
Schließen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -154,31 +120,38 @@ const Clients: React.FC = () => {
|
||||
|
||||
// DataGrid row template für Details- und Entfernen-Button
|
||||
const detailsButtonTemplate = (props: Client) => (
|
||||
<div style={{ display: 'flex', gap: '8px' }}>
|
||||
<button
|
||||
className="e-btn e-primary"
|
||||
onClick={() => setDetailsClient(props)}
|
||||
style={{ minWidth: 80 }}
|
||||
>
|
||||
<div style={{ display: 'flex', gap: 8, justifyContent: 'center' }}>
|
||||
<ButtonComponent cssClass="e-primary" onClick={() => setDetailsClient(props)}>
|
||||
Details
|
||||
</button>
|
||||
<button
|
||||
className="e-btn e-danger"
|
||||
</ButtonComponent>
|
||||
<ButtonComponent
|
||||
cssClass="e-danger"
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
handleDelete(props.uuid);
|
||||
}}
|
||||
style={{ minWidth: 80 }}
|
||||
>
|
||||
Entfernen
|
||||
</button>
|
||||
</ButtonComponent>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h2 className="text-xl font-bold">Client-Übersicht</h2>
|
||||
<div id="dialog-target">
|
||||
<div
|
||||
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 />
|
||||
</div>
|
||||
{groups.length > 0 ? (
|
||||
@@ -190,7 +163,7 @@ const Clients: React.FC = () => {
|
||||
toolbar={['Search', 'Edit', 'Update', 'Cancel']}
|
||||
allowSorting={true}
|
||||
allowFiltering={true}
|
||||
height={400}
|
||||
height={420}
|
||||
editSettings={{
|
||||
allowEditing: true,
|
||||
allowAdding: false,
|
||||
@@ -228,17 +201,17 @@ const Clients: React.FC = () => {
|
||||
width="140"
|
||||
/>
|
||||
<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
|
||||
field="last_alive"
|
||||
headerText="Last Alive"
|
||||
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
|
||||
headerText="Aktion"
|
||||
width="190"
|
||||
width="210"
|
||||
template={detailsButtonTemplate}
|
||||
textAlign="Center"
|
||||
allowEditing={false}
|
||||
@@ -246,17 +219,31 @@ const Clients: React.FC = () => {
|
||||
</ColumnsDirective>
|
||||
<Inject services={[Page, Toolbar, Search, Sort, Edit]} />
|
||||
</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 && (
|
||||
<DialogComponent
|
||||
visible={showDialog}
|
||||
@@ -264,6 +251,7 @@ const Clients: React.FC = () => {
|
||||
content="Möchten Sie diesen Client wirklich entfernen?"
|
||||
showCloseIcon={true}
|
||||
width="400px"
|
||||
target="#dialog-target"
|
||||
buttons={[
|
||||
{ click: confirmDelete, buttonModel: { content: 'Ja', isPrimary: true } },
|
||||
{ click: cancelDelete, buttonModel: { content: 'Abbrechen' } },
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Wrench } from 'lucide-react';
|
||||
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
|
||||
|
||||
const SetupModeButton: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
return (
|
||||
<button
|
||||
className="setupmode-btn flex items-center gap-2 px-4 py-2 bg-yellow-200 hover:bg-yellow-300 rounded"
|
||||
<ButtonComponent
|
||||
cssClass="e-warning"
|
||||
onClick={() => navigate('/setup')}
|
||||
title="Erweiterungsmodus starten"
|
||||
>
|
||||
<Wrench size={18} />
|
||||
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
|
||||
<Wrench size={14.4} />
|
||||
Erweiterungsmodus
|
||||
</button>
|
||||
</span>
|
||||
</ButtonComponent>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -171,10 +171,12 @@ const Dashboard: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<header className="mb-8 pb-4 border-b-2 border-[#d6c3a6]">
|
||||
<h2 className="text-3xl font-extrabold mb-2">Dashboard</h2>
|
||||
<header style={{ marginBottom: 32, paddingBottom: 16, borderBottom: '2px solid #d6c3a6' }}>
|
||||
<h2 style={{ fontSize: '1.75rem', fontWeight: 800, margin: 0, marginBottom: 8 }}>Dashboard</h2>
|
||||
</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
|
||||
dataSource={groups}
|
||||
allowPaging={true}
|
||||
|
||||
@@ -335,7 +335,7 @@ const Infoscreen_groups: React.FC = () => {
|
||||
|
||||
return (
|
||||
<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
|
||||
style={{
|
||||
display: 'flex',
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
module.exports = {
|
||||
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
|
||||
corePlugins: {
|
||||
preflight: false,
|
||||
},
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
Reference in New Issue
Block a user