Modern React dashboard starter with PHP backend, Radix UI Themes, dark/light mode, authentication flow, and responsive design.
- Frontend: React 19 + Vite 8 + @radix-ui/themes + react-router-dom
- Backend: PHP 8+ (custom router, no framework)
- Database: MySQL (PDO)
- Linting: Oxlint
├── api/ # PHP backend
│ ├── index.php # Front-controller / router
│ ├── Database.php # PDO singleton
│ ├── .env # DB creds + FRONTEND_URL
│ ├── .htaccess # URL rewriting (API + SPA fallback)
│ ├── controllers/
│ │ ├── AuthTrait.php # authenticate() middleware
│ │ ├── AuthController.php # Login, register, 2FA, password reset
│ │ ├── UserController.php # CRUD users + admin 2FA management
│ │ ├── DashboardController.php # Stats endpoints
│ │ └── SettingsController.php # Mail settings
│ ├── helpers/
│ │ ├── TOTP.php # TOTP (RFC 6238) secret generation & verification
│ │ └── Mailer.php # PHPMailer wrapper
│ ├── lib/phpmailer/ # PHPMailer library
│ └── migrations/
│ └── database.sql # Full DB backup
├── dist/ # Production frontend build (SPA fallback)
src/
├── auth.jsx # Auth context + hook (sessionStorage)
├── components/
│ ├── ActivitySection.jsx # Recent activity list
│ ├── ChartsSection.jsx # Revenue chart + traffic sources
│ ├── Footer.jsx # Public footer
│ ├── Icon.jsx # SVG icon library
│ ├── PublicNavbar.jsx # Public site navbar with theme toggle
│ ├── Sidebar.jsx # Dashboard sidebar (collapsible, nav, user, theme)
│ ├── StatsGrid.jsx # Stats cards (Revenue, Users, Orders, Growth)
│ └── TopBar.jsx # Dashboard top bar (search, notifications, avatar)
├── pages/
│ ├── Landing.jsx # Public landing page (hero, features, stats, CTA)
│ ├── Login.jsx # Login form
│ ├── ForgotPassword.jsx # Password reset request
│ ├── ResetPassword.jsx # New password form
│ └── Dashboard.jsx # Protected admin dashboard
├── App.jsx # Router + Theme provider
├── main.jsx # Entry point
├── index.css # Global reset
└── App.css # All component styles
| Path | Page | Auth |
|---|---|---|
/ |
Landing | Public |
/login |
Login | Public |
/forgot-password |
Forgot password | Public |
/reset-password |
Reset password | Public |
/dashboard |
Dashboard | Protected |
- Dark/Light mode — toggle in sidebar (dashboard) and navbar (landing), persisted via Theme prop
- Collapsible sidebar — expand/collapse with animated width transition (260px ↔ 64px)
- Responsive — sidebar becomes overlay on mobile (<768px), stats grid adapts
- Auth flow — auth context, protected routes, logout
- Login system — login, forgot password, reset password pages
- Dashboard widgets — stats cards, bar chart, traffic progress bars, activity feed
- Radix theming — accent color, radius, and dark mode controlled from
<Theme>
All endpoints are prefixed with /dashboard-starter/api.
Authentication via Authorization: Bearer <token> header (exceptions noted).
| Method | Endpoint | Auth | Body | Response |
|---|---|---|---|---|
| POST | /auth/login |
No | { email, password } |
{ data: { token, user } } or { data: { requires_2fa: true, user_id } } |
| POST | /auth/register |
No | { name, email, password } |
{ data: { id, name, email } } |
| GET | /auth/me |
Yes | — | { data: { id, name, email, role, two_factor_enabled } } |
| POST | /auth/forgot-password |
No | { email } |
{ data: { message } } |
| POST | /auth/reset-password |
No | { token, password } |
{ data: { message } } |
| Method | Endpoint | Auth | Body | Response |
|---|---|---|---|---|
| POST | /auth/2fa/enable |
Yes | — | { data: { secret, otpauth_url } } |
| POST | /auth/2fa/confirm |
Yes | { code } |
{ data: { message, recovery_codes } } |
| POST | /auth/2fa/disable |
Yes | { password } |
{ data: { message } } |
| POST | /auth/2fa/verify |
No | { user_id, code } |
{ data: { token, user } } |
| POST | /auth/2fa/recovery |
No | { user_id, code } |
{ data: { token, user } } |
| Method | Endpoint | Auth | Body | Response |
|---|---|---|---|---|
| POST | /users/{id}/2fa/enable |
Yes | — | { data: { secret, otpauth_url } } |
| POST | /users/{id}/2fa/confirm |
Yes | { code } |
{ data: { message, recovery_codes } } |
| POST | /users/{id}/2fa/disable |
Yes | — | { data: { message } } |
| Method | Endpoint | Auth | Body | Response |
|---|---|---|---|---|
| GET | /users |
Yes | — | { data: [ { id, name, email, role, two_factor_enabled, created_at } ] } |
| POST | /users |
Yes | { name, email, password, role? } |
{ data: { id, name, email, role } } |
| GET | /users/{id} |
Yes | — | { data: { id, name, email, role, two_factor_enabled, created_at } } |
| PUT | /users/{id} |
Yes | { name?, email?, role?, password? } |
{ data: { ...user } } |
| DELETE | /users/{id} |
Yes | — | { data: { message } } |
| Method | Endpoint | Auth | Response |
|---|---|---|---|
| GET | /dashboard/stats |
Yes | { data: [ { label, value, change, positive } ] } |
| GET | /dashboard/revenue |
Yes | { data: [ { month, total } ] } |
| GET | /dashboard/activity |
Yes | { data: [ { text, time } ] } |
| GET | /dashboard/traffic |
Yes | { data: [ { label, value } ] } (value = percentage) |
| Method | Endpoint | Auth | Body | Response |
|---|---|---|---|---|
| GET | /mail-settings |
Yes | — | { data: { mail_host, mail_port, ... } } |
| PUT | /mail-settings |
Yes | { mail_host?, mail_port?, ... } |
{ data: { message, updated } } |
| POST | /mail-settings/test-email |
Yes | { email } |
{ data: { message } } |
All endpoints return errors in the format { error: "message" } with appropriate HTTP status codes:
- 400 — Bad request (missing/invalid fields)
- 401 — Unauthorized (missing/expired token, invalid credentials)
- 404 — Not found
- 409 — Conflict (duplicate email)
- 500 — Server error
Create a MySQL database (e.g. dashboard-starter) and import the backup:
mysql -u root -p dashboard-starter < api/migrations/database.sqlOr via phpMyAdmin: create database → import api/migrations/database.sql.
Copy the api/ folder to your web server root and edit api/.env:
DB_HOST=localhost
DB_PORT=3306
DB_NAME=dashboard-starter
DB_USER=root
DB_PASS=
FRONTEND_URL=http://localhost:5173# Copy the whole project (or just api/) to:
D:\wamp64\www\dashboard-starter\
# Then access:
http://localhost/dashboard-starter/api/auth/login# Copy the whole project (or just api/) to:
C:\xampp\htdocs\dashboard-starter\
# Then access:
http://localhost/dashboard-starter/api/auth/login# Copy the whole project (or just api/) to:
C:\laragon\www\dashboard-starter\
# Laragon auto-creates a virtual host, access:
http://dashboard-starter.test/api/auth/loginNote: Apache
mod_rewritemust be enabled. The included.htaccessrewrites/api/*toindex.phpand serves the SPA fromdist/as fallback.
npm install
npm run devThe dev server runs on http://localhost:5173.
Make sure VITE_API_URL in .env matches your API URL:
VITE_API_URL=http://localhost/dashboard-starter/api| Field | Value |
|---|---|
admin@luyka.com |
|
| Password | admin123 |
This user is included in the database backup (api/migrations/database.sql) with 2FA disabled by default.
npm run dev # Start Vite dev server (frontend)
npm run build # Production build (frontend)
npm run preview # Preview production build
npm run lint # Run oxlint