Put down initial structure of background and cards
This commit is contained in:
parent
6fe67dcd19
commit
883b3d93f7
25
Dockerfile
Normal file
25
Dockerfile
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
FROM node:18-alpine AS base
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
FROM base AS deps
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
# Build the app
|
||||||
|
FROM base AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Production image
|
||||||
|
FROM base AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
ENV NODE_ENV production
|
||||||
|
COPY --from=builder /app/public ./public
|
||||||
|
COPY --from=builder /app/.next/standalone ./
|
||||||
|
COPY --from=builder /app/.next/static ./.next/static
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["node", "server.js"]
|
||||||
72
app/page.tsx
72
app/page.tsx
|
|
@ -1,65 +1,25 @@
|
||||||
import Image from "next/image";
|
import EventCard from "@/components/EventCard";
|
||||||
|
import dynamic from 'next/dynamic';
|
||||||
|
|
||||||
|
const Starfield = dynamic(() => import('@/components/Starfield'), {
|
||||||
|
ssr: false
|
||||||
|
});
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black">
|
<main className="min-h-screen flex flex-col items-center justify-center p-8">
|
||||||
<main className="flex min-h-screen w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
|
<Starfield />
|
||||||
<Image
|
|
||||||
className="dark:invert"
|
<h1 className="text-white font-mono text-2xl mb-12 tracking-[0.2em] uppercase">
|
||||||
src="/next.svg"
|
Mission Control // Ground Station
|
||||||
alt="Next.js logo"
|
|
||||||
width={100}
|
|
||||||
height={20}
|
|
||||||
priority
|
|
||||||
/>
|
|
||||||
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
|
|
||||||
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
|
|
||||||
To get started, edit the page.tsx file.
|
|
||||||
</h1>
|
</h1>
|
||||||
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
|
|
||||||
Looking for a starting point or more instructions? Head over to{" "}
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 w-full max-w-4xl">
|
||||||
<a
|
<EventCard
|
||||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
title="ISS Pass: Home"
|
||||||
className="font-medium text-zinc-950 dark:text-zinc-50"
|
targetDate={new Date()}
|
||||||
>
|
|
||||||
Templates
|
|
||||||
</a>{" "}
|
|
||||||
or the{" "}
|
|
||||||
<a
|
|
||||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
className="font-medium text-zinc-950 dark:text-zinc-50"
|
|
||||||
>
|
|
||||||
Learning
|
|
||||||
</a>{" "}
|
|
||||||
center.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
|
|
||||||
<a
|
|
||||||
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
|
|
||||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
className="dark:invert"
|
|
||||||
src="/vercel.svg"
|
|
||||||
alt="Vercel logomark"
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
/>
|
/>
|
||||||
Deploy Now
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
|
|
||||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Documentation
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
32
components/EventCard.tsx
Normal file
32
components/EventCard.tsx
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
"use client";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import { Rocket } from "lucide-react";
|
||||||
|
import { EventCardProps } from "@/types/space"
|
||||||
|
|
||||||
|
|
||||||
|
export default function EventCard({ title, targetDate, icon }: EventCardProps) {
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
className="relative p-6 rounded-xl border border-slate-800 bg-slate-900/50 backdrop-blur-md overflow-hidden group"
|
||||||
|
>
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-r from-blue-500/20 to-purple-500/20 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none" />
|
||||||
|
|
||||||
|
<div className="flex items-center gap-4 mb-4">
|
||||||
|
<div className="p-2 bg-blue-500/10 rounded-lg text-blue-400">
|
||||||
|
{icon || <Rocket size={20} />}
|
||||||
|
</div>
|
||||||
|
<h3 className="text-slate-300 font-mono tracking-widest uppercase text-sm">{title}</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-4xl font-mono text-white flex gap-2">
|
||||||
|
<span>02</span><span className="text-slate-500">:</span>
|
||||||
|
<span>14</span><span className="text-slate-500">:</span>
|
||||||
|
<span>55</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-xs text-slate-500 mt-2 font-mono uppercase">T-Minus to Horizon</p>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
41
components/Starfield.tsx
Normal file
41
components/Starfield.tsx
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
"use client";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { Star } from "@/types/space"
|
||||||
|
|
||||||
|
export default function Starfield() {
|
||||||
|
const [stars] = useState<Star[]>(() =>
|
||||||
|
Array.from({ length: 80 }).map((_, i) => ({
|
||||||
|
id: i,
|
||||||
|
left: `${Math.random() * 100}%`,
|
||||||
|
top: `${Math.random() * 100}%`,
|
||||||
|
size: Math.random() * 2 + 1,
|
||||||
|
duration: 2 + Math.random() * 3,
|
||||||
|
delay: Math.random() * 5,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 z-[-1] bg-[#020617] overflow-hidden">
|
||||||
|
<div className="absolute inset-0 bg-[radial-gradient(circle_at_50%_50%,_rgba(30,58,138,0.2)_0%,_transparent_50%)]" />
|
||||||
|
{stars.map((star) => (
|
||||||
|
<motion.div
|
||||||
|
key={star.id}
|
||||||
|
className="absolute bg-white rounded-full shadow-[0_0_5px_white]"
|
||||||
|
style={{
|
||||||
|
top: star.top,
|
||||||
|
left: star.left,
|
||||||
|
width: star.size,
|
||||||
|
height: star.size,
|
||||||
|
}}
|
||||||
|
animate={{ opacity: [0.2, 0.8, 0.2] }}
|
||||||
|
transition={{
|
||||||
|
duration: star.duration,
|
||||||
|
repeat: Infinity,
|
||||||
|
delay: star.delay,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@ import type { NextConfig } from "next";
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
/* config options here */
|
/* config options here */
|
||||||
reactCompiler: true,
|
output: 'standalone',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
|
|
|
||||||
5
types/cards.ts
Normal file
5
types/cards.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
interface EventCardProps {
|
||||||
|
title: string;
|
||||||
|
targetDate: Date;
|
||||||
|
icon?: React.ReactNode;
|
||||||
|
}
|
||||||
30
types/space.ts
Normal file
30
types/space.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
|
export interface ISSPass {
|
||||||
|
id: string;
|
||||||
|
timestamp: number;
|
||||||
|
duration: number;
|
||||||
|
maxElevation: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CelestialEvent {
|
||||||
|
title: string;
|
||||||
|
date: Date;
|
||||||
|
type: 'iss' | 'eclipse' | 'comet' | 'launch';
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Star {
|
||||||
|
id: number;
|
||||||
|
left: string;
|
||||||
|
top: string;
|
||||||
|
size: number;
|
||||||
|
duration: number;
|
||||||
|
delay: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EventCardProps {
|
||||||
|
title: string;
|
||||||
|
targetDate: Date;
|
||||||
|
icon?: ReactNode;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue