Begin consumption of API

This commit is contained in:
Kilian Hofmann 2024-07-24 01:25:12 +02:00
parent b40ae7d701
commit 1b62b890b9
15 changed files with 1023 additions and 526 deletions

617
exam/dist/assets/index-Bv9g1hLV.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,7 @@
<link rel="icon" type="image/svg+xml" href="/phpCourse/exam/dist/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<script type="module" crossorigin src="/phpCourse/exam/dist/assets/index-CCI8Q-vE.js"></script>
<script type="module" crossorigin src="/phpCourse/exam/dist/assets/index-Bv9g1hLV.js"></script>
</head>
<body>
<div id="root"></div>

1
exam/react/.nvmrc Normal file
View File

@ -0,0 +1 @@
v18

View File

@ -13,12 +13,18 @@
"@emotion/react": "^11.13.0",
"@emotion/styled": "^11.13.0",
"@mui/material": "^5.16.4",
"@tanstack/react-query": "^5.51.11",
"@tanstack/react-router": "^1.45.8",
"@types/node": "^20.14.12",
"react": "^18.3.1",
"react-dom": "^18.3.1"
"react-dom": "^18.3.1",
"zustand": "^4.5.4"
},
"devDependencies": {
"@redux-devtools/extension": "^3.3.0",
"@tanstack/eslint-plugin-query": "^5.51.12",
"@tanstack/router-devtools": "^1.45.8",
"@tanstack/react-query-devtools": "^5.51.11",
"@tanstack/router-plugin": "^1.45.8",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",

View File

@ -17,22 +17,40 @@ importers:
'@mui/material':
specifier: ^5.16.4
version: 5.16.4(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@tanstack/react-query':
specifier: ^5.51.11
version: 5.51.11(react@18.3.1)
'@tanstack/react-query-devtools':
specifier: ^5.51.11
version: 5.51.11(@tanstack/react-query@5.51.11(react@18.3.1))(react@18.3.1)
'@tanstack/react-router':
specifier: ^1.45.8
version: 1.45.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@types/node':
specifier: ^20.14.12
version: 20.14.12
react:
specifier: ^18.3.1
version: 18.3.1
react-dom:
specifier: ^18.3.1
version: 18.3.1(react@18.3.1)
zustand:
specifier: ^4.5.4
version: 4.5.4(@types/react@18.3.3)(react@18.3.1)
devDependencies:
'@redux-devtools/extension':
specifier: ^3.3.0
version: 3.3.0(redux@5.0.1)
'@tanstack/eslint-plugin-query':
specifier: ^5.51.12
version: 5.51.12(eslint@8.57.0)(typescript@5.5.3)
'@tanstack/router-devtools':
specifier: ^1.45.8
version: 1.45.8(@tanstack/react-router@1.45.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(csstype@3.1.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@tanstack/router-plugin':
specifier: ^1.45.8
version: 1.45.8(vite@5.3.4)
version: 1.45.8(vite@5.3.4(@types/node@20.14.12))
'@types/react':
specifier: ^18.3.3
version: 18.3.3
@ -47,7 +65,7 @@ importers:
version: 7.16.1(eslint@8.57.0)(typescript@5.5.3)
'@vitejs/plugin-react':
specifier: ^4.3.1
version: 4.3.1(vite@5.3.4)
version: 4.3.1(vite@5.3.4(@types/node@20.14.12))
eslint:
specifier: ^8.57.0
version: 8.57.0
@ -68,7 +86,7 @@ importers:
version: 5.5.3
vite:
specifier: ^5.3.4
version: 5.3.4
version: 5.3.4(@types/node@20.14.12)
packages:
@ -528,6 +546,11 @@ packages:
'@popperjs/core@2.11.8':
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
'@redux-devtools/extension@3.3.0':
resolution: {integrity: sha512-X34S/rC8S/M1BIrkYD1mJ5f8vlH0BDqxXrs96cvxSBo4FhMdbhU+GUGsmNYov1xjSyLMHgo8NYrUG8bNX7525g==}
peerDependencies:
redux: ^3.1.0 || ^4.0.0 || ^5.0.0
'@rollup/rollup-android-arm-eabi@4.18.1':
resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==}
cpu: [arm]
@ -608,10 +631,32 @@ packages:
cpu: [x64]
os: [win32]
'@tanstack/eslint-plugin-query@5.51.12':
resolution: {integrity: sha512-vzUXXIVzDP2c6wVSJ+1imPGaKQ2ILuWnta64FJc/JnQ5WunfO17bQJSk6uKDbzTQG/YKgPYBMG3C9qFA4b7Ayg==}
peerDependencies:
eslint: ^8 || ^9
'@tanstack/history@1.45.3':
resolution: {integrity: sha512-n4XXInV9irIq0obRvINIkESkGk280Q+xkIIbswmM0z9nAu2wsIRZNvlmPrtYh6bgNWtItOWWoihFUjLTW8g6Jg==}
engines: {node: '>=12'}
'@tanstack/query-core@5.51.9':
resolution: {integrity: sha512-HsAwaY5J19MD18ykZDS3aVVh+bAt0i7m6uQlFC2b77DLV9djo+xEN7MWQAQQTR8IM+7r/zbozTQ7P0xr0bHuew==}
'@tanstack/query-devtools@5.51.9':
resolution: {integrity: sha512-FQqJynaEDuwQxoFLP3/i10HQwNYh4wxgs0NeSoL24BLWvpUdstgHqUm2zgwRov8Tmh5kjndPIWaXenwl0D47EA==}
'@tanstack/react-query-devtools@5.51.11':
resolution: {integrity: sha512-8nQRbhdtvl/J9bO+bk/kPQesCOtDgk+oI4AmZJDnkf5OfKTJL3J4tTe+fhuXph7KP4DUOS+ge9o9TGt0OgWFHw==}
peerDependencies:
'@tanstack/react-query': ^5.51.11
react: ^18 || ^19
'@tanstack/react-query@5.51.11':
resolution: {integrity: sha512-4Kq2x0XpDlpvSnaLG+8pHNH60zEc3mBvb3B2tOMDjcPCi/o+Du3p/9qpPLwJOTliVxxPJAP27fuIhLrsRdCr7A==}
peerDependencies:
react: ^18.0.0
'@tanstack/react-router@1.45.8':
resolution: {integrity: sha512-hLJOKDK5lGHteoMjpF6COQrlhsl4C6GyBCzmSJHFcoh26GBa7tv/94li0H1a3deJpzMNpSvmSXrQDpxj9h9bNA==}
engines: {node: '>=12'}
@ -670,6 +715,9 @@ packages:
'@types/estree@1.0.5':
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
'@types/node@20.14.12':
resolution: {integrity: sha512-r7wNXakLeSsGT0H1AU863vS2wa5wBOK4bWMjZz2wj+8nBx+m5PeIn0k8AloSLpRuiwdRQZwarZqHE4FNArPuJQ==}
'@types/parse-json@4.0.2':
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
@ -710,6 +758,10 @@ packages:
resolution: {integrity: sha512-nYpyv6ALte18gbMz323RM+vpFpTjfNdyakbf3nsLvF43uF9KeNC289SUEW3QLZ1xPtyINJ1dIsZOuWuSRIWygw==}
engines: {node: ^18.18.0 || >=20.0.0}
'@typescript-eslint/scope-manager@8.0.0-alpha.30':
resolution: {integrity: sha512-FGW/iPWGyPFamAVZ60oCAthMqQrqafUGebF8UKuq/ha+e9SVG6YhJoRzurlQXOVf8dHfOhJ0ADMXyFnMc53clg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/type-utils@7.16.1':
resolution: {integrity: sha512-rbu/H2MWXN4SkjIIyWcmYBjlp55VT+1G3duFOIukTNFxr9PI35pLc2ydwAfejCEitCv4uztA07q0QWanOHC7dA==}
engines: {node: ^18.18.0 || >=20.0.0}
@ -724,6 +776,10 @@ packages:
resolution: {integrity: sha512-AQn9XqCzUXd4bAVEsAXM/Izk11Wx2u4H3BAfQVhSfzfDOm/wAON9nP7J5rpkCxts7E5TELmN845xTUCQrD1xIQ==}
engines: {node: ^18.18.0 || >=20.0.0}
'@typescript-eslint/types@8.0.0-alpha.30':
resolution: {integrity: sha512-4WzLlw27SO9pK9UFj/Hu7WGo8WveT0SEiIpFVsV2WwtQmLps6kouwtVCB8GJPZKJyurhZhcqCoQVQFmpv441Vg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/typescript-estree@7.16.1':
resolution: {integrity: sha512-0vFPk8tMjj6apaAZ1HlwM8w7jbghC8jc1aRNJG5vN8Ym5miyhTQGMqU++kuBFDNKe9NcPeZ6x0zfSzV8xC1UlQ==}
engines: {node: ^18.18.0 || >=20.0.0}
@ -733,16 +789,35 @@ packages:
typescript:
optional: true
'@typescript-eslint/typescript-estree@8.0.0-alpha.30':
resolution: {integrity: sha512-WSXbc9ZcXI+7yC+6q95u77i8FXz6HOLsw3ST+vMUlFy1lFbXyFL/3e6HDKQCm2Clt0krnoCPiTGvIn+GkYPn4Q==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
'@typescript-eslint/utils@7.16.1':
resolution: {integrity: sha512-WrFM8nzCowV0he0RlkotGDujx78xudsxnGMBHI88l5J8wEhED6yBwaSLP99ygfrzAjsQvcYQ94quDwI0d7E1fA==}
engines: {node: ^18.18.0 || >=20.0.0}
peerDependencies:
eslint: ^8.56.0
'@typescript-eslint/utils@8.0.0-alpha.30':
resolution: {integrity: sha512-rfhqfLqFyXhHNDwMnHiVGxl/Z2q/3guQ1jLlGQ0hi9Rb7inmwz42crM+NnLPR+2vEnwyw1P/g7fnQgQ3qvFx4g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
'@typescript-eslint/visitor-keys@7.16.1':
resolution: {integrity: sha512-Qlzzx4sE4u3FsHTPQAAQFJFNOuqtuY0LFrZHwQ8IHK705XxBiWOFkfKRWu6niB7hwfgnwIpO4jTC75ozW1PHWg==}
engines: {node: ^18.18.0 || >=20.0.0}
'@typescript-eslint/visitor-keys@8.0.0-alpha.30':
resolution: {integrity: sha512-XZuNurZxBqmr6ZIRIwWFq7j5RZd6ZlkId/HZEWyfciK+CWoyOxSF9Pv2VXH9Rlu2ZG2PfbhLz2Veszl4Pfn7yA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@ungap/structured-clone@1.2.0':
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
@ -1067,6 +1142,9 @@ packages:
resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
engines: {node: '>= 4'}
immutable@4.3.7:
resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==}
import-fresh@3.3.0:
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
engines: {node: '>=6'}
@ -1316,6 +1394,9 @@ packages:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
redux@5.0.1:
resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==}
regenerator-runtime@0.14.1:
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
@ -1435,6 +1516,9 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
unplugin@1.11.0:
resolution: {integrity: sha512-3r7VWZ/webh0SGgJScpWl2/MRCZK5d3ZYFcNaeci/GQ7Teop7zf0Nl2pUuz7G21BwPd9pcUPOC5KmJ2L3WgC5g==}
engines: {node: '>=14.0.0'}
@ -1448,6 +1532,11 @@ packages:
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
use-sync-external-store@1.2.0:
resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
use-sync-external-store@1.2.2:
resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==}
peerDependencies:
@ -1514,6 +1603,21 @@ packages:
zod@3.23.8:
resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==}
zustand@4.5.4:
resolution: {integrity: sha512-/BPMyLKJPtFEvVL0E9E9BTUM63MNyhPGlvxk1XjrfWTUlV+BR8jufjsovHzrtR6YNcBEcL7cMHovL1n9xHawEg==}
engines: {node: '>=12.7.0'}
peerDependencies:
'@types/react': '>=16.8'
immer: '>=9.0.6'
react: '>=16.8'
peerDependenciesMeta:
'@types/react':
optional: true
immer:
optional: true
react:
optional: true
snapshots:
'@ampproject/remapping@2.3.0':
@ -1972,6 +2076,12 @@ snapshots:
'@popperjs/core@2.11.8': {}
'@redux-devtools/extension@3.3.0(redux@5.0.1)':
dependencies:
'@babel/runtime': 7.24.8
immutable: 4.3.7
redux: 5.0.1
'@rollup/rollup-android-arm-eabi@4.18.1':
optional: true
@ -2020,8 +2130,31 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.18.1':
optional: true
'@tanstack/eslint-plugin-query@5.51.12(eslint@8.57.0)(typescript@5.5.3)':
dependencies:
'@typescript-eslint/utils': 8.0.0-alpha.30(eslint@8.57.0)(typescript@5.5.3)
eslint: 8.57.0
transitivePeerDependencies:
- supports-color
- typescript
'@tanstack/history@1.45.3': {}
'@tanstack/query-core@5.51.9': {}
'@tanstack/query-devtools@5.51.9': {}
'@tanstack/react-query-devtools@5.51.11(@tanstack/react-query@5.51.11(react@18.3.1))(react@18.3.1)':
dependencies:
'@tanstack/query-devtools': 5.51.9
'@tanstack/react-query': 5.51.11(react@18.3.1)
react: 18.3.1
'@tanstack/react-query@5.51.11(react@18.3.1)':
dependencies:
'@tanstack/query-core': 5.51.9
react: 18.3.1
'@tanstack/react-router@1.45.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@tanstack/history': 1.45.3
@ -2053,7 +2186,7 @@ snapshots:
prettier: 3.3.3
zod: 3.23.8
'@tanstack/router-plugin@1.45.8(vite@5.3.4)':
'@tanstack/router-plugin@1.45.8(vite@5.3.4(@types/node@20.14.12))':
dependencies:
'@babel/core': 7.24.9
'@babel/generator': 7.24.10
@ -2073,7 +2206,7 @@ snapshots:
unplugin: 1.11.0
zod: 3.23.8
optionalDependencies:
vite: 5.3.4
vite: 5.3.4(@types/node@20.14.12)
transitivePeerDependencies:
- supports-color
@ -2102,6 +2235,10 @@ snapshots:
'@types/estree@1.0.5': {}
'@types/node@20.14.12':
dependencies:
undici-types: 5.26.5
'@types/parse-json@4.0.2': {}
'@types/prop-types@15.7.12': {}
@ -2155,6 +2292,11 @@ snapshots:
'@typescript-eslint/types': 7.16.1
'@typescript-eslint/visitor-keys': 7.16.1
'@typescript-eslint/scope-manager@8.0.0-alpha.30':
dependencies:
'@typescript-eslint/types': 8.0.0-alpha.30
'@typescript-eslint/visitor-keys': 8.0.0-alpha.30
'@typescript-eslint/type-utils@7.16.1(eslint@8.57.0)(typescript@5.5.3)':
dependencies:
'@typescript-eslint/typescript-estree': 7.16.1(typescript@5.5.3)
@ -2169,6 +2311,8 @@ snapshots:
'@typescript-eslint/types@7.16.1': {}
'@typescript-eslint/types@8.0.0-alpha.30': {}
'@typescript-eslint/typescript-estree@7.16.1(typescript@5.5.3)':
dependencies:
'@typescript-eslint/types': 7.16.1
@ -2184,6 +2328,21 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/typescript-estree@8.0.0-alpha.30(typescript@5.5.3)':
dependencies:
'@typescript-eslint/types': 8.0.0-alpha.30
'@typescript-eslint/visitor-keys': 8.0.0-alpha.30
debug: 4.3.5
globby: 11.1.0
is-glob: 4.0.3
minimatch: 9.0.5
semver: 7.6.3
ts-api-utils: 1.3.0(typescript@5.5.3)
optionalDependencies:
typescript: 5.5.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/utils@7.16.1(eslint@8.57.0)(typescript@5.5.3)':
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
@ -2195,21 +2354,37 @@ snapshots:
- supports-color
- typescript
'@typescript-eslint/utils@8.0.0-alpha.30(eslint@8.57.0)(typescript@5.5.3)':
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
'@typescript-eslint/scope-manager': 8.0.0-alpha.30
'@typescript-eslint/types': 8.0.0-alpha.30
'@typescript-eslint/typescript-estree': 8.0.0-alpha.30(typescript@5.5.3)
eslint: 8.57.0
transitivePeerDependencies:
- supports-color
- typescript
'@typescript-eslint/visitor-keys@7.16.1':
dependencies:
'@typescript-eslint/types': 7.16.1
eslint-visitor-keys: 3.4.3
'@typescript-eslint/visitor-keys@8.0.0-alpha.30':
dependencies:
'@typescript-eslint/types': 8.0.0-alpha.30
eslint-visitor-keys: 3.4.3
'@ungap/structured-clone@1.2.0': {}
'@vitejs/plugin-react@4.3.1(vite@5.3.4)':
'@vitejs/plugin-react@4.3.1(vite@5.3.4(@types/node@20.14.12))':
dependencies:
'@babel/core': 7.24.9
'@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.24.9)
'@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.24.9)
'@types/babel__core': 7.20.5
react-refresh: 0.14.2
vite: 5.3.4
vite: 5.3.4(@types/node@20.14.12)
transitivePeerDependencies:
- supports-color
@ -2582,6 +2757,8 @@ snapshots:
ignore@5.3.1: {}
immutable@4.3.7: {}
import-fresh@3.3.0:
dependencies:
parent-module: 1.0.1
@ -2788,6 +2965,8 @@ snapshots:
dependencies:
picomatch: 2.3.1
redux@5.0.1: {}
regenerator-runtime@0.14.1: {}
resolve-from@4.0.0: {}
@ -2892,6 +3071,8 @@ snapshots:
typescript@5.5.3: {}
undici-types@5.26.5: {}
unplugin@1.11.0:
dependencies:
acorn: 8.12.1
@ -2909,16 +3090,21 @@ snapshots:
dependencies:
punycode: 2.3.1
use-sync-external-store@1.2.0(react@18.3.1):
dependencies:
react: 18.3.1
use-sync-external-store@1.2.2(react@18.3.1):
dependencies:
react: 18.3.1
vite@5.3.4:
vite@5.3.4(@types/node@20.14.12):
dependencies:
esbuild: 0.21.5
postcss: 8.4.39
rollup: 4.18.1
optionalDependencies:
'@types/node': 20.14.12
fsevents: 2.3.3
webpack-sources@3.2.3: {}
@ -2940,3 +3126,10 @@ snapshots:
yocto-queue@0.1.0: {}
zod@3.23.8: {}
zustand@4.5.4(@types/react@18.3.3)(react@18.3.1):
dependencies:
use-sync-external-store: 1.2.0(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.3
react: 18.3.1

61
exam/react/src/api/Api.ts Normal file
View File

@ -0,0 +1,61 @@
import { User } from '../types/User';
const BASE = 'https://khofmann.userpage.fu-berlin.de/phpCourse/exam/api/';
let instance: ApiImpl;
class ApiImpl {
private token: string = '';
constructor() {
if (instance) {
throw new Error('New instance cannot be created!!');
}
instance = this;
}
public logIn = async (email: string, password: string): Promise<User> => {
const { user, token } = await (await this.post('login', { email, password })).json();
this.token = token;
return user;
};
public logOut = async (): Promise<boolean> => {
return await (await this.postAuth('logout')).json();
};
private post = async (
endpoint: string,
body: Record<string, unknown> | undefined = undefined,
headers: HeadersInit | undefined = undefined
) => {
const response = await fetch(`${BASE}${endpoint}`, {
mode: 'cors',
method: 'post',
headers,
body: JSON.stringify(body),
});
if (response.ok) return response;
throw await response.json();
};
private postAuth = async (
endpoint: string,
body: Record<string, unknown> | undefined = undefined,
headers: HeadersInit | undefined = undefined
) => {
const response = await fetch(`${BASE}${endpoint}`, {
mode: 'cors',
method: 'post',
headers: { token: this.token, ...headers },
body: JSON.stringify(body),
});
if (response.ok) return response;
throw await response.json();
};
}
const Api = new ApiImpl();
export default Api;

View File

@ -1,12 +1,27 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { RouterProvider, createRouter } from '@tanstack/react-router';
import { StrictMode } from 'react';
import ReactDOM from 'react-dom/client';
// Import the generated route tree
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { routeTree } from './routeTree.gen';
// Query Client
const queryClient = new QueryClient();
// Create a new router instance
const router = createRouter({ routeTree });
const router = createRouter({
routeTree,
context: {
queryClient,
},
defaultPreload: 'intent',
// Since we're using React Query, we don't want loader calls to ever be stale
// This will ensure that the loader is always called when the route is preloaded or visited
defaultPreloadStaleTime: 0,
basepath: process.env.NODE_ENV === 'development' ? 'phpCourse/exam/dist' : '/phpCourse/exam',
});
// Register the router instance for type safety
declare module '@tanstack/react-router' {
@ -21,7 +36,10 @@ if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement);
root.render(
<StrictMode>
<RouterProvider router={router} />
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
</StrictMode>
);
}

View File

@ -11,18 +11,32 @@
// Import Routes
import { Route as rootRoute } from './routes/__root'
import { Route as IndexImport } from './routes/index'
// Create/Update Routes
const IndexRoute = IndexImport.update({
path: '/',
getParentRoute: () => rootRoute,
} as any)
// Populate the FileRoutesByPath interface
declare module '@tanstack/react-router' {
interface FileRoutesByPath {}
interface FileRoutesByPath {
'/': {
id: '/'
path: '/'
fullPath: '/'
preLoaderRoute: typeof IndexImport
parentRoute: typeof rootRoute
}
}
}
// Create and export the route tree
export const routeTree = rootRoute.addChildren({})
export const routeTree = rootRoute.addChildren({ IndexRoute })
/* prettier-ignore-end */
@ -31,7 +45,12 @@ export const routeTree = rootRoute.addChildren({})
"routes": {
"__root__": {
"filePath": "__root.tsx",
"children": []
"children": [
"/"
]
},
"/": {
"filePath": "index.tsx"
}
}
}

View File

@ -1,9 +1,19 @@
import { createRootRoute, Outlet } from '@tanstack/react-router';
import { QueryClient } from '@tanstack/react-query';
import { createRootRouteWithContext, Link, Outlet } from '@tanstack/react-router';
import { TanStackRouterDevtools } from '@tanstack/router-devtools';
export const Route = createRootRoute({
export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()({
component: () => (
<>
<Link
to="/"
activeProps={{
className: 'font-bold',
}}
activeOptions={{ exact: true }}
>
Home
</Link>
<Outlet />
<TanStackRouterDevtools />
</>

View File

@ -0,0 +1,40 @@
import { Button } from '@mui/material';
import { createFileRoute } from '@tanstack/react-router';
import Api from '../api/Api';
import useGuestBookStore from '../store/store';
export const Route = createFileRoute('/')({
component: Home,
});
function Home() {
const setUser = useGuestBookStore((state) => state.setUser);
return (
<>
<Button
onClick={async () => {
try {
setUser(await Api.logIn('max@moritz.net', 'max'));
} catch (error) {
console.log(error);
}
}}
>
Log In
</Button>
<Button
onClick={async () => {
try {
await Api.logOut();
setUser(undefined);
} catch (error) {
console.log(error);
}
}}
>
Log Out
</Button>
</>
);
}

View File

@ -0,0 +1,18 @@
import type {} from '@redux-devtools/extension'; // required for devtools typing
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
import { User } from '../types/User';
interface GuestBookState {
user: User | undefined;
setUser: (user: User | undefined) => void;
}
const useGuestBookStore = create<GuestBookState>()(
devtools((set) => ({
user: undefined,
setUser: (user: User | undefined) => set(() => ({ user })),
}))
);
export default useGuestBookStore;

View File

@ -0,0 +1,6 @@
import { User } from './User';
export interface LoginResponse {
token: string;
user: User;
}

View File

@ -0,0 +1,5 @@
export interface Timestamp {
date: string;
timezone_type: number;
timezone: string;
}

View File

@ -0,0 +1,12 @@
import { Timestamp } from './Timestamp';
export interface User {
id: number;
username: string;
status: number;
email: string;
image: string;
isAdmin: boolean;
memberSince: Timestamp;
postCount: number;
}