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