Internal admin dashboard for Bato de Luna Art Gallery (BDLAG), featuring a state-of-the-art Apple-inspired glassmorphism design system.
This is a modern full-stack application built with React (Vite) on the frontend and a Node.js/Express-based serverless API backend. It features a stunning visual interface with frosted glass effects, refined typography (Inter font), and a cohesive design language that adapts seamlessly between light and dark modes. The project includes role-based access control (JWT) for protected routes, secure CRUD operations for employee data via serverless functions, and optimized PDF reporting. The entire system is backed by Supabase for database and storage.
- Apple-inspired UI: Premium glassmorphism design system with frosted glass effects and refined typography.
- Dynamic Theming: Seamless dark and light mode integration using CSS variables.
- Improved Reporting: Optimized PDF generation using
jspdf-autotablefor precise layouts. - Flexible Management: Employee CRUD via secure serverless API and value history tracking.
- Secure Access: JWT login with role-based access (
superadmin,employee,viewer). - Real-time State: Global state management for Auth and Theming via React Context.
- Supabase Integration: Robust PostgreSQL data layer and secure storage.
- Frontend: React 19, Vite, React Router DOM v6
- UI Components & Charts: Recharts, Custom Glassmorphism UI Components
- PDF Generation: jsPDF, jspdf-autotable
- Backend: Vercel Serverless Functions (Node.js/Express-like), Express, CORS, JSON Web Token (JWT), bcryptjs
- Database: Supabase (PostgreSQL)
- Styling: Tailwind CSS v4, Custom CSS variables, Bootstrap Icons, Apple Inter Font
- Linting & Code Quality: ESLint
/
|-- api/ # Vercel Serverless Functions (Backend)
| |-- employee-values/ # Values history endpoint
|-- public/
|-- src/ # React Frontend Source
| |-- assets/
| |-- components/
| | |-- auth/
| | |-- employee/
| | |-- layout/
| | |-- pages/
| | |-- ui/
| |-- config/
| |-- context/
| | |-- AuthContext.jsx
| | |-- ThemeContext.jsx
| |-- hooks/
| |-- utils/
| |-- App.jsx # Main application component with routing
| |-- index.css # Global styles
| |-- main.jsx # Application entry point
|-- supabase/ # SQL schemas and migrations
|-- .env.local # Frontend environment variables
|-- .env.server # Backend environment variables
|-- package.json
|-- vercel.json # Vercel deployment configuration
Routing is defined in src/App.jsx and managed by react-router-dom.
ProtectedRoute: Wraps routes that require authentication. It checks for a valid user session and can also enforce role-based access.PublicOnlyRoute: Wraps routes like/loginto prevent access by already authenticated users, redirecting them to the dashboard.Layout: A wrapper component for protected pages, providing a consistent UI shell (e.g., sidebar, navbar).TitleUpdater: A utility component that dynamically updates the document title based on the current route.
| Path | Component | Access | Notes |
|---|---|---|---|
/ |
Redirect | - | Redirects to /login |
/login |
Login |
Public Only | Accessible only if not logged in |
/dashboard |
Dashboard |
Protected | Wrapped in Layout component |
/employees |
Employees |
Protected | Wrapped in Layout component |
/settings |
Settings |
Protected | Roles: superadmin, viewer |
/reports |
Reports |
Protected | Roles: superadmin, viewer |
/hanachimo |
HanachimoProfile |
Public | - |
* |
NotFound |
Public | 404 Page |
Global state is managed via React's Context API.
src/context/AuthContext.jsx: Provides authentication state (e.g., user object, login/logout functions) to the entire application.src/context/ThemeContext.jsx: Manages the application's theme (e.g., light/dark mode).
npm installCreate backend env file in project root (.env.server):
PORT=4000
FRONTEND_URL=http://localhost:5173
JWT_SECRET=your_long_random_secret
JWT_EXPIRES_IN=8h
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key_hereCreate frontend env file in project root (.env.local):
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your_anon_key_here
VITE_API_BASE_URL=Run SQL in Supabase:
supabase/sql/auth_schema.sqlsupabase/sql/2026_03_13_employee_schema_update.sql- (optional)
supabase/sql/employee_values_backfill.sql
Start dev servers (two terminals):
npm run dev:server
npm run dev:clientGenerate bcrypt hash:
node server/scripts/hashPassword.js yourpasswordInsert a user in Supabase:
insert into public.app_users (username, password_hash, role)
values ('admin', '<bcrypt-hash>', 'superadmin');Set env vars in Vercel Project Settings:
SUPABASE_URL
SUPABASE_SERVICE_ROLE_KEY
JWT_SECRET
JWT_EXPIRES_IN
VITE_SUPABASE_URL
VITE_SUPABASE_ANON_KEY
VITE_API_BASE_URL
Notes:
- Keep
SUPABASE_SERVICE_ROLE_KEYout of frontend env files. VITE_API_BASE_URLshould be empty for same-origin/api.
After API CRUD is live, run:
supabase/sql/employees_rls_lockdown.sql
This removes direct anon/authenticated access for public.employees.