From ea8440f86f0b5e39d141754a406d35befbd7e80d Mon Sep 17 00:00:00 2001 From: Lars Hampe Date: Thu, 3 Oct 2024 10:52:50 +0200 Subject: [PATCH] feat(app): add react query and changelog hooks --- apps/app/package.json | 2 + apps/app/src/hooks/useChangelog.ts | 79 +++++++++++++++++++ apps/app/src/main.tsx | 7 +- apps/app/src/routes/changelog/index.lazy.tsx | 28 ++++++- apps/app/src/utils/queryFetch.ts | 25 ++++++ bun.lockb | Bin 415000 -> 419080 bytes 6 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 apps/app/src/hooks/useChangelog.ts create mode 100644 apps/app/src/utils/queryFetch.ts diff --git a/apps/app/package.json b/apps/app/package.json index 0404338..fbd65b7 100644 --- a/apps/app/package.json +++ b/apps/app/package.json @@ -12,7 +12,9 @@ "dependencies": { "@boring.tools/ui": "workspace:*", "@clerk/clerk-react": "^5.9.4", + "@tanstack/react-query": "^5.59.0", "@tanstack/react-router": "^1.58.15", + "axios": "^1.7.7", "lucide-react": "^0.446.0", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/apps/app/src/hooks/useChangelog.ts b/apps/app/src/hooks/useChangelog.ts new file mode 100644 index 0000000..74ee00f --- /dev/null +++ b/apps/app/src/hooks/useChangelog.ts @@ -0,0 +1,79 @@ +import type { + ChangelogCreateInput, + ChangelogOutput, +} from '@boring.tools/schema' +import { useAuth } from '@clerk/clerk-react' +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' +import type { z } from 'zod' +import { queryFetch } from '../utils/queryFetch' + +type Changelog = z.infer +type ChangelogCreate = z.infer + +export const useChangelogList = () => { + const { getToken } = useAuth() + return useQuery({ + queryKey: ['changelogList'], + queryFn: async (): Promise> => + await queryFetch({ + path: 'changelog', + method: 'get', + token: await getToken(), + }), + }) +} + +export const useChangelogById = ({ id }: { id: string }) => { + const { getToken } = useAuth() + + return useQuery({ + queryKey: ['changelogById', id], + queryFn: async (): Promise> => + await queryFetch({ + path: `changelog/${id}`, + method: 'get', + token: await getToken(), + }), + }) +} + +export const useChangelogCreate = () => { + const { getToken } = useAuth() + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async ( + payload: ChangelogCreate, + ): Promise> => + await queryFetch({ + path: 'changelog', + data: payload, + method: 'post', + token: await getToken(), + }), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['changelogList'] }) + }, + }) +} + +export const useChangelogRemove = () => { + const { getToken } = useAuth() + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async ({ + id, + }: { id: string }): Promise> => + await queryFetch({ + path: `changelog/${id}`, + method: 'delete', + token: await getToken(), + }), + onSuccess: (data) => { + queryClient.invalidateQueries({ + queryKey: ['changelogList', 'changelogById', data.id], + }) + }, + }) +} diff --git a/apps/app/src/main.tsx b/apps/app/src/main.tsx index 57e58b5..0ad22fa 100644 --- a/apps/app/src/main.tsx +++ b/apps/app/src/main.tsx @@ -5,12 +5,15 @@ import { StrictMode } from 'react' import ReactDOM from 'react-dom/client' import './base.css' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' // Import the generated route tree import { routeTree } from './routeTree.gen' // Create a new router instance const router = createRouter({ routeTree }) +const queryClient = new QueryClient() + // Register the router instance for type safety declare module '@tanstack/react-router' { interface Register { @@ -33,7 +36,9 @@ if (!rootElement.innerHTML) { - + + + , diff --git a/apps/app/src/routes/changelog/index.lazy.tsx b/apps/app/src/routes/changelog/index.lazy.tsx index a631627..f1f3194 100644 --- a/apps/app/src/routes/changelog/index.lazy.tsx +++ b/apps/app/src/routes/changelog/index.lazy.tsx @@ -1,5 +1,31 @@ import { createLazyFileRoute } from '@tanstack/react-router' +import { useChangelogList } from '../../hooks/useChangelog' + +const Component = () => { + const { data, error, isPending } = useChangelogList() + + if (error) { + return ( +
+

Changelogs

+

Please try again later

+
+ ) + } + + return ( +
+

Changelogs

+ + {!isPending && + data && + data.map((changelog) => { + return
{changelog.title}
+ })} +
+ ) +} export const Route = createLazyFileRoute('/changelog/')({ - component: () =>
Hello /changelog/!
, + component: Component, }) diff --git a/apps/app/src/utils/queryFetch.ts b/apps/app/src/utils/queryFetch.ts new file mode 100644 index 0000000..c249b67 --- /dev/null +++ b/apps/app/src/utils/queryFetch.ts @@ -0,0 +1,25 @@ +import axios from 'axios' + +type Fetch = { + path: string + method: 'get' | 'post' | 'put' | 'delete' + data?: unknown + token?: string | null +} + +const url = + import.meta.env.NODE_ENV === 'production' + ? 'https://api.boring.tools' + : 'http://localhost:3000' + +export const queryFetch = async ({ path, method, data, token }: Fetch) => { + const response = await axios({ + method, + url: `${url}/v1/${path}`, + data, + headers: { + Authorization: `Bearer ${token}`, + }, + }) + return response.data +} diff --git a/bun.lockb b/bun.lockb index 3193d2b1795a25269bedb2ae5004e9460cab907a..e35d768cb3015b00744015f206e485f818f1b0ae 100755 GIT binary patch delta 16621 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