Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Project-1/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"preview": "vite preview"
},
"dependencies": {
"@tanstack/react-query": "^5.102.8",
"react": "^19.2.8",
"react-dom": "^19.2.8"
},
Expand Down
469 changes: 465 additions & 4 deletions Project-1/pnpm-lock.yaml

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Project-1/pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
allowBuilds:
msw: true
19 changes: 14 additions & 5 deletions Project-1/src/App.tsx
Original file line number Diff line number Diff line change
@@ -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 */
<QueryClientProvider client={queryClient}>
/* --------------------------------------- */
<section id="center">
<div className="hero">
<img src={heroImg} className="base" width="170" height="179" alt="" />
Expand Down Expand Up @@ -115,6 +121,9 @@ function App() {

<div className="ticks"></div>
<section id="spacer"></section>
/** IKKE FJERN ELLER FLYTT DENNE */
</QueryClientProvider>
/** ----------------------------- */
</>
)
}
Expand Down
62 changes: 62 additions & 0 deletions Project-1/src/api/client.ts
Original file line number Diff line number Diff line change
@@ -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<RequestInit, 'body'> {
body?: unknown;
}

async function request<T>(path: string, options: RequestOptions = {}): Promise<T> {
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: <T>(path: string, options?: RequestOptions) =>
request<T>(path, { ...options, method: 'GET' }),
post: <T>(path: string, body?: unknown, options?: RequestOptions) =>
request<T>(path, { ...options, method: 'POST', body }),
put: <T>(path: string, body?: unknown, options?: RequestOptions) =>
request<T>(path, { ...options, method: 'PUT', body }),
patch: <T>(path: string, body?: unknown, options?: RequestOptions) =>
request<T>(path, { ...options, method: 'PATCH', body }),
delete: <T>(path: string, options?: RequestOptions) =>
request<T>(path, { ...options, method: 'DELETE' }),
};
16 changes: 16 additions & 0 deletions Project-1/src/api/endpoints.ts
Original file line number Diff line number Diff line change
@@ -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<Car, 'Make_ID' | 'Model_ID'>;
export type UpdateUserInput = Partial<CreateUserInput>;

export const carsApi = {
getAllMakes: () => api.get<Car[]>('/getallmakes?format=json'),
getAllModelsByMake: (Make_ID: string) => api.get<Car>(`/GetModelsForMake/${Make_ID}?format=json`),
};
21 changes: 21 additions & 0 deletions Project-1/src/hooks/useCars.ts
Original file line number Diff line number Diff line change
@@ -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),
});
}