diff --git a/src/app/globals.css b/src/app/globals.css index 6a25b1b..9fc4131 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -7,9 +7,9 @@ @theme inline { --color-background: var(--background); --color-foreground: var(--foreground); - --font-sans: var(--font-sans); + --font-sans: var(--font-geist-sans); --font-mono: var(--font-geist-mono); - --font-heading: var(--font-sans); + --font-heading: var(--font-geist-sans); --color-sidebar-ring: var(--sidebar-ring); --color-sidebar-border: var(--sidebar-border); --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); @@ -49,72 +49,72 @@ } :root { - --background: oklch(1 0 0); - --foreground: oklch(0.145 0 0); - --card: oklch(1 0 0); - --card-foreground: oklch(0.145 0 0); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.145 0 0); - --primary: oklch(0.205 0 0); - --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.97 0 0); - --secondary-foreground: oklch(0.205 0 0); - --muted: oklch(0.97 0 0); - --muted-foreground: oklch(0.556 0 0); - --accent: oklch(0.97 0 0); - --accent-foreground: oklch(0.205 0 0); - --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.922 0 0); - --input: oklch(0.922 0 0); - --ring: oklch(0.708 0 0); - --chart-1: oklch(0.87 0 0); - --chart-2: oklch(0.556 0 0); - --chart-3: oklch(0.439 0 0); - --chart-4: oklch(0.371 0 0); - --chart-5: oklch(0.269 0 0); - --radius: 0.625rem; - --sidebar: oklch(0.985 0 0); - --sidebar-foreground: oklch(0.145 0 0); - --sidebar-primary: oklch(0.205 0 0); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.97 0 0); - --sidebar-accent-foreground: oklch(0.205 0 0); - --sidebar-border: oklch(0.922 0 0); - --sidebar-ring: oklch(0.708 0 0); + --background: oklch(0.985 0.005 85); + --foreground: oklch(0.15 0.025 265); + --card: oklch(0.99 0.003 85); + --card-foreground: oklch(0.15 0.025 265); + --popover: oklch(0.99 0.003 85); + --popover-foreground: oklch(0.15 0.025 265); + --primary: oklch(0.45 0.13 265); + --primary-foreground: oklch(0.985 0.005 85); + --secondary: oklch(0.93 0.02 265); + --secondary-foreground: oklch(0.25 0.03 265); + --muted: oklch(0.95 0.01 265); + --muted-foreground: oklch(0.55 0.02 265); + --accent: oklch(0.7 0.15 75); + --accent-foreground: oklch(0.2 0.02 265); + --destructive: oklch(0.6 0.2 25); + --border: oklch(0.88 0.015 265); + --input: oklch(0.88 0.015 265); + --ring: oklch(0.45 0.13 265); + --chart-1: oklch(0.7 0.15 75); + --chart-2: oklch(0.45 0.13 265); + --chart-3: oklch(0.6 0.12 195); + --chart-4: oklch(0.5 0.18 330); + --chart-5: oklch(0.75 0.1 155); + --radius: 0.5rem; + --sidebar: oklch(0.98 0.005 85); + --sidebar-foreground: oklch(0.15 0.025 265); + --sidebar-primary: oklch(0.45 0.13 265); + --sidebar-primary-foreground: oklch(0.985 0.005 85); + --sidebar-accent: oklch(0.95 0.01 265); + --sidebar-accent-foreground: oklch(0.15 0.025 265); + --sidebar-border: oklch(0.88 0.015 265); + --sidebar-ring: oklch(0.45 0.13 265); } .dark { - --background: oklch(0.145 0 0); - --foreground: oklch(0.985 0 0); - --card: oklch(0.205 0 0); - --card-foreground: oklch(0.985 0 0); - --popover: oklch(0.205 0 0); - --popover-foreground: oklch(0.985 0 0); - --primary: oklch(0.922 0 0); - --primary-foreground: oklch(0.205 0 0); - --secondary: oklch(0.269 0 0); - --secondary-foreground: oklch(0.985 0 0); - --muted: oklch(0.269 0 0); - --muted-foreground: oklch(0.708 0 0); - --accent: oklch(0.269 0 0); - --accent-foreground: oklch(0.985 0 0); - --destructive: oklch(0.704 0.191 22.216); - --border: oklch(1 0 0 / 10%); - --input: oklch(1 0 0 / 15%); - --ring: oklch(0.556 0 0); - --chart-1: oklch(0.87 0 0); - --chart-2: oklch(0.556 0 0); - --chart-3: oklch(0.439 0 0); - --chart-4: oklch(0.371 0 0); - --chart-5: oklch(0.269 0 0); - --sidebar: oklch(0.205 0 0); - --sidebar-foreground: oklch(0.985 0 0); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.269 0 0); - --sidebar-accent-foreground: oklch(0.985 0 0); - --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.556 0 0); + --background: oklch(0.13 0.02 265); + --foreground: oklch(0.95 0.008 85); + --card: oklch(0.17 0.025 265); + --card-foreground: oklch(0.95 0.008 85); + --popover: oklch(0.17 0.025 265); + --popover-foreground: oklch(0.95 0.008 85); + --primary: oklch(0.7 0.15 75); + --primary-foreground: oklch(0.15 0.02 265); + --secondary: oklch(0.22 0.03 265); + --secondary-foreground: oklch(0.9 0.01 85); + --muted: oklch(0.2 0.02 265); + --muted-foreground: oklch(0.6 0.02 265); + --accent: oklch(0.6 0.12 195); + --accent-foreground: oklch(0.95 0.008 85); + --destructive: oklch(0.65 0.2 25); + --border: oklch(0.25 0.03 265); + --input: oklch(0.25 0.03 265); + --ring: oklch(0.7 0.15 75); + --chart-1: oklch(0.7 0.15 75); + --chart-2: oklch(0.65 0.12 265); + --chart-3: oklch(0.6 0.12 195); + --chart-4: oklch(0.65 0.15 330); + --chart-5: oklch(0.7 0.1 155); + --sidebar: oklch(0.15 0.02 265); + --sidebar-foreground: oklch(0.95 0.008 85); + --sidebar-primary: oklch(0.7 0.15 75); + --sidebar-primary-foreground: oklch(0.15 0.02 265); + --sidebar-accent: oklch(0.2 0.02 265); + --sidebar-accent-foreground: oklch(0.9 0.01 85); + --sidebar-border: oklch(0.25 0.03 265); + --sidebar-ring: oklch(0.7 0.15 75); } @layer base { @@ -128,3 +128,14 @@ @apply font-sans; } } + +/* Signature: glowing "server LED" focus ring */ +.dark *:focus-visible { + @apply ring-[3px]; + outline: none; + box-shadow: 0 0 8px oklch(0.7 0.15 75 / 0.3), 0 0 0 3px oklch(0.7 0.15 75 / 0.5); +} +html:not(.dark) *:focus-visible { + @apply ring-[3px]; + outline: none; +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index bfec96c..ae22ee1 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,5 +1,6 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; +import { ThemeProvider } from "next-themes"; import { DashboardHeader } from "@/components/dashboard/header"; import "./globals.css"; @@ -16,7 +17,7 @@ const geistMono = Geist_Mono({ export const metadata: Metadata = { title: "Asep Haryana Saputra", description: - "Backend & infrastructure engineer — portfolio, projects, and live monitoring dashboard by Asep Haryana Saputra.", + "Teknik Informatika student exploring backend, infrastructure & DevOps — portfolio and projects by Asep Haryana Saputra.", }; export default function RootLayout({ @@ -28,10 +29,13 @@ export default function RootLayout({
-+ ~ whoami +
++ # + Teknik Informatika student exploring backend systems, distributed + infrastructure, and DevOps — still learning, still building. +
- Backend & infrastructure engineer who builds distributed systems, - RESTful APIs, and production-grade observability stacks. -
-+ ~ about +
+- I build backend systems and infrastructure for personal projects, - research collaborations, and production deployments. My work spans - RESTful API design, container orchestration with Docker & - Traefik, distributed tracing with OpenTelemetry & Jaeger, and - event-driven architectures on Dapr & NATS. + I’m a Teknik Informatika student passionate about backend + engineering, distributed systems, and infrastructure. I spend my + time building RESTful APIs, learning container orchestration with + Docker & Traefik, and exploring observability with OpenTelemetry + & Jaeger.
-- I was the Back-End lead on the ZeaVis Edu capstone team ("AI - for Smart Education" by Pijak × IBM SkillsBuild), where - I designed the system architecture, API contracts, and deployment - pipeline for a computer vision-based corn leaf disease detection - platform. +
+ I was the Back-End lead on the ZeaVis Edu capstone team (“AI + for Smart Education” by Pijak × IBM SkillsBuild), + a 5-person team with task division across Back-End, Front-End, + and Machine Learning. I handled the system architecture, API + design, RESTful API development, and Docker deployment on VPS.
- - ++ ~ skills +
++ ~ featured + project +
+
+
Asisten Edukasi Interaktif untuk Deteksi Penyakit Daun Jagung
@@ -179,19 +186,19 @@ export default function Home() {+
A Computer Vision-powered educational platform that helps farmers, agricultural students, and field extension officers identify corn leaf diseases by uploading a photo. Built as a capstone project for the Pijak × IBM SkillsBuild program.
-+
What it does
-+
My role
-This site doubles as a live infrastructure monitor.
The dashboard tracks Docker services, Traefik request metrics, - Prometheus system resources, and Jaeger traces across the - deployment. + Prometheus system resources, and Jaeger traces — all running on + my VPS deployment.
Live Dashboard diff --git a/src/components/dashboard/header.tsx b/src/components/dashboard/header.tsx index b203581..7719f1d 100644 --- a/src/components/dashboard/header.tsx +++ b/src/components/dashboard/header.tsx @@ -1,13 +1,17 @@ "use client"; +import { Moon, Sun } from "lucide-react"; +import { useTheme } from "next-themes"; import { useEffect, useState } from "react"; import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; import type { DashboardData } from "@/lib/dashboard/types"; export function DashboardHeader() { const [degraded, setDegraded] = useState(0); const [total, setTotal] = useState(0); const [time, setTime] = useState(""); + const { theme, setTheme } = useTheme(); useEffect(() => { const fetchStatus = async () => { @@ -74,6 +78,15 @@ export function DashboardHeader() { ? `${degraded} degraded` : "All Systems Operational"} + {time}