diff --git a/packages/web-console/src/components/TableSchemaDialog/dialog.tsx b/packages/web-console/src/components/TableSchemaDialog/dialog.tsx index e19c3fa49..beb62fa02 100644 --- a/packages/web-console/src/components/TableSchemaDialog/dialog.tsx +++ b/packages/web-console/src/components/TableSchemaDialog/dialog.tsx @@ -50,7 +50,7 @@ type Props = { isEditLocked: boolean hasWalSetting: boolean walEnabled?: boolean - onOpenChange: (openedFileName: string | undefined) => void + onOpenChange: (openedFileName?: string) => void onSchemaChange: (values: SchemaFormValues) => void name: string schema: SchemaColumn[] diff --git a/packages/web-console/src/scenes/Import/ImportCSVFiles/files-to-upload.tsx b/packages/web-console/src/scenes/Import/ImportCSVFiles/files-to-upload.tsx index 80ae52b2d..080ce0b11 100644 --- a/packages/web-console/src/scenes/Import/ImportCSVFiles/files-to-upload.tsx +++ b/packages/web-console/src/scenes/Import/ImportCSVFiles/files-to-upload.tsx @@ -58,9 +58,9 @@ const FileTextBox = styled(Box)` type Props = { files: ProcessedFile[] onDialogToggle: (open: boolean) => void - onFileRemove: (filename: string) => void - onFileUpload: (filename: string) => void - onFilePropertyChange: (filename: string, file: Partial) => void + onFileRemove: (id: string) => void + onFileUpload: (id: string) => void + onFilePropertyChange: (id: string, file: Partial) => void } export const FilesToUpload = ({ @@ -71,11 +71,11 @@ export const FilesToUpload = ({ onFileUpload, }: Props) => { const [renameDialogOpen, setRenameDialogOpen] = React.useState< - string | null + string | undefined >() const [schemaDialogOpen, setSchemaDialogOpen] = React.useState< - string | null + string | undefined >() useEffect(() => { @@ -157,13 +157,12 @@ export const FilesToUpload = ({ align: "flex-end", width: "200px", render: ({ data }) => { - const name = data.table_name ?? data.fileObject.name return ( setRenameDialogOpen(f?.id)} onNameChange={(name) => { - onFilePropertyChange(data.fileObject.name, { + onFilePropertyChange(data.id, { table_name: name, }) }} @@ -197,10 +196,12 @@ export const FilesToUpload = ({ return ( + setSchemaDialogOpen(name ? data.id : undefined) + } onSchemaChange={(schema) => { - onFilePropertyChange(data.fileObject.name, { + onFilePropertyChange(data.id, { schema: schema.schemaColumns, partitionBy: schema.partitionBy, timestamp: schema.timestamp, @@ -246,7 +247,7 @@ export const FilesToUpload = ({ name="overwrite" defaultValue={data.settings.overwrite ? "true" : "false"} onChange={(e: React.ChangeEvent) => - onFilePropertyChange(data.fileObject.name, { + onFilePropertyChange(data.id, { settings: { ...data.settings, overwrite: e.target.value === "true", diff --git a/packages/web-console/src/scenes/Import/ImportCSVFiles/index.tsx b/packages/web-console/src/scenes/Import/ImportCSVFiles/index.tsx index 3dea4ee9c..0e2e27683 100644 --- a/packages/web-console/src/scenes/Import/ImportCSVFiles/index.tsx +++ b/packages/web-console/src/scenes/Import/ImportCSVFiles/index.tsx @@ -12,38 +12,18 @@ import { useSelector } from "react-redux" import { selectors } from "../../../store" import { DEFAULT_TIMESTAMP_FORMAT, MAX_UNCOMMITTED_ROWS } from "./const" import { useIsVisible } from "../../../components/Hooks" +import { + extractPrecionFromGeohash, + isGeoHash, + mapColumnTypeToQuestDB, + mapColumnTypeToUI, + uuid, +} from "./utils" type Props = { onImported: (result: UploadResult) => void } -const isGeoHash = (type: string) => type.startsWith("GEOHASH") - -function extractPrecionFromGeohash(geohash: string) { - const regex = /\(([^)]+)\)/g - const matches = regex.exec(geohash) - if (matches && matches.length > 1) { - return matches[1] - } - return "" -} - -const mapColumnTypeToUI = (type: string) => { - if (isGeoHash(type)) { - return "GEOHASH" - } else return type.toUpperCase() -} - -const mapColumnTypeToQuestDB = (column: SchemaColumn) => { - if (column.type === "GEOHASH") { - return { - ...column, - type: `GEOHASH(${column.precision})`, - } - } - return column -} - export const ImportCSVFiles = ({ onImported }: Props) => { const { quest } = useContext(QuestContext) const [filesDropped, setFilesDropped] = useState([]) @@ -52,13 +32,10 @@ export const ImportCSVFiles = ({ onImported }: Props) => { const rootRef = useRef(null) const isVisible = useIsVisible(rootRef) - const setFileProperties = ( - filename: string, - file: Partial, - ) => { + const setFileProperties = (id: string, file: Partial) => { setFilesDropped((files) => files.map((f) => { - if (f.table_name === filename) { + if (f.id === id) { return { ...f, ...file, @@ -70,7 +47,7 @@ export const ImportCSVFiles = ({ onImported }: Props) => { } const setIsUploading = (file: ProcessedFile, isUploading: boolean) => { - setFileProperties(file.table_name, { isUploading }) + setFileProperties(file.id, { isUploading }) } const getFileConfigs = async (files: File[]): Promise => { @@ -117,6 +94,7 @@ export const ImportCSVFiles = ({ onImported }: Props) => { : "" return { + id: uuid(), fileObject: file, table_name: file.name, status: result.status, @@ -175,10 +153,8 @@ export const ImportCSVFiles = ({ onImported }: Props) => { { - const file = filesDropped.find( - (f) => f.table_name === filename, - ) as ProcessedFile + onFileUpload={async (id) => { + const file = filesDropped.find((f) => f.id === id) as ProcessedFile if (file.isUploading) { return @@ -193,12 +169,12 @@ export const ImportCSVFiles = ({ onImported }: Props) => { partitionBy: file.partitionBy, timestamp: file.timestamp, onProgress: (progress) => { - setFileProperties(file.table_name, { + setFileProperties(file.id, { uploadProgress: progress, }) }, }) - setFileProperties(file.table_name, { + setFileProperties(file.id, { uploaded: response.status === "OK", uploadResult: response.status === "OK" ? response : undefined, schema: @@ -232,7 +208,7 @@ export const ImportCSVFiles = ({ onImported }: Props) => { setIsUploading(file, false) } catch (err) { setIsUploading(file, false) - setFileProperties(file.table_name, { + setFileProperties(file.id, { uploaded: false, uploadResult: undefined, uploadProgress: 0, @@ -240,20 +216,18 @@ export const ImportCSVFiles = ({ onImported }: Props) => { }) } }} - onFileRemove={(filename) => { - const file = filesDropped.find( - (f) => f.table_name === filename, - ) as ProcessedFile + onFileRemove={(id) => { + const file = filesDropped.find((f) => f.id === id) as ProcessedFile setFilesDropped( filesDropped.filter( (f) => f.fileObject.name !== file.fileObject.name, ), ) }} - onFilePropertyChange={async (filename, partialFile) => { + onFilePropertyChange={async (id, partialFile) => { const processedFiles = await Promise.all( filesDropped.map(async (file) => { - if (file.fileObject.name === filename) { + if (file.id === id) { // Only check for file existence if table name is changed const result = partialFile.table_name ? await quest.checkCSVFile(partialFile.table_name) diff --git a/packages/web-console/src/scenes/Import/ImportCSVFiles/rename-table-dialog.tsx b/packages/web-console/src/scenes/Import/ImportCSVFiles/rename-table-dialog.tsx index 68f1456be..1f0aa0e1e 100644 --- a/packages/web-console/src/scenes/Import/ImportCSVFiles/rename-table-dialog.tsx +++ b/packages/web-console/src/scenes/Import/ImportCSVFiles/rename-table-dialog.tsx @@ -27,7 +27,7 @@ const StyledDescription = styled(Dialog.Description)` type Props = { open: boolean - onOpenChange: (openedFileName: string | undefined) => void + onOpenChange: (file?: ProcessedFile) => void onNameChange: (name: string) => void file: ProcessedFile } @@ -61,7 +61,7 @@ export const RenameTableDialog = ({ diff --git a/packages/web-console/src/scenes/Import/ImportCSVFiles/types.ts b/packages/web-console/src/scenes/Import/ImportCSVFiles/types.ts index fb4d24eec..c21353f0a 100644 --- a/packages/web-console/src/scenes/Import/ImportCSVFiles/types.ts +++ b/packages/web-console/src/scenes/Import/ImportCSVFiles/types.ts @@ -2,6 +2,7 @@ import { UploadResult, UploadModeSettings } from "utils" import { SchemaColumn } from "../../../components/TableSchemaDialog/types" export type ProcessedFile = { + id: string fileObject: File status: string table_name: string diff --git a/packages/web-console/src/scenes/Import/ImportCSVFiles/upload-actions.tsx b/packages/web-console/src/scenes/Import/ImportCSVFiles/upload-actions.tsx index 24cae6f63..ebd119ebf 100644 --- a/packages/web-console/src/scenes/Import/ImportCSVFiles/upload-actions.tsx +++ b/packages/web-console/src/scenes/Import/ImportCSVFiles/upload-actions.tsx @@ -33,7 +33,7 @@ export const UploadActions = ({ disabled={file.isUploading} skin="primary" prefixIcon={} - onClick={() => onUpload(file.table_name)} + onClick={() => onUpload(file.id)} > {file.isUploading ? "Uploading..." : "Upload"} @@ -44,7 +44,7 @@ export const UploadActions = ({ disabled={file.isUploading} skin="secondary" onClick={() => { - onRemove(file.table_name) + onRemove(file.id) }} > diff --git a/packages/web-console/src/scenes/Import/ImportCSVFiles/utils.ts b/packages/web-console/src/scenes/Import/ImportCSVFiles/utils.ts new file mode 100644 index 000000000..79115e632 --- /dev/null +++ b/packages/web-console/src/scenes/Import/ImportCSVFiles/utils.ts @@ -0,0 +1,49 @@ +import { SchemaColumn } from "components/TableSchemaDialog/types" + +export const isGeoHash = (type: string) => type.startsWith("GEOHASH") + +export const extractPrecionFromGeohash = (geohash: string) => { + const regex = /\(([^)]+)\)/g + const matches = regex.exec(geohash) + if (matches && matches.length > 1) { + return matches[1] + } + return "" +} + +export const mapColumnTypeToUI = (type: string) => { + if (isGeoHash(type)) { + return "GEOHASH" + } else return type.toUpperCase() +} + +export const mapColumnTypeToQuestDB = (column: SchemaColumn) => { + if (column.type === "GEOHASH") { + return { + ...column, + type: `GEOHASH(${column.precision})`, + } + } + return column +} + +const str = () => + ( + "00000000000000000" + (Math.random() * 0xffffffffffffffff).toString(16) + ).slice(-16) + +export const uuid = () => { + const a = str() + const b = str() + return ( + a.slice(0, 8) + + "-" + + a.slice(8, 12) + + "-4" + + a.slice(13) + + "-a" + + b.slice(1, 4) + + "-" + + b.slice(4) + ) +}