fork of hey-api/openapi-ts because I need some additional things
1import './App.css';
2
3import * as Form from '@radix-ui/react-form';
4import { DownloadIcon, PlusIcon, ReloadIcon } from '@radix-ui/react-icons';
5import {
6 Avatar,
7 Box,
8 Button,
9 Card,
10 Container,
11 Flex,
12 Heading,
13 Section,
14 Text,
15 TextField,
16} from '@radix-ui/themes';
17import { useState } from 'react';
18
19import { createClient } from './client/client';
20import { PetSchema } from './client/schemas.gen';
21import { addPet, getPetById, updatePet } from './client/sdk.gen';
22import type { Pet } from './client/types.gen';
23
24const localClient = createClient({
25 // set default base url for requests made by this client
26 baseUrl: 'https://petstore3.swagger.io/api/v3',
27 /**
28 * Set default headers only for requests made by this client. This is to
29 * demonstrate local clients and their configuration taking precedence over
30 * internal service client.
31 */
32 headers: {
33 Authorization: 'Bearer <token_from_local_client>',
34 },
35});
36
37localClient.interceptors.request.use((request, options) => {
38 // Middleware is great for adding authorization tokens to requests made to
39 // protected paths. Headers are set randomly here to allow surfacing the
40 // default headers, too.
41 if (
42 options.url === '/pet/{petId}' &&
43 options.method === 'GET' &&
44 Math.random() < 0.5
45 ) {
46 request.headers.set('Authorization', 'Bearer <token_from_interceptor>');
47 }
48 return request;
49});
50
51localClient.interceptors.error.use((error) => {
52 console.log(error);
53 return error;
54});
55
56function App() {
57 const [pet, setPet] = useState<Pet>();
58 const [isRequiredNameError, setIsRequiredNameError] = useState(false);
59
60 const onAddPet = async (formData: FormData) => {
61 // simple form field validation to demonstrate using schemas
62 if (PetSchema.required.includes('name') && !formData.get('name')) {
63 setIsRequiredNameError(true);
64 return;
65 }
66
67 const { data, error } = await addPet({
68 body: {
69 category: {
70 id: 0,
71 name: formData.get('category') as string,
72 },
73 id: 0,
74 name: formData.get('name') as string,
75 photoUrls: ['string'],
76 status: 'available',
77 tags: [
78 {
79 id: 0,
80 name: 'string',
81 },
82 ],
83 },
84 });
85 if (error) {
86 console.log(error);
87 return;
88 }
89 setPet(data!);
90 setIsRequiredNameError(false);
91 };
92
93 const onGetPetById = async () => {
94 const { data, error } = await getPetById({
95 client: localClient,
96 path: {
97 // random id 1-10
98 petId: Math.floor(Math.random() * (10 - 1 + 1) + 1),
99 },
100 });
101 if (error) {
102 console.log(error);
103 return;
104 }
105 setPet(data!);
106 };
107
108 const onUpdatePet = async () => {
109 const { data, error } = await updatePet({
110 body: {
111 category: {
112 id: 0,
113 name: 'Cats',
114 },
115 id: 2,
116 name: 'Updated Kitty',
117 photoUrls: ['string'],
118 status: 'available',
119 tags: [
120 {
121 id: 0,
122 name: 'string',
123 },
124 ],
125 },
126 // setting headers per request
127 headers: {
128 Authorization: 'Bearer <token_from_method>',
129 },
130 });
131 if (error) {
132 console.log(error);
133 return;
134 }
135 setPet(data!);
136 };
137
138 return (
139 <Box
140 style={{ background: 'var(--gray-a2)', borderRadius: 'var(--radius-3)' }}
141 >
142 <Container size="1">
143 <Section size="1" />
144 <Flex align="center">
145 <a className="shrink-0" href="https://heyapi.dev/" target="_blank">
146 <img
147 src="https://heyapi.dev/assets/raw/logo.png"
148 className="h-16 w-16 transition duration-300 will-change-auto"
149 alt="Hey API logo"
150 />
151 </a>
152 <Heading>@hey-api/openapi-ts 🤝 Fetch API</Heading>
153 </Flex>
154 <Section size="1" />
155 <Flex direction="column" gapY="2">
156 <Box maxWidth="240px">
157 <Card>
158 <Flex gap="3" align="center">
159 <Avatar
160 size="3"
161 src={pet?.photoUrls[0]}
162 radius="full"
163 fallback={pet?.name.slice(0, 1) ?? 'N'}
164 />
165 <Box>
166 <Text as="div" size="2" weight="bold">
167 Name: {pet?.name ?? 'N/A'}
168 </Text>
169 <Text as="div" size="2" color="gray">
170 Category: {pet?.category?.name ?? 'N/A'}
171 </Text>
172 </Box>
173 </Flex>
174 </Card>
175 </Box>
176 <Button onClick={onGetPetById}>
177 <DownloadIcon /> Get Random Pet
178 </Button>
179 </Flex>
180 <Section size="1" />
181 <Flex direction="column" gapY="2">
182 <Form.Root
183 className="w-[260px]"
184 onSubmit={(event) => {
185 event.preventDefault();
186 onAddPet(new FormData(event.currentTarget));
187 }}
188 >
189 <Form.Field className="grid mb-[10px]" name="email">
190 <div className="flex items-baseline justify-between">
191 <Form.Label className="text-[15px] font-medium leading-[35px] text-white">
192 Name
193 </Form.Label>
194 {isRequiredNameError && (
195 <Form.Message className="text-[13px] text-white opacity-[0.8]">
196 Please enter a name
197 </Form.Message>
198 )}
199 </div>
200 <Form.Control asChild>
201 <TextField.Root placeholder="Kitty" name="name" type="text" />
202 </Form.Control>
203 </Form.Field>
204 <Form.Field className="grid mb-[10px]" name="question">
205 <div className="flex items-baseline justify-between">
206 <Form.Label className="text-[15px] font-medium leading-[35px] text-white">
207 Category
208 </Form.Label>
209 <Form.Message
210 className="text-[13px] text-white opacity-[0.8]"
211 match="valueMissing"
212 >
213 Please enter a category
214 </Form.Message>
215 </div>
216 <Form.Control asChild>
217 <TextField.Root
218 placeholder="Cats"
219 name="category"
220 type="text"
221 required
222 />
223 </Form.Control>
224 </Form.Field>
225 <Flex gapX="2">
226 <Form.Submit asChild>
227 <Button type="submit">
228 <PlusIcon /> Add Pet
229 </Button>
230 </Form.Submit>
231 <Button onClick={onUpdatePet} type="button">
232 <ReloadIcon /> Update Pet
233 </Button>
234 </Flex>
235 </Form.Root>
236 </Flex>
237 <Section size="1" />
238 </Container>
239 </Box>
240 );
241}
242
243export default App;