Skip to content

Commit

Permalink
Persist marked state and notes, results table
Browse files Browse the repository at this point in the history
  • Loading branch information
marlonbaeten committed May 27, 2024
1 parent b62ff89 commit f004fc2
Show file tree
Hide file tree
Showing 11 changed files with 477 additions and 90 deletions.
14 changes: 9 additions & 5 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import Results from './Results';
import Table from './Table';
import usePersistence from './usePersistence';

export default function App(): JSX.Element {
const { state, select } = usePersistence();
export default function App(): JSX.Element {
const { state, select, mark, note } = usePersistence();

return (
<div>
<>
<h1>Ervaringskompas</h1>
<p>
Welke ervaring heb je opgedaan met deze beroepsactiviteit in de praktijk
Expand All @@ -19,7 +20,10 @@ export default function App(): JSX.Element {
))}
</ol>
</div>
<Table state={state} select={select} />
</div>
<hr />
<Table state={state} select={select} mark={mark} />
<hr />
<Results state={state} mark={mark} note={note} />
</>
);
}
51 changes: 51 additions & 0 deletions src/CompetenceResult.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { MAX_LEVEL, State } from './usePersistence';

export default function CompetenceResult({
state,
}: {
state: State;
}): JSX.Element {
const results = state.competences.map((competence, index) => {
const [score, max] = state.themes.reduce(
(acc, theme) => {
const [score, max] = theme.experiences.reduce(
(acc, exp) => {
return [
acc[0] + exp.competences[index] * exp.level,
acc[1] + exp.competences[index] * MAX_LEVEL,
];
},
[0, 0]
);
return [acc[0] + score, acc[1] + max];
},
[0, 0]
);

return {
competence,
percentage: Math.round((score / max) * 100),
};
});

return (
<table className="result">
<tbody>
{results.map(({ competence, percentage }) => (
<tr key={competence}>
<th>{competence}</th>
<td>{percentage}%</td>
<td className="progress">
<div
className="bar"
style={{
width: `${percentage}%`,
}}
></div>
</td>
</tr>
))}
</tbody>
</table>
);
}
24 changes: 20 additions & 4 deletions src/ExperienceRow.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
interface ExperienceRowProps {
name: string;
themeIndex: number,
themeIndex: number;
index: number;
color: string;
levels: string[];
selected: number;
marked: boolean;
onMark: (marked: boolean) => void;
onSelect: (level: number) => void;
}

export default function ExperienceRow({ name, themeIndex, index, color, levels, selected, onSelect }: ExperienceRowProps): JSX.Element {
export default function ExperienceRow({
name,
themeIndex,
index,
color,
levels,
marked,
selected,
onMark,
onSelect,
}: ExperienceRowProps): JSX.Element {
return (
<tr className="selectable">
<td style={{ borderBottomColor: color, borderLeftColor: color }}>
Expand All @@ -17,6 +29,8 @@ export default function ExperienceRow({ name, themeIndex, index, color, levels,
type="checkbox"
id={`check-${themeIndex}-${index}`}
name={`check-${themeIndex}-${index}`}
checked={marked}
onChange={(e) => onMark(e.target.checked)}
/>
</label>
</td>
Expand All @@ -33,8 +47,10 @@ export default function ExperienceRow({ name, themeIndex, index, color, levels,
key={level}
style={{
borderBottomColor: color,
borderRightColor: levelIndex === levels.length - 1 ? color : undefined
}}>
borderRightColor:
levelIndex === levels.length - 1 ? color : undefined,
}}
>
<label htmlFor={key} title={level}>
<input
type="radio"
Expand Down
37 changes: 37 additions & 0 deletions src/Marked.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Mark, State } from './usePersistence';

interface MarkedProps {
state: State;
mark: Mark;
}

export default function Marked({ state, mark }: MarkedProps): JSX.Element {
const items = state.themes
.map((theme, themeIndex) =>
theme.experiences.map((experience, index) => {
if (!experience.marked) {
return null;
}

return (
<li key={experience.name}>
<input
type="checkbox"
name={`check-${index}`}
checked={true}
onChange={() => mark(themeIndex, index, false)}
/>
{experience.name}
</li>
);
})
)
.flat()
.filter((item) => item !== null);

if (items.length === 0) {
return <p>Nog geen gemarkeerde ervaringen</p>;
}

return <ul>{items}</ul>;
}
44 changes: 44 additions & 0 deletions src/Results.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import CompetenceResult from './CompetenceResult';
import Marked from './Marked';
import ThemeResult from './ThemeResult';
import { Mark, State } from './usePersistence';

interface ResultProps {
state: State;
mark: Mark;
note: (content: string) => void;
}

export default function Results({
state,
mark,
note,
}: ResultProps): JSX.Element {
return (
<>
<h2>Ervaring</h2>
<h3>Per thema/lijn</h3>
<div className="result">
<ThemeResult state={state} />
</div>
<h3>Per competentiegebied</h3>
<div className="result">
<CompetenceResult state={state} />
</div>
<p>
De score zegt iets over hoe vaak je praktijkervaring hebt opgedaan in
betreffende thema/lijn en competentiegebied, niet over je niveau van
competentie.
</p>
<hr />
<h2>Remarkeerde ervaringen</h2>
<Marked state={state} mark={mark} />
<hr />
<h2>Notities</h2>
<textarea
value={state.notes}
onChange={(event) => note(event.target.value)}
/>
</>
);
}
55 changes: 33 additions & 22 deletions src/Table.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,39 @@
import ThemeSection from './ThemeSection';
import { PersistenceState } from './usePersistence';
import { Mark, Select, State } from './usePersistence';

export default function Table({ state, select }: PersistenceState): JSX.Element {
interface TableProps {
state: State;
mark: Mark;
select: Select;
}

export default function Table({
state,
select,
mark,
}: TableProps): JSX.Element {
return (
<table>
<thead>
<tr>
<th colSpan={2} style={{ border: 'none' }}></th>
{state.levels.map((level, levelIndex) => (
<th key={level}>{levelIndex + 1}</th>
))}
</tr>
</thead>
<tbody>
{state.themes.map((theme, index) => (
<ThemeSection
key={theme.name}
theme={theme}
index={index}
levels={state.levels}
select={select}
/>
<table className="form">
<thead>
<tr>
<th colSpan={2} style={{ border: 'none' }}></th>
{state.levels.map((level, levelIndex) => (
<th key={level}>{levelIndex + 1}</th>
))}
</tbody>
</table>
</tr>
</thead>
<tbody>
{state.themes.map((theme, index) => (
<ThemeSection
key={theme.name}
theme={theme}
index={index}
levels={state.levels}
select={select}
mark={mark}
/>
))}
</tbody>
</table>
);
}
56 changes: 56 additions & 0 deletions src/ThemeResult.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { MAX_LEVEL, State } from './usePersistence';

export default function ThemeResult({ state }: { state: State }): JSX.Element {
let totalScore = 0;
let totalMax = 0;

const results = state.themes.map((theme) => {
const max = theme.experiences.length * MAX_LEVEL;
const score = theme.experiences.reduce((acc, exp) => acc + exp.level, 0);

totalScore += score;
totalMax += max;

return {
theme: theme.name,
color: theme.color,
percentage: Math.round((score / max) * 100),
};
});

const totalPercentage = Math.round((totalScore / totalMax) * 100);

return (
<table className="result">
<tbody>
{results.map(({ theme, color, percentage }) => (
<tr key={theme}>
<th>{theme}</th>
<td>{percentage}%</td>
<td className="progress">
<div
className="bar"
style={{
backgroundColor: color,
width: `${percentage}%`,
}}
></div>
</td>
</tr>
))}
<tr>
<th>Totaal</th>
<td>{totalPercentage}%</td>
<td className="progress">
<div
className="bar"
style={{
width: `${totalPercentage}%`,
}}
></div>
</td>
</tr>
</tbody>
</table>
);
}
12 changes: 8 additions & 4 deletions src/ThemeSection.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { Fragment } from 'react';
import { Theme } from './usePersistence';
import { Mark, Select, Theme } from './usePersistence';
import ExperienceRow from './ExperienceRow';

interface ThemeProps {
theme: Theme;
index: number,
levels: string[],
select: (theme: number, item: number, level: number) => void;
index: number;
levels: string[];
select: Select;
mark: Mark;
}

export default function ThemeSection({
theme: { name, color, experiences },
index: themeIndex,
levels,
select,
mark,
}: ThemeProps): JSX.Element {
return (
<Fragment>
Expand All @@ -40,6 +42,8 @@ export default function ThemeSection({
levels={levels}
color={color}
selected={experience.level}
marked={experience.marked}
onMark={(marked: boolean) => mark(themeIndex, index, marked)}
onSelect={(level: number) => select(themeIndex, index, level)}
/>
))}
Expand Down
Loading

0 comments on commit f004fc2

Please sign in to comment.