From 4186ff60d526eada4d88e448130475d62ec34534 Mon Sep 17 00:00:00 2001 From: Sidney Alcantara Date: Thu, 9 Sep 2021 22:27:00 +1000 Subject: [PATCH 1/4] CellValidation: fix cell selected state --- src/components/Table/CellValidation.tsx | 41 +++++++++---------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/src/components/Table/CellValidation.tsx b/src/components/Table/CellValidation.tsx index 401a2ea3..5dbe1ec3 100644 --- a/src/components/Table/CellValidation.tsx +++ b/src/components/Table/CellValidation.tsx @@ -5,7 +5,7 @@ import WarningIcon from "@material-ui/icons/WarningAmber"; import RichTooltip from "components/RichTooltip"; -const Root = styled(Box)({ +const Root = styled(Box)(({ theme, ...props }) => ({ width: "100%", height: "100%", padding: "var(--cell-padding)", @@ -15,7 +15,15 @@ const Root = styled(Box)({ contain: "strict", display: "flex", alignItems: "center", -}); + + ...((props as any).error + ? { + ".rdg-cell:not([aria-selected=true]) &": { + boxShadow: `inset 0 0 0 2px ${theme.palette.error.main}`, + }, + } + : {}), +})); const Dot = styled("div")(({ theme }) => ({ position: "absolute", @@ -51,8 +59,7 @@ export default function CellValidation({ required, validationRegex, children, -}: // ...props -ICellValidationProps) { +}: ICellValidationProps) { const isInvalid = validationRegex && !new RegExp(validationRegex).test(value); const isMissing = required && value === undefined; @@ -67,14 +74,7 @@ ICellValidationProps) { render={({ openTooltip }) => } /> - `inset 0 0 0 2px ${theme.palette.error.main}`, - }} - > - {children} - + {children} ); @@ -89,22 +89,9 @@ ICellValidationProps) { render={({ openTooltip }) => } /> - `inset 0 0 0 2px ${theme.palette.error.main}`, - }} - > - {children} - + {children} ); - return ( - - {children} - - ); + return {children}; } From 61f49f41487d5bf657c55f16613b82be30c57783 Mon Sep 17 00:00:00 2001 From: Sidney Alcantara Date: Thu, 9 Sep 2021 22:27:34 +1000 Subject: [PATCH 2/4] CellValidation: remove unncessary Box --- src/components/Table/CellValidation.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/Table/CellValidation.tsx b/src/components/Table/CellValidation.tsx index 5dbe1ec3..ce1fda8e 100644 --- a/src/components/Table/CellValidation.tsx +++ b/src/components/Table/CellValidation.tsx @@ -1,11 +1,10 @@ import { styled } from "@material-ui/core/styles"; -import { Box } from "@material-ui/core"; import ErrorIcon from "@material-ui/icons/ErrorOutline"; import WarningIcon from "@material-ui/icons/WarningAmber"; import RichTooltip from "components/RichTooltip"; -const Root = styled(Box)(({ theme, ...props }) => ({ +const Root = styled("div")(({ theme, ...props }) => ({ width: "100%", height: "100%", padding: "var(--cell-padding)", From 80ee7d030e44824fefd1f4861587e041909019eb Mon Sep 17 00:00:00 2001 From: Sidney Alcantara Date: Thu, 9 Sep 2021 22:54:27 +1000 Subject: [PATCH 3/4] support multiple tables pointing to the same collection --- src/components/Navigation/Breadcrumbs.tsx | 16 ++--- src/components/Navigation/NavDrawer.tsx | 15 +++- src/components/Navigation/NavTableSection.tsx | 51 ++++++++------ src/components/Table/EmptyTable.tsx | 8 ++- .../Table/TableHeader/LoadedRowsStatus.tsx | 2 +- .../Table/TableHeader/ReExecute.tsx | 2 - .../Table/TableHeader/TableSettings.tsx | 17 +++-- src/components/TableSettings/form.tsx | 6 ++ src/components/TableSettings/index.tsx | 43 +++++------- .../fields/ConnectTable/Settings.tsx | 10 +-- src/components/fields/SubTable/utils.ts | 17 +++-- src/contexts/ProjectContext.tsx | 20 +++--- src/hooks/useBasicSearch.ts | 6 +- src/hooks/useSettings.ts | 68 +++++++++---------- src/hooks/useTable/index.ts | 28 ++++---- src/hooks/useTable/useTableConfig.ts | 7 +- src/hooks/useTable/useTableData.tsx | 14 ++-- src/pages/Table.tsx | 39 +++++------ src/utils/fns.ts | 1 + 19 files changed, 193 insertions(+), 177 deletions(-) diff --git a/src/components/Navigation/Breadcrumbs.tsx b/src/components/Navigation/Breadcrumbs.tsx index 6ced1cb7..fb434ea6 100644 --- a/src/components/Navigation/Breadcrumbs.tsx +++ b/src/components/Navigation/Breadcrumbs.tsx @@ -17,18 +17,19 @@ import routes from "constants/routes"; export default function Breadcrumbs(props: BreadcrumbsProps) { const { tables, tableState } = useProjectContext(); - const collection = tableState?.tablePath || ""; + const id = tableState?.config.id || ""; + const collection = id || tableState?.tablePath || ""; const router = useRouter(); - const parentLabel = decodeURIComponent( + let parentLabel = decodeURIComponent( queryString.parse(router.location.search).parentLabel as string ); + if (parentLabel === "undefined") parentLabel = ""; const breadcrumbs = collection.split("/"); - const section = _find(tables, ["collection", breadcrumbs[0]])?.section || ""; - const getLabel = (collection: string) => - _find(tables, ["collection", collection])?.name || collection; + const section = _find(tables, ["id", breadcrumbs[0]])?.section || ""; + const getLabel = (id: string) => _find(tables, ["id", id])?.name || id; return ( diff --git a/src/components/Navigation/NavDrawer.tsx b/src/components/Navigation/NavDrawer.tsx index 8bc85bfa..ed8c4fc9 100644 --- a/src/components/Navigation/NavDrawer.tsx +++ b/src/components/Navigation/NavDrawer.tsx @@ -1,4 +1,5 @@ -import { useLocation } from "react-router-dom"; +import _find from "lodash/find"; +import _groupBy from "lodash/groupBy"; import { Drawer, @@ -21,6 +22,7 @@ import Logo from "assets/Logo"; import NavItem from "./NavItem"; import NavTableSection from "./NavTableSection"; +import { useAppContext } from "contexts/AppContext"; import { useProjectContext } from "contexts/ProjectContext"; import { routes } from "constants/routes"; @@ -35,7 +37,16 @@ export default function NavDrawer({ currentSection, ...props }: INavDrawerProps) { - const { userClaims, sections } = useProjectContext(); + const { userDoc } = useAppContext(); + const { userClaims, tables } = useProjectContext(); + + const favorites = Array.isArray(userDoc.state.doc?.favoriteTables) + ? userDoc.state.doc.favoriteTables + : []; + const sections = { + Favorites: favorites.map((id) => _find(tables, { id })), + ..._groupBy(tables, "section"), + }; const closeDrawer = (e: {}) => props.onClose(e, "escapeKeyDown"); diff --git a/src/components/Navigation/NavTableSection.tsx b/src/components/Navigation/NavTableSection.tsx index 8c971d1d..5d2e33ae 100644 --- a/src/components/Navigation/NavTableSection.tsx +++ b/src/components/Navigation/NavTableSection.tsx @@ -1,4 +1,5 @@ import { useState } from "react"; +import { useLocation } from "react-router-dom"; import { List, ListItemText, Collapse } from "@material-ui/core"; import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown"; @@ -8,6 +9,7 @@ import { Table } from "contexts/ProjectContext"; import { routes } from "constants/routes"; export interface INavDrawerItemProps { + open?: boolean; section: string; tables: Table[]; currentSection?: string; @@ -15,12 +17,14 @@ export interface INavDrawerItemProps { } export default function NavDrawerItem({ + open: openProp, section, tables, currentSection, closeDrawer, }: INavDrawerItemProps) { - const [open, setOpen] = useState(section === currentSection); + const { pathname } = useLocation(); + const [open, setOpen] = useState(openProp || section === currentSection); return (
  • @@ -42,27 +46,30 @@ export default function NavDrawerItem({ - {tables.map((table) => ( -
  • - `calc(100% - ${theme.spacing(2 + 0.5)})`, - }} - > - - -
  • - ))} + {tables + .filter((x) => x) + .map((table) => { + const route = table.isCollectionGroup + ? `${routes.tableGroup}/${table.id}` + : `${routes.table}/${table.id.replace(/\//g, "~2F")}`; + + return ( +
  • + + `calc(100% - ${theme.spacing(2 + 0.5)})`, + }} + > + + +
  • + ); + })} diff --git a/src/components/Table/EmptyTable.tsx b/src/components/Table/EmptyTable.tsx index 5be8a476..3dae6819 100644 --- a/src/components/Table/EmptyTable.tsx +++ b/src/components/Table/EmptyTable.tsx @@ -55,7 +55,13 @@ export default function EmptyTable() { Get Started - There is no data in this table. + + There is no data in the Firestore collection: +
    + + {tableState?.tablePath} + +
    diff --git a/src/components/Table/TableHeader/LoadedRowsStatus.tsx b/src/components/Table/TableHeader/LoadedRowsStatus.tsx index 5a6ea6fc..bc9c3542 100644 --- a/src/components/Table/TableHeader/LoadedRowsStatus.tsx +++ b/src/components/Table/TableHeader/LoadedRowsStatus.tsx @@ -22,7 +22,7 @@ export default function LoadedRowsStatus() { variant="body2" color="text.disabled" display="block" - style={{ userSelect: "none", display: "flex", alignItems: "center" }} + style={{ userSelect: "none" }} > Loaded {allLoaded && "all "} {tableState.rows.length} rows diff --git a/src/components/Table/TableHeader/ReExecute.tsx b/src/components/Table/TableHeader/ReExecute.tsx index ac77a2de..733f02f9 100644 --- a/src/components/Table/TableHeader/ReExecute.tsx +++ b/src/components/Table/TableHeader/ReExecute.tsx @@ -8,8 +8,6 @@ import { db } from "../../../firebase"; import { isCollectionGroup } from "utils/fns"; import CircularProgress from "@material-ui/core/CircularProgress"; -import { DialogContentText } from "@material-ui/core"; - import Modal from "components/Modal"; export default function ReExecute() { diff --git a/src/components/Table/TableHeader/TableSettings.tsx b/src/components/Table/TableHeader/TableSettings.tsx index 8c700b18..61895f4a 100644 --- a/src/components/Table/TableHeader/TableSettings.tsx +++ b/src/components/Table/TableHeader/TableSettings.tsx @@ -1,4 +1,5 @@ import { useState } from "react"; +import _find from "lodash/find"; import TableHeaderButton from "./TableHeaderButton"; import SettingsIcon from "@material-ui/icons/SettingsOutlined"; @@ -11,7 +12,8 @@ import { useProjectContext } from "contexts/ProjectContext"; export default function TableSettings() { const [open, setOpen] = useState(false); - const { tableState } = useProjectContext(); + const { tableState, tables } = useProjectContext(); + const table = _find(tables, { id: tableState?.config.id }); return ( <> @@ -19,13 +21,16 @@ export default function TableSettings() { title="Table Settings" onClick={() => setOpen(true)} icon={} + disabled={!table} /> - setOpen(false)} - mode={open ? TableSettingsDialogModes.update : null} - data={open ? tableState?.config.tableConfig.doc : null} - /> + {table && ( + setOpen(false)} + mode={open ? TableSettingsDialogModes.update : null} + data={open ? table : null} + /> + )} ); } diff --git a/src/components/TableSettings/form.tsx b/src/components/TableSettings/form.tsx index 725feae7..a3b439f5 100644 --- a/src/components/TableSettings/form.tsx +++ b/src/components/TableSettings/form.tsx @@ -20,6 +20,12 @@ export const tableSettings = ( label: "Table Name", required: true, }, + { + type: FieldType.shortText, + name: "id", + label: "Table ID", + required: true, + }, { type: FieldType.shortText, name: "collection", diff --git a/src/components/TableSettings/index.tsx b/src/components/TableSettings/index.tsx index 4bacb5a7..7cb08163 100644 --- a/src/components/TableSettings/index.tsx +++ b/src/components/TableSettings/index.tsx @@ -10,11 +10,11 @@ import Confirmation from "components/Confirmation"; import { FormDialog } from "@antlerengineering/form-builder"; import { tableSettings } from "./form"; -import { useProjectContext } from "contexts/ProjectContext"; +import { useProjectContext, Table } from "contexts/ProjectContext"; import useRouter from "../../hooks/useRouter"; import { db } from "../../firebase"; import { name } from "@root/package.json"; -import { SETTINGS, TABLE_SCHEMAS } from "config/dbPaths"; +import { SETTINGS, TABLE_SCHEMAS, TABLE_GROUP_SCHEMAS } from "config/dbPaths"; export enum TableSettingsDialogModes { create, @@ -23,15 +23,7 @@ export enum TableSettingsDialogModes { export interface ICreateTableDialogProps { mode: TableSettingsDialogModes | null; clearDialog: () => void; - data: { - name: string; - collection: string; - tableType: string; - section: string; - description: string; - isCollectionGroup: boolean; - roles: string[]; - } | null; + data: Table | null; } const FORM_EMPTY_STATE = { @@ -67,8 +59,10 @@ export default function TableSettingsDialog({ }: ICreateTableDialogProps) { const classes = useStyles(); - const { settingsActions, sections, roles, tables } = useProjectContext(); - const sectionNames = sections ? Object.keys(sections) : []; + const { settingsActions, roles, tables } = useProjectContext(); + const sectionNames = Array.from( + new Set((tables ?? []).map((t) => t.section)) + ); const router = useRouter(); const open = mode !== null; @@ -101,22 +95,21 @@ export default function TableSettingsDialog({ }; if (values.schemaSource) - data.schemaSource = _find(tables, { collection: values.schemaSource }); + data.schemaSource = _find(tables, { id: values.schemaSource }); if (mode === TableSettingsDialogModes.update) { await Promise.all([settingsActions?.updateTable(data), handleClose()]); - window.location.reload(); } else { settingsActions?.createTable(data); if (router.location.pathname === "/") { router.history.push( `${values.tableType === "collectionGroup" ? "tableGroup" : "table"}/${ - values.collection + values.id }` ); } else { - router.history.push(values.collection); + router.history.push(values.id); } } @@ -124,7 +117,7 @@ export default function TableSettingsDialog({ }; const handleResetStructure = async () => { - const schemaDocRef = db.doc(`${TABLE_SCHEMAS}/${data!.collection}`); + const schemaDocRef = db.doc(`${TABLE_SCHEMAS}/${data!.id}`); await schemaDocRef.update({ columns: {} }); handleClose(); }; @@ -134,13 +127,13 @@ export default function TableSettingsDialog({ const tableData = (await tablesDocRef.get()).data(); const updatedTables = tableData?.tables.filter( (table) => - table.collection !== data?.collection || + table.id !== data?.id || table.isCollectionGroup !== data?.isCollectionGroup ); await tablesDocRef.update({ tables: updatedTables }); - await tablesDocRef - .collection(Boolean(data?.isCollectionGroup) ? "schema" : "groupSchema") - .doc(data?.collection) + await db + .collection(data?.isCollectionGroup ? TABLE_SCHEMAS : TABLE_GROUP_SCHEMAS) + .doc(data?.id) .delete(); window.location.reload(); handleClose(); @@ -158,7 +151,7 @@ export default function TableSettingsDialog({ mode, roles, sectionNames, - tables?.map((table) => ({ label: table.name, value: table.collection })) + tables?.map((table) => ({ label: table.name, value: table.id })) )} values={{ tableType: data?.isCollectionGroup @@ -205,7 +198,7 @@ export default function TableSettingsDialog({
    @@ -241,7 +234,7 @@ export default function TableSettingsDialog({ diff --git a/src/components/fields/ConnectTable/Settings.tsx b/src/components/fields/ConnectTable/Settings.tsx index e07d6276..9cc7514e 100644 --- a/src/components/fields/ConnectTable/Settings.tsx +++ b/src/components/fields/ConnectTable/Settings.tsx @@ -3,7 +3,6 @@ import { ISettingsProps } from "../types"; import _sortBy from "lodash/sortBy"; import { TextField } from "@material-ui/core"; -import Subheading from "components/Table/ColumnMenu/Subheading"; import MultiSelect from "@antlerengineering/multiselect"; import { FieldType } from "constants/fields"; @@ -15,8 +14,8 @@ export default function Settings({ handleChange, config }: ISettingsProps) { const { tables } = useProjectContext(); const tableOptions = _sortBy( tables?.map((t) => ({ - label: `${t.section} - ${t.name}`, - value: t.collection, + label: `${t.section} – ${t.name} (${t.collection})`, + value: t.id, })) ?? [], "label" ); @@ -44,16 +43,17 @@ export default function Settings({ handleChange, config }: ISettingsProps) { return ( <> - Table Connect Config void; settingsActions: { createTable: (data: { + id: string; collection: string; name: string; description: string; @@ -48,13 +48,14 @@ interface ProjectContextProps { section: string; }) => void; updateTable: (data: { + id: string; collection: string; name: string; description: string; roles: string[]; section: string; }) => Promise; - deleteTable: (collection: string) => void; + deleteTable: (id: string) => void; }; userClaims: any; @@ -97,7 +98,6 @@ export const ProjectContextProvider: React.FC = ({ children }) => { const { enqueueSnackbar } = useSnackbar(); const { tableState, tableActions } = useTable(); const [tables, setTables] = useState(); - const [sections, setSections] = useState(); const [settings, settingsActions] = useSettings(); const [userRoles, setUserRoles] = useState(); const [userClaims, setUserClaims] = useState(); @@ -106,7 +106,7 @@ export const ProjectContextProvider: React.FC = ({ children }) => { const [authToken, setAuthToken] = useState(""); useEffect(() => { const { tables } = settings; - if (tables && userRoles && !sections) { + if (tables && userRoles) { const filteredTables = _sortBy(tables, "name") .filter( (table) => @@ -118,13 +118,14 @@ export const ProjectContextProvider: React.FC = ({ children }) => { section: table.section ? table.section.trim() : "Other", })); - const _sections = _groupBy(filteredTables, "section"); - setSections(_sections); setTables( - filteredTables.map((table) => ({ ...table, id: table.collection })) + filteredTables.map((table) => ({ + ...table, + id: table.id || table.collection, // Ensure id exists + })) ); } - }, [settings, userRoles, sections]); + }, [settings, userRoles]); const roles = useMemo( () => @@ -228,7 +229,6 @@ export const ProjectContextProvider: React.FC = ({ children }) => { settingsActions, roles, tables, - sections, userClaims, dataGridRef, sideDrawerRef, diff --git a/src/hooks/useBasicSearch.ts b/src/hooks/useBasicSearch.ts index b95e2001..47825662 100644 --- a/src/hooks/useBasicSearch.ts +++ b/src/hooks/useBasicSearch.ts @@ -2,7 +2,7 @@ import { useState } from "react"; import { useDebouncedCallback } from "use-debounce"; export default function useBasicSearch( - collection: T[], + list: T[], predicate: (item: T, query: string) => boolean, debounce: number = 400 ) { @@ -10,8 +10,8 @@ export default function useBasicSearch( const [handleQuery] = useDebouncedCallback(setQuery, debounce); const results = query - ? collection.filter((user) => predicate(user, query.toLowerCase())) - : collection; + ? list.filter((user) => predicate(user, query.toLowerCase())) + : list; return [results, query, handleQuery] as const; } diff --git a/src/hooks/useSettings.ts b/src/hooks/useSettings.ts index 7b7191e8..554f7999 100644 --- a/src/hooks/useSettings.ts +++ b/src/hooks/useSettings.ts @@ -1,4 +1,6 @@ import { useEffect } from "react"; +import _findIndex from "lodash/findIndex"; + import useDoc from "./useDoc"; import { db } from "../firebase"; import { SETTINGS, TABLE_GROUP_SCHEMAS, TABLE_SCHEMAS } from "config/dbPaths"; @@ -21,6 +23,7 @@ const useSettings = () => { }, [settingsState]); const createTable = async (data: { + id: string; name: string; collection: string; description: string; @@ -34,67 +37,58 @@ const useSettings = () => { tableSettings.tableType !== "collectionGroup" ? TABLE_SCHEMAS : TABLE_GROUP_SCHEMAS - }/${tableSettings.collection}`; + }/${tableSettings.id}`; const tableSchemaDocRef = db.doc(tableSchemaPath); - let columns = {}; + // Get columns from schemaSource if provided + let columns = []; if (schemaSource) { const schemaSourcePath = `${ tableSettings.tableType !== "collectionGroup" ? TABLE_SCHEMAS : TABLE_GROUP_SCHEMAS - }/${schemaSource.collection}`; + }/${schemaSource.id}`; const sourceDoc = await db.doc(schemaSourcePath).get(); columns = sourceDoc.get("columns"); } - // updates the setting doc - await db - .doc(SETTINGS) - .set( - { tables: tables ? [...tables, tableSettings] : [tableSettings] }, - { merge: true } - ); - //create the rowy collection doc with empty columns - await tableSchemaDocRef.set({ ...tableSettings, columns }, { merge: true }); + // Appends table to settings doc + await db.doc(SETTINGS).set( + { + tables: Array.isArray(tables) + ? [...tables, tableSettings] + : [tableSettings], + }, + { merge: true } + ); + + // Creates schema doc with columns + await tableSchemaDocRef.set({ columns }, { merge: true }); }; - const updateTable = (data: { + const updateTable = async (data: { + id: string; name: string; collection: string; description: string; roles: string[]; }) => { const { tables } = settingsState; - const table = tables.filter((t) => t.collection === data.collection)[0]; - return Promise.all([ - db.doc(SETTINGS).set( - { - tables: tables - ? [ - ...tables.filter( - (table) => table.collection !== data.collection - ), - { table, ...data }, - ] - : [data], - }, - { merge: true } - ), - //update the rowy collection doc with empty columns - db - .collection(TABLE_SCHEMAS) - .doc(data.collection) - .set({ ...data }, { merge: true }), - ]); + const newTables = Array.isArray(tables) ? [...tables] : []; + const foundIndex = _findIndex(newTables, { id: data.id }); + const tableIndex = foundIndex > -1 ? foundIndex : tables.length; + newTables[tableIndex] = { ...newTables[tableIndex], ...data }; + + await db.doc(SETTINGS).set({ tables: newTables }, { merge: true }); }; - const deleteTable = (collection: string) => { + + const deleteTable = (id: string) => { const { tables } = settingsState; db.doc(SETTINGS).update({ - tables: tables.filter((table) => table.collection !== collection), + tables: tables.filter((table) => table.id !== id), }); - db.collection(TABLE_SCHEMAS).doc(collection).delete(); + db.collection(TABLE_SCHEMAS).doc(id).delete(); }; const settingsActions = { createTable, updateTable, deleteTable }; return [settingsState, settingsActions]; diff --git a/src/hooks/useTable/index.ts b/src/hooks/useTable/index.ts index 56d558d2..cdcc56c0 100644 --- a/src/hooks/useTable/index.ts +++ b/src/hooks/useTable/index.ts @@ -13,7 +13,7 @@ export type TableActions = { }; row: { add: Function; delete: Function; more: Function; update: Function }; table: { - set: Function; + set: (id: string, collection: string, filters: TableFilter[]) => void; filter: Function; updateConfig: Function; orderBy: Function; @@ -24,6 +24,7 @@ export type TableState = { orderBy: TableOrder; tablePath: string; config: { + id: string; rowHeight: number; tableConfig: any; webhooks: any; @@ -45,25 +46,19 @@ export type TableFilter = { }; export type TableOrder = { key: string; direction: "asc" | "desc" }[]; -export default function useTable( - collectionName?: string, - filters?: TableFilter[], - orderBy?: TableOrder -) { - const [tableConfig, configActions] = useTableConfig(collectionName); - const [tableState, tableActions] = useTableData({ - path: collectionName, - filters, - orderBy, - }); +export default function useTable() { + const [tableConfig, configActions] = useTableConfig(); + const [tableState, tableActions] = useTableData(); /** set collection path of table */ - const setTable = (collectionName: string, filters: TableFilter[]) => { - if (collectionName !== tableState.path || filters !== tableState.filters) { - configActions.setTable(collectionName); - tableActions.setTable(collectionName, filters); + const setTable = (id: string, collection: string, filters: TableFilter[]) => { + if (collection !== tableState.path || filters !== tableState.filters) { + configActions.setTable(id); + // Wait for config doc to load to get collection path + tableActions.setTable(collection, filters); } }; + const filterTable = (filters: TableFilter[]) => { tableActions.dispatch({ filters }); }; @@ -76,6 +71,7 @@ export default function useTable( filters: tableState.filters, columns: tableConfig.columns, config: { + id: tableConfig.id, rowHeight: tableConfig.rowHeight, webhooks: tableConfig.doc?.webhooks, sparks: tableConfig.doc?.sparks, diff --git a/src/hooks/useTable/useTableConfig.ts b/src/hooks/useTable/useTableConfig.ts index 1481aeba..fca6e860 100644 --- a/src/hooks/useTable/useTableConfig.ts +++ b/src/hooks/useTable/useTableConfig.ts @@ -40,12 +40,13 @@ const useTableConfig = (tablePath?: string) => { /** used for specifying the table in use * @param table firestore collection path */ - const setTable = (table: string) => { + const setTable = (id: string) => { documentDispatch({ - path: formatPath(table), + id, + path: formatPath(id), columns: [], doc: null, - ref: db.doc(formatPath(table)), + ref: db.doc(formatPath(id)), loading: true, }); }; diff --git a/src/hooks/useTable/useTableData.tsx b/src/hooks/useTable/useTableData.tsx index c3ad89b9..0d24521d 100644 --- a/src/hooks/useTable/useTableData.tsx +++ b/src/hooks/useTable/useTableData.tsx @@ -2,10 +2,9 @@ import { db } from "../../firebase"; import { useSnackbar } from "notistack"; import Button from "@material-ui/core/Button"; -import { useEffect, useReducer, useContext } from "react"; +import { useEffect, useReducer } from "react"; import _isEqual from "lodash/isEqual"; import _merge from "lodash/merge"; -import _find from "lodash/find"; import firebase from "firebase/app"; import { TableFilter, TableOrder } from "."; @@ -13,7 +12,6 @@ import { isCollectionGroup, generateSmallerId, missingFieldsReducer, - deepMerge, deepen, } from "utils/fns"; import { projectId } from "../../firebase"; @@ -103,14 +101,14 @@ const tableInitialState = { cap: CAP, }; -const useTableData = (initialOverrides: any) => { +const useTableData = () => { const { enqueueSnackbar } = useSnackbar(); const { currentUser } = useAppContext(); - const [tableState, tableDispatch] = useReducer(tableReducer, { - ...tableInitialState, - ...initialOverrides, - }); + const [tableState, tableDispatch] = useReducer( + tableReducer, + tableInitialState + ); const [rows, rowsDispatch] = useReducer(rowsReducer, []); /** set collection listener diff --git a/src/pages/Table.tsx b/src/pages/Table.tsx index 6eba175b..ea3f5f07 100644 --- a/src/pages/Table.tsx +++ b/src/pages/Table.tsx @@ -1,4 +1,5 @@ import { useEffect } from "react"; +import { useLocation, useRouteMatch } from "react-router-dom"; import queryString from "query-string"; import _isEmpty from "lodash/isEmpty"; import _find from "lodash/find"; @@ -16,29 +17,27 @@ import EmptyTable from "components/Table/EmptyTable"; import { useProjectContext } from "contexts/ProjectContext"; import { useAppContext } from "contexts/AppContext"; import { TableFilter } from "hooks/useTable"; -import useRouter from "hooks/useRouter"; import { DocActions } from "hooks/useDoc"; import ActionParamsProvider from "components/fields/Action/FormDialog/Provider"; export default function TablePage() { - const router = useRouter(); - const tableCollection = decodeURIComponent(router.match.params.id); + const location = useLocation(); + const match = useRouteMatch<{ id: string }>(); + const urlPath = decodeURIComponent(match.params.id); + const urlPathSplit = urlPath.split("/"); const { tableState, tableActions, sideDrawerRef, tables } = useProjectContext(); const { userDoc } = useAppContext(); // Find the matching section for the current route - const currentSection = _find(tables, [ - "collection", - tableCollection?.split("/")[0], - ])?.section; - const currentTable = tableCollection?.split("/")[0]; - const tableName = - _find(tables, ["collection", currentTable])?.name || currentTable; + const currentTableId = urlPathSplit[0]; + const currentSection = _find(tables, ["id", currentTableId])?.section; + const table = _find(tables, ["id", currentTableId]); + const tableName = table?.name || currentTableId; let filters: TableFilter[] = []; - const parsed = queryString.parse(router.location.search); + const parsed = queryString.parse(location.search); if (typeof parsed.filters === "string") { filters = JSON.parse(parsed.filters); // TODO: json schema validator @@ -46,24 +45,26 @@ export default function TablePage() { useEffect(() => { if ( + table && tableActions && tableState && - tableState.tablePath !== tableCollection + tableState.config.id !== urlPath ) { - tableActions.table.set(tableCollection, filters); + // Support multiple tables for top-level collection but unique sub-table configs + const collection = [table.collection, ...urlPathSplit.slice(1)].join("/"); + + tableActions.table.set(urlPath, collection, filters); if (filters && filters.length !== 0) { userDoc.dispatch({ action: DocActions.update, - data: { - tables: { [`${tableState.tablePath}`]: { filters } }, - }, + data: { tables: { [urlPath]: { filters } } }, }); } if (sideDrawerRef?.current) sideDrawerRef.current.setCell!(null); } - }, [tableCollection]); + }, [urlPath, tableActions, tableState, table]); - if (!tableState) return null; + if (!tableState || !table) return null; return ( ) : ( <> - +
    diff --git a/src/utils/fns.ts b/src/utils/fns.ts index 16a7429b..2b3dfb23 100644 --- a/src/utils/fns.ts +++ b/src/utils/fns.ts @@ -94,6 +94,7 @@ export const generateBiggerId = (id: string) => { else return id[0] + generateBiggerId(id.substr(1, id.length - 1)); }; +// Gets sub-table ID in $1 const formatPathRegex = /\/[^\/]+\/([^\/]+)/g; export const formatPath = (tablePath: string) => { From f1ba1030bcd538d54e91009fe5b3736b158ab7cd Mon Sep 17 00:00:00 2001 From: Sidney Alcantara Date: Thu, 9 Sep 2021 22:56:16 +1000 Subject: [PATCH 4/4] =?UTF-8?q?NavDrawer:=20don=E2=80=99t=20show=20Favorit?= =?UTF-8?q?es=20section=20if=20user=20has=20none?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Navigation/NavDrawer.tsx | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/components/Navigation/NavDrawer.tsx b/src/components/Navigation/NavDrawer.tsx index ed8c4fc9..7cb89751 100644 --- a/src/components/Navigation/NavDrawer.tsx +++ b/src/components/Navigation/NavDrawer.tsx @@ -114,15 +114,17 @@ export default function NavDrawer({ {sections && - Object.entries(sections).map(([section, tables]) => ( - - ))} + Object.entries(sections) + .filter(([, tables]) => tables.length > 0) + .map(([section, tables]) => ( + + ))}