From 87471a19a447475b8fa47c131c916c8a844b1a6d Mon Sep 17 00:00:00 2001 From: Maksim Chervonnyi Date: Mon, 2 Sep 2024 13:42:23 +0200 Subject: [PATCH] Show `database` and `catalogName` (#4123) Co-authored-by: Alexei Mochalov --- catalog/CHANGELOG.md | 2 + .../Bucket/Queries/Athena/Database.tsx | 208 ++++++++---------- .../Bucket/Queries/Athena/QueryEditor.tsx | 58 ++++- 3 files changed, 141 insertions(+), 127 deletions(-) diff --git a/catalog/CHANGELOG.md b/catalog/CHANGELOG.md index 657a4534516..d159f505049 100644 --- a/catalog/CHANGELOG.md +++ b/catalog/CHANGELOG.md @@ -16,6 +16,8 @@ where verb is one of ## Changes +- [Changed] Athena: always show catalog name, simplify setting execution context +([#4123](https://github.com/quiltdata/quilt/pull/4123)) - [Added] Support `ui.actions.downloadObject` and `ui.actions.downloadPackage` options for configuring visibility of download buttons under "Bucket" and "Packages" respectively diff --git a/catalog/app/containers/Bucket/Queries/Athena/Database.tsx b/catalog/app/containers/Bucket/Queries/Athena/Database.tsx index 136a8c29396..dbe46d38b72 100644 --- a/catalog/app/containers/Bucket/Queries/Athena/Database.tsx +++ b/catalog/app/containers/Bucket/Queries/Athena/Database.tsx @@ -4,26 +4,39 @@ import * as M from '@material-ui/core' import * as Lab from '@material-ui/lab' import Skeleton from 'components/Skeleton' +import * as Dialogs from 'utils/GlobalDialogs' import * as requests from '../requests' interface SelectErrorProps { + className?: string error: Error } -function SelectError({ error }: SelectErrorProps) { +function SelectError({ className, error }: SelectErrorProps) { + const openDialog = Dialogs.use() + const handleClick = React.useCallback(() => { + openDialog(() => ( + + {error.message} + + )) + }, [error.message, openDialog]) return ( - - {error.name} - {error.message} + + Show more + + } + className={className} + severity="error" + > + {error.name} ) } -function SelectSkeleton() { - return -} - const LOAD_MORE = '__load-more__' interface Response { @@ -38,14 +51,24 @@ const useSelectStyles = M.makeStyles({ }) interface SelectProps { + className?: string data: Response label: string onChange: (value: string) => void onLoadMore: (prev: Response) => void value: string | null -} - -function Select({ data, label, onChange, onLoadMore, value }: SelectProps) { + disabled?: boolean +} + +function Select({ + className, + data, + disabled, + label, + onChange, + onLoadMore, + value, +}: SelectProps) { const classes = useSelectStyles() const handleChange = React.useCallback( (event) => { @@ -59,7 +82,7 @@ function Select({ data, label, onChange, onLoadMore, value }: SelectProps) { ) return ( - + {label} {data.list.map((item) => ( @@ -74,11 +97,12 @@ function Select({ data, label, onChange, onLoadMore, value }: SelectProps) { } interface SelectCatalogNameProps { + className?: string value: requests.athena.CatalogName | null onChange: (catalogName: requests.athena.CatalogName) => void } -function SelectCatalogName({ value, onChange }: SelectCatalogNameProps) { +function SelectCatalogName({ className, value, onChange }: SelectCatalogNameProps) { const [prev, setPrev] = React.useState( null, ) @@ -86,6 +110,7 @@ function SelectCatalogName({ value, onChange }: SelectCatalogNameProps) { return data.case({ Ok: (response) => ( ), - Err: (error) => , - _: () => , + Err: (error) => , + _: () => , }) } -const useDialogStyles = M.makeStyles((t) => ({ - select: { - width: '100%', - '& + &': { - marginTop: t.spacing(2), - }, - }, -})) - -interface DialogProps { - initialValue: requests.athena.ExecutionContext | null - onChange: (value: requests.athena.ExecutionContext) => void - onClose: () => void - open: boolean -} - -function Dialog({ initialValue, open, onChange, onClose }: DialogProps) { - const classes = useDialogStyles() - const [catalogName, setCatalogName] = - React.useState(initialValue?.catalogName || null) - const [database, setDatabase] = React.useState( - initialValue?.database || null, - ) - const handleSubmit = React.useCallback(() => { - if (!catalogName || !database) return - onChange({ catalogName, database }) - onClose() - }, [catalogName, database, onChange, onClose]) - return ( - - Select data catalog and database - -
- -
- {catalogName && ( -
- -
- )} -
- - - Cancel - - - Submit - - -
- ) -} - -const useChangeButtonStyles = M.makeStyles((t) => ({ +const useStyles = M.makeStyles((t) => ({ root: { alignItems: 'center', display: 'flex', }, + field: { + cursor: 'pointer', + marginRight: t.spacing(2), + width: '50%', + '& input': { + cursor: 'pointer', + }, + '& > *': { + cursor: 'pointer', + }, + }, button: { marginLeft: t.spacing(1), }, })) -interface ChangeButtonProps { - className?: string - database?: requests.athena.Database - onClick: () => void -} - -function ChangeButton({ className, database, onClick }: ChangeButtonProps) { - const classes = useChangeButtonStyles() - return ( - - Use {database ? {database} : 'default'} database or - - {database ? 'change' : 'set'} database - - - ) -} - interface DatabaseProps { className?: string value: requests.athena.ExecutionContext | null - onChange: (value: requests.athena.ExecutionContext) => void + onChange: (value: requests.athena.ExecutionContext | null) => void } export default function Database({ className, value, onChange }: DatabaseProps) { - const [open, setOpen] = React.useState(false) + const classes = useStyles() + const [catalogName, setCatalogName] = + React.useState(value?.catalogName || null) + const handleCatalogName = React.useCallback( + (name) => { + setCatalogName(name) + onChange(null) + }, + [onChange], + ) + const handleDatabase = React.useCallback( + (database) => { + if (!catalogName) return + onChange({ catalogName, database }) + }, + [catalogName, onChange], + ) return ( - <> - setOpen(false)} - open={open} +
+ - setOpen(true)} + - +
) } diff --git a/catalog/app/containers/Bucket/Queries/Athena/QueryEditor.tsx b/catalog/app/containers/Bucket/Queries/Athena/QueryEditor.tsx index eca3ff08d60..584f53ff45f 100644 --- a/catalog/app/containers/Bucket/Queries/Athena/QueryEditor.tsx +++ b/catalog/app/containers/Bucket/Queries/Athena/QueryEditor.tsx @@ -7,6 +7,7 @@ import * as Lab from '@material-ui/lab' import 'ace-builds/src-noconflict/mode-sql' import 'ace-builds/src-noconflict/theme-eclipse' +import { useConfirm } from 'components/Dialog' import Skeleton from 'components/Skeleton' import * as Notifications from 'containers/Notifications' import * as NamedRoutes from 'utils/NamedRoutes' @@ -178,10 +179,23 @@ export { FormSkeleton as Skeleton } const useFormStyles = M.makeStyles((t) => ({ actions: { - alignItems: 'center', - justifyContent: 'space-between', display: 'flex', + justifyContent: 'space-between', margin: t.spacing(2, 0), + [t.breakpoints.up('sm')]: { + alignItems: 'center', + }, + [t.breakpoints.down('sm')]: { + flexDirection: 'column', + }, + }, + database: { + [t.breakpoints.up('sm')]: { + width: '50%', + }, + [t.breakpoints.down('sm')]: { + marginBottom: t.spacing(2), + }, }, error: { margin: t.spacing(1, 0, 0), @@ -209,14 +223,45 @@ export function Form({ bucket, className, onChange, value, workgroup }: FormProp : null, [value], ) + const confirm = useConfirm({ + onSubmit: (confirmed) => { + if (confirmed) { + if (!value?.query) { + throw new Error('Query is not set') + } + onSubmit(value!.query, executionContext) + } + }, + submitTitle: 'Proceed', + title: 'Execution context is not set', + }) const { loading, error, onSubmit } = useQueryRun(bucket, workgroup, value?.id) const handleSubmit = React.useCallback(() => { if (!value?.query) return - onSubmit(value?.query, executionContext) - }, [executionContext, onSubmit, value]) + if (!executionContext) { + return confirm.open() + } + onSubmit(value.query, executionContext) + }, [confirm, executionContext, onSubmit, value]) + const handleExecutionContext = React.useCallback( + (exeContext) => { + if (!exeContext) { + onChange({ ...value, catalog: undefined, db: undefined }) + return + } + const { catalogName, database } = exeContext + onChange({ ...value, catalog: catalogName, db: database }) + }, + [onChange, value], + ) return (
+ {confirm.render( + + Data catalog and database are not set. Run query without them? + , + )} onChange({ ...value, query })} query={value?.query || ''} @@ -230,9 +275,8 @@ export function Form({ bucket, className, onChange, value, workgroup }: FormProp
- onChange({ ...value, catalog: catalogName, db: database }) - } + className={classes.database} + onChange={handleExecutionContext} value={executionContext} />