This commit is contained in:
simonaltschaffl@gmail.com 2025-11-08 14:23:50 +01:00
parent d40106445e
commit 013af75cb6
4 changed files with 610 additions and 309 deletions

26
package-lock.json generated
View File

@ -82,6 +82,7 @@
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz",
"integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@ampproject/remapping": "^2.2.0", "@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.26.2", "@babel/code-frame": "^7.26.2",
@ -722,6 +723,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.26.0.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.26.0.tgz",
"integrity": "sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg==", "integrity": "sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@babel/helper-plugin-utils": "^7.25.9" "@babel/helper-plugin-utils": "^7.25.9"
}, },
@ -1586,6 +1588,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz",
"integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-annotate-as-pure": "^7.25.9",
"@babel/helper-module-imports": "^7.25.9", "@babel/helper-module-imports": "^7.25.9",
@ -3106,6 +3109,7 @@
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"license": "MIT", "license": "MIT",
"peer": true,
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
"url": "https://opencollective.com/popperjs" "url": "https://opencollective.com/popperjs"
@ -3542,6 +3546,7 @@
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz",
"integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.10.4", "@babel/code-frame": "^7.10.4",
"@babel/runtime": "^7.12.5", "@babel/runtime": "^7.12.5",
@ -3997,6 +4002,7 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz",
"integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==", "integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
@ -4123,6 +4129,7 @@
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz",
"integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@eslint-community/regexpp": "^4.4.0", "@eslint-community/regexpp": "^4.4.0",
"@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/scope-manager": "5.62.0",
@ -4176,6 +4183,7 @@
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz",
"integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"peer": true,
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/scope-manager": "5.62.0",
"@typescript-eslint/types": "5.62.0", "@typescript-eslint/types": "5.62.0",
@ -4545,6 +4553,7 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
}, },
@ -4631,6 +4640,7 @@
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"fast-deep-equal": "^3.1.1", "fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0", "fast-json-stable-stringify": "^2.0.0",
@ -5547,6 +5557,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"caniuse-lite": "^1.0.30001688", "caniuse-lite": "^1.0.30001688",
"electron-to-chromium": "^1.5.73", "electron-to-chromium": "^1.5.73",
@ -6659,6 +6670,7 @@
"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz",
"integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=0.10" "node": ">=0.10"
} }
@ -7041,6 +7053,7 @@
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
"license": "ISC", "license": "ISC",
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
@ -8006,6 +8019,7 @@
"integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1", "@eslint-community/regexpp": "^4.6.1",
@ -10984,6 +10998,7 @@
"resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz",
"integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@jest/core": "^27.5.1", "@jest/core": "^27.5.1",
"import-local": "^3.0.2", "import-local": "^3.0.2",
@ -14192,6 +14207,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"nanoid": "^3.3.8", "nanoid": "^3.3.8",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
@ -15379,6 +15395,7 @@
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"cssesc": "^3.0.0", "cssesc": "^3.0.0",
"util-deprecate": "^1.0.2" "util-deprecate": "^1.0.2"
@ -15776,6 +15793,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -15944,6 +15962,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"scheduler": "^0.26.0" "scheduler": "^0.26.0"
}, },
@ -16022,6 +16041,7 @@
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
"integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -16647,6 +16667,7 @@
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz",
"integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"rollup": "dist/bin/rollup" "rollup": "dist/bin/rollup"
}, },
@ -16907,6 +16928,7 @@
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1", "fast-uri": "^3.0.1",
@ -18587,6 +18609,7 @@
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
"license": "(MIT OR CC0-1.0)", "license": "(MIT OR CC0-1.0)",
"peer": true,
"engines": { "engines": {
"node": ">=10" "node": ">=10"
}, },
@ -19212,6 +19235,7 @@
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.6.tgz", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.6.tgz",
"integrity": "sha512-TJOLrJ6oeccsGWPl7ujCYuc0pIq2cNsuD6GZDma8i5o5Npvcco/z+NKvZSFsP0/x6SShVb0+X2JK/JHUjKY9dQ==", "integrity": "sha512-TJOLrJ6oeccsGWPl7ujCYuc0pIq2cNsuD6GZDma8i5o5Npvcco/z+NKvZSFsP0/x6SShVb0+X2JK/JHUjKY9dQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@types/eslint-scope": "^3.7.7", "@types/eslint-scope": "^3.7.7",
"@types/estree": "^1.0.6", "@types/estree": "^1.0.6",
@ -19281,6 +19305,7 @@
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz",
"integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@types/bonjour": "^3.5.9", "@types/bonjour": "^3.5.9",
"@types/connect-history-api-fallback": "^1.3.5", "@types/connect-history-api-fallback": "^1.3.5",
@ -19693,6 +19718,7 @@
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1", "fast-uri": "^3.0.1",

View File

@ -12,7 +12,7 @@ import ServerPage from "./projects/server";
import LanguagestudyPage from "./projects/languageStudy"; import LanguagestudyPage from "./projects/languageStudy";
import OnlineCasinoPage from "./finalprojects/onlineCasino"; import OnlineCasinoPage from "./finalprojects/onlineCasino";
import WohnungUndFahrgemeinschaftenPage from "./finalprojects/wohnungUndFahrgemeinschaften"; import WohnungUndFahrgemeinschaftenPage from "./finalprojects/wohnungUndFahrgemeinschaften";
import KrimiDinnerPage from "./finalprojects/krimiDinner"; import KrimiDinnerPage from "./projects/krimiDinner";
function Home() { function Home() {
return <HomePage />; return <HomePage />;
@ -75,7 +75,7 @@ function App() {
<Route path="/projects/server" element={<Server />} /> <Route path="/projects/server" element={<Server />} />
<Route path="/finalprojects/onlineCasino" element={<OnlineCasino />} /> <Route path="/finalprojects/onlineCasino" element={<OnlineCasino />} />
<Route path="/finalprojects/wufg" element={<WohnungUndFahrgemeinschaften />} /> <Route path="/finalprojects/wufg" element={<WohnungUndFahrgemeinschaften />} />
<Route path="/finalprojects/krimidinner" element={<KrimiDinner />} /> <Route path="/projects/crime-dinner" element={<KrimiDinner />} />
</Routes> </Routes>
</div> </div>
<FooterSection /> <FooterSection />

View File

@ -1,307 +0,0 @@
import React, { useEffect } from "react";
import { Container, Row, Col, Card, Badge, Table } from "react-bootstrap";
import mermaid from "mermaid";
function KrimiDinner() {
useEffect(() => {
mermaid.initialize({ startOnLoad: false, theme: 'neutral' });
mermaid.contentLoaded();
}, []);
return (
<div className="project-page">
<Container>
<header className="text-center border-b pb-4 mb-5">
<h1 className="project-title">Projekt-Blaupause: KI-Krimi-Dinner "Projekt Chimera"</h1>
<div className="mt-4 d-flex justify-content-center flex-wrap">
<div className="project-meta-item">
<Badge bg="secondary" className="text-uppercase">Version</Badge>
<span className="meta-value">3.0 (Final & Complete UI)</span>
</div>
<div className="project-meta-item">
<Badge bg="secondary" className="text-uppercase">Autor</Badge>
<span className="meta-value">D. Schuhbaum & S. Altschäffl</span>
</div>
<div className="project-meta-item">
<Badge bg="secondary" className="text-uppercase">Datum</Badge>
<span className="meta-value">03.10.2025</span>
</div>
</div>
</header>
<section className="mb-5">
<h2>1. Projektübersicht</h2>
<p>
"Projekt Chimera" ist ein hybrides Krimi-Dinner-Erlebnis, das
physische soziale Interaktion mit einer digitalen App-Komponente
verbindet. Das Alleinstellungsmerkmal ist der Einsatz einer
zweistufigen KI-Architektur, die nicht nur einzigartige narrative
Szenarien generiert, sondern diese auch proaktiv auf logische
Konsistenz validiert und korrigiert.
</p>
<p>
Die Architektur basiert auf einer klaren Trennung:{" "}
<strong>Supabase</strong> dient als dedizierter Service für die
Benutzerauthentifizierung, während alle Spieldaten (Szenarien,
Spieler, Spielstände) in einer{" "}
<strong>separaten, dedizierten PostgreSQL-Datenbank</strong>{" "}
gehostet werden. Ein zentrales <strong>FastAPI-Backend</strong>{" "}
fungiert als sichere Schnittstelle zwischen den Clients und
beiden Diensten.
</p>
</section>
<section className="mb-5">
<h2>2. Narratives Design & Validierung</h2>
<p>
Die Generierung jedes Spiels folgt einem strengen, zweistufigen
Prozess, um die narrative Qualität und logische Integrität zu
gewährleisten.
</p>
<Row className="mt-4">
<Col md={6}>
<Card className="h-100">
<Card.Body>
<Card.Title as="h3">Schritt 1: Kreative Generierung (Der "Autor")</Card.Title>
<Card.Text>
In dieser Phase agiert die KI als kreativer Autor. Sie erhält einen umfassenden Prompt, der die gewünschten Rahmenbedingungen vorgibt.
</Card.Text>
<ul>
<li>Hintergrundgeschichte</li>
<li>Charaktere</li>
<li>Geheimnisse & Ziele</li>
<li>Der Mord & lückenloser Zeitstrahl</li>
<li>Beziehungsgeflecht</li>
<li>Hinweise</li>
</ul>
</Card.Body>
</Card>
</Col>
<Col md={6}>
<Card className="h-100">
<Card.Body>
<Card.Title as="h3">Schritt 2: Logische Validierung & Korrektur (Der "Logik-Lektor")</Card.Title>
<Card.Text>
Der rohe Output wird an eine zweite KI-Instanz übergeben, die als akribischer Logik-Lektor agiert und das Szenario auf Fehler überprüft.
</Card.Text>
<ul>
<li>Widersprüche in Zeitlinien</li>
<li>Motiv-Konsistenz</li>
<li>Hinweis-Validität</li>
<li>Charakter-Konsistenz</li>
<li>Regelkonformität</li>
</ul>
</Card.Body>
</Card>
</Col>
</Row>
</section>
<section className="mb-5">
<h2>3. Technische Architektur</h2>
<p>
Die Architektur ist auf maximale Kontrolle und Skalierbarkeit ausgelegt. Die React Native-Clients kommunizieren ausschließlich mit dem FastAPI-Backend. Echtzeit-Updates werden über eine WebSocket-Verbindung gepusht.
</p>
<div className="text-center bg-light p-3 rounded">
<pre className="mermaid">
{`
graph TD
subgraph "Spieler-Geräte"
P1["React Native Apps"]
end
subgraph "Authentifizierung"
Auth["Supabase Auth"]
end
subgraph "Sicheres Backend"
FastAPI["FastAPI Server <br> REST & WebSockets"]
end
subgraph "Spieldaten-Bank"
PG_DB["PostgreSQL DB"]
end
subgraph "Externe Dienste"
OAI["OpenAI API"]
end
P1 -- "1. Login" --> Auth
P1 -- "2. API Calls (mit Token)" --> FastAPI
FastAPI -- "3. KI Call" --> OAI
FastAPI -- "4. DB-Zugriff" --> PG_DB
FastAPI -- "5. WebSocket Updates" --> P1
`}
</pre>
</div>
<h3 className="mt-4">Datenfluss-Beschreibung:</h3>
<ol>
<li><strong>Authentifizierung:</strong> Benutzer authentifiziert sich bei Supabase Auth und erhält ein JWT.</li>
<li><strong>API-Anfragen:</strong> Die App sendet Anfragen mit dem JWT an den FastAPI-Server.</li>
<li><strong>KI-Generierung:</strong> FastAPI ruft die OpenAI-API für die Spielerstellung auf.</li>
<li><strong>Datenbank-Interaktion:</strong> FastAPI ist die einzige Komponente, die mit der PostgreSQL-DB kommuniziert.</li>
<li><strong>Echtzeit-Updates:</strong> Clients bauen eine WebSocket-Verbindung zum FastAPI-Server auf.</li>
</ol>
</section>
<section className="mb-5">
<h2>4. User Interface (UI) Konzept</h2>
<p>Die UI-Konzepte werden als klare, moderne Wireframes visualisiert, um den gesamten App-Flow darzustellen.</p>
<Row>
{[
{title: "1. Login", content: "Standard-Login-Formular mit E-Mail und Passwort."},
{title: "2. Registrierung", content: "Formular zur Erstellung eines neuen Kontos."},
{title: "3. Home Dashboard", content: "Übersicht der Spiele des Nutzers und Optionen, ein neues Spiel zu starten oder einem beizutreten."},
{title: "4. Spiel erstellen", content: "Formular zur Definition des Szenarios und der Rundenanzahl."},
{title: "5. Host-Lobby", content: "Anzeige des Spiel-Codes und der beigetretenen Spieler."},
{title: "6. Spieler-Ansicht (Rolle)", content: "Anzeige der zugewiesenen Rolle, Ziele und Geheimnisse."},
{title: "7. Spieler-Ansicht (Hinweise)", content: "Darstellung der pro Runde freigeschalteten Hinweise."},
{title: "8. Auflösung", content: "Enthüllung des Mörders und des Abstimmungsergebnisses."},
{title: "9. Host Notfall-Dashboard", content: "Steuerungselemente für den Host, um das Spiel zu managen."}
].map(item => (
<Col md={4} className="mb-4" key={item.title}>
<Card>
<Card.Header as="h5" className="text-center">{item.title}</Card.Header>
<Card.Body>
<Card.Text>{item.content}</Card.Text>
</Card.Body>
</Card>
</Col>
))}
</Row>
</section>
<section className="mb-5">
<h2>5. API-Endpunkte & Datenbank-Interaktion</h2>
<Row>
<Col md={6}>
<h3>FastAPI-Backend</h3>
<ul className="list-group">
<li className="list-group-item"><code>POST /game/create</code></li>
<li className="list-group-item"><code>POST /game/{'{game_code}'}/join</code></li>
<li className="list-group-item"><code>GET /game/{'{game_code}'}/state</code></li>
<li className="list-group-item"><code>POST /game/{'{game_code}'}/vote</code></li>
<li className="list-group-item"><code>GET /game/{'{game_code}'}/character</code></li>
<li className="list-group-item"><code>/ws/{'{game_code}'}</code></li>
</ul>
</Col>
<Col md={6}>
<h3>Client-Interaktionen</h3>
<ul>
<li><strong>Supabase Auth (`supabase-js`):</strong> Nur für Login, Registrierung und Auth-Zustand.</li>
<li><strong>API-Client (z.B. Axios):</strong> Für alle HTTP-Anfragen an das FastAPI-Backend.</li>
<li><strong>WebSocket-Client:</strong> Für Echtzeit-Verbindung zum Backend.</li>
</ul>
</Col>
</Row>
</section>
<section className="mb-5">
<h2>6. Datenmodelle</h2>
<p>Die dedizierte PostgreSQL-Datenbank enthält die gesamte Spiellogik.</p>
<h3 className="mt-4">Tabelle: `games`</h3>
<Table striped bordered hover responsive>
<thead>
<tr><th>Spalte</th><th>Datentyp</th><th>Beschreibung</th></tr>
</thead>
<tbody>
<tr><td>id</td><td>uuid</td><td>Eindeutige ID für ein Spiel.</td></tr>
<tr><td>host_id</td><td>uuid</td><td>Referenz auf die User-ID des Hosts.</td></tr>
<tr><td>game_code</td><td>varchar</td><td>Kurzer Code zum Beitreten.</td></tr>
<tr><td>status</td><td>varchar</td><td>setup, lobby, active, finished.</td></tr>
<tr><td>game_data</td><td>jsonb</td><td>Das komplette KI-generierte Szenario.</td></tr>
</tbody>
</Table>
<h3 className="mt-4">Tabelle: `players`</h3>
<Table striped bordered hover responsive>
<thead>
<tr><th>Spalte</th><th>Datentyp</th><th>Beschreibung</th></tr>
</thead>
<tbody>
<tr><td>id</td><td>uuid</td><td>Eindeutiger Eintrag für einen Spieler.</td></tr>
<tr><td>game_id</td><td>uuid</td><td>Verweist auf das zugehörige Spiel.</td></tr>
<tr><td>player_id</td><td>uuid</td><td>Referenz auf die User-ID aus Supabase.</td></tr>
<tr><td>player_name</td><td>varchar</td><td>Der im Spiel angezeigte Name.</td></tr>
<tr><td>character_id</td><td>varchar</td><td>Verknüpft den Spieler mit seiner Rolle.</td></tr>
</tbody>
</Table>
</section>
<section className="mb-5">
<h2>7. Prompt-Engineering-Strategie</h2>
<Row>
<Col md={6}>
<Card className="h-100">
<Card.Body>
<Card.Title>Prompt 1: Der kreative Autor</Card.Title>
<Card.Subtitle className="mb-2 text-muted">System-Prompt:</Card.Subtitle>
<Card.Text as="div"><pre className="prompt-box">Du bist ein preisgekrönter Autor von Kriminalromanen...</pre></Card.Text>
<Card.Subtitle className="mb-2 mt-3 text-muted">User-Prompt:</Card.Subtitle>
<Card.Text as="div"><pre className="prompt-box">Erstelle ein Krimi-Dinner-Szenario für 5 Spieler...</pre></Card.Text>
</Card.Body>
</Card>
</Col>
<Col md={6}>
<Card className="h-100">
<Card.Body>
<Card.Title>Prompt 2: Der Logik-Lektor</Card.Title>
<Card.Subtitle className="mb-2 text-muted">System-Prompt:</Card.Subtitle>
<Card.Text as="div"><pre className="prompt-box">Du bist ein extrem detailorientierter und logisch denkender Lektor...</pre></Card.Text>
<Card.Subtitle className="mb-2 mt-3 text-muted">User-Prompt:</Card.Subtitle>
<Card.Text as="div"><pre className="prompt-box">Hier ist ein JSON-Objekt. Bitte agiere als Logik-Lektor...</pre></Card.Text>
</Card.Body>
</Card>
</Col>
</Row>
</section>
<section className="mb-5">
<h2>8. User Journey & Spielablauf</h2>
<ol>
<li><strong>Phase 0: Generierung & Vorbereitung:</strong> Host generiert Spiel und erhält Einladungscode.</li>
<li><strong>Phase 1: Lobby:</strong> Spieler treten bei, Host startet das Spiel.</li>
<li><strong>Phase 2: Rollenverteilung:</strong> Backend weist Rollen zu und sendet geheime Infos.</li>
<li><strong>Phase 3: Spielrunden:</strong> System gibt pro Runde neue Hinweise frei.</li>
<li><strong>Phase 4: Abstimmung & Auflösung:</strong> Abstimmung wird freigeschaltet, Mörder wird enthüllt.</li>
</ol>
</section>
<section className="mb-5">
<h2>9. Geschäftsmodell & Monetarisierung</h2>
<Row>
<Col md={6}>
<div className="p-3 bg-light rounded h-100">
<h3>Abonnement-Modell</h3>
<p>Nutzer zahlen einen monatlichen oder jährlichen Beitrag für ein festes Kontingent an Spielen.</p>
</div>
</Col>
<Col md={6}>
<div className="p-3 bg-light rounded h-100">
<h3>Einmalkauf (Pay-per-Game)</h3>
<p>Alternativ können Nutzer einzelne Spiele per Einmalkauf freischalten.</p>
</div>
</Col>
</Row>
</section>
<section className="mb-5">
<h2>10. Skalierbarkeit, Kosten & Fehlerbehandlung</h2>
<ul>
<li><strong>Kostenkontrolle (OpenAI):</strong> Rate-Limiting auf dem Endpunkt zur Spielerstellung.</li>
<li><strong>Fehlerbehandlung bei Generierung:</strong> Backend unternimmt automatische Wiederholungsversuche.</li>
<li><strong>Nahtloses Wiederverbinden:</strong> Spieler können jederzeit wieder in ein laufendes Spiel einsteigen.</li>
<li><strong>Notfall-Dashboard (Host):</strong> Spoiler-freies Admin-Panel für den Host zur Spielsteuerung.</li>
</ul>
</section>
<section className="mb-5">
<h2>11. Rechtliches & Datenschutz</h2>
<ul>
<li><strong>DSGVO-konforme Datenschutzerklärung</strong></li>
<li><strong>Impressum</strong></li>
<li><strong>Möglichkeit zur Löschung des Accounts</strong></li>
</ul>
</section>
</Container>
</div>
);
}
export default KrimiDinner;

View File

@ -0,0 +1,582 @@
import React, { useEffect } from "react";
import { Container, Row, Col, Card, Badge, Table, Button, Form, Nav } from "react-bootstrap";
import mermaid from "mermaid";
const Wireframe = ({ title, children }) => (
<Col md={6} lg={4} className="mb-4">
<Card className="h-100 shadow-sm">
<Card.Header as="h5" className="text-center bg-light">{title}</Card.Header>
<Card.Body className="d-flex justify-content-center align-items-center p-2">
<div className="bg-white w-100 h-100 border rounded-3 shadow-inner p-3 d-flex flex-column" style={{ aspectRatio: '9 / 19.5' }}>
{children}
</div>
</Card.Body>
</Card>
</Col>
);
function KrimiDinner() {
useEffect(() => {
mermaid.initialize({ startOnLoad: false, theme: 'neutral' });
try {
mermaid.run();
} catch (e) {
console.error("Mermaid run error:", e);
}
}, []);
return (
<div className="project-page">
<Container>
<header className="text-center border-bottom pb-4 mb-5">
<h1 className="project-title">Projekt-Blaupause: KI-Krimi-Dinner "Projekt Chimera"</h1>
<div className="mt-4 d-flex justify-content-center flex-wrap">
<div className="project-meta-item mx-2">
<Badge bg="secondary" className="text-uppercase">Version</Badge>
<span className="meta-value ms-2">3.0 (Complete UI)</span>
</div>
<div className="project-meta-item mx-2">
<Badge bg="secondary" className="text-uppercase">Autor</Badge>
<span className="meta-value ms-2">D. Schuhbaum & S. Altschäffl</span>
</div>
<div className="project-meta-item mx-2">
<Badge bg="secondary" className="text-uppercase">Datum</Badge>
<span className="meta-value ms-2">03.10.2025</span>
</div>
</div>
</header>
<section className="mb-5">
<h2>1. Projektübersicht</h2>
<p>
"Projekt Chimera" ist ein hybrides Krimi-Dinner-Erlebnis, das
physische soziale Interaktion mit einer digitalen App-Komponente
verbindet. Das Alleinstellungsmerkmal ist der Einsatz einer
zweistufigen KI-Architektur, die nicht nur einzigartige narrative
Szenarien generiert, sondern diese auch proaktiv auf logische
Konsistenz validiert und korrigiert.
</p>
<p>
Die Architektur basiert auf einer klaren Trennung:{" "}
<strong>Supabase</strong> dient als dedizierter Service für die
Benutzerauthentifizierung, während alle Spieldaten (Szenarien,
Spieler, Spielstände) in einer{" "}
<strong>separaten, dedizierten PostgreSQL-Datenbank</strong>{" "}
gehostet werden. Ein zentrales <strong>FastAPI-Backend</strong>{" "}
fungiert als sichere Schnittstelle zwischen den Clients und
beiden Diensten.
</p>
</section>
<section className="mb-5">
<h2>2. Narratives Design & Validierung</h2>
<p>
Die Generierung jedes Spiels folgt einem strengen, zweistufigen
Prozess, um die narrative Qualität und logische Integrität zu
gewährleisten.
</p>
<Row className="mt-4">
<Col md={6}>
<Card className="h-100">
<Card.Body>
<Card.Title as="h3">Schritt 1: Kreative Generierung (Der "Autor")</Card.Title>
<ul>
<li>Hintergrundgeschichte</li>
<li>Charaktere</li>
<li>Geheimnisse & Ziele</li>
<li>Der Mord & lückenloser Zeitstrahl</li>
<li>Beziehungsgeflecht</li>
<li>Hinweise</li>
</ul>
</Card.Body>
</Card>
</Col>
<Col md={6}>
<Card className="h-100">
<Card.Body>
<Card.Title as="h3">Schritt 2: Logische Validierung & Korrektur (Der "Logik-Lektor")</Card.Title>
<ul>
<li>Widersprüche in Zeitlinien</li>
<li>Motiv-Konsistenz</li>
<li>Hinweis-Validität</li>
<li>Charakter-Konsistenz</li>
<li>Regelkonformität</li>
</ul>
</Card.Body>
</Card>
</Col>
</Row>
</section>
<section className="mb-5">
<h2>3. Technische Architektur</h2>
<p>
Die Architektur ist auf maximale Kontrolle und Skalierbarkeit ausgelegt. Die React Native-Clients kommunizieren ausschließlich mit dem FastAPI-Backend. Echtzeit-Updates werden über eine WebSocket-Verbindung gepusht.
</p>
<div className="text-center bg-light p-3 rounded">
<pre className="mermaid">
{`
graph TD
subgraph "Spieler-Geräte"
P1["React Native Apps"]
end
subgraph "Authentifizierung"
Auth["Supabase Auth"]
end
subgraph "Sicheres Backend"
FastAPI["FastAPI Server <br> REST & WebSockets"]
end
subgraph "Spieldaten-Bank"
PG_DB["PostgreSQL DB"]
end
subgraph "Externe Dienste"
OAI["OpenAI API"]
end
%% Verbindungen
P1 -- "1. Login" --> Auth
P1 -- "2. API Calls (mit Token)" --> FastAPI
FastAPI -- "3. KI Call" --> OAI
FastAPI -- "4. DB-Zugriff" --> PG_DB
FastAPI -- "5. WebSocket Updates" --> P1
`}
</pre>
</div>
<h3 className="mt-4">Datenfluss-Beschreibung:</h3>
<ol>
<li><strong>Authentifizierung:</strong> Benutzer authentifiziert sich bei Supabase Auth und erhält ein JWT.</li>
<li><strong>API-Anfragen:</strong> Die App sendet Anfragen mit dem JWT an den FastAPI-Server.</li>
<li><strong>KI-Generierung:</strong> FastAPI ruft die OpenAI-API für die Spielerstellung auf.</li>
<li><strong>Datenbank-Interaktion:</strong> FastAPI ist die einzige Komponente, die mit der PostgreSQL-DB kommuniziert.</li>
<li><strong>Echtzeit-Updates:</strong> Clients bauen eine WebSocket-Verbindung zum FastAPI-Server auf.</li>
</ol>
</section>
<section className="mb-5">
<h2>4. User Interface (UI) Konzept</h2>
<p>Die UI-Konzepte werden als klare, moderne Wireframes visualisiert, um den gesamten App-Flow darzustellen.</p>
<Row>
<Wireframe title="1. Login">
<div className="text-center pt-4 pb-3">
<h4 className="fw-bold fs-5">PROJEKT CHIMERA</h4>
</div>
<div className="px-3 flex-grow-1 d-flex flex-column">
<Form.Control type="text" placeholder="E-Mail-Adresse" className="mb-2" />
<Form.Control type="password" placeholder="Passwort" className="mb-3" />
<Button variant="primary" className="w-100 fw-semibold">Anmelden</Button>
<p className="text-center small text-muted mt-2">Noch kein Konto? <a href="#">Jetzt registrieren</a></p>
</div>
</Wireframe>
<Wireframe title="2. Registrierung">
<div className="text-center pt-4 pb-3">
<h4 className="fw-bold fs-5">Konto erstellen</h4>
</div>
<div className="px-3 flex-grow-1 d-flex flex-column">
<Form.Control type="text" placeholder="E-Mail-Adresse" className="mb-2" />
<Form.Control type="password" placeholder="Passwort" className="mb-2" />
<Form.Control type="password" placeholder="Passwort bestätigen" className="mb-3" />
<Button variant="primary" className="w-100 fw-semibold">Registrieren</Button>
<p className="text-center small text-muted mt-2">Bereits ein Konto? <a href="#">Zum Login</a></p>
</div>
</Wireframe>
<Wireframe title="3. Home Dashboard">
<div className="p-2 d-flex justify-content-between align-items-center border-bottom">
<h4 className="fw-bold fs-6 mb-0">Deine Spiele</h4>
<Button variant="link" size="sm">Logout</Button>
</div>
<div className="p-2 flex-grow-1 overflow-auto">
<Card className="mb-2">
<Card.Body className="p-2">
<p className="fw-bold mb-1">Tech-Konferenz 2035</p>
<p className="small text-muted mb-2">Status: <span className="fw-semibold text-success">Aktiv (Runde 2/3)</span></p>
<Button variant="primary" size="sm" className="w-100">Weiterspielen</Button>
</Card.Body>
</Card>
<Card>
<Card.Body className="p-2">
<p className="fw-bold mb-1">Villa Vendetta</p>
<p className="small text-muted mb-2">Status: <span className="fw-semibold text-secondary">Lobby (0 Spieler)</span></p>
<Button variant="primary" size="sm" className="w-100">Zur Lobby</Button>
</Card.Body>
</Card>
</div>
<div className="p-2 border-top">
<Row>
<Col><Button variant="success" className="w-100">Neues Spiel</Button></Col>
<Col><Button variant="secondary" className="w-100">Beitreten</Button></Col>
</Row>
</div>
</Wireframe>
<Wireframe title="4. Spiel erstellen (Host)">
<div className="text-center pt-3 pb-2 border-bottom">
<h4 className="fw-bold fs-6">Neues Spiel erstellen</h4>
</div>
<div className="px-3 py-3 flex-grow-1">
<Form.Label className="fw-semibold small">Szenario-Beschreibung</Form.Label>
<Form.Control as="textarea" placeholder="z.B. Ein Mord auf einer Tech-Konferenz im Jahr 2035..." rows={3} className="small mb-3" />
<Form.Label className="fw-semibold small">Rundenanzahl</Form.Label>
<div className="d-flex align-items-center">
<Form.Range min="2" max="5" defaultValue="3" className="w-100" />
<span className="fw-bold fs-5 ms-3">3</span>
</div>
<p className="text-muted small mt-3">Die Spieleranzahl ist flexibel (mind. 3 Spieler) und wird in der Lobby bestimmt.</p>
</div>
<div className="px-3 pb-3">
<Button variant="primary" className="w-100 fw-semibold">Szenario generieren & Lobby öffnen</Button>
</div>
</Wireframe>
<Wireframe title="5. Spiel beitreten (Spieler)">
<div className="text-center pt-4 pb-3">
<h4 className="fw-bold fs-5">Spiel beitreten</h4>
</div>
<div className="px-3 flex-grow-1 d-flex flex-column justify-content-center">
<Form.Label htmlFor="game-code-input" className="fw-semibold small text-center">Gib den Spiel-Code ein</Form.Label>
<Form.Control id="game-code-input" type="text" placeholder="ABC-123" className="mb-3 text-center fs-4" style={{letterSpacing: '0.2rem'}} />
<Button variant="primary" className="w-100 fw-semibold">Lobby beitreten</Button>
</div>
</Wireframe>
<Wireframe title="6. Host-Lobby">
<div className="text-center pt-3 pb-2 border-bottom">
<p className="small text-muted mb-0">Spiel-Lobby</p>
<h4 className="fw-bold fs-6">Tech-Konferenz 2035</h4>
</div>
<div className="px-3 py-3 text-center flex-grow-1">
<p className="small text-muted">Teile diesen Code mit deinen Mitspielern:</p>
<div className="my-2 p-3 border border-2 border-dashed rounded-3 text-primary">
<span className="fw-bold fs-3" style={{letterSpacing: '0.2rem'}}>GHY-781</span>
</div>
<h5 className="fw-semibold text-start small mt-3 mb-2">Beigetretene Spieler (3)</h5>
<ul className="list-group text-start">
<li className="list-group-item d-flex justify-content-between align-items-center p-2">Maria <Button variant="outline-danger" size="sm">X</Button></li>
<li className="list-group-item d-flex justify-content-between align-items-center p-2">Jonas <Button variant="outline-danger" size="sm">X</Button></li>
<li className="list-group-item d-flex justify-content-between align-items-center p-2">Sandra <Button variant="outline-danger" size="sm">X</Button></li>
</ul>
</div>
<div className="px-3 pb-3">
<Button variant="outline-danger" className="w-100 mb-2 btn-sm">Szenario verwerfen & Neu generieren</Button>
<Button variant="success" className="w-100 fw-semibold">Spiel starten (3 Spieler)</Button>
</div>
</Wireframe>
<Wireframe title="7. Spieler-Ansicht (Rolle)">
<div className="p-2 text-center border-bottom">
<p className="small text-muted mb-0">Deine Rolle</p>
<h4 className="fw-bold fs-6">Dr. Aris Thorne</h4>
</div>
<Nav variant="tabs" defaultActiveKey="#rolle" justify>
<Nav.Item><Nav.Link href="#rolle" className="pb-1 pt-2 small">Rolle</Nav.Link></Nav.Item>
<Nav.Item><Nav.Link href="#hinweise" className="pb-1 pt-2 small text-muted">Hinweise</Nav.Link></Nav.Item>
<Nav.Item><Nav.Link href="#charaktere" className="pb-1 pt-2 small text-muted">Charaktere</Nav.Link></Nav.Item>
<Nav.Item><Nav.Link href="#zeitstrahl" className="pb-1 pt-2 small text-muted">Zeitstrahl</Nav.Link></Nav.Item>
</Nav>
<div className="p-3 flex-grow-1 overflow-auto small">
<h5 className="fw-bold small">Deine Ziele:</h5>
<ul className="list-unstyled small ps-3">
<li>- Finde heraus, wer deine Forschung sabotiert hat.</li>
<li>- Verhindere, dass dein Geheimnis ans Licht kommt.</li>
</ul>
<h5 className="fw-bold small pt-2">Deine Geheimnisse:</h5>
<p className="small">Du hast vor einem Monat bei einem nicht genehmigten Experiment die Stromversorgung der Station überlastet, was zu einem kritischen Ausfall führte. Nur das Opfer wusste davon.</p>
</div>
</Wireframe>
<Wireframe title="8. Spieler-Ansicht (Hinweise)">
<div className="p-2 text-center border-bottom">
<p className="small text-muted mb-0">Deine Rolle</p>
<h4 className="fw-bold fs-6">Dr. Aris Thorne</h4>
</div>
<Nav variant="tabs" defaultActiveKey="#hinweise" justify>
<Nav.Item><Nav.Link href="#rolle" className="pb-1 pt-2 small text-muted">Rolle</Nav.Link></Nav.Item>
<Nav.Item><Nav.Link href="#hinweise" className="pb-1 pt-2 small position-relative">Hinweise<Badge pill bg="danger" className="position-absolute top-0 start-100 translate-middle" style={{fontSize: '0.5rem'}}>&nbsp;</Badge></Nav.Link></Nav.Item>
<Nav.Item><Nav.Link href="#charaktere" className="pb-1 pt-2 small text-muted">Charaktere</Nav.Link></Nav.Item>
<Nav.Item><Nav.Link href="#zeitstrahl" className="pb-1 pt-2 small text-muted">Zeitstrahl</Nav.Link></Nav.Item>
</Nav>
<div className="p-3 flex-grow-1 overflow-auto small space-y-4">
<div>
<h5 className="fw-bold">Runde 2 (18:00 - 19:00 Uhr)</h5>
<div className="alert alert-warning p-2">
<p className="fw-semibold small text-warning-emphasis mb-0">HINWEIS #3</p>
<p className="small text-warning-emphasis mt-1">Ein zerrissener Brief wurde gefunden. Die Worte "Schulden" und "letzte Warnung" sind lesbar.</p>
</div>
</div>
<div className="opacity-75">
<h5 className="fw-bold">Runde 1 (16:00 - 18:00 Uhr)</h5>
<div className="alert alert-secondary p-2">
<p className="fw-semibold small mb-0">HINWEIS #1</p>
<p className="small mt-1">Das Opfer wurde mit einem schweren Gegenstand erschlagen.</p>
</div>
</div>
</div>
</Wireframe>
<Wireframe title="9. Spieler-Ansicht (Charaktere)">
<div className="p-2 text-center border-bottom">
<p className="small text-muted mb-0">Deine Rolle</p>
<h4 className="fw-bold fs-6">Dr. Aris Thorne</h4>
</div>
<Nav variant="tabs" defaultActiveKey="#charaktere" justify>
<Nav.Item><Nav.Link href="#rolle" className="pb-1 pt-2 small text-muted">Rolle</Nav.Link></Nav.Item>
<Nav.Item><Nav.Link href="#hinweise" className="pb-1 pt-2 small text-muted">Hinweise</Nav.Link></Nav.Item>
<Nav.Item><Nav.Link href="#charaktere" className="pb-1 pt-2 small">Charaktere</Nav.Link></Nav.Item>
<Nav.Item><Nav.Link href="#zeitstrahl" className="pb-1 pt-2 small text-muted">Zeitstrahl</Nav.Link></Nav.Item>
</Nav>
<div className="p-3 flex-grow-1 overflow-auto small">
<div className="list-group">
<div className="list-group-item list-group-item-action p-2">
<p className="fw-bold mb-0">Evelyn Reed</p>
<p className="small text-muted mb-0">Investorin</p>
</div>
<div className="list-group-item list-group-item-action p-2">
<p className="fw-bold mb-0">Ivan Petrov</p>
<p className="small text-muted mb-0">Sicherheitschef</p>
</div>
</div>
</div>
</Wireframe>
<Wireframe title="10. Spieler-Ansicht (Zeitstrahl)">
<div className="p-2 text-center border-bottom">
<p className="small text-muted mb-0">Deine Rolle</p>
<h4 className="fw-bold fs-6">Dr. Aris Thorne</h4>
</div>
<Nav variant="tabs" defaultActiveKey="#zeitstrahl" justify>
<Nav.Item><Nav.Link href="#rolle" className="pb-1 pt-2 small text-muted">Rolle</Nav.Link></Nav.Item>
<Nav.Item><Nav.Link href="#hinweise" className="pb-1 pt-2 small text-muted">Hinweise</Nav.Link></Nav.Item>
<Nav.Item><Nav.Link href="#charaktere" className="pb-1 pt-2 small text-muted">Charaktere</Nav.Link></Nav.Item>
<Nav.Item><Nav.Link href="#zeitstrahl" className="pb-1 pt-2 small">Zeitstrahl</Nav.Link></Nav.Item>
</Nav>
<div className="p-3 flex-grow-1 overflow-auto small">
<ul className="list-unstyled">
<li className="mb-3">
<p className="fw-bold mb-0">16:00 Uhr</p>
<p className="small text-muted">Beginn der Konferenz-Keynote.</p>
</li>
<li className="mb-3">
<p className="fw-bold mb-0">17:15 Uhr</p>
<p className="small text-muted">Opfer verlässt den Saal für ein Telefonat.</p>
</li>
<li className="mb-3">
<p className="fw-bold mb-0 text-danger">17:30 Uhr - Mordzeitpunkt</p>
<p className="small text-muted">Stromausfall im Westflügel.</p>
</li>
<li className="mb-3">
<p className="fw-bold mb-0">17:45 Uhr</p>
<p className="small text-muted">Leiche wird im Serverraum gefunden.</p>
</li>
</ul>
</div>
</Wireframe>
<Wireframe title="11. Abstimmung">
<div className="text-center pt-3 pb-2 border-bottom">
<p className="small text-muted mb-0">Runde 3/3</p>
<h4 className="fw-bold fs-6">Wer ist der Mörder?</h4>
</div>
<div className="px-3 py-3 flex-grow-1 overflow-auto">
<p className="small text-center text-muted">Wähle den Spieler aus, den du für den Täter hältst.</p>
<div className="d-grid gap-2 mt-3">
<Button variant="outline-secondary" className="p-3 text-start" disabled>
<span className="fw-bold">Dr. Aris Thorne (Du)</span><br/>
<span className="small text-muted">KI-Ethiker</span>
</Button>
<Button variant="outline-secondary" className="p-3 text-start">
<span className="fw-bold">Evelyn Reed</span><br/>
<span className="small text-muted">Investorin</span>
</Button>
<Button variant="outline-secondary" className="p-3 text-start">
<span className="fw-bold">Ivan Petrov</span><br/>
<span className="small text-muted">Sicherheitschef</span>
</Button>
</div>
</div>
<div className="px-3 pb-3">
<Button variant="primary" className="w-100 fw-semibold" disabled>Stimme abgeben</Button>
</div>
</Wireframe>
<Wireframe title="12. Auflösung">
<div className="text-center pt-3 pb-2 border-bottom bg-danger-subtle">
<h4 className="fw-bold fs-6 text-danger-emphasis">Der Mörder war...</h4>
<p className="fs-5 fw-bold">Ivan Petrov!</p>
</div>
<div className="p-3 flex-grow-1 overflow-auto small">
<p>Sein Motiv war Rache, da das Opfer ihn erpresst hat. Die Mordwaffe war ein schwerer Schraubenschlüssel aus dem Werkzeugraum.</p>
<h5 className="fw-semibold pt-2 small">Abstimmungsergebnis</h5>
<ul className="list-unstyled small">
<li>Du hast für <strong>Evelyn Reed</strong> gestimmt.</li>
<li>Maria hat für <strong>Ivan Petrov</strong> gestimmt. (Korrekt!)</li>
<li>Jonas hat für <strong>Ivan Petrov</strong> gestimmt.</li>
</ul>
</div>
<div className="p-3 border-top">
<Button variant="primary" className="w-100 fw-semibold btn-sm">Alle Geschichten lesen</Button>
</div>
</Wireframe>
<Wireframe title="13. Host Notfall-Dashboard">
<div className="text-center pt-3 pb-2 border-bottom">
<h4 className="fw-bold fs-6 text-danger">Host Notfall-Dashboard</h4>
<p className="small text-muted">Spoiler-freie Steuerung (im Spiel)</p>
</div>
<div className="px-3 py-3 flex-grow-1 d-grid gap-3">
<div>
<h5 className="fw-semibold small">Spiel-Steuerung</h5>
<div className="d-grid gap-2">
<Button variant="warning" size="sm">Spiel Pausieren</Button>
<Button variant="info" size="sm">Nächste Runde erzwingen</Button>
</div>
</div>
<div>
<h5 className="fw-semibold small">Experten-Optionen</h5>
<Button variant="dark" size="sm" className="w-100">Hinweise neu generieren</Button>
<p className="text-muted small mt-1">Achtung: Generiert nur die Hinweise für die AKTUELLE Runde neu.</p>
</div>
<div>
<h5 className="fw-semibold small text-danger">Gefahrenzone</h5>
<Button variant="danger" size="sm" className="w-100">Spiel komplett neu starten</Button>
<p className="text-muted small mt-1">Beendet das Spiel für alle und startet die Lobby neu.</p>
</div>
</div>
</Wireframe>
</Row>
</section>
<section className="mb-5">
<h2>5. API-Endpunkte & Datenbank-Interaktion</h2>
<Row>
<Col md={6}>
<h3>FastAPI-Backend</h3>
<ul className="list-group">
<li className="list-group-item"><code>POST /game/create</code></li>
<li className="list-group-item"><code>POST /game/{'{game_code}'}/join</code></li>
<li className="list-group-item"><code>GET /game/{'{game_code}'}/state</code></li>
<li className="list-group-item"><code>POST /game/{'{game_code}'}/vote</code></li>
<li className="list-group-item"><code>GET /game/{'{game_code}'}/character</code></li>
<li className="list-group-item"><code>/ws/{'{game_code}'}</code></li>
</ul>
</Col>
<Col md={6}>
<h3>Client-Interaktionen</h3>
<ul>
<li><strong>Supabase Auth (`supabase-js`):</strong> Nur für Login, Registrierung und Auth-Zustand.</li>
<li><strong>API-Client (z.B. Axios):</strong> Für alle HTTP-Anfragen an das FastAPI-Backend.</li>
<li><strong>WebSocket-Client:</strong> Für Echtzeit-Verbindung zum Backend.</li>
</ul>
</Col>
</Row>
</section>
<section className="mb-5">
<h2>6. Datenmodelle</h2>
<p>Die dedizierte PostgreSQL-Datenbank enthält die gesamte Spiellogik.</p>
<h3 className="mt-4">Tabelle: `games`</h3>
<Table striped bordered hover responsive>
<thead>
<tr><th>Spalte</th><th>Datentyp</th><th>Beschreibung</th></tr>
</thead>
<tbody>
<tr><td>id</td><td>uuid</td><td>Eindeutige ID für ein Spiel.</td></tr>
<tr><td>host_id</td><td>uuid</td><td>Referenz auf die User-ID des Hosts.</td></tr>
<tr><td>game_code</td><td>varchar</td><td>Kurzer Code zum Beitreten.</td></tr>
<tr><td>status</td><td>varchar</td><td>setup, lobby, active, finished.</td></tr>
<tr><td>game_data</td><td>jsonb</td><td>Das komplette KI-generierte Szenario.</td></tr>
</tbody>
</Table>
<h3 className="mt-4">Tabelle: `players`</h3>
<Table striped bordered hover responsive>
<thead>
<tr><th>Spalte</th><th>Datentyp</th><th>Beschreibung</th></tr>
</thead>
<tbody>
<tr><td>id</td><td>uuid</td><td>Eindeutiger Eintrag für einen Spieler.</td></tr>
<tr><td>game_id</td><td>uuid</td><td>Verweist auf das zugehörige Spiel.</td></tr>
<tr><td>player_id</td><td>uuid</td><td>Referenz auf die User-ID aus Supabase.</td></tr>
<tr><td>player_name</td><td>varchar</td><td>Der im Spiel angezeigte Name.</td></tr>
<tr><td>character_id</td><td>varchar</td><td>Verknüpft den Spieler mit seiner Rolle.</td></tr>
</tbody>
</Table>
</section>
<section className="mb-5">
<h2>7. Prompt-Engineering-Strategie</h2>
<Row>
<Col md={6}>
<Card className="h-100">
<Card.Body>
<Card.Title>Prompt 1: Der kreative Autor</Card.Title>
<Card.Subtitle className="mb-2 text-muted">System-Prompt:</Card.Subtitle>
<Card.Text as="div"><pre className="bg-light p-2 rounded">Du bist ein preisgekrönter Autor von Kriminalromanen...</pre></Card.Text>
<Card.Subtitle className="mb-2 mt-3 text-muted">User-Prompt:</Card.Subtitle>
<Card.Text as="div"><pre className="bg-light p-2 rounded">Erstelle ein Krimi-Dinner-Szenario für 5 Spieler...</pre></Card.Text>
</Card.Body>
</Card>
</Col>
<Col md={6}>
<Card className="h-100">
<Card.Body>
<Card.Title>Prompt 2: Der Logik-Lektor</Card.Title>
<Card.Subtitle className="mb-2 text-muted">System-Prompt:</Card.Subtitle>
<Card.Text as="div"><pre className="bg-light p-2 rounded">Du bist ein extrem detailorientierter und logisch denkender Lektor...</pre></Card.Text>
<Card.Subtitle className="mb-2 mt-3 text-muted">User-Prompt:</Card.Subtitle>
<Card.Text as="div"><pre className="bg-light p-2 rounded">Hier ist ein JSON-Objekt. Bitte agiere als Logik-Lektor...</pre></Card.Text>
</Card.Body>
</Card>
</Col>
</Row>
</section>
<section className="mb-5">
<h2>8. User Journey & Spielablauf</h2>
<ol>
<li><strong>Phase 0: Generierung & Vorbereitung:</strong> Host generiert Spiel und erhält Einladungscode.</li>
<li><strong>Phase 1: Lobby:</strong> Spieler treten bei, Host startet das Spiel.</li>
<li><strong>Phase 2: Rollenverteilung:</strong> Backend weist Rollen zu und sendet geheime Infos.</li>
<li><strong>Phase 3: Spielrunden:</strong> System gibt pro Runde neue Hinweise frei.</li>
<li><strong>Phase 4: Abstimmung & Auflösung:</strong> Abstimmung wird freigeschaltet, Mörder wird enthüllt.</li>
</ol>
</section>
<section className="mb-5">
<h2>9. Geschäftsmodell & Monetarisierung</h2>
<Row>
<Col md={6}>
<div className="p-3 bg-light rounded h-100">
<h3>Abonnement-Modell</h3>
<p>Nutzer zahlen einen monatlichen oder jährlichen Beitrag für ein festes Kontingent an Spielen.</p>
</div>
</Col>
<Col md={6}>
<div className="p-3 bg-light rounded h-100">
<h3>Einmalkauf (Pay-per-Game)</h3>
<p>Alternativ können Nutzer einzelne Spiele per Einmalkauf freischalten.</p>
</div>
</Col>
</Row>
</section>
<section className="mb-5">
<h2>10. Skalierbarkeit, Kosten & Fehlerbehandlung</h2>
<ul>
<li><strong>Kostenkontrolle (OpenAI):</strong> Rate-Limiting auf dem Endpunkt zur Spielerstellung.</li>
<li><strong>Fehlerbehandlung bei Generierung:</strong> Backend unternimmt automatische Wiederholungsversuche.</li>
<li><strong>Nahtloses Wiederverbinden:</strong> Spieler können jederzeit wieder in ein laufendes Spiel einsteigen.</li>
<li><strong>Notfall-Dashboard (Host):</strong> Spoiler-freies Admin-Panel für den Host zur Spielsteuerung.</li>
</ul>
</section>
<section className="mb-5">
<h2>11. Rechtliches & Datenschutz</h2>
<ul>
<li><strong>DSGVO-konforme Datenschutzerklärung</strong></li>
<li><strong>Impressum</strong></li>
<li><strong>Möglichkeit zur Löschung des Accounts</strong></li>
</ul>
</section>
</Container>
</div>
);
}
export default KrimiDinner;