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