feat(Tracer Bullet)

This commit is contained in:
2025-08-11 23:47:05 +02:00
parent 582678e226
commit a50b1b3822
43 changed files with 1665 additions and 292 deletions
@@ -1,36 +1,173 @@
package at.mocode.client.web
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import at.mocode.client.common.BaseApp
import androidx.compose.runtime.*
import org.jetbrains.compose.web.dom.*
import org.jetbrains.compose.web.css.*
import kotlinx.coroutines.launch
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
@Serializable
data class PingResponse(val status: String)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun App() {
BaseApp {
Scaffold(
topBar = {
TopAppBar(
title = { Text("Meldestelle - Reitersport Management") }
)
var responseStatus by remember { mutableStateOf<String?>(null) }
var isLoading by remember { mutableStateOf(false) }
var errorMessage by remember { mutableStateOf<String?>(null) }
val scope = rememberCoroutineScope()
val httpClient = remember {
HttpClient {
install(ContentNegotiation) {
json(Json { ignoreUnknownKeys = true })
}
) { paddingValues ->
Column(
modifier = Modifier.padding(paddingValues).fillMaxSize(),
verticalArrangement = Arrangement.Center
) {
// Placeholder content
Text("Welcome to Meldestelle - Reitersport Management")
Text("This is a desktop application for managing equestrian events")
}
}
Div({
style {
fontFamily("Arial, sans-serif")
padding(20.px)
maxWidth(800.px)
margin("0 auto")
}
}) {
H1({
style {
color(Color.darkblue)
textAlign("center")
marginBottom(30.px)
}
}) {
Text("Meldestelle - Reitersport Management")
}
Div({
style {
textAlign("center")
marginBottom(20.px)
}
}) {
P { Text("Welcome to the Meldestelle Web Application") }
P { Text("Click the button below to test the backend connection") }
}
Div({
style {
textAlign("center")
marginBottom(20.px)
}
}) {
Button({
style {
backgroundColor(Color.lightblue)
color(Color.white)
border(0.px)
padding(10.px, 20.px)
fontSize(16.px)
cursor("pointer")
borderRadius(5.px)
}
onClick {
scope.launch {
try {
isLoading = true
errorMessage = null
responseStatus = null
// Try different potential gateway URLs with correct routing
val gatewayUrls = listOf(
"http://localhost:8080/api/ping/ping", // Correct gateway path
"http://localhost:8080/ping", // Direct service call (fallback)
"http://localhost:8081/api/ping/ping" // Alternative gateway port
)
var success = false
for (url in gatewayUrls) {
try {
val response: HttpResponse = httpClient.get(url)
val responseText = response.bodyAsText()
// Try to parse as JSON first
try {
val pingResponse = Json.decodeFromString<PingResponse>(responseText)
responseStatus = pingResponse.status
success = true
break
} catch (e: Exception) {
// If JSON parsing fails, use the raw response
responseStatus = responseText
success = true
break
}
} catch (e: Exception) {
// Continue to next URL
continue
}
}
if (!success) {
errorMessage = "Could not reach any backend service. Please ensure the backend is running."
}
} catch (e: Exception) {
errorMessage = "Error: ${e.message}"
} finally {
isLoading = false
}
}
}
disabled(isLoading)
}) {
Text(if (isLoading) "Loading..." else "Ping Backend")
}
}
// Response display area
Div({
style {
textAlign("center")
marginTop(20.px)
minHeight(100.px)
border(1.px, LineStyle.Solid, Color.lightgray)
borderRadius(5.px)
padding(20.px)
backgroundColor(Color.lightyellow)
}
}) {
when {
isLoading -> {
P { Text("Sending request to backend...") }
}
errorMessage != null -> {
P({
style {
color(Color.red)
fontWeight("bold")
}
}) {
Text(errorMessage!!)
}
}
responseStatus != null -> {
P({
style {
color(Color.green)
fontWeight("bold")
fontSize(18.px)
}
}) {
Text("Backend Response: $responseStatus")
}
}
else -> {
P { Text("Click the button above to test backend connection") }
}
}
}
}
@@ -1,14 +1,10 @@
package at.mocode.client.web
import androidx.compose.runtime.Composable
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import org.jetbrains.compose.web.renderComposable
fun main() = application {
Window(
title = "Meldestelle - Reitersport Management",
onCloseRequest = ::exitApplication
) {
fun main() {
renderComposable(rootElementId = "root") {
App()
}
}
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Meldestelle - Reitersport Management</title>
<style>
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}
</style>
</head>
<body>
<div id="root"></div>
<script src="MeldestelleWebApp.js"></script>
</body>
</html>