diff --git a/src/components/Table/Breadcrumbs.tsx b/src/components/Table/Breadcrumbs.tsx deleted file mode 100644 index 5e7a9f9a..00000000 --- a/src/components/Table/Breadcrumbs.tsx +++ /dev/null @@ -1,180 +0,0 @@ -import { useAtom } from "jotai"; -import { - useLocation, - useSearchParams, - Link as RouterLink, -} from "react-router-dom"; -import { find, camelCase, uniq } from "lodash-es"; - -import { - Breadcrumbs as MuiBreadcrumbs, - BreadcrumbsProps, - Link, - Typography, - Tooltip, -} from "@mui/material"; -import ReadOnlyIcon from "@mui/icons-material/EditOffOutlined"; - -import InfoTooltip from "@src/components/InfoTooltip"; -import RenderedMarkdown from "@src/components/RenderedMarkdown"; - -import { - globalScope, - userRolesAtom, - tableDescriptionDismissedAtom, - tablesAtom, -} from "@src/atoms/globalScope"; -import { ROUTES } from "@src/constants/routes"; -import { spreadSx } from "@src/utils/ui"; - -export default function Breadcrumbs({ sx = [], ...props }: BreadcrumbsProps) { - const [userRoles] = useAtom(userRolesAtom, globalScope); - const [dismissed, setDismissed] = useAtom( - tableDescriptionDismissedAtom, - globalScope - ); - const [tables] = useAtom(tablesAtom, globalScope); - - const { pathname } = useLocation(); - const id = pathname.replace(ROUTES.table + "/", ""); - const [searchParams] = useSearchParams(); - - const tableSettings = find(tables, ["id", id]); - if (!tableSettings) return null; - - const collection = id || tableSettings.collection; - const parentLabel = decodeURIComponent(searchParams.get("parentLabel") || ""); - const breadcrumbs = collection.split("/"); - const section = tableSettings.section; - const getLabel = (id: string) => find(tables, ["id", id])?.name || id; - - return ( - theme.typography.h6.fontSize, - fontWeight: "medium", - color: "text.disabled", - - "& .MuiBreadcrumbs-ol": { - userSelect: "none", - flexWrap: "nowrap", - whiteSpace: "nowrap", - }, - "& .MuiBreadcrumbs-li": { display: "flex" }, - }, - ...spreadSx(sx), - ]} - > - {/* Section name */} - {section && ( - - {section} - - )} - - {breadcrumbs.map((crumb: string, index) => { - // If it’s the first breadcrumb, show with specific style - const crumbProps = { - key: index, - variant: "h6", - component: index === 0 ? "h1" : "div", - color: - index === breadcrumbs.length - 1 ? "textPrimary" : "textSecondary", - } as const; - - // If it’s the last crumb, just show the label without linking - if (index === breadcrumbs.length - 1) - return ( -
- - {getLabel(crumb) || crumb.replace(/([A-Z])/g, " $1")} - - {crumb === tableSettings.id && tableSettings.readOnly && ( - - - - )} - - {crumb === tableSettings.id && tableSettings.description && ( - - -
- } - buttonLabel="Table info" - tooltipProps={{ - componentsProps: { - popper: { sx: { zIndex: "appBar" } }, - tooltip: { sx: { maxWidth: "75vw" } }, - } as any, - }} - defaultOpen={!dismissed.includes(tableSettings.id)} - onClose={() => - setDismissed((d) => uniq([...d, tableSettings.id])) - } - /> - )} - - ); - - // If odd: breadcrumb points to a document — link to rowRef - // FUTURE: show a picker here to switch between sub tables - if (index % 2 === 1) - return ( - - {getLabel( - parentLabel.split(",")[Math.ceil(index / 2) - 1] || crumb - )} - - ); - - // Otherwise, even: breadcrumb points to a Firestore collection - return ( - - {getLabel(crumb) || crumb.replace(/([A-Z])/g, " $1")} - - ); - })} -
- ); -} diff --git a/src/components/Table/BreadcrumbsSubTable.tsx b/src/components/Table/BreadcrumbsSubTable.tsx new file mode 100644 index 00000000..dd2583ee --- /dev/null +++ b/src/components/Table/BreadcrumbsSubTable.tsx @@ -0,0 +1,92 @@ +import { useAtom } from "jotai"; +import { Link as RouterLink, useSearchParams } from "react-router-dom"; +import { camelCase } from "lodash-es"; + +import { Stack, Breadcrumbs, Link, Typography, Tooltip } from "@mui/material"; +import MoreHorizIcon from "@mui/icons-material/MoreHoriz"; +import ReadOnlyIcon from "@mui/icons-material/EditOffOutlined"; + +import { globalScope, userRolesAtom } from "@src/atoms/globalScope"; +import { ROUTES } from "@src/constants/routes"; +import { TableSettings } from "@src/types/table"; + +export interface IBreadcrumbsSubTableProps { + rootTableSettings: TableSettings; + subTableSettings: TableSettings; + rootTableLink: string; + parentLabel?: string; +} + +export default function BreadcrumbsSubTable({ + rootTableSettings, + subTableSettings, + rootTableLink, + parentLabel, +}: IBreadcrumbsSubTableProps) { + const [userRoles] = useAtom(userRolesAtom, globalScope); + const [searchParams] = useSearchParams(); + const splitSubTableId = subTableSettings.id.split("/"); + + return ( + + theme.typography.h6.fontSize, + color: "text.disabled", + + "& .MuiBreadcrumbs-ol": { + userSelect: "none", + flexWrap: "nowrap", + whiteSpace: "nowrap", + }, + "& .MuiBreadcrumbs-li": { display: "flex" }, + "& .MuiTypography-inherit": { typography: "h6" }, + }} + > + + {rootTableSettings.section} + + + + {rootTableSettings.name} + + + {splitSubTableId.length > 3 && ( + + )} + + + {searchParams.get("parentLabel") || + splitSubTableId[splitSubTableId.length - 2]} + + + + {subTableSettings.name} + + + + {rootTableSettings.readOnly && ( + + + + )} + + ); +} diff --git a/src/components/Table/BreadcrumbsTableRoot.tsx b/src/components/Table/BreadcrumbsTableRoot.tsx new file mode 100644 index 00000000..e9888194 --- /dev/null +++ b/src/components/Table/BreadcrumbsTableRoot.tsx @@ -0,0 +1,110 @@ +import { useAtom } from "jotai"; +import { useLocation, useParams, Link as RouterLink } from "react-router-dom"; +import { find, camelCase, uniq } from "lodash-es"; + +import { + Stack, + StackProps, + Breadcrumbs, + Link, + Typography, + Tooltip, +} from "@mui/material"; +import ReadOnlyIcon from "@mui/icons-material/EditOffOutlined"; + +import InfoTooltip from "@src/components/InfoTooltip"; +import RenderedMarkdown from "@src/components/RenderedMarkdown"; + +import { + globalScope, + userRolesAtom, + tableDescriptionDismissedAtom, + tablesAtom, +} from "@src/atoms/globalScope"; +import { ROUTES } from "@src/constants/routes"; + +/** + * Breadcrumbs is rendered by the Navigation component, + * so it does not have access to tableScope + */ +export default function BreadcrumbsTableRoot(props: StackProps) { + const { id } = useParams(); + + const [userRoles] = useAtom(userRolesAtom, globalScope); + const [dismissed, setDismissed] = useAtom( + tableDescriptionDismissedAtom, + globalScope + ); + const [tables] = useAtom(tablesAtom, globalScope); + + const tableSettings = find(tables, ["id", id]); + if (!tableSettings) return null; + + return ( + + theme.typography.h6.fontSize, + color: "text.disabled", + + "& .MuiBreadcrumbs-ol": { + userSelect: "none", + flexWrap: "nowrap", + whiteSpace: "nowrap", + }, + "& .MuiBreadcrumbs-li": { display: "flex" }, + "& .MuiTypography-inherit": { typography: "h6" }, + }} + > + + {tableSettings.section} + + + + {tableSettings.name} + + + + {tableSettings.readOnly && ( + + + + )} + + {tableSettings.description && ( + + + + } + buttonLabel="Table info" + tooltipProps={{ + componentsProps: { + popper: { sx: { zIndex: "appBar" } }, + tooltip: { sx: { maxWidth: "75vw" } }, + } as any, + }} + defaultOpen={!dismissed.includes(tableSettings.id)} + onClose={() => setDismissed((d) => uniq([...d, tableSettings.id]))} + /> + )} + + ); +} diff --git a/src/components/fields/SubTable/utils.ts b/src/components/fields/SubTable/utils.ts index d07032f6..1c1dcae0 100644 --- a/src/components/fields/SubTable/utils.ts +++ b/src/components/fields/SubTable/utils.ts @@ -30,7 +30,8 @@ export const useSubTableData = ( ].join("/"); // if (parentLabels) subTablePath += `${parentLabels ?? ""},${label ?? ""}`; - // else subTablePath += encodeURIComponent(label ?? ""); + // else + subTablePath += "?parentLabel=" + encodeURIComponent(label ?? ""); return { documentCount, label, subTablePath }; }; diff --git a/src/constants/routes.tsx b/src/constants/routes.tsx index 2ead84e2..841ce0e0 100644 --- a/src/constants/routes.tsx +++ b/src/constants/routes.tsx @@ -1,5 +1,5 @@ import Logo from "@src/assets/Logo"; -import Breadcrumbs from "@src/components/Table/Breadcrumbs"; +import BreadcrumbsTableRoot from "@src/components/Table/BreadcrumbsTableRoot"; import { GrowProps } from "@mui/material"; export enum ROUTES { @@ -59,7 +59,7 @@ export const ROUTE_TITLES = { [ROUTES.table]: { title: "Table", titleComponent: (open, pinned) => ( - + ), titleTransitionProps: { style: { transformOrigin: "0 50%" } }, }, diff --git a/src/pages/Table/ProvidedSubTablePage.tsx b/src/pages/Table/ProvidedSubTablePage.tsx index a0eb3163..c0b37398 100644 --- a/src/pages/Table/ProvidedSubTablePage.tsx +++ b/src/pages/Table/ProvidedSubTablePage.tsx @@ -7,7 +7,7 @@ import { useLocation, useNavigate, useParams } from "react-router-dom"; import { find, isEqual } from "lodash-es"; import Modal from "@src/components/Modal"; -import Breadcrumbs from "@src/components/Table/Breadcrumbs"; +import BreadcrumbsSubTable from "@src/components/Table/BreadcrumbsSubTable"; import ErrorFallback from "@src/components/ErrorFallback"; import TableSourceFirestore from "@src/sources/TableSourceFirestore"; import TablePage from "./TablePage"; @@ -36,7 +36,7 @@ export default function ProvidedSubTablePage() { const [currentUser] = useAtom(currentUserAtom, globalScope); // Get table settings and the source column from root table - const [tableSettings] = useAtom(tableSettingsAtom, tableScope); + const [rootTableSettings] = useAtom(tableSettingsAtom, tableScope); const [sourceColumn] = useAtom( useMemo( () => @@ -57,25 +57,31 @@ export default function ProvidedSubTablePage() { // Must be compatible with `getTableSchemaPath`: tableId/rowId/subTableKey // This is why we can’t have a sub-table column fieldName !== key const subTableId = - docPath?.replace(tableSettings.collection, tableSettings.id) + + docPath?.replace(rootTableSettings.collection, rootTableSettings.id) + "/" + subTableKey; // Write fake tableSettings const subTableSettings = { - ...tableSettings, + ...rootTableSettings, collection: subTableCollection, id: subTableId, tableType: "primaryCollection" as "primaryCollection", - name: sourceColumn?.name || subTableKey, + name: sourceColumn?.name || subTableKey || "", }; + const rootTableLink = location.pathname.split("/" + ROUTES.subTable)[0]; + return ( Sub-table: {subTableCollection}} - onClose={() => - navigate(location.pathname.split("/" + ROUTES.subTable)[0]) + title={ + } + onClose={() => navigate(rootTableLink)} disableBackdropClick disableEscapeKeyDown fullScreen