Modern React portfolio with admin panel for editing all content. Live: https://noby.aizim.co.zw
- React 18 + Vite + plain JavaScript
- Tailwind CSS v4
- Framer Motion
- Supabase (Auth + Postgres + Storage)
- Radix UI primitives + custom shadcn-style components
- React Router v6, react-helmet-async, lucide-react, react-hot-toast
npm install
cp .env.example .env.local # then fill in Supabase URL + anon key
npm run devOpen http://localhost:5173.
- Create a project at supabase.com (free tier).
- SQL Editor → New query, paste
supabase/schema.sql, Run. - Authentication → Users → Add user: enter your admin email + a strong password.
- Project Settings → API: copy the Project URL and
anonpublic key into.env.local:VITE_SUPABASE_URL=... VITE_SUPABASE_ANON_KEY=... VITE_SITE_URL=https://noby.aizim.co.zw - Run the app, visit
/admin/login, log in. Edit your profile, then add projects, services, skills, testimonials.
npm run buildThis produces dist/ containing static HTML + assets. Includes .htaccess, robots.txt, sitemap.xml.
One-command deploy via SSH:
npm run deployBuilds locally, then tar | ssh streams dist/ contents into public_html/noby/
on the cPanel server. Smoke-tests https://noby.aizim.co.zw afterwards.
The script uses an SSH host alias aizim. Add this to ~/.ssh/config:
Host aizim
HostName ssh.us.stackcp.com
Port 22
User aizim.co.zw
IdentityFile ~/.ssh/aizim_cpanel
IdentitiesOnly yes
StrictHostKeyChecking accept-new
…and place the matching private key at ~/.ssh/aizim_cpanel (chmod 600).
Test once with ssh aizim "pwd" — should print /home/sites/....
If SSH is unavailable, build with npm run build and upload the contents of
dist/ to public_html/noby/ via cPanel File Manager — make sure .htaccess
is included for SPA routing to work on deep links like /services or
/products/foo.
/admin/login — once signed in, you can manage:
- Profile — name, headline, bio, headshot, resume, socials, contact info
- Projects — full CRUD with cover images, tags, tech stack, GitHub/live URLs, featured toggle
- Services, Skills, Testimonials — simple CRUD
- Inbox — read/reply/delete contact form messages
- Service role key is never used in this app; everything goes through
anon+ RLS policies. - Don't commit
.env.local(it's gitignored). - Rotate the admin password before launch via Supabase dashboard → Auth → Users.
src/
├── components/ ui/, layout/, ProtectedRoute.jsx, SEO.jsx
├── context/ AuthContext, ThemeContext
├── lib/ supabase.js, queries.js, utils.js
├── pages/ Home, About, Services, Projects, ProjectDetail, Contact, NotFound
├── admin/ Login, AdminLayout, Dashboard, ProjectsAdmin, etc.
├── App.jsx routes
├── main.jsx providers
└── index.css Tailwind v4 + theme
supabase/schema.sql run this once in Supabase SQL editor
public/ .htaccess, robots.txt, sitemap.xml