mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-29 00:16:39 +01:00
use snackbar to generator firestore indices
This commit is contained in:
@@ -93,7 +93,10 @@ export default function ExportCSV(props: Props) {
|
||||
}
|
||||
async function handleExport(columns: any[]) {
|
||||
handleClose();
|
||||
snackContext.open("preparing file, download will start shortly", 5000);
|
||||
snackContext.open({
|
||||
message: "preparing file, download will start shortly",
|
||||
duration: 5000,
|
||||
});
|
||||
const data = await exportTable({
|
||||
collectionPath: collection,
|
||||
filters: [],
|
||||
|
||||
@@ -4,6 +4,8 @@ import IconButton from "@material-ui/core/IconButton";
|
||||
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
|
||||
import Chip from "@material-ui/core/Chip";
|
||||
|
||||
const snapshotReducer = (accumulator: string, currentValue: any) => {};
|
||||
const getPrimaryValue = (config: { primaryKeys: string[] }) => {};
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
@@ -39,7 +41,6 @@ const DocSelect = (props: Props) => {
|
||||
const { value, row, onSubmit, collectionPath, config, setSearch } = props;
|
||||
|
||||
const classes = useStyles();
|
||||
|
||||
const handleClick = (
|
||||
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||
) => {
|
||||
@@ -59,6 +60,7 @@ const DocSelect = (props: Props) => {
|
||||
newValue.splice(index, 1);
|
||||
onSubmit(newValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<IconButton onClick={handleClick}>
|
||||
|
||||
@@ -59,7 +59,6 @@ const Image = (props: Props) => {
|
||||
const { fieldName, value, row, onSubmit } = props;
|
||||
const [uploaderState, upload] = useUploader();
|
||||
const { progress } = uploaderState;
|
||||
console.log(uploaderState);
|
||||
const [localImage, setLocalImage] = useState<string | null>(null);
|
||||
const onDrop = useCallback(acceptedFiles => {
|
||||
// Do something with the files
|
||||
|
||||
@@ -4,7 +4,7 @@ import { SnackContext } from "../contexts/snackContext";
|
||||
export default function Snack() {
|
||||
const snackContext = useContext(SnackContext);
|
||||
|
||||
const { position, isOpen, close, message, duration } = snackContext;
|
||||
const { position, isOpen, close, message, duration, action } = snackContext;
|
||||
const { vertical, horizontal } = position;
|
||||
|
||||
useEffect(() => {
|
||||
@@ -20,6 +20,7 @@ export default function Snack() {
|
||||
"aria-describedby": "message-id",
|
||||
}}
|
||||
message={<span id="message-id">{message}</span>}
|
||||
action={action}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
190
www/src/components/Table/ColumnFilter/index.tsx
Normal file
190
www/src/components/Table/ColumnFilter/index.tsx
Normal file
@@ -0,0 +1,190 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import InputLabel from "@material-ui/core/InputLabel";
|
||||
import MenuItem from "@material-ui/core/MenuItem";
|
||||
import FormHelperText from "@material-ui/core/FormHelperText";
|
||||
import FormControl from "@material-ui/core/FormControl";
|
||||
|
||||
import Popper from "@material-ui/core/Popper";
|
||||
import Fade from "@material-ui/core/Fade";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Select from "@material-ui/core/Select";
|
||||
import ClickAwayListener from "@material-ui/core/ClickAwayListener";
|
||||
import { createStyles, makeStyles } from "@material-ui/core/styles";
|
||||
import ToggleButton from "@material-ui/lab/ToggleButton";
|
||||
import ToggleButtonGroup from "@material-ui/lab/ToggleButtonGroup";
|
||||
|
||||
import FormatColorFillIcon from "@material-ui/icons/FormatColorFill";
|
||||
import DeleteIcon from "@material-ui/icons/Delete";
|
||||
|
||||
import { Tooltip } from "@material-ui/core";
|
||||
|
||||
const useStyles = makeStyles(Theme =>
|
||||
createStyles({
|
||||
container: {
|
||||
padding: 15,
|
||||
},
|
||||
typography: {
|
||||
padding: 1,
|
||||
},
|
||||
header: {
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
top: 0,
|
||||
},
|
||||
button: {
|
||||
// margin: theme.spacing(1)
|
||||
},
|
||||
root: {
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
},
|
||||
formControl: {
|
||||
margin: Theme.spacing(1),
|
||||
minWidth: 120,
|
||||
},
|
||||
selectEmpty: {
|
||||
marginTop: Theme.spacing(2),
|
||||
},
|
||||
toggleGrouped: {
|
||||
margin: Theme.spacing(0.5),
|
||||
border: "none",
|
||||
padding: Theme.spacing(0, 1),
|
||||
"&:not(:first-child)": {
|
||||
borderRadius: Theme.shape.borderRadius,
|
||||
},
|
||||
"&:first-child": {
|
||||
borderRadius: Theme.shape.borderRadius,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
const ColumnEditor = (props: any) => {
|
||||
const { anchorEl, column, handleClose, actions } = props;
|
||||
|
||||
const [values, setValues] = useState({
|
||||
type: null,
|
||||
name: "",
|
||||
options: [],
|
||||
collectionPath: "",
|
||||
config: {},
|
||||
});
|
||||
const [flags, setFlags] = useState(() => [""]);
|
||||
const classes = useStyles();
|
||||
|
||||
function handleChange(
|
||||
event: React.ChangeEvent<{ name?: string; value: unknown }>
|
||||
) {
|
||||
setValues(oldValues => ({
|
||||
...oldValues,
|
||||
[event.target.name as string]: event.target.value,
|
||||
}));
|
||||
}
|
||||
const setValue = (key: string, value: any) => {
|
||||
setValues(oldValues => ({
|
||||
...oldValues,
|
||||
[key]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (column && !column.isNew) {
|
||||
setValues(oldValues => ({
|
||||
...oldValues,
|
||||
name: column.name,
|
||||
type: column.type,
|
||||
key: column.key,
|
||||
isNew: column.isNew,
|
||||
}));
|
||||
if (column.options) {
|
||||
setValue("options", column.options);
|
||||
} else {
|
||||
setValue("options", []);
|
||||
}
|
||||
if (column.collectionPath) {
|
||||
setValue("collectionPath", column.collectionPath);
|
||||
}
|
||||
["resizable", "editable", "fixed", "hidden"].map(flag => {
|
||||
if (column[flag]) {
|
||||
setFlags([...flags, flag]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [column]);
|
||||
const clearValues = () => {
|
||||
setValues({
|
||||
type: null,
|
||||
name: "",
|
||||
options: [],
|
||||
collectionPath: "",
|
||||
config: {},
|
||||
});
|
||||
};
|
||||
const onClickAway = (event: any) => {
|
||||
const elementId = event.target.id;
|
||||
if (!elementId.includes("select")) {
|
||||
handleClose();
|
||||
clearValues();
|
||||
}
|
||||
};
|
||||
|
||||
const handleToggle = (
|
||||
event: React.MouseEvent<HTMLElement>,
|
||||
newFlags: string[]
|
||||
) => {
|
||||
setFlags(newFlags);
|
||||
};
|
||||
|
||||
const createNewColumn = () => {
|
||||
const { name, type, options, collectionPath, config } = values;
|
||||
|
||||
actions.add(name, type, { options, collectionPath, config });
|
||||
|
||||
handleClose();
|
||||
clearValues();
|
||||
};
|
||||
const deleteColumn = () => {
|
||||
actions.remove(props.column.idx);
|
||||
handleClose();
|
||||
clearValues();
|
||||
};
|
||||
|
||||
const disableAdd = () => {
|
||||
const { type, name, options, collectionPath, config } = values;
|
||||
if (!type || name === "") return true;
|
||||
//TODO: Add more validation
|
||||
return false;
|
||||
};
|
||||
if (column) {
|
||||
return (
|
||||
<ClickAwayListener onClickAway={onClickAway}>
|
||||
<Popper
|
||||
className={classes.header}
|
||||
id={`id-${column.name}-filter`}
|
||||
open={!!anchorEl}
|
||||
anchorEl={anchorEl}
|
||||
placement={"bottom-end"}
|
||||
transition
|
||||
>
|
||||
{({ TransitionProps }) => (
|
||||
<Fade {...TransitionProps} timeout={350}>
|
||||
<Paper
|
||||
className={classes.container}
|
||||
style={{ minWidth: column.width ? column.width - 20 : 200 }}
|
||||
>
|
||||
<Grid container direction="column"></Grid>
|
||||
</Paper>
|
||||
</Fade>
|
||||
)}
|
||||
</Popper>
|
||||
</ClickAwayListener>
|
||||
);
|
||||
}
|
||||
return <div />;
|
||||
};
|
||||
|
||||
export default ColumnEditor;
|
||||
@@ -42,7 +42,7 @@ interface Props {
|
||||
|
||||
function Table(props: Props) {
|
||||
const { collection, filters } = props;
|
||||
const { tableState, tableActions } = useFiretable(collection);
|
||||
const { tableState, tableActions } = useFiretable(collection, filters);
|
||||
const [selectedCell, setSelectedCell] = useState<{ row: any; column: any }>({
|
||||
row: {},
|
||||
column: {},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from "react";
|
||||
import { SnackbarOrigin } from "@material-ui/core/Snackbar";
|
||||
|
||||
// Default State of our SnackBar
|
||||
const position: SnackbarOrigin = { vertical: "bottom", horizontal: "left" };
|
||||
const DEFAULT_STATE = {
|
||||
@@ -8,9 +9,15 @@ const DEFAULT_STATE = {
|
||||
duration: 2000, // time SnackBar should be visible
|
||||
position,
|
||||
close: () => {},
|
||||
open: (message: string, duration?: number, position?: SnackbarOrigin) => {
|
||||
console.log(message, duration);
|
||||
open: (props: {
|
||||
message: string;
|
||||
duration?: number;
|
||||
position?: SnackbarOrigin;
|
||||
action?: JSX.Element;
|
||||
}) => {
|
||||
console.log(props.message, props.duration);
|
||||
},
|
||||
action: <div />,
|
||||
};
|
||||
// Create our Context
|
||||
export const SnackContext = React.createContext(DEFAULT_STATE);
|
||||
@@ -33,14 +33,15 @@ export type FireTableFilter = {
|
||||
value: string | number | boolean;
|
||||
};
|
||||
|
||||
const useFiretable = (collectionName: string) => {
|
||||
const useFiretable = (collectionName: string, filters?: FireTableFilter[]) => {
|
||||
const [tableConfig, configActions] = useTableConfig(collectionName);
|
||||
const [tableState, tableActions] = useTable({
|
||||
path: collectionName,
|
||||
filters,
|
||||
});
|
||||
/** set collection path of table */
|
||||
const setTable = (collectionName: string, filters: FireTableFilter[]) => {
|
||||
if (collectionName !== tableState.path) {
|
||||
if (collectionName !== tableState.path && filters !== tableState.filters) {
|
||||
configActions.setTable(collectionName);
|
||||
tableActions.setTable(collectionName, filters);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { db } from "../../firebase";
|
||||
|
||||
import { useEffect, useReducer } from "react";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import React, { useEffect, useReducer, useContext } from "react";
|
||||
import equals from "ramda/es/equals";
|
||||
import firebase from "firebase/app";
|
||||
import { algoliaUpdateDoc } from "../../firebase/callables";
|
||||
import { FireTableFilter } from ".";
|
||||
import { SnackContext } from "../../contexts/snackContext";
|
||||
|
||||
const CAP = 1000; // safety paramter sets the upper limit of number of docs fetched by this hook
|
||||
|
||||
@@ -25,6 +27,8 @@ const tableInitialState = {
|
||||
};
|
||||
|
||||
const useTable = (initialOverrides: any) => {
|
||||
const snackContext = useContext(SnackContext);
|
||||
|
||||
const [tableState, tableDispatch] = useReducer(tableReducer, {
|
||||
...tableInitialState,
|
||||
...initialOverrides,
|
||||
@@ -99,10 +103,24 @@ const useTable = (initialOverrides: any) => {
|
||||
//TODO:callable to create new index
|
||||
if (error.message.includes("indexes?create_composite=")) {
|
||||
const url =
|
||||
"https://console.firebase.google.com/project/firetable-antler/database/firestore/" +
|
||||
`https://console.firebase.google.com/project/${process.env.REACT_APP_FIREBASE_PROJECT_NAME}/database/firestore/` +
|
||||
"indexes?create_composite=" +
|
||||
error.message.split("indexes?create_composite=")[1];
|
||||
console.log(url);
|
||||
snackContext.open({
|
||||
message: "needs a new index",
|
||||
duration: 10000,
|
||||
action: (
|
||||
<Button
|
||||
color="secondary"
|
||||
onClick={() => {
|
||||
window.open(url, "_blank");
|
||||
}}
|
||||
>
|
||||
create
|
||||
</Button>
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -10,6 +10,7 @@ export const SnackProvider: React.FC<ISnackProviderProps> = ({ children }) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [message, setMessage] = useState("");
|
||||
const [duration, setDuration] = useState(3000);
|
||||
const [action, setAction] = useState(<div />);
|
||||
const [position, setPosition] = useState<SnackbarOrigin>({
|
||||
vertical: "bottom",
|
||||
horizontal: "left",
|
||||
@@ -19,12 +20,17 @@ export const SnackProvider: React.FC<ISnackProviderProps> = ({ children }) => {
|
||||
setMessage("");
|
||||
setDuration(0);
|
||||
};
|
||||
const open = (
|
||||
message: string,
|
||||
duration?: number,
|
||||
position?: SnackbarOrigin
|
||||
) => {
|
||||
const open = (props: {
|
||||
message: string;
|
||||
duration?: number;
|
||||
position?: SnackbarOrigin;
|
||||
action?: JSX.Element;
|
||||
}) => {
|
||||
const { message, duration, position, action } = props;
|
||||
setMessage(message);
|
||||
if (action) {
|
||||
setAction(action);
|
||||
}
|
||||
if (duration) {
|
||||
setDuration(duration);
|
||||
} else {
|
||||
@@ -46,6 +52,7 @@ export const SnackProvider: React.FC<ISnackProviderProps> = ({ children }) => {
|
||||
position,
|
||||
close,
|
||||
open,
|
||||
action,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
Reference in New Issue
Block a user