Move in progress apps temporarily
This commit is contained in:
parent
ba04aa108c
commit
6eaaf8e9be
360 changed files with 6171 additions and 55 deletions
|
|
@ -1,366 +0,0 @@
|
|||
import { FC, useState, useEffect } from "react";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Grid,
|
||||
Popover,
|
||||
Accordion,
|
||||
AccordionSummary,
|
||||
AccordionDetails,
|
||||
Dialog,
|
||||
Divider,
|
||||
Paper,
|
||||
MenuList,
|
||||
MenuItem,
|
||||
ListItemText,
|
||||
ListItemIcon,
|
||||
TextField,
|
||||
} from "@mui/material";
|
||||
import {
|
||||
ExpandMore as ExpandMoreIcon,
|
||||
AddCircleOutline as AddCircleOutlineIcon,
|
||||
SavedSearch as SavedSearchIcon,
|
||||
RemoveCircle as RemoveCircleIcon,
|
||||
} from "@mui/icons-material";
|
||||
import { useTranslate } from "react-polyglot";
|
||||
import { QueryBuilder } from "components/QueryBuilder";
|
||||
import { QueryText } from "components/QueryText";
|
||||
import { LiveDataViewer } from "components/LiveDataViewer";
|
||||
import { Tooltip } from "components/Tooltip";
|
||||
import visualizationMap from "config/visualizationMap.json";
|
||||
import { VisualizationSelectCard } from "./VisualizationSelectCard";
|
||||
import { MetricSelectCard } from "./MetricSelectCard";
|
||||
import { useAppContext } from "./AppProvider";
|
||||
|
||||
interface VisualizationBuilderProps {
|
||||
templates: any[];
|
||||
}
|
||||
|
||||
export const VisualizationBuilder: FC<VisualizationBuilderProps> = ({
|
||||
templates,
|
||||
}) => {
|
||||
const t = useTranslate();
|
||||
const {
|
||||
typography: { h4 },
|
||||
colors: { white, leafcutterElectricBlue, cdrLinkOrange },
|
||||
foundCount,
|
||||
query,
|
||||
replaceQuery,
|
||||
clearQuery,
|
||||
} = useAppContext();
|
||||
const { visualizations } = visualizationMap;
|
||||
const [selectedVisualizationType, setSelectedVisualizationType] =
|
||||
useState(null);
|
||||
const toggleSelectedVisualizationType = (visualizationType: string) => {
|
||||
if (visualizationType === selectedVisualizationType) {
|
||||
setSelectedVisualizationType(null);
|
||||
} else {
|
||||
setSelectedVisualizationType(visualizationType);
|
||||
}
|
||||
};
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [savedSearches, setSavedSearches] = useState([]);
|
||||
const [savedSearchName, setSavedSearchName] = useState("");
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
|
||||
const updateSearches = async () => {
|
||||
const result = await fetch("/api/searches/list");
|
||||
const existingSearches = await result.json();
|
||||
setSavedSearches(existingSearches);
|
||||
};
|
||||
useEffect(() => {
|
||||
updateSearches();
|
||||
}, [setSavedSearches]);
|
||||
|
||||
const showSavedSearchPopup = (event) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
const handleClose = () => {
|
||||
setSavedSearchName("");
|
||||
setAnchorEl(null);
|
||||
};
|
||||
const closeDialog = () => {
|
||||
setDialogOpen(false);
|
||||
};
|
||||
const createSavedSearch = async (name: string, q: any) => {
|
||||
await fetch("/api/searches/create", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ name, query: q }),
|
||||
});
|
||||
await updateSearches();
|
||||
handleClose();
|
||||
closeDialog();
|
||||
};
|
||||
|
||||
const deleteSavedSearch = async (name: string) => {
|
||||
await fetch("/api/searches/delete", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ name }),
|
||||
});
|
||||
await updateSearches();
|
||||
closeDialog();
|
||||
};
|
||||
|
||||
const updateSearch = (name: string) => {
|
||||
handleClose();
|
||||
closeDialog();
|
||||
const found = savedSearches.find((search) => search.name === name);
|
||||
replaceQuery(found.query);
|
||||
};
|
||||
|
||||
const clearSearch = () => clearQuery();
|
||||
|
||||
const open = Boolean(anchorEl);
|
||||
const elementID = open ? "simple-popover" : undefined;
|
||||
const [queryExpanded, setQueryExpanded] = useState(true);
|
||||
const [resultsExpanded, setResultsExpanded] = useState(false);
|
||||
const minHeight = "42px";
|
||||
const maxHeight = "42px";
|
||||
const summaryStyles = {
|
||||
backgroundColor: leafcutterElectricBlue,
|
||||
height: "14px",
|
||||
minHeight,
|
||||
maxHeight,
|
||||
"&.Mui-expanded": {
|
||||
minHeight,
|
||||
maxHeight,
|
||||
},
|
||||
};
|
||||
const buttonStyles = {
|
||||
fontFamily: "Poppins, sans-serif",
|
||||
fontWeight: 700,
|
||||
color: `${white} !important`,
|
||||
borderRadius: 999,
|
||||
backgroundColor: leafcutterElectricBlue,
|
||||
padding: "6px 30px",
|
||||
margin: "20px 0px",
|
||||
whiteSpace: "nowrap",
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Dialog open={dialogOpen}>
|
||||
<Box sx={{ pt: 3, pl: 3, pr: 3 }}>
|
||||
<Grid container direction="column" spacing={2}>
|
||||
<Grid item>
|
||||
<TextField
|
||||
size="small"
|
||||
placeholder="Saved search name"
|
||||
sx={{ width: 400 }}
|
||||
onChange={(e) => setSavedSearchName(e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item container direction="row" justifyContent="space-between">
|
||||
<Grid item>
|
||||
<Button sx={buttonStyles} onClick={closeDialog}>
|
||||
Cancel
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button
|
||||
sx={buttonStyles}
|
||||
onClick={async () => {
|
||||
await createSavedSearch(savedSearchName, query);
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</Dialog>
|
||||
<Grid
|
||||
container
|
||||
direction="row"
|
||||
sx={{ mt: 4, mb: 2 }}
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<Grid item>
|
||||
<Tooltip
|
||||
title={t("searchAndCreateTitle")}
|
||||
description={t("searchAndCreateDescription")}
|
||||
tooltipID="searchCreate"
|
||||
nextURL="/create?tooltip=categories"
|
||||
previousURL="/?tooltip=profile"
|
||||
placement="top"
|
||||
>
|
||||
<Box sx={h4}>Search Criteria</Box>
|
||||
</Tooltip>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button
|
||||
aria-describedby={elementID}
|
||||
variant="contained"
|
||||
onClick={showSavedSearchPopup}
|
||||
sx={{
|
||||
backgroundColor: cdrLinkOrange,
|
||||
textTransform: "none",
|
||||
fontStyle: "italic",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
<SavedSearchIcon sx={{ mr: 1 }} />
|
||||
{t("savedSearch")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={clearSearch}
|
||||
sx={{
|
||||
backgroundColor: leafcutterElectricBlue,
|
||||
textTransform: "none",
|
||||
fontWeight: "bold",
|
||||
ml: 3,
|
||||
}}
|
||||
>
|
||||
{t("clear")}
|
||||
</Button>
|
||||
<Popover
|
||||
id={elementID}
|
||||
open={open}
|
||||
anchorEl={anchorEl}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "right",
|
||||
}}
|
||||
transformOrigin={{ vertical: "top", horizontal: "right" }}
|
||||
>
|
||||
<Paper>
|
||||
<MenuList>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleClose();
|
||||
setDialogOpen(true);
|
||||
}}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<AddCircleOutlineIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>{t("saveCurrentSearch")}</ListItemText>
|
||||
</MenuItem>
|
||||
<Divider />
|
||||
{savedSearches.map((savedSearch) => (
|
||||
<MenuItem
|
||||
key={savedSearch.name}
|
||||
onClick={() => updateSearch(savedSearch.name)}
|
||||
>
|
||||
<ListItemIcon />
|
||||
<ListItemText>{savedSearch.name}</ListItemText>
|
||||
<Box
|
||||
onClick={() => deleteSavedSearch(savedSearch.name)}
|
||||
sx={{ p: 0, m: 0, zIndex: 100 }}
|
||||
>
|
||||
<RemoveCircleIcon
|
||||
sx={{
|
||||
color: cdrLinkOrange,
|
||||
p: 0,
|
||||
m: 0,
|
||||
":hover": { color: leafcutterElectricBlue },
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</MenuItem>
|
||||
))}
|
||||
</MenuList>
|
||||
</Paper>
|
||||
</Popover>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<QueryBuilder />
|
||||
<Accordion
|
||||
expanded={queryExpanded}
|
||||
onClick={() => setQueryExpanded(!queryExpanded)}
|
||||
>
|
||||
<AccordionSummary
|
||||
expandIcon={<ExpandMoreIcon sx={{ color: white, fontSize: 28 }} />}
|
||||
sx={summaryStyles}
|
||||
>
|
||||
<Box sx={{ ...h4, color: white }}>{t("query")}</Box>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails sx={{ p: 2 }}>
|
||||
<QueryText />
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
<Accordion
|
||||
sx={{ mt: 2 }}
|
||||
expanded={resultsExpanded}
|
||||
onClick={() => setResultsExpanded(!resultsExpanded)}
|
||||
>
|
||||
<AccordionSummary
|
||||
expandIcon={<ExpandMoreIcon sx={{ color: white, fontSize: 28 }} />}
|
||||
sx={summaryStyles}
|
||||
>
|
||||
<Box sx={{ ...h4, color: white }}>{`${t(
|
||||
"results"
|
||||
)} (${foundCount})`}</Box>
|
||||
</AccordionSummary>
|
||||
<Tooltip
|
||||
title={`${t("queryResultsCardTitle")}`}
|
||||
description={t("queryResultsCardDescription")}
|
||||
tooltipID="queryResults"
|
||||
placement="top"
|
||||
previousURL="/create?tooltip=advancedOptions"
|
||||
nextURL="/create?tooltip=viewResults"
|
||||
>
|
||||
<AccordionDetails sx={{ p: 2, pb: 4 }}>
|
||||
<LiveDataViewer />
|
||||
</AccordionDetails>
|
||||
</Tooltip>
|
||||
</Accordion>
|
||||
<Tooltip
|
||||
title={t("viewResultsCardTitle")}
|
||||
description={t("viewResultsCardDescription")}
|
||||
tooltipID="viewResults"
|
||||
placement="top"
|
||||
previousURL="/create?tooltip=queryResults"
|
||||
>
|
||||
<Box sx={{ ...h4, mt: 6, mb: 2 }}>{t("selectVisualization")}:</Box>
|
||||
</Tooltip>
|
||||
<Box display="grid" gridTemplateColumns="repeat(5, 1fr)" gap={2}>
|
||||
{Object.keys(visualizations).map((key: string) => (
|
||||
<VisualizationSelectCard
|
||||
key={key}
|
||||
visualizationType={key}
|
||||
title={visualizations[key].name}
|
||||
enabled={
|
||||
selectedVisualizationType === key ||
|
||||
selectedVisualizationType === null
|
||||
}
|
||||
selected={selectedVisualizationType === key}
|
||||
toggleSelected={toggleSelectedVisualizationType}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
<Box sx={{ ...h4, mt: 6, mb: 2 }}>{t("selectFieldVisualize")}:</Box>
|
||||
<Box
|
||||
display="grid"
|
||||
gridTemplateColumns="repeat(5, 1fr)"
|
||||
gap={2}
|
||||
sx={{ minHeight: 200 }}
|
||||
>
|
||||
{templates
|
||||
.filter(
|
||||
(template: any) => template.type === selectedVisualizationType
|
||||
)
|
||||
.map((template: any) => {
|
||||
const { id, type, title, description } = template;
|
||||
const cleanTitle = title
|
||||
.replace("Templated", "")
|
||||
.replace(visualizations[type].name, "");
|
||||
const metricType = cleanTitle.replace(/\s/g, "").toLowerCase();
|
||||
return (
|
||||
<MetricSelectCard
|
||||
key={id}
|
||||
visualizationID={id}
|
||||
metricType={metricType}
|
||||
title={`By ${cleanTitle}`}
|
||||
description={description}
|
||||
enabled
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue