// Cancer Types & Identification Guide — ported from main project
// (frontend/components/cancer-types-guide.tsx). Data lives in
// cancer_types_data.js so both apps stay in sync from one source.
//
// View states:
// • grid — searchable card grid (default, what user landed on)
// • detail — full reference for one cancer type (clicked "View Full Guide")
const _CG = window;
// Lucide-style line icons mapped from the data's icon name strings.
const _CGIcon = ({ d, size = 24 }) => (
);
const ICON_MAP = {
heart: ,
wind: ,
activity: ,
shield: ,
droplets: <>>,
dna: <>>,
brain: <>>,
eye: <>>,
bone: ,
baby: <>>,
pill: <>>,
syringe: <>>,
microscope: <>>,
stethoscope: <>>,
target: <>>,
radiation: <>>,
trendingup: <>>,
filetext: <>>,
bookopen: <>>,
users: <>>,
// Generic fallback so unknown icon names still render something
__default: ,
};
const _CGIconByName = ({ name, size = 24 }) => {
const d = ICON_MAP[(name || '').toLowerCase()] || ICON_MAP.__default;
return <_CGIcon d={d} size={size}/>;
};
// ─── Detail view ─────────────────────────────────────────────────────────────
const _CGDetail = ({ ct, onBack }) => {
const t = window.useLanguage().t;
const Section = ({ title, children }) => (
);
return (
<_CGIconByName name={ct.icon} size={28}/>
{t('Prevalence')}
{ct.prevalence}
{t('Survival Rate')}
{ct.survival_rate}
{ct.subtypes?.length > 0 && (
{ct.subtypes.map((s, i) => - {s}
)}
)}
{ct.risk_factors?.length > 0 && (
{ct.risk_factors.map((s, i) => {s})}
)}
{ct.early_symptoms?.length > 0 && (
{ct.early_symptoms.map((s, i) => - {s}
)}
)}
{ct.advanced_symptoms?.length > 0 && (
{ct.advanced_symptoms.map((s, i) => - {s}
)}
)}
{ct.screening_methods?.length > 0 && (
{ct.screening_methods.map((s, i) => - {s}
)}
)}
{ct.diagnostic_tests?.length > 0 && (
| {t('Test')} | {t('Purpose')} | {t('When')} |
{ct.diagnostic_tests.map((d, i) => (
| {d.test} | {d.purpose} | {d.when} |
))}
)}
{ct.biomarkers?.length > 0 && (
| {t('Marker')} | {t('Significance')} | {t('Actionable')} |
{ct.biomarkers.map((b, i) => (
| {b.marker} |
{b.significance} |
{b.actionable
? ● {t('Yes')}
: —} |
))}
)}
{ct.staging?.length > 0 && (
| {t('Stage')} | {t('Description')} | {t('Treatment Approach')} |
{ct.staging.map((s, i) => (
| {s.stage} | {s.description} | {s.treatment_approach} |
))}
)}
{ct.treatment_options?.length > 0 && (
{ct.treatment_options.map((s, i) => {s})}
)}
{ct.key_mutations?.length > 0 && (
{ct.key_mutations.map((s, i) => {s})}
)}
{ct.prevention?.length > 0 && (
{ct.prevention.map((s, i) => - {s}
)}
)}
);
};
// ─── Grid view ───────────────────────────────────────────────────────────────
const CancerGuide = () => {
const t = window.useLanguage().t;
const [q, setQ] = React.useState('');
const [active, setActive] = React.useState(null); // currently-detailed cancer object
const data = window.CANCER_TYPES || [];
const filtered = React.useMemo(() => {
if (!q.trim()) return data;
const needle = q.toLowerCase();
return data.filter(c => {
const haystack = [
c.name, c.category, c.overview,
...(c.key_mutations || []),
...(c.subtypes || []),
...(c.biomarkers || []).map(b => b.marker),
].join(' ').toLowerCase();
return haystack.includes(needle);
});
}, [q, data]);
if (active) return <_CGDetail ct={active} onBack={() => setActive(null)}/>;
return (
<_CGIconByName name="bookopen" size={26}/>
{t('Cancer Types & Identification Guide')}
{t('End-to-end reference: symptoms, diagnosis, biomarkers, staging, treatment')}
setQ(e.target.value)}
/>
{filtered.map(ct => (
setActive(ct)}>
<_CGIconByName name={ct.icon} size={22}/>
{ct.biomarkers?.length > 0 && (
{ct.biomarkers.length} {t('Biomarkers')}
)}
{ct.name}
{ct.category}
{ct.overview}
{t('Subtypes')}
{ct.subtypes?.length ?? 0}
{t('Key Genes')}
{ct.key_mutations?.length ?? 0}
))}
{filtered.length === 0 && (
{t('No matches.')}
)}
);
};
window.CancerGuide = CancerGuide;