// fp-dashboard.jsx — Brand list + creation
// Exports: Dashboard
function Dashboard({ onSelectBrand, onManageUsers }) {
const { profile, setProfile } = useAuth();
const isAdmin = profile?.role === 'admin';
const [brands, setBrands] = useState([]);
const [loading, setLoading] = useState(true);
const [showNew, setShowNew] = useState(false);
const [newName, setNewName] = useState('');
const [newUsername, setNewUsername] = useState('');
const [creating, setCreating] = useState(false);
// Edit own profile
const [showEditProfile, setShowEditProfile] = useState(false);
const [editName, setEditName] = useState('');
const [editPassword, setEditPassword] = useState('');
const [editPassword2, setEditPassword2] = useState('');
const [savingProfile, setSavingProfile] = useState(false);
const [profileMsg, setProfileMsg] = useState({ text: '', ok: true });
const openEditProfile = () => {
setEditName(profile?.name || '');
setEditPassword('');
setEditPassword2('');
setProfileMsg({ text: '', ok: true });
setShowEditProfile(true);
};
const saveProfile = async e => {
e.preventDefault();
setProfileMsg({ text: '', ok: true });
if (editPassword && editPassword !== editPassword2) {
setProfileMsg({ text: 'Las contraseñas no coinciden.', ok: false });
return;
}
setSavingProfile(true);
if (editName.trim() && editName.trim() !== profile?.name) {
await sbClient.from('profiles').update({ name: editName.trim() }).eq('id', profile.id);
setProfile(p => ({ ...p, name: editName.trim() }));
}
if (editPassword) {
const { error } = await sbClient.auth.updateUser({ password: editPassword });
if (error) {
setProfileMsg({ text: 'Error al cambiar contraseña: ' + error.message, ok: false });
setSavingProfile(false);
return;
}
}
setSavingProfile(false);
setProfileMsg({ text: '¡Cambios guardados correctamente!', ok: true });
setEditPassword('');
setEditPassword2('');
};
const loadBrands = async () => {
setLoading(true);
let q = sbClient.from('brands').select('*, brand_access(count)').order('created_at', { ascending: false });
if (!isAdmin) {
const { data: access } = await sbClient.from('brand_access').select('brand_id').eq('user_id', profile.id);
const ids = (access || []).map(a => a.brand_id);
if (!ids.length) { setBrands([]); setLoading(false); return; }
q = q.in('id', ids);
}
const { data } = await q;
setBrands(data || []);
setLoading(false);
};
useEffect(() => { if (profile) loadBrands(); }, [profile]);
const createBrand = async e => {
e.preventDefault();
if (!newName.trim()) return;
setCreating(true);
const { data, error } = await sbClient.from('brands').insert({
name: newName.trim(),
username: newUsername.trim() || newName.toLowerCase().replace(/\s+/g,''),
created_by: profile.id,
}).select().single();
if (!error && data) { setBrands(b => [data, ...b]); setShowNew(false); setNewName(''); setNewUsername(''); }
setCreating(false);
};
const deleteBrand = async (id, e) => {
e.stopPropagation();
if (!confirm('¿Eliminar esta marca y todos sus posts?')) return;
await sbClient.from('brands').delete().eq('id', id);
setBrands(b => b.filter(x => x.id !== id));
};
const logout = () => sbClient.auth.signOut();
return (
{/* Top bar */}
Feed Planner
{isAdmin && (
)}
{profile?.name || profile?.email}
{profile?.role}
Marcas
{brands.length} marca{brands.length !== 1 ? 's' : ''} activa{brands.length !== 1 ? 's' : ''}
{isAdmin && (
)}
{loading ? (
) : brands.length === 0 ? (
✦
Sin marcas todavía
{isAdmin &&
Crea tu primera marca para comenzar
}
) : (
{brands.map(b => (
onSelectBrand(b)}>
{b.avatar_url ? (

) : (
{(b.username || b.name).slice(0,1).toUpperCase()}
)}
{b.name}
@{b.username}
{b.bio &&
{b.bio}
}
{isAdmin && (
)}
))}
)}
{/* New brand modal */}
{showNew && (
setShowNew(false)}>
e.stopPropagation()}>
Nueva marca
)}
{/* Edit profile modal */}
{showEditProfile && (
setShowEditProfile(false)}>
e.stopPropagation()}>
Mi perfil
{profile?.email}
{profileMsg.text && (
{profileMsg.text}
)}
)}
);
}
const dbStyles = {
page: { minHeight:'100vh', background:'var(--cream)', display:'flex', flexDirection:'column' },
topbar: { background:'#fff', borderBottom:'1px solid var(--border)', padding:'14px 32px', display:'flex', alignItems:'center', justifyContent:'space-between' },
logo: { fontFamily:"'Playfair Display',serif", fontSize:20, fontWeight:600, display:'flex', alignItems:'center', gap:8 },
dot: { width:8, height:8, borderRadius:'50%', background:'var(--accent)', display:'inline-block' },
content: { flex:1, maxWidth:960, margin:'0 auto', width:'100%', padding:'36px 24px' },
header: { display:'flex', alignItems:'flex-end', justifyContent:'space-between', marginBottom:28 },
title: { fontFamily:"'Playfair Display',serif", fontSize:28, fontWeight:600, letterSpacing:'-0.5px' },
subtitle:{ fontSize:13, color:'var(--ink-muted)', marginTop:4 },
grid: { display:'grid', gridTemplateColumns:'repeat(auto-fill,minmax(260px,1fr))', gap:16 },
card: { background:'#fff', borderRadius:16, overflow:'hidden', cursor:'pointer', boxShadow:'0 1px 4px rgba(28,25,23,0.07)', transition:'all 0.18s', position:'relative' },
cardColor:{ height:90, display:'flex', alignItems:'center', justifyContent:'center', overflow:'hidden' },
cardBody:{ padding:'14px 16px 16px' },
cardDel: { position:'absolute', top:8, right:8, width:28, height:28, background:'rgba(255,255,255,0.85)', border:'none', borderRadius:8, display:'flex', alignItems:'center', justifyContent:'center', cursor:'pointer', color:'var(--ink-soft)', opacity:0, transition:'opacity 0.15s' },
empty: { textAlign:'center', padding:'80px 24px', color:'var(--ink-soft)' },
btnPrimary: { background:'var(--accent)', color:'#fff', border:'none', borderRadius:9, padding:'9px 16px', fontFamily:"'DM Sans',sans-serif", fontSize:13, fontWeight:600, cursor:'pointer', display:'flex', alignItems:'center', gap:6 },
btnOutline: { background:'#fff', color:'var(--ink-soft)', border:'1px solid var(--border)', borderRadius:9, padding:'9px 14px', fontFamily:"'DM Sans',sans-serif", fontSize:13, fontWeight:500, cursor:'pointer', display:'flex', alignItems:'center', gap:6 },
btnIcon: { background:'none', border:'none', cursor:'pointer', color:'var(--ink-soft)', padding:6, borderRadius:8, display:'flex' },
modalBackdrop: { position:'fixed', inset:0, background:'rgba(28,25,23,0.45)', display:'flex', alignItems:'center', justifyContent:'center', zIndex:100, backdropFilter:'blur(4px)' },
modalCard: { background:'#fff', borderRadius:18, padding:'28px 28px 24px', width:380, boxShadow:'0 12px 40px rgba(28,25,23,0.16)' },
field: { display:'flex', flexDirection:'column', gap:5 },
fieldLabel: { fontSize:11, fontWeight:600, color:'var(--ink-soft)', letterSpacing:'0.06em', textTransform:'uppercase' },
input: { background:'var(--cream)', border:'1px solid var(--cream-dark)', borderRadius:8, padding:'9px 12px', fontFamily:"'DM Sans',sans-serif", fontSize:13.5, color:'var(--ink)', outline:'none', width:'100%' },
};
// hover effect via CSS class injection
const dashboardCSS = document.createElement('style');
dashboardCSS.textContent = `.db-card:hover { box-shadow: 0 6px 24px rgba(28,25,23,0.12) !important; transform: translateY(-2px); } .db-card:hover .card-del { opacity: 1 !important; }`;
document.head.appendChild(dashboardCSS);
Object.assign(window, { Dashboard });