Compare commits
10 Commits
python-run
...
dashboard-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5fbe5221d | ||
|
|
a4a295ca0c | ||
|
|
2f133d9394 | ||
|
|
7d25ad8f59 | ||
|
|
84507a1f53 | ||
|
|
f7ac5543ec | ||
|
|
0d747e3dea | ||
|
|
3a715e7582 | ||
|
|
e4f6d46c05 | ||
|
|
ba609272be |
@@ -1,6 +1,3 @@
|
||||
import type { Query } from "history";
|
||||
import type { ComponentType, FC } from "react";
|
||||
import type { ConnectedProps } from "react-redux";
|
||||
import _ from "underscore";
|
||||
|
||||
import type { DashboardEventHandlersProps } from "embedding-sdk/types/dashboard";
|
||||
@@ -10,90 +7,26 @@ import * as dashboardActions from "metabase/dashboard/actions";
|
||||
import type { NavigateToNewCardFromDashboardOpts } from "metabase/dashboard/components/DashCard/types";
|
||||
import { Dashboard } from "metabase/dashboard/components/Dashboard/Dashboard";
|
||||
import {
|
||||
getClickBehaviorSidebarDashcard,
|
||||
getDashboardBeforeEditing,
|
||||
getDashboardComplete,
|
||||
getDocumentTitle,
|
||||
getFavicon,
|
||||
getIsAddParameterPopoverOpen,
|
||||
getIsAdditionalInfoVisible,
|
||||
getIsDashCardsLoadingComplete,
|
||||
getIsDashCardsRunning,
|
||||
getIsDirty,
|
||||
getIsEditing,
|
||||
getIsEditingParameter,
|
||||
getIsHeaderVisible,
|
||||
getIsNavigatingBackToDashboard,
|
||||
getIsSharing,
|
||||
getLoadingStartTime,
|
||||
getParameterValues,
|
||||
getSelectedTabId,
|
||||
getSidebar,
|
||||
getSlowCards,
|
||||
} from "metabase/dashboard/selectors";
|
||||
DashboardContextProvider,
|
||||
useDashboardContext,
|
||||
} from "metabase/dashboard/context";
|
||||
import type {
|
||||
DashboardFullscreenControls,
|
||||
DashboardLoaderWrapperProps,
|
||||
DashboardRefreshPeriodControls,
|
||||
} from "metabase/dashboard/types";
|
||||
import { connect } from "metabase/lib/redux";
|
||||
import type { PublicOrEmbeddedDashboardEventHandlersProps } from "metabase/public/containers/PublicOrEmbeddedDashboard/types";
|
||||
import { useDashboardLoadHandlers } from "metabase/public/containers/PublicOrEmbeddedDashboard/use-dashboard-load-handlers";
|
||||
import { closeNavbar, setErrorPage } from "metabase/redux/app";
|
||||
import { getIsNavbarOpen } from "metabase/selectors/app";
|
||||
import {
|
||||
canManageSubscriptions,
|
||||
getUserIsAdmin,
|
||||
} from "metabase/selectors/user";
|
||||
import type { DashboardId } from "metabase-types/api";
|
||||
import type { State } from "metabase-types/store";
|
||||
|
||||
const mapStateToProps = (state: State) => {
|
||||
return {
|
||||
canManageSubscriptions: canManageSubscriptions(state),
|
||||
isAdmin: getUserIsAdmin(state),
|
||||
isNavbarOpen: getIsNavbarOpen(state),
|
||||
isEditing: getIsEditing(state),
|
||||
isSharing: getIsSharing(state),
|
||||
dashboardBeforeEditing: getDashboardBeforeEditing(state),
|
||||
isEditingParameter: getIsEditingParameter(state),
|
||||
isDirty: getIsDirty(state),
|
||||
dashboard: getDashboardComplete(state),
|
||||
slowCards: getSlowCards(state),
|
||||
parameterValues: getParameterValues(state),
|
||||
loadingStartTime: getLoadingStartTime(state),
|
||||
clickBehaviorSidebarDashcard: getClickBehaviorSidebarDashcard(state),
|
||||
isAddParameterPopoverOpen: getIsAddParameterPopoverOpen(state),
|
||||
sidebar: getSidebar(state),
|
||||
pageFavicon: getFavicon(state),
|
||||
documentTitle: getDocumentTitle(state),
|
||||
isRunning: getIsDashCardsRunning(state),
|
||||
isLoadingComplete: getIsDashCardsLoadingComplete(state),
|
||||
isHeaderVisible: getIsHeaderVisible(state),
|
||||
isAdditionalInfoVisible: getIsAdditionalInfoVisible(state),
|
||||
selectedTabId: getSelectedTabId(state),
|
||||
isNavigatingBackToDashboard: getIsNavigatingBackToDashboard(state),
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = {
|
||||
...dashboardActions,
|
||||
closeNavbar,
|
||||
setErrorPage,
|
||||
};
|
||||
|
||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||
type ReduxProps = ConnectedProps<typeof connector>;
|
||||
|
||||
type ConnectedDashboardProps = {
|
||||
dashboardId: DashboardId;
|
||||
isLoading: boolean;
|
||||
parameterQueryParams: Query;
|
||||
parameterQueryParams: Record<string, string>;
|
||||
|
||||
downloadsEnabled?: boolean;
|
||||
onNavigateToNewCardFromDashboard: (
|
||||
onNavigateToNewCardFromDashboard?: (
|
||||
opts: NavigateToNewCardFromDashboardOpts,
|
||||
) => void;
|
||||
|
||||
plugins?: MetabasePluginsConfig;
|
||||
} & Pick<CommonStylingProps, "className"> &
|
||||
DashboardFullscreenControls &
|
||||
@@ -102,28 +35,54 @@ type ConnectedDashboardProps = {
|
||||
DashboardEventHandlersProps;
|
||||
|
||||
const ConnectedDashboardInner = ({
|
||||
dashboard,
|
||||
onLoad,
|
||||
onLoadWithoutCards,
|
||||
onNavigateToNewCardFromDashboard,
|
||||
...restProps
|
||||
}: ConnectedDashboardProps & ReduxProps) => {
|
||||
}: PublicOrEmbeddedDashboardEventHandlersProps) => {
|
||||
const { dashboard } = useDashboardContext();
|
||||
|
||||
useDashboardLoadHandlers({ dashboard, onLoad, onLoadWithoutCards });
|
||||
|
||||
return <Dashboard />;
|
||||
};
|
||||
|
||||
export const ConnectedDashboard = ({
|
||||
dashboardId,
|
||||
parameterQueryParams,
|
||||
|
||||
downloadsEnabled,
|
||||
onNavigateToNewCardFromDashboard,
|
||||
|
||||
isFullscreen,
|
||||
onFullscreenChange,
|
||||
|
||||
refreshPeriod,
|
||||
onRefreshPeriodChange,
|
||||
setRefreshElapsedHook,
|
||||
|
||||
onLoad,
|
||||
onLoadWithoutCards,
|
||||
}: ConnectedDashboardProps) => {
|
||||
return (
|
||||
<Dashboard
|
||||
dashboard={dashboard}
|
||||
{...restProps}
|
||||
<DashboardContextProvider
|
||||
dashboardId={dashboardId}
|
||||
parameterQueryParams={parameterQueryParams}
|
||||
downloadsEnabled={downloadsEnabled ?? false}
|
||||
isNightMode={false}
|
||||
onNightModeChange={_.noop}
|
||||
hasNightModeToggle={false}
|
||||
isFullscreen={isFullscreen}
|
||||
onFullscreenChange={onFullscreenChange}
|
||||
refreshPeriod={refreshPeriod}
|
||||
onRefreshPeriodChange={onRefreshPeriodChange}
|
||||
setRefreshElapsedHook={setRefreshElapsedHook}
|
||||
navigateToNewCardFromDashboard={onNavigateToNewCardFromDashboard}
|
||||
autoScrollToDashcardId={undefined}
|
||||
reportAutoScrolledToDashcard={_.noop}
|
||||
/>
|
||||
>
|
||||
<ConnectedDashboardInner
|
||||
onLoad={onLoad}
|
||||
onLoadWithoutCards={onLoadWithoutCards}
|
||||
/>
|
||||
</DashboardContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export const ConnectedDashboard = connector<
|
||||
ComponentType<ConnectedDashboardProps & ReduxProps>
|
||||
>(ConnectedDashboardInner) as FC<ConnectedDashboardProps>;
|
||||
|
||||
@@ -136,7 +136,6 @@ export const EditableDashboard = ({
|
||||
>
|
||||
<ConnectedDashboard
|
||||
dashboardId={dashboardId}
|
||||
isLoading={isLoading}
|
||||
parameterQueryParams={initialParameters}
|
||||
refreshPeriod={refreshPeriod}
|
||||
onRefreshPeriodChange={onRefreshPeriodChange}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Query } from "history";
|
||||
import type { CSSProperties } from "react";
|
||||
import { pick } from "underscore";
|
||||
|
||||
import type { SdkDashboardId } from "embedding-sdk/types/dashboard";
|
||||
|
||||
@@ -1,26 +1,13 @@
|
||||
import cx from "classnames";
|
||||
import type { Query } from "history";
|
||||
import type { ReactNode } from "react";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { usePrevious, useUnmount } from "react-use";
|
||||
import _ from "underscore";
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { useListDatabasesQuery } from "metabase/api";
|
||||
import { deletePermanently } from "metabase/archive/actions";
|
||||
import { ArchivedEntityBanner } from "metabase/archive/components/ArchivedEntityBanner";
|
||||
import {
|
||||
type NewDashCardOpts,
|
||||
type SetDashboardAttributesOpts,
|
||||
moveDashboardToCollection,
|
||||
setArchivedDashboard,
|
||||
} from "metabase/dashboard/actions";
|
||||
import type { NavigateToNewCardFromDashboardOpts } from "metabase/dashboard/components/DashCard/types";
|
||||
import { DashboardHeader } from "metabase/dashboard/components/DashboardHeader";
|
||||
import { useDashboardContext } from "metabase/dashboard/context";
|
||||
import type {
|
||||
CancelledFetchDashboardResult,
|
||||
DashboardDisplayOptionControls,
|
||||
FetchDashboardResult,
|
||||
SuccessfulFetchDashboardResult,
|
||||
} from "metabase/dashboard/types";
|
||||
import Bookmarks from "metabase/entities/bookmarks";
|
||||
import Dashboards from "metabase/entities/dashboards";
|
||||
@@ -28,29 +15,10 @@ import { useDispatch } from "metabase/lib/redux";
|
||||
import { getHasDataAccess, getHasNativeWrite } from "metabase/selectors/data";
|
||||
import { FullWidthContainer } from "metabase/styled-components/layout/FullWidthContainer";
|
||||
import { Box, Flex } from "metabase/ui";
|
||||
import type {
|
||||
CardId,
|
||||
DashCardId,
|
||||
DashCardVisualizationSettings,
|
||||
DashboardCard,
|
||||
DashboardId,
|
||||
DashboardTabId,
|
||||
Dashboard as IDashboard,
|
||||
ParameterId,
|
||||
ParameterValueOrArray,
|
||||
TemporalUnit,
|
||||
ValuesQueryType,
|
||||
ValuesSourceConfig,
|
||||
ValuesSourceType,
|
||||
} from "metabase-types/api";
|
||||
import type { DashboardCard } from "metabase-types/api";
|
||||
import { isObject } from "metabase-types/guards";
|
||||
import type {
|
||||
DashboardSidebarName,
|
||||
SelectedTabId,
|
||||
State,
|
||||
} from "metabase-types/store";
|
||||
|
||||
import { DASHBOARD_PDF_EXPORT_ROOT_ID, SIDEBAR_NAME } from "../../constants";
|
||||
import { DASHBOARD_PDF_EXPORT_ROOT_ID } from "../../constants";
|
||||
import { DashboardGridConnected } from "../DashboardGrid";
|
||||
import { DashboardParameterPanel } from "../DashboardParameterPanel";
|
||||
import { DashboardSidebars } from "../DashboardSidebars";
|
||||
@@ -62,156 +30,64 @@ import {
|
||||
DashboardEmptyStateWithoutAddPrompt,
|
||||
} from "./DashboardEmptyState/DashboardEmptyState";
|
||||
|
||||
export type DashboardProps = {
|
||||
children?: ReactNode;
|
||||
canManageSubscriptions: boolean;
|
||||
isAdmin: boolean;
|
||||
isNavbarOpen: boolean;
|
||||
isEditing: boolean;
|
||||
isSharing: boolean;
|
||||
dashboardBeforeEditing: IDashboard | null;
|
||||
isEditingParameter: boolean;
|
||||
isDirty: boolean;
|
||||
dashboard: IDashboard | null;
|
||||
slowCards: Record<DashCardId, boolean>;
|
||||
parameterValues: Record<ParameterId, ParameterValueOrArray>;
|
||||
loadingStartTime: number | null;
|
||||
clickBehaviorSidebarDashcard: DashboardCard | null;
|
||||
isAddParameterPopoverOpen: boolean;
|
||||
sidebar: State["dashboard"]["sidebar"];
|
||||
isHeaderVisible: boolean;
|
||||
isAdditionalInfoVisible: boolean;
|
||||
selectedTabId: SelectedTabId;
|
||||
isNavigatingBackToDashboard: boolean;
|
||||
addCardOnLoad?: DashCardId;
|
||||
editingOnLoad?: string | string[] | boolean;
|
||||
autoScrollToDashcardId: DashCardId | undefined;
|
||||
dashboardId: DashboardId;
|
||||
parameterQueryParams: Query;
|
||||
|
||||
initialize: (opts?: { clearCache?: boolean }) => void;
|
||||
cancelFetchDashboardCardData: () => void;
|
||||
addCardToDashboard: (opts: {
|
||||
dashId: DashboardId;
|
||||
cardId: CardId;
|
||||
tabId: DashboardTabId | null;
|
||||
}) => void;
|
||||
addHeadingDashCardToDashboard: (opts: NewDashCardOpts) => void;
|
||||
addMarkdownDashCardToDashboard: (opts: NewDashCardOpts) => void;
|
||||
addLinkDashCardToDashboard: (opts: NewDashCardOpts) => void;
|
||||
|
||||
setEditingDashboard: (dashboard: IDashboard | null) => void;
|
||||
setDashboardAttributes: (opts: SetDashboardAttributesOpts) => void;
|
||||
setSharing: (isSharing: boolean) => void;
|
||||
toggleSidebar: (sidebarName: DashboardSidebarName) => void;
|
||||
closeSidebar: () => void;
|
||||
|
||||
closeNavbar: () => void;
|
||||
setErrorPage: (error: unknown) => void;
|
||||
|
||||
setParameterName: (id: ParameterId, name: string) => void;
|
||||
setParameterType: (id: ParameterId, type: string, sectionId: string) => void;
|
||||
navigateToNewCardFromDashboard: (
|
||||
opts: NavigateToNewCardFromDashboardOpts,
|
||||
) => void;
|
||||
setParameterDefaultValue: (id: ParameterId, value: unknown) => void;
|
||||
setParameterRequired: (id: ParameterId, value: boolean) => void;
|
||||
setParameterTemporalUnits: (
|
||||
id: ParameterId,
|
||||
temporalUnits: TemporalUnit[],
|
||||
) => void;
|
||||
setParameterIsMultiSelect: (id: ParameterId, isMultiSelect: boolean) => void;
|
||||
setParameterQueryType: (id: ParameterId, queryType: ValuesQueryType) => void;
|
||||
setParameterSourceType: (
|
||||
id: ParameterId,
|
||||
sourceType: ValuesSourceType,
|
||||
) => void;
|
||||
setParameterSourceConfig: (
|
||||
id: ParameterId,
|
||||
config: ValuesSourceConfig,
|
||||
) => void;
|
||||
setParameterFilteringParameters: (
|
||||
parameterId: ParameterId,
|
||||
filteringParameters: ParameterId[],
|
||||
) => void;
|
||||
showAddParameterPopover: () => void;
|
||||
removeParameter: (id: ParameterId) => void;
|
||||
|
||||
onReplaceAllDashCardVisualizationSettings: (
|
||||
id: DashCardId,
|
||||
settings: DashCardVisualizationSettings | null | undefined,
|
||||
) => void;
|
||||
onUpdateDashCardVisualizationSettings: (
|
||||
id: DashCardId,
|
||||
settings: DashCardVisualizationSettings | null | undefined,
|
||||
) => void;
|
||||
onUpdateDashCardColumnSettings: (
|
||||
id: DashCardId,
|
||||
columnKey: string,
|
||||
settings?: Record<string, unknown> | null,
|
||||
) => void;
|
||||
updateDashboardAndCards: () => void;
|
||||
|
||||
setSidebar: (opts: { name: DashboardSidebarName }) => void;
|
||||
hideAddParameterPopover: () => void;
|
||||
|
||||
fetchDashboard: (opts: {
|
||||
dashId: DashboardId;
|
||||
queryParams: Query;
|
||||
options?: {
|
||||
clearCache?: boolean;
|
||||
preserveParameters?: boolean;
|
||||
};
|
||||
}) => Promise<FetchDashboardResult>;
|
||||
|
||||
fetchDashboardCardData: (opts?: {
|
||||
isRefreshing?: boolean;
|
||||
reload?: boolean;
|
||||
clearCache?: boolean;
|
||||
}) => void;
|
||||
|
||||
reportAutoScrolledToDashcard: () => void;
|
||||
} & DashboardDisplayOptionControls;
|
||||
|
||||
function Dashboard(props: DashboardProps) {
|
||||
function Dashboard() {
|
||||
const {
|
||||
addCardOnLoad,
|
||||
addCardToDashboard,
|
||||
autoScrollToDashcardId,
|
||||
cancelFetchDashboardCardData,
|
||||
dashboard,
|
||||
dashboardId,
|
||||
editingOnLoad,
|
||||
fetchDashboard,
|
||||
fetchDashboardCardData,
|
||||
initialize,
|
||||
isEditing,
|
||||
isFullscreen,
|
||||
isNavigatingBackToDashboard,
|
||||
isNightMode = false,
|
||||
isSharing,
|
||||
onRefreshPeriodChange,
|
||||
parameterValues,
|
||||
reportAutoScrolledToDashcard,
|
||||
selectedTabId,
|
||||
setEditingDashboard,
|
||||
setErrorPage,
|
||||
setSharing,
|
||||
toggleSidebar,
|
||||
parameterQueryParams,
|
||||
downloadsEnabled = true,
|
||||
noLoaderWrapper = false,
|
||||
} = props;
|
||||
parameterQueryParams = {},
|
||||
downloadsEnabled,
|
||||
handleAddQuestion,
|
||||
shouldRenderAsNightMode,
|
||||
setArchivedDashboard,
|
||||
moveDashboardToCollection,
|
||||
deletePermanently,
|
||||
|
||||
dashboardBeforeEditing,
|
||||
onFullscreenChange,
|
||||
isAdditionalInfoVisible,
|
||||
hasNightModeToggle,
|
||||
onNightModeChange,
|
||||
refreshPeriod,
|
||||
setRefreshElapsedHook,
|
||||
|
||||
isEditingParameter,
|
||||
slowCards,
|
||||
navigateToNewCardFromDashboard,
|
||||
showAddParameterPopover,
|
||||
removeParameter,
|
||||
addCardToDashboard,
|
||||
clickBehaviorSidebarDashcard,
|
||||
onReplaceAllDashCardVisualizationSettings,
|
||||
onUpdateDashCardVisualizationSettings,
|
||||
onUpdateDashCardColumnSettings,
|
||||
setParameterName,
|
||||
setParameterType,
|
||||
setParameterDefaultValue,
|
||||
setParameterIsMultiSelect,
|
||||
setParameterQueryType,
|
||||
setParameterSourceType,
|
||||
setParameterSourceConfig,
|
||||
setParameterFilteringParameters,
|
||||
setParameterRequired,
|
||||
setParameterTemporalUnits,
|
||||
sidebar,
|
||||
closeSidebar,
|
||||
} = useDashboardContext();
|
||||
|
||||
const canWrite = Boolean(dashboard?.can_write);
|
||||
const canRestore = Boolean(dashboard?.can_restore);
|
||||
const canDelete = Boolean(dashboard?.can_delete);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [isInitialized, setIsInitialized] = useState(false);
|
||||
const [error, setError] = useState<unknown>(null);
|
||||
|
||||
const previousDashboard = usePrevious(dashboard);
|
||||
const previousDashboardId = usePrevious(dashboardId);
|
||||
const previousTabId = usePrevious(selectedTabId);
|
||||
const previousParameterValues = usePrevious(parameterValues);
|
||||
const invalidateBookmarks = async () =>
|
||||
await dispatch(Bookmarks.actions.invalidateLists());
|
||||
|
||||
const currentTabDashcards = useMemo(() => {
|
||||
if (!dashboard || !Array.isArray(dashboard.dashcards)) {
|
||||
@@ -225,9 +101,6 @@ function Dashboard(props: DashboardProps) {
|
||||
);
|
||||
}, [dashboard, selectedTabId]);
|
||||
|
||||
const canWrite = Boolean(dashboard?.can_write);
|
||||
const canRestore = Boolean(dashboard?.can_restore);
|
||||
const canDelete = Boolean(dashboard?.can_delete);
|
||||
const tabHasCards = currentTabDashcards.length > 0;
|
||||
const dashboardHasCards = dashboard && dashboard.dashcards.length > 0;
|
||||
|
||||
@@ -243,125 +116,6 @@ function Dashboard(props: DashboardProps) {
|
||||
);
|
||||
const canCreateQuestions = hasDataAccess || hasNativeWrite;
|
||||
|
||||
const shouldRenderAsNightMode = isNightMode && isFullscreen;
|
||||
|
||||
const handleSetEditing = useCallback(
|
||||
(dashboard: IDashboard | null) => {
|
||||
if (!isEditing) {
|
||||
onRefreshPeriodChange(null);
|
||||
setEditingDashboard(dashboard);
|
||||
}
|
||||
},
|
||||
[isEditing, onRefreshPeriodChange, setEditingDashboard],
|
||||
);
|
||||
|
||||
const handleAddQuestion = useCallback(() => {
|
||||
handleSetEditing(dashboard);
|
||||
toggleSidebar(SIDEBAR_NAME.addQuestion);
|
||||
}, [handleSetEditing, dashboard, toggleSidebar]);
|
||||
|
||||
const handleLoadDashboard = useCallback(
|
||||
async (dashboardId: DashboardId) => {
|
||||
initialize({ clearCache: !isNavigatingBackToDashboard });
|
||||
|
||||
const result = await fetchDashboard({
|
||||
dashId: dashboardId,
|
||||
queryParams: parameterQueryParams,
|
||||
options: {
|
||||
clearCache: !isNavigatingBackToDashboard,
|
||||
preserveParameters: isNavigatingBackToDashboard,
|
||||
},
|
||||
});
|
||||
|
||||
if (!isSuccessfulFetchDashboardResult(result)) {
|
||||
if (!isCancelledFetchDashboardResult(result)) {
|
||||
setErrorPage(result.payload);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const dashboard = result.payload.dashboard;
|
||||
|
||||
if (editingOnLoad) {
|
||||
onRefreshPeriodChange(null);
|
||||
setEditingDashboard(dashboard);
|
||||
}
|
||||
if (addCardOnLoad != null) {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const tabParam = searchParams.get("tab");
|
||||
const tabId = tabParam ? parseInt(tabParam, 10) : null;
|
||||
|
||||
addCardToDashboard({
|
||||
dashId: dashboardId,
|
||||
cardId: addCardOnLoad,
|
||||
tabId,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Response && error.status === 404) {
|
||||
setErrorPage({ ...error, context: "dashboard" });
|
||||
} else {
|
||||
console.error(error);
|
||||
setError(error);
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
addCardOnLoad,
|
||||
addCardToDashboard,
|
||||
editingOnLoad,
|
||||
fetchDashboard,
|
||||
initialize,
|
||||
isNavigatingBackToDashboard,
|
||||
onRefreshPeriodChange,
|
||||
parameterQueryParams,
|
||||
setEditingDashboard,
|
||||
setErrorPage,
|
||||
],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const hasDashboardChanged = dashboardId !== previousDashboardId;
|
||||
if (hasDashboardChanged) {
|
||||
handleLoadDashboard(dashboardId).then(() => setIsInitialized(true));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dashboard) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hasDashboardLoaded = !previousDashboard;
|
||||
const hasTabChanged = selectedTabId !== previousTabId;
|
||||
const hasParameterValueChanged = !_.isEqual(
|
||||
parameterValues,
|
||||
previousParameterValues,
|
||||
);
|
||||
|
||||
if (hasDashboardLoaded) {
|
||||
fetchDashboardCardData({ reload: false, clearCache: true });
|
||||
} else if (hasTabChanged || hasParameterValueChanged) {
|
||||
fetchDashboardCardData();
|
||||
}
|
||||
}, [
|
||||
dashboard,
|
||||
dashboardId,
|
||||
fetchDashboardCardData,
|
||||
handleLoadDashboard,
|
||||
isInitialized,
|
||||
parameterValues,
|
||||
previousDashboard,
|
||||
previousDashboardId,
|
||||
previousParameterValues,
|
||||
previousTabId,
|
||||
selectedTabId,
|
||||
]);
|
||||
|
||||
useUnmount(() => {
|
||||
cancelFetchDashboardCardData();
|
||||
});
|
||||
|
||||
const renderEmptyStates = () => {
|
||||
if (!dashboardHasCards) {
|
||||
return canWrite ? (
|
||||
@@ -404,8 +158,7 @@ function Dashboard(props: DashboardProps) {
|
||||
isFullscreen={isFullscreen}
|
||||
isNightMode={shouldRenderAsNightMode}
|
||||
loading={!dashboard}
|
||||
error={error}
|
||||
noWrapper={noLoaderWrapper}
|
||||
noWrapper={true}
|
||||
>
|
||||
{() => {
|
||||
if (!dashboard) {
|
||||
@@ -425,14 +178,14 @@ function Dashboard(props: DashboardProps) {
|
||||
canRestore={canRestore}
|
||||
canDelete={canDelete}
|
||||
onUnarchive={async () => {
|
||||
await dispatch(setArchivedDashboard(false));
|
||||
await dispatch(Bookmarks.actions.invalidateLists());
|
||||
await setArchivedDashboard(false);
|
||||
await invalidateBookmarks();
|
||||
}}
|
||||
onMove={({ id }) => dispatch(moveDashboardToCollection({ id }))}
|
||||
onMove={({ id }) => moveDashboardToCollection({ id })}
|
||||
onDeletePermanently={() => {
|
||||
const { id } = dashboard;
|
||||
const deleteAction = Dashboards.actions.delete({ id });
|
||||
dispatch(deletePermanently(deleteAction));
|
||||
deletePermanently(deleteAction);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -457,13 +210,13 @@ function Dashboard(props: DashboardProps) {
|
||||
isNightMode={shouldRenderAsNightMode}
|
||||
isFullscreen={isFullscreen}
|
||||
onRefreshPeriodChange={onRefreshPeriodChange}
|
||||
dashboardBeforeEditing={props.dashboardBeforeEditing}
|
||||
onFullscreenChange={props.onFullscreenChange}
|
||||
isAdditionalInfoVisible={props.isAdditionalInfoVisible}
|
||||
hasNightModeToggle={props.hasNightModeToggle}
|
||||
onNightModeChange={props.onNightModeChange}
|
||||
refreshPeriod={props.refreshPeriod}
|
||||
setRefreshElapsedHook={props.setRefreshElapsedHook}
|
||||
dashboardBeforeEditing={dashboardBeforeEditing}
|
||||
onFullscreenChange={onFullscreenChange}
|
||||
isAdditionalInfoVisible={isAdditionalInfoVisible}
|
||||
hasNightModeToggle={hasNightModeToggle}
|
||||
onNightModeChange={onNightModeChange}
|
||||
refreshPeriod={refreshPeriod}
|
||||
setRefreshElapsedHook={setRefreshElapsedHook}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -495,19 +248,18 @@ function Dashboard(props: DashboardProps) {
|
||||
>
|
||||
<DashboardGridConnected
|
||||
clickBehaviorSidebarDashcard={
|
||||
props.clickBehaviorSidebarDashcard
|
||||
clickBehaviorSidebarDashcard
|
||||
}
|
||||
isNightMode={shouldRenderAsNightMode}
|
||||
isFullscreen={props.isFullscreen}
|
||||
isEditingParameter={props.isEditingParameter}
|
||||
isEditing={props.isEditing}
|
||||
isFullscreen={isFullscreen}
|
||||
isEditingParameter={isEditingParameter}
|
||||
isEditing={isEditing}
|
||||
dashboard={dashboard}
|
||||
slowCards={props.slowCards}
|
||||
slowCards={slowCards}
|
||||
navigateToNewCardFromDashboard={
|
||||
props.navigateToNewCardFromDashboard
|
||||
navigateToNewCardFromDashboard
|
||||
}
|
||||
selectedTabId={selectedTabId}
|
||||
onEditingChange={handleSetEditing}
|
||||
downloadsEnabled={downloadsEnabled}
|
||||
autoScrollToDashcardId={autoScrollToDashcardId}
|
||||
reportAutoScrolledToDashcard={
|
||||
@@ -520,36 +272,32 @@ function Dashboard(props: DashboardProps) {
|
||||
|
||||
<DashboardSidebars
|
||||
dashboard={dashboard}
|
||||
showAddParameterPopover={props.showAddParameterPopover}
|
||||
removeParameter={props.removeParameter}
|
||||
addCardToDashboard={props.addCardToDashboard}
|
||||
clickBehaviorSidebarDashcard={
|
||||
props.clickBehaviorSidebarDashcard
|
||||
}
|
||||
showAddParameterPopover={showAddParameterPopover}
|
||||
removeParameter={removeParameter}
|
||||
addCardToDashboard={addCardToDashboard}
|
||||
clickBehaviorSidebarDashcard={clickBehaviorSidebarDashcard}
|
||||
onReplaceAllDashCardVisualizationSettings={
|
||||
props.onReplaceAllDashCardVisualizationSettings
|
||||
onReplaceAllDashCardVisualizationSettings
|
||||
}
|
||||
onUpdateDashCardVisualizationSettings={
|
||||
props.onUpdateDashCardVisualizationSettings
|
||||
onUpdateDashCardVisualizationSettings
|
||||
}
|
||||
onUpdateDashCardColumnSettings={
|
||||
props.onUpdateDashCardColumnSettings
|
||||
}
|
||||
setParameterName={props.setParameterName}
|
||||
setParameterType={props.setParameterType}
|
||||
setParameterDefaultValue={props.setParameterDefaultValue}
|
||||
setParameterIsMultiSelect={props.setParameterIsMultiSelect}
|
||||
setParameterQueryType={props.setParameterQueryType}
|
||||
setParameterSourceType={props.setParameterSourceType}
|
||||
setParameterSourceConfig={props.setParameterSourceConfig}
|
||||
onUpdateDashCardColumnSettings={onUpdateDashCardColumnSettings}
|
||||
setParameterName={setParameterName}
|
||||
setParameterType={setParameterType}
|
||||
setParameterDefaultValue={setParameterDefaultValue}
|
||||
setParameterIsMultiSelect={setParameterIsMultiSelect}
|
||||
setParameterQueryType={setParameterQueryType}
|
||||
setParameterSourceType={setParameterSourceType}
|
||||
setParameterSourceConfig={setParameterSourceConfig}
|
||||
setParameterFilteringParameters={
|
||||
props.setParameterFilteringParameters
|
||||
setParameterFilteringParameters
|
||||
}
|
||||
setParameterRequired={props.setParameterRequired}
|
||||
setParameterTemporalUnits={props.setParameterTemporalUnits}
|
||||
isFullscreen={props.isFullscreen}
|
||||
sidebar={props.sidebar}
|
||||
closeSidebar={props.closeSidebar}
|
||||
setParameterRequired={setParameterRequired}
|
||||
setParameterTemporalUnits={setParameterTemporalUnits}
|
||||
isFullscreen={isFullscreen}
|
||||
sidebar={sidebar}
|
||||
closeSidebar={closeSidebar}
|
||||
selectedTabId={selectedTabId}
|
||||
onCancel={() => setSharing(false)}
|
||||
/>
|
||||
@@ -561,13 +309,6 @@ function Dashboard(props: DashboardProps) {
|
||||
);
|
||||
}
|
||||
|
||||
function isSuccessfulFetchDashboardResult(
|
||||
result: FetchDashboardResult,
|
||||
): result is SuccessfulFetchDashboardResult {
|
||||
const hasError = "error" in result;
|
||||
return !hasError;
|
||||
}
|
||||
|
||||
export function isCancelledFetchDashboardResult(
|
||||
result: FetchDashboardResult,
|
||||
): result is CancelledFetchDashboardResult {
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
} from "metabase/lib/dashboard_grid";
|
||||
import { connect } from "metabase/lib/redux";
|
||||
import EmbedFrameS from "metabase/public/components/EmbedFrame/EmbedFrame.module.css";
|
||||
import type { EmbedResourceDownloadOptions } from "metabase/public/lib/types";
|
||||
import { addUndo } from "metabase/redux/undo";
|
||||
import { Box, Flex } from "metabase/ui";
|
||||
import LegendS from "metabase/visualizations/components/Legend.module.css";
|
||||
@@ -151,7 +152,7 @@ type OwnProps = {
|
||||
opts: NavigateToNewCardFromDashboardOpts,
|
||||
) => void;
|
||||
onEditingChange?: (dashboard: Dashboard | null) => void;
|
||||
downloadsEnabled: boolean;
|
||||
downloadsEnabled: EmbedResourceDownloadOptions;
|
||||
autoScrollToDashcardId: DashCardId | undefined;
|
||||
reportAutoScrolledToDashcard: () => void;
|
||||
};
|
||||
@@ -526,7 +527,7 @@ class DashboardGridInner extends Component<
|
||||
isMobile: boolean;
|
||||
gridItemWidth: number;
|
||||
totalNumGridCols: number;
|
||||
downloadsEnabled: boolean;
|
||||
downloadsEnabled: EmbedResourceDownloadOptions;
|
||||
shouldAutoScrollTo: boolean;
|
||||
reportAutoScrolledToDashcard: () => void;
|
||||
},
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { Query } from "history";
|
||||
import { useState } from "react";
|
||||
import { useMount } from "react-use";
|
||||
import { t } from "ttag";
|
||||
@@ -37,7 +36,7 @@ import { CancelEditButton, SaveEditButton } from "./buttons";
|
||||
export type DashboardHeaderProps = {
|
||||
dashboard: Dashboard;
|
||||
dashboardBeforeEditing?: Dashboard | null;
|
||||
parameterQueryParams: Query;
|
||||
parameterQueryParams: Record<string, string>;
|
||||
isAdditionalInfoVisible: boolean;
|
||||
} & DashboardFullscreenControls &
|
||||
DashboardRefreshPeriodControls &
|
||||
|
||||
@@ -2,28 +2,25 @@ import { type Route, type WithRouterProps, withRouter } from "react-router";
|
||||
import { t } from "ttag";
|
||||
|
||||
import { updateDashboardAndCards } from "metabase/dashboard/actions/save";
|
||||
import { getIsDirty, getIsEditing } from "metabase/dashboard/selectors";
|
||||
import { useConfirmRouteLeaveModal } from "metabase/hooks/use-confirm-route-leave-modal";
|
||||
import { useDispatch } from "metabase/lib/redux";
|
||||
import { useDispatch, useSelector } from "metabase/lib/redux";
|
||||
import { dismissAllUndo } from "metabase/redux/undo";
|
||||
import { Box, Button, Flex, Modal, Text } from "metabase/ui";
|
||||
|
||||
import { isNavigatingToCreateADashboardQuestion } from "./utils";
|
||||
|
||||
interface DashboardLeaveConfirmationModalProps extends WithRouterProps {
|
||||
isEditing: boolean;
|
||||
isDirty: boolean;
|
||||
route: Route;
|
||||
}
|
||||
|
||||
export const DashboardLeaveConfirmationModal = withRouter(
|
||||
({
|
||||
isEditing,
|
||||
isDirty,
|
||||
router,
|
||||
route,
|
||||
}: DashboardLeaveConfirmationModalProps) => {
|
||||
({ router, route }: DashboardLeaveConfirmationModalProps) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const isEditing = useSelector(getIsEditing);
|
||||
const isDirty = useSelector(getIsDirty);
|
||||
|
||||
const { opened, close, confirm, nextLocation } = useConfirmRouteLeaveModal({
|
||||
isEnabled: isEditing && isDirty,
|
||||
route,
|
||||
|
||||
@@ -1,57 +1,28 @@
|
||||
import cx from "classnames";
|
||||
import type { ReactNode } from "react";
|
||||
import { useCallback, useEffect } from "react";
|
||||
import type { ConnectedProps } from "react-redux";
|
||||
import type { PropsWithChildren } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import type { Route, WithRouterProps } from "react-router";
|
||||
import { push } from "react-router-redux";
|
||||
import { useUnmount } from "react-use";
|
||||
import { t } from "ttag";
|
||||
import _ from "underscore";
|
||||
|
||||
import ErrorBoundary from "metabase/ErrorBoundary";
|
||||
import CS from "metabase/css/core/index.css";
|
||||
import {
|
||||
addCardToDashboard,
|
||||
addHeadingDashCardToDashboard,
|
||||
addLinkDashCardToDashboard,
|
||||
addMarkdownDashCardToDashboard,
|
||||
cancelFetchDashboardCardData,
|
||||
closeDashboard,
|
||||
closeSidebar,
|
||||
fetchDashboard,
|
||||
fetchDashboardCardData,
|
||||
hideAddParameterPopover,
|
||||
initialize,
|
||||
navigateToNewCardFromDashboard,
|
||||
onReplaceAllDashCardVisualizationSettings,
|
||||
onUpdateDashCardColumnSettings,
|
||||
onUpdateDashCardVisualizationSettings,
|
||||
removeParameter,
|
||||
reset,
|
||||
setDashboardAttributes,
|
||||
setEditingDashboard,
|
||||
setParameterDefaultValue,
|
||||
setParameterFilteringParameters,
|
||||
setParameterIsMultiSelect,
|
||||
setParameterName,
|
||||
setParameterQueryType,
|
||||
setParameterRequired,
|
||||
setParameterSourceConfig,
|
||||
setParameterSourceType,
|
||||
setParameterTemporalUnits,
|
||||
setParameterType,
|
||||
setSharing,
|
||||
setSidebar,
|
||||
showAddParameterPopover,
|
||||
toggleSidebar,
|
||||
updateDashboardAndCards,
|
||||
} from "metabase/dashboard/actions";
|
||||
import { Dashboard } from "metabase/dashboard/components/Dashboard/Dashboard";
|
||||
import { DashboardLeaveConfirmationModal } from "metabase/dashboard/components/DashboardLeaveConfirmationModal";
|
||||
import {
|
||||
DashboardContextProvider,
|
||||
useDashboardContext,
|
||||
} from "metabase/dashboard/context";
|
||||
import {
|
||||
useDashboardUrlParams,
|
||||
useDashboardUrlQuery,
|
||||
useRefreshDashboard,
|
||||
} from "metabase/dashboard/hooks";
|
||||
import type { SuccessfulFetchDashboardResult } from "metabase/dashboard/types";
|
||||
import title from "metabase/hoc/Title";
|
||||
import titleWithLoadingTime from "metabase/hoc/TitleWithLoadingTime";
|
||||
import { useFavicon } from "metabase/hooks/use-favicon";
|
||||
@@ -59,203 +30,27 @@ import { useLoadingTimer } from "metabase/hooks/use-loading-timer";
|
||||
import { useUniqueId } from "metabase/hooks/use-unique-id";
|
||||
import { useWebNotification } from "metabase/hooks/use-web-notification";
|
||||
import { parseHashOptions } from "metabase/lib/browser";
|
||||
import { connect, useDispatch } from "metabase/lib/redux";
|
||||
import { useDispatch, useSelector } from "metabase/lib/redux";
|
||||
import * as Urls from "metabase/lib/urls";
|
||||
import { closeNavbar, setErrorPage } from "metabase/redux/app";
|
||||
import { setErrorPage } from "metabase/redux/app";
|
||||
import { addUndo, dismissUndo } from "metabase/redux/undo";
|
||||
import { getIsNavbarOpen } from "metabase/selectors/app";
|
||||
import {
|
||||
canManageSubscriptions,
|
||||
getUserIsAdmin,
|
||||
} from "metabase/selectors/user";
|
||||
import type { DashboardId } from "metabase-types/api";
|
||||
import type { State } from "metabase-types/store";
|
||||
|
||||
import { DASHBOARD_SLOW_TIMEOUT } from "../../constants";
|
||||
import {
|
||||
getClickBehaviorSidebarDashcard,
|
||||
getDashboardBeforeEditing,
|
||||
getDashboardComplete,
|
||||
getDocumentTitle,
|
||||
getFavicon,
|
||||
getIsAddParameterPopoverOpen,
|
||||
getIsAdditionalInfoVisible,
|
||||
getIsDashCardsLoadingComplete,
|
||||
getIsDashCardsRunning,
|
||||
getIsDirty,
|
||||
getIsEditing,
|
||||
getIsEditingParameter,
|
||||
getIsHeaderVisible,
|
||||
getIsNavigatingBackToDashboard,
|
||||
getIsSharing,
|
||||
getLoadingStartTime,
|
||||
getParameterValues,
|
||||
getSelectedTabId,
|
||||
getSidebar,
|
||||
getSlowCards,
|
||||
} from "../../selectors";
|
||||
import { getFavicon } from "../../selectors";
|
||||
|
||||
type OwnProps = {
|
||||
interface DashboardAppProps extends PropsWithChildren {
|
||||
dashboardId?: DashboardId;
|
||||
route: Route;
|
||||
params: { slug: string };
|
||||
children?: ReactNode;
|
||||
};
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: State) => {
|
||||
return {
|
||||
canManageSubscriptions: canManageSubscriptions(state),
|
||||
isAdmin: getUserIsAdmin(state),
|
||||
isNavbarOpen: getIsNavbarOpen(state),
|
||||
isEditing: getIsEditing(state),
|
||||
isSharing: getIsSharing(state),
|
||||
dashboardBeforeEditing: getDashboardBeforeEditing(state),
|
||||
isEditingParameter: getIsEditingParameter(state),
|
||||
isDirty: getIsDirty(state),
|
||||
dashboard: getDashboardComplete(state),
|
||||
slowCards: getSlowCards(state),
|
||||
parameterValues: getParameterValues(state),
|
||||
loadingStartTime: getLoadingStartTime(state),
|
||||
clickBehaviorSidebarDashcard: getClickBehaviorSidebarDashcard(state),
|
||||
isAddParameterPopoverOpen: getIsAddParameterPopoverOpen(state),
|
||||
sidebar: getSidebar(state),
|
||||
pageFavicon: getFavicon(state),
|
||||
documentTitle: getDocumentTitle(state),
|
||||
isRunning: getIsDashCardsRunning(state),
|
||||
isLoadingComplete: getIsDashCardsLoadingComplete(state),
|
||||
isHeaderVisible: getIsHeaderVisible(state),
|
||||
isAdditionalInfoVisible: getIsAdditionalInfoVisible(state),
|
||||
selectedTabId: getSelectedTabId(state),
|
||||
isNavigatingBackToDashboard: getIsNavigatingBackToDashboard(state),
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = {
|
||||
initialize,
|
||||
cancelFetchDashboardCardData,
|
||||
addCardToDashboard,
|
||||
addHeadingDashCardToDashboard,
|
||||
addMarkdownDashCardToDashboard,
|
||||
addLinkDashCardToDashboard,
|
||||
setEditingDashboard,
|
||||
setDashboardAttributes,
|
||||
setSharing,
|
||||
toggleSidebar,
|
||||
closeSidebar,
|
||||
closeNavbar,
|
||||
setErrorPage,
|
||||
setParameterName,
|
||||
setParameterType,
|
||||
navigateToNewCardFromDashboard,
|
||||
setParameterDefaultValue,
|
||||
setParameterRequired,
|
||||
setParameterTemporalUnits,
|
||||
setParameterIsMultiSelect,
|
||||
setParameterQueryType,
|
||||
setParameterSourceType,
|
||||
setParameterSourceConfig,
|
||||
setParameterFilteringParameters,
|
||||
showAddParameterPopover,
|
||||
removeParameter,
|
||||
onReplaceAllDashCardVisualizationSettings,
|
||||
onUpdateDashCardVisualizationSettings,
|
||||
onUpdateDashCardColumnSettings,
|
||||
updateDashboardAndCards,
|
||||
setSidebar,
|
||||
hideAddParameterPopover,
|
||||
fetchDashboard,
|
||||
fetchDashboardCardData,
|
||||
onChangeLocation: push,
|
||||
};
|
||||
|
||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||
type ReduxProps = ConnectedProps<typeof connector>;
|
||||
|
||||
export type DashboardAppProps = OwnProps & ReduxProps & WithRouterProps;
|
||||
|
||||
const DashboardApp = (props: DashboardAppProps) => {
|
||||
useFavicon({ favicon: props.pageFavicon });
|
||||
|
||||
const {
|
||||
dashboard,
|
||||
isRunning,
|
||||
isLoadingComplete,
|
||||
isEditing,
|
||||
isDirty,
|
||||
route,
|
||||
router,
|
||||
documentTitle: _documentTitle,
|
||||
isRunning: _isRunning,
|
||||
isLoadingComplete: _isLoadingComplete,
|
||||
location,
|
||||
canManageSubscriptions,
|
||||
isAdmin,
|
||||
isNavbarOpen,
|
||||
isSharing,
|
||||
dashboardBeforeEditing,
|
||||
isEditingParameter,
|
||||
slowCards,
|
||||
parameterValues,
|
||||
loadingStartTime,
|
||||
clickBehaviorSidebarDashcard,
|
||||
isAddParameterPopoverOpen,
|
||||
sidebar,
|
||||
isHeaderVisible,
|
||||
isAdditionalInfoVisible,
|
||||
selectedTabId,
|
||||
isNavigatingBackToDashboard,
|
||||
initialize,
|
||||
cancelFetchDashboardCardData,
|
||||
addCardToDashboard,
|
||||
addHeadingDashCardToDashboard,
|
||||
addMarkdownDashCardToDashboard,
|
||||
addLinkDashCardToDashboard,
|
||||
setEditingDashboard,
|
||||
setDashboardAttributes,
|
||||
setSharing,
|
||||
toggleSidebar,
|
||||
closeSidebar,
|
||||
closeNavbar,
|
||||
setErrorPage,
|
||||
setParameterName,
|
||||
setParameterType,
|
||||
navigateToNewCardFromDashboard,
|
||||
setParameterDefaultValue,
|
||||
setParameterRequired,
|
||||
setParameterTemporalUnits,
|
||||
setParameterIsMultiSelect,
|
||||
setParameterQueryType,
|
||||
setParameterSourceType,
|
||||
setParameterSourceConfig,
|
||||
setParameterFilteringParameters,
|
||||
showAddParameterPopover,
|
||||
removeParameter,
|
||||
onReplaceAllDashCardVisualizationSettings,
|
||||
onUpdateDashCardVisualizationSettings,
|
||||
onUpdateDashCardColumnSettings,
|
||||
updateDashboardAndCards,
|
||||
setSidebar,
|
||||
hideAddParameterPopover,
|
||||
fetchDashboard,
|
||||
fetchDashboardCardData,
|
||||
} = props;
|
||||
|
||||
const parameterQueryParams = location.query;
|
||||
const dashboardId = getDashboardId(props);
|
||||
|
||||
const options = parseHashOptions(window.location.hash);
|
||||
const editingOnLoad = options.edit;
|
||||
const addCardOnLoad = options.add != null ? Number(options.add) : undefined;
|
||||
const useSlowCardNotification = () => {
|
||||
const { dashboard, isRunning, isLoadingComplete } = useDashboardContext();
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { requestPermission, showNotification } = useWebNotification();
|
||||
|
||||
useUnmount(() => {
|
||||
dispatch(reset());
|
||||
dispatch(closeDashboard());
|
||||
});
|
||||
|
||||
const slowToastId = useUniqueId();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -306,9 +101,33 @@ const DashboardApp = (props: DashboardAppProps) => {
|
||||
timer: DASHBOARD_SLOW_TIMEOUT,
|
||||
onTimeout,
|
||||
});
|
||||
};
|
||||
|
||||
export const DashboardApp = ({
|
||||
location,
|
||||
params,
|
||||
router,
|
||||
route,
|
||||
dashboardId: _dashboardId,
|
||||
children,
|
||||
}: DashboardAppProps & WithRouterProps<{ slug: string }>) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [error, setError] = useState<string>();
|
||||
|
||||
const favicon = useSelector(getFavicon);
|
||||
useFavicon({ favicon });
|
||||
|
||||
const parameterQueryParams = location.query;
|
||||
const dashboardId =
|
||||
_dashboardId || (Urls.extractEntityId(params.slug) as DashboardId);
|
||||
|
||||
const options = parseHashOptions(window.location.hash);
|
||||
const editingOnLoad = options.edit;
|
||||
const addCardOnLoad = options.add != null ? Number(options.add) : undefined;
|
||||
|
||||
const { refreshDashboard } = useRefreshDashboard({
|
||||
dashboardId: dashboardId,
|
||||
dashboardId,
|
||||
parameterQueryParams,
|
||||
});
|
||||
|
||||
@@ -323,114 +142,98 @@ const DashboardApp = (props: DashboardAppProps) => {
|
||||
onRefreshPeriodChange,
|
||||
autoScrollToDashcardId,
|
||||
reportAutoScrolledToDashcard,
|
||||
downloadsEnabled,
|
||||
background,
|
||||
bordered,
|
||||
titled,
|
||||
theme,
|
||||
setTheme,
|
||||
} = useDashboardUrlParams({ location, onRefresh: refreshDashboard });
|
||||
|
||||
useDashboardUrlQuery(router, location);
|
||||
|
||||
const onLoadDashboard = (result: SuccessfulFetchDashboardResult) => {
|
||||
const dashboard = result.payload.dashboard;
|
||||
|
||||
try {
|
||||
if (editingOnLoad) {
|
||||
onRefreshPeriodChange(null);
|
||||
setEditingDashboard(dashboard);
|
||||
}
|
||||
if (addCardOnLoad != null) {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const tabParam = searchParams.get("tab");
|
||||
const tabId = tabParam ? parseInt(tabParam, 10) : null;
|
||||
|
||||
dispatch(
|
||||
addCardToDashboard({
|
||||
dashId: dashboardId,
|
||||
cardId: addCardOnLoad,
|
||||
tabId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Response && error.status === 404) {
|
||||
setErrorPage({ ...error, context: "dashboard" });
|
||||
} else {
|
||||
console.error(error);
|
||||
setError(error as string);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cx(CS.shrinkBelowContentSize, CS.fullHeight)}>
|
||||
<DashboardLeaveConfirmationModal
|
||||
route={route}
|
||||
isDirty={isDirty}
|
||||
isEditing={isEditing}
|
||||
/>
|
||||
<Dashboard
|
||||
<ErrorBoundary message={error}>
|
||||
<DashboardContextProvider
|
||||
dashboardId={dashboardId}
|
||||
editingOnLoad={editingOnLoad}
|
||||
addCardOnLoad={addCardOnLoad}
|
||||
parameterQueryParams={parameterQueryParams}
|
||||
isFullscreen={isFullscreen}
|
||||
onFullscreenChange={onFullscreenChange}
|
||||
hasNightModeToggle={hasNightModeToggle}
|
||||
onNightModeChange={onNightModeChange}
|
||||
isNightMode={isNightMode}
|
||||
refreshPeriod={refreshPeriod}
|
||||
setRefreshElapsedHook={setRefreshElapsedHook}
|
||||
onRefreshPeriodChange={onRefreshPeriodChange}
|
||||
background={background}
|
||||
bordered={bordered}
|
||||
titled={titled}
|
||||
theme={theme}
|
||||
setTheme={setTheme}
|
||||
downloadsEnabled={downloadsEnabled}
|
||||
autoScrollToDashcardId={autoScrollToDashcardId}
|
||||
reportAutoScrolledToDashcard={reportAutoScrolledToDashcard}
|
||||
isFullscreen={isFullscreen}
|
||||
refreshPeriod={refreshPeriod}
|
||||
isNightMode={isNightMode}
|
||||
hasNightModeToggle={hasNightModeToggle}
|
||||
setRefreshElapsedHook={setRefreshElapsedHook}
|
||||
onNightModeChange={onNightModeChange}
|
||||
onFullscreenChange={onFullscreenChange}
|
||||
onRefreshPeriodChange={onRefreshPeriodChange}
|
||||
parameterQueryParams={parameterQueryParams}
|
||||
canManageSubscriptions={canManageSubscriptions}
|
||||
isAdmin={isAdmin}
|
||||
isNavbarOpen={isNavbarOpen}
|
||||
isEditing={isEditing}
|
||||
isSharing={isSharing}
|
||||
dashboardBeforeEditing={dashboardBeforeEditing}
|
||||
isEditingParameter={isEditingParameter}
|
||||
isDirty={isDirty}
|
||||
dashboard={dashboard}
|
||||
slowCards={slowCards}
|
||||
parameterValues={parameterValues}
|
||||
loadingStartTime={loadingStartTime}
|
||||
clickBehaviorSidebarDashcard={clickBehaviorSidebarDashcard}
|
||||
isAddParameterPopoverOpen={isAddParameterPopoverOpen}
|
||||
sidebar={sidebar}
|
||||
isHeaderVisible={isHeaderVisible}
|
||||
isAdditionalInfoVisible={isAdditionalInfoVisible}
|
||||
selectedTabId={selectedTabId}
|
||||
isNavigatingBackToDashboard={isNavigatingBackToDashboard}
|
||||
initialize={initialize}
|
||||
cancelFetchDashboardCardData={cancelFetchDashboardCardData}
|
||||
addCardToDashboard={addCardToDashboard}
|
||||
addHeadingDashCardToDashboard={addHeadingDashCardToDashboard}
|
||||
addMarkdownDashCardToDashboard={addMarkdownDashCardToDashboard}
|
||||
addLinkDashCardToDashboard={addLinkDashCardToDashboard}
|
||||
setEditingDashboard={setEditingDashboard}
|
||||
setDashboardAttributes={setDashboardAttributes}
|
||||
setSharing={setSharing}
|
||||
toggleSidebar={toggleSidebar}
|
||||
closeSidebar={closeSidebar}
|
||||
closeNavbar={closeNavbar}
|
||||
setErrorPage={setErrorPage}
|
||||
setParameterName={setParameterName}
|
||||
setParameterType={setParameterType}
|
||||
navigateToNewCardFromDashboard={navigateToNewCardFromDashboard}
|
||||
setParameterDefaultValue={setParameterDefaultValue}
|
||||
setParameterRequired={setParameterRequired}
|
||||
setParameterTemporalUnits={setParameterTemporalUnits}
|
||||
setParameterIsMultiSelect={setParameterIsMultiSelect}
|
||||
setParameterQueryType={setParameterQueryType}
|
||||
setParameterSourceType={setParameterSourceType}
|
||||
setParameterSourceConfig={setParameterSourceConfig}
|
||||
setParameterFilteringParameters={setParameterFilteringParameters}
|
||||
showAddParameterPopover={showAddParameterPopover}
|
||||
removeParameter={removeParameter}
|
||||
onReplaceAllDashCardVisualizationSettings={
|
||||
onReplaceAllDashCardVisualizationSettings
|
||||
}
|
||||
onUpdateDashCardVisualizationSettings={
|
||||
onUpdateDashCardVisualizationSettings
|
||||
}
|
||||
onUpdateDashCardColumnSettings={onUpdateDashCardColumnSettings}
|
||||
updateDashboardAndCards={updateDashboardAndCards}
|
||||
setSidebar={setSidebar}
|
||||
hideAddParameterPopover={hideAddParameterPopover}
|
||||
fetchDashboard={fetchDashboard}
|
||||
fetchDashboardCardData={fetchDashboardCardData}
|
||||
/>
|
||||
{/* For rendering modal urls */}
|
||||
{props.children}
|
||||
</div>
|
||||
onLoad={onLoadDashboard}
|
||||
onError={(result) => dispatch(setErrorPage(result.payload))}
|
||||
hideParameters={null}
|
||||
font={null}
|
||||
>
|
||||
<DashboardTitle />
|
||||
<div className={cx(CS.shrinkBelowContentSize, CS.fullHeight)}>
|
||||
<DashboardLeaveConfirmationModal route={route} />
|
||||
<Dashboard />
|
||||
{/* For rendering modal urls */}
|
||||
{children}
|
||||
</div>
|
||||
</DashboardContextProvider>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
function getDashboardId({ dashboardId, params }: DashboardAppProps) {
|
||||
if (dashboardId) {
|
||||
return dashboardId;
|
||||
}
|
||||
const DashboardTitle = () => {
|
||||
const { dashboard, documentTitle, pageFavicon } = useDashboardContext();
|
||||
|
||||
return Urls.extractEntityId(params.slug) as DashboardId;
|
||||
}
|
||||
useFavicon({ favicon: pageFavicon });
|
||||
useSlowCardNotification();
|
||||
|
||||
export const DashboardAppConnected = _.compose(
|
||||
connector,
|
||||
title(
|
||||
({
|
||||
dashboard,
|
||||
documentTitle,
|
||||
}: Pick<ReduxProps, "dashboard" | "documentTitle">) => ({
|
||||
const Component = _.compose(
|
||||
title(() => ({
|
||||
title: documentTitle || dashboard?.name,
|
||||
titleIndex: 1,
|
||||
}),
|
||||
),
|
||||
titleWithLoadingTime("loadingStartTime"),
|
||||
)(DashboardApp);
|
||||
})),
|
||||
titleWithLoadingTime("loadingStartTime"),
|
||||
)(() => null);
|
||||
|
||||
return <Component />;
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
screen,
|
||||
waitForLoaderToBeRemoved,
|
||||
} from "__support__/ui";
|
||||
import { DashboardAppConnected } from "metabase/dashboard/containers/DashboardApp/DashboardApp";
|
||||
import { DashboardApp } from "metabase/dashboard/containers/DashboardApp/DashboardApp";
|
||||
import { BEFORE_UNLOAD_UNSAVED_MESSAGE } from "metabase/hooks/use-before-unload";
|
||||
import { checkNotNull } from "metabase/lib/types";
|
||||
import type { Dashboard } from "metabase-types/api";
|
||||
@@ -91,7 +91,7 @@ async function setup({ dashboard }: Options = {}) {
|
||||
return (
|
||||
<main>
|
||||
<link rel="icon" />
|
||||
<DashboardAppConnected {...props} />
|
||||
<DashboardApp {...props} />
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
319
frontend/src/metabase/dashboard/context/context.tsx
Normal file
319
frontend/src/metabase/dashboard/context/context.tsx
Normal file
@@ -0,0 +1,319 @@
|
||||
import {
|
||||
type PropsWithChildren,
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
} from "react";
|
||||
import { usePrevious, useUnmount } from "react-use";
|
||||
import { isEqual, isObject, noop } from "underscore";
|
||||
|
||||
import { useDispatch } from "metabase/lib/redux";
|
||||
import type {
|
||||
DisplayTheme,
|
||||
EmbedResourceDownloadOptions,
|
||||
} from "metabase/public/lib/types";
|
||||
import type { DashboardId } from "metabase-types/api";
|
||||
|
||||
import { navigateToNewCardFromDashboard } from "../actions";
|
||||
import type { NavigateToNewCardFromDashboardOpts } from "../components/DashCard/types";
|
||||
import { SIDEBAR_NAME } from "../constants";
|
||||
import type { UseAutoScrollToDashcardResult } from "../hooks/use-auto-scroll-to-dashcard";
|
||||
import type {
|
||||
DashboardFullscreenControls,
|
||||
DashboardNightModeControls,
|
||||
DashboardRefreshPeriodControls,
|
||||
EmbedBackground,
|
||||
EmbedFont,
|
||||
EmbedHideParameters,
|
||||
EmbedTitle,
|
||||
FailedFetchDashboardResult,
|
||||
FetchDashboardResult,
|
||||
SuccessfulFetchDashboardResult,
|
||||
} from "../types";
|
||||
|
||||
import { type ReduxProps, connector } from "./redux";
|
||||
|
||||
type DashboardLoadingState = {
|
||||
isLoading: boolean;
|
||||
};
|
||||
|
||||
type OwnProps = {
|
||||
dashboardId: DashboardId;
|
||||
parameterQueryParams?: Record<string, string>;
|
||||
onLoad?: (result: SuccessfulFetchDashboardResult) => void;
|
||||
onError?: (result: FailedFetchDashboardResult) => void;
|
||||
navigateToNewCardFromDashboard?: (
|
||||
opts: NavigateToNewCardFromDashboardOpts,
|
||||
) => void;
|
||||
};
|
||||
|
||||
type OwnResult = {
|
||||
shouldRenderAsNightMode: boolean;
|
||||
handleAddQuestion: () => void;
|
||||
};
|
||||
|
||||
type DashboardOptions = {
|
||||
background: EmbedBackground;
|
||||
bordered: boolean;
|
||||
titled: EmbedTitle;
|
||||
hideParameters: EmbedHideParameters;
|
||||
font: EmbedFont;
|
||||
theme: DisplayTheme;
|
||||
setTheme: (theme: DisplayTheme) => void;
|
||||
downloadsEnabled: EmbedResourceDownloadOptions;
|
||||
};
|
||||
|
||||
type DashboardControls = DashboardFullscreenControls &
|
||||
DashboardNightModeControls &
|
||||
DashboardRefreshPeriodControls &
|
||||
UseAutoScrollToDashcardResult &
|
||||
DashboardOptions;
|
||||
|
||||
type ContextProps = OwnProps & ReduxProps & Partial<DashboardControls>;
|
||||
|
||||
type ContextReturned = OwnResult &
|
||||
OwnProps &
|
||||
ReduxProps &
|
||||
Required<DashboardControls> &
|
||||
DashboardLoadingState;
|
||||
|
||||
const DashboardContext = createContext<ContextReturned | undefined>(undefined);
|
||||
|
||||
const DashboardContextProviderInner = ({
|
||||
dashboardId,
|
||||
parameterQueryParams = {},
|
||||
onLoad,
|
||||
onError,
|
||||
|
||||
children,
|
||||
|
||||
// url params
|
||||
isFullscreen = false,
|
||||
onFullscreenChange = noop,
|
||||
hasNightModeToggle = false,
|
||||
onNightModeChange = noop,
|
||||
isNightMode = false,
|
||||
refreshPeriod = null,
|
||||
setRefreshElapsedHook = noop,
|
||||
onRefreshPeriodChange = noop,
|
||||
background = true,
|
||||
bordered = true,
|
||||
titled = true,
|
||||
font = null,
|
||||
theme = "light",
|
||||
setTheme = noop,
|
||||
hideParameters = null,
|
||||
downloadsEnabled = { pdf: true, dashcard: true },
|
||||
autoScrollToDashcardId = undefined,
|
||||
reportAutoScrolledToDashcard = noop,
|
||||
|
||||
// redux selectors
|
||||
dashboard,
|
||||
selectedTabId,
|
||||
isEditing,
|
||||
isNavigatingBackToDashboard,
|
||||
parameterValues,
|
||||
|
||||
// redux actions
|
||||
addCardToDashboard,
|
||||
cancelFetchDashboardCardData,
|
||||
fetchDashboard,
|
||||
fetchDashboardCardData,
|
||||
initialize,
|
||||
setEditingDashboard,
|
||||
toggleSidebar,
|
||||
reset,
|
||||
closeDashboard,
|
||||
navigateToNewCardFromDashboard: _navigateToNewCardFromDashboard,
|
||||
...reduxProps
|
||||
}: PropsWithChildren<ContextProps>) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [isInitialized, setIsInitialized] = useState(false);
|
||||
|
||||
const previousDashboard = usePrevious(dashboard);
|
||||
const previousDashboardId = usePrevious(dashboardId);
|
||||
const previousTabId = usePrevious(selectedTabId);
|
||||
const previousParameterValues = usePrevious(parameterValues);
|
||||
|
||||
const shouldRenderAsNightMode = Boolean(isNightMode && isFullscreen);
|
||||
|
||||
const handleAddQuestion = useCallback(() => {
|
||||
if (!isEditing) {
|
||||
onRefreshPeriodChange?.(null);
|
||||
setEditingDashboard(dashboard);
|
||||
}
|
||||
toggleSidebar(SIDEBAR_NAME.addQuestion);
|
||||
}, [
|
||||
dashboard,
|
||||
isEditing,
|
||||
onRefreshPeriodChange,
|
||||
setEditingDashboard,
|
||||
toggleSidebar,
|
||||
]);
|
||||
|
||||
const handleLoadDashboard = useCallback(
|
||||
async (dashboardId: DashboardId) => {
|
||||
initialize({ clearCache: !isNavigatingBackToDashboard });
|
||||
|
||||
const result = await fetchDashboard({
|
||||
dashId: dashboardId,
|
||||
queryParams: parameterQueryParams,
|
||||
options: {
|
||||
clearCache: !isNavigatingBackToDashboard,
|
||||
preserveParameters: isNavigatingBackToDashboard,
|
||||
},
|
||||
});
|
||||
|
||||
if (!isSuccessfulFetchDashboardResult(result)) {
|
||||
if (isFailedFetchDashboardResult(result)) {
|
||||
onError?.(result);
|
||||
}
|
||||
return;
|
||||
}
|
||||
onLoad?.(result);
|
||||
},
|
||||
[
|
||||
fetchDashboard,
|
||||
initialize,
|
||||
isNavigatingBackToDashboard,
|
||||
onError,
|
||||
onLoad,
|
||||
parameterQueryParams,
|
||||
],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const hasDashboardChanged = dashboardId !== previousDashboardId;
|
||||
if (hasDashboardChanged) {
|
||||
handleLoadDashboard(dashboardId).then(() => setIsInitialized(true));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dashboard) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hasDashboardLoaded = !previousDashboard;
|
||||
const hasTabChanged = selectedTabId !== previousTabId;
|
||||
const hasParameterValueChanged = !isEqual(
|
||||
parameterValues,
|
||||
previousParameterValues,
|
||||
);
|
||||
|
||||
if (hasDashboardLoaded) {
|
||||
fetchDashboardCardData({ reload: false, clearCache: true });
|
||||
} else if (hasTabChanged || hasParameterValueChanged) {
|
||||
fetchDashboardCardData();
|
||||
}
|
||||
}, [
|
||||
dashboard,
|
||||
dashboardId,
|
||||
fetchDashboardCardData,
|
||||
handleLoadDashboard,
|
||||
isInitialized,
|
||||
parameterValues,
|
||||
previousDashboard,
|
||||
previousDashboardId,
|
||||
previousParameterValues,
|
||||
previousTabId,
|
||||
selectedTabId,
|
||||
]);
|
||||
|
||||
useUnmount(() => {
|
||||
cancelFetchDashboardCardData();
|
||||
});
|
||||
|
||||
useUnmount(() => {
|
||||
reset();
|
||||
closeDashboard();
|
||||
});
|
||||
|
||||
return (
|
||||
<DashboardContext.Provider
|
||||
value={{
|
||||
dashboardId,
|
||||
parameterQueryParams,
|
||||
onLoad,
|
||||
onError,
|
||||
|
||||
isLoading: !dashboard,
|
||||
|
||||
handleAddQuestion,
|
||||
|
||||
isFullscreen,
|
||||
onFullscreenChange,
|
||||
hasNightModeToggle,
|
||||
onNightModeChange,
|
||||
isNightMode,
|
||||
shouldRenderAsNightMode,
|
||||
refreshPeriod,
|
||||
setRefreshElapsedHook,
|
||||
onRefreshPeriodChange,
|
||||
background,
|
||||
bordered,
|
||||
titled,
|
||||
font,
|
||||
theme,
|
||||
setTheme,
|
||||
hideParameters,
|
||||
downloadsEnabled,
|
||||
autoScrollToDashcardId,
|
||||
reportAutoScrolledToDashcard,
|
||||
|
||||
// redux selectors
|
||||
dashboard,
|
||||
selectedTabId,
|
||||
isEditing,
|
||||
isNavigatingBackToDashboard,
|
||||
parameterValues,
|
||||
|
||||
// redux actions
|
||||
addCardToDashboard,
|
||||
cancelFetchDashboardCardData,
|
||||
fetchDashboard,
|
||||
fetchDashboardCardData,
|
||||
initialize,
|
||||
setEditingDashboard,
|
||||
toggleSidebar,
|
||||
reset,
|
||||
closeDashboard,
|
||||
navigateToNewCardFromDashboard:
|
||||
_navigateToNewCardFromDashboard ??
|
||||
(() => dispatch(navigateToNewCardFromDashboard())),
|
||||
...reduxProps,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</DashboardContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const DashboardContextProvider = connector(
|
||||
DashboardContextProviderInner,
|
||||
);
|
||||
|
||||
export function useDashboardContext() {
|
||||
const context = useContext(DashboardContext);
|
||||
if (context === undefined) {
|
||||
throw new Error(
|
||||
"useDashboardContext must be used within a DashboardContextProvider",
|
||||
);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
function isSuccessfulFetchDashboardResult(
|
||||
result: FetchDashboardResult,
|
||||
): result is SuccessfulFetchDashboardResult {
|
||||
const hasError = "error" in result;
|
||||
return !hasError;
|
||||
}
|
||||
|
||||
export function isFailedFetchDashboardResult(
|
||||
result: FetchDashboardResult,
|
||||
): result is FailedFetchDashboardResult {
|
||||
return isObject(result.payload) && "error" in result;
|
||||
}
|
||||
1
frontend/src/metabase/dashboard/context/index.ts
Normal file
1
frontend/src/metabase/dashboard/context/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./context";
|
||||
145
frontend/src/metabase/dashboard/context/redux.ts
Normal file
145
frontend/src/metabase/dashboard/context/redux.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import type { ConnectedProps } from "react-redux";
|
||||
import { push } from "react-router-redux";
|
||||
|
||||
import { deletePermanently } from "metabase/archive/actions";
|
||||
import { connect } from "metabase/lib/redux";
|
||||
import { closeNavbar, setErrorPage } from "metabase/redux/app";
|
||||
import { dismissUndo } from "metabase/redux/undo";
|
||||
import { getIsNavbarOpen } from "metabase/selectors/app";
|
||||
import {
|
||||
canManageSubscriptions,
|
||||
getUserIsAdmin,
|
||||
} from "metabase/selectors/user";
|
||||
import type { State } from "metabase-types/store";
|
||||
|
||||
import {
|
||||
addCardToDashboard,
|
||||
addHeadingDashCardToDashboard,
|
||||
addLinkDashCardToDashboard,
|
||||
addMarkdownDashCardToDashboard,
|
||||
cancelFetchDashboardCardData,
|
||||
closeDashboard,
|
||||
closeSidebar,
|
||||
fetchDashboard,
|
||||
fetchDashboardCardData,
|
||||
hideAddParameterPopover,
|
||||
initialize,
|
||||
moveDashboardToCollection,
|
||||
onReplaceAllDashCardVisualizationSettings,
|
||||
onUpdateDashCardColumnSettings,
|
||||
onUpdateDashCardVisualizationSettings,
|
||||
removeParameter,
|
||||
reset,
|
||||
setArchivedDashboard,
|
||||
setDashboardAttributes,
|
||||
setEditingDashboard,
|
||||
setParameterDefaultValue,
|
||||
setParameterFilteringParameters,
|
||||
setParameterIsMultiSelect,
|
||||
setParameterName,
|
||||
setParameterQueryType,
|
||||
setParameterRequired,
|
||||
setParameterSourceConfig,
|
||||
setParameterSourceType,
|
||||
setParameterTemporalUnits,
|
||||
setParameterType,
|
||||
setSharing,
|
||||
setSidebar,
|
||||
showAddParameterPopover,
|
||||
toggleSidebar,
|
||||
updateDashboardAndCards,
|
||||
} from "../actions";
|
||||
import {
|
||||
getClickBehaviorSidebarDashcard,
|
||||
getDashboardBeforeEditing,
|
||||
getDashboardComplete,
|
||||
getDocumentTitle,
|
||||
getFavicon,
|
||||
getIsAddParameterPopoverOpen,
|
||||
getIsAdditionalInfoVisible,
|
||||
getIsDashCardsLoadingComplete,
|
||||
getIsDashCardsRunning,
|
||||
getIsDirty,
|
||||
getIsEditing,
|
||||
getIsEditingParameter,
|
||||
getIsHeaderVisible,
|
||||
getIsNavigatingBackToDashboard,
|
||||
getIsSharing,
|
||||
getLoadingStartTime,
|
||||
getParameterValues,
|
||||
getSelectedTabId,
|
||||
getSidebar,
|
||||
getSlowCards,
|
||||
} from "../selectors";
|
||||
|
||||
const mapStateToProps = (state: State) => ({
|
||||
canManageSubscriptions: canManageSubscriptions(state),
|
||||
isAdmin: getUserIsAdmin(state),
|
||||
isNavbarOpen: getIsNavbarOpen(state),
|
||||
isEditing: getIsEditing(state),
|
||||
isSharing: getIsSharing(state),
|
||||
dashboardBeforeEditing: getDashboardBeforeEditing(state),
|
||||
isEditingParameter: getIsEditingParameter(state),
|
||||
isDirty: getIsDirty(state),
|
||||
dashboard: getDashboardComplete(state),
|
||||
slowCards: getSlowCards(state),
|
||||
parameterValues: getParameterValues(state),
|
||||
loadingStartTime: getLoadingStartTime(state),
|
||||
clickBehaviorSidebarDashcard: getClickBehaviorSidebarDashcard(state),
|
||||
isAddParameterPopoverOpen: getIsAddParameterPopoverOpen(state),
|
||||
sidebar: getSidebar(state),
|
||||
pageFavicon: getFavicon(state),
|
||||
documentTitle: getDocumentTitle(state),
|
||||
isRunning: getIsDashCardsRunning(state),
|
||||
isLoadingComplete: getIsDashCardsLoadingComplete(state),
|
||||
isHeaderVisible: getIsHeaderVisible(state),
|
||||
isAdditionalInfoVisible: getIsAdditionalInfoVisible(state),
|
||||
selectedTabId: getSelectedTabId(state),
|
||||
isNavigatingBackToDashboard: getIsNavigatingBackToDashboard(state),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
initialize,
|
||||
cancelFetchDashboardCardData,
|
||||
addCardToDashboard,
|
||||
addHeadingDashCardToDashboard,
|
||||
addMarkdownDashCardToDashboard,
|
||||
addLinkDashCardToDashboard,
|
||||
setEditingDashboard,
|
||||
setDashboardAttributes,
|
||||
setSharing,
|
||||
toggleSidebar,
|
||||
closeSidebar,
|
||||
closeNavbar,
|
||||
setErrorPage,
|
||||
setParameterName,
|
||||
setParameterType,
|
||||
setParameterDefaultValue,
|
||||
setParameterRequired,
|
||||
setParameterTemporalUnits,
|
||||
setParameterIsMultiSelect,
|
||||
setParameterQueryType,
|
||||
setParameterSourceType,
|
||||
setParameterSourceConfig,
|
||||
setParameterFilteringParameters,
|
||||
showAddParameterPopover,
|
||||
removeParameter,
|
||||
onReplaceAllDashCardVisualizationSettings,
|
||||
onUpdateDashCardVisualizationSettings,
|
||||
onUpdateDashCardColumnSettings,
|
||||
updateDashboardAndCards,
|
||||
setSidebar,
|
||||
hideAddParameterPopover,
|
||||
fetchDashboard,
|
||||
fetchDashboardCardData,
|
||||
onChangeLocation: push,
|
||||
reset,
|
||||
closeDashboard,
|
||||
dismissUndo,
|
||||
setArchivedDashboard,
|
||||
moveDashboardToCollection,
|
||||
deletePermanently,
|
||||
};
|
||||
|
||||
export const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||
export type ReduxProps = ConnectedProps<typeof connector>;
|
||||
@@ -6,7 +6,7 @@ import { parseHashOptions, stringifyHashOptions } from "metabase/lib/browser";
|
||||
import { useDispatch } from "metabase/lib/redux";
|
||||
import type { DashCardId } from "metabase-types/api";
|
||||
|
||||
interface UseAutoScrollToDashcardResult {
|
||||
export interface UseAutoScrollToDashcardResult {
|
||||
autoScrollToDashcardId: DashCardId | undefined;
|
||||
reportAutoScrolledToDashcard: () => void;
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ export const getDashboardById = (state: State, dashboardId: DashboardId) => {
|
||||
|
||||
export const getDashboardComplete = createSelector(
|
||||
[getDashboard, getDashcards],
|
||||
(dashboard, dashcards) => {
|
||||
(dashboard, dashcards): Dashboard | null => {
|
||||
if (!dashboard) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ import type {
|
||||
} from "metabase/public/lib/types";
|
||||
import type { ClickActionModeGetter } from "metabase/visualizations/types";
|
||||
|
||||
type EmbedBackground = boolean;
|
||||
export type EmbedBackground = boolean;
|
||||
|
||||
type EmbedTitle = boolean;
|
||||
export type EmbedTitle = boolean;
|
||||
|
||||
export type EmbedHideParameters = string | null;
|
||||
export type EmbedHideParametersControls = {
|
||||
|
||||
@@ -36,7 +36,7 @@ export type EmbeddingParametersValues = Record<string, string>;
|
||||
*/
|
||||
export type EmbeddingDisplayOptions = {
|
||||
font: null | string;
|
||||
theme: DisplayTheme;
|
||||
theme?: DisplayTheme;
|
||||
background: boolean;
|
||||
bordered: boolean;
|
||||
titled: boolean;
|
||||
|
||||
@@ -29,7 +29,7 @@ import { DashboardCopyModalConnected } from "metabase/dashboard/components/Dashb
|
||||
import { DashboardMoveModalConnected } from "metabase/dashboard/components/DashboardMoveModal";
|
||||
import { ArchiveDashboardModalConnected } from "metabase/dashboard/containers/ArchiveDashboardModal";
|
||||
import { AutomaticDashboardAppConnected } from "metabase/dashboard/containers/AutomaticDashboardApp";
|
||||
import { DashboardAppConnected } from "metabase/dashboard/containers/DashboardApp/DashboardApp";
|
||||
import { DashboardApp } from "metabase/dashboard/containers/DashboardApp/DashboardApp";
|
||||
import { ModalRoute } from "metabase/hoc/ModalRoute";
|
||||
import { Route } from "metabase/hoc/Title";
|
||||
import { HomePage } from "metabase/home/components/HomePage";
|
||||
@@ -199,7 +199,7 @@ export const getRoutes = (store) => {
|
||||
<Route
|
||||
path="dashboard/:slug"
|
||||
title={t`Dashboard`}
|
||||
component={DashboardAppConnected}
|
||||
component={DashboardApp}
|
||||
>
|
||||
<ModalRoute
|
||||
path="move"
|
||||
|
||||
Reference in New Issue
Block a user