├── .gitignore ├── README.md ├── codegen.yml ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── App.css ├── App.test.tsx ├── App.tsx ├── components │ ├── LaunchList │ │ ├── LaunchList.tsx │ │ ├── index.tsx │ │ ├── query.ts │ │ └── styles.css │ └── LaunchProfile │ │ ├── LaunchProfile.tsx │ │ ├── index.tsx │ │ ├── query.ts │ │ └── styles.css ├── generated │ └── graphql.tsx ├── index.css ├── index.tsx ├── logo.svg ├── react-app-env.d.ts ├── reportWebVitals.ts └── setupTests.ts ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### [Skilled.dev - Coding Interview Course](https://skilled.dev) 2 | Master the programming interview [Learn More →](https://skilled.dev) 3 | 4 | ### [Software Engineer Resume Builder](https://gitconnected.com/resume-builder) 5 | Effortlessly generate a developer resume. [Learn More →](https://gitconnected.com/resume-builder) 6 | 7 | # React App with GraphQL and TypeScript 8 | 9 | A fully functioning React app using a [GraphQL implemention](https://spacexdata.herokuapp.com/graphql) of the [public SpaceX API](https://docs.spacexdata.com/). 10 | 11 | Follow the step-by-step [tutorial](https://levelup.gitconnected.com/build-a-graphql-react-app-with-typescript-9661f908b26?source=friends_link&sk=efe66b87d9f785c04b3e68f05ed25b7d) 12 | 13 | ## Overview 14 | 15 | - Create React App with the TypeScript setting to bootstrap the App 16 | - Implement React Apollo using React Hooks to make GraphQL queries 17 | - Automatically generate TypeScript types for our queries and components based off the GraphQL schema 18 | 19 | ## Find coding tutorials 20 | 21 | ### [React](https://gitconnected.com/learn/react) | [TypeScript](https://gitconnected.com/learn/typescript) | [GraphQL](https://gitconnected.com/learn/graphql) 22 | -------------------------------------------------------------------------------- /codegen.yml: -------------------------------------------------------------------------------- 1 | overwrite: true 2 | schema: "https://spacexdata.herokuapp.com/graphql" 3 | documents: "./src/components/**/*.{ts,tsx}" 4 | generates: 5 | src/generated/graphql.tsx: 6 | plugins: 7 | - "typescript" 8 | - "typescript-operations" 9 | - "typescript-react-apollo" 10 | config: 11 | withHooks: true 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graphql-typescript-react", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@apollo/client": "^3.4.4", 7 | "@testing-library/jest-dom": "^5.11.4", 8 | "@testing-library/react": "^11.1.0", 9 | "@testing-library/user-event": "^12.1.10", 10 | "@types/jest": "^26.0.15", 11 | "@types/node": "^12.0.0", 12 | "@types/react": "^17.0.0", 13 | "@types/react-dom": "^17.0.0", 14 | "graphql": "^15.5.1", 15 | "react": "^17.0.2", 16 | "react-dom": "^17.0.2", 17 | "react-scripts": "4.0.3", 18 | "typescript": "^4.1.2", 19 | "web-vitals": "^1.0.1" 20 | }, 21 | "scripts": { 22 | "start": "react-scripts start", 23 | "build": "react-scripts build", 24 | "test": "react-scripts test", 25 | "eject": "react-scripts eject", 26 | "codegen": "graphql-codegen --config codegen.yml" 27 | }, 28 | "eslintConfig": { 29 | "extends": [ 30 | "react-app", 31 | "react-app/jest" 32 | ] 33 | }, 34 | "browserslist": { 35 | "production": [ 36 | ">0.2%", 37 | "not dead", 38 | "not op_mini all" 39 | ], 40 | "development": [ 41 | "last 1 chrome version", 42 | "last 1 firefox version", 43 | "last 1 safari version" 44 | ] 45 | }, 46 | "devDependencies": { 47 | "@graphql-codegen/cli": "2.0.1", 48 | "@graphql-codegen/typescript-react-apollo": "3.0.0", 49 | "@graphql-codegen/typescript-operations": "2.0.1", 50 | "@graphql-codegen/typescript": "2.0.0" 51 | } 52 | } -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeathKnight813/graphql-react-typescript/831b0ba36e62ed2646f7d8ebb6e47c9b5ee5f871/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeathKnight813/graphql-react-typescript/831b0ba36e62ed2646f7d8ebb6e47c9b5ee5f871/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeathKnight813/graphql-react-typescript/831b0ba36e62ed2646f7d8ebb6e47c9b5ee5f871/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | display: flex; 3 | width: 100vw; 4 | height: 100vh; 5 | overflow: hidden; 6 | } 7 | -------------------------------------------------------------------------------- /src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | render(); 7 | const linkElement = screen.getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import LaunchList from './components/LaunchList'; 3 | import LaunchProfile from './components/LaunchProfile'; 4 | 5 | import './App.css'; 6 | 7 | const App = () => { 8 | const [id, setId] = React.useState(42); 9 | const handleIdChange = React.useCallback((newId) => { 10 | setId(newId); 11 | }, []); 12 | 13 | return ( 14 |
15 | 16 | 17 |
18 | ); 19 | }; 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /src/components/LaunchList/LaunchList.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { LaunchListQuery } from '../../generated/graphql'; 3 | import './styles.css'; 4 | 5 | export interface OwnProps { 6 | handleIdChange: (newId: number) => void; 7 | } 8 | 9 | interface Props extends OwnProps { 10 | data: LaunchListQuery; 11 | } 12 | 13 | const className = 'LaunchList'; 14 | 15 | const LaunchList: React.FC = ({ data, handleIdChange }) => ( 16 |
17 |

Launches

18 |
    19 | {!!data.launches && 20 | data.launches.map( 21 | (launch, i) => 22 | !!launch && ( 23 |
  1. handleIdChange(launch.flight_number!)} 27 | > 28 | {launch.mission_name} ({launch.launch_year}) 29 |
  2. 30 | ), 31 | )} 32 |
33 |
34 | ); 35 | 36 | export default LaunchList; 37 | -------------------------------------------------------------------------------- /src/components/LaunchList/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useLaunchListQuery } from '../../generated/graphql'; 3 | import LaunchList, { OwnProps } from './LaunchList'; 4 | 5 | const LaunchListContainer: React.FC = (props) => { 6 | const { data, error, loading } = useLaunchListQuery(); 7 | 8 | if (loading) { 9 | return
Loading...
; 10 | } 11 | 12 | if (error || !data) { 13 | return
ERROR
; 14 | } 15 | 16 | return ; 17 | }; 18 | 19 | export default LaunchListContainer; 20 | -------------------------------------------------------------------------------- /src/components/LaunchList/query.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client'; 2 | 3 | export const QUERY_LAUNCH_LIST = gql` 4 | query LaunchList { 5 | launches { 6 | flight_number 7 | mission_name 8 | launch_year 9 | } 10 | } 11 | `; 12 | -------------------------------------------------------------------------------- /src/components/LaunchList/styles.css: -------------------------------------------------------------------------------- 1 | .LaunchList { 2 | height: 100vh; 3 | overflow: hidden auto; 4 | background-color: #ececec; 5 | width: 300px; 6 | padding-left: 20px; 7 | padding-right: 20px; 8 | } 9 | 10 | .LaunchList__list { 11 | list-style: none; 12 | margin: 0; 13 | padding: 0; 14 | } 15 | 16 | .LaunchList__item { 17 | padding-top: 20px; 18 | padding-bottom: 20px; 19 | border-top: 1px solid #919191; 20 | cursor: pointer; 21 | } 22 | -------------------------------------------------------------------------------- /src/components/LaunchProfile/LaunchProfile.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { LaunchProfileQuery } from '../../generated/graphql'; 3 | import './styles.css'; 4 | 5 | interface Props { 6 | data: LaunchProfileQuery; 7 | } 8 | 9 | const className = 'LaunchProfile'; 10 | 11 | const LaunchProfile: React.FC = ({ data }) => { 12 | if (!data.launch) { 13 | return
No launch available
; 14 | } 15 | 16 | return ( 17 |
18 |
19 | Flight {data.launch.flight_number}: 20 | {data.launch.launch_success ? ( 21 | Success 22 | ) : ( 23 | Failed 24 | )} 25 |
26 |

27 | {data.launch.mission_name} 28 | {data.launch.rocket && 29 | ` (${data.launch.rocket.rocket_name} | ${data.launch.rocket.rocket_type})`} 30 |

31 |

{data.launch.details}

32 | {!!data.launch.links && !!data.launch.links.flickr_images && ( 33 |
34 | {data.launch.links.flickr_images.map((image, i) => 35 | image ? ( 36 | {`${data.launch?.mission_name} 42 | ) : null, 43 | )} 44 |
45 | )} 46 |
47 | ); 48 | }; 49 | 50 | export default LaunchProfile; 51 | -------------------------------------------------------------------------------- /src/components/LaunchProfile/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useLaunchProfileQuery } from '../../generated/graphql'; 3 | import LaunchProfile from './LaunchProfile'; 4 | 5 | interface OwnProps { 6 | id: number; 7 | } 8 | 9 | const LaunchProfileContainer: React.FC = ({ id }) => { 10 | const { data, error, loading, refetch } = useLaunchProfileQuery({ 11 | variables: { id: String(id) }, 12 | }); 13 | React.useEffect(() => { 14 | refetch({ id: String(id) }); 15 | }, [refetch, id]); 16 | 17 | if (loading) { 18 | return
Loading...
; 19 | } 20 | 21 | if (error) { 22 | return
ERROR
; 23 | } 24 | 25 | if (!data) { 26 | return
Select a flight from the panel
; 27 | } 28 | 29 | return ; 30 | }; 31 | 32 | export default LaunchProfileContainer; 33 | -------------------------------------------------------------------------------- /src/components/LaunchProfile/query.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client'; 2 | 3 | export const QUERY_LAUNCH_PROFILE = gql` 4 | query LaunchProfile($id: String!) { 5 | launch(id: $id) { 6 | flight_number 7 | mission_name 8 | launch_year 9 | launch_success 10 | details 11 | launch_site { 12 | site_name 13 | } 14 | rocket { 15 | rocket_name 16 | rocket_type 17 | } 18 | links { 19 | flickr_images 20 | } 21 | } 22 | } 23 | `; 24 | -------------------------------------------------------------------------------- /src/components/LaunchProfile/styles.css: -------------------------------------------------------------------------------- 1 | .LaunchProfile { 2 | height: 100vh; 3 | max-height: 100%; 4 | width: calc(100vw - 300px); 5 | overflow: hidden auto; 6 | padding-left: 20px; 7 | padding-right: 20px; 8 | } 9 | 10 | .LaunchProfile__status { 11 | margin-top: 40px; 12 | } 13 | 14 | .LaunchProfile__title { 15 | margin-top: 0; 16 | margin-bottom: 4px; 17 | } 18 | 19 | .LaunchProfile__success { 20 | color: #2cb84b; 21 | } 22 | 23 | .LaunchProfile__failed { 24 | color: #ff695e; 25 | } 26 | 27 | .LaunchProfile__image-list { 28 | display: grid; 29 | grid-gap: 20px; 30 | grid-template-columns: repeat(2, 1fr); 31 | margin-top: 40px; 32 | padding-bottom: 100px; 33 | } 34 | 35 | .LaunchProfile__image { 36 | width: 100%; 37 | } 38 | -------------------------------------------------------------------------------- /src/generated/graphql.tsx: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client'; 2 | import * as Apollo from '@apollo/client'; 3 | export type Maybe = T | null; 4 | export type Exact = { [K in keyof T]: T[K] }; 5 | export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; 6 | export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; 7 | const defaultOptions = {} 8 | /** All built-in and custom scalars, mapped to their actual values */ 9 | export type Scalars = { 10 | ID: string; 11 | String: string; 12 | Boolean: boolean; 13 | Int: number; 14 | Float: number; 15 | /** The `Upload` scalar type represents a file upload. */ 16 | Upload: any; 17 | }; 18 | 19 | 20 | export type BasicMission = { 21 | __typename?: 'BasicMission'; 22 | name?: Maybe; 23 | flight?: Maybe; 24 | }; 25 | 26 | export enum CacheControlScope { 27 | Public = 'PUBLIC', 28 | Private = 'PRIVATE' 29 | } 30 | 31 | export type Capsule = { 32 | __typename?: 'Capsule'; 33 | capsule_serial?: Maybe; 34 | capsule_id?: Maybe; 35 | status?: Maybe; 36 | original_launch?: Maybe; 37 | original_launch_unix?: Maybe; 38 | missions?: Maybe>>; 39 | landings?: Maybe; 40 | type?: Maybe; 41 | details?: Maybe; 42 | reuse_count?: Maybe; 43 | }; 44 | 45 | export enum CapsuleRange { 46 | Past = 'past', 47 | Upcoming = 'upcoming' 48 | } 49 | 50 | export type Core = { 51 | __typename?: 'Core'; 52 | core_serial?: Maybe; 53 | block?: Maybe; 54 | status?: Maybe; 55 | original_launch?: Maybe; 56 | original_launch_unix?: Maybe; 57 | missions?: Maybe>>; 58 | reuse_count?: Maybe; 59 | rtls_attempts?: Maybe; 60 | rtls_landings?: Maybe; 61 | asds_attempts?: Maybe; 62 | asds_landings?: Maybe; 63 | water_landing?: Maybe; 64 | details?: Maybe; 65 | }; 66 | 67 | export type Dimension = { 68 | __typename?: 'Dimension'; 69 | meters?: Maybe; 70 | feet?: Maybe; 71 | }; 72 | 73 | export type Dragon = { 74 | __typename?: 'Dragon'; 75 | id?: Maybe; 76 | name?: Maybe; 77 | type?: Maybe; 78 | active?: Maybe; 79 | crew_capacity?: Maybe; 80 | sidewall_angle_deg?: Maybe; 81 | orbit_duration_yr?: Maybe; 82 | dry_mass_kg?: Maybe; 83 | dry_mass_lb?: Maybe; 84 | first_flight?: Maybe; 85 | heat_shield?: Maybe; 86 | thrusters?: Maybe>>; 87 | launch_payload_mass?: Maybe; 88 | launch_payload_vol?: Maybe; 89 | return_payload_mass?: Maybe; 90 | return_payload_vol?: Maybe; 91 | pressurized_capsule?: Maybe; 92 | trunk?: Maybe; 93 | height_w_trunk?: Maybe; 94 | diameter?: Maybe; 95 | wikipedia?: Maybe; 96 | description?: Maybe; 97 | }; 98 | 99 | export type Engines = { 100 | __typename?: 'Engines'; 101 | number?: Maybe; 102 | type?: Maybe; 103 | version?: Maybe; 104 | layout?: Maybe; 105 | engine_loss_max?: Maybe; 106 | propellant_1?: Maybe; 107 | propellant_2?: Maybe; 108 | thrust_sea_level?: Maybe; 109 | thrust_vacuum?: Maybe; 110 | thrust_to_weight?: Maybe; 111 | }; 112 | 113 | export type Fairing = { 114 | __typename?: 'Fairing'; 115 | height?: Maybe; 116 | diameter?: Maybe; 117 | }; 118 | 119 | export type Headquarters = { 120 | __typename?: 'Headquarters'; 121 | address?: Maybe; 122 | city?: Maybe; 123 | state?: Maybe; 124 | }; 125 | 126 | export type HeatShield = { 127 | __typename?: 'HeatShield'; 128 | material?: Maybe; 129 | size_meters?: Maybe; 130 | temp_degrees?: Maybe; 131 | dev_partner?: Maybe; 132 | }; 133 | 134 | export type History = { 135 | __typename?: 'History'; 136 | id?: Maybe; 137 | title?: Maybe; 138 | event_date_utc?: Maybe; 139 | event_date_unix?: Maybe; 140 | flight_number?: Maybe; 141 | details?: Maybe; 142 | links?: Maybe; 143 | }; 144 | 145 | export type HistoryLinks = { 146 | __typename?: 'HistoryLinks'; 147 | article?: Maybe; 148 | reddit?: Maybe; 149 | wikipedia?: Maybe; 150 | }; 151 | 152 | export type Info = { 153 | __typename?: 'Info'; 154 | name?: Maybe; 155 | founder?: Maybe; 156 | founded?: Maybe; 157 | employees?: Maybe; 158 | vehicles?: Maybe; 159 | launch_sites?: Maybe; 160 | test_sites?: Maybe; 161 | ceo?: Maybe; 162 | cto?: Maybe; 163 | coo?: Maybe; 164 | cto_propulsion?: Maybe; 165 | valuation?: Maybe; 166 | headquarters?: Maybe; 167 | summary?: Maybe; 168 | }; 169 | 170 | export type LandingLegs = { 171 | __typename?: 'LandingLegs'; 172 | number?: Maybe; 173 | material?: Maybe; 174 | }; 175 | 176 | export type Landingpad = { 177 | __typename?: 'Landingpad'; 178 | id?: Maybe; 179 | full_name?: Maybe; 180 | status?: Maybe; 181 | location?: Maybe; 182 | landing_type?: Maybe; 183 | attempted_landings?: Maybe; 184 | successful_landings?: Maybe; 185 | wikipedia?: Maybe; 186 | details?: Maybe; 187 | }; 188 | 189 | export type Launch = { 190 | __typename?: 'Launch'; 191 | flight_number?: Maybe; 192 | mission_id?: Maybe>>; 193 | mission_name?: Maybe; 194 | launch_year?: Maybe; 195 | launch_date_unix?: Maybe; 196 | launch_date_utc?: Maybe; 197 | launch_date_local?: Maybe; 198 | is_tentative?: Maybe; 199 | tentative_max_precision?: Maybe; 200 | tbd?: Maybe; 201 | launch_window?: Maybe; 202 | rocket?: Maybe; 203 | ships?: Maybe>>; 204 | telemetry?: Maybe; 205 | launch_site?: Maybe; 206 | launch_success?: Maybe; 207 | links?: Maybe; 208 | details?: Maybe; 209 | upcoming?: Maybe; 210 | static_fire_date_utc?: Maybe; 211 | static_fire_date_unix?: Maybe; 212 | timeline?: Maybe; 213 | }; 214 | 215 | export type LaunchLinks = { 216 | __typename?: 'LaunchLinks'; 217 | mission_patch?: Maybe; 218 | mission_patch_small?: Maybe; 219 | reddit_campaign?: Maybe; 220 | reddit_launch?: Maybe; 221 | reddit_recovery?: Maybe; 222 | reddit_media?: Maybe; 223 | presskit?: Maybe; 224 | article_link?: Maybe; 225 | wikipedia?: Maybe; 226 | video_link?: Maybe; 227 | youtube_id?: Maybe; 228 | flickr_images?: Maybe>>; 229 | }; 230 | 231 | export enum LaunchRange { 232 | Latest = 'latest', 233 | Next = 'next', 234 | Past = 'past', 235 | Upcoming = 'upcoming' 236 | } 237 | 238 | export type LaunchRocket = { 239 | __typename?: 'LaunchRocket'; 240 | rocket_id?: Maybe; 241 | rocket_name?: Maybe; 242 | rocket_type?: Maybe; 243 | first_stage?: Maybe; 244 | second_stage?: Maybe; 245 | fairings?: Maybe; 246 | }; 247 | 248 | export type LaunchRocketFairings = { 249 | __typename?: 'LaunchRocketFairings'; 250 | reused?: Maybe; 251 | recovery_attempt?: Maybe; 252 | recovered?: Maybe; 253 | ship?: Maybe; 254 | }; 255 | 256 | export type LaunchRocketFirstStage = { 257 | __typename?: 'LaunchRocketFirstStage'; 258 | cores?: Maybe>>; 259 | }; 260 | 261 | export type LaunchRocketFirstStageCore = { 262 | __typename?: 'LaunchRocketFirstStageCore'; 263 | core_serial?: Maybe; 264 | flight?: Maybe; 265 | block?: Maybe; 266 | gridfins?: Maybe; 267 | legs?: Maybe; 268 | reused?: Maybe; 269 | land_success?: Maybe; 270 | landing_intent?: Maybe; 271 | landing_type?: Maybe; 272 | landing_vehicle?: Maybe; 273 | }; 274 | 275 | export type LaunchRocketSecondStage = { 276 | __typename?: 'LaunchRocketSecondStage'; 277 | block?: Maybe; 278 | payloads?: Maybe>>; 279 | }; 280 | 281 | export type LaunchSite = { 282 | __typename?: 'LaunchSite'; 283 | site_id?: Maybe; 284 | site_name?: Maybe; 285 | site_name_long?: Maybe; 286 | }; 287 | 288 | export type LaunchTelemetry = { 289 | __typename?: 'LaunchTelemetry'; 290 | flight_club?: Maybe; 291 | }; 292 | 293 | export type LaunchTimeline = { 294 | __typename?: 'LaunchTimeline'; 295 | webcast_liftoff?: Maybe; 296 | go_for_prop_loading?: Maybe; 297 | rp1_loading?: Maybe; 298 | stage1_rp1_loading?: Maybe; 299 | stage1_lox_loading?: Maybe; 300 | stage2_rp1_loading?: Maybe; 301 | stage2_lox_loading?: Maybe; 302 | engine_chill?: Maybe; 303 | prelaunch_checks?: Maybe; 304 | propellant_pressurization?: Maybe; 305 | go_for_launch?: Maybe; 306 | ignition?: Maybe; 307 | liftoff?: Maybe; 308 | maxq?: Maybe; 309 | beco?: Maybe; 310 | side_core_sep?: Maybe; 311 | side_core_boostback?: Maybe; 312 | meco?: Maybe; 313 | stage_sep?: Maybe; 314 | center_stage_sep?: Maybe; 315 | second_stage_ignition?: Maybe; 316 | center_core_boostback?: Maybe; 317 | fairing_deploy?: Maybe; 318 | first_stage_entry_burn?: Maybe; 319 | side_core_entry_burn?: Maybe; 320 | center_core_entry_burn?: Maybe; 321 | seco_1?: Maybe; 322 | first_stage_landing_burn?: Maybe; 323 | first_stage_landing?: Maybe; 324 | side_core_landing?: Maybe; 325 | center_core_landing?: Maybe; 326 | second_stage_restart?: Maybe; 327 | seco_2?: Maybe; 328 | payload_deploy?: Maybe; 329 | payload_deploy_1?: Maybe; 330 | payload_deploy_2?: Maybe; 331 | dragon_separation?: Maybe; 332 | dragon_solar_deploy?: Maybe; 333 | dragon_bay_door_deploy?: Maybe; 334 | }; 335 | 336 | export type Launchpad = { 337 | __typename?: 'Launchpad'; 338 | id?: Maybe; 339 | status?: Maybe; 340 | location?: Maybe; 341 | vehicles_launched?: Maybe>>; 342 | attempted_launches?: Maybe; 343 | successful_launches?: Maybe; 344 | wikipedia?: Maybe; 345 | details?: Maybe; 346 | site_id?: Maybe; 347 | site_name_long?: Maybe; 348 | }; 349 | 350 | export type Location = { 351 | __typename?: 'Location'; 352 | name?: Maybe; 353 | region?: Maybe; 354 | latitude?: Maybe; 355 | longitude?: Maybe; 356 | }; 357 | 358 | export type Mass = { 359 | __typename?: 'Mass'; 360 | kg?: Maybe; 361 | lb?: Maybe; 362 | }; 363 | 364 | export type Mission = { 365 | __typename?: 'Mission'; 366 | mission_name?: Maybe; 367 | mission_id?: Maybe; 368 | manufacturers?: Maybe>>; 369 | payload_ids?: Maybe>>; 370 | wikipedia?: Maybe; 371 | website?: Maybe; 372 | twitter?: Maybe; 373 | description?: Maybe; 374 | }; 375 | 376 | export enum Order { 377 | Asc = 'asc', 378 | Desc = 'desc' 379 | } 380 | 381 | export type Payload = { 382 | __typename?: 'Payload'; 383 | payload_id?: Maybe; 384 | norad_id?: Maybe>>; 385 | cap_serial?: Maybe; 386 | reused?: Maybe; 387 | customers?: Maybe>>; 388 | nationality?: Maybe; 389 | manufacturer?: Maybe; 390 | payload_type?: Maybe; 391 | payload_mass_kg?: Maybe; 392 | payload_mass_lbs?: Maybe; 393 | orbit?: Maybe; 394 | orbit_params?: Maybe; 395 | mass_returned_kg?: Maybe; 396 | mass_returned_lbs?: Maybe; 397 | flight_time_sec?: Maybe; 398 | cargo_manifest?: Maybe; 399 | }; 400 | 401 | export type PayloadOrbitParams = { 402 | __typename?: 'PayloadOrbitParams'; 403 | reference_system?: Maybe; 404 | regime?: Maybe; 405 | longitude?: Maybe; 406 | semi_major_axis_km?: Maybe; 407 | eccentricity?: Maybe; 408 | periapsis_km?: Maybe; 409 | apoapsis_km?: Maybe; 410 | inclination_deg?: Maybe; 411 | period_min?: Maybe; 412 | lifespan_years?: Maybe; 413 | epoch?: Maybe; 414 | mean_motion?: Maybe; 415 | raan?: Maybe; 416 | arg_of_pericenter?: Maybe; 417 | mean_anomaly?: Maybe; 418 | }; 419 | 420 | export type PayloadVolume = { 421 | __typename?: 'PayloadVolume'; 422 | cubic_meters?: Maybe; 423 | cubic_feet?: Maybe; 424 | }; 425 | 426 | export type Position = { 427 | __typename?: 'Position'; 428 | latitude?: Maybe; 429 | longitude?: Maybe; 430 | }; 431 | 432 | export type PressurizedCapsule = { 433 | __typename?: 'PressurizedCapsule'; 434 | payload_volume?: Maybe; 435 | }; 436 | 437 | export type Query = { 438 | __typename?: 'Query'; 439 | capsule?: Maybe; 440 | capsules?: Maybe>>; 441 | core?: Maybe; 442 | cores?: Maybe>>; 443 | dragon?: Maybe; 444 | dragons?: Maybe>>; 445 | history?: Maybe>>; 446 | info?: Maybe; 447 | landingpad?: Maybe; 448 | landingpads?: Maybe>>; 449 | launch?: Maybe; 450 | launches?: Maybe>>; 451 | launchpad?: Maybe; 452 | launchpads?: Maybe>>; 453 | mission?: Maybe; 454 | missions?: Maybe>>; 455 | payload?: Maybe; 456 | payloads?: Maybe>>; 457 | roadster?: Maybe; 458 | rocket?: Maybe; 459 | rockets?: Maybe>>; 460 | ship?: Maybe; 461 | ships?: Maybe>>; 462 | }; 463 | 464 | 465 | export type QueryCapsuleArgs = { 466 | capsule_serial: Scalars['String']; 467 | }; 468 | 469 | 470 | export type QueryCapsulesArgs = { 471 | range?: Maybe; 472 | limit?: Maybe; 473 | offset?: Maybe; 474 | order?: Maybe; 475 | sort?: Maybe; 476 | }; 477 | 478 | 479 | export type QueryCoreArgs = { 480 | core_serial: Scalars['String']; 481 | }; 482 | 483 | 484 | export type QueryCoresArgs = { 485 | limit?: Maybe; 486 | offset?: Maybe; 487 | order?: Maybe; 488 | sort?: Maybe; 489 | }; 490 | 491 | 492 | export type QueryDragonArgs = { 493 | id: Scalars['String']; 494 | }; 495 | 496 | 497 | export type QueryDragonsArgs = { 498 | limit?: Maybe; 499 | offset?: Maybe; 500 | }; 501 | 502 | 503 | export type QueryHistoryArgs = { 504 | limit?: Maybe; 505 | offset?: Maybe; 506 | order?: Maybe; 507 | sort?: Maybe; 508 | id?: Maybe; 509 | }; 510 | 511 | 512 | export type QueryLandingpadArgs = { 513 | id?: Maybe; 514 | }; 515 | 516 | 517 | export type QueryLandingpadsArgs = { 518 | limit?: Maybe; 519 | offset?: Maybe; 520 | }; 521 | 522 | 523 | export type QueryLaunchArgs = { 524 | id?: Maybe; 525 | }; 526 | 527 | 528 | export type QueryLaunchesArgs = { 529 | range?: Maybe; 530 | limit?: Maybe; 531 | offset?: Maybe; 532 | order?: Maybe; 533 | sort?: Maybe; 534 | ids?: Maybe>>; 535 | }; 536 | 537 | 538 | export type QueryLaunchpadArgs = { 539 | id: Scalars['String']; 540 | }; 541 | 542 | 543 | export type QueryLaunchpadsArgs = { 544 | limit?: Maybe; 545 | offset?: Maybe; 546 | }; 547 | 548 | 549 | export type QueryMissionArgs = { 550 | id: Scalars['String']; 551 | }; 552 | 553 | 554 | export type QueryMissionsArgs = { 555 | limit?: Maybe; 556 | offset?: Maybe; 557 | }; 558 | 559 | 560 | export type QueryPayloadArgs = { 561 | id: Scalars['String']; 562 | }; 563 | 564 | 565 | export type QueryPayloadsArgs = { 566 | limit?: Maybe; 567 | offset?: Maybe; 568 | order?: Maybe; 569 | sort?: Maybe; 570 | }; 571 | 572 | 573 | export type QueryRocketArgs = { 574 | id: Scalars['String']; 575 | }; 576 | 577 | 578 | export type QueryRocketsArgs = { 579 | limit?: Maybe; 580 | offset?: Maybe; 581 | }; 582 | 583 | 584 | export type QueryShipArgs = { 585 | id: Scalars['String']; 586 | }; 587 | 588 | 589 | export type QueryShipsArgs = { 590 | limit?: Maybe; 591 | offset?: Maybe; 592 | order?: Maybe; 593 | sort?: Maybe; 594 | }; 595 | 596 | export type Roadster = { 597 | __typename?: 'Roadster'; 598 | name: Scalars['String']; 599 | launch_date_utc?: Maybe; 600 | launch_date_unix?: Maybe; 601 | launch_mass_kg?: Maybe; 602 | launch_mass_lbs?: Maybe; 603 | norad_id?: Maybe; 604 | epoch_jd?: Maybe; 605 | orbit_type?: Maybe; 606 | apoapsis_au?: Maybe; 607 | semi_major_axis_au?: Maybe; 608 | eccentricity?: Maybe; 609 | inclination?: Maybe; 610 | longitude?: Maybe; 611 | periapsis_arg?: Maybe; 612 | period_days?: Maybe; 613 | speed_kph?: Maybe; 614 | speed_mph?: Maybe; 615 | earth_distance_km?: Maybe; 616 | earth_distance_mi?: Maybe; 617 | mars_distance_km?: Maybe; 618 | mars_distance_mi?: Maybe; 619 | wikipedia?: Maybe; 620 | details?: Maybe; 621 | }; 622 | 623 | export type Rocket = { 624 | __typename?: 'Rocket'; 625 | id?: Maybe; 626 | active?: Maybe; 627 | stages?: Maybe; 628 | boosters?: Maybe; 629 | cost_per_launch?: Maybe; 630 | success_rate_pct?: Maybe; 631 | first_flight?: Maybe; 632 | country?: Maybe; 633 | company?: Maybe; 634 | height?: Maybe; 635 | diameter?: Maybe; 636 | mass?: Maybe; 637 | payload_weights?: Maybe>>; 638 | first_stage?: Maybe; 639 | second_stage?: Maybe; 640 | engines?: Maybe; 641 | landing_legs?: Maybe; 642 | flickr_images?: Maybe>>; 643 | wikipedia?: Maybe; 644 | description?: Maybe; 645 | rocket_id?: Maybe; 646 | rocket_name?: Maybe; 647 | rocket_type?: Maybe; 648 | }; 649 | 650 | export type RocketFirstStage = { 651 | __typename?: 'RocketFirstStage'; 652 | reusable?: Maybe; 653 | engines?: Maybe; 654 | fuel_amount_tons?: Maybe; 655 | burn_time_sec?: Maybe; 656 | thrust_sea_level?: Maybe; 657 | thrust_vacuum?: Maybe; 658 | }; 659 | 660 | export type RocketPayload = { 661 | __typename?: 'RocketPayload'; 662 | option_1?: Maybe; 663 | option_2?: Maybe; 664 | composite_fairing?: Maybe; 665 | }; 666 | 667 | export type RocketPayloadWeight = { 668 | __typename?: 'RocketPayloadWeight'; 669 | id?: Maybe; 670 | name?: Maybe; 671 | kg?: Maybe; 672 | lb?: Maybe; 673 | }; 674 | 675 | export type RocketSecondStage = { 676 | __typename?: 'RocketSecondStage'; 677 | engines?: Maybe; 678 | fuel_amount_tons?: Maybe; 679 | burn_time_sec?: Maybe; 680 | thrust?: Maybe; 681 | payloads?: Maybe; 682 | }; 683 | 684 | export type Ship = { 685 | __typename?: 'Ship'; 686 | ship_id?: Maybe; 687 | ship_name?: Maybe; 688 | ship_model?: Maybe; 689 | ship_type?: Maybe; 690 | roles?: Maybe>>; 691 | active?: Maybe; 692 | imo?: Maybe; 693 | mmsi?: Maybe; 694 | abs?: Maybe; 695 | class?: Maybe; 696 | weight_lbs?: Maybe; 697 | weight_kg?: Maybe; 698 | year_built?: Maybe; 699 | home_port?: Maybe; 700 | status?: Maybe; 701 | speed_kn?: Maybe; 702 | course_deg?: Maybe; 703 | position?: Maybe; 704 | successful_landings?: Maybe; 705 | attempted_landings?: Maybe; 706 | missions?: Maybe>>; 707 | url?: Maybe; 708 | image?: Maybe; 709 | }; 710 | 711 | export type Thrust = { 712 | __typename?: 'Thrust'; 713 | kN?: Maybe; 714 | lbf?: Maybe; 715 | }; 716 | 717 | export type Thruster = { 718 | __typename?: 'Thruster'; 719 | type?: Maybe; 720 | amount?: Maybe; 721 | pods?: Maybe; 722 | fuel_1?: Maybe; 723 | fuel_2?: Maybe; 724 | thrust?: Maybe; 725 | }; 726 | 727 | export type Trunk = { 728 | __typename?: 'Trunk'; 729 | trunk_volume?: Maybe; 730 | cargo?: Maybe; 731 | }; 732 | 733 | export type TrunkCargo = { 734 | __typename?: 'TrunkCargo'; 735 | solar_array?: Maybe; 736 | unpressurized_cargo?: Maybe; 737 | }; 738 | 739 | 740 | export type LaunchListQueryVariables = Exact<{ [key: string]: never; }>; 741 | 742 | 743 | export type LaunchListQuery = { __typename?: 'Query', launches?: Maybe, mission_name?: Maybe, launch_year?: Maybe }>>> }; 744 | 745 | export type LaunchProfileQueryVariables = Exact<{ 746 | id: Scalars['String']; 747 | }>; 748 | 749 | 750 | export type LaunchProfileQuery = { __typename?: 'Query', launch?: Maybe<{ __typename?: 'Launch', flight_number?: Maybe, mission_name?: Maybe, launch_year?: Maybe, launch_success?: Maybe, details?: Maybe, launch_site?: Maybe<{ __typename?: 'LaunchSite', site_name?: Maybe }>, rocket?: Maybe<{ __typename?: 'LaunchRocket', rocket_name?: Maybe, rocket_type?: Maybe }>, links?: Maybe<{ __typename?: 'LaunchLinks', flickr_images?: Maybe>> }> }> }; 751 | 752 | 753 | export const LaunchListDocument = gql` 754 | query LaunchList { 755 | launches { 756 | flight_number 757 | mission_name 758 | launch_year 759 | } 760 | } 761 | `; 762 | 763 | /** 764 | * __useLaunchListQuery__ 765 | * 766 | * To run a query within a React component, call `useLaunchListQuery` and pass it any options that fit your needs. 767 | * When your component renders, `useLaunchListQuery` returns an object from Apollo Client that contains loading, error, and data properties 768 | * you can use to render your UI. 769 | * 770 | * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; 771 | * 772 | * @example 773 | * const { data, loading, error } = useLaunchListQuery({ 774 | * variables: { 775 | * }, 776 | * }); 777 | */ 778 | export function useLaunchListQuery(baseOptions?: Apollo.QueryHookOptions) { 779 | const options = {...defaultOptions, ...baseOptions} 780 | return Apollo.useQuery(LaunchListDocument, options); 781 | } 782 | export function useLaunchListLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { 783 | const options = {...defaultOptions, ...baseOptions} 784 | return Apollo.useLazyQuery(LaunchListDocument, options); 785 | } 786 | export type LaunchListQueryHookResult = ReturnType; 787 | export type LaunchListLazyQueryHookResult = ReturnType; 788 | export type LaunchListQueryResult = Apollo.QueryResult; 789 | export const LaunchProfileDocument = gql` 790 | query LaunchProfile($id: String!) { 791 | launch(id: $id) { 792 | flight_number 793 | mission_name 794 | launch_year 795 | launch_success 796 | details 797 | launch_site { 798 | site_name 799 | } 800 | rocket { 801 | rocket_name 802 | rocket_type 803 | } 804 | links { 805 | flickr_images 806 | } 807 | } 808 | } 809 | `; 810 | 811 | /** 812 | * __useLaunchProfileQuery__ 813 | * 814 | * To run a query within a React component, call `useLaunchProfileQuery` and pass it any options that fit your needs. 815 | * When your component renders, `useLaunchProfileQuery` returns an object from Apollo Client that contains loading, error, and data properties 816 | * you can use to render your UI. 817 | * 818 | * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; 819 | * 820 | * @example 821 | * const { data, loading, error } = useLaunchProfileQuery({ 822 | * variables: { 823 | * id: // value for 'id' 824 | * }, 825 | * }); 826 | */ 827 | export function useLaunchProfileQuery(baseOptions: Apollo.QueryHookOptions) { 828 | const options = {...defaultOptions, ...baseOptions} 829 | return Apollo.useQuery(LaunchProfileDocument, options); 830 | } 831 | export function useLaunchProfileLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { 832 | const options = {...defaultOptions, ...baseOptions} 833 | return Apollo.useLazyQuery(LaunchProfileDocument, options); 834 | } 835 | export type LaunchProfileQueryHookResult = ReturnType; 836 | export type LaunchProfileLazyQueryHookResult = ReturnType; 837 | export type LaunchProfileQueryResult = Apollo.QueryResult; -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client'; 4 | import './index.css'; 5 | import App from './App'; 6 | 7 | const client = new ApolloClient({ 8 | uri: 'https://spacexdata.herokuapp.com/graphql', 9 | cache: new InMemoryCache(), 10 | }); 11 | 12 | ReactDOM.render( 13 | 14 | 15 | , 16 | document.getElementById('root'), 17 | ); 18 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | --------------------------------------------------------------------------------