// Dateora — in-app screens: AppShell (sidebar), Conversation, History, Account
// ─── AppShell with sidebar nav ───────────────────────────────────
const AppShell = ({ t, currentView, onNav, onLogout, children }) => {
const items = [
{ id: 'conversation', label: 'Session', icon: },
{ id: 'history', label: 'History', icon: },
{ id: 'account', label: 'Account', icon: },
];
return (
{children}
);
};
// ─── Conversation screen — the heart ───────────────────────────
const QUICK_REPLIES = [
"I went blank again",
"Give me a real opener",
"What should I have said?",
"She walked off",
"I rehearsed and it worked",
];
const ConversationScreen = ({ t, messages, onSend, sending }) => {
const [draft, setDraft] = React.useState('');
const transcriptRef = React.useRef(null);
React.useEffect(() => {
const el = transcriptRef.current;
if (el) el.scrollTop = el.scrollHeight;
}, [messages, sending]);
const send = () => {
const v = draft.trim();
if (!v || sending) return;
onSend(v);
setDraft('');
};
// Show 3 rotating chips only when the last message was the coach's
const lastIsCoach = messages.length > 0 && messages[messages.length - 1].role === 'coach';
const sugg = lastIsCoach ? QUICK_REPLIES.slice(0, 3) : [];
// Layout differs by direction. Mentor: full-bleed avatar with overlay UI.
// Editorial: 2-column with portrait card. Studio: split layout.
return (
);
};
const ConversationHeader = ({ t }) => (
Adam · {t.coachRole}
Live · session 04 · 06:18
Save reps
End session
);
const ConversationStage = ({ t, messages, sending, transcriptRef }) => {
if (t.id === 'mentor') {
// Full-bleed avatar with transcript floating bottom-right
return (
{messages.map((m, i) => )}
{sending && }
Now rehearsing
The approach: 30 seconds, no script.
);
}
if (t.id === 'editorial') {
return (
SESSION 04 · IN PROGRESS
The approach
Today, we rehearse the first 30 seconds. Less than the time it takes to forget what you wanted to say.
Continued from p. 23
{messages.map((m, i) => )}
{sending && }
);
}
// studio
return (
LIVE
{[3, 7, 12, 18, 14, 9, 5, 11, 15, 8].map((h, i) => (
))}
SESSION 04 · APPROACH DRILLS · 06:18
{messages.map((m, i) =>
)}
{sending &&
}
);
};
const PillBtn = ({ t, children, active }) => (
);
const Bubble = ({ t, m }) => {
const isCoach = m.role === 'coach';
if (t.id === 'editorial') {
return (
{isCoach ? 'ADAM' : 'YOU'}
{m.text}
);
}
return (
);
};
const TypingIndicator = ({ t }) => (
{[0, 1, 2].map((i) => (
))}
);
const ConversationInputBar = ({ t, draft, setDraft, send, sending, suggestions = [] }) => (
{suggestions.length > 0 && !sending && !draft && (
{suggestions.map((s) => (
))}
)}
setDraft(e.target.value)}
onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); send(); } }}
placeholder={sending ? 'Adam is thinking…' : "Type your response, or describe a real moment"}
disabled={sending}
style={{
flex: 1, border: 'none', outline: 'none',
background: 'transparent', color: t.ink,
fontSize: 15, fontFamily: t.fontBody, padding: '10px 0',
}}
/>
Adam can be wrong. Don't use Dateora for crisis support — see help & resources.
);
// ─── History ─────────────────────────────────────────────────
const HistoryScreen = ({ t, sessions, onDelete, onOpen }) => (
Your reps
History
Past sessions with Adam. Reopen to continue, or delete — gone for good.
{sessions.map((s, i) => (
onDelete(s.id)} onOpen={() => onOpen(s.id)} first={i === 0} />
))}
);
const SessionRow = ({ t, session, onDelete, onOpen, first }) => {
const [hover, setHover] = React.useState(false);
if (t.id === 'editorial') {
return (
setHover(true)} onMouseLeave={() => setHover(false)}
style={{
display: 'grid', gridTemplateColumns: '80px 1fr auto', gap: 24, alignItems: 'baseline',
padding: '24px 0', borderTop: first ? '1px solid ' + t.borderStrong : 'none',
borderBottom: '1px solid ' + t.border, cursor: 'pointer',
}}
onClick={onOpen}>
{String(session.idx).padStart(2, '0')}
{session.title}
{session.when} · {session.minutes} min · {session.reps} reps
“{session.snippet}”
);
}
return (
setHover(true)} onMouseLeave={() => setHover(false)}
onClick={onOpen}
style={{
padding: '16px 18px', borderRadius: t.radiusLg,
background: hover ? t.surface : 'transparent',
border: '1px solid ' + (hover ? t.border : 'transparent'),
display: 'flex', alignItems: 'center', gap: 16, cursor: 'pointer',
transition: 'all .15s',
}}>
{String(session.idx).padStart(2, '0')}
{session.title}
{session.when}
·
{session.minutes} min
·
{session.reps} reps
"{session.snippet}"
);
};
// ─── Account ─────────────────────────────────────────────────
const AccountScreen = ({ t, email }) => {
return (
Settings
Account
Need help?
Reply to any Dateora email — a person reads them. Or write us at support@dateora.com.
);
};
const Section = ({ t, title, children }) => (
);
const Row = ({ t, label, value, action, actionDanger }) => (
);
Object.assign(window, { AppShell, ConversationScreen, HistoryScreen, AccountScreen, Bubble, TypingIndicator });