feat: initial project scaffold

- React 19 + Vite + TailwindCSS frontend
- Express + TypeScript backend API
- PostgreSQL schema and migrations
- Docker Compose orchestration
- Drone CI/CD pipeline
- Pages: Dashboard, Servers, Containers, Services, Logs, Metrics, Settings
This commit is contained in:
Ernie Butcher
2026-03-18 17:09:08 -04:00
commit 65471c2a70
54 changed files with 7304 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
import { useQuery } from '@tanstack/react-query'
import { api } from '@/lib/api'
import { Layers } from 'lucide-react'
export default function Services() {
const { data, isLoading } = useQuery({
queryKey: ['services'],
queryFn: () => api.get('/services').then(r => r.data),
})
const services = data?.services ?? []
return (
<div className="space-y-4">
<div className="rounded-xl border border-[hsl(217.2_32.6%_17.5%)] bg-[hsl(222.2_84%_6%)] overflow-hidden">
<table className="w-full">
<thead>
<tr className="border-b border-[hsl(217.2_32.6%_17.5%)]">
{['Service', 'Type', 'Port', 'Status'].map(h => (
<th key={h} className="text-left px-5 py-3 text-xs font-medium text-[hsl(215_20.2%_65.1%)] uppercase tracking-wider">{h}</th>
))}
</tr>
</thead>
<tbody>
{isLoading ? (
<tr><td colSpan={4} className="text-center py-10 text-sm text-[hsl(215_20.2%_65.1%)]">Loading...</td></tr>
) : services.length === 0 ? (
<tr>
<td colSpan={4} className="py-12 text-center">
<Layers className="w-10 h-10 mx-auto mb-3 text-[hsl(215_20.2%_40%)]" />
<p className="text-sm text-[hsl(215_20.2%_65.1%)]">No services configured yet.</p>
</td>
</tr>
) : services.map((s: any) => (
<tr key={s.id} className="border-t border-[hsl(217.2_32.6%_17.5%)] hover:bg-[hsl(217.2_32.6%_10%)]">
<td className="px-5 py-4 text-sm font-medium text-white">{s.name}</td>
<td className="px-5 py-4 text-sm text-[hsl(215_20.2%_65.1%)]">{s.type}</td>
<td className="px-5 py-4 text-sm text-[hsl(215_20.2%_65.1%)]">{s.port ?? '—'}</td>
<td className="px-5 py-4">
<span className={`text-xs px-2 py-1 rounded ${s.status === 'active' ? 'bg-green-500/10 text-green-400' : 'bg-red-500/10 text-red-400'}`}>
{s.status ?? 'unknown'}
</span>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)
}