diff --git a/src/Providers.tsx b/src/Providers.tsx index c30e05f4..c71630fa 100644 --- a/src/Providers.tsx +++ b/src/Providers.tsx @@ -5,6 +5,7 @@ import { BrowserRouter } from "react-router-dom"; import { HelmetProvider } from "react-helmet-async"; import { Provider, Atom } from "jotai"; import { globalScope } from "@src/atoms/globalScope"; +import { DebugAtoms } from "@src/atoms/utils"; import LocalizationProvider from "@mui/lab/LocalizationProvider"; import AdapterDateFns from "@mui/lab/AdapterDateFns"; import createCache from "@emotion/cache"; @@ -36,6 +37,7 @@ export default function Providers({ scope={globalScope} initialValues={initialAtomValues} > + diff --git a/src/atoms/tableScope/ui.ts b/src/atoms/tableScope/ui.ts index 25aecd2a..aca5aae6 100644 --- a/src/atoms/tableScope/ui.ts +++ b/src/atoms/tableScope/ui.ts @@ -5,6 +5,12 @@ import type { PopoverProps } from "@mui/material"; import type { ColumnConfig, TableFilter } from "@src/types/table"; import { SEVERITY_LEVELS } from "@src/components/TableToolbar/CloudLogs/CloudLogSeverityIcon"; +function beforeUnloadHandler(event: BeforeUnloadEvent) { + event.preventDefault(); + return (event.returnValue = + "Are you sure you want to leave? You may have unsaved changes."); +} + /** * Open table column menu. Set to `null` to close. * @@ -36,7 +42,8 @@ export const columnMenuAtom = atom<{ * * @example Close: * ``` - * openColumnModal(null) + * import { RESET } from "jotai/utils"; + * openColumnModal(RESET) * ``` */ export const columnModalAtom = atomWithHash<{ diff --git a/src/atoms/utils.ts b/src/atoms/utils.ts new file mode 100644 index 00000000..7903ca86 --- /dev/null +++ b/src/atoms/utils.ts @@ -0,0 +1,9 @@ +import { Scope } from "jotai/core/atom"; +import { useAtomsDebugValue } from "jotai/devtools"; + +export function DebugAtoms( + options: { scope: Scope } & Parameters[0] +) { + useAtomsDebugValue(options); + return null; +} diff --git a/src/components/ColumnModals/ColumnModals.tsx b/src/components/ColumnModals/ColumnModals.tsx index d7f00489..1d20a536 100644 --- a/src/components/ColumnModals/ColumnModals.tsx +++ b/src/components/ColumnModals/ColumnModals.tsx @@ -1,4 +1,5 @@ import { useAtom } from "jotai"; +import { RESET } from "jotai/utils"; import NewColumnModal from "./NewColumnModal"; import NameChangeModal from "./NameChangeModal"; @@ -23,7 +24,7 @@ export default function ColumnModals() { if (!columnModal) return null; - const handleClose = () => setColumnModal(null); + const handleClose = () => setColumnModal(RESET); if (columnModal.type === "new") return ; diff --git a/src/components/Table/Table.tsx b/src/components/Table/Table.tsx index 3b291ddf..bfb7146b 100644 --- a/src/components/Table/Table.tsx +++ b/src/components/Table/Table.tsx @@ -274,16 +274,21 @@ export default function Table({ value, }); }} - onSelectedCellChange={({ rowIdx, idx }) => - setSelectedCell({ - path: rows[rowIdx]._rowy_ref.path, - columnKey: tableColumnsOrdered.filter((col) => - userDocHiddenFields - ? !userDocHiddenFields.includes(col.key) - : true - )[idx].key, - }) - } + onSelectedCellChange={({ rowIdx, idx }) => { + if (!rows[rowIdx]?._rowy_ref) return; // May be the header row + + const path = rows[rowIdx]._rowy_ref.path; + if (!path) return; + + const columnKey = tableColumnsOrdered.filter((col) => + userDocHiddenFields + ? !userDocHiddenFields.includes(col.key) + : true + )[idx]?.key; + if (!columnKey) return; // May be the final column + + setSelectedCell({ path, columnKey }); + }} /> diff --git a/src/components/TableToolbar/CloudLogs/CloudLogs.tsx b/src/components/TableToolbar/CloudLogs/CloudLogs.tsx index 0b215329..1316339f 100644 --- a/src/components/TableToolbar/CloudLogs/CloudLogs.tsx +++ b/src/components/TableToolbar/CloudLogs/CloudLogs.tsx @@ -1,4 +1,5 @@ import { useAtom, useSetAtom } from "jotai"; +import { RESET } from "jotai/utils"; import TableToolbarButton from "@src/components/TableToolbar/TableToolbarButton"; import { CloudLogs as LogsIcon } from "@src/assets/icons"; @@ -17,7 +18,7 @@ export default function CloudLogs() { const [modal, setModal] = useAtom(tableModalAtom, tableScope); const open = modal === "cloudLogs"; - const setOpen = (open: boolean) => setModal(open ? "cloudLogs" : null); + const setOpen = (open: boolean) => setModal(open ? "cloudLogs" : RESET); return ( <> diff --git a/src/components/TableToolbar/Export/Export.tsx b/src/components/TableToolbar/Export/Export.tsx index d8a2619e..9e20e705 100644 --- a/src/components/TableToolbar/Export/Export.tsx +++ b/src/components/TableToolbar/Export/Export.tsx @@ -1,5 +1,6 @@ import { useState, useMemo } from "react"; import { useAtom } from "jotai"; +import { RESET } from "jotai/utils"; import { query as firestoreQuery, Query, @@ -42,7 +43,7 @@ export interface IExportModalContentsProps { export default function Export() { const [modal, setModal] = useAtom(tableModalAtom, tableScope); const open = modal === "export"; - const setOpen = (open: boolean) => setModal(open ? "export" : null); + const setOpen = (open: boolean) => setModal(open ? "export" : RESET); const [mode, setMode] = useState<"Export" | "Download">("Export"); const [firebaseDb] = useAtom(firebaseDbAtom, globalScope); diff --git a/src/components/TableToolbar/Extensions/Extensions.tsx b/src/components/TableToolbar/Extensions/Extensions.tsx index c4b4881b..b5b2e29a 100644 --- a/src/components/TableToolbar/Extensions/Extensions.tsx +++ b/src/components/TableToolbar/Extensions/Extensions.tsx @@ -1,5 +1,6 @@ import { useState } from "react"; import { useAtom, useSetAtom } from "jotai"; +import { RESET } from "jotai/utils"; import { isEqual } from "lodash-es"; import TableToolbarButton from "@src/components/TableToolbar/TableToolbarButton"; @@ -50,7 +51,7 @@ export default function Extensions() { ); const open = modal === "extensions"; - const setOpen = (open: boolean) => setModal(open ? "extensions" : null); + const setOpen = (open: boolean) => setModal(open ? "extensions" : RESET); const [openMigrationGuide, setOpenMigrationGuide] = useState(false); const [extensionModal, setExtensionModal] = useState<{ diff --git a/src/components/TableToolbar/Webhooks/Webhooks.tsx b/src/components/TableToolbar/Webhooks/Webhooks.tsx index 48566b1b..c8079703 100644 --- a/src/components/TableToolbar/Webhooks/Webhooks.tsx +++ b/src/components/TableToolbar/Webhooks/Webhooks.tsx @@ -1,5 +1,6 @@ import { useState } from "react"; import { useAtom, useSetAtom } from "jotai"; +import { RESET } from "jotai/utils"; import { isEqual } from "lodash-es"; import { useSnackbar } from "notistack"; @@ -50,7 +51,7 @@ export default function Webhooks() { useState(currentWebhooks); const open = modal === "webhooks"; - const setOpen = (open: boolean) => setModal(open ? "webhooks" : null); + const setOpen = (open: boolean) => setModal(open ? "webhooks" : RESET); const [webhookModal, setWebhookModal] = useState<{ mode: "add" | "update"; diff --git a/src/index.tsx b/src/index.tsx index f4d7b2c9..5d545f47 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -8,11 +8,11 @@ import reportWebVitals from "./reportWebVitals"; const container = document.getElementById("root")!; const root = createRoot(container); root.render( - // - - - - // + + + + + ); // If you want to start measuring performance in your app, pass a function diff --git a/src/pages/Table.tsx b/src/pages/Table.tsx index b053036e..c6b5caa0 100644 --- a/src/pages/Table.tsx +++ b/src/pages/Table.tsx @@ -1,5 +1,6 @@ import { useRef, Suspense } from "react"; import { useAtom, Provider } from "jotai"; +import { DebugAtoms } from "@src/atoms/utils"; import { useParams } from "react-router-dom"; import { DataGridHandle } from "react-data-grid"; import { isEmpty } from "lodash-es"; @@ -89,6 +90,7 @@ export default function ProvidedTablePage() { [currentUserAtom, currentUser], ]} > +