feat: revamp portfolio projects, skills, and nav

Upgrade project case studies to accurate stack, group skills into 5 domains, streamline homepage hierarchy, and fix mobile navigation.
This commit is contained in:
ssbg04 committed 2026-10-22 01:47:35 +08:00
1 parent 5e5010c06c
commit 974863e237
7 files changed
+328 -122

No files matched your search

+21 -7
View File
@@ -13,6 +13,7 @@ interface HeroProps {
isAvailable?: boolean;
availabilityStatus?: string;
socialLinks?: SocialLink[];
resumeFile?: string;
}
const getPlatformIcon = (platform: string) => {
@@ -67,7 +68,8 @@ export default function Hero({
heroImageNight,
isAvailable = true,
availabilityStatus = 'Available for new projects',
socialLinks = []
socialLinks = [],
resumeFile = '/CV-Cris-Charles-Garcia.pdf'
}: HeroProps) {
const handleSmoothScroll = (e: React.MouseEvent<HTMLAnchorElement>, targetId: string) => {
e.preventDefault();
@@ -82,9 +84,8 @@ export default function Hero({
? socialLinks.filter(l => l.showInHero !== false)
: [
{ platform: "GitHub", url: "https://github.com/ssbg04", icon: "github", order: 1 },
{ platform: "Facebook", url: "https://www.facebook.com/kristyarls345/", icon: "facebook", order: 2 },
{ platform: "TikTok", url: "https://www.tiktok.com/@sisibigi", icon: "tiktok", order: 3 },
{ platform: "Email", url: "mailto:crischarlesgarcia345@gmail.com", icon: "mail", order: 4 }
{ platform: "LinkedIn", url: "https://www.linkedin.com/in/cris-charles-garcia/", icon: "linkedin", order: 2 },
{ platform: "Email", url: "mailto:crischarlesgarcia345@gmail.com", icon: "mail", order: 3 }
];
const dayImg = heroImage || (ppDay as any).src || ppDay;
@@ -148,23 +149,36 @@ export default function Hero({
</div>
{/* CTA Buttons */}
<div className="flex flex-row items-center gap-3 w-full sm:w-auto justify-center">
<div className="flex flex-wrap sm:flex-nowrap items-center gap-2.5 sm:gap-3 w-full sm:w-auto justify-center">
<a
href="#projects"
onClick={(e) => handleSmoothScroll(e, 'projects')}
aria-label="Jump to projects section"
className="w-1/2 sm:w-auto px-5 py-2.5 rounded-lg bg-foreground-custom text-background-custom font-semibold text-sm border border-foreground-custom hover:opacity-90 active:scale-[0.98] transition-all duration-150 text-center cursor-pointer flex items-center justify-center gap-2 shadow-xs group"
className="flex-1 sm:flex-initial px-4 sm:px-5 py-2.5 rounded-lg bg-foreground-custom text-background-custom font-semibold text-sm border border-foreground-custom hover:opacity-90 active:scale-[0.98] transition-all duration-150 text-center cursor-pointer flex items-center justify-center gap-2 shadow-xs group whitespace-nowrap"
>
Projects
<svg className="w-3.5 h-3.5 transform group-hover:translate-x-0.5 transition-transform duration-150" fill="none" stroke="currentColor" strokeWidth="2.5" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M13.5 4.5L21 12m0 0l-7.5 7.5M21 12H3" />
</svg>
</a>
<a
href={resumeFile}
target="_blank"
rel="noopener noreferrer"
download="CV-Cris-Charles-Garcia.pdf"
aria-label="Download CV"
className="flex-1 sm:flex-initial px-4 sm:px-5 py-2.5 rounded-lg bg-card-custom text-primary-custom font-semibold text-sm border border-primary-custom/30 hover:border-primary-custom hover:bg-primary-custom/5 active:scale-[0.98] transition-all duration-150 text-center cursor-pointer flex items-center justify-center gap-2 shadow-xs whitespace-nowrap"
>
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" strokeWidth="2.5" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3" />
</svg>
Download CV
</a>
<a
href="#contact"
onClick={(e) => handleSmoothScroll(e, 'contact')}
aria-label="Jump to contact section"
className="w-1/2 sm:w-auto px-5 py-2.5 rounded-lg bg-card-custom text-foreground-custom font-semibold text-sm border border-border-custom hover:bg-muted-custom/60 active:scale-[0.98] transition-all duration-150 text-center cursor-pointer flex items-center justify-center gap-2 shadow-xs"
className="w-full sm:w-auto px-4 sm:px-5 py-2.5 rounded-lg bg-card-custom text-foreground-custom font-semibold text-sm border border-border-custom hover:bg-muted-custom/60 active:scale-[0.98] transition-all duration-150 text-center cursor-pointer flex items-center justify-center gap-2 shadow-xs whitespace-nowrap"
>
Contact
<svg className="w-3.5 h-3.5 opacity-70" fill="none" stroke="currentColor" strokeWidth="2.5" viewBox="0 0 24 24">
+18 -7
View File
@@ -377,13 +377,24 @@ export default function Navbar({ fullName = 'Cris Charles', logoImage = '/logo.p
<ControlsDropdown />
</div>
<a
href="/contact"
onClick={() => { setActivePath('/contact'); setDrawerOpen(false); }}
className="tap-target relative flex items-center justify-center w-full px-4 py-3.5 rounded-lg text-sm font-semibold bg-foreground-custom text-background-custom border border-foreground-custom hover:opacity-85 active:opacity-70 transition-all focus-visible:ring-2 focus-visible:ring-primary-custom focus-visible:outline-none"
>
Get in Touch
</a>
<div className="grid grid-cols-2 gap-2">
<a
href="/CV-Cris-Charles-Garcia.pdf"
target="_blank"
rel="noopener noreferrer"
className="tap-target relative flex items-center justify-center gap-1.5 px-3 py-3 rounded-lg text-sm font-semibold bg-elevated-custom text-foreground-custom border border-border-custom hover:border-primary-custom/40 active:opacity-70 transition-all"
>
<span>Resume / CV</span>
<span className="text-xs font-mono">↗</span>
</a>
<a
href="/contact"
onClick={() => { setActivePath('/contact'); setDrawerOpen(false); }}
className="tap-target relative flex items-center justify-center px-3 py-3 rounded-lg text-sm font-semibold bg-foreground-custom text-background-custom border border-foreground-custom hover:opacity-85 active:opacity-70 transition-all focus-visible:ring-2 focus-visible:ring-primary-custom focus-visible:outline-none text-center"
>
Get in Touch
</a>
</div>
</div>
</div>
)}
+2
View File
@@ -60,6 +60,8 @@ export default function Skills({
{filterOptions.map((opt) => (
<button
key={opt}
type="button"
aria-pressed={activeFilter === opt}
onClick={() => setActiveFilter(opt)}
className={`px-3 py-1 rounded-md text-xs font-mono tracking-wide border transition-all cursor-pointer ${
activeFilter === opt
+1 -44
View File
@@ -9,50 +9,7 @@ interface TestimonialsProps {
export default function Testimonials({ testimonials }: TestimonialsProps) {
if (!testimonials || testimonials.length === 0) {
return (
<section id="testimonials" className="py-20 relative overflow-hidden">
<SectionBackground variant="testimonials" />
<div className="max-w-6xl mx-auto px-4 sm:px-6 relative z-10">
{/* Section Header */}
<ScrollReveal variant="fade-up" className="mb-10">
<div className="flex flex-col sm:flex-row sm:items-end justify-between gap-4 pb-4 border-b border-border-custom">
<div>
<h2 className="text-3xl sm:text-4xl font-bold font-heading text-foreground-custom tracking-tight">
Client &amp; Peer Testimonials
</h2>
</div>
</div>
</ScrollReveal>
{/* Empty Note Card */}
<ScrollReveal variant="fade-up" delay={100}>
<div className="bento-card p-8 sm:p-12 text-center border border-border-custom max-w-xl mx-auto flex flex-col items-center justify-center">
<div className="w-12 h-12 rounded-2xl bg-primary-custom/10 border border-primary-custom/20 text-primary-custom flex items-center justify-center mb-4 shadow-sm">
<svg className="w-6 h-6" fill="none" stroke="currentColor" strokeWidth="1.5" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M7.5 8.25h9m-9 3H12m-9.75 1.51c0 1.6 1.123 2.994 2.707 3.227 1.129.166 2.27.293 3.423.379.35.026.67.21.865.501L12 21l2.755-4.133a1.14 1.14 0 01.865-.501 48.172 48.172 0 003.423-.379c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0012 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018z" />
</svg>
</div>
<h3 className="text-lg font-bold font-heading text-foreground-custom mb-2">
No Testimonials Yet
</h3>
<p className="text-sm text-muted-foreground-custom leading-relaxed mb-6">
Client reviews, supervisor feedback, and project endorsements will be showcased here once published via Sanity Studio.
</p>
<a
href="#contact"
className="inline-flex items-center gap-2 px-4 py-2.5 rounded-xl bg-primary-custom/10 hover:bg-primary-custom/20 border border-primary-custom/30 text-primary-custom text-xs font-mono font-medium transition-all"
>
Work with me &amp; share feedback →
</a>
</div>
</ScrollReveal>
</div>
</section>
);
return null;
}
const isCarousel = testimonials.length > 3;
+207 -46
View File
@@ -146,13 +146,13 @@ export interface SiteSettings {
export const mockSiteSettings: SiteSettings = {
fullName: "Cris Charles Garcia",
title: "Software Developer",
valueProposition: "I am an IT student majoring in Software Development based in Laguna, Philippines. I focus on building reliable cross platforms applications with AI assisted.",
title: "Software Developer | Web, Mobile & Information Systems",
valueProposition: "I build practical software applications, from user interfaces and REST APIs to database-driven information systems. My experience includes Flutter, Node.js, SQL databases, document processing, and application deployment.",
isAvailable: true,
availabilityStatus: "Available for new projects",
biography: [
"I am a senior IT student majoring in Software Development. I write code to solve real-world problems.",
"My primary stack includes PHP, React, Node.js, and MySQL. I enjoy database design, front-end development, and bug fixing."
"I am a senior IT student majoring in Software Development at Dalubhasaan ng Lungsod ng San Pablo. I write code to solve real-world problems in institutional records and business workflows.",
"My primary focus includes Flutter multi-platform development, Node.js REST APIs, database modeling (SQLite/MySQL), and automated document processing with OCR."
],
email: "crischarlesgarcia345@gmail.com",
location: "Laguna, Philippines",
@@ -167,37 +167,149 @@ export const mockSiteSettings: SiteSettings = {
export const mockSocialLinks: SocialLink[] = [
{ platform: "GitHub", url: "https://github.com/ssbg04", icon: "github", order: 1, showInHero: true, showInFooter: true, showInLinksPage: true },
{ platform: "Facebook", url: "https://www.facebook.com/kristyarls345/", icon: "facebook", order: 2, showInHero: true, showInFooter: true, showInLinksPage: true },
{ platform: "TikTok", url: "https://www.tiktok.com/@sisibigi", icon: "tiktok", order: 3, showInHero: true, showInFooter: false, showInLinksPage: true },
{ platform: "Email", url: "mailto:crischarlesgarcia345@gmail.com", icon: "mail", order: 4, showInHero: true, showInFooter: true, showInLinksPage: true }
{ platform: "LinkedIn", url: "https://www.linkedin.com/in/cris-charles-garcia/", icon: "linkedin", order: 2, showInHero: true, showInFooter: true, showInLinksPage: true },
{ platform: "Email", url: "mailto:crischarlesgarcia345@gmail.com", icon: "mail", order: 3, showInHero: true, showInFooter: true, showInLinksPage: true },
{ platform: "Facebook", url: "https://www.facebook.com/kristyarls345/", icon: "facebook", order: 4, showInHero: false, showInFooter: true, showInLinksPage: true },
{ platform: "TikTok", url: "https://www.tiktok.com/@sisibigi", icon: "tiktok", order: 5, showInHero: false, showInFooter: false, showInLinksPage: true }
];
export const mockSkills: Skill[] = [
{ name: "HTML5/CSS3/JS", category: "Frontend", icon: "fa-brands fa-html5", order: 1 },
{ name: "React", category: "Frontend", icon: "fa-brands fa-react", order: 2 },
{ name: "PHP", category: "Backend", icon: "fa-brands fa-php", order: 1 },
{ name: "Node.js", category: "Backend", icon: "fa-brands fa-node-js", order: 2 },
{ name: "Flutter", category: "Mobile", icon: "fa-solid fa-mobile-screen-button", order: 1 },
{ name: "MySQL", category: "Database", icon: "fa-solid fa-database", order: 1 },
{ name: "Git & GitHub", category: "Tools", icon: "fa-brands fa-github", order: 1 },
{ name: "Cybersecurity Basics", category: "Cybersecurity", icon: "fa-solid fa-shield-halved", order: 1 }
// Languages
{ name: "Dart", category: "Languages", icon: "fa-solid fa-code", order: 1 },
{ name: "TypeScript", category: "Languages", icon: "fa-brands fa-js", order: 2 },
{ name: "JavaScript", category: "Languages", icon: "fa-brands fa-js", order: 3 },
{ name: "PHP", category: "Languages", icon: "fa-brands fa-php", order: 4 },
{ name: "SQL", category: "Languages", icon: "fa-solid fa-database", order: 5 },
{ name: "Python", category: "Languages", icon: "fa-brands fa-python", order: 6 },
// Frontend & Mobile
{ name: "Flutter", category: "Frontend & Mobile", icon: "fa-solid fa-mobile-screen-button", order: 1 },
{ name: "React", category: "Frontend & Mobile", icon: "fa-brands fa-react", order: 2 },
{ name: "Vite", category: "Frontend & Mobile", icon: "fa-solid fa-bolt", order: 3 },
{ name: "Tailwind CSS", category: "Frontend & Mobile", icon: "fa-brands fa-css3-alt", order: 4 },
{ name: "HTML5/CSS3", category: "Frontend & Mobile", icon: "fa-brands fa-html5", order: 5 },
// Backend & APIs
{ name: "Node.js", category: "Backend & APIs", icon: "fa-brands fa-node-js", order: 1 },
{ name: "Express", category: "Backend & APIs", icon: "fa-solid fa-server", order: 2 },
{ name: "RESTful APIs", category: "Backend & APIs", icon: "fa-solid fa-network-wired", order: 3 },
{ name: "Socket.IO", category: "Backend & APIs", icon: "fa-solid fa-plug", order: 4 },
{ name: "JWT Authentication", category: "Backend & APIs", icon: "fa-solid fa-key", order: 5 },
// Databases
{ name: "SQLite", category: "Databases", icon: "fa-solid fa-database", order: 1 },
{ name: "MySQL", category: "Databases", icon: "fa-solid fa-database", order: 2 },
{ name: "Database Modeling", category: "Databases", icon: "fa-solid fa-table", order: 3 },
// Systems & Tools
{ name: "Git & GitHub", category: "Systems & Tools", icon: "fa-brands fa-github", order: 1 },
{ name: "Tesseract OCR", category: "Systems & Tools", icon: "fa-solid fa-file-lines", order: 2 },
{ name: "Inno Setup", category: "Systems & Tools", icon: "fa-brands fa-windows", order: 3 },
{ name: "Cybersecurity Basics", category: "Systems & Tools", icon: "fa-solid fa-shield-halved", order: 4 }
];
export const mockCertifications: Certification[] = [];
export const mockCertifications: Certification[] = [
{
id: "cert-tesda-nc2",
code: "01",
type: "certificate",
title: "Computer Systems Servicing NC II",
issuer: "TESDA",
category: "Hardware & Systems",
badgeImage: "/images/cert_nc2.jpg",
badgeUrl: "https://www.tesda.gov.ph",
description: "National qualification validating competency in computer systems installation, network and server configuration, hardware diagnostics, and preventive maintenance.",
skills: ["System Installation", "Network Setup", "Server Administration", "Hardware Diagnostics"],
featured: true,
order: 1,
issueDate: "2024-05-18"
},
{
id: "cert-cisco-itn",
code: "02",
type: "certificate",
title: "CCNA: Introduction to Networks (ITN)",
issuer: "Cisco Networking Academy",
category: "Networking",
badgeImage: "/images/cert_cisco_itn.jpg",
badgeUrl: "https://www.netacad.com",
description: "Network architecture, IPv4/IPv6 addressing models, Ethernet switching, router configuration, and IP subnetting fundamentals.",
skills: ["IPv4 / IPv6 Subnetting", "Cisco IOS", "Switching & Routing", "Network Security"],
featured: true,
order: 2,
issueDate: "2023-11-14"
},
{
id: "cert-hardware-basics",
code: "03",
type: "badge",
title: "Computer Hardware Basics",
issuer: "Cisco",
category: "Hardware & Systems",
badgeImage: "https://images.credly.com/images/19e742ef-13be-4d26-87ed-ac8f5fd0643c/linkedin_thumb_image.png",
badgeUrl: "https://www.credly.com/badges/d2f0c176-2e45-4b99-88e2-fa9fb9ff34c6/public_url",
description: "Hardware diagnostics, component installation, preventative maintenance, and troubleshooting tools.",
skills: ["PC Architecture", "Component Diagnostics", "Hardware Assembly", "System Repair"],
featured: true,
order: 3
},
{
id: "cert-endpoint-security",
code: "04",
type: "badge",
title: "Endpoint Security",
issuer: "Cisco",
category: "Cybersecurity",
badgeImage: "https://images.credly.com/images/0ca5f542-fb5e-4a22-9b7a-c1a1ce4c3db7/linkedin_thumb_EndpointSecurity.png",
badgeUrl: "https://www.credly.com/badges/2aae16d4-b16c-474f-a84e-f06c330193cd/public_url",
description: "Host and OS hardening, endpoint telemetry, malware detection, and defense-in-depth security principles.",
skills: ["Endpoint Hardening", "Threat Telemetry", "OS Security", "Mitigation Protocols"],
featured: true,
order: 4
},
{
id: "cert-intro-cybersecurity",
code: "05",
type: "badge",
title: "Introduction to Cybersecurity",
issuer: "Cisco",
category: "Cybersecurity",
badgeImage: "https://images.credly.com/images/af8c6b4e-fc31-47c4-8dcb-eb7a2065dc5b/linkedin_thumb_I2CS__1_.png",
badgeUrl: "https://www.credly.com/badges/adc49a5e-87bb-4917-991d-a28160942c13/public_url",
description: "Global threat landscape, vulnerability assessment methodologies, incident mitigation, and defensive cybersecurity operations.",
skills: ["Threat Modeling", "Vulnerability Assessment", "Incident Response", "Network Defense"],
featured: true,
order: 5
},
{
id: "cert-digital-awareness",
code: "06",
type: "badge",
title: "Digital Awareness",
issuer: "Cisco × OpenEDG",
category: "Digital Literacy",
badgeImage: "https://images.credly.com/images/29e7c859-4719-4081-a12f-6bdc073a43d2/linkedin_thumb_image.png",
badgeUrl: "https://www.credly.com/badges/20ec510f-8325-483c-b16e-2ca00ae660ae/public_url",
description: "Digital tools ecosystem, online safety protocols, data management, and ethical technology practices in modern computing.",
skills: ["Digital Privacy", "Cyber Hygiene", "Content Ethics", "Security Best Practices"],
featured: true,
order: 6
}
];
export const mockProjects: Project[] = [
{
title: "TIS Record Management System",
slug: "tis-rms",
category: "System / Record Management",
status: "LIVE PRODUCTION",
summary: "Comprehensive institutional record and student management platform engineered for administrative operational efficiency.",
description: "Designed and built to streamline academic data, attendance records, and student profiles with secure role-based access and intuitive data dashboards.",
category: "Desktop & Mobile System · OCR & Document Processing",
status: "OPERATIONAL (LAN)",
summary: "Institutional client-server records and transcript management platform with automated OCR data extraction and DepEd SF10 generation.",
description: "Multi-platform client-server records system designed for local network deployment. Built with a Flutter client (Windows Desktop & Android) communicating with a Node.js REST API and local SQLite database, featuring bundled Tesseract OCR for physical document ingestion, DepEd School Form 10 (SF10) report compilation, and custom Windows installer via Inno Setup.",
coverImage: "https://images.unsplash.com/photo-1551288049-bebda4e38f71?auto=format&fit=crop&w=1200&q=80",
technologies: ["PHP", "MySQL", "JavaScript", "Bootstrap"],
highlights: ["Role-Based Access Control", "Automated Student Profiling", "Institutional Audit Logging"],
repositoryUrl: "https://github.com/ssbg04",
liveUrl: "https://github.com/ssbg04",
technologies: ["Flutter", "Dart", "Node.js", "Express", "SQLite", "Tesseract OCR", "Inno Setup"],
highlights: ["Offline-First LAN Architecture", "Tesseract OCR & Ghostscript Pipeline", "DepEd SF10 Automated Generator", "Inno Setup Windows Packaging"],
repositoryUrl: "https://github.com/ssbg04/TIS_RMS",
liveUrl: "https://web.tis-rms.cc.cd",
featured: true,
order: 1,
publishedAt: "2026-04-26"
@@ -205,19 +317,67 @@ export const mockProjects: Project[] = [
{
title: "Isalin | Multi-Converter",
slug: "isalin",
category: "Web Application / Tool",
category: "Web Application · Client-Side Utility",
status: "LIVE PRODUCTION",
summary: "High-performance multi-converter utility web application for format, language, and file transformation.",
description: "Built to deliver instant, client-optimized conversion workflows with responsive design, keyboard shortcuts, and zero-latency transformations.",
summary: "High-performance multi-converter utility web application for instant, privacy-conscious file, text, and format transformations.",
description: "Built to deliver instant, client-optimized conversion workflows with zero-latency transformations, responsive glass UI, keyboard shortcuts, and strict TypeScript safety. Operates entirely in-browser with zero server-side data leaks.",
coverImage: "https://images.unsplash.com/photo-1517694712202-14dd9538aa97?auto=format&fit=crop&w=800&q=80",
technologies: ["React", "TypeScript", "Tailwind CSS"],
highlights: ["Instant Client Transformations", "Offline Support", "Responsive Bento UI"],
repositoryUrl: "https://github.com/ssbg04",
liveUrl: "https://github.com/ssbg04",
technologies: ["React", "TypeScript", "Vite", "Tailwind CSS"],
highlights: ["Zero-Latency In-Browser Processing", "Offline Support", "Responsive Bento UI", "Oxlint & Strict Typing"],
repositoryUrl: "https://github.com/ssbg04/isal.in",
liveUrl: "https://isal-in.vercel.app",
featured: true,
order: 2,
publishedAt: "2026-10"
},
{
title: "TindaGrid | Offline-First Retail Micro-ERP",
slug: "tindagrid",
category: "Mobile & Web PWA · Offline Micro-ERP",
status: "IN DEVELOPMENT",
summary: "Offline-first micro-ERP, retail POS, and crowdsourced telemetry platform engineered for Philippine neighborhood stores (sari-sari stores).",
description: "Engineered to prevent transaction data loss in areas with volatile connectivity. Features a Flutter PWA with persistent storage (navigator.storage.persist()), service-worker offline caching, Soft UI Taglish design system, and a NestJS monorepo microservice backend.",
coverImage: "https://images.unsplash.com/photo-1556742049-0a67c5574f73?auto=format&fit=crop&w=800&q=80",
technologies: ["Flutter", "Dart", "NestJS", "TypeScript", "PWA", "Riverpod"],
highlights: ["Persistent Storage (PWA)", "Bilingual Taglish Soft UI", "NestJS Monorepo Architecture", "Offline-First Caching"],
repositoryUrl: "https://github.com/ssbg04/tindagrid",
liveUrl: "https://github.com/ssbg04/tindagrid",
featured: true,
order: 3,
publishedAt: "2026-10"
},
{
title: "Glimpse | Real-Time WebSockets Platform",
slug: "glimpse",
category: "Web Application · Real-Time Networking",
status: "COMPLETED",
summary: "Anonymous real-time peer matching and communication platform utilizing Socket.IO rooms and event-driven architecture.",
description: "Event-driven real-time chat application demonstrating duplex WebSockets networking. Implemented FIFO user matchmaking queues for text and video channels, dynamic room lifecycle management, and connection telemetry.",
coverImage: "https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?auto=format&fit=crop&w=800&q=80",
technologies: ["Node.js", "Express", "Socket.IO", "WebSockets", "JavaScript"],
highlights: ["Duplex WebSocket Protocol", "Automated Queue Matchmaking", "Room Lifecycle Routing"],
repositoryUrl: "https://github.com/ssbg04/glimpse",
liveUrl: "https://github.com/ssbg04/glimpse",
featured: false,
order: 4,
publishedAt: "2025-11"
},
{
title: "Susi | Mobile Credential Vault",
slug: "susi",
category: "Mobile Application · Security",
status: "IN DEVELOPMENT",
summary: "Offline-first mobile password and credential vault built with React Native, Expo Router, and glassmorphic aesthetics.",
description: "Local encrypted credential management mobile application. Features biometric authentication hooks, hardware-backed secure storage, Expo Router file-based navigation, and native fluid visual feedback.",
coverImage: "https://images.unsplash.com/photo-1563986768609-322da13575f3?auto=format&fit=crop&w=800&q=80",
technologies: ["React Native", "Expo", "TypeScript", "Expo Router", "Tailwind CSS"],
highlights: ["Biometric Authentication", "Encrypted Local Storage", "Native Glass UI"],
repositoryUrl: "https://github.com/ssbg04/susi",
liveUrl: "https://github.com/ssbg04/susi",
featured: false,
order: 5,
publishedAt: "2026-09"
},
{
title: "ICIS Supply/Inventory Management System",
slug: "icis-inventory",
@@ -226,12 +386,12 @@ export const mockProjects: Project[] = [
summary: "Institutional supply and asset tracking system streamlining stock level audits, equipment logistics, and reporting.",
description: "Engineered to eliminate stock discrepancies, generate automated replenishment alerts, and maintain detailed ledger audits across departments.",
coverImage: "https://images.unsplash.com/photo-1586528116311-ad8dd3c8310d?auto=format&fit=crop&w=800&q=80",
technologies: ["PHP", "MySQL", "JavaScript", "Tailwind CSS"],
technologies: ["PHP", "MySQL", "JavaScript", "Tailwind CSS", "Docker"],
highlights: ["Stock Level Telemetry", "Automated Reorder Alerts", "Multi-Warehouse Tracking"],
repositoryUrl: "https://github.com/ssbg04",
liveUrl: "https://github.com/ssbg04",
featured: true,
order: 3,
repositoryUrl: "https://github.com/ssbg04/icis-inventory",
liveUrl: "https://github.com/ssbg04/icis-inventory",
featured: false,
order: 6,
publishedAt: "2026-05"
}
];
@@ -239,16 +399,17 @@ export const mockProjects: Project[] = [
export const mockExperience: Experience[] = [
{
company: "Talisay Integrated School",
role: "Programmer",
role: "Systems Developer / IT Programmer Intern",
employmentType: "Internship",
location: "Laguna, Philippines",
startDate: "2025",
endDate: "Present",
description: [
"Engineered automated school record management solutions and optimized database queries.",
"Collaborated with administration staff to digitize physical records and improve data retrieval speed."
"Architected and deployed a multi-platform records management solution (TIS RMS) for campus administrative staff, digitizing physical student files.",
"Engineered an automated document pipeline bundling Tesseract OCR & Ghostscript to extract Learner Reference Numbers (LRN) and academic marks from scanned documents.",
"Designed an offline-first client-server LAN topology using Node.js as a persistent Windows Service with a local SQLite database to guarantee zero disruption during internet outages."
],
technologies: ["PHP", "MySQL", "JavaScript", "HTML/CSS"]
technologies: ["Flutter", "Dart", "Node.js", "SQLite", "Tesseract OCR", "REST APIs"]
}
];
@@ -423,7 +584,7 @@ export async function getSkills(): Promise<Skill[]> {
export async function getCertifications(featuredOnly = false): Promise<Certification[]> {
try {
if (!sanityClient) return mockCertifications;
if (!sanityClient) return featuredOnly ? mockCertifications.filter(c => c.featured) : mockCertifications;
const query = featuredOnly
? `*[_type == "certification" && featured == true] | order(coalesce(order, 99) asc)`
@@ -431,7 +592,7 @@ export async function getCertifications(featuredOnly = false): Promise<Certifica
const certs = await sanityClient.fetch(query);
if (!certs || certs.length === 0) return mockCertifications;
if (!certs || certs.length === 0) return featuredOnly ? mockCertifications.filter(c => c.featured) : mockCertifications;
return certs.map((c: any, idx: number) => ({
id: c._id || `cert-${idx}`,
@@ -454,13 +615,13 @@ export async function getCertifications(featuredOnly = false): Promise<Certifica
}));
} catch (error) {
console.error('Error fetching certification from Sanity:', error);
return [];
return featuredOnly ? mockCertifications.filter(c => c.featured) : mockCertifications;
}
}
export async function getProjects(featuredOnly = false): Promise<Project[]> {
try {
if (!sanityClient) return mockProjects;
if (!sanityClient) return featuredOnly ? mockProjects.filter(p => p.featured) : mockProjects;
const query = featuredOnly
? `*[_type == "project" && featured == true] | order(coalesce(order, 99) asc)`
@@ -468,7 +629,7 @@ export async function getProjects(featuredOnly = false): Promise<Project[]> {
const projects = await sanityClient.fetch(query);
if (!projects || projects.length === 0) return mockProjects;
if (!projects || projects.length === 0) return featuredOnly ? mockProjects.filter(p => p.featured) : mockProjects;
return projects.map((p: any, idx: number) => ({
title: p.title || 'Untitled Project',
@@ -495,7 +656,7 @@ export async function getProjects(featuredOnly = false): Promise<Project[]> {
}));
} catch (error) {
console.error('Error fetching projects from Sanity:', error);
return mockProjects;
return featuredOnly ? mockProjects.filter(p => p.featured) : mockProjects;
}
}
+14 -5
View File
@@ -52,18 +52,27 @@ const socialLinks = await getSocialLinks();
isAvailable={settings.isAvailable}
availabilityStatus={settings.availabilityStatus}
socialLinks={socialLinks}
resumeFile={settings.resumeFile}
/>
<About settings={settings} experience={experience} education={education} isCompact={true} client:visible />
<Skills skills={skills} limit={12} showSeeAll={true} seeAllHref="/about#skills" client:visible />
<!-- 02. Flagship & Selected Projects -->
<Projects initialProjects={featuredProjects} limit={3} client:visible />
<!-- 03. Evidence-Backed Technical Skills -->
<Skills skills={skills} client:visible />
<!-- 04. Experience & Education -->
<About settings={settings} experience={experience} education={education} isCompact={false} client:visible />
<!-- 05. Verified Certifications -->
<Certifications certifications={certifications} client:visible />
<Testimonials testimonials={testimonials} client:visible />
<!-- 06. Client & Peer Testimonials (only when published) -->
{testimonials && testimonials.length > 0 && (
<Testimonials testimonials={testimonials} client:visible />
)}
<!-- 07. Contact & Outreach -->
<ContactForm
socialLinks={socialLinks}
contactHeading={settings.contactHeading}
+65 -13
View File
@@ -161,6 +161,7 @@ const nextProject =
{/* Section: System Architecture Diagram (Flagship Case Study for TIS Record Management System) */}
{project.slug === 'tis-rms' && (
<>
<section class="space-y-4">
<div class="flex items-center justify-between">
<h2 class="text-xl sm:text-2xl font-bold font-heading text-foreground-custom flex items-center gap-2">
@@ -186,12 +187,12 @@ const nextProject =
<rect x="12" y="14" width="28" height="28" rx="6" class="fill-primary-custom/15 text-primary-custom" />
<text x="26" y="32" text-anchor="middle" font-family="monospace" font-size="12" font-weight="bold" fill="currentColor">01</text>
<text x="14" y="66" font-family="sans-serif" font-size="13" font-weight="bold" fill="currentColor">Client Apps</text>
<text x="14" y="84" font-family="monospace" font-size="10" fill="currentColor" opacity="0.6">Flutter Mobile</text>
<text x="14" y="98" font-family="monospace" font-size="10" fill="currentColor" opacity="0.6">C# WPF Desktop</text>
<text x="14" y="112" font-family="monospace" font-size="10" fill="currentColor" opacity="0.6">Role-Based UI</text>
<text x="14" y="84" font-family="monospace" font-size="10" fill="currentColor" opacity="0.6">Flutter Windows</text>
<text x="14" y="98" font-family="monospace" font-size="10" fill="currentColor" opacity="0.6">Flutter Android</text>
<text x="14" y="112" font-family="monospace" font-size="10" fill="currentColor" opacity="0.6">Riverpod &amp; Dio</text>
</g>
{/* Node 2: Local Cache & Offline Sync */}
{/* Node 2: Local Cache & Offline Engine */}
<g transform="translate(250, 50)">
<rect width="140" height="120" rx="14" class="fill-card-custom stroke-border-custom hover:stroke-primary-custom transition-all" stroke-width="1.5" />
<rect x="12" y="14" width="28" height="28" rx="6" class="fill-emerald-500/15 text-emerald-500" />
@@ -209,28 +210,79 @@ const nextProject =
<text x="26" y="32" text-anchor="middle" font-family="monospace" font-size="12" font-weight="bold" fill="currentColor">03</text>
<text x="14" y="66" font-family="sans-serif" font-size="13" font-weight="bold" fill="currentColor">Backend &amp; OCR</text>
<text x="14" y="84" font-family="monospace" font-size="10" fill="currentColor" opacity="0.6">Node.js Express</text>
<text x="14" y="98" font-family="monospace" font-size="10" fill="currentColor" opacity="0.6">Tesseract OCR</text>
<text x="14" y="112" font-family="monospace" font-size="10" fill="currentColor" opacity="0.6">Ghostscript PDF</text>
<text x="14" y="98" font-family="monospace" font-size="10" fill="currentColor" opacity="0.6">Windows Service</text>
<text x="14" y="112" font-family="monospace" font-size="10" fill="currentColor" opacity="0.6">Tesseract OCR</text>
</g>
{/* Node 4: Secure Edge & Central Database */}
{/* Node 4: Form Engine & LAN Architecture */}
<g transform="translate(710, 50)">
<rect width="120" height="120" rx="14" class="fill-card-custom stroke-border-custom hover:stroke-primary-custom transition-all" stroke-width="1.5" />
<rect x="12" y="14" width="28" height="28" rx="6" class="fill-amber-500/15 text-amber-500" />
<text x="26" y="32" text-anchor="middle" font-family="monospace" font-size="12" font-weight="bold" fill="currentColor">04</text>
<text x="14" y="66" font-family="sans-serif" font-size="13" font-weight="bold" fill="currentColor">Edge &amp; Sync</text>
<text x="14" y="84" font-family="monospace" font-size="10" fill="currentColor" opacity="0.6">Cloudflare Tunnel</text>
<text x="14" y="98" font-family="monospace" font-size="10" fill="currentColor" opacity="0.6">Encrypted Tunnels</text>
<text x="14" y="112" font-family="monospace" font-size="10" fill="currentColor" opacity="0.6">MySQL Cluster</text>
<text x="14" y="66" font-family="sans-serif" font-size="13" font-weight="bold" fill="currentColor">Form Engine</text>
<text x="14" y="84" font-family="monospace" font-size="10" fill="currentColor" opacity="0.6">DepEd SF10</text>
<text x="14" y="98" font-family="monospace" font-size="10" fill="currentColor" opacity="0.6">Ghostscript PDF</text>
<text x="14" y="112" font-family="monospace" font-size="10" fill="currentColor" opacity="0.6">Air-Gapped LAN</text>
</g>
</svg>
</div>
<p class="text-xs font-mono text-muted-foreground-custom mt-3 pt-3 border-t border-border-custom/50 flex items-center justify-between">
<span>Pipeline: Client Capture → Local Offline SQLite → Automated OCR Indexing → Encrypted Cloudflare Sync</span>
<span class="text-primary-custom font-semibold">100% AIR-GAPPED READY</span>
<span>Pipeline: Flutter Multi-Platform Client → Node.js Windows Service → Tesseract OCR Engine → Local SQLite Database</span>
<span class="text-primary-custom font-semibold">100% AIR-GAPPED &amp; LAN READY</span>
</p>
</div>
</section>
{/* Engineering Deep-Dive (Flagship Details) */}
<section class="space-y-4">
<h2 class="text-xl sm:text-2xl font-bold font-heading text-foreground-custom flex items-center gap-2">
<span class="w-2 h-2 rounded-full bg-primary-custom" />
Engineering Challenges &amp; Technical Solutions
</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="p-5 rounded-2xl border border-border-custom bg-card-custom/60 space-y-2">
<h3 class="text-sm font-bold font-heading text-foreground-custom flex items-center gap-2">
<span class="text-primary-custom font-mono">01.</span>
Offline LAN Reliability
</h3>
<p class="text-xs text-muted-foreground-custom leading-relaxed">
Campus internet fluctuations caused web apps to fail. Architected a local client-server network with a persistent Node.js Windows background service and local SQLite database, guaranteeing zero downtime during registration peaks.
</p>
</div>
<div class="p-5 rounded-2xl border border-border-custom bg-card-custom/60 space-y-2">
<h3 class="text-sm font-bold font-heading text-foreground-custom flex items-center gap-2">
<span class="text-primary-custom font-mono">02.</span>
Automated OCR Extraction
</h3>
<p class="text-xs text-muted-foreground-custom leading-relaxed">
Bundled Tesseract OCR and Ghostscript to ingest paper transcripts and extract Learner Reference Numbers (LRN) and historical grades into structured database records, eliminating hours of manual data entry.
</p>
</div>
<div class="p-5 rounded-2xl border border-border-custom bg-card-custom/60 space-y-2">
<h3 class="text-sm font-bold font-heading text-foreground-custom flex items-center gap-2">
<span class="text-primary-custom font-mono">03.</span>
Internal Scanner Tooling
</h3>
<p class="text-xs text-muted-foreground-custom leading-relaxed">
Wrote an automated Python preprocessing script (<span class="font-mono text-primary-custom">iskandpotobgrmbr</span>) using image processing to detect boundaries and crop flatbed scanner white margins prior to OCR indexing.
</p>
</div>
<div class="p-5 rounded-2xl border border-border-custom bg-card-custom/60 space-y-2">
<h3 class="text-sm font-bold font-heading text-foreground-custom flex items-center gap-2">
<span class="text-primary-custom font-mono">04.</span>
Inno Setup Installer Packaging
</h3>
<p class="text-xs text-muted-foreground-custom leading-relaxed">
Compiled the Flutter desktop client and its environment dependencies into a one-click Windows executable installer via Inno Setup and custom release PowerShell scripts (<span class="font-mono text-primary-custom">build_release.ps1</span>).
</p>
</div>
</div>
</section>
</>
)}
{/* Section: Key Highlights & Features */}