Begin consumption of API

This commit is contained in:
2024-07-24 01:25:12 +02:00
parent b40ae7d701
commit 1b62b890b9
15 changed files with 1023 additions and 526 deletions
+61
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;
+20 -2
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>
);
}
+22 -3
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"
}
}
}
+12 -2
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 />
</>
+40
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>
</>
);
}
+18
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;
+6
View File
@@ -0,0 +1,6 @@
import { User } from './User';
export interface LoginResponse {
token: string;
user: User;
}
+5
View File
@@ -0,0 +1,5 @@
export interface Timestamp {
date: string;
timezone_type: number;
timezone: string;
}
+12
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;
}