From b40f6bc0578dc4e3575c23d16fd48d9e298d0339 Mon Sep 17 00:00:00 2001 From: itMatos Date: Sat, 4 Jul 2026 23:15:29 -0300 Subject: [PATCH 01/11] refactor launches card --- pages/launches/index.tsx | 43 ++++++++----------- .../LaunchCard/LaunchSummaryCard.tsx | 24 +++++------ src/shared/styles/colors.ts | 1 + 3 files changed, 29 insertions(+), 39 deletions(-) create mode 100644 src/shared/styles/colors.ts diff --git a/pages/launches/index.tsx b/pages/launches/index.tsx index 21619b3..37970ec 100644 --- a/pages/launches/index.tsx +++ b/pages/launches/index.tsx @@ -10,6 +10,7 @@ import HeadTags from '@/components/general/HeadTags'; import NavBar from '@/src/components/Navbar/NavBar'; import HeroSection from '@/components/projects-components/HeroSection'; import { LOCALE } from '@/src/shared/consts/locales.const'; +import { BACKGROUND_COLOR } from '@/src/shared/styles/colors'; const SELECTED_LAUNCH_STORAGE_KEY = 'zenith-selected-launch'; const SCROLL_POSITION_KEY = 'zenith-launches-scroll'; @@ -20,8 +21,6 @@ export default function LaunchesPage() { const router = useRouter(); const { t } = useTranslation(); - const launchesTitle = t('allLaunches:allLaunchesPage.header.title'); - const launchesDescription = t('allLaunches:allLaunchesPage.header.description'); useEffect(() => { if (isLoadingAllLaunches || launches.length === 0) return; @@ -55,24 +54,15 @@ export default function LaunchesPage() { subtitle={t(LOCALE.LAUNCHES.META_TAGS.SUBTITLE)} page="launches" /> - - {/* - - {launchesTitle} - - - {launchesDescription} - - */} - + {error && ( - + {error} )} {isLoadingAllLaunches && ( - + )} @@ -80,17 +70,7 @@ export default function LaunchesPage() { {!isLoadingAllLaunches && launches.length === 0 && Nenhum lançamento encontrado.} {!isLoadingAllLaunches && launches.length > 0 && ( - + {launches.map((launch) => ( ); } + +const containerSx = { py: 6, backgroundColor: BACKGROUND_COLOR }; +const errorAlertSx = { mb: 3 }; +const loadingBoxSx = { display: 'flex', justifyContent: 'center', py: 10 }; +const launchesGridSx = { + display: 'grid', + gap: 3, + gridTemplateColumns: { + xs: '1fr', + sm: 'repeat(2, minmax(0, 1fr))', + md: 'repeat(2, minmax(0, 1fr))' + } +}; diff --git a/src/components/LaunchCard/LaunchSummaryCard.tsx b/src/components/LaunchCard/LaunchSummaryCard.tsx index 2940192..cc18c34 100644 --- a/src/components/LaunchCard/LaunchSummaryCard.tsx +++ b/src/components/LaunchCard/LaunchSummaryCard.tsx @@ -4,16 +4,20 @@ import HeightIcon from '@mui/icons-material/Height'; import LaunchAndLandingCities from '@/src/components/LaunchAndLandingCities/LaunchAndLandingCities'; import { formatAltitudeInKm, formatLaunchDatetime, formatLaunchName } from '@/src/shared/utils/formatters.utils'; import { LaunchSummaryCardProps } from '@/src/shared/props/components/launch-summary-card.props'; +import MapIcon from '@mui/icons-material/Map'; export default function LaunchSummaryCard({ launch, onDetailsClick }: LaunchSummaryCardProps) { return ( - + - {formatLaunchName(launch.name)} - + + + {formatLaunchName(launch.name)} + + } label={formatAltitudeInKm(launch.max_altitude)} color="primary" variant="filled" /> + } subheader={formatLaunchDatetime(launch.launch_datetime)} /> @@ -21,19 +25,11 @@ export default function LaunchSummaryCard({ launch, onDetailsClick }: LaunchSumm - - } - label={formatAltitudeInKm(launch.max_altitude)} - color="primary" - variant="filled" - sx={{ alignSelf: 'flex-start', mt: 3 }} - /> - - diff --git a/src/shared/styles/colors.ts b/src/shared/styles/colors.ts new file mode 100644 index 0000000..8ac677e --- /dev/null +++ b/src/shared/styles/colors.ts @@ -0,0 +1 @@ +export const BACKGROUND_COLOR = '#EFEFEF' as const; From c3531c8d26d868939716922fb630e45727ae6baa Mon Sep 17 00:00:00 2001 From: itMatos Date: Sat, 4 Jul 2026 23:18:27 -0300 Subject: [PATCH 02/11] remove inline style --- .../LaunchCard/LaunchSummaryCard.tsx | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/components/LaunchCard/LaunchSummaryCard.tsx b/src/components/LaunchCard/LaunchSummaryCard.tsx index cc18c34..d40c84f 100644 --- a/src/components/LaunchCard/LaunchSummaryCard.tsx +++ b/src/components/LaunchCard/LaunchSummaryCard.tsx @@ -8,12 +8,12 @@ import MapIcon from '@mui/icons-material/Map'; export default function LaunchSummaryCard({ launch, onDetailsClick }: LaunchSummaryCardProps) { return ( - + - + + {formatLaunchName(launch.name)} } label={formatAltitudeInKm(launch.max_altitude)} color="primary" variant="filled" /> @@ -21,14 +21,14 @@ export default function LaunchSummaryCard({ launch, onDetailsClick }: LaunchSumm } subheader={formatLaunchDatetime(launch.launch_datetime)} /> - + - + - + @@ -36,3 +36,11 @@ export default function LaunchSummaryCard({ launch, onDetailsClick }: LaunchSumm ); } + +const cardSx = { height: '100%', borderRadius: 3 }; +const cardHeaderSx = { pb: 0 }; +const titleBoxSx = { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 2 }; +const titleTypographySx = { fontWeight: 700 }; +const cardContentSx = { py: 0, mb: 0 }; +const citiesBoxSx = { display: 'flex', alignItems: 'flex-start' }; +const cardActionsSx = { justifyContent: 'flex-end', pt: 0 }; From fc41fba9099d5879061cf4c1fd536520f6a89dd0 Mon Sep 17 00:00:00 2001 From: itMatos Date: Sat, 4 Jul 2026 23:53:53 -0300 Subject: [PATCH 03/11] enhance page --- pages/launches/index.tsx | 15 +++++++-------- src/shared/styles/colors.ts | 2 ++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/pages/launches/index.tsx b/pages/launches/index.tsx index 37970ec..1154e3b 100644 --- a/pages/launches/index.tsx +++ b/pages/launches/index.tsx @@ -19,7 +19,6 @@ const SCROLL_POSITION_KEY = 'zenith-launches-scroll'; export default function LaunchesPage() { const { launches, isLoadingAllLaunches, error } = useAllLaunches(); const router = useRouter(); - const { t } = useTranslation(); useEffect(() => { @@ -38,15 +37,15 @@ export default function LaunchesPage() { router.push(`/launches/${slugifyLaunchName(launch.name)}`); }; + const pageName = t(LOCALE.LAUNCHES.META_TAGS.PAGE_NAME); + const headTagsTitle = t(LOCALE.LAUNCHES.META_TAGS.TITLE); + const pageTitle = t(LOCALE.LAUNCHES.META_TAGS.PAGE_TITLE); + const pageDescription = t(LOCALE.LAUNCHES.META_TAGS.DESCRIPTION); + const lang = t(LOCALE.LAUNCHES.META_TAGS.LANG); + return ( <> - + Date: Sun, 5 Jul 2026 20:16:32 -0300 Subject: [PATCH 04/11] update timeline component and colors --- pages/launches/index.tsx | 53 ++++++++++--------- .../LaunchAndLandingCities.tsx | 35 +++++++----- .../LaunchCard/LaunchSummaryCard.tsx | 19 ++++++- src/shared/styles/colors.ts | 45 ++++++++++++++++ 4 files changed, 113 insertions(+), 39 deletions(-) diff --git a/pages/launches/index.tsx b/pages/launches/index.tsx index 1154e3b..2285114 100644 --- a/pages/launches/index.tsx +++ b/pages/launches/index.tsx @@ -53,38 +53,41 @@ export default function LaunchesPage() { subtitle={t(LOCALE.LAUNCHES.META_TAGS.SUBTITLE)} page="launches" /> - - {error && ( - - {error} - - )} + + + {error && ( + + {error} + + )} - {isLoadingAllLaunches && ( - - - - )} + {isLoadingAllLaunches && ( + + + + )} - {!isLoadingAllLaunches && launches.length === 0 && Nenhum lançamento encontrado.} + {!isLoadingAllLaunches && launches.length === 0 && Nenhum lançamento encontrado.} - {!isLoadingAllLaunches && launches.length > 0 && ( - - {launches.map((launch) => ( - - ))} - - )} - + {!isLoadingAllLaunches && launches.length > 0 && ( + + {launches.map((launch) => ( + + ))} + + )} + + ); } -const containerSx = { py: 6, backgroundColor: BACKGROUND_COLOR }; +const pageBackgroundSx = { backgroundColor: BACKGROUND_COLOR }; +const containerSx = { py: 6 }; const errorAlertSx = { mb: 3 }; const loadingBoxSx = { display: 'flex', justifyContent: 'center', py: 10 }; const launchesGridSx = { diff --git a/src/components/LaunchAndLandingCities/LaunchAndLandingCities.tsx b/src/components/LaunchAndLandingCities/LaunchAndLandingCities.tsx index 1eb7cce..d02e1ec 100644 --- a/src/components/LaunchAndLandingCities/LaunchAndLandingCities.tsx +++ b/src/components/LaunchAndLandingCities/LaunchAndLandingCities.tsx @@ -12,40 +12,51 @@ import type {} from '@mui/lab/themeAugmentation'; import ShareLocationIcon from '@mui/icons-material/ShareLocation'; import PinDropIcon from '@mui/icons-material/PinDrop'; import { LaunchAndLandingCitiesProps } from '@/src/shared/props/components/launch-and-landing.props'; +import { colors } from '@/src/shared/styles/colors'; export default function LaunchAndLandingCities({ startLabel, endLabel, - startIcon = , - endIcon = + startIcon = , + endIcon = }: LaunchAndLandingCitiesProps) { return ( - + }} + > + - {startIcon} - + {startIcon} - {startLabel} + {startLabel} - + + + + + + + + - {endIcon} + {endIcon} - {endLabel} + {endLabel} ); } + +const timelineContentSx = { display: 'flex', alignItems: 'center', py: 0, my: 0 }; +const timelineConnectorSx = { minHeight: 32 }; +const connectorItemSx = { minHeight: 0, mb: 0, py: 0 }; +const connectorSeparatorSx = { alignItems: 'center', width: 24, pl: 2 }; diff --git a/src/components/LaunchCard/LaunchSummaryCard.tsx b/src/components/LaunchCard/LaunchSummaryCard.tsx index d40c84f..b841826 100644 --- a/src/components/LaunchCard/LaunchSummaryCard.tsx +++ b/src/components/LaunchCard/LaunchSummaryCard.tsx @@ -5,6 +5,7 @@ import LaunchAndLandingCities from '@/src/components/LaunchAndLandingCities/Laun import { formatAltitudeInKm, formatLaunchDatetime, formatLaunchName } from '@/src/shared/utils/formatters.utils'; import { LaunchSummaryCardProps } from '@/src/shared/props/components/launch-summary-card.props'; import MapIcon from '@mui/icons-material/Map'; +import { colors } from '@/src/shared/styles/colors'; export default function LaunchSummaryCard({ launch, onDetailsClick }: LaunchSummaryCardProps) { return ( @@ -16,7 +17,13 @@ export default function LaunchSummaryCard({ launch, onDetailsClick }: LaunchSumm {formatLaunchName(launch.name)} - } label={formatAltitudeInKm(launch.max_altitude)} color="primary" variant="filled" /> + } + label={formatAltitudeInKm(launch.max_altitude)} + color="primary" + variant="filled" + sx={chipSx} + /> } subheader={formatLaunchDatetime(launch.launch_datetime)} @@ -29,7 +36,13 @@ export default function LaunchSummaryCard({ launch, onDetailsClick }: LaunchSumm - @@ -38,9 +51,11 @@ export default function LaunchSummaryCard({ launch, onDetailsClick }: LaunchSumm } const cardSx = { height: '100%', borderRadius: 3 }; +const chipSx = { backgroundColor: colors.primary[600] }; const cardHeaderSx = { pb: 0 }; const titleBoxSx = { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 2 }; const titleTypographySx = { fontWeight: 700 }; const cardContentSx = { py: 0, mb: 0 }; const citiesBoxSx = { display: 'flex', alignItems: 'flex-start' }; const cardActionsSx = { justifyContent: 'flex-end', pt: 0 }; +const detailsButtonSx = { color: colors.secondary[500] }; diff --git a/src/shared/styles/colors.ts b/src/shared/styles/colors.ts index 471e633..26803f9 100644 --- a/src/shared/styles/colors.ts +++ b/src/shared/styles/colors.ts @@ -1,3 +1,48 @@ export const BACKGROUND_COLOR = '#EFEFEF' as const; export const ZINC950 = '#09090B' as const; + +export const colors = { + // O Azul (#0193ED) - Ideal para a marca principal, links e botões primários + primary: { + 50: '#eff7ff', + 100: '#deefff', + 200: '#b6e1ff', + 300: '#75c9ff', + 400: '#2db0ff', + 500: '#0193ed', // <-- cor base + 600: '#007cd3', + 700: '#0062aa', + 800: '#00518c', + 900: '#064474', + 950: '#042b4d' + }, + // O Laranja (#F28705) - Ideal para botões de ação (Call to Action), destaque secundário + secondary: { + 50: '#fff5d4', + 100: '#ffe8a7', + 200: '#ffd770', + 300: '#ffba36', + 400: '#ffa30f', + 500: '#f28705', // <-- cor base + 600: '#c86606', + 700: '#9e500e', + 800: '#7f430f', + 900: '#452005', + 950: '#452009' + }, + // O Amarelo (#FED329) - Ideal para pequenos detalhes, ícones, alertas e "energia" + accent: { + 50: '#fffce6', + 100: '#fefce8', + 200: '#fffac2', + 300: '#fff188', + 400: '#ffe144', + 500: '#fed329', // <-- cor base + 600: '#eeb504', + 700: '#cd8b01', + 800: '#a46204', + 900: '#874d0c', + 950: '#733f10' + } +}; From 4c1c85e595ea627f3664070012fbe91d68ecf632 Mon Sep 17 00:00:00 2001 From: itMatos Date: Sun, 5 Jul 2026 20:24:33 -0300 Subject: [PATCH 05/11] add loading progressbar --- pages/_app.jsx | 4 +- pages/launches/index.tsx | 6 +++ .../RouteProgressBar/RouteProgressBar.tsx | 45 +++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 src/components/RouteProgressBar/RouteProgressBar.tsx diff --git a/pages/_app.jsx b/pages/_app.jsx index 5929631..c64b171 100644 --- a/pages/_app.jsx +++ b/pages/_app.jsx @@ -1,14 +1,16 @@ import React from 'react'; import Head from 'next/head'; import 'styles/global.css'; +import RouteProgressBar from '@/src/components/RouteProgressBar/RouteProgressBar'; export default function MyApp({ Component, pageProps }) { - + return ( <> + ) diff --git a/pages/launches/index.tsx b/pages/launches/index.tsx index 2285114..a64d9db 100644 --- a/pages/launches/index.tsx +++ b/pages/launches/index.tsx @@ -31,6 +31,12 @@ export default function LaunchesPage() { } }, [isLoadingAllLaunches, launches]); + useEffect(() => { + if (isLoadingAllLaunches || launches.length === 0) return; + + router.prefetch('/launches/[launchName]', `/launches/${slugifyLaunchName(launches[0].name)}`); + }, [isLoadingAllLaunches, launches, router]); + const handleLaunchDetails = (launch: (typeof launches)[number]) => { sessionStorage.setItem(SELECTED_LAUNCH_STORAGE_KEY, JSON.stringify(launch)); sessionStorage.setItem(SCROLL_POSITION_KEY, String(window.scrollY)); diff --git a/src/components/RouteProgressBar/RouteProgressBar.tsx b/src/components/RouteProgressBar/RouteProgressBar.tsx new file mode 100644 index 0000000..fc60c5d --- /dev/null +++ b/src/components/RouteProgressBar/RouteProgressBar.tsx @@ -0,0 +1,45 @@ +import { useEffect, useState } from 'react'; +import { useRouter } from 'next/router'; +import { Box } from '@mui/material'; +import { colors } from '@/src/shared/styles/colors'; + +export default function RouteProgressBar() { + const router = useRouter(); + const [isNavigating, setIsNavigating] = useState(false); + + useEffect(() => { + const handleStart = () => setIsNavigating(true); + const handleDone = () => setIsNavigating(false); + + router.events.on('routeChangeStart', handleStart); + router.events.on('routeChangeComplete', handleDone); + router.events.on('routeChangeError', handleDone); + + return () => { + router.events.off('routeChangeStart', handleStart); + router.events.off('routeChangeComplete', handleDone); + router.events.off('routeChangeError', handleDone); + }; + }, [router]); + + if (!isNavigating) return null; + + return ; +} + +const barSx = { + position: 'fixed', + top: 0, + left: 0, + height: 3, + width: '100%', + zIndex: 2000, + backgroundColor: colors.secondary[500], + transformOrigin: 'left', + animation: 'route-progress-bar 1.2s ease-in-out infinite', + '@keyframes route-progress-bar': { + '0%': { transform: 'scaleX(0)', opacity: 1 }, + '70%': { transform: 'scaleX(0.85)', opacity: 1 }, + '100%': { transform: 'scaleX(0.95)', opacity: 0.6 } + } +}; From 87194d5c272b61b3a0f72dc779f77efdc04b5d83 Mon Sep 17 00:00:00 2001 From: itMatos Date: Sun, 5 Jul 2026 20:28:06 -0300 Subject: [PATCH 06/11] add prefetch to launch page --- pages/launches/[launchName].tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pages/launches/[launchName].tsx b/pages/launches/[launchName].tsx index c6384a2..e64042c 100644 --- a/pages/launches/[launchName].tsx +++ b/pages/launches/[launchName].tsx @@ -44,6 +44,10 @@ export default function LaunchDetailsPage() { [] ); + useEffect(() => { + router.prefetch('/launches'); + }, [router]); + useEffect(() => { if (!router.isReady) { return; From 8d54b9ebaa704321825bd5e7235826a3350919cc Mon Sep 17 00:00:00 2001 From: itMatos Date: Sun, 5 Jul 2026 20:35:25 -0300 Subject: [PATCH 07/11] enhance behavior when change pages --- pages/launches/[launchName].tsx | 85 ++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 28 deletions(-) diff --git a/pages/launches/[launchName].tsx b/pages/launches/[launchName].tsx index e64042c..5bbab17 100644 --- a/pages/launches/[launchName].tsx +++ b/pages/launches/[launchName].tsx @@ -10,6 +10,7 @@ import { slugifyLaunchName } from '@/src/shared/utils/formatters.utils'; import { useGetLaunchContent } from '@/src/core/services/launches/useGetLaunchContent.service'; +import { getAllLaunches } from '@/src/core/services/launches.service'; import { LaunchSummary } from '@/src/shared/types/api/launches-api.types'; import dynamic from 'next/dynamic'; import LaunchAndLandingCities from '@/src/components/LaunchAndLandingCities/LaunchAndLandingCities'; @@ -32,6 +33,7 @@ export default function LaunchDetailsPage() { const launchName = typeof router.query.launchName === 'string' ? router.query.launchName : ''; const [launch, setLaunch] = useState(null); const [launchResolved, setLaunchResolved] = useState(false); + const [launchNotFound, setLaunchNotFound] = useState(false); const { records, isLoadingRecords, recordsError } = useGetLaunchContent(launch?.download_url ?? '', Boolean(launch)); @@ -53,32 +55,65 @@ export default function LaunchDetailsPage() { return; } - setLaunchResolved(false); + const controller = new AbortController(); - const storedLaunch = sessionStorage.getItem(SELECTED_LAUNCH_STORAGE_KEY); + const resolveFromStorage = (): LaunchSummary | null => { + const storedLaunch = sessionStorage.getItem(SELECTED_LAUNCH_STORAGE_KEY); + if (!storedLaunch) { + return null; + } - if (!storedLaunch) { - setLaunch(null); - setLaunchResolved(true); - return; - } + try { + const parsedLaunch: LaunchSummary = JSON.parse(storedLaunch); + return slugifyLaunchName(parsedLaunch.name) === launchName ? parsedLaunch : null; + } catch { + return null; + } + }; - try { - const parsedLaunch: LaunchSummary = JSON.parse(storedLaunch); - const storedLaunchName = slugifyLaunchName(parsedLaunch.name); + const resolveLaunch = async () => { + setLaunchResolved(false); + setLaunchNotFound(false); - if (storedLaunchName !== launchName) { - setLaunch(null); + const storedMatch = resolveFromStorage(); + if (storedMatch) { + setLaunch(storedMatch); setLaunchResolved(true); return; } - setLaunch(parsedLaunch); - } catch { - setLaunch(null); - } finally { - setLaunchResolved(true); - } + try { + const allLaunches = await getAllLaunches(); + if (controller.signal.aborted) { + return; + } + + const matchedLaunch = allLaunches.find((item) => slugifyLaunchName(item.name) === launchName) ?? null; + + if (matchedLaunch) { + sessionStorage.setItem(SELECTED_LAUNCH_STORAGE_KEY, JSON.stringify(matchedLaunch)); + setLaunch(matchedLaunch); + } else { + setLaunch(null); + setLaunchNotFound(true); + } + } catch { + if (!controller.signal.aborted) { + setLaunch(null); + setLaunchNotFound(true); + } + } finally { + if (!controller.signal.aborted) { + setLaunchResolved(true); + } + } + }; + + resolveLaunch(); + + return () => { + controller.abort(); + }; }, [launchName, router.isReady]); return ( @@ -86,7 +121,7 @@ export default function LaunchDetailsPage() {