From 7e5e77e82a406ec6ea3427f96344f04640b3ac18 Mon Sep 17 00:00:00 2001 From: Phillip Kelley-Dotson Date: Fri, 25 Feb 2022 14:20:24 -0800 Subject: [PATCH 01/17] feat: allow uploads in crud view --- .../views/CRUD/data/database/DatabaseList.tsx | 49 ++++++++++++++++++- .../src/views/components/Menu.tsx | 2 +- .../src/views/components/SubMenu.tsx | 32 +++++++++++- 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx index 08f75a6bac5e5..53ff72189afde 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx @@ -18,10 +18,14 @@ */ import { SupersetClient, t, styled } from '@superset-ui/core'; import React, { useState, useMemo } from 'react'; +import { useSelector } from 'react-redux'; import Loading from 'src/components/Loading'; import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags'; import { useListViewResource } from 'src/views/CRUD/hooks'; -import { createErrorHandler } from 'src/views/CRUD/utils'; +import { + createErrorHandler, + checkUploadExtensions, +} from 'src/views/CRUD/utils'; import withToasts from 'src/components/MessageToasts/withToasts'; import SubMenu, { SubMenuProps } from 'src/views/components/SubMenu'; import DeleteModal from 'src/components/DeleteModal'; @@ -31,6 +35,7 @@ import ListView, { FilterOperator, Filters } from 'src/components/ListView'; import { commonMenuData } from 'src/views/CRUD/data/common'; import ImportModelsModal from 'src/components/ImportModal/index'; import handleResourceExport from 'src/utils/export'; +import { ExtentionConfigs } from 'src/views/components/types'; import DatabaseModal from './DatabaseModal'; import { DatabaseObject } from './types'; @@ -103,6 +108,12 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) { const [importingDatabase, showImportModal] = useState(false); const [passwordFields, setPasswordFields] = useState([]); const [preparingExport, setPreparingExport] = useState(false); + const { + CSV_EXTENSIONS, + COLUMNAR_EXTENSIONS, + EXCEL_EXTENSIONS, + ALLOWED_EXTENSIONS, + } = useSelector(state => state.common.conf); const openDatabaseImportModal = () => { showImportModal(true); @@ -171,8 +182,43 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) { const canExport = hasPerm('can_export') && isFeatureEnabled(FeatureFlag.VERSIONED_EXPORT); + const uploadDropdownMenu = [ + { + label: t('Upload file to database'), + childs: [ + { + label: t('Upload a CSV'), + name: 'Upload a CSV', + url: '/csvtodatabaseview/form', + perm: CSV_EXTENSIONS, + }, + { + label: t('Upload a Columnar File'), + name: 'Upload a Columnar file', + url: '/columnartodatabaseview/form', + perm: COLUMNAR_EXTENSIONS, + }, + { + label: t('Upload Excel'), + name: 'Upload Excel', + url: '/exceltodatabaseview/form', + perm: EXCEL_EXTENSIONS, + }, + ], + }, + ]; + + const filteredDropDown = uploadDropdownMenu.map(link => { + // eslint-disable-next-line no-param-reassign + link.childs = link.childs.filter(item => + checkUploadExtensions(item.perm, ALLOWED_EXTENSIONS), + ); + return link; + }); + const menuData: SubMenuProps = { activeChild: 'Databases', + dropDownLinks: filteredDropDown, ...commonMenuData, }; @@ -222,6 +268,7 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) { } const initialSort = [{ id: 'changed_on_delta_humanized', desc: true }]; + const columns = useMemo( () => [ { diff --git a/superset-frontend/src/views/components/Menu.tsx b/superset-frontend/src/views/components/Menu.tsx index 313c4b5cac071..31276b4c0c3e9 100644 --- a/superset-frontend/src/views/components/Menu.tsx +++ b/superset-frontend/src/views/components/Menu.tsx @@ -72,7 +72,7 @@ export interface MenuProps { isFrontendRoute?: (path?: string) => boolean; } -interface MenuObjectChildProps { +export interface MenuObjectChildProps { label: string; name?: string; icon?: string; diff --git a/superset-frontend/src/views/components/SubMenu.tsx b/superset-frontend/src/views/components/SubMenu.tsx index 917adfab703f2..55a4719a07600 100644 --- a/superset-frontend/src/views/components/SubMenu.tsx +++ b/superset-frontend/src/views/components/SubMenu.tsx @@ -21,8 +21,15 @@ import { Link, useHistory } from 'react-router-dom'; import { styled } from '@superset-ui/core'; import cx from 'classnames'; import { debounce } from 'lodash'; -import { Menu, MenuMode, Row } from 'src/common/components'; +import { + Menu, + MenuMode, + Row, + MainNav as DropdownMenu, +} from 'src/common/components'; import Button, { OnClickHandler } from 'src/components/Button'; +import Icons from 'src/components/Icons'; +import { MenuObjectChildProps, MenuObjectProps } from './Menu'; const StyledHeader = styled.div` margin-bottom: ${({ theme }) => theme.gridUnit * 4}px; @@ -38,11 +45,17 @@ const StyledHeader = styled.div` .nav-right { display: flex; align-items: center; - padding: 14px 0; + padding: 20px 0; margin-right: ${({ theme }) => theme.gridUnit * 3}px; float: right; position: absolute; right: 0; + ul.ant-menu-root { + padding: 0px; + .ant-menu-submenu-title > span { + top: ${({ theme }) => theme.gridUnit * 1.5}px; + } + } } .nav-right-collapse { display: flex; @@ -151,8 +164,11 @@ export interface SubMenuProps { * otherwise, a 'You should not use outside a ' error will be thrown */ usesRouter?: boolean; color?: string; + dropDownLinks?: Array; } +const { SubMenu } = DropdownMenu; + const SubMenuComponent: React.FunctionComponent = props => { const [showMenu, setMenu] = useState('horizontal'); const [navRightStyle, setNavRightStyle] = useState('nav-right'); @@ -176,6 +192,7 @@ const SubMenuComponent: React.FunctionComponent = props => { props.buttons.length >= 3 && window.innerWidth >= 795 ) { + // eslint-disable-next-line no-unused-expressions setNavRightStyle('nav-right'); } else if ( props.buttons && @@ -230,6 +247,17 @@ const SubMenuComponent: React.FunctionComponent = props => { })}
+ + {props.dropDownLinks?.map((link, i) => ( + }> + {link.childs?.map(item => ( + + {item.label} + + ))} + + ))} + {props.buttons?.map((btn, i) => (