diff --git a/.woodpecker.yaml b/.woodpecker.yaml index 2375dd6..e450332 100644 --- a/.woodpecker.yaml +++ b/.woodpecker.yaml @@ -2,14 +2,15 @@ variables: - &app_name "mission-control" when: - event: [push] - branch: main + - event: release steps: build-web: image: woodpeckerci/plugin-docker-buildx privileged: true settings: + build_args: + APP_VERSION: ${CI_COMMIT_TAG} platforms: linux/amd64 registry: git.georgew.dev repo: git.georgew.dev/georgew/mission-control-web diff --git a/Dockerfile b/Dockerfile index 32d6951..e83448c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,11 @@ +ARG APP_VERSION + FROM --platform=linux/amd64 node:20-bookworm AS builder WORKDIR /app COPY package*.json ./ RUN npm install +ARG APP_VERSION +ENV NEXT_PUBLIC_APP_VERSION=$APP_VERSION COPY . . RUN npm run build diff --git a/app/page.tsx b/app/page.tsx index c93a8c9..ad4520a 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -2,6 +2,9 @@ export const dynamic = 'force-dynamic'; import MissionControl from '@/components/MissionControl'; import db from '@/lib/db'; +import Footer from '@/components/Footer'; +import Starfield from '@/components/Starfield'; + export default function Home() { const issRow = db.prepare(` @@ -40,13 +43,23 @@ export default function Home() { const cosmicEnd = cosmicStart ? new Date(cosmicStart.getTime() + 14400000) : null; return ( -
+
+ +
+

+ GeorgewObservatory +

+

+ Ground Station // [55.6761° N, 12.5683° E] +

+
+
); } \ No newline at end of file diff --git a/components/EventCard.tsx b/components/EventCard.tsx index 7070051..626d02c 100644 --- a/components/EventCard.tsx +++ b/components/EventCard.tsx @@ -1,8 +1,10 @@ "use client"; +import React from 'react'; import { useState, useEffect, useRef } from 'react'; import { EventCardProps } from '@/types/space'; import { useRouter } from 'next/navigation'; -import { Sparkles } from 'lucide-react'; +import { motion, AnimatePresence } from 'framer-motion'; +import { LucideProps } from 'lucide-react'; export default function EventCard({ id, title, targetDate, endDate, icon }: EventCardProps) { const [isLive, setIsLive] = useState(false); @@ -85,15 +87,39 @@ export default function EventCard({ id, title, targetDate, endDate, icon }: Even ); } - return ( -
+return ( +
+ + {isLive && ( + + )} + +
{isLive ? ( -
- -

+
+ + {React.isValidElement(icon) + ? React.cloneElement(icon as React.ReactElement, { size: 35 }) + : icon} + + +

{id === 'iss' ? 'Look Up!' : 'Event in Progress'}

-

+ +

{id === 'iss' ? 'The ISS is directly above. Give them a wave! 👋' : `The ${title} is occurring right now.`} @@ -101,52 +127,47 @@ export default function EventCard({ id, title, targetDate, endDate, icon }: Even

) : ( <> -
-
- {icon} +
+
{icon}
+

+ {title} +

-

- {title} -

-
-
- {parseInt(timeLeft.d) >= 2 ? ( - <> - {timeLeft.d} - Days - - ) : parseInt(timeLeft.d) === 1 ? ( - <> - 01d - : - {timeLeft.h}h - - ) : ( - <> - {timeLeft.h} - : - {timeLeft.m} - : - {timeLeft.s} - - )} -
-

- {isLive ? ( - "Mission in Progress" - ) : id === 'iss' ? ( - "T-Minus to Horizon" - ) : id === 'launch' ? ( - "T-Minus to Ignition" - ) : id === 'moon' ? ( - "Until Lunar Phase" - ) : ( - "Days to Peak Phase" - )} -

+
+ {parseInt(timeLeft.d) >= 2 ? ( + <> + {timeLeft.d} + Days + + ) : ( +
+ {timeLeft.h}: + {timeLeft.m}: +
+ + + {timeLeft.s} + + +
+
+ )} +
+ +

+ {id === 'iss' ? "T-Minus to Horizon" : id === 'launch' ? "T-Minus to Ignition" : id === 'moon' ? "Until Lunar Phase" : "Days to Peak Phase"} +

)}
- ); +

+); } \ No newline at end of file diff --git a/components/Footer.tsx b/components/Footer.tsx new file mode 100644 index 0000000..81b9056 --- /dev/null +++ b/components/Footer.tsx @@ -0,0 +1,29 @@ +export default function Footer() { + return ( +
+
+ +
+
+

Pipeline Status

+ Build Status +
+
+

Engine: Next.js 15 (Standalone)

+
+ +
+
+ + Deploy: {process.env.NEXT_PUBLIC_APP_VERSION || 'v1.0.0-dev'} + +
+ +
+
+ ); +} \ No newline at end of file diff --git a/components/MissionControl.tsx b/components/MissionControl.tsx index f67db1e..c4692e1 100644 --- a/components/MissionControl.tsx +++ b/components/MissionControl.tsx @@ -1,15 +1,10 @@ "use client"; import { useMemo } from 'react'; -import dynamic from 'next/dynamic'; import EventCard from '@/components/EventCard'; import { Satellite, Rocket, Moon, Sparkles } from 'lucide-react'; import { MissionControlProps } from '@/types/space'; - - -const Starfield = dynamic(() => import('@/components/Starfield'), { - ssr: false -}); +import { motion } from 'framer-motion'; export default function MissionControl({ iss, moon, cosmic, launch }: MissionControlProps) { @@ -45,28 +40,39 @@ export default function MissionControl({ iss, moon, cosmic, launch }: MissionCon ], [iss, moon, cosmic, launch]); return ( -
- - -
-

- GeorgewObservatory -

-

- Ground Station // [55.6761° N, 12.5683° E] -

-
- -
- {events.map((event) => ( +
+
+ {events.map((event, index) => ( + +
+
+
+ + {event.id === 'iss' && "ISS Zenith // CPH"} + {event.id === 'moon' && "Lunar Cycle"} + {event.id === 'cosmic' && "Celestial Event"} + {event.id === 'launch' && "Launch Schedule"} + +
+
+ ))}