From 97ffd8af793fc0bfe66c906f93692ad0ca190e22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Hansen=20L=C3=B8seth?= Date: Tue, 8 Sep 2026 12:16:07 +0200 Subject: [PATCH] Added initial API code, as well as TanStack Query to the project and App.tsx --- Project-1/package.json | 1 + Project-1/pnpm-lock.yaml | 469 ++++++++++++++++++++++++++++++++- Project-1/pnpm-workspace.yaml | 2 + Project-1/src/App.tsx | 19 +- Project-1/src/api/client.ts | 62 +++++ Project-1/src/api/endpoints.ts | 16 ++ Project-1/src/hooks/useCars.ts | 21 ++ 7 files changed, 581 insertions(+), 9 deletions(-) create mode 100644 Project-1/pnpm-workspace.yaml create mode 100644 Project-1/src/api/client.ts create mode 100644 Project-1/src/api/endpoints.ts create mode 100644 Project-1/src/hooks/useCars.ts diff --git a/Project-1/package.json b/Project-1/package.json index bb98212..f7dfacf 100644 --- a/Project-1/package.json +++ b/Project-1/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "@tanstack/react-query": "^5.102.8", "react": "^19.2.8", "react-dom": "^19.2.8" }, diff --git a/Project-1/pnpm-lock.yaml b/Project-1/pnpm-lock.yaml index 7a0b283..aa99cd4 100644 --- a/Project-1/pnpm-lock.yaml +++ b/Project-1/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@tanstack/react-query': + specifier: ^5.102.8 + version: 5.102.8(react@19.2.8) react: specifier: ^19.2.8 version: 19.2.8 @@ -65,7 +68,7 @@ importers: version: 8.2.2(@types/node@24.13.3) vitest: specifier: ^4.1.11 - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)) + version: 4.1.11(@types/node@24.13.3)(msw@2.15.0(@types/node@24.13.3)(typescript@6.0.3))(vite@8.2.2(@types/node@24.13.3)) packages: @@ -195,6 +198,41 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@inquirer/ansi@2.0.8': + resolution: {integrity: sha512-WpQM+Ti6Z40EFwwt+uL2p4UabT+W179zHp6HhLVOzfbwnVn05IPO/eXIZXGNqcT1jbQ15SujNLzQ39k4QPPxBQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + + '@inquirer/confirm@6.3.1': + resolution: {integrity: sha512-HvnOHal39DTenOVoRpIy8Z+n4YYfNa3Qhi/P8zFqn9/d1crpmQG0DPx34rwOeOFvotgKr9Vov/14OmKR/Iwfjw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@12.0.2': + resolution: {integrity: sha512-9pBhkxE14uUlnHhs8lOt7qVPtS4caRY6CjADl8COejl9Mf7w8i6Uoe3DrljCqYtmYM3Iv2Q6EmPxx/oTYbvTAQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@2.0.9': + resolution: {integrity: sha512-EAWgUTGQ/Umgga51dE3B2PUHbufuXarDfg86uVgoSgNHNNQnyFKcOrQLWVqYMghuSyHh8+2HUH0Js9cTC1WAdg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + + '@inquirer/type@4.1.1': + resolution: {integrity: sha512-yJoHYrMnxIsJZCY+0Vb66Dy3he3kL3e2wOBKhoSwWWAzZAY82emlxwgprCtp6yRixvNRNq9ztfRWQYPNr3Go7A==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -211,6 +249,22 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@mswjs/interceptors@0.41.9': + resolution: {integrity: sha512-VVPPgHyQ6ShqnrmDWuxjmUIsO9gWyOZFmuOfLd9LfBGQJwZfy0gvv9pbHSJuoFNIYC7ZDX9aoFwowjcdSC4E8w==} + engines: {node: '>=18'} + + '@open-draft/deferred-promise@2.2.0': + resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} + + '@open-draft/deferred-promise@3.0.0': + resolution: {integrity: sha512-XW375UK8/9SqUVNVa6M0yEy8+iTi4QN5VZ7aZuRFQmy76LRwI9wy5F4YIBU6T+eTe2/DNDo8tqu8RHlwLHM6RA==} + + '@open-draft/logger@0.3.0': + resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} + + '@open-draft/until@2.1.0': + resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + '@oxc-project/types@0.147.0': resolution: {integrity: sha512-IJ3s6ltHLp45S0bh7phkX+gJO7A1Wuz2EaqpAhb8WjqDwbzMiWKHhyyT42tskaWjEYXtHtVCPpnBJVT9+dcRLg==} @@ -333,6 +387,14 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@tanstack/query-core@5.102.8': + resolution: {integrity: sha512-ZNjkJ33CqvPNec/6lZBnHqLc3EVGPZ9ySLhYahU9TcuRFdmwXewuj0c4hwSWcGHqEUwcSrKeZ+oGcvPBqXcQcg==} + + '@tanstack/react-query@5.102.8': + resolution: {integrity: sha512-TYBea4OuXWD7MhaSHq069TWbFe7rcwWN6kzT7JF0OKi1K6c1gTv2IzD6A6ExJsCMozdkqBWeuIUZmu4KQg0O5A==} + peerDependencies: + react: ^18 || ^19 + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -371,6 +433,12 @@ packages: '@types/react@19.2.18': resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==} + '@types/set-cookie-parser@2.4.10': + resolution: {integrity: sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==} + + '@types/statuses@2.0.6': + resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} + '@typescript-eslint/eslint-plugin@8.68.0': resolution: {integrity: sha512-WASHDpCm6qO5jj9g1a+8NiW5+GCkAyLReR56/4VruYmNgfUmqpxOfZ2Yfb8xGfJPWv5Qi6LSD8sXdces3vbp/Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -488,6 +556,14 @@ packages: ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -520,9 +596,28 @@ packages: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -549,6 +644,9 @@ packages: electron-to-chromium@1.5.416: resolution: {integrity: sha512-K6bvB2BjnNrugtIih6ewlbBI9DXa976jIdiIlRLHhBoEI9a4JaQjjHyF+A1IQI543aQYR4LnmOrT/K5fZj0aPA==} + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + es-module-lexer@2.3.2: resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} @@ -629,6 +727,15 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -662,6 +769,10 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} @@ -670,6 +781,13 @@ packages: resolution: {integrity: sha512-Z2I8hM+PbJDXQDq3Icgpzv+mPdwr68iZUU9d5WW4FuXfDUQfkZaZuvjMv42/5crNyw154+9+VWXbYrUgDXbxNw==} engines: {node: '>=18'} + graphql@16.14.2: + resolution: {integrity: sha512-Chq1s4CY7jmh8gO2qvLIJyfCDIN+EHLFW/9iShnp1z8FjBQMoodWP1kDC36VAMXXIvAjj4ARa7ntfAV2BrjsbA==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + + headers-polyfill@5.0.1: + resolution: {integrity: sha512-1TJ6Fih/b8h5TIcv+1+Hw0PDQWJTKDKzFZzcKOiW1wJza3XoAQlkCuXLbymPYB8+ZQyw8mHvdw560e8zVFIWyA==} + hermes-estree@0.25.1: resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} @@ -692,10 +810,17 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-node-process@1.2.0: + resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -819,6 +944,20 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + msw@2.15.0: + resolution: {integrity: sha512-2wQAmKkQKxRuXvYJxVhPGG0wZNBQyD06oJvxqw90XqLvptdqxdlHrFUfEteKkpaNORX3Xzc+HtEl/q0nfmN2wQ==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: '>= 4.8.x' + peerDependenciesMeta: + typescript: + optional: true + + mute-stream@3.0.0: + resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} + engines: {node: ^20.17.0 || >=22.9.0} + nanoid@3.3.18: resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -839,6 +978,9 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + outvariant@1.4.3: + resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -855,6 +997,9 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -886,6 +1031,13 @@ packages: resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==} engines: {node: '>=0.10.0'} + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + rettime@0.11.11: + resolution: {integrity: sha512-ILJRqVWBCTlg9r42fFgwVZx1gnFAcQF8mRoMkbgQfIrjEDf9nbBFDFx00oloOa+Q869FUtaYDXZvEfnecQSCoQ==} + rolldown@1.2.6: resolution: {integrity: sha512-vMM4q3aixf46GiF1Kok8jDPFsEpXgFWGjUHXNkNHNm+Y2adXAG2dbX91jkti3i0ZRsOlcmbuzAz1poObSHCmUA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -903,6 +1055,9 @@ packages: engines: {node: '>=10'} hasBin: true + set-cookie-parser@3.1.2: + resolution: {integrity: sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw==} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -914,6 +1069,10 @@ packages: siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -921,9 +1080,28 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + std-env@4.2.0: resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} + strict-event-emitter@0.5.1: + resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -939,6 +1117,17 @@ packages: resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} engines: {node: '>=14.0.0'} + tldts-core@7.4.12: + resolution: {integrity: sha512-nYNzS2WRf4QJmjzFFgAxLOBjyBxAGRbCy9PVBPaglcYyYajh40VBn+v5Ngr96ZMc7oM0+aCJdtQnNejvdBnXMQ==} + + tldts@7.4.12: + resolution: {integrity: sha512-WylhSDKVeYnWXL3a+vKTaOxjnOeEGw938hImY8zoRWJjRRK/Jp1K+IihBzIONpUmW4e3WmXT6q5FW6vlESVZCA==} + hasBin: true + + tough-cookie@6.0.2: + resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==} + engines: {node: '>=16'} + ts-api-utils@2.5.0: resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} engines: {node: '>=18.12'} @@ -949,6 +1138,10 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} + type-fest@5.9.0: + resolution: {integrity: sha512-yANm3Jr3GiJ1qgJlxGAVxTOIcEOk1rhQHamlXtnrCK7EHP4HeM9OGxtMg/W7HFdrVzw/ZWJKGVIJusVH85sLtw==} + engines: {node: '>=20'} + typescript-eslint@8.68.0: resolution: {integrity: sha512-MHy0Y0ynqeEbx/S45+i/bBssdy3X6KNBfmJAP35GrgtNxu2TQ5K5xsFDhAnmsq1jvpdoZOPG1LGtJo0HWqYCrQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -964,6 +1157,9 @@ packages: undici-types@7.18.2: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + until-async@3.0.2: + resolution: {integrity: sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==} + update-browserslist-db@1.3.2: resolution: {integrity: sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==} hasBin: true @@ -1071,9 +1267,25 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.3: + resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} + engines: {node: '>=12'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -1239,6 +1451,38 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@inquirer/ansi@2.0.8': + optional: true + + '@inquirer/confirm@6.3.1(@types/node@24.13.3)': + dependencies: + '@inquirer/core': 12.0.2(@types/node@24.13.3) + '@inquirer/type': 4.1.1(@types/node@24.13.3) + optionalDependencies: + '@types/node': 24.13.3 + optional: true + + '@inquirer/core@12.0.2(@types/node@24.13.3)': + dependencies: + '@inquirer/ansi': 2.0.8 + '@inquirer/figures': 2.0.9 + '@inquirer/type': 4.1.1(@types/node@24.13.3) + cli-width: 4.1.0 + fast-wrap-ansi: 0.2.2 + mute-stream: 3.0.0 + signal-exit: 4.1.0 + optionalDependencies: + '@types/node': 24.13.3 + optional: true + + '@inquirer/figures@2.0.9': + optional: true + + '@inquirer/type@4.1.1(@types/node@24.13.3)': + optionalDependencies: + '@types/node': 24.13.3 + optional: true + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.6.0 @@ -1258,6 +1502,31 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.6.0 + '@mswjs/interceptors@0.41.9': + dependencies: + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/logger': 0.3.0 + '@open-draft/until': 2.1.0 + is-node-process: 1.2.0 + outvariant: 1.4.3 + strict-event-emitter: 0.5.1 + optional: true + + '@open-draft/deferred-promise@2.2.0': + optional: true + + '@open-draft/deferred-promise@3.0.0': + optional: true + + '@open-draft/logger@0.3.0': + dependencies: + is-node-process: 1.2.0 + outvariant: 1.4.3 + optional: true + + '@open-draft/until@2.1.0': + optional: true + '@oxc-project/types@0.147.0': {} '@rolldown/binding-android-arm-eabi@1.2.6': @@ -1317,6 +1586,13 @@ snapshots: '@standard-schema/spec@1.1.0': {} + '@tanstack/query-core@5.102.8': {} + + '@tanstack/react-query@5.102.8(react@19.2.8)': + dependencies: + '@tanstack/query-core': 5.102.8 + react: 19.2.8 + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.29.8 @@ -1363,6 +1639,14 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/set-cookie-parser@2.4.10': + dependencies: + '@types/node': 24.13.3 + optional: true + + '@types/statuses@2.0.6': + optional: true + '@typescript-eslint/eslint-plugin@8.68.0(@typescript-eslint/parser@8.68.0(eslint@10.9.1)(typescript@6.0.3))(eslint@10.9.1)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 @@ -1471,12 +1755,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.1 - '@vitest/mocker@4.1.11(vite@8.2.2(@types/node@24.13.3))': + '@vitest/mocker@4.1.11(msw@2.15.0(@types/node@24.13.3)(typescript@6.0.3))(vite@8.2.2(@types/node@24.13.3))': dependencies: '@vitest/spy': 4.1.11 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: + msw: 2.15.0(@types/node@24.13.3)(typescript@6.0.3) vite: 8.2.2(@types/node@24.13.3) '@vitest/pretty-format@4.1.11': @@ -1516,6 +1801,14 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ansi-regex@5.0.1: + optional: true + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + optional: true + assertion-error@2.0.1: {} babel-plugin-react-compiler@1.0.0: @@ -1542,8 +1835,29 @@ snapshots: chai@6.2.2: {} + cli-width@4.1.0: + optional: true + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + optional: true + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + optional: true + + color-name@1.1.4: + optional: true + convert-source-map@2.0.0: {} + cookie@1.1.1: + optional: true + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -1562,6 +1876,9 @@ snapshots: electron-to-chromium@1.5.416: {} + emoji-regex@8.0.0: + optional: true + es-module-lexer@2.3.2: {} escalade@3.2.0: {} @@ -1659,6 +1976,19 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-string-truncated-width@3.0.3: + optional: true + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + optional: true + + fast-wrap-ansi@0.2.2: + dependencies: + fast-string-width: 3.0.2 + optional: true + fdir@6.5.0(picomatch@4.0.7): optionalDependencies: picomatch: 4.0.7 @@ -1684,12 +2014,24 @@ snapshots: gensync@1.0.0-beta.2: {} + get-caller-file@2.0.5: + optional: true + glob-parent@6.0.2: dependencies: is-glob: 4.0.3 globals@17.11.0: {} + graphql@16.14.2: + optional: true + + headers-polyfill@5.0.1: + dependencies: + '@types/set-cookie-parser': 2.4.10 + set-cookie-parser: 3.1.2 + optional: true + hermes-estree@0.25.1: {} hermes-parser@0.25.1: @@ -1704,10 +2046,16 @@ snapshots: is-extglob@2.1.1: {} + is-fullwidth-code-point@3.0.0: + optional: true + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 + is-node-process@1.2.0: + optional: true + isexe@2.0.0: {} js-tokens@4.0.0: {} @@ -1798,6 +2146,35 @@ snapshots: ms@2.1.3: {} + msw@2.15.0(@types/node@24.13.3)(typescript@6.0.3): + dependencies: + '@inquirer/confirm': 6.3.1(@types/node@24.13.3) + '@mswjs/interceptors': 0.41.9 + '@open-draft/deferred-promise': 3.0.0 + '@types/statuses': 2.0.6 + cookie: 1.1.1 + graphql: 16.14.2 + headers-polyfill: 5.0.1 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + picocolors: 1.1.1 + rettime: 0.11.11 + statuses: 2.0.2 + strict-event-emitter: 0.5.1 + tough-cookie: 6.0.2 + type-fest: 5.9.0 + until-async: 3.0.2 + yargs: 17.7.3 + optionalDependencies: + typescript: 6.0.3 + transitivePeerDependencies: + - '@types/node' + optional: true + + mute-stream@3.0.0: + optional: true + nanoid@3.3.18: {} natural-compare@1.4.0: {} @@ -1815,6 +2192,9 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + outvariant@1.4.3: + optional: true + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -1827,6 +2207,9 @@ snapshots: path-key@3.1.1: {} + path-to-regexp@6.3.0: + optional: true + pathe@2.0.3: {} picocolors@1.1.1: {} @@ -1850,6 +2233,12 @@ snapshots: react@19.2.8: {} + require-directory@2.1.1: + optional: true + + rettime@0.11.11: + optional: true + rolldown@1.2.6: dependencies: '@oxc-project/types': 0.147.0 @@ -1877,6 +2266,9 @@ snapshots: semver@7.8.5: {} + set-cookie-parser@3.1.2: + optional: true + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -1885,12 +2277,36 @@ snapshots: siginfo@2.0.0: {} + signal-exit@4.1.0: + optional: true + source-map-js@1.2.1: {} stackback@0.0.2: {} + statuses@2.0.2: + optional: true + std-env@4.2.0: {} + strict-event-emitter@0.5.1: + optional: true + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + optional: true + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + optional: true + + tagged-tag@1.0.0: + optional: true + tinybench@2.9.0: {} tinyexec@1.3.0: {} @@ -1902,6 +2318,19 @@ snapshots: tinyrainbow@3.1.1: {} + tldts-core@7.4.12: + optional: true + + tldts@7.4.12: + dependencies: + tldts-core: 7.4.12 + optional: true + + tough-cookie@6.0.2: + dependencies: + tldts: 7.4.12 + optional: true + ts-api-utils@2.5.0(typescript@6.0.3): dependencies: typescript: 6.0.3 @@ -1910,6 +2339,11 @@ snapshots: dependencies: prelude-ls: 1.2.1 + type-fest@5.9.0: + dependencies: + tagged-tag: 1.0.0 + optional: true + typescript-eslint@8.68.0(eslint@10.9.1)(typescript@6.0.3): dependencies: '@typescript-eslint/eslint-plugin': 8.68.0(@typescript-eslint/parser@8.68.0(eslint@10.9.1)(typescript@6.0.3))(eslint@10.9.1)(typescript@6.0.3) @@ -1925,6 +2359,9 @@ snapshots: undici-types@7.18.2: {} + until-async@3.0.2: + optional: true + update-browserslist-db@1.3.2(browserslist@4.28.8): dependencies: browserslist: 4.28.8 @@ -1946,10 +2383,10 @@ snapshots: '@types/node': 24.13.3 fsevents: 2.3.3 - vitest@4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)): + vitest@4.1.11(@types/node@24.13.3)(msw@2.15.0(@types/node@24.13.3)(typescript@6.0.3))(vite@8.2.2(@types/node@24.13.3)): dependencies: '@vitest/expect': 4.1.11 - '@vitest/mocker': 4.1.11(vite@8.2.2(@types/node@24.13.3)) + '@vitest/mocker': 4.1.11(msw@2.15.0(@types/node@24.13.3)(typescript@6.0.3))(vite@8.2.2(@types/node@24.13.3)) '@vitest/pretty-format': 4.1.11 '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 @@ -1984,8 +2421,32 @@ snapshots: word-wrap@1.2.5: {} + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + optional: true + + y18n@5.0.8: + optional: true + yallist@3.1.1: {} + yargs-parser@21.1.1: + optional: true + + yargs@17.7.3: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + optional: true + yocto-queue@0.1.0: {} zod-validation-error@4.0.2(zod@4.5.4): diff --git a/Project-1/pnpm-workspace.yaml b/Project-1/pnpm-workspace.yaml new file mode 100644 index 0000000..64437ab --- /dev/null +++ b/Project-1/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +allowBuilds: + msw: true diff --git a/Project-1/src/App.tsx b/Project-1/src/App.tsx index 9ca8e8d..9c5477a 100644 --- a/Project-1/src/App.tsx +++ b/Project-1/src/App.tsx @@ -1,14 +1,20 @@ -import { useState } from 'react' -import heroImg from './assets/hero.png' -import reactLogo from './assets/react.svg' -import viteLogo from './assets/vite.svg' -import './App.css' +import { useState } from 'react'; +import heroImg from './assets/hero.png'; +import reactLogo from './assets/react.svg'; +import viteLogo from './assets/vite.svg'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import './App.css'; + +const queryClient = new QueryClient(); function App() { const [count, setCount] = useState(0) return ( <> + /** IKKE FJERN ELLER FLYTT DENNE */ + + /* --------------------------------------- */
@@ -115,6 +121,9 @@ function App() {
+ /** IKKE FJERN ELLER FLYTT DENNE */ + + /** ----------------------------- */ ) } diff --git a/Project-1/src/api/client.ts b/Project-1/src/api/client.ts new file mode 100644 index 0000000..1e3d3f5 --- /dev/null +++ b/Project-1/src/api/client.ts @@ -0,0 +1,62 @@ +const BASE_URL = import.meta.env.VITE_API_BASE_URL as string; + +export class ApiError extends Error { + status: number; + data: unknown; + + constructor(message: string, status: number, data: unknown) { + super(message); + this.name = 'ApiError'; + this.status = status; + this.data = data; + } +} + +interface RequestOptions extends Omit { + body?: unknown; +} + +async function request(path: string, options: RequestOptions = {}): Promise { + const url = `${BASE_URL}${path}`; + + const config: RequestInit = { + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers, + }, + body: + options.body !== undefined && typeof options.body !== 'string' + ? JSON.stringify(options.body) + : (options.body as string | undefined), + }; + + const response = await fetch(url, config); + + // Handle empty responses (e.g. 204 No Content) + const text = await response.text(); + const data = text ? JSON.parse(text) : null; + + if (!response.ok) { + throw new ApiError( + data?.message || `Request failed with status ${response.status}`, + response.status, + data + ); + } + + return data as T; +} + +export const api = { + get: (path: string, options?: RequestOptions) => + request(path, { ...options, method: 'GET' }), + post: (path: string, body?: unknown, options?: RequestOptions) => + request(path, { ...options, method: 'POST', body }), + put: (path: string, body?: unknown, options?: RequestOptions) => + request(path, { ...options, method: 'PUT', body }), + patch: (path: string, body?: unknown, options?: RequestOptions) => + request(path, { ...options, method: 'PATCH', body }), + delete: (path: string, options?: RequestOptions) => + request(path, { ...options, method: 'DELETE' }), +}; \ No newline at end of file diff --git a/Project-1/src/api/endpoints.ts b/Project-1/src/api/endpoints.ts new file mode 100644 index 0000000..b75e18f --- /dev/null +++ b/Project-1/src/api/endpoints.ts @@ -0,0 +1,16 @@ +import { api } from './client'; + +export interface Car { + Make_ID: string; + Make_Name: string; + Model_ID: string; + Model_Name: string; +} + +export type CreateUserInput = Omit; +export type UpdateUserInput = Partial; + +export const carsApi = { + getAllMakes: () => api.get('/getallmakes?format=json'), + getAllModelsByMake: (Make_ID: string) => api.get(`/GetModelsForMake/${Make_ID}?format=json`), +}; \ No newline at end of file diff --git a/Project-1/src/hooks/useCars.ts b/Project-1/src/hooks/useCars.ts new file mode 100644 index 0000000..cefe5f3 --- /dev/null +++ b/Project-1/src/hooks/useCars.ts @@ -0,0 +1,21 @@ +import { useQuery } from '@tanstack/react-query'; +import { carsApi } from '../api/endpoints'; + +export const userKeys = { + all: ['users'] as const, + detail: (id: string) => ['users', id] as const, +}; + +export function useGetAllModels() { + return useQuery({ + queryKey: userKeys.all, + queryFn: carsApi.getAllMakes, + }); +} + +export function useGetAllModelsByMake(id: string) { + return useQuery({ + queryKey: userKeys.detail(id), + queryFn: () => carsApi.getAllModelsByMake(id), + }); +} \ No newline at end of file