client-web umbau

This commit is contained in:
stefan
2025-09-10 14:40:18 +02:00
parent 13c8ed9816
commit fb37c3a64a
27 changed files with 1566 additions and 1053 deletions
+303
View File
@@ -0,0 +1,303 @@
# Meldestelle Web-App
Eine moderne Web-Anwendung basierend auf **Compose for Web** (Compose Multiplatform) für das Meldestelle-Projekt.
## 📋 Überblick
Diese Web-Anwendung implementiert das Frontend für das Meldestelle-System unter Verwendung von Compose for Web. Sie folgt dem Architekturprinzip der maximalen Code-Wiederverwendung durch die Nutzung des `commonMain`-Source-Sets von Kotlin Multiplatform.
### Technologie-Stack
- **Frontend Framework**: Compose for Web (Compose Multiplatform 1.8.2)
- **Programmiersprache**: Kotlin/JS
- **Build-System**: Gradle 8.10
- **HTTP-Client**: Ktor Client
- **UI-Komponenten**: Compose Material 3 (aus commonMain)
- **Bundler**: Webpack (über Kotlin/JS Plugin)
- **Container**: Nginx (Production)
## 🏗️ Architektur
```
client/web-app/
├── src/
│ └── jsMain/
│ ├── kotlin/at/mocode/client/web/
│ │ └── main.kt # Entry Point
│ └── resources/
│ └── index.html # HTML Template
├── build.gradle.kts # Build Konfiguration
└── build/ # Build Artefakte
└── dist/js/productionExecutable/ # Produktionsversion
```
### Design Prinzipien
1. **Code-Wiederverwendung**: Maximale Nutzung des `client:common-ui` Moduls
2. **Compose for Web**: Deklarative UI mit `@Composable` Funktionen
3. **State Management**: Zustandsverwaltung über ViewModels im `commonMain`
4. **Plattform-Trennung**: UI-Code in `jsMain`, Logik in `commonMain`
## 🚀 Schnellstart
### Voraussetzungen
- Java 21+
- Docker und Docker Compose
- Node.js 18+ (wird automatisch im Container installiert)
### Entwicklung starten
#### Mit Docker (Empfohlen)
```bash
# Web-App im Entwicklungsmodus starten
docker compose -f docker-compose.yml -f docker-compose.clients.yml up -d web-app
# Anwendung ist verfügbar unter:
# http://localhost:3000
```
#### Lokale Entwicklung
```bash
# Abhängigkeiten installieren und Entwicklungsserver starten
./gradlew :client:web-app:jsBrowserDevelopmentRun
# Anwendung läuft auf http://localhost:8080
```
### Produktionsbuild
```bash
# Optimierte JavaScript-Bundles erstellen
./gradlew :client:web-app:jsBrowserDistribution
# Artefakte befinden sich in:
# client/web-app/build/dist/js/productionExecutable/
```
## 🔧 Entwicklung
### Projekt-Struktur
```kotlin
// main.kt - Entry Point
fun main() {
renderComposable(rootElementId = "root") {
WebApp()
}
}
@Composable
fun WebApp() {
// Verwendet die gemeinsame App-Komponente aus commonMain
App()
}
```
### Hot-Reload
Die Entwicklungsumgebung unterstützt Hot-Reload:
- Änderungen an Kotlin-Code werden automatisch neu kompiliert
- Browser wird automatisch aktualisiert
- Schnelle Entwicklungszyklen durch Webpack Dev Server
### Build-Konfiguration
Die `build.gradle.kts` konfiguriert:
```kotlin
kotlin {
js(IR) {
browser {
commonWebpackConfig {
outputFileName = "web-app.js"
// Webpack optimization directory
configDirectory = project.projectDir.resolve("webpack.config.d")
}
webpackTask {
mainOutputFileName = "web-app.js"
}
}
binaries.executable()
}
}
```
### Webpack-Optimierungen
Das Projekt verwendet erweiterte Webpack-Optimierungen für bessere Performance:
#### Code Splitting
- **Separate Chunks**: Bundle wird in ~60 kleinere, cacheable Dateien aufgeteilt
- **Vendor Chunks**: Große Libraries (Kotlin stdlib, Compose runtime, Coroutines) werden separat geladen
- **Lazy Loading**: Verbessertes Caching durch getrennte Vendor- und App-Code-Chunks
#### Bundle-Größenoptimierung
- **Tree Shaking**: Entfernt ungenutzten Code
- **Minification**: Aggressive Komprimierung im Produktionsbuild
- **Scope Hoisting**: Optimiert JavaScript-Execution
- **Performance Budget**: Warnt bei zu großen Bundles (500KB pro Asset, 800KB Gesamt)
#### Generierte Chunks (Beispiel)
```
web-app-runtime.js 1.67 KiB (Runtime)
web-app.js 482 bytes (Main App)
web-app-compose-runtime-*.js 274 KiB (Compose Framework)
web-app-kotlin-stdlib.js 165 KiB (Kotlin Standard Library)
web-app-coroutines.js 119 KiB (Kotlinx Coroutines)
web-app-vendors-*.js 1.17 MiB (Weitere Dependencies)
```
```
**Abhängigkeiten:**
- `compose.web.core` - Compose for Web Framework
- `compose.runtime` - Compose Runtime
- `project(":client:common-ui")` - Gemeinsame UI-Komponenten
- `kotlinx-coroutines-core-js` - Coroutines für Web
## 🌐 Deployment
### Docker Container
Die Anwendung wird als Docker-Container deployed:
```dockerfile
# Multi-stage Build
FROM gradle:8.10-jdk21 AS builder
# ... Build Phase
FROM nginx:1.25-alpine AS production
# ... Production Phase
```
**Features:**
- Multi-stage Build für optimale Image-Größe
- Nginx als Static File Server
- Health Checks
- Security Headers
- Gzip Kompression
### Konfiguration
Umgebungsvariablen:
- `NODE_ENV`: Entwicklungs-/Produktionsmodus
- `API_BASE_URL`: Backend API URL
- `APP_TITLE`: Anwendungstitel
- `APP_VERSION`: Versionsnummer
### Health Checks
```bash
# Container Health Check
curl --fail http://localhost:3000/health
# Antwort: {"status":"ok","service":"web-app"}
```
## 🔗 Integration
### Backend-Kommunikation
Die Web-App kommuniziert mit dem Backend über:
- **API Gateway**: `http://api-gateway:8081`
- **REST APIs**: Über Ktor Client
- **WebSocket**: Für Realtime-Updates (geplant)
### Gemeinsame Komponenten
Nutzt Komponenten aus `client:common-ui`:
- **ViewModels**: `PingViewModel` für Backend-Tests
- **UI-Komponenten**: `App`, `PingScreen`
- **Services**: `PingService` für HTTP-Aufrufe
- **Models**: Datenklassen und UI-States
### Beispiel Integration
```kotlin
@Composable
fun WebApp() {
// Verwendet die gemeinsame App-Komponente
// Diese enthält Material 3 Komponenten und ViewModels
App(baseUrl = "http://localhost:8081")
}
```
## 📊 Build-Artefakte
Nach dem Build werden folgende Dateien generiert:
```
build/dist/js/productionExecutable/
├── web-app.js # Hauptanwendung (minifiziert)
├── web-app.js.map # Source Maps
├── 731.js # Code Splitting Chunk
├── index.html # HTML Template
├── skiko.wasm # Compose Runtime (WebAssembly)
└── skiko.js # Compose JavaScript Runtime
```
## 🧪 Testing
### Entwicklungstests
```bash
# Backend-Konnektivität testen
# Öffne http://localhost:3000
# Klicke "Ping Backend" Button
```
### Build-Validierung
```bash
# Build ohne Ausführung testen
./gradlew :client:web-app:jsBrowserDevelopmentRun --dry-run
# Produktionsbuild testen
./gradlew :client:web-app:jsBrowserDistribution
```
## 📚 Weiterführende Dokumentation
- [Web-App Guideline](../../.junie/guidelines/web-app-guideline.md) - Architektur-Richtlinien
- [Docker README](../../README-DOCKER.md) - Container-Dokumentation
- [Compose for Web Docs](https://github.com/JetBrains/compose-multiplatform) - Offizielle Dokumentation
## 🔍 Troubleshooting
### Häufige Probleme
**Problem**: `Cannot connect to backend`
```bash
# Lösung: Backend Services starten
docker-compose -f docker-compose.yml -f docker-compose.services.yml up -d
```
**Problem**: `Module build failed`
```bash
# Lösung: Clean Build
./gradlew :client:web-app:clean :client:web-app:jsBrowserDevelopmentRun
```
**Problem**: `Port 3000 already in use`
```bash
# Lösung: Port in docker-compose.clients.yml ändern
ports:
- "3001:3000" # Externer Port ändern
```
### Logs und Debugging
```bash
# Container Logs anzeigen
docker logs meldestelle-web-app
# Build Logs mit Details
./gradlew :client:web-app:jsBrowserDevelopmentRun --info --stacktrace
```
---
**Letzte Aktualisierung**: 2025-09-10
**Version**: 1.0.0
+57
View File
@@ -0,0 +1,57 @@
plugins {
kotlin("multiplatform")
id("org.jetbrains.compose")
id("org.jetbrains.kotlin.plugin.compose")
}
group = "at.mocode.client.web"
version = "1.0.0"
kotlin {
js(IR) {
browser {
commonWebpackConfig {
devServer = devServer?.copy(
port = 8080,
static = mutableListOf("src/jsMain/resources")
)
// Webpack optimization settings
configDirectory = project.projectDir.resolve("webpack.config.d")
}
webpackTask {
args.add("--devtool=source-map")
}
runTask {
args.add("--devtool=source-map")
}
// Add npm dependencies for webpack plugins
useCommonJs()
}
binaries.executable()
}
sourceSets {
val jsMain by getting {
dependencies {
// Compose for Web
implementation(compose.html.core)
implementation(compose.runtime)
// Common UI module (contains ViewModels and shared components)
implementation(project(":client:common-ui"))
// Coroutines for web
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:1.7.3")
}
}
val jsTest by getting {
dependencies {
implementation(kotlin("test-js"))
}
}
}
}
@@ -0,0 +1,22 @@
package at.mocode.client.web
import androidx.compose.runtime.Composable
import at.mocode.client.ui.App
import org.jetbrains.compose.web.renderComposable
/**
* Entry point for the Compose for Web application.
* Follows the web-app guideline by using the shared App component from commonMain.
*/
fun main() {
renderComposable(rootElementId = "root") {
WebApp()
}
}
@Composable
fun WebApp() {
// Use the shared App component from commonMain
// This follows the guideline principle of maximum code reuse
App()
}
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Meldestelle</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
}
#root {
width: 100%;
height: 100vh;
}
</style>
</head>
<body>
<div id="root"></div>
</body>
</html>
@@ -0,0 +1,37 @@
// Compression and module resolution optimizations
// Enhanced module resolution to reduce bundle size
config.resolve = config.resolve || {};
config.resolve.alias = config.resolve.alias || {};
// Resolve optimizations
config.resolve.modules = ['node_modules'];
config.resolve.extensions = ['.js', '.json', '.wasm'];
// Output optimizations
config.output = config.output || {};
config.output.pathinfo = false; // Disable path info in production for smaller bundles
// Module concatenation for better tree shaking
config.optimization = config.optimization || {};
config.optimization.concatenateModules = true;
// Enable scope hoisting for better performance
config.optimization.moduleIds = 'deterministic';
config.optimization.chunkIds = 'deterministic';
// Webpack production mode optimizations
if (config.mode === 'production') {
// Disable development features
config.devtool = false; // Disable source maps in production for smaller size
// Additional optimization flags
config.optimization.flagIncludedChunks = true;
config.optimization.mergeDuplicateChunks = true;
config.optimization.removeAvailableModules = true;
config.optimization.removeEmptyChunks = true;
// Aggressive dead code elimination
config.optimization.innerGraph = true;
config.optimization.mangleExports = true;
}
@@ -0,0 +1,3 @@
// Development server configuration
config.devServer = config.devServer || {};
config.devServer.historyApiFallback = true;
@@ -0,0 +1,104 @@
const path = require('path');
// Webpack optimization configuration for production builds
config.optimization = {
// Enable tree shaking and dead code elimination
usedExports: true,
sideEffects: false,
// Code splitting configuration optimized for Kotlin/JS
splitChunks: {
chunks: 'all',
minSize: 30000,
maxSize: 300000,
maxInitialRequests: 8, // Allow more initial requests for better caching
maxAsyncRequests: 15,
cacheGroups: {
// Kotlin standard library - separate chunk
kotlinStdlib: {
test: /kotlin-kotlin-stdlib/,
name: 'kotlin-stdlib',
chunks: 'all',
enforce: true,
priority: 30,
reuseExistingChunk: true,
},
// Coroutines - separate chunk
coroutines: {
test: /kotlinx-coroutines/,
name: 'coroutines',
chunks: 'all',
enforce: true,
priority: 25,
reuseExistingChunk: true,
},
// Compose runtime - separate chunk
composeRuntime: {
test: /compose.*runtime/,
name: 'compose-runtime',
chunks: 'all',
enforce: true,
priority: 20,
reuseExistingChunk: true,
},
// Large vendor libraries
largeVendors: {
test: /ktor|androidx-collection|kotlinx-serialization/,
name: 'large-vendors',
chunks: 'all',
enforce: true,
priority: 15,
reuseExistingChunk: true,
},
// Common vendors
vendors: {
test: /[\\/]kotlin[\\/]/,
name: 'vendors',
chunks: 'all',
priority: 10,
reuseExistingChunk: true,
},
// Application code
default: {
minChunks: 2,
priority: -10,
reuseExistingChunk: true,
},
},
},
// Minimize bundle size
minimize: true,
minimizer: [
// Use default TerserPlugin for JS minification
'...',
],
// Runtime chunk optimization
runtimeChunk: {
name: 'runtime',
},
};
// Performance budget adjusted for Kotlin/JS applications
// Note: Kotlin/JS apps require all dependencies loaded initially, so larger budgets are realistic
config.performance = {
maxAssetSize: 400000, // 400KB per asset (realistic for Kotlin libs)
maxEntrypointSize: 2000000, // 2MB total entry point (realistic for Kotlin/JS + Compose)
hints: 'warning',
assetFilter: function(assetFilename) {
// Only check JS files for performance
return assetFilename.endsWith('.js');
},
};
// Production-specific optimizations
if (config.mode === 'production') {
// Additional compression and optimizations
config.optimization.concatenateModules = true;
config.optimization.providedExports = true;
// More aggressive code splitting for production
config.optimization.splitChunks.maxInitialRequests = 10;
config.optimization.splitChunks.maxAsyncRequests = 10;
}