agentlytics
Comprehensive analytics dashboard for AI coding agents — Cursor, Windsurf, Claude Code, VS Code Copilot, Zed, Antigravi…
Personal portfolio built with React + Supabase, featuring an admin dashboard for managing projects, certificates, and c…
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
This project is built using modern web technologies:
| 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 |
>= 14.xgit clone https://github.com/EkiZR/Portofolio_V5.git cd Portofolio_V5 npm install
If you encounter peer dependency issues:
npm install --legacy-peer-deps
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.
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)
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,
"TechStack" 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;
-- Authenticated users may only read their own profile.
-- This is required by the login flow to check the user's role.
CREATE POLICY "User can read own profile"
ON public.profiles
FOR SELECT
TO authenticated
USING ((SELECT auth.uid()) = id);
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 ALL
TO authenticated
USING (
EXISTS (
SELECT 1 FROM public.profiles
WHERE id = auth.uid() AND role = 'admin'
)
)
WITH CHECK (
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');
INSERT INTO storage.buckets (id, name, public)
VALUES ('profile-images', 'profile-images', true)
ON CONFLICT DO NOTHING;
CREATE POLICY "public upload profile images"
ON storage.objects FOR INSERT
TO public
WITH CHECK (bucket_id = 'profile-images');
CREATE POLICY "public read profile images"
ON storage.objects FOR SELECT
TO public
USING (bucket_id = 'profile-images');
-- Optional: default pinned comment
INSERT INTO public.portfolio_comments (
content,
user_name,
profile_image,
is_pinned,
created_at
)
SELECT
'developed by ekizr. This project is open-source and free to use.',
'ekizr',
'https://egwzigagwyrmwjsrebzx.supabase.co/storage/v1/object/public/profile-images/profile-images/1771939421615_xx2q8hgya6e.jpeg',
true,
now()
WHERE NOT EXISTS (
SELECT 1
FROM public.portfolio_comments
WHERE is_pinned = true
);
Go to Table Editor → portfolio_comments → Enable Realtime.
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');
npm run dev
Open http://localhost:5173 in your browser.
npm run build
Upload the contents of the dist/ folder to your hosting provider.
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!
more like this
Comprehensive analytics dashboard for AI coding agents — Cursor, Windsurf, Claude Code, VS Code Copilot, Zed, Antigravi…
an efficient and user friendly OTA server equipped with a powerful WEB UI, designed to effortlessly manage both your ES…
React Media Library is a UI package similar to the WordPress' media library, but for React. It features file drag-and-d…
search projects, people, and tags