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