diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 00000000..30cf57ed
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,10 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Ignored default folder with query files
+/queries/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 00000000..351ae1ba
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+Meldestelle
\ No newline at end of file
diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml
new file mode 100644
index 00000000..4a53bee8
--- /dev/null
+++ b/.idea/AndroidProjectSystem.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/auth_js_1_0_0.xml b/.idea/artifacts/auth_js_1_0_0.xml
new file mode 100644
index 00000000..3802c0e5
--- /dev/null
+++ b/.idea/artifacts/auth_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/core/auth/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/auth_jvm_1_0_0.xml b/.idea/artifacts/auth_jvm_1_0_0.xml
new file mode 100644
index 00000000..e70629e8
--- /dev/null
+++ b/.idea/artifacts/auth_jvm_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/core/auth/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/auth_wasm_js_1_0_0.xml b/.idea/artifacts/auth_wasm_js_1_0_0.xml
new file mode 100644
index 00000000..77c88fe3
--- /dev/null
+++ b/.idea/artifacts/auth_wasm_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/core/auth/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/billing_domain_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/billing_domain_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..fb2545e2
--- /dev/null
+++ b/.idea/artifacts/billing_domain_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/backend/services/billing/billing-domain/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/billing_domain_jvm_1_0_0_SNAPSHOT.xml b/.idea/artifacts/billing_domain_jvm_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..dc055f93
--- /dev/null
+++ b/.idea/artifacts/billing_domain_jvm_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/backend/services/billing/billing-domain/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/billing_domain_wasm_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/billing_domain_wasm_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..5a857bb0
--- /dev/null
+++ b/.idea/artifacts/billing_domain_wasm_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/backend/services/billing/billing-domain/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/billing_feature_js_1_0_0.xml b/.idea/artifacts/billing_feature_js_1_0_0.xml
new file mode 100644
index 00000000..d67a3e85
--- /dev/null
+++ b/.idea/artifacts/billing_feature_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/billing-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/billing_feature_jvm_1_0_0.xml b/.idea/artifacts/billing_feature_jvm_1_0_0.xml
new file mode 100644
index 00000000..4d89ed85
--- /dev/null
+++ b/.idea/artifacts/billing_feature_jvm_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/billing-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/billing_feature_wasm_js_1_0_0.xml b/.idea/artifacts/billing_feature_wasm_js_1_0_0.xml
new file mode 100644
index 00000000..24ebeca8
--- /dev/null
+++ b/.idea/artifacts/billing_feature_wasm_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/billing-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/core_domain_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/core_domain_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..2ce49309
--- /dev/null
+++ b/.idea/artifacts/core_domain_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/core/core-domain/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/core_domain_jvm_1_0_0_SNAPSHOT.xml b/.idea/artifacts/core_domain_jvm_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..44d39edf
--- /dev/null
+++ b/.idea/artifacts/core_domain_jvm_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/core/core-domain/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/core_domain_wasm_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/core_domain_wasm_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..059a09a3
--- /dev/null
+++ b/.idea/artifacts/core_domain_wasm_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/core/core-domain/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/core_utils_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/core_utils_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..134a3aff
--- /dev/null
+++ b/.idea/artifacts/core_utils_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/core/core-utils/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/core_utils_jvm_1_0_0_SNAPSHOT.xml b/.idea/artifacts/core_utils_jvm_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..4abcd323
--- /dev/null
+++ b/.idea/artifacts/core_utils_jvm_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/core/core-utils/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/core_utils_wasm_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/core_utils_wasm_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..91cf60ce
--- /dev/null
+++ b/.idea/artifacts/core_utils_wasm_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/core/core-utils/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/design_system_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/design_system_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..2a81f3d9
--- /dev/null
+++ b/.idea/artifacts/design_system_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/core/design-system/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/design_system_jvm_1_0_0_SNAPSHOT.xml b/.idea/artifacts/design_system_jvm_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..88d8266d
--- /dev/null
+++ b/.idea/artifacts/design_system_jvm_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/core/design-system/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/design_system_wasm_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/design_system_wasm_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..da61b1d8
--- /dev/null
+++ b/.idea/artifacts/design_system_wasm_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/core/design-system/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/device_initialization_js_1_0_0.xml b/.idea/artifacts/device_initialization_js_1_0_0.xml
new file mode 100644
index 00000000..3558176e
--- /dev/null
+++ b/.idea/artifacts/device_initialization_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/device-initialization/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/device_initialization_jvm_1_0_0.xml b/.idea/artifacts/device_initialization_jvm_1_0_0.xml
new file mode 100644
index 00000000..52954795
--- /dev/null
+++ b/.idea/artifacts/device_initialization_jvm_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/device-initialization/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/device_initialization_wasm_js_1_0_0.xml b/.idea/artifacts/device_initialization_wasm_js_1_0_0.xml
new file mode 100644
index 00000000..2b6bc4da
--- /dev/null
+++ b/.idea/artifacts/device_initialization_wasm_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/device-initialization/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/domain_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/domain_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..c3407b57
--- /dev/null
+++ b/.idea/artifacts/domain_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/core/domain/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/domain_jvm_1_0_0_SNAPSHOT.xml b/.idea/artifacts/domain_jvm_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..6278c6bb
--- /dev/null
+++ b/.idea/artifacts/domain_jvm_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/core/domain/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/domain_wasm_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/domain_wasm_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..a7120f15
--- /dev/null
+++ b/.idea/artifacts/domain_wasm_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/core/domain/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/entries_api_js_1_0_0.xml b/.idea/artifacts/entries_api_js_1_0_0.xml
new file mode 100644
index 00000000..3129ad09
--- /dev/null
+++ b/.idea/artifacts/entries_api_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/backend/services/entries/entries-api/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/entries_api_jvm_1_0_0.xml b/.idea/artifacts/entries_api_jvm_1_0_0.xml
new file mode 100644
index 00000000..9f742751
--- /dev/null
+++ b/.idea/artifacts/entries_api_jvm_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/backend/services/entries/entries-api/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/entries_api_wasm_js_1_0_0.xml b/.idea/artifacts/entries_api_wasm_js_1_0_0.xml
new file mode 100644
index 00000000..11f7c054
--- /dev/null
+++ b/.idea/artifacts/entries_api_wasm_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/backend/services/entries/entries-api/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/entries_domain_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/entries_domain_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..2f3b3bc2
--- /dev/null
+++ b/.idea/artifacts/entries_domain_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/backend/services/entries/entries-domain/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/entries_domain_jvm_1_0_0_SNAPSHOT.xml b/.idea/artifacts/entries_domain_jvm_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..d3a10993
--- /dev/null
+++ b/.idea/artifacts/entries_domain_jvm_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/backend/services/entries/entries-domain/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/entries_domain_wasm_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/entries_domain_wasm_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..dc89e3bf
--- /dev/null
+++ b/.idea/artifacts/entries_domain_wasm_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/backend/services/entries/entries-domain/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/events_common_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/events_common_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..0d99e25c
--- /dev/null
+++ b/.idea/artifacts/events_common_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/backend/services/events/events-common/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/events_common_jvm_1_0_0_SNAPSHOT.xml b/.idea/artifacts/events_common_jvm_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..a7668efd
--- /dev/null
+++ b/.idea/artifacts/events_common_jvm_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/backend/services/events/events-common/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/events_common_wasm_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/events_common_wasm_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..683869ed
--- /dev/null
+++ b/.idea/artifacts/events_common_wasm_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/backend/services/events/events-common/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/funktionaer_feature_js_1_0_0.xml b/.idea/artifacts/funktionaer_feature_js_1_0_0.xml
new file mode 100644
index 00000000..0e2dbcee
--- /dev/null
+++ b/.idea/artifacts/funktionaer_feature_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/funktionaer-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/funktionaer_feature_jvm_1_0_0.xml b/.idea/artifacts/funktionaer_feature_jvm_1_0_0.xml
new file mode 100644
index 00000000..0c14c527
--- /dev/null
+++ b/.idea/artifacts/funktionaer_feature_jvm_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/funktionaer-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/funktionaer_feature_wasm_js_1_0_0.xml b/.idea/artifacts/funktionaer_feature_wasm_js_1_0_0.xml
new file mode 100644
index 00000000..c27e8a44
--- /dev/null
+++ b/.idea/artifacts/funktionaer_feature_wasm_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/funktionaer-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/local_db_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/local_db_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..835ddcce
--- /dev/null
+++ b/.idea/artifacts/local_db_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/core/local-db/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/local_db_jvm_1_0_0_SNAPSHOT.xml b/.idea/artifacts/local_db_jvm_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..1f914f38
--- /dev/null
+++ b/.idea/artifacts/local_db_jvm_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/core/local-db/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/local_db_wasm_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/local_db_wasm_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..ed9ad4eb
--- /dev/null
+++ b/.idea/artifacts/local_db_wasm_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/core/local-db/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/masterdata_domain_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/masterdata_domain_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..11972bd4
--- /dev/null
+++ b/.idea/artifacts/masterdata_domain_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/backend/services/masterdata/masterdata-domain/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/masterdata_domain_jvm_1_0_0_SNAPSHOT.xml b/.idea/artifacts/masterdata_domain_jvm_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..5e795d38
--- /dev/null
+++ b/.idea/artifacts/masterdata_domain_jvm_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/backend/services/masterdata/masterdata-domain/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/masterdata_domain_wasm_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/masterdata_domain_wasm_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..063e3af7
--- /dev/null
+++ b/.idea/artifacts/masterdata_domain_wasm_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/backend/services/masterdata/masterdata-domain/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/meldestelle_desktop_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/meldestelle_desktop_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..35e12f37
--- /dev/null
+++ b/.idea/artifacts/meldestelle_desktop_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/shells/meldestelle-desktop/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/meldestelle_desktop_jvm_1_0_0_SNAPSHOT.xml b/.idea/artifacts/meldestelle_desktop_jvm_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..8b1a1598
--- /dev/null
+++ b/.idea/artifacts/meldestelle_desktop_jvm_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/shells/meldestelle-desktop/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/meldestelle_desktop_wasm_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/meldestelle_desktop_wasm_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..357bb572
--- /dev/null
+++ b/.idea/artifacts/meldestelle_desktop_wasm_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/shells/meldestelle-desktop/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/meldestelle_web_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/meldestelle_web_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..c8ce3119
--- /dev/null
+++ b/.idea/artifacts/meldestelle_web_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/shells/meldestelle-web/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/meldestelle_web_jvm_1_0_0_SNAPSHOT.xml b/.idea/artifacts/meldestelle_web_jvm_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..faaf1d58
--- /dev/null
+++ b/.idea/artifacts/meldestelle_web_jvm_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/shells/meldestelle-web/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/meldestelle_web_wasm_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/meldestelle_web_wasm_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..c83c1fcf
--- /dev/null
+++ b/.idea/artifacts/meldestelle_web_wasm_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/shells/meldestelle-web/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/navigation_js_1_0_0.xml b/.idea/artifacts/navigation_js_1_0_0.xml
new file mode 100644
index 00000000..ec74654c
--- /dev/null
+++ b/.idea/artifacts/navigation_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/core/navigation/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/navigation_jvm_1_0_0.xml b/.idea/artifacts/navigation_jvm_1_0_0.xml
new file mode 100644
index 00000000..8b3e48e4
--- /dev/null
+++ b/.idea/artifacts/navigation_jvm_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/core/navigation/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/navigation_wasm_js_1_0_0.xml b/.idea/artifacts/navigation_wasm_js_1_0_0.xml
new file mode 100644
index 00000000..404ced9d
--- /dev/null
+++ b/.idea/artifacts/navigation_wasm_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/core/navigation/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/nennung_feature_js_1_0_0.xml b/.idea/artifacts/nennung_feature_js_1_0_0.xml
new file mode 100644
index 00000000..b7dcd471
--- /dev/null
+++ b/.idea/artifacts/nennung_feature_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/nennung-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/nennung_feature_jvm_1_0_0.xml b/.idea/artifacts/nennung_feature_jvm_1_0_0.xml
new file mode 100644
index 00000000..d279f926
--- /dev/null
+++ b/.idea/artifacts/nennung_feature_jvm_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/nennung-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/nennung_feature_wasm_js_1_0_0.xml b/.idea/artifacts/nennung_feature_wasm_js_1_0_0.xml
new file mode 100644
index 00000000..8d91ec11
--- /dev/null
+++ b/.idea/artifacts/nennung_feature_wasm_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/nennung-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/network_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/network_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..7a2ec1b1
--- /dev/null
+++ b/.idea/artifacts/network_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/core/network/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/network_jvm_1_0_0_SNAPSHOT.xml b/.idea/artifacts/network_jvm_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..ab613d35
--- /dev/null
+++ b/.idea/artifacts/network_jvm_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/core/network/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/network_wasm_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/network_wasm_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..e3c208b0
--- /dev/null
+++ b/.idea/artifacts/network_wasm_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/core/network/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/pferde_feature_js_1_0_0.xml b/.idea/artifacts/pferde_feature_js_1_0_0.xml
new file mode 100644
index 00000000..cc8461c4
--- /dev/null
+++ b/.idea/artifacts/pferde_feature_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/pferde-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/pferde_feature_jvm_1_0_0.xml b/.idea/artifacts/pferde_feature_jvm_1_0_0.xml
new file mode 100644
index 00000000..90987cfc
--- /dev/null
+++ b/.idea/artifacts/pferde_feature_jvm_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/pferde-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/pferde_feature_wasm_js_1_0_0.xml b/.idea/artifacts/pferde_feature_wasm_js_1_0_0.xml
new file mode 100644
index 00000000..a2621cd0
--- /dev/null
+++ b/.idea/artifacts/pferde_feature_wasm_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/pferde-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/ping_api_js_1_0_0.xml b/.idea/artifacts/ping_api_js_1_0_0.xml
new file mode 100644
index 00000000..2cec193c
--- /dev/null
+++ b/.idea/artifacts/ping_api_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/contracts/ping-api/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/ping_api_jvm_1_0_0.xml b/.idea/artifacts/ping_api_jvm_1_0_0.xml
new file mode 100644
index 00000000..a6dfd1a8
--- /dev/null
+++ b/.idea/artifacts/ping_api_jvm_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/contracts/ping-api/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/ping_api_wasm_js_1_0_0.xml b/.idea/artifacts/ping_api_wasm_js_1_0_0.xml
new file mode 100644
index 00000000..d9cad493
--- /dev/null
+++ b/.idea/artifacts/ping_api_wasm_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/contracts/ping-api/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/ping_feature_js_1_0_0.xml b/.idea/artifacts/ping_feature_js_1_0_0.xml
new file mode 100644
index 00000000..d90590b3
--- /dev/null
+++ b/.idea/artifacts/ping_feature_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/ping-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/ping_feature_jvm_1_0_0.xml b/.idea/artifacts/ping_feature_jvm_1_0_0.xml
new file mode 100644
index 00000000..eb6e0336
--- /dev/null
+++ b/.idea/artifacts/ping_feature_jvm_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/ping-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/ping_feature_wasm_js_1_0_0.xml b/.idea/artifacts/ping_feature_wasm_js_1_0_0.xml
new file mode 100644
index 00000000..d84ad0d8
--- /dev/null
+++ b/.idea/artifacts/ping_feature_wasm_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/ping-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/profile_feature_js_1_0_0.xml b/.idea/artifacts/profile_feature_js_1_0_0.xml
new file mode 100644
index 00000000..8d17f2c0
--- /dev/null
+++ b/.idea/artifacts/profile_feature_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/profile-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/profile_feature_jvm_1_0_0.xml b/.idea/artifacts/profile_feature_jvm_1_0_0.xml
new file mode 100644
index 00000000..2757aad9
--- /dev/null
+++ b/.idea/artifacts/profile_feature_jvm_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/profile-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/profile_feature_wasm_js_1_0_0.xml b/.idea/artifacts/profile_feature_wasm_js_1_0_0.xml
new file mode 100644
index 00000000..ff24f673
--- /dev/null
+++ b/.idea/artifacts/profile_feature_wasm_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/profile-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/reiter_feature_js_1_0_0.xml b/.idea/artifacts/reiter_feature_js_1_0_0.xml
new file mode 100644
index 00000000..b0a28788
--- /dev/null
+++ b/.idea/artifacts/reiter_feature_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/reiter-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/reiter_feature_jvm_1_0_0.xml b/.idea/artifacts/reiter_feature_jvm_1_0_0.xml
new file mode 100644
index 00000000..3c1a7edd
--- /dev/null
+++ b/.idea/artifacts/reiter_feature_jvm_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/reiter-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/reiter_feature_wasm_js_1_0_0.xml b/.idea/artifacts/reiter_feature_wasm_js_1_0_0.xml
new file mode 100644
index 00000000..dda6b0dd
--- /dev/null
+++ b/.idea/artifacts/reiter_feature_wasm_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/reiter-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/sync_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/sync_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..b45a72b0
--- /dev/null
+++ b/.idea/artifacts/sync_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/core/sync/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/sync_jvm_1_0_0_SNAPSHOT.xml b/.idea/artifacts/sync_jvm_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..a501decf
--- /dev/null
+++ b/.idea/artifacts/sync_jvm_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/core/sync/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/sync_wasm_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/sync_wasm_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..4f214cad
--- /dev/null
+++ b/.idea/artifacts/sync_wasm_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/core/sync/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/turnier_feature_js_1_0_0.xml b/.idea/artifacts/turnier_feature_js_1_0_0.xml
new file mode 100644
index 00000000..3cb0bfbf
--- /dev/null
+++ b/.idea/artifacts/turnier_feature_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/turnier-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/turnier_feature_jvm_1_0_0.xml b/.idea/artifacts/turnier_feature_jvm_1_0_0.xml
new file mode 100644
index 00000000..e60df57f
--- /dev/null
+++ b/.idea/artifacts/turnier_feature_jvm_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/turnier-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/turnier_feature_wasm_js_1_0_0.xml b/.idea/artifacts/turnier_feature_wasm_js_1_0_0.xml
new file mode 100644
index 00000000..7ed8d596
--- /dev/null
+++ b/.idea/artifacts/turnier_feature_wasm_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/turnier-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/veranstalter_feature_js_1_0_0.xml b/.idea/artifacts/veranstalter_feature_js_1_0_0.xml
new file mode 100644
index 00000000..88405354
--- /dev/null
+++ b/.idea/artifacts/veranstalter_feature_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/veranstalter-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/veranstalter_feature_jvm_1_0_0.xml b/.idea/artifacts/veranstalter_feature_jvm_1_0_0.xml
new file mode 100644
index 00000000..70f969ad
--- /dev/null
+++ b/.idea/artifacts/veranstalter_feature_jvm_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/veranstalter-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/veranstalter_feature_wasm_js_1_0_0.xml b/.idea/artifacts/veranstalter_feature_wasm_js_1_0_0.xml
new file mode 100644
index 00000000..2332d2fa
--- /dev/null
+++ b/.idea/artifacts/veranstalter_feature_wasm_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/veranstalter-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/veranstaltung_feature_js_1_0_0.xml b/.idea/artifacts/veranstaltung_feature_js_1_0_0.xml
new file mode 100644
index 00000000..9f314fbb
--- /dev/null
+++ b/.idea/artifacts/veranstaltung_feature_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/veranstaltung-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/veranstaltung_feature_jvm_1_0_0.xml b/.idea/artifacts/veranstaltung_feature_jvm_1_0_0.xml
new file mode 100644
index 00000000..1364dff8
--- /dev/null
+++ b/.idea/artifacts/veranstaltung_feature_jvm_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/veranstaltung-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/veranstaltung_feature_wasm_js_1_0_0.xml b/.idea/artifacts/veranstaltung_feature_wasm_js_1_0_0.xml
new file mode 100644
index 00000000..70784b5d
--- /dev/null
+++ b/.idea/artifacts/veranstaltung_feature_wasm_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/veranstaltung-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/verein_feature_js_1_0_0.xml b/.idea/artifacts/verein_feature_js_1_0_0.xml
new file mode 100644
index 00000000..2cdd53ec
--- /dev/null
+++ b/.idea/artifacts/verein_feature_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/verein-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/verein_feature_jvm_1_0_0.xml b/.idea/artifacts/verein_feature_jvm_1_0_0.xml
new file mode 100644
index 00000000..644e9080
--- /dev/null
+++ b/.idea/artifacts/verein_feature_jvm_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/verein-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/verein_feature_wasm_js_1_0_0.xml b/.idea/artifacts/verein_feature_wasm_js_1_0_0.xml
new file mode 100644
index 00000000..5d444454
--- /dev/null
+++ b/.idea/artifacts/verein_feature_wasm_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/verein-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/zns_import_feature_js_1_0_0.xml b/.idea/artifacts/zns_import_feature_js_1_0_0.xml
new file mode 100644
index 00000000..ea8d9d8f
--- /dev/null
+++ b/.idea/artifacts/zns_import_feature_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/zns-import-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/zns_import_feature_jvm_1_0_0.xml b/.idea/artifacts/zns_import_feature_jvm_1_0_0.xml
new file mode 100644
index 00000000..dc988405
--- /dev/null
+++ b/.idea/artifacts/zns_import_feature_jvm_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/zns-import-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/zns_import_feature_wasm_js_1_0_0.xml b/.idea/artifacts/zns_import_feature_wasm_js_1_0_0.xml
new file mode 100644
index 00000000..27311542
--- /dev/null
+++ b/.idea/artifacts/zns_import_feature_wasm_js_1_0_0.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/frontend/features/zns-import-feature/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/zns_parser_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/zns_parser_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..94575072
--- /dev/null
+++ b/.idea/artifacts/zns_parser_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/core/zns-parser/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/zns_parser_jvm_1_0_0_SNAPSHOT.xml b/.idea/artifacts/zns_parser_jvm_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..42a9329f
--- /dev/null
+++ b/.idea/artifacts/zns_parser_jvm_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/core/zns-parser/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/zns_parser_wasm_js_1_0_0_SNAPSHOT.xml b/.idea/artifacts/zns_parser_wasm_js_1_0_0_SNAPSHOT.xml
new file mode 100644
index 00000000..67b2ea49
--- /dev/null
+++ b/.idea/artifacts/zns_parser_wasm_js_1_0_0_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/core/zns-parser/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 00000000..efb4a3a6
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
new file mode 100644
index 00000000..7b1f8144
--- /dev/null
+++ b/.idea/dataSources.xml
@@ -0,0 +1,56 @@
+
+
+
+
+ postgresql
+ true
+ org.postgresql.Driver
+ jdbc:postgresql://localhost:5432/pg-meldestelle-db
+
+
+
+
+
+ $ProjectFileDir$
+
+
+ h2.unified
+ true
+ true
+ org.h2.Driver
+ jdbc:h2:mem:billing_test;DB_CLOSE_DELAY=-1
+
+
+
+
+
+ $ProjectFileDir$
+
+
+ postgresql
+ true
+ true
+ org.postgresql.Driver
+ jdbc:postgresql://localhost:5432/postgres
+
+
+
+
+
+ $ProjectFileDir$
+
+
+ h2.unified
+ true
+ true
+ org.h2.Driver
+ jdbc:h2:mem:maildb;DB_CLOSE_DELAY=-1
+
+
+
+
+
+ $ProjectFileDir$
+
+
+
\ No newline at end of file
diff --git a/.idea/db-forest-config.xml b/.idea/db-forest-config.xml
new file mode 100644
index 00000000..00579c2a
--- /dev/null
+++ b/.idea/db-forest-config.xml
@@ -0,0 +1,11 @@
+
+
+
+ .
+ ----------------------------------------
+ 1:0:5c2e7682-32d6-4ae1-b108-e1da8b228f13
+ 2:0:55030c5a-f28c-4e83-b731-f02a0b02abcf
+ 3:0:f5e93de4-49c9-483b-a9ba-7499690e28e4
+ .
+
+
\ No newline at end of file
diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml
new file mode 100644
index 00000000..0349cbac
--- /dev/null
+++ b/.idea/dictionaries/project.xml
@@ -0,0 +1,49 @@
+
+
+
+ Abschluesse
+ Abteilungs
+ Bewerbs
+ Bewerbsuebersicht
+ Bewerbsübersicht
+ Caprilli
+ Eintraege
+ Funktionaere
+ Konfig
+ Lebensnr
+ Nachnenngebühr
+ Nennungs
+ Oeto
+ Ruecknavigation
+ Saveable
+ Springpferdepruefung
+ Standardspringpruefung
+ Startwuensche
+ Ueberschreitung
+ Veranstalterwahl
+ Vollstaendigkeit
+ Vorschlaege
+ Zusatzgebuehr
+ beruecksichtigen
+ bewerb
+ cdnp
+ eintraege
+ fuer
+ funktionaer
+ hoehe
+ hoeher
+ konfig
+ nachnenngebuehr
+ nachnennung
+ oeps
+ pruefungs
+ richt
+ startnr
+ strasse
+ teilungs
+ valkey
+ waehlt
+ zusaetzlich
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 00000000..899851d8
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 00000000..ebd2e7b2
--- /dev/null
+++ b/.idea/kotlinc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 00000000..d1b260c8
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml
new file mode 100644
index 00000000..1dfd769f
--- /dev/null
+++ b/.idea/sqldialects.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000..35eb1ddf
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/services/billing/billing-domain/build.gradle.kts b/backend/services/billing/billing-domain/build.gradle.kts
index ece17fde..a6134de8 100644
--- a/backend/services/billing/billing-domain/build.gradle.kts
+++ b/backend/services/billing/billing-domain/build.gradle.kts
@@ -9,9 +9,6 @@ plugins {
kotlin {
jvm()
- js(IR) {
- browser()
- }
wasmJs {
browser()
}
diff --git a/backend/services/entries/entries-api/build.gradle.kts b/backend/services/entries/entries-api/build.gradle.kts
index 673045e6..3aa9c7b7 100644
--- a/backend/services/entries/entries-api/build.gradle.kts
+++ b/backend/services/entries/entries-api/build.gradle.kts
@@ -13,10 +13,6 @@ version = "1.0.0"
kotlin {
jvm()
- js(IR) {
- browser()
- }
-
wasmJs {
browser()
}
diff --git a/backend/services/entries/entries-domain/build.gradle.kts b/backend/services/entries/entries-domain/build.gradle.kts
index 66e6b8a6..bd647d35 100644
--- a/backend/services/entries/entries-domain/build.gradle.kts
+++ b/backend/services/entries/entries-domain/build.gradle.kts
@@ -10,10 +10,6 @@ plugins {
kotlin {
jvm()
- js(IR) {
- browser()
- }
-
wasmJs {
browser()
}
diff --git a/backend/services/events/events-common/build.gradle.kts b/backend/services/events/events-common/build.gradle.kts
index 74f94d0d..b3287fbf 100644
--- a/backend/services/events/events-common/build.gradle.kts
+++ b/backend/services/events/events-common/build.gradle.kts
@@ -10,10 +10,6 @@ plugins {
kotlin {
jvm()
- js(IR) {
- browser()
- }
-
wasmJs {
browser()
}
diff --git a/backend/services/masterdata/masterdata-domain/build.gradle.kts b/backend/services/masterdata/masterdata-domain/build.gradle.kts
index 74f94d0d..b3287fbf 100644
--- a/backend/services/masterdata/masterdata-domain/build.gradle.kts
+++ b/backend/services/masterdata/masterdata-domain/build.gradle.kts
@@ -10,10 +10,6 @@ plugins {
kotlin {
jvm()
- js(IR) {
- browser()
- }
-
wasmJs {
browser()
}
diff --git a/contracts/ping-api/build.gradle.kts b/contracts/ping-api/build.gradle.kts
index 6e00d366..902aa6f4 100644
--- a/contracts/ping-api/build.gradle.kts
+++ b/contracts/ping-api/build.gradle.kts
@@ -13,15 +13,6 @@ version = "1.0.0"
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
diff --git a/core/core-domain/build.gradle.kts b/core/core-domain/build.gradle.kts
index 752d3459..7e3268cc 100644
--- a/core/core-domain/build.gradle.kts
+++ b/core/core-domain/build.gradle.kts
@@ -1,3 +1,5 @@
+import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
+
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.kotlinSerialization)
@@ -10,17 +12,7 @@ kotlin {
}
}
- js(IR) {
- binaries.library()
- // Re-enabled browser environment after Root NodeJs fix
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
- @OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
+ @OptIn(ExperimentalWasmDsl::class)
wasmJs {
binaries.library()
browser()
@@ -35,17 +27,10 @@ kotlin {
commonMain.dependencies {
api(libs.kotlinx.serialization.json)
api(libs.kotlinx.datetime)
- }
-
- commonTest.dependencies {
- implementation(libs.kotlin.test)
- }
-
- jsMain.dependencies {
api(libs.kotlinx.coroutines.core)
}
- jsTest.dependencies {
+ commonTest.dependencies {
implementation(libs.kotlin.test)
}
diff --git a/core/core-utils/build.gradle.kts b/core/core-utils/build.gradle.kts
index 087bbcf7..b3b68c95 100644
--- a/core/core-utils/build.gradle.kts
+++ b/core/core-utils/build.gradle.kts
@@ -1,3 +1,5 @@
+import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
+
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.kotlinSerialization)
@@ -5,15 +7,7 @@ plugins {
kotlin {
jvm()
- js {
- browser {
- testTask {
- enabled = false
- }
- }
- }
- // Wasm support enabled?
- @OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
+ @OptIn(ExperimentalWasmDsl::class)
wasmJs {
browser()
}
diff --git a/core/zns-parser/build.gradle.kts b/core/zns-parser/build.gradle.kts
index be005811..b82f89f8 100644
--- a/core/zns-parser/build.gradle.kts
+++ b/core/zns-parser/build.gradle.kts
@@ -10,15 +10,6 @@ plugins {
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
diff --git a/docs/99_Journal/2026-04-18_Correction-KMP-Targets.md b/docs/99_Journal/2026-04-18_Correction-KMP-Targets.md
new file mode 100644
index 00000000..9db42704
--- /dev/null
+++ b/docs/99_Journal/2026-04-18_Correction-KMP-Targets.md
@@ -0,0 +1,25 @@
+# Journal: Korrektur der Multiplatform-Targets (18.04.2026)
+
+## 🎯 Ziel
+
+Sofortige Korrektur der fälschlicherweise eingeführten `jsMain`-Struktur im Modul `device-initialization`.
+
+## 🛠️ Durchgeführte Maßnahmen
+
+1. **Gradle-Bereinigung:** Entfernung der `js(IR)`-Konfiguration aus
+ `frontend/features/device-initialization/build.gradle.kts`.
+2. **Dateisystem-Bereinigung:** Vollständiges Löschen des Verzeichnisses `src/jsMain/` im Modul `device-initialization`.
+3. **Fokus-Wahrung:** Sicherstellung, dass nur `jvmMain` (Desktop) und `wasmJsMain` (Web) als valide Targets im
+ Frontend-Feature existieren.
+
+## 🧐 Analyse
+
+Der Fehler entstand durch eine automatisierte Generierung während des Refactorings auf die `expect/actual`-Struktur.
+Dies widersprach der strategischen Ausrichtung (Desktop: JVM, Web: WASM).
+
+## ✅ Ergebnis
+
+Das Modul ist nun wieder konform zur Architekturvorgabe. Keine unerwünschten JS-Altlasten mehr vorhanden.
+
+---
+🏗️ **[Lead Architect]** & 🧹 **[Curator]**
diff --git a/docs/99_Journal/2026-04-18_DeviceInitialization-PlugAndPlay.md b/docs/99_Journal/2026-04-18_DeviceInitialization-PlugAndPlay.md
new file mode 100644
index 00000000..dde33a7e
--- /dev/null
+++ b/docs/99_Journal/2026-04-18_DeviceInitialization-PlugAndPlay.md
@@ -0,0 +1,56 @@
+# Journal: Feature-Modul `device-initialization` (Plug-and-Play)
+
+**Datum:** 18. April 2026
+**Agent:** 🏗️ [Lead Architect] / 🎨 [Frontend Expert]
+
+## 🎯 Zielsetzung
+
+Umstellung des Onboarding-Prozesses auf die neue **Plug-and-Play Struktur (ADR-0024)**. Ziel ist die Kapselung der
+Geräte-Initialisierung in ein autarkes Feature-Modul mit sauberem State-Management.
+
+## 🛠️ Durchgeführte Änderungen
+
+### 1. Neues Feature-Modul: `device-initialization`
+
+* Erstellung von `frontend/features/device-initialization`.
+* Implementierung der Multiplatform-Struktur (Common + JVM).
+* **Domain:** `DeviceInitializationSettings`, `NetworkRole`, `DeviceInitializationValidator`.
+* **Presentation:** `DeviceInitializationViewModel` (StateFlow-basiert), `DeviceInitializationUiState`.
+* **DI:** `DeviceInitializationModule.kt` für Koin.
+
+### 2. UI-Refactoring
+
+* Extraktion der UI aus `OnboardingScreen` in zustandslose Composables.
+* `DeviceInitializationScreen` im `commonMain` für den Rahmen.
+* `DeviceInitializationConfig.jvm.kt` im `jvmMain` für plattformspezifische UI (z.B. `JFileChooser`).
+* Umstellung der Nomenklatur von "Onboarding" auf **"Geräte-Initialisierung"** (DeviceInitialization).
+
+### 3. Integration in Desktop-Shell
+
+* Registrierung des Moduls in `settings.gradle.kts`.
+* Abhängigkeit in `meldestelle-desktop/build.gradle.kts` hinzugefügt.
+* Koin-Modul in `main.kt` registriert.
+* `DesktopApp.kt` und `DesktopMainLayout.kt` auf das neue ViewModel und den neuen Screen umgestellt.
+* Einführung des `DeviceInitializationSettingsManager`.
+
+## ⚠️ Bekannte Themen (Cleanup)
+
+* Die alten Klassen `OnboardingScreen`, `OnboardingSettings`, `OnboardingValidator` und `SettingsManager` in
+ `at.mocode.desktop.screens.onboarding` sind noch vorhanden und müssen in einer folgenden Session gelöscht werden,
+ sobald die Referenzen stabil sind.
+* IDE-Referenzfehler bei neuen KMP-Modulen erfordern oft einen Gradle-Sync/Rebuild.
+
+## 🏁 Fazit
+
+Der erste Schritt des "Schritt-für-Schritt"-Umbaus ist abgeschlossen. Die Startseite der App folgt nun dem Plug-and-Play
+Prinzip und ist fachlich präzise benannt.
+
+## 🧹 Update (Nach-Korrektur)
+
+* **ViewModel-Fix:** `DeviceInitializationViewModel` erbt nun von `androidx.lifecycle.ViewModel`, was die Integration in
+ `koinViewModel` ermöglicht.
+* **DesktopMainLayout:** Syntaxfehler beim `koinViewModel`-Aufruf behoben und Typos (`geraetName` -> `deviceName`)
+ korrigiert.
+* **Multiplatform-Härtung:** `DeviceInitializationSettingsManager` und `DeviceInitializationConfig` auf `expect/actual`
+ umgestellt, um JVM-Lecks im Common-Code zu vermeiden und JS/WasmJS Kompatibilität (via Stubs) sicherzustellen.
+* **UI-Cleanup:** Code-Duplikate in der Desktop-Konfiguration durch `MsSettingsField` reduziert.
diff --git a/docs/99_Journal/2026-04-18_WASM-Transition-Welle-1-3_Abschluss.md b/docs/99_Journal/2026-04-18_WASM-Transition-Welle-1-3_Abschluss.md
new file mode 100644
index 00000000..6a27e904
--- /dev/null
+++ b/docs/99_Journal/2026-04-18_WASM-Transition-Welle-1-3_Abschluss.md
@@ -0,0 +1,54 @@
+# Journal: Umstellung auf JVM + wasmJs (Welle 1-3)
+
+**Datum:** 18. April 2026
+**Status:** Abgeschlossen
+**Agent:** 🏗️ [Lead Architect] & 🧹 [Curator]
+
+## 🎯 Zielsetzung
+
+Vollständige Eliminierung des redundanten `js(IR)` Targets aus allen Modulen des Projekts. Strategische Fokussierung auf
+**JVM (Desktop)** und **wasmJs (Web)** gemäß der "Meldestelle"-Architekturvorgaben.
+
+## 🛠️ Durchgeführte Maßnahmen
+
+### 1. Gradle-Bereinigung (Entfernung `js(IR)`)
+
+In den folgenden Modul-Kategorien wurde der `js(IR) { ... }` Block sowie JS-spezifische Dependencies aus den
+`build.gradle.kts` Dateien entfernt:
+
+- **Core-Module:** `auth`, `domain`, `local-db`, `network`, `core-domain`, `design-system`, `navigation`, `sync`,
+ `core-utils`.
+- **Feature-Module:** `billing`, `funktionaer`, `nennung`, `pferde`, `ping`, `profile`, `reiter`, `turnier`,
+ `veranstalter`, `veranstaltung`, `verein`, `zns-import`, `device-initialization`.
+- **Contracts & Backend-Common:** `ping-api`, `entries-api`, `entries-domain`, `masterdata-domain`, `billing-domain`,
+ `events-common`, `zns-parser`.
+- **Shells:** `meldestelle-web`, `meldestelle-desktop`.
+
+### 2. Quellcode-Migration & Cleanup
+
+- Alle `src/jsMain/` und `src/jsTest/` Verzeichnisse wurden in den oben genannten Modulen gelöscht.
+- Logik wurde (falls noch nicht geschehen) nach `wasmJsMain` migriert.
+- Veraltete JS-spezifische Ktor-Client Abhängigkeiten wurden entfernt.
+
+### 3. Stabilität & Bugfixes (KMP & Yarn)
+
+- **Network-Modul:** Die `NoOpDiscoveryService`-Implementierung in `wasmJsMain` wurde aktualisiert, um dem
+ `NetworkDiscoveryService`-Interface (`commonMain`) zu entsprechen.
+- **Yarn Lock:** Korrektur des `yarn.lock` für WASM via `kotlinWasmUpgradeYarnLock` nach Wegfall der JS-Targets.
+- **Dependency Resolution:** Beseitigung aller KMP-Fehlermeldungen ("Unresolved platforms: [js]") durch vollständige
+ Bereinigung der Target-Ketten.
+
+## ✅ Verifizierung
+
+- `./gradlew clean build`: **ERFOLGREICH** (keine KMP-Auflösungsfehler mehr)
+- `./gradlew :frontend:shells:meldestelle-desktop:jvmJar`: **ERFOLGREICH**
+- `./gradlew :frontend:shells:meldestelle-web:wasmJsBrowserDistribution -PenableWasm=true`: **ERFOLGREICH**
+
+## 📝 Fazit
+
+Die technische Schuld der redundanten JS-Targets wurde getilgt. Das Projekt verfügt nun über eine saubere, zweigleisige
+Architektur (JVM & WASM), was die Build-Stabilität erhöht und die Komplexität der Plattform-Implementierungen (
+`expect/actual`) reduziert.
+
+---
+*Dokumentiert durch den Curator am 18.04.2026.*
diff --git a/docs/99_Journal/2026-04-18_WASM-Transition-Welle-1.md b/docs/99_Journal/2026-04-18_WASM-Transition-Welle-1.md
new file mode 100644
index 00000000..b2bfc0e6
--- /dev/null
+++ b/docs/99_Journal/2026-04-18_WASM-Transition-Welle-1.md
@@ -0,0 +1,51 @@
+# Journal: Welle 1 - WASM-Only Transition
+
+**Datum:** 18. April 2026
+**Status:** Welle 1 abgeschlossen
+**Agent:** 🏗️ [Lead Architect] & 🧹 [Curator]
+
+## 🎯 Ziel
+
+Vollständige Entfernung des `js(IR)`-Targets aus den Core-Modulen und Umstellung auf ein reines **JVM (Desktop)** + *
+*wasmJs (Web)** Modell.
+
+## 🛠️ Durchgeführte Änderungen
+
+### 1. Gradle-Bereinigung (Entfernung `js(IR)`)
+
+In folgenden Modulen wurde der `js(IR) { ... }` Block und die entsprechenden JS-Dependencies aus den `sourceSets`
+entfernt:
+
+- `core/core-domain`
+- `frontend/core/auth`
+- `frontend/core/domain`
+- `frontend/core/local-db`
+- `frontend/core/network`
+- `frontend/core/design-system`
+- `frontend/core/navigation`
+- `frontend/shells/meldestelle-web`
+
+### 2. Quellcode-Migration & Bereinigung
+
+- **Löschung:** Alle `src/jsMain/` und `src/jsTest/` Verzeichnisse in den oben genannten Modulen wurden gelöscht.
+- **Migration:**
+ - Logik aus `OidcCallback.js.kt` wurde bereits zuvor weitgehend in `OidcCallback.wasmJs.kt` übernommen.
+ - In `local-db` wurde die `DatabaseDriverFactory.wasmJs.kt` auf einen stabilen Rumpf-Stand gebracht (
+ WebWorkerDriver-Migration ist aufgrund fehlender DOM-Libraries für WASM aktuell noch blockiert).
+- **Konsolidierung:** Dependencies wie `kotlinx-coroutines-core`, die zuvor in `jsMain` lagen, wurden nach `commonMain`
+ oder `wasmJsMain` verschoben.
+
+## 🛡️ Verifizierung
+
+- `./gradlew clean`: **Erfolgreich**
+- `./gradlew :frontend:shells:meldestelle-desktop:jvmJar`: **Erfolgreich** (Desktop-Shell baut stabil).
+- Build-Check für WASM (`meldestelle-web`) zeigt noch Fehler in den Feature-Modulen (Welle 2), was erwartungskonform
+ ist, da diese noch `js(IR)` referenzieren.
+
+## 🚀 Nächste Schritte
+
+- **Welle 2:** Systematische Bereinigung aller `frontend/features/*` Module.
+- **Welle 3:** Finalisierung der Web-Shell und vollständige Entfernung aller JS-Leichen im Projekt.
+
+---
+*Dokumentiert durch den Curator.*
diff --git a/frontend/core/auth/build.gradle.kts b/frontend/core/auth/build.gradle.kts
index 5b65ed67..e12952b1 100644
--- a/frontend/core/auth/build.gradle.kts
+++ b/frontend/core/auth/build.gradle.kts
@@ -15,15 +15,6 @@ version = "1.0.0"
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
@@ -81,9 +72,5 @@ kotlin {
implementation(libs.kotlin.stdlib.wasm.js)
implementation(libs.ktor.client.js)
}
-
- jsMain.dependencies {
- implementation(libs.ktor.client.js)
- }
}
}
diff --git a/frontend/core/auth/src/jsMain/kotlin/at/mocode/frontend/core/auth/data/OidcCallback.js.kt b/frontend/core/auth/src/jsMain/kotlin/at/mocode/frontend/core/auth/data/OidcCallback.js.kt
deleted file mode 100644
index fc209759..00000000
--- a/frontend/core/auth/src/jsMain/kotlin/at/mocode/frontend/core/auth/data/OidcCallback.js.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-package at.mocode.frontend.core.auth.data
-
-import kotlinx.browser.window
-
-/**
- * JS-Implementierung: leitet die gesamte Seite zu Keycloak weiter.
- * Die Funktion kehrt nicht zurück —
- * der Callback wird beim nächsten App-Start via [consumePendingOidcCallback] verarbeitet.
- */
-actual suspend fun launchOidcFlow(
- authUrl: String,
- callbackPort: Int // wird im Browser ignoriert (kein lokaler Server)
-): OidcCallbackResult {
- window.location.href = authUrl
- // Diese Zeile wird nie erreicht — der Browser leitet weiter
- return OidcCallbackResult.Redirecting
-}
-
-/**
- * JS-Implementierung: prüft beim App-Start, ob die aktuelle URL einen
- * OIDC-Callback enthält (code= und state= Parameter von Keycloak).
- * Bereinigt nach dem Auslesen die URL via replaceState.
- */
-actual fun consumePendingOidcCallback(): OidcCallbackResult? {
- val search = window.location.search
- if (!search.contains("code=")) return null
-
- val params = parseJsQueryParams(search.removePrefix("?"))
- val code = params["code"] ?: return null
- val state = params["state"] ?: return null
- val error = params["error"]
-
- // URL bereinigen — Code soll nicht im Browser-Verlauf bleiben
- try {
- window.history.replaceState(null, "", window.location.pathname)
- } catch (_: Throwable) {
- // ignore — kein kritischer Fehler
- }
-
- return if (error != null) {
- OidcCallbackResult.Error(
- error = error,
- description = params["error_description"]
- )
- } else {
- OidcCallbackResult.Success(code = code, state = state)
- }
-}
-
-private fun parseJsQueryParams(query: String): Map =
- query.split("&")
- .filter { it.contains("=") }
- .associate {
- val parts = it.split("=", limit = 2)
- parts[0] to decodeURIComponent(parts.getOrElse(1) { "" })
- }
-
-actual fun getOidcRedirectUri(): String {
- val origin = try {
- window.location.origin
- } catch (_: Throwable) {
- "http://localhost"
- }
- return origin + at.mocode.frontend.core.domain.AppConstants.OIDC_REDIRECT_URI_JS_PATH
-}
-
-private fun decodeURIComponent(encoded: String): String =
- js("decodeURIComponent(encoded)").unsafeCast()
diff --git a/frontend/core/design-system/build.gradle.kts b/frontend/core/design-system/build.gradle.kts
index 7f4b97ea..7dce0842 100644
--- a/frontend/core/design-system/build.gradle.kts
+++ b/frontend/core/design-system/build.gradle.kts
@@ -12,15 +12,6 @@ plugins {
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
diff --git a/frontend/core/domain/build.gradle.kts b/frontend/core/domain/build.gradle.kts
index 4d5c64b4..ee224996 100644
--- a/frontend/core/domain/build.gradle.kts
+++ b/frontend/core/domain/build.gradle.kts
@@ -10,15 +10,6 @@ plugins {
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
diff --git a/frontend/core/domain/src/jsMain/kotlin/at/mocode/frontend/core/domain/PlatformType.js.kt b/frontend/core/domain/src/jsMain/kotlin/at/mocode/frontend/core/domain/PlatformType.js.kt
deleted file mode 100644
index 6f4be043..00000000
--- a/frontend/core/domain/src/jsMain/kotlin/at/mocode/frontend/core/domain/PlatformType.js.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package at.mocode.frontend.core.domain
-
-actual fun currentPlatform(): PlatformType = PlatformType.WEB
diff --git a/frontend/core/local-db/build.gradle.kts b/frontend/core/local-db/build.gradle.kts
index 67872431..7c455898 100644
--- a/frontend/core/local-db/build.gradle.kts
+++ b/frontend/core/local-db/build.gradle.kts
@@ -11,15 +11,6 @@ plugins {
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
@@ -41,19 +32,10 @@ kotlin {
implementation(libs.sqldelight.driver.sqlite)
}
- jsMain.dependencies {
- implementation(libs.sqldelight.driver.web)
- implementation(npm("@sqlite.org/sqlite-wasm", libs.versions.sqliteWasm.get()))
- }
-
jvmTest.dependencies {
implementation(libs.kotlin.test)
}
- jsTest.dependencies {
- implementation(libs.kotlin.test)
- }
-
wasmJsMain.dependencies {
implementation(libs.kotlin.stdlib.wasm.js)
implementation(libs.sqldelight.driver.web)
diff --git a/frontend/core/local-db/src/jsMain/kotlin/at/mocode/frontend/core/localdb/DatabaseDriverFactory.js.kt b/frontend/core/local-db/src/jsMain/kotlin/at/mocode/frontend/core/localdb/DatabaseDriverFactory.js.kt
deleted file mode 100644
index 1f77cc39..00000000
--- a/frontend/core/local-db/src/jsMain/kotlin/at/mocode/frontend/core/localdb/DatabaseDriverFactory.js.kt
+++ /dev/null
@@ -1,87 +0,0 @@
-package at.mocode.frontend.core.localdb
-
-import app.cash.sqldelight.db.SqlCursor
-import app.cash.sqldelight.db.SqlDriver
-import app.cash.sqldelight.driver.worker.WebWorkerDriver
-import org.w3c.dom.Worker
-
-@Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
-actual class DatabaseDriverFactory {
- actual suspend fun createDriver(): SqlDriver {
- // Wir nutzen eine Helper-Funktion, um den Worker zu erstellen.
- val worker = createWorker()
- val driver = WebWorkerDriver(worker)
-
- try {
- val version = getVersion(driver)
- val schemaVersion = AppDatabase.Schema.version
-
- console.log("Database version check: Current=$version, Schema=$schemaVersion")
-
- if (version == 0L) {
- console.log("Creating Database Schema...")
- try {
- AppDatabase.Schema.create(driver).await()
- setVersion(driver, schemaVersion)
- console.log("Database Schema created and version set to $schemaVersion")
- } catch (e: Throwable) {
- // If tables already exist but a version was 0 (e.g., previous broken run), we might get here.
- val msg = e.message ?: ""
- if (msg.contains("already exists", ignoreCase = true)) {
- console.warn("Tables already exist but version was 0. Assuming DB is initialized. Setting version to $schemaVersion.")
- setVersion(driver, schemaVersion)
- } else {
- throw e
- }
- }
- } else if (version < schemaVersion) {
- console.log("Migrating Database Schema from $version to $schemaVersion...")
- AppDatabase.Schema.migrate(driver, version, schemaVersion).await()
- setVersion(driver, schemaVersion)
- console.log("Database Schema migrated")
- } else {
- console.log("Database Schema is up to date.")
- }
- } catch (e: Throwable) {
- console.error("Error initializing database schema:", e)
- throw e
- }
-
- return driver
- }
-
- private suspend fun getVersion(driver: SqlDriver): Long {
- // Workaround for QueryResult issues:
- // We capture the cursor in a local variable and return the Boolean result from next().
- // Then we read from the captured cursor.
-
- var cursorRef: SqlCursor? = null
-
- // executeQuery returns QueryResult because mapper returns QueryResult
- val hasNext = driver.executeQuery(
- identifier = null,
- sql = "PRAGMA user_version;",
- mapper = { cursor ->
- cursorRef = cursor
- cursor.next()
- },
- parameters = 0
- ).await()
-
- return if (hasNext) {
- cursorRef?.getLong(0) ?: 0L
- } else {
- 0L
- }
- }
-
- private suspend fun setVersion(driver: SqlDriver, version: Long) {
- driver.execute(null, "PRAGMA user_version = $version;", 0).await()
- }
-}
-
-// Helper function to create the worker
-private fun createWorker(): Worker {
- // Try the relative path again, as an absolute path might fail depending on base href
- return js("new Worker('sqlite.worker.js')")
-}
diff --git a/frontend/core/local-db/src/jsMain/resources/sqlite.worker.js b/frontend/core/local-db/src/jsMain/resources/sqlite.worker.js
deleted file mode 100644
index 0bf119a1..00000000
--- a/frontend/core/local-db/src/jsMain/resources/sqlite.worker.js
+++ /dev/null
@@ -1,146 +0,0 @@
-'use strict';
-
-// --- State ---
-let db = null;
-let isReady = false;
-let initError = null;
-const messageQueue = [];
-
-// --- Message Handler ---
-self.onmessage = (event) => {
- if (!isReady) {
- if (initError) {
- postMessage({id: event.data?.id, error: `Worker not initialized: ${initError}`});
- } else {
- messageQueue.push(event);
- }
- return;
- }
- processMessage(event);
-};
-
-self.onerror = (event) => {
- console.error('[sqlite.worker] Uncaught error:', event.message, `${event.filename}:${event.lineno}`);
-};
-
-// --- Message Processor ---
-function processMessage(event) {
- const data = event.data;
- if (!data) return;
-
- let result;
- try {
- result = executeAction(data);
- } catch (err) {
- console.error('[sqlite.worker] Error processing message:', err);
- postMessage({id: data.id, error: err?.message ?? String(err)});
- return;
- }
- postMessage({id: data.id, ...result});
-}
-
-function executeAction(data) {
- switch (data.action) {
- case 'exec': {
- if (!data.sql) {
- postMessage({id: data.id, error: 'exec: Missing sql string'});
- return null;
- }
- const rows = [];
- db.exec({
- sql: data.sql,
- bind: data.params ?? [],
- rowMode: 'array',
- callback: (row) => rows.push(row),
- });
- return {results: {values: rows}};
- }
- case 'begin_transaction':
- db.exec('BEGIN TRANSACTION;');
- return {results: []};
- case 'end_transaction':
- db.exec('END TRANSACTION;');
- return {results: []};
- case 'rollback_transaction':
- db.exec('ROLLBACK TRANSACTION;');
- return {results: []};
- default:
- postMessage({id: data.id, error: `Unsupported action: ${data.action}`});
- return null;
- }
-}
-
-// --- Initialization ---
-async function init() {
- // Load sqlite3.js via importScripts (avoids Webpack bundling issues)
- const loadResult = tryImportScripts();
- if (loadResult !== null) {
- handleInitError(loadResult);
- return;
- }
-
- if (typeof self.sqlite3InitModule !== 'function') {
- handleInitError('sqlite3InitModule not found after importScripts – sqlite3.js may be corrupt or wrong version.');
- return;
- }
-
- // Fetch WASM binary manually so we control the URL and can detect failures early
- let wasmBinary;
- try {
- const wasmResponse = await fetch('sqlite3.wasm');
- if (!wasmResponse.ok) {
- handleInitError(`Failed to fetch sqlite3.wasm: ${wasmResponse.status} ${wasmResponse.statusText}`);
- return;
- }
- wasmBinary = await wasmResponse.arrayBuffer();
- } catch (e) {
- handleInitError(`Failed to fetch sqlite3.wasm: ${e.message}`);
- return;
- }
-
- try {
- const sqlite3 = await self.sqlite3InitModule({
- print: console.log,
- printErr: console.error,
- wasmBinary,
- });
-
- const dbName = 'app.db';
- if ('opfs' in sqlite3) {
- console.log(`[sqlite.worker] Using persistent OPFS database: ${dbName}`);
- db = new sqlite3.oo1.OpfsDb(dbName);
- } else {
- console.warn('[sqlite.worker] OPFS not available – falling back to in-memory database');
- db = new sqlite3.oo1.DB(dbName);
- }
-
- // Flush buffered messages
- isReady = true;
- console.log(`[sqlite.worker] Ready. Flushing ${messageQueue.length} buffered message(s).`);
- while (messageQueue.length > 0) {
- processMessage(messageQueue.shift());
- }
- } catch (e) {
- handleInitError(e?.message ?? String(e));
- }
-}
-
-function tryImportScripts() {
- try {
- importScripts('sqlite3.js');
- return null;
- } catch (e) {
- return `importScripts('sqlite3.js') failed – is the file served at root? ${e.message}`;
- }
-}
-
-function handleInitError(message) {
- initError = message;
- console.error('[sqlite.worker] Initialization failed:', message);
- while (messageQueue.length > 0) {
- const queued = messageQueue.shift();
- postMessage({id: queued.data?.id, error: `Worker initialization failed: ${initError}`});
- }
-}
-
-init();
diff --git a/frontend/core/local-db/src/wasmJsMain/kotlin/at/mocode/frontend/core/localdb/DatabaseDriverFactory.wasmJs.kt b/frontend/core/local-db/src/wasmJsMain/kotlin/at/mocode/frontend/core/localdb/DatabaseDriverFactory.wasmJs.kt
index 7e70b3fe..f67f639f 100644
--- a/frontend/core/local-db/src/wasmJsMain/kotlin/at/mocode/frontend/core/localdb/DatabaseDriverFactory.wasmJs.kt
+++ b/frontend/core/local-db/src/wasmJsMain/kotlin/at/mocode/frontend/core/localdb/DatabaseDriverFactory.wasmJs.kt
@@ -6,15 +6,8 @@ import app.cash.sqldelight.db.SqlDriver
actual class DatabaseDriverFactory {
actual suspend fun createDriver(): SqlDriver {
// Provisorische Implementierung für den Build-Erfolg
- // In einer echten Umgebung müsste hier der WebWorkerDriver konfiguriert werden,
- // sobald die org.w3c.dom Abhängigkeiten korrekt aufgelöst werden können.
+ // WebWorkerDriver für WasmJs erfordert org.w3c.dom (DOM-Library),
+ // welche in diesem Projekt für WasmJs noch nicht vollständig konfiguriert ist.
throw UnsupportedOperationException("Database on Wasm is not yet fully implemented due to missing org.w3c.dom")
}
-
- private suspend fun getVersion(driver: SqlDriver): Long {
- return 0L
- }
-
- private suspend fun setVersion(driver: SqlDriver, version: Long) {
- }
}
diff --git a/frontend/core/navigation/build.gradle.kts b/frontend/core/navigation/build.gradle.kts
index 8cb9e2d4..b55b1ece 100644
--- a/frontend/core/navigation/build.gradle.kts
+++ b/frontend/core/navigation/build.gradle.kts
@@ -15,15 +15,6 @@ version = "1.0.0"
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
diff --git a/frontend/core/network/build.gradle.kts b/frontend/core/network/build.gradle.kts
index 27a4d23b..476522d3 100644
--- a/frontend/core/network/build.gradle.kts
+++ b/frontend/core/network/build.gradle.kts
@@ -10,15 +10,6 @@ plugins {
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
@@ -57,9 +48,5 @@ kotlin {
implementation(libs.ktor.client.js)
implementation(libs.kotlinx.coroutines.core)
}
-
- jsMain.dependencies {
- implementation(libs.ktor.client.js)
- }
}
}
diff --git a/frontend/core/network/src/jsMain/kotlin/at/mocode/frontend/core/network/PlatformConfig.js.kt b/frontend/core/network/src/jsMain/kotlin/at/mocode/frontend/core/network/PlatformConfig.js.kt
deleted file mode 100644
index 549fa104..00000000
--- a/frontend/core/network/src/jsMain/kotlin/at/mocode/frontend/core/network/PlatformConfig.js.kt
+++ /dev/null
@@ -1,78 +0,0 @@
-package at.mocode.frontend.core.network
-
-import kotlinx.browser.window
-
-@Suppress("UnsafeCastFromDynamic", "EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
-actual object PlatformConfig {
-
- private val globalScope: dynamic
- get() = js("typeof globalThis !== 'undefined' ? globalThis : (typeof window !== 'undefined' ? window : (typeof self !== 'undefined' ? self : {}))")
-
- actual fun resolveApiBaseUrl(): String {
- // 1) Prefer a global JS variable (injected by main.kt via AppConfig)
- val fromGlobal = try {
- (globalScope.API_BASE_URL as? String)?.trim().orEmpty()
- } catch (_: dynamic) {
- ""
- }
- if (fromGlobal.isNotEmpty()) {
- console.log("[PlatformConfig] Resolved API_BASE_URL from global: $fromGlobal")
- return fromGlobal.removeSuffix("/")
- }
- // 2) Try window location origin (same origin gateway/proxy setup)
- val origin = try {
- window.location.origin
- } catch (_: dynamic) {
- null
- }
- if (!origin.isNullOrBlank()) {
- val resolvedUrl = origin.removeSuffix("/") + "/api"
- console.log("[PlatformConfig] Resolved API_BASE_URL from window.location.origin: $resolvedUrl")
- return resolvedUrl
- }
- // 3) Fallback to the local gateway directly (e.g., for tests without a window)
- val fallbackUrl = "http://localhost:8081/api"
- console.log("[PlatformConfig] Fallback API_BASE_URL: $fallbackUrl")
- return fallbackUrl
- }
-
- actual fun resolveMailServiceUrl(): String {
- val fromGlobal = try {
- (globalScope.MAIL_SERVICE_URL as? String)?.trim().orEmpty()
- } catch (_: dynamic) {
- ""
- }
- if (fromGlobal.isNotEmpty()) {
- return fromGlobal.removeSuffix("/")
- }
- return "http://localhost:8085"
- }
-
- actual fun resolveKeycloakUrl(): String {
- // 1) Prefer a global JS variable (injected by main.kt via AppConfig)
- val fromGlobal = try {
- (globalScope.KEYCLOAK_URL as? String)?.trim().orEmpty()
- } catch (_: dynamic) {
- ""
- }
- if (fromGlobal.isNotEmpty()) {
- console.log("[PlatformConfig] Resolved KEYCLOAK_URL from global: $fromGlobal")
- return fromGlobal.removeSuffix("/")
- }
- // 2) Derive from window.location.hostname with default Keycloak port
- val hostname = try {
- window.location.hostname
- } catch (_: dynamic) {
- null
- }
- if (!hostname.isNullOrBlank()) {
- val resolvedUrl = "http://$hostname:8180"
- console.log("[PlatformConfig] Resolved KEYCLOAK_URL from window.location.hostname: $resolvedUrl")
- return resolvedUrl
- }
- // 3) Fallback for local development
- val fallbackUrl = "http://localhost:8180"
- console.log("[PlatformConfig] Fallback KEYCLOAK_URL: $fallbackUrl")
- return fallbackUrl
- }
-}
diff --git a/frontend/core/network/src/jsMain/kotlin/at/mocode/frontend/core/network/discovery/DiscoveryModule.kt b/frontend/core/network/src/jsMain/kotlin/at/mocode/frontend/core/network/discovery/DiscoveryModule.kt
deleted file mode 100644
index bdb61747..00000000
--- a/frontend/core/network/src/jsMain/kotlin/at/mocode/frontend/core/network/discovery/DiscoveryModule.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package at.mocode.frontend.core.network.discovery
-
-import org.koin.core.module.Module
-import org.koin.dsl.module
-
-/**
- * JS-spezifische Implementierung (vorerst No-op, da mDNS im Browser nicht nativ möglich).
- */
-actual val discoveryModule: Module = module {
- single { NoOpDiscoveryService() }
-}
-
-class NoOpDiscoveryService : NetworkDiscoveryService {
- override fun startDiscovery() {}
- override fun stopDiscovery() {}
- override fun registerService(port: Int) {}
- override fun getDiscoveredServices(): List = emptyList()
-}
diff --git a/frontend/core/network/src/jsMain/kotlin/at/mocode/frontend/core/network/sync/SyncModule.kt b/frontend/core/network/src/jsMain/kotlin/at/mocode/frontend/core/network/sync/SyncModule.kt
deleted file mode 100644
index d8f402c2..00000000
--- a/frontend/core/network/src/jsMain/kotlin/at/mocode/frontend/core/network/sync/SyncModule.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package at.mocode.frontend.core.network.sync
-
-import org.koin.core.module.Module
-import org.koin.dsl.module
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.emptyFlow
-
-/**
- * JS-spezifische Implementierung (vorerst No-op).
- */
-actual val syncModule: Module = module {
- single { NoOpP2pSyncService() }
- single { SyncManager(get(), get()) }
-}
-
-class NoOpP2pSyncService : P2pSyncService {
- override fun startServer(port: Int) {}
- override fun stopServer() {}
- override suspend fun connectToPeer(host: String, port: Int) {}
- override suspend fun broadcastEvent(event: SyncEvent) {}
- override val incomingEvents: Flow = emptyFlow()
- override val connectedPeers: Flow> = emptyFlow()
-}
diff --git a/frontend/core/network/src/wasmJsMain/kotlin/at/mocode/frontend/core/network/discovery/DiscoveryModule.kt b/frontend/core/network/src/wasmJsMain/kotlin/at/mocode/frontend/core/network/discovery/DiscoveryModule.kt
index 2be8bd12..58b54b84 100644
--- a/frontend/core/network/src/wasmJsMain/kotlin/at/mocode/frontend/core/network/discovery/DiscoveryModule.kt
+++ b/frontend/core/network/src/wasmJsMain/kotlin/at/mocode/frontend/core/network/discovery/DiscoveryModule.kt
@@ -1,5 +1,8 @@
package at.mocode.frontend.core.network.discovery
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
import org.koin.core.module.Module
import org.koin.dsl.module
@@ -11,6 +14,8 @@ actual val discoveryModule: Module = module {
}
class NoOpDiscoveryService : NetworkDiscoveryService {
+ override val discoveredServices: StateFlow> =
+ MutableStateFlow>(emptyList()).asStateFlow()
override fun startDiscovery() {}
override fun stopDiscovery() {}
override fun registerService(port: Int) {}
diff --git a/frontend/core/sync/build.gradle.kts b/frontend/core/sync/build.gradle.kts
index 8674229f..6abedab6 100644
--- a/frontend/core/sync/build.gradle.kts
+++ b/frontend/core/sync/build.gradle.kts
@@ -10,15 +10,6 @@ plugins {
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
@@ -32,7 +23,7 @@ kotlin {
commonMain.dependencies {
// Correct dependency: Syncable interface is in shared core domain
implementation(projects.core.coreDomain)
- // Also include frontend domain if needed (e.g. for frontend specific models)
+ // Also include frontend domain if needed (e.g., for frontend-specific models)
implementation(projects.frontend.core.domain)
// Networking
diff --git a/frontend/features/billing-feature/build.gradle.kts b/frontend/features/billing-feature/build.gradle.kts
index a769da31..8641a0c9 100644
--- a/frontend/features/billing-feature/build.gradle.kts
+++ b/frontend/features/billing-feature/build.gradle.kts
@@ -18,15 +18,6 @@ version = "1.0.0"
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
diff --git a/frontend/features/device-initialization/build.gradle.kts b/frontend/features/device-initialization/build.gradle.kts
new file mode 100644
index 00000000..ccbb852e
--- /dev/null
+++ b/frontend/features/device-initialization/build.gradle.kts
@@ -0,0 +1,55 @@
+@file:OptIn(ExperimentalWasmDsl::class)
+
+import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
+
+/**
+ * Dieses Modul kapselt die Geräte-Initialisierung (Onboarding).
+ */
+plugins {
+ alias(libs.plugins.kotlinMultiplatform)
+ alias(libs.plugins.composeMultiplatform)
+ alias(libs.plugins.composeCompiler)
+ alias(libs.plugins.kotlinSerialization)
+}
+
+group = "at.mocode.clients"
+version = "1.0.0"
+
+kotlin {
+ jvm()
+
+ wasmJs {
+ binaries.library()
+ browser {
+ testTask {
+ enabled = false
+ }
+ }
+ }
+
+ sourceSets {
+ commonMain.dependencies {
+ implementation(projects.frontend.core.designSystem)
+ implementation(projects.frontend.core.auth)
+ implementation(projects.frontend.core.domain)
+ implementation(projects.frontend.core.network)
+
+ implementation(compose.foundation)
+ implementation(compose.runtime)
+ implementation(compose.material3)
+ implementation(compose.ui)
+ implementation(compose.components.resources)
+ implementation(compose.materialIconsExtended)
+
+ implementation(libs.bundles.kmp.common)
+ implementation(libs.bundles.compose.common)
+
+ implementation(libs.koin.core)
+ implementation(libs.koin.compose)
+ }
+
+ jvmMain.dependencies {
+ implementation(compose.uiTooling)
+ }
+ }
+}
diff --git a/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/di/DeviceInitializationModule.kt b/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/di/DeviceInitializationModule.kt
new file mode 100644
index 00000000..ccedbbc1
--- /dev/null
+++ b/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/di/DeviceInitializationModule.kt
@@ -0,0 +1,10 @@
+package at.mocode.frontend.features.deviceinitialization.di
+
+import at.mocode.frontend.features.deviceinitialization.presentation.DeviceInitializationViewModel
+import org.koin.dsl.module
+
+val deviceInitializationModule = module {
+ factory { (onComplete: (at.mocode.frontend.features.deviceinitialization.domain.DeviceInitializationSettings) -> Unit) ->
+ DeviceInitializationViewModel(get(), onComplete)
+ }
+}
diff --git a/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/domain/DeviceInitializationSettings.kt b/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/domain/DeviceInitializationSettings.kt
new file mode 100644
index 00000000..56fe373b
--- /dev/null
+++ b/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/domain/DeviceInitializationSettings.kt
@@ -0,0 +1,35 @@
+package at.mocode.frontend.features.deviceinitialization.domain
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+enum class NetworkRole {
+ MASTER,
+ CLIENT,
+ RICHTER,
+ ZEITNEHMER,
+ STALLMEISTER,
+ ANZEIGE,
+ PARCOURS_CHEF
+}
+
+@Serializable
+data class ExpectedClient(
+ val name: String,
+ val role: NetworkRole,
+ val isOnline: Boolean = false,
+ val isSynchronized: Boolean = true
+)
+
+@Serializable
+data class DeviceInitializationSettings(
+ val deviceName: String = "",
+ val sharedKey: String = "",
+ val backupPath: String = "",
+ val networkRole: NetworkRole = NetworkRole.CLIENT,
+ val expectedClients: List = emptyList(),
+ val syncInterval: Int = 30, // in Minuten
+ val defaultPrinter: String = ""
+) {
+ val isConfigured: Boolean get() = deviceName.isNotBlank() && sharedKey.isNotBlank()
+}
diff --git a/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/domain/DeviceInitializationSettingsManager.kt b/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/domain/DeviceInitializationSettingsManager.kt
new file mode 100644
index 00000000..adf18164
--- /dev/null
+++ b/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/domain/DeviceInitializationSettingsManager.kt
@@ -0,0 +1,7 @@
+package at.mocode.frontend.features.deviceinitialization.domain
+
+expect object DeviceInitializationSettingsManager {
+ fun saveSettings(settings: DeviceInitializationSettings)
+ fun loadSettings(): DeviceInitializationSettings?
+ fun isConfigured(): Boolean
+}
diff --git a/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/domain/DeviceInitializationValidator.kt b/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/domain/DeviceInitializationValidator.kt
new file mode 100644
index 00000000..cc242674
--- /dev/null
+++ b/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/domain/DeviceInitializationValidator.kt
@@ -0,0 +1,44 @@
+package at.mocode.frontend.features.deviceinitialization.domain
+
+/**
+ * Validierungslogik für den Geräte-Initialisierungs-Wizard.
+ */
+object DeviceInitializationValidator {
+
+ /** Mindestlänge für den Gerätenamen. */
+ const val MIN_NAME_LENGTH = 3
+
+ /** Mindestlänge für den Sicherheitsschlüssel. */
+ const val MIN_KEY_LENGTH = 8
+
+ /** Gibt `true` zurück, wenn der Gerätename gültig ist. */
+ fun isNameValid(name: String): Boolean = name.trim().length >= MIN_NAME_LENGTH
+
+ /** Gibt `true` zurück, wenn der Sicherheitsschlüssel gültig ist. */
+ fun isKeyValid(key: String): Boolean = key.trim().length >= MIN_KEY_LENGTH
+
+ /** Gibt `true` zurück, wenn der Backup-Pfad gültig ist. */
+ fun isBackupPathValid(path: String): Boolean = path.isNotBlank()
+
+ /** Gibt `true` zurück, wenn das Sync-Intervall gültig ist. */
+ fun isSyncIntervalValid(interval: Int): Boolean = interval in 1..60
+
+ /**
+ * Gibt `true` zurück, wenn alle Pflichtfelder gültig sind.
+ */
+ fun canContinue(settings: DeviceInitializationSettings): Boolean {
+ val basicValid = isNameValid(settings.deviceName) &&
+ isKeyValid(settings.sharedKey) &&
+ (if (settings.networkRole == NetworkRole.MASTER) isBackupPathValid(settings.backupPath) else true) &&
+ isSyncIntervalValid(settings.syncInterval)
+
+ if (!basicValid) return false
+
+ // Falls Master, müssen alle erwarteten Clients einen Namen haben
+ if (settings.networkRole == NetworkRole.MASTER) {
+ return settings.expectedClients.all { it.name.trim().isNotEmpty() }
+ }
+
+ return true
+ }
+}
diff --git a/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/presentation/DeviceInitializationScreen.kt b/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/presentation/DeviceInitializationScreen.kt
new file mode 100644
index 00000000..e8bedc09
--- /dev/null
+++ b/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/presentation/DeviceInitializationScreen.kt
@@ -0,0 +1,107 @@
+package at.mocode.frontend.features.deviceinitialization.presentation
+
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material.icons.automirrored.filled.ArrowForward
+import androidx.compose.material.icons.filled.Check
+import androidx.compose.material3.*
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import at.mocode.frontend.features.deviceinitialization.domain.DeviceInitializationValidator
+
+@Composable
+fun DeviceInitializationScreen(
+ viewModel: DeviceInitializationViewModel
+) {
+ val uiState by viewModel.uiState.collectAsState()
+
+ // Automatische Discovery starten, wenn wir auf Schritt 0 sind
+ LaunchedEffect(uiState.currentStep) {
+ if (uiState.currentStep == 0) viewModel.startDiscovery()
+ }
+
+ Surface(color = MaterialTheme.colorScheme.background) {
+ Column(
+ modifier = Modifier.fillMaxSize().padding(24.dp).verticalScroll(rememberScrollState()),
+ verticalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ Text(
+ "Willkommen bei der Meldestelle",
+ style = MaterialTheme.typography.headlineSmall,
+ fontWeight = FontWeight.SemiBold
+ )
+ Text(
+ if (uiState.currentStep == 0) "Schritt 1: Netzwerk-Rolle festlegen" else "Schritt 2: Rollenspezifische Konfiguration",
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.onSurfaceVariant
+ )
+
+ if (uiState.currentStep == 0) {
+ // PHASE 1: NETZWERK-ROLLE
+ Card(modifier = Modifier.fillMaxWidth()) {
+ Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
+ Text("🌐 Netzwerk-Rolle wählen", style = MaterialTheme.typography.titleMedium)
+ Text(
+ "Wähle aus, ob dieses Gerät als Master (zentrale Datenbank) oder als Client fungiert.",
+ style = MaterialTheme.typography.bodySmall
+ )
+
+ NetworkRoleSelector(
+ selectedRole = uiState.settings.networkRole,
+ onRoleSelected = { viewModel.setNetworkRole(it) }
+ )
+
+ Button(
+ onClick = { viewModel.nextStep() },
+ modifier = Modifier.align(Alignment.End)
+ ) {
+ Text("Weiter")
+ Icon(Icons.AutoMirrored.Filled.ArrowForward, contentDescription = null)
+ }
+ }
+ }
+ } else {
+ // PHASE 2: ROLLENSPEZIFISCH (JVM spezifische Implementierung folgt)
+ DeviceInitializationConfig(
+ uiState = uiState,
+ viewModel = viewModel
+ )
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ TextButton(onClick = { viewModel.previousStep() }) {
+ Icon(Icons.AutoMirrored.Filled.ArrowBack, null)
+ Spacer(Modifier.width(8.dp))
+ Text("Zurück zur Rollenauswahl")
+ }
+
+ Button(
+ onClick = { viewModel.completeInitialization() },
+ enabled = DeviceInitializationValidator.canContinue(uiState.settings)
+ ) {
+ Text("Konfiguration abschließen")
+ Icon(Icons.Default.Check, null, Modifier.padding(start = 8.dp))
+ }
+ }
+ }
+ }
+ }
+}
+
+@Composable
+expect fun DeviceInitializationConfig(
+ uiState: DeviceInitializationUiState,
+ viewModel: DeviceInitializationViewModel
+)
diff --git a/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/presentation/DeviceInitializationUiState.kt b/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/presentation/DeviceInitializationUiState.kt
new file mode 100644
index 00000000..60fd3fbc
--- /dev/null
+++ b/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/presentation/DeviceInitializationUiState.kt
@@ -0,0 +1,12 @@
+package at.mocode.frontend.features.deviceinitialization.presentation
+
+import at.mocode.frontend.core.network.discovery.DiscoveredService
+import at.mocode.frontend.features.deviceinitialization.domain.DeviceInitializationSettings
+
+data class DeviceInitializationUiState(
+ val currentStep: Int = 0,
+ val settings: DeviceInitializationSettings = DeviceInitializationSettings(),
+ val discoveredMasters: List = emptyList(),
+ val isProcessing: Boolean = false,
+ val error: String? = null
+)
diff --git a/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/presentation/DeviceInitializationViewModel.kt b/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/presentation/DeviceInitializationViewModel.kt
new file mode 100644
index 00000000..cf6c569c
--- /dev/null
+++ b/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/presentation/DeviceInitializationViewModel.kt
@@ -0,0 +1,66 @@
+package at.mocode.frontend.features.deviceinitialization.presentation
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import at.mocode.frontend.core.network.discovery.NetworkDiscoveryService
+import at.mocode.frontend.features.deviceinitialization.domain.DeviceInitializationSettings
+import at.mocode.frontend.features.deviceinitialization.domain.ExpectedClient
+import at.mocode.frontend.features.deviceinitialization.domain.NetworkRole
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+
+class DeviceInitializationViewModel(
+ private val discoveryService: NetworkDiscoveryService,
+ private val onInitializationComplete: (DeviceInitializationSettings) -> Unit
+) : ViewModel() {
+ private val _uiState = MutableStateFlow(DeviceInitializationUiState())
+ val uiState: StateFlow = _uiState.asStateFlow()
+
+ init {
+ viewModelScope.launch {
+ discoveryService.discoveredServices.collect { services ->
+ _uiState.update { it.copy(discoveredMasters = services) }
+ }
+ }
+ }
+
+ fun startDiscovery() {
+ discoveryService.startDiscovery()
+ }
+
+ fun nextStep() {
+ _uiState.update { it.copy(currentStep = it.currentStep + 1) }
+ }
+
+ fun previousStep() {
+ _uiState.update { it.copy(currentStep = (it.currentStep - 1).coerceAtLeast(0)) }
+ }
+
+ fun updateSettings(update: (DeviceInitializationSettings) -> DeviceInitializationSettings) {
+ _uiState.update { it.copy(settings = update(it.settings)) }
+ }
+
+ fun setNetworkRole(role: NetworkRole) {
+ updateSettings { it.copy(networkRole = role) }
+ }
+
+ fun addExpectedClient(name: String, role: NetworkRole) {
+ updateSettings {
+ it.copy(expectedClients = it.expectedClients + ExpectedClient(name, role))
+ }
+ }
+
+ fun removeExpectedClient(index: Int) {
+ updateSettings {
+ val newList = it.expectedClients.toMutableList().apply { removeAt(index) }
+ it.copy(expectedClients = newList)
+ }
+ }
+
+ fun completeInitialization() {
+ onInitializationComplete(_uiState.value.settings)
+ }
+}
diff --git a/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/presentation/NetworkRoleSelector.kt b/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/presentation/NetworkRoleSelector.kt
new file mode 100644
index 00000000..f7c8383a
--- /dev/null
+++ b/frontend/features/device-initialization/src/commonMain/kotlin/at/mocode/frontend/features/deviceinitialization/presentation/NetworkRoleSelector.kt
@@ -0,0 +1,63 @@
+package at.mocode.frontend.features.deviceinitialization.presentation
+
+import androidx.compose.foundation.layout.*
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.RadioButton
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import at.mocode.frontend.features.deviceinitialization.domain.NetworkRole
+
+@Composable
+fun NetworkRoleSelector(
+ selectedRole: NetworkRole,
+ onRoleSelected: (NetworkRole) -> Unit
+) {
+ Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
+ NetworkRoleCard(
+ title = "Master (Host)",
+ description = "Verwaltet die zentrale Datenbank und koordiniert den Sync.",
+ isSelected = selectedRole == NetworkRole.MASTER,
+ onClick = { onRoleSelected(NetworkRole.MASTER) }
+ )
+
+ NetworkRoleCard(
+ title = "Client",
+ description = "Verbindet sich mit einem Master-Gerät im lokalen Netzwerk.",
+ isSelected = selectedRole == NetworkRole.CLIENT,
+ onClick = { onRoleSelected(NetworkRole.CLIENT) }
+ )
+ }
+}
+
+@Composable
+private fun NetworkRoleCard(
+ title: String,
+ description: String,
+ isSelected: Boolean,
+ onClick: () -> Unit
+) {
+ Surface(
+ onClick = onClick,
+ shape = MaterialTheme.shapes.medium,
+ color = if (isSelected) MaterialTheme.colorScheme.primaryContainer else MaterialTheme.colorScheme.surfaceVariant,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Row(Modifier.padding(16.dp), verticalAlignment = Alignment.CenterVertically) {
+ RadioButton(
+ selected = isSelected,
+ onClick = onClick
+ )
+ Column {
+ Text(title, style = MaterialTheme.typography.labelLarge)
+ Text(
+ description,
+ style = MaterialTheme.typography.bodySmall
+ )
+ }
+ }
+ }
+}
diff --git a/frontend/features/device-initialization/src/jvmMain/kotlin/at/mocode/frontend/features/deviceinitialization/domain/DeviceInitializationSettingsManager.jvm.kt b/frontend/features/device-initialization/src/jvmMain/kotlin/at/mocode/frontend/features/deviceinitialization/domain/DeviceInitializationSettingsManager.jvm.kt
new file mode 100644
index 00000000..b10b003f
--- /dev/null
+++ b/frontend/features/device-initialization/src/jvmMain/kotlin/at/mocode/frontend/features/deviceinitialization/domain/DeviceInitializationSettingsManager.jvm.kt
@@ -0,0 +1,34 @@
+package at.mocode.frontend.features.deviceinitialization.domain
+
+import kotlinx.serialization.json.Json
+import java.io.File
+
+actual object DeviceInitializationSettingsManager {
+ private val settingsFile = File("settings.json")
+ private val json = Json { prettyPrint = true; ignoreUnknownKeys = true }
+
+ actual fun saveSettings(settings: DeviceInitializationSettings) {
+ try {
+ val content = json.encodeToString(settings)
+ settingsFile.writeText(content)
+ } catch (e: Exception) {
+ println("Fehler beim Speichern der Einstellungen: ${e.message}")
+ }
+ }
+
+ actual fun loadSettings(): DeviceInitializationSettings? {
+ if (!settingsFile.exists()) return null
+ return try {
+ val content = settingsFile.readText()
+ json.decodeFromString(content)
+ } catch (e: Exception) {
+ println("Fehler beim Laden der Einstellungen: ${e.message}")
+ null
+ }
+ }
+
+ actual fun isConfigured(): Boolean {
+ val settings = loadSettings() ?: return false
+ return DeviceInitializationValidator.canContinue(settings)
+ }
+}
diff --git a/frontend/features/device-initialization/src/jvmMain/kotlin/at/mocode/frontend/features/deviceinitialization/presentation/DeviceInitializationConfig.jvm.kt b/frontend/features/device-initialization/src/jvmMain/kotlin/at/mocode/frontend/features/deviceinitialization/presentation/DeviceInitializationConfig.jvm.kt
new file mode 100644
index 00000000..045e4364
--- /dev/null
+++ b/frontend/features/device-initialization/src/jvmMain/kotlin/at/mocode/frontend/features/deviceinitialization/presentation/DeviceInitializationConfig.jvm.kt
@@ -0,0 +1,268 @@
+package at.mocode.frontend.features.deviceinitialization.presentation
+
+import androidx.compose.foundation.layout.*
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Delete
+import androidx.compose.material.icons.outlined.FolderOpen
+import androidx.compose.material.icons.outlined.Visibility
+import androidx.compose.material.icons.outlined.VisibilityOff
+import androidx.compose.material3.*
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.input.PasswordVisualTransformation
+import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.unit.dp
+import at.mocode.frontend.core.designsystem.components.MsEnumDropdown
+import at.mocode.frontend.features.deviceinitialization.domain.DeviceInitializationValidator
+import at.mocode.frontend.features.deviceinitialization.domain.NetworkRole
+import java.io.File
+import javax.swing.JFileChooser
+import javax.swing.UIManager
+
+@Composable
+actual fun DeviceInitializationConfig(
+ uiState: DeviceInitializationUiState,
+ viewModel: DeviceInitializationViewModel
+) {
+ val settings = uiState.settings
+
+ Card(modifier = Modifier.fillMaxWidth()) {
+ Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
+ Text("⚙️ Geräte-Konfiguration (${settings.networkRole})", style = MaterialTheme.typography.titleMedium)
+
+ MsSettingsField(
+ value = settings.deviceName,
+ onValueChange = { viewModel.updateSettings { s -> s.copy(deviceName = it) } },
+ label = "Gerätename",
+ placeholder = "z.B. Meldestelle-PC-1",
+ isError = settings.deviceName.isNotEmpty() && !DeviceInitializationValidator.isNameValid(settings.deviceName),
+ errorText = "Mindestens ${DeviceInitializationValidator.MIN_NAME_LENGTH} Zeichen erforderlich."
+ )
+
+ var passwordVisible by remember { mutableStateOf(false) }
+ MsSettingsField(
+ value = settings.sharedKey,
+ onValueChange = { viewModel.updateSettings { s -> s.copy(sharedKey = it) } },
+ label = "Sicherheitsschlüssel (Sync-Key)",
+ placeholder = "Mindestens 8 Zeichen",
+ isError = settings.sharedKey.isNotEmpty() && !DeviceInitializationValidator.isKeyValid(settings.sharedKey),
+ errorText = "Mindestens ${DeviceInitializationValidator.MIN_KEY_LENGTH} Zeichen erforderlich.",
+ visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
+ trailingIcon = {
+ IconButton(onClick = { passwordVisible = !passwordVisible }) {
+ Icon(
+ imageVector = if (passwordVisible) Icons.Outlined.VisibilityOff else Icons.Outlined.Visibility,
+ contentDescription = if (passwordVisible) "Verbergen" else "Anzeigen"
+ )
+ }
+ }
+ )
+
+ if (settings.networkRole == NetworkRole.MASTER) {
+ HorizontalDivider(Modifier, DividerDefaults.Thickness, DividerDefaults.color)
+ Text("👥 Erwartete Clients (Richter, Zeitnehmer, etc.)", style = MaterialTheme.typography.titleSmall)
+ Text(
+ "Definiere, welche Geräte sich mit diesem Master synchronisieren dürfen.",
+ style = MaterialTheme.typography.bodySmall
+ )
+
+ Spacer(Modifier.height(8.dp))
+
+ settings.expectedClients.forEachIndexed { index, client ->
+ ListItem(
+ headlineContent = {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(12.dp)
+ ) {
+ Text(
+ client.name,
+ style = MaterialTheme.typography.bodyLarge,
+ fontWeight = FontWeight.Medium
+ )
+ SuggestionChip(
+ onClick = {},
+ label = { Text(client.role.name) },
+ colors = SuggestionChipDefaults.suggestionChipColors(
+ containerColor = MaterialTheme.colorScheme.secondaryContainer,
+ labelColor = MaterialTheme.colorScheme.onSecondaryContainer
+ )
+ )
+ }
+ },
+ supportingContent = {
+ Text(
+ if (client.isOnline) "Verbunden" else "Offline",
+ style = MaterialTheme.typography.labelSmall,
+ color = if (client.isOnline) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant
+ )
+ },
+ trailingContent = {
+ IconButton(onClick = { viewModel.removeExpectedClient(index) }) {
+ Icon(
+ Icons.Default.Delete,
+ contentDescription = "Löschen",
+ tint = MaterialTheme.colorScheme.error,
+ modifier = Modifier.size(20.dp)
+ )
+ }
+ },
+ colors = ListItemDefaults.colors(
+ containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f)
+ ),
+ modifier = Modifier.padding(vertical = 4.dp)
+ )
+ }
+
+ var newClientName by remember { mutableStateOf("") }
+ var newClientRole by remember { mutableStateOf(NetworkRole.RICHTER) }
+ var showAddClient by remember { mutableStateOf(false) }
+
+ if (showAddClient) {
+ Row(
+ Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ OutlinedTextField(
+ value = newClientName,
+ onValueChange = { newClientName = it },
+ label = { Text("Gerätename des Clients") },
+ modifier = Modifier.weight(1f)
+ )
+
+ MsEnumDropdown(
+ label = "Rolle",
+ options = NetworkRole.entries.filter { it != NetworkRole.MASTER }.toTypedArray(),
+ selectedOption = newClientRole,
+ onOptionSelected = { newClientRole = it },
+ modifier = Modifier.weight(0.5f)
+ )
+
+ Button(
+ onClick = {
+ if (newClientName.isNotBlank()) {
+ viewModel.addExpectedClient(newClientName, newClientRole)
+ newClientName = ""
+ showAddClient = false
+ }
+ },
+ enabled = newClientName.isNotBlank()
+ ) {
+ Icon(Icons.Default.Add, null)
+ }
+ }
+ } else {
+ TextButton(onClick = { showAddClient = true }) {
+ Icon(Icons.Default.Add, null)
+ Spacer(Modifier.width(8.dp))
+ Text("Client hinzufügen")
+ }
+ }
+
+ HorizontalDivider(Modifier, DividerDefaults.Thickness, DividerDefaults.color)
+ OutlinedTextField(
+ value = settings.backupPath,
+ onValueChange = { viewModel.updateSettings { s -> s.copy(backupPath = it) } },
+ label = { Text("Backup-Verzeichnis (Pfad)") },
+ placeholder = { Text("/pfad/zu/den/backups") },
+ modifier = Modifier.fillMaxWidth(),
+ trailingIcon = {
+ IconButton(onClick = {
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName())
+ val chooser = JFileChooser().apply {
+ fileSelectionMode = JFileChooser.DIRECTORIES_ONLY
+ dialogTitle = "Backup-Verzeichnis wählen"
+ if (settings.backupPath.isNotEmpty()) {
+ val currentDir = File(settings.backupPath)
+ if (currentDir.exists()) currentDirectory = currentDir
+ }
+ }
+ val result = chooser.showOpenDialog(null)
+ if (result == JFileChooser.APPROVE_OPTION) {
+ viewModel.updateSettings { s -> s.copy(backupPath = chooser.selectedFile.absolutePath) }
+ }
+ } catch (e: Exception) {
+ println("[Error] Fehler beim Öffnen des Verzeichnis-Wählers: ${e.message}")
+ }
+ }) {
+ Icon(Icons.Outlined.FolderOpen, contentDescription = "Verzeichnis wählen")
+ }
+ },
+ isError = settings.backupPath.isNotEmpty() && !DeviceInitializationValidator.isBackupPathValid(settings.backupPath)
+ )
+
+ Text("Sync-Intervall: ${settings.syncInterval} Min.", style = MaterialTheme.typography.labelMedium)
+ Slider(
+ value = settings.syncInterval.toFloat(),
+ onValueChange = { viewModel.updateSettings { s -> s.copy(syncInterval = it.toInt()) } },
+ valueRange = 1f..60f,
+ steps = 59
+ )
+ } else {
+ HorizontalDivider(Modifier, DividerDefaults.Thickness, DividerDefaults.color)
+ Text("🔍 Verfügbare Master im Netzwerk", style = MaterialTheme.typography.titleSmall)
+
+ if (uiState.discoveredMasters.isEmpty()) {
+ Box(Modifier.fillMaxWidth().padding(16.dp), contentAlignment = Alignment.Center) {
+ CircularProgressIndicator(modifier = Modifier.size(24.dp))
+ Text("Suche nach Master...", modifier = Modifier.padding(start = 40.dp))
+ }
+ }
+
+ uiState.discoveredMasters.forEach { service ->
+ ListItem(
+ headlineContent = { Text(service.name) },
+ supportingContent = { Text("${service.host}:${service.port}") },
+ trailingContent = {
+ Button(onClick = {
+ viewModel.updateSettings { s -> s.copy(sharedKey = service.metadata["key"] ?: s.sharedKey) }
+ }) {
+ Text("Verbinden")
+ }
+ },
+ colors = ListItemDefaults.colors(containerColor = MaterialTheme.colorScheme.primaryContainer)
+ )
+ }
+
+ Text(
+ "Hinweis: Als Client wird dieses Gerät automatisch versuchen, den Master im Netzwerk zu finden.",
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.onSurfaceVariant
+ )
+ }
+ }
+ }
+}
+
+@Composable
+private fun MsSettingsField(
+ value: String,
+ onValueChange: (String) -> Unit,
+ label: String,
+ placeholder: String,
+ isError: Boolean,
+ errorText: String,
+ visualTransformation: VisualTransformation = VisualTransformation.None,
+ trailingIcon: @Composable (() -> Unit)? = null
+) {
+ OutlinedTextField(
+ value = value,
+ onValueChange = onValueChange,
+ label = { Text(label) },
+ placeholder = { Text(placeholder) },
+ modifier = Modifier.fillMaxWidth(),
+ isError = isError,
+ visualTransformation = visualTransformation,
+ trailingIcon = trailingIcon,
+ supportingText = {
+ if (isError) {
+ Text(errorText)
+ }
+ }
+ )
+}
diff --git a/frontend/features/device-initialization/src/wasmJsMain/kotlin/at/mocode/frontend/features/deviceinitialization/domain/DeviceInitializationSettingsManager.wasmJs.kt b/frontend/features/device-initialization/src/wasmJsMain/kotlin/at/mocode/frontend/features/deviceinitialization/domain/DeviceInitializationSettingsManager.wasmJs.kt
new file mode 100644
index 00000000..4b94ffdd
--- /dev/null
+++ b/frontend/features/device-initialization/src/wasmJsMain/kotlin/at/mocode/frontend/features/deviceinitialization/domain/DeviceInitializationSettingsManager.wasmJs.kt
@@ -0,0 +1,11 @@
+package at.mocode.frontend.features.deviceinitialization.domain
+
+actual object DeviceInitializationSettingsManager {
+ actual fun saveSettings(settings: DeviceInitializationSettings) {
+ // Nicht implementiert für WasmJS
+ }
+
+ actual fun loadSettings(): DeviceInitializationSettings? = null
+
+ actual fun isConfigured(): Boolean = false
+}
diff --git a/frontend/features/device-initialization/src/wasmJsMain/kotlin/at/mocode/frontend/features/deviceinitialization/presentation/DeviceInitializationConfig.wasmJs.kt b/frontend/features/device-initialization/src/wasmJsMain/kotlin/at/mocode/frontend/features/deviceinitialization/presentation/DeviceInitializationConfig.wasmJs.kt
new file mode 100644
index 00000000..1a1c7e73
--- /dev/null
+++ b/frontend/features/device-initialization/src/wasmJsMain/kotlin/at/mocode/frontend/features/deviceinitialization/presentation/DeviceInitializationConfig.wasmJs.kt
@@ -0,0 +1,12 @@
+package at.mocode.frontend.features.deviceinitialization.presentation
+
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+
+@Composable
+actual fun DeviceInitializationConfig(
+ uiState: DeviceInitializationUiState,
+ viewModel: DeviceInitializationViewModel
+) {
+ Text("Konfiguration für Web (WasmJS) ist nicht implementiert.")
+}
diff --git a/frontend/features/funktionaer-feature/build.gradle.kts b/frontend/features/funktionaer-feature/build.gradle.kts
index 21e09916..a6c9327d 100644
--- a/frontend/features/funktionaer-feature/build.gradle.kts
+++ b/frontend/features/funktionaer-feature/build.gradle.kts
@@ -17,15 +17,6 @@ version = "1.0.0"
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
diff --git a/frontend/features/nennung-feature/build.gradle.kts b/frontend/features/nennung-feature/build.gradle.kts
index 4758946a..d85fd5f4 100644
--- a/frontend/features/nennung-feature/build.gradle.kts
+++ b/frontend/features/nennung-feature/build.gradle.kts
@@ -19,15 +19,6 @@ version = "1.0.0"
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
diff --git a/frontend/features/pferde-feature/build.gradle.kts b/frontend/features/pferde-feature/build.gradle.kts
index 80a247c4..9cf9fe9d 100644
--- a/frontend/features/pferde-feature/build.gradle.kts
+++ b/frontend/features/pferde-feature/build.gradle.kts
@@ -17,15 +17,6 @@ version = "1.0.0"
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
diff --git a/frontend/features/ping-feature/build.gradle.kts b/frontend/features/ping-feature/build.gradle.kts
index 7e91c05b..53416f5c 100644
--- a/frontend/features/ping-feature/build.gradle.kts
+++ b/frontend/features/ping-feature/build.gradle.kts
@@ -18,15 +18,6 @@ version = "1.0.0"
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
@@ -78,8 +69,5 @@ kotlin {
implementation(compose.uiTooling)
}
- jsMain.dependencies {
- implementation(libs.ktor.client.js)
- }
}
}
diff --git a/frontend/features/profile-feature/build.gradle.kts b/frontend/features/profile-feature/build.gradle.kts
index 0ea129de..6295f690 100644
--- a/frontend/features/profile-feature/build.gradle.kts
+++ b/frontend/features/profile-feature/build.gradle.kts
@@ -18,15 +18,6 @@ version = "1.0.0"
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
diff --git a/frontend/features/reiter-feature/build.gradle.kts b/frontend/features/reiter-feature/build.gradle.kts
index 62b161ce..90503fe7 100644
--- a/frontend/features/reiter-feature/build.gradle.kts
+++ b/frontend/features/reiter-feature/build.gradle.kts
@@ -15,15 +15,6 @@ version = "1.0.0"
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
diff --git a/frontend/features/turnier-feature/build.gradle.kts b/frontend/features/turnier-feature/build.gradle.kts
index 999d6218..af9b425d 100644
--- a/frontend/features/turnier-feature/build.gradle.kts
+++ b/frontend/features/turnier-feature/build.gradle.kts
@@ -17,15 +17,6 @@ version = "1.0.0"
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
diff --git a/frontend/features/turnier-feature/src/jsMain/kotlin/at/mocode/turnier/feature/di/TurnierFeatureModule.kt b/frontend/features/turnier-feature/src/jsMain/kotlin/at/mocode/turnier/feature/di/TurnierFeatureModule.kt
deleted file mode 100644
index 2f5a51aa..00000000
--- a/frontend/features/turnier-feature/src/jsMain/kotlin/at/mocode/turnier/feature/di/TurnierFeatureModule.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package at.mocode.turnier.feature.di
-
-import org.koin.dsl.module
-
-actual val turnierFeatureModule = module {
- // No-op or minimal for JS/Web
-}
diff --git a/frontend/features/veranstalter-feature/build.gradle.kts b/frontend/features/veranstalter-feature/build.gradle.kts
index 8aa8f0ba..38d2b06d 100644
--- a/frontend/features/veranstalter-feature/build.gradle.kts
+++ b/frontend/features/veranstalter-feature/build.gradle.kts
@@ -4,7 +4,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
/**
* Feature-Modul: Veranstalter-Verwaltung (Desktop-only)
- * Kapselt alle Screens und Logik für Veranstalter-Auswahl, -Detail und -Neuanlage.
+ * kapselt alle Screens und Logik für Veranstalter-Auswahl, -Detail und -Neuanlage.
*/
plugins {
alias(libs.plugins.kotlinMultiplatform)
@@ -16,15 +16,6 @@ version = "1.0.0"
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
diff --git a/frontend/features/veranstaltung-feature/build.gradle.kts b/frontend/features/veranstaltung-feature/build.gradle.kts
index e1387518..1ba5e438 100644
--- a/frontend/features/veranstaltung-feature/build.gradle.kts
+++ b/frontend/features/veranstaltung-feature/build.gradle.kts
@@ -16,15 +16,6 @@ version = "1.0.0"
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
diff --git a/frontend/features/verein-feature/build.gradle.kts b/frontend/features/verein-feature/build.gradle.kts
index 09f0709e..767b83b1 100644
--- a/frontend/features/verein-feature/build.gradle.kts
+++ b/frontend/features/verein-feature/build.gradle.kts
@@ -17,15 +17,6 @@ version = "1.0.0"
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
diff --git a/frontend/features/zns-import-feature/build.gradle.kts b/frontend/features/zns-import-feature/build.gradle.kts
index 00488041..826cf492 100644
--- a/frontend/features/zns-import-feature/build.gradle.kts
+++ b/frontend/features/zns-import-feature/build.gradle.kts
@@ -4,7 +4,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
/**
* Feature-Modul: ZNS-Stammdaten-Import (Desktop-only)
- * Kapselt ViewModel, State, API-Kommunikation und UI-Screen für den ZNS-Import.
+ * kapselt ViewModel, State, API-Kommunikation und UI-Screen für den ZNS-Import.
*/
plugins {
alias(libs.plugins.kotlinMultiplatform)
@@ -18,15 +18,6 @@ version = "1.0.0"
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
diff --git a/frontend/shells/meldestelle-desktop/build.gradle.kts b/frontend/shells/meldestelle-desktop/build.gradle.kts
index f7a550d3..dba38ee9 100644
--- a/frontend/shells/meldestelle-desktop/build.gradle.kts
+++ b/frontend/shells/meldestelle-desktop/build.gradle.kts
@@ -10,13 +10,13 @@ import java.util.*
* Setzt alle Core- und Feature-Module zu einer lauffähigen Desktop-Anwendung zusammen.
*
* Packaging:
- * ./gradlew :frontend:shells:meldestelle-desktop:packageDeb → Linux .deb
- * ./gradlew :frontend:shells:meldestelle-desktop:packageMsi → Windows .msi
- * ./gradlew :frontend:shells:meldestelle-desktop:packageDmg → macOS .dmg
+ * ./gradlew :frontend:shells:meldestelle-desktop:packageDeb → Linux .deb
+ * ./gradlew :frontend:shells:meldestelle-desktop:packageMsi → Windows .msi
+ * ./gradlew :frontend:shells:meldestelle-desktop:packageDmg → macOS .dmg
* ./gradlew :frontend:shells:meldestelle-desktop:packageReleaseDistributables → alle Plattformen
*
- * Version: wird automatisch aus version.properties im Root-Projekt gelesen (SemVer).
- * Icons: src/jvmMain/resources/icon.png / icon.ico / icon.icns
+ * Version: Wird automatisch aus version.properties im Root-Projekt gelesen (SemVer).
+ * Icons: src/jvmMain/resources/icon.png / icon.ico / icon.icns
* → siehe ICONS_PLACEHOLDER.md für Anforderungen
*/
plugins {
@@ -42,15 +42,6 @@ val packageVer = "$vMajor.$vMinor.$vPatch"
kotlin {
jvm()
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
binaries.library()
browser {
@@ -86,6 +77,7 @@ kotlin {
implementation(projects.frontend.features.vereinFeature)
implementation(projects.frontend.features.turnierFeature)
implementation(projects.frontend.features.billingFeature)
+ implementation(projects.frontend.features.deviceInitialization)
// Compose Desktop
implementation(compose.desktop.currentOs)
diff --git a/frontend/shells/meldestelle-desktop/src/jvmMain/kotlin/at/mocode/desktop/DesktopApp.kt b/frontend/shells/meldestelle-desktop/src/jvmMain/kotlin/at/mocode/desktop/DesktopApp.kt
index 1d512660..9ca220df 100644
--- a/frontend/shells/meldestelle-desktop/src/jvmMain/kotlin/at/mocode/desktop/DesktopApp.kt
+++ b/frontend/shells/meldestelle-desktop/src/jvmMain/kotlin/at/mocode/desktop/DesktopApp.kt
@@ -10,12 +10,12 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import at.mocode.desktop.navigation.DesktopNavigationPort
import at.mocode.desktop.screens.layout.DesktopMainLayout
-import at.mocode.desktop.screens.onboarding.SettingsManager
import at.mocode.frontend.core.auth.data.AuthTokenManager
import at.mocode.frontend.core.auth.presentation.LoginScreen
import at.mocode.frontend.core.auth.presentation.LoginViewModel
import at.mocode.frontend.core.designsystem.theme.AppTheme
import at.mocode.frontend.core.navigation.AppScreen
+import at.mocode.frontend.features.deviceinitialization.domain.DeviceInitializationSettingsManager
import org.koin.compose.koinInject
import org.koin.compose.viewmodel.koinViewModel
@@ -37,7 +37,7 @@ fun DesktopApp() {
// DeviceInitialization-Check beim Start
LaunchedEffect(Unit) {
- if (!SettingsManager.isConfigured()) {
+ if (!DeviceInitializationSettingsManager.isConfigured()) {
nav.navigateToScreen(AppScreen.DeviceInitialization)
}
}
diff --git a/frontend/shells/meldestelle-desktop/src/jvmMain/kotlin/at/mocode/desktop/main.kt b/frontend/shells/meldestelle-desktop/src/jvmMain/kotlin/at/mocode/desktop/main.kt
index d9d01e49..cfe1b91b 100644
--- a/frontend/shells/meldestelle-desktop/src/jvmMain/kotlin/at/mocode/desktop/main.kt
+++ b/frontend/shells/meldestelle-desktop/src/jvmMain/kotlin/at/mocode/desktop/main.kt
@@ -12,6 +12,7 @@ import at.mocode.frontend.core.localdb.localDbModule
import at.mocode.frontend.core.network.networkModule
import at.mocode.frontend.core.sync.di.syncModule
import at.mocode.frontend.features.billing.di.billingModule
+import at.mocode.frontend.features.deviceinitialization.di.deviceInitializationModule
import at.mocode.frontend.features.nennung.di.nennungFeatureModule
import at.mocode.frontend.features.pferde.di.pferdeModule
import at.mocode.frontend.features.profile.di.profileModule
@@ -43,6 +44,7 @@ fun main() = application {
reiterModule,
vereinFeatureModule,
turnierFeatureModule,
+ deviceInitializationModule,
desktopModule,
)
}
diff --git a/frontend/shells/meldestelle-desktop/src/jvmMain/kotlin/at/mocode/desktop/screens/layout/DesktopMainLayout.kt b/frontend/shells/meldestelle-desktop/src/jvmMain/kotlin/at/mocode/desktop/screens/layout/DesktopMainLayout.kt
index da81d627..b6e17eef 100644
--- a/frontend/shells/meldestelle-desktop/src/jvmMain/kotlin/at/mocode/desktop/screens/layout/DesktopMainLayout.kt
+++ b/frontend/shells/meldestelle-desktop/src/jvmMain/kotlin/at/mocode/desktop/screens/layout/DesktopMainLayout.kt
@@ -24,9 +24,6 @@ import at.mocode.desktop.screens.management.VeranstalterAuswahl
import at.mocode.desktop.screens.management.VeranstalterDetail
import at.mocode.desktop.screens.management.VeranstalterVerwaltungScreen
import at.mocode.desktop.screens.nennung.NennungsEingangScreen
-import at.mocode.desktop.screens.onboarding.OnboardingScreen
-import at.mocode.desktop.screens.onboarding.OnboardingSettings
-import at.mocode.desktop.screens.onboarding.SettingsManager
import at.mocode.desktop.screens.profile.FunktionaerProfil
import at.mocode.desktop.screens.veranstaltung.*
import at.mocode.frontend.core.designsystem.theme.AppColors
@@ -37,6 +34,10 @@ import at.mocode.frontend.core.network.ConnectivityTracker
import at.mocode.frontend.core.network.discovery.NetworkDiscoveryService
import at.mocode.frontend.features.billing.presentation.BillingScreen
import at.mocode.frontend.features.billing.presentation.BillingViewModel
+import at.mocode.frontend.features.deviceinitialization.domain.DeviceInitializationSettings
+import at.mocode.frontend.features.deviceinitialization.domain.DeviceInitializationSettingsManager
+import at.mocode.frontend.features.deviceinitialization.presentation.DeviceInitializationScreen
+import at.mocode.frontend.features.deviceinitialization.presentation.DeviceInitializationViewModel
import at.mocode.frontend.features.nennung.presentation.NennungViewModel
import at.mocode.frontend.features.nennung.presentation.NennungsMaske
import at.mocode.frontend.features.pferde.presentation.PferdeScreen
@@ -79,7 +80,11 @@ fun DesktopMainLayout(
) {
println("[Navigation] Rendering Screen: ${currentScreen::class.simpleName} (Details: $currentScreen)")
// DeviceInitialization-Daten (On-the-fly geladen oder Default)
- var onboardingSettings by remember { mutableStateOf(SettingsManager.loadSettings() ?: OnboardingSettings()) }
+ var onboardingSettings by remember {
+ mutableStateOf(
+ DeviceInitializationSettingsManager.loadSettings() ?: DeviceInitializationSettings()
+ )
+ }
// Automatische Umleitung zum DeviceInitialization, wenn Setup fehlt (außer wir sind bereits dort)
LaunchedEffect(onboardingSettings) {
@@ -111,7 +116,7 @@ fun DesktopMainLayout(
onBack = onBack,
onSettingsChange = {
onboardingSettings = it
- SettingsManager.saveSettings(it)
+ DeviceInitializationSettingsManager.saveSettings(it)
},
settings = onboardingSettings,
)
@@ -518,25 +523,25 @@ private fun DesktopContentArea(
currentScreen: AppScreen,
onNavigate: (AppScreen) -> Unit,
onBack: () -> Unit,
- settings: OnboardingSettings,
- onSettingsChange: (OnboardingSettings) -> Unit,
+ settings: DeviceInitializationSettings,
+ onSettingsChange: (DeviceInitializationSettings) -> Unit,
) {
when (currentScreen) {
// DeviceInitialization (Geräte-Setup)
is AppScreen.DeviceInitialization -> {
println("[Screen] Rendering DeviceInitialization")
- OnboardingScreen(
- settings = settings,
- onSettingsChange = onSettingsChange,
- onContinue = { finalSettings: OnboardingSettings ->
- SettingsManager.saveSettings(finalSettings)
+ val viewModel = koinViewModel {
+ org.koin.core.parameter.parametersOf({ finalSettings: DeviceInitializationSettings ->
+ DeviceInitializationSettingsManager.saveSettings(finalSettings)
// Vision_04: Sicherheitsschlüssel als Token setzen, damit Cloud-Suche funktioniert
val authTokenManager =
org.koin.core.context.GlobalContext.get().get()
authTokenManager.setToken(finalSettings.sharedKey)
+ onSettingsChange(finalSettings)
onNavigate(AppScreen.VeranstaltungVerwaltung)
- }
- )
+ })
+ }
+ DeviceInitializationScreen(viewModel = viewModel)
}
// Haupt-Zentrale: Veranstaltung-Verwaltung
@@ -871,7 +876,7 @@ private fun DesktopContentArea(
@Composable
private fun DesktopFooterBar(
- settings: OnboardingSettings,
+ settings: DeviceInitializationSettings,
onSetupClick: () -> Unit = {}
) {
val connectivityTracker = koinInject()
@@ -881,7 +886,7 @@ private fun DesktopFooterBar(
val online by connectivityTracker.isOnline.collectAsState()
val znsState = znsImporter.state
val discoveredServices = remember { mutableStateOf(discoveryService.getDiscoveredServices()) }
- val deviceName = settings.geraetName.ifBlank { "Unbekannt" }
+ val deviceName = settings.deviceName.ifBlank { "Unbekannt" }
// Periodisches Update der LAN-Geräte (mDNS)
LaunchedEffect(Unit) {
diff --git a/frontend/shells/meldestelle-web/build.gradle.kts b/frontend/shells/meldestelle-web/build.gradle.kts
index ed3d9a63..b58e8ae6 100644
--- a/frontend/shells/meldestelle-web/build.gradle.kts
+++ b/frontend/shells/meldestelle-web/build.gradle.kts
@@ -15,15 +15,6 @@ kotlin {
jvm()
if (isWasmEnabled) {
- js(IR) {
- binaries.library()
- browser {
- testTask {
- enabled = false
- }
- }
- }
-
wasmJs {
browser {
testTask {
diff --git a/platform/architecture-tests/build.gradle.kts b/platform/architecture-tests/build.gradle.kts
index 15f144df..d41ae9f8 100644
--- a/platform/architecture-tests/build.gradle.kts
+++ b/platform/architecture-tests/build.gradle.kts
@@ -17,24 +17,27 @@ dependencies {
// This list must be maintained manually.
// --- CONTRACTS ---
- implementation(project(":contracts:ping-api"))
+ implementation(projects.contracts.pingApi)
// --- CORE ---
- implementation(project(":core:core-domain"))
- implementation(project(":core:core-utils"))
+ implementation(projects.core.coreDomain)
+ implementation(projects.core.coreUtils)
// --- BACKEND ---
- implementation(project(":backend:services:ping:ping-service"))
+ implementation(projects.backend.services.ping.pingService)
// --- FRONTEND ---
- implementation(project(":frontend:features:ping-feature"))
- implementation(project(":frontend:core:auth"))
- implementation(project(":frontend:core:domain"))
- implementation(project(":frontend:core:design-system"))
- implementation(project(":frontend:core:navigation"))
- implementation(project(":frontend:core:network"))
- implementation(project(":frontend:core:local-db"))
- implementation(project(":frontend:core:sync"))
- implementation(project(":frontend:shells:meldestelle-desktop"))
- implementation(project(":frontend:features:zns-import-feature"))
+ implementation(projects.frontend.features.pingFeature)
+ implementation(projects.frontend.features.znsImportFeature)
+
+ implementation(projects.frontend.core.auth)
+ implementation(projects.frontend.core.domain)
+ implementation(projects.frontend.core.designSystem)
+ implementation(projects.frontend.core.navigation)
+ implementation(projects.frontend.core.network)
+ implementation(projects.frontend.core.localDb)
+ implementation(projects.frontend.core.sync)
+
+ implementation(projects.frontend.shells.meldestelleDesktop)
+ implementation(projects.frontend.shells.meldestelleWeb)
}
diff --git a/platform/platform-bom/build.gradle.kts b/platform/platform-bom/build.gradle.kts
index 08cc65eb..3f5d5cdd 100644
--- a/platform/platform-bom/build.gradle.kts
+++ b/platform/platform-bom/build.gradle.kts
@@ -3,84 +3,83 @@
// zu erstellen, die von allen anderen Modulen importiert wird.
// Dies ist die ultimative "Single Source of Truth" für Versionen.
plugins {
- `java-platform`
- `maven-publish` // Nützlich, falls die BOM extern veröffentlicht werden soll
+ `java-platform`
+ `maven-publish` // Nützlich, falls die BOM extern veröffentlicht werden soll
}
javaPlatform {
- // Erlaubt die Deklaration von Abhängigkeiten in einer Plattform.
- allowDependencies()
+ // Erlaubt die Deklaration von Abhängigkeiten in einer Plattform.
+ allowDependencies()
}
dependencies {
- // Importiert andere wichtige BOMs. Die Versionen werden durch diese
- // importierten Plattformen transitiv verwaltet.
- // Wichtig: Spring Cloud zuerst importieren, Spring Boot BOM zuletzt,
- // damit Boot die finalen Versionen (inkl. spring-web) vorgibt.
- api(platform(libs.spring.cloud.dependencies))
- api(platform(libs.spring.boot.dependencies))
- api(platform(libs.kotlin.bom))
- api(platform(libs.kotlinx.coroutines.bom))
+ // Importiert andere wichtige BOMs. Die Versionen werden durch diese
+ // importierten Plattformen transitiv verwaltet.
+ // Wichtig: Spring Cloud zuerst importieren, Spring Boot BOM zuletzt,
+ // damit Boot die finalen Versionen (inkl. spring-web) vorgibt.
+ api(platform(libs.spring.cloud.dependencies))
+ api(platform(libs.spring.boot.dependencies))
+ api(platform(libs.kotlin.bom))
+ api(platform(libs.kotlinx.coroutines.bom))
- // `constraints` erzwingt spezifische Versionen für einzelne Bibliotheken.
- // Alle Versionen werden sicher aus `libs.versions.toml` bezogen.
- constraints {
- // --- Utilities & Other ---
- api(libs.caffeine)
- api(libs.reactor.kafka)
- api(libs.redisson)
- api(libs.bignum)
- api(libs.spring.cloud.starter.consul.discovery)
- api(libs.kotlin.logging.jvm)
- api(libs.jakarta.annotation.api)
- api(libs.auth0.java.jwt)
+ // `constraints` erzwingt spezifische Versionen für einzelne Bibliotheken.
+ // Alle Versionen werden sicher aus `libs.versions.toml` bezogen.
+ constraints {
+ // --- Utilities & Other ---
+ api(libs.caffeine)
+ api(libs.reactor.kafka)
+ api(libs.redisson)
+ api(libs.bignum)
+ api(libs.spring.cloud.starter.consul.discovery)
+ api(libs.kotlin.logging.jvm)
+ api(libs.jakarta.annotation.api)
+ api(libs.auth0.java.jwt)
- // Logging: WICHTIG! Core und Classic müssen synchronisiert sein.
- api(libs.logback.classic)
- api(libs.logback.core)
+ // Logging: WICHTIG! Core und Classic müssen synchronisiert sein.
+ api(libs.logback.classic)
+ api(libs.logback.core)
- // --- Spring & SpringDoc ---
- api(libs.springdoc.openapi.starter.common)
- api(libs.springdoc.openapi.starter.webflux.ui)
+ // --- Spring & SpringDoc ---
+ api(libs.springdoc.openapi.starter.common)
+ api(libs.springdoc.openapi.starter.webflux.ui)
- // --- Database & Persistence ---
- api(libs.exposed.core)
- api(libs.exposed.dao)
- api(libs.exposed.jdbc)
- api(libs.exposed.kotlin.datetime)
+ // --- Database & Persistence ---
+ api(libs.exposed.core)
+ api(libs.exposed.dao)
+ api(libs.exposed.jdbc)
+ api(libs.exposed.kotlin.datetime)
- api(libs.flyway.core)
- api(libs.flyway.postgresql)
+ api(libs.flyway.core)
+ api(libs.flyway.postgresql)
- api(libs.postgresql.driver)
- api(libs.hikari.cp)
- api(libs.h2.driver)
- api(libs.lettuce.core)
+ api(libs.postgresql.driver)
+ api(libs.hikari.cp)
+ api(libs.h2.driver)
+ api(libs.lettuce.core)
- // --- Kotlinx Libraries ---
- api(libs.kotlinx.serialization.json)
- api(libs.kotlinx.datetime)
+ // --- Kotlinx Libraries ---
+ api(libs.kotlinx.serialization.json)
+ api(libs.kotlinx.datetime)
- // --- Jackson Modules ---
- api(libs.jackson.module.kotlin)
- api(libs.jackson.datatype.jsr310)
+ // --- Jackson Modules ---
+ api(libs.jackson.module.kotlin)
+ api(libs.jackson.datatype.jsr310)
- // --- Ktor OpenAPI ---
- api("io.ktor:ktor-server-routing-openapi:${libs.versions.ktor.get()}")
-
- // --- Testcontainers ---
- api(libs.testcontainers.core)
- api(libs.testcontainers.junit.jupiter)
- api(libs.testcontainers.postgresql)
- api(libs.testcontainers.keycloak)
- }
+ // --- Ktor OpenAPI ---
+ api(libs.ktor.server.routing.openapi)
+ // --- Testcontainers ---
+ api(libs.testcontainers.core)
+ api(libs.testcontainers.junit.jupiter)
+ api(libs.testcontainers.postgresql)
+ api(libs.testcontainers.keycloak)
+ }
}
// Konfiguration für das Veröffentlichen der BOM (optional, aber gute Praxis).
publishing {
- publications {
- create("maven") {
- from(components["javaPlatform"])
- }
+ publications {
+ create("maven") {
+ from(components["javaPlatform"])
}
+ }
}
diff --git a/platform/platform-dependencies/build.gradle.kts b/platform/platform-dependencies/build.gradle.kts
index eff154d7..e3b13474 100644
--- a/platform/platform-dependencies/build.gradle.kts
+++ b/platform/platform-dependencies/build.gradle.kts
@@ -2,18 +2,18 @@
// Es hat keinen eigenen Code, sondern bündelt nur gemeinsame Laufzeit-Abhängigkeiten,
// die von den meisten JVM-Modulen benötigt werden.
plugins {
- alias(libs.plugins.kotlinJvm)
+ alias(libs.plugins.kotlinJvm)
}
dependencies {
- // Importiert die zentrale BOM, um konsistente Versionen zu gewährleisten.
- api(platform(projects.platform.platformBom))
- // Stellt die wichtigsten Kotlin(x)-Bibliotheken via `api` bereit,
- // damit jedes Modul, das von `platform-dependencies` abhängt, diese automatisch erhält.
- api(libs.kotlinx.coroutines.core)
- api(libs.kotlinx.serialization.json)
- api(libs.kotlinx.datetime)
- api(libs.kotlin.logging.jvm)
- api(libs.kotlinx.coroutines.reactor)
- api(libs.logback.classic)
+ // Importiert die zentrale BOM, um konsistente Versionen zu gewährleisten.
+ api(platform(projects.platform.platformBom))
+ // Stellt die wichtigsten Kotlin(x)-Bibliotheken via `api` bereit,
+ // damit jedes Modul, das von `platform-dependencies` abhängt, diese automatisch erhält.
+ api(libs.kotlinx.coroutines.core)
+ api(libs.kotlinx.serialization.json)
+ api(libs.kotlinx.datetime)
+ api(libs.kotlin.logging.jvm)
+ api(libs.kotlinx.coroutines.reactor)
+ api(libs.logback.classic)
}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 7fbadb9b..0f104cb0 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -156,6 +156,7 @@ include(":frontend:features:pferde-feature")
include(":frontend:features:verein-feature")
include(":frontend:features:turnier-feature")
include(":frontend:features:billing-feature")
+include(":frontend:features:device-initialization")
// --- SHELLS ---
include(":frontend:shells:meldestelle-desktop")