This is an automated email from the ASF dual-hosted git repository. yjc pushed a commit to branch home-screen-mvp in repository https://gitbox.apache.org/repos/asf/incubator-superset.git
commit 5e2b9a4fbff63072a094338dad605098bd92d608 Author: Phillip Kelley-Dotson <[email protected]> AuthorDate: Wed Sep 23 13:27:37 2020 -0700 more adding and slicing --- .../src/components/ListViewCard/index.tsx | 51 ++-- .../src/views/CRUD/dashboard/DashboardCard.tsx | 168 +++++++++++ .../src/views/CRUD/dashboard/DashboardList.tsx | 92 +----- .../src/views/CRUD/welcome/ChartTable.tsx | 148 +++++++++ .../src/views/CRUD/welcome/DashboardTable.tsx | 329 +++++++-------------- .../src/views/CRUD/welcome/SavedQueries.tsx | 23 +- .../src/views/CRUD/welcome/Welcome.tsx | 125 +++----- superset/queries/api.py | 3 + superset/views/api.py | 2 + 9 files changed, 531 insertions(+), 410 deletions(-) diff --git a/superset-frontend/src/components/ListViewCard/index.tsx b/superset-frontend/src/components/ListViewCard/index.tsx index 4a0cf53..f07970c 100644 --- a/superset-frontend/src/components/ListViewCard/index.tsx +++ b/superset-frontend/src/components/ListViewCard/index.tsx @@ -152,6 +152,8 @@ interface CardProps { coverLeft?: React.ReactNode; coverRight?: React.ReactNode; actions: React.ReactNode; + showImg: boolean; + rows?: number | string; } function ListViewCard({ @@ -166,31 +168,40 @@ function ListViewCard({ actions, loading, imgPosition = 'top', + showImg = true, + rows, }: CardProps) { return ( <StyledCard data-test="styled-card" cover={ - <Cover> - <a href={url}> - <div className="gradient-container"> - <ImageLoader - src={imgURL} - fallback={imgFallbackURL} - isLoading={loading} - position={imgPosition} - /> - </div> - </a> - <CoverFooter className="cover-footer"> - {!loading && coverLeft && ( - <CoverFooterLeft>{coverLeft}</CoverFooterLeft> - )} - {!loading && coverRight && ( - <CoverFooterRight>{coverRight}</CoverFooterRight> - )} - </CoverFooter> - </Cover> + showImg ? ( + <Cover> + <a href={url}> + <div className="gradient-container"> + <ImageLoader + src={imgURL} + fallback={imgFallbackURL} + isLoading={loading} + position={imgPosition} + /> + </div> + </a> + <CoverFooter className="cover-footer"> + {!loading && coverLeft && ( + <CoverFooterLeft>{coverLeft}</CoverFooterLeft> + )} + {!loading && coverRight && ( + <CoverFooterRight>{coverRight}</CoverFooterRight> + )} + </CoverFooter> + </Cover> + ) : ( + <> + <div>Rows</div> + <div>{rows}</div> + </> + ) } > {loading && ( diff --git a/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx b/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx new file mode 100644 index 0000000..46d3088 --- /dev/null +++ b/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx @@ -0,0 +1,168 @@ +import React from 'react'; +import { SupersetClient, t } from '@superset-ui/core'; +import rison from 'rison'; +import { Dropdown, Menu } from 'src/common/components'; +import ConfirmStatusChange from 'src/components/ConfirmStatusChange'; +import ListViewCard from 'src/components/ListViewCard'; +import Icon from 'src/components/Icon'; +import Label from 'src/components/Label'; +import FacePile from 'src/components/FacePile'; +import FaveStar from 'src/components/FaveStar'; +import Owner from 'src/types/Owner'; + +import { createErrorHandler } from 'src/views/CRUD/utils'; +import { useFavoriteStatus } from 'src/views/CRUD/hooks'; + +const FAVESTAR_BASE_URL = '/superset/favstar/Dashboard'; + +interface Dashboard { + changed_by_name: string; + changed_by_url: string; + changed_on_delta_humanized: string; + changed_by: string; + dashboard_title: string; + slice_name: string; + id: number; + published: boolean; + url: string; + thumbnail_url: string; + owners: Owner[]; + loading: boolean; +} + +interface DashboardCardProps { + isChart?: boolean; + dashboard: Dashboard; + hasPerm: (name: string) => boolean; + bulkSelectEnabled: boolean; + refreshData: () => void; + addDangerToast: (msg: string) => void; + addSuccessToast: (msg: string) => void; + openDashboardEditModal?: (d: Dashboard) => void; +} + +function DashboardCard({ + isChart, + dashboard, + hasPerm, + bulkSelectEnabled, + refreshData, + addDangerToast, + addSuccessToast, + openDashboardEditModal, +}: DashboardCardProps) { + const canEdit = hasPerm('can_edit'); + const canDelete = hasPerm('can_delete'); + const canExport = hasPerm('can_mulexport'); + const [favoriteStatusRef, fetchFaveStar, saveFaveStar] = useFavoriteStatus( + {}, + FAVESTAR_BASE_URL, + addDangerToast, + ); + + function handleDashboardDelete({ + id, + dashboard_title: dashboardTitle, + }: Dashboard) { + return SupersetClient.delete({ + endpoint: `/api/v1/dashboard/${id}`, + }).then( + () => { + refreshData(); + addSuccessToast(t('Deleted: %s', dashboardTitle)); + }, + createErrorHandler(errMsg => + addDangerToast( + t('There was an issue deleting %s: %s', dashboardTitle, errMsg), + ), + ), + ); + } + + function handleBulkDashboardExport(dashboardsToExport: Dashboard[]) { + return window.location.assign( + `/api/v1/dashboard/export/?q=${rison.encode( + dashboardsToExport.map(({ id }) => id), + )}`, + ); + } + + const cardTitle = isChart ? dashboard.slice_name : dashboard.dashboard_title; + + const menu = ( + <Menu> + {canDelete && ( + <Menu.Item> + <ConfirmStatusChange + title={t('Please Confirm')} + description={ + <> + {t('Are you sure you want to delete')} <b>{cardTitle}</b>? + </> + } + onConfirm={() => handleDashboardDelete(dashboard)} + > + {confirmDelete => ( + <div + role="button" + tabIndex={0} + className="action-button" + onClick={confirmDelete} + > + <ListViewCard.MenuIcon name="trash" /> Delete + </div> + )} + </ConfirmStatusChange> + </Menu.Item> + )} + {canExport && ( + <Menu.Item + role="button" + tabIndex={0} + onClick={() => handleBulkDashboardExport([dashboard])} + > + <ListViewCard.MenuIcon name="share" /> Export + </Menu.Item> + )} + {canEdit && openDashboardEditModal && ( + <Menu.Item + role="button" + tabIndex={0} + onClick={() => + openDashboardEditModal && openDashboardEditModal(dashboard) + } + > + <ListViewCard.MenuIcon name="pencil" /> Edit + </Menu.Item> + )} + </Menu> + ); + return ( + <ListViewCard + loading={dashboard.loading} + title={dashboard.dashboard_title} + titleRight={<Label>{dashboard.published ? 'published' : 'draft'}</Label>} + url={bulkSelectEnabled ? undefined : dashboard.url} + imgURL={dashboard.thumbnail_url} + imgFallbackURL="/static/assets/images/dashboard-card-fallback.png" + description={t('Last modified %s', dashboard.changed_on_delta_humanized)} + coverLeft={<FacePile users={dashboard.owners || []} />} + actions={ + <ListViewCard.Actions> + <FaveStar + itemId={dashboard.id} + fetchFaveStar={fetchFaveStar} + saveFaveStar={saveFaveStar} + isStarred={!!favoriteStatusRef.current[dashboard.id]} + /> + <Dropdown overlay={menu}> + <Icon name="more-horiz" /> + </Dropdown> + </ListViewCard.Actions> + } + showImg + /> + ); +} + +export default DashboardCard; diff --git a/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx b/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx index ac4d747..1fe8427 100644 --- a/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx +++ b/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx @@ -24,17 +24,15 @@ import { createFetchRelated, createErrorHandler } from 'src/views/CRUD/utils'; import { useListViewResource, useFavoriteStatus } from 'src/views/CRUD/hooks'; import ConfirmStatusChange from 'src/components/ConfirmStatusChange'; import SubMenu, { SubMenuProps } from 'src/components/Menu/SubMenu'; -import FacePile from 'src/components/FacePile'; import ListView, { ListViewProps, Filters } from 'src/components/ListView'; import Owner from 'src/types/Owner'; import withToasts from 'src/messageToasts/enhancers/withToasts'; +import FacePile from 'src/components/FacePile'; import Icon from 'src/components/Icon'; -import Label from 'src/components/Label'; import FaveStar from 'src/components/FaveStar'; import PropertiesModal from 'src/dashboard/components/PropertiesModal'; -import ListViewCard from 'src/components/ListViewCard'; -import { Dropdown, Menu } from 'src/common/components'; import TooltipWrapper from 'src/components/TooltipWrapper'; +import DashboardCard from './DashboardCard'; const PAGE_SIZE = 25; const FAVESTAR_BASE_URL = '/superset/favstar/Dashboard'; @@ -81,7 +79,6 @@ function DashboardList(props: DashboardListProps) { FAVESTAR_BASE_URL, props.addDangerToast, ); - const [dashboardToEdit, setDashboardToEdit] = useState<Dashboard | null>( null, ); @@ -419,82 +416,17 @@ function DashboardList(props: DashboardListProps) { ]; function renderCard(dashboard: Dashboard & { loading: boolean }) { - const menu = ( - <Menu> - {canDelete && ( - <Menu.Item> - <ConfirmStatusChange - title={t('Please Confirm')} - description={ - <> - {t('Are you sure you want to delete')}{' '} - <b>{dashboard.dashboard_title}</b>? - </> - } - onConfirm={() => handleDashboardDelete(dashboard)} - > - {confirmDelete => ( - <div - role="button" - tabIndex={0} - className="action-button" - onClick={confirmDelete} - > - <ListViewCard.MenuIcon - data-test="dashboard-list-view-card-trash-icon" - name="trash" - />{' '} - Delete - </div> - )} - </ConfirmStatusChange> - </Menu.Item> - )} - {canExport && ( - <Menu.Item - role="button" - tabIndex={0} - onClick={() => handleBulkDashboardExport([dashboard])} - > - <ListViewCard.MenuIcon name="share" /> Export - </Menu.Item> - )} - {canEdit && ( - <Menu.Item - data-test="dashboard-list-edit-option" - role="button" - tabIndex={0} - onClick={() => openDashboardEditModal(dashboard)} - > - <ListViewCard.MenuIcon name="edit-alt" /> Edit - </Menu.Item> - )} - </Menu> - ); - return ( - <ListViewCard - loading={dashboard.loading} - title={dashboard.dashboard_title} - titleRight={ - <Label>{dashboard.published ? 'published' : 'draft'}</Label> - } - url={bulkSelectEnabled ? undefined : dashboard.url} - imgURL={dashboard.thumbnail_url} - imgFallbackURL="/static/assets/images/dashboard-card-fallback.png" - description={t( - 'Last modified %s', - dashboard.changed_on_delta_humanized, - )} - coverLeft={<FacePile users={dashboard.owners || []} />} - actions={ - <ListViewCard.Actions> - {renderFaveStar(dashboard.id)} - <Dropdown overlay={menu}> - <Icon name="more-horiz" /> - </Dropdown> - </ListViewCard.Actions> - } + <DashboardCard + {...{ + dashboard, + hasPerm, + bulkSelectEnabled, + refreshData, + addDangerToast: props.addDangerToast, + addSuccessToast: props.addSuccessToast, + openDashboardEditModal, + }} /> ); } diff --git a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx new file mode 100644 index 0000000..9b8529e --- /dev/null +++ b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx @@ -0,0 +1,148 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React, { useEffect } from 'react'; +import { t } from '@superset-ui/core'; +import { debounce } from 'lodash'; +import ListView, { FetchDataConfig } from 'src/components/ListView'; +import { useListViewResource, useFavoriteStatus } from 'src/views/CRUD/hooks'; +import withToasts from 'src/messageToasts/enhancers/withToasts'; +import { User } from 'src/types/bootstrapTypes'; +import ListViewCard from 'src/components/ListViewCard'; +import { Dropdown, Menu } from 'src/common/components'; +import Icon from 'src/components/Icon'; +import Label from 'src/components/Label'; +import Owner from 'src/types/Owner'; +import ConfirmStatusChange from 'src/components/ConfirmStatusChange'; +import DashboardCard from '../dashboard/DashboardCard'; +const PAGE_SIZE = 3; + +interface ChartTableProps { + addDangerToast: (message: string) => void; + addSuccessToast: (message: string) => void; + search: string; + chartFilter?: string; + user?: User; +} + +interface Dashboard { + changed_by_name: string; + changed_by_url: string; + changed_on_delta_humanized: string; + changed_by: string; + dashboard_title: string; + slice_name: string; + id: number; + published: boolean; + url: string; + thumbnail_url: string; + owners: Owner[]; + loading: boolean; +} + +interface ChartTableState { + charts: Dashboard[]; + chart_count: number; + loading: boolean; +} + +export interface FilterValue { + col: string; + operator: string; + value: string | boolean | number | null | undefined; +} + +export interface FetchDataConfig { + filters: FilterValue[]; +} + +function ChartTable({ + chartFilter, + user, + addDangerToast, + addSuccessToast, +}: ChartTableProps) { + const { + state: { loading, resourceCollection: charts, bulkSelectEnabled }, + hasPerm, + refreshData, + fetchData, + } = useListViewResource<Dashboard>('chart', t('chart'), addDangerToast); + console.log('dashboardFilter', chartFilter); + const getFilters = () => { + const filters = []; + + if (chartFilter === 'Mine') { + filters.push({ + id: 'owners', + operator: 'rel_m_m', + value: `${user?.userId}`, + }); + } else { + filters.push({ + id: 'favorite', // API currently can't filter by favorite + operator: 'eq', + value: true, + }); + } + // Do we need search? + /* filters.concat([ + { + id: 'dashboard_title', + operator: 'ct', + value: search, + }, + ]); + */ + return filters; + }; + + useEffect(() => { + fetchData({ + pageIndex: 0, + pageSize: PAGE_SIZE, + sortBy: [ + { + id: 'changed_on_delta_humanized', + desc: true, + }, + ], + filters: getFilters(), + }); + }, [chartFilter]); + console.log("----charts: ", charts); + return ( + <div> + {charts.map(e => ( + <DashboardCard + {...{ + dashboard: e, + hasPerm, + bulkSelectEnabled, + refreshData, + addDangerToast, + addSuccessToast, + }} + isChart + /> + ))} + </div> + ); +} + +export default withToasts(ChartTable); diff --git a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx index f0560e4..39c5503 100644 --- a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx +++ b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx @@ -16,27 +16,46 @@ * specific language governing permissions and limitations * under the License. */ -import React from 'react'; -import { t, SupersetClient } from '@superset-ui/core'; +import React, { useEffect } from 'react'; +import { t } from '@superset-ui/core'; import { debounce } from 'lodash'; import ListView, { FetchDataConfig } from 'src/components/ListView'; +import { useListViewResource, useFavoriteStatus } from 'src/views/CRUD/hooks'; import withToasts from 'src/messageToasts/enhancers/withToasts'; +import { User } from 'src/types/bootstrapTypes'; import ListViewCard from 'src/components/ListViewCard'; -import { Dashboard } from 'src/types/bootstrapTypes'; import { Dropdown, Menu } from 'src/common/components'; import Icon from 'src/components/Icon'; import Label from 'src/components/Label'; +import Owner from 'src/types/Owner'; import ConfirmStatusChange from 'src/components/ConfirmStatusChange'; - +import DashboardCard from '../dashboard/DashboardCard'; const PAGE_SIZE = 3; -//const canEdit = hasPerm('can_edit'); -//const canDelete = hasPerm('can_delete'); -//const canExport = hasPerm('can_mulexport'); +// const canEdit = hasPerm('can_edit'); +// const canDelete = hasPerm('can_delete'); +// const canExport = hasPerm('can_mulexport'); interface DashboardTableProps { addDangerToast: (message: string) => void; - search?: string; + addSuccessToast: (message: string) => void; + search: string; + dashboardFilter?: string; + user?: User; +} + +interface Dashboard { + changed_by_name: string; + changed_by_url: string; + changed_on_delta_humanized: string; + changed_by: string; + dashboard_title: string; + id: number; + published: boolean; + url: string; + thumbnail_url: string; + owners: Owner[]; + loading: boolean; } interface DashboardTableState { @@ -45,232 +64,90 @@ interface DashboardTableState { loading: boolean; } -class DashboardTable extends React.PureComponent< - DashboardTableProps, - DashboardTableState -> { - columns = [ - { - accessor: 'dashboard_title', - Header: 'Dashboard', - Cell: ({ - row: { - original: { url, dashboard_title: dashboardTitle }, - }, - }: { - row: { - original: { - url: string; - dashboard_title: string; - }; - }; - }) => <a href={url}>{dashboardTitle}</a>, - }, - { - accessor: 'changed_by.first_name', - Header: 'Modified By', - Cell: ({ - row: { - original: { changed_by_name: changedByName, changedByUrl }, - }, - }: { - row: { - original: { - changed_by_name: string; - changedByUrl: string; - }; - }; - }) => <a href={changedByUrl}>{changedByName}</a>, - }, - { - accessor: 'changed_on_delta_humanized', - Header: 'Modified', - Cell: ({ - row: { - original: { changed_on_delta_humanized: changedOn }, - }, - }: { - row: { - original: { - changed_on_delta_humanized: string; - }; - }; - }) => <span className="no-wrap">{changedOn}</span>, - }, - ]; - - initialSort = [{ id: 'changed_on_delta_humanized', desc: true }]; +export interface FilterValue { + col: string; + operator: string; + value: string | boolean | number | null | undefined; +} - constructor(props: DashboardTableProps) { - super(props); - this.state = { - dashboards: [], - dashboard_count: 0, - loading: false, - }; - } +export interface FetchDataConfig { + filters: FilterValue[]; +} - componentDidMount() { - console.log('component did mount!!!'); - this.fetchDataDebounced({ - pageSize: PAGE_SIZE, - pageIndex: 0, - sortBy: this.initialSort, - filters: [], - }); - } +function DashboardTable({ + dashboardFilter, + user, + addDangerToast, + addSuccessToast, + search, +}: DashboardTableProps) { + const { + state: { loading, resourceCollection: dashboards, bulkSelectEnabled }, + hasPerm, + refreshData, + fetchData, + } = useListViewResource<Dashboard>( + 'dashboard', + t('dashboard'), + addDangerToast, + ); + console.log('dashboardFilter', dashboardFilter); + const getFilters = () => { + const filters = []; - componentDidUpdate(prevProps: DashboardTableProps) { - if (prevProps.search !== this.props.search) { - this.fetchDataDebounced({ - pageSize: PAGE_SIZE, - pageIndex: 0, - sortBy: this.initialSort, - filters: [], + if (dashboardFilter === 'Mine') { + filters.push({ + id: 'owners', + operator: 'rel_m_m', + value: `${user?.userId}`, + }); + } else { + filters.push({ + id: 'favorite', // API currently can't filter by favorite + operator: 'eq', + value: true, }); } - } - - fetchData = ({ pageIndex, pageSize, sortBy, filters }: FetchDataConfig) => { - this.setState({ loading: true }); - const filterExps = Object.keys(filters) - .map(fk => ({ - col: fk, - opr: filters[fk].filterId, - value: filters[fk].filterValue, - })) - .concat( - this.props.search - ? [ - { - col: 'dashboard_title', - opr: 'ct', - value: this.props.search, - }, - ] - : [], - ); - - /*const { dashboardFilter, user} = this.props; - const filters = []; + filters.concat([ + { + id: 'dashboard_title', + operator: 'ct', + value: search, + }, + ]); + return filters; + }; - if (dashboardFilter === "Mine") { - filters.push[{ - { - col: "owners.id", // API does not allow filter by owner id - opr: "eq", - value: user.id - } - }]; - } else { - filter.push[{ + useEffect(() => { + fetchData({ + pageIndex: 0, + pageSize: PAGE_SIZE, + sortBy: [ { - col: "favorite", // API currently can't filter by favorite - opr: "eq", - value: true - } - }]; - }*/ - - /*const menu = ( - <Menu> - {canDelete && ( - <Menu.Item> - <ConfirmStatusChange - title={t('Please Confirm')} - description={ - <> - {t('Are you sure you want to delete')}{' '} - </> - } - onConfirm={() => handleDashboardDelete(dashboard)} - > - {confirmDelete => ( - <div - role="button" - tabIndex={0} - className="action-button" - onClick={confirmDelete} - > - <ListViewCard.MenuIcon name="trash" /> Delete - </div> - )} - </ConfirmStatusChange> - </Menu.Item> - )} - {canExport && ( - <Menu.Item - role="button" - tabIndex={0} - onClick={() => handleBulkDashboardExport([dashboard])} - > - <ListViewCard.MenuIcon name="share" /> Export - </Menu.Item> - )} - {canEdit && ( - <Menu.Item - role="button" - tabIndex={0} - onClick={() => openDashboardEditModal(dashboard)} - > - <ListViewCard.MenuIcon name="pencil" /> Edit - </Menu.Item> - )} - </Menu> - ); */ - - console.log('sortBy', sortBy); - const queryParams = JSON.stringify({ - order_column: sortBy[0].id, - order_direction: sortBy[0].desc ? 'desc' : 'asc', - page: pageIndex, - page_size: pageSize, - //filters, - ...(filterExps.length ? { filters: filterExps } : {}), + id: 'changed_on_delta_humanized', + desc: true, + }, + ], + filters: getFilters(), }); - console.log('hello!!!'); - return SupersetClient.get({ - endpoint: `/api/v1/dashboard/?q=${queryParams}`, - }) - .then(({ json }) => { - console.log('json', json); - this.setState({ dashboards: json.result, dashboard_count: json.count }); - console.log('this.state', this.state); - }) - .catch(response => { - if (response.status === 401) { - this.props.addDangerToast( - t( - "You don't have the necessary permissions to load dashboards. Please contact your administrator.", - ), - ); - } else { - this.props.addDangerToast( - t('An error occurred while fetching Dashboards'), - ); - } - }) - .finally(() => this.setState({ loading: false })); - }; - - // sort-comp disabled because of conflict with no-use-before-define rule - // eslint-disable-next-line react/sort-comp - fetchDataDebounced = debounce(this.fetchData, 200); - - render() { - return ( - <div> - {this.state.dashboards.map(e => ( - <ListViewCard - title={e.dashboard_title} - loading={this.state.loading} - titleRight={<Label>{e.published ? 'published' : 'draft'}</Label>} - description={t('Last modified %s', e.changed_on_delta_humanized)} - /> - ))} - </div> - ); - } + }, [dashboardFilter]); + + return ( + <div> + {dashboards.map(e => ( + <DashboardCard + {...{ + dashboard: e, + hasPerm, + bulkSelectEnabled, + refreshData, + addDangerToast, + addSuccessToast, + }} + /> + ))} + </div> + ); } export default withToasts(DashboardTable); diff --git a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx index 62e9e0e..7f261cc 100644 --- a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx +++ b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx @@ -29,32 +29,43 @@ class SavedQueries extends React.PureComponent { queries: [], }; } - componentDidMount(){ + componentDidMount() { this.fetchData(); } - fetchData = () => { + fetchData = async () => { try { - const { json } = SupersetClient.get({ + const ids = []; + const { json } = await SupersetClient.get({ endpoint: `/api/v1/query/`, }); + /*(json.ids.forEach(id => { + const { json } = SupersetClient.get({ + endpoint: `/api/v1/query/${id}`, + }); + console.log('data', json); + });*/ + console.log('json.result', json); this.setState({ queries: json.result }); } catch (e) { return console.log(e); } }; render() { + console.log('q', this.state.queries) return ( <div> {this.state.queries.map(q => ( <ListViewCard - title={q.database_name} + title={q.database.database_name} + rows={q.rows} loading={false} description={t('Last run ', q.end_time)} + showImg={false} /> ))} </div> - ) + ); } } -export default SavedQueries; \ No newline at end of file +export default SavedQueries; diff --git a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx index 5e311dd..337b4e1 100644 --- a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx +++ b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx @@ -31,6 +31,7 @@ import { useQueryParam, StringParam, QueryParamConfig } from 'use-query-params'; import { User } from 'src/types/bootstrapTypes'; import RecentActivity from 'src/profile/components/RecentActivity'; import Favorites from 'src/profile/components/Favorites'; +import ChartTable from './ChartTable'; import SavedQueries from './SavedQueries'; import DashboardTable from './DashboardTable'; @@ -60,7 +61,7 @@ function useSyncQueryState( } function ding(e: any) { - console.log('event',e); + console.log('event', e); } export default function Welcome({ user }: WelcomeProps) { @@ -70,24 +71,13 @@ export default function Welcome({ user }: WelcomeProps) { 'all', ); const [dashboardFilter, setDashboardFilter] = useState('Favorite'); + const [chartFilter, setChartFilter] = useState('Favorite'); const [searchQuery, setSearchQuery] = useSyncQueryState( 'search', StringParam, '', ); - - const onFormControlChange = useCallback( - (e: React.FormEvent<FormControl & FormControlProps>) => { - const { value } = e.currentTarget; - setSearchQuery((value as string) ?? ''); - }, - [], - ); - - const onTabsSelect = useCallback((e: any) => { - setActiveTab(e as string); - }, []); - + console.log('user', user); return ( <Collapse defaultActiveKey={['1']}> <Panel header={t('Recents')} key="1"> @@ -117,7 +107,7 @@ export default function Welcome({ user }: WelcomeProps) { </Panel> <Panel header={t('Dashboards')} key="2"> - <SubMenu + <SubMenu activeChild={dashboardFilter} name="" // eslint-disable-next-line react/no-children-prop @@ -142,74 +132,53 @@ export default function Welcome({ user }: WelcomeProps) { // @ts-ignore React bootstrap types aren't quite right here onChange={e => setSearchQuery(e.currentTarget.value)} /> - <DashboardTable search={searchQuery} filter={dashboardFilter} /> + <DashboardTable + search={searchQuery} + dashboardFilter={dashboardFilter} + user={user} + /> </Panel> - <Panel header={t('Saved Queries')} key="3" > + <Panel header={t('Saved Queries')} key="3"> + <SubMenu + activeChild={dashboardFilter} + name="" + // eslint-disable-next-line react/no-children-prop + children={[ + { + name: 'Favorite', + label: t('Favorite'), + onClick: () => setChartFilter('Favorite'), + }, + { + name: 'Mine', + label: t('Mine'), + onClick: () => setChartFilter('Mine'), + }, + ]} + /> <SavedQueries /> </Panel> - <Panel header={t('Charts')} key="4" > - Stuff here! + <Panel header={t('Charts')} key="4"> + <SubMenu + activeChild={chartFilter} + name="" + // eslint-disable-next-line react/no-children-prop + children={[ + { + name: 'Favorite', + label: t('Favorite'), + onClick: () => setChartFilter('Favorite'), + }, + { + name: 'Mine', + label: t('Mine'), + onClick: () => setChartFilter('Mine'), + }, + ]} + /> + <ChartTable chartFilter={chartFilter} user={user} /> </Panel> </Collapse> - /* - <div className="container welcome"> - <Tabs - activeKey={activeTab} - onSelect={onTabsSelect} - id="uncontrolled-tab-example" - > - <Tab eventKey="all" title={t('Dashboards')}> - <Panel> - <Panel.Body> - <Row> - <Col md={8}> - <h2>{t('Dashboards')}</h2> - </Col> - <Col md={4}> - <FormControl - type="text" - bsSize="sm" - style={{ marginTop: '25px' }} - placeholder="Search" - value={searchQuery} - onChange={onFormControlChange} - /> - </Col> - </Row> - <hr /> - <DashboardTable search={searchQuery} /> - </Panel.Body> - </Panel> - </Tab> - <Tab eventKey="recent" title={t('Recently Viewed')}> - <Panel> - <Panel.Body> - <Row> - <Col md={8}> - <h2>{t('Recently Viewed')}</h2> - </Col> - </Row> - <hr /> - <RecentActivity user={user} /> - </Panel.Body> - </Panel> - </Tab> - <Tab eventKey="favorites" title={t('Favorites')}> - <Panel> - <Panel.Body> - <Row> - <Col md={8}> - <h2>{t('Favorites')}</h2> - </Col> - </Row> - <hr /> - <Favorites user={user} /> - </Panel.Body> - </Panel> - </Tab> - </Tabs> - </div> - */ ); } diff --git a/superset/queries/api.py b/superset/queries/api.py index 0f368d3..ce4fca5 100644 --- a/superset/queries/api.py +++ b/superset/queries/api.py @@ -42,6 +42,9 @@ class QueryRestApi(BaseSupersetModelRestApi): "status", "start_time", "end_time", + "rows", + "tmp_table_name", + "tracking_url", ] show_columns = [ "client_id", diff --git a/superset/views/api.py b/superset/views/api.py index a5090b3..19686a6 100644 --- a/superset/views/api.py +++ b/superset/views/api.py @@ -42,9 +42,11 @@ class Api(BaseSupersetView): raises SupersetSecurityException: If the user cannot access the resource """ + print("hello") query_context = QueryContext(**json.loads(request.form["query_context"])) query_context.raise_for_access() payload_json = query_context.get_payload() + print("playload", payload_json) return json.dumps( payload_json, default=utils.json_int_dttm_ser, ignore_nan=True )
