diff --git a/superset-frontend/images/empty-query.svg b/superset-frontend/images/empty-query.svg new file mode 100644 index 0000000000..be72857d8e --- /dev/null +++ b/superset-frontend/images/empty-query.svg @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/superset-frontend/src/components/ListViewCard/index.tsx b/superset-frontend/src/components/ListViewCard/index.tsx index 26030e0daa..94f14a8e40 100644 --- a/superset-frontend/src/components/ListViewCard/index.tsx +++ b/superset-frontend/src/components/ListViewCard/index.tsx @@ -145,7 +145,7 @@ const SkeletonActions = styled(Skeleton.Button)` const paragraphConfig = { rows: 1, width: 150 }; interface CardProps { - title: React.ReactNode; + title?: React.ReactNode; url?: string; imgURL?: string; imgFallbackURL?: string; @@ -155,7 +155,7 @@ interface CardProps { titleRight?: React.ReactNode; coverLeft?: React.ReactNode; coverRight?: React.ReactNode; - actions: React.ReactNode | null; + actions?: React.ReactNode | null; rows?: number | string; avatar?: string; cover?: React.ReactNode | null; diff --git a/superset-frontend/src/views/CRUD/data/query/QueryList.tsx b/superset-frontend/src/views/CRUD/data/query/QueryList.tsx index 4ed20dea22..ed9329fd1c 100644 --- a/superset-frontend/src/views/CRUD/data/query/QueryList.tsx +++ b/superset-frontend/src/views/CRUD/data/query/QueryList.tsx @@ -20,7 +20,7 @@ import React, { useMemo, useState, useCallback } from 'react'; import { SupersetClient, t, styled } from '@superset-ui/core'; import moment from 'moment'; -import { createErrorHandler } from 'src/views/CRUD/utils'; +import { createErrorHandler, shortenSQL } from 'src/views/CRUD/utils'; import withToasts from 'src/messageToasts/enhancers/withToasts'; import { useListViewResource } from 'src/views/CRUD/hooks'; import SubMenu, { SubMenuProps } from 'src/components/Menu/SubMenu'; @@ -38,6 +38,7 @@ import { QueryObject } from 'src/views/CRUD/types'; import QueryPreviewModal from './QueryPreviewModal'; const PAGE_SIZE = 25; +const SQL_PREVIEW_MAX_LINES = 4; const TopAlignedListView = styled(ListView)>` table .table-cell { @@ -52,15 +53,7 @@ const StyledSyntaxHighlighter = styled(SyntaxHighlighter)` text-overflow: ellipsis; white-space: nowrap; `; -const SQL_PREVIEW_MAX_LINES = 4; -function shortenSQL(sql: string) { - let lines: string[] = sql.split('\n'); - if (lines.length >= SQL_PREVIEW_MAX_LINES) { - lines = lines.slice(0, SQL_PREVIEW_MAX_LINES); - lines.push('...'); - } - return lines.join('\n'); -} + interface QueryListProps { addDangerToast: (msg: string, config?: any) => any; addSuccessToast: (msg: string, config?: any) => any; @@ -298,7 +291,7 @@ function QueryList({ addDangerToast, addSuccessToast }: QueryListProps) { onClick={() => setQueryCurrentlyPreviewing(original)} > - {shortenSQL(original.sql)} + {shortenSQL(original.sql, SQL_PREVIEW_MAX_LINES)} ); diff --git a/superset-frontend/src/views/CRUD/utils.tsx b/superset-frontend/src/views/CRUD/utils.tsx index 2cc99991c9..e22310dd4d 100644 --- a/superset-frontend/src/views/CRUD/utils.tsx +++ b/superset-frontend/src/views/CRUD/utils.tsx @@ -264,22 +264,31 @@ export function handleDashboardDelete( ); } +export function shortenSQL(sql: string, maxLines: number) { + let lines: string[] = sql.split('\n'); + if (lines.length >= maxLines) { + lines = lines.slice(0, maxLines); + lines.push('...'); + } + return lines.join('\n'); +} + const breakpoints = [576, 768, 992, 1200]; export const mq = breakpoints.map(bp => `@media (max-width: ${bp}px)`); export const CardContainer = styled.div` display: grid; - grid-template-columns: repeat(auto-fit, minmax(31%, max-content)); + grid-template-columns: repeat(auto-fit, minmax(31%, 31%)); ${[mq[3]]} { - grid-template-columns: repeat(auto-fit, minmax(31%, max-content)); + grid-template-columns: repeat(auto-fit, minmax(31%, 31%)); } ${[mq[2]]} { - grid-template-columns: repeat(auto-fit, minmax(48%, max-content)); + grid-template-columns: repeat(auto-fit, minmax(48%, 48%)); } ${[mq[1]]} { - grid-template-columns: repeat(auto-fit, minmax(50%, max-content)); + grid-template-columns: repeat(auto-fit, minmax(50%, 80%)); } grid-gap: ${({ theme }) => theme.gridUnit * 8}px; justify-content: left; diff --git a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx index 6de2f3c8e4..6232ec176f 100644 --- a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx +++ b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx @@ -18,6 +18,9 @@ */ import React, { useState } from 'react'; import { t, SupersetClient, styled } from '@superset-ui/core'; +import SyntaxHighlighter from 'react-syntax-highlighter/dist/cjs/light'; +import sql from 'react-syntax-highlighter/dist/cjs/languages/hljs/sql'; +import github from 'react-syntax-highlighter/dist/cjs/styles/hljs/github'; import withToasts from 'src/messageToasts/enhancers/withToasts'; import { Dropdown, Menu } from 'src/common/components'; import { useListViewResource, copyQueryLink } from 'src/views/CRUD/hooks'; @@ -30,9 +33,11 @@ import { IconContainer, CardContainer, createErrorHandler, - CardStyles, + shortenSQL, } from '../utils'; +SyntaxHighlighter.registerLanguage('sql', sql); + const PAGE_SIZE = 3; interface Query { @@ -45,6 +50,8 @@ interface Query { description?: string; end_time?: string; label?: string; + changed_on_delta_humanized?: string; + sql?: string | null; } interface SavedQueriesProps { @@ -57,19 +64,51 @@ interface SavedQueriesProps { mine: Array; } -const QueryData = styled.div` - display: flex; - flex-direction: row; - justify-content: flex-start; - border-bottom: 1px solid ${({ theme }) => theme.colors.grayscale.light2}; - .title { - font-weight: ${({ theme }) => theme.typography.weights.normal}; - color: ${({ theme }) => theme.colors.grayscale.light1}; +export const CardStyles = styled.div` + cursor: pointer; + a { + text-decoration: none; } - .holder { - margin: ${({ theme }) => theme.gridUnit * 2}px; + .ant-card-cover { + border-bottom: 1px solid ${({ theme }) => theme.colors.grayscale.light2}; + & > div { + height: 171px; + } + } + .gradient-container > div { + background-size: contain; + background-repeat: no-repeat; + background-position: center; + background-color: ${({ theme }) => theme.colors.secondary.light3}; + display: inline-block; + width: 100%; + height: 179px; + background-repeat: no-repeat; + vertical-align: middle; } `; + +const QueryData = styled.div` + svg { + margin-left: ${({ theme }) => theme.gridUnit * 10}px; + } + .query-title { + padding: ${({ theme }) => theme.gridUnit * 2 + 2}px; + font-size: ${({ theme }) => theme.typography.sizes.l}px; + } +`; + +const QueryContainer = styled.div` + pre { + height: ${({ theme }) => theme.gridUnit * 40}px; + border: none !important; + background-color: ${({ theme }) => + theme.colors.grayscale.light5} !important; + overflow: hidden; + padding: ${({ theme }) => theme.gridUnit * 4}px !important; + } +`; + const SavedQueries = ({ user, addDangerToast, @@ -259,35 +298,50 @@ const SavedQueries = ({ key={q.id} > - - {t('Tables')} - {q?.sql_tables?.length} - - - {t('Datasource Name')} - {q?.sql_tables && q.sql_tables[0]?.table} - - + q?.sql?.length ? ( + + + {shortenSQL(q.sql, 25)} + + + ) : ( + false + ) } actions={ - { - e.stopPropagation(); - e.preventDefault(); - }} - > - - - - + + { + e.stopPropagation(); + e.preventDefault(); + }} + > + + + + + } />