-
- );
-}
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 (
- //
- //