feat(welcome): make examples tab customizable (#22302)
This commit is contained in:
parent
7a94f3afc6
commit
b954f8f560
|
|
@ -16,6 +16,8 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { BootstrapData, CommonBootstrapData } from './types/bootstrapTypes';
|
||||
|
||||
export const DATETIME_WITH_TIME_ZONE = 'YYYY-MM-DD HH:mm:ssZ';
|
||||
export const TIME_WITH_MS = 'HH:mm:ss.SSS';
|
||||
|
||||
|
|
@ -141,3 +143,55 @@ export const SLOW_DEBOUNCE = 500;
|
|||
* Display null as `N/A`
|
||||
*/
|
||||
export const NULL_DISPLAY = 'N/A';
|
||||
|
||||
export const DEFAULT_COMMON_BOOTSTRAP_DATA: CommonBootstrapData = {
|
||||
flash_messages: [],
|
||||
conf: {},
|
||||
locale: 'en',
|
||||
feature_flags: {},
|
||||
language_pack: {
|
||||
domain: '',
|
||||
locale_data: {
|
||||
superset: {
|
||||
'': {
|
||||
domain: 'superset',
|
||||
lang: 'en',
|
||||
plural_forms: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
extra_categorical_color_schemes: [],
|
||||
extra_sequential_color_schemes: [],
|
||||
theme_overrides: {},
|
||||
menu_data: {
|
||||
menu: [],
|
||||
brand: {
|
||||
path: '',
|
||||
icon: '',
|
||||
alt: '',
|
||||
tooltip: '',
|
||||
text: '',
|
||||
},
|
||||
navbar_right: {
|
||||
show_watermark: true,
|
||||
languages: {},
|
||||
show_language_picker: true,
|
||||
user_is_anonymous: false,
|
||||
user_info_url: '',
|
||||
user_login_url: '',
|
||||
user_logout_url: '',
|
||||
user_profile_url: '',
|
||||
locale: '',
|
||||
},
|
||||
settings: [],
|
||||
environment_tag: {
|
||||
text: '',
|
||||
color: '',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const DEFAULT_BOOTSTRAP_DATA: BootstrapData = {
|
||||
common: DEFAULT_COMMON_BOOTSTRAP_DATA,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* 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
|
||||
* "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
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* 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
|
||||
* "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
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import { FeatureFlagMap, FeatureFlag } from '@superset-ui/core';
|
|||
export { FeatureFlag } from '@superset-ui/core';
|
||||
export type { FeatureFlagMap } from '@superset-ui/core';
|
||||
|
||||
export function initFeatureFlags(featureFlags: FeatureFlagMap) {
|
||||
export function initFeatureFlags(featureFlags?: FeatureFlagMap) {
|
||||
if (!window.featureFlags) {
|
||||
window.featureFlags = featureFlags || {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,22 +26,20 @@ import setupClient from './setup/setupClient';
|
|||
import setupColors from './setup/setupColors';
|
||||
import setupFormatters from './setup/setupFormatters';
|
||||
import setupDashboardComponents from './setup/setupDasboardComponents';
|
||||
import { BootstrapUser, User } from './types/bootstrapTypes';
|
||||
import { BootstrapData, User } from './types/bootstrapTypes';
|
||||
import { initFeatureFlags } from './featureFlags';
|
||||
import { DEFAULT_COMMON_BOOTSTRAP_DATA } from './constants';
|
||||
|
||||
if (process.env.WEBPACK_MODE === 'development') {
|
||||
setHotLoaderConfig({ logLevel: 'debug', trackTailUpdates: false });
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/no-mutable-exports
|
||||
export let bootstrapData: {
|
||||
user?: BootstrapUser;
|
||||
common?: any;
|
||||
config?: any;
|
||||
embedded?: {
|
||||
dashboard_id: string;
|
||||
export let bootstrapData: BootstrapData = {
|
||||
common: {
|
||||
...DEFAULT_COMMON_BOOTSTRAP_DATA,
|
||||
},
|
||||
};
|
||||
} = {};
|
||||
|
||||
// Configure translation
|
||||
if (typeof window !== 'undefined') {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,14 @@
|
|||
import { JsonObject, Locale } from '@superset-ui/core';
|
||||
import {
|
||||
ColorSchemeConfig,
|
||||
FeatureFlagMap,
|
||||
JsonObject,
|
||||
LanguagePack,
|
||||
Locale,
|
||||
SequentialSchemeConfig,
|
||||
} from '@superset-ui/core';
|
||||
import { isPlainObject } from 'lodash';
|
||||
import { FlashMessage } from '../components/FlashProvider';
|
||||
import { Languages } from '../views/components/LanguagePicker';
|
||||
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
|
|
@ -74,11 +83,78 @@ export type ChartResponse = {
|
|||
result: ChartData[];
|
||||
};
|
||||
|
||||
export interface BrandProps {
|
||||
path: string;
|
||||
icon: string;
|
||||
alt: string;
|
||||
tooltip: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface NavBarProps {
|
||||
show_watermark: boolean;
|
||||
bug_report_url?: string;
|
||||
version_string?: string;
|
||||
version_sha?: string;
|
||||
build_number?: string;
|
||||
documentation_url?: string;
|
||||
languages: Languages;
|
||||
show_language_picker: boolean;
|
||||
user_is_anonymous: boolean;
|
||||
user_info_url: string;
|
||||
user_login_url: string;
|
||||
user_logout_url: string;
|
||||
user_profile_url: string | null;
|
||||
locale: string;
|
||||
}
|
||||
|
||||
export interface MenuObjectChildProps {
|
||||
label: string;
|
||||
name?: string;
|
||||
icon?: string;
|
||||
index?: number;
|
||||
url?: string;
|
||||
isFrontendRoute?: boolean;
|
||||
perm?: string | boolean;
|
||||
view?: string;
|
||||
disable?: boolean;
|
||||
}
|
||||
|
||||
export interface MenuObjectProps extends MenuObjectChildProps {
|
||||
childs?: (MenuObjectChildProps | string)[];
|
||||
isHeader?: boolean;
|
||||
}
|
||||
|
||||
export interface MenuData {
|
||||
menu: MenuObjectProps[];
|
||||
brand: BrandProps;
|
||||
navbar_right: NavBarProps;
|
||||
settings: MenuObjectProps[];
|
||||
environment_tag: {
|
||||
text: string;
|
||||
color: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface CommonBootstrapData {
|
||||
flash_messages: string[][];
|
||||
flash_messages: FlashMessage[];
|
||||
conf: JsonObject;
|
||||
locale: Locale;
|
||||
feature_flags: Record<string, boolean>;
|
||||
feature_flags: FeatureFlagMap;
|
||||
language_pack: LanguagePack;
|
||||
extra_categorical_color_schemes: ColorSchemeConfig[];
|
||||
extra_sequential_color_schemes: SequentialSchemeConfig[];
|
||||
theme_overrides: JsonObject;
|
||||
menu_data: MenuData;
|
||||
}
|
||||
|
||||
export interface BootstrapData {
|
||||
user?: BootstrapUser;
|
||||
common: CommonBootstrapData;
|
||||
config?: any;
|
||||
embedded?: {
|
||||
dashboard_id: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function isUser(user: any): user is User {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* 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 { BootstrapData } from 'src/types/bootstrapTypes';
|
||||
|
||||
export default function getBootstrapData(): BootstrapData {
|
||||
const appContainer = document.getElementById('app');
|
||||
const dataBootstrap = appContainer?.getAttribute('data-bootstrap');
|
||||
return dataBootstrap ? JSON.parse(dataBootstrap) : {};
|
||||
}
|
||||
|
|
@ -17,8 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { TableTabTypes } from 'src/views/CRUD/types';
|
||||
import { SetTabType } from 'src/views/CRUD/welcome/ActivityTable';
|
||||
import { TableTab } from 'src/views/CRUD/types';
|
||||
import { DashboardContextForExplore } from 'src/types/DashboardContextForExplore';
|
||||
|
||||
export enum LocalStorageKeys {
|
||||
|
|
@ -65,11 +64,11 @@ export type LocalStorageValues = {
|
|||
controls_width: number;
|
||||
datasource_width: number;
|
||||
is_datapanel_open: boolean;
|
||||
homepage_chart_filter: TableTabTypes;
|
||||
homepage_dashboard_filter: TableTabTypes;
|
||||
homepage_chart_filter: TableTab;
|
||||
homepage_dashboard_filter: TableTab;
|
||||
homepage_collapse_state: string[];
|
||||
datasetname_set_successful: boolean;
|
||||
homepage_activity_filter: SetTabType | null;
|
||||
homepage_activity_filter: TableTab | null;
|
||||
sqllab__is_autocomplete_enabled: boolean;
|
||||
explore__data_table_original_formatted_time_columns: Record<string, string[]>;
|
||||
dashboard__custom_filter_bar_widths: Record<string, number>;
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@
|
|||
* 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
|
||||
* "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
|
||||
* "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.
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@
|
|||
* 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
|
||||
* "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
|
||||
* "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.
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ import ListView, { FilterOperator, Filters } from 'src/components/ListView';
|
|||
import handleResourceExport from 'src/utils/export';
|
||||
import { ExtentionConfigs } from 'src/views/components/types';
|
||||
import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
|
||||
import type { MenuObjectProps } from 'src/views/components/Menu';
|
||||
import type { MenuObjectProps } from 'src/types/bootstrapTypes';
|
||||
import DatabaseModal from './DatabaseModal';
|
||||
|
||||
import { DatabaseObject } from './types';
|
||||
|
|
|
|||
|
|
@ -24,13 +24,16 @@ export type FavoriteStatus = {
|
|||
[id: number]: boolean;
|
||||
};
|
||||
|
||||
export enum TableTabTypes {
|
||||
FAVORITE = 'Favorite',
|
||||
MINE = 'Mine',
|
||||
EXAMPLES = 'Examples',
|
||||
export enum TableTab {
|
||||
Favorite = 'Favorite',
|
||||
Mine = 'Mine',
|
||||
Other = 'Other',
|
||||
Viewed = 'Viewed',
|
||||
Created = 'Created',
|
||||
Edited = 'Edited',
|
||||
}
|
||||
|
||||
export type Filters = {
|
||||
export type Filter = {
|
||||
col: string;
|
||||
opr: string;
|
||||
value: string | number;
|
||||
|
|
@ -39,12 +42,12 @@ export type Filters = {
|
|||
export interface DashboardTableProps {
|
||||
addDangerToast: (message: string) => void;
|
||||
addSuccessToast: (message: string) => void;
|
||||
search: string;
|
||||
user?: User;
|
||||
mine: Array<Dashboard>;
|
||||
showThumbnails?: boolean;
|
||||
featureFlag?: boolean;
|
||||
examples: Array<Dashboard>;
|
||||
otherTabData: Array<Dashboard>;
|
||||
otherTabFilters: Filter[];
|
||||
otherTabTitle: string;
|
||||
}
|
||||
|
||||
export interface Dashboard {
|
||||
|
|
|
|||
|
|
@ -18,13 +18,17 @@
|
|||
*/
|
||||
import rison from 'rison';
|
||||
import {
|
||||
isNeedsPassword,
|
||||
isAlreadyExists,
|
||||
getPasswordsNeeded,
|
||||
getAlreadyExists,
|
||||
hasTerminalValidation,
|
||||
checkUploadExtensions,
|
||||
getAlreadyExists,
|
||||
getFilterValues,
|
||||
getPasswordsNeeded,
|
||||
hasTerminalValidation,
|
||||
isAlreadyExists,
|
||||
isNeedsPassword,
|
||||
} from 'src/views/CRUD/utils';
|
||||
import { User } from 'src/types/bootstrapTypes';
|
||||
import { Filter, TableTab } from './types';
|
||||
import { WelcomeTable } from './welcome/types';
|
||||
|
||||
const terminalErrors = {
|
||||
errors: [
|
||||
|
|
@ -228,3 +232,165 @@ test('checkUploadExtensions should return valid upload extensions', () => {
|
|||
checkUploadExtensions(randomExtensionThree, uploadExtensionTest),
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
test('getFilterValues', () => {
|
||||
const userId = 1234;
|
||||
const mockUser: User = {
|
||||
firstName: 'foo',
|
||||
lastName: 'bar',
|
||||
username: 'baz',
|
||||
userId,
|
||||
isActive: true,
|
||||
isAnonymous: false,
|
||||
};
|
||||
const testCases: [
|
||||
TableTab,
|
||||
WelcomeTable,
|
||||
User | undefined,
|
||||
Filter[] | undefined,
|
||||
ReturnType<typeof getFilterValues>,
|
||||
][] = [
|
||||
[
|
||||
TableTab.Mine,
|
||||
WelcomeTable.SavedQueries,
|
||||
mockUser,
|
||||
undefined,
|
||||
[
|
||||
{
|
||||
id: 'created_by',
|
||||
operator: 'rel_o_m',
|
||||
value: `${userId}`,
|
||||
},
|
||||
],
|
||||
],
|
||||
[
|
||||
TableTab.Favorite,
|
||||
WelcomeTable.SavedQueries,
|
||||
mockUser,
|
||||
undefined,
|
||||
[
|
||||
{
|
||||
id: 'id',
|
||||
operator: 'saved_query_is_fav',
|
||||
value: true,
|
||||
},
|
||||
],
|
||||
],
|
||||
[
|
||||
TableTab.Created,
|
||||
WelcomeTable.Charts,
|
||||
mockUser,
|
||||
undefined,
|
||||
[
|
||||
{
|
||||
id: 'created_by',
|
||||
operator: 'rel_o_m',
|
||||
value: `${userId}`,
|
||||
},
|
||||
],
|
||||
],
|
||||
[
|
||||
TableTab.Created,
|
||||
WelcomeTable.Dashboards,
|
||||
mockUser,
|
||||
undefined,
|
||||
[
|
||||
{
|
||||
id: 'created_by',
|
||||
operator: 'rel_o_m',
|
||||
value: `${userId}`,
|
||||
},
|
||||
],
|
||||
],
|
||||
[
|
||||
TableTab.Created,
|
||||
WelcomeTable.Recents,
|
||||
mockUser,
|
||||
undefined,
|
||||
[
|
||||
{
|
||||
id: 'created_by',
|
||||
operator: 'rel_o_m',
|
||||
value: `${userId}`,
|
||||
},
|
||||
],
|
||||
],
|
||||
[
|
||||
TableTab.Mine,
|
||||
WelcomeTable.Charts,
|
||||
mockUser,
|
||||
undefined,
|
||||
[
|
||||
{
|
||||
id: 'owners',
|
||||
operator: 'rel_m_m',
|
||||
value: `${userId}`,
|
||||
},
|
||||
],
|
||||
],
|
||||
[
|
||||
TableTab.Mine,
|
||||
WelcomeTable.Dashboards,
|
||||
mockUser,
|
||||
undefined,
|
||||
[
|
||||
{
|
||||
id: 'owners',
|
||||
operator: 'rel_m_m',
|
||||
value: `${userId}`,
|
||||
},
|
||||
],
|
||||
],
|
||||
[
|
||||
TableTab.Favorite,
|
||||
WelcomeTable.Dashboards,
|
||||
mockUser,
|
||||
undefined,
|
||||
[
|
||||
{
|
||||
id: 'id',
|
||||
operator: 'dashboard_is_favorite',
|
||||
value: true,
|
||||
},
|
||||
],
|
||||
],
|
||||
[
|
||||
TableTab.Favorite,
|
||||
WelcomeTable.Charts,
|
||||
mockUser,
|
||||
undefined,
|
||||
[
|
||||
{
|
||||
id: 'id',
|
||||
operator: 'chart_is_favorite',
|
||||
value: true,
|
||||
},
|
||||
],
|
||||
],
|
||||
[
|
||||
TableTab.Other,
|
||||
WelcomeTable.Charts,
|
||||
mockUser,
|
||||
[
|
||||
{
|
||||
col: 'created_by',
|
||||
opr: 'rel_o_m',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
id: 'created_by',
|
||||
operator: 'rel_o_m',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
],
|
||||
];
|
||||
testCases.forEach(testCase => {
|
||||
const [tab, welcomeTable, user, otherTabFilters, expectedValue] = testCase;
|
||||
expect(getFilterValues(tab, welcomeTable, user, otherTabFilters)).toEqual(
|
||||
expectedValue,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,22 +18,24 @@
|
|||
*/
|
||||
|
||||
import {
|
||||
t,
|
||||
SupersetClient,
|
||||
SupersetClientResponse,
|
||||
css,
|
||||
logging,
|
||||
styled,
|
||||
SupersetClient,
|
||||
SupersetClientResponse,
|
||||
SupersetTheme,
|
||||
css,
|
||||
t,
|
||||
} from '@superset-ui/core';
|
||||
import Chart from 'src/types/Chart';
|
||||
import { intersection } from 'lodash';
|
||||
import rison from 'rison';
|
||||
import { getClientErrorObject } from 'src/utils/getClientErrorObject';
|
||||
import { FetchDataConfig } from 'src/components/ListView';
|
||||
import { FetchDataConfig, FilterValue } from 'src/components/ListView';
|
||||
import SupersetText from 'src/utils/textUtils';
|
||||
import { findPermission } from 'src/utils/findPermission';
|
||||
import { Dashboard, Filters } from './types';
|
||||
import { User } from 'src/types/bootstrapTypes';
|
||||
import { Dashboard, Filter, TableTab } from './types';
|
||||
import { WelcomeTable } from './welcome/types';
|
||||
|
||||
// Modifies the rison encoding slightly to match the backend's rison encoding/decoding. Applies globally.
|
||||
// Code pulled from rison.js (https://github.com/Nanonid/rison), rison is licensed under the MIT license.
|
||||
|
|
@ -120,7 +122,7 @@ const createFetchResourceMethod =
|
|||
};
|
||||
|
||||
export const PAGE_SIZE = 5;
|
||||
const getParams = (filters?: Array<Filters>) => {
|
||||
const getParams = (filters?: Filter[]) => {
|
||||
const params = {
|
||||
order_column: 'changed_on_delta_humanized',
|
||||
order_direction: 'desc',
|
||||
|
|
@ -164,7 +166,7 @@ export const getEditedObjects = (userId: string | number) => {
|
|||
export const getUserOwnedObjects = (
|
||||
userId: string | number,
|
||||
resource: string,
|
||||
filters: Array<Filters> = [
|
||||
filters: Filter[] = [
|
||||
{
|
||||
col: 'owners',
|
||||
opr: 'rel_m_m',
|
||||
|
|
@ -180,16 +182,10 @@ export const getRecentAcitivtyObjs = (
|
|||
userId: string | number,
|
||||
recent: string,
|
||||
addDangerToast: (arg1: string, arg2: any) => any,
|
||||
filters: Filter[],
|
||||
) =>
|
||||
SupersetClient.get({ endpoint: recent }).then(recentsRes => {
|
||||
const res: any = {};
|
||||
const filters = [
|
||||
{
|
||||
col: 'created_by',
|
||||
opr: 'rel_o_m',
|
||||
value: 0,
|
||||
},
|
||||
];
|
||||
const newBatch = [
|
||||
SupersetClient.get({
|
||||
endpoint: `/api/v1/chart/?q=${getParams(filters)}`,
|
||||
|
|
@ -200,7 +196,7 @@ export const getRecentAcitivtyObjs = (
|
|||
];
|
||||
return Promise.all(newBatch)
|
||||
.then(([chartRes, dashboardRes]) => {
|
||||
res.examples = [...chartRes.json.result, ...dashboardRes.json.result];
|
||||
res.other = [...chartRes.json.result, ...dashboardRes.json.result];
|
||||
res.viewed = recentsRes.json;
|
||||
return res;
|
||||
})
|
||||
|
|
@ -442,3 +438,64 @@ export const uploadUserPerms = (
|
|||
canUploadData: canUploadCSV || canUploadColumnar || canUploadExcel,
|
||||
};
|
||||
};
|
||||
|
||||
export function getFilterValues(
|
||||
tab: TableTab,
|
||||
welcomeTable: WelcomeTable,
|
||||
user?: User,
|
||||
otherTabFilters?: Filter[],
|
||||
): FilterValue[] {
|
||||
if (
|
||||
tab === TableTab.Created ||
|
||||
(welcomeTable === WelcomeTable.SavedQueries && tab === TableTab.Mine)
|
||||
) {
|
||||
return [
|
||||
{
|
||||
id: 'created_by',
|
||||
operator: 'rel_o_m',
|
||||
value: `${user?.userId}`,
|
||||
},
|
||||
];
|
||||
}
|
||||
if (welcomeTable === WelcomeTable.SavedQueries && tab === TableTab.Favorite) {
|
||||
return [
|
||||
{
|
||||
id: 'id',
|
||||
operator: 'saved_query_is_fav',
|
||||
value: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
if (tab === TableTab.Mine && user) {
|
||||
return [
|
||||
{
|
||||
id: 'owners',
|
||||
operator: 'rel_m_m',
|
||||
value: `${user.userId}`,
|
||||
},
|
||||
];
|
||||
}
|
||||
if (
|
||||
tab === TableTab.Favorite &&
|
||||
[WelcomeTable.Dashboards, WelcomeTable.Charts].includes(welcomeTable)
|
||||
) {
|
||||
return [
|
||||
{
|
||||
id: 'id',
|
||||
operator:
|
||||
welcomeTable === WelcomeTable.Dashboards
|
||||
? 'dashboard_is_favorite'
|
||||
: 'chart_is_favorite',
|
||||
value: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
if (tab === TableTab.Other) {
|
||||
return (otherTabFilters || []).map(flt => ({
|
||||
id: flt.col,
|
||||
operator: flt.opr,
|
||||
value: flt.value,
|
||||
}));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import fetchMock from 'fetch-mock';
|
|||
import thunk from 'redux-thunk';
|
||||
import configureStore from 'redux-mock-store';
|
||||
import ActivityTable from 'src/views/CRUD/welcome/ActivityTable';
|
||||
import { TableTab } from 'src/views/CRUD/types';
|
||||
|
||||
const mockStore = configureStore([thunk]);
|
||||
const store = mockStore({});
|
||||
|
|
@ -33,7 +34,7 @@ const chartsEndpoint = 'glob:*/api/v1/chart/?*';
|
|||
const dashboardsEndpoint = 'glob:*/api/v1/dashboard/?*';
|
||||
|
||||
const mockData = {
|
||||
Viewed: [
|
||||
[TableTab.Viewed]: [
|
||||
{
|
||||
slice_name: 'ChartyChart',
|
||||
changed_on_utc: '24 Feb 2014 10:13:14',
|
||||
|
|
@ -42,7 +43,7 @@ const mockData = {
|
|||
table: {},
|
||||
},
|
||||
],
|
||||
Created: [
|
||||
[TableTab.Created]: [
|
||||
{
|
||||
dashboard_title: 'Dashboard_Test',
|
||||
changed_on_utc: '24 Feb 2014 10:13:14',
|
||||
|
|
@ -77,7 +78,7 @@ fetchMock.get(dashboardsEndpoint, {
|
|||
|
||||
describe('ActivityTable', () => {
|
||||
const activityProps = {
|
||||
activeChild: 'Created',
|
||||
activeChild: TableTab.Created,
|
||||
activityData: mockData,
|
||||
setActiveChild: jest.fn(),
|
||||
user: { userId: '1' },
|
||||
|
|
@ -122,7 +123,7 @@ describe('ActivityTable', () => {
|
|||
});
|
||||
it('show empty state if there is no data', () => {
|
||||
const activityProps = {
|
||||
activeChild: 'Created',
|
||||
activeChild: TableTab.Created,
|
||||
activityData: {},
|
||||
setActiveChild: jest.fn(),
|
||||
user: { userId: '1' },
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import { setItem, LocalStorageKeys } from 'src/utils/localStorageHelpers';
|
|||
import { Link } from 'react-router-dom';
|
||||
import ListViewCard from 'src/components/ListViewCard';
|
||||
import SubMenu from 'src/views/components/SubMenu';
|
||||
import { Dashboard, SavedQueryObject, TableTab } from 'src/views/CRUD/types';
|
||||
import { ActivityData, LoadingCards } from 'src/views/CRUD/welcome/Welcome';
|
||||
import {
|
||||
CardContainer,
|
||||
|
|
@ -30,7 +31,6 @@ import {
|
|||
getEditedObjects,
|
||||
} from 'src/views/CRUD/utils';
|
||||
import { Chart } from 'src/types/Chart';
|
||||
import { Dashboard, SavedQueryObject } from 'src/views/CRUD/types';
|
||||
|
||||
import Icons from 'src/components/Icons';
|
||||
|
||||
|
|
@ -57,12 +57,6 @@ interface RecentDashboard extends RecentActivity {
|
|||
item_type: 'dashboard';
|
||||
}
|
||||
|
||||
export enum SetTabType {
|
||||
EDITED = 'Edited',
|
||||
CREATED = 'Created',
|
||||
VIEWED = 'Viewed',
|
||||
EXAMPLE = 'Examples',
|
||||
}
|
||||
/**
|
||||
* Recent activity objects fetched by `getRecentAcitivtyObjs`.
|
||||
*/
|
||||
|
|
@ -148,7 +142,7 @@ export default function ActivityTable({
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (activeChild === 'Edited') {
|
||||
if (activeChild === TableTab.Edited) {
|
||||
setLoadingState(true);
|
||||
getEditedCards();
|
||||
}
|
||||
|
|
@ -156,36 +150,38 @@ export default function ActivityTable({
|
|||
|
||||
const tabs = [
|
||||
{
|
||||
name: 'Edited',
|
||||
name: TableTab.Edited,
|
||||
label: t('Edited'),
|
||||
onClick: () => {
|
||||
setActiveChild('Edited');
|
||||
setItem(LocalStorageKeys.homepage_activity_filter, SetTabType.EDITED);
|
||||
setActiveChild(TableTab.Edited);
|
||||
setItem(LocalStorageKeys.homepage_activity_filter, TableTab.Edited);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Created',
|
||||
name: TableTab.Created,
|
||||
label: t('Created'),
|
||||
onClick: () => {
|
||||
setActiveChild('Created');
|
||||
setItem(LocalStorageKeys.homepage_activity_filter, SetTabType.CREATED);
|
||||
setActiveChild(TableTab.Created);
|
||||
setItem(LocalStorageKeys.homepage_activity_filter, TableTab.Created);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
if (activityData?.Viewed) {
|
||||
if (activityData?.[TableTab.Viewed]) {
|
||||
tabs.unshift({
|
||||
name: 'Viewed',
|
||||
name: TableTab.Viewed,
|
||||
label: t('Viewed'),
|
||||
onClick: () => {
|
||||
setActiveChild('Viewed');
|
||||
setItem(LocalStorageKeys.homepage_activity_filter, SetTabType.VIEWED);
|
||||
setActiveChild(TableTab.Viewed);
|
||||
setItem(LocalStorageKeys.homepage_activity_filter, TableTab.Viewed);
|
||||
},
|
||||
});
|
||||
}
|
||||
const renderActivity = () =>
|
||||
(activeChild !== 'Edited' ? activityData[activeChild] : editedObjs).map(
|
||||
(entity: ActivityObject) => {
|
||||
(activeChild !== TableTab.Edited
|
||||
? activityData[activeChild]
|
||||
: editedObjs
|
||||
).map((entity: ActivityObject) => {
|
||||
const url = getEntityUrl(entity);
|
||||
const lastActionOn = getEntityLastActionOn(entity);
|
||||
return (
|
||||
|
|
@ -202,8 +198,7 @@ export default function ActivityTable({
|
|||
</Link>
|
||||
</CardStyles>
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
const doneFetching = loadedCount < 3;
|
||||
|
||||
|
|
@ -214,7 +209,9 @@ export default function ActivityTable({
|
|||
<Styles>
|
||||
<SubMenu activeChild={activeChild} tabs={tabs} />
|
||||
{activityData[activeChild]?.length > 0 ||
|
||||
(activeChild === 'Edited' && editedObjs && editedObjs.length > 0) ? (
|
||||
(activeChild === TableTab.Edited &&
|
||||
editedObjs &&
|
||||
editedObjs.length > 0) ? (
|
||||
<CardContainer className="recentCards">
|
||||
{renderActivity()}
|
||||
</CardContainer>
|
||||
|
|
|
|||
|
|
@ -62,6 +62,11 @@ describe('ChartTable', () => {
|
|||
user: {
|
||||
userId: '2',
|
||||
},
|
||||
mine: [],
|
||||
otherTabData: [],
|
||||
otherTabFilters: [],
|
||||
otherTabTitle: 'Other',
|
||||
showThumbnails: false,
|
||||
};
|
||||
|
||||
let wrapper: ReactWrapper;
|
||||
|
|
@ -89,13 +94,35 @@ describe('ChartTable', () => {
|
|||
expect(wrapper.find('ChartCard')).toExist();
|
||||
});
|
||||
|
||||
it('renders other tab by default', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mount(
|
||||
<ChartTable
|
||||
user={{ userId: '2' }}
|
||||
mine={[]}
|
||||
otherTabData={mockCharts}
|
||||
otherTabFilters={[]}
|
||||
otherTabTitle="Other"
|
||||
showThumbnails={false}
|
||||
store={store}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
await waitForComponentToPaint(wrapper);
|
||||
expect(wrapper.find('EmptyState')).not.toExist();
|
||||
expect(wrapper.find('ChartCard')).toExist();
|
||||
});
|
||||
|
||||
it('display EmptyState if there is no data', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mount(
|
||||
<ChartTable
|
||||
chartFilter="Mine"
|
||||
user={{ userId: '2' }}
|
||||
mine={[]}
|
||||
otherTabData={[]}
|
||||
otherTabFilters={[]}
|
||||
otherTabTitle="Other"
|
||||
showThumbnails={false}
|
||||
store={store}
|
||||
/>,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -26,15 +26,19 @@ import {
|
|||
} from 'src/views/CRUD/hooks';
|
||||
import {
|
||||
getItem,
|
||||
setItem,
|
||||
LocalStorageKeys,
|
||||
setItem,
|
||||
} from 'src/utils/localStorageHelpers';
|
||||
import withToasts from 'src/components/MessageToasts/withToasts';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { TableTabTypes } from 'src/views/CRUD/types';
|
||||
import { Filter, TableTab } from 'src/views/CRUD/types';
|
||||
import PropertiesModal from 'src/explore/components/PropertiesModal';
|
||||
import { User } from 'src/types/bootstrapTypes';
|
||||
import { CardContainer, PAGE_SIZE } from 'src/views/CRUD/utils';
|
||||
import {
|
||||
CardContainer,
|
||||
getFilterValues,
|
||||
PAGE_SIZE,
|
||||
} from 'src/views/CRUD/utils';
|
||||
import { LoadingCards } from 'src/views/CRUD/welcome/Welcome';
|
||||
import ChartCard from 'src/views/CRUD/chart/ChartCard';
|
||||
import Chart from 'src/types/Chart';
|
||||
|
|
@ -48,12 +52,12 @@ import { WelcomeTable } from './types';
|
|||
interface ChartTableProps {
|
||||
addDangerToast: (message: string) => void;
|
||||
addSuccessToast: (message: string) => void;
|
||||
search: string;
|
||||
chartFilter?: string;
|
||||
user?: User;
|
||||
mine: Array<any>;
|
||||
showThumbnails: boolean;
|
||||
examples?: Array<object>;
|
||||
otherTabData?: Array<object>;
|
||||
otherTabFilters: Filter[];
|
||||
otherTabTitle: string;
|
||||
}
|
||||
|
||||
function ChartTable({
|
||||
|
|
@ -62,16 +66,17 @@ function ChartTable({
|
|||
addSuccessToast,
|
||||
mine,
|
||||
showThumbnails,
|
||||
examples,
|
||||
otherTabData,
|
||||
otherTabFilters,
|
||||
otherTabTitle,
|
||||
}: ChartTableProps) {
|
||||
const history = useHistory();
|
||||
const filterStore = getItem(
|
||||
const initialTab = getItem(
|
||||
LocalStorageKeys.homepage_chart_filter,
|
||||
TableTabTypes.EXAMPLES,
|
||||
TableTab.Other,
|
||||
);
|
||||
const initialFilter = filterStore;
|
||||
|
||||
const filteredExamples = filter(examples, obj => 'viz_type' in obj);
|
||||
const filteredOtherTabData = filter(otherTabData, obj => 'viz_type' in obj);
|
||||
|
||||
const {
|
||||
state: { loading, resourceCollection: charts, bulkSelectEnabled },
|
||||
|
|
@ -84,7 +89,7 @@ function ChartTable({
|
|||
t('chart'),
|
||||
addDangerToast,
|
||||
true,
|
||||
initialFilter === 'Mine' ? mine : filteredExamples,
|
||||
initialTab === TableTab.Mine ? mine : filteredOtherTabData,
|
||||
[],
|
||||
false,
|
||||
);
|
||||
|
|
@ -102,36 +107,11 @@ function ChartTable({
|
|||
closeChartEditModal,
|
||||
} = useChartEditModal(setCharts, charts);
|
||||
|
||||
const [chartFilter, setChartFilter] = useState(initialFilter);
|
||||
const [activeTab, setActiveTab] = useState(initialTab);
|
||||
const [preparingExport, setPreparingExport] = useState<boolean>(false);
|
||||
const [loaded, setLoaded] = useState<boolean>(false);
|
||||
|
||||
const getFilters = (filterName: string) => {
|
||||
const filters = [];
|
||||
|
||||
if (filterName === 'Mine') {
|
||||
filters.push({
|
||||
id: 'owners',
|
||||
operator: 'rel_m_m',
|
||||
value: `${user?.userId}`,
|
||||
});
|
||||
} else if (filterName === 'Favorite') {
|
||||
filters.push({
|
||||
id: 'id',
|
||||
operator: 'chart_is_favorite',
|
||||
value: true,
|
||||
});
|
||||
} else if (filterName === 'Examples') {
|
||||
filters.push({
|
||||
id: 'created_by',
|
||||
operator: 'rel_o_m',
|
||||
value: 0,
|
||||
});
|
||||
}
|
||||
return filters;
|
||||
};
|
||||
|
||||
const getData = (filter: string) =>
|
||||
const getData = (tab: TableTab) =>
|
||||
fetchData({
|
||||
pageIndex: 0,
|
||||
pageSize: PAGE_SIZE,
|
||||
|
|
@ -141,15 +121,15 @@ function ChartTable({
|
|||
desc: true,
|
||||
},
|
||||
],
|
||||
filters: getFilters(filter),
|
||||
filters: getFilterValues(tab, WelcomeTable.Charts, user, otherTabFilters),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (loaded || chartFilter === 'Favorite') {
|
||||
getData(chartFilter);
|
||||
if (loaded || activeTab === TableTab.Favorite) {
|
||||
getData(activeTab);
|
||||
}
|
||||
setLoaded(true);
|
||||
}, [chartFilter]);
|
||||
}, [activeTab]);
|
||||
|
||||
const handleBulkChartExport = (chartsToExport: Chart[]) => {
|
||||
const ids = chartsToExport.map(({ id }) => id);
|
||||
|
|
@ -161,29 +141,29 @@ function ChartTable({
|
|||
|
||||
const menuTabs = [
|
||||
{
|
||||
name: 'Favorite',
|
||||
name: TableTab.Favorite,
|
||||
label: t('Favorite'),
|
||||
onClick: () => {
|
||||
setChartFilter(TableTabTypes.FAVORITE);
|
||||
setItem(LocalStorageKeys.homepage_chart_filter, TableTabTypes.FAVORITE);
|
||||
setActiveTab(TableTab.Favorite);
|
||||
setItem(LocalStorageKeys.homepage_chart_filter, TableTab.Favorite);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Mine',
|
||||
name: TableTab.Mine,
|
||||
label: t('Mine'),
|
||||
onClick: () => {
|
||||
setChartFilter(TableTabTypes.MINE);
|
||||
setItem(LocalStorageKeys.homepage_chart_filter, TableTabTypes.MINE);
|
||||
setActiveTab(TableTab.Mine);
|
||||
setItem(LocalStorageKeys.homepage_chart_filter, TableTab.Mine);
|
||||
},
|
||||
},
|
||||
];
|
||||
if (examples) {
|
||||
if (otherTabData) {
|
||||
menuTabs.push({
|
||||
name: 'Examples',
|
||||
label: t('Examples'),
|
||||
name: TableTab.Other,
|
||||
label: otherTabTitle,
|
||||
onClick: () => {
|
||||
setChartFilter(TableTabTypes.EXAMPLES);
|
||||
setItem(LocalStorageKeys.homepage_chart_filter, TableTabTypes.EXAMPLES);
|
||||
setActiveTab(TableTab.Other);
|
||||
setItem(LocalStorageKeys.homepage_chart_filter, TableTab.Other);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -201,7 +181,7 @@ function ChartTable({
|
|||
)}
|
||||
|
||||
<SubMenu
|
||||
activeChild={chartFilter}
|
||||
activeChild={activeTab}
|
||||
tabs={menuTabs}
|
||||
buttons={[
|
||||
{
|
||||
|
|
@ -221,7 +201,7 @@ function ChartTable({
|
|||
buttonStyle: 'link',
|
||||
onClick: () => {
|
||||
const target =
|
||||
chartFilter === 'Favorite'
|
||||
activeTab === TableTab.Favorite
|
||||
? `/chart/list/?filters=(favorite:(label:${t(
|
||||
'Yes',
|
||||
)},value:!t))`
|
||||
|
|
@ -237,7 +217,7 @@ function ChartTable({
|
|||
<ChartCard
|
||||
key={`${e.id}`}
|
||||
openChartEditModal={openChartEditModal}
|
||||
chartFilter={chartFilter}
|
||||
chartFilter={activeTab}
|
||||
chart={e}
|
||||
userId={user?.userId}
|
||||
hasPerm={hasPerm}
|
||||
|
|
@ -253,7 +233,11 @@ function ChartTable({
|
|||
))}
|
||||
</CardContainer>
|
||||
) : (
|
||||
<EmptyState tableName={WelcomeTable.Charts} tab={chartFilter} />
|
||||
<EmptyState
|
||||
tableName={WelcomeTable.Charts}
|
||||
tab={activeTab}
|
||||
otherTabTitle={otherTabTitle}
|
||||
/>
|
||||
)}
|
||||
{preparingExport && <Loading />}
|
||||
</ErrorBoundary>
|
||||
|
|
|
|||
|
|
@ -20,22 +20,19 @@ import React, { useEffect, useMemo, useState } from 'react';
|
|||
import { SupersetClient, t } from '@superset-ui/core';
|
||||
import { filter } from 'lodash';
|
||||
import { useFavoriteStatus, useListViewResource } from 'src/views/CRUD/hooks';
|
||||
import {
|
||||
Dashboard,
|
||||
DashboardTableProps,
|
||||
TableTabTypes,
|
||||
} from 'src/views/CRUD/types';
|
||||
import { Dashboard, DashboardTableProps, TableTab } from 'src/views/CRUD/types';
|
||||
import handleResourceExport from 'src/utils/export';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import {
|
||||
getItem,
|
||||
setItem,
|
||||
LocalStorageKeys,
|
||||
setItem,
|
||||
} from 'src/utils/localStorageHelpers';
|
||||
import { LoadingCards } from 'src/views/CRUD/welcome/Welcome';
|
||||
import {
|
||||
CardContainer,
|
||||
createErrorHandler,
|
||||
getFilterValues,
|
||||
PAGE_SIZE,
|
||||
} from 'src/views/CRUD/utils';
|
||||
import withToasts from 'src/components/MessageToasts/withToasts';
|
||||
|
|
@ -46,28 +43,26 @@ import SubMenu from 'src/views/components/SubMenu';
|
|||
import EmptyState from './EmptyState';
|
||||
import { WelcomeTable } from './types';
|
||||
|
||||
export interface FilterValue {
|
||||
col: string;
|
||||
operator: string;
|
||||
value: string | boolean | number | null | undefined;
|
||||
}
|
||||
|
||||
function DashboardTable({
|
||||
user,
|
||||
addDangerToast,
|
||||
addSuccessToast,
|
||||
mine,
|
||||
showThumbnails,
|
||||
examples,
|
||||
otherTabData,
|
||||
otherTabFilters,
|
||||
otherTabTitle,
|
||||
}: DashboardTableProps) {
|
||||
const history = useHistory();
|
||||
const filterStore = getItem(
|
||||
const defaultTab = getItem(
|
||||
LocalStorageKeys.homepage_dashboard_filter,
|
||||
TableTabTypes.EXAMPLES,
|
||||
TableTab.Other,
|
||||
);
|
||||
const defaultFilter = filterStore;
|
||||
|
||||
const filteredExamples = filter(examples, obj => !('viz_type' in obj));
|
||||
const filteredOtherTabData = filter(
|
||||
otherTabData,
|
||||
obj => !('viz_type' in obj),
|
||||
);
|
||||
|
||||
const {
|
||||
state: { loading, resourceCollection: dashboards },
|
||||
|
|
@ -80,7 +75,7 @@ function DashboardTable({
|
|||
t('dashboard'),
|
||||
addDangerToast,
|
||||
true,
|
||||
defaultFilter === 'Mine' ? mine : filteredExamples,
|
||||
defaultTab === TableTab.Mine ? mine : filteredOtherTabData,
|
||||
[],
|
||||
false,
|
||||
);
|
||||
|
|
@ -92,35 +87,11 @@ function DashboardTable({
|
|||
);
|
||||
|
||||
const [editModal, setEditModal] = useState<Dashboard>();
|
||||
const [dashboardFilter, setDashboardFilter] = useState(defaultFilter);
|
||||
const [activeTab, setActiveTab] = useState(defaultTab);
|
||||
const [preparingExport, setPreparingExport] = useState<boolean>(false);
|
||||
const [loaded, setLoaded] = useState<boolean>(false);
|
||||
|
||||
const getFilters = (filterName: string) => {
|
||||
const filters = [];
|
||||
if (filterName === 'Mine') {
|
||||
filters.push({
|
||||
id: 'owners',
|
||||
operator: 'rel_m_m',
|
||||
value: `${user?.userId}`,
|
||||
});
|
||||
} else if (filterName === 'Favorite') {
|
||||
filters.push({
|
||||
id: 'id',
|
||||
operator: 'dashboard_is_favorite',
|
||||
value: true,
|
||||
});
|
||||
} else if (filterName === 'Examples') {
|
||||
filters.push({
|
||||
id: 'created_by',
|
||||
operator: 'rel_o_m',
|
||||
value: 0,
|
||||
});
|
||||
}
|
||||
return filters;
|
||||
};
|
||||
|
||||
const getData = (filter: string) =>
|
||||
const getData = (tab: TableTab) =>
|
||||
fetchData({
|
||||
pageIndex: 0,
|
||||
pageSize: PAGE_SIZE,
|
||||
|
|
@ -130,15 +101,20 @@ function DashboardTable({
|
|||
desc: true,
|
||||
},
|
||||
],
|
||||
filters: getFilters(filter),
|
||||
filters: getFilterValues(
|
||||
tab,
|
||||
WelcomeTable.Dashboards,
|
||||
user,
|
||||
otherTabFilters,
|
||||
),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (loaded || dashboardFilter === 'Favorite') {
|
||||
getData(dashboardFilter);
|
||||
if (loaded || activeTab === TableTab.Favorite) {
|
||||
getData(activeTab);
|
||||
}
|
||||
setLoaded(true);
|
||||
}, [dashboardFilter]);
|
||||
}, [activeTab]);
|
||||
|
||||
const handleBulkDashboardExport = (dashboardsToExport: Dashboard[]) => {
|
||||
const ids = dashboardsToExport.map(({ id }) => id);
|
||||
|
|
@ -171,36 +147,30 @@ function DashboardTable({
|
|||
|
||||
const menuTabs = [
|
||||
{
|
||||
name: 'Favorite',
|
||||
name: TableTab.Favorite,
|
||||
label: t('Favorite'),
|
||||
onClick: () => {
|
||||
setDashboardFilter(TableTabTypes.FAVORITE);
|
||||
setItem(
|
||||
LocalStorageKeys.homepage_dashboard_filter,
|
||||
TableTabTypes.FAVORITE,
|
||||
);
|
||||
setActiveTab(TableTab.Favorite);
|
||||
setItem(LocalStorageKeys.homepage_dashboard_filter, TableTab.Favorite);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Mine',
|
||||
name: TableTab.Mine,
|
||||
label: t('Mine'),
|
||||
onClick: () => {
|
||||
setDashboardFilter(TableTabTypes.MINE);
|
||||
setItem(LocalStorageKeys.homepage_dashboard_filter, TableTabTypes.MINE);
|
||||
setActiveTab(TableTab.Mine);
|
||||
setItem(LocalStorageKeys.homepage_dashboard_filter, TableTab.Mine);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
if (examples) {
|
||||
if (otherTabData) {
|
||||
menuTabs.push({
|
||||
name: 'Examples',
|
||||
label: t('Examples'),
|
||||
name: TableTab.Other,
|
||||
label: otherTabTitle,
|
||||
onClick: () => {
|
||||
setDashboardFilter(TableTabTypes.EXAMPLES);
|
||||
setItem(
|
||||
LocalStorageKeys.homepage_dashboard_filter,
|
||||
TableTabTypes.EXAMPLES,
|
||||
);
|
||||
setActiveTab(TableTab.Other);
|
||||
setItem(LocalStorageKeys.homepage_dashboard_filter, TableTab.Other);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -209,7 +179,7 @@ function DashboardTable({
|
|||
return (
|
||||
<>
|
||||
<SubMenu
|
||||
activeChild={dashboardFilter}
|
||||
activeChild={activeTab}
|
||||
tabs={menuTabs}
|
||||
buttons={[
|
||||
{
|
||||
|
|
@ -229,7 +199,7 @@ function DashboardTable({
|
|||
buttonStyle: 'link',
|
||||
onClick: () => {
|
||||
const target =
|
||||
dashboardFilter === 'Favorite'
|
||||
activeTab === TableTab.Favorite
|
||||
? `/dashboard/list/?filters=(favorite:(label:${t(
|
||||
'Yes',
|
||||
)},value:!t))`
|
||||
|
|
@ -256,7 +226,7 @@ function DashboardTable({
|
|||
hasPerm={hasPerm}
|
||||
bulkSelectEnabled={false}
|
||||
showThumbnails={showThumbnails}
|
||||
dashboardFilter={dashboardFilter}
|
||||
dashboardFilter={activeTab}
|
||||
refreshData={refreshData}
|
||||
addDangerToast={addDangerToast}
|
||||
addSuccessToast={addSuccessToast}
|
||||
|
|
@ -273,7 +243,7 @@ function DashboardTable({
|
|||
</CardContainer>
|
||||
)}
|
||||
{dashboards.length === 0 && (
|
||||
<EmptyState tableName={WelcomeTable.Dashboards} tab={dashboardFilter} />
|
||||
<EmptyState tableName={WelcomeTable.Dashboards} tab={activeTab} />
|
||||
)}
|
||||
{preparingExport && <Loading />}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -18,47 +18,48 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import { styledMount as mount } from 'spec/helpers/theming';
|
||||
import EmptyState from 'src/views/CRUD/welcome/EmptyState';
|
||||
import { TableTab } from 'src/views/CRUD/types';
|
||||
import EmptyState, { EmptyStateProps } from 'src/views/CRUD/welcome/EmptyState';
|
||||
import { WelcomeTable } from './types';
|
||||
|
||||
describe('EmptyState', () => {
|
||||
const variants = [
|
||||
const variants: EmptyStateProps[] = [
|
||||
{
|
||||
tab: 'Favorite',
|
||||
tab: TableTab.Favorite,
|
||||
tableName: WelcomeTable.Dashboards,
|
||||
},
|
||||
{
|
||||
tab: 'Mine',
|
||||
tab: TableTab.Mine,
|
||||
tableName: WelcomeTable.Dashboards,
|
||||
},
|
||||
{
|
||||
tab: 'Favorite',
|
||||
tab: TableTab.Favorite,
|
||||
tableName: WelcomeTable.Charts,
|
||||
},
|
||||
{
|
||||
tab: 'Mine',
|
||||
tab: TableTab.Mine,
|
||||
tableName: WelcomeTable.Charts,
|
||||
},
|
||||
{
|
||||
tab: 'Favorite',
|
||||
tab: TableTab.Favorite,
|
||||
tableName: WelcomeTable.SavedQueries,
|
||||
},
|
||||
{
|
||||
tab: 'Mine',
|
||||
tab: TableTab.Mine,
|
||||
tableName: WelcomeTable.SavedQueries,
|
||||
},
|
||||
];
|
||||
const recents = [
|
||||
const recents: EmptyStateProps[] = [
|
||||
{
|
||||
tab: 'Viewed',
|
||||
tab: TableTab.Viewed,
|
||||
tableName: WelcomeTable.Recents,
|
||||
},
|
||||
{
|
||||
tab: 'Edited',
|
||||
tab: TableTab.Edited,
|
||||
tableName: WelcomeTable.Recents,
|
||||
},
|
||||
{
|
||||
tab: 'Created',
|
||||
tab: TableTab.Created,
|
||||
tableName: WelcomeTable.Recents,
|
||||
},
|
||||
];
|
||||
|
|
@ -68,10 +69,10 @@ describe('EmptyState', () => {
|
|||
expect(wrapper).toExist();
|
||||
const textContainer = wrapper.find('.ant-empty-description');
|
||||
expect(textContainer.text()).toEqual(
|
||||
variant.tab === 'Favorite'
|
||||
variant.tab === TableTab.Favorite
|
||||
? "You don't have any favorites yet!"
|
||||
: `No ${
|
||||
variant.tableName === 'SAVED_QUERIES'
|
||||
variant.tableName === WelcomeTable.SavedQueries
|
||||
? 'saved queries'
|
||||
: variant.tableName.toLowerCase()
|
||||
} yet`,
|
||||
|
|
@ -80,13 +81,13 @@ describe('EmptyState', () => {
|
|||
});
|
||||
});
|
||||
recents.forEach(recent => {
|
||||
it(`it renders an ${recent.tab} ${recent.tableName} empty state`, () => {
|
||||
it(`it renders a ${recent.tab} ${recent.tableName} empty state`, () => {
|
||||
const wrapper = mount(<EmptyState {...recent} />);
|
||||
expect(wrapper).toExist();
|
||||
const textContainer = wrapper.find('.ant-empty-description');
|
||||
expect(wrapper.find('.ant-empty-image').children()).toHaveLength(1);
|
||||
expect(textContainer.text()).toContain(
|
||||
`Recently ${recent.tab.toLowerCase()} charts, dashboards, and saved queries will appear here`,
|
||||
`Recently ${recent.tab?.toLowerCase()} charts, dashboards, and saved queries will appear here`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@
|
|||
import React from 'react';
|
||||
import Button from 'src/components/Button';
|
||||
import { Empty } from 'src/components';
|
||||
import { t, styled } from '@superset-ui/core';
|
||||
import { TableTab } from 'src/views/CRUD/types';
|
||||
import { styled, t } from '@superset-ui/core';
|
||||
import { WelcomeTable } from './types';
|
||||
|
||||
const welcomeTableLabels: Record<WelcomeTable, string> = {
|
||||
|
|
@ -29,9 +30,10 @@ const welcomeTableLabels: Record<WelcomeTable, string> = {
|
|||
[WelcomeTable.SavedQueries]: t('saved queries'),
|
||||
};
|
||||
|
||||
interface EmptyStateProps {
|
||||
export interface EmptyStateProps {
|
||||
tableName: WelcomeTable;
|
||||
tab?: string;
|
||||
otherTabTitle?: string;
|
||||
}
|
||||
const EmptyContainer = styled.div`
|
||||
min-height: 200px;
|
||||
|
|
@ -52,7 +54,11 @@ type Redirects = Record<
|
|||
string
|
||||
>;
|
||||
|
||||
export default function EmptyState({ tableName, tab }: EmptyStateProps) {
|
||||
export default function EmptyState({
|
||||
tableName,
|
||||
tab,
|
||||
otherTabTitle,
|
||||
}: EmptyStateProps) {
|
||||
const mineRedirects: Redirects = {
|
||||
[WelcomeTable.Charts]: '/chart/add',
|
||||
[WelcomeTable.Dashboards]: '/dashboard/new',
|
||||
|
|
@ -77,22 +83,23 @@ export default function EmptyState({ tableName, tab }: EmptyStateProps) {
|
|||
const recent = (
|
||||
<span className="no-recents">
|
||||
{(() => {
|
||||
if (tab === 'Viewed') {
|
||||
if (tab === TableTab.Viewed) {
|
||||
return t(
|
||||
`Recently viewed charts, dashboards, and saved queries will appear here`,
|
||||
);
|
||||
}
|
||||
if (tab === 'Created') {
|
||||
if (tab === TableTab.Created) {
|
||||
return t(
|
||||
'Recently created charts, dashboards, and saved queries will appear here',
|
||||
);
|
||||
}
|
||||
if (tab === 'Examples') {
|
||||
return t('Example %(tableName)s will appear here', {
|
||||
if (tab === TableTab.Other) {
|
||||
return t('%(other)s %(tableName)s will appear here', {
|
||||
other: otherTabTitle || t('Other'),
|
||||
tableName: tableName.toLowerCase(),
|
||||
});
|
||||
}
|
||||
if (tab === 'Edited') {
|
||||
if (tab === TableTab.Edited) {
|
||||
return t(
|
||||
`Recently edited charts, dashboards, and saved queries will appear here`,
|
||||
);
|
||||
|
|
@ -101,17 +108,24 @@ export default function EmptyState({ tableName, tab }: EmptyStateProps) {
|
|||
})()}
|
||||
</span>
|
||||
);
|
||||
|
||||
// Mine and Recent Activity(all tabs) tab empty state
|
||||
if (tab === 'Mine' || tableName === 'RECENTS' || tab === 'Examples') {
|
||||
if (
|
||||
tab === TableTab.Mine ||
|
||||
tableName === WelcomeTable.Recents ||
|
||||
tab === TableTab.Other
|
||||
) {
|
||||
return (
|
||||
<EmptyContainer>
|
||||
<Empty
|
||||
image={`/static/assets/images/${tableIcon[tableName]}`}
|
||||
description={
|
||||
tableName === 'RECENTS' || tab === 'Examples' ? recent : mine
|
||||
tableName === WelcomeTable.Recents || tab === TableTab.Other
|
||||
? recent
|
||||
: mine
|
||||
}
|
||||
>
|
||||
{tableName !== 'RECENTS' && (
|
||||
{tableName !== WelcomeTable.Recents && (
|
||||
<ButtonContainer>
|
||||
<Button
|
||||
buttonStyle="primary"
|
||||
|
|
@ -120,7 +134,7 @@ export default function EmptyState({ tableName, tab }: EmptyStateProps) {
|
|||
}}
|
||||
>
|
||||
<i className="fa fa-plus" />
|
||||
{tableName === 'SAVED_QUERIES'
|
||||
{tableName === WelcomeTable.SavedQueries
|
||||
? t('SQL query')
|
||||
: tableName
|
||||
.split('')
|
||||
|
|
@ -152,7 +166,7 @@ export default function EmptyState({ tableName, tab }: EmptyStateProps) {
|
|||
>
|
||||
{t('See all %(tableName)s', {
|
||||
tableName:
|
||||
tableName === 'SAVED_QUERIES'
|
||||
tableName === WelcomeTable.SavedQueries
|
||||
? t('SQL Lab queries')
|
||||
: welcomeTableLabels[tableName],
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ 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 { LoadingCards } from 'src/views/CRUD/welcome/Welcome';
|
||||
import { TableTab } from 'src/views/CRUD/types';
|
||||
import withToasts from 'src/components/MessageToasts/withToasts';
|
||||
import { AntdDropdown } from 'src/components';
|
||||
import { Menu } from 'src/components/Menu';
|
||||
|
|
@ -30,10 +31,12 @@ import ListViewCard from 'src/components/ListViewCard';
|
|||
import DeleteModal from 'src/components/DeleteModal';
|
||||
import Icons from 'src/components/Icons';
|
||||
import SubMenu from 'src/views/components/SubMenu';
|
||||
import { User } from 'src/types/bootstrapTypes';
|
||||
import EmptyState from './EmptyState';
|
||||
import {
|
||||
CardContainer,
|
||||
createErrorHandler,
|
||||
getFilterValues,
|
||||
PAGE_SIZE,
|
||||
shortenSQL,
|
||||
} from '../utils';
|
||||
|
|
@ -56,9 +59,7 @@ interface Query {
|
|||
}
|
||||
|
||||
interface SavedQueriesProps {
|
||||
user: {
|
||||
userId: string | number;
|
||||
};
|
||||
user: User;
|
||||
queryFilter: string;
|
||||
addDangerToast: (arg0: string) => void;
|
||||
addSuccessToast: (arg0: string) => void;
|
||||
|
|
@ -134,7 +135,7 @@ const SavedQueries = ({
|
|||
[],
|
||||
false,
|
||||
);
|
||||
const [queryFilter, setQueryFilter] = useState('Mine');
|
||||
const [activeTab, setActiveTab] = useState(TableTab.Mine);
|
||||
const [queryDeleteModal, setQueryDeleteModal] = useState(false);
|
||||
const [currentlyEdited, setCurrentlyEdited] = useState<Query>({});
|
||||
const [ifMine, setMine] = useState(true);
|
||||
|
|
@ -149,13 +150,11 @@ const SavedQueries = ({
|
|||
}).then(
|
||||
() => {
|
||||
const queryParams = {
|
||||
filters: [
|
||||
{
|
||||
id: 'created_by',
|
||||
operator: 'rel_o_m',
|
||||
value: `${user?.userId}`,
|
||||
},
|
||||
],
|
||||
filters: getFilterValues(
|
||||
TableTab.Created,
|
||||
WelcomeTable.SavedQueries,
|
||||
user,
|
||||
),
|
||||
pageSize: PAGE_SIZE,
|
||||
sortBy: [
|
||||
{
|
||||
|
|
@ -178,25 +177,7 @@ const SavedQueries = ({
|
|||
);
|
||||
};
|
||||
|
||||
const getFilters = (filterName: string) => {
|
||||
const filters = [];
|
||||
if (filterName === 'Mine') {
|
||||
filters.push({
|
||||
id: 'created_by',
|
||||
operator: 'rel_o_m',
|
||||
value: `${user?.userId}`,
|
||||
});
|
||||
} else {
|
||||
filters.push({
|
||||
id: 'id',
|
||||
operator: 'saved_query_is_fav',
|
||||
value: true,
|
||||
});
|
||||
}
|
||||
return filters;
|
||||
};
|
||||
|
||||
const getData = (filter: string) =>
|
||||
const getData = (tab: TableTab) =>
|
||||
fetchData({
|
||||
pageIndex: 0,
|
||||
pageSize: PAGE_SIZE,
|
||||
|
|
@ -206,7 +187,7 @@ const SavedQueries = ({
|
|||
desc: true,
|
||||
},
|
||||
],
|
||||
filters: getFilters(filter),
|
||||
filters: getFilterValues(tab, WelcomeTable.SavedQueries, user),
|
||||
});
|
||||
|
||||
const renderMenu = (query: Query) => (
|
||||
|
|
@ -263,21 +244,13 @@ const SavedQueries = ({
|
|||
/>
|
||||
)}
|
||||
<SubMenu
|
||||
activeChild={queryFilter}
|
||||
activeChild={activeTab}
|
||||
tabs={[
|
||||
/* @TODO uncomment when fav functionality is implemented
|
||||
{
|
||||
name: 'Favorite',
|
||||
label: t('Favorite'),
|
||||
onClick: () => {
|
||||
getData('Favorite').then(() => setQueryFilter('Favorite'));
|
||||
},
|
||||
},
|
||||
*/
|
||||
{
|
||||
name: 'Mine',
|
||||
name: TableTab.Mine,
|
||||
label: t('Mine'),
|
||||
onClick: () => getData('Mine').then(() => setQueryFilter('Mine')),
|
||||
onClick: () =>
|
||||
getData(TableTab.Mine).then(() => setActiveTab(TableTab.Mine)),
|
||||
},
|
||||
]}
|
||||
buttons={[
|
||||
|
|
@ -366,7 +339,7 @@ const SavedQueries = ({
|
|||
))}
|
||||
</CardContainer>
|
||||
) : (
|
||||
<EmptyState tableName={WelcomeTable.SavedQueries} tab={queryFilter} />
|
||||
<EmptyState tableName={WelcomeTable.SavedQueries} tab={activeTab} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -16,8 +16,13 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { styled, t, getExtensionsRegistry } from '@superset-ui/core';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
getExtensionsRegistry,
|
||||
JsonObject,
|
||||
styled,
|
||||
t,
|
||||
} from '@superset-ui/core';
|
||||
import Collapse from 'src/components/Collapse';
|
||||
import { User } from 'src/types/bootstrapTypes';
|
||||
import { reject } from 'lodash';
|
||||
|
|
@ -40,7 +45,9 @@ import {
|
|||
} from 'src/views/CRUD/utils';
|
||||
import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags';
|
||||
import { AntdSwitch } from 'src/components';
|
||||
|
||||
import getBootstrapData from 'src/utils/getBootstrapData';
|
||||
import { TableTab } from 'src/views/CRUD/types';
|
||||
import { WelcomePageLastTab } from './types';
|
||||
import ActivityTable from './ActivityTable';
|
||||
import ChartTable from './ChartTable';
|
||||
import SavedQueries from './SavedQueries';
|
||||
|
|
@ -54,10 +61,10 @@ interface WelcomeProps {
|
|||
}
|
||||
|
||||
export interface ActivityData {
|
||||
Created?: Array<object>;
|
||||
Edited?: Array<object>;
|
||||
Viewed?: Array<object>;
|
||||
Examples?: Array<object>;
|
||||
[TableTab.Created]?: JsonObject[];
|
||||
[TableTab.Edited]?: JsonObject[];
|
||||
[TableTab.Viewed]?: JsonObject[];
|
||||
[TableTab.Other]?: JsonObject[];
|
||||
}
|
||||
|
||||
interface LoadingProps {
|
||||
|
|
@ -138,6 +145,8 @@ const WelcomeNav = styled.div`
|
|||
`}
|
||||
`;
|
||||
|
||||
const bootstrapData = getBootstrapData();
|
||||
|
||||
export const LoadingCards = ({ cover }: LoadingProps) => (
|
||||
<CardContainer showThumbnails={cover} className="loading-cards">
|
||||
{[...new Array(loadingCardCount)].map((_, index) => (
|
||||
|
|
@ -185,28 +194,58 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
|
|||
'welcome.main.replacement',
|
||||
);
|
||||
|
||||
const [otherTabTitle, otherTabFilters] = useMemo(() => {
|
||||
const lastTab = bootstrapData.common?.conf
|
||||
.WELCOME_PAGE_LAST_TAB as WelcomePageLastTab;
|
||||
const [customTitle, customFilter] = Array.isArray(lastTab)
|
||||
? lastTab
|
||||
: [undefined, undefined];
|
||||
if (customTitle && customFilter) {
|
||||
return [t(customTitle), customFilter];
|
||||
}
|
||||
if (lastTab === 'all') {
|
||||
return [t('All'), []];
|
||||
}
|
||||
return [
|
||||
t('Examples'),
|
||||
[
|
||||
{
|
||||
col: 'created_by',
|
||||
opr: 'rel_o_m',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
];
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!otherTabFilters) {
|
||||
return;
|
||||
}
|
||||
const activeTab = getItem(LocalStorageKeys.homepage_activity_filter, null);
|
||||
setActiveState(collapseState.length > 0 ? collapseState : DEFAULT_TAB_ARR);
|
||||
getRecentAcitivtyObjs(user.userId!, recent, addDangerToast)
|
||||
getRecentAcitivtyObjs(user.userId!, recent, addDangerToast, otherTabFilters)
|
||||
.then(res => {
|
||||
const data: ActivityData | null = {};
|
||||
data.Examples = res.examples;
|
||||
data[TableTab.Other] = res.other;
|
||||
if (res.viewed) {
|
||||
const filtered = reject(res.viewed, ['item_url', null]).map(r => r);
|
||||
data.Viewed = filtered;
|
||||
if (!activeTab && data.Viewed) {
|
||||
setActiveChild('Viewed');
|
||||
} else if (!activeTab && !data.Viewed) {
|
||||
setActiveChild('Created');
|
||||
} else setActiveChild(activeTab || 'Created');
|
||||
} else if (!activeTab) setActiveChild('Created');
|
||||
data[TableTab.Viewed] = filtered;
|
||||
if (!activeTab && data[TableTab.Viewed]) {
|
||||
setActiveChild(TableTab.Viewed);
|
||||
} else if (!activeTab && !data[TableTab.Viewed]) {
|
||||
setActiveChild(TableTab.Created);
|
||||
} else setActiveChild(activeTab || TableTab.Created);
|
||||
} else if (!activeTab) setActiveChild(TableTab.Created);
|
||||
else setActiveChild(activeTab);
|
||||
setActivityData(activityData => ({ ...activityData, ...data }));
|
||||
})
|
||||
.catch(
|
||||
createErrorHandler((errMsg: unknown) => {
|
||||
setActivityData(activityData => ({ ...activityData, Viewed: [] }));
|
||||
setActivityData(activityData => ({
|
||||
...activityData,
|
||||
[TableTab.Viewed]: [],
|
||||
}));
|
||||
addDangerToast(
|
||||
t('There was an issue fetching your recent activity: %s', errMsg),
|
||||
);
|
||||
|
|
@ -255,7 +294,7 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
|
|||
t('There was an issues fetching your saved queries: %s', err),
|
||||
);
|
||||
});
|
||||
}, []);
|
||||
}, [otherTabFilters]);
|
||||
|
||||
const handleToggle = () => {
|
||||
setChecked(!checked);
|
||||
|
|
@ -277,13 +316,13 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
|
|||
}, [chartData, queryData, dashboardData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!collapseState && activityData?.Viewed?.length) {
|
||||
if (!collapseState && activityData?.[TableTab.Viewed]?.length) {
|
||||
setActiveState(activeState => ['1', ...activeState]);
|
||||
}
|
||||
}, [activityData]);
|
||||
|
||||
const isRecentActivityLoading =
|
||||
!activityData?.Examples && !activityData?.Viewed;
|
||||
!activityData?.[TableTab.Other] && !activityData?.[TableTab.Viewed];
|
||||
return (
|
||||
<WelcomeContainer>
|
||||
{WelcomeMessageExtension && <WelcomeMessageExtension />}
|
||||
|
|
@ -308,9 +347,9 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
|
|||
>
|
||||
<Collapse.Panel header={t('Recents')} key="1">
|
||||
{activityData &&
|
||||
(activityData.Viewed ||
|
||||
activityData.Examples ||
|
||||
activityData.Created) &&
|
||||
(activityData[TableTab.Viewed] ||
|
||||
activityData[TableTab.Other] ||
|
||||
activityData[TableTab.Created]) &&
|
||||
activeChild !== 'Loading' ? (
|
||||
<ActivityTable
|
||||
user={{ userId: user.userId! }} // user is definitely not a guest user on this page
|
||||
|
|
@ -331,7 +370,9 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
|
|||
user={user}
|
||||
mine={dashboardData}
|
||||
showThumbnails={checked}
|
||||
examples={activityData?.Examples}
|
||||
otherTabData={activityData?.[TableTab.Other]}
|
||||
otherTabFilters={otherTabFilters}
|
||||
otherTabTitle={otherTabTitle}
|
||||
/>
|
||||
)}
|
||||
</Collapse.Panel>
|
||||
|
|
@ -343,7 +384,9 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
|
|||
showThumbnails={checked}
|
||||
user={user}
|
||||
mine={chartData}
|
||||
examples={activityData?.Examples}
|
||||
otherTabData={activityData?.[TableTab.Other]}
|
||||
otherTabFilters={otherTabFilters}
|
||||
otherTabTitle={otherTabTitle}
|
||||
/>
|
||||
)}
|
||||
</Collapse.Panel>
|
||||
|
|
|
|||
|
|
@ -17,9 +17,13 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { Filter } from '../types';
|
||||
|
||||
export enum WelcomeTable {
|
||||
Charts = 'CHARTS',
|
||||
Dashboards = 'DASHBOARDS',
|
||||
Recents = 'RECENTS',
|
||||
SavedQueries = 'SAVED_QUERIES',
|
||||
}
|
||||
|
||||
export type WelcomePageLastTab = 'examples' | 'all' | [string, Filter[]];
|
||||
|
|
|
|||
|
|
@ -29,65 +29,18 @@ import { GenericLink } from 'src/components/GenericLink/GenericLink';
|
|||
import Icons from 'src/components/Icons';
|
||||
import { useUiConfig } from 'src/components/UiConfigContext';
|
||||
import { URL_PARAMS } from 'src/constants';
|
||||
import {
|
||||
MenuObjectChildProps,
|
||||
MenuObjectProps,
|
||||
MenuData,
|
||||
} from 'src/types/bootstrapTypes';
|
||||
import RightMenu from './RightMenu';
|
||||
import { Languages } from './LanguagePicker';
|
||||
|
||||
interface BrandProps {
|
||||
path: string;
|
||||
icon: string;
|
||||
alt: string;
|
||||
tooltip: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface NavBarProps {
|
||||
show_watermark: boolean;
|
||||
bug_report_url?: string;
|
||||
version_string?: string;
|
||||
version_sha?: string;
|
||||
build_number?: string;
|
||||
documentation_url?: string;
|
||||
languages: Languages;
|
||||
show_language_picker: boolean;
|
||||
user_is_anonymous: boolean;
|
||||
user_info_url: string;
|
||||
user_login_url: string;
|
||||
user_logout_url: string;
|
||||
user_profile_url: string | null;
|
||||
locale: string;
|
||||
}
|
||||
|
||||
export interface MenuProps {
|
||||
data: {
|
||||
menu: MenuObjectProps[];
|
||||
brand: BrandProps;
|
||||
navbar_right: NavBarProps;
|
||||
settings: MenuObjectProps[];
|
||||
environment_tag: {
|
||||
text: string;
|
||||
color: string;
|
||||
};
|
||||
};
|
||||
interface MenuProps {
|
||||
data: MenuData;
|
||||
isFrontendRoute?: (path?: string) => boolean;
|
||||
}
|
||||
|
||||
export interface MenuObjectChildProps {
|
||||
label: string;
|
||||
name?: string;
|
||||
icon?: string;
|
||||
index?: number;
|
||||
url?: string;
|
||||
isFrontendRoute?: boolean;
|
||||
perm?: string | boolean;
|
||||
view?: string;
|
||||
disable?: boolean;
|
||||
}
|
||||
|
||||
export interface MenuObjectProps extends MenuObjectChildProps {
|
||||
childs?: (MenuObjectChildProps | string)[];
|
||||
isHeader?: boolean;
|
||||
}
|
||||
|
||||
const StyledHeader = styled.header`
|
||||
${({ theme }) => `
|
||||
background-color: ${theme.colors.grayscale.light5};
|
||||
|
|
|
|||
|
|
@ -38,7 +38,10 @@ import Icons from 'src/components/Icons';
|
|||
import Label from 'src/components/Label';
|
||||
import { findPermission } from 'src/utils/findPermission';
|
||||
import { isUserAdmin } from 'src/dashboard/util/permissionUtils';
|
||||
import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
|
||||
import {
|
||||
MenuObjectProps,
|
||||
UserWithPermissionsAndRoles,
|
||||
} from 'src/types/bootstrapTypes';
|
||||
import { RootState } from 'src/dashboard/types';
|
||||
import LanguagePicker from './LanguagePicker';
|
||||
import DatabaseModal from '../CRUD/data/database/DatabaseModal';
|
||||
|
|
@ -48,7 +51,6 @@ import {
|
|||
GlobalMenuDataOptions,
|
||||
RightMenuProps,
|
||||
} from './types';
|
||||
import { MenuObjectProps } from './Menu';
|
||||
import AddDatasetModal from '../CRUD/data/dataset/AddDatasetModal';
|
||||
|
||||
const extensionsRegistry = getExtensionsRegistry();
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import { Row } from 'src/components';
|
|||
import { Menu, MenuMode, MainNav as DropdownMenu } from 'src/components/Menu';
|
||||
import Button, { OnClickHandler } from 'src/components/Button';
|
||||
import Icons from 'src/components/Icons';
|
||||
import { MenuObjectProps } from './Menu';
|
||||
import { MenuObjectProps } from 'src/types/bootstrapTypes';
|
||||
|
||||
const StyledHeader = styled.div`
|
||||
margin-bottom: ${({ theme }) => theme.gridUnit * 4}px;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { NavBarProps, MenuObjectProps } from './Menu';
|
||||
import { NavBarProps, MenuObjectProps } from 'src/types/bootstrapTypes';
|
||||
|
||||
export interface ExtentionConfigs {
|
||||
ALLOWED_EXTENSIONS: Array<any>;
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ from typing import (
|
|||
Literal,
|
||||
Optional,
|
||||
Set,
|
||||
Tuple,
|
||||
Type,
|
||||
TYPE_CHECKING,
|
||||
Union,
|
||||
|
|
@ -1452,6 +1453,17 @@ ADVANCED_DATA_TYPES: Dict[str, AdvancedDataType] = {
|
|||
"port": internet_port,
|
||||
}
|
||||
|
||||
# By default, the Welcome page features example charts and dashboards. This can be
|
||||
# changed to show all charts/dashboards the user has access to, or a custom view
|
||||
# by providing the title and a FAB filter:
|
||||
# WELCOME_PAGE_LAST_TAB = (
|
||||
# "Xyz",
|
||||
# [{"col": 'created_by', "opr": 'rel_o_m', "value": 10}],
|
||||
# )
|
||||
WELCOME_PAGE_LAST_TAB: Union[
|
||||
Literal["examples", "all"], Tuple[str, List[Dict[str, Any]]]
|
||||
] = "examples"
|
||||
|
||||
# Configuration for environment tag shown on the navbar. Setting 'text' to '' will hide the tag.
|
||||
# 'color' can either be a hex color code, or a dot-indexed theme color (e.g. error.base)
|
||||
ENVIRONMENT_TAG_CONFIG = {
|
||||
|
|
|
|||
|
|
@ -19,13 +19,11 @@ import textwrap
|
|||
from typing import Dict, List, Tuple, Union
|
||||
|
||||
import pandas as pd
|
||||
from flask_appbuilder.security.sqla.models import User
|
||||
from sqlalchemy import DateTime, inspect, String
|
||||
from sqlalchemy.sql import column
|
||||
|
||||
from superset import app, db, security_manager
|
||||
from superset import app, db
|
||||
from superset.connectors.sqla.models import SqlaTable, SqlMetric, TableColumn
|
||||
from superset.exceptions import NoDataException
|
||||
from superset.models.core import Database
|
||||
from superset.models.dashboard import Dashboard
|
||||
from superset.models.slice import Slice
|
||||
|
|
@ -42,17 +40,6 @@ from .helpers import (
|
|||
)
|
||||
|
||||
|
||||
def get_admin_user() -> User:
|
||||
admin = security_manager.find_user("admin")
|
||||
if admin is None:
|
||||
raise NoDataException(
|
||||
"Admin user does not exist. "
|
||||
"Please, check if test users are properly loaded "
|
||||
"(`superset load_test_users`)."
|
||||
)
|
||||
return admin
|
||||
|
||||
|
||||
def gen_filter(
|
||||
subject: str, comparator: str, operator: str = "=="
|
||||
) -> Dict[str, Union[bool, str]]:
|
||||
|
|
@ -125,7 +112,7 @@ def load_birth_names(
|
|||
|
||||
db.session.commit()
|
||||
|
||||
slices, _ = create_slices(obj, admin_owner=True)
|
||||
slices, _ = create_slices(obj)
|
||||
create_dashboard(slices)
|
||||
|
||||
|
||||
|
|
@ -165,7 +152,7 @@ def _add_table_metrics(datasource: SqlaTable) -> None:
|
|||
datasource.metrics = metrics
|
||||
|
||||
|
||||
def create_slices(tbl: SqlaTable, admin_owner: bool) -> Tuple[List[Slice], List[Slice]]:
|
||||
def create_slices(tbl: SqlaTable) -> Tuple[List[Slice], List[Slice]]:
|
||||
metrics = [
|
||||
{
|
||||
"expressionType": "SIMPLE",
|
||||
|
|
@ -206,26 +193,16 @@ def create_slices(tbl: SqlaTable, admin_owner: bool) -> Tuple[List[Slice], List[
|
|||
],
|
||||
}
|
||||
|
||||
admin = get_admin_user()
|
||||
if admin_owner:
|
||||
slice_props = dict(
|
||||
datasource_id=tbl.id,
|
||||
datasource_type=DatasourceType.TABLE,
|
||||
owners=[admin],
|
||||
created_by=admin,
|
||||
)
|
||||
else:
|
||||
slice_props = dict(
|
||||
datasource_id=tbl.id,
|
||||
datasource_type=DatasourceType.TABLE,
|
||||
owners=[],
|
||||
created_by=admin,
|
||||
)
|
||||
slice_kwargs = {
|
||||
"datasource_id": tbl.id,
|
||||
"datasource_type": DatasourceType.TABLE,
|
||||
"owners": [],
|
||||
}
|
||||
|
||||
print("Creating some slices")
|
||||
slices = [
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Participants",
|
||||
viz_type="big_number",
|
||||
params=get_slice_json(
|
||||
|
|
@ -238,7 +215,7 @@ def create_slices(tbl: SqlaTable, admin_owner: bool) -> Tuple[List[Slice], List[
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Genders",
|
||||
viz_type="pie",
|
||||
params=get_slice_json(
|
||||
|
|
@ -246,7 +223,7 @@ def create_slices(tbl: SqlaTable, admin_owner: bool) -> Tuple[List[Slice], List[
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Trends",
|
||||
viz_type="line",
|
||||
params=get_slice_json(
|
||||
|
|
@ -260,7 +237,7 @@ def create_slices(tbl: SqlaTable, admin_owner: bool) -> Tuple[List[Slice], List[
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Genders by State",
|
||||
viz_type="dist_bar",
|
||||
params=get_slice_json(
|
||||
|
|
@ -296,7 +273,7 @@ def create_slices(tbl: SqlaTable, admin_owner: bool) -> Tuple[List[Slice], List[
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Girls",
|
||||
viz_type="table",
|
||||
params=get_slice_json(
|
||||
|
|
@ -309,7 +286,7 @@ def create_slices(tbl: SqlaTable, admin_owner: bool) -> Tuple[List[Slice], List[
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Girl Name Cloud",
|
||||
viz_type="word_cloud",
|
||||
params=get_slice_json(
|
||||
|
|
@ -325,7 +302,7 @@ def create_slices(tbl: SqlaTable, admin_owner: bool) -> Tuple[List[Slice], List[
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Boys",
|
||||
viz_type="table",
|
||||
params=get_slice_json(
|
||||
|
|
@ -338,7 +315,7 @@ def create_slices(tbl: SqlaTable, admin_owner: bool) -> Tuple[List[Slice], List[
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Boy Name Cloud",
|
||||
viz_type="word_cloud",
|
||||
params=get_slice_json(
|
||||
|
|
@ -354,7 +331,7 @@ def create_slices(tbl: SqlaTable, admin_owner: bool) -> Tuple[List[Slice], List[
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Top 10 Girl Name Share",
|
||||
viz_type="area",
|
||||
params=get_slice_json(
|
||||
|
|
@ -371,7 +348,7 @@ def create_slices(tbl: SqlaTable, admin_owner: bool) -> Tuple[List[Slice], List[
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Top 10 Boy Name Share",
|
||||
viz_type="area",
|
||||
params=get_slice_json(
|
||||
|
|
@ -388,7 +365,7 @@ def create_slices(tbl: SqlaTable, admin_owner: bool) -> Tuple[List[Slice], List[
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Pivot Table v2",
|
||||
viz_type="pivot_table_v2",
|
||||
params=get_slice_json(
|
||||
|
|
@ -411,7 +388,7 @@ def create_slices(tbl: SqlaTable, admin_owner: bool) -> Tuple[List[Slice], List[
|
|||
]
|
||||
misc_slices = [
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Average and Sum Trends",
|
||||
viz_type="dual_line",
|
||||
params=get_slice_json(
|
||||
|
|
@ -430,13 +407,13 @@ def create_slices(tbl: SqlaTable, admin_owner: bool) -> Tuple[List[Slice], List[
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Num Births Trend",
|
||||
viz_type="line",
|
||||
params=get_slice_json(defaults, viz_type="line", metrics=metrics),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Daily Totals",
|
||||
viz_type="table",
|
||||
params=get_slice_json(
|
||||
|
|
@ -449,7 +426,7 @@ def create_slices(tbl: SqlaTable, admin_owner: bool) -> Tuple[List[Slice], List[
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Number of California Births",
|
||||
viz_type="big_number_total",
|
||||
params=get_slice_json(
|
||||
|
|
@ -468,7 +445,7 @@ def create_slices(tbl: SqlaTable, admin_owner: bool) -> Tuple[List[Slice], List[
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Top 10 California Names Timeseries",
|
||||
viz_type="line",
|
||||
params=get_slice_json(
|
||||
|
|
@ -500,7 +477,7 @@ def create_slices(tbl: SqlaTable, admin_owner: bool) -> Tuple[List[Slice], List[
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Names Sorted by Num in California",
|
||||
viz_type="table",
|
||||
params=get_slice_json(
|
||||
|
|
@ -520,7 +497,7 @@ def create_slices(tbl: SqlaTable, admin_owner: bool) -> Tuple[List[Slice], List[
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Number of Girls",
|
||||
viz_type="big_number_total",
|
||||
params=get_slice_json(
|
||||
|
|
@ -533,7 +510,7 @@ def create_slices(tbl: SqlaTable, admin_owner: bool) -> Tuple[List[Slice], List[
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Pivot Table",
|
||||
viz_type="pivot_table",
|
||||
params=get_slice_json(
|
||||
|
|
@ -557,12 +534,10 @@ def create_slices(tbl: SqlaTable, admin_owner: bool) -> Tuple[List[Slice], List[
|
|||
|
||||
def create_dashboard(slices: List[Slice]) -> Dashboard:
|
||||
print("Creating a dashboard")
|
||||
admin = get_admin_user()
|
||||
dash = db.session.query(Dashboard).filter_by(slug="births").first()
|
||||
if not dash:
|
||||
dash = Dashboard()
|
||||
dash.owners = [admin]
|
||||
dash.created_by = admin
|
||||
dash.owners = []
|
||||
db.session.add(dash)
|
||||
|
||||
dash.published = True
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ from typing import List
|
|||
|
||||
from sqlalchemy import inspect
|
||||
|
||||
from superset import db, security_manager
|
||||
from superset import db
|
||||
from superset.connectors.sqla.models import SqlaTable
|
||||
from superset.models.dashboard import Dashboard
|
||||
from superset.models.slice import Slice
|
||||
|
|
@ -41,13 +41,11 @@ DASH_SLUG = "supported_charts_dash"
|
|||
|
||||
|
||||
def create_slices(tbl: SqlaTable) -> List[Slice]:
|
||||
admin = security_manager.find_user("admin")
|
||||
slice_props = dict(
|
||||
datasource_id=tbl.id,
|
||||
datasource_type=DatasourceType.TABLE,
|
||||
owners=[admin],
|
||||
created_by=admin,
|
||||
)
|
||||
slice_kwargs = {
|
||||
"datasource_id": tbl.id,
|
||||
"datasource_type": DatasourceType.TABLE,
|
||||
"owners": [],
|
||||
}
|
||||
|
||||
defaults = {
|
||||
"limit": "25",
|
||||
|
|
@ -62,7 +60,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
# TIER 1
|
||||
# ---------------------
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Big Number",
|
||||
viz_type="big_number_total",
|
||||
params=get_slice_json(
|
||||
|
|
@ -72,7 +70,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Big Number with Trendline",
|
||||
viz_type="big_number",
|
||||
params=get_slice_json(
|
||||
|
|
@ -82,7 +80,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Table",
|
||||
viz_type="table",
|
||||
params=get_slice_json(
|
||||
|
|
@ -93,7 +91,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Pivot Table",
|
||||
viz_type="pivot_table_v2",
|
||||
params=get_slice_json(
|
||||
|
|
@ -105,7 +103,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Time-Series Line Chart",
|
||||
viz_type="echarts_timeseries_line",
|
||||
params=get_slice_json(
|
||||
|
|
@ -116,7 +114,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Time-Series Area Chart",
|
||||
viz_type="echarts_area",
|
||||
params=get_slice_json(
|
||||
|
|
@ -127,7 +125,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Time-Series Bar Chart V2",
|
||||
viz_type="echarts_timeseries_bar",
|
||||
params=get_slice_json(
|
||||
|
|
@ -138,7 +136,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Time-Series Scatter Chart",
|
||||
viz_type="echarts_timeseries_scatter",
|
||||
params=get_slice_json(
|
||||
|
|
@ -149,7 +147,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Pie Chart",
|
||||
viz_type="pie",
|
||||
params=get_slice_json(
|
||||
|
|
@ -161,7 +159,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Bar Chart",
|
||||
viz_type="dist_bar",
|
||||
params=get_slice_json(
|
||||
|
|
@ -175,7 +173,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
# TIER 2
|
||||
# ---------------------
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Box Plot Chart",
|
||||
viz_type="box_plot",
|
||||
params=get_slice_json(
|
||||
|
|
@ -187,7 +185,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Bubble Chart",
|
||||
viz_type="bubble",
|
||||
params=get_slice_json(
|
||||
|
|
@ -217,7 +215,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Calendar Heatmap",
|
||||
viz_type="cal_heatmap",
|
||||
params=get_slice_json(
|
||||
|
|
@ -228,7 +226,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Chord Chart",
|
||||
viz_type="chord",
|
||||
params=get_slice_json(
|
||||
|
|
@ -240,7 +238,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Time-Series Percent Change Chart",
|
||||
viz_type="compare",
|
||||
params=get_slice_json(
|
||||
|
|
@ -251,7 +249,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Time-Series Generic Chart",
|
||||
viz_type="echarts_timeseries",
|
||||
params=get_slice_json(
|
||||
|
|
@ -262,7 +260,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Time-Series Smooth Line Chart",
|
||||
viz_type="echarts_timeseries_smooth",
|
||||
params=get_slice_json(
|
||||
|
|
@ -273,7 +271,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Time-Series Step Line Chart",
|
||||
viz_type="echarts_timeseries_step",
|
||||
params=get_slice_json(
|
||||
|
|
@ -284,7 +282,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Funnel Chart",
|
||||
viz_type="funnel",
|
||||
params=get_slice_json(
|
||||
|
|
@ -295,7 +293,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Gauge Chart",
|
||||
viz_type="gauge_chart",
|
||||
params=get_slice_json(
|
||||
|
|
@ -306,7 +304,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Heatmap Chart",
|
||||
viz_type="heatmap",
|
||||
params=get_slice_json(
|
||||
|
|
@ -318,7 +316,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Line Chart",
|
||||
viz_type="line",
|
||||
params=get_slice_json(
|
||||
|
|
@ -329,7 +327,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Mixed Chart",
|
||||
viz_type="mixed_timeseries",
|
||||
params=get_slice_json(
|
||||
|
|
@ -342,7 +340,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Partition Chart",
|
||||
viz_type="partition",
|
||||
params=get_slice_json(
|
||||
|
|
@ -353,7 +351,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Radar Chart",
|
||||
viz_type="radar",
|
||||
params=get_slice_json(
|
||||
|
|
@ -376,7 +374,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Nightingale Chart",
|
||||
viz_type="rose",
|
||||
params=get_slice_json(
|
||||
|
|
@ -387,7 +385,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Sankey Chart",
|
||||
viz_type="sankey",
|
||||
params=get_slice_json(
|
||||
|
|
@ -398,7 +396,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Sunburst Chart",
|
||||
viz_type="sunburst",
|
||||
params=get_slice_json(
|
||||
|
|
@ -409,7 +407,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Treemap Chart",
|
||||
viz_type="treemap",
|
||||
params=get_slice_json(
|
||||
|
|
@ -420,7 +418,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Treemap V2 Chart",
|
||||
viz_type="treemap_v2",
|
||||
params=get_slice_json(
|
||||
|
|
@ -431,7 +429,7 @@ def create_slices(tbl: SqlaTable) -> List[Slice]:
|
|||
),
|
||||
),
|
||||
Slice(
|
||||
**slice_props,
|
||||
**slice_kwargs,
|
||||
slice_name="Word Cloud Chart",
|
||||
viz_type="word_cloud",
|
||||
params=get_slice_json(
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ FRONTEND_CONF_KEYS = (
|
|||
"DEFAULT_TIME_FILTER",
|
||||
"HTML_SANITIZATION",
|
||||
"HTML_SANITIZATION_SCHEMA_EXTENSIONS",
|
||||
"WELCOME_PAGE_LAST_TAB",
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ def _create_dashboards():
|
|||
|
||||
from superset.examples.birth_names import create_dashboard, create_slices
|
||||
|
||||
slices, _ = create_slices(table, admin_owner=False)
|
||||
slices, _ = create_slices(table)
|
||||
dash = create_dashboard(slices)
|
||||
slices_ids_to_delete = [slice.id for slice in slices]
|
||||
dash_id_to_delete = dash.id
|
||||
|
|
|
|||
Loading…
Reference in New Issue