Fixed tailwind markdown beahviour and improved aesthetics on the project details page

This commit is contained in:
GeorgeWebberley 2026-01-30 12:31:47 +01:00
parent 471b251fd7
commit 49e62d5e2f
7 changed files with 9574 additions and 34 deletions

View file

@ -1,5 +1,4 @@
@import "tailwindcss";
@plugin "@tailwindcss/typography";
:root {
@ -26,3 +25,9 @@ body {
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
}
.prose {
width: 100%;
max-width: none;
word-break: break-word;
}

View file

@ -0,0 +1,138 @@
"use client";
import { use } from "react";
import { motion } from "framer-motion";
import Link from "next/link";
import { ArrowLeft, ExternalLink, Github, ShieldCheck, Cpu, Users } from "lucide-react";
import { PROJECT_REGISTRY } from "@/data/projects";
import Mermaid from "@/components/Mermaid";
import ProjectShowcase from "@/components/ProjectShowcase";
import ImageCarousel from "@/components/ImageCarousel";
import ReactMarkdown from 'react-markdown';
export default function ProjectDetail({ params }: { params: Promise<{ category: string, slug: string }> }) {
const { category, slug } = use(params);
const project = PROJECT_REGISTRY.find((p) => p.slug === slug);
if (!project) return <div>Project Not Found</div>;
if (!project) return <div className="p-24 text-white font-mono">Project Log Not Found.</div>;
return (
<main className="min-h-screen bg-[#0a0a0a] text-white p-6 md:p-12 lg:p-24">
<div className="max-w-6xl mx-auto">
{/* Navigation */}
<Link href={`/projects/${category}`} className="flex items-center gap-2 text-neutral-500 hover:text-white transition-colors mb-12 font-mono text-[10px] uppercase tracking-widest">
<ArrowLeft size={12} /> Back to {category}
</Link>
{/* Header Section */}
<header className="grid grid-cols-1 lg:grid-cols-2 gap-12 mb-20">
<motion.div initial={{ opacity: 0, x: -20 }} animate={{ opacity: 1, x: 0 }}>
<h1 className="text-6xl font-bold tracking-tighter mb-4">{project.title}</h1>
<p className="text-blue-500 font-mono text-sm uppercase tracking-widest mb-6">{project.subtitle}</p>
<p className="text-neutral-400 text-lg leading-relaxed">{project.description}</p>
<div className="flex gap-4 mt-8">
{project.liveUrl && (
<a href={project.liveUrl} className="flex items-center gap-2 bg-white text-black px-6 py-3 rounded-full font-bold text-sm hover:bg-neutral-200 transition-all">
Launch Site <ExternalLink size={14} />
</a>
)}
{project?.repoUrl && (
<a href={project.repoUrl} className="flex items-center gap-2 border border-neutral-800 px-6 py-3 rounded-full font-bold text-sm hover:bg-neutral-900 transition-all">
View Source <Github size={14} />
</a>
)}
</div>
</motion.div>
{/* Senior Stats Sidebar */}
<motion.div initial={{ opacity: 0, x: 20 }} animate={{ opacity: 1, x: 0 }} className="bg-neutral-900/30 border border-neutral-800 p-8 rounded-3xl h-fit">
<div className="space-y-8">
<div className="flex items-center gap-4">
<ShieldCheck className="text-blue-500" />
<div>
<p className="text-[10px] text-neutral-500 uppercase font-mono tracking-tighter">My Role</p>
<p className="text-sm font-semibold">{project.role}</p>
</div>
</div>
<div className="flex items-center gap-4">
<Cpu className="text-purple-500" />
<div>
<p className="text-[10px] text-neutral-500 uppercase font-mono tracking-tighter">Stack</p>
<p className="text-sm font-semibold">{project.stack.join(", ")}</p>
</div>
</div>
<div className="flex items-center gap-4">
<Users className="text-green-500" />
<div>
<p className="text-[10px] text-neutral-500 uppercase font-mono tracking-tighter">Impact</p>
<p className="text-sm font-semibold">{project.metrics.join(" • ")}</p>
</div>
</div>
</div>
</motion.div>
</header>
<section className="mb-20">
{/* Desktop Showcase View */}
<div className="hidden lg:block">
<ProjectShowcase images={project.images} />
</div>
{/* Mobile Carousel View */}
<div className="block lg:hidden">
<ImageCarousel images={project.images} />
</div>
<p className="mt-4 text-center text-[10px] font-mono text-neutral-600 uppercase tracking-[0.2em]">
Interactive Gallery Select or swipe to explore
</p>
</section>
{/* SYSTEM ARCHITECTURE (New Mermaid Section) */}
{project.mermaidChart && (
<section className="mb-8">
<div className="flex items-center gap-3 mb-8">
<h3 className="text-[10px] font-mono text-blue-500 uppercase tracking-[0.3em] font-bold">
TECHNICAL_ARCH // 01
</h3>
<div className="h-px flex-1 bg-neutral-900" />
<h2 className="text-xl font-bold tracking-tighter">System Architecture Log</h2>
</div>
<div className="bg-neutral-900/20 rounded-3xl border border-neutral-800/50 p-4 md:p-12">
<Mermaid chart={project.mermaidChart} />
</div>
</section>
)}
{/* Engineering Narrative */}
{/* Updated Engineering Narrative Header */}
<article className="w-full py-12 border-t border-neutral-900 mt-16">
<div className="flex items-center gap-3 mb-12">
<h3 className="text-[10px] font-mono text-purple-500 uppercase tracking-[0.3em] font-bold">
PROJECT_LOG // 02
</h3>
<div className="h-px flex-1 bg-neutral-900" />
<h2 className="text-xl font-bold tracking-tighter">The Engineering Story</h2>
</div>
<div className="prose prose-invert prose-neutral max-w-none text-left
prose-p:text-neutral-400 prose-p:leading-relaxed prose-p:text-[16px]
prose-h4:text-white prose-h4:text-sm prose-h4:mb-2 prose-h4:mt-8
prose-strong:text-white prose-strong:font-bold">
<ReactMarkdown>{project.engineeringStory}</ReactMarkdown>
</div>
</article>
</div>
</main>
);
}

View file

@ -115,19 +115,20 @@ export default function ProjectDetail({ params }: { params: Promise<{ category:
)}
{/* Engineering Narrative */}
<article className="w-full max-w-3xl mx-auto px-6 py-20 border-t border-neutral-900 mt-20">
<h2 className="text-2xl font-bold mb-10 italic underline decoration-blue-500 underline-offset-8 text-left text-white">
The Engineering Story
</h2>
<div className="prose prose-invert prose-neutral max-w-none text-left
prose-h4:text-blue-400 prose-h4:font-mono prose-h4:uppercase prose-h4:text-[10px] prose-h4:tracking-[0.2em]
prose-p:text-neutral-400 prose-p:leading-relaxed">
<ReactMarkdown>
{project.engineeringStory}
</ReactMarkdown>
{/* Updated Engineering Narrative Header */}
<section className="w-full pb-20 mt-12">
<div className="flex items-center gap-3 mb-12">
<h3 className="text-[10px] font-mono text-blue-500 uppercase tracking-[0.3em] font-bold">
PROJECT LOG // {project.storyLabel || "NARRATIVE"}
</h3>
<div className="h-px flex-1 bg-neutral-900" />
<h2 className="text-xl font-bold tracking-tighter">The Engineering Story</h2>
</div>
</article>
<div className="prose prose-invert prose-neutral max-w-none text-left">
<ReactMarkdown>{project.engineeringStory}</ReactMarkdown>
</div>
</section>
</div>
</main>
);

View file

@ -36,37 +36,43 @@ export const PROJECT_REGISTRY: Project[] = [
liveUrl: "https://ratoong.com",
isPrivate: false,
mermaidChart: `
graph TD
graph LR
subgraph Client_Side [Frontend]
A[Angular Web App]:::traffic
end
subgraph Firebase_GCP [Cloud Infrastructure]
direction TB
Hub((Firebase SDK)):::hub
B[Firebase Auth]:::node
C[Firestore DB]:::node
D[Cloud Functions]:::node
E[Partner API Proxy]:::node
end
%% Move APIs to a vertical stack to save horizontal width %%
subgraph External [Third Party]
direction TB
F[Weather API]:::traffic
G[Google Maps API]:::traffic
H[Affiliate Partners]:::traffic
end
A <-->|Real-time Sync| C
A -->|Auth Request| B
A -->|Triggers| D
D -->|Fetch & Normalize| F
D -->|Geocoding| G
D -->|Affiliate Logic| H
E -->|Read Data| C
A ==> Hub
Hub -->|Identity| B
Hub <-->|Data Sync| C
Hub -->|Triggers| D
D --> F
D --> G
D --> H
E -.->|Internal Access| C
%% Styles %%
classDef traffic fill:#2563eb,stroke:#3b82f6,color:#fff
classDef node fill:#16a34a,stroke:#4285F4,color:#fff
classDef node fill:#16a34a,stroke:#22c55e,color:#fff
classDef hub fill:#f59e0b,stroke:#d97706,color:#fff,stroke-width:2px
`,
storyLabel: "DATA // UI EFFICIENCY",
},
{
slug: "datasaur",
@ -82,6 +88,7 @@ export const PROJECT_REGISTRY: Project[] = [
repoUrl: "https://git.georgew.dev/georgew/datasaur",
liveUrl: "https://ratoong.com",
engineeringStory: "In this high-stakes medical environment, I implemented a custom audit logging system that ensured every state change was immutable...",
storyLabel: "DATA EFFICIENCY",
isPrivate: true,
},
{
@ -98,6 +105,7 @@ export const PROJECT_REGISTRY: Project[] = [
images: ["/ratoong-hero.jpg", "/ratoong-dashboard.jpg"],
liveUrl: "https://ratoong.com",
isPrivate: true,
storyLabel: "DATA EFFICIENCY",
},
{
slug: "flutter-1",
@ -113,6 +121,7 @@ export const PROJECT_REGISTRY: Project[] = [
repoUrl: "https://git.georgew.dev/georgew/datasaur",
liveUrl: "https://ratoong.com",
engineeringStory: "In this high-stakes medical environment, I implemented a custom audit logging system that ensured every state change was immutable...",
storyLabel: "DATA EFFICIENCY",
isPrivate: true,
},
{
@ -129,6 +138,7 @@ export const PROJECT_REGISTRY: Project[] = [
repoUrl: "https://git.georgew.dev/georgew/datasaur",
liveUrl: "https://ratoong.com",
engineeringStory: "In this high-stakes medical environment, I implemented a custom audit logging system that ensured every state change was immutable...",
storyLabel: "DATA EFFICIENCY",
isPrivate: true,
},
];

9384
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -9,6 +9,7 @@
"lint": "eslint"
},
"dependencies": {
"@tailwindcss/upgrade": "^4.1.18",
"framer-motion": "^12.29.2",
"lucide-react": "^0.563.0",
"mermaid": "^11.12.2",

View file

@ -9,6 +9,7 @@ export interface Project {
metrics: string[];
description: string;
engineeringStory: string;
storyLabel?: string;
images: string[];
liveUrl?: string;
repoUrl?: string;