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