@gen-epix/ui-client-casedb 4.0.2 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/_chunks/AddCasesToEventDialog.js.map +1 -1
  2. package/dist/_chunks/CaseDbDownloadUtil.js +1 -1
  3. package/dist/_chunks/CaseDbDownloadUtil.js.map +1 -1
  4. package/dist/_chunks/CaseInfoDialog.js +4 -4
  5. package/dist/_chunks/CaseInfoDialog.js.map +1 -1
  6. package/dist/_chunks/CaseSetInfoDialog.js +4 -4
  7. package/dist/_chunks/CaseSetInfoDialog.js.map +1 -1
  8. package/dist/_chunks/CasesAlreadyInCaseSetWarning.js.map +1 -1
  9. package/dist/_chunks/Dashboard.js +3 -2
  10. package/dist/_chunks/Dashboard.js.map +1 -1
  11. package/dist/_chunks/FindSimilarCasesDialog.js +6 -6
  12. package/dist/_chunks/FindSimilarCasesDialog.js.map +1 -1
  13. package/dist/_chunks/HomePageTrends.js.map +1 -1
  14. package/dist/_chunks/LineListWidget.js +46 -45
  15. package/dist/_chunks/LineListWidget.js.map +1 -1
  16. package/dist/_chunks/Upload.js +308 -304
  17. package/dist/_chunks/Upload.js.map +1 -1
  18. package/dist/_chunks/UploadUtil.js +44 -40
  19. package/dist/_chunks/UploadUtil.js.map +1 -1
  20. package/dist/_chunks/dashboardStore.js.map +1 -1
  21. package/dist/classes/services/EventBusService/EventBusService.d.ts +9 -2
  22. package/dist/classes/services/EventBusService/index.js.map +1 -1
  23. package/dist/components/ui/MapWidget/index.js.map +1 -1
  24. package/dist/components/ui/RemoveCasesFromEventDialog/index.js.map +1 -1
  25. package/dist/components/ui/SequenceDownloadDialog/index.js +53 -40
  26. package/dist/components/ui/SequenceDownloadDialog/index.js.map +1 -1
  27. package/dist/components/ui/TreeWidget/index.js +8 -2
  28. package/dist/components/ui/TreeWidget/index.js.map +1 -1
  29. package/dist/locale/en.json +0 -11
  30. package/dist/locale/nl.json +0 -11
  31. package/dist/models/config.d.ts +2 -0
  32. package/dist/utils/UploadUtil/UploadUtil.d.ts +1 -0
  33. package/package.json +15 -23
  34. package/dist/components/ui/FileSelector/FileSelector.d.ts +0 -7
  35. package/dist/components/ui/FileSelector/index.d.ts +0 -1
  36. package/dist/components/ui/FileSelector/index.js +0 -206
  37. package/dist/components/ui/FileSelector/index.js.map +0 -1
  38. package/dist/utils/FileUtil/FileUtil.d.ts +0 -5
  39. package/dist/utils/FileUtil/index.d.ts +0 -1
  40. package/dist/utils/FileUtil/index.js +0 -15
  41. package/dist/utils/FileUtil/index.js.map +0 -1
@@ -292,14 +292,14 @@ var G = {
292
292
  }
293
293
  }) : [], [q.data, K.organizationFilter]), Y = I(() => {
294
294
  let e = /* @__PURE__ */ new Map();
295
- for (let { case_date: t, is_own_case: n } of q.data ?? []) if (t) {
296
- let r = e.get(t) ?? {
295
+ for (let { is_own_case: t, timed_at: n } of q.data ?? []) if (n) {
296
+ let r = e.get(n) ?? {
297
297
  count: 0,
298
- date: t,
298
+ date: n,
299
299
  otherOrganizationCaseCount: 0,
300
300
  ownCaseCount: 0
301
301
  };
302
- r.count += 1, n ? r.ownCaseCount += 1 : r.otherOrganizationCaseCount += 1, e.set(t, r);
302
+ r.count += 1, t ? r.ownCaseCount += 1 : r.otherOrganizationCaseCount += 1, e.set(n, r);
303
303
  }
304
304
  return Array.from(e.values()).sort((e, t) => e.date.localeCompare(t.date));
305
305
  }, [q.data]), X = I(() => {
@@ -307,8 +307,8 @@ var G = {
307
307
  if (!t) return J;
308
308
  let [r, i] = t;
309
309
  return J.filter((t) => {
310
- if (!t.case_date) return !1;
311
- let a = e(n(t.case_date), E.DATE);
310
+ if (!t.timed_at) return !1;
311
+ let a = e(n(t.timed_at), E.DATE);
312
312
  return a >= r && a <= i;
313
313
  });
314
314
  }, [K.dateRange, J]), Z = I(() => {
@@ -1 +1 @@
1
- {"version":3,"file":"FindSimilarCasesDialog.js","names":[],"sources":["../../src/components/ui/FindSimilarCasesDialog/FindSimilarCasesDialogDateRangeChart.tsx","../../src/components/ui/FindSimilarCasesDialog/FindSimilarCasesDialog.tsx"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { ReactElement } from 'react';\nimport {\n dispose,\n getInstanceByDom,\n init,\n use as registerECharts,\n} from 'echarts/core';\nimport {\n DataZoomComponent,\n GridComponent,\n LegendComponent,\n TooltipComponent,\n} from 'echarts/components';\nimport {\n BarChart,\n LineChart,\n} from 'echarts/charts';\nimport { CanvasRenderer } from 'echarts/renderers';\nimport EChartsReact from 'echarts-for-react';\nimport type {\n Control,\n FieldValues,\n Path,\n} from 'react-hook-form';\nimport { Controller } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport {\n Box,\n useTheme,\n} from '@mui/material';\nimport { ConfigService } from '@gen-epix/ui-client-common/classes/services/ConfigService';\n\nimport { FindSimilarCasesUtil } from '../../../utils/FindSimilarCasesUtil';\nimport type {\n FindSimilarCasesChartDataPoint,\n FindSimilarCasesOrganizationFilter,\n} from '../../../models/caseDb';\nimport type { CaseDbConfig } from '../../../models/config';\n\nregisterECharts([BarChart, LineChart, GridComponent, LegendComponent, TooltipComponent, DataZoomComponent, CanvasRenderer]);\n\nconst echartsCore = {\n dispose,\n getInstanceByDom,\n init,\n};\n\nexport interface FindSimilarCasesDialogDateRangeChartProps<\n TFieldValues extends FieldValues,\n TName extends Path<TFieldValues> = Path<TFieldValues>,\n> {\n control: Control<TFieldValues>;\n data: FindSimilarCasesChartDataPoint[];\n name: TName;\n onOrganizationFilterChange: (organizationFilter: FindSimilarCasesOrganizationFilter) => void;\n organizationFilter: FindSimilarCasesOrganizationFilter;\n}\n\nexport const FindSimilarCasesDialogDateRangeChart = <\n TFieldValues extends FieldValues,\n TName extends Path<TFieldValues> = Path<TFieldValues>,\n>({\n control,\n data,\n name,\n onOrganizationFilterChange,\n organizationFilter,\n}: FindSimilarCasesDialogDateRangeChartProps<TFieldValues, TName>): ReactElement => {\n const { t } = useTranslation();\n const intervals = useMemo(() => FindSimilarCasesUtil.buildChartIntervals(data), [data]);\n const theme = useTheme();\n const [tooltipState, setTooltipState] = useState<{ content: string; x: number; y: number } | null>(null);\n const ownCasesLabel = t`Own cases`;\n const otherOrganizationCasesLabel = t`Cases from other organizations`;\n\n const intervalsRef = useRef(intervals);\n useEffect(() => {\n intervalsRef.current = intervals;\n }, [intervals]);\n\n // Keeps a stable reference to the latest onChange so onDataZoom never goes stale.\n const onChangeRef = useRef<((value: [string, string] | null) => void) | null>(null);\n\n const onDataZoom = useCallback((params: unknown) => {\n type ZoomPayload = { end?: number; endValue?: number; start?: number; startValue?: number };\n const event = params as { batch?: ZoomPayload[] } & ZoomPayload;\n const base = event.batch?.[0] ?? event;\n\n const n = intervalsRef.current.length - 1;\n let startIndex: number;\n let endIndex: number;\n\n if (base.startValue !== undefined && base.endValue !== undefined) {\n // index-based (e.g. inside/wheel zoom)\n startIndex = Math.round(base.startValue);\n endIndex = Math.round(base.endValue);\n } else if (base.start !== undefined && base.end !== undefined) {\n // percentage-based (slider drag)\n startIndex = Math.round((base.start / 100) * n);\n endIndex = Math.round((base.end / 100) * n);\n } else {\n onChangeRef.current?.(null);\n return;\n }\n\n const startInterval = intervalsRef.current[startIndex];\n const endInterval = intervalsRef.current[endIndex];\n if (startInterval && endInterval) {\n onChangeRef.current?.([startInterval.startDate, endInterval.endDate]);\n return;\n }\n onChangeRef.current?.(null);\n }, []);\n\n const option = useMemo(() => {\n const { BASE_COLORS } = ConfigService.getInstance<CaseDbConfig>().config.epi.STRATIFICATION;\n return {\n color: BASE_COLORS,\n dataZoom: [{\n borderColor: theme.palette.primary.light,\n bottom: 0,\n fillerColor: `${theme.palette.primary.main}33`,\n handleStyle: { borderColor: theme.palette.primary.main, color: theme.palette.primary.main },\n height: 50,\n left: 80,\n right: 80,\n selectedDataBackground: { areaStyle: { color: theme.palette.primary.light }, lineStyle: { color: theme.palette.primary.main } },\n type: 'slider',\n }],\n grid: { bottom: 110, left: 80, right: 80, top: 40 },\n legend: {\n data: [ownCasesLabel, otherOrganizationCasesLabel],\n selected: {\n [otherOrganizationCasesLabel]: organizationFilter !== 'own',\n [ownCasesLabel]: organizationFilter !== 'otherOrganization',\n },\n top: 0,\n },\n series: [\n {\n data: intervals.map(i => i.count),\n lineStyle: { opacity: 0 },\n name: 'dataZoomShadow',\n silent: true,\n symbol: 'none',\n type: 'line',\n },\n { data: intervals.map(i => i.ownCaseCount), itemStyle: { color: BASE_COLORS[0] }, name: ownCasesLabel, stack: 'similarCases', type: 'bar' },\n { data: intervals.map(i => i.otherOrganizationCaseCount), itemStyle: { color: BASE_COLORS[1] }, name: otherOrganizationCasesLabel, stack: 'similarCases', type: 'bar' },\n ],\n tooltip: { show: false },\n xAxis: { axisLabel: {\n height: 100,\n rotate: 45,\n }, axisTick: {\n alignWithLabel: true,\n show: true,\n }, data: intervals.map(i => i.label),\n type: 'category' },\n yAxis: { max: Math.max(...intervals.map(i => i.count), 1), minInterval: 1, type: 'value' },\n };\n }, [intervals, organizationFilter, otherOrganizationCasesLabel, ownCasesLabel, theme]);\n\n const onLegendSelectChanged = useCallback((params: unknown) => {\n const { selected } = params as { selected?: Record<string, boolean> };\n if (!selected) {\n return;\n }\n const ownCasesSelected = selected[ownCasesLabel] ?? true;\n const otherOrganizationCasesSelected = selected[otherOrganizationCasesLabel] ?? true;\n if (ownCasesSelected && !otherOrganizationCasesSelected) {\n onOrganizationFilterChange('own');\n return;\n }\n if (!ownCasesSelected && otherOrganizationCasesSelected) {\n onOrganizationFilterChange('otherOrganization');\n return;\n }\n onOrganizationFilterChange('all');\n }, [onOrganizationFilterChange, otherOrganizationCasesLabel, ownCasesLabel]);\n\n const onEvents = useMemo(() => ({\n datazoom: onDataZoom,\n legendselectchanged: onLegendSelectChanged,\n mouseout: () => {\n setTooltipState(null);\n },\n mouseover: (event: unknown) => {\n const e = event as { event?: { event?: MouseEvent }; name?: string; value?: number };\n const mouseEvent = e.event?.event;\n if (mouseEvent && e.name !== null && e.name !== undefined) {\n setTooltipState({ content: `${e.name} (n=${e.value ?? 0})`, x: mouseEvent.clientX + 12, y: mouseEvent.clientY + 12 });\n }\n },\n }), [onDataZoom, onLegendSelectChanged]);\n\n const renderChart = useCallback(({ field: { onChange } }: { field: { onChange: (value: [string, string] | null) => void } }) => {\n onChangeRef.current = onChange;\n return (\n <EChartsReact\n echarts={echartsCore}\n notMerge\n onEvents={onEvents}\n option={option}\n style={{ height: 400 }}\n />\n );\n }, [onEvents, option]);\n\n return (\n <>\n <Controller\n control={control}\n name={name}\n render={renderChart}\n />\n {tooltipState && (\n <Box\n sx={{\n bgcolor: 'background.paper',\n border: '1px solid',\n borderColor: 'divider',\n borderRadius: 1,\n fontSize: 12,\n left: tooltipState.x,\n pointerEvents: 'none',\n position: 'fixed',\n px: 1,\n py: 0.5,\n top: tooltipState.y,\n zIndex: 'tooltip',\n }}\n >\n {tooltipState.content}\n </Box>\n )}\n </>\n );\n};\n","import {\n use,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useState,\n} from 'react';\nimport type { ReactElement } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport {\n mixed,\n object,\n string,\n} from 'yup';\nimport { yupResolver } from '@hookform/resolvers/yup';\nimport type { Resolver } from 'react-hook-form';\nimport {\n useForm,\n useWatch,\n} from 'react-hook-form';\nimport {\n Alert,\n AlertTitle,\n Box,\n Button,\n Divider,\n Typography,\n} from '@mui/material';\nimport { useStore } from 'zustand';\nimport { produce } from 'immer';\nimport {\n format,\n parseISO,\n} from 'date-fns';\nimport type {\n CaseDbCase,\n CaseDbCol,\n CaseDbCompleteCaseType,\n} from '@gen-epix/api-casedb';\nimport { CaseDbCaseApi } from '@gen-epix/api-casedb';\nimport type { DialogAction } from '@gen-epix/ui-core-components/components/Dialog';\nimport type {\n WithDialogRefMethods,\n WithDialogRenderProps,\n} from '@gen-epix/ui-core-components/hoc/withDialog';\nimport { ConfigService } from '@gen-epix/ui-client-common/classes/services/ConfigService';\nimport { QueryClientService } from '@gen-epix/ui-client-common/classes/services/QueryClientService';\nimport { ResponseHandler } from '@gen-epix/ui-client-common/components/ui/ResponseHandler';\nimport { useQueryMemo } from '@gen-epix/ui-client-common/hooks/useQueryMemo';\nimport { withDialog } from '@gen-epix/ui-core-components/hoc/withDialog';\nimport { TestIdUtil } from '@gen-epix/ui-core/utils/TestIdUtil';\nimport { GenericForm } from '@gen-epix/ui-core-form/components/helpers/GenericForm';\nimport type {\n AutoCompleteOption,\n FormFieldDefinition,\n} from '@gen-epix/ui-core-form/models/form';\nimport { FORM_FIELD_DEFINITION_TYPE } from '@gen-epix/ui-core-form/models/form';\nimport { SchemaUtil } from '@gen-epix/ui-core-form/utils/SchemaUtil';\nimport { DATE_FORMAT } from '@gen-epix/ui-core/constants/date';\n\nimport { CASEDB_QUERY_KEY } from '../../../constants/query';\nimport type { CaseDbConfig } from '../../../models/config';\nimport { DashboardStoreContext } from '../../../stores/dashboardStore';\nimport { TreeWidgetUtil } from '../../../utils/TreeWidgetUtil';\nimport type {\n CaseDbSimilarCaseWithIsOwnCase,\n FindSimilarCasesChartDataPoint,\n FindSimilarCasesOrganizationFilter,\n} from '../../../models/caseDb';\nimport type { TreeWidgetDataPersistable } from '../../../models/dashboard';\nimport { DASHBOARD_COMPONENT_NAME } from '../../../constants/dashboard';\n\nimport { FindSimilarCasesDialogDateRangeChart } from './FindSimilarCasesDialogDateRangeChart';\n\nexport interface FindSimilarCasesDialogOpenProps {\n allRows: CaseDbCase[];\n completeCaseType: CaseDbCompleteCaseType;\n selectedRows: CaseDbCase[];\n}\n\nexport interface FindSimilarCasesDialogProps extends WithDialogRenderProps<FindSimilarCasesDialogOpenProps> {\n //\n}\n\nexport type FindSimilarCasesDialogRefMethods = WithDialogRefMethods<FindSimilarCasesDialogProps, FindSimilarCasesDialogOpenProps>;\n\ntype FormFields = {\n dateRange: [string, string] | null;\n maxDistance: number;\n organizationFilter: FindSimilarCasesOrganizationFilter;\n treeColId: string;\n};\n\nexport const FindSimilarCasesDialog = withDialog<FindSimilarCasesDialogProps, FindSimilarCasesDialogOpenProps>((\n {\n onActionsChange,\n onClose,\n onTitleChange,\n openProps,\n }: FindSimilarCasesDialogProps,\n): ReactElement => {\n const { t } = useTranslation();\n const searchFormId = useId();\n const dashboardStore = use(DashboardStoreContext);\n const treeConfiguration = dashboardStore.getState().getWidgetDataPersistable<TreeWidgetDataPersistable>(DASHBOARD_COMPONENT_NAME.TREE).treeConfiguration;\n const setFindSimilarCasesResults = useStore(dashboardStore, (state) => state.setFindSimilarCasesResults);\n const findSimilarCasesResults = useStore(dashboardStore, (state) => state.findSimilarCasesResults);\n const treeConfigurations = useMemo(() => TreeWidgetUtil.getTreeConfigurations(openProps.completeCaseType), [openProps.completeCaseType]);\n const [formData, setFormData] = useState<FormFields>(null);\n\n const schema = useMemo(() => object<FormFields>().shape({\n dateRange: mixed<[string, string] | null>().nullable().default(null),\n maxDistance: SchemaUtil.number.required().positive().when('treeColId', ([treeColId], s) => {\n const currentTreeConfiguration = treeConfigurations.find(x => x.col.id === treeColId);\n if (!currentTreeConfiguration) {\n return s;\n }\n const sWithIntegerCheck = s.integer(t`Max distance must be an integer`);\n return sWithIntegerCheck.max(currentTreeConfiguration.geneticDistanceProtocol.seqdb_max_stored_distance || ConfigService.getInstance<CaseDbConfig>().config.epi.SEQDB_MAX_STORED_DISTANCE_FALLBACK);\n }),\n organizationFilter: string<FindSimilarCasesOrganizationFilter>().required().oneOf(['all', 'own', 'otherOrganization']),\n treeColId: string().required(),\n }), [t, treeConfigurations]);\n\n const treeOptions = useMemo<AutoCompleteOption<string>[]>(() => {\n const cols = new Set<CaseDbCol>();\n treeConfigurations.forEach(x => {\n cols.add(x.col);\n });\n return Array.from(cols).map(x => {\n return {\n label: x.label,\n value: x.id,\n } satisfies AutoCompleteOption<string>;\n });\n\n }, [treeConfigurations]);\n\n const formMethods = useForm<FormFields>({\n defaultValues: {\n dateRange: null,\n maxDistance: 0,\n organizationFilter: 'all',\n treeColId: treeConfiguration ? treeConfiguration.col.id : null,\n },\n resolver: yupResolver(schema, undefined, { raw: true }) as Resolver<FormFields>,\n });\n const { control, handleSubmit } = formMethods;\n\n const formValues = useWatch({ control });\n\n const searchFormFieldDefinitions = useMemo(() => {\n const currentTreeConfiguration = treeConfigurations.find(x => x.col.id === formValues.treeColId);\n return [\n {\n definition: FORM_FIELD_DEFINITION_TYPE.SELECT,\n label: t`Tree`,\n name: 'treeColId',\n options: treeOptions,\n } as const satisfies FormFieldDefinition<FormFields>,\n {\n definition: FORM_FIELD_DEFINITION_TYPE.NUMBER,\n label: t`Max distance`,\n max: currentTreeConfiguration?.geneticDistanceProtocol?.seqdb_max_stored_distance || ConfigService.getInstance<CaseDbConfig>().config.epi.SEQDB_MAX_STORED_DISTANCE_FALLBACK,\n min: 0,\n name: 'maxDistance',\n showSlider: true,\n step: 1,\n } as const satisfies FormFieldDefinition<FormFields>,\n ];\n }, [formValues.treeColId, t, treeConfigurations, treeOptions]);\n\n useEffect(() => {\n onTitleChange(t`Find similar cases`);\n }, [t, onTitleChange]);\n\n const onFormSubmit = useCallback((data: FormFields) => {\n formMethods.setValue('dateRange', null);\n setFormData({ ...data, dateRange: null });\n }, [formMethods]);\n\n const onOrganizationFilterChange = useCallback((organizationFilter: FindSimilarCasesOrganizationFilter) => {\n formMethods.setValue('organizationFilter', organizationFilter);\n }, [formMethods]);\n\n const query = useQueryMemo({\n enabled: !!formData,\n queryFn: async ({ signal }) => {\n const retrieveSimilarCasesResponse = await CaseDbCaseApi.getInstance().retrieveSimilarCases({\n case_ids: openProps.selectedRows.map(x => x.id),\n case_type_id: openProps.completeCaseType.id,\n genetic_distance_col_id: formData?.treeColId,\n max_distance: formData?.maxDistance,\n }, { signal });\n const retrieveIsOwnCasesResponse = await CaseDbCaseApi.getInstance().retrieveIsOwnCases({\n case_ids: retrieveSimilarCasesResponse.data.cases.map(x => x.id),\n case_type_id: openProps.completeCaseType.id,\n }, { signal });\n\n return retrieveSimilarCasesResponse.data.cases.map(caseDbCase => {\n return {\n ...caseDbCase,\n is_own_case: retrieveIsOwnCasesResponse.data[caseDbCase.id] || false,\n } satisfies CaseDbSimilarCaseWithIsOwnCase;\n });\n },\n queryKey: [QueryClientService.getInstance().getGenericKey(CASEDB_QUERY_KEY.SIMILAR_CASES), JSON.stringify({ formData, rowIds: openProps.selectedRows.map(row => row.id) })],\n });\n\n const organizationFilteredCases = useMemo<CaseDbSimilarCaseWithIsOwnCase[]>(() => {\n if (!query.data) {\n return [];\n }\n return query.data.filter(c => {\n switch (formValues.organizationFilter) {\n case 'all': return true;\n case 'otherOrganization': return !c.is_own_case;\n case 'own': return c.is_own_case;\n default: return true;\n }\n });\n }, [query.data, formValues.organizationFilter]);\n\n const chartData = useMemo<FindSimilarCasesChartDataPoint[]>(() => {\n const counts = new Map<string, FindSimilarCasesChartDataPoint>();\n for (const { case_date, is_own_case } of query.data ?? []) {\n if (case_date) {\n const currentCount = counts.get(case_date) ?? {\n count: 0,\n date: case_date,\n otherOrganizationCaseCount: 0,\n ownCaseCount: 0,\n };\n currentCount.count += 1;\n if (is_own_case) {\n currentCount.ownCaseCount += 1;\n } else {\n currentCount.otherOrganizationCaseCount += 1;\n }\n counts.set(case_date, currentCount);\n }\n }\n return Array.from(counts.values())\n .sort((a, b) => a.date.localeCompare(b.date));\n }, [query.data]);\n\n const filteredCases = useMemo<CaseDbSimilarCaseWithIsOwnCase[]>(() => {\n const dateRange = formValues.dateRange;\n if (!dateRange) {\n return organizationFilteredCases;\n }\n const [startDate, endDate] = dateRange;\n return organizationFilteredCases.filter(c => {\n if (!c.case_date) {\n return false;\n }\n const caseDateOnly = format(parseISO(c.case_date), DATE_FORMAT.DATE);\n return caseDateOnly >= startDate && caseDateOnly <= endDate;\n });\n }, [formValues.dateRange, organizationFilteredCases]);\n\n const similarCaseIdsNotInLineListWithDateFilter = useMemo<string[]>(() => {\n const allRowIds = openProps.allRows.map(x => x.id);\n return filteredCases.reduce<string[]>((acc, caseIdAndDate) => {\n const caseId = caseIdAndDate.id;\n if (!allRowIds.includes(caseId)) {\n acc.push(caseId);\n }\n return acc;\n }, []);\n }, [filteredCases, openProps.allRows]);\n\n const similarCaseIdsAlreadyInLineListWithDateFilter = useMemo<string[]>(() => {\n const allRowIds = openProps.allRows.map(x => x.id);\n return filteredCases.reduce<string[]>((acc, caseIdAndDate) => {\n const caseId = caseIdAndDate.id;\n if (allRowIds.includes(caseId)) {\n acc.push(caseId);\n }\n return acc;\n }, []);\n }, [filteredCases, openProps.allRows]);\n\n const onAddToLineListButtonClick = useCallback(async () => {\n\n await setFindSimilarCasesResults(produce(findSimilarCasesResults, draft => {\n draft.push({\n colId: formData?.treeColId,\n distance: formData?.maxDistance,\n key: findSimilarCasesResults.length.toString(),\n originalCaseIds: openProps.allRows.map(x => x.id),\n similarCaseIds: similarCaseIdsNotInLineListWithDateFilter,\n });\n return draft;\n }));\n\n onClose();\n }, [setFindSimilarCasesResults, findSimilarCasesResults, onClose, similarCaseIdsNotInLineListWithDateFilter, formData?.maxDistance, formData?.treeColId, openProps.allRows]);\n\n useEffect(() => {\n const actions: DialogAction[] = [];\n actions.push({\n ...TestIdUtil.createAttributes('FindSimilarCasesDialog-closeButton'),\n color: 'primary',\n label: t`Close`,\n onClick: onClose,\n variant: 'outlined',\n });\n actions.push({\n ...TestIdUtil.createAttributes('FindSimilarCasesDialog-addSimilarCasesButton'),\n color: 'secondary',\n disabled: similarCaseIdsNotInLineListWithDateFilter.length === 0,\n label: t('Add {{count}} similar cases to line list', { count: similarCaseIdsNotInLineListWithDateFilter.length }),\n onClick: onAddToLineListButtonClick,\n variant: 'contained',\n });\n onActionsChange(actions);\n }, [onActionsChange, onAddToLineListButtonClick, onClose, similarCaseIdsNotInLineListWithDateFilter.length, t]);\n\n return (\n <Box>\n <Box\n sx={{\n marginBottom: 2,\n }}\n >\n <Typography>\n {t('Select a tree and maximum distance to find similar cases for the {{count}} selected cases.', { count: openProps.selectedRows.length })}\n </Typography>\n </Box>\n <Box>\n <GenericForm<FormFields>\n formFieldDefinitions={searchFormFieldDefinitions}\n formId={searchFormId}\n formMethods={formMethods}\n onSubmit={handleSubmit(onFormSubmit)}\n schema={schema}\n />\n </Box>\n <Box\n sx={{\n display: 'flex',\n justifyContent: 'flex-end',\n marginTop: 2,\n }}\n >\n <Button\n color={'secondary'}\n form={searchFormId}\n type={'submit'}\n variant={'contained'}\n >\n {formData ? t`Search again` : t`Search`}\n </Button>\n </Box>\n <Divider sx={{ marginY: 2 }} />\n <ResponseHandler\n inlineSpinner\n loadables={query}\n >\n {query.data && (\n <>\n <Box\n sx={{\n marginY: 2,\n }}\n >\n <Alert\n severity={query.data.length > 0 ? 'success' : 'warning'}\n >\n <AlertTitle>\n {t('Found {{count}} similar cases with a distance of {{distance}}.', { count: query.data.length, distance: formData?.maxDistance })}\n </AlertTitle>\n </Alert>\n </Box>\n {chartData.length > 0 && (\n <Box\n sx={{\n marginY: 2,\n }}\n >\n <FindSimilarCasesDialogDateRangeChart<FormFields>\n control={control}\n data={chartData}\n name={'dateRange'}\n onOrganizationFilterChange={onOrganizationFilterChange}\n organizationFilter={formValues.organizationFilter}\n />\n </Box>\n )}\n <Box\n sx={{\n marginY: 2,\n }}\n >\n <Alert\n severity={'info'}\n >\n <AlertTitle>\n {t('{{count}} similar cases can be added to the line list.', { count: similarCaseIdsNotInLineListWithDateFilter.length })}\n </AlertTitle>\n </Alert>\n </Box>\n {similarCaseIdsAlreadyInLineListWithDateFilter.length > 0 && (\n <Alert\n severity={'warning'}\n >\n <AlertTitle>\n {t('{{count}} similar cases found that are already in the line list.', { count: similarCaseIdsAlreadyInLineListWithDateFilter.length })}\n </AlertTitle>\n </Alert>\n )}\n </>\n )}\n </ResponseHandler>\n </Box>\n );\n}, {\n defaultTitle: '',\n fullWidth: true,\n maxWidth: 'md',\n testId: 'FindSimilarCasesDialog',\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CA,EAAgB;CAAC;CAAU;CAAW;CAAe;CAAiB;CAAkB;CAAmB;AAAc,CAAC;AAE1H,IAAM,IAAc;CAClB;CACA;CACA;AACF,GAaa,MAGX,EACA,YACA,SACA,SACA,+BACA,4BACkF;CAClF,IAAM,EAAE,SAAM,EAAe,GACvB,IAAY,QAAc,EAAqB,oBAAoB,CAAI,GAAG,CAAC,CAAI,CAAC,GAChF,IAAQ,EAAS,GACjB,CAAC,GAAc,KAAmB,EAA2D,IAAI,GACjG,IAAgB,CAAC,aACjB,IAA8B,CAAC,kCAE/B,IAAe,EAAO,CAAS;CACrC,QAAgB;EACd,EAAa,UAAU;CACzB,GAAG,CAAC,CAAS,CAAC;CAGd,IAAM,IAAc,EAA0D,IAAI,GAE5E,IAAa,GAAa,MAAoB;EAElD,IAAM,IAAQ,GACR,IAAO,EAAM,QAAQ,MAAM,GAE3B,IAAI,EAAa,QAAQ,SAAS,GACpC,GACA;EAEJ,IAAI,EAAK,eAAe,KAAA,KAAa,EAAK,aAAa,KAAA,GAGrD,AADA,IAAa,KAAK,MAAM,EAAK,UAAU,GACvC,IAAW,KAAK,MAAM,EAAK,QAAQ;OAC9B,IAAI,EAAK,UAAU,KAAA,KAAa,EAAK,QAAQ,KAAA,GAGlD,AADA,IAAa,KAAK,MAAO,EAAK,QAAQ,MAAO,CAAC,GAC9C,IAAW,KAAK,MAAO,EAAK,MAAM,MAAO,CAAC;OACrC;GACL,EAAY,UAAU,IAAI;GAC1B;EACF;EAEA,IAAM,IAAgB,EAAa,QAAQ,IACrC,IAAc,EAAa,QAAQ;EACzC,IAAI,KAAiB,GAAa;GAChC,EAAY,UAAU,CAAC,EAAc,WAAW,EAAY,OAAO,CAAC;GACpE;EACF;EACA,EAAY,UAAU,IAAI;CAC5B,GAAG,CAAC,CAAC,GAEC,IAAS,QAAc;EAC3B,IAAM,EAAE,mBAAgB,EAAc,YAA0B,CAAC,CAAC,OAAO,IAAI;EAC7E,OAAO;GACL,OAAO;GACP,UAAU,CAAC;IACT,aAAa,EAAM,QAAQ,QAAQ;IACnC,QAAQ;IACR,aAAa,GAAG,EAAM,QAAQ,QAAQ,KAAK;IAC3C,aAAa;KAAE,aAAa,EAAM,QAAQ,QAAQ;KAAM,OAAO,EAAM,QAAQ,QAAQ;IAAK;IAC1F,QAAQ;IACR,MAAM;IACN,OAAO;IACP,wBAAwB;KAAE,WAAW,EAAE,OAAO,EAAM,QAAQ,QAAQ,MAAM;KAAG,WAAW,EAAE,OAAO,EAAM,QAAQ,QAAQ,KAAK;IAAE;IAC9H,MAAM;GACR,CAAC;GACD,MAAM;IAAE,QAAQ;IAAK,MAAM;IAAI,OAAO;IAAI,KAAK;GAAG;GAClD,QAAQ;IACN,MAAM,CAAC,GAAe,CAA2B;IACjD,UAAU;MACP,IAA8B,MAAuB;MACrD,IAAgB,MAAuB;IAC1C;IACA,KAAK;GACP;GACA,QAAQ;IACN;KACE,MAAM,EAAU,KAAI,MAAK,EAAE,KAAK;KAChC,WAAW,EAAE,SAAS,EAAE;KACxB,MAAM;KACN,QAAQ;KACR,QAAQ;KACR,MAAM;IACR;IACA;KAAE,MAAM,EAAU,KAAI,MAAK,EAAE,YAAY;KAAG,WAAW,EAAE,OAAO,EAAY,GAAG;KAAG,MAAM;KAAe,OAAO;KAAgB,MAAM;IAAM;IAC1I;KAAE,MAAM,EAAU,KAAI,MAAK,EAAE,0BAA0B;KAAG,WAAW,EAAE,OAAO,EAAY,GAAG;KAAG,MAAM;KAA6B,OAAO;KAAgB,MAAM;IAAM;GACxK;GACA,SAAS,EAAE,MAAM,GAAM;GACvB,OAAO;IAAE,WAAW;KAClB,QAAQ;KACR,QAAQ;IACV;IAAG,UAAU;KACX,gBAAgB;KAChB,MAAM;IACR;IAAG,MAAM,EAAU,KAAI,MAAK,EAAE,KAAK;IACnC,MAAM;GAAW;GACjB,OAAO;IAAE,KAAK,KAAK,IAAI,GAAG,EAAU,KAAI,MAAK,EAAE,KAAK,GAAG,CAAC;IAAG,aAAa;IAAG,MAAM;GAAQ;EAC3F;CACF,GAAG;EAAC;EAAW;EAAoB;EAA6B;EAAe;CAAK,CAAC,GAE/E,IAAwB,GAAa,MAAoB;EAC7D,IAAM,EAAE,gBAAa;EACrB,IAAI,CAAC,GACH;EAEF,IAAM,IAAmB,EAAS,MAAkB,IAC9C,IAAiC,EAAS,MAAgC;EAChF,IAAI,KAAoB,CAAC,GAAgC;GACvD,EAA2B,KAAK;GAChC;EACF;EACA,IAAI,CAAC,KAAoB,GAAgC;GACvD,EAA2B,mBAAmB;GAC9C;EACF;EACA,EAA2B,KAAK;CAClC,GAAG;EAAC;EAA4B;EAA6B;CAAa,CAAC,GAErE,IAAW,SAAe;EAC9B,UAAU;EACV,qBAAqB;EACrB,gBAAgB;GACd,EAAgB,IAAI;EACtB;EACA,YAAY,MAAmB;GAC7B,IAAM,IAAI,GACJ,IAAa,EAAE,OAAO;GAC5B,AAAI,KAAc,EAAE,SAAS,QAAQ,EAAE,SAAS,KAAA,KAC9C,EAAgB;IAAE,SAAS,GAAG,EAAE,KAAK,MAAM,EAAE,SAAS,EAAE;IAAI,GAAG,EAAW,UAAU;IAAI,GAAG,EAAW,UAAU;GAAG,CAAC;EAExH;CACF,IAAI,CAAC,GAAY,CAAqB,CAAC,GAEjC,IAAc,GAAa,EAAE,OAAO,EAAE,qBAC1C,EAAY,UAAU,GAEpB,kBAAC,GAAD;EACE,SAAS;EACT,UAAA;EACU;EACF;EACR,OAAO,EAAE,QAAQ,IAAI;CACtB,CAAA,IAEF,CAAC,GAAU,CAAM,CAAC;CAErB,OACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;EACW;EACH;EACN,QAAQ;CACT,CAAA,GACA,KACC,kBAAC,GAAD;EACE,IAAI;GACF,SAAS;GACT,QAAQ;GACR,aAAa;GACb,cAAc;GACd,UAAU;GACV,MAAM,EAAa;GACnB,eAAe;GACf,UAAU;GACV,IAAI;GACJ,IAAI;GACJ,KAAK,EAAa;GAClB,QAAQ;EACV;EAEC,UAAA,EAAa;CACX,CAAA,CAEP,EAAA,CAAA;AAEN,GCvJa,IAAyB,GACpC,EACE,oBACA,YACA,kBACA,mBAEe;CACjB,IAAM,EAAE,SAAM,EAAe,GACvB,IAAe,EAAM,GACrB,IAAiB,EAAI,CAAqB,GAC1C,IAAoB,EAAe,SAAS,CAAC,CAAC,yBAAoD,EAAyB,IAAI,CAAC,CAAC,mBACjI,IAA6B,EAAS,IAAiB,MAAU,EAAM,0BAA0B,GACjG,IAA0B,EAAS,IAAiB,MAAU,EAAM,uBAAuB,GAC3F,IAAqB,QAAc,EAAe,sBAAsB,EAAU,gBAAgB,GAAG,CAAC,EAAU,gBAAgB,CAAC,GACjI,CAAC,GAAU,KAAe,EAAqB,IAAI,GAEnD,IAAS,QAAc,GAAmB,CAAC,CAAC,MAAM;EACtD,WAAW,GAA+B,CAAC,CAAC,SAAS,CAAC,CAAC,QAAQ,IAAI;EACnE,aAAa,GAAW,OAAO,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,KAAK,cAAc,CAAC,IAAY,MAAM;GACzF,IAAM,IAA2B,EAAmB,MAAK,MAAK,EAAE,IAAI,OAAO,CAAS;GAKpF,OAJK,IAGqB,EAAE,QAAQ,CAAC,iCAC9B,CAAA,CAAkB,IAAI,EAAyB,wBAAwB,6BAA6B,EAAc,YAA0B,CAAC,CAAC,OAAO,IAAI,kCAAkC,IAHzL;EAIX,CAAC;EACD,oBAAoB,EAA2C,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM;GAAC;GAAO;GAAO;EAAmB,CAAC;EACrH,WAAW,EAAO,CAAC,CAAC,SAAS;CAC/B,CAAC,GAAG,CAAC,GAAG,CAAkB,CAAC,GAErB,IAAc,QAA4C;EAC9D,IAAM,oBAAO,IAAI,IAAe;EAIhC,OAHA,EAAmB,SAAQ,MAAK;GAC9B,EAAK,IAAI,EAAE,GAAG;EAChB,CAAC,GACM,MAAM,KAAK,CAAI,CAAC,CAAC,KAAI,OACnB;GACL,OAAO,EAAE;GACT,OAAO,EAAE;EACX,EACD;CAEH,GAAG,CAAC,CAAkB,CAAC,GAEjB,IAAc,GAAoB;EACtC,eAAe;GACb,WAAW;GACX,aAAa;GACb,oBAAoB;GACpB,WAAW,IAAoB,EAAkB,IAAI,KAAK;EAC5D;EACA,UAAU,GAAY,GAAQ,KAAA,GAAW,EAAE,KAAK,GAAK,CAAC;CACxD,CAAC,GACK,EAAE,YAAS,oBAAiB,GAE5B,IAAa,GAAS,EAAE,WAAQ,CAAC,GAEjC,KAA6B,QAAc;EAC/C,IAAM,IAA2B,EAAmB,MAAK,MAAK,EAAE,IAAI,OAAO,EAAW,SAAS;EAC/F,OAAO,CACL;GACE,YAAY,EAA2B;GACvC,OAAO,CAAC;GACR,MAAM;GACN,SAAS;EACX,GACA;GACE,YAAY,EAA2B;GACvC,OAAO,CAAC;GACR,KAAK,GAA0B,yBAAyB,6BAA6B,EAAc,YAA0B,CAAC,CAAC,OAAO,IAAI;GAC1I,KAAK;GACL,MAAM;GACN,YAAY;GACZ,MAAM;EACR,CACF;CACF,GAAG;EAAC,EAAW;EAAW;EAAG;EAAoB;CAAW,CAAC;CAE7D,QAAgB;EACd,EAAc,CAAC,oBAAoB;CACrC,GAAG,CAAC,GAAG,CAAa,CAAC;CAErB,IAAM,KAAe,GAAa,MAAqB;EAErD,AADA,EAAY,SAAS,aAAa,IAAI,GACtC,EAAY;GAAE,GAAG;GAAM,WAAW;EAAK,CAAC;CAC1C,GAAG,CAAC,CAAW,CAAC,GAEV,KAA6B,GAAa,MAA2D;EACzG,EAAY,SAAS,sBAAsB,CAAkB;CAC/D,GAAG,CAAC,CAAW,CAAC,GAEV,IAAQ,GAAa;EACzB,SAAS,CAAC,CAAC;EACX,SAAS,OAAO,EAAE,gBAAa;GAC7B,IAAM,IAA+B,MAAM,EAAc,YAAY,CAAC,CAAC,qBAAqB;IAC1F,UAAU,EAAU,aAAa,KAAI,MAAK,EAAE,EAAE;IAC9C,cAAc,EAAU,iBAAiB;IACzC,yBAAyB,GAAU;IACnC,cAAc,GAAU;GAC1B,GAAG,EAAE,UAAO,CAAC,GACP,IAA6B,MAAM,EAAc,YAAY,CAAC,CAAC,mBAAmB;IACtF,UAAU,EAA6B,KAAK,MAAM,KAAI,MAAK,EAAE,EAAE;IAC/D,cAAc,EAAU,iBAAiB;GAC3C,GAAG,EAAE,UAAO,CAAC;GAEb,OAAO,EAA6B,KAAK,MAAM,KAAI,OAC1C;IACL,GAAG;IACH,aAAa,EAA2B,KAAK,EAAW,OAAO;GACjE,EACD;EACH;EACA,UAAU,CAAC,EAAmB,YAAY,CAAC,CAAC,cAAc,EAAiB,aAAa,GAAG,KAAK,UAAU;GAAE;GAAU,QAAQ,EAAU,aAAa,KAAI,MAAO,EAAI,EAAE;EAAE,CAAC,CAAC;CAC5K,CAAC,GAEK,IAA4B,QAC3B,EAAM,OAGJ,EAAM,KAAK,QAAO,MAAK;EAC5B,QAAQ,EAAW,oBAAnB;GACE,KAAK,OAAO,OAAO;GACnB,KAAK,qBAAqB,OAAO,CAAC,EAAE;GACpC,KAAK,OAAO,OAAO,EAAE;GACrB,SAAS,OAAO;EAClB;CACF,CAAC,IATQ,CAAC,GAUT,CAAC,EAAM,MAAM,EAAW,kBAAkB,CAAC,GAExC,IAAY,QAAgD;EAChE,IAAM,oBAAS,IAAI,IAA4C;EAC/D,KAAK,IAAM,EAAE,cAAW,oBAAiB,EAAM,QAAQ,CAAC,GACtD,IAAI,GAAW;GACb,IAAM,IAAe,EAAO,IAAI,CAAS,KAAK;IAC5C,OAAO;IACP,MAAM;IACN,4BAA4B;IAC5B,cAAc;GAChB;GAOA,AANA,EAAa,SAAS,GAClB,IACF,EAAa,gBAAgB,IAE7B,EAAa,8BAA8B,GAE7C,EAAO,IAAI,GAAW,CAAY;EACpC;EAEF,OAAO,MAAM,KAAK,EAAO,OAAO,CAAC,CAAC,CAC/B,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;CAChD,GAAG,CAAC,EAAM,IAAI,CAAC,GAET,IAAgB,QAAgD;EACpE,IAAM,IAAY,EAAW;EAC7B,IAAI,CAAC,GACH,OAAO;EAET,IAAM,CAAC,GAAW,KAAW;EAC7B,OAAO,EAA0B,QAAO,MAAK;GAC3C,IAAI,CAAC,EAAE,WACL,OAAO;GAET,IAAM,IAAe,EAAO,EAAS,EAAE,SAAS,GAAG,EAAY,IAAI;GACnE,OAAO,KAAgB,KAAa,KAAgB;EACtD,CAAC;CACH,GAAG,CAAC,EAAW,WAAW,CAAyB,CAAC,GAE9C,IAA4C,QAAwB;EACxE,IAAM,IAAY,EAAU,QAAQ,KAAI,MAAK,EAAE,EAAE;EACjD,OAAO,EAAc,QAAkB,GAAK,MAAkB;GAC5D,IAAM,IAAS,EAAc;GAI7B,OAHK,EAAU,SAAS,CAAM,KAC5B,EAAI,KAAK,CAAM,GAEV;EACT,GAAG,CAAC,CAAC;CACP,GAAG,CAAC,GAAe,EAAU,OAAO,CAAC,GAE/B,IAAgD,QAAwB;EAC5E,IAAM,IAAY,EAAU,QAAQ,KAAI,MAAK,EAAE,EAAE;EACjD,OAAO,EAAc,QAAkB,GAAK,MAAkB;GAC5D,IAAM,IAAS,EAAc;GAI7B,OAHI,EAAU,SAAS,CAAM,KAC3B,EAAI,KAAK,CAAM,GAEV;EACT,GAAG,CAAC,CAAC;CACP,GAAG,CAAC,GAAe,EAAU,OAAO,CAAC,GAE/B,IAA6B,EAAY,YAAY;EAazD,AAXA,MAAM,EAA2B,EAAQ,IAAyB,OAChE,EAAM,KAAK;GACT,OAAO,GAAU;GACjB,UAAU,GAAU;GACpB,KAAK,EAAwB,OAAO,SAAS;GAC7C,iBAAiB,EAAU,QAAQ,KAAI,MAAK,EAAE,EAAE;GAChD,gBAAgB;EAClB,CAAC,GACM,EACR,CAAC,GAEF,EAAQ;CACV,GAAG;EAAC;EAA4B;EAAyB;EAAS;EAA2C,GAAU;EAAa,GAAU;EAAW,EAAU;CAAO,CAAC;CAsB3K,OApBA,QAAgB;EACd,IAAM,IAA0B,CAAC;EAgBjC,AAfA,EAAQ,KAAK;GACX,GAAG,EAAW,iBAAiB,oCAAoC;GACnE,OAAO;GACP,OAAO,CAAC;GACR,SAAS;GACT,SAAS;EACX,CAAC,GACD,EAAQ,KAAK;GACX,GAAG,EAAW,iBAAiB,8CAA8C;GAC7E,OAAO;GACP,UAAU,EAA0C,WAAW;GAC/D,OAAO,EAAE,4CAA4C,EAAE,OAAO,EAA0C,OAAO,CAAC;GAChH,SAAS;GACT,SAAS;EACX,CAAC,GACD,EAAgB,CAAO;CACzB,GAAG;EAAC;EAAiB;EAA4B;EAAS,EAA0C;EAAQ;CAAC,CAAC,GAG5G,kBAAC,GAAD,EAAA,UAAA;EACE,kBAAC,GAAD;GACE,IAAI,EACF,cAAc,EAChB;GAEA,UAAA,kBAAC,IAAD,EAAA,UACG,EAAE,8FAA8F,EAAE,OAAO,EAAU,aAAa,OAAO,CAAC,EAC/H,CAAA;EACT,CAAA;EACL,kBAAC,GAAD,EAAA,UACE,kBAAC,IAAD;GACE,sBAAsB;GACtB,QAAQ;GACK;GACb,UAAU,EAAa,EAAY;GAC3B;EACT,CAAA,EACE,CAAA;EACL,kBAAC,GAAD;GACE,IAAI;IACF,SAAS;IACT,gBAAgB;IAChB,WAAW;GACb;GAEA,UAAA,kBAAC,IAAD;IACE,OAAO;IACP,MAAM;IACN,MAAM;IACN,SAAS;IAER,UAAA,IAAW,CAAC,iBAAiB,CAAC;GACzB,CAAA;EACL,CAAA;EACL,kBAAC,IAAD,EAAS,IAAI,EAAE,SAAS,EAAE,EAAI,CAAA;EAC9B,kBAAC,IAAD;GACE,eAAA;GACA,WAAW;GAEV,UAAA,EAAM,QACL,kBAAA,GAAA,EAAA,UAAA;IACE,kBAAC,GAAD;KACE,IAAI,EACF,SAAS,EACX;KAEA,UAAA,kBAAC,GAAD;MACE,UAAU,EAAM,KAAK,SAAS,IAAI,YAAY;MAE9C,UAAA,kBAAC,GAAD,EAAA,UACG,EAAE,kEAAkE;OAAE,OAAO,EAAM,KAAK;OAAQ,UAAU,GAAU;MAAY,CAAC,EACxH,CAAA;KACP,CAAA;IACJ,CAAA;IACJ,EAAU,SAAS,KAClB,kBAAC,GAAD;KACE,IAAI,EACF,SAAS,EACX;KAEA,UAAA,kBAAC,IAAD;MACW;MACT,MAAM;MACN,MAAM;MACsB;MAC5B,oBAAoB,EAAW;KAChC,CAAA;IACE,CAAA;IAEP,kBAAC,GAAD;KACE,IAAI,EACF,SAAS,EACX;KAEA,UAAA,kBAAC,GAAD;MACE,UAAU;MAEV,UAAA,kBAAC,GAAD,EAAA,UACG,EAAE,0DAA0D,EAAE,OAAO,EAA0C,OAAO,CAAC,EAC9G,CAAA;KACP,CAAA;IACJ,CAAA;IACJ,EAA8C,SAAS,KACtD,kBAAC,GAAD;KACE,UAAU;KAEV,UAAA,kBAAC,GAAD,EAAA,UACG,EAAE,oEAAoE,EAAE,OAAO,EAA8C,OAAO,CAAC,EAC5H,CAAA;IACP,CAAA;GAET,EAAA,CAAA;EAEW,CAAA;CACd,EAAA,CAAA;AAET,GAAG;CACD,cAAc;CACd,WAAW;CACX,UAAU;CACV,QAAQ;AACV,CAAC"}
1
+ {"version":3,"file":"FindSimilarCasesDialog.js","names":[],"sources":["../../src/components/ui/FindSimilarCasesDialog/FindSimilarCasesDialogDateRangeChart.tsx","../../src/components/ui/FindSimilarCasesDialog/FindSimilarCasesDialog.tsx"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { ReactElement } from 'react';\nimport {\n dispose,\n getInstanceByDom,\n init,\n use as registerECharts,\n} from 'echarts/core';\nimport {\n DataZoomComponent,\n GridComponent,\n LegendComponent,\n TooltipComponent,\n} from 'echarts/components';\nimport {\n BarChart,\n LineChart,\n} from 'echarts/charts';\nimport { CanvasRenderer } from 'echarts/renderers';\nimport EChartsReact from 'echarts-for-react';\nimport type {\n Control,\n FieldValues,\n Path,\n} from 'react-hook-form';\nimport { Controller } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport {\n Box,\n useTheme,\n} from '@mui/material';\nimport { ConfigService } from '@gen-epix/ui-client-common/classes/services/ConfigService';\n\nimport { FindSimilarCasesUtil } from '../../../utils/FindSimilarCasesUtil';\nimport type {\n FindSimilarCasesChartDataPoint,\n FindSimilarCasesOrganizationFilter,\n} from '../../../models/caseDb';\nimport type { CaseDbConfig } from '../../../models/config';\n\nregisterECharts([BarChart, LineChart, GridComponent, LegendComponent, TooltipComponent, DataZoomComponent, CanvasRenderer]);\n\nconst echartsCore = {\n dispose,\n getInstanceByDom,\n init,\n};\n\nexport interface FindSimilarCasesDialogDateRangeChartProps<\n TFieldValues extends FieldValues,\n TName extends Path<TFieldValues> = Path<TFieldValues>,\n> {\n control: Control<TFieldValues>;\n data: FindSimilarCasesChartDataPoint[];\n name: TName;\n onOrganizationFilterChange: (organizationFilter: FindSimilarCasesOrganizationFilter) => void;\n organizationFilter: FindSimilarCasesOrganizationFilter;\n}\n\nexport const FindSimilarCasesDialogDateRangeChart = <\n TFieldValues extends FieldValues,\n TName extends Path<TFieldValues> = Path<TFieldValues>,\n>({\n control,\n data,\n name,\n onOrganizationFilterChange,\n organizationFilter,\n}: FindSimilarCasesDialogDateRangeChartProps<TFieldValues, TName>): ReactElement => {\n const { t } = useTranslation();\n const intervals = useMemo(() => FindSimilarCasesUtil.buildChartIntervals(data), [data]);\n const theme = useTheme();\n const [tooltipState, setTooltipState] = useState<{ content: string; x: number; y: number } | null>(null);\n const ownCasesLabel = t`Own cases`;\n const otherOrganizationCasesLabel = t`Cases from other organizations`;\n\n const intervalsRef = useRef(intervals);\n useEffect(() => {\n intervalsRef.current = intervals;\n }, [intervals]);\n\n // Keeps a stable reference to the latest onChange so onDataZoom never goes stale.\n const onChangeRef = useRef<((value: [string, string] | null) => void) | null>(null);\n\n const onDataZoom = useCallback((params: unknown) => {\n type ZoomPayload = { end?: number; endValue?: number; start?: number; startValue?: number };\n const event = params as { batch?: ZoomPayload[] } & ZoomPayload;\n const base = event.batch?.[0] ?? event;\n\n const n = intervalsRef.current.length - 1;\n let startIndex: number;\n let endIndex: number;\n\n if (base.startValue !== undefined && base.endValue !== undefined) {\n // index-based (e.g. inside/wheel zoom)\n startIndex = Math.round(base.startValue);\n endIndex = Math.round(base.endValue);\n } else if (base.start !== undefined && base.end !== undefined) {\n // percentage-based (slider drag)\n startIndex = Math.round((base.start / 100) * n);\n endIndex = Math.round((base.end / 100) * n);\n } else {\n onChangeRef.current?.(null);\n return;\n }\n\n const startInterval = intervalsRef.current[startIndex];\n const endInterval = intervalsRef.current[endIndex];\n if (startInterval && endInterval) {\n onChangeRef.current?.([startInterval.startDate, endInterval.endDate]);\n return;\n }\n onChangeRef.current?.(null);\n }, []);\n\n const option = useMemo(() => {\n const { BASE_COLORS } = ConfigService.getInstance<CaseDbConfig>().config.epi.STRATIFICATION;\n return {\n color: BASE_COLORS,\n dataZoom: [{\n borderColor: theme.palette.primary.light,\n bottom: 0,\n fillerColor: `${theme.palette.primary.main}33`,\n handleStyle: { borderColor: theme.palette.primary.main, color: theme.palette.primary.main },\n height: 50,\n left: 80,\n right: 80,\n selectedDataBackground: { areaStyle: { color: theme.palette.primary.light }, lineStyle: { color: theme.palette.primary.main } },\n type: 'slider',\n }],\n grid: { bottom: 110, left: 80, right: 80, top: 40 },\n legend: {\n data: [ownCasesLabel, otherOrganizationCasesLabel],\n selected: {\n [otherOrganizationCasesLabel]: organizationFilter !== 'own',\n [ownCasesLabel]: organizationFilter !== 'otherOrganization',\n },\n top: 0,\n },\n series: [\n {\n data: intervals.map(i => i.count),\n lineStyle: { opacity: 0 },\n name: 'dataZoomShadow',\n silent: true,\n symbol: 'none',\n type: 'line',\n },\n { data: intervals.map(i => i.ownCaseCount), itemStyle: { color: BASE_COLORS[0] }, name: ownCasesLabel, stack: 'similarCases', type: 'bar' },\n { data: intervals.map(i => i.otherOrganizationCaseCount), itemStyle: { color: BASE_COLORS[1] }, name: otherOrganizationCasesLabel, stack: 'similarCases', type: 'bar' },\n ],\n tooltip: { show: false },\n xAxis: { axisLabel: {\n height: 100,\n rotate: 45,\n }, axisTick: {\n alignWithLabel: true,\n show: true,\n }, data: intervals.map(i => i.label),\n type: 'category' },\n yAxis: { max: Math.max(...intervals.map(i => i.count), 1), minInterval: 1, type: 'value' },\n };\n }, [intervals, organizationFilter, otherOrganizationCasesLabel, ownCasesLabel, theme]);\n\n const onLegendSelectChanged = useCallback((params: unknown) => {\n const { selected } = params as { selected?: Record<string, boolean> };\n if (!selected) {\n return;\n }\n const ownCasesSelected = selected[ownCasesLabel] ?? true;\n const otherOrganizationCasesSelected = selected[otherOrganizationCasesLabel] ?? true;\n if (ownCasesSelected && !otherOrganizationCasesSelected) {\n onOrganizationFilterChange('own');\n return;\n }\n if (!ownCasesSelected && otherOrganizationCasesSelected) {\n onOrganizationFilterChange('otherOrganization');\n return;\n }\n onOrganizationFilterChange('all');\n }, [onOrganizationFilterChange, otherOrganizationCasesLabel, ownCasesLabel]);\n\n const onEvents = useMemo(() => ({\n datazoom: onDataZoom,\n legendselectchanged: onLegendSelectChanged,\n mouseout: () => {\n setTooltipState(null);\n },\n mouseover: (event: unknown) => {\n const e = event as { event?: { event?: MouseEvent }; name?: string; value?: number };\n const mouseEvent = e.event?.event;\n if (mouseEvent && e.name !== null && e.name !== undefined) {\n setTooltipState({ content: `${e.name} (n=${e.value ?? 0})`, x: mouseEvent.clientX + 12, y: mouseEvent.clientY + 12 });\n }\n },\n }), [onDataZoom, onLegendSelectChanged]);\n\n const renderChart = useCallback(({ field: { onChange } }: { field: { onChange: (value: [string, string] | null) => void } }) => {\n onChangeRef.current = onChange;\n return (\n <EChartsReact\n echarts={echartsCore}\n notMerge\n onEvents={onEvents}\n option={option}\n style={{ height: 400 }}\n />\n );\n }, [onEvents, option]);\n\n return (\n <>\n <Controller\n control={control}\n name={name}\n render={renderChart}\n />\n {tooltipState && (\n <Box\n sx={{\n bgcolor: 'background.paper',\n border: '1px solid',\n borderColor: 'divider',\n borderRadius: 1,\n fontSize: 12,\n left: tooltipState.x,\n pointerEvents: 'none',\n position: 'fixed',\n px: 1,\n py: 0.5,\n top: tooltipState.y,\n zIndex: 'tooltip',\n }}\n >\n {tooltipState.content}\n </Box>\n )}\n </>\n );\n};\n","import {\n use,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useState,\n} from 'react';\nimport type { ReactElement } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport {\n mixed,\n object,\n string,\n} from 'yup';\nimport { yupResolver } from '@hookform/resolvers/yup';\nimport type { Resolver } from 'react-hook-form';\nimport {\n useForm,\n useWatch,\n} from 'react-hook-form';\nimport {\n Alert,\n AlertTitle,\n Box,\n Button,\n Divider,\n Typography,\n} from '@mui/material';\nimport { useStore } from 'zustand';\nimport { produce } from 'immer';\nimport {\n format,\n parseISO,\n} from 'date-fns';\nimport type {\n CaseDbCase,\n CaseDbCol,\n CaseDbCompleteCaseType,\n} from '@gen-epix/api-casedb';\nimport { CaseDbCaseApi } from '@gen-epix/api-casedb';\nimport type { DialogAction } from '@gen-epix/ui-core-components/components/Dialog';\nimport type {\n WithDialogRefMethods,\n WithDialogRenderProps,\n} from '@gen-epix/ui-core-components/hoc/withDialog';\nimport { ConfigService } from '@gen-epix/ui-client-common/classes/services/ConfigService';\nimport { QueryClientService } from '@gen-epix/ui-client-common/classes/services/QueryClientService';\nimport { ResponseHandler } from '@gen-epix/ui-client-common/components/ui/ResponseHandler';\nimport { useQueryMemo } from '@gen-epix/ui-client-common/hooks/useQueryMemo';\nimport { withDialog } from '@gen-epix/ui-core-components/hoc/withDialog';\nimport { TestIdUtil } from '@gen-epix/ui-core/utils/TestIdUtil';\nimport { GenericForm } from '@gen-epix/ui-core-form/components/helpers/GenericForm';\nimport type {\n AutoCompleteOption,\n FormFieldDefinition,\n} from '@gen-epix/ui-core-form/models/form';\nimport { FORM_FIELD_DEFINITION_TYPE } from '@gen-epix/ui-core-form/models/form';\nimport { SchemaUtil } from '@gen-epix/ui-core-form/utils/SchemaUtil';\nimport { DATE_FORMAT } from '@gen-epix/ui-core/constants/date';\n\nimport { CASEDB_QUERY_KEY } from '../../../constants/query';\nimport type { CaseDbConfig } from '../../../models/config';\nimport { DashboardStoreContext } from '../../../stores/dashboardStore';\nimport { TreeWidgetUtil } from '../../../utils/TreeWidgetUtil';\nimport type {\n CaseDbSimilarCaseWithIsOwnCase,\n FindSimilarCasesChartDataPoint,\n FindSimilarCasesOrganizationFilter,\n} from '../../../models/caseDb';\nimport type { TreeWidgetDataPersistable } from '../../../models/dashboard';\nimport { DASHBOARD_COMPONENT_NAME } from '../../../constants/dashboard';\n\nimport { FindSimilarCasesDialogDateRangeChart } from './FindSimilarCasesDialogDateRangeChart';\n\nexport interface FindSimilarCasesDialogOpenProps {\n allRows: CaseDbCase[];\n completeCaseType: CaseDbCompleteCaseType;\n selectedRows: CaseDbCase[];\n}\n\nexport interface FindSimilarCasesDialogProps extends WithDialogRenderProps<FindSimilarCasesDialogOpenProps> {\n //\n}\n\nexport type FindSimilarCasesDialogRefMethods = WithDialogRefMethods<FindSimilarCasesDialogProps, FindSimilarCasesDialogOpenProps>;\n\ntype FormFields = {\n dateRange: [string, string] | null;\n maxDistance: number;\n organizationFilter: FindSimilarCasesOrganizationFilter;\n treeColId: string;\n};\n\nexport const FindSimilarCasesDialog = withDialog<FindSimilarCasesDialogProps, FindSimilarCasesDialogOpenProps>((\n {\n onActionsChange,\n onClose,\n onTitleChange,\n openProps,\n }: FindSimilarCasesDialogProps,\n): ReactElement => {\n const { t } = useTranslation();\n const searchFormId = useId();\n const dashboardStore = use(DashboardStoreContext);\n const treeConfiguration = dashboardStore.getState().getWidgetDataPersistable<TreeWidgetDataPersistable>(DASHBOARD_COMPONENT_NAME.TREE).treeConfiguration;\n const setFindSimilarCasesResults = useStore(dashboardStore, (state) => state.setFindSimilarCasesResults);\n const findSimilarCasesResults = useStore(dashboardStore, (state) => state.findSimilarCasesResults);\n const treeConfigurations = useMemo(() => TreeWidgetUtil.getTreeConfigurations(openProps.completeCaseType), [openProps.completeCaseType]);\n const [formData, setFormData] = useState<FormFields>(null);\n\n const schema = useMemo(() => object<FormFields>().shape({\n dateRange: mixed<[string, string] | null>().nullable().default(null),\n maxDistance: SchemaUtil.number.required().positive().when('treeColId', ([treeColId], s) => {\n const currentTreeConfiguration = treeConfigurations.find(x => x.col.id === treeColId);\n if (!currentTreeConfiguration) {\n return s;\n }\n const sWithIntegerCheck = s.integer(t`Max distance must be an integer`);\n return sWithIntegerCheck.max(currentTreeConfiguration.geneticDistanceProtocol.seqdb_max_stored_distance || ConfigService.getInstance<CaseDbConfig>().config.epi.SEQDB_MAX_STORED_DISTANCE_FALLBACK);\n }),\n organizationFilter: string<FindSimilarCasesOrganizationFilter>().required().oneOf(['all', 'own', 'otherOrganization']),\n treeColId: string().required(),\n }), [t, treeConfigurations]);\n\n const treeOptions = useMemo<AutoCompleteOption<string>[]>(() => {\n const cols = new Set<CaseDbCol>();\n treeConfigurations.forEach(x => {\n cols.add(x.col);\n });\n return Array.from(cols).map(x => {\n return {\n label: x.label,\n value: x.id,\n } satisfies AutoCompleteOption<string>;\n });\n\n }, [treeConfigurations]);\n\n const formMethods = useForm<FormFields>({\n defaultValues: {\n dateRange: null,\n maxDistance: 0,\n organizationFilter: 'all',\n treeColId: treeConfiguration ? treeConfiguration.col.id : null,\n },\n resolver: yupResolver(schema, undefined, { raw: true }) as Resolver<FormFields>,\n });\n const { control, handleSubmit } = formMethods;\n\n const formValues = useWatch({ control });\n\n const searchFormFieldDefinitions = useMemo(() => {\n const currentTreeConfiguration = treeConfigurations.find(x => x.col.id === formValues.treeColId);\n return [\n {\n definition: FORM_FIELD_DEFINITION_TYPE.SELECT,\n label: t`Tree`,\n name: 'treeColId',\n options: treeOptions,\n } as const satisfies FormFieldDefinition<FormFields>,\n {\n definition: FORM_FIELD_DEFINITION_TYPE.NUMBER,\n label: t`Max distance`,\n max: currentTreeConfiguration?.geneticDistanceProtocol?.seqdb_max_stored_distance || ConfigService.getInstance<CaseDbConfig>().config.epi.SEQDB_MAX_STORED_DISTANCE_FALLBACK,\n min: 0,\n name: 'maxDistance',\n showSlider: true,\n step: 1,\n } as const satisfies FormFieldDefinition<FormFields>,\n ];\n }, [formValues.treeColId, t, treeConfigurations, treeOptions]);\n\n useEffect(() => {\n onTitleChange(t`Find similar cases`);\n }, [t, onTitleChange]);\n\n const onFormSubmit = useCallback((data: FormFields) => {\n formMethods.setValue('dateRange', null);\n setFormData({ ...data, dateRange: null });\n }, [formMethods]);\n\n const onOrganizationFilterChange = useCallback((organizationFilter: FindSimilarCasesOrganizationFilter) => {\n formMethods.setValue('organizationFilter', organizationFilter);\n }, [formMethods]);\n\n const query = useQueryMemo({\n enabled: !!formData,\n queryFn: async ({ signal }) => {\n const retrieveSimilarCasesResponse = await CaseDbCaseApi.getInstance().retrieveSimilarCases({\n case_ids: openProps.selectedRows.map(x => x.id),\n case_type_id: openProps.completeCaseType.id,\n genetic_distance_col_id: formData?.treeColId,\n max_distance: formData?.maxDistance,\n }, { signal });\n const retrieveIsOwnCasesResponse = await CaseDbCaseApi.getInstance().retrieveIsOwnCases({\n case_ids: retrieveSimilarCasesResponse.data.cases.map(x => x.id),\n case_type_id: openProps.completeCaseType.id,\n }, { signal });\n\n return retrieveSimilarCasesResponse.data.cases.map(caseDbCase => {\n return {\n ...caseDbCase,\n is_own_case: retrieveIsOwnCasesResponse.data[caseDbCase.id] || false,\n } satisfies CaseDbSimilarCaseWithIsOwnCase;\n });\n },\n queryKey: [QueryClientService.getInstance().getGenericKey(CASEDB_QUERY_KEY.SIMILAR_CASES), JSON.stringify({ formData, rowIds: openProps.selectedRows.map(row => row.id) })],\n });\n\n const organizationFilteredCases = useMemo<CaseDbSimilarCaseWithIsOwnCase[]>(() => {\n if (!query.data) {\n return [];\n }\n return query.data.filter(c => {\n switch (formValues.organizationFilter) {\n case 'all': return true;\n case 'otherOrganization': return !c.is_own_case;\n case 'own': return c.is_own_case;\n default: return true;\n }\n });\n }, [query.data, formValues.organizationFilter]);\n\n const chartData = useMemo<FindSimilarCasesChartDataPoint[]>(() => {\n const counts = new Map<string, FindSimilarCasesChartDataPoint>();\n for (const { is_own_case, timed_at } of query.data ?? []) {\n if (timed_at) {\n const currentCount = counts.get(timed_at) ?? {\n count: 0,\n date: timed_at,\n otherOrganizationCaseCount: 0,\n ownCaseCount: 0,\n };\n currentCount.count += 1;\n if (is_own_case) {\n currentCount.ownCaseCount += 1;\n } else {\n currentCount.otherOrganizationCaseCount += 1;\n }\n counts.set(timed_at, currentCount);\n }\n }\n return Array.from(counts.values())\n .sort((a, b) => a.date.localeCompare(b.date));\n }, [query.data]);\n\n const filteredCases = useMemo<CaseDbSimilarCaseWithIsOwnCase[]>(() => {\n const dateRange = formValues.dateRange;\n if (!dateRange) {\n return organizationFilteredCases;\n }\n const [startDate, endDate] = dateRange;\n return organizationFilteredCases.filter(c => {\n if (!c.timed_at) {\n return false;\n }\n const caseDateOnly = format(parseISO(c.timed_at), DATE_FORMAT.DATE);\n return caseDateOnly >= startDate && caseDateOnly <= endDate;\n });\n }, [formValues.dateRange, organizationFilteredCases]);\n\n const similarCaseIdsNotInLineListWithDateFilter = useMemo<string[]>(() => {\n const allRowIds = openProps.allRows.map(x => x.id);\n return filteredCases.reduce<string[]>((acc, caseIdAndDate) => {\n const caseId = caseIdAndDate.id;\n if (!allRowIds.includes(caseId)) {\n acc.push(caseId);\n }\n return acc;\n }, []);\n }, [filteredCases, openProps.allRows]);\n\n const similarCaseIdsAlreadyInLineListWithDateFilter = useMemo<string[]>(() => {\n const allRowIds = openProps.allRows.map(x => x.id);\n return filteredCases.reduce<string[]>((acc, caseIdAndDate) => {\n const caseId = caseIdAndDate.id;\n if (allRowIds.includes(caseId)) {\n acc.push(caseId);\n }\n return acc;\n }, []);\n }, [filteredCases, openProps.allRows]);\n\n const onAddToLineListButtonClick = useCallback(async () => {\n\n await setFindSimilarCasesResults(produce(findSimilarCasesResults, draft => {\n draft.push({\n colId: formData?.treeColId,\n distance: formData?.maxDistance,\n key: findSimilarCasesResults.length.toString(),\n originalCaseIds: openProps.allRows.map(x => x.id),\n similarCaseIds: similarCaseIdsNotInLineListWithDateFilter,\n });\n return draft;\n }));\n\n onClose();\n }, [setFindSimilarCasesResults, findSimilarCasesResults, onClose, similarCaseIdsNotInLineListWithDateFilter, formData?.maxDistance, formData?.treeColId, openProps.allRows]);\n\n useEffect(() => {\n const actions: DialogAction[] = [];\n actions.push({\n ...TestIdUtil.createAttributes('FindSimilarCasesDialog-closeButton'),\n color: 'primary',\n label: t`Close`,\n onClick: onClose,\n variant: 'outlined',\n });\n actions.push({\n ...TestIdUtil.createAttributes('FindSimilarCasesDialog-addSimilarCasesButton'),\n color: 'secondary',\n disabled: similarCaseIdsNotInLineListWithDateFilter.length === 0,\n label: t('Add {{count}} similar cases to line list', { count: similarCaseIdsNotInLineListWithDateFilter.length }),\n onClick: onAddToLineListButtonClick,\n variant: 'contained',\n });\n onActionsChange(actions);\n }, [onActionsChange, onAddToLineListButtonClick, onClose, similarCaseIdsNotInLineListWithDateFilter.length, t]);\n\n return (\n <Box>\n <Box\n sx={{\n marginBottom: 2,\n }}\n >\n <Typography>\n {t('Select a tree and maximum distance to find similar cases for the {{count}} selected cases.', { count: openProps.selectedRows.length })}\n </Typography>\n </Box>\n <Box>\n <GenericForm<FormFields>\n formFieldDefinitions={searchFormFieldDefinitions}\n formId={searchFormId}\n formMethods={formMethods}\n onSubmit={handleSubmit(onFormSubmit)}\n schema={schema}\n />\n </Box>\n <Box\n sx={{\n display: 'flex',\n justifyContent: 'flex-end',\n marginTop: 2,\n }}\n >\n <Button\n color={'secondary'}\n form={searchFormId}\n type={'submit'}\n variant={'contained'}\n >\n {formData ? t`Search again` : t`Search`}\n </Button>\n </Box>\n <Divider sx={{ marginY: 2 }} />\n <ResponseHandler\n inlineSpinner\n loadables={query}\n >\n {query.data && (\n <>\n <Box\n sx={{\n marginY: 2,\n }}\n >\n <Alert\n severity={query.data.length > 0 ? 'success' : 'warning'}\n >\n <AlertTitle>\n {t('Found {{count}} similar cases with a distance of {{distance}}.', { count: query.data.length, distance: formData?.maxDistance })}\n </AlertTitle>\n </Alert>\n </Box>\n {chartData.length > 0 && (\n <Box\n sx={{\n marginY: 2,\n }}\n >\n <FindSimilarCasesDialogDateRangeChart<FormFields>\n control={control}\n data={chartData}\n name={'dateRange'}\n onOrganizationFilterChange={onOrganizationFilterChange}\n organizationFilter={formValues.organizationFilter}\n />\n </Box>\n )}\n <Box\n sx={{\n marginY: 2,\n }}\n >\n <Alert\n severity={'info'}\n >\n <AlertTitle>\n {t('{{count}} similar cases can be added to the line list.', { count: similarCaseIdsNotInLineListWithDateFilter.length })}\n </AlertTitle>\n </Alert>\n </Box>\n {similarCaseIdsAlreadyInLineListWithDateFilter.length > 0 && (\n <Alert\n severity={'warning'}\n >\n <AlertTitle>\n {t('{{count}} similar cases found that are already in the line list.', { count: similarCaseIdsAlreadyInLineListWithDateFilter.length })}\n </AlertTitle>\n </Alert>\n )}\n </>\n )}\n </ResponseHandler>\n </Box>\n );\n}, {\n defaultTitle: '',\n fullWidth: true,\n maxWidth: 'md',\n testId: 'FindSimilarCasesDialog',\n});\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"HomePageTrends.js","names":[],"sources":["../../src/components/ui/HomePageTrends/HomePageTrendCard.tsx","../../src/components/ui/HomePageTrends/HomePageTrends.tsx"],"sourcesContent":["import { TestIdUtil } from '@gen-epix/ui-core/utils/TestIdUtil';\nimport {\n Box,\n Button,\n Card,\n CardActions,\n CardContent,\n darken,\n Typography,\n useTheme,\n} from '@mui/material';\nimport { t } from 'i18next';\nimport { useCallback } from 'react';\n\n\nexport type HomagePageTrendCardProps = {\n readonly callback?: () => void;\n readonly callbackLabel?: string;\n readonly diffPercentage: number;\n readonly header: string;\n readonly sinceLabel: string;\n readonly value: number;\n};\n\nexport const HomePageTrendCard = ({ callback, callbackLabel, diffPercentage, header, sinceLabel, value }: HomagePageTrendCardProps) => {\n const theme = useTheme();\n\n const onCallbackButtonClick = useCallback(() => {\n callback();\n }, [callback]);\n\n return (\n <Card\n {...TestIdUtil.createAttributes('HomePageTrendCard', { header })}\n elevation={1}\n sx={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'space-between',\n }}\n >\n <CardContent\n sx={{\n paddingBottom: 0,\n }}\n >\n <Typography\n component={'h3'}\n sx={{\n display: 'block',\n fontSize: '1rem',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {header}\n </Typography>\n <Typography\n {...TestIdUtil.createAttributes('HomePageTrendCard-value')}\n sx={{ fontSize: '1.8rem' }}\n >\n {value.toLocaleString()}\n </Typography>\n </CardContent>\n {callback && callbackLabel && (\n <CardActions\n sx={{\n justifyContent: 'flex-end',\n }}\n >\n <Button\n {...TestIdUtil.createAttributes('HomePageTrendCard-button')}\n onClick={onCallbackButtonClick}\n size={'small'}\n variant={'outlined'}\n >\n {callbackLabel}\n </Button>\n </CardActions>\n )}\n <Box\n sx={{\n background: theme.palette.grey['100'],\n justifyContent: 'flex-end',\n padding: 1,\n }}\n >\n <Box\n sx={{\n marginX: 1,\n }}\n >\n {diffPercentage === 0 && (\n <Typography>\n <Box\n {...TestIdUtil.createAttributes('HomePageTrendCard-diffPercentage-label')}\n component={'span'}\n sx={{\n color: theme.palette.text.secondary,\n fontWeight: 'bold',\n }}\n >\n {t('No change')}\n </Box>\n </Typography>\n )}\n {diffPercentage !== 0 && (\n <Typography>\n <Box\n {...TestIdUtil.createAttributes('HomePageTrendCard-diffPercentage')}\n component={'span'}\n sx={{\n color: diffPercentage > 0 ? darken(theme.palette.success.main, 0.4) : theme.palette.error.main,\n fontWeight: 'bold',\n }}\n >\n {diffPercentage > 0 ? '+' : ''}\n {diffPercentage.toLocaleString()}\n {'%'}\n\n </Box>\n <Box\n {...TestIdUtil.createAttributes('HomePageTrendCard-diffPercentage-label')}\n component={'span'}\n >\n {' '}\n {sinceLabel}\n </Box>\n </Typography>\n )}\n </Box>\n </Box>\n </Card>\n );\n};\n","import {\n Box,\n Button,\n Skeleton,\n Typography,\n useTheme,\n} from '@mui/material';\nimport {\n useCallback,\n useMemo,\n} from 'react';\nimport { useTranslation } from 'react-i18next';\nimport round from 'lodash/round';\nimport type {\n CaseDbApiPermission,\n CaseDbCaseStats,\n CaseDbEpiFilter,\n CaseDbRetrieveCaseTypeStatsRequestBody,\n CaseDbTypedDatetimeRangeFilter,\n} from '@gen-epix/api-casedb';\nimport {\n CaseDbCaseApi,\n CaseDbCommandName,\n CaseDbPermissionType,\n} from '@gen-epix/api-casedb';\nimport { AxiosUtil } from '@gen-epix/ui-client-common/utils/AxiosUtil';\nimport { ConfigService } from '@gen-epix/ui-client-common/classes/services/ConfigService';\nimport { LoadableUtil } from '@gen-epix/ui-client-common/utils/LoadableUtil';\nimport { QueryClientService } from '@gen-epix/ui-client-common/classes/services/QueryClientService';\nimport { ResponseHandler } from '@gen-epix/ui-client-common/components/ui/ResponseHandler';\nimport { RouterService } from '@gen-epix/ui-client-common/classes/services/RouterService';\nimport { useQueryMemo } from '@gen-epix/ui-client-common/hooks/useQueryMemo';\nimport { withPermissions } from '@gen-epix/ui-client-common/hoc/withPermissions';\nimport { useArray } from '@gen-epix/ui-core/hooks/useArray';\n\nimport { useCaseSetsQuery } from '../../../dataHooks/useCaseSetsQuery';\nimport { useCaseTypeMapQuery } from '../../../dataHooks/useCaseTypesQuery';\nimport { CaseTypeUtil } from '../../../utils/CaseTypeUtil';\nimport type { CaseDbConfig } from '../../../models/config';\nimport { CASEDB_QUERY_KEY } from '../../../constants/query';\n\nimport { HomePageTrendCard } from './HomePageTrendCard';\n\ntype CaseStatsWithDiff = {\n diffPercentage: number;\n} & CaseDbCaseStats;\n\ntype Statistic = {\n callback?: () => void;\n callbackLabel?: string;\n diffPercentage: number;\n header: string;\n value: number;\n};\n\nexport const HomePageTrends = withPermissions<CaseDbApiPermission>(() => {\n const theme = useTheme();\n const { t } = useTranslation();\n\n const dateTimeRangeFilter = useMemo<CaseDbTypedDatetimeRangeFilter>(() => ({\n type: 'DATETIME_RANGE',\n upper_bound: ConfigService.getInstance<CaseDbConfig>().config.trends.homePage.getSinceDate(),\n upper_bound_censor: '<=',\n } satisfies CaseDbTypedDatetimeRangeFilter), []);\n\n const caseSetQueryFilter = useMemo<CaseDbEpiFilter>(() => ({\n key: 'case_set_date',\n type: 'DATETIME_RANGE',\n upper_bound: ConfigService.getInstance<CaseDbConfig>().config.trends.homePage.getSinceDate(),\n upper_bound_censor: '<=',\n } satisfies CaseDbEpiFilter), []);\n\n const caseTypeStatsQueryNow = useQueryMemo({\n queryFn: async ({ signal }) => {\n const response = await CaseDbCaseApi.getInstance().retrieveCaseTypeStats({}, { signal });\n return response.data;\n },\n queryKey: QueryClientService.getInstance().getGenericKey(CASEDB_QUERY_KEY.CASE_TYPE_STATS),\n });\n\n const retrieveTypeCaseStatsRequestBody = useMemo<CaseDbRetrieveCaseTypeStatsRequestBody>(() => {\n if (!caseTypeStatsQueryNow.data) {\n return undefined;\n }\n return {\n case_type_ids: caseTypeStatsQueryNow.data?.filter(stat => stat.n_cases > 0).map(stat => stat.case_type_id),\n datetime_range_filter: dateTimeRangeFilter,\n };\n }, [dateTimeRangeFilter, caseTypeStatsQueryNow.data]);\n\n const caseTypeStatsQueryPast = useQueryMemo({\n enabled: !!retrieveTypeCaseStatsRequestBody,\n queryFn: async ({ signal }) => {\n const response = await CaseDbCaseApi.getInstance().retrieveCaseTypeStats(retrieveTypeCaseStatsRequestBody ?? {}, { signal });\n return response.data;\n },\n queryKey: QueryClientService.getInstance().getGenericKey(CASEDB_QUERY_KEY.CASE_TYPE_STATS, retrieveTypeCaseStatsRequestBody ?? {}),\n });\n const caseSetsNowQuery = useCaseSetsQuery();\n const caseTypeMapQuery = useCaseTypeMapQuery();\n\n const { data: caseSetsThenData, ...caseSetsThenQuery } = useQueryMemo({\n queryFn: async ({ signal }) => {\n const response = await CaseDbCaseApi.getInstance().caseSetsPostQuery(caseSetQueryFilter, null, null, { signal });\n return response.data;\n },\n queryKey: QueryClientService.getInstance().getGenericKey(CASEDB_QUERY_KEY.CASE_SETS, caseSetQueryFilter),\n });\n\n const loadables = useArray([\n caseTypeStatsQueryNow,\n caseTypeStatsQueryPast,\n caseTypeMapQuery,\n caseSetsNowQuery,\n caseSetsThenQuery,\n ]);\n\n const statistics = useMemo<Statistic[]>(() => {\n const s: Statistic[] = [];\n\n if (LoadableUtil.isSomeLoading(loadables) || LoadableUtil.hasSomeError(loadables) || !caseTypeStatsQueryNow.data?.length || !caseTypeStatsQueryPast.data?.length) {\n return s;\n }\n\n const nowTotalCases = caseTypeStatsQueryNow.data?.reduce((acc, stat) => acc + stat.n_cases, 0) ?? 0;\n const thenTotalCases = caseTypeStatsQueryPast.data?.reduce((acc, stat) => acc + stat.n_cases, 0) ?? 0;\n\n s.push(\n {\n callback: async () => {\n await RouterService.getInstance().router.navigate('/cases');\n },\n callbackLabel: t`View all cases`,\n diffPercentage: round((nowTotalCases - thenTotalCases) / (thenTotalCases || 1) * 100, 2),\n header: 'Cases',\n value: nowTotalCases,\n },\n );\n\n const numberOfCaseSetsNow = caseSetsNowQuery.data.length;\n const numberOfCaseSetsThen = caseSetsThenData.length;\n s.push(\n {\n callback: async () => {\n await RouterService.getInstance().router.navigate('/events');\n },\n callbackLabel: t`View all events`,\n diffPercentage: round((numberOfCaseSetsNow - numberOfCaseSetsThen) / (numberOfCaseSetsThen || 1) * 100, 2),\n header: 'Events',\n value: numberOfCaseSetsNow,\n },\n );\n\n const caseStatsThenByCaseTypeId = new Map<string, CaseDbCaseStats>(caseTypeStatsQueryPast.data?.map(stat => [stat.case_type_id, stat]));\n const sortedStats = caseTypeStatsQueryNow?.data?.map<CaseStatsWithDiff>(stat => {\n const nowNCases = stat.n_cases;\n const thenNCases = caseStatsThenByCaseTypeId.get(stat.case_type_id)?.n_cases ?? 0;\n const diff = nowNCases - thenNCases;\n let diffPercentage: number;\n if (thenNCases > 0) {\n diffPercentage = round((diff / thenNCases) * 100, 2);\n } else {\n diffPercentage = 0;\n }\n return ({\n ...stat,\n diffPercentage,\n });\n }).sort((a, b) => b.diffPercentage - a.diffPercentage);\n\n for (let i = 0; i < 6; i += 1) {\n if (sortedStats?.[i]) {\n const caseType = caseTypeMapQuery.map?.get(sortedStats?.[i].case_type_id ?? '');\n const caseTypeName = caseType?.name;\n if (caseTypeName) {\n s.push(\n {\n callback: async () => {\n await RouterService.getInstance().router.navigate(CaseTypeUtil.createCaseTypeLink(caseType));\n },\n callbackLabel: t`View cases`,\n diffPercentage: sortedStats[i].diffPercentage,\n header: t('{{caseTypeName}} cases', { caseTypeName, number: i + 1 }),\n value: sortedStats[i].n_cases,\n },\n );\n }\n }\n }\n\n return s;\n }, [caseSetsNowQuery.data?.length, caseSetsThenData?.length, caseTypeMapQuery.map, caseTypeStatsQueryNow.data, caseTypeStatsQueryPast.data, loadables, t]);\n\n const onViewMoreTrendsButtonClick = useCallback(async () => {\n await RouterService.getInstance().router.navigate('/trends');\n }, []);\n\n if (AxiosUtil.isAxiosForbiddenError(LoadableUtil.findFirstError(loadables))) {\n return null;\n }\n\n return (\n <Box>\n <Box\n sx={{\n marginBottom: 1,\n }}\n >\n <Box\n sx={{\n alignItems: 'center',\n display: 'flex',\n justifyContent: 'space-between',\n }}\n >\n <Box>\n <Typography\n variant={'h2'}\n >\n {t`Trends summary`}\n </Typography>\n </Box>\n <Box>\n <Button\n color={'primary'}\n disabled\n onClick={onViewMoreTrendsButtonClick}\n variant={'outlined'}\n >\n {t`View more trends`}\n </Button>\n </Box>\n </Box>\n </Box>\n <ResponseHandler\n inlineSpinner\n loadables={loadables}\n loadingContent={(\n <Box\n sx={{\n display: 'grid',\n gap: 2,\n gridTemplateColumns: 'repeat(1, 1fr)',\n marginBottom: 2,\n [theme.breakpoints.up('md')]: {\n gridTemplateColumns: 'repeat(4, 1fr)',\n },\n [theme.breakpoints.up('sm')]: {\n gridTemplateColumns: 'repeat(2, 1fr)',\n },\n }}\n >\n {[...Array<string>(8)].map((_, index) => (\n <Box\n // eslint-disable-next-line @eslint-react/no-array-index-key\n key={index}\n sx={{\n alignItems: 'center',\n display: 'flex',\n height: 165.75,\n justifyContent: 'center',\n }}\n >\n <Skeleton\n animation={'pulse'}\n height={'100%'}\n variant={'rounded'}\n width={'100%'}\n />\n </Box>\n ))}\n </Box>\n )}\n shouldHideActionButtons\n >\n {statistics.length === 0 && (\n <Box\n sx={{\n marginY: 2,\n }}\n >\n <Typography>\n {t`No trends available.`}\n </Typography>\n </Box>\n )}\n {statistics.length > 0 && (\n <Box\n sx={{\n display: 'grid',\n gap: 2,\n gridTemplateColumns: 'repeat(1, 1fr)',\n marginBottom: 2,\n [theme.breakpoints.up('md')]: {\n gridTemplateColumns: 'repeat(4, 1fr)',\n },\n [theme.breakpoints.up('sm')]: {\n gridTemplateColumns: 'repeat(2, 1fr)',\n },\n }}\n >\n {statistics.map(statistic => (\n <HomePageTrendCard\n callback={statistic.callback}\n callbackLabel={statistic.callbackLabel}\n diffPercentage={statistic.diffPercentage}\n header={statistic.header}\n key={statistic.header}\n sinceLabel={ConfigService.getInstance<CaseDbConfig>().config.trends.homePage.getSinceLabel(t)}\n value={statistic.value}\n />\n ))}\n </Box>\n )}\n </ResponseHandler>\n </Box>\n );\n}, {\n requiredPermissions: [\n { command_name: CaseDbCommandName.RetrieveCaseTypeStatsCommand, permission_type: CaseDbPermissionType.EXECUTE },\n { command_name: CaseDbCommandName.CaseSetCrudCommand, permission_type: CaseDbPermissionType.READ },\n { command_name: CaseDbCommandName.CaseTypeCrudCommand, permission_type: CaseDbPermissionType.READ },\n ],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"HomePageTrends.js","names":[],"sources":["../../src/components/ui/HomePageTrends/HomePageTrendCard.tsx","../../src/components/ui/HomePageTrends/HomePageTrends.tsx"],"sourcesContent":["import { TestIdUtil } from '@gen-epix/ui-core/utils/TestIdUtil';\nimport {\n Box,\n Button,\n Card,\n CardActions,\n CardContent,\n darken,\n Typography,\n useTheme,\n} from '@mui/material';\nimport { t } from 'i18next';\nimport { useCallback } from 'react';\n\n\nexport type HomagePageTrendCardProps = {\n readonly callback?: () => void;\n readonly callbackLabel?: string;\n readonly diffPercentage: number;\n readonly header: string;\n readonly sinceLabel: string;\n readonly value: number;\n};\n\nexport const HomePageTrendCard = ({ callback, callbackLabel, diffPercentage, header, sinceLabel, value }: HomagePageTrendCardProps) => {\n const theme = useTheme();\n\n const onCallbackButtonClick = useCallback(() => {\n callback();\n }, [callback]);\n\n return (\n <Card\n {...TestIdUtil.createAttributes('HomePageTrendCard', { header })}\n elevation={1}\n sx={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'space-between',\n }}\n >\n <CardContent\n sx={{\n paddingBottom: 0,\n }}\n >\n <Typography\n component={'h3'}\n sx={{\n display: 'block',\n fontSize: '1rem',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {header}\n </Typography>\n <Typography\n {...TestIdUtil.createAttributes('HomePageTrendCard-value')}\n sx={{ fontSize: '1.8rem' }}\n >\n {value.toLocaleString()}\n </Typography>\n </CardContent>\n {callback && callbackLabel && (\n <CardActions\n sx={{\n justifyContent: 'flex-end',\n }}\n >\n <Button\n {...TestIdUtil.createAttributes('HomePageTrendCard-button')}\n onClick={onCallbackButtonClick}\n size={'small'}\n variant={'outlined'}\n >\n {callbackLabel}\n </Button>\n </CardActions>\n )}\n <Box\n sx={{\n background: theme.palette.grey['100'],\n justifyContent: 'flex-end',\n padding: 1,\n }}\n >\n <Box\n sx={{\n marginX: 1,\n }}\n >\n {diffPercentage === 0 && (\n <Typography>\n <Box\n {...TestIdUtil.createAttributes('HomePageTrendCard-diffPercentage-label')}\n component={'span'}\n sx={{\n color: theme.palette.text.secondary,\n fontWeight: 'bold',\n }}\n >\n {t('No change')}\n </Box>\n </Typography>\n )}\n {diffPercentage !== 0 && (\n <Typography>\n <Box\n {...TestIdUtil.createAttributes('HomePageTrendCard-diffPercentage')}\n component={'span'}\n sx={{\n color: diffPercentage > 0 ? darken(theme.palette.success.main, 0.4) : theme.palette.error.main,\n fontWeight: 'bold',\n }}\n >\n {diffPercentage > 0 ? '+' : ''}\n {diffPercentage.toLocaleString()}\n {'%'}\n\n </Box>\n <Box\n {...TestIdUtil.createAttributes('HomePageTrendCard-diffPercentage-label')}\n component={'span'}\n >\n {' '}\n {sinceLabel}\n </Box>\n </Typography>\n )}\n </Box>\n </Box>\n </Card>\n );\n};\n","import {\n Box,\n Button,\n Skeleton,\n Typography,\n useTheme,\n} from '@mui/material';\nimport {\n useCallback,\n useMemo,\n} from 'react';\nimport { useTranslation } from 'react-i18next';\nimport round from 'lodash/round';\nimport type {\n CaseDbApiPermission,\n CaseDbCaseStats,\n CaseDbDatetimeRangeFilter,\n CaseDbRetrieveCaseTypeStatsRequestBody,\n} from '@gen-epix/api-casedb';\nimport {\n CaseDbCaseApi,\n CaseDbCommandName,\n CaseDbPermissionType,\n} from '@gen-epix/api-casedb';\nimport { AxiosUtil } from '@gen-epix/ui-client-common/utils/AxiosUtil';\nimport { ConfigService } from '@gen-epix/ui-client-common/classes/services/ConfigService';\nimport { LoadableUtil } from '@gen-epix/ui-client-common/utils/LoadableUtil';\nimport { QueryClientService } from '@gen-epix/ui-client-common/classes/services/QueryClientService';\nimport { ResponseHandler } from '@gen-epix/ui-client-common/components/ui/ResponseHandler';\nimport { RouterService } from '@gen-epix/ui-client-common/classes/services/RouterService';\nimport { useQueryMemo } from '@gen-epix/ui-client-common/hooks/useQueryMemo';\nimport { withPermissions } from '@gen-epix/ui-client-common/hoc/withPermissions';\nimport { useArray } from '@gen-epix/ui-core/hooks/useArray';\n\nimport { useCaseSetsQuery } from '../../../dataHooks/useCaseSetsQuery';\nimport { useCaseTypeMapQuery } from '../../../dataHooks/useCaseTypesQuery';\nimport { CaseTypeUtil } from '../../../utils/CaseTypeUtil';\nimport type { CaseDbConfig } from '../../../models/config';\nimport { CASEDB_QUERY_KEY } from '../../../constants/query';\n\nimport { HomePageTrendCard } from './HomePageTrendCard';\n\ntype CaseStatsWithDiff = {\n diffPercentage: number;\n} & CaseDbCaseStats;\n\ntype Statistic = {\n callback?: () => void;\n callbackLabel?: string;\n diffPercentage: number;\n header: string;\n value: number;\n};\n\nexport const HomePageTrends = withPermissions<CaseDbApiPermission>(() => {\n const theme = useTheme();\n const { t } = useTranslation();\n\n const dateTimeRangeFilter = useMemo<CaseDbDatetimeRangeFilter>(() => ({\n type: 'DATETIME_RANGE',\n upper_bound: ConfigService.getInstance<CaseDbConfig>().config.trends.homePage.getSinceDate(),\n upper_bound_censor: '<=',\n } satisfies CaseDbDatetimeRangeFilter), []);\n\n const caseSetQueryFilter = useMemo<CaseDbDatetimeRangeFilter>(() => ({\n key: 'case_set_date',\n type: 'DATETIME_RANGE',\n upper_bound: ConfigService.getInstance<CaseDbConfig>().config.trends.homePage.getSinceDate(),\n upper_bound_censor: '<=',\n } satisfies CaseDbDatetimeRangeFilter), []);\n\n const caseTypeStatsQueryNow = useQueryMemo({\n queryFn: async ({ signal }) => {\n const response = await CaseDbCaseApi.getInstance().retrieveCaseTypeStats({}, { signal });\n return response.data;\n },\n queryKey: QueryClientService.getInstance().getGenericKey(CASEDB_QUERY_KEY.CASE_TYPE_STATS),\n });\n\n const retrieveTypeCaseStatsRequestBody = useMemo<CaseDbRetrieveCaseTypeStatsRequestBody>(() => {\n if (!caseTypeStatsQueryNow.data) {\n return undefined;\n }\n return {\n case_type_ids: caseTypeStatsQueryNow.data?.filter(stat => stat.n_cases > 0).map(stat => stat.case_type_id),\n datetime_range_filter: dateTimeRangeFilter,\n };\n }, [dateTimeRangeFilter, caseTypeStatsQueryNow.data]);\n\n const caseTypeStatsQueryPast = useQueryMemo({\n enabled: !!retrieveTypeCaseStatsRequestBody,\n queryFn: async ({ signal }) => {\n const response = await CaseDbCaseApi.getInstance().retrieveCaseTypeStats(retrieveTypeCaseStatsRequestBody ?? {}, { signal });\n return response.data;\n },\n queryKey: QueryClientService.getInstance().getGenericKey(CASEDB_QUERY_KEY.CASE_TYPE_STATS, retrieveTypeCaseStatsRequestBody ?? {}),\n });\n const caseSetsNowQuery = useCaseSetsQuery();\n const caseTypeMapQuery = useCaseTypeMapQuery();\n\n const { data: caseSetsThenData, ...caseSetsThenQuery } = useQueryMemo({\n queryFn: async ({ signal }) => {\n const response = await CaseDbCaseApi.getInstance().caseSetsPostQuery(caseSetQueryFilter, null, null, { signal });\n return response.data;\n },\n queryKey: QueryClientService.getInstance().getGenericKey(CASEDB_QUERY_KEY.CASE_SETS, caseSetQueryFilter),\n });\n\n const loadables = useArray([\n caseTypeStatsQueryNow,\n caseTypeStatsQueryPast,\n caseTypeMapQuery,\n caseSetsNowQuery,\n caseSetsThenQuery,\n ]);\n\n const statistics = useMemo<Statistic[]>(() => {\n const s: Statistic[] = [];\n\n if (LoadableUtil.isSomeLoading(loadables) || LoadableUtil.hasSomeError(loadables) || !caseTypeStatsQueryNow.data?.length || !caseTypeStatsQueryPast.data?.length) {\n return s;\n }\n\n const nowTotalCases = caseTypeStatsQueryNow.data?.reduce((acc, stat) => acc + stat.n_cases, 0) ?? 0;\n const thenTotalCases = caseTypeStatsQueryPast.data?.reduce((acc, stat) => acc + stat.n_cases, 0) ?? 0;\n\n s.push(\n {\n callback: async () => {\n await RouterService.getInstance().router.navigate('/cases');\n },\n callbackLabel: t`View all cases`,\n diffPercentage: round((nowTotalCases - thenTotalCases) / (thenTotalCases || 1) * 100, 2),\n header: 'Cases',\n value: nowTotalCases,\n },\n );\n\n const numberOfCaseSetsNow = caseSetsNowQuery.data.length;\n const numberOfCaseSetsThen = caseSetsThenData.length;\n s.push(\n {\n callback: async () => {\n await RouterService.getInstance().router.navigate('/events');\n },\n callbackLabel: t`View all events`,\n diffPercentage: round((numberOfCaseSetsNow - numberOfCaseSetsThen) / (numberOfCaseSetsThen || 1) * 100, 2),\n header: 'Events',\n value: numberOfCaseSetsNow,\n },\n );\n\n const caseStatsThenByCaseTypeId = new Map<string, CaseDbCaseStats>(caseTypeStatsQueryPast.data?.map(stat => [stat.case_type_id, stat]));\n const sortedStats = caseTypeStatsQueryNow?.data?.map<CaseStatsWithDiff>(stat => {\n const nowNCases = stat.n_cases;\n const thenNCases = caseStatsThenByCaseTypeId.get(stat.case_type_id)?.n_cases ?? 0;\n const diff = nowNCases - thenNCases;\n let diffPercentage: number;\n if (thenNCases > 0) {\n diffPercentage = round((diff / thenNCases) * 100, 2);\n } else {\n diffPercentage = 0;\n }\n return ({\n ...stat,\n diffPercentage,\n });\n }).sort((a, b) => b.diffPercentage - a.diffPercentage);\n\n for (let i = 0; i < 6; i += 1) {\n if (sortedStats?.[i]) {\n const caseType = caseTypeMapQuery.map?.get(sortedStats?.[i].case_type_id ?? '');\n const caseTypeName = caseType?.name;\n if (caseTypeName) {\n s.push(\n {\n callback: async () => {\n await RouterService.getInstance().router.navigate(CaseTypeUtil.createCaseTypeLink(caseType));\n },\n callbackLabel: t`View cases`,\n diffPercentage: sortedStats[i].diffPercentage,\n header: t('{{caseTypeName}} cases', { caseTypeName, number: i + 1 }),\n value: sortedStats[i].n_cases,\n },\n );\n }\n }\n }\n\n return s;\n }, [caseSetsNowQuery.data?.length, caseSetsThenData?.length, caseTypeMapQuery.map, caseTypeStatsQueryNow.data, caseTypeStatsQueryPast.data, loadables, t]);\n\n const onViewMoreTrendsButtonClick = useCallback(async () => {\n await RouterService.getInstance().router.navigate('/trends');\n }, []);\n\n if (AxiosUtil.isAxiosForbiddenError(LoadableUtil.findFirstError(loadables))) {\n return null;\n }\n\n return (\n <Box>\n <Box\n sx={{\n marginBottom: 1,\n }}\n >\n <Box\n sx={{\n alignItems: 'center',\n display: 'flex',\n justifyContent: 'space-between',\n }}\n >\n <Box>\n <Typography\n variant={'h2'}\n >\n {t`Trends summary`}\n </Typography>\n </Box>\n <Box>\n <Button\n color={'primary'}\n disabled\n onClick={onViewMoreTrendsButtonClick}\n variant={'outlined'}\n >\n {t`View more trends`}\n </Button>\n </Box>\n </Box>\n </Box>\n <ResponseHandler\n inlineSpinner\n loadables={loadables}\n loadingContent={(\n <Box\n sx={{\n display: 'grid',\n gap: 2,\n gridTemplateColumns: 'repeat(1, 1fr)',\n marginBottom: 2,\n [theme.breakpoints.up('md')]: {\n gridTemplateColumns: 'repeat(4, 1fr)',\n },\n [theme.breakpoints.up('sm')]: {\n gridTemplateColumns: 'repeat(2, 1fr)',\n },\n }}\n >\n {[...Array<string>(8)].map((_, index) => (\n <Box\n // eslint-disable-next-line @eslint-react/no-array-index-key\n key={index}\n sx={{\n alignItems: 'center',\n display: 'flex',\n height: 165.75,\n justifyContent: 'center',\n }}\n >\n <Skeleton\n animation={'pulse'}\n height={'100%'}\n variant={'rounded'}\n width={'100%'}\n />\n </Box>\n ))}\n </Box>\n )}\n shouldHideActionButtons\n >\n {statistics.length === 0 && (\n <Box\n sx={{\n marginY: 2,\n }}\n >\n <Typography>\n {t`No trends available.`}\n </Typography>\n </Box>\n )}\n {statistics.length > 0 && (\n <Box\n sx={{\n display: 'grid',\n gap: 2,\n gridTemplateColumns: 'repeat(1, 1fr)',\n marginBottom: 2,\n [theme.breakpoints.up('md')]: {\n gridTemplateColumns: 'repeat(4, 1fr)',\n },\n [theme.breakpoints.up('sm')]: {\n gridTemplateColumns: 'repeat(2, 1fr)',\n },\n }}\n >\n {statistics.map(statistic => (\n <HomePageTrendCard\n callback={statistic.callback}\n callbackLabel={statistic.callbackLabel}\n diffPercentage={statistic.diffPercentage}\n header={statistic.header}\n key={statistic.header}\n sinceLabel={ConfigService.getInstance<CaseDbConfig>().config.trends.homePage.getSinceLabel(t)}\n value={statistic.value}\n />\n ))}\n </Box>\n )}\n </ResponseHandler>\n </Box>\n );\n}, {\n requiredPermissions: [\n { command_name: CaseDbCommandName.RetrieveCaseTypeStatsCommand, permission_type: CaseDbPermissionType.EXECUTE },\n { command_name: CaseDbCommandName.CaseSetCrudCommand, permission_type: CaseDbPermissionType.READ },\n { command_name: CaseDbCommandName.CaseTypeCrudCommand, permission_type: CaseDbPermissionType.READ },\n ],\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;mCAwBa,KAAqB,EAAE,aAAU,kBAAe,mBAAgB,WAAQ,eAAY,eAAsC;CACrI,IAAM,IAAQ,EAAS,GAEjB,IAAwB,QAAkB;EAC9C,EAAS;CACX,GAAG,CAAC,CAAQ,CAAC;CAEb,OACE,kBAAC,GAAD;EACE,GAAI,EAAW,iBAAiB,qBAAqB,EAAE,UAAO,CAAC;EAC/D,WAAW;EACX,IAAI;GACF,SAAS;GACT,eAAe;GACf,gBAAgB;EAClB;EAPF,UAAA;GASE,kBAAC,GAAD;IACE,IAAI,EACF,eAAe,EACjB;IAHF,UAAA,CAKE,kBAAC,GAAD;KACE,WAAW;KACX,IAAI;MACF,SAAS;MACT,UAAU;MACV,UAAU;MACV,cAAc;MACd,YAAY;KACd;KAEC,UAAA;IACS,CAAA,GACZ,kBAAC,GAAD;KACE,GAAI,EAAW,iBAAiB,yBAAyB;KACzD,IAAI,EAAE,UAAU,SAAS;KAExB,UAAA,EAAM,eAAe;IACZ,CAAA,CACD;;GACZ,KAAY,KACX,kBAAC,GAAD;IACE,IAAI,EACF,gBAAgB,WAClB;IAEA,UAAA,kBAAC,GAAD;KACE,GAAI,EAAW,iBAAiB,0BAA0B;KAC1D,SAAS;KACT,MAAM;KACN,SAAS;KAER,UAAA;IACK,CAAA;GACG,CAAA;GAEf,kBAAC,GAAD;IACE,IAAI;KACF,YAAY,EAAM,QAAQ,KAAK;KAC/B,gBAAgB;KAChB,SAAS;IACX;IAEA,UAAA,kBAAC,GAAD;KACE,IAAI,EACF,SAAS,EACX;KAHF,UAAA,CAKG,MAAmB,KAClB,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MACE,GAAI,EAAW,iBAAiB,wCAAwC;MACxE,WAAW;MACX,IAAI;OACF,OAAO,EAAM,QAAQ,KAAK;OAC1B,YAAY;MACd;MAEC,UAAA,EAAE,WAAW;KACX,CAAA,EACK,CAAA,GAEb,MAAmB,KAClB,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;MACE,GAAI,EAAW,iBAAiB,kCAAkC;MAClE,WAAW;MACX,IAAI;OACF,OAAO,IAAiB,IAAI,EAAO,EAAM,QAAQ,QAAQ,MAAM,EAAG,IAAI,EAAM,QAAQ,MAAM;OAC1F,YAAY;MACd;MANF,UAAA;OAQG,IAAiB,IAAI,MAAM;OAC3B,EAAe,eAAe;OAC9B;MAEE;KACL,CAAA,GAAA,kBAAC,GAAD;MACE,GAAI,EAAW,iBAAiB,wCAAwC;MACxE,WAAW;MAFb,UAAA,CAIG,KACA,CACE;KACK,CAAA,CAAA,EAAA,CAAA,CAEX;;GACF,CAAA;EACD;;AAEV,GCjFa,IAAiB,QAA2C;CACvE,IAAM,IAAQ,EAAS,GACjB,EAAE,SAAM,EAAe,GAEvB,IAAsB,SAA0C;EACpE,MAAM;EACN,aAAa,EAAc,YAA0B,CAAC,CAAC,OAAO,OAAO,SAAS,aAAa;EAC3F,oBAAoB;CACtB,IAAwC,CAAC,CAAC,GAEpC,IAAqB,SAA0C;EACnE,KAAK;EACL,MAAM;EACN,aAAa,EAAc,YAA0B,CAAC,CAAC,OAAO,OAAO,SAAS,aAAa;EAC3F,oBAAoB;CACtB,IAAwC,CAAC,CAAC,GAEpC,IAAwB,EAAa;EACzC,SAAS,OAAO,EAAE,iBAET,MADgB,EAAc,YAAY,CAAC,CAAC,sBAAsB,CAAC,GAAG,EAAE,UAAO,CAAC,EAAA,CACvE;EAElB,UAAU,EAAmB,YAAY,CAAC,CAAC,cAAc,EAAiB,eAAe;CAC3F,CAAC,GAEK,IAAmC,QAAsD;EACxF,MAAsB,MAG3B,OAAO;GACL,eAAe,EAAsB,MAAM,QAAO,MAAQ,EAAK,UAAU,CAAC,CAAC,CAAC,KAAI,MAAQ,EAAK,YAAY;GACzG,uBAAuB;EACzB;CACF,GAAG,CAAC,GAAqB,EAAsB,IAAI,CAAC,GAE9C,IAAyB,EAAa;EAC1C,SAAS,CAAC,CAAC;EACX,SAAS,OAAO,EAAE,iBAET,MADgB,EAAc,YAAY,CAAC,CAAC,sBAAsB,KAAoC,CAAC,GAAG,EAAE,UAAO,CAAC,EAAA,CAC3G;EAElB,UAAU,EAAmB,YAAY,CAAC,CAAC,cAAc,EAAiB,iBAAiB,KAAoC,CAAC,CAAC;CACnI,CAAC,GACK,IAAmB,EAAiB,GACpC,IAAmB,EAAoB,GAEvC,EAAE,MAAM,GAAkB,GAAG,MAAsB,EAAa;EACpE,SAAS,OAAO,EAAE,iBAET,MADgB,EAAc,YAAY,CAAC,CAAC,kBAAkB,GAAoB,MAAM,MAAM,EAAE,UAAO,CAAC,EAAA,CAC/F;EAElB,UAAU,EAAmB,YAAY,CAAC,CAAC,cAAc,EAAiB,WAAW,CAAkB;CACzG,CAAC,GAEK,IAAY,EAAS;EACzB;EACA;EACA;EACA;EACA;CACF,CAAC,GAEK,IAAa,QAA2B;EAC5C,IAAM,IAAiB,CAAC;EAExB,IAAI,EAAa,cAAc,CAAS,KAAK,EAAa,aAAa,CAAS,KAAK,CAAC,EAAsB,MAAM,UAAU,CAAC,EAAuB,MAAM,QACxJ,OAAO;EAGT,IAAM,IAAgB,EAAsB,MAAM,QAAQ,GAAK,MAAS,IAAM,EAAK,SAAS,CAAC,KAAK,GAC5F,IAAiB,EAAuB,MAAM,QAAQ,GAAK,MAAS,IAAM,EAAK,SAAS,CAAC,KAAK;EAEpG,EAAE,KACA;GACE,UAAU,YAAY;IACpB,MAAM,EAAc,YAAY,CAAC,CAAC,OAAO,SAAS,QAAQ;GAC5D;GACA,eAAe,CAAC;GAChB,iBAAA,GAAgB,EAAA,QAAA,EAAO,IAAgB,MAAmB,KAAkB,KAAK,KAAK,CAAC;GACvF,QAAQ;GACR,OAAO;EACT,CACF;EAEA,IAAM,IAAsB,EAAiB,KAAK,QAC5C,IAAuB,EAAiB;EAC9C,EAAE,KACA;GACE,UAAU,YAAY;IACpB,MAAM,EAAc,YAAY,CAAC,CAAC,OAAO,SAAS,SAAS;GAC7D;GACA,eAAe,CAAC;GAChB,iBAAA,GAAgB,EAAA,QAAA,EAAO,IAAsB,MAAyB,KAAwB,KAAK,KAAK,CAAC;GACzG,QAAQ;GACR,OAAO;EACT,CACF;EAEA,IAAM,IAA4B,IAAI,IAA6B,EAAuB,MAAM,KAAI,MAAQ,CAAC,EAAK,cAAc,CAAI,CAAC,CAAC,GAChI,IAAc,GAAuB,MAAM,KAAuB,MAAQ;GAC9E,IAAM,IAAY,EAAK,SACjB,IAAa,EAA0B,IAAI,EAAK,YAAY,CAAC,EAAE,WAAW,GAC1E,IAAO,IAAY,GACrB;GAMJ,OALA,AAGE,IAHE,IAAa,KACf,GAAiB,EAAA,QAAA,CAAO,IAAO,IAAc,KAAK,CAAC,IAElC,GAEX;IACN,GAAG;IACH;GACF;EACF,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,iBAAiB,EAAE,cAAc;EAErD,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK,GAC1B,IAAI,IAAc,IAAI;GACpB,IAAM,IAAW,EAAiB,KAAK,IAAI,IAAc,EAAE,CAAC,gBAAgB,EAAE,GACxE,IAAe,GAAU;GAC/B,AAAI,KACF,EAAE,KACA;IACE,UAAU,YAAY;KACpB,MAAM,EAAc,YAAY,CAAC,CAAC,OAAO,SAAS,EAAa,mBAAmB,CAAQ,CAAC;IAC7F;IACA,eAAe,CAAC;IAChB,gBAAgB,EAAY,EAAE,CAAC;IAC/B,QAAQ,EAAE,0BAA0B;KAAE;KAAc,QAAQ,IAAI;IAAE,CAAC;IACnE,OAAO,EAAY,EAAE,CAAC;GACxB,CACF;EAEJ;EAGF,OAAO;CACT,GAAG;EAAC,EAAiB,MAAM;EAAQ,GAAkB;EAAQ,EAAiB;EAAK,EAAsB;EAAM,EAAuB;EAAM;EAAW;CAAC,CAAC,GAEnJ,IAA8B,EAAY,YAAY;EAC1D,MAAM,EAAc,YAAY,CAAC,CAAC,OAAO,SAAS,SAAS;CAC7D,GAAG,CAAC,CAAC;CAML,OAJI,EAAU,sBAAsB,EAAa,eAAe,CAAS,CAAC,IACjE,OAIP,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;EACE,IAAI,EACF,cAAc,EAChB;EAEA,UAAA,kBAAC,GAAD;GACE,IAAI;IACF,YAAY;IACZ,SAAS;IACT,gBAAgB;GAClB;GALF,UAAA,CAOE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;IACE,SAAS;IAER,UAAA,CAAC;GACQ,CAAA,EACT,CAAA,GACL,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;IACE,OAAO;IACP,UAAA;IACA,SAAS;IACT,SAAS;IAER,UAAA,CAAC;GACI,CAAA,EACL,CAAA,CACF;;CACF,CAAA,GACL,kBAAC,GAAD;EACE,eAAA;EACW;EACX,gBACE,kBAAC,GAAD;GACE,IAAI;IACF,SAAS;IACT,KAAK;IACL,qBAAqB;IACrB,cAAc;KACb,EAAM,YAAY,GAAG,IAAI,IAAI,EAC5B,qBAAqB,iBACvB;KACC,EAAM,YAAY,GAAG,IAAI,IAAI,EAC5B,qBAAqB,iBACvB;GACF;GAEC,UAAA,CAAC,GAAG,MAAc,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,MAC7B,kBAAC,GAAD;IAGE,IAAI;KACF,YAAY;KACZ,SAAS;KACT,QAAQ;KACR,gBAAgB;IAClB;IAEA,UAAA,kBAAC,GAAD;KACE,WAAW;KACX,QAAQ;KACR,SAAS;KACT,OAAO;IACR,CAAA;GACE,GAdE,CAcF,CACN;EACE,CAAA;EAEP,yBAAA;EAvCF,UAAA,CAyCG,EAAW,WAAW,KACrB,kBAAC,GAAD;GACE,IAAI,EACF,SAAS,EACX;GAEA,UAAA,kBAAC,GAAD,EAAA,UACG,CAAC,uBACQ,CAAA;EACT,CAAA,GAEN,EAAW,SAAS,KACnB,kBAAC,GAAD;GACE,IAAI;IACF,SAAS;IACT,KAAK;IACL,qBAAqB;IACrB,cAAc;KACb,EAAM,YAAY,GAAG,IAAI,IAAI,EAC5B,qBAAqB,iBACvB;KACC,EAAM,YAAY,GAAG,IAAI,IAAI,EAC5B,qBAAqB,iBACvB;GACF;GAEC,UAAA,EAAW,KAAI,MACd,kBAAC,GAAD;IACE,UAAU,EAAU;IACpB,eAAe,EAAU;IACzB,gBAAgB,EAAU;IAC1B,QAAQ,EAAU;IAElB,YAAY,EAAc,YAA0B,CAAC,CAAC,OAAO,OAAO,SAAS,cAAc,CAAC;IAC5F,OAAO,EAAU;GAClB,GAHM,EAAU,MAGhB,CACF;EACE,CAAA,CAEQ;CACd,CAAA,CAAA,EAAA,CAAA;AAET,GAAG,EACD,qBAAqB;CACnB;EAAE,cAAc,EAAkB;EAA8B,iBAAiB,EAAqB;CAAQ;CAC9G;EAAE,cAAc,EAAkB;EAAoB,iBAAiB,EAAqB;CAAK;CACjG;EAAE,cAAc,EAAkB;EAAqB,iBAAiB,EAAqB;CAAK;AACpG,EACF,CAAC"}
@@ -249,52 +249,53 @@ var de = I(({ row: e }) => {
249
249
  let { t: e } = H(), t = L(u), n = l(t, m((e) => e.sortedData)), r = l(t, m((e) => e.completeCaseType)), i = R(() => t.getState().getCurrentColumnVisualSettings().filter((e) => e.isVisible).map((e) => e.id), [t]);
250
250
  z(() => {
251
251
  let a = (t) => {
252
+ let a = [{
253
+ disabled: !n?.length,
254
+ items: [
255
+ {
256
+ callback: async () => p.downloadAsExcel(n, i(), r, e),
257
+ label: e`Download as Excel`
258
+ },
259
+ {
260
+ callback: () => p.downloadAsCsv(n, i(), r, e),
261
+ label: e`Download as CSV`
262
+ },
263
+ {
264
+ callback: () => f.getInstance().emit("openSequenceDownloadDialog", { cases: n }),
265
+ label: e`Download sequences`
266
+ },
267
+ {
268
+ callback: () => null,
269
+ disabled: !0,
270
+ label: e`Download allele profiles`
271
+ }
272
+ ],
273
+ label: e`All rows`
274
+ }, {
275
+ disabled: t.length === 0,
276
+ items: [
277
+ {
278
+ callback: async () => p.downloadAsExcel(o.getSelectedRows(n, t), i(), r, e),
279
+ label: e`Download as Excel`
280
+ },
281
+ {
282
+ callback: () => p.downloadAsCsv(o.getSelectedRows(n, t), i(), r, e),
283
+ label: e`Download as CSV`
284
+ },
285
+ {
286
+ callback: () => f.getInstance().emit("openSequenceDownloadDialog", { cases: n.filter((e) => t.includes(e.id)) }),
287
+ label: e`Download sequences`
288
+ },
289
+ {
290
+ callback: () => null,
291
+ disabled: !0,
292
+ label: e`Download allele profiles`
293
+ }
294
+ ],
295
+ label: e`Selected rows`
296
+ }];
252
297
  f.getInstance().emit("onDownloadOptionsChanged", {
253
- items: [{
254
- disabled: !n?.length,
255
- items: [
256
- {
257
- callback: async () => p.downloadAsExcel(n, i(), r, e),
258
- label: e`Download as Excel`
259
- },
260
- {
261
- callback: () => p.downloadAsCsv(n, i(), r, e),
262
- label: e`Download as CSV`
263
- },
264
- {
265
- callback: () => f.getInstance().emit("openSequenceDownloadDialog", { cases: n }),
266
- label: e`Download sequences`
267
- },
268
- {
269
- callback: () => null,
270
- disabled: !0,
271
- label: e`Download allele profiles`
272
- }
273
- ],
274
- label: e`All rows`
275
- }, {
276
- disabled: t.length === 0,
277
- items: [
278
- {
279
- callback: async () => p.downloadAsExcel(o.getSelectedRows(n, t), i(), r, e),
280
- label: e`Download as Excel`
281
- },
282
- {
283
- callback: () => p.downloadAsCsv(o.getSelectedRows(n, t), i(), r, e),
284
- label: e`Download as CSV`
285
- },
286
- {
287
- callback: () => f.getInstance().emit("openSequenceDownloadDialog", { cases: n.filter((e) => t.includes(e.id)) }),
288
- label: e`Download sequences`
289
- },
290
- {
291
- callback: () => null,
292
- disabled: !0,
293
- label: e`Download allele profiles`
294
- }
295
- ],
296
- label: e`Selected rows`
297
- }],
298
+ items: a,
298
299
  zone: c.LINE_LIST,
299
300
  zoneLabel: e`Line list`
300
301
  });