/* === library.jsx ====================================================
Login screen + Demo library home page for UpFlux Studio.
===================================================================== */
const DEFAULT_STUDIO_PASSWORD = 'upflux2026';
/* Read the current password from localStorage; falls back to the default
on first run. Persisted plain (no hashing) — this is a soft access
gate for sales team, not a security boundary. */
function getStudioPassword() {
try {
const stored = localStorage.getItem('studio_password');
return (stored && stored.length > 0) ? stored : DEFAULT_STUDIO_PASSWORD;
} catch { return DEFAULT_STUDIO_PASSWORD; }
}
function setStudioPassword(next) {
try { localStorage.setItem('studio_password', next); } catch {}
}
/* ==================================================================
LOGIN SCREEN
================================================================== */
function LoginScreen({ onAuth }) {
const [pwd, setPwd] = React.useState('');
const [err, setErr] = React.useState(false);
const submit = (e) => {
e?.preventDefault();
if (pwd === getStudioPassword()) onAuth();
else { setErr(true); setTimeout(() => setErr(false), 1800); }
};
return (
);
}
/* ==================================================================
LIBRARY SCREEN
================================================================== */
function LibraryScreen({ library, onOpen, onClone, onDelete, onCreate, onLogout }) {
const [filter, setFilter] = React.useState('all');
const [query, setQuery] = React.useState('');
const [menuOpenId, setMenuOpenId] = React.useState(null);
const [showNewForm, setShowNewForm] = React.useState(false);
const [showSettings, setShowSettings] = React.useState(false);
React.useEffect(() => {
const close = () => setMenuOpenId(null);
window.addEventListener('click', close);
return () => window.removeEventListener('click', close);
}, []);
const filtered = library.filter(d => {
if (filter !== 'all' && d.status !== filter) return false;
if (query && !`${d.name} ${d.prospect} ${d.industry}`.toLowerCase().includes(query.toLowerCase())) return false;
return true;
});
const counts = {
all: library.length,
draft: library.filter(d => d.status === 'draft').length,
ready: library.filter(d => d.status === 'ready').length,
shared: library.filter(d => d.status === 'shared').length,
};
return (
U
UpFlux Studio
{window.STUDIO_DATA.STUDIO_VERSION}
Abrir Nous
Biblioteca de demos
Crie uma demo nova, abra uma existente ou clone para começar
rápido a partir de um template.
{filtered.map((d) => (
{ e.stopPropagation(); setMenuOpenId(menuOpenId === d.id ? null : d.id); }}
onOpen={() => onOpen(d.id)}
onClone={() => { onClone(d.id); setMenuOpenId(null); }}
onDelete={() => {
if (confirm(`Excluir a demo "${d.name}"? Esta ação não pode ser desfeita.`)) {
onDelete(d.id);
}
setMenuOpenId(null);
}}
/>
))}
{filtered.length === 0 && (
Nenhuma demo encontrada para esses filtros.
)}
{showNewForm && (
setShowNewForm(false)}
onCreate={(name) => { setShowNewForm(false); onCreate(name); }}
onClone={(id) => { setShowNewForm(false); onClone(id); }}
/>
)}
{showSettings && (
setShowSettings(false)} />
)}
);
}
/* ==================================================================
SETTINGS MODAL — change password + studio info
================================================================== */
function SettingsModal({ onClose }) {
const [current, setCurrent] = React.useState('');
const [next, setNext] = React.useState('');
const [confirm, setConfirm] = React.useState('');
const [feedback, setFeedback] = React.useState(null); /* { kind: 'ok'|'err', msg } */
const [show, setShow] = React.useState(false);
const submit = (e) => {
e?.preventDefault();
const stored = getStudioPassword();
if (current !== stored) {
setFeedback({ kind: 'err', msg: 'Senha atual incorreta.' });
return;
}
if (!next || next.length < 4) {
setFeedback({ kind: 'err', msg: 'A nova senha precisa ter ao menos 4 caracteres.' });
return;
}
if (next !== confirm) {
setFeedback({ kind: 'err', msg: 'A confirmação não bate com a nova senha.' });
return;
}
setStudioPassword(next);
setFeedback({ kind: 'ok', msg: 'Senha atualizada. Use a nova senha na próxima vez que entrar.' });
setCurrent(''); setNext(''); setConfirm('');
};
const reset = () => {
if (confirm('Restaurar a senha padrão "upflux2026"?')) {
try { localStorage.removeItem('studio_password'); } catch {}
setFeedback({ kind: 'ok', msg: 'Senha restaurada para o padrão "upflux2026".' });
}
};
return (
{demo.prospect.charAt(0).toUpperCase()}
{/* Subtle decorative shapes */}
{menuOpen && (
e.stopPropagation()}>
)}
{demo.name}
{st.label}
{demo.industry}
{demo.persona}
{demo.thumbHint}
{demo.ownerInitials}
Editada em {demo.updatedAt}
{versionMismatch && (
{demo.version}
)}
{demo.views > 0 && (
{demo.views}
)}
);
}
/* ==================================================================
NEW DEMO MODAL — blank or clone-from-template
================================================================== */
function NewDemoModal({ library, onClose, onCreate, onClone }) {
const [name, setName] = React.useState('');
const [mode, setMode] = React.useState('blank'); /* 'blank' | 'clone' */
const [pickedId, setPickedId] = React.useState(library[0]?.id);
return (