This commit is contained in:
shamsmosowi
2021-09-09 23:21:40 +10:00
20 changed files with 218 additions and 214 deletions

View File

@@ -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 (
<MuiBreadcrumbs
@@ -41,11 +42,6 @@ export default function Breadcrumbs(props: BreadcrumbsProps) {
flexWrap: "nowrap",
whiteSpace: "nowrap",
},
"& li": {
textTransform: "capitalize",
"&:first-of-type": { textTransform: "capitalize" },
},
}}
{...(props as any)}
>

View File

@@ -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");
@@ -103,15 +114,17 @@ export default function NavDrawer({
<Divider variant="middle" sx={{ my: 1 }} />
{sections &&
Object.entries(sections).map(([section, tables]) => (
<NavTableSection
key={section}
section={section}
tables={tables}
currentSection={currentSection}
closeDrawer={closeDrawer}
/>
))}
Object.entries(sections)
.filter(([, tables]) => tables.length > 0)
.map(([section, tables]) => (
<NavTableSection
key={section}
section={section}
tables={tables}
currentSection={currentSection}
closeDrawer={closeDrawer}
/>
))}
</List>
</nav>
</Drawer>

View File

@@ -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 (
<li>
@@ -42,27 +46,30 @@ export default function NavDrawerItem({
<Collapse in={open}>
<List disablePadding>
{tables.map((table) => (
<li key={table.collection}>
<NavItem
to={
table.isCollectionGroup
? `${routes.tableGroup}/${table.collection}`
: `${routes.table}/${table.collection.replace(
/\//g,
"~2F"
)}`
}
onClick={closeDrawer}
sx={{
ml: 2,
width: (theme) => `calc(100% - ${theme.spacing(2 + 0.5)})`,
}}
>
<ListItemText primary={table.name} />
</NavItem>
</li>
))}
{tables
.filter((x) => x)
.map((table) => {
const route = table.isCollectionGroup
? `${routes.tableGroup}/${table.id}`
: `${routes.table}/${table.id.replace(/\//g, "~2F")}`;
return (
<li key={table.id}>
<NavItem
to={route}
selected={pathname.split("%2F")[0] === route}
onClick={closeDrawer}
sx={{
ml: 2,
width: (theme) =>
`calc(100% - ${theme.spacing(2 + 0.5)})`,
}}
>
<ListItemText primary={table.name} />
</NavItem>
</li>
);
})}
</List>
</Collapse>
</li>

View File

@@ -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)({
const Root = styled("div")(({ theme, ...props }) => ({
width: "100%",
height: "100%",
padding: "var(--cell-padding)",
@@ -15,7 +14,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 +58,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 +73,7 @@ ICellValidationProps) {
render={({ openTooltip }) => <Dot onClick={openTooltip} />}
/>
<Root
// {...props}
sx={{
boxShadow: (theme) => `inset 0 0 0 2px ${theme.palette.error.main}`,
}}
>
{children}
</Root>
<Root {...({ error: true } as any)}>{children}</Root>
</>
);
@@ -89,22 +88,9 @@ ICellValidationProps) {
render={({ openTooltip }) => <Dot onClick={openTooltip} />}
/>
<Root
// {...props}
sx={{
boxShadow: (theme) => `inset 0 0 0 2px ${theme.palette.error.main}`,
}}
>
{children}
</Root>
<Root {...({ error: true } as any)}>{children}</Root>
</>
);
return (
<Root
// {...props}
>
{children}
</Root>
);
return <Root>{children}</Root>;
}

View File

@@ -55,7 +55,13 @@ export default function EmptyTable() {
<Typography variant="h6" component="h2" gutterBottom>
Get Started
</Typography>
<Typography>There is no data in this table.</Typography>
<Typography>
There is no data in the Firestore collection:
<br />
<Typography component="span" sx={{ fontFamily: "mono" }}>
{tableState?.tablePath}
</Typography>
</Typography>
</div>
<Grid container spacing={1}>

View File

@@ -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

View File

@@ -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() {

View File

@@ -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={<SettingsIcon />}
disabled={!table}
/>
<TableSettingsDialog
clearDialog={() => setOpen(false)}
mode={open ? TableSettingsDialogModes.update : null}
data={open ? tableState?.config.tableConfig.doc : null}
/>
{table && (
<TableSettingsDialog
clearDialog={() => setOpen(false)}
mode={open ? TableSettingsDialogModes.update : null}
data={open ? table : null}
/>
)}
</>
);
}

View File

@@ -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",

View File

@@ -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({
<div className={classes.formFooter}>
<Confirmation
message={{
title: `Are you sure you want to delete the table structure for “${formState.name}”?`,
title: `Delete the table structure for “${formState.name}”?`,
body: (
<>
<DialogContentText>
@@ -241,7 +234,7 @@ export default function TableSettingsDialog({
<Confirmation
message={{
title: `Are you sure you want to delete the table “${formState.name}”?`,
title: `Delete the table “${formState.name}”?`,
body: (
<>
<DialogContentText>

View File

@@ -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 (
<>
<Subheading>Table Connect Config</Subheading>
<MultiSelect
options={tableOptions}
freeText={false}
value={config.index}
onChange={handleChange("index")}
multiple={false}
label="Table"
labelPlural="Tables"
/>
<TextField
label="filter template"
label="Filter Template"
name="filters"
fullWidth
value={config.filters}

View File

@@ -1,5 +1,5 @@
import queryString from "query-string";
import useRouter from "hooks/useRouter";
import { useLocation } from "react-router-dom";
export const useSubTableData = (
column: any,
@@ -18,18 +18,21 @@ export const useSubTableData = (
const fieldName = column.key;
const documentCount: string = row[fieldName]?.count ?? "";
const router = useRouter();
const parentLabels = queryString.parse(router.location.search).parentLabel;
const location = useLocation();
const parentLabels = queryString.parse(location.search).parentLabel;
const parentPath = decodeURIComponent(
location.pathname.split("/").pop() ?? ""
);
let subTablePath = "";
if (parentLabels)
subTablePath =
encodeURIComponent(`${docRef.path}/${fieldName}`) +
`?parentLabel=${parentLabels},${label}`;
encodeURIComponent(`${parentPath}/${docRef.id}/${fieldName}`) +
`?parentLabel=${parentLabels ?? ""},${label ?? ""}`;
else
subTablePath =
encodeURIComponent(`${docRef.path}/${fieldName}`) +
`?parentLabel=${encodeURIComponent(label)}`;
encodeURIComponent(`${parentPath}/${docRef.id}/${fieldName}`) +
`?parentLabel=${encodeURIComponent(label ?? "")}`;
return { documentCount, label, subTablePath };
};

View File

@@ -1,6 +1,5 @@
import React, { useState, useContext, useEffect, useRef, useMemo } from "react";
import { useSnackbar } from "notistack";
import _groupBy from "lodash/groupBy";
import _sortBy from "lodash/sortBy";
import { DataGridHandle } from "react-data-grid";
import firebase from "firebase/app";
@@ -21,12 +20,12 @@ export type Table = {
description: string;
section: string;
isCollectionGroup: boolean;
tableType: string;
};
interface ProjectContextProps {
tables: Table[];
roles: string[];
sections: { [sectionName: string]: Table[] };
tableState: TableState;
tableActions: TableActions;
updateCell: (
@@ -41,6 +40,7 @@ interface ProjectContextProps {
) => 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<any>;
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<ProjectContextProps["tables"]>();
const [sections, setSections] = useState<ProjectContextProps["sections"]>();
const [settings, settingsActions] = useSettings();
const [userRoles, setUserRoles] = useState<null | string[]>();
const [userClaims, setUserClaims] = useState<any>();
@@ -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,

View File

@@ -2,7 +2,7 @@ import { useState } from "react";
import { useDebouncedCallback } from "use-debounce";
export default function useBasicSearch<T>(
collection: T[],
list: T[],
predicate: (item: T, query: string) => boolean,
debounce: number = 400
) {
@@ -10,8 +10,8 @@ export default function useBasicSearch<T>(
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;
}

View File

@@ -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];

View File

@@ -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,

View File

@@ -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,
});
};

View File

@@ -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

View File

@@ -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 (
<Navigation
@@ -81,7 +82,7 @@ export default function TablePage() {
<EmptyTable />
) : (
<>
<Table key={tableCollection} />
<Table key={currentTableId} />
<Hidden smDown>
<SideDrawer />
</Hidden>

View File

@@ -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) => {