Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/browser-tests/questdb
Submodule questdb updated 137 files
5 changes: 5 additions & 0 deletions packages/web-console/assets/csv-file.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions packages/web-console/assets/parquet-file.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion packages/web-console/serve-dist.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ const server = http.createServer((req, res) => {
reqPathName.startsWith("/settings") ||
reqPathName.startsWith("/warnings") ||
reqPathName.startsWith("/chk") ||
reqPathName.startsWith("/imp")
reqPathName.startsWith("/imp") ||
reqPathName.startsWith("/api/")
) {
// proxy /exec requests to localhost:9000
const options = {
Expand Down
2 changes: 1 addition & 1 deletion packages/web-console/src/components/Toast/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from "react-toastify"
import { useNotificationCenter as RTNotificationCenter } from "react-toastify/addons/use-notification-center"
import { NotificationCenterItem as RNotificationCenterItem } from "react-toastify/addons/use-notification-center/useNotificationCenter"
import { BadgeType } from "../../scenes/Import/ImportCSVFiles/types"
import { BadgeType } from "../../scenes/Import/FileStatus"
import {
CloseCircle,
ErrorWarning,
Expand Down
2 changes: 2 additions & 0 deletions packages/web-console/src/consts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,6 @@ export const API = `https://${BASE}.questdb.io`
// the console will understand
export const API_VERSION = "2"

export const API_ROUTE_V1 = "/api/v1"

export const BUTTON_ICON_SIZE = "26px"
24 changes: 14 additions & 10 deletions packages/web-console/src/modules/ZeroState/start.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,28 +66,32 @@ export const Start = () => {
</StyledHeading>
<Actions>
<Action
onClick={() =>
onClick={() => {
dispatch(actions.console.setImportType("parquet"))
dispatch(actions.console.setActiveBottomPanel("import"))
}
}}
>
<img
alt="File upload icon"
width="60"
height="80"
src="assets/upload.svg"
height="72"
src="assets/parquet-file.svg"
/>
<Heading level={5}>Import CSV</Heading>
<Heading level={5}>Import Parquet</Heading>
</Action>
<Action
onClick={() => dispatch(actions.console.setActiveSidebar("create"))}
onClick={() => {
dispatch(actions.console.setImportType("csv"))
dispatch(actions.console.setActiveBottomPanel("import"))
}}
>
<img
alt="Create table icon"
alt="File upload icon"
width="60"
height="80"
src="assets/create-table.svg"
height="72"
src="assets/csv-file.svg"
/>
<Heading level={5}>Create table</Heading>
<Heading level={5}>Import CSV</Heading>
</Action>
</Actions>
</Items>
Expand Down
26 changes: 0 additions & 26 deletions packages/web-console/src/scenes/Console/import.tsx

This file was deleted.

87 changes: 62 additions & 25 deletions packages/web-console/src/scenes/Console/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import { actions, selectors } from "../../store"
import { Tooltip } from "../../components"
import { Sidebar } from "../../components/Sidebar"
import { Navigation } from "../../components/Sidebar/navigation"
import { Database2, Grid, PieChart, Search, FileSearch } from "@styled-icons/remix-line"
import { Database2, Grid, PieChart, FileSearch } from "@styled-icons/remix-line"
import { ResultViewMode } from "./types"
import { BUTTON_ICON_SIZE } from "../../consts"
import { PrimaryToggleButton } from "../../components"
import { Import } from "./import"
import { Import } from "../Import"
import { DropdownMenu } from "../../components/DropdownMenu"
import { BottomPanel } from "../../store/Console/types"
import { Allotment, AllotmentHandle } from "allotment"
import { Import as ImportIcon } from "../../components/icons/import"
Expand Down Expand Up @@ -78,6 +79,7 @@ const Console = () => {
useLocalStorage()
const result = useSelector(selectors.query.getResult)
const activeBottomPanel = useSelector(selectors.console.getActiveBottomPanel)
const importType = useSelector(selectors.console.getImportType)
const { consoleConfig } = useSettings()
const { isSearchPanelOpen, setSearchPanelOpen, searchPanelRef } = useSearch()

Expand Down Expand Up @@ -231,29 +233,64 @@ const Console = () => {
<Tooltip>{tooltipText}</Tooltip>
</PopperHover>
))}
<PopperHover
placement="right"
trigger={
<PrimaryToggleButton
readOnly={consoleConfig.readOnly}
{...(!consoleConfig.readOnly && {
onClick: () => {
dispatch(actions.console.setActiveBottomPanel("import"))
},
})}
selected={activeBottomPanel === "import"}
data-hook="import-panel-button"
{consoleConfig.readOnly ? (
<PopperHover
placement="right"
trigger={
<PrimaryToggleButton
readOnly={consoleConfig.readOnly}
selected={activeBottomPanel === "import"}
data-hook="import-panel-button"
>
<ImportIcon size={BUTTON_ICON_SIZE} />
</PrimaryToggleButton>
}
>
<Tooltip>
To use this feature, turn off read-only mode in the configuration file
</Tooltip>
</PopperHover>
) : (
<DropdownMenu.Root>
<PopperHover
placement="right"
trigger={
<DropdownMenu.Trigger asChild>
<PrimaryToggleButton
selected={activeBottomPanel === "import"}
data-hook="import-panel-button"
>
<ImportIcon size={BUTTON_ICON_SIZE} />
</PrimaryToggleButton>
</DropdownMenu.Trigger>
}
>
<ImportIcon size={BUTTON_ICON_SIZE} />
</PrimaryToggleButton>
}
>
<Tooltip>
{consoleConfig.readOnly
? "To use this feature, turn off read-only mode in the configuration file"
: "Import files from CSV"}
</Tooltip>
</PopperHover>
<Tooltip>Import data</Tooltip>
</PopperHover>
<DropdownMenu.Portal>
<DropdownMenu.Content>
<DropdownMenu.Item
data-hook="import-parquet-button"
onSelect={() => {
dispatch(actions.console.setImportType("parquet"))
dispatch(actions.console.setActiveBottomPanel("import"))
}}
>
Import Parquet
</DropdownMenu.Item>
<DropdownMenu.Item
data-hook="import-csv-button"
onSelect={() => {
dispatch(actions.console.setImportType("csv"))
dispatch(actions.console.setActiveBottomPanel("import"))
}}
>
Import CSV
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
)}
</Sidebar>
<Tab ref={resultRef}>
{result && <Result viewMode={resultViewMode} />}
Expand All @@ -262,7 +299,7 @@ const Console = () => {
<ZeroState />
</Tab>
<Tab ref={importRef}>
<Import />
<Import type={importType} />
</Tab>
</Bottom>
</Allotment.Pane>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { useRef, useState, useEffect } from "react"
import styled from "styled-components"
import { Box } from "../../../components/Box"
import { ProcessedFile } from "./types"
import { Box } from "../../components/Box"

const getFileDuplicates = (
inputFiles: FileList,
Expand All @@ -13,39 +12,37 @@ const getFileDuplicates = (
return duplicates
}

const Root = styled(Box).attrs({ flexDirection: "column" })<{
isDragging: boolean
}>`
const Root = styled(Box).attrs({ flexDirection: "column" })<{ $isDragging: boolean }>`
flex: 1;
width: 100%;
padding: 4rem 0 0;
gap: 2rem;
background: ${({ theme }) => theme.color.backgroundLighter};
border: 3px dashed ${({ isDragging }) => (isDragging ? "#7f839b" : "#333543")};
background: ${({ theme, $isDragging }) => $isDragging ? theme.color.selectionDarker : theme.color.backgroundLighter};
box-shadow: inset 0 0 10px 0 #1b1c23;
transition: all 0.15s ease-in-out;
`

type Props = {
files: ProcessedFile[]
existingFileNames: string[]
onFilesDropped: (files: File[]) => void
dialogOpen: boolean
dialogOpen?: boolean
enablePaste?: boolean
render: (props: {
duplicates: File[]
addToQueue: (inputFiles: FileList) => void
uploadInputRef: React.RefObject<HTMLInputElement>
}) => React.ReactNode
}

export const DropBox = ({
files,
export const Dropbox = ({
existingFileNames,
onFilesDropped,
dialogOpen,
dialogOpen = false,
enablePaste = true,
render,
}: Props) => {
const [isDragging, setIsDragging] = useState(false)
const [duplicates, setDuplicates] = useState<File[]>([])
const uploadInputRef = useRef<HTMLInputElement | null>(null)
const filenames = useRef<string[]>(files.map((f) => f.table_name))

const handleDrag = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault()
Expand All @@ -61,7 +58,7 @@ export const DropBox = ({
}

const addToQueue = (inputFiles: FileList) => {
const duplicates = getFileDuplicates(inputFiles, filenames.current)
const duplicates = getFileDuplicates(inputFiles, existingFileNames)
setDuplicates(duplicates)
onFilesDropped(
Array.from(inputFiles).filter((f) => !duplicates.includes(f)),
Expand All @@ -78,33 +75,33 @@ export const DropBox = ({
}

useEffect(() => {
if (!enablePaste) return

return () => {
window.removeEventListener("paste", handlePaste)
}
}, [])
}, [enablePaste])

useEffect(() => {
if (!enablePaste) return

if (dialogOpen) {
window.removeEventListener("paste", handlePaste)
} else {
window.addEventListener("paste", handlePaste)
}
}, [dialogOpen])

useEffect(() => {
filenames.current = files.map((f) => f.table_name)
}, [files])
}, [dialogOpen, enablePaste])

return (
<Root
onDragEnter={handleDrag}
onDragOver={handleDrag}
onDragLeave={handleDrag}
onDrop={handleDrop}
isDragging={isDragging}
data-hook="import-dropbox"
$isDragging={isDragging}
>
{render({ duplicates, addToQueue })}
{render({ duplicates, addToQueue, uploadInputRef })}
</Root>
)
}
}
Loading
Loading