chore(homepage): separate out api calls to make homepage load more dynamically (#13500)
* separate out api calls * add new loading states * remove consoles * update tests * fix types and lint * make code more robust and add test * address comments * address comments * fix lint
This commit is contained in:
parent
54b2bda2b0
commit
bbc306c64f
|
|
@ -18,6 +18,10 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import { styledMount as mount } from 'spec/helpers/theming';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { ReactWrapper } from 'enzyme';
|
||||
import { Provider } from 'react-redux';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import thunk from 'redux-thunk';
|
||||
import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
|
||||
import configureStore from 'redux-mock-store';
|
||||
|
|
@ -26,6 +30,9 @@ import ActivityTable from 'src/views/CRUD/welcome/ActivityTable';
|
|||
const mockStore = configureStore([thunk]);
|
||||
const store = mockStore({});
|
||||
|
||||
const chartsEndpoint = 'glob:*/api/v1/chart/?*';
|
||||
const dashboardsEndpoint = 'glob:*/api/v1/dashboard/?*';
|
||||
|
||||
const mockData = {
|
||||
Viewed: [
|
||||
{
|
||||
|
|
@ -36,14 +43,6 @@ const mockData = {
|
|||
table: {},
|
||||
},
|
||||
],
|
||||
Edited: [
|
||||
{
|
||||
dashboard_title: 'Dashboard_Test',
|
||||
changed_on_utc: '24 Feb 2014 10:13:14',
|
||||
url: '/fakeUrl/dashboard',
|
||||
id: '3',
|
||||
},
|
||||
],
|
||||
Created: [
|
||||
{
|
||||
dashboard_title: 'Dashboard_Test',
|
||||
|
|
@ -54,20 +53,48 @@ const mockData = {
|
|||
],
|
||||
};
|
||||
|
||||
fetchMock.get(chartsEndpoint, {
|
||||
result: [
|
||||
{
|
||||
slice_name: 'ChartyChart',
|
||||
changed_on_utc: '24 Feb 2014 10:13:14',
|
||||
url: '/fakeUrl/explore',
|
||||
id: '4',
|
||||
table: {},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
fetchMock.get(dashboardsEndpoint, {
|
||||
result: [
|
||||
{
|
||||
dashboard_title: 'Dashboard_Test',
|
||||
changed_on_utc: '24 Feb 2014 10:13:14',
|
||||
url: '/fakeUrl/dashboard',
|
||||
id: '3',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
describe('ActivityTable', () => {
|
||||
const activityProps = {
|
||||
activeChild: 'Edited',
|
||||
activeChild: 'Created',
|
||||
activityData: mockData,
|
||||
setActiveChild: jest.fn(),
|
||||
user: { userId: '1' },
|
||||
loading: false,
|
||||
};
|
||||
const wrapper = mount(<ActivityTable {...activityProps} />, {
|
||||
context: { store },
|
||||
});
|
||||
|
||||
let wrapper: ReactWrapper;
|
||||
|
||||
beforeAll(async () => {
|
||||
await waitForComponentToPaint(wrapper);
|
||||
await act(async () => {
|
||||
wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<ActivityTable {...activityProps} />
|
||||
</Provider>,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('the component renders', () => {
|
||||
|
|
@ -79,4 +106,32 @@ describe('ActivityTable', () => {
|
|||
it('renders ActivityCards', async () => {
|
||||
expect(wrapper.find('ListViewCard')).toExist();
|
||||
});
|
||||
it('calls the getEdited batch call when edited tab is clicked', async () => {
|
||||
act(() => {
|
||||
const handler = wrapper.find('li.no-router a').at(1).prop('onClick');
|
||||
if (handler) {
|
||||
handler({} as any);
|
||||
}
|
||||
});
|
||||
await waitForComponentToPaint(wrapper);
|
||||
const dashboardCall = fetchMock.calls(/dashboard\/\?q/);
|
||||
const chartCall = fetchMock.calls(/chart\/\?q/);
|
||||
expect(chartCall).toHaveLength(1);
|
||||
expect(dashboardCall).toHaveLength(1);
|
||||
});
|
||||
it('show empty state if there is data', () => {
|
||||
const activityProps = {
|
||||
activeChild: 'Created',
|
||||
activityData: {},
|
||||
setActiveChild: jest.fn(),
|
||||
user: { userId: '1' },
|
||||
loading: false,
|
||||
};
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<ActivityTable {...activityProps} />
|
||||
</Provider>,
|
||||
);
|
||||
expect(wrapper.find('EmptyState')).toExist();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -67,7 +67,10 @@ fetchMock.get(savedQueryEndpoint, {
|
|||
result: [],
|
||||
});
|
||||
|
||||
fetchMock.get(recentActivityEndpoint, {});
|
||||
fetchMock.get(recentActivityEndpoint, {
|
||||
Created: [],
|
||||
Viewed: [],
|
||||
});
|
||||
|
||||
fetchMock.get(chartInfoEndpoint, {
|
||||
permissions: [],
|
||||
|
|
@ -122,10 +125,14 @@ describe('Welcome', () => {
|
|||
expect(wrapper.find('CollapsePanel')).toHaveLength(8);
|
||||
});
|
||||
|
||||
it('calls batch method on page load', () => {
|
||||
it('calls api methods in parallel on page load', () => {
|
||||
const chartCall = fetchMock.calls(/chart\/\?q/);
|
||||
const savedQueryCall = fetchMock.calls(/saved_query\/\?q/);
|
||||
const recentCall = fetchMock.calls(/superset\/recent_activity\/*/);
|
||||
const dashboardCall = fetchMock.calls(/dashboard\/\?q/);
|
||||
expect(chartCall).toHaveLength(2);
|
||||
expect(dashboardCall).toHaveLength(2);
|
||||
expect(chartCall).toHaveLength(1);
|
||||
expect(recentCall).toHaveLength(1);
|
||||
expect(savedQueryCall).toHaveLength(1);
|
||||
expect(dashboardCall).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -56,11 +56,12 @@ export function useListViewResource<D extends object = any>(
|
|||
infoEnable = true,
|
||||
defaultCollectionValue: D[] = [],
|
||||
baseFilters?: FilterValue[], // must be memoized
|
||||
initialLoadingState = true,
|
||||
) {
|
||||
const [state, setState] = useState<ListViewResourceState<D>>({
|
||||
count: 0,
|
||||
collection: defaultCollectionValue,
|
||||
loading: true,
|
||||
loading: initialLoadingState,
|
||||
lastFetchDataConfig: null,
|
||||
permissions: [],
|
||||
bulkSelectEnabled: false,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,12 @@ export type FavoriteStatus = {
|
|||
[id: number]: boolean;
|
||||
};
|
||||
|
||||
export type Filters = {
|
||||
col: string;
|
||||
opr: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export interface DashboardTableProps {
|
||||
addDangerToast: (message: string) => void;
|
||||
addSuccessToast: (message: string) => void;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import Chart from 'src/types/Chart';
|
|||
import rison from 'rison';
|
||||
import { getClientErrorObject } from 'src/utils/getClientErrorObject';
|
||||
import { FetchDataConfig } from 'src/components/ListView';
|
||||
import { Dashboard } from './types';
|
||||
import { Dashboard, Filters } from './types';
|
||||
|
||||
const createFetchResourceMethod = (method: string) => (
|
||||
resource: string,
|
||||
|
|
@ -61,25 +61,20 @@ const createFetchResourceMethod = (method: string) => (
|
|||
return [];
|
||||
};
|
||||
|
||||
export const getRecentAcitivtyObjs = (
|
||||
userId: string | number,
|
||||
recent: string,
|
||||
addDangerToast: (arg1: string, arg2: any) => any,
|
||||
) => {
|
||||
const getParams = (filters?: Array<any>) => {
|
||||
const params = {
|
||||
order_column: 'changed_on_delta_humanized',
|
||||
order_direction: 'desc',
|
||||
page: 0,
|
||||
page_size: 3,
|
||||
filters,
|
||||
};
|
||||
if (!filters) delete params.filters;
|
||||
return rison.encode(params);
|
||||
const getParams = (filters?: Array<Filters>) => {
|
||||
const params = {
|
||||
order_column: 'changed_on_delta_humanized',
|
||||
order_direction: 'desc',
|
||||
page: 0,
|
||||
page_size: 3,
|
||||
filters,
|
||||
};
|
||||
if (!filters) delete params.filters;
|
||||
return rison.encode(params);
|
||||
};
|
||||
|
||||
export const getEditedObjects = (userId: string | number) => {
|
||||
const filters = {
|
||||
// chart and dashbaord uses same filters
|
||||
// for edited and created
|
||||
edited: [
|
||||
{
|
||||
col: 'changed_by',
|
||||
|
|
@ -87,6 +82,31 @@ export const getRecentAcitivtyObjs = (
|
|||
value: `${userId}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
const batch = [
|
||||
SupersetClient.get({
|
||||
endpoint: `/api/v1/dashboard/?q=${getParams(filters.edited)}`,
|
||||
}),
|
||||
SupersetClient.get({
|
||||
endpoint: `/api/v1/chart/?q=${getParams(filters.edited)}`,
|
||||
}),
|
||||
];
|
||||
return Promise.all(batch)
|
||||
.then(([editedCharts, editedDashboards]) => {
|
||||
const res = {
|
||||
editedDash: editedDashboards.json?.result.slice(0, 3),
|
||||
editedChart: editedCharts.json?.result.slice(0, 3),
|
||||
};
|
||||
return res;
|
||||
})
|
||||
.catch(err => err);
|
||||
};
|
||||
|
||||
export const getUserOwnedObjects = (
|
||||
userId: string | number,
|
||||
resource: string,
|
||||
) => {
|
||||
const filters = {
|
||||
created: [
|
||||
{
|
||||
col: 'created_by',
|
||||
|
|
@ -95,68 +115,41 @@ export const getRecentAcitivtyObjs = (
|
|||
},
|
||||
],
|
||||
};
|
||||
const baseBatch = [
|
||||
SupersetClient.get({ endpoint: recent }),
|
||||
SupersetClient.get({
|
||||
endpoint: `/api/v1/dashboard/?q=${getParams(filters.edited)}`,
|
||||
}),
|
||||
SupersetClient.get({
|
||||
endpoint: `/api/v1/chart/?q=${getParams(filters.edited)}`,
|
||||
}),
|
||||
SupersetClient.get({
|
||||
endpoint: `/api/v1/dashboard/?q=${getParams(filters.created)}`,
|
||||
}),
|
||||
SupersetClient.get({
|
||||
endpoint: `/api/v1/chart/?q=${getParams(filters.created)}`,
|
||||
}),
|
||||
SupersetClient.get({
|
||||
endpoint: `/api/v1/saved_query/?q=${getParams(filters.created)}`,
|
||||
}),
|
||||
];
|
||||
return Promise.all(baseBatch).then(
|
||||
([
|
||||
recentsRes,
|
||||
editedDash,
|
||||
editedChart,
|
||||
createdByDash,
|
||||
createdByChart,
|
||||
createdByQuery,
|
||||
]) => {
|
||||
const res: any = {
|
||||
editedDash: editedDash.json?.result.slice(0, 3),
|
||||
editedChart: editedChart.json?.result.slice(0, 3),
|
||||
createdByDash: createdByDash.json?.result.slice(0, 3),
|
||||
createdByChart: createdByChart.json?.result.slice(0, 3),
|
||||
createdByQuery: createdByQuery.json?.result.slice(0, 3),
|
||||
};
|
||||
if (recentsRes.json.length === 0) {
|
||||
const newBatch = [
|
||||
SupersetClient.get({ endpoint: `/api/v1/chart/?q=${getParams()}` }),
|
||||
SupersetClient.get({
|
||||
endpoint: `/api/v1/dashboard/?q=${getParams()}`,
|
||||
}),
|
||||
];
|
||||
return Promise.all(newBatch)
|
||||
.then(([chartRes, dashboardRes]) => {
|
||||
res.examples = [
|
||||
...chartRes.json.result,
|
||||
...dashboardRes.json.result,
|
||||
];
|
||||
return res;
|
||||
})
|
||||
.catch(errMsg =>
|
||||
addDangerToast(
|
||||
t('There was an error fetching your recent activity:'),
|
||||
errMsg,
|
||||
),
|
||||
);
|
||||
}
|
||||
res.viewed = recentsRes.json;
|
||||
return res;
|
||||
},
|
||||
);
|
||||
return SupersetClient.get({
|
||||
endpoint: `/api/v1/${resource}/?q=${getParams(filters.created)}`,
|
||||
}).then(res => res.json?.result);
|
||||
};
|
||||
|
||||
export const getRecentAcitivtyObjs = (
|
||||
userId: string | number,
|
||||
recent: string,
|
||||
addDangerToast: (arg1: string, arg2: any) => any,
|
||||
) =>
|
||||
SupersetClient.get({ endpoint: recent }).then(recentsRes => {
|
||||
const res: any = {};
|
||||
if (recentsRes.json.length === 0) {
|
||||
const newBatch = [
|
||||
SupersetClient.get({ endpoint: `/api/v1/chart/?q=${getParams()}` }),
|
||||
SupersetClient.get({
|
||||
endpoint: `/api/v1/dashboard/?q=${getParams()}`,
|
||||
}),
|
||||
];
|
||||
return Promise.all(newBatch)
|
||||
.then(([chartRes, dashboardRes]) => {
|
||||
res.examples = [...chartRes.json.result, ...dashboardRes.json.result];
|
||||
return res;
|
||||
})
|
||||
.catch(errMsg =>
|
||||
addDangerToast(
|
||||
t('There was an error fetching your recent activity:'),
|
||||
errMsg,
|
||||
),
|
||||
);
|
||||
}
|
||||
res.viewed = recentsRes.json;
|
||||
return res;
|
||||
});
|
||||
|
||||
export const createFetchRelated = createFetchResourceMethod('related');
|
||||
export const createFetchDistinct = createFetchResourceMethod('distinct');
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import moment from 'moment';
|
||||
import { styled, t } from '@superset-ui/core';
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ import ListViewCard from 'src/components/ListViewCard';
|
|||
import SubMenu from 'src/components/Menu/SubMenu';
|
||||
import { Chart } from 'src/types/Chart';
|
||||
import { Dashboard, SavedQueryObject } from 'src/views/CRUD/types';
|
||||
import { mq, CardStyles } from 'src/views/CRUD/utils';
|
||||
import { mq, CardStyles, getEditedObjects } from 'src/views/CRUD/utils';
|
||||
|
||||
import { ActivityData } from './Welcome';
|
||||
import EmptyState from './EmptyState';
|
||||
|
|
@ -66,7 +66,6 @@ interface ActivityProps {
|
|||
};
|
||||
activeChild: string;
|
||||
setActiveChild: (arg0: string) => void;
|
||||
loading: boolean;
|
||||
activityData: ActivityData;
|
||||
}
|
||||
|
||||
|
|
@ -156,17 +155,27 @@ const getEntityLastActionOn = (entity: ActivityObject) => {
|
|||
};
|
||||
|
||||
export default function ActivityTable({
|
||||
loading,
|
||||
activeChild,
|
||||
setActiveChild,
|
||||
activityData,
|
||||
user,
|
||||
}: ActivityProps) {
|
||||
const [editedObjs, setEditedObjs] = useState<Array<ActivityData>>();
|
||||
const [loadingState, setLoadingState] = useState(false);
|
||||
const getEditedCards = () => {
|
||||
setLoadingState(true);
|
||||
getEditedObjects(user.userId).then(r => {
|
||||
setEditedObjs([...r.editedChart, ...r.editedDash]);
|
||||
setLoadingState(false);
|
||||
});
|
||||
};
|
||||
const tabs = [
|
||||
{
|
||||
name: 'Edited',
|
||||
label: t('Edited'),
|
||||
onClick: () => {
|
||||
setActiveChild('Edited');
|
||||
getEditedCards();
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -197,30 +206,32 @@ export default function ActivityTable({
|
|||
}
|
||||
|
||||
const renderActivity = () =>
|
||||
activityData[activeChild].map((entity: ActivityObject) => {
|
||||
const url = getEntityUrl(entity);
|
||||
const lastActionOn = getEntityLastActionOn(entity);
|
||||
return (
|
||||
<CardStyles
|
||||
onClick={() => {
|
||||
window.location.href = url;
|
||||
}}
|
||||
key={url}
|
||||
>
|
||||
<ListViewCard
|
||||
loading={loading}
|
||||
cover={<></>}
|
||||
url={url}
|
||||
title={getEntityTitle(entity)}
|
||||
description={lastActionOn}
|
||||
avatar={getEntityIconName(entity)}
|
||||
actions={null}
|
||||
/>
|
||||
</CardStyles>
|
||||
);
|
||||
});
|
||||
|
||||
if (loading) return <Loading position="inline" />;
|
||||
(activeChild !== 'Edited' ? activityData[activeChild] : editedObjs).map(
|
||||
(entity: ActivityObject) => {
|
||||
const url = getEntityUrl(entity);
|
||||
const lastActionOn = getEntityLastActionOn(entity);
|
||||
return (
|
||||
<CardStyles
|
||||
onClick={() => {
|
||||
window.location.href = url;
|
||||
}}
|
||||
key={url}
|
||||
>
|
||||
<ListViewCard
|
||||
cover={<></>}
|
||||
url={url}
|
||||
title={getEntityTitle(entity)}
|
||||
description={lastActionOn}
|
||||
avatar={getEntityIconName(entity)}
|
||||
actions={null}
|
||||
/>
|
||||
</CardStyles>
|
||||
);
|
||||
},
|
||||
);
|
||||
if (loadingState && !editedObjs) {
|
||||
return <Loading position="inline" />;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<SubMenu
|
||||
|
|
@ -229,7 +240,8 @@ export default function ActivityTable({
|
|||
tabs={tabs}
|
||||
/>
|
||||
<>
|
||||
{activityData[activeChild]?.length > 0 ? (
|
||||
{activityData[activeChild]?.length > 0 ||
|
||||
(activeChild === 'Edited' && editedObjs && editedObjs.length > 0) ? (
|
||||
<ActivityContainer>{renderActivity()}</ActivityContainer>
|
||||
) : (
|
||||
<EmptyState tableName="RECENTS" tab={activeChild} />
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import PropertiesModal from 'src/explore/components/PropertiesModal';
|
|||
import { User } from 'src/types/bootstrapTypes';
|
||||
import ChartCard from 'src/views/CRUD/chart/ChartCard';
|
||||
import Chart from 'src/types/Chart';
|
||||
import Loading from 'src/components/Loading';
|
||||
import ErrorBoundary from 'src/components/ErrorBoundary';
|
||||
import SubMenu from 'src/components/Menu/SubMenu';
|
||||
import EmptyState from './EmptyState';
|
||||
|
|
@ -53,7 +54,7 @@ function ChartTable({
|
|||
}: ChartTableProps) {
|
||||
const history = useHistory();
|
||||
const {
|
||||
state: { resourceCollection: charts, bulkSelectEnabled },
|
||||
state: { loading, resourceCollection: charts, bulkSelectEnabled },
|
||||
setResourceCollection: setCharts,
|
||||
hasPerm,
|
||||
refreshData,
|
||||
|
|
@ -64,7 +65,10 @@ function ChartTable({
|
|||
addDangerToast,
|
||||
true,
|
||||
mine,
|
||||
[],
|
||||
false,
|
||||
);
|
||||
|
||||
const chartIds = useMemo(() => charts.map(c => c.id), [charts]);
|
||||
const [saveFavoriteStatus, favoriteStatus] = useFavoriteStatus(
|
||||
'chart',
|
||||
|
|
@ -112,6 +116,7 @@ function ChartTable({
|
|||
filters: getFilters(filter),
|
||||
});
|
||||
|
||||
if (loading) return <Loading position="inline" />;
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
{sliceCurrentlyEditing && (
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import { useListViewResource, useFavoriteStatus } from 'src/views/CRUD/hooks';
|
|||
import { Dashboard, DashboardTableProps } from 'src/views/CRUD/types';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import withToasts from 'src/messageToasts/enhancers/withToasts';
|
||||
import Loading from 'src/components/Loading';
|
||||
import PropertiesModal from 'src/dashboard/components/PropertiesModal';
|
||||
import DashboardCard from 'src/views/CRUD/dashboard/DashboardCard';
|
||||
import SubMenu from 'src/components/Menu/SubMenu';
|
||||
|
|
@ -55,6 +56,8 @@ function DashboardTable({
|
|||
addDangerToast,
|
||||
true,
|
||||
mine,
|
||||
[],
|
||||
false,
|
||||
);
|
||||
const dashboardIds = useMemo(() => dashboards.map(c => c.id), [dashboards]);
|
||||
const [saveFavoriteStatus, favoriteStatus] = useFavoriteStatus(
|
||||
|
|
@ -125,6 +128,7 @@ function DashboardTable({
|
|||
filters: getFilters(filter),
|
||||
});
|
||||
|
||||
if (loading) return <Loading position="inline" />;
|
||||
return (
|
||||
<>
|
||||
<SubMenu
|
||||
|
|
|
|||
|
|
@ -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 withToasts from 'src/messageToasts/enhancers/withToasts';
|
||||
import Loading from 'src/components/Loading';
|
||||
import { Dropdown, Menu } from 'src/common/components';
|
||||
import { useListViewResource, copyQueryLink } from 'src/views/CRUD/hooks';
|
||||
import ListViewCard from 'src/components/ListViewCard';
|
||||
|
|
@ -111,7 +112,7 @@ const SavedQueries = ({
|
|||
mine,
|
||||
}: SavedQueriesProps) => {
|
||||
const {
|
||||
state: { resourceCollection: queries },
|
||||
state: { loading, resourceCollection: queries },
|
||||
hasPerm,
|
||||
fetchData,
|
||||
refreshData,
|
||||
|
|
@ -121,6 +122,8 @@ const SavedQueries = ({
|
|||
addDangerToast,
|
||||
true,
|
||||
mine,
|
||||
[],
|
||||
false,
|
||||
);
|
||||
const [queryFilter, setQueryFilter] = useState('Mine');
|
||||
const [queryDeleteModal, setQueryDeleteModal] = useState(false);
|
||||
|
|
@ -227,6 +230,8 @@ const SavedQueries = ({
|
|||
)}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
if (loading) return <Loading position="inline" />;
|
||||
return (
|
||||
<>
|
||||
{queryDeleteModal && (
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import {
|
|||
createErrorHandler,
|
||||
getRecentAcitivtyObjs,
|
||||
mq,
|
||||
getUserOwnedObjects,
|
||||
} from 'src/views/CRUD/utils';
|
||||
|
||||
import ActivityTable from './ActivityTable';
|
||||
|
|
@ -44,9 +45,6 @@ export interface ActivityData {
|
|||
Edited?: Array<object>;
|
||||
Viewed?: Array<object>;
|
||||
Examples?: Array<object>;
|
||||
myChart?: Array<object>;
|
||||
myDash?: Array<object>;
|
||||
myQuery?: Array<object>;
|
||||
}
|
||||
|
||||
const WelcomeContainer = styled.div`
|
||||
|
|
@ -88,22 +86,17 @@ const WelcomeContainer = styled.div`
|
|||
function Welcome({ user, addDangerToast }: WelcomeProps) {
|
||||
const recent = `/superset/recent_activity/${user.userId}/?limit=6`;
|
||||
const [activeChild, setActiveChild] = useState('Viewed');
|
||||
const [activityData, setActivityData] = useState<ActivityData>({});
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [activityData, setActivityData] = useState<ActivityData | null>(null);
|
||||
const [chartData, setChartData] = useState<Array<object> | null>(null);
|
||||
const [queryData, setQueryData] = useState<Array<object> | null>(null);
|
||||
const [dashboardData, setDashboardData] = useState<Array<object> | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
getRecentAcitivtyObjs(user.userId, recent, addDangerToast)
|
||||
.then(res => {
|
||||
const data: any = {
|
||||
Created: [
|
||||
...res.createdByChart,
|
||||
...res.createdByDash,
|
||||
...res.createdByQuery,
|
||||
],
|
||||
myChart: res.createdByChart,
|
||||
myDash: res.createdByDash,
|
||||
myQuery: res.createdByQuery,
|
||||
Edited: [...res.editedChart, ...res.editedDash],
|
||||
};
|
||||
const data: ActivityData | null = {};
|
||||
if (res.viewed) {
|
||||
const filtered = reject(res.viewed, ['item_url', null]).map(r => r);
|
||||
data.Viewed = filtered;
|
||||
|
|
@ -112,54 +105,94 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
|
|||
data.Examples = res.examples;
|
||||
setActiveChild('Examples');
|
||||
}
|
||||
setActivityData(data);
|
||||
setLoading(false);
|
||||
setActivityData(activityData => ({ ...activityData, ...data }));
|
||||
})
|
||||
.catch(
|
||||
createErrorHandler((errMsg: unknown) => {
|
||||
setLoading(false);
|
||||
setActivityData(activityData => ({ ...activityData, Viewed: [] }));
|
||||
addDangerToast(
|
||||
t('There was an issue fetching your recent activity: %s', errMsg),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
// Sets other activity data in parallel with recents api call
|
||||
const id = user.userId;
|
||||
getUserOwnedObjects(id, 'dashboard')
|
||||
.then(r => {
|
||||
setDashboardData(r);
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
setDashboardData([]);
|
||||
addDangerToast(
|
||||
t('There was an issues fetching your dashboards: %s', err),
|
||||
);
|
||||
});
|
||||
getUserOwnedObjects(id, 'chart')
|
||||
.then(r => {
|
||||
setChartData(r);
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
setChartData([]);
|
||||
addDangerToast(t('There was an issues fetching your chart: %s', err));
|
||||
});
|
||||
getUserOwnedObjects(id, 'saved_query')
|
||||
.then(r => {
|
||||
setQueryData(r);
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
setQueryData([]);
|
||||
addDangerToast(
|
||||
t('There was an issues fetching your saved queries: %s', err),
|
||||
);
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setActivityData(activityData => ({
|
||||
...activityData,
|
||||
Created: [
|
||||
...(chartData || []),
|
||||
...(dashboardData || []),
|
||||
...(queryData || []),
|
||||
],
|
||||
}));
|
||||
}, [chartData, queryData, dashboardData]);
|
||||
|
||||
return (
|
||||
<WelcomeContainer>
|
||||
<Collapse defaultActiveKey={['1', '2', '3', '4']} ghost bigger>
|
||||
<Collapse.Panel header={t('Recents')} key="1">
|
||||
<ActivityTable
|
||||
user={user}
|
||||
activeChild={activeChild}
|
||||
setActiveChild={setActiveChild}
|
||||
loading={loading}
|
||||
activityData={activityData}
|
||||
/>
|
||||
{activityData && (activityData.Viewed || activityData.Examples) ? (
|
||||
<ActivityTable
|
||||
user={user}
|
||||
activeChild={activeChild}
|
||||
setActiveChild={setActiveChild}
|
||||
activityData={activityData}
|
||||
/>
|
||||
) : (
|
||||
<Loading position="inline" />
|
||||
)}
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel header={t('Dashboards')} key="2">
|
||||
{loading ? (
|
||||
{!dashboardData ? (
|
||||
<Loading position="inline" />
|
||||
) : (
|
||||
<DashboardTable
|
||||
user={user}
|
||||
mine={activityData.myDash}
|
||||
isLoading={loading}
|
||||
/>
|
||||
<DashboardTable user={user} mine={dashboardData} />
|
||||
)}
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel header={t('Saved queries')} key="3">
|
||||
{loading ? (
|
||||
{!queryData ? (
|
||||
<Loading position="inline" />
|
||||
) : (
|
||||
<SavedQueries user={user} mine={activityData.myQuery} />
|
||||
<SavedQueries user={user} mine={queryData} />
|
||||
)}
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel header={t('Charts')} key="4">
|
||||
{loading ? (
|
||||
{!chartData ? (
|
||||
<Loading position="inline" />
|
||||
) : (
|
||||
<ChartTable user={user} mine={activityData.myChart} />
|
||||
<ChartTable user={user} mine={chartData} />
|
||||
)}
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
|
|
|
|||
Loading…
Reference in New Issue