diff --git a/src/assets/icons/index.ts b/src/assets/icons/index.ts index 3503e99b..4e60591f 100644 --- a/src/assets/icons/index.ts +++ b/src/assets/icons/index.ts @@ -103,6 +103,9 @@ export { FileTableBoxOutline as Project }; import { TableColumn } from "mdi-material-ui"; export { TableColumn }; +import { InformationOutline } from "mdi-material-ui"; +export { InformationOutline as TableInformation }; + export * from "./AddRow"; export * from "./AddRowTop"; export * from "./ChevronDown"; diff --git a/src/atoms/tableScope/ui.ts b/src/atoms/tableScope/ui.ts index fa987118..b5aaf882 100644 --- a/src/atoms/tableScope/ui.ts +++ b/src/atoms/tableScope/ui.ts @@ -121,6 +121,12 @@ export const importAirtableAtom = atom<{ tableId: string; }>({ airtableData: null, apiKey: "", baseId: "", tableId: "" }); +/** Store side drawer open state */ +export const sideDrawerAtom = atomWithHash<"table-information" | null>( + "sideDrawer", + null, + { replaceState: true } +); /** Store side drawer open state */ export const sideDrawerOpenAtom = atom(false); diff --git a/src/components/TableInformationDrawer/Details.tsx b/src/components/TableInformationDrawer/Details.tsx new file mode 100644 index 00000000..a3d4640d --- /dev/null +++ b/src/components/TableInformationDrawer/Details.tsx @@ -0,0 +1,178 @@ +import { useMemo } from "react"; +import { format } from "date-fns"; +import MDEditor from "@uiw/react-md-editor"; + +import { + Box, + Grid, + IconButton, + Stack, + Typography, + useTheme, +} from "@mui/material"; + +import EditIcon from "@mui/icons-material/EditOutlined"; + +import { tableScope, tableSettingsAtom } from "@src/atoms/tableScope"; +import { useAtom, useSetAtom } from "jotai"; +import { + projectScope, + tablesAtom, + tableSettingsDialogAtom, + userRolesAtom, +} from "@src/atoms/projectScope"; +import { find } from "lodash-es"; + +export interface IDetailsProps { + handleOpenTemplate?: any; +} + +export default function Details({ handleOpenTemplate }: IDetailsProps) { + const [userRoles] = useAtom(userRolesAtom, projectScope); + const [tableSettings] = useAtom(tableSettingsAtom, tableScope); + const [tables] = useAtom(tablesAtom, projectScope); + const openTableSettingsDialog = useSetAtom( + tableSettingsDialogAtom, + projectScope + ); + + const settings = useMemo( + () => find(tables, ["id", tableSettings.id]), + [tables, tableSettings.id] + ); + + const theme = useTheme(); + + if (!settings) { + return null; + } + + const editButton = userRoles.includes("ADMIN") && ( + + openTableSettingsDialog({ + mode: "update", + data: settings, + }) + } + disabled={!openTableSettingsDialog || settings.id.includes("/")} + > + + + ); + + const { description, details, _createdBy } = settings; + + return ( + .MuiGrid-root": { + position: "relative", + }, + }} + > + {/* Description */} + + + + Description + + {editButton} + + + {description ? description : "No description"} + + + + {/* Details */} + + + + Details + + {editButton} + + {!details ? ( + + No details + + ) : ( + + + + )} + + + {/* Table Audits */} + {_createdBy && ( + + + Created by{" "} + + {_createdBy.displayName} + {" "} + at{" "} + + {format(_createdBy.timestamp.toDate(), "LLL d, yyyy · p")} + + + + )} + + {/* Template Settings */} + {/* {handleOpenTemplate && ( + + + + + + ); +} diff --git a/src/components/TableSettingsDialog/form.tsx b/src/components/TableSettingsDialog/form.tsx index 7b62176b..190b02bf 100644 --- a/src/components/TableSettingsDialog/form.tsx +++ b/src/components/TableSettingsDialog/form.tsx @@ -244,7 +244,23 @@ export const tableSettings = ( type: FieldType.paragraph, name: "description", label: "Description (optional)", - minRows: 2, + }, + { + step: "display", + type: "tableDetails", + name: "details", + label: "Details (optional)", + }, + { + step: "display", + type: "tableThumbnail", + name: "thumbnailFile", + label: "Thumbnail Image (optional)", + }, + { + step: "display", + type: FieldType.hidden, + name: "thumbnailURL", }, // Step 3: Access controls diff --git a/src/components/TableSettingsDialog/utils.ts b/src/components/TableSettingsDialog/utils.ts new file mode 100644 index 00000000..b70cfe07 --- /dev/null +++ b/src/components/TableSettingsDialog/utils.ts @@ -0,0 +1,14 @@ +import { + FirebaseStorage, + getDownloadURL, + ref, + uploadBytes, +} from "firebase/storage"; + +export const uploadTableThumbnail = + (storage: FirebaseStorage) => (tableId: string, imageFile: File) => { + const storageRef = ref(storage, `__thumbnails__/${tableId}`); + return uploadBytes(storageRef, imageFile).then(({ ref }) => + getDownloadURL(ref) + ); + }; diff --git a/src/components/TableToolbar/TableInformation.tsx b/src/components/TableToolbar/TableInformation.tsx new file mode 100644 index 00000000..f53688f1 --- /dev/null +++ b/src/components/TableToolbar/TableInformation.tsx @@ -0,0 +1,25 @@ +import { useAtom } from "jotai"; +import { RESET } from "jotai/utils"; + +import { + sideDrawerAtom, + tableScope, + tableSettingsAtom, +} from "@src/atoms/tableScope"; + +import TableToolbarButton from "@src/components/TableToolbar/TableToolbarButton"; +import { TableInformation as TableInformationIcon } from "@src/assets/icons"; + +export default function TableInformation() { + const [tableSettings] = useAtom(tableSettingsAtom, tableScope); + const [sideDrawer, setSideDrawer] = useAtom(sideDrawerAtom, tableScope); + + return ( + } + onClick={() => setSideDrawer(sideDrawer ? RESET : "table-information")} + disabled={!setSideDrawer || tableSettings.id.includes("/")} + /> + ); +} diff --git a/src/components/TableToolbar/TableToolbar.tsx b/src/components/TableToolbar/TableToolbar.tsx index e940e92b..3e288a88 100644 --- a/src/components/TableToolbar/TableToolbar.tsx +++ b/src/components/TableToolbar/TableToolbar.tsx @@ -36,6 +36,9 @@ import { FieldType } from "@src/constants/fields"; const Filters = lazy(() => import("./Filters" /* webpackChunkName: "Filters" */)); // prettier-ignore const ImportData = lazy(() => import("./ImportData/ImportData" /* webpackChunkName: "ImportData" */)); +// prettier-ignore +const TableInformation = lazy(() => import("./TableInformation" /* webpackChunkName: "TableInformation" */)); + // prettier-ignore const ReExecute = lazy(() => import("./ReExecute" /* webpackChunkName: "ReExecute" */)); @@ -147,6 +150,9 @@ export default function TableToolbar() { )} + }> + +
); diff --git a/src/components/Tables/TableGrid/TableCard.tsx b/src/components/Tables/TableGrid/TableCard.tsx index 21be4ece..b601b90a 100644 --- a/src/components/Tables/TableGrid/TableCard.tsx +++ b/src/components/Tables/TableGrid/TableCard.tsx @@ -7,10 +7,9 @@ import { Typography, CardActions, Button, + Box, } from "@mui/material"; import { Go as GoIcon } from "@src/assets/icons"; - -import RenderedMarkdown from "@src/components/RenderedMarkdown"; import { TableSettings } from "@src/types/table"; export interface ITableCardProps extends TableSettings { @@ -19,6 +18,7 @@ export interface ITableCardProps extends TableSettings { } export default function TableCard({ + thumbnailURL, section, name, description, @@ -37,27 +37,46 @@ export default function TableCard({ - - - - (theme.typography.body2.lineHeight as number) * 2 + "em", - display: "flex", - flexDirection: "column", - gap: 1, - }} - component="div" - > - {description && ( - + + - )} - - + + + )} + {description && ( + + + {description} + + + )}