@@ -4,17 +4,18 @@ Prevention -- A research institute of the Ludwig Boltzmann Gesellschaft,
44Oesterreichische Vereinigung zur Foerderung der wissenschaftlichen Forschung).
55Licensed under the Elastic License 2.0. */
66<script setup lang="ts">
7- import { PropType , Ref , ref } from ' vue' ;
7+ import { computed , PropType , Ref , ref } from ' vue' ;
88 import { useComponentsApi , useObservationsApi } from ' ../composable/useApi' ;
99 import {
1010 ComponentFactory ,
1111 Event ,
12- Observation , ObservationGroup ,
12+ Observation ,
13+ ObservationGroup ,
1314 ObservationSchedule ,
1415 RelativeEvent ,
1516 StudyGroup ,
1617 StudyRole ,
17- StudyStatus
18+ StudyStatus ,
1819 } from ' @gs' ;
1920 import {
2021 MoreObservationListTableRow ,
@@ -24,7 +25,7 @@ Licensed under the Elastic License 2.0. */
2425 MoreTableFieldType ,
2526 MoreTableRowActionResult ,
2627 MoreTableSortOptions ,
27- RowSelectionMode
28+ RowSelectionMode ,
2829 } from ' ../models/MoreTableModel' ;
2930 import ConfirmDialog from ' primevue/confirmdialog' ;
3031 import DynamicDialog from ' primevue/dynamicdialog' ;
@@ -38,8 +39,10 @@ Licensed under the Elastic License 2.0. */
3839 import DeleteMoreTableRowDialog from ' ./dialog/DeleteMoreTableRowDialog.vue' ;
3940 import { ScheduleType } from ' ../models/Scheduler' ;
4041 import Button from ' primevue/button' ;
41- import Menu from ' primevue/menu' ;
4242 import { timeToHourMinuteString } from ' ../utils/dateUtils' ;
43+ import OverlayPanel from ' primevue/overlaypanel' ;
44+ import InputText from ' primevue/inputtext' ;
45+ import { extractCurrentLimeDomain } from ' ../utils/limeSurveyUtils' ;
4346
4447 const loader = useLoader ();
4548 const { observationsApi } = useObservationsApi ();
@@ -54,7 +57,10 @@ Licensed under the Elastic License 2.0. */
5457 studyId: { type: Number , required: true },
5558 studyGroups: { type: Array as PropType <Array <StudyGroup >>, required: true },
5659 studyStatus: { type: String as PropType <StudyStatus >, required: true },
57- observationGroups: { type: Array as PropType <Array <ObservationGroup >>, required: true }
60+ observationGroups: {
61+ type: Array as PropType <Array <ObservationGroup >>,
62+ required: true ,
63+ },
5864 });
5965
6066 const sortOptions: MoreTableSortOptions = {
@@ -79,13 +85,14 @@ Licensed under the Elastic License 2.0. */
7985 value: null ,
8086 } as MoreTableChoice );
8187
82- const observationGroupStatuses: MoreTableChoice [] = props .observationGroups .map (
83- (observationGroup ) =>
84- ({
85- label: observationGroup .title ,
86- value: observationGroup .observationGroupId ?.toString (),
87- }) as MoreTableChoice ,
88- );
88+ const observationGroupStatuses: MoreTableChoice [] =
89+ props .observationGroups .map (
90+ (observationGroup ) =>
91+ ({
92+ label: observationGroup .title ,
93+ value: observationGroup .observationGroupId ?.toString (),
94+ }) as MoreTableChoice ,
95+ );
8996
9097 async function getFactories(): Promise <ComponentFactory []> {
9198 return componentsApi
@@ -94,15 +101,20 @@ Licensed under the Elastic License 2.0. */
94101 }
95102
96103 const factories: ComponentFactory [] = await getFactories ();
97- const observationTypes: any [] = factories .map ((cf : ComponentFactory ) => ({
98- label: cf .title ? t (cf .title ) : ' ' ,
99- value: cf .componentId ,
100- command : (): void => {
101- openObservationDialog (t (' observation.dialog.header.create' ), {
102- type: cf .componentId ,
103- });
104- },
105- }));
104+ const observationTypes: any [] = factories
105+ .map ((cf : ComponentFactory ) => ({
106+ label: cf .title ? t (cf .title ) : ' ' ,
107+ value: cf .componentId ,
108+ description: cf .description
109+ ? t (cf .description , { link: extractCurrentLimeDomain () })
110+ : ' ' ,
111+ command : (): void => {
112+ openObservationDialog (t (' observation.dialog.header.create' ), {
113+ type: cf .componentId ,
114+ });
115+ },
116+ }))
117+ .sort ((a , b ) => a .label .localeCompare (b .label ));
106118
107119 const observationColumns: MoreTableColumn [] = [
108120 {
@@ -144,11 +156,11 @@ Licensed under the Elastic License 2.0. */
144156 arrayLabels: observationGroupStatuses ,
145157 editable: {
146158 enabled: actionsVisible ,
147- values: observationGroupStatuses
159+ values: observationGroupStatuses ,
148160 },
149161 sortable: true ,
150162 placeholder: t (' global.placeholder.noGroup' ),
151- columnWidth: ' 10vw'
163+ columnWidth: ' 10vw' ,
152164 },
153165 {
154166 field: ' hidden' ,
@@ -255,7 +267,9 @@ Licensed under the Elastic License 2.0. */
255267 }
256268
257269 function getObservationGroupItem(id : number ): MoreTableChoice | undefined {
258- return observationGroupStatuses ?.find ((groupStatus ) => groupStatus .value === id .toString ())
270+ return observationGroupStatuses ?.find (
271+ (groupStatus ) => groupStatus .value === id .toString (),
272+ );
259273 }
260274
261275 async function listObservations(): Promise <void > {
@@ -269,7 +283,9 @@ Licensed under the Elastic License 2.0. */
269283 studyGroupId: observation .studyGroupId ,
270284 observationGroupIds: observation .observationGroupIds ,
271285 observationGroupValues: observation .observationGroupIds ?.length
272- ? observation .observationGroupIds .map ((id : number ) => getObservationGroupItem (id ))
286+ ? observation .observationGroupIds .map ((id : number ) =>
287+ getObservationGroupItem (id ),
288+ )
273289 : [],
274290 title: observation .title ,
275291 purpose: observation .purpose ,
@@ -288,7 +304,7 @@ Licensed under the Elastic License 2.0. */
288304 hidden: observation .hidden ,
289305 noSchedule: observation .noSchedule ,
290306 hasRepetition: getScheduleHasRepetition (observation .schedule ),
291- reminder: observation .reminder
307+ reminder: observation .reminder ,
292308 };
293309 });
294310 })
@@ -377,16 +393,23 @@ Licensed under the Elastic License 2.0. */
377393 }
378394 }
379395
380- async function updateObservation(observation : MoreObservationListTableRow , fromDialog : boolean = false ): Promise <void > {
381- const {observationGroupValues, ... newObservation} = observation ;
396+ async function updateObservation(
397+ observation : MoreObservationListTableRow ,
398+ fromDialog : boolean = false ,
399+ ): Promise <void > {
400+ const { observationGroupValues, ... newObservation } = observation ;
382401
383402 await observationsApi
384403 .updateObservation (
385404 props .studyId ,
386405 newObservation .observationId as number ,
387406 {
388407 ... newObservation ,
389- observationGroupIds: fromDialog ? newObservation .observationGroupIds : observationGroupValues ?.map ((choice : MoreTableChoice ) => parseInt (choice .value as string ))
408+ observationGroupIds: fromDialog
409+ ? newObservation .observationGroupIds
410+ : observationGroupValues ?.map ((choice : MoreTableChoice ) =>
411+ parseInt (choice .value as string ),
412+ ),
390413 },
391414 )
392415 .then (listObservations )
@@ -488,9 +511,24 @@ Licensed under the Elastic License 2.0. */
488511
489512 listObservations ();
490513
491- const menu = ref ();
492- function toggleButtonMenu(event : MouseEvent ): void {
493- menu .value .toggle (event );
514+ const observationTypeOverlayPanel = ref < InstanceType <
515+ typeof OverlayPanel
516+ > | null > (null );
517+
518+ const observationTypeQuery = ref (' ' );
519+
520+ const filteredObservationTypes = computed (() => {
521+ const q = observationTypeQuery .value .trim ().toLowerCase ();
522+ if (! q ) return observationTypes ;
523+ return observationTypes .filter ((i ) => i .label .toLowerCase ().includes (q ));
524+ });
525+
526+ function openObservatinTypeOverlay(event : MouseEvent ): void {
527+ observationTypeOverlayPanel .value ?.toggle (event );
528+ }
529+ function selectObservationType(item : any ): any {
530+ item .command ();
531+ observationTypeOverlayPanel .value ?.hide ();
494532 }
495533 </script >
496534
@@ -521,11 +559,44 @@ Licensed under the Elastic License 2.0. */
521559 <Button
522560 type="button"
523561 :disabled =" isInEditMode ? true : ! actionsVisible "
524- @click =" toggleButtonMenu ($event )"
562+ @click =" openObservatinTypeOverlay ($event )"
525563 >{{ t('observation.observationList.action.add') }}
526564 <span class =" pi pi-angle-down ml-3" ></span
527565 ></Button >
528- <Menu ref="menu" :model =" observationTypes " :popup =" true " />
566+
567+ <OverlayPanel
568+ ref="observationTypeOverlayPanel"
569+ style =" width : 40vw ; min-width : 32rem "
570+ >
571+ <InputText
572+ v-model =" observationTypeQuery "
573+ placeholder="Search…"
574+ class="mb-3 w-full"
575+ />
576+
577+ <div class =" scrollbar-stable max-h-[38vh] overflow-y-auto" >
578+ <button
579+ v-for =" observationType in filteredObservationTypes"
580+ :key =" observationType.label"
581+ type =" button"
582+ class =" w-full px-3 py-2 text-left hover:bg-gray-50"
583+ @click =" selectObservationType(observationType)"
584+ >
585+ <div class =" font-medium" >{{ observationType.label }}</div >
586+ <div class =" text-sm opacity-70" >
587+ <!-- eslint-disable vue/no-v-html -->
588+ <span v-html =" observationType.description" />
589+ </div >
590+ </button >
591+
592+ <div
593+ v-if =" filteredObservationTypes.length === 0"
594+ class =" px-3 py-2 text-sm opacity-70"
595+ >
596+ {{ $t('studyCollaborator.placeholder.noResultsFound') }}
597+ </div >
598+ </div >
599+ </OverlayPanel >
529600 </div >
530601 </template >
531602 </MoreTable >
0 commit comments