From ed1e0ceb30c482adf4f0773750358231be957542 Mon Sep 17 00:00:00 2001
From: Neil <55785687+carkom@users.noreply.github.com>
Date: Wed, 28 Feb 2024 21:50:23 +0000
Subject: [PATCH] Custom Reports AutoComplete (#2350)

* updated saved work

* merge fixes

* Disable CREATE TABLE

* notes

* turn on db table

* Fix TableGraph recall crash

* table format changes

* type fixes

* fixing some card displays

* merge fixes

* revert table change

* cardMenu width

* Add Saved Reports Autocomplete

* notes

* fix invalid values crash

* Title and auto-focus and esc

* notes

* fix filtering logic

* reload saved filters

* lint fix

* visual graph changes

* merge fixes

* fix

* review updates
---
 .../autocomplete/ReportAutocomplete.tsx       |  34 ++++++
 .../components/autocomplete/ReportList.tsx    |  52 ++++++++
 .../src/components/common/Menu.tsx            |   4 +-
 .../src/components/reports/ReportOptions.ts   |   2 +
 .../src/components/reports/ReportTopbar.jsx   |   2 -
 .../src/components/reports/SaveReport.tsx     |  31 ++++-
 .../components/reports/SaveReportChoose.tsx   |  82 +++++++++++++
 .../src/components/reports/SaveReportMenu.tsx | 114 ++++++++++--------
 .../src/components/reports/SaveReportName.tsx |   8 +-
 .../reports/reports/CustomReport.jsx          |  65 +++++-----
 .../src/components/util/GenericInput.jsx      |  18 +++
 .../loot-core/src/types/models/reports.d.ts   |   8 +-
 upcoming-release-notes/2350.md                |   6 +
 13 files changed, 319 insertions(+), 107 deletions(-)
 create mode 100644 packages/desktop-client/src/components/autocomplete/ReportAutocomplete.tsx
 create mode 100644 packages/desktop-client/src/components/autocomplete/ReportList.tsx
 create mode 100644 packages/desktop-client/src/components/reports/SaveReportChoose.tsx
 create mode 100644 upcoming-release-notes/2350.md

diff --git a/packages/desktop-client/src/components/autocomplete/ReportAutocomplete.tsx b/packages/desktop-client/src/components/autocomplete/ReportAutocomplete.tsx
new file mode 100644
index 000000000..7a390bbd0
--- /dev/null
+++ b/packages/desktop-client/src/components/autocomplete/ReportAutocomplete.tsx
@@ -0,0 +1,34 @@
+import React, { type ComponentProps } from 'react';
+
+import { useReports } from 'loot-core/client/data-hooks/reports';
+import { type CustomReportEntity } from 'loot-core/src/types/models/reports';
+
+import { Autocomplete } from './Autocomplete';
+import { ReportList } from './ReportList';
+
+export function ReportAutocomplete({
+  embedded,
+  ...props
+}: {
+  embedded?: boolean;
+} & ComponentProps<typeof Autocomplete<CustomReportEntity>>) {
+  const reports = useReports() || [];
+
+  return (
+    <Autocomplete
+      strict={true}
+      highlightFirst={true}
+      embedded={embedded}
+      suggestions={reports}
+      renderItems={(items, getItemProps, highlightedIndex) => (
+        <ReportList
+          items={items}
+          getItemProps={getItemProps}
+          highlightedIndex={highlightedIndex}
+          embedded={embedded}
+        />
+      )}
+      {...props}
+    />
+  );
+}
diff --git a/packages/desktop-client/src/components/autocomplete/ReportList.tsx b/packages/desktop-client/src/components/autocomplete/ReportList.tsx
new file mode 100644
index 000000000..81e3ff978
--- /dev/null
+++ b/packages/desktop-client/src/components/autocomplete/ReportList.tsx
@@ -0,0 +1,52 @@
+import React, { Fragment, type ComponentProps } from 'react';
+
+import { theme } from '../../style/theme';
+import { View } from '../common/View';
+
+import { ItemHeader } from './ItemHeader';
+
+export function ReportList<T extends { id: string; name: string }>({
+  items,
+  getItemProps,
+  highlightedIndex,
+  embedded,
+}: {
+  items: T[];
+  getItemProps: (arg: { item: T }) => ComponentProps<typeof View>;
+  highlightedIndex: number;
+  embedded?: boolean;
+}) {
+  return (
+    <View>
+      <View
+        style={{
+          overflow: 'auto',
+          padding: '5px 0',
+          ...(!embedded && { maxHeight: 175 }),
+        }}
+      >
+        <Fragment>{ItemHeader({ title: 'Saved Reports' })}</Fragment>
+        {items.map((item, idx) => {
+          return [
+            <div
+              {...(getItemProps ? getItemProps({ item }) : null)}
+              key={item.id}
+              style={{
+                backgroundColor:
+                  highlightedIndex === idx
+                    ? theme.menuAutoCompleteBackgroundHover
+                    : 'transparent',
+                padding: 4,
+                paddingLeft: 20,
+                borderRadius: embedded ? 4 : 0,
+              }}
+              data-highlighted={highlightedIndex === idx || undefined}
+            >
+              {item.name}
+            </div>,
+          ];
+        })}
+      </View>
+    </View>
+  );
+}
diff --git a/packages/desktop-client/src/components/common/Menu.tsx b/packages/desktop-client/src/components/common/Menu.tsx
index 57dd789ab..4577a2756 100644
--- a/packages/desktop-client/src/components/common/Menu.tsx
+++ b/packages/desktop-client/src/components/common/Menu.tsx
@@ -38,11 +38,11 @@ type MenuItem = {
   tooltip?: string;
 };
 
-type MenuProps<T extends MenuItem = MenuItem> = {
+export type MenuProps<T extends MenuItem = MenuItem> = {
   header?: ReactNode;
   footer?: ReactNode;
   items: Array<T | typeof Menu.line>;
-  onMenuSelect: (itemName: T['name']) => void;
+  onMenuSelect?: (itemName: T['name']) => void;
   style?: CSSProperties;
 };
 
diff --git a/packages/desktop-client/src/components/reports/ReportOptions.ts b/packages/desktop-client/src/components/reports/ReportOptions.ts
index 1f868ef1f..523e024ac 100644
--- a/packages/desktop-client/src/components/reports/ReportOptions.ts
+++ b/packages/desktop-client/src/components/reports/ReportOptions.ts
@@ -11,6 +11,8 @@ const startDate = monthUtils.subMonths(monthUtils.currentMonth(), 5);
 const endDate = monthUtils.currentMonth();
 
 export const defaultReport: CustomReportEntity = {
+  id: '',
+  name: '',
   startDate,
   endDate,
   isDateStatic: false,
diff --git a/packages/desktop-client/src/components/reports/ReportTopbar.jsx b/packages/desktop-client/src/components/reports/ReportTopbar.jsx
index 5ffeaee5c..81728a30b 100644
--- a/packages/desktop-client/src/components/reports/ReportTopbar.jsx
+++ b/packages/desktop-client/src/components/reports/ReportTopbar.jsx
@@ -30,7 +30,6 @@ export function ReportTopbar({
   onApplyFilter,
   onChangeViews,
   onReportChange,
-  onResetReports,
 }) {
   return (
     <View
@@ -179,7 +178,6 @@ export function ReportTopbar({
         report={report}
         savedStatus={savedStatus}
         onReportChange={onReportChange}
-        onResetReports={onResetReports}
       />
     </View>
   );
diff --git a/packages/desktop-client/src/components/reports/SaveReport.tsx b/packages/desktop-client/src/components/reports/SaveReport.tsx
index 4200d781f..a71416e32 100644
--- a/packages/desktop-client/src/components/reports/SaveReport.tsx
+++ b/packages/desktop-client/src/components/reports/SaveReport.tsx
@@ -1,5 +1,6 @@
 import React, { createRef, useState } from 'react';
 
+import { useReports } from 'loot-core/client/data-hooks/reports';
 import { send, sendCatch } from 'loot-core/src/platform/client/fetch';
 import { type CustomReportEntity } from 'loot-core/src/types/models';
 
@@ -8,6 +9,7 @@ import { Button } from '../common/Button';
 import { Text } from '../common/Text';
 import { View } from '../common/View';
 
+import { SaveReportChoose } from './SaveReportChoose';
 import { SaveReportMenu } from './SaveReportMenu';
 import { SaveReportName } from './SaveReportName';
 
@@ -22,7 +24,6 @@ type SaveReportProps<T extends CustomReportEntity = CustomReportEntity> = {
     savedReport?: T;
     type: string;
   }) => void;
-  onResetReports: () => void;
 };
 
 export function SaveReport({
@@ -30,15 +31,23 @@ export function SaveReport({
   report,
   savedStatus,
   onReportChange,
-  onResetReports,
 }: SaveReportProps) {
+  const listReports = useReports();
   const [nameMenuOpen, setNameMenuOpen] = useState(false);
   const [menuOpen, setMenuOpen] = useState(false);
+  const [chooseMenuOpen, setChooseMenuOpen] = useState(false);
   const [menuItem, setMenuItem] = useState('');
   const [err, setErr] = useState('');
   const [name, setName] = useState(report.name ?? '');
   const inputRef = createRef<HTMLInputElement>();
 
+  async function onApply(cond: string) {
+    const chooseSavedReport = listReports.find(r => cond === r.id);
+    onReportChange({ savedReport: chooseSavedReport, type: 'choose' });
+    setChooseMenuOpen(false);
+    setName(chooseSavedReport === undefined ? '' : chooseSavedReport.name);
+  }
+
   const onAddUpdate = async (menuChoice: string) => {
     if (menuChoice === 'save-report') {
       const newSavedReport = {
@@ -78,7 +87,6 @@ export function SaveReport({
       setNameMenuOpen(true);
       return;
     }
-
     setNameMenuOpen(false);
     onReportChange({
       savedReport: updatedReport,
@@ -98,7 +106,7 @@ export function SaveReport({
         setMenuOpen(false);
         setName('');
         await send('report/delete', report.id);
-        onResetReports();
+        onReportChange({ type: 'reset' });
         break;
       case 'update-report':
         setErr('');
@@ -117,7 +125,12 @@ export function SaveReport({
       case 'reset-report':
         setMenuOpen(false);
         setName('');
-        onResetReports();
+        onReportChange({ type: 'reset' });
+        break;
+      case 'choose-report':
+        setErr('');
+        setMenuOpen(false);
+        setChooseMenuOpen(true);
         break;
       default:
     }
@@ -153,9 +166,9 @@ export function SaveReport({
       {menuOpen && (
         <SaveReportMenu
           onClose={() => setMenuOpen(false)}
-          report={report}
           onMenuSelect={onMenuSelect}
           savedStatus={savedStatus}
+          listReports={listReports && listReports.length}
         />
       )}
       {nameMenuOpen && (
@@ -169,6 +182,12 @@ export function SaveReport({
           err={err}
         />
       )}
+      {chooseMenuOpen && (
+        <SaveReportChoose
+          onApply={onApply}
+          onClose={() => setChooseMenuOpen(false)}
+        />
+      )}
     </View>
   );
 }
diff --git a/packages/desktop-client/src/components/reports/SaveReportChoose.tsx b/packages/desktop-client/src/components/reports/SaveReportChoose.tsx
new file mode 100644
index 000000000..2a6b3f123
--- /dev/null
+++ b/packages/desktop-client/src/components/reports/SaveReportChoose.tsx
@@ -0,0 +1,82 @@
+import React, { createRef, useEffect, useState } from 'react';
+
+import { theme } from '../../style/theme';
+import { Button } from '../common/Button';
+import { Stack } from '../common/Stack';
+import { Text } from '../common/Text';
+import { View } from '../common/View';
+import { Tooltip } from '../tooltips';
+import { GenericInput } from '../util/GenericInput';
+
+type SaveReportChooseProps = {
+  onApply: (cond: string) => void;
+  onClose: () => void;
+};
+
+export function SaveReportChoose({ onApply, onClose }: SaveReportChooseProps) {
+  const inputRef = createRef<HTMLInputElement>();
+  const [err, setErr] = useState('');
+  const [value, setValue] = useState('');
+
+  useEffect(() => {
+    if (inputRef.current) {
+      inputRef.current.focus();
+    }
+  });
+
+  return (
+    <Tooltip
+      position="bottom-right"
+      style={{ padding: 15, color: theme.menuItemText }}
+      width={275}
+      onClose={onClose}
+    >
+      <form>
+        <View style={{ flexDirection: 'row', align: 'center' }}>
+          <Text style={{ userSelect: 'none', flex: 1 }}>Choose Report</Text>
+          <View style={{ flex: 1 }} />
+        </View>
+        <GenericInput
+          inputRef={inputRef}
+          field="report"
+          subfield={null}
+          type="saved"
+          value={value}
+          multi={false}
+          style={{ marginTop: 10 }}
+          onChange={(v: string) => setValue(v)}
+        />
+
+        <Stack
+          direction="row"
+          justify="flex-end"
+          align="center"
+          style={{ marginTop: 15 }}
+        >
+          <View style={{ flex: 1 }} />
+          <Button
+            type="primary"
+            onClick={e => {
+              e.preventDefault();
+              if (!value) {
+                setErr('Invalid report entered');
+                return;
+              }
+
+              onApply(value);
+            }}
+          >
+            Apply
+          </Button>
+        </Stack>
+      </form>
+      {err !== '' ? (
+        <Stack direction="row" align="center" style={{ padding: 10 }}>
+          <Text style={{ color: theme.errorText }}>{err}</Text>
+        </Stack>
+      ) : (
+        <View />
+      )}
+    </Tooltip>
+  );
+}
diff --git a/packages/desktop-client/src/components/reports/SaveReportMenu.tsx b/packages/desktop-client/src/components/reports/SaveReportMenu.tsx
index 01e311dd2..9b5ec358e 100644
--- a/packages/desktop-client/src/components/reports/SaveReportMenu.tsx
+++ b/packages/desktop-client/src/components/reports/SaveReportMenu.tsx
@@ -1,75 +1,83 @@
 import React from 'react';
 
-import { type CustomReportEntity } from 'loot-core/src/types/models';
-
-import { Menu } from '../common/Menu';
+import { Menu, type MenuProps } from '../common/Menu';
 import { MenuTooltip } from '../common/MenuTooltip';
 
 export function SaveReportMenu({
-  report,
   onClose,
   onMenuSelect,
   savedStatus,
+  listReports,
 }: {
-  report: CustomReportEntity;
   onClose: () => void;
   onMenuSelect: (item: string) => void;
   savedStatus: string;
+  listReports: number;
 }) {
+  const savedMenu: MenuProps =
+    savedStatus === 'saved'
+      ? {
+          items: [
+            { name: 'rename-report', text: 'Rename' },
+            { name: 'delete-report', text: 'Delete' },
+            Menu.line,
+          ],
+        }
+      : {
+          items: [],
+        };
+
+  const modifiedMenu: MenuProps =
+    savedStatus === 'modified'
+      ? {
+          items: [
+            { name: 'rename-report', text: 'Rename' },
+            {
+              name: 'update-report',
+              text: 'Update report',
+            },
+            {
+              name: 'reload-report',
+              text: 'Revert changes',
+            },
+            { name: 'delete-report', text: 'Delete' },
+            Menu.line,
+          ],
+        }
+      : {
+          items: [],
+        };
+
+  const unsavedMenu: MenuProps = {
+    items: [
+      {
+        name: 'save-report',
+        text: 'Save new report',
+      },
+      {
+        name: 'reset-report',
+        text: 'Reset to default',
+      },
+      Menu.line,
+      {
+        name: 'choose-report',
+        text: 'Choose Report',
+        disabled: listReports > 0 ? false : true,
+      },
+    ],
+  };
+
   return (
     <MenuTooltip width={150} onClose={onClose}>
       <Menu
         onMenuSelect={item => {
           onMenuSelect(item);
         }}
-        items={
-          report.id === undefined
-            ? [
-                {
-                  name: 'save-report',
-                  text: 'Save new report',
-                },
-                {
-                  name: 'reset-report',
-                  text: 'Reset to default',
-                },
-              ]
-            : savedStatus === 'saved'
-              ? [
-                  { name: 'rename-report', text: 'Rename' },
-                  { name: 'delete-report', text: 'Delete' },
-                  Menu.line,
-                  {
-                    name: 'save-report',
-                    text: 'Save new report',
-                  },
-                  {
-                    name: 'reset-report',
-                    text: 'Reset to default',
-                  },
-                ]
-              : [
-                  { name: 'rename-report', text: 'Rename' },
-                  {
-                    name: 'update-report',
-                    text: 'Update report',
-                  },
-                  {
-                    name: 'reload-report',
-                    text: 'Revert changes',
-                  },
-                  { name: 'delete-report', text: 'Delete' },
-                  Menu.line,
-                  {
-                    name: 'save-report',
-                    text: 'Save new report',
-                  },
-                  {
-                    name: 'reset-report',
-                    text: 'Reset to default',
-                  },
-                ]
-        }
+        items={[
+          ...savedMenu.items,
+          ...modifiedMenu.items,
+          ...unsavedMenu.items,
+        ]}
       />
     </MenuTooltip>
   );
diff --git a/packages/desktop-client/src/components/reports/SaveReportName.tsx b/packages/desktop-client/src/components/reports/SaveReportName.tsx
index c6ef39ed6..828a20a60 100644
--- a/packages/desktop-client/src/components/reports/SaveReportName.tsx
+++ b/packages/desktop-client/src/components/reports/SaveReportName.tsx
@@ -6,6 +6,7 @@ import { Input } from '../common/Input';
 import { MenuTooltip } from '../common/MenuTooltip';
 import { Stack } from '../common/Stack';
 import { Text } from '../common/Text';
+import { View } from '../common/View';
 import { FormField, FormLabel } from '../forms';
 
 type SaveReportNameProps = {
@@ -41,7 +42,7 @@ export function SaveReportName({
             direction="row"
             justify="flex-end"
             align="center"
-            style={{ padding: 10 }}
+            style={{ padding: 15 }}
           >
             <FormField style={{ flex: 1 }}>
               <FormLabel
@@ -54,11 +55,12 @@ export function SaveReportName({
                 id="name-field"
                 inputRef={inputRef}
                 onUpdate={setName}
+                style={{ marginTop: 10 }}
               />
             </FormField>
             <Button
               type="primary"
-              style={{ marginTop: 18 }}
+              style={{ marginTop: 30 }}
               onClick={e => {
                 e.preventDefault();
                 onAddUpdate(menuItem);
@@ -74,7 +76,7 @@ export function SaveReportName({
           <Text style={{ color: theme.errorText }}>{err}</Text>
         </Stack>
       ) : (
-        <Text />
+        <View />
       )}
     </MenuTooltip>
   );
diff --git a/packages/desktop-client/src/components/reports/reports/CustomReport.jsx b/packages/desktop-client/src/components/reports/reports/CustomReport.jsx
index 93559528d..ed0bd28d1 100644
--- a/packages/desktop-client/src/components/reports/reports/CustomReport.jsx
+++ b/packages/desktop-client/src/components/reports/reports/CustomReport.jsx
@@ -248,30 +248,29 @@ export function CustomReport() {
     }
   };
 
-  const onResetReports = () => {
+  const setReportData = input => {
     const selectAll = [];
     categories.grouped.map(categoryGroup =>
       categoryGroup.categories.map(category => selectAll.push(category)),
     );
 
-    setStartDate(defaultReport.startDate);
-    setEndDate(defaultReport.endDate);
-    setIsDateStatic(defaultReport.isDateStatic);
-    setDateRange(defaultReport.dateRange);
-    setMode(defaultReport.mode);
-    setGroupBy(defaultReport.groupBy);
-    setInterval(defaultReport.interval);
-    setBalanceType(defaultReport.balanceType);
-    setShowEmpty(defaultReport.showEmpty);
-    setShowOffBudget(defaultReport.showOffBudget);
-    setShowHiddenCategories(defaultReport.showHiddenCategories);
-    setShowUncategorized(defaultReport.showUncategorized);
-    setSelectedCategories(selectAll);
-    setGraphType(defaultReport.graphType);
+    setStartDate(input.startDate);
+    setEndDate(input.endDate);
+    setIsDateStatic(input.isDateStatic);
+    setDateRange(input.dateRange);
+    setMode(input.mode);
+    setGroupBy(input.groupBy);
+    setInterval(input.interval);
+    setBalanceType(input.balanceType);
+    setShowEmpty(input.showEmpty);
+    setShowOffBudget(input.showOffBudget);
+    setShowHiddenCategories(input.showHiddenCategories);
+    setShowUncategorized(input.showUncategorized);
+    setSelectedCategories(input.selectedCategories ?? selectAll);
+    setGraphType(input.graphType);
     onApplyFilter(null);
-    onCondOpChange(defaultReport.conditionsOp);
-    setReport(defaultReport);
-    setSavedStatus('new');
+    input.conditions.forEach(condition => onApplyFilter(condition));
+    onCondOpChange(input.conditionsOp);
   };
 
   const onChangeAppliedFilter = (filter, changedElement) => {
@@ -295,24 +294,17 @@ export function CustomReport() {
         break;
       case 'reload':
         setSavedStatus('saved');
-
-        setStartDate(report.startDate);
-        setEndDate(report.endDate);
-        setIsDateStatic(report.isDateStatic);
-        setDateRange(report.dateRange);
-        setMode(report.mode);
-        setGroupBy(report.groupBy);
-        setInterval(report.interval);
-        setBalanceType(report.balanceType);
-        setShowEmpty(report.showEmpty);
-        setShowOffBudget(report.showOffBudget);
-        setShowHiddenCategories(report.showHiddenCategories);
-        setShowUncategorized(report.showUncategorized);
-        setSelectedCategories(report.selectedCategories);
-        setGraphType(report.graphType);
-        onApplyFilter(null);
-        report.conditions.forEach(condition => onApplyFilter(condition));
-        onCondOpChange(report.conditionsOp);
+        setReportData(report);
+        break;
+      case 'reset':
+        setSavedStatus('new');
+        setReport(defaultReport);
+        setReportData(defaultReport);
+        break;
+      case 'choose':
+        setSavedStatus('saved');
+        setReport(savedReport);
+        setReportData(savedReport);
         break;
       default:
     }
@@ -372,7 +364,6 @@ export function CustomReport() {
             onApplyFilter={onApplyFilter}
             onChangeViews={onChangeViews}
             onReportChange={onReportChange}
-            onResetReports={onResetReports}
           />
           {filters && filters.length > 0 && (
             <View
diff --git a/packages/desktop-client/src/components/util/GenericInput.jsx b/packages/desktop-client/src/components/util/GenericInput.jsx
index c2e1ec23a..3c46d3a43 100644
--- a/packages/desktop-client/src/components/util/GenericInput.jsx
+++ b/packages/desktop-client/src/components/util/GenericInput.jsx
@@ -1,6 +1,7 @@
 import React from 'react';
 import { useSelector } from 'react-redux';
 
+import { useReports } from 'loot-core/client/data-hooks/reports';
 import { getMonthYearFormat } from 'loot-core/src/shared/months';
 
 import { useCategories } from '../../hooks/useCategories';
@@ -10,6 +11,7 @@ import { Autocomplete } from '../autocomplete/Autocomplete';
 import { CategoryAutocomplete } from '../autocomplete/CategoryAutocomplete';
 import { FilterAutocomplete } from '../autocomplete/FilterAutocomplete';
 import { PayeeAutocomplete } from '../autocomplete/PayeeAutocomplete';
+import { ReportAutocomplete } from '../autocomplete/ReportAutocomplete';
 import { Input } from '../common/Input';
 import { View } from '../common/View';
 import { Checkbox } from '../forms';
@@ -27,6 +29,7 @@ export function GenericInput({
   onChange,
 }) {
   const { grouped: categoryGroups } = useCategories();
+  const savedReports = useReports();
   const saved = useSelector(state => state.queries.saved);
   const dateFormat = useDateFormat() || 'MM/dd/yyyy';
 
@@ -111,6 +114,21 @@ export function GenericInput({
             />
           );
           break;
+        case 'report':
+          content = (
+            <ReportAutocomplete
+              saved={savedReports}
+              value={value}
+              multi={multi}
+              openOnFocus={true}
+              onSelect={onChange}
+              inputProps={{
+                inputRef,
+                ...(showPlaceholder ? { placeholder: 'nothing' } : null),
+              }}
+            />
+          );
+          break;
 
         default:
       }
diff --git a/packages/loot-core/src/types/models/reports.d.ts b/packages/loot-core/src/types/models/reports.d.ts
index cb966d6e3..4d6464573 100644
--- a/packages/loot-core/src/types/models/reports.d.ts
+++ b/packages/loot-core/src/types/models/reports.d.ts
@@ -2,8 +2,8 @@ import { CategoryEntity } from './category';
 import { type RuleConditionEntity } from './rule';
 
 export interface CustomReportEntity {
-  id?: string;
-  name?: string;
+  id: string;
+  name: string;
   startDate: string;
   endDate: string;
   isDateStatic: boolean;
@@ -73,8 +73,8 @@ export type Month = {
 };
 
 export interface CustomReportData {
-  id?: string;
-  name?: string;
+  id: string;
+  name: string;
   start_date: string;
   end_date: string;
   date_static: number;
diff --git a/upcoming-release-notes/2350.md b/upcoming-release-notes/2350.md
new file mode 100644
index 000000000..413640133
--- /dev/null
+++ b/upcoming-release-notes/2350.md
@@ -0,0 +1,6 @@
+---
+category: Enhancements
+authors: [carkom]
+---
+
+Creating an autocomplete for custom reports so they can be recalled without switching back to the dashboard.
-- 
GitLab