From 6de0d9d1c06e6a08026ffadf586d0bdab5f69747 Mon Sep 17 00:00:00 2001 From: Harald Mack <39521902+MaHaWo@users.noreply.github.com> Date: Thu, 16 Jan 2025 09:06:13 +0100 Subject: [PATCH] Incorporate client suggestions for feedback page (#222) * adjust upper part of feedback page to proposed layout * reorganize layout and make legend collapseable again * add better styling of tabs and legend * add better colors, restyle legend and feedback page * change modal styling, add overall summary * add print button, correct some stylings * add scroll to bottom functionality * correct scrolling * make legend better * add background coloring * add report printing function * format report --------- Co-authored-by: Liam Keegan --- .../lib/components/ChildrenFeedback.svelte | 271 ++++++++++++------ frontend/src/lib/translations.ts | 29 +- frontend/src/locales/de.json | 265 ----------------- frontend/tailwind.config.ts | 8 + 4 files changed, 216 insertions(+), 357 deletions(-) delete mode 100644 frontend/src/locales/de.json diff --git a/frontend/src/lib/components/ChildrenFeedback.svelte b/frontend/src/lib/components/ChildrenFeedback.svelte index fc920268..9520f92d 100644 --- a/frontend/src/lib/components/ChildrenFeedback.svelte +++ b/frontend/src/lib/components/ChildrenFeedback.svelte @@ -48,7 +48,7 @@ let milestoneGroups = $state( {} as Record>, ); let sessionkeys = $state([] as number[]); -let showHistory = $state(false); +let showHistory = $state(true); let detailed = $state({}) as Record; let summary = $state({}) as Record; let answerSessions = $state({}) as Record; @@ -57,23 +57,29 @@ let showMoreInfo = $state(false); const intervalSize = 4; let currentSessionIndices = $state([0, intervalSize]); let relevant_sessionkeys = $state([] as number[]); -const milestonePresentation = [ +let milestonePresentation = $state([ { icon: CheckCircleSolid, - color: "green", text: i18n.tr.milestone.recommendOk, + short: i18n.tr.milestone.recommendOkShort, + class: "text-feedback-0 w-16", + showExplanation: false, }, { icon: ExclamationCircleSolid, - color: "orange", text: i18n.tr.milestone.recommendWatch, + short: i18n.tr.milestone.recommendWatchShort, + class: "text-feedback-1 w-16", + showExplanation: false, }, { icon: CloseCircleSolid, - color: "red", text: i18n.tr.milestone.recommmendHelp, + short: i18n.tr.milestone.recommendHelpShort, + class: "text-feedback-2 w-16 ", + showExplanation: false, }, -]; +]); const breadcrumbdata: any[] = [ { label: currentChild.name, @@ -204,6 +210,59 @@ async function loadNext() { await loadDetailedFeedback(relevant_sessionkeys); } +function generateReport(): string { + let report = ""; + // add title + report += `

${i18n.tr.milestone.reportTitle}

\n\n`; + // add today's date + report += `${i18n.tr.milestone.date}: ${new Date().toLocaleDateString()} \n\n`; + + // add name and age of child in the beginning + report += `${i18n.tr.milestone.child}: ${currentChild.name}\n`; + report += `${i18n.tr.milestone.born}: ${currentChild.month}/${currentChild.year} \n\n`; + + // iterate over all answersessions + + for (let [aid, values] of Object.entries(summary)) { + // aid : value + + const min = Math.min(...(Object.values(values) as number[])); + report += `

${i18n.tr.milestone.timeperiod}: ${makeTitle(Number(aid))}

\n`; + report += `${i18n.tr.milestone.summaryScore}: ${min === 1 ? i18n.tr.milestone.recommendOk : min === 0 ? i18n.tr.milestone.recommendWatch : min === -1 ? i18n.tr.milestone.recommmendHelp : i18n.tr.milestone.notEnoughDataYet} \n\n`; + + for (let [mid, score] of Object.entries(values)) { + // mid : score + report += `

${milestoneGroups[aid][Number(mid)].text[i18n.locale].title}

`; + report += ` ${score === 1 ? i18n.tr.milestone.recommendOkMs : score === 0 ? i18n.tr.milestone.recommendWatchMs : score === -1 ? i18n.tr.milestone.recommmendHelp : i18n.tr.milestone.notEnoughDataYet} \n\n`; + + for (let [ms_id, ms_score] of Object.entries(detailed[aid][mid])) { + // ms_id : ms_score + report += ` ${ + milestoneGroups[aid][Number(mid)].milestones.find((element: any) => { + return element.id === Number(ms_id); + }).text[i18n.locale].title + }:`; + report += ` ${ms_score === 1 ? i18n.tr.milestone.recommendOkShort : ms_score === 0 ? i18n.tr.milestone.recommendWatchShort : ms_score === -1 ? i18n.tr.milestone.recommendHelpShort : i18n.tr.milestone.notEnoughDataYet} \n`; + } + } + + report += "\n"; + } + + return report; +} + +function printReport() { + const report = generateReport(); + const printWindow = window.open("", "", "height=600,width=800"); + if (printWindow === null) { + return; + } + printWindow.document.write(`
${report}
`); + printWindow.document.close(); + printWindow.print(); +} + function formatDate(date: string): string { const dateObj = new Date(date); return [ @@ -219,6 +278,13 @@ function makeTitle(aid: number): string { : formatDate(answerSessions[aid].created_at); } +function scrollToBottom() { + window.scrollTo({ + top: document.body.scrollHeight * 0.35, + behavior: "instant", + }); +} + async function setup() { await loadAnswersessions(); if (Object.keys(answerSessions).length === 0) { @@ -231,73 +297,84 @@ async function setup() { let promise = $state(setup()); -{#snippet evaluation(aid: number, milestone_or_group: MilestonePublic | MilestoneGroupPublic | undefined, value: number, isMilestone: boolean, withText: boolean = false)} -
- {console.log(' aid: ', aid, milestone_or_group?.text[i18n.locale].title, value, isMilestone, withText)} +{#snippet summaryEvaluation(aid: number)} +
+ {#if Math.min(...(Object.values(summary[aid]) as number[])) === 1} + + {i18n.tr.milestone.summaryScore} + {i18n.tr.milestone.recommendOk} + {:else if Math.min(...(Object.values(summary[aid]) as number[])) === 0} + + {i18n.tr.milestone.summaryScore} + {i18n.tr.milestone.recommendWatch} + {:else if Math.min(...(Object.values(summary[aid]) as number[])) === -1} + + {i18n.tr.milestone.summaryScore} + {i18n.tr.milestone.recommmendHelp} + {:else} + + {i18n.tr.milestone.summaryScore} + {i18n.tr.milestone.notEnoughDataYet} + {/if} +
+
+ +{/snippet} + + +{#snippet evaluation( milestone_or_group: MilestonePublic | MilestoneGroupPublic | undefined, value: number, isMilestone: boolean,)} +
{#if value === 1} -
- - +
+ + {milestone_or_group?.text[i18n.locale].title}
- {#if withText} -

{i18n.tr.milestone.recommendOk}

- {/if}
{:else if value === 0} -
- - +
+ + {milestone_or_group?.text[i18n.locale].title}
- {#if withText} -

{i18n.tr.milestone.recommendWatch}

- {/if}
{#if isMilestone} - + {milestone_or_group?.text[i18n.locale].help} {/if} {:else if value === -1} -
- - +
+ + {milestone_or_group?.text[i18n.locale].title}
- {#if withText} -

{i18n.tr.milestone.recommmendHelp}

- {/if}
{#if isMilestone} - - + {milestone_or_group?.text[i18n.locale].help} {/if} {:else } -
+
- + {milestone_or_group?.text[i18n.locale].title}
- {#if withText} -

{i18n.tr.milestone.notEnoughDataYet}

- {/if}
{/if} @@ -319,79 +396,94 @@ let promise = $state(setup());
{:then} - {i18n.tr.milestone.feedbackTitle} + {i18n.tr.milestone.feedbackTitle} -
-

{i18n.tr.milestone.feedbackExplanation}

+
+

{i18n.tr.milestone.feedbackExplanation}

- + +

{i18n.tr.milestone.feedbackExplanationDetailed}

+

{i18n.tr.milestone.feedbackDetailsMilestoneGroup}

+

{i18n.tr.milestone.feedbackDetailsMilestone}

+
+ + + {i18n.tr.milestone.legend} +
+ {#each milestonePresentation as milestone} +
- -

{i18n.tr.milestone.feedbackDetailsMilestoneGroup}

-

{i18n.tr.milestone.feedbackDetailsMilestone}

-
+ - - - - - {i18n.tr.milestone.legend} - - -
- {#each milestonePresentation as milestone} -
- -

{milestone.text}

+ {milestone.short} + +
-
+ + {milestone.text} + {/each} -
-
-
+
+ + + +
+ +
+
- {i18n.tr.milestone.showHistory} -
-
+
+ +
-
- +

{i18n.tr.milestone.selectFeedback}

+ + {i18n.tr.milestone.showHistory} + +
+
+ + {#if showHistory === true} {/if} -
+
{#if relevant_sessionkeys.length=== 0}

{i18n.tr.milestone.noFeedback}

{:else} {#each relevant_sessionkeys as aid} {#if showHistory === true || aid === sessionkeys[0]} - - + + + {@render summaryEvaluation(aid)} + + {#each Object.entries(summary[aid]) as [mid, score]} - - - {@render evaluation(aid, milestoneGroups[aid][Number(mid)], score as number, false, false)} +
+ + + {@render evaluation(milestoneGroups[aid][Number(mid)], score as number, false)} -
- {#each Object.entries(detailed[aid][mid]) as [ms_id, ms_score]} - {@render evaluation( - aid, - milestoneGroups[aid][Number(mid)].milestones.find((element: any) => - { - return element.id === Number(ms_id); - }), - Number(ms_score), - true, true - )} -
- {/each} -
+ {#each Object.entries(detailed[aid][mid]) as [ms_id, ms_score]} + {@render evaluation( + milestoneGroups[aid][Number(mid)].milestones.find((element: any) => + { + return element.id === Number(ms_id); + }), + Number(ms_score), + true + )} +
+ {/each}
+
{/each}
@@ -399,13 +491,18 @@ let promise = $state(setup()); {/each} {/if}
+ {#if showHistory === true} {/if} -
+ +
+ +
{:catch error} Für alle wichtigen Bereiche der Entwicklung bis zum Schulalter haben wir eine Liste von Meilensteine zusammengestellt. Jeder Meilenstein steht für eine Kompetenz, die KInder typischerweise irgendwann zwischen 0 und 6 Jahren erreichen. Meilensteine können Sie ohne besondere Ausbildung oder Testmaterial im Alltag gut beobachten und gezielt fördern. Gerne können Sie sich die Liste der Meilensteine vorab anschauen oder herunterladen.

Wenn Sie sich anmelden, bitten wir Sie zunächst um einige Angaben zu Ihrer Person und zum beobachteten Kind. Diese Information benötigen wir ausschließlich, um ihre Daten richtig einordnen zu können und unsere Stichprobe in wissenschaftlichen Publikationen korrekt beschreiben zu können.

Gerne können Sie sich die Liste der Personenbezogenen Daten vorab anschauen oder herunterladen, nach denen wir Sie fragen werden.

Anschließend sollen Sie für jeden Entwicklungsbereich alle Meilensteine bewerten, indem Sie beurteilen, ob das Kind die beschriebene Kompetenz:
  • „noch nicht“,
  • „in Ansätzen“,
  • \n
  • „weitgehend“
  • oder „zuverlässig“ zeigt.


Für die Bewertung jedes Meilensteins gibt es zur Unterstützung abrufbare Beobachtungshinweise.

Bei der ersten Bestandsaufnahme werden Sie zunächst durch alle Meilensteine geführt. Je nach Alter des KIndes dauert das etwas länger. Wenn Sie die Entwicklung des gleichen KIndes später kontinuierlich weiter dokumentiere möchten, werden nur noch die Meilensteine abgefragt, die zuvor noch nicht als „zuverlässig gekonnt“ bewertet wurden.

Am Ende Ihrer Bestandsaufnahme erhalten Sie ein Ampel-Feedback. Wir geben Ihnen für jeden Entwicklungsbereich einzeln Rückmeldung, ob das Kind:
  • altergemäß entwickelt ist (Ampel grün),
  • ob einzelne Meilensteine verspätet sind (Ampel gelb),
  • oder ob in einem Bereich Anzeichen für eine deutlich verzögerte Entwicklung gibt (Ampel rot).
  • \n
\nDas Feedback basiert auf einem Vergleich mit einer großen Standard-Stichprobe. Auch Ihre Daten gehen in diese Vergleichsstichprobe ein. Sie wächst damit ständig und wird immer zuverlässiger.

Zusätzlich erhalten Sie auf Wunsch auch Meilenstein-bezogene Förder-Hinweise.

Wichtig ist, dass Sie Ihre Beobachtung in regelmäßigen Abständen vornehmen, um feststellen zu können, ob sich Änderungen am Entwicklungsstatus ergeben.
\nJedes Kind hat sein eigenes Tempo - vieles kann sich schnell ändern.

Mit MONDEY lernen Sie:
  • die Entwicklungsfortschritte von Kindern kontinuierlich genau zu beobachten
  • die Kinder beim Erwerb wichtiger neuer Kompetenzen gezielt zu unterstützen
  • den Entwicklungsstand eines Kindes einzuschätzen und ggf. rechtzeitig Unterstützung zu geben
Außerdem leisten Sie einen wichtigen Beitrag zur Entwicklungspsychologischen Grundlagenforschung!

", - "heading2": "Häufig gestellte Fragen", - "summary2": "Ein mit Eltern und Fachkräften entwickeltes, wissenschaftlich geprüftes Program zur Beobachtung und Dokumentation der Entwicklung von Kindern bis 6 Jahre. Sie bewerten, wie gut das Kind bestimmte Alltagshandlungen ausführen kann." - } -} diff --git a/frontend/tailwind.config.ts b/frontend/tailwind.config.ts index 2afd86b1..36c17f20 100644 --- a/frontend/tailwind.config.ts +++ b/frontend/tailwind.config.ts @@ -52,6 +52,14 @@ export default { 2: "#a7c957", 3: "#6a994e", }, + feedback: { + 0: "#98CB6A", + 1: "#FFCA67", + 2: "#E9715F", + }, + "feedback-background": { + 0: "#f3ddb0", + }, }, fontSize: { xs: ".75rem",