Back to Projects

Personal Portfolio & Admin Dashboard

Full-stack portfolio website with a comprehensive admin dashboard featuring role-based access control, real-time notifications, and content management system.

Next.js 16
React 19
TypeScript
Tailwind CSS
Shadcn/UI
TanStack Query
TanStack Form
TanStack Table
Zustand
Zod
Framer Motion
Go
PostgreSQL
JWT
SSE

title: "Personal Portfolio & Admin Dashboard" description: "Full-stack portfolio website with a comprehensive admin dashboard featuring role-based access control, real-time notifications, and content management system." technologies:

  • Next.js 16
  • React 19
  • TypeScript
  • Tailwind CSS
  • Shadcn/UI
  • TanStack Query
  • TanStack Form
  • TanStack Table
  • Zustand
  • Zod
  • Framer Motion
  • Go
  • PostgreSQL
  • JWT
  • SSE thumbnail: "/images/projects/personal-portfolio-admin-dashboard/thumbnail.webp" demo: "https://lehadegalego.com" date: "2024-12-09" featured: true

Overview

This project is my personal portfolio website combined with a powerful admin dashboard. It serves dual purposes: showcasing my work to potential clients and employers, while providing a complete content management system for managing projects, user inquiries, and system settings.

Portfolio landing page hero section
Clean, modern landing page with smooth animations

The Challenge

I needed a portfolio that wasn't just a static showcase, but a living platform where I could:

  • Manage content without touching code
  • Handle client inquiries efficiently
  • Control who has access to what features
  • Receive real-time notifications about new inquiries

Solution Architecture

Frontend

The frontend is built with Next.js 16 and React 19, taking advantage of the latest features including Server Components and the new App Router. The UI is crafted using Shadcn/UI components with Tailwind CSS for styling.

Key Features

  • Server-side rendering for optimal SEO
  • Responsive design for all devices
  • Dark/light theme support
  • Smooth animations with Framer Motion
  • Type-safe forms with TanStack Form + Zod validation

Backend

The backend is written in Go, providing a fast and reliable API. It uses PostgreSQL for data persistence and implements JWT authentication with refresh tokens.

State Management

Client state is managed with Zustand for its simplicity and performance. Server state is handled by TanStack Query, providing caching, background refetching, and optimistic updates.

Key Features

Role-Based Access Control (RBAC)

One of the most complex features is the granular permission system. Instead of simple role-based checks, the system allows configuring access at the route level:

// Each route is a "section" with its own permission
const sections = [
  "users",           // /dashboard/users
  "users.new",       // /dashboard/users/new
  "users.[id]",      // /dashboard/users/:id
  "settings.roles",  // /dashboard/settings/roles
  // ...
];

// Check access
function canAccess(user: IUser, section: string): boolean {
  return user.role.permissions.some(
    (p) => p.section_name === section && p.can_access
  );
}

This approach allows for very fine-grained control. For example, a user could have access to view the users list but not create new users.

Role permissions configuration
Granular permission control for each route

Real-Time Notifications

The dashboard implements Server-Sent Events (SSE) for real-time notifications. When a new inquiry comes in through the contact form, all logged-in admins receive an instant notification.

// SSE connection with automatic reconnection
useEffect(() => {
  const eventSource = new EventSource("/api/v1/notifications/stream");

  eventSource.onmessage = (event) => {
    const notification = JSON.parse(event.data);
    addNotification(notification);
    toast.info(notification.title);
  };

  return () => eventSource.close();
}, []);
Real-time notifications
Instant notifications for new inquiries

Data Tables

All list views use TanStack Table with:

Table Features

  • Server-side pagination
  • Column sorting
  • Search/filtering
  • Row selection
  • Skeleton loading states
Users management table
Full-featured data table with sorting and pagination

Technical Decisions

Why Go for Backend?

  • Excellent performance for API servers
  • Strong typing catches errors at compile time
  • Simple deployment (single binary)
  • Great standard library for HTTP servers

Why Zustand over Redux?

  • Minimal boilerplate
  • No providers needed
  • Built-in persistence middleware
  • TypeScript support out of the box
  • Tiny bundle size (~1KB)

Why TanStack Form?

  • Headless - works with any UI library
  • Built-in async validation
  • TypeScript-first design
  • Integrates well with Zod schemas

Lessons Learned

  1. Start with permissions early - Adding RBAC to an existing system is much harder than building it from the start.

  2. SSE vs WebSockets - For one-way server-to-client communication, SSE is simpler and sufficient. No need for WebSocket complexity.

  3. Type safety pays off - The time invested in proper TypeScript types saved countless hours of debugging.

What's Next

  • Implement analytics dashboard
  • Add multi-language support
  • Email notifications for new inquiries