// Prodz — Shoutout detail. Reached by tapping a shoutout on a profile.
// · owner === 'me' → MINE: a summary card of the call, the people who applied
// as tappable avatars, and the chat you had for that role opening inline at
// the bottom (no page change).
// · owner === other → THEIRS: view the call, Save it, or Apply & reply inline.
function statusMeta(L, st) {
switch (st) {
case 'shortlist': return { label: L('In shortlist', 'Shortlisted'), color: 'var(--prodz-teal)' };
case 'hired': return { label: L('Scelto', 'Hired'), color: 'var(--green)' };
case 'closed': return { label: L('Chiusa', 'Closed'), color: 'var(--text-subtle)' };
default: return { label: L('Nuova', 'New'), color: 'var(--prodz-gold)' };
}
}
// Shared summary card at the top of the detail.
function ShoutoutSummary({ s, owner, L }) {
const { Avatar, Tag } = window.ProdzDesignSystem_d0b87b;
const mine = owner === 'me';
const open = mine ? s.status === 'open' : true;
const prod = s.prod ? PRODUCTIONS.find((x) => x.id === s.prod) : null;
const author = mine ? PEOPLE.me : (s.person ? PEOPLE[s.person] : null);
const authorName = author ? author.name : s.author;
const authorRole = author ? tr(L, author.role) : tr(L, s.role);
const authorCity = author ? author.city : s.city;
return (
{s.kind === 'job' ? L('Job Call', 'Job Call') : 'Collab'}
{s.paid && {L('Retribuito', 'Paid')}}
{mine && (
{open ? L('Aperta', 'Open') : L('Chiusa', 'Closed')}
)}
{tr(L, s.title)}
{/* author line — for someone else's call, who posted it */}
{!mine && (
{authorName}
{authorRole}{authorCity ? ' · ' + authorCity : ''}
)}
{s.blurb &&
{tr(L, s.blurb)}
}
{prod && {prod.title}}
{tr(L, s.date)}
{mine && {s.applicants} {L('candidature', 'applicants')}}
{s.tags.map((t) => {t})}
);
}
function ShoutoutDetail({ id, owner = 'me' }) {
const { L } = useL();
const nav = useNav();
const { Avatar, Button, Tag } = window.ProdzDesignSystem_d0b87b;
const mine = owner === 'me';
const s = (mine ? MY_SHOUTOUTS : shoutoutsOf(owner)).find((x) => x.id === id);
// ---- MINE: applicants + inline chat ----
const applicants = (s && s.applicantList) || [];
const [sel, setSel] = useStateS(null); // selected applicant person id
const [threads, setThreads] = useStateS(() => {
const o = {}; applicants.forEach((a) => { o[a.person] = a.messages.map((m) => ({ ...m })); }); return o;
});
const [draft, setDraft] = useStateS('');
const panelRef = useRefS(null);
const selApp = applicants.find((a) => a.person === sel);
useEffectS(() => {
if (sel && panelRef.current) {
const sc = panelRef.current.closest('.prodz-scroll');
if (sc) sc.scrollTo({ top: panelRef.current.offsetTop - 64, behavior: 'smooth' });
}
}, [sel]);
// ---- THEIRS: save / apply / reply ----
const [saved, setSaved] = useStateS(false);
const [applied, setApplied] = useStateS(false);
const [showApply, setShowApply] = useStateS(false);
const [reply, setReply] = useStateS('');
if (!s) {
return (
{/* ============ MINE: applicants + inline chat ============ */}
{mine && (
<>
{L('Candidature', 'Applicants')} · {applicants.length}
{applicants.length === 0 ? (
{L('Ancora nessuna candidatura.', 'No applications yet.')}
) : (
{applicants.map((a) => {
const p = PEOPLE[a.person]; if (!p) return null;
const active = a.person === sel;
const st = statusMeta(L, a.status);
return (
);
})}
)}
{applicants.length > 0 && !sel && (
{L('Tocca un candidato per leggere la chat che hai avuto per il ruolo.', 'Tap an applicant to read the chat you had for the role.')}
)}
{/* inline chat panel — opens below, page stays put */}
{selApp && (() => {
const p = PEOPLE[selApp.person];
const st = statusMeta(L, selApp.status);
const msgs = threads[selApp.person] || [];
return (
{/* chat header */}
{p.name}
{tr(L, p.role)} · {p.city}
{st.label}
{/* messages */}
{msgs.map((m, i) => (
))}
{/* quick actions */}
} onClick={() => nav.openProfile(selApp.person)}>{L('Profilo', 'Profile')}
{s.status === 'open' && } onClick={() => nav.toast(L(p.name.split(' ')[0] + ' aggiunto alla shortlist', p.name.split(' ')[0] + ' added to shortlist'), null)}>{L('Shortlist', 'Shortlist')}}
{/* composer */}
{s.status === 'open' ? (
setDraft(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); sendInChat(); } }}
placeholder={L('Rispondi a ' + p.name.split(' ')[0] + '…', 'Reply to ' + p.name.split(' ')[0] + '…')}
style={{ flex: 1, height: 40, padding: '0 14px', borderRadius: 'var(--radius-pill)', border: 'none', outline: 'none', background: 'var(--surface-card)', boxShadow: 'var(--ring-inset-hairline)', color: 'var(--text-body)', fontSize: 13.5, fontFamily: 'var(--font-ui)' }} />
) : (
{L('Questa call è chiusa.', 'This call is closed.')}
)}
);
})()}
>
)}
{/* ============ THEIRS: save / apply / reply ============ */}
{!mine && (
{!showApply && !applied && (
}
onClick={() => { setSaved((v) => !v); nav.toast(saved ? L('Rimosso dai salvati', 'Removed from saved') : L('Salvato nella coda', 'Saved to queue'), null); }}>
{saved ? L('Salvato', 'Saved') : L('Salva', 'Save')}
} onClick={() => setShowApply(true)}>
{s.kind === 'job' ? L('Candidati', 'Apply') : L('Proponi', 'Propose')}
)}
{showApply && (
{s.kind === 'job' ? L('La tua candidatura', 'Your application') : L('La tua proposta', 'Your proposal')}
)}
{applied && (
{L('Candidatura inviata', 'Application sent')}
{L('La conversazione è nella tua Inbox.', 'The conversation is in your Inbox.')}
)}
)}
);
}
Object.assign(window, { ShoutoutDetail, ShoutoutSummary });