import { useCollection, useIdentity } from '@deplixo/sdk';
import { BOX_SIZES, STYLES, STEPS, TREATS } from './components/constants.js';
import {
StepBox, StepStyle, StepColors, StepAccent, StepTreats, StepDetails
} from './components/Steps.jsx';
import Concept from './components/Concept.jsx';
const EMPTY = {
box: null,
customQty: 12,
style: null,
colors: [],
accent: null,
treats: [],
occasion: '',
nameNum: '',
notes: '',
};
function Wizard({ onSaved }) {
const [step, setStep] = React.useState(0);
const [selection, setSelection] = React.useState(EMPTY);
const [savedId, setSavedId] = React.useState(null);
const designs = useCollection('designs');
const update = (patch) => setSelection(s => ({ ...s, ...patch }));
const box = BOX_SIZES.find(b => b.id === selection.box);
const style = STYLES.find(s => s.id === selection.style);
const canAdvance = () => {
switch (step) {
case 0: return !!selection.box;
case 1: return !!selection.style;
case 2: return style ? selection.colors.length >= style.minColors : false;
case 3: return true; // accent optional
case 4: return selection.treats.length >= 1;
case 5: return true; // details optional
default: return true;
}
};
const restart = () => {
setSelection(EMPTY);
setStep(0);
setSavedId(null);
};
const saveConcept = async (concept) => {
const record = {
selection,
concept,
savedAt: new Date().toISOString(),
};
const created = await designs.add(record);
setSavedId(created?.id || 'saved');
if (onSaved) onSaved();
};
return (
{STEPS.map((label, i) => (
{label}
))}
{step === 0 &&
}
{step === 1 &&
}
{step === 2 &&
}
{step === 3 &&
}
{step === 4 &&
}
{step === 5 &&
}
{step === 6 && (
)}
{step < 6 && (
)}
);
}
function SavedDesigns() {
const { items, loading, remove } = useCollection('designs');
const { user } = useIdentity();
const mine = (items || []).filter(d => d.author?.id === user?.id);
const [viewing, setViewing] = React.useState(null);
if (loading) return Loading saved designs…
;
if (!mine.length) return (
Saved designs
You haven't saved any designs yet. Complete the wizard and tap Save this design to keep it here.
);
if (viewing) {
const d = viewing;
const box = BOX_SIZES.find(b => b.id === d.value?.selection?.box);
const style = STYLES.find(s => s.id === d.value?.selection?.style);
return (
{d.value?.concept?.title || 'Saved design'}
{d.value?.concept?.title}
{d.value?.concept?.tagline}
Box{box?.name}
Style{style?.name}
Colors{(d.value?.selection?.colors || []).join(', ')}
Accent{d.value?.selection?.accent || 'None'}
Treats
{(d.value?.selection?.treats || []).map(id => TREATS.find(t => t.id === id)?.name).filter(Boolean).join(', ')}
Overall design
{d.value?.concept?.overallDesign}
{d.value?.concept?.treatPlan && (
Treat-by-treat plan
{d.value.concept.treatPlan.map((p, i) => {
const t = TREATS.find(x => x.id === p.treatId);
return (
{t?.emoji} {t?.name || p.treatId}
×{p.count}
{p.decoration}
);
})}
)}
);
}
return (
Saved designs
Your previously generated design concepts.
{mine.map(d => {
const box = BOX_SIZES.find(b => b.id === d.value?.selection?.box);
const style = STYLES.find(s => s.id === d.value?.selection?.style);
return (
setViewing(d)}>
{d.value?.concept?.title || 'Untitled concept'}
{box?.name} • {style?.name}
{new Date(d.value?.savedAt || d.createdAt).toLocaleDateString()}
);
})}
);
}
function App() {
const [tab, setTab] = React.useState('design');
return (
{tab === 'design' && setTab('saved')} />}
{tab === 'saved' && }
);
}
ReactDOM.createRoot(document.getElementById('root')).render();