diff --git a/www/src/components/Table/EmptyTable.tsx b/www/src/components/Table/EmptyTable.tsx index e20371ae..18e40410 100644 --- a/www/src/components/Table/EmptyTable.tsx +++ b/www/src/components/Table/EmptyTable.tsx @@ -15,7 +15,7 @@ import { APP_BAR_HEIGHT } from "components/Navigation"; import { useFiretableContext } from "contexts/firetableContext"; import ColumnMenu from "./ColumnMenu"; import ImportWizard from "components/Wizards/ImportWizard"; -import ImportCSV from "./TableHeader/ImportCSV"; +import ImportCSV from "./TableHeader/ImportCsv"; const useStyles = makeStyles((theme) => createStyles({ diff --git a/www/src/components/Table/HiddenFields.tsx b/www/src/components/Table/HiddenFields.tsx index aae6ab91..418bc3fd 100644 --- a/www/src/components/Table/HiddenFields.tsx +++ b/www/src/components/Table/HiddenFields.tsx @@ -7,7 +7,7 @@ import VisibilityOffIcon from "@material-ui/icons/VisibilityOff"; import MultiSelect from "@antlerengineering/multiselect"; import ButtonWithStatus from "components/ButtonWithStatus"; -import Column from "components/Wizards/ImportWizard/Column"; +import Column from "components/Wizards/Column"; import { useFiretableContext } from "contexts/firetableContext"; import { useAppContext } from "contexts/appContext"; diff --git a/www/src/components/Table/TableHeader/ExportCSV.tsx b/www/src/components/Table/TableHeader/ExportCSV.tsx index 47afca29..fe549f63 100644 --- a/www/src/components/Table/TableHeader/ExportCSV.tsx +++ b/www/src/components/Table/TableHeader/ExportCSV.tsx @@ -96,7 +96,7 @@ const selectedColumnsReducer = (doc: any) => ( } }; -export default function ExportCSV() { +export default function ExportCsv() { const classes = useStyles(); const [open, setOpen] = useState(false); diff --git a/www/src/components/Table/TableHeader/ImportCSV.tsx b/www/src/components/Table/TableHeader/ImportCSV.tsx index 0c72ae19..90bfda73 100644 --- a/www/src/components/Table/TableHeader/ImportCSV.tsx +++ b/www/src/components/Table/TableHeader/ImportCSV.tsx @@ -24,9 +24,13 @@ import TabPanel from "@material-ui/lab/TabPanel"; import ImportIcon from "assets/icons/Import"; import FileUploadIcon from "assets/icons/FileUpload"; -import AttachmentIcon from "@material-ui/icons/AttachFile"; +import CheckIcon from "@material-ui/icons/CheckCircle"; import GoIcon from "assets/icons/Go"; +import ImportCsvWizard, { + IImportCsvWizardProps, +} from "components/Wizards/ImportCsvWizard"; + const useStyles = makeStyles((theme) => createStyles({ button: { @@ -76,22 +80,23 @@ const useStyles = makeStyles((theme) => }) ); -export interface IImportCSVProps { +export interface IImportCsvProps { render?: ( onClick: (event: React.MouseEvent) => void ) => React.ReactNode; } -export default function ImportCSV({ render }: IImportCSVProps) { +export default function ImportCsv({ render }: IImportCsvProps) { const classes = useStyles(); const [open, setOpen] = useState(null); const [tab, setTab] = useState("upload"); - const [csvData, setCsvData] = useState<{ - columns: string[]; - rows: string[][]; - } | null>(null); + const [csvData, setCsvData] = useState( + null + ); const [error, setError] = useState(""); + const validCsv = + csvData !== null && csvData?.columns.length > 0 && csvData?.rows.length > 0; const handleOpen = (event: React.MouseEvent) => setOpen(event.currentTarget); @@ -152,6 +157,8 @@ export default function ImportCSV({ render }: IImportCSVProps) { }); }, 1000); + const [openWizard, setOpenWizard] = useState(false); + return ( <> {render ? ( @@ -218,11 +225,17 @@ export default function ImportCSV({ render }: IImportCSVProps) { ) : ( <> - + {validCsv ? ( + + ) : ( + + )} - Click to upload or drop CSV file here + {validCsv + ? "Valid CSV" + : "Click to upload or drop CSV file here"} @@ -277,16 +290,49 @@ export default function ImportCSV({ render }: IImportCSVProps) { + + ); } diff --git a/www/src/components/Table/TableHeader/index.tsx b/www/src/components/Table/TableHeader/index.tsx index 32df1f53..27590090 100644 --- a/www/src/components/Table/TableHeader/index.tsx +++ b/www/src/components/Table/TableHeader/index.tsx @@ -14,8 +14,8 @@ import { isCollectionGroup } from "util/fns"; import AddRowIcon from "assets/icons/AddRow"; import Filters from "../Filters"; -import ImportCSV from "./ImportCSV"; -import ExportCSV from "./ExportCSV"; +import ImportCSV from "./ImportCsv"; +import ExportCSV from "./ExportCsv"; import TableSettings from "./TableSettings"; import { DRAWER_COLLAPSED_WIDTH } from "components/SideDrawer"; diff --git a/www/src/components/Wizards/ImportWizard/Cell.tsx b/www/src/components/Wizards/Cell.tsx similarity index 100% rename from www/src/components/Wizards/ImportWizard/Cell.tsx rename to www/src/components/Wizards/Cell.tsx diff --git a/www/src/components/Wizards/ImportWizard/Column.tsx b/www/src/components/Wizards/Column.tsx similarity index 100% rename from www/src/components/Wizards/ImportWizard/Column.tsx rename to www/src/components/Wizards/Column.tsx diff --git a/www/src/components/Wizards/ImportWizard/FadeList.tsx b/www/src/components/Wizards/FadeList.tsx similarity index 100% rename from www/src/components/Wizards/ImportWizard/FadeList.tsx rename to www/src/components/Wizards/FadeList.tsx diff --git a/www/src/components/Wizards/ImportCsvWizard/Step1Columns.tsx b/www/src/components/Wizards/ImportCsvWizard/Step1Columns.tsx new file mode 100644 index 00000000..43ee2c0f --- /dev/null +++ b/www/src/components/Wizards/ImportCsvWizard/Step1Columns.tsx @@ -0,0 +1,154 @@ +import React, { useState, useEffect } from "react"; +import MultiSelect from "@antlerengineering/multiselect"; +import _find from "lodash/find"; + +import { + makeStyles, + createStyles, + Grid, + Typography, + Divider, + FormControlLabel, + Checkbox, +} from "@material-ui/core"; +import ArrowIcon from "@material-ui/icons/ArrowForward"; + +import { IStepProps } from "."; +import FadeList from "../FadeList"; +import Column from "../Column"; +import EmptyState from "components/EmptyState"; +import AddColumnIcon from "assets/icons/AddColumn"; + +import { useFiretableContext } from "contexts/firetableContext"; + +const useStyles = makeStyles((theme) => + createStyles({ + csvListItem: { display: "flex" }, + csvColumn: {}, + + formControlLabel: { + marginRight: 0, + flex: 1, + }, + columnLabel: { flex: 1 }, + + arrowGridItem: { + width: theme.spacing(7), + display: "flex", + alignItems: "center", + justifyContent: "center", + }, + }) +); + +export default function Step1Columns({ + csvData, + config, + updateConfig, + isXs, +}: IStepProps) { + const classes = useStyles(); + + const { tableState } = useFiretableContext(); + const tableColumns = Object.values( + tableState?.columns ?? {} + ).map((column) => ({ label: column.name, value: column.key })); + + const [selectedFields, setSelectedFields] = useState( + config.pairs.map((pair) => pair.csvKey) + ); + + const handleSelect = (field: string) => (_, checked: boolean) => { + if (checked) { + setSelectedFields((x) => [...x, field]); + } else { + const newValue = [...selectedFields]; + newValue.splice(newValue.indexOf(field), 1); + setSelectedFields(newValue); + } + }; + + const handleChange = (csvKey: string) => (columnKey: string) => + updateConfig({ pairs: [{ csvKey, columnKey }] }); + + return ( + <> + + {!isXs && ( + + + Select Columns ({config.pairs.length} of {csvData.columns.length}) + + + )} + + + Firetable Columns + + + + + + + + {csvData.columns.map((field) => { + const selected = selectedFields.indexOf(field) > -1; + const ftColumnKey = + _find(config.pairs, { csvKey: field })?.columnKey ?? null; + const matchingColumn = ftColumnKey + ? tableState?.columns[ftColumnKey] + : null; + + return ( + + + + } + label={} + classes={{ + root: classes.formControlLabel, + label: classes.columnLabel, + }} + /> + + + + + + + + {selected && ( + { + if (!ftColumnKey) return "Select or add column"; + else return matchingColumn.name; + }, + }, + }} + clearable={false} + displayEmpty + /> + )} + + + ); + })} + + + ); +} diff --git a/www/src/components/Wizards/ImportCsvWizard/index.tsx b/www/src/components/Wizards/ImportCsvWizard/index.tsx new file mode 100644 index 00000000..4c8ff16a --- /dev/null +++ b/www/src/components/Wizards/ImportCsvWizard/index.tsx @@ -0,0 +1,113 @@ +import React, { useState, useEffect } from "react"; +import _merge from "lodash/merge"; + +import { useTheme, useMediaQuery, Typography } from "@material-ui/core"; + +import WizardDialog from "../WizardDialog"; +import Step1Columns from "./Step1Columns"; + +import { ColumnConfig } from "hooks/useFiretable/useTableConfig"; +import { useFiretableContext } from "contexts/firetableContext"; + +export type CsvConfig = { + pairs: { csvKey: string; columnKey: string }[]; + newColumns: string[]; +}; + +export interface IStepProps { + csvData: NonNullable; + config: CsvConfig; + setConfig: React.Dispatch>; + updateConfig: (value: Partial) => void; + isXs: boolean; +} + +export interface IImportCsvWizardProps { + open: boolean; + setOpen: React.Dispatch>; + csvData: { + columns: string[]; + rows: string[][]; + } | null; +} + +export default function ImportCsvWizard({ + open, + setOpen, + csvData, +}: IImportCsvWizardProps) { + const theme = useTheme(); + const isXs = useMediaQuery(theme.breakpoints.down("xs")); + + const { tableState, tableActions } = useFiretableContext(); + + const [config, setConfig] = useState({ + pairs: [], + newColumns: [], + }); + const updateConfig: IStepProps["updateConfig"] = (value) => { + setConfig((prev) => ({ ..._merge(prev, value) })); + }; + + useEffect(() => { + if (!tableState || !open) return; + + if (Array.isArray(tableState.filters) && tableState.filters?.length > 0) + tableActions!.table.filter([]); + + if (Array.isArray(tableState.orderBy) && tableState.orderBy?.length > 0) + tableActions!.table.orderBy([]); + }, [open, tableState]); + + if (tableState?.rows.length === 0) return null; + + const handleFinish = () => { + tableActions?.table.updateConfig("columns", config); + setOpen(false); + }; + + if (!csvData) return null; + + return ( + setOpen(false)} + title="Import CSV" + steps={[ + { + title: "choose columns", + description: "Select which columns to import to your table.", + content: ( + + ), + disableNext: Object.keys(config).length === 0, + }, + { + title: "rename columns", + description: + "Rename your Firetable columns with user-friendly names. These changes will not update the field names in your database.", + content:
Step2Rename
, + }, + { + title: "set column types", + description: + "Set the type of each column to display your data correctly. Some column types have been suggested based off your data.", + content:
Step3Types
, + }, + { + title: "preview", + description: + "Preview your data with your configured columns. You can change column types by clicking “Edit Type” from the column menu at any time.", + content:
Step4Preview
, + }, + ]} + onFinish={handleFinish} + /> + ); +} diff --git a/www/src/components/Wizards/ImportWizard/Step1Columns.tsx b/www/src/components/Wizards/ImportWizard/Step1Columns.tsx index 58341c84..3e742ec7 100644 --- a/www/src/components/Wizards/ImportWizard/Step1Columns.tsx +++ b/www/src/components/Wizards/ImportWizard/Step1Columns.tsx @@ -21,8 +21,8 @@ import { import DragHandleIcon from "@material-ui/icons/DragHandle"; import { IStepProps } from "."; -import FadeList from "./FadeList"; -import Column from "./Column"; +import FadeList from "../FadeList"; +import Column from "../Column"; import EmptyState from "components/EmptyState"; import AddColumnIcon from "assets/icons/AddColumn"; diff --git a/www/src/components/Wizards/ImportWizard/Step2Rename.tsx b/www/src/components/Wizards/ImportWizard/Step2Rename.tsx index d8c2bcf4..1e728c9a 100644 --- a/www/src/components/Wizards/ImportWizard/Step2Rename.tsx +++ b/www/src/components/Wizards/ImportWizard/Step2Rename.tsx @@ -15,8 +15,8 @@ import EditIcon from "@material-ui/icons/Edit"; import DoneIcon from "@material-ui/icons/Done"; import { IStepProps } from "."; -import FadeList from "./FadeList"; -import Column from "./Column"; +import FadeList from "../FadeList"; +import Column from "../Column"; const useStyles = makeStyles((theme) => createStyles({ diff --git a/www/src/components/Wizards/ImportWizard/Step3Types.tsx b/www/src/components/Wizards/ImportWizard/Step3Types.tsx index ace55893..6edd6088 100644 --- a/www/src/components/Wizards/ImportWizard/Step3Types.tsx +++ b/www/src/components/Wizards/ImportWizard/Step3Types.tsx @@ -11,9 +11,9 @@ import { import ChevronRightIcon from "@material-ui/icons/ChevronRight"; import { IStepProps } from "."; -import FadeList from "./FadeList"; -import Column from "./Column"; -import Cell from "./Cell"; +import FadeList from "../FadeList"; +import Column from "../Column"; +import Cell from "../Cell"; import FieldsDropdown from "components/Table/ColumnMenu/FieldsDropdown"; import { useFiretableContext } from "contexts/firetableContext"; diff --git a/www/src/components/Wizards/ImportWizard/Step4Preview.tsx b/www/src/components/Wizards/ImportWizard/Step4Preview.tsx index 34664353..0181e4a4 100644 --- a/www/src/components/Wizards/ImportWizard/Step4Preview.tsx +++ b/www/src/components/Wizards/ImportWizard/Step4Preview.tsx @@ -4,8 +4,8 @@ import { ScrollSync, ScrollSyncPane } from "react-scroll-sync"; import { makeStyles, createStyles, Grid } from "@material-ui/core"; import { IStepProps } from "."; -import Column from "./Column"; -import Cell from "./Cell"; +import Column from "../Column"; +import Cell from "../Cell"; import { useFiretableContext } from "contexts/firetableContext";