Before refactor
This commit is contained in:
parent
56afa86704
commit
02c12b6e15
136
app/infrastructure/page.tsx
Normal file
136
app/infrastructure/page.tsx
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
"use client";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import { ShieldCheck, Zap, Globe, Cpu, Terminal } from "lucide-react";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
const CAPABILITIES = [
|
||||||
|
{
|
||||||
|
title: "Cloud Orchestration",
|
||||||
|
icon: <Cpu size={16} />,
|
||||||
|
skills: ["Kubernetes", "Docker", "Cloud Run", "Multi-Region VPC"],
|
||||||
|
description:
|
||||||
|
"Managing containerized application lifecycles and software-defined networks to ensure high availability and regional data sovereignty.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Provisioning & IaC",
|
||||||
|
icon: <Terminal size={16} />,
|
||||||
|
skills: ["Terraform", "Makefiles", "Shell Scripting", "Versioned State"],
|
||||||
|
description:
|
||||||
|
"Defining environment state through version-controlled configurations to ensure reproducible, predictable, and audit-ready cloud resources.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Deployment Pipelines",
|
||||||
|
icon: <Zap size={16} />,
|
||||||
|
skills: [
|
||||||
|
"GitHub Actions",
|
||||||
|
"Woodpecker CI",
|
||||||
|
"Fastlane",
|
||||||
|
"Automated Testing",
|
||||||
|
],
|
||||||
|
description:
|
||||||
|
"Architecting CI/CD workflows that bridge the gap between local development and production environments with zero-downtime strategies.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Governance & Security",
|
||||||
|
icon: <ShieldCheck size={16} />,
|
||||||
|
skills: ["NHS DSPT", "Cyber Essentials", "DPIA", "Secret Management"],
|
||||||
|
description:
|
||||||
|
"Hardening infrastructure and delivery pipelines to maintain alignment with UK health data standards and government security frameworks.",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function InfrastructurePage() {
|
||||||
|
return (
|
||||||
|
<div className="max-w-5xl mx-auto px-6 py-16 font-mono">
|
||||||
|
{/* Tightened Header */}
|
||||||
|
<header className="mb-16">
|
||||||
|
<div className="flex items-center gap-2 mb-3">
|
||||||
|
<span className="w-1.5 h-1.5 rounded-full bg-blue-500 animate-pulse" />
|
||||||
|
<h1 className="text-3xl font-bold tracking-tighter text-white uppercase">
|
||||||
|
Infrastructure and Operationss
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<p className="text-neutral-500 max-w-xl leading-relaxed text-sm">
|
||||||
|
Technical manifest of experiences in cloud orchestration, deployment
|
||||||
|
pipelines, and security frameworks designed for resilient services in
|
||||||
|
regulated environments.
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{/* Tightened Specification List */}
|
||||||
|
<div className="space-y-6 mb-32">
|
||||||
|
{CAPABILITIES.map((cap, i) => (
|
||||||
|
<motion.div
|
||||||
|
key={cap.title}
|
||||||
|
initial={{ opacity: 0, y: 10 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ delay: i * 0.05, duration: 0.4 }}
|
||||||
|
className="grid grid-cols-1 md:grid-cols-12 gap-6 border-t border-neutral-800/60 pt-10"
|
||||||
|
>
|
||||||
|
<div className="md:col-span-4">
|
||||||
|
<div className="flex items-center gap-3 text-blue-500/90">
|
||||||
|
{cap.icon}
|
||||||
|
<h3 className="text-[10px] font-bold tracking-[0.25em] uppercase">
|
||||||
|
{cap.title}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="md:col-span-8">
|
||||||
|
<p className="text-neutral-400 text-sm leading-relaxed mb-6">
|
||||||
|
{cap.description}
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-wrap gap-x-6 gap-y-2">
|
||||||
|
{cap.skills.map((skill) => (
|
||||||
|
<span
|
||||||
|
key={skill}
|
||||||
|
className="text-[9px] font-mono text-neutral-600 uppercase tracking-widest"
|
||||||
|
>
|
||||||
|
{`// ${skill}`}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Corrected Alignment Featured Section */}
|
||||||
|
<section className="bg-neutral-900/40 border border-neutral-800 p-8 md:p-12 rounded-3xl">
|
||||||
|
<div className="flex flex-col lg:flex-row lg:items-center justify-between gap-10">
|
||||||
|
<div className="flex-1">
|
||||||
|
<h2 className="text-[10px] font-mono text-blue-500 uppercase tracking-[0.3em] mb-4">
|
||||||
|
Selected Case Study
|
||||||
|
</h2>
|
||||||
|
<h3 className="text-xl font-bold text-white mb-3 tracking-tight">
|
||||||
|
Medical-Grade Architecture Analysis
|
||||||
|
</h3>
|
||||||
|
<p className="text-neutral-500 leading-relaxed text-sm max-w-2xl">
|
||||||
|
An examination of a multi-cloud environment built to satisfy
|
||||||
|
<span className="text-neutral-300"> UK Cyber Essentials</span> and
|
||||||
|
<span className="text-neutral-300"> NHS DSPT</span> standards,
|
||||||
|
focusing on data residency and infrastructure-level security.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="shrink-0">
|
||||||
|
<Link href="/projects/ayla">
|
||||||
|
<motion.div
|
||||||
|
whileHover={{
|
||||||
|
scale: 1.02,
|
||||||
|
backgroundColor: "#3b82f6",
|
||||||
|
color: "#fff",
|
||||||
|
}}
|
||||||
|
whileTap={{ scale: 0.98 }}
|
||||||
|
className="px-8 py-4 rounded-xl bg-white text-black font-bold text-[11px] uppercase tracking-[0.2em] flex items-center gap-3 transition-all duration-300"
|
||||||
|
>
|
||||||
|
Examine Logic
|
||||||
|
<Zap size={14} />
|
||||||
|
</motion.div>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
11
app/page.tsx
11
app/page.tsx
|
|
@ -181,21 +181,22 @@ export default function Home() {
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{/* Middle Row: DevOps */}
|
{/* Middle Row: DevOps */}
|
||||||
<Link href="/projects/infrastructure" className="group md:col-span-2">
|
<Link href="/infrastructure" className="group md:col-span-2">
|
||||||
<motion.div
|
<motion.div
|
||||||
whileHover={{ y: -5 }}
|
whileHover={{ y: -5 }}
|
||||||
className="p-6 rounded-3xl bg-neutral-900 border border-neutral-800 h-full flex flex-col justify-between min-h-[260px] relative transition-colors hover:border-green-500/30"
|
className="p-6 rounded-3xl bg-neutral-900 border border-neutral-800 h-full flex flex-col justify-between min-h-[260px] relative transition-colors hover:border-green-500/30"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<Server className="text-green-400 w-6 h-6 mb-4" />
|
<Server className="text-green-400 w-6 h-6 mb-4" />
|
||||||
<h3 className="font-bold text-xl mb-2">DevOps</h3>
|
<h3 className="font-bold text-xl mb-2">Infrastructure</h3>
|
||||||
<p className="text-sm text-neutral-500 leading-relaxed">
|
<p className="text-sm text-neutral-500 leading-relaxed">
|
||||||
Managing self-hosted cloud nodes with automated CI/CD
|
Architecting resilient cloud environments with automated IaC,
|
||||||
pipelines and proactive monitoring.
|
multi-region orchestration, and high-integrity security
|
||||||
|
protocols.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-2 mt-6">
|
<div className="flex flex-wrap gap-2 mt-6">
|
||||||
{["Docker", "Woodpecker", "Hetzner", "Linux", "Uptime"].map(
|
{["Kubernetes", "Terraform", "GCP", "CI/CD", "Security"].map(
|
||||||
(tech) => (
|
(tech) => (
|
||||||
<span
|
<span
|
||||||
key={tech}
|
key={tech}
|
||||||
|
|
|
||||||
104
components/MobileStack copy.tsx
Normal file
104
components/MobileStack copy.tsx
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
"use client";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
|
import { ArrowRight } from "lucide-react";
|
||||||
|
import { MobileFrame } from "./MobileFrame";
|
||||||
|
|
||||||
|
export default function MobileStack({ images }: { images: string[] }) {
|
||||||
|
const [currentIndex, setCurrentIndex] = useState(0);
|
||||||
|
|
||||||
|
const DRAG_THRESHOLD = -150;
|
||||||
|
|
||||||
|
const getRelativeIndex = (index: number) => {
|
||||||
|
const len = images.length;
|
||||||
|
return (index - currentIndex + len) % len;
|
||||||
|
};
|
||||||
|
|
||||||
|
const next = () => setCurrentIndex((prev) => (prev + 1) % images.length);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative h-[750px] w-full flex flex-col items-center py-20 overflow-hidden group">
|
||||||
|
<div className="relative h-[650px] w-full flex justify-center items-center">
|
||||||
|
<AnimatePresence initial={false}>
|
||||||
|
{images.map((img, index) => {
|
||||||
|
const relIndex = getRelativeIndex(index);
|
||||||
|
const isTop = relIndex === 0;
|
||||||
|
|
||||||
|
const xOffset = relIndex * 90;
|
||||||
|
|
||||||
|
if (relIndex > 5) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
key={img}
|
||||||
|
style={{ zIndex: images.length - relIndex }}
|
||||||
|
initial={{ opacity: 0, x: 400 }}
|
||||||
|
animate={{
|
||||||
|
opacity: 1,
|
||||||
|
x: isTop ? 0 : xOffset,
|
||||||
|
scale: isTop ? 1 : 0.96,
|
||||||
|
filter: isTop ? "brightness(1)" : "brightness(0.4)",
|
||||||
|
pointerEvents: isTop ? "auto" : "all",
|
||||||
|
}}
|
||||||
|
exit={{
|
||||||
|
x: -1000,
|
||||||
|
opacity: 0,
|
||||||
|
transition: { duration: 0.4, ease: "easeIn" },
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
duration: 0.6,
|
||||||
|
ease: [0.22, 1, 0.36, 1],
|
||||||
|
}}
|
||||||
|
whileHover={
|
||||||
|
!isTop ? { scale: 0.98, filter: "brightness(0.6)" } : {}
|
||||||
|
}
|
||||||
|
drag={isTop ? "x" : false}
|
||||||
|
dragConstraints={{ left: 0, right: 0 }}
|
||||||
|
dragElastic={0.8}
|
||||||
|
onDrag={(_, info) => {
|
||||||
|
if (isTop && info.offset.x < DRAG_THRESHOLD) {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onDragEnd={(_, info) => {
|
||||||
|
// Backup check for quick flicks
|
||||||
|
if (isTop && info.offset.x < -100) {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onClick={() => !isTop && setCurrentIndex(index)}
|
||||||
|
className="absolute"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`${isTop ? "cursor-grab active:cursor-grabbing" : "cursor-pointer"}`}
|
||||||
|
>
|
||||||
|
<MobileFrame>
|
||||||
|
<img
|
||||||
|
src={img}
|
||||||
|
alt="App Screenshot"
|
||||||
|
draggable="false"
|
||||||
|
className="w-full h-full object-cover select-none"
|
||||||
|
/>
|
||||||
|
</MobileFrame>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</AnimatePresence>
|
||||||
|
|
||||||
|
{/* Navigation Button */}
|
||||||
|
<div className="absolute -bottom-12 z-[100]">
|
||||||
|
<button
|
||||||
|
onClick={next}
|
||||||
|
className="flex items-center gap-3 px-6 py-3 rounded-full bg-black/40 backdrop-blur-xl border border-white/5 text-white hover:bg-white/10 transition-all opacity-0 group-hover:opacity-100 group-hover:translate-y-[-20px]"
|
||||||
|
>
|
||||||
|
<span className="text-[10px] font-mono uppercase tracking-[0.2em]">
|
||||||
|
Next Screen
|
||||||
|
</span>
|
||||||
|
<ArrowRight size={16} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
113
data/projects.ts
113
data/projects.ts
|
|
@ -265,7 +265,7 @@ graph TB
|
||||||
],
|
],
|
||||||
description:
|
description:
|
||||||
"A social decision-making app that solves 'choice paralysis' by allowing groups to swipe on movies and TV shows, using a real-time matching algorithm to find common interests.",
|
"A social decision-making app that solves 'choice paralysis' by allowing groups to swipe on movies and TV shows, using a real-time matching algorithm to find common interests.",
|
||||||
storyLabel: "UX // MOBILE_SYNCHRONIZATION",
|
storyLabel: "UX // MOBILE SYNCHRONIZATION",
|
||||||
images: [
|
images: [
|
||||||
"/projects/choosa/choosa-1.jpg",
|
"/projects/choosa/choosa-1.jpg",
|
||||||
"/projects/choosa/choosa-2.jpg",
|
"/projects/choosa/choosa-2.jpg",
|
||||||
|
|
@ -320,42 +320,85 @@ graph LR
|
||||||
classDef hub fill:#f59e0b,stroke:#d97706,color:#fff
|
classDef hub fill:#f59e0b,stroke:#d97706,color:#fff
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
slug: "flutter-1",
|
slug: "nutriveat",
|
||||||
category: "mobile",
|
category: "mobile",
|
||||||
title: "Flutter-1",
|
title: "Nutriveat",
|
||||||
subtitle: "Personal R&D Pipeline",
|
subtitle: "AI-Powered Personalized Nutrition",
|
||||||
role: "Architect & Creator",
|
role: "Lead Developer & Architect",
|
||||||
duration: "2025 — Present",
|
duration: "2024 — Present",
|
||||||
stack: ["Python", "FastAPI", "Next.js", "Redis"],
|
stack: [
|
||||||
metrics: ["Sub-50ms Latency", "Automated ETL", "Self-Hosted"],
|
"Flutter",
|
||||||
|
"Firebase",
|
||||||
|
"OpenAI (GPT-4o)",
|
||||||
|
"Novita AI",
|
||||||
|
"StoreKit / Play Billing",
|
||||||
|
],
|
||||||
|
metrics: [
|
||||||
|
"Fine-tuned LLM Assistants",
|
||||||
|
"Direct Store Integrations",
|
||||||
|
"Multi-Tier Subscriptions",
|
||||||
|
],
|
||||||
description:
|
description:
|
||||||
"A data science pipeline tool built to explore high-speed processing and real-time visualization of large datasets.",
|
"A comprehensive health and nutrition platform that leverages fine-tuned generative AI to architect personalized meal plans, automate shopping lists, and provide real-time culinary assistance.",
|
||||||
images: ["/datasaur-1.jpg"],
|
storyLabel: "AI ORCHESTRATION // MONETIZATION",
|
||||||
repoUrl: "https://git.georgew.dev/georgew/datasaur",
|
images: [
|
||||||
liveUrl: "https://ratoong.com",
|
"/projects/nutriveat/nutriveat-6.jpg",
|
||||||
engineeringStory:
|
"/projects/nutriveat/nutriveat-1.jpg",
|
||||||
"In this high-stakes medical environment, I implemented a custom audit logging system that ensured every state change was immutable...",
|
"/projects/nutriveat/nutriveat-2.jpg",
|
||||||
storyLabel: "DATA EFFICIENCY",
|
"/projects/nutriveat/nutriveat-3.jpg",
|
||||||
isPrivate: true,
|
"/projects/nutriveat/nutriveat-4.jpg",
|
||||||
},
|
"/projects/nutriveat/nutriveat-5.jpg",
|
||||||
{
|
],
|
||||||
slug: "flutter-2",
|
isPrivate: false,
|
||||||
category: "mobile",
|
engineeringStory: `
|
||||||
title: "Flutter-1",
|
Nutriveat represents a deep dive into the practical application of Large Language Models (LLMs) in a consumer-facing mobile environment. The goal was to move beyond a standard "chat wrapper" and create a deeply integrated tool that understands the nuance of dietary constraints, kitchen logistics, and user budgets.
|
||||||
subtitle: "Personal R&D Pipeline",
|
|
||||||
role: "Architect & Creator",
|
#### Fine-Tuned AI & Structured Output
|
||||||
duration: "2025 — Present",
|
A major engineering hurdle was ensuring the AI generated valid, consistent, and safe meal plans. I implemented a system of fine-tuned system prompts and strict schema validation within **Cloud Functions** to force GPT-4o to return structured data. This allowed the app to take raw AI output and instantly transform it into actionable Firestore documents, shopping list items, and high-fidelity image prompts for **Novita AI**.
|
||||||
stack: ["Python", "FastAPI", "Next.js", "Redis"],
|
|
||||||
metrics: ["Sub-50ms Latency", "Automated ETL", "Self-Hosted"],
|
#### Native Subscription Architecture
|
||||||
description:
|
To support the ongoing API costs of generative AI, I architected a robust multi-tier subscription model (Monthly/Annual). I implemented the monetization layer by integrating directly with the **Apple App Store (StoreKit)** and **Google Play Console (Billing Library)**. This involved architecting a custom server-side validation system in Cloud Functions to handle real-time subscription status, grace periods, and cross-platform entitlement logic without the use of third-party middleware.
|
||||||
"A data science pipeline tool built to explore high-speed processing and real-time visualization of large datasets.",
|
|
||||||
images: ["/datasaur-1.jpg"],
|
#### Context-Aware Culinary Assistance
|
||||||
repoUrl: "https://git.georgew.dev/georgew/datasaur",
|
I developed a specialized AI Chatbot designed to function as a "Kitchen Assistant." Unlike general-purpose bots, this assistant is provided with the specific context of the user's current meal plan, dietary allergies, and available utensils. By using **RAG-lite (Retrieval-Augmented Generation)** principles, the bot can provide accurate unit conversions and tailored cooking instructions that respect the user's specific kitchen setup.
|
||||||
liveUrl: "https://ratoong.com",
|
`,
|
||||||
engineeringStory:
|
mermaidChart: `
|
||||||
"In this high-stakes medical environment, I implemented a custom audit logging system that ensured every state change was immutable...",
|
graph LR
|
||||||
storyLabel: "DATA EFFICIENCY",
|
subgraph Client_Mobile [Flutter Frontend]
|
||||||
isPrivate: true,
|
A[Mobile App]:::traffic
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Firebase_Backend [Control Plane]
|
||||||
|
Hub((Firebase SDK)):::hub
|
||||||
|
C[Firestore DB]:::node
|
||||||
|
D[Cloud Functions]:::node
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph AI_Orchestration [Intelligence Layer]
|
||||||
|
F[OpenAI / GPT-4o]:::node
|
||||||
|
G[Novita AI / Stable Diffusion]:::node
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Store_Integrations [Native Billing]
|
||||||
|
H[App Store / Play Store]:::traffic
|
||||||
|
end
|
||||||
|
|
||||||
|
A <--> Hub
|
||||||
|
Hub <--> C
|
||||||
|
Hub --> D
|
||||||
|
|
||||||
|
D ==>|Fine-tuned Prompts| F
|
||||||
|
D ==>|Image Generation| G
|
||||||
|
F -.->|JSON Parsing| D
|
||||||
|
|
||||||
|
A <-->|Native IAP Flow| H
|
||||||
|
H -.->|Server-to-Server Hooks| D
|
||||||
|
|
||||||
|
classDef traffic fill:#2563eb,stroke:#3b82f6,color:#fff
|
||||||
|
classDef node fill:#16a34a,stroke:#22c55e,color:#fff
|
||||||
|
classDef hub fill:#f59e0b,stroke:#d97706,color:#fff
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
BIN
public/projects/nutriveat/nutriveat-1.jpg
Normal file
BIN
public/projects/nutriveat/nutriveat-1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 91 KiB |
BIN
public/projects/nutriveat/nutriveat-2.jpg
Normal file
BIN
public/projects/nutriveat/nutriveat-2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 71 KiB |
BIN
public/projects/nutriveat/nutriveat-3.jpg
Normal file
BIN
public/projects/nutriveat/nutriveat-3.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
BIN
public/projects/nutriveat/nutriveat-4.jpg
Normal file
BIN
public/projects/nutriveat/nutriveat-4.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 73 KiB |
BIN
public/projects/nutriveat/nutriveat-5.jpg
Normal file
BIN
public/projects/nutriveat/nutriveat-5.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 109 KiB |
BIN
public/projects/nutriveat/nutriveat-6.jpg
Normal file
BIN
public/projects/nutriveat/nutriveat-6.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 92 KiB |
Loading…
Reference in a new issue