From 1ceffa0dea0315f5545ba6d81af95a1d97936612 Mon Sep 17 00:00:00 2001 From: Fbase965 Date: Wed, 11 Mar 2026 12:46:53 +0000 Subject: [PATCH] commit --- angular-app/src/app/app.config.ts | 2 + angular-app/src/app/app.html | 342 --------- angular-app/src/app/app.routes.ts | 16 +- angular-app/src/app/app.scss | 98 +++ angular-app/src/app/app.spec.ts | 4 +- angular-app/src/app/app.ts | 50 +- angular-app/src/app/data/mock-data.ts | 391 ++++++++++ .../src/app/pages/contact/contact.page.ts | 32 + angular-app/src/app/pages/data/data.page.ts | 49 ++ .../src/app/pages/gallery/gallery.page.ts | 22 + angular-app/src/app/pages/home/home.page.ts | 37 + .../src/app/pages/launches/launches.page.ts | 110 +++ angular-app/src/app/pages/news/news.page.ts | 42 + .../src/app/services/spacex-api.service.ts | 90 +++ angular-app/src/styles.scss | 275 ++++++- package.json | 10 + reference-site/ATTRIBUTIONS.md | 3 + reference-site/README.md | 11 + reference-site/guidelines/Guidelines.md | 61 ++ reference-site/index.html | 15 + reference-site/package.json | 89 +++ reference-site/postcss.config.mjs | 15 + reference-site/src/app/App.tsx | 6 + reference-site/src/app/components/Layout.tsx | 85 ++ .../components/figma/ImageWithFallback.tsx | 27 + .../src/app/components/ui/accordion.tsx | 66 ++ .../src/app/components/ui/alert-dialog.tsx | 157 ++++ .../src/app/components/ui/alert.tsx | 66 ++ .../src/app/components/ui/aspect-ratio.tsx | 11 + .../src/app/components/ui/avatar.tsx | 53 ++ .../src/app/components/ui/badge.tsx | 46 ++ .../src/app/components/ui/breadcrumb.tsx | 109 +++ .../src/app/components/ui/button.tsx | 58 ++ .../src/app/components/ui/calendar.tsx | 75 ++ reference-site/src/app/components/ui/card.tsx | 92 +++ .../src/app/components/ui/carousel.tsx | 241 ++++++ .../src/app/components/ui/chart.tsx | 353 +++++++++ .../src/app/components/ui/checkbox.tsx | 32 + .../src/app/components/ui/collapsible.tsx | 33 + .../src/app/components/ui/command.tsx | 177 +++++ .../src/app/components/ui/context-menu.tsx | 252 ++++++ .../src/app/components/ui/dialog.tsx | 135 ++++ .../src/app/components/ui/drawer.tsx | 132 ++++ .../src/app/components/ui/dropdown-menu.tsx | 257 +++++++ reference-site/src/app/components/ui/form.tsx | 168 ++++ .../src/app/components/ui/hover-card.tsx | 44 ++ .../src/app/components/ui/input-otp.tsx | 77 ++ .../src/app/components/ui/input.tsx | 21 + .../src/app/components/ui/label.tsx | 24 + .../src/app/components/ui/menubar.tsx | 276 +++++++ .../src/app/components/ui/navigation-menu.tsx | 168 ++++ .../src/app/components/ui/pagination.tsx | 127 +++ .../src/app/components/ui/popover.tsx | 48 ++ .../src/app/components/ui/progress.tsx | 31 + .../src/app/components/ui/radio-group.tsx | 45 ++ .../src/app/components/ui/resizable.tsx | 56 ++ .../src/app/components/ui/scroll-area.tsx | 58 ++ .../src/app/components/ui/select.tsx | 189 +++++ .../src/app/components/ui/separator.tsx | 28 + .../src/app/components/ui/sheet.tsx | 139 ++++ .../src/app/components/ui/sidebar.tsx | 726 ++++++++++++++++++ .../src/app/components/ui/skeleton.tsx | 13 + .../src/app/components/ui/slider.tsx | 63 ++ .../src/app/components/ui/sonner.tsx | 25 + .../src/app/components/ui/switch.tsx | 31 + .../src/app/components/ui/table.tsx | 116 +++ reference-site/src/app/components/ui/tabs.tsx | 66 ++ .../src/app/components/ui/textarea.tsx | 18 + .../src/app/components/ui/toggle-group.tsx | 73 ++ .../src/app/components/ui/toggle.tsx | 47 ++ .../src/app/components/ui/tooltip.tsx | 61 ++ .../src/app/components/ui/use-mobile.ts | 21 + reference-site/src/app/components/ui/utils.ts | 6 + reference-site/src/app/data/mockData.ts | 399 ++++++++++ reference-site/src/app/pages/Capsules.tsx | 129 ++++ reference-site/src/app/pages/LandingPads.tsx | 201 +++++ reference-site/src/app/pages/Launches.tsx | 124 +++ reference-site/src/app/pages/Launchpads.tsx | 143 ++++ reference-site/src/app/pages/Rockets.tsx | 127 +++ reference-site/src/app/pages/Starlink.tsx | 131 ++++ reference-site/src/app/routes.tsx | 23 + reference-site/src/main.tsx | 7 + reference-site/src/styles/fonts.css | 0 reference-site/src/styles/index.css | 3 + reference-site/src/styles/tailwind.css | 4 + reference-site/src/styles/theme.css | 181 +++++ reference-site/vite.config.ts | 22 + 87 files changed, 8133 insertions(+), 353 deletions(-) delete mode 100644 angular-app/src/app/app.html create mode 100644 angular-app/src/app/data/mock-data.ts create mode 100644 angular-app/src/app/pages/contact/contact.page.ts create mode 100644 angular-app/src/app/pages/data/data.page.ts create mode 100644 angular-app/src/app/pages/gallery/gallery.page.ts create mode 100644 angular-app/src/app/pages/home/home.page.ts create mode 100644 angular-app/src/app/pages/launches/launches.page.ts create mode 100644 angular-app/src/app/pages/news/news.page.ts create mode 100644 angular-app/src/app/services/spacex-api.service.ts create mode 100644 package.json create mode 100644 reference-site/ATTRIBUTIONS.md create mode 100644 reference-site/README.md create mode 100644 reference-site/guidelines/Guidelines.md create mode 100644 reference-site/index.html create mode 100644 reference-site/package.json create mode 100644 reference-site/postcss.config.mjs create mode 100644 reference-site/src/app/App.tsx create mode 100644 reference-site/src/app/components/Layout.tsx create mode 100644 reference-site/src/app/components/figma/ImageWithFallback.tsx create mode 100644 reference-site/src/app/components/ui/accordion.tsx create mode 100644 reference-site/src/app/components/ui/alert-dialog.tsx create mode 100644 reference-site/src/app/components/ui/alert.tsx create mode 100644 reference-site/src/app/components/ui/aspect-ratio.tsx create mode 100644 reference-site/src/app/components/ui/avatar.tsx create mode 100644 reference-site/src/app/components/ui/badge.tsx create mode 100644 reference-site/src/app/components/ui/breadcrumb.tsx create mode 100644 reference-site/src/app/components/ui/button.tsx create mode 100644 reference-site/src/app/components/ui/calendar.tsx create mode 100644 reference-site/src/app/components/ui/card.tsx create mode 100644 reference-site/src/app/components/ui/carousel.tsx create mode 100644 reference-site/src/app/components/ui/chart.tsx create mode 100644 reference-site/src/app/components/ui/checkbox.tsx create mode 100644 reference-site/src/app/components/ui/collapsible.tsx create mode 100644 reference-site/src/app/components/ui/command.tsx create mode 100644 reference-site/src/app/components/ui/context-menu.tsx create mode 100644 reference-site/src/app/components/ui/dialog.tsx create mode 100644 reference-site/src/app/components/ui/drawer.tsx create mode 100644 reference-site/src/app/components/ui/dropdown-menu.tsx create mode 100644 reference-site/src/app/components/ui/form.tsx create mode 100644 reference-site/src/app/components/ui/hover-card.tsx create mode 100644 reference-site/src/app/components/ui/input-otp.tsx create mode 100644 reference-site/src/app/components/ui/input.tsx create mode 100644 reference-site/src/app/components/ui/label.tsx create mode 100644 reference-site/src/app/components/ui/menubar.tsx create mode 100644 reference-site/src/app/components/ui/navigation-menu.tsx create mode 100644 reference-site/src/app/components/ui/pagination.tsx create mode 100644 reference-site/src/app/components/ui/popover.tsx create mode 100644 reference-site/src/app/components/ui/progress.tsx create mode 100644 reference-site/src/app/components/ui/radio-group.tsx create mode 100644 reference-site/src/app/components/ui/resizable.tsx create mode 100644 reference-site/src/app/components/ui/scroll-area.tsx create mode 100644 reference-site/src/app/components/ui/select.tsx create mode 100644 reference-site/src/app/components/ui/separator.tsx create mode 100644 reference-site/src/app/components/ui/sheet.tsx create mode 100644 reference-site/src/app/components/ui/sidebar.tsx create mode 100644 reference-site/src/app/components/ui/skeleton.tsx create mode 100644 reference-site/src/app/components/ui/slider.tsx create mode 100644 reference-site/src/app/components/ui/sonner.tsx create mode 100644 reference-site/src/app/components/ui/switch.tsx create mode 100644 reference-site/src/app/components/ui/table.tsx create mode 100644 reference-site/src/app/components/ui/tabs.tsx create mode 100644 reference-site/src/app/components/ui/textarea.tsx create mode 100644 reference-site/src/app/components/ui/toggle-group.tsx create mode 100644 reference-site/src/app/components/ui/toggle.tsx create mode 100644 reference-site/src/app/components/ui/tooltip.tsx create mode 100644 reference-site/src/app/components/ui/use-mobile.ts create mode 100644 reference-site/src/app/components/ui/utils.ts create mode 100644 reference-site/src/app/data/mockData.ts create mode 100644 reference-site/src/app/pages/Capsules.tsx create mode 100644 reference-site/src/app/pages/LandingPads.tsx create mode 100644 reference-site/src/app/pages/Launches.tsx create mode 100644 reference-site/src/app/pages/Launchpads.tsx create mode 100644 reference-site/src/app/pages/Rockets.tsx create mode 100644 reference-site/src/app/pages/Starlink.tsx create mode 100644 reference-site/src/app/routes.tsx create mode 100644 reference-site/src/main.tsx create mode 100644 reference-site/src/styles/fonts.css create mode 100644 reference-site/src/styles/index.css create mode 100644 reference-site/src/styles/tailwind.css create mode 100644 reference-site/src/styles/theme.css create mode 100644 reference-site/vite.config.ts diff --git a/angular-app/src/app/app.config.ts b/angular-app/src/app/app.config.ts index cb1270e..adcaea5 100644 --- a/angular-app/src/app/app.config.ts +++ b/angular-app/src/app/app.config.ts @@ -1,4 +1,5 @@ import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core'; +import { provideHttpClient } from '@angular/common/http'; import { provideRouter } from '@angular/router'; import { routes } from './app.routes'; @@ -6,6 +7,7 @@ import { routes } from './app.routes'; export const appConfig: ApplicationConfig = { providers: [ provideBrowserGlobalErrorListeners(), + provideHttpClient(), provideRouter(routes) ] }; diff --git a/angular-app/src/app/app.html b/angular-app/src/app/app.html deleted file mode 100644 index 7528372..0000000 --- a/angular-app/src/app/app.html +++ /dev/null @@ -1,342 +0,0 @@ - - - - - - - - - - - -
-
-
- -

Hello, {{ title() }}

-

Congratulations! Your app is running. 🎉

-
- -
-
- @for (item of [ - { title: 'Explore the Docs', link: 'https://angular.dev' }, - { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, - { title: 'Prompt and best practices for AI', link: 'https://angular.dev/ai/develop-with-ai'}, - { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, - { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' }, - { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, - ]; track item.title) { - - {{ item.title }} - - - - - } -
- -
-
-
- - - - - - - - - - - diff --git a/angular-app/src/app/app.routes.ts b/angular-app/src/app/app.routes.ts index dc39edb..e3540f3 100644 --- a/angular-app/src/app/app.routes.ts +++ b/angular-app/src/app/app.routes.ts @@ -1,3 +1,17 @@ import { Routes } from '@angular/router'; +import { CapsulesPage } from './pages/capsules/capsules.page'; +import { LandingpadsPage } from './pages/landingpads/landingpads.page'; +import { LaunchesPage } from './pages/launches/launches.page'; +import { LaunchpadsPage } from './pages/launchpads/launchpads.page'; +import { RocketsPage } from './pages/rockets/rockets.page'; +import { StarlinkPage } from './pages/starlink/starlink.page'; -export const routes: Routes = []; +export const routes: Routes = [ + { path: '', component: LaunchesPage }, + { path: 'rockets', component: RocketsPage }, + { path: 'capsules', component: CapsulesPage }, + { path: 'starlink', component: StarlinkPage }, + { path: 'launchpads', component: LaunchpadsPage }, + { path: 'landingpads', component: LandingpadsPage }, + { path: '**', redirectTo: '' } +]; diff --git a/angular-app/src/app/app.scss b/angular-app/src/app/app.scss index e69de29..598bd54 100644 --- a/angular-app/src/app/app.scss +++ b/angular-app/src/app/app.scss @@ -0,0 +1,98 @@ +.site-header { + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + backdrop-filter: blur(6px); + position: sticky; + top: 0; + background: rgba(7, 10, 20, 0.85); + z-index: 50; +} + +.nav-wrap { + min-height: 72px; + display: flex; + align-items: center; + justify-content: flex-start; +} + +.brand-wrap { + display: flex; + align-items: center; + gap: 12px; +} + +.brand-icon { + width: 40px; + height: 40px; + border-radius: 10px; + display: grid; + place-items: center; + background: #2563eb; +} + +.brand { + display: block; + font-size: 1.25rem; + color: #ffffff; +} + +.brand-wrap small { + display: block; + color: #94a3b8; + font-size: 0.75rem; +} + +.sub-nav { + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + position: sticky; + top: 72px; + z-index: 40; + backdrop-filter: blur(6px); + background: rgba(15, 23, 42, 0.55); +} + +.sub-nav-wrap { + display: flex; + overflow-x: auto; + gap: 2px; +} + +.sub-nav a { + color: #94a3b8; + font-size: 0.95rem; + padding: 14px 16px; + border-bottom: 2px solid transparent; + white-space: nowrap; +} + +.sub-nav a:hover, +.sub-nav a.active { + color: #ffffff; + border-color: #3b82f6; +} + +.page-shell { + padding-top: 48px; + padding-bottom: 52px; +} + +.site-footer { + border-top: 1px solid rgba(255, 255, 255, 0.1); + padding: 22px 0; + margin-top: 64px; +} + +.footer-wrap { + display: flex; + justify-content: center; +} + +.footer-wrap p { + margin: 0; + color: #9aabc8; + font-size: 0.9rem; +} + +.footer-wrap a { + color: #60a5fa; + margin-left: 6px; +} diff --git a/angular-app/src/app/app.spec.ts b/angular-app/src/app/app.spec.ts index dd7f3ea..fcd0024 100644 --- a/angular-app/src/app/app.spec.ts +++ b/angular-app/src/app/app.spec.ts @@ -14,10 +14,10 @@ describe('App', () => { expect(app).toBeTruthy(); }); - it('should render title', async () => { + it('should render brand title', async () => { const fixture = TestBed.createComponent(App); await fixture.whenStable(); const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('h1')?.textContent).toContain('Hello, angular-app'); + expect(compiled.querySelector('.brand')?.textContent).toContain('SpaceX Explorer'); }); }); diff --git a/angular-app/src/app/app.ts b/angular-app/src/app/app.ts index 437d380..0cc9126 100644 --- a/angular-app/src/app/app.ts +++ b/angular-app/src/app/app.ts @@ -1,12 +1,48 @@ -import { Component, signal } from '@angular/core'; -import { RouterOutlet } from '@angular/router'; +import { Component } from '@angular/core'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; @Component({ selector: 'app-root', - imports: [RouterOutlet], - templateUrl: './app.html', + imports: [RouterOutlet, RouterLink, RouterLinkActive], + template: ` + + + + +
+ +
+ + + `, styleUrl: './app.scss' }) -export class App { - protected readonly title = signal('angular-app'); -} +export class App {} diff --git a/angular-app/src/app/data/mock-data.ts b/angular-app/src/app/data/mock-data.ts new file mode 100644 index 0000000..ff2dd5c --- /dev/null +++ b/angular-app/src/app/data/mock-data.ts @@ -0,0 +1,391 @@ +export const launches = [ + { + id: '1', + name: 'Starlink 4-36', + date_utc: '2024-11-15T10:30:00.000Z', + success: true, + details: 'This mission launched 53 Starlink satellites to low Earth orbit.', + rocket: 'Falcon 9', + launchpad: 'Kennedy Space Center LC-39A', + flight_number: 245, + upcoming: false + }, + { + id: '2', + name: 'Crew-8', + date_utc: '2024-10-20T14:15:00.000Z', + success: true, + details: "NASA's SpaceX Crew-8 mission to the International Space Station.", + rocket: 'Falcon 9', + launchpad: 'Kennedy Space Center LC-39A', + flight_number: 244, + upcoming: false + }, + { + id: '3', + name: 'Starship Flight 5', + date_utc: '2024-09-30T08:00:00.000Z', + success: true, + details: 'Fifth orbital test flight of the Starship super heavy-lift launch vehicle.', + rocket: 'Starship', + launchpad: 'Starbase', + flight_number: 5, + upcoming: false + }, + { + id: '4', + name: 'PACE Mission', + date_utc: '2024-08-12T18:45:00.000Z', + success: true, + details: 'Plankton, Aerosol, Cloud, ocean Ecosystem mission for NASA.', + rocket: 'Falcon 9', + launchpad: 'Vandenberg SFB SLC-4E', + flight_number: 243, + upcoming: false + }, + { + id: '5', + name: 'Starlink 6-15', + date_utc: '2026-04-25T12:00:00.000Z', + success: null, + details: 'Next generation Starlink V2 satellites deployment.', + rocket: 'Falcon 9', + launchpad: 'Cape Canaveral SLC-40', + flight_number: 280, + upcoming: true + }, + { + id: '6', + name: 'Europa Clipper', + date_utc: '2026-06-10T09:30:00.000Z', + success: null, + details: "NASA mission to explore Jupiter's moon Europa.", + rocket: 'Falcon Heavy', + launchpad: 'Kennedy Space Center LC-39A', + flight_number: 12, + upcoming: true + } +]; + +export const rockets = [ + { + id: '1', + name: 'Falcon 9', + active: true, + stages: 2, + boosters: 0, + cost_per_launch: 67000000, + success_rate_pct: 99, + first_flight: '2010-06-04', + country: 'United States', + company: 'SpaceX', + height: { meters: 70, feet: 229.6 }, + diameter: { meters: 3.7, feet: 12 }, + mass: { kg: 549054, lb: 1207920 }, + description: + 'Falcon 9 is a reusable, two-stage rocket designed and manufactured by SpaceX for the reliable and safe transport of people and payloads into Earth orbit and beyond.' + }, + { + id: '2', + name: 'Falcon Heavy', + active: true, + stages: 2, + boosters: 2, + cost_per_launch: 97000000, + success_rate_pct: 100, + first_flight: '2018-02-06', + country: 'United States', + company: 'SpaceX', + height: { meters: 70, feet: 229.6 }, + diameter: { meters: 12.2, feet: 39.9 }, + mass: { kg: 1420788, lb: 3125735 }, + description: + 'With the ability to lift into orbit nearly 64 metric tons (141,000 lb) Falcon Heavy is the most powerful operational rocket in the world by a factor of two.' + }, + { + id: '3', + name: 'Starship', + active: true, + stages: 2, + boosters: 0, + cost_per_launch: 10000000, + success_rate_pct: 80, + first_flight: '2023-04-20', + country: 'United States', + company: 'SpaceX', + height: { meters: 120, feet: 394 }, + diameter: { meters: 9, feet: 30 }, + mass: { kg: 5000000, lb: 11000000 }, + description: + 'Starship is a fully reusable super heavy-lift launch vehicle under development by SpaceX. It is the largest and most powerful rocket ever built.' + }, + { + id: '4', + name: 'Falcon 1', + active: false, + stages: 2, + boosters: 0, + cost_per_launch: 6700000, + success_rate_pct: 40, + first_flight: '2006-03-24', + country: 'United States', + company: 'SpaceX', + height: { meters: 22.25, feet: 73 }, + diameter: { meters: 1.68, feet: 5.5 }, + mass: { kg: 30146, lb: 66460 }, + description: + 'The Falcon 1 was an expendable launch system privately developed and manufactured by SpaceX during 2006-2009.' + } +]; + +export const capsules = [ + { + id: '1', + serial: 'C201', + status: 'active', + type: 'Dragon 2', + reuse_count: 5, + water_landings: 5, + land_landings: 0, + last_update: 'Capsule successfully recovered after latest mission', + launches: ['Crew-1', 'Crew-2', 'Axiom-1', 'Crew-6', 'Axiom-3'] + }, + { + id: '2', + serial: 'C206', + status: 'active', + type: 'Dragon 2', + reuse_count: 3, + water_landings: 3, + land_landings: 0, + last_update: 'Currently docked at ISS', + launches: ['CRS-22', 'CRS-26', 'CRS-28'] + }, + { + id: '3', + serial: 'C112', + status: 'retired', + type: 'Dragon 1', + reuse_count: 3, + water_landings: 3, + land_landings: 0, + last_update: 'Retired after successful mission completion', + launches: ['CRS-10', 'CRS-13', 'CRS-16'] + }, + { + id: '4', + serial: 'C208', + status: 'active', + type: 'Dragon 2', + reuse_count: 2, + water_landings: 2, + land_landings: 0, + last_update: 'Undergoing refurbishment', + launches: ['Crew-4', 'Axiom-2'] + }, + { + id: '5', + serial: 'C210', + status: 'unknown', + type: 'Dragon 2', + reuse_count: 0, + water_landings: 0, + land_landings: 0, + last_update: 'Capsule under construction', + launches: [] + } +]; + +export const starlink = [ + { + id: '1', + version: 'v2.0 Mini', + launch: '2024-11-15', + longitude: -122.5, + latitude: 37.8, + height_km: 550, + velocity_kms: 7.59 + }, + { + id: '2', + version: 'v1.5', + launch: '2024-09-20', + longitude: -95.3, + latitude: 29.7, + height_km: 540, + velocity_kms: 7.61 + }, + { + id: '3', + version: 'v2.0 Mini', + launch: '2024-10-05', + longitude: 2.3, + latitude: 48.8, + height_km: 545, + velocity_kms: 7.6 + }, + { + id: '4', + version: 'v1.5', + launch: '2024-08-12', + longitude: 139.6, + latitude: 35.6, + height_km: 538, + velocity_kms: 7.62 + }, + { + id: '5', + version: 'v2.0 Mini', + launch: '2024-11-01', + longitude: -0.1, + latitude: 51.5, + height_km: 548, + velocity_kms: 7.59 + }, + { + id: '6', + version: 'v1.0', + launch: '2023-05-15', + longitude: 12.4, + latitude: 41.9, + height_km: 535, + velocity_kms: 7.63 + } +]; + +export const launchpads = [ + { + id: '1', + name: 'Kennedy Space Center LC-39A', + full_name: 'Kennedy Space Center Historic Launch Complex 39A', + status: 'active', + locality: 'Cape Canaveral', + region: 'Florida', + latitude: 28.6, + longitude: -80.6, + launch_attempts: 187, + launch_successes: 185, + rockets: ['Falcon 9', 'Falcon Heavy', 'Starship'], + details: + 'LC-39A has been used for SpaceX launches since 2017. It was previously used for the Saturn V and Space Shuttle launches.' + }, + { + id: '2', + name: 'Vandenberg SFB SLC-4E', + full_name: 'Vandenberg Space Force Base Space Launch Complex 4E', + status: 'active', + locality: 'Vandenberg', + region: 'California', + latitude: 34.6, + longitude: -120.6, + launch_attempts: 95, + launch_successes: 94, + rockets: ['Falcon 9'], + details: "SpaceX's primary West Coast launch site for polar orbit missions." + }, + { + id: '3', + name: 'Cape Canaveral SLC-40', + full_name: 'Cape Canaveral Space Force Station Space Launch Complex 40', + status: 'active', + locality: 'Cape Canaveral', + region: 'Florida', + latitude: 28.5, + longitude: -80.5, + launch_attempts: 156, + launch_successes: 154, + rockets: ['Falcon 9'], + details: "SpaceX's primary East Coast launch site, used for most commercial missions." + }, + { + id: '4', + name: 'Starbase', + full_name: 'SpaceX South Texas Launch Site', + status: 'active', + locality: 'Boca Chica', + region: 'Texas', + latitude: 25.9, + longitude: -97.1, + launch_attempts: 5, + launch_successes: 4, + rockets: ['Starship'], + details: "SpaceX's private launch facility for Starship development and testing." + } +]; + +export const landingpads = [ + { + id: '1', + name: 'LZ-1', + full_name: 'Landing Zone 1', + status: 'active', + type: 'RTLS', + locality: 'Cape Canaveral', + region: 'Florida', + latitude: 28.4, + longitude: -80.5, + landing_attempts: 45, + landing_successes: 44, + details: + "SpaceX's first East Coast landing pad, located at Cape Canaveral Space Force Station." + }, + { + id: '2', + name: 'LZ-2', + full_name: 'Landing Zone 2', + status: 'active', + type: 'RTLS', + locality: 'Cape Canaveral', + region: 'Florida', + latitude: 28.4, + longitude: -80.5, + landing_attempts: 28, + landing_successes: 28, + details: + 'Second East Coast landing pad, used for Falcon Heavy side boosters.' + }, + { + id: '3', + name: 'LZ-4', + full_name: 'Landing Zone 4', + status: 'active', + type: 'RTLS', + locality: 'Vandenberg', + region: 'California', + latitude: 34.6, + longitude: -120.6, + landing_attempts: 32, + landing_successes: 31, + details: "SpaceX's West Coast landing facility at Vandenberg Space Force Base." + }, + { + id: '4', + name: 'OCISLY', + full_name: 'Of Course I Still Love You', + status: 'active', + type: 'ASDS', + locality: 'Atlantic Ocean', + region: 'Sea', + latitude: null, + longitude: null, + landing_attempts: 102, + landing_successes: 98, + details: + 'Autonomous spaceport drone ship stationed in the Atlantic Ocean for offshore landings.' + }, + { + id: '5', + name: 'JRTI', + full_name: 'Just Read the Instructions', + status: 'active', + type: 'ASDS', + locality: 'Pacific Ocean', + region: 'Sea', + latitude: null, + longitude: null, + landing_attempts: 67, + landing_successes: 64, + details: + 'Autonomous spaceport drone ship stationed in the Pacific Ocean for West Coast missions.' + } +]; diff --git a/angular-app/src/app/pages/contact/contact.page.ts b/angular-app/src/app/pages/contact/contact.page.ts new file mode 100644 index 0000000..4a19335 --- /dev/null +++ b/angular-app/src/app/pages/contact/contact.page.ts @@ -0,0 +1,32 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-contact-page', + template: ` +
+

Contact

+

Contato

+

Fale com a equipe para parcerias, imprensa e oportunidades técnicas.

+
+ +
+
+

Imprensa

+

media@spacex-data.local

+
+
+

Parcerias

+

partnerships@spacex-data.local

+
+
+

Suporte técnico

+

support@spacex-data.local

+
+
+

Base operacional

+

Hawthorne, Califórnia — Estados Unidos

+
+
+ ` +}) +export class ContactPage {} diff --git a/angular-app/src/app/pages/data/data.page.ts b/angular-app/src/app/pages/data/data.page.ts new file mode 100644 index 0000000..e54d7a5 --- /dev/null +++ b/angular-app/src/app/pages/data/data.page.ts @@ -0,0 +1,49 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-data-page', + template: ` +
+

Data Hub

+

Centro de dados espaciais

+

Métricas essenciais de desempenho e histórico de frota.

+
+ +
+
+

Falcon 9

+
    +
  • Reutilização média: 14 voos por booster
  • +
  • Taxa de sucesso: 99,1%
  • +
  • Cargas em órbita: LEO, GTO, ISS
  • +
+
+ +
+

Starship

+
    +
  • Arquitetura totalmente reutilizável
  • +
  • Capacidade alvo: 100t+ para órbita
  • +
  • Foco em missões lunares e marcianas
  • +
+
+ +
+

Cadência de lançamento

+

+ O volume anual de lançamentos cresce com otimização de turnaround e operação integrada + entre produção, integração e recuperação. +

+
+ +
+

Infraestrutura

+

+ Operações coordenadas entre bases costeiras e terrestres para ampliar capacidade e + reduzir janela entre missões. +

+
+
+ ` +}) +export class DataPage {} diff --git a/angular-app/src/app/pages/gallery/gallery.page.ts b/angular-app/src/app/pages/gallery/gallery.page.ts new file mode 100644 index 0000000..db1486a --- /dev/null +++ b/angular-app/src/app/pages/gallery/gallery.page.ts @@ -0,0 +1,22 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-gallery-page', + template: ` +
+

Visual Archive

+

Galeria

+

Seleção de momentos-chave em solo, decolagem e recuperação.

+
+ + + ` +}) +export class GalleryPage {} diff --git a/angular-app/src/app/pages/home/home.page.ts b/angular-app/src/app/pages/home/home.page.ts new file mode 100644 index 0000000..f7fab11 --- /dev/null +++ b/angular-app/src/app/pages/home/home.page.ts @@ -0,0 +1,37 @@ +import { Component } from '@angular/core'; +import { RouterLink } from '@angular/router'; + +@Component({ + selector: 'app-home-page', + imports: [RouterLink], + template: ` +
+

SpaceX Data Platform

+

Missões, veículos e história espacial em tempo real.

+

+ Explore dados organizados sobre lançamentos, notícias e marcos da engenharia aeroespacial + em uma experiência limpa e direta. +

+ +
+ +
+
+

+380

+

Lançamentos catalogados

+
+
+

99%

+

Tempo de atividade da plataforma

+
+
+

24/7

+

Atualização de eventos e telemetria

+
+
+ ` +}) +export class HomePage {} diff --git a/angular-app/src/app/pages/launches/launches.page.ts b/angular-app/src/app/pages/launches/launches.page.ts new file mode 100644 index 0000000..564027c --- /dev/null +++ b/angular-app/src/app/pages/launches/launches.page.ts @@ -0,0 +1,110 @@ +import { Component, OnInit, inject } from '@angular/core'; +import { SpacexApiService } from '../../services/spacex-api.service'; + +@Component({ + selector: 'app-launches-page', + template: ` +
+

SpaceX Launches

+

Explore past and upcoming SpaceX missions

+
+ + @if (loading) { +

Loading real data from SpaceX API...

+ } + @if (error) { +

{{ error }}

+ } + +
+ + + +
+ +
+ @for (launch of filteredLaunches; track launch.id) { +
+
+
+

{{ launch.name }}

+

{{ formatDate(launch.date_utc) }}

+
+ @if (launch.upcoming) { + Upcoming + } @else { + + {{ launch.success ? 'Success' : 'Failed' }} + + } +
+ +

{{ launch.details }}

+ +
+

Rocket: {{ launch.rocket || 'Unknown' }}

+

Pad: {{ launch.launchpad || 'Unknown' }}

+
+

Flight #{{ launch.flight_number }}

+
+ } +
+ ` +}) +export class LaunchesPage implements OnInit { + private readonly spacexApi = inject(SpacexApiService); + + launches: any[] = []; + tab: 'all' | 'upcoming' | 'past' = 'all'; + loading = true; + error = ''; + + ngOnInit() { + this.spacexApi.getLaunches().subscribe({ + next: (docs) => { + this.launches = docs.map((launch) => ({ + id: launch.id, + name: launch.name, + date_utc: launch.date_utc, + success: launch.success, + details: launch.details || 'No details available.', + rocket: typeof launch.rocket === 'object' ? launch.rocket?.name : launch.rocket, + launchpad: typeof launch.launchpad === 'object' ? launch.launchpad?.name : launch.launchpad, + flight_number: launch.flight_number, + upcoming: launch.upcoming + })); + this.loading = false; + }, + error: () => { + this.error = 'Could not load launches from SpaceX API.'; + this.loading = false; + } + }); + } + + get upcomingLaunches() { + return this.launches.filter((launch) => launch.upcoming); + } + + get pastLaunches() { + return this.launches.filter((launch) => !launch.upcoming); + } + + get filteredLaunches() { + if (this.tab === 'upcoming') return this.upcomingLaunches; + if (this.tab === 'past') return this.pastLaunches; + return this.launches; + } + + formatDate(dateValue: string) { + return new Date(dateValue).toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', + hour: '2-digit', + minute: '2-digit' + }); + } +} diff --git a/angular-app/src/app/pages/news/news.page.ts b/angular-app/src/app/pages/news/news.page.ts new file mode 100644 index 0000000..269a6bc --- /dev/null +++ b/angular-app/src/app/pages/news/news.page.ts @@ -0,0 +1,42 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-news-page', + template: ` +
+

News Feed

+

Últimas notícias

+

Atualizações de missões, testes e marcos operacionais.

+
+ +
+
+

11 mar 2026

+

Nova missão de reabastecimento conclui acoplamento com sucesso

+

+ A missão entregou suprimentos críticos e realizou experimentos científicos de longa + duração para pesquisa em microgravidade. +

+
+ +
+

08 mar 2026

+

Booster reutilizado atinge novo recorde de voos

+

+ O mesmo estágio foi empregado com mínima manutenção, validando ciclos mais rápidos e + redução consistente de custo por lançamento. +

+
+ +
+

03 mar 2026

+

Teste de infraestrutura amplia janela operacional

+

+ Novas rotinas de preparação diminuem o tempo entre campanhas e aumentam previsibilidade + para clientes institucionais e comerciais. +

+
+
+ ` +}) +export class NewsPage {} diff --git a/angular-app/src/app/services/spacex-api.service.ts b/angular-app/src/app/services/spacex-api.service.ts new file mode 100644 index 0000000..9047d7d --- /dev/null +++ b/angular-app/src/app/services/spacex-api.service.ts @@ -0,0 +1,90 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable, inject } from '@angular/core'; +import { Observable, map } from 'rxjs'; + +type QueryResult = { docs: T[] }; + +@Injectable({ providedIn: 'root' }) +export class SpacexApiService { + private readonly http = inject(HttpClient); + private readonly baseUrl = 'https://api.spacexdata.com/v4'; + + getLaunches(): Observable { + return this.http + .post>(`${this.baseUrl}/launches/query`, { + query: {}, + options: { + sort: { date_utc: -1 }, + pagination: false, + populate: [ + { path: 'rocket', select: { name: 1 } }, + { path: 'launchpad', select: { name: 1 } } + ] + } + }) + .pipe(map((result) => result.docs ?? [])); + } + + getRockets(): Observable { + return this.http + .post>(`${this.baseUrl}/rockets/query`, { + query: {}, + options: { sort: { first_flight: -1 }, pagination: false } + }) + .pipe(map((result) => result.docs ?? [])); + } + + getCapsules(): Observable { + return this.http + .post>(`${this.baseUrl}/capsules/query`, { + query: {}, + options: { sort: { serial: 1 }, pagination: false } + }) + .pipe(map((result) => result.docs ?? [])); + } + + getStarlink(): Observable { + return this.http + .post>(`${this.baseUrl}/starlink/query`, { + query: {}, + options: { + select: { + version: 1, + longitude: 1, + latitude: 1, + height_km: 1, + velocity_kms: 1, + spaceTrack: 1 + }, + limit: 120, + sort: { launch: -1 } + } + }) + .pipe( + map((result) => + (result.docs ?? []).filter((item) => item.latitude !== null && item.longitude !== null) + ) + ); + } + + getLaunchpads(): Observable { + return this.http + .post>(`${this.baseUrl}/launchpads/query`, { + query: {}, + options: { + pagination: false, + populate: [{ path: 'rockets', select: { name: 1 } }] + } + }) + .pipe(map((result) => result.docs ?? [])); + } + + getLandpads(): Observable { + return this.http + .post>(`${this.baseUrl}/landpads/query`, { + query: {}, + options: { pagination: false } + }) + .pipe(map((result) => result.docs ?? [])); + } +} diff --git a/angular-app/src/styles.scss b/angular-app/src/styles.scss index 90d4ee0..be1886e 100644 --- a/angular-app/src/styles.scss +++ b/angular-app/src/styles.scss @@ -1 +1,274 @@ -/* You can add global styles to this file, and also import other style files */ +:root { + color-scheme: dark; +} + +* { + box-sizing: border-box; +} + +html, +body { + margin: 0; + min-height: 100%; + font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + background: + radial-gradient(circle at 15% 20%, rgba(80, 128, 255, 0.16), transparent 34%), + radial-gradient(circle at 85% 10%, rgba(74, 219, 255, 0.12), transparent 28%), + #050812; + color: #f7fbff; +} + +app-root { + display: block; + min-height: 100vh; + background: linear-gradient(135deg, #020617 0%, #0f172a 48%, #020617 100%); +} + +a { + color: inherit; + text-decoration: none; +} + +.container { + width: min(1120px, calc(100% - 2rem)); + margin-inline: auto; +} + +section + section { + margin-top: 24px; +} + +h1, +h2, +h3, +p { + margin: 0; +} + +h2 { + font-size: clamp(1.7rem, 2.4vw, 2rem); + color: #fff; +} + +.page-head p { + color: #94a3b8; + margin-top: 8px; +} + +.tabs { + display: flex; + flex-wrap: wrap; + gap: 8px; + padding: 8px; + border: 1px solid #1e293b; + border-radius: 12px; + background: rgba(15, 23, 42, 0.5); +} + +.tabs button { + border: 0; + border-radius: 9px; + padding: 10px 12px; + background: transparent; + color: #94a3b8; + cursor: pointer; +} + +.tabs button.active { + background: #1e293b; + color: #f8fafc; +} + +.cards { + display: grid; + gap: 16px; +} + +.two-cols { + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); +} + +.three-cols { + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); +} + +.four-cols { + grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); +} + +.panel { + border: 1px solid #1e293b; + border-radius: 14px; + padding: 18px; + background: rgba(15, 23, 42, 0.5); +} + +.panel h3 { + color: #fff; + font-size: 1.25rem; +} + +.muted { + color: #94a3b8; + line-height: 1.5; +} + +.small { + font-size: 0.85rem; + color: #94a3b8; +} + +.strong { + color: #fff; + font-weight: 700; +} + +.row { + display: flex; + gap: 10px; + align-items: center; +} + +.row.start { + align-items: flex-start; +} + +.row.between { + justify-content: space-between; +} + +.badge { + font-size: 0.74rem; + border-radius: 999px; + padding: 4px 10px; + font-weight: 600; + text-transform: capitalize; + background: #334155; + color: #e2e8f0; +} + +.badge-green { + background: #16a34a; + color: #f0fdf4; +} + +.badge-red { + background: #dc2626; + color: #fef2f2; +} + +.badge-blue { + background: #2563eb; + color: #eff6ff; +} + +.badge-purple { + background: #7c3aed; + color: #f5f3ff; +} + +.badge-yellow { + background: #ca8a04; + color: #fefce8; +} + +.badge-cyan { + background: #0e7490; + color: #ecfeff; +} + +.badge-gray { + background: #475569; + color: #f1f5f9; +} + +.meta-grid { + margin-top: 14px; + padding-top: 12px; + border-top: 1px solid #1e293b; + display: grid; + gap: 8px 12px; +} + +.cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.meta-grid p { + margin: 0; + color: #cbd5e1; + font-size: 0.92rem; +} + +.progress-wrap { + margin-top: 12px; +} + +.progress { + margin-top: 8px; + width: 100%; + height: 8px; + background: #0f172a; + border-radius: 999px; + overflow: hidden; +} + +.progress span { + display: block; + height: 100%; + background: linear-gradient(90deg, #2563eb, #38bdf8); +} + +.chips { + margin-top: 14px; + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.chip { + border: 1px solid #334155; + border-radius: 999px; + padding: 4px 10px; + font-size: 0.8rem; + color: #cbd5e1; +} + +.stats .stat { + border: 0; +} + +.stat p { + color: rgba(255, 255, 255, 0.85); + font-size: 0.82rem; +} + +.stat h3 { + margin-top: 8px; + color: #fff; + font-size: 2rem; +} + +.stat.blue { + background: linear-gradient(135deg, #2563eb, #1d4ed8); +} + +.stat.purple { + background: linear-gradient(135deg, #7c3aed, #6d28d9); +} + +.stat.pink { + background: linear-gradient(135deg, #db2777, #be185d); +} + +.stat.cyan { + background: linear-gradient(135deg, #0891b2, #0e7490); +} + +.stat.green { + background: linear-gradient(135deg, #16a34a, #15803d); +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..73843ee --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "name": "trabalho-api-workspace", + "private": true, + "scripts": { + "start": "npm --prefix angular-app run start", + "build": "npm --prefix angular-app run build", + "test": "npm --prefix angular-app run test", + "watch": "npm --prefix angular-app run watch" + } +} diff --git a/reference-site/ATTRIBUTIONS.md b/reference-site/ATTRIBUTIONS.md new file mode 100644 index 0000000..5df5c40 --- /dev/null +++ b/reference-site/ATTRIBUTIONS.md @@ -0,0 +1,3 @@ +This Figma Make file includes components from [shadcn/ui](https://ui.shadcn.com/) used under [MIT license](https://github.com/shadcn-ui/ui/blob/main/LICENSE.md). + +This Figma Make file includes photos from [Unsplash](https://unsplash.com) used under [license](https://unsplash.com/license). diff --git a/reference-site/README.md b/reference-site/README.md new file mode 100644 index 0000000..19ab865 --- /dev/null +++ b/reference-site/README.md @@ -0,0 +1,11 @@ + + # SpaceX Data Website + + This is a code bundle for SpaceX Data Website. The original project is available at https://www.figma.com/design/21yJ0y1FSGdnQwlMQoZsUq/SpaceX-Data-Website. + + ## Running the code + + Run `npm i` to install the dependencies. + + Run `npm run dev` to start the development server. + \ No newline at end of file diff --git a/reference-site/guidelines/Guidelines.md b/reference-site/guidelines/Guidelines.md new file mode 100644 index 0000000..110f117 --- /dev/null +++ b/reference-site/guidelines/Guidelines.md @@ -0,0 +1,61 @@ +**Add your own guidelines here** + diff --git a/reference-site/index.html b/reference-site/index.html new file mode 100644 index 0000000..a25a998 --- /dev/null +++ b/reference-site/index.html @@ -0,0 +1,15 @@ + + + + + + + SpaceX Data Website + + + +
+ + + + \ No newline at end of file diff --git a/reference-site/package.json b/reference-site/package.json new file mode 100644 index 0000000..c36df6f --- /dev/null +++ b/reference-site/package.json @@ -0,0 +1,89 @@ +{ + "name": "@figma/my-make-file", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "build": "vite build", + "dev": "vite" + }, + "dependencies": { + "@emotion/react": "11.14.0", + "@emotion/styled": "11.14.1", + "@mui/icons-material": "7.3.5", + "@mui/material": "7.3.5", + "@popperjs/core": "2.11.8", + "@radix-ui/react-accordion": "1.2.3", + "@radix-ui/react-alert-dialog": "1.1.6", + "@radix-ui/react-aspect-ratio": "1.1.2", + "@radix-ui/react-avatar": "1.1.3", + "@radix-ui/react-checkbox": "1.1.4", + "@radix-ui/react-collapsible": "1.1.3", + "@radix-ui/react-context-menu": "2.2.6", + "@radix-ui/react-dialog": "1.1.6", + "@radix-ui/react-dropdown-menu": "2.1.6", + "@radix-ui/react-hover-card": "1.1.6", + "@radix-ui/react-label": "2.1.2", + "@radix-ui/react-menubar": "1.1.6", + "@radix-ui/react-navigation-menu": "1.2.5", + "@radix-ui/react-popover": "1.1.6", + "@radix-ui/react-progress": "1.1.2", + "@radix-ui/react-radio-group": "1.2.3", + "@radix-ui/react-scroll-area": "1.2.3", + "@radix-ui/react-select": "2.1.6", + "@radix-ui/react-separator": "1.1.2", + "@radix-ui/react-slider": "1.2.3", + "@radix-ui/react-slot": "1.1.2", + "@radix-ui/react-switch": "1.1.3", + "@radix-ui/react-tabs": "1.1.3", + "@radix-ui/react-toggle-group": "1.1.2", + "@radix-ui/react-toggle": "1.1.2", + "@radix-ui/react-tooltip": "1.1.8", + "class-variance-authority": "0.7.1", + "clsx": "2.1.1", + "cmdk": "1.1.1", + "date-fns": "3.6.0", + "embla-carousel-react": "8.6.0", + "input-otp": "1.4.2", + "lucide-react": "0.487.0", + "motion": "12.23.24", + "next-themes": "0.4.6", + "react-day-picker": "8.10.1", + "react-dnd": "16.0.1", + "react-dnd-html5-backend": "16.0.1", + "react-hook-form": "7.55.0", + "react-popper": "2.3.0", + "react-resizable-panels": "2.1.7", + "react-responsive-masonry": "2.7.1", + "react-router": "7.13.0", + "react-slick": "0.31.0", + "recharts": "2.15.2", + "sonner": "2.0.3", + "tailwind-merge": "3.2.0", + "tw-animate-css": "1.3.8", + "vaul": "1.1.2" + }, + "devDependencies": { + "@tailwindcss/vite": "4.1.12", + "@vitejs/plugin-react": "4.7.0", + "tailwindcss": "4.1.12", + "vite": "6.3.5" + }, + "peerDependencies": { + "react": "18.3.1", + "react-dom": "18.3.1" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + }, + "pnpm": { + "overrides": { + "vite": "6.3.5" + } + } +} \ No newline at end of file diff --git a/reference-site/postcss.config.mjs b/reference-site/postcss.config.mjs new file mode 100644 index 0000000..531dbec --- /dev/null +++ b/reference-site/postcss.config.mjs @@ -0,0 +1,15 @@ +/** + * PostCSS Configuration + * + * Tailwind CSS v4 (via @tailwindcss/vite) automatically sets up all required + * PostCSS plugins — you do NOT need to include `tailwindcss` or `autoprefixer` here. + * + * This file only exists for adding additional PostCSS plugins, if needed. + * For example: + * + * import postcssNested from 'postcss-nested' + * export default { plugins: [postcssNested()] } + * + * Otherwise, you can leave this file empty. + */ +export default {} diff --git a/reference-site/src/app/App.tsx b/reference-site/src/app/App.tsx new file mode 100644 index 0000000..3b3eb8d --- /dev/null +++ b/reference-site/src/app/App.tsx @@ -0,0 +1,6 @@ +import { RouterProvider } from "react-router"; +import { router } from "./routes"; + +export default function App() { + return ; +} \ No newline at end of file diff --git a/reference-site/src/app/components/Layout.tsx b/reference-site/src/app/components/Layout.tsx new file mode 100644 index 0000000..bb006ef --- /dev/null +++ b/reference-site/src/app/components/Layout.tsx @@ -0,0 +1,85 @@ +import { Link, Outlet, useLocation } from "react-router"; +import { Rocket, Satellite, Package, Radio, MapPin, Target } from "lucide-react"; + +export function Layout() { + const location = useLocation(); + + const navItems = [ + { path: "/", label: "Launches", icon: Rocket }, + { path: "/rockets", label: "Rockets", icon: Rocket }, + { path: "/capsules", label: "Capsules", icon: Package }, + { path: "/starlink", label: "Starlink", icon: Satellite }, + { path: "/launchpads", label: "Launchpads", icon: MapPin }, + { path: "/landingpads", label: "Landing Pads", icon: Target }, + ]; + + return ( +
+ {/* Header */} +
+
+
+ +
+ +
+
+

SpaceX Explorer

+

Launch & Mission Data

+
+ +
+
+
+ + {/* Navigation */} + + + {/* Main Content */} +
+ +
+ + {/* Footer */} + +
+ ); +} diff --git a/reference-site/src/app/components/figma/ImageWithFallback.tsx b/reference-site/src/app/components/figma/ImageWithFallback.tsx new file mode 100644 index 0000000..0e26139 --- /dev/null +++ b/reference-site/src/app/components/figma/ImageWithFallback.tsx @@ -0,0 +1,27 @@ +import React, { useState } from 'react' + +const ERROR_IMG_SRC = + 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODgiIGhlaWdodD0iODgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBvcGFjaXR5PSIuMyIgZmlsbD0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIzLjciPjxyZWN0IHg9IjE2IiB5PSIxNiIgd2lkdGg9IjU2IiBoZWlnaHQ9IjU2IiByeD0iNiIvPjxwYXRoIGQ9Im0xNiA1OCAxNi0xOCAzMiAzMiIvPjxjaXJjbGUgY3g9IjUzIiBjeT0iMzUiIHI9IjciLz48L3N2Zz4KCg==' + +export function ImageWithFallback(props: React.ImgHTMLAttributes) { + const [didError, setDidError] = useState(false) + + const handleError = () => { + setDidError(true) + } + + const { src, alt, style, className, ...rest } = props + + return didError ? ( +
+
+ Error loading image +
+
+ ) : ( + {alt} + ) +} diff --git a/reference-site/src/app/components/ui/accordion.tsx b/reference-site/src/app/components/ui/accordion.tsx new file mode 100644 index 0000000..bd6b1e3 --- /dev/null +++ b/reference-site/src/app/components/ui/accordion.tsx @@ -0,0 +1,66 @@ +"use client"; + +import * as React from "react"; +import * as AccordionPrimitive from "@radix-ui/react-accordion"; +import { ChevronDownIcon } from "lucide-react"; + +import { cn } from "./utils"; + +function Accordion({ + ...props +}: React.ComponentProps) { + return ; +} + +function AccordionItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AccordionTrigger({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + svg]:rotate-180", + className, + )} + {...props} + > + {children} + + + + ); +} + +function AccordionContent({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + +
{children}
+
+ ); +} + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }; diff --git a/reference-site/src/app/components/ui/alert-dialog.tsx b/reference-site/src/app/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..875b8df --- /dev/null +++ b/reference-site/src/app/components/ui/alert-dialog.tsx @@ -0,0 +1,157 @@ +"use client"; + +import * as React from "react"; +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"; + +import { cn } from "./utils"; +import { buttonVariants } from "./button"; + +function AlertDialog({ + ...props +}: React.ComponentProps) { + return ; +} + +function AlertDialogTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogPortal({ + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + + ); +} + +function AlertDialogHeader({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function AlertDialogFooter({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function AlertDialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogAction({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogCancel({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +}; diff --git a/reference-site/src/app/components/ui/alert.tsx b/reference-site/src/app/components/ui/alert.tsx new file mode 100644 index 0000000..9c35976 --- /dev/null +++ b/reference-site/src/app/components/ui/alert.tsx @@ -0,0 +1,66 @@ +import * as React from "react"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "./utils"; + +const alertVariants = cva( + "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current", + { + variants: { + variant: { + default: "bg-card text-card-foreground", + destructive: + "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +function Alert({ + className, + variant, + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
+ ); +} + +function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function AlertDescription({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ); +} + +export { Alert, AlertTitle, AlertDescription }; diff --git a/reference-site/src/app/components/ui/aspect-ratio.tsx b/reference-site/src/app/components/ui/aspect-ratio.tsx new file mode 100644 index 0000000..c16d6bc --- /dev/null +++ b/reference-site/src/app/components/ui/aspect-ratio.tsx @@ -0,0 +1,11 @@ +"use client"; + +import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"; + +function AspectRatio({ + ...props +}: React.ComponentProps) { + return ; +} + +export { AspectRatio }; diff --git a/reference-site/src/app/components/ui/avatar.tsx b/reference-site/src/app/components/ui/avatar.tsx new file mode 100644 index 0000000..c990451 --- /dev/null +++ b/reference-site/src/app/components/ui/avatar.tsx @@ -0,0 +1,53 @@ +"use client"; + +import * as React from "react"; +import * as AvatarPrimitive from "@radix-ui/react-avatar"; + +import { cn } from "./utils"; + +function Avatar({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AvatarImage({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AvatarFallback({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export { Avatar, AvatarImage, AvatarFallback }; diff --git a/reference-site/src/app/components/ui/badge.tsx b/reference-site/src/app/components/ui/badge.tsx new file mode 100644 index 0000000..2ccc2c4 --- /dev/null +++ b/reference-site/src/app/components/ui/badge.tsx @@ -0,0 +1,46 @@ +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "./utils"; + +const badgeVariants = cva( + "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90", + secondary: + "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + destructive: + "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +function Badge({ + className, + variant, + asChild = false, + ...props +}: React.ComponentProps<"span"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot : "span"; + + return ( + + ); +} + +export { Badge, badgeVariants }; diff --git a/reference-site/src/app/components/ui/breadcrumb.tsx b/reference-site/src/app/components/ui/breadcrumb.tsx new file mode 100644 index 0000000..8f84d7e --- /dev/null +++ b/reference-site/src/app/components/ui/breadcrumb.tsx @@ -0,0 +1,109 @@ +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { ChevronRight, MoreHorizontal } from "lucide-react"; + +import { cn } from "./utils"; + +function Breadcrumb({ ...props }: React.ComponentProps<"nav">) { + return