@@ -48,7 +48,7 @@ let milestoneGroups = $state(
48
48
{} as Record <number , Record <number , MilestoneGroupPublic >>,
49
49
);
50
50
let sessionkeys = $state ([] as number []);
51
- let showHistory = $state (false );
51
+ let showHistory = $state (true );
52
52
let detailed = $state ({}) as Record <number , any >;
53
53
let summary = $state ({}) as Record <number , any >;
54
54
let answerSessions = $state ({}) as Record <number , MilestoneAnswerSessionPublic >;
@@ -57,23 +57,29 @@ let showMoreInfo = $state(false);
57
57
const intervalSize = 4 ;
58
58
let currentSessionIndices = $state ([0 , intervalSize ]);
59
59
let relevant_sessionkeys = $state ([] as number []);
60
- const milestonePresentation = [
60
+ let milestonePresentation = $state ( [
61
61
{
62
62
icon: CheckCircleSolid ,
63
- color: " green" ,
64
63
text: i18n .tr .milestone .recommendOk ,
64
+ short: i18n .tr .milestone .recommendOkShort ,
65
+ class: " text-feedback-0 w-16" ,
66
+ showExplanation: false ,
65
67
},
66
68
{
67
69
icon: ExclamationCircleSolid ,
68
- color: " orange" ,
69
70
text: i18n .tr .milestone .recommendWatch ,
71
+ short: i18n .tr .milestone .recommendWatchShort ,
72
+ class: " text-feedback-1 w-16" ,
73
+ showExplanation: false ,
70
74
},
71
75
{
72
76
icon: CloseCircleSolid ,
73
- color: " red" ,
74
77
text: i18n .tr .milestone .recommmendHelp ,
78
+ short: i18n .tr .milestone .recommendHelpShort ,
79
+ class: " text-feedback-2 w-16 " ,
80
+ showExplanation: false ,
75
81
},
76
- ];
82
+ ]) ;
77
83
const breadcrumbdata: any [] = [
78
84
{
79
85
label: currentChild .name ,
@@ -204,6 +210,59 @@ async function loadNext() {
204
210
await loadDetailedFeedback (relevant_sessionkeys );
205
211
}
206
212
213
+ function generateReport(): string {
214
+ let report = " " ;
215
+ // add title
216
+ report += ` <h1>${i18n .tr .milestone .reportTitle }</h1>\n\n ` ;
217
+ // add today's date
218
+ report += ` ${i18n .tr .milestone .date }: ${new Date ().toLocaleDateString ()} \n\n ` ;
219
+
220
+ // add name and age of child in the beginning
221
+ report += ` ${i18n .tr .milestone .child }: ${currentChild .name }\n ` ;
222
+ report += ` ${i18n .tr .milestone .born }: ${currentChild .month }/${currentChild .year } \n\n ` ;
223
+
224
+ // iterate over all answersessions
225
+
226
+ for (let [aid, values] of Object .entries (summary )) {
227
+ // aid : value
228
+
229
+ const min = Math .min (... (Object .values (values ) as number []));
230
+ report += ` <h2>${i18n .tr .milestone .timeperiod }: ${makeTitle (Number (aid ))}</h2> \n ` ;
231
+ report += ` <strong>${i18n .tr .milestone .summaryScore }:</strong> ${min === 1 ? i18n .tr .milestone .recommendOk : min === 0 ? i18n .tr .milestone .recommendWatch : min === - 1 ? i18n .tr .milestone .recommmendHelp : i18n .tr .milestone .notEnoughDataYet } \n\n ` ;
232
+
233
+ for (let [mid, score] of Object .entries (values )) {
234
+ // mid : score
235
+ report += ` <h3> ${milestoneGroups [aid ][Number (mid )].text [i18n .locale ].title }</h3> ` ;
236
+ 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 ` ;
237
+
238
+ for (let [ms_id, ms_score] of Object .entries (detailed [aid ][mid ])) {
239
+ // ms_id : ms_score
240
+ report += ` <strong>${
241
+ milestoneGroups [aid ][Number (mid )].milestones .find ((element : any ) => {
242
+ return element .id === Number (ms_id );
243
+ }).text [i18n .locale ].title
244
+ }:</strong> ` ;
245
+ 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 ` ;
246
+ }
247
+ }
248
+
249
+ report += " \n " ;
250
+ }
251
+
252
+ return report ;
253
+ }
254
+
255
+ function printReport() {
256
+ const report = generateReport ();
257
+ const printWindow = window .open (" " , " " , " height=600,width=800" );
258
+ if (printWindow === null ) {
259
+ return ;
260
+ }
261
+ printWindow .document .write (` <pre>${report }</pre> ` );
262
+ printWindow .document .close ();
263
+ printWindow .print ();
264
+ }
265
+
207
266
function formatDate(date : string ): string {
208
267
const dateObj = new Date (date );
209
268
return [
@@ -219,6 +278,13 @@ function makeTitle(aid: number): string {
219
278
: formatDate (answerSessions [aid ].created_at );
220
279
}
221
280
281
+ function scrollToBottom() {
282
+ window .scrollTo ({
283
+ top: document .body .scrollHeight * 0.35 ,
284
+ behavior: " instant" ,
285
+ });
286
+ }
287
+
222
288
async function setup() {
223
289
await loadAnswersessions ();
224
290
if (Object .keys (answerSessions ).length === 0 ) {
@@ -231,73 +297,84 @@ async function setup() {
231
297
let promise = $state (setup ());
232
298
</script >
233
299
234
- {#snippet evaluation (aid : number , milestone_or_group : MilestonePublic | MilestoneGroupPublic | undefined , value : number , isMilestone : boolean , withText : boolean = false )}
235
- <div class =" text-gray-700 dark:text-gray-400 space-x-2 space-y-4 p-2 m-2" >
236
- {console .log (' aid: ' , aid , milestone_or_group ?.text [i18n .locale ].title , value , isMilestone , withText )}
300
+ {#snippet summaryEvaluation (aid : number )}
301
+ <div class =" flex flex-col md:flex-row items-center justify-center w-full m-2 p-2 text-gray-700 dark:text-gray-400" >
302
+ {#if Math .min (... (Object .values (summary [aid ]) as number [])) === 1 }
303
+ <CheckCircleSolid size =" xl" class =" text-feedback-0 mr-2 pr-2" />
304
+ <span class ="font-bold mx-2 px-2 items-center justify-center" >{i18n .tr .milestone .summaryScore }</span >
305
+ {i18n .tr .milestone .recommendOk }
306
+ {:else if Math .min (... (Object .values (summary [aid ]) as number [])) === 0 }
307
+ <BellActiveSolid size =" xl" class =" text-feedback-1 mr-2 pr-2" />
308
+ <span class ="font-bold mx-2 px-2 items-center justify-center" >{i18n .tr .milestone .summaryScore }</span >
309
+ {i18n .tr .milestone .recommendWatch }
310
+ {:else if Math .min (... (Object .values (summary [aid ]) as number [])) === - 1 }
311
+ <CloseCircleSolid size =" xl" class =" text-feedback-2 mr-2 pr-2" />
312
+ <span class ="font-bold mx-2 px-2 items-center justify-center" > {i18n .tr .milestone .summaryScore }</span >
313
+ {i18n .tr .milestone .recommmendHelp }
314
+ {:else }
315
+ <CloseCircleSolid size =" xl" color = " gray" />
316
+ <span class ="font-bold mx-2 px-2 items-center justify-center" >{i18n .tr .milestone .summaryScore }</span >
317
+ {i18n .tr .milestone .notEnoughDataYet }
318
+ {/if }
319
+ </div >
320
+ <Hr classHr =" mx-2" />
321
+
322
+ {/ snippet }
323
+
324
+
325
+ {#snippet evaluation ( milestone_or_group : MilestonePublic | MilestoneGroupPublic | undefined , value : number , isMilestone : boolean ,)}
326
+ <div class ={` rounded-lg space-x-2 space-y-4 p-2 m-2 flex flex-col ${(value === 0 || value === - 1 ) && isMilestone === true ? " bg-feedback-background-0" : " " } ` }>
237
327
{#if value === 1 }
238
- <div class =" flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-2 items-center" >
239
- <CheckCircleSolid color = " green " size = " xl " />
240
- <span class = " text-gray-700 dark:text-gray-400 font-bold " >
328
+ <div class =" flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-2 items-center m-2 p-2 " >
329
+ <CheckCircleSolid size = " xl " class = " text-feedback-0 " />
330
+ <span class = {` font-bold ${ isMilestone ? " text-gray-700 dark:text-gray-400" : " " }`} >
241
331
{milestone_or_group ?.text [i18n .locale ].title }
242
332
</span >
243
333
<Hr class =" mx-2" />
244
- {#if withText }
245
- <p >{i18n .tr .milestone .recommendOk }</p >
246
- {/if }
247
334
</div >
248
335
{:else if value === 0 }
249
- <div class =" flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-2 items-center" >
250
- <BellActiveSolid color = " orange " size = " xl " />
251
- <span class = " text-gray-700 dark:text-gray-400 font-bold " >
336
+ <div class =" flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-2 items-center m-2 p-2 " >
337
+ <BellActiveSolid size = " xl " class = " text-feedback-1 " />
338
+ <span class = {` font-bold ${ isMilestone ? " text-gray-700 dark:text-gray-400" : " " }`} >
252
339
{milestone_or_group ?.text [i18n .locale ].title }
253
340
</span >
254
341
<Hr class =" mx-2" />
255
- {#if withText }
256
- <p >{i18n .tr .milestone .recommendWatch }</p >
257
- {/if }
258
342
</div >
259
343
{#if isMilestone }
260
344
<span class = " ml-auto mt-4" >
261
345
<Button id ="b1" onclick ={()=> {
262
346
showHelp = true ;
263
347
}}>{i18n .tr .milestone .help }</Button >
264
- <Modal title ={i18n .tr .milestone .help } bind:open ={showHelp } dismissable ={true }>
348
+ <Modal class = " m-2 p-2 " title ={i18n .tr .milestone .help } bind:open ={showHelp } dismissable ={true }>
265
349
{milestone_or_group ?.text [i18n .locale ].help }
266
350
</Modal >
267
351
</span >
268
352
{/if }
269
353
{:else if value === - 1 }
270
- <div class =" flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-2 items-center" >
271
- <CloseCircleSolid color = " red " size = " xl " />
272
- <span class = " text-gray-700 dark:text-gray-400 font-bold " >
354
+ <div class =" flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-2 items-center m-2 p-2 " >
355
+ <CloseCircleSolid size = " xl " class = " text-feedback-2 " />
356
+ <span class = {` font-bold ${ isMilestone ? " text-gray-700 dark:text-gray-400" : " " }`} >
273
357
{milestone_or_group ?.text [i18n .locale ].title }
274
358
</span >
275
359
<Hr class =" mx-2" />
276
- {#if withText }
277
- <p >{i18n .tr .milestone .recommmendHelp }</p >
278
- {/if }
279
360
</div >
280
361
{#if isMilestone }
281
362
<span class = " ml-auto mt-4" >
282
363
<Button id ="b1" onclick ={()=> {
283
364
showHelp = true ;
284
365
}}>{i18n .tr .milestone .help }</Button >
285
-
286
- <Modal title ={i18n .tr .milestone .help } bind:open ={showHelp } dismissable ={true }>
366
+ <Modal class = " m-2 p-2" title ={i18n .tr .milestone .help } bind:open ={showHelp } dismissable ={true }>
287
367
{milestone_or_group ?.text [i18n .locale ].help }
288
368
</Modal >
289
369
</span >
290
370
{/if }
291
371
{:else }
292
- <div class =" flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-2 items-center" >
372
+ <div class =" flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-2 items-center m-2 p-2 " >
293
373
<CloseCircleSolid color = " gray" size =" xl" />
294
- <span class = " text-gray-700 dark:text-gray-400 font-bold " >
374
+ <span class = {` font-bold ${ isMilestone ? " text-gray-700 dark:text-gray-400" : " " }`} >
295
375
{milestone_or_group ?.text [i18n .locale ].title }
296
376
</span >
297
377
<Hr class =" mx-2" />
298
- {#if withText }
299
- <p >{i18n .tr .milestone .notEnoughDataYet }</p >
300
- {/if }
301
378
</div >
302
379
303
380
{/if }
@@ -319,93 +396,113 @@ let promise = $state(setup());
319
396
</div >
320
397
{:then }
321
398
322
- <Heading tag ="h2" class = " text-gray-700 dark:text-gray-400 items-center p-2 m-2 pb-4" >{i18n .tr .milestone .feedbackTitle } </Heading >
399
+ <Heading tag ="h2" class = " text-gray-700 dark:text-gray-400 items-center p-2 m-2 pb-4" >{i18n .tr .milestone .feedbackTitle } </Heading >
323
400
324
- <div class =" m-2 p-2 pb-4 " >
325
- <p class ="m-2 p-2 pb-4 text-gray-700 dark:text-gray-400" >{i18n .tr .milestone .feedbackExplanation }</p >
401
+ <div class =" m-2 p-2 pb-4 " >
402
+ <p class ="m-2 p-2 pb-4 text-gray-700 dark:text-gray-400 font-medium text-sm md:text-md " >{i18n .tr .milestone .feedbackExplanation }</p >
326
403
327
- <Button class = " m-2 p-2 pb-4 mb-4 items-center justify-center md:w-1/4" onclick = {() => {
328
- showMoreInfo = true ;
329
- }}>{i18n .tr .milestone .moreInfoOnEval }</Button >
404
+ <Modal class = " m-2 p-2" classHeader ="flex justify-between items-center p-4 md:p-5 rounded-t-lg text-gray-700 dark:text-gray-400" title ={i18n .tr .milestone .info } bind:open ={showMoreInfo } dismissable ={true }>
405
+ <p class =" text-gray-700 dark:text-gray-400 font-medium text-sm md:text-md" >{i18n .tr .milestone .feedbackExplanationDetailed }</p >
406
+ <p class =" text-gray-700 dark:text-gray-400 font-medium text-sm md:text-md" >{i18n .tr .milestone .feedbackDetailsMilestoneGroup }</p >
407
+ <p class =" text-gray-700 dark:text-gray-400 font-medium text-sm md:text-md" >{i18n .tr .milestone .feedbackDetailsMilestone }</p >
408
+ </Modal >
330
409
410
+ <Accordion class =" p-2 m-2 w-full" >
411
+ <AccordionItem >
412
+ <span slot ="header" class ="text-gray-700 dark:text-gray-400" >{i18n .tr .milestone .legend }</span >
413
+ <div class =" w-full flex flex-col md:flex-row items-center justify-start" >
414
+ {#each milestonePresentation as milestone }
415
+ <div class =" text-gray-700 dark:text-gray-400 flex flex-col md:flex-row font-medium text-sm md:text-md items-center justify-start m-2 p-2" >
331
416
332
- <Modal classHeader ="flex justify-between items-center p-4 md:p-5 rounded-t-lg text-gray-700 dark:text-gray-400" title ={i18n .tr .milestone .info } bind:open ={showMoreInfo } dismissable ={true }>
333
- <p class =" text-gray-700 dark:text-gray-400" >{i18n .tr .milestone .feedbackDetailsMilestoneGroup }</p >
334
- <p class =" text-gray-700 dark:text-gray-400" >{i18n .tr .milestone .feedbackDetailsMilestone }</p >
335
- </Modal >
417
+ <svelte:component this ={milestone .icon } size ="xl" class ={milestone .class } />
336
418
337
- <Accordion >
338
- <AccordionItem >
339
- <span slot =" header" class =" text-gray-700 dark:text-gray-400 flex items-center justify-center" >
340
- <span class =" font-bold" >
341
- {i18n .tr .milestone .legend }
342
- </span >
343
- </span >
344
- <div class =" flex flex-col text-gray-700 dark:text-gray-400 items-start p-2 m-2 space-y-6 justify-center" >
345
- {#each milestonePresentation as milestone }
346
- <div class =" mx-2 px-2 w-full flex flex-row items-center" >
347
- <svelte:component this ={milestone .icon } color ={milestone .color } size =" xl" class =" mx-2" />
348
- <p >{milestone .text }</p >
419
+ <span class ="font-bold justify-center mr-auto pr-auto" >{milestone .short }</span >
420
+
421
+ <Button class ="m-2 p-2 md:w-24 justify-center" onclick ={() => {milestone .showExplanation = true ;}}>{i18n .tr .milestone .moreInfoOnLegend }</Button >
349
422
</div >
350
- <Hr classHr =" mx-2 px-2 items-end w-full" />
423
+ <Modal class = " m-2 p-2" classHeader ="flex justify-between items-center p-4 md:p-5 rounded-t-lg text-gray-700 dark:text-gray-400" bind:open ={milestone .showExplanation } dismissable ={true } title ={milestone .short }>
424
+ {milestone .text }
425
+ </Modal >
351
426
{/each }
352
- </div >
353
- </AccordionItem >
354
- </Accordion >
427
+ </div >
428
+ </AccordionItem >
429
+ </Accordion >
430
+
431
+ <div class =" flex items-center justify-start w-full m-2 p-2" >
432
+ <Button class = " m-2 p-2 pb-4 mb-4 items-center justify-center md:w-1/6 w-5/6" onclick = {() => {
433
+ showMoreInfo = true ;
434
+ }}>{i18n .tr .milestone .moreInfoOnEval }
435
+ </Button >
436
+ </div >
437
+ </div >
355
438
356
- <Checkbox class = " pb-4 m-2 p-2 text-gray-700 dark:text-gray-400 " bind:checked ={ showHistory } >{ i18n . tr . milestone . showHistory }</ Checkbox >
357
- < Hr classHr = " mx-2 " />
358
- </ div >
439
+ <Hr classHr = " w-full mx-2 " / >
440
+
441
+ < div class = " m-2 p-2 pb-4 " >
359
442
360
- <div class =" m-2 p-2 pb-4 w-full" >
361
- <Tabs tabStyle =" underline" class =" items-center flex flex-wrap" >
443
+ <p class = " justify-center font-bold m-2 p-2 text-gray-700 dark:text-gray-400" >{i18n .tr .milestone .selectFeedback }</p >
444
+
445
+ <Checkbox class = " pb-4 m-2 p-2 text-gray-700 dark:text-gray-400" bind:checked ={showHistory } >{i18n .tr .milestone .showHistory }</Checkbox >
446
+
447
+ <Hr classHr = " mx-2" />
448
+ </div >
449
+
450
+ <Tabs defaultClass =" m-2 p-2 pb-4 items-center flex flex-wrap justify-between w-full text-gray-700 dark:text-gray-400" >
362
451
{#if showHistory === true }
363
452
<Button size ="md" type ="button" class ="md:w-16 md:h-8" on:click ={() => {
364
453
promise = loadLast ();
454
+ scrollToBottom ();
365
455
}}><ArrowLeftOutline class =" w-4 h-4" /></Button >
366
456
{/if }
367
- <div class =" flex flex-col md:flex-row" >
457
+ <div class =" flex flex-col md:flex-row justify-between " >
368
458
{#if relevant_sessionkeys .length === 0 }
369
459
<p class ="m-2 p-2 pb-4 text-gray-700 dark:text-gray-400" >{i18n .tr .milestone .noFeedback }</p >
370
460
{:else }
371
461
{#each relevant_sessionkeys as aid }
372
462
{#if showHistory === true || aid === sessionkeys [0 ]}
373
- <TabItem defaultClass ="font-bold text-gray-700 dark:text-gray-400 m-2 p-2" title ={makeTitle (aid )} open ={aid === relevant_sessionkeys [0 ] }>
374
- <Accordion class =" p-2 m-2" >
463
+ <TabItem defaultClass ="font-bold m-2 p-2" title ={makeTitle (aid )} open ={aid === relevant_sessionkeys [0 ] }>
464
+
465
+ {@render summaryEvaluation (aid )}
466
+
467
+ <Accordion class =" p-2 m-2 grid grid-cols-1 md:grid-cols-3 gap-4" >
375
468
{#each Object .entries (summary [aid ]) as [mid, score]}
376
- <AccordionItem >
377
- <span slot =" header" class =" text-gray-700 dark:text-gray-400 items-center flex justify-center space-x-2" >
378
- {@render evaluation (aid , milestoneGroups [aid ][Number (mid )], score as number , false , false )}
469
+ <div class =" flex flex-col" >
470
+ <AccordionItem activeClass =" flex flex-col m-2 rounded-xl text-white dark:text-white bg-primary-700 dark:bg-primary-700 hover:bg-primary-600 dark:hover:bg-primary-600 items-center justify-between w-full font-medium text-left" inactiveClass =" flex flex-col rounded-xl text-white dark:text-white bg-primary-800 dark:bg-primary-800 hover:bg-primary-700 dark:hover:bg-primary-700 items-center justify-between w-full font-medium text-left m-2" >
471
+ <span slot =" header" class =" items-center flex justify-center space-x-2" >
472
+ {@render evaluation (milestoneGroups [aid ][Number (mid )], score as number , false )}
379
473
</span >
380
- <div class =" flex-row justify-between" >
381
- {#each Object .entries (detailed [aid ][mid ]) as [ms_id, ms_score]}
382
- {@render evaluation (
383
- aid ,
384
- milestoneGroups [aid ][Number (mid )].milestones .find ((element : any ) =>
385
- {
386
- return element .id === Number (ms_id );
387
- }),
388
- Number (ms_score ),
389
- true , true
390
- )}
391
- <Hr classHr =" mx-2" />
392
- {/each }
393
- </div >
474
+ {#each Object .entries (detailed [aid ][mid ]) as [ms_id, ms_score]}
475
+ {@render evaluation (
476
+ milestoneGroups [aid ][Number (mid )].milestones .find ((element : any ) =>
477
+ {
478
+ return element .id === Number (ms_id );
479
+ }),
480
+ Number (ms_score ),
481
+ true
482
+ )}
483
+ <Hr classHr =" mx-2" />
484
+ {/each }
394
485
</AccordionItem >
486
+ </div >
395
487
{/each }
396
488
</Accordion >
397
489
</TabItem >
398
490
{/if }
399
491
{/each }
400
492
{/if }
401
493
</div >
494
+
402
495
{#if showHistory === true }
403
496
<Button size ="md" type ="button" class ="md:w-16 md:h-8" on:click ={() => {
404
497
promise = loadNext ();
498
+ scrollToBottom ();
405
499
}}><ArrowRightOutline class =" w-4 h-4" /></Button >
406
500
{/if }
407
501
</Tabs >
408
- </div >
502
+
503
+ <div class =" flex items-center justify-start w-full m-2 p-2 mb-4 pb-4" >
504
+ <Button class ="md:w-64 md:h-8 m-2 p-2" onclick ={printReport }>{i18n .tr .milestone .printReport }</Button >
505
+ </div >
409
506
{:catch error }
410
507
<AlertMessage
411
508
message = {`${alertMessage } ${error }`}
0 commit comments