CreativelyNanda.co.za
Portfolio & Digital HQ — The Website That Builds Itself
My personal portfolio, digital headquarters, and product platform — a magazine-inspired, AI-integrated website with a Notion template shop, poetry collection, blog, and AI assistant. Built with Next.js, Supabase, and PayFast.
Serving
African creative technologists
Market
Monthly recurring revenue
Problem
Generic portfolio problem
Result
72+ commits, 6 Notion templates live
What needed solving
Generic developer portfolios fail to capture multidimensional creatives. A platform was needed that simultaneously showcases technical excellence, creative identity, and functions as a revenue-generating business.
How I built it
A full-stack personal brand platform with editorial design aesthetics, a functional Notion template shop (PayFast integrated), published poetry collection, AI sales assistant, blog CMS, and PWA capabilities.
The goal from day one: not a static brochure. A cultural destination — the professional credibility of LinkedIn, the seamless commerce of a digital storefront, the literary community of a poetry platform, the editorial authority of a long-form publication, all in one. Every architecture decision was written with a rationale. PayFast over Stripe: Stripe doesn't process ZAR-denominated cards directly for SA merchants without complex setup. PayFast has ~60% market share in SA, supports EFT and instant EFT (dominant payment methods in ZA), and processes in Rand natively. For a site targeting SA students and entrepreneurs, Stripe adds friction and currency confusion. PayFast was always the correct choice for this market. Supabase over Firebase: Firestore's document model would require denormalising the product-order relationship that Postgres handles naturally with foreign keys and indexes. Supabase provides full PostgreSQL, Row Level Security at the database layer (orders readable by buyer, not other users), and signed Storage URLs for digital delivery — all without custom code. Zustand for the cart: the cart must persist across App Router navigations and survive browser refreshes. The critical pattern is skipHydration: true on the store, with rehydrate() called after mount — this prevents the 'cart flicker' where server HTML shows an empty cart but client state has items. TypeScript caught the PayFast signature bug before production: the PayfastPaymentData interface ensures every field passed to the signature generator is accounted for. A type error in the PayFast signature function would cause silent revenue loss. TypeScript strict mode on payment code costs nothing at build time; the same error in production costs revenue.
// Cart store — skipHydration pattern prevents SSR/client mismatch
// Without this: server renders empty cart, client rehydrates with items → layout shift
export const useCartStore = create<CartStore>()(
persist(
(set, get) => ({
items: [],
addItem: (item) => set((s) => {
if (s.items.find(i => i.id === item.id)) return s; // No duplicates
return { items: [...s.items, { ...item, quantity: 1 }] };
}),
removeItem: (id) => set((s) => ({ items: s.items.filter(i => i.id !== id) })),
clearCart: () => set({ items: [] }),
get total() { return get().items.reduce((sum, i) => sum + (i.price * i.quantity), 0); },
}),
{
name: 'mirembe-cart',
storage: createJSONStorage(() => localStorage),
skipHydration: true, // Critical — prevents SSR/client cart flicker
}
)
);
// Call rehydrate() after mount — never on the server
useEffect(() => { useCartStore.persist.rehydrate(); }, []);
// PayFast signature — field ORDER matters; alphabetical sort BREAKS it
// This bug caused silent payment failures before the fix
export function generateSignature(data: PayfastPaymentData, passphrase: string): string {
const params = Object.entries(data) // Insertion order — DO NOT .sort()
.filter(([, v]) => v !== '' && v !== null)
.map(([k, v]) => `${k}=${encodeURIComponent(String(v)).replace(/%20/g, '+')}`)
.join('&');
return md5(`${params}&passphrase=${encodeURIComponent(passphrase)}`);
}What This Taught Me
- 1
skipHydration: true on Zustand persist + rehydrate() after mount prevents the cart flicker in SSR/App Router applications
- 2
PayFast signature is field-ORDER sensitive — alphabetical sort breaks it silently; this is not documented prominently in PayFast docs
- 3
Supabase over Firebase: PostgreSQL + RLS + signed Storage URLs solves product-order-delivery in one platform
- 4
PayFast over Stripe: ZAR native, EFT support, 60% SA market share — the correct payment gateway for the SA market
- 5
TypeScript strict mode on payment code: the type error that catches a PayFast bug costs nothing at build time
- 6
Arcjet composable middleware: bot detection + rate limiting in one API — less custom code, more security coverage
Next.js 14
App Router, SEO, PWA
TypeScript
Type safety across all features
Supabase
Products, orders, blog, poetry database
Claude API
AI assistant for visitor engagement
PayFast
South African payment gateway for template sales
Resend
Transactional email delivery
Framer Motion
Editorial animation system
# CreativelyNanda.co.za > Portfolio & Digital HQ — The Website That Builds Itself ## Project Context **Category:** Web App · Personal Brand · Full-Stack **Status:** Live · 2025 **Author:** Nandawula Regine Kabali-Kagwa — East London, South Africa **Company:** Mirembe Muse (Pty) Ltd · Reg: 2026-005658 ## Stack ``` Next.js 14 # App Router, SEO, PWA TypeScript # Type safety across all features Supabase # Products, orders, blog, poetry database Claude API # AI assistant for visitor engagement PayFast # South African payment gateway for template sales Resend # Transactional email delivery Framer Motion # Editorial animation system ``` ## Architecture Notes - All data mutations validated server-side via Next.js API routes - Row-Level Security enforced at database level (Supabase) - Mobile-first, PWA-ready, offline-tolerant where connectivity is unreliable - PayFast integration for ZAR-native payments (no USD conversion) - SEO-optimised: metadata, JSON-LD, canonical URLs, sitemap - POPIA compliant — data minimisation + user consent by design ## Environment Variables ```env NEXT_PUBLIC_SUPABASE_URL= NEXT_PUBLIC_SUPABASE_ANON_KEY= SUPABASE_SERVICE_ROLE_KEY= ANTHROPIC_API_KEY= NEXT_PUBLIC_PAYFAST_MERCHANT_ID= NEXT_PUBLIC_PAYFAST_MERCHANT_KEY= PAYFAST_PASSPHRASE= RESEND_API_KEY= ``` ## Links - Live: https://creativelynanda.co.za - GitHub: https://github.com/Nanda-Regine/CreativelyNanda.co.za - Portfolio: https://creativelynanda.co.za/projects/creativelynanda --- Built from East London, South Africa · Nine months · Zero to production
More Projects
Interested in similar work?
Let's discuss how I can build something like this for your business.
Let's Talk