fork of hey-api/openapi-ts because I need some additional things
at main 243 lines 7.0 kB view raw
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;