Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui-common): integrate GlideDataGrid into MatrixGrid #2134

Merged
merged 13 commits into from
Sep 12, 2024
Merged
3 changes: 2 additions & 1 deletion webapp/.npmrc
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
engine-strict=true
engine-strict=true
save-prefix= # prevent the caret (^) symbol use when installing new dependencies
1 change: 1 addition & 0 deletions webapp/public/locales/en/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
"global.error.delete": "Deletion failed",
"global.area.add": "Add an area",
"global.add": "Add",
"global.timeSeries": "Time Series",
"login.error": "Failed to authenticate",
"tasks.title": "Tasks",
"api.title": "API",
Expand Down
1 change: 1 addition & 0 deletions webapp/public/locales/fr/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
"global.error.delete": "La suppression a échoué",
"global.area.add": "Ajouter une zone",
"global.add": "Ajouter",
"global.timeSeries": "Séries temporelles",
"login.error": "Échec de l'authentification",
"tasks.title": "Tâches",
"api.title": "API",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
import { useOutletContext } from "react-router";
import useAppSelector from "../../../../../../redux/hooks/useAppSelector";
import { getCurrentAreaId } from "../../../../../../redux/selectors";
import { MatrixStats, StudyMetadata } from "../../../../../../common/types";
import MatrixInput from "../../../../../common/MatrixInput";
import { Root } from "./style";
import Matrix from "../../../../../common/MatrixGrid/Matrix";

function Load() {
const { study } = useOutletContext<{ study: StudyMetadata }>();
const currentArea = useAppSelector(getCurrentAreaId);
const url = `input/load/series/load_${currentArea}`;

////////////////////////////////////////////////////////////////
// JSX
////////////////////////////////////////////////////////////////

return (
<Root>
<MatrixInput study={study} url={url} computStats={MatrixStats.STATS} />
</Root>
);
return <Matrix url={url} />;
}

export default Load;
106 changes: 106 additions & 0 deletions webapp/src/components/common/MatrixGrid/Matrix.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { Divider, Skeleton } from "@mui/material";
import MatrixGrid from ".";
import { useMatrix } from "./useMatrix";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import ImportDialog from "../dialogs/ImportDialog";
import { useOutletContext } from "react-router";
import { StudyMetadata } from "../../../common/types";
import { MatrixContainer, MatrixHeader, MatrixTitle } from "./style";
import MatrixActions from "./MatrixActions";
import EmptyView from "../page/SimpleContent";

interface MatrixProps {
url: string;
title?: string;
enableTimeSeriesColumns?: boolean;
enableAggregateColumns?: boolean;
}

function Matrix({
url,
title = "global.timeSeries",
enableTimeSeriesColumns = true,
enableAggregateColumns = false,
}: MatrixProps) {
const { t } = useTranslation();
const { study } = useOutletContext<{ study: StudyMetadata }>();
const [openImportDialog, setOpenImportDialog] = useState(false);

const {
data,
error,
isLoading,
isSubmitting,
columns,
dateTime,
handleCellEdit,
handleMultipleCellsEdit,
handleImport,
handleSaveUpdates,
pendingUpdatesCount,
undo,
redo,
canUndo,
canRedo,
} = useMatrix(study.id, url, enableTimeSeriesColumns, enableAggregateColumns);

////////////////////////////////////////////////////////////////
// JSX
////////////////////////////////////////////////////////////////

if (isLoading) {
hdinia marked this conversation as resolved.
Show resolved Hide resolved
return <Skeleton sx={{ height: 1, transform: "none" }} />;
}

if (error) {
return <EmptyView title={error.toString()} />;
}

if (!data || data.length === 0) {
return <EmptyView title={t("matrix.message.matrixEmpty")} />;
}

return (
<MatrixContainer>
<MatrixHeader>
<MatrixTitle>{t(title)}</MatrixTitle>
<MatrixActions
onImport={() => setOpenImportDialog(true)}
onSave={handleSaveUpdates}
studyId={study.id}
path={url}
disabled={data.length === 0}
pendingUpdatesCount={pendingUpdatesCount}
isSubmitting={isSubmitting}
undo={undo}
redo={redo}
canUndo={canUndo}
canRedo={canRedo}
/>
</MatrixHeader>
<Divider sx={{ width: 1, mt: 1, mb: 2 }} />
<MatrixGrid
data={data}
columns={columns}
rows={data.length}
dateTime={dateTime}
onCellEdit={handleCellEdit}
onMultipleCellsEdit={handleMultipleCellsEdit}
readOnly={isSubmitting}
/>
{openImportDialog && (
<ImportDialog
open={openImportDialog}
title={t("matrix.importNewMatrix")}
dropzoneText={t("matrix.message.importHint")}
onCancel={() => setOpenImportDialog(false)}
onImport={handleImport}
accept={{ "text/*": [".csv", ".tsv", ".txt"] }}
/>
)}
</MatrixContainer>
);
}

export default Matrix;
101 changes: 101 additions & 0 deletions webapp/src/components/common/MatrixGrid/MatrixActions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { Box, Divider, IconButton, Tooltip } from "@mui/material";
import SplitButton from "../buttons/SplitButton";
import DownloadMatrixButton from "../DownloadMatrixButton";
import FileDownload from "@mui/icons-material/FileDownload";
import { useTranslation } from "react-i18next";
import { LoadingButton } from "@mui/lab";
import Save from "@mui/icons-material/Save";
import { Undo, Redo } from "@mui/icons-material";

interface MatrixActionsProps {
onImport: VoidFunction;
onSave: VoidFunction;
studyId: string;
path: string;
disabled: boolean;
pendingUpdatesCount: number;
isSubmitting: boolean;
undo: VoidFunction;
redo: VoidFunction;
canUndo: boolean;
canRedo: boolean;
}

function MatrixActions({
onImport,
onSave,
studyId,
path,
disabled,
pendingUpdatesCount,
isSubmitting,
undo,
redo,
canUndo,
canRedo,
}: MatrixActionsProps) {
const { t } = useTranslation();

////////////////////////////////////////////////////////////////
// JSX
////////////////////////////////////////////////////////////////

return (
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
<Tooltip title={t("global.undo")}>
<span>
<IconButton
onClick={undo}
disabled={!canUndo}
size="small"
color="primary"
>
<Undo fontSize="small" />
</IconButton>
</span>
</Tooltip>
<Tooltip title={t("global.redo")}>
<span>
<IconButton
onClick={redo}
disabled={!canRedo}
size="small"
color="primary"
>
<Redo fontSize="small" />
</IconButton>
</span>
</Tooltip>
<LoadingButton
onClick={onSave}
loading={isSubmitting}
loadingPosition="start"
startIcon={<Save />}
variant="contained"
size="small"
disabled={pendingUpdatesCount === 0}
>
({pendingUpdatesCount})
</LoadingButton>
<Divider sx={{ mx: 2 }} orientation="vertical" flexItem />
<SplitButton
options={[t("global.import.fromFile"), t("global.import.fromDatabase")]}
onClick={onImport}
size="small"
ButtonProps={{
startIcon: <FileDownload />,
}}
disabled={isSubmitting}
>
{t("global.import")}
</SplitButton>
<DownloadMatrixButton
studyId={studyId}
path={path}
disabled={disabled || isSubmitting}
/>
</Box>
);
}

export default MatrixActions;
Loading
Loading