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