feat: CI badges per project card, reorder Poimen after Homelab
Build & Push Portfolio Image / build-push (push) Successful in 3m31s

This commit is contained in:
Story Crater Bot
2026-09-03 08:18:58 -07:00
parent b3631211af
commit 7f0c12d029
4 changed files with 210 additions and 87 deletions
+64 -8
View File
@@ -1,9 +1,55 @@
'use client'
import { motion } from 'framer-motion'
import { ExternalLink, MessageSquare } from 'lucide-react'
import { ExternalLink, MessageSquare, CheckCircle, XCircle, Loader2, AlertCircle } from 'lucide-react'
import { useState, useEffect } from 'react'
import { useTerminal } from '@/lib/TerminalContext'
function RepoCIBadge({ label, repo, forgejoBase }: { label: string; repo: string; forgejoBase: string }) {
const [ci, setCI] = useState<{ sha: string; status: string } | null>(null)
useEffect(() => {
fetch(`/api/ci-status?repo=${repo}`)
.then(r => r.json())
.then(data => setCI({
sha: data.sha?.substring(0, 7) || '',
status: data.conclusion || data.status || 'unknown',
}))
.catch(() => null)
}, [repo])
const icon = !ci ? <Loader2 size={11} className="animate-spin text-gray-400" />
: ci.status === 'success' ? <CheckCircle size={11} className="text-green-500" />
: ci.status === 'failure' ? <XCircle size={11} className="text-red-500" />
: <AlertCircle size={11} className="text-yellow-500" />
return (
<div className="flex items-center gap-1.5 text-xs">
<span className="text-gray-500 dark:text-gray-400 font-medium w-20 truncate">{label}</span>
{ci?.sha ? (
<a
href={`${forgejoBase}/commit/${ci.sha}`}
target="_blank"
rel="noopener noreferrer"
className="font-mono text-blue-600 dark:text-blue-400 hover:underline"
>
{ci.sha}
</a>
) : (
<span className="font-mono text-gray-400">···</span>
)}
<span className="text-gray-300 dark:text-gray-600">|</span>
{icon}
</div>
)
}
interface CIRepo {
label: string
repo: string
forgejoBase: string
}
interface ProjectShowcaseProps {
id?: string
title: string
@@ -26,6 +72,7 @@ interface ProjectShowcaseProps {
label: string
url: string
}
ciRepos?: CIRepo[]
}
export function ProjectShowcase({
@@ -44,6 +91,7 @@ export function ProjectShowcase({
askPoimenText = 'Ask Poimen for technical details',
bullets,
deepDive,
ciRepos,
}: ProjectShowcaseProps) {
const { setIsOpen } = useTerminal()
@@ -139,13 +187,21 @@ export function ProjectShowcase({
</a>
)}
</div>
<span
className={`ml-4 px-3 py-1 rounded-full text-xs font-semibold whitespace-nowrap ${
statusColors[status]
}`}
>
{status.charAt(0).toUpperCase() + status.slice(1)}
</span>
{ciRepos ? (
<div className="ml-4 flex flex-col gap-1.5 shrink-0">
{ciRepos.map((r) => (
<RepoCIBadge key={r.repo} label={r.label} repo={r.repo} forgejoBase={r.forgejoBase} />
))}
</div>
) : (
<span
className={`ml-4 px-3 py-1 rounded-full text-xs font-semibold whitespace-nowrap ${
statusColors[status]
}`}
>
{status.charAt(0).toUpperCase() + status.slice(1)}
</span>
)}
</div>
{/* Stat */}