/** * PortfolioPage — real My Portfolio CRUD, wired to backend. * Uses existing /api/:rolePrefix/portfolio/me endpoints. * Professionals only. */ import { For, Show, createSignal, onMount } from 'solid-js'; import { CARD, BTN_ORANGE, BTN_GHOST, BTN_PRIMARY, INPUT, LABEL } from '~/components/DashboardShell'; const API = '/api/gateway'; // ── Role prefix map ─────────────────────────────────────────────────────────── const ROLE_PREFIX: Record = { PHOTOGRAPHER: 'photographers', MAKEUP_ARTIST: 'makeup-artists', TUTOR: 'tutors', DEVELOPER: 'developers', VIDEO_EDITOR: 'video-editors', GRAPHIC_DESIGNER: 'graphic-designers', SOCIAL_MEDIA_MANAGER:'social-media-managers', FITNESS_TRAINER: 'fitness-trainers', CATERING_SERVICES: 'catering-services', UGC_CONTENT_CREATOR: 'ugc-content-creators', }; // ── Types ───────────────────────────────────────────────────────────────────── interface PortfolioItem { id: string; title: string; description?: string; tags?: string[]; created_at?: string; } interface FormState { title: string; description: string; tags: string; } const EMPTY_FORM: FormState = { title: '', description: '', tags: '' }; // ── Helpers ─────────────────────────────────────────────────────────────────── async function apiFetch(path: string, opts?: RequestInit) { return fetch(`${API}${path}`, { ...opts, credentials: 'include', headers: { 'Content-Type': 'application/json', ...(opts?.headers ?? {}) }, }); } // ── Component ───────────────────────────────────────────────────────────────── interface Props { roleKey: string; } export default function PortfolioPage(props: Props) { const prefix = () => ROLE_PREFIX[props.roleKey] ?? ''; const isProfessional = () => Boolean(prefix()); const [items, setItems] = createSignal([]); const [loading, setLoading] = createSignal(true); const [showForm, setShowForm] = createSignal(false); const [editId, setEditId] = createSignal(null); const [form, setForm] = createSignal({ ...EMPTY_FORM }); const [saving, setSaving] = createSignal(false); const [deleting, setDeleting] = createSignal(null); const [error, setError] = createSignal(''); const loadItems = async () => { if (!isProfessional()) { setLoading(false); return; } setLoading(true); try { const res = await apiFetch(`/api/${prefix()}/portfolio/me`); if (res.ok) { const data = await res.json(); setItems(Array.isArray(data) ? data : (data.items ?? [])); } } finally { setLoading(false); } }; onMount(loadItems); const openCreate = () => { setEditId(null); setForm({ ...EMPTY_FORM }); setError(''); setShowForm(true); }; const openEdit = (item: PortfolioItem) => { setEditId(item.id); setForm({ title: item.title, description: item.description ?? '', tags: (item.tags ?? []).join(', '), }); setError(''); setShowForm(true); }; const cancelForm = () => { setShowForm(false); setEditId(null); setForm({ ...EMPTY_FORM }); setError(''); }; const setField = (key: keyof FormState, val: string) => setForm((prev) => ({ ...prev, [key]: val })); const handleSave = async () => { if (!form().title.trim()) { setError('Title is required.'); return; } setSaving(true); setError(''); const payload = { title: form().title.trim(), description: form().description.trim() || undefined, tags: form().tags .split(',') .map((t) => t.trim()) .filter(Boolean), }; try { const id = editId(); const res = id ? await apiFetch(`/api/${prefix()}/portfolio/me/${id}`, { method: 'PATCH', body: JSON.stringify(payload) }) : await apiFetch(`/api/${prefix()}/portfolio/me`, { method: 'POST', body: JSON.stringify(payload) }); if (res.ok) { await loadItems(); cancelForm(); } else { const d = await res.json().catch(() => ({})); setError(d.error ?? d.message ?? 'Failed to save. Please try again.'); } } catch { setError('Network error. Please try again.'); } finally { setSaving(false); } }; const handleDelete = async (id: string) => { if (!confirm('Delete this portfolio item?')) return; setDeleting(id); try { await apiFetch(`/api/${prefix()}/portfolio/me/${id}`, { method: 'DELETE' }); await loadItems(); } finally { setDeleting(null); } }; // ── Not a professional role ───────────────────────────────────────────── if (!isProfessional()) { return (

Portfolio is available for professional roles only.

); } return (
{/* ── Header ────────────────────────────────────────────────────── */}

My Portfolio

Showcase your work to attract clients.

{/* ── Create / Edit form ─────────────────────────────────────────── */}

{editId() ? 'Edit Portfolio Item' : 'New Portfolio Item'}

setField('title', e.currentTarget.value)} style={INPUT} />