diff --git a/.env.example b/.env.example index 275ef85a..0e8f2bff 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,2 @@ -REACT_APP_FIREBASE_PROJECT_ID= -REACT_APP_FIREBASE_PROJECT_WEB_API_KEY= \ No newline at end of file +VITE_APP_FIREBASE_PROJECT_ID= +VITE_APP_FIREBASE_PROJECT_WEB_API_KEY= \ No newline at end of file diff --git a/package.json b/package.json index d6a677d5..9b6a09e5 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "startWithEmulators": "VITE_APP_FIREBASE_EMULATORS=true vite --port 7699", "emulators": "firebase emulators:start --only firestore,auth --import ./emulators/ --export-on-exit", "test": "vitest", - "build": "tsc && vite build", + "build": "tsc && cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build", "preview": "vite preview --port 7699", "analyze": "source-map-explorer ./build/static/js/*.js", "prepare": "husky install", diff --git a/src/App.tsx b/src/App.tsx index d169bb1e..653b6a88 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -51,8 +51,6 @@ const ProvidedSubTablePage = lazy(() => import("@src/pages/Table/ProvidedSubTabl // prettier-ignore const TableTutorialPage = lazy(() => import("@src/pages/Table/TableTutorialPage" /* webpackChunkName: "TableTutorialPage" */)); -// prettier-ignore -const FunctionPage = lazy(() => import("@src/pages/FunctionPage" /* webpackChunkName: "FunctionPage" */)); // prettier-ignore const UserSettingsPage = lazy(() => import("@src/pages/Settings/UserSettingsPage" /* webpackChunkName: "UserSettingsPage" */)); // prettier-ignore @@ -169,13 +167,6 @@ export default function App() { element={} /> - - } - /> - } /> - } diff --git a/src/components/Function/Function.tsx b/src/components/Function/Function.tsx deleted file mode 100644 index 74503e55..00000000 --- a/src/components/Function/Function.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { AutoTypings, LocalStorageCache } from "monaco-editor-auto-typings"; -import Editor, { OnMount } from "@monaco-editor/react"; - -const defaultCode = `import React from "react"; -function App() { - return ( -
-

Hello World!

-
- ); -} -`; -const handleEditorMount: OnMount = (monacoEditor, monaco) => { - console.log("handleEditorMount"); - monaco.languages.typescript.typescriptDefaults.setCompilerOptions({ - target: monaco.languages.typescript.ScriptTarget.ES2016, - allowNonTsExtensions: true, - moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs, - module: monaco.languages.typescript.ModuleKind.CommonJS, - noEmit: true, - typeRoots: ["node_modules/@types"], - }); - - monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({ - noSemanticValidation: false, - noSyntaxValidation: false, - }); - - const autoTypings = AutoTypings.create(monacoEditor, { - sourceCache: new LocalStorageCache(), // Cache loaded sources in localStorage. May be omitted - monaco: monaco, - onError: (error) => { - console.log(error); - }, - onUpdate: (update, textual) => { - console.log(textual); - }, - }); -}; -export default function Function() { - const onChange = (value: string | undefined, ev: any) => { - //console.log(value) - }; - return ( - - ); -} diff --git a/src/components/Function/index.ts b/src/components/Function/index.ts deleted file mode 100644 index b6263050..00000000 --- a/src/components/Function/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -//export * from "./Function"; -export { default } from "./Function"; diff --git a/src/components/Functions/FunctionGrid/TableCard.tsx b/src/components/Functions/FunctionGrid/TableCard.tsx deleted file mode 100644 index 21be4ece..00000000 --- a/src/components/Functions/FunctionGrid/TableCard.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { Link } from "react-router-dom"; - -import { - Card, - CardActionArea, - CardContent, - Typography, - CardActions, - Button, -} 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 { - link: string; - actions?: React.ReactNode; -} - -export default function TableCard({ - section, - name, - description, - link, - actions, -}: ITableCardProps) { - return ( - - - - - {section} - - - {name} - - - - - - - (theme.typography.body2.lineHeight as number) * 2 + "em", - display: "flex", - flexDirection: "column", - gap: 1, - }} - component="div" - > - {description && ( - - )} - - - - - - -
- - {actions} - - - ); -} diff --git a/src/components/Functions/FunctionGrid/TableCardSkeleton.tsx b/src/components/Functions/FunctionGrid/TableCardSkeleton.tsx deleted file mode 100644 index c6611bde..00000000 --- a/src/components/Functions/FunctionGrid/TableCardSkeleton.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { - Card, - CardContent, - Typography, - CardActions, - Skeleton, -} from "@mui/material"; - -export default function TableCardSkeleton() { - return ( - - - - - - - - - - (theme.typography.body2.lineHeight as number) * 2 + "em", - }} - > - - - - - - - - - - - ); -} diff --git a/src/components/Functions/FunctionGrid/TableGrid.tsx b/src/components/Functions/FunctionGrid/TableGrid.tsx deleted file mode 100644 index 20d3b774..00000000 --- a/src/components/Functions/FunctionGrid/TableGrid.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { TransitionGroup } from "react-transition-group"; - -import { Box, Grid, Collapse } from "@mui/material"; - -import SectionHeading from "@src/components/SectionHeading"; -import TableCard from "./TableCard"; -import SlideTransition from "@src/components/Modal/SlideTransition"; - -import { TableSettings } from "@src/types/table"; - -export interface ITableGridProps { - sections: Record; - getLink: (table: TableSettings) => string; - getActions?: (table: TableSettings) => React.ReactNode; -} - -export default function TableGrid({ - sections, - getLink, - getActions, -}: ITableGridProps) { - return ( - - {Object.entries(sections).map( - ([sectionName, sectionTables], sectionIndex) => { - const tableItems = sectionTables - .map((table, tableIndex) => { - if (!table) return null; - - return ( - - - - - - ); - }) - .filter((item) => item !== null); - - if (tableItems.length === 0) return null; - - return ( - - - - - {sectionName} - - - - - {tableItems} - - - - ); - } - )} - - ); -} diff --git a/src/components/Functions/FunctionGrid/index.ts b/src/components/Functions/FunctionGrid/index.ts deleted file mode 100644 index 0397ca90..00000000 --- a/src/components/Functions/FunctionGrid/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./TableGrid"; -export { default } from "./TableGrid"; diff --git a/src/components/Functions/FunctionList/TableList.tsx b/src/components/Functions/FunctionList/TableList.tsx deleted file mode 100644 index 62911137..00000000 --- a/src/components/Functions/FunctionList/TableList.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { TransitionGroup } from "react-transition-group"; - -import { Box, Paper, Collapse, List } from "@mui/material"; - -import SectionHeading from "@src/components/SectionHeading"; -import TableListItem from "./TableListItem"; -import SlideTransition from "@src/components/Modal/SlideTransition"; - -import { TableSettings } from "@src/types/table"; - -export interface ITableListProps { - sections: Record; - getLink: (table: TableSettings) => string; - getActions?: (table: TableSettings) => React.ReactNode; -} - -export default function TableList({ - sections, - getLink, - getActions, -}: ITableListProps) { - return ( - - {Object.entries(sections).map( - ([sectionName, sectionTables], sectionIndex) => { - const tableItems = sectionTables - .map((table) => { - if (!table) return null; - - return ( - - - - ); - }) - .filter((item) => item !== null); - - if (tableItems.length === 0) return null; - - return ( - - - - - {sectionName} - - - - - - - {tableItems} - - - - - - ); - } - )} - - ); -} diff --git a/src/components/Functions/FunctionList/TableListItem.tsx b/src/components/Functions/FunctionList/TableListItem.tsx deleted file mode 100644 index e54c56a5..00000000 --- a/src/components/Functions/FunctionList/TableListItem.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { Link } from "react-router-dom"; - -import { - ListItem, - ListItemButton, - Typography, - IconButton, -} from "@mui/material"; -import GoIcon from "@mui/icons-material/ArrowForward"; - -import RenderedMarkdown from "@src/components/RenderedMarkdown"; -import { TableSettings } from "@src/types/table"; - -export interface ITableListItemProps extends TableSettings { - link: string; - actions?: React.ReactNode; -} - -export default function TableListItem({ - // section, - name, - description, - link, - actions, -}: ITableListItemProps) { - return ( - - *": { lineHeight: "48px !important" }, - flexWrap: "nowrap", - overflow: "hidden", - - flexBasis: 160 + 16, - flexGrow: 0, - flexShrink: 0, - mr: 2, - }} - > - - {name} - - - - - {description && ( - - )} - - -
- {actions} - - - - -
-
- ); -} diff --git a/src/components/Functions/FunctionList/TableListItemSkeleton.tsx b/src/components/Functions/FunctionList/TableListItemSkeleton.tsx deleted file mode 100644 index a2854ee8..00000000 --- a/src/components/Functions/FunctionList/TableListItemSkeleton.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { ListItem, Skeleton } from "@mui/material"; - -export default function TableListItemSkeleton() { - return ( - - - - - - - - ); -} diff --git a/src/components/Functions/FunctionList/index.ts b/src/components/Functions/FunctionList/index.ts deleted file mode 100644 index 20165432..00000000 --- a/src/components/Functions/FunctionList/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./TableList"; -export { default } from "./TableList"; diff --git a/src/components/Functions/HomeWelcomePrompt.tsx b/src/components/Functions/HomeWelcomePrompt.tsx deleted file mode 100644 index 1f2e10af..00000000 --- a/src/components/Functions/HomeWelcomePrompt.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { Zoom, Stack, Typography } from "@mui/material"; - -export default function HomeWelcomePrompt() { - return ( - - - - Get started - - - - Create a function - - - - ); -} diff --git a/src/components/Table/EmptyTable.tsx b/src/components/Table/EmptyTable.tsx index 2fb06875..5b96220d 100644 --- a/src/components/Table/EmptyTable.tsx +++ b/src/components/Table/EmptyTable.tsx @@ -33,7 +33,7 @@ export default function EmptyTable() { : false; let contents = <>; - if (tableSettings.isCollection !== false && hasData) { + if (hasData) { contents = ( <>
@@ -41,9 +41,15 @@ export default function EmptyTable() { Get started - There is existing data in the Firestore collection: + {tableSettings.isCollection === false + ? "There is existing data in the Array Sub Table:" + : "There is existing data in the Firestore collection:"}
- {tableSettings.collection} + + {tableSettings.collection} + {tableSettings.subTableKey?.length && + `.${tableSettings.subTableKey}`} +
diff --git a/src/components/Table/useMenuAction.tsx b/src/components/Table/useMenuAction.tsx index 2ef19c1b..7fd2276e 100644 --- a/src/components/Table/useMenuAction.tsx +++ b/src/components/Table/useMenuAction.tsx @@ -19,6 +19,9 @@ import { format } from "date-fns"; import { DATE_FORMAT, DATE_TIME_FORMAT } from "@src/constants/dates"; import { isDate, isFunction } from "lodash-es"; import { getDurationString } from "@src/components/fields/Duration/utils"; +import { doc } from "firebase/firestore"; +import { firebaseDbAtom } from "@src/sources/ProjectSourceFirebase"; +import { projectScope } from "@src/atoms/projectScope"; export const SUPPORTED_TYPES_COPY = new Set([ // TEXT @@ -56,6 +59,8 @@ export const SUPPORTED_TYPES_COPY = new Set([ FieldType.updatedBy, FieldType.createdAt, FieldType.updatedAt, + // CONNECTION + FieldType.reference, ]); export const SUPPORTED_TYPES_PASTE = new Set([ @@ -75,6 +80,8 @@ export const SUPPORTED_TYPES_PASTE = new Set([ FieldType.json, FieldType.code, FieldType.markdown, + // CONNECTION + FieldType.reference, ]); export function useMenuAction( @@ -87,6 +94,7 @@ export function useMenuAction( const updateField = useSetAtom(updateFieldAtom, tableScope); const [cellValue, setCellValue] = useState(); const [selectedCol, setSelectedCol] = useState(); + const [firebaseDb] = useAtom(firebaseDbAtom, projectScope); const handleCopy = useCallback(async () => { try { @@ -165,6 +173,13 @@ export function useMenuAction( case "string": parsed = text; break; + case "reference": + try { + parsed = doc(firebaseDb, text); + } catch (e: any) { + enqueueSnackbar(`Invalid reference.`, { variant: "error" }); + } + break; default: parsed = JSON.parse(text); break; @@ -319,6 +334,8 @@ export function useMenuAction( case FieldType.createdBy: case FieldType.updatedBy: return cellValue.displayName; + case FieldType.reference: + return cellValue.path; default: return cellValue; } diff --git a/src/components/TableModals/ImportAirtableWizard/Step1Columns.tsx b/src/components/TableModals/ImportAirtableWizard/Step1Columns.tsx index 42f73226..bfb521dc 100644 --- a/src/components/TableModals/ImportAirtableWizard/Step1Columns.tsx +++ b/src/components/TableModals/ImportAirtableWizard/Step1Columns.tsx @@ -36,6 +36,17 @@ import { FieldType } from "@src/constants/fields"; import { getFieldProp } from "@src/components/fields"; import { suggestType } from "@src/components/TableModals/ImportAirtableWizard/utils"; +function getFieldKeys(records: any[]) { + let fieldKeys = new Set(); + for (let i = 0; i < records.length; i++) { + const keys = Object.keys(records[i].fields); + for (let j = 0; j < keys.length; j++) { + fieldKeys.add(keys[j]); + } + } + return [...fieldKeys]; +} + export default function Step1Columns({ airtableData, config, @@ -57,8 +68,7 @@ export default function Step1Columns({ config.pairs.map((pair) => pair.fieldKey) ); - const fieldKeys = Object.keys(airtableData.records[0].fields); - + const fieldKeys = getFieldKeys(airtableData.records); // When a field is selected to be imported const handleSelect = (field: string) => (e: React.ChangeEvent) => { diff --git a/src/components/TableSettingsDialog/TableName.tsx b/src/components/TableSettingsDialog/TableName.tsx index 66a5c9d5..1eebd2d9 100644 --- a/src/components/TableSettingsDialog/TableName.tsx +++ b/src/components/TableSettingsDialog/TableName.tsx @@ -20,10 +20,14 @@ export default function TableName({ watchedField, ...props }: ITableNameProps) { const watchedValue = useWatch({ control, name: watchedField } as any); useEffect(() => { if (!disabled) { - if (typeof value === "string" && value.trim() !== "") { - onChange(value); - } else if (typeof watchedValue === "string" && !!watchedValue) { + const touched = control.getFieldState(props.name).isTouched; + + if (!touched && typeof watchedValue === "string" && !!watchedValue) { + // if table name field is not touched, and watched value is valid, set table name to watched value onChange(startCase(watchedValue)); + } else if (typeof value === "string") { + // otherwise if table name is valid, set watched value to table name + onChange(value.trim()); } } }, [watchedValue, disabled, onChange, value]); diff --git a/src/components/TableSettingsDialog/form.tsx b/src/components/TableSettingsDialog/form.tsx index 8cfcaa14..9d611e06 100644 --- a/src/components/TableSettingsDialog/form.tsx +++ b/src/components/TableSettingsDialog/form.tsx @@ -149,6 +149,7 @@ export const tableSettings = ( // https://firebase.google.com/docs/firestore/quotas#collections_documents_and_fields validation: [ ["matches", /^[^\s]+$/, "Collection name cannot have spaces"], + ["matches", /^[^.]+$/, "Collection name cannot have dots"], ["notOneOf", [".", ".."], "Collection name cannot be . or .."], [ "test", @@ -194,6 +195,7 @@ export const tableSettings = ( // https://firebase.google.com/docs/firestore/quotas#collections_documents_and_fields validation: [ ["matches", /^[^\s]+$/, "Collection name cannot have spaces"], + ["matches", /^[^.]+$/, "Collection name cannot have dots"], ["notOneOf", [".", ".."], "Collection name cannot be . or .."], [ "test", diff --git a/src/components/TableToolbar/Filters/Filters.tsx b/src/components/TableToolbar/Filters/Filters.tsx index 8ebc32ff..868d6f86 100644 --- a/src/components/TableToolbar/Filters/Filters.tsx +++ b/src/components/TableToolbar/Filters/Filters.tsx @@ -1,7 +1,10 @@ +/* eslint-disable react-hooks/exhaustive-deps */ import { useState, useEffect } from "react"; import { useAtom } from "jotai"; import useMemoValue from "use-memo-value"; import { isEmpty, isDate } from "lodash-es"; +import { useSearchParams } from "react-router-dom"; +import { useSnackbar } from "notistack"; import { Tab, @@ -19,6 +22,7 @@ import TabPanel from "@mui/lab/TabPanel"; import FiltersPopover from "./FiltersPopover"; import FilterInputs from "./FilterInputs"; +import { changePageUrl, separateOperands } from "./utils"; import { projectScope, @@ -62,12 +66,17 @@ export default function Filters() { const [, setTableSorts] = useAtom(tableSortsAtom, tableScope); const [updateTableSchema] = useAtom(updateTableSchemaAtom, tableScope); const [{ defaultQuery }] = useAtom(tableFiltersPopoverAtom, tableScope); - const tableFilterInputs = useFilterInputs(tableColumnsOrdered); const setTableQuery = tableFilterInputs.setQuery; const userFilterInputs = useFilterInputs(tableColumnsOrdered, defaultQuery); const setUserQuery = userFilterInputs.setQuery; - const { availableFilters } = userFilterInputs; + const { availableFilters, filterColumns } = userFilterInputs; + const [searchParams] = useSearchParams(); + const { enqueueSnackbar } = useSnackbar(); + useEffect(() => { + let isFiltered = searchParams.get("filter"); + if (isFiltered) updateUserFilter(isFiltered); + }, [searchParams]); // Get table filters & user filters from config documents const tableFilters = useMemoValue( @@ -82,6 +91,44 @@ export default function Filters() { const hasTableFilters = Array.isArray(tableFilters) && tableFilters.length > 0; const hasUserFilters = Array.isArray(userFilters) && userFilters.length > 0; + function updateUserFilter(str: string) { + let { operators, operands = [] } = separateOperands(str); + if (!operators.length) return; + if (operators.length) { + let appliedFilter: TableFilter[] = []; + appliedFilter = [ + { + key: operands[0], + operator: operators[0], + value: Number(operands[1]), + }, + ]; + let isValidFilter = checkFilterValidation(appliedFilter[0]); + if (isValidFilter) { + setOverrideTableFilters(true); + setUserFilters(appliedFilter); + } else { + enqueueSnackbar("Oops, Invalid filter!!!", { variant: "error" }); + setUserFilters([]); + setOverrideTableFilters(false); + userFilterInputs.resetQuery(); + } + } + } + function checkFilterValidation(filter: TableFilter): boolean { + let isFilterableColumn = filterColumns?.filter( + (item) => + item.key === filter.key || + item.label === filter.key || + item.type === filter.key + ); + if (!isFilterableColumn?.length) return false; + filter.key = isFilterableColumn?.[0]?.value; + filter.operator = filter.operator === "-is-" ? "id-equal" : filter.operator; + filter.value = + filter.operator === "id-equal" ? filter.value.toString() : filter.value; + return true; + } // Set the local table filter useEffect(() => { @@ -109,7 +156,7 @@ export default function Filters() { } else if (hasUserFilters) { filtersToApply = userFilters; } - + updatePageURL(filtersToApply); setLocalFilters(filtersToApply); // Reset order so we don’t have to make a new index if (filtersToApply.length) { @@ -120,7 +167,6 @@ export default function Filters() { hasUserFilters, setLocalFilters, setTableSorts, - setTableQuery, tableFilters, tableFiltersOverridable, setUserQuery, @@ -173,7 +219,21 @@ export default function Filters() { if (updateUserSettings && filters) updateUserSettings({ tables: { [`${tableId}`]: { filters } } }); }; - + function updatePageURL(filters: TableFilter[]) { + if (!filters.length) { + changePageUrl(); + } else { + const [filter] = filters; + const fieldName = filter.key === "_rowy_ref.id" ? "ID" : filter.key; + const operator = + filter.operator === "id-equal" ? "-is-" : filter.operator; + const formattedValue = availableFilters?.valueFormatter + ? availableFilters.valueFormatter(filter.value, filter.operator) + : filter.value.toString(); + const queryParams = `?filter=${fieldName}${operator}${formattedValue}`; + changePageUrl(queryParams); + } + } return ( `\\${op}`).join("|"), "g") + ); + return { operators, operands }; +} +export function changePageUrl(newURL: string | undefined = URL) { + if (newURL !== URL) { + newURL = URL + newURL; + } + window.history.pushState({ path: newURL }, "", newURL); +} + +function findOperators(str: string) { + const operators = [">=", "<=", ">", "<", "==", "!=", "=", "-is-"]; + const regex = new RegExp(operators.map((op) => `\\${op}`).join("|"), "g"); + return str.match(regex) || []; +} diff --git a/src/components/fields/Reference/index.tsx b/src/components/fields/Reference/index.tsx index 5e95f2a6..7a7ee267 100644 --- a/src/components/fields/Reference/index.tsx +++ b/src/components/fields/Reference/index.tsx @@ -7,6 +7,7 @@ import DisplayCell from "./DisplayCell"; import EditorCell from "./EditorCell"; import { filterOperators } from "@src/components/fields/ShortText/Filter"; import { valueFormatter } from "./filters"; +import BasicContextMenuActions from "@src/components/Table/ContextMenu/BasicCellContextMenuActions"; const SideDrawerField = lazy( () => @@ -24,6 +25,7 @@ export const config: IFieldConfig = { initializable: true, icon: , description: "Firestore document reference", + contextMenuActions: BasicContextMenuActions, TableCell: withRenderTableCell(DisplayCell, EditorCell, "focus", { disablePadding: true, }), diff --git a/src/components/fields/SingleSelect/Settings.tsx b/src/components/fields/SingleSelect/Settings.tsx index 46e44604..9d8123fc 100644 --- a/src/components/fields/SingleSelect/Settings.tsx +++ b/src/components/fields/SingleSelect/Settings.tsx @@ -47,7 +47,7 @@ export const getColors = ( option: string ): SelectColorThemeOptions => { const defaultColor = paletteToMui(palette.aGray); - const key = option.toLocaleLowerCase().replace(" ", "_").trim(); + const key = option.toLocaleLowerCase?.().replace(" ", "_").trim(); const color = list.find((opt: IColors) => opt.name === key); // Null check in return return color || defaultColor; @@ -113,7 +113,7 @@ export default function Settings({ onChange, config }: ISettingsProps) { color?: SelectColorThemeOptions, newKey?: string ) => { - const _key = key.toLocaleLowerCase().replace(" ", "_").trim(); + const _key = key.toLocaleLowerCase?.().replace(" ", "_").trim(); const exists = colors.findIndex((option: IColors) => option.name === _key); // If saving Check if object with the `color.name` is equal to `_key` and replace value at the index of `exists` @@ -135,7 +135,7 @@ export default function Settings({ onChange, config }: ISettingsProps) { } if (type === "update" && newKey) { - const _newKey = newKey.toLocaleLowerCase().replace(" ", "_").trim(); + const _newKey = newKey.toLocaleLowerCase?.().replace(" ", "_").trim(); const updatedColors = colors.map((option: IColors) => option.name === _key ? { ...option, name: _newKey } : option ); diff --git a/src/constants/routes.tsx b/src/constants/routes.tsx index 70e8d9eb..ec1f496f 100644 --- a/src/constants/routes.tsx +++ b/src/constants/routes.tsx @@ -16,10 +16,6 @@ export enum ROUTES { pageNotFound = "/404", tables = "/tables", - automations = "/automations", - functions = "/functions", - function = "/function", - functionWithId = "/function/:id", table = "/table", tableWithId = "/table/:id", diff --git a/src/pages/FunctionPage.tsx b/src/pages/FunctionPage.tsx deleted file mode 100644 index 721eefb1..00000000 --- a/src/pages/FunctionPage.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { Suspense } from "react"; -import { useAtom, Provider } from "jotai"; -import { useParams } from "react-router-dom"; -import { isEmpty } from "lodash-es"; - -import { Fade } from "@mui/material"; - -//import TableHeaderSkeleton from "@src/components/Table/Skeleton/TableHeaderSkeleton"; -//import TableSkeleton from "@src/components/Table/Skeleton/TableSkeleton"; -//import EmptyTable from "@src/components/Table/EmptyTable"; -import Function from "@src/components/Function"; - -import { currentUserAtom, projectScope } from "@src/atoms/projectScope"; -// import TableSourceFirestore from "@src/sources/TableSourceFirestore"; -// import { -// tableScope, -// tableIdAtom, -// tableSettingsAtom, -// tableSchemaAtom, -// } from "@src/atoms/tableScope"; - -export default function FunctionPage() { - // const [tableId] = useAtom(tableIdAtom, tableScope); - // const [tableSettings] = useAtom(tableSettingsAtom, tableScope); - // const [tableSchema] = useAtom(tableSchemaAtom, tableScope); - - // console.log(tableSchema); - - // if (isEmpty(tableSchema.columns)) - // return ( - // - //
- // - //
- //
- // ); - - return ; -} - -function ProvidedFunctionPage() { - const { id } = useParams(); - const [currentUser] = useAtom(currentUserAtom, projectScope); - - return ( - - {/* - */} - - } - > - {/* */} - {/* */} - - {/* */} - - ); -} diff --git a/src/pages/FunctionsPage.tsx b/src/pages/FunctionsPage.tsx deleted file mode 100644 index dd1dcd93..00000000 --- a/src/pages/FunctionsPage.tsx +++ /dev/null @@ -1,226 +0,0 @@ -import { useAtom, useSetAtom } from "jotai"; -import { find, groupBy } from "lodash-es"; - -import { - Container, - Stack, - Typography, - ToggleButtonGroup, - ToggleButton, - Tooltip, - Fab, - Checkbox, - IconButton, - Zoom, -} from "@mui/material"; -import ViewListIcon from "@mui/icons-material/ViewListOutlined"; -import ViewGridIcon from "@mui/icons-material/ViewModuleOutlined"; -import FavoriteBorderIcon from "@mui/icons-material/FavoriteBorder"; -import FavoriteIcon from "@mui/icons-material/Favorite"; -import EditIcon from "@mui/icons-material/EditOutlined"; -import AddIcon from "@mui/icons-material/Add"; - -import FloatingSearch from "@src/components/FloatingSearch"; -import SlideTransition from "@src/components/Modal/SlideTransition"; -import FunctionGrid from "@src/components/Functions/FunctionGrid"; -import FunctionList from "@src/components/Functions/FunctionList"; -import HomeWelcomePrompt from "@src/components/Functions/HomeWelcomePrompt"; -import EmptyState from "@src/components/EmptyState"; - -import { - projectScope, - userRolesAtom, - userSettingsAtom, - updateUserSettingsAtom, - tablesAtom, - tablesViewAtom, - tableSettingsDialogAtom, -} from "@src/atoms/projectScope"; -import { TableSettings } from "@src/types/table"; -import { ROUTES } from "@src/constants/routes"; -import useBasicSearch from "@src/hooks/useBasicSearch"; -import { TOP_BAR_HEIGHT } from "@src/layouts/Navigation/TopBar"; - -const SEARCH_KEYS = ["id", "name", "section", "description"]; - -export default function HomePage() { - const [userRoles] = useAtom(userRolesAtom, projectScope); - const [userSettings] = useAtom(userSettingsAtom, projectScope); - const [updateUserSettings] = useAtom(updateUserSettingsAtom, projectScope); - const [tables] = useAtom(tablesAtom, projectScope); - const [view, setView] = useAtom(tablesViewAtom, projectScope); - const openTableSettingsDialog = useSetAtom( - tableSettingsDialogAtom, - projectScope - ); - - const [results, query, handleQuery] = useBasicSearch( - tables ?? [], - SEARCH_KEYS - ); - - const favorites = Array.isArray(userSettings.favoriteTables) - ? userSettings.favoriteTables - : []; - const sections: Record = { - Favorites: favorites.map((id) => find(results, { id })) as TableSettings[], - ...groupBy(results, "section"), - }; - - if (!Array.isArray(tables)) - throw new Error( - "Project settings are not configured correctly. `tables` is not an array." - ); - - const createFunctionFab = ( - - - openTableSettingsDialog({ mode: "create" })} - sx={{ - zIndex: "speedDial", - position: "fixed", - bottom: (theme) => ({ - xs: `max(${theme.spacing(2)}, env(safe-area-inset-bottom))`, - sm: `max(${theme.spacing(3)}, env(safe-area-inset-bottom))`, - }), - right: (theme) => ({ - xs: `max(${theme.spacing(2)}, env(safe-area-inset-right))`, - sm: `max(${theme.spacing(3)}, env(safe-area-inset-right))`, - }), - }} - > - - - - - ); - - if (tables.length === 0) { - if (userRoles.includes("ADMIN")) - return ( - <> - - {createFunctionFab} - - ); - - return ( - - ); - } - - const getLink = (table: TableSettings) => - `${ROUTES.table}/${table.id.replace(/\//g, "~2F")}`; - - const handleFavorite = - (id: string) => (e: React.ChangeEvent) => { - const favoriteTables = e.target.checked - ? [...favorites, id] - : favorites.filter((f) => f !== id); - - if (updateUserSettings) updateUserSettings({ favoriteTables }); - }; - - const getActions = (table: TableSettings) => ( - <> - {userRoles.includes("ADMIN") && ( - - openTableSettingsDialog({ mode: "update", data: table }) - } - size={view === "list" ? "large" : undefined} - > - - - )} - } - checkedIcon={ - - - - } - name={`favorite-${table.id}`} - inputProps={{ "aria-label": "Favorite" }} - sx={view === "list" ? { p: 1.5 } : undefined} - color="secondary" - /> - - ); - - return ( - - handleQuery(e.target.value)} - paperSx={{ - maxWidth: (theme) => ({ md: theme.breakpoints.values.sm - 48 }), - mb: { xs: 2, md: -6 }, - }} - /> - - - - - {query ? `${results.length} of ${tables.length}` : tables.length}{" "} - tables - - - { - if (v !== null) setView(v); - }} - aria-label="Table view" - sx={{ "& .MuiToggleButton-root": { borderRadius: 2 } }} - > - - - - - - - - - - - {view === "list" ? ( - - ) : ( - - )} - - {userRoles.includes("ADMIN") && createFunctionFab} - - ); -} diff --git a/src/types/table.d.ts b/src/types/table.d.ts index 3ed4b7c3..5b33e6d7 100644 --- a/src/types/table.d.ts +++ b/src/types/table.d.ts @@ -193,7 +193,8 @@ export type TableFilter = { | "time-minute-equal" | "id-equal" | "color-equal" - | "color-not-equal"; + | "color-not-equal" + | "-is-"; value: any; };