use snackbar to generator firestore indices

This commit is contained in:
shams mosowi
2019-11-11 11:46:22 +11:00
parent 9418f4bc97
commit e9d4376967
10 changed files with 244 additions and 16 deletions

View File

@@ -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: [],

View File

@@ -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}>

View File

@@ -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

View File

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

View 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;

View File

@@ -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: {},

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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>
),
});
}
}
);

View File

@@ -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}