Skip to content

Commit c4facd8

Browse files
committed
Merge branch 'trpc-react-node-crud'
2 parents 86f99a8 + 67b2b88 commit c4facd8

38 files changed

+4970
-2416
lines changed

packages/client/package.json

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,33 @@
33
"version": "0.1.0",
44
"private": true,
55
"dependencies": {
6-
"@hookform/resolvers": "^2.9.3",
7-
"@testing-library/jest-dom": "^5.16.4",
8-
"@testing-library/react": "^13.3.0",
9-
"@testing-library/user-event": "^13.5.0",
10-
"@trpc/client": "^9.25.3",
11-
"@trpc/react": "^9.25.3",
12-
"@types/jest": "^27.5.2",
13-
"@types/node": "^16.11.41",
14-
"@types/react": "^18.0.14",
15-
"@types/react-dom": "^18.0.5",
6+
"@hookform/resolvers": "^2.9.10",
7+
"@tanstack/react-query": "^4.13.0",
8+
"@tanstack/react-query-devtools": "^4.13.0",
9+
"@testing-library/jest-dom": "^5.16.5",
10+
"@testing-library/react": "^13.4.0",
11+
"@testing-library/user-event": "^14.4.3",
12+
"@trpc/client": "^10.0.0-proxy-beta.26",
13+
"@trpc/react": "^9.27.4",
14+
"@trpc/react-query": "^10.0.0-proxy-beta.26",
15+
"@trpc/server": "^10.0.0-proxy-beta.26",
16+
"@types/jest": "^29.2.0",
17+
"@types/node": "^18.11.6",
18+
"@types/react": "^18.0.23",
19+
"@types/react-dom": "^18.0.7",
1620
"react": "^18.2.0",
1721
"react-cookie": "^4.1.1",
1822
"react-dom": "^18.2.0",
19-
"react-hook-form": "^7.33.0",
20-
"react-query": "^3.39.1",
21-
"react-router-dom": "^6.3.0",
23+
"react-hook-form": "^7.38.0",
24+
"react-router-dom": "^6.4.2",
2225
"react-scripts": "5.0.1",
23-
"react-toastify": "^9.0.5",
24-
"server": "1.0.0",
25-
"typescript": "^4.7.4",
26-
"web-vitals": "^2.1.4",
27-
"zod": "^3.17.3"
26+
"react-toastify": "^9.0.8",
27+
"server": "^1.0.37",
28+
"tailwind-merge": "^1.7.0",
29+
"typescript": "^4.8.4",
30+
"web-vitals": "^3.0.4",
31+
"zod": "^3.19.1",
32+
"zustand": "^4.1.3"
2833
},
2934
"scripts": {
3035
"start": "react-scripts start",
@@ -51,8 +56,8 @@
5156
]
5257
},
5358
"devDependencies": {
54-
"autoprefixer": "^10.4.7",
55-
"postcss": "^8.4.14",
56-
"tailwindcss": "^3.1.4"
59+
"autoprefixer": "^10.4.12",
60+
"postcss": "^8.4.18",
61+
"tailwindcss": "^3.2.1"
5762
}
5863
}

packages/client/public/index.html

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,34 +10,16 @@
1010
content="Web site created using create-react-app"
1111
/>
1212
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
13-
<!--
14-
manifest.json provides metadata used when your web app is installed on a
15-
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
16-
-->
1713
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
18-
<!--
19-
Notice the use of %PUBLIC_URL% in the tags above.
20-
It will be replaced with the URL of the `public` folder during the build.
21-
Only files inside the `public` folder can be referenced from the HTML.
22-
23-
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
24-
work correctly both with client-side routing and a non-root public URL.
25-
Learn how to configure a non-root public URL by running `npm run build`.
26-
-->
14+
<link
15+
href="https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css"
16+
rel="stylesheet"
17+
/>
2718
<title>React App</title>
2819
</head>
2920
<body>
3021
<noscript>You need to enable JavaScript to run this app.</noscript>
3122
<div id="root"></div>
32-
<!--
33-
This HTML file is a template.
34-
If you open it directly in the browser, you will see an empty page.
35-
36-
You can add webfonts, meta tags, or analytics to this file.
37-
The build step will place the bundled scripts into the <body> tag.
38-
39-
To begin the development, run `npm start` or `yarn start`.
40-
To create a production bundle, use `npm run build` or `yarn build`.
41-
-->
23+
<div id="post-modal"></div>
4224
</body>
4325
</html>

packages/client/src/App.tsx

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import { useState } from 'react';
2-
import { QueryClientProvider, QueryClient } from 'react-query';
3-
import { ReactQueryDevtools } from 'react-query/devtools';
4-
import { useRoutes } from 'react-router-dom';
5-
import { getFetch } from '@trpc/client';
6-
import routes from './router';
7-
import { trpc } from './trpc';
8-
import AuthMiddleware from './middleware/AuthMiddleware';
1+
import { useState } from "react";
2+
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
3+
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
4+
import { useRoutes } from "react-router-dom";
5+
import { getFetch, httpBatchLink, loggerLink } from "@trpc/client";
6+
import routes from "./router";
7+
import { trpc } from "./trpc";
8+
import AuthMiddleware from "./middleware/AuthMiddleware";
9+
import { CookiesProvider } from "react-cookie";
910

1011
function AppContent() {
1112
const content = useRoutes(routes);
@@ -23,27 +24,35 @@ function App() {
2324
},
2425
})
2526
);
27+
2628
const [trpcClient] = useState(() =>
2729
trpc.createClient({
28-
url: 'http://localhost:8000/api/trpc',
29-
fetch: async (input, init?) => {
30-
const fetch = getFetch();
31-
return fetch(input, {
32-
...init,
33-
credentials: 'include',
34-
});
35-
},
30+
links: [
31+
loggerLink(),
32+
httpBatchLink({
33+
url: "http://localhost:8000/api/trpc",
34+
fetch: async (input, init?) => {
35+
const fetch = getFetch();
36+
return fetch(input, {
37+
...init,
38+
credentials: "include",
39+
});
40+
},
41+
}),
42+
],
3643
})
3744
);
3845
return (
39-
<trpc.Provider client={trpcClient} queryClient={queryClient}>
40-
<QueryClientProvider client={queryClient}>
41-
<AuthMiddleware>
42-
<AppContent />
43-
</AuthMiddleware>
44-
<ReactQueryDevtools initialIsOpen={false} />
45-
</QueryClientProvider>
46-
</trpc.Provider>
46+
<CookiesProvider>
47+
<trpc.Provider client={trpcClient} queryClient={queryClient}>
48+
<QueryClientProvider client={queryClient}>
49+
<AuthMiddleware>
50+
<AppContent />
51+
</AuthMiddleware>
52+
<ReactQueryDevtools initialIsOpen={false} />
53+
</QueryClientProvider>
54+
</trpc.Provider>
55+
</CookiesProvider>
4756
);
4857
}
4958

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import React, { useCallback } from 'react';
2+
import { Controller, useController, useFormContext } from 'react-hook-form';
3+
import useStore from '../store';
4+
import Spinner from './Spinner';
5+
6+
type FileUpLoaderProps = {
7+
name: string;
8+
};
9+
const FileUpLoader: React.FC<FileUpLoaderProps> = ({ name }) => {
10+
const {
11+
control,
12+
formState: { errors },
13+
} = useFormContext();
14+
const { field } = useController({ name, control });
15+
const store = useStore();
16+
17+
const onFileDrop = useCallback(
18+
async (e: React.SyntheticEvent<EventTarget>) => {
19+
const target = e.target as HTMLInputElement;
20+
if (!target.files || target.files.length === 0) return;
21+
const newFile = Object.values(target.files).map((file: File) => file);
22+
const formData = new FormData();
23+
formData.append('file', newFile[0]);
24+
formData.append('upload_preset', 'trpc-api');
25+
26+
store.setUploadingImage(true);
27+
const data = await fetch(
28+
'https://api.cloudinary.com/v1_1/Codevo/image/upload',
29+
{
30+
method: 'POST',
31+
body: formData,
32+
}
33+
)
34+
.then((res) => {
35+
store.setUploadingImage(false);
36+
37+
return res.json();
38+
})
39+
.catch((err) => {
40+
store.setUploadingImage(false);
41+
console.log(err);
42+
});
43+
44+
if (data.secure_url) {
45+
field.onChange(data.secure_url);
46+
}
47+
},
48+
49+
[field, store]
50+
);
51+
52+
return (
53+
<Controller
54+
name={name}
55+
defaultValue=''
56+
control={control}
57+
render={({ field: { name, onBlur, ref } }) => (
58+
<>
59+
<div className='mb-2 flex justify-between items-center'>
60+
<div>
61+
<span className='block mb-2'>Choose profile photo</span>
62+
<input
63+
className='block text-sm mb-2 text-slate-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-violet-50 file:text-violet-700 hover:file:bg-violet-100'
64+
type='file'
65+
name={name}
66+
onBlur={onBlur}
67+
ref={ref}
68+
onChange={onFileDrop}
69+
multiple={false}
70+
accept='image/jpg, image/png, image/jpeg'
71+
/>
72+
</div>
73+
<div>
74+
{store.uploadingImage && <Spinner color='text-yellow-400' />}
75+
</div>
76+
</div>
77+
<p
78+
className={`text-red-500 text-xs italic mb-2 ${
79+
errors[name] ? 'visible' : 'invisible'
80+
}`}
81+
>
82+
{errors[name] && (errors[name]?.message as string)}
83+
</p>
84+
</>
85+
)}
86+
/>
87+
);
88+
};
89+
90+
export default FileUpLoader;

0 commit comments

Comments
 (0)