🌟 🎉 Major Update:
This commit is contained in:
534
frontend/README.md
Normal file
534
frontend/README.md
Normal file
@@ -0,0 +1,534 @@
|
||||
# MYP Frontend-System
|
||||
|
||||
## 🌐 Moderne Web-Oberfläche & Analytics-Dashboard
|
||||
|
||||
**Entwickler**: Torben Haack
|
||||
**Fachrichtung**: Fachinformatiker für Daten- und Prozessanalyse
|
||||
**Zweck**: Moderne React-basierte Benutzeroberfläche für das MYP-Druckerverwaltungssystem
|
||||
|
||||
## 🎯 Projektübersicht
|
||||
|
||||
Das **Frontend-System** ist eine vollständige **Next.js-Webanwendung**, die als moderne Benutzeroberfläche für Till Tomczaks Backend-APIs dient. Es bietet eine intuitive, responsive Bedienung und erweiterte Analytics-Funktionen für alle Stakeholder des MYP-Systems.
|
||||
|
||||
### Kernfunktionen
|
||||
- **Moderne Web-UI**: React 18 + Next.js 14 für optimale Performance
|
||||
- **Backend-Integration**: Nahtlose Anbindung an Till Tomczaks REST-APIs
|
||||
- **Advanced Analytics**: Interaktive Dashboards und Datenvisualisierung
|
||||
- **Responsive Design**: Optimiert für Desktop, Tablet und Mobile
|
||||
- **Real-time Updates**: Live-Synchronisation mit Backend-Daten
|
||||
|
||||
## 🏗️ Architektur & Integration
|
||||
|
||||
### System-Übersicht
|
||||
```
|
||||
┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
|
||||
│ Frontend-Server │◄──►│ Backend-Server │◄──►│ Raspberry Pi │
|
||||
│ (Port 3000) │ │ (Port 5000/443) │ │ (Hardware) │
|
||||
│ Torben Haack │ │ Till Tomczak │ │ Till Tomczak │
|
||||
│ │ │ │ │ │
|
||||
│ • Next.js App │ │ • Flask REST-API │ │ • Smart-Plug Control│
|
||||
│ • React Components │ │ • SQLite Database │ │ • Kiosk Interface │
|
||||
│ • Analytics UI │ │ • Authentication │ │ • Offline Operation │
|
||||
│ • Chart Libraries │ │ • Business Logic │ │ • Touch Interface │
|
||||
│ • Export Functions │ │ • Hardware APIs │ │ • System Services │
|
||||
└─────────────────────┘ └─────────────────────┘ └─────────────────────┘
|
||||
```
|
||||
|
||||
### Frontend-Technologie-Stack
|
||||
- **Framework**: Next.js 14 mit App Router
|
||||
- **Language**: TypeScript für Type-Safety
|
||||
- **UI-Library**: Radix UI + Tailwind CSS
|
||||
- **State Management**: React Server Components + SWR
|
||||
- **Charts**: Recharts + Tremor für Datenvisualisierung
|
||||
- **Database**: Drizzle ORM mit SQLite (für Frontend-spezifische Daten)
|
||||
- **Authentication**: Backend-Session-Integration
|
||||
|
||||
## 🚀 Installation & Setup
|
||||
|
||||
### Voraussetzungen
|
||||
- Node.js 18+
|
||||
- pnpm (empfohlen) oder npm
|
||||
- Zugriff auf Till Tomczaks Backend-Server
|
||||
|
||||
### 1. Frontend-Installation
|
||||
```bash
|
||||
# Repository klonen
|
||||
git clone <repository-url>
|
||||
cd Projektarbeit-MYP/frontend
|
||||
|
||||
# Abhängigkeiten installieren
|
||||
pnpm install
|
||||
|
||||
# Frontend-Datenbank einrichten (für UI-spezifische Daten)
|
||||
pnpm db:create-default
|
||||
pnpm db:generate-sqlite
|
||||
pnpm db:migrate
|
||||
|
||||
# Environment-Konfiguration
|
||||
cp .env.example .env.local
|
||||
# Backend-API-URL in .env.local eintragen
|
||||
```
|
||||
|
||||
### 2. Backend-Integration konfigurieren
|
||||
```env
|
||||
# .env.local
|
||||
NEXT_PUBLIC_BACKEND_API_URL=http://backend-server:5000/api
|
||||
NEXT_PUBLIC_BACKEND_WS_URL=ws://backend-server:5000/ws
|
||||
|
||||
# Für lokale Entwicklung
|
||||
NEXT_PUBLIC_BACKEND_API_URL=http://localhost:5000/api
|
||||
```
|
||||
|
||||
### 3. Entwicklung starten
|
||||
```bash
|
||||
# Entwicklungsserver starten
|
||||
pnpm dev
|
||||
|
||||
# Frontend läuft auf http://localhost:3000
|
||||
# Backend-APIs werden von http://backend-server:5000/api konsumiert
|
||||
```
|
||||
|
||||
### 4. Produktions-Deployment
|
||||
```bash
|
||||
# Build für Produktion
|
||||
pnpm build
|
||||
|
||||
# Produktionsserver starten
|
||||
pnpm start
|
||||
|
||||
# Oder mit PM2 für Produktionsumgebung
|
||||
pm2 start ecosystem.config.js
|
||||
```
|
||||
|
||||
## 📁 Projektstruktur
|
||||
|
||||
```
|
||||
frontend/
|
||||
├── src/
|
||||
│ ├── app/ # Next.js App Router
|
||||
│ │ ├── (dashboard)/ # Dashboard-Layout-Gruppe
|
||||
│ │ │ ├── page.tsx # Haupt-Dashboard
|
||||
│ │ │ ├── printers/ # Drucker-Management
|
||||
│ │ │ ├── jobs/ # Job-Verwaltung
|
||||
│ │ │ └── analytics/ # Analytics-Dashboards
|
||||
│ │ ├── admin/ # Admin-Bereich
|
||||
│ │ ├── auth/ # Authentifizierung (Backend-Integration)
|
||||
│ │ └── api/ # Frontend-API-Routes (Proxy/Cache)
|
||||
│ ├── components/ # React-Komponenten
|
||||
│ │ ├── ui/ # Basis-UI-Komponenten (Radix UI)
|
||||
│ │ ├── charts/ # Chart-Komponenten (Recharts)
|
||||
│ │ ├── forms/ # Formular-Komponenten
|
||||
│ │ ├── layout/ # Layout-Komponenten
|
||||
│ │ └── printer/ # Drucker-spezifische Komponenten
|
||||
│ ├── lib/ # Utility-Bibliotheken
|
||||
│ │ ├── api/ # Backend-API-Client
|
||||
│ │ ├── auth/ # Authentifizierung
|
||||
│ │ ├── utils/ # Helper-Funktionen
|
||||
│ │ └── analytics/ # Analytics-Algorithmen
|
||||
│ ├── hooks/ # Custom React Hooks
|
||||
│ ├── types/ # TypeScript-Typen
|
||||
│ └── styles/ # Global Styles
|
||||
├── public/ # Statische Assets
|
||||
├── docs/ # Frontend-Dokumentation
|
||||
├── drizzle/ # Frontend-DB-Migrationen
|
||||
└── package.json # Dependencies
|
||||
```
|
||||
|
||||
## 🔗 Backend-API-Integration
|
||||
|
||||
### API-Client-Implementation
|
||||
```typescript
|
||||
// src/lib/api/myp-client.ts
|
||||
export class MYPApiClient {
|
||||
private baseURL: string;
|
||||
|
||||
constructor() {
|
||||
this.baseURL = process.env.NEXT_PUBLIC_BACKEND_API_URL || 'http://localhost:5000/api';
|
||||
}
|
||||
|
||||
// Drucker-Management (Till Tomczaks Backend APIs)
|
||||
async getPrinters(): Promise<Printer[]> {
|
||||
const response = await fetch(`${this.baseURL}/printers`, {
|
||||
credentials: 'include', // Session-Cookies übertragen
|
||||
});
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async createPrinter(printer: CreatePrinterRequest): Promise<Printer> {
|
||||
const response = await fetch(`${this.baseURL}/printers`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(printer),
|
||||
});
|
||||
return response.json();
|
||||
}
|
||||
|
||||
// Job-Management
|
||||
async getJobs(): Promise<Job[]> {
|
||||
const response = await fetch(`${this.baseURL}/jobs`, {
|
||||
credentials: 'include',
|
||||
});
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async createJob(job: CreateJobRequest): Promise<Job> {
|
||||
const response = await fetch(`${this.baseURL}/jobs`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(job),
|
||||
});
|
||||
return response.json();
|
||||
}
|
||||
|
||||
// Smart-Plug-Steuerung (über Backend)
|
||||
async controlPrinter(printerId: string, action: 'on' | 'off'): Promise<void> {
|
||||
await fetch(`${this.baseURL}/plugs/${printerId}/${action}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
}
|
||||
|
||||
// Analytics & Statistiken
|
||||
async getStats(): Promise<Statistics> {
|
||||
const response = await fetch(`${this.baseURL}/stats`, {
|
||||
credentials: 'include',
|
||||
});
|
||||
return response.json();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### React Hooks für API-Integration
|
||||
```typescript
|
||||
// src/hooks/usePrinters.ts
|
||||
import useSWR from 'swr';
|
||||
import { MYPApiClient } from '@/lib/api/myp-client';
|
||||
|
||||
const apiClient = new MYPApiClient();
|
||||
|
||||
export function usePrinters() {
|
||||
const { data, error, mutate } = useSWR('/printers', () => apiClient.getPrinters(), {
|
||||
refreshInterval: 30000, // Alle 30 Sekunden aktualisieren
|
||||
});
|
||||
|
||||
return {
|
||||
printers: data || [],
|
||||
isLoading: !error && !data,
|
||||
isError: error,
|
||||
refresh: mutate,
|
||||
};
|
||||
}
|
||||
|
||||
export function useJobs() {
|
||||
const { data, error, mutate } = useSWR('/jobs', () => apiClient.getJobs(), {
|
||||
refreshInterval: 10000, // Alle 10 Sekunden aktualisieren
|
||||
});
|
||||
|
||||
return {
|
||||
jobs: data || [],
|
||||
isLoading: !error && !data,
|
||||
isError: error,
|
||||
refresh: mutate,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 Analytics & Visualisierung
|
||||
|
||||
### Dashboard-Komponenten
|
||||
```typescript
|
||||
// src/components/charts/PrinterUsageChart.tsx
|
||||
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
|
||||
|
||||
interface PrinterUsageChartProps {
|
||||
data: UsageData[];
|
||||
}
|
||||
|
||||
export function PrinterUsageChart({ data }: PrinterUsageChartProps) {
|
||||
return (
|
||||
<ResponsiveContainer width="100%" height={400}>
|
||||
<LineChart data={data}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="date" />
|
||||
<YAxis />
|
||||
<Tooltip />
|
||||
<Line type="monotone" dataKey="usage" stroke="#8884d8" strokeWidth={2} />
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Analytics-Algorithmen
|
||||
```typescript
|
||||
// src/lib/analytics/calculations.ts
|
||||
export class Analytics {
|
||||
static calculateUsageStats(jobs: Job[]): UsageStats {
|
||||
const totalJobs = jobs.length;
|
||||
const totalDuration = jobs.reduce((sum, job) => sum + job.duration, 0);
|
||||
const averageDuration = totalDuration / totalJobs;
|
||||
|
||||
return {
|
||||
totalJobs,
|
||||
totalDuration,
|
||||
averageDuration,
|
||||
peakHours: this.identifyPeakHours(jobs),
|
||||
efficiency: this.calculateEfficiency(jobs),
|
||||
};
|
||||
}
|
||||
|
||||
static generateReport(data: any[]): ReportData {
|
||||
// Report-Generierung für PDF/Excel-Export
|
||||
return {
|
||||
summary: this.calculateSummary(data),
|
||||
charts: this.prepareChartData(data),
|
||||
tables: this.prepareTableData(data),
|
||||
};
|
||||
}
|
||||
|
||||
private static identifyPeakHours(jobs: Job[]): number[] {
|
||||
const hourCounts = new Array(24).fill(0);
|
||||
jobs.forEach(job => {
|
||||
const hour = new Date(job.startTime).getHours();
|
||||
hourCounts[hour]++;
|
||||
});
|
||||
return hourCounts;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🎨 UI/UX-Design
|
||||
|
||||
### Design-System
|
||||
```typescript
|
||||
// src/components/ui/button.tsx (Radix UI + Tailwind)
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link';
|
||||
size?: 'default' | 'sm' | 'lg' | 'icon';
|
||||
}
|
||||
|
||||
export function Button({ className, variant = 'default', size = 'default', ...props }: ButtonProps) {
|
||||
return (
|
||||
<button
|
||||
className={cn(
|
||||
'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
||||
{
|
||||
'bg-primary text-primary-foreground hover:bg-primary/90': variant === 'default',
|
||||
'bg-destructive text-destructive-foreground hover:bg-destructive/90': variant === 'destructive',
|
||||
// ... weitere Varianten
|
||||
},
|
||||
{
|
||||
'h-10 px-4 py-2': size === 'default',
|
||||
'h-9 rounded-md px-3': size === 'sm',
|
||||
// ... weitere Größen
|
||||
},
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Responsive Layout
|
||||
```typescript
|
||||
// src/components/layout/DashboardLayout.tsx
|
||||
export function DashboardLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<header className="border-b">
|
||||
<div className="container mx-auto px-4 py-4">
|
||||
<nav className="flex items-center justify-between">
|
||||
<h1 className="text-2xl font-bold">MYP Dashboard</h1>
|
||||
<UserMenu />
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6">
|
||||
<aside className="lg:col-span-1">
|
||||
<Navigation />
|
||||
</aside>
|
||||
<main className="lg:col-span-3">
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## 📈 Erweiterte Features
|
||||
|
||||
### Real-time Updates via WebSocket
|
||||
```typescript
|
||||
// src/hooks/useRealTimeUpdates.ts
|
||||
export function useRealTimeUpdates() {
|
||||
const [socket, setSocket] = useState<WebSocket | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const ws = new WebSocket(process.env.NEXT_PUBLIC_BACKEND_WS_URL!);
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
// Updates an SWR-Cache weiterleiten
|
||||
mutate('/printers', data.printers, false);
|
||||
mutate('/jobs', data.jobs, false);
|
||||
};
|
||||
|
||||
setSocket(ws);
|
||||
return () => ws.close();
|
||||
}, []);
|
||||
|
||||
return socket;
|
||||
}
|
||||
```
|
||||
|
||||
### Export-Funktionen
|
||||
```typescript
|
||||
// src/lib/export/exportUtils.ts
|
||||
export class ExportUtils {
|
||||
static async generatePDFReport(data: ReportData): Promise<Blob> {
|
||||
const { jsPDF } = await import('jspdf');
|
||||
const doc = new jsPDF();
|
||||
|
||||
// PDF-Generierung
|
||||
doc.text('MYP Analytics Report', 20, 20);
|
||||
// ... Report-Inhalte hinzufügen
|
||||
|
||||
return doc.output('blob');
|
||||
}
|
||||
|
||||
static async generateExcelReport(data: ReportData): Promise<Blob> {
|
||||
const XLSX = await import('xlsx');
|
||||
const workbook = XLSX.utils.book_new();
|
||||
|
||||
// Excel-Sheet erstellen
|
||||
const worksheet = XLSX.utils.json_to_sheet(data.tables);
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, 'Analytics');
|
||||
|
||||
return XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🚀 Deployment
|
||||
|
||||
### Produktions-Konfiguration
|
||||
```bash
|
||||
# .env.production
|
||||
NEXT_PUBLIC_BACKEND_API_URL=https://backend.myp.mercedes-benz.com/api
|
||||
NEXT_PUBLIC_BACKEND_WS_URL=wss://backend.myp.mercedes-benz.com/ws
|
||||
```
|
||||
|
||||
### PM2 Ecosystem
|
||||
```javascript
|
||||
// ecosystem.config.js
|
||||
module.exports = {
|
||||
apps: [{
|
||||
name: 'myp-frontend',
|
||||
script: 'npm',
|
||||
args: 'start',
|
||||
instances: 'max',
|
||||
exec_mode: 'cluster',
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
PORT: 3000,
|
||||
},
|
||||
}],
|
||||
};
|
||||
```
|
||||
|
||||
### Docker-Support
|
||||
```dockerfile
|
||||
# Dockerfile
|
||||
FROM node:18-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Dependencies
|
||||
COPY package*.json ./
|
||||
RUN npm ci --only=production
|
||||
|
||||
# App
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["npm", "start"]
|
||||
```
|
||||
|
||||
## 🔧 Development-Workflow
|
||||
|
||||
### Code-Quality
|
||||
```bash
|
||||
# Linting & Formatting
|
||||
pnpm lint # ESLint check
|
||||
pnpm lint:fix # ESLint fix
|
||||
pnpm format # Prettier formatting
|
||||
|
||||
# Testing
|
||||
pnpm test # Unit tests
|
||||
pnpm test:e2e # End-to-end tests
|
||||
|
||||
# Build-Checks
|
||||
pnpm build # Production build
|
||||
pnpm type-check # TypeScript check
|
||||
```
|
||||
|
||||
### Integration-Testing
|
||||
```typescript
|
||||
// tests/integration/api.test.ts
|
||||
describe('Backend-Integration', () => {
|
||||
test('should fetch printers from backend', async () => {
|
||||
const client = new MYPApiClient();
|
||||
const printers = await client.getPrinters();
|
||||
|
||||
expect(printers).toBeInstanceOf(Array);
|
||||
expect(printers[0]).toHaveProperty('id');
|
||||
expect(printers[0]).toHaveProperty('name');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## 👥 Entwickler-Information
|
||||
|
||||
### Torben Haack - Frontend & Analytics-Spezialist
|
||||
- **UI/UX-Expertise**: React-Komponenten und responsive Design
|
||||
- **Integration-Spezialist**: Nahtlose Backend-API-Anbindung
|
||||
- **Analytics-Entwicklung**: Datenvisualisierung und Reporting
|
||||
- **Performance-Optimierung**: Next.js und React Best Practices
|
||||
|
||||
### Beitrag zum Gesamtsystem
|
||||
Das Frontend-System ergänzt Till Tomczaks Backend-Infrastructure um:
|
||||
- **Moderne Benutzeroberfläche**: Intuitive Web-UI für alle Stakeholder
|
||||
- **Advanced Analytics**: Erweiterte Datenauswertung und Visualisierung
|
||||
- **Cross-Platform-Support**: Responsive Design für alle Endgeräte
|
||||
- **Export-Funktionen**: PDF/Excel-Reports für Management und Analyse
|
||||
|
||||
## 📚 Dokumentation & Support
|
||||
|
||||
### Entwickler-Ressourcen
|
||||
- **Component-Library**: [`src/components/ui/`](src/components/ui/) - Wiederverwendbare UI-Komponenten
|
||||
- **API-Integration**: [`src/lib/api/`](src/lib/api/) - Backend-Anbindung
|
||||
- **Analytics-Tools**: [`src/lib/analytics/`](src/lib/analytics/) - Auswertungs-Algorithmen
|
||||
|
||||
### Integration mit Backend
|
||||
- **Backend-APIs**: Till Tomczaks REST-Endpunkte unter `/api/*`
|
||||
- **Authentifizierung**: Session-basiert über Backend-System
|
||||
- **Real-time**: WebSocket-Verbindung für Live-Updates
|
||||
|
||||
---
|
||||
|
||||
**Entwickelt von**: Torben Haack
|
||||
**Projektart**: Frontend & Analytics für MYP-System
|
||||
**Framework**: Next.js 14 + TypeScript + React 18
|
||||
**Integration**: Vollständige Backend-API-Anbindung (Till Tomczak)
|
||||
**Status**: Produktionsbereit
|
Reference in New Issue
Block a user