feat: SimpleProgram no longer relies on types
ref: N25B-399
This commit is contained in:
@@ -2,41 +2,6 @@ import React from "react";
|
|||||||
import styles from "./SimpleProgram.module.css";
|
import styles from "./SimpleProgram.module.css";
|
||||||
import useProgramStore from "../../utils/programStore.ts";
|
import useProgramStore from "../../utils/programStore.ts";
|
||||||
|
|
||||||
/* ---------- Types ---------- */
|
|
||||||
|
|
||||||
type Norm = {
|
|
||||||
id: string;
|
|
||||||
label: string;
|
|
||||||
norm: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Goal = {
|
|
||||||
id: string;
|
|
||||||
label: string;
|
|
||||||
description: string;
|
|
||||||
achieved: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type TriggerKeyword = {
|
|
||||||
id: string;
|
|
||||||
keyword: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type KeywordTrigger = {
|
|
||||||
id: string;
|
|
||||||
label: string;
|
|
||||||
type: string;
|
|
||||||
keywords: TriggerKeyword[];
|
|
||||||
};
|
|
||||||
|
|
||||||
type Phase = {
|
|
||||||
id: string;
|
|
||||||
label: string;
|
|
||||||
norms: Norm[];
|
|
||||||
goals: Goal[];
|
|
||||||
triggers: KeywordTrigger[];
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ---------- Reusable UI ---------- */
|
/* ---------- Reusable UI ---------- */
|
||||||
|
|
||||||
type BoxProps = {
|
type BoxProps = {
|
||||||
@@ -53,70 +18,111 @@ const Box: React.FC<BoxProps> = ({ title, children }) => (
|
|||||||
|
|
||||||
/* ---------- Lists ---------- */
|
/* ---------- Lists ---------- */
|
||||||
|
|
||||||
const GoalList: React.FC<{ goals: Goal[] }> = ({ goals }) => {
|
const GoalList: React.FC<{ goals: unknown[] }> = ({ goals }) => {
|
||||||
if (goals.length === 0) return <p className={styles.empty}>No goals defined.</p>;
|
if (!goals.length) {
|
||||||
|
return <p className={styles.empty}>No goals defined.</p>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul className={styles.iconList}>
|
<ul className={styles.iconList}>
|
||||||
{goals.map((goal) => (
|
{goals.map((g, idx) => {
|
||||||
<li key={goal.id}>
|
const goal = g as {
|
||||||
<span className={goal.achieved ? styles.successIcon : styles.failIcon}>
|
id?: string;
|
||||||
{goal.achieved ? "✔" : "✖"}
|
description?: string;
|
||||||
</span>
|
achieved?: boolean;
|
||||||
{goal.description}
|
};
|
||||||
</li>
|
|
||||||
))}
|
return (
|
||||||
|
<li key={goal.id ?? idx}>
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
goal.achieved ? styles.successIcon : styles.failIcon
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{goal.achieved ? "✔" : "✖"}
|
||||||
|
</span>
|
||||||
|
{goal.description ?? "Unnamed goal"}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const TriggerList: React.FC<{ triggers: KeywordTrigger[] }> = ({ triggers }) => {
|
const TriggerList: React.FC<{ triggers: unknown[] }> = ({ triggers }) => {
|
||||||
if (triggers.length === 0) return <p className={styles.empty}>No triggers defined.</p>;
|
if (!triggers.length) {
|
||||||
|
return <p className={styles.empty}>No triggers defined.</p>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul className={styles.iconList}>
|
<ul className={styles.iconList}>
|
||||||
{triggers.map((trigger) => (
|
{triggers.map((t, idx) => {
|
||||||
<li key={trigger.id}>
|
const trigger = t as {
|
||||||
<span className={styles.failIcon}>✖</span>
|
id?: string;
|
||||||
{trigger.label}
|
label?: string;
|
||||||
</li>
|
};
|
||||||
))}
|
|
||||||
|
return (
|
||||||
|
<li key={trigger.id ?? idx}>
|
||||||
|
<span className={styles.failIcon}>✖</span>
|
||||||
|
{trigger.label ?? "Unnamed trigger"}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const NormList: React.FC<{ norms: Norm[] }> = ({ norms }) => {
|
const NormList: React.FC<{ norms: unknown[] }> = ({ norms }) => {
|
||||||
if (norms.length === 0) return <p className={styles.empty}>No norms defined.</p>;
|
if (!norms.length) {
|
||||||
|
return <p className={styles.empty}>No norms defined.</p>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul className={styles.bulletList}>
|
<ul className={styles.bulletList}>
|
||||||
{norms.map((norm) => (
|
{norms.map((n, idx) => {
|
||||||
<li key={norm.id}>{norm.norm}</li>
|
const norm = n as {
|
||||||
))}
|
id?: string;
|
||||||
|
norm?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
return <li key={norm.id ?? idx}>{norm.norm ?? "Unnamed norm"}</li>;
|
||||||
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ---------- Phase Grid ---------- */
|
/* ---------- Phase Grid ---------- */
|
||||||
|
|
||||||
const PhaseGrid: React.FC<{ phase: Phase }> = ({ phase }) => {
|
type PhaseGridProps = {
|
||||||
|
norms: unknown[];
|
||||||
|
goals: unknown[];
|
||||||
|
triggers: unknown[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const PhaseGrid: React.FC<PhaseGridProps> = ({
|
||||||
|
norms,
|
||||||
|
goals,
|
||||||
|
triggers,
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className={styles.phaseGrid}>
|
<div className={styles.phaseGrid}>
|
||||||
<Box title="Norms">
|
<Box title="Norms">
|
||||||
<NormList norms={phase.norms} />
|
<NormList norms={norms} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box title="Triggers">
|
<Box title="Triggers">
|
||||||
<TriggerList triggers={phase.triggers} />
|
<TriggerList triggers={triggers} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box title="Goals">
|
<Box title="Goals">
|
||||||
<GoalList goals={phase.goals} />
|
<GoalList goals={goals} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box title="Conditional Norms">
|
<Box title="Conditional Norms">
|
||||||
<p className={styles.empty}>No conditional norms defined.</p>
|
<p className={styles.empty}>No conditional norms defined.</p>
|
||||||
</Box>
|
</Box>
|
||||||
|
{/* Let er dus op dat deze erbij moeten */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -124,35 +130,50 @@ const PhaseGrid: React.FC<{ phase: Phase }> = ({ phase }) => {
|
|||||||
/* ---------- Main Component ---------- */
|
/* ---------- Main Component ---------- */
|
||||||
|
|
||||||
const SimpleProgram: React.FC = () => {
|
const SimpleProgram: React.FC = () => {
|
||||||
// Get the phases from the program store
|
const getPhaseIds = useProgramStore((s) => s.getPhaseIds);
|
||||||
const phases = useProgramStore((state) => state.currentProgram.phases) as Phase[];
|
const getNormsInPhase = useProgramStore((s) => s.getNormsInPhase);
|
||||||
|
const getGoalsInPhase = useProgramStore((s) => s.getGoalsInPhase);
|
||||||
|
const getTriggersInPhase = useProgramStore((s) => s.getTriggersInPhase);
|
||||||
|
|
||||||
|
const phaseIds = getPhaseIds();
|
||||||
const [phaseIndex, setPhaseIndex] = React.useState(0);
|
const [phaseIndex, setPhaseIndex] = React.useState(0);
|
||||||
|
|
||||||
// If no phases are available, display a message
|
if (phaseIds.length === 0) {
|
||||||
if (phases.length === 0) return <p className={styles.empty}>No program loaded.</p>;
|
return <p className={styles.empty}>No program loaded.</p>;
|
||||||
|
}
|
||||||
|
|
||||||
const phase = phases[phaseIndex];
|
const phaseId = phaseIds[phaseIndex];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<header className={styles.header}>
|
<header className={styles.header}>
|
||||||
<h2>
|
<h2>
|
||||||
Phase {phaseIndex + 1} / {phases.length}: {phase.label}
|
Phase {phaseIndex + 1} / {phaseIds.length}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div className={styles.controls}>
|
<div className={styles.controls}>
|
||||||
<button disabled={phaseIndex === 0} onClick={() => setPhaseIndex(i => i - 1)}>
|
<button
|
||||||
|
disabled={phaseIndex === 0}
|
||||||
|
onClick={() => setPhaseIndex((i) => i - 1)}
|
||||||
|
>
|
||||||
◀ Prev
|
◀ Prev
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button disabled={phaseIndex === phases.length - 1} onClick={() => setPhaseIndex(i => i + 1)}>
|
<button
|
||||||
|
disabled={phaseIndex === phaseIds.length - 1}
|
||||||
|
onClick={() => setPhaseIndex((i) => i + 1)}
|
||||||
|
>
|
||||||
Next ▶
|
Next ▶
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<PhaseGrid phase={phase} />
|
<PhaseGrid
|
||||||
|
norms={getNormsInPhase(phaseId)}
|
||||||
|
goals={getGoalsInPhase(phaseId)}
|
||||||
|
triggers={getTriggersInPhase(phaseId)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user