Portfolio V5
Hello everyone! 👋
Let me introduce myself, I'm Eki Zulfar Rachman. On this occasion, I'd like to share the portfolio website project that I've developed. built with React and Supabase, featuring a public-facing site and an admin dashboard.
Live Demo: https://ekizr.com
🛠️ Tech Stack
This project is built using modern web technologies:
- ReactJS - Frontend framework
- Tailwind CSS - Utility-first CSS framework
- Supabase - Backend for portfolio data, certificates, and comment system
- AOS - Animate On Scroll library
- Framer Motion - Animation library
- Lucide - Icon library
- Material UI - React component library
- SweetAlert2 - Beautiful alert dialogs
User Roles
| Role | Access |
|---|---|
| Visitor (Public) | View projects, certificates, and comments — leave a comment |
| Admin | Login to dashboard — full CRUD on projects & certificates — delete & pin/unpin comments |
Getting Started
Prerequisites
- Node.js
>= 14.x - npm or yarn
1. Clone & Install
git clone https://github.com/EkiZR/Portofolio_V5.git cd Portofolio_V5 npm install
If you encounter peer dependency issues:
npm install --legacy-peer-deps
2. Environment Variables
Create a .env file in the root directory:
VITE_SUPABASE_URL=your-supabase-project-url VITE_SUPABASE_ANON_KEY=your-supabase-anon-key
Find these in your Supabase project under Settings → API.
⚠️ Never commit.envto version control — make sure it's in.gitignore.
3. Supabase Client (src/supabase.js)
import { createClient } from '@supabase/supabase-js' const supabaseUrl = import.meta.env.VITE_SUPABASE_URL const supabaseKey = import.meta.env.VITE_SUPABASE_ANON_KEY if (!supabaseUrl || !supabaseKey) { throw new Error('Supabase credentials missing. Check your .env file.') } export const supabase = createClient(supabaseUrl, supabaseKey)
4. Database Setup
Go to your Supabase project → SQL Editor → run the script below (run once):
-- ============================ -- TABLES -- ============================ CREATE TABLE public.projects ( id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, title text, description text, img text, link text, github text, features jsonb, tech_stack jsonb, is_published boolean DEFAULT true, order_index int DEFAULT 0, created_at timestamptz DEFAULT now() ); CREATE TABLE public.certificates ( id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, img text, created_at timestamptz DEFAULT now() ); CREATE TABLE public.portfolio_comments ( id uuid DEFAULT gen_random_uuid() PRIMARY KEY, content text NOT NULL, user_name text NOT NULL, profile_image text, is_pinned boolean DEFAULT false, created_at timestamptz DEFAULT now() ); CREATE TABLE public.profiles ( id uuid PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE, username text UNIQUE NOT NULL, role text NOT NULL CHECK (role IN ('admin', 'user')), created_at timestamptz DEFAULT now() ); -- ============================ -- RLS -- ============================ ALTER TABLE public.projects ENABLE ROW LEVEL SECURITY; ALTER TABLE public.certificates ENABLE ROW LEVEL SECURITY; ALTER TABLE public.portfolio_comments ENABLE ROW LEVEL SECURITY; ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY; CREATE POLICY "public read projects" ON public.projects FOR SELECT USING (true); CREATE POLICY "public read certificates" ON public.certificates FOR SELECT USING (true); CREATE POLICY "public read comments" ON public.portfolio_comments FOR SELECT USING (true); CREATE POLICY "public insert comment" ON public.portfolio_comments FOR INSERT WITH CHECK (is_pinned = false); CREATE POLICY "admin manage projects" ON public.projects FOR ALL USING ( EXISTS ( SELECT 1 FROM public.profiles WHERE id = auth.uid() AND role = 'admin' ) ); CREATE POLICY "admin manage certificates" ON public.certificates FOR ALL USING ( EXISTS ( SELECT 1 FROM public.profiles WHERE id = auth.uid() AND role = 'admin' ) ); CREATE POLICY "admin manage comments" ON public.portfolio_comments FOR UPDATE, DELETE USING ( EXISTS ( SELECT 1 FROM public.profiles WHERE id = auth.uid() AND role = 'admin' ) ); -- ============================ -- STORAGE -- ============================ INSERT INTO storage.buckets (id, name, public) VALUES ('project-images', 'project-images', true) ON CONFLICT DO NOTHING; CREATE POLICY "admin upload project images" ON storage.objects FOR INSERT WITH CHECK ( bucket_id = 'project-images' AND EXISTS ( SELECT 1 FROM public.profiles WHERE id = auth.uid() AND role = 'admin' ) ); CREATE POLICY "public read project images" ON storage.objects FOR SELECT USING (bucket_id = 'project-images'); INSERT INTO storage.buckets (id, name, public) VALUES ('certificate-images', 'certificate-images', true) ON CONFLICT DO NOTHING; CREATE POLICY "admin upload certificate images" ON storage.objects FOR INSERT WITH CHECK ( bucket_id = 'certificate-images' AND EXISTS ( SELECT 1 FROM public.profiles WHERE id = auth.uid() AND role = 'admin' ) ); CREATE POLICY "public read certificate images" ON storage.objects FOR SELECT USING (bucket_id = 'certificate-images');
5. Enable Realtime (Comments)
Go to Table Editor → portfolio_comments → Enable Realtime.
6. Create Admin Account
Step 1 — Go to Authentication → Users → Add User in Supabase Dashboard, then copy the generated User ID.
Step 2 — Run this in the SQL Editor (replace USER_UUID with the copied ID):
INSERT INTO public.profiles (id, username, role) VALUES ('USER_UUID', 'eki', 'admin');
7. Run Locally
npm run dev
Open http://localhost:5173 in your browser.
Pages & Features
Public (Visitor)
- Home — Hero section, about, skills
- Projects — List of published projects with detail modal
- Certificates — Certificate gallery
- Comments — View all comments, submit a new comment with name and optional profile photo
Admin (Dashboard)
- Login Page — Email & password authentication via Supabase Auth
- Dashboard — Overview panel after login
- Projects — Create, edit, delete projects; manage image, links, features, tech stack, publish status, and order
- Certificates — Upload and delete certificate images
- Comments — View all comments; pin/unpin for highlighting; delete inappropriate comments
Build for Production
npm run build
Upload the contents of the dist/ folder to your hosting provider.
Troubleshooting
- Ensure Node.js is installed and you're in the correct directory.
- Double-check your
.envvalues and restart the dev server after changes. - If RLS is blocking requests, verify the
profilesrow exists for your admin user. - Clear browser cache if you see stale data.
Credits & Contact
Eki Zulfar Rachman
Website: eki.my.id · GitHub: EkiZR
Thanks to LottieFiles and Claude.
⭐ If this project helped you, consider giving it a star on GitHub!