Public Access
Add i18n, routing and dark-mode support
Introduce internationalization and basic routing: add i18n initialization (src/i18n.js), translation files (public/i18n/en.json, de.json), and wire i18n into the app (import in main.jsx). Update App.jsx to use react-router (BrowserRouter, Routes) and react-i18next (useTranslation), provide simple Landing/Login/Imprint pages, a language toggle and a dark-mode toggle. Add dependencies in package.json (i18next, i18next-http-backend, react-i18next, react-router-dom) and small Tailwind style tweak (custom-variant for dark) in src/style.css.
This commit is contained in:
1 parent
7ff53c0f6c
commit
c8f1d66cd2
8 files changed
+343
-64
No files matched your search
Generated
+219
-4
@@ -10,8 +10,12 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdi/font": "~7.4.47",
|
"@mdi/font": "~7.4.47",
|
||||||
"@tailwindcss/vite": "~4.2.2",
|
"@tailwindcss/vite": "~4.2.2",
|
||||||
|
"i18next": "~26.0.0",
|
||||||
|
"i18next-http-backend": "~3.0.2",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4"
|
"react-dom": "^19.2.4",
|
||||||
|
"react-i18next": "~17.0.0",
|
||||||
|
"react-router-dom": "~7.13.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-react": "^6.0.1",
|
"@vitejs/plugin-react": "^6.0.1",
|
||||||
@@ -21,6 +25,15 @@
|
|||||||
"vite": "^8.0.1"
|
"vite": "^8.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/runtime": {
|
||||||
|
"version": "7.29.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
|
||||||
|
"integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@emnapi/core": {
|
"node_modules/@emnapi/core": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz",
|
||||||
@@ -773,6 +786,28 @@
|
|||||||
],
|
],
|
||||||
"license": "CC-BY-4.0"
|
"license": "CC-BY-4.0"
|
||||||
},
|
},
|
||||||
|
"node_modules/cookie": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cross-fetch": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"node-fetch": "^2.6.12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/detect-libc": {
|
"node_modules/detect-libc": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||||
@@ -863,6 +898,55 @@
|
|||||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/html-parse-stringify": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"void-elements": "3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/i18next": {
|
||||||
|
"version": "26.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/i18next/-/i18next-26.0.0.tgz",
|
||||||
|
"integrity": "sha512-+Dg27j7VH40WRy+Q010hj3jxlxDb3qFkA7EwuK4kx0+glLxEqZsxdB8MqliPBkTrXEuNlEfeosM3lucAHK+0tQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://www.locize.com/i18next"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://www.locize.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.29.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5 || ^6"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/i18next-http-backend": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-PdlvPnvIp4E1sYi46Ik4tBYh/v/NbYfFFgTjkwFl0is8A18s7/bx9aXqsrOax9WUbeNS6mD2oix7Z0yGGf6m5g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cross-fetch": "4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jiti": {
|
"node_modules/jiti": {
|
||||||
"version": "2.6.1",
|
"version": "2.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
|
||||||
@@ -1148,6 +1232,26 @@
|
|||||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-fetch": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||||
|
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "4.x || >=6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"encoding": "^0.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"encoding": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-releases": {
|
"node_modules/node-releases": {
|
||||||
"version": "2.0.36",
|
"version": "2.0.36",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz",
|
||||||
@@ -1162,9 +1266,9 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/picomatch": {
|
"node_modules/picomatch": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
@@ -1229,6 +1333,71 @@
|
|||||||
"react": "^19.2.4"
|
"react": "^19.2.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-i18next": {
|
||||||
|
"version": "17.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-17.0.0.tgz",
|
||||||
|
"integrity": "sha512-L7aqwOePCExt6nlF7000lN2YKWnR7IpSpQId9sj01798Xn3LAncBdTHKl9lA/nr+YrG78BTqWPJxq9mlrrmH7Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.29.2",
|
||||||
|
"html-parse-stringify": "^3.0.1",
|
||||||
|
"use-sync-external-store": "^1.6.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"i18next": ">= 25.10.10",
|
||||||
|
"react": ">= 16.8.0",
|
||||||
|
"typescript": "^5 || ^6"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-native": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-router": {
|
||||||
|
"version": "7.13.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.2.tgz",
|
||||||
|
"integrity": "sha512-tX1Aee+ArlKQP+NIUd7SE6Li+CiGKwQtbS+FfRxPX6Pe4vHOo6nr9d++u5cwg+Z8K/x8tP+7qLmujDtfrAoUJA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cookie": "^1.0.1",
|
||||||
|
"set-cookie-parser": "^2.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=18",
|
||||||
|
"react-dom": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-router-dom": {
|
||||||
|
"version": "7.13.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.2.tgz",
|
||||||
|
"integrity": "sha512-aR7SUORwTqAW0JDeiWF07e9SBE9qGpByR9I8kJT5h/FrBKxPMS6TiC7rmVO+gC0q52Bx7JnjWe8Z1sR9faN4YA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"react-router": "7.13.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=18",
|
||||||
|
"react-dom": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/rolldown": {
|
"node_modules/rolldown": {
|
||||||
"version": "1.0.0-rc.10",
|
"version": "1.0.0-rc.10",
|
||||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.10.tgz",
|
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.10.tgz",
|
||||||
@@ -1274,6 +1443,12 @@
|
|||||||
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
|
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/set-cookie-parser": {
|
||||||
|
"version": "2.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
|
||||||
|
"integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
@@ -1318,6 +1493,12 @@
|
|||||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/tslib": {
|
"node_modules/tslib": {
|
||||||
"version": "2.8.1",
|
"version": "2.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
@@ -1356,6 +1537,15 @@
|
|||||||
"browserslist": ">= 4.21.0"
|
"browserslist": ">= 4.21.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/use-sync-external-store": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "8.0.1",
|
"version": "8.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.1.tgz",
|
||||||
@@ -1432,6 +1622,31 @@
|
|||||||
"optional": true
|
"optional": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/void-elements": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||||
|
"license": "BSD-2-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+5
-1
@@ -11,8 +11,12 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdi/font": "~7.4.47",
|
"@mdi/font": "~7.4.47",
|
||||||
"@tailwindcss/vite": "~4.2.2",
|
"@tailwindcss/vite": "~4.2.2",
|
||||||
|
"i18next": "~26.0.0",
|
||||||
|
"i18next-http-backend": "~3.0.2",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4"
|
"react-dom": "^19.2.4",
|
||||||
|
"react-i18next": "~17.0.0",
|
||||||
|
"react-router-dom": "~7.13.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-react": "^6.0.1",
|
"@vitejs/plugin-react": "^6.0.1",
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"welcome": "Dein Game-Backlog unter Kontrolle",
|
||||||
|
"login": "Anmelden",
|
||||||
|
"imprint": "Impressum"
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"welcome": "Control your game backlog",
|
||||||
|
"login": "Login",
|
||||||
|
"imprint": "Imprint"
|
||||||
|
}
|
||||||
+87
-59
@@ -1,67 +1,95 @@
|
|||||||
import React from 'react';
|
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
function App() {
|
import { useState, useEffect } from 'react';
|
||||||
// Array to generate the placeholder game cards
|
|
||||||
const mockGames = [1, 2, 3];
|
|
||||||
|
|
||||||
|
// Placeholder components to keep the routing structure simple
|
||||||
|
function LandingPage() {
|
||||||
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-slate-50 flex flex-col">
|
<div className="grow flex items-center justify-center p-4">
|
||||||
<header className="border-b shadow-sm bg-white p-4 flex justify-between items-center">
|
<h1 className="text-3xl md:text-5xl font-bold text-slate-900 dark:text-white">
|
||||||
<div className="font-bold text-blue-600 flex items-center">
|
{t('welcome')}
|
||||||
<i className="mdi mdi-controller mr-2 text-xl"></i>
|
|
||||||
To Be Played
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button className="mr-4">Dashboard</button>
|
|
||||||
<button className="bg-blue-600 text-white px-4 py-2 rounded">Login</button>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main className="grow container mx-auto py-20 px-4">
|
|
||||||
<section className="text-center mb-16">
|
|
||||||
<h1 className="text-4xl md:text-5xl font-extrabold text-slate-900 mb-4">
|
|
||||||
Dein Game-Backlog unter Kontrolle.
|
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-lg text-slate-600 max-w-2xl mx-auto">
|
|
||||||
Willkommen bei deiner neuen Applikation. Hier werden bald deine Spiele aus dem
|
|
||||||
<code className="bg-slate-200 px-1 rounded text-blue-600 ml-1">game-service</code> gelistet.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
||||||
{mockGames.map((n) => {
|
|
||||||
return (
|
|
||||||
<div key={n} className="border border-slate-200 rounded-xl bg-white overflow-hidden">
|
|
||||||
<div className="h-48 bg-slate-100">
|
|
||||||
<img
|
|
||||||
src={`https://picsum.photos/500/300?random=${n}`}
|
|
||||||
alt={`Beispiel Spiel ${n}`}
|
|
||||||
className="w-full h-full object-cover"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="p-4">
|
|
||||||
<h3 className="font-bold text-lg">Beispiel Spiel {n}</h3>
|
|
||||||
<p className="text-sm text-slate-500 mb-2">Status: In Warteschlange</p>
|
|
||||||
<p className="text-slate-500 text-sm">
|
|
||||||
Dies ist ein Platzhalter für ein Spiel in deiner Liste. Später kommen hier echte Daten per API.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="border-t p-4">
|
|
||||||
<button className="w-full bg-blue-100 text-blue-700 py-2 rounded font-medium hover:bg-blue-200">
|
|
||||||
Details ansehen
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer className="bg-white border-t py-4 text-center text-slate-400 text-sm">
|
|
||||||
© 2026 To Be Played — Entwickelt mit React & Tailwind v4
|
|
||||||
</footer>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function LoginPage() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<div className="grow flex items-center justify-center p-4">
|
||||||
|
<h2 className="text-2xl text-slate-800 dark:text-slate-200">{t('login')}</h2>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ImprintPage() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<div className="grow p-8">
|
||||||
|
<h2 className="text-2xl text-slate-800 dark:text-slate-200">{t('imprint')}</h2>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const { t, i18n } = useTranslation();
|
||||||
|
const [isDarkMode, setIsDarkMode] = useState(true);
|
||||||
|
|
||||||
|
// Apply dark mode class to the root HTML element for Tailwind
|
||||||
|
useEffect(() => {
|
||||||
|
if (isDarkMode) {
|
||||||
|
document.documentElement.classList.add('dark');
|
||||||
|
} else {
|
||||||
|
document.documentElement.classList.remove('dark');
|
||||||
|
}
|
||||||
|
}, [isDarkMode]);
|
||||||
|
|
||||||
|
const toggleLanguage = () => {
|
||||||
|
if (i18n.language === 'de') {
|
||||||
|
i18n.changeLanguage('en').then(r => console.log("Changed language to en"));
|
||||||
|
} else {
|
||||||
|
i18n.changeLanguage('de').then(r => console.log("Changed language to en"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BrowserRouter>
|
||||||
|
<div className="min-h-screen flex flex-col bg-slate-50 dark:bg-slate-950 transition-colors duration-300">
|
||||||
|
<header className="p-4 flex justify-between items-center border-b border-slate-200 dark:border-slate-800 bg-white dark:bg-slate-900">
|
||||||
|
<Link to="/" className="font-bold text-blue-600 dark:text-blue-400 text-xl flex items-center">
|
||||||
|
<i className="mdi mdi-controller mr-2"></i> To Be Played
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<div className="flex gap-4 items-center">
|
||||||
|
<button onClick={toggleLanguage} className="text-sm font-medium dark:text-slate-300">
|
||||||
|
{i18n.language.toUpperCase()}
|
||||||
|
</button>
|
||||||
|
<button onClick={() => { setIsDarkMode(!isDarkMode); }} className="text-xl dark:text-slate-300">
|
||||||
|
<i className={isDarkMode ? "mdi mdi-weather-sunny" : "mdi mdi-weather-night"}></i>
|
||||||
|
</button>
|
||||||
|
<Link to="/login" className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg font-medium transition-colors">
|
||||||
|
{t('login')}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main className="flex-grow flex flex-col">
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<LandingPage />} />
|
||||||
|
<Route path="/login" element={<LoginPage />} />
|
||||||
|
<Route path="/impressum" element={<ImprintPage />} />
|
||||||
|
</Routes>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer className="p-4 text-center border-t border-slate-200 dark:border-slate-800 text-slate-500 text-sm bg-white dark:bg-slate-900">
|
||||||
|
<Link to="/impressum" className="hover:text-blue-500 transition-colors">
|
||||||
|
{t('imprint')}
|
||||||
|
</Link>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</BrowserRouter>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
import i18n from 'i18next';
|
||||||
|
import {initReactI18next} from 'react-i18next';
|
||||||
|
import HttpBackend from 'i18next-http-backend';
|
||||||
|
|
||||||
|
|
||||||
|
i18n.use(HttpBackend)
|
||||||
|
.use(initReactI18next)
|
||||||
|
.init({
|
||||||
|
lng: 'de',
|
||||||
|
fallbackLng: 'en',
|
||||||
|
backend: {
|
||||||
|
loadPath: '/i18n/{{lng}}.json',
|
||||||
|
},
|
||||||
|
interpolation: {
|
||||||
|
escapeValue: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default i18n;
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { StrictMode } from 'react';
|
import { StrictMode } from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
import App from './App.jsx';
|
import App from './App.jsx';
|
||||||
|
import './i18n';
|
||||||
import './style.css';
|
import './style.css';
|
||||||
import '@mdi/font/css/materialdesignicons.css';
|
import '@mdi/font/css/materialdesignicons.css';
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,3 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
@custom-variant dark (&:is(.dark *));
|
||||||
Reference in new issue
Block a user