From dac0d7fd1b6b0de106e0d461acc727c191e1245b Mon Sep 17 00:00:00 2001 From: Paolo D'Amico Date: Tue, 9 Mar 2021 08:22:00 -0800 Subject: [PATCH 01/21] dashboard scene is now light --- frontend/src/scenes/sceneLogic.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/frontend/src/scenes/sceneLogic.ts b/frontend/src/scenes/sceneLogic.ts index d77d67e80cd98..691647bc54fb2 100644 --- a/frontend/src/scenes/sceneLogic.ts +++ b/frontend/src/scenes/sceneLogic.ts @@ -83,17 +83,14 @@ export const scenes: Record any> = { interface SceneConfig { onlyUnauthenticated?: boolean // Route should only be accessed when logged out (N.B. should be added to posthog/urls.py too) - allowUnauthenticated?: boolean // Route **can** be accessed when logged out (i.e. can be accessed when logged in too) + allowUnauthenticated?: boolean // Route **can** be accessed when logged out (i.e. can be accessed when logged in too; should be added to posthog/urls.py too) dark?: boolean // Background is $bg_mid plain?: boolean // Only keeps the main content and the top navigation bar hideTopNav?: boolean // Hides the top navigation bar (regardless of whether `plain` is `true` or not) - hideDemoWarnings?: boolean // Hides demo project (DemoWarning.tsx) + hideDemoWarnings?: boolean // Hides demo project warnings (DemoWarning.tsx) } export const sceneConfigurations: Partial> = { - [Scene.Dashboard]: { - dark: true, - }, [Scene.Insights]: { dark: true, }, From 10b9cfd00fd2a24ec0e537ac7a81001fe110ae18 Mon Sep 17 00:00:00 2001 From: Paolo D'Amico Date: Tue, 9 Mar 2021 08:24:32 -0800 Subject: [PATCH 02/21] significantly simplify logic of empty/error states --- frontend/src/scenes/dashboard/Dashboard.tsx | 30 +++++++++---------- .../src/scenes/dashboard/dashboardLogic.js | 8 ++--- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/frontend/src/scenes/dashboard/Dashboard.tsx b/frontend/src/scenes/dashboard/Dashboard.tsx index d753c4dd916ef..f41b7fb02c20d 100644 --- a/frontend/src/scenes/dashboard/Dashboard.tsx +++ b/frontend/src/scenes/dashboard/Dashboard.tsx @@ -2,7 +2,6 @@ import React from 'react' import { Link } from 'lib/components/Link' import { SceneLoading } from 'lib/utils' import { BindLogic, useValues } from 'kea' -import { userLogic } from 'scenes/userLogic' import { dashboardLogic } from 'scenes/dashboard/dashboardLogic' import { DashboardHeader } from 'scenes/dashboard/DashboardHeader' import { DashboardItems } from 'scenes/dashboard/DashboardItems' @@ -26,31 +25,32 @@ function _Dashboard({ id, shareToken }: Props): JSX.Element { function DashboardView({ id, shareToken }: Props): JSX.Element { const { dashboard, itemsLoading, items } = useValues(dashboardLogic) - const { user } = useValues(userLogic) const { dashboardsLoading } = useValues(dashboardsModel) + if (dashboardsLoading || itemsLoading) { + return + } + + if (!dashboard) { + return ( + <> +

A dashboard with the ID {id} was not found!

+ + + ) + } + return (
{!shareToken && } - {dashboardsLoading ? ( - - ) : !dashboard ? ( - <> -

A dashboard with the ID {id} was not found!

- - - ) : items && items.length > 0 ? ( + {items && items.length ? ( - ) : itemsLoading ? ( - - ) : user?.team?.ingested_event ? ( + ) : (

There are no panels on this dashboard.{' '} Click here to add some!

- ) : ( -

)}

) diff --git a/frontend/src/scenes/dashboard/dashboardLogic.js b/frontend/src/scenes/dashboard/dashboardLogic.js index 2ef10e0a81b24..c71d27a3d8a06 100644 --- a/frontend/src/scenes/dashboard/dashboardLogic.js +++ b/frontend/src/scenes/dashboard/dashboardLogic.js @@ -48,7 +48,6 @@ export const dashboardLogic = kea({ return dashboard } catch (error) { if (error.status === 404) { - // silently escape return [] } throw error @@ -120,10 +119,9 @@ export const dashboardLogic = kea({ items: [() => [selectors.allItems], (allItems) => allItems?.items?.filter((i) => !i.deleted)], itemsLoading: [() => [selectors.allItemsLoading], (allItemsLoading) => allItemsLoading], dashboard: [ - () => [selectors.allItems, dashboardsModel.selectors.dashboards], - (allItems, dashboards) => { - let dashboard = dashboards.find((d) => d.id === props.id) || false - return dashboard ? dashboard : allItems + () => [dashboardsModel.selectors.dashboards], + (dashboards) => { + return dashboards.find((d) => d.id === props.id) }, ], breakpoints: [() => [], () => ({ lg: 1600, sm: 940, xs: 480, xxs: 0 })], From b3d849dc145cfc2d91eac0f768f1b6bdca432d23 Mon Sep 17 00:00:00 2001 From: Paolo D'Amico Date: Tue, 9 Mar 2021 08:35:35 -0800 Subject: [PATCH 03/21] isOnSharedMode --- frontend/src/scenes/dashboard/Dashboard.tsx | 14 ++++----- .../src/scenes/dashboard/dashboardLogic.js | 31 ++++++++++++------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/frontend/src/scenes/dashboard/Dashboard.tsx b/frontend/src/scenes/dashboard/Dashboard.tsx index f41b7fb02c20d..19514b1f8312d 100644 --- a/frontend/src/scenes/dashboard/Dashboard.tsx +++ b/frontend/src/scenes/dashboard/Dashboard.tsx @@ -6,7 +6,6 @@ import { dashboardLogic } from 'scenes/dashboard/dashboardLogic' import { DashboardHeader } from 'scenes/dashboard/DashboardHeader' import { DashboardItems } from 'scenes/dashboard/DashboardItems' import { dashboardsModel } from '~/models/dashboardsModel' -import { HedgehogOverlay } from 'lib/components/HedgehogOverlay/HedgehogOverlay' import { hot } from 'react-hot-loader/root' interface Props { @@ -18,13 +17,13 @@ export const Dashboard = hot(_Dashboard) function _Dashboard({ id, shareToken }: Props): JSX.Element { return ( - + ) } -function DashboardView({ id, shareToken }: Props): JSX.Element { - const { dashboard, itemsLoading, items } = useValues(dashboardLogic) +function DashboardView(): JSX.Element { + const { dashboard, itemsLoading, items, isOnSharedMode } = useValues(dashboardLogic) const { dashboardsLoading } = useValues(dashboardsModel) if (dashboardsLoading || itemsLoading) { @@ -34,18 +33,17 @@ function DashboardView({ id, shareToken }: Props): JSX.Element { if (!dashboard) { return ( <> -

A dashboard with the ID {id} was not found!

- +

Dashboard not found.

) } return (
- {!shareToken && } + {!isOnSharedMode && } {items && items.length ? ( - + ) : (

There are no panels on this dashboard.{' '} diff --git a/frontend/src/scenes/dashboard/dashboardLogic.js b/frontend/src/scenes/dashboard/dashboardLogic.js index c71d27a3d8a06..38d1d3bde7b9e 100644 --- a/frontend/src/scenes/dashboard/dashboardLogic.js +++ b/frontend/src/scenes/dashboard/dashboardLogic.js @@ -32,6 +32,7 @@ export const dashboardLogic = kea({ refreshDashboardItem: (id) => ({ id }), refreshAllDashboardItems: true, updateAndRefreshDashboard: true, + setIsOnSharedMode: (isOnSharedMode) => ({ isOnSharedMode }), // whether the user is opening the dashboard in shared mode (i.e. with a shareToken) }), loaders: ({ props }) => ({ @@ -94,12 +95,10 @@ export const dashboardLogic = kea({ return { ...state, items: item.dashboard === parseInt(props.id) ? [...state.items, item] : state.items } }, }, - draggingEnabled: [ - () => (isAndroidOrIOS() ? 'off' : 'on'), + columns: [ + null, { - enableDragging: () => 'on', - enableWobblyDragging: () => 'wobbly', - disableDragging: () => 'off', + updateContainerWidth: (_, { columns }) => columns, }, ], containerWidth: [ @@ -108,10 +107,18 @@ export const dashboardLogic = kea({ updateContainerWidth: (_, { containerWidth }) => containerWidth, }, ], - columns: [ - null, + draggingEnabled: [ + () => (isAndroidOrIOS() ? 'off' : 'on'), { - updateContainerWidth: (_, { columns }) => columns, + enableDragging: () => 'on', + enableWobblyDragging: () => 'wobbly', + disableDragging: () => 'off', + }, + ], + isOnSharedMode: [ + false, + { + setIsOnSharedMode: (_, { isOnSharedMode }) => isOnSharedMode, }, ], }), @@ -224,8 +231,11 @@ export const dashboardLogic = kea({ }, ], }), - events: ({ actions, cache }) => ({ - afterMount: [actions.loadDashboardItems], + events: ({ actions, cache, props }) => ({ + afterMount: () => { + actions.loadDashboardItems() + actions.setIsOnSharedMode(!!props.shareToken) + }, beforeUnmount: () => { if (cache.draggingToastId) { toast.dismiss(cache.draggingToastId) @@ -233,7 +243,6 @@ export const dashboardLogic = kea({ } }, }), - listeners: ({ actions, values, key, cache }) => ({ addNewDashboard: async () => { prompt({ key: `new-dashboard-${key}` }).actions.prompt({ From 0d8229169af3932eb8719504821ca58dfac8aba6 Mon Sep 17 00:00:00 2001 From: Paolo D'Amico Date: Tue, 9 Mar 2021 09:09:30 -0800 Subject: [PATCH 04/21] fully refactor dashboard header --- frontend/src/global.scss | 9 + frontend/src/scenes/dashboard/Dashboard.tsx | 22 ++- .../src/scenes/dashboard/DashboardHeader.scss | 2 + .../src/scenes/dashboard/DashboardHeader.tsx | 158 +++++++----------- 4 files changed, 90 insertions(+), 101 deletions(-) diff --git a/frontend/src/global.scss b/frontend/src/global.scss index 9fc2694faf5be..4ceecc6b7afc0 100644 --- a/frontend/src/global.scss +++ b/frontend/src/global.scss @@ -341,6 +341,15 @@ code.code { margin-left: 5px; } +.btn-lg-2x { + font-size: 1.5rem !important; + line-height: 1 !important; + svg { + width: 1.5rem !important; + height: 1.5rem !important; + } +} + // Badges styles .badge { border-radius: 50%; diff --git a/frontend/src/scenes/dashboard/Dashboard.tsx b/frontend/src/scenes/dashboard/Dashboard.tsx index 19514b1f8312d..ebf7632fbcb47 100644 --- a/frontend/src/scenes/dashboard/Dashboard.tsx +++ b/frontend/src/scenes/dashboard/Dashboard.tsx @@ -1,12 +1,14 @@ import React from 'react' import { Link } from 'lib/components/Link' import { SceneLoading } from 'lib/utils' -import { BindLogic, useValues } from 'kea' +import { BindLogic, useActions, useValues } from 'kea' import { dashboardLogic } from 'scenes/dashboard/dashboardLogic' import { DashboardHeader } from 'scenes/dashboard/DashboardHeader' import { DashboardItems } from 'scenes/dashboard/DashboardItems' import { dashboardsModel } from '~/models/dashboardsModel' import { hot } from 'react-hot-loader/root' +import { DateFilter } from 'lib/components/DateFilter' +import { CalendarOutlined } from '@ant-design/icons' interface Props { id: string @@ -25,6 +27,7 @@ function _Dashboard({ id, shareToken }: Props): JSX.Element { function DashboardView(): JSX.Element { const { dashboard, itemsLoading, items, isOnSharedMode } = useValues(dashboardLogic) const { dashboardsLoading } = useValues(dashboardsModel) + const { updateAndRefreshDashboard } = useActions(dashboardLogic) if (dashboardsLoading || itemsLoading) { return @@ -43,7 +46,22 @@ function DashboardView(): JSX.Element { {!isOnSharedMode && } {items && items.length ? ( - +

+
+ ( + <> + + {key} + + )} + /> +
+ +
) : (

There are no panels on this dashboard.{' '} diff --git a/frontend/src/scenes/dashboard/DashboardHeader.scss b/frontend/src/scenes/dashboard/DashboardHeader.scss index dec8335cbce25..afd75cc84ecd1 100644 --- a/frontend/src/scenes/dashboard/DashboardHeader.scss +++ b/frontend/src/scenes/dashboard/DashboardHeader.scss @@ -40,6 +40,8 @@ } .dashboard-meta { white-space: nowrap; + display: flex; + align-items: center; .ant-btn { .anticon { vertical-align: baseline; diff --git a/frontend/src/scenes/dashboard/DashboardHeader.tsx b/frontend/src/scenes/dashboard/DashboardHeader.tsx index b2c0164a20c98..09c2c7ed57d76 100644 --- a/frontend/src/scenes/dashboard/DashboardHeader.tsx +++ b/frontend/src/scenes/dashboard/DashboardHeader.tsx @@ -1,7 +1,5 @@ -import './DashboardHeader.scss' - import { Loading, triggerResizeAfterADelay } from 'lib/utils' -import { Button, Dropdown, Menu, Select, Tooltip } from 'antd' +import { Button, Dropdown, Menu, Select } from 'antd' import { router } from 'kea-router' import React, { useState } from 'react' import { useActions, useValues } from 'kea' @@ -15,33 +13,27 @@ import { DeleteOutlined, FullscreenOutlined, FullscreenExitOutlined, - LockOutlined, - UnlockOutlined, ShareAltOutlined, - ReloadOutlined, - CalendarOutlined, } from '@ant-design/icons' import { FullScreen } from 'lib/components/FullScreen' import moment from 'moment' import { dashboardLogic } from 'scenes/dashboard/dashboardLogic' import { DashboardType } from '~/types' -import { DateFilter } from 'lib/components/DateFilter' +import './DashboardHeader.scss' export function DashboardHeader(): JSX.Element { - const { dashboard, draggingEnabled } = useValues(dashboardLogic) - const { - addNewDashboard, - renameDashboard, - enableDragging, - disableDragging, - updateAndRefreshDashboard, - refreshAllDashboardItems, - } = useActions(dashboardLogic) + const { dashboard } = useValues(dashboardLogic) + const { addNewDashboard } = useActions(dashboardLogic) const { dashboards, dashboardsLoading } = useValues(dashboardsModel) const { pinDashboard, unpinDashboard, deleteDashboard } = useActions(dashboardsModel) const [fullScreen, setFullScreen] = useState(false) const [showShareModal, setShowShareModal] = useState(false) + const togglePresentationMode = (): void => { + setFullScreen(!fullScreen) + triggerResizeAfterADelay() + } + return (

{fullScreen ? setFullScreen(false)} /> : null} @@ -76,104 +68,72 @@ export function DashboardHeader(): JSX.Element {
) : null}
- {dashboard ? ( -
- - ( - <> - - {key} - - )} - /> - - {!fullScreen ? ( - - - - ) : null} - - - - - - - - - - - - - - - - - {!fullScreen ? ( +
+ {!fullScreen ? ( + <> - } onClick={renameDashboard}> - Rename "{dashboard.name}" + }>Edit mode (E) + } onClick={togglePresentationMode}> + Presentation mode (F12) + {dashboard.pinned ? ( + } + onClick={() => unpinDashboard(dashboard.id)} + > + Unpin dashboard + + ) : ( + } + onClick={() => pinDashboard(dashboard.id)} + > + Pin dashboard + + )} + + } onClick={() => deleteDashboard({ id: dashboard.id, redirect: true })} - className="text-danger" + danger > - Delete + Delete dashboard } placement="bottomRight" > - +
- ) : null} + + + ) : ( + + )} +
)} From 99cdfe3ca9334228271212780aa17cbf51ef73e0 Mon Sep 17 00:00:00 2001 From: Paolo D'Amico Date: Tue, 9 Mar 2021 09:14:48 -0800 Subject: [PATCH 05/21] refactor created by notice --- .../src/scenes/dashboard/DashboardHeader.tsx | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/frontend/src/scenes/dashboard/DashboardHeader.tsx b/frontend/src/scenes/dashboard/DashboardHeader.tsx index 09c2c7ed57d76..bfefee7561484 100644 --- a/frontend/src/scenes/dashboard/DashboardHeader.tsx +++ b/frontend/src/scenes/dashboard/DashboardHeader.tsx @@ -59,14 +59,6 @@ export function DashboardHeader(): JSX.Element { ))} + New Dashboard - {dashboard.created_by ? ( -
- Created by {dashboard.created_by.first_name || dashboard.created_by.email || '-'} on{' '} - {moment(dashboard.created_at).format( - moment(dashboard.created_at).year() === moment().year() ? 'MMMM Do' : 'MMMM Do YYYY' - )} -
- ) : null}
@@ -76,6 +68,23 @@ export function DashboardHeader(): JSX.Element { trigger={['click']} overlay={ + {dashboard.created_by && ( + <> + + Created by{' '} + {dashboard.created_by.first_name || + dashboard.created_by.email || + '-'}{' '} + on{' '} + {moment(dashboard.created_at).format( + moment(dashboard.created_at).year() === moment().year() + ? 'MMMM Do' + : 'MMMM Do YYYY' + )} + + + + )} }>Edit mode (E) } onClick={togglePresentationMode}> Presentation mode (F12) From cb174cb5b2da8a11db7dd502e0e216b3d537d98e Mon Sep 17 00:00:00 2001 From: Paolo D'Amico Date: Tue, 9 Mar 2021 09:43:17 -0800 Subject: [PATCH 06/21] add last refreshed & refresh everything --- .../{DashboardHeader.scss => Dashboard.scss} | 17 +++++++++++++++++ frontend/src/scenes/dashboard/Dashboard.tsx | 19 ++++++++++++++----- .../src/scenes/dashboard/DashboardHeader.tsx | 1 - .../src/scenes/dashboard/dashboardLogic.js | 17 +++++++++++++++++ 4 files changed, 48 insertions(+), 6 deletions(-) rename frontend/src/scenes/dashboard/{DashboardHeader.scss => Dashboard.scss} (86%) diff --git a/frontend/src/scenes/dashboard/DashboardHeader.scss b/frontend/src/scenes/dashboard/Dashboard.scss similarity index 86% rename from frontend/src/scenes/dashboard/DashboardHeader.scss rename to frontend/src/scenes/dashboard/Dashboard.scss index afd75cc84ecd1..737064dc18cdb 100644 --- a/frontend/src/scenes/dashboard/DashboardHeader.scss +++ b/frontend/src/scenes/dashboard/Dashboard.scss @@ -1,5 +1,22 @@ @import '~/vars'; +.dashboard { + margin-top: $default_spacing * 2; + + .dashboard-items-actions { + margin-bottom: $default_spacing; + display: flex; + + .left-item { + flex-grow: 1; + } + + .ant-btn { + padding-left: 4px !important; + } + } +} + .dashboard-header { display: flex; justify-content: space-between; diff --git a/frontend/src/scenes/dashboard/Dashboard.tsx b/frontend/src/scenes/dashboard/Dashboard.tsx index ebf7632fbcb47..cd1b6b7ab66a1 100644 --- a/frontend/src/scenes/dashboard/Dashboard.tsx +++ b/frontend/src/scenes/dashboard/Dashboard.tsx @@ -8,7 +8,10 @@ import { DashboardItems } from 'scenes/dashboard/DashboardItems' import { dashboardsModel } from '~/models/dashboardsModel' import { hot } from 'react-hot-loader/root' import { DateFilter } from 'lib/components/DateFilter' -import { CalendarOutlined } from '@ant-design/icons' +import { CalendarOutlined, ReloadOutlined } from '@ant-design/icons' +import moment from 'moment' +import { Button } from 'antd' +import './Dashboard.scss' interface Props { id: string @@ -25,9 +28,9 @@ function _Dashboard({ id, shareToken }: Props): JSX.Element { } function DashboardView(): JSX.Element { - const { dashboard, itemsLoading, items, isOnSharedMode } = useValues(dashboardLogic) + const { dashboard, itemsLoading, items, isOnSharedMode, lastRefreshed } = useValues(dashboardLogic) const { dashboardsLoading } = useValues(dashboardsModel) - const { updateAndRefreshDashboard } = useActions(dashboardLogic) + const { updateAndRefreshDashboard, refreshAllDashboardItems } = useActions(dashboardLogic) if (dashboardsLoading || itemsLoading) { return @@ -42,12 +45,18 @@ function DashboardView(): JSX.Element { } return ( -
+
{!isOnSharedMode && } {items && items.length ? (
-
+
+
+ Last updated {moment(lastRefreshed).fromNow()} + +
({ items: [() => [selectors.allItems], (allItems) => allItems?.items?.filter((i) => !i.deleted)], itemsLoading: [() => [selectors.allItemsLoading], (allItemsLoading) => allItemsLoading], + lastRefreshed: [ + () => [selectors.items], + (items) => { + if (!items || !items.length) { + return null + } + let lastRefreshed = items[0].last_refresh + + for (const item of items) { + if (item.last_refresh < lastRefreshed) { + lastRefreshed = item.last_refresh + } + } + + return lastRefreshed + }, + ], dashboard: [ () => [dashboardsModel.selectors.dashboards], (dashboards) => { From 59e64a47cb78a27d804a80407f0ef78200f2784c Mon Sep 17 00:00:00 2001 From: Paolo D'Amico Date: Tue, 9 Mar 2021 10:04:08 -0800 Subject: [PATCH 07/21] remove individual dashboard item refresh --- frontend/src/scenes/dashboard/Dashboard.tsx | 2 +- .../src/scenes/dashboard/DashboardItem.tsx | 20 ------------------- .../src/scenes/dashboard/DashboardItems.tsx | 2 -- .../src/scenes/dashboard/dashboardLogic.js | 13 ++---------- 4 files changed, 3 insertions(+), 34 deletions(-) diff --git a/frontend/src/scenes/dashboard/Dashboard.tsx b/frontend/src/scenes/dashboard/Dashboard.tsx index cd1b6b7ab66a1..f30b3e8cc79e9 100644 --- a/frontend/src/scenes/dashboard/Dashboard.tsx +++ b/frontend/src/scenes/dashboard/Dashboard.tsx @@ -52,7 +52,7 @@ function DashboardView(): JSX.Element {
- Last updated {moment(lastRefreshed).fromNow()} + Last updated {moment(lastRefreshed).fromNow() || 'a while ago'} diff --git a/frontend/src/scenes/dashboard/DashboardItem.tsx b/frontend/src/scenes/dashboard/DashboardItem.tsx index 9352b96c120a7..8b41620d9052e 100644 --- a/frontend/src/scenes/dashboard/DashboardItem.tsx +++ b/frontend/src/scenes/dashboard/DashboardItem.tsx @@ -22,14 +22,12 @@ import { BlockOutlined, CopyOutlined, DeliveredProcedureOutlined, - ReloadOutlined, BarChartOutlined, SaveOutlined, } from '@ant-design/icons' import { dashboardColorNames, dashboardColors } from 'lib/colors' import { useLongPress } from 'lib/hooks/useLongPress' import { usePrevious } from 'lib/hooks/usePrevious' -import moment from 'moment' import { logicFromInsight, ViewType } from 'scenes/insights/insightLogic' import { dashboardsModel } from '~/models' import { RetentionContainer } from 'scenes/retention/RetentionContainer' @@ -48,7 +46,6 @@ interface Props { enableWobblyDragging?: () => void index: number layout?: any - onRefresh?: () => void footer?: JSX.Element onClick?: () => void preventLoading?: boolean @@ -167,7 +164,6 @@ export function DashboardItem({ enableWobblyDragging, index, layout, - onRefresh, footer, onClick, preventLoading, @@ -212,7 +208,6 @@ export function DashboardItem({ preventLoading, } - const { loadResults } = useActions(logicFromInsight(item.filters.insight, logicProps)) const { results, resultsLoading } = useValues(logicFromInsight(item.filters.insight, logicProps)) const previousLoading = usePrevious(resultsLoading) @@ -220,8 +215,6 @@ export function DashboardItem({ useEffect(() => { if (previousLoading && !resultsLoading && !initialLoaded) { setInitialLoaded(true) - } else if (previousLoading && !resultsLoading && initialLoaded) { - onRefresh && onRefresh() } }, [resultsLoading]) @@ -294,19 +287,6 @@ export function DashboardItem({ /> ))} - - Refreshed:{' '} - {item.last_refresh ? moment(item.last_refresh).fromNow() : 'just now'} - - } - > - loadResults(true)} - /> - refreshDashboardItem(item.id)} />
))} diff --git a/frontend/src/scenes/dashboard/dashboardLogic.js b/frontend/src/scenes/dashboard/dashboardLogic.js index 4988869567c75..6b434754e74db 100644 --- a/frontend/src/scenes/dashboard/dashboardLogic.js +++ b/frontend/src/scenes/dashboard/dashboardLogic.js @@ -21,7 +21,8 @@ export const dashboardLogic = kea({ actions: () => ({ addNewDashboard: true, renameDashboard: true, - setIsSharedDashboard: (id, isShared) => ({ id, isShared }), + setIsSharedDashboard: (id, isShared) => ({ id, isShared }), // whether the dashboard is shared or not + setIsOnSharedMode: (isOnSharedMode) => ({ isOnSharedMode }), // whether the dashboard is open in shared mode (i.e. with a shareToken) updateLayouts: (layouts) => ({ layouts }), updateContainerWidth: (containerWidth, columns) => ({ containerWidth, columns }), saveLayouts: true, @@ -29,10 +30,8 @@ export const dashboardLogic = kea({ enableDragging: true, enableWobblyDragging: true, disableDragging: true, - refreshDashboardItem: (id) => ({ id }), refreshAllDashboardItems: true, updateAndRefreshDashboard: true, - setIsOnSharedMode: (isOnSharedMode) => ({ isOnSharedMode }), // whether the user is opening the dashboard in shared mode (i.e. with a shareToken) }), loaders: ({ props }) => ({ @@ -345,14 +344,6 @@ export const dashboardLogic = kea({ cache.draggingToastId = null } }, - refreshDashboardItem: async ({ id }, breakpoint) => { - const dashboardItem = await api.get(`api/insight/${id}`) - await breakpoint() - dashboardsModel.actions.updateDashboardItem(dashboardItem) - if (dashboardItem.refreshing) { - setTimeout(() => actions.refreshDashboardItem(id), 1000) - } - }, refreshAllDashboardItems: async (_, breakpoint) => { await breakpoint(200) dashboardItemsModel.actions.refreshAllDashboardItems({}) From 1cedc011d0141e3a6ceba83b5d868c1d66d4d4cc Mon Sep 17 00:00:00 2001 From: Paolo D'Amico Date: Tue, 9 Mar 2021 10:56:06 -0800 Subject: [PATCH 08/21] introducing edit mode --- .../src/scenes/dashboard/DashboardHeader.tsx | 163 +++++++++--------- .../src/scenes/dashboard/DashboardItem.tsx | 10 +- .../src/scenes/dashboard/DashboardItems.tsx | 24 +-- .../src/scenes/dashboard/dashboardLogic.js | 79 ++++----- 4 files changed, 127 insertions(+), 149 deletions(-) diff --git a/frontend/src/scenes/dashboard/DashboardHeader.tsx b/frontend/src/scenes/dashboard/DashboardHeader.tsx index 5d447b2a17b20..f9ec9741532df 100644 --- a/frontend/src/scenes/dashboard/DashboardHeader.tsx +++ b/frontend/src/scenes/dashboard/DashboardHeader.tsx @@ -21,8 +21,8 @@ import { dashboardLogic } from 'scenes/dashboard/dashboardLogic' import { DashboardType } from '~/types' export function DashboardHeader(): JSX.Element { - const { dashboard } = useValues(dashboardLogic) - const { addNewDashboard } = useActions(dashboardLogic) + const { dashboard, isOnEditMode } = useValues(dashboardLogic) + const { addNewDashboard, setIsOnEditMode } = useActions(dashboardLogic) const { dashboards, dashboardsLoading } = useValues(dashboardsModel) const { pinDashboard, unpinDashboard, deleteDashboard } = useActions(dashboardsModel) const [fullScreen, setFullScreen] = useState(false) @@ -33,6 +33,82 @@ export function DashboardHeader(): JSX.Element { triggerResizeAfterADelay() } + const actionsDefault = ( + <> + + {dashboard.created_by && ( + <> + + Created by {dashboard.created_by.first_name || dashboard.created_by.email || '-'} on{' '} + {moment(dashboard.created_at).format( + moment(dashboard.created_at).year() === moment().year() + ? 'MMMM Do' + : 'MMMM Do YYYY' + )} + + + + )} + } onClick={() => setIsOnEditMode(true)}> + Edit mode (E) + + } onClick={togglePresentationMode}> + Presentation mode (F12) + + {dashboard.pinned ? ( + } onClick={() => unpinDashboard(dashboard.id)}> + Unpin dashboard + + ) : ( + } onClick={() => pinDashboard(dashboard.id)}> + Pin dashboard + + )} + + + } + onClick={() => deleteDashboard({ id: dashboard.id, redirect: true })} + danger + > + Delete dashboard + +
+ } + placement="bottomRight" + > + + + ) + + const actionsPresentationMode = ( + + ) + + const actionsEditMode = ( + + ) + return (
{fullScreen ? setFullScreen(false)} /> : null} @@ -61,85 +137,12 @@ export function DashboardHeader(): JSX.Element {
- {!fullScreen ? ( - <> - - {dashboard.created_by && ( - <> - - Created by{' '} - {dashboard.created_by.first_name || - dashboard.created_by.email || - '-'}{' '} - on{' '} - {moment(dashboard.created_at).format( - moment(dashboard.created_at).year() === moment().year() - ? 'MMMM Do' - : 'MMMM Do YYYY' - )} - - - - )} - }>Edit mode (E) - } onClick={togglePresentationMode}> - Presentation mode (F12) - - {dashboard.pinned ? ( - } - onClick={() => unpinDashboard(dashboard.id)} - > - Unpin dashboard - - ) : ( - } - onClick={() => pinDashboard(dashboard.id)} - > - Pin dashboard - - )} - - - } - onClick={() => deleteDashboard({ id: dashboard.id, redirect: true })} - danger - > - Delete dashboard - - - } - placement="bottomRight" - > - - + {isOnEditMode ? ( + <>{actionsEditMode} + ) : !fullScreen ? ( + <>{actionsDefault} ) : ( - + <>{actionsPresentationMode} )}
diff --git a/frontend/src/scenes/dashboard/DashboardItem.tsx b/frontend/src/scenes/dashboard/DashboardItem.tsx index 8b41620d9052e..0ee033c2c618c 100644 --- a/frontend/src/scenes/dashboard/DashboardItem.tsx +++ b/frontend/src/scenes/dashboard/DashboardItem.tsx @@ -43,7 +43,8 @@ interface Props { loadDashboardItems?: () => void isDraggingRef?: RefObject inSharedMode?: boolean - enableWobblyDragging?: () => void + isOnEditMode: boolean + setEditMode?: () => void index: number layout?: any footer?: JSX.Element @@ -161,7 +162,8 @@ export function DashboardItem({ loadDashboardItems, isDraggingRef, inSharedMode, - enableWobblyDragging, + isOnEditMode, + setEditMode, index, layout, footer, @@ -193,7 +195,7 @@ export function DashboardItem({ const { renameDashboardItem } = useActions(dashboardItemsModel) const otherDashboards: DashboardType[] = dashboards.filter((d: DashboardType) => d.id !== dashboardId) - const longPressProps = useLongPress(enableWobblyDragging, { + const longPressProps = useLongPress(setEditMode, { ms: 500, touch: true, click: false, @@ -233,7 +235,7 @@ export function DashboardItem({
)}
-
+
{inSharedMode ? ( item.name diff --git a/frontend/src/scenes/dashboard/DashboardItems.tsx b/frontend/src/scenes/dashboard/DashboardItems.tsx index 7e669dae00f4d..9e4c466d28967 100644 --- a/frontend/src/scenes/dashboard/DashboardItems.tsx +++ b/frontend/src/scenes/dashboard/DashboardItems.tsx @@ -11,17 +11,12 @@ import { dashboardItemsModel } from '~/models/dashboardItemsModel' import { dashboardLogic } from 'scenes/dashboard/dashboardLogic' const ReactGridLayout = WidthProvider(Responsive) -const noop = (): void => {} export function DashboardItems({ inSharedMode }: { inSharedMode: boolean }): JSX.Element { - const { dashboard, items, layouts, layoutForItem, breakpoints, cols, draggingEnabled } = useValues(dashboardLogic) - const { - loadDashboardItems, - updateLayouts, - updateContainerWidth, - updateItemColor, - enableWobblyDragging, - } = useActions(dashboardLogic) + const { dashboard, items, layouts, layoutForItem, breakpoints, cols, isOnEditMode } = useValues(dashboardLogic) + const { loadDashboardItems, updateLayouts, updateContainerWidth, updateItemColor, setIsOnEditMode } = useActions( + dashboardLogic + ) const { duplicateDashboardItem } = useActions(dashboardItemsModel) // make sure the dashboard takes up the right size @@ -34,11 +29,9 @@ export function DashboardItems({ inSharedMode }: { inSharedMode: boolean }): JSX return ( setIsOnEditMode(true)} index={index} />
diff --git a/frontend/src/scenes/dashboard/dashboardLogic.js b/frontend/src/scenes/dashboard/dashboardLogic.js index 6b434754e74db..10154bd4af3d8 100644 --- a/frontend/src/scenes/dashboard/dashboardLogic.js +++ b/frontend/src/scenes/dashboard/dashboardLogic.js @@ -6,7 +6,7 @@ import { router } from 'kea-router' import { toast } from 'react-toastify' import { Link } from 'lib/components/Link' import React from 'react' -import { isAndroidOrIOS, clearDOMTextSelection, toParams } from 'lib/utils' +import { clearDOMTextSelection, toParams } from 'lib/utils' import { dashboardItemsModel } from '~/models/dashboardItemsModel' import { PATHS_VIZ, ACTIONS_LINE_GRAPH_LINEAR } from 'lib/constants' import { ViewType } from 'scenes/insights/insightLogic' @@ -27,9 +27,7 @@ export const dashboardLogic = kea({ updateContainerWidth: (containerWidth, columns) => ({ containerWidth, columns }), saveLayouts: true, updateItemColor: (id, color) => ({ id, color }), - enableDragging: true, - enableWobblyDragging: true, - disableDragging: true, + setIsOnEditMode: (isOnEditMode) => ({ isOnEditMode }), refreshAllDashboardItems: true, updateAndRefreshDashboard: true, }), @@ -106,12 +104,10 @@ export const dashboardLogic = kea({ updateContainerWidth: (_, { containerWidth }) => containerWidth, }, ], - draggingEnabled: [ - () => (isAndroidOrIOS() ? 'off' : 'on'), + isOnEditMode: [ + false, { - enableDragging: () => 'on', - enableWobblyDragging: () => 'wobbly', - disableDragging: () => 'off', + setIsOnEditMode: (_, { isOnEditMode }) => isOnEditMode, }, ], isOnSharedMode: [ @@ -269,15 +265,12 @@ export const dashboardLogic = kea({ success: (name) => dashboardsModel.actions.addDashboard({ name }), }) }, - [dashboardsModel.actions.addDashboardSuccess]: ({ dashboard }) => { router.actions.push(`/dashboard/${dashboard.id}`) }, - setIsSharedDashboard: ({ id, isShared }) => { dashboardsModel.actions.setIsSharedDashboard({ id, isShared }) }, - renameDashboard: async () => { prompt({ key: `rename-dashboard-${key}` }).actions.prompt({ title: 'Rename dashboard', @@ -287,11 +280,9 @@ export const dashboardLogic = kea({ success: (name) => dashboardsModel.actions.renameDashboard({ id: values.dashboard.id, name }), }) }, - updateLayouts: () => { actions.saveLayouts() }, - saveLayouts: async (_, breakpoint) => { await breakpoint(300) await api.update(`api/dashboard_item/layouts`, { @@ -305,45 +296,9 @@ export const dashboardLogic = kea({ }), }) }, - updateItemColor: ({ id, color }) => { api.update(`api/insight/${id}`, { color }) }, - - enableWobblyDragging: () => { - clearDOMTextSelection() - window.setTimeout(clearDOMTextSelection, 200) - window.setTimeout(clearDOMTextSelection, 1000) - - if (!cache.draggingToastId) { - cache.draggingToastId = toast( - <> -

Rearranging panels!

-

- actions.disableDragging()}>Click here to stop. -

- , - { - autoClose: false, - onClick: () => actions.disableDragging(), - closeButton: false, - className: 'drag-items-toast', - } - ) - } - }, - enableDragging: () => { - if (cache.draggingToastId) { - toast.dismiss(cache.draggingToastId) - cache.draggingToastId = null - } - }, - disableDragging: () => { - if (cache.draggingToastId) { - toast.dismiss(cache.draggingToastId) - cache.draggingToastId = null - } - }, refreshAllDashboardItems: async (_, breakpoint) => { await breakpoint(200) dashboardItemsModel.actions.refreshAllDashboardItems({}) @@ -357,5 +312,29 @@ export const dashboardLogic = kea({ actions.updateDashboard(filters) dashboardItemsModel.actions.refreshAllDashboardItems(filters) }, + setIsOnEditMode: ({ isOnEditMode }) => { + if (isOnEditMode) { + clearDOMTextSelection() + window.setTimeout(clearDOMTextSelection, 200) + window.setTimeout(clearDOMTextSelection, 1000) + + if (!cache.draggingToastId) { + cache.draggingToastId = toast( + <> +

Rearranging panels!

+

+ actions.setIsOnEditMode(false)}>Click here to stop. +

+ , + { + autoClose: false, + onClick: () => actions.setIsOnEditMode(false), + closeButton: false, + className: 'drag-items-toast', + } + ) + } + } + }, }), }) From 94c27374e739d25037c5ef9fc2c616be6e1e7fb4 Mon Sep 17 00:00:00 2001 From: Paolo D'Amico Date: Tue, 9 Mar 2021 11:54:10 -0800 Subject: [PATCH 09/21] fix toasts UI & build mode toast --- frontend/src/global.scss | 28 ++++++++++++++----- .../src/scenes/dashboard/DashboardHeader.tsx | 6 ++++ .../src/scenes/dashboard/DashboardItems.scss | 22 --------------- .../src/scenes/dashboard/dashboardLogic.js | 19 +++++++++---- 4 files changed, 40 insertions(+), 35 deletions(-) diff --git a/frontend/src/global.scss b/frontend/src/global.scss index 4ceecc6b7afc0..a98456acd58e2 100644 --- a/frontend/src/global.scss +++ b/frontend/src/global.scss @@ -240,14 +240,13 @@ code.code { color: $text_default; font-family: inherit; background-color: $bg_light; -} + border: 1px solid $border; + @extend .text-default; + color: $text_default; -.Toastify__toast-body { - @extend .l3; - color: $success; - p { - @extend .text-default; - color: $text_default; + h1 { + font-size: 1.14rem; + font-weight: bold; } } @@ -255,6 +254,21 @@ code.code { background: $success; } +.Toastify__toast--success { + h1 { + color: $success; + } +} + +.Toastify__toast--info { + h1 { + color: $primary_alt; + } + &.accent-border { + border-color: $primary_alt; + } +} + .Toastify__toast--error { h1 { color: $danger; diff --git a/frontend/src/scenes/dashboard/DashboardHeader.tsx b/frontend/src/scenes/dashboard/DashboardHeader.tsx index f9ec9741532df..65b30f23843ea 100644 --- a/frontend/src/scenes/dashboard/DashboardHeader.tsx +++ b/frontend/src/scenes/dashboard/DashboardHeader.tsx @@ -82,6 +82,12 @@ export function DashboardHeader(): JSX.Element { > +
, { + type: 'info', autoClose: false, onClick: () => actions.setIsOnEditMode(false), closeButton: false, - className: 'drag-items-toast', + className: 'drag-items-toast accent-border', } ) } + } else { + if (cache.draggingToastId) { + toast.dismiss(cache.draggingToastId) + cache.draggingToastId = null + } } }, }), From 6a7d0b48d23473f904ee0e9792d01fea4517adef Mon Sep 17 00:00:00 2001 From: Paolo D'Amico Date: Tue, 9 Mar 2021 12:04:40 -0800 Subject: [PATCH 10/21] fix UI of dashboard select --- frontend/src/scenes/dashboard/Dashboard.scss | 24 ++++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/frontend/src/scenes/dashboard/Dashboard.scss b/frontend/src/scenes/dashboard/Dashboard.scss index 737064dc18cdb..43e345c703d35 100644 --- a/frontend/src/scenes/dashboard/Dashboard.scss +++ b/frontend/src/scenes/dashboard/Dashboard.scss @@ -1,5 +1,7 @@ @import '~/vars'; +$dashboard-title-size: 32px; + .dashboard { margin-top: $default_spacing * 2; @@ -24,7 +26,6 @@ margin-top: -1rem; margin-bottom: 2rem; - --dashboard-title-size: 32px; width: 100%; &.full-screen { @@ -36,23 +37,32 @@ overflow: hidden; .ant-select-single { max-width: 100%; - line-height: var(--dashboard-title-size); + line-height: $dashboard-title-size; .ant-select-selector { padding-left: 0; - padding-right: 15px; - line-height: var(--dashboard-title-size); - height: var(--dashboard-title-size); + padding-right: 8px; + line-height: $dashboard-title-size; + height: $dashboard-title-size; .ant-select-selection-item { - font-size: var(--dashboard-title-size); - line-height: var(--dashboard-title-size); + font-size: $dashboard-title-size; + line-height: $dashboard-title-size; width: 100%; height: 35px; overflow: hidden; text-overflow: ellipsis; } } + &.ant-select-open { + .ant-select-arrow { + color: rgba($text_default, 0.4); + } + } + .ant-select-arrow { + color: $text_default; + font-size: 0.5 * $dashboard-title-size; + } } } .dashboard-meta { From dab770471db5ac8def4f2ba225dc3edda116ac4f Mon Sep 17 00:00:00 2001 From: Paolo D'Amico Date: Tue, 9 Mar 2021 12:14:30 -0800 Subject: [PATCH 11/21] shared dashboard indicator --- frontend/src/scenes/dashboard/Dashboard.scss | 9 +++++++++ frontend/src/scenes/dashboard/DashboardHeader.tsx | 14 +++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/frontend/src/scenes/dashboard/Dashboard.scss b/frontend/src/scenes/dashboard/Dashboard.scss index 43e345c703d35..b256a8f4995a2 100644 --- a/frontend/src/scenes/dashboard/Dashboard.scss +++ b/frontend/src/scenes/dashboard/Dashboard.scss @@ -52,6 +52,15 @@ $dashboard-title-size: 32px; height: 35px; overflow: hidden; text-overflow: ellipsis; + display: flex; + align-items: center; + + .anticon-share-alt { + font-size: $dashboard-title-size * 0.65; + color: $success; + margin-left: 6px !important; + margin-right: 4px; + } } } &.ant-select-open { diff --git a/frontend/src/scenes/dashboard/DashboardHeader.tsx b/frontend/src/scenes/dashboard/DashboardHeader.tsx index 65b30f23843ea..ce157e29cc3bc 100644 --- a/frontend/src/scenes/dashboard/DashboardHeader.tsx +++ b/frontend/src/scenes/dashboard/DashboardHeader.tsx @@ -1,5 +1,5 @@ import { Loading, triggerResizeAfterADelay } from 'lib/utils' -import { Button, Dropdown, Menu, Select } from 'antd' +import { Button, Dropdown, Menu, Select, Tooltip } from 'antd' import { router } from 'kea-router' import React, { useState } from 'react' import { useActions, useValues } from 'kea' @@ -53,10 +53,10 @@ export function DashboardHeader(): JSX.Element { )} } onClick={() => setIsOnEditMode(true)}> - Edit mode (E) + Edit mode } onClick={togglePresentationMode}> - Presentation mode (F12) + Presentation mode {dashboard.pinned ? ( } onClick={() => unpinDashboard(dashboard.id)}> @@ -132,10 +132,14 @@ export function DashboardHeader(): JSX.Element { bordered={false} dropdownMatchSelectWidth={false} > - {!dashboard ? Not Found : null} {dashboards.map((dash: DashboardType) => ( - {dash.name || Untitled} + {dash.name || Untitled} + {dash.is_shared && ( + + + + )} ))} + New Dashboard From 2d9fae3dc2cca1c5451163c0457eb541ca7cc06e Mon Sep 17 00:00:00 2001 From: Paolo D'Amico Date: Tue, 9 Mar 2021 12:28:00 -0800 Subject: [PATCH 12/21] edit dashboard name inline --- frontend/src/models/dashboardsModel.js | 6 +- .../src/scenes/dashboard/DashboardHeader.tsx | 68 ++++++++++++------- .../src/scenes/dashboard/dashboardLogic.js | 12 +--- 3 files changed, 52 insertions(+), 34 deletions(-) diff --git a/frontend/src/models/dashboardsModel.js b/frontend/src/models/dashboardsModel.js index 207728d3ca584..58ed6572f6090 100644 --- a/frontend/src/models/dashboardsModel.js +++ b/frontend/src/models/dashboardsModel.js @@ -41,7 +41,11 @@ export const dashboardsModel = kea({ } return result }, - renameDashboard: async ({ id, name }) => await api.update(`api/dashboard/${id}`, { name }), + renameDashboard: async ({ id, name }, breakpoint) => { + await breakpoint(700) + const response = await api.update(`api/dashboard/${id}`, { name }) + return response + }, setIsSharedDashboard: async ({ id, isShared }) => await api.update(`api/dashboard/${id}`, { is_shared: isShared }), deleteDashboard: async ({ id }) => await api.update(`api/dashboard/${id}`, { deleted: true }), diff --git a/frontend/src/scenes/dashboard/DashboardHeader.tsx b/frontend/src/scenes/dashboard/DashboardHeader.tsx index ce157e29cc3bc..4ea7712b7e64a 100644 --- a/frontend/src/scenes/dashboard/DashboardHeader.tsx +++ b/frontend/src/scenes/dashboard/DashboardHeader.tsx @@ -1,5 +1,5 @@ import { Loading, triggerResizeAfterADelay } from 'lib/utils' -import { Button, Dropdown, Menu, Select, Tooltip } from 'antd' +import { Button, Dropdown, Input, Menu, Select, Tooltip } from 'antd' import { router } from 'kea-router' import React, { useState } from 'react' import { useActions, useValues } from 'kea' @@ -22,11 +22,12 @@ import { DashboardType } from '~/types' export function DashboardHeader(): JSX.Element { const { dashboard, isOnEditMode } = useValues(dashboardLogic) - const { addNewDashboard, setIsOnEditMode } = useActions(dashboardLogic) + const { addNewDashboard, setIsOnEditMode, renameDashboard } = useActions(dashboardLogic) const { dashboards, dashboardsLoading } = useValues(dashboardsModel) const { pinDashboard, unpinDashboard, deleteDashboard } = useActions(dashboardsModel) const [fullScreen, setFullScreen] = useState(false) const [showShareModal, setShowShareModal] = useState(false) + const [newDashboardName, setNewDashboardName] = useState(dashboard.name) const togglePresentationMode = (): void => { setFullScreen(!fullScreen) @@ -123,28 +124,47 @@ export function DashboardHeader(): JSX.Element { ) : ( <> -
- -
+ {isOnEditMode ? ( + { + setNewDashboardName(e.target.value) // To update the input immediately + renameDashboard(e.target.value) // This is breakpointed (i.e. debounced) to avoid multiple API calls + }} + onKeyDown={(e) => { + if (e.key === 'Enter') { + setIsOnEditMode(false) + } + }} + /> + ) : ( +
+ +
+ )}
{isOnEditMode ? ( diff --git a/frontend/src/scenes/dashboard/dashboardLogic.js b/frontend/src/scenes/dashboard/dashboardLogic.js index c2b60da7ffe4e..0da6818152303 100644 --- a/frontend/src/scenes/dashboard/dashboardLogic.js +++ b/frontend/src/scenes/dashboard/dashboardLogic.js @@ -20,7 +20,7 @@ export const dashboardLogic = kea({ actions: () => ({ addNewDashboard: true, - renameDashboard: true, + renameDashboard: (name) => ({ name }), setIsSharedDashboard: (id, isShared) => ({ id, isShared }), // whether the dashboard is shared or not setIsOnSharedMode: (isOnSharedMode) => ({ isOnSharedMode }), // whether the dashboard is open in shared mode (i.e. with a shareToken) updateLayouts: (layouts) => ({ layouts }), @@ -271,14 +271,8 @@ export const dashboardLogic = kea({ setIsSharedDashboard: ({ id, isShared }) => { dashboardsModel.actions.setIsSharedDashboard({ id, isShared }) }, - renameDashboard: async () => { - prompt({ key: `rename-dashboard-${key}` }).actions.prompt({ - title: 'Rename dashboard', - placeholder: 'Please enter the new name', - value: values.dashboard.name, - error: 'You must enter name', - success: (name) => dashboardsModel.actions.renameDashboard({ id: values.dashboard.id, name }), - }) + renameDashboard: ({ name }) => { + dashboardsModel.actions.renameDashboard({ id: values.dashboard.id, name }) }, updateLayouts: () => { actions.saveLayouts() From 5d614802d8afe520b3d864c1c965da7674e0aebb Mon Sep 17 00:00:00 2001 From: Paolo D'Amico Date: Tue, 9 Mar 2021 15:44:04 -0800 Subject: [PATCH 13/21] fix ts --- .../scenes/insights/InsightHistoryPanel/InsightHistoryPanel.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/scenes/insights/InsightHistoryPanel/InsightHistoryPanel.tsx b/frontend/src/scenes/insights/InsightHistoryPanel/InsightHistoryPanel.tsx index 0c6963f071007..e2b366e0af829 100644 --- a/frontend/src/scenes/insights/InsightHistoryPanel/InsightHistoryPanel.tsx +++ b/frontend/src/scenes/insights/InsightHistoryPanel/InsightHistoryPanel.tsx @@ -77,6 +77,7 @@ function InsightPane({ preventLoading={true} footer={
{footer(insight)}
} index={index} + isOnEditMode={false} /> ))} From 82f1b49cdf7f7a176fb84faf6ad0ea8b087b9456 Mon Sep 17 00:00:00 2001 From: Paolo D'Amico Date: Tue, 9 Mar 2021 16:08:00 -0800 Subject: [PATCH 14/21] report edit mode toggled --- frontend/src/lib/utils/eventUsageLogic.ts | 46 ++++++++++++++++++- .../src/scenes/dashboard/DashboardHeader.tsx | 12 +++-- .../src/scenes/dashboard/DashboardItems.tsx | 2 +- .../src/scenes/dashboard/dashboardLogic.js | 7 +-- 4 files changed, 58 insertions(+), 9 deletions(-) diff --git a/frontend/src/lib/utils/eventUsageLogic.ts b/frontend/src/lib/utils/eventUsageLogic.ts index 5276005cc503a..7e94f86ea64b6 100644 --- a/frontend/src/lib/utils/eventUsageLogic.ts +++ b/frontend/src/lib/utils/eventUsageLogic.ts @@ -14,7 +14,6 @@ export const eventUsageLogic = kea ({ annotations }), reportPersonDetailViewed: (person: PersonType) => ({ person }), reportInsightViewed: (filters: Partial, isFirstLoad: boolean) => ({ filters, isFirstLoad }), - reportDashboardViewed: (dashboard: DashboardType, hasShareToken: boolean) => ({ dashboard, hasShareToken }), reportBookmarkletDragged: true, reportIngestionBookmarkletCollapsible: (activePanels: string[]) => ({ activePanels }), reportProjectCreationSubmitted: (projectCount: number, nameLength: number) => ({ projectCount, nameLength }), @@ -47,6 +46,24 @@ export const eventUsageLogic = kea ({ action, totalProperties, oldPropertyType, newPropertyType }), + reportDashboardViewed: (dashboard: DashboardType, hasShareToken: boolean) => ({ dashboard, hasShareToken }), + reportDashboardEditModeToggled: ( + isOnEditMode: boolean, + source: 'long_press' | 'more_dropdown' | 'dashboard_header' | 'hotkey' | 'rename_input' | 'toast' | null + ) => ({ isOnEditMode, source }), + reportDashboardRefreshed: (lastRefreshed: string) => ({ lastRefreshed }), + reportDashboardDateRangeChanged: (dateFrom?: string, dateTo?: string) => ({ dateFrom, dateTo }), + reportDashboardPinToggled: (pinned: boolean, source: 'more_dropdown' | 'main_nav' | 'dashboard_list') => ({ + pinned, + source, + }), + reportDashboardPresentationModeToggled: (isPresentationMode: boolean, source: 'more_dropdown' | 'hotkey') => ({ + isPresentationMode, + source, + }), + reportDashboardDropdownNavigation: (destIsShared: boolean) => ({ destIsShared }), + reportDashboardRenamed: (originalLength: number, newLength: number) => ({ originalLength, newLength }), + reportDashboardShareToggled: (isShared: boolean) => ({ isShared }), }, listeners: { reportAnnotationViewed: async ({ annotations }, breakpoint) => { @@ -238,5 +255,32 @@ export const eventUsageLogic = kea { + posthog.capture(`dashboard edit mode toggled`, { is_on_edit_mode: isOnEditMode, source }) + }, + reportDashboardRefreshed: async ({ lastRefreshed }) => { + posthog.capture(`dashboard refreshed`, { last_refreshed: lastRefreshed }) + }, + reportDashboardDateRangeChanged: async ({ dateFrom, dateTo }) => { + posthog.capture(`dashboard date range changed`, { date_from: dateFrom, date_to: dateTo }) + }, + reportDashboardPinToggled: async ({ pinned, source }) => { + posthog.capture(`dashboard date range changed`, { pinned: pinned, source }) + }, + reportDashboardPresentationModeToggled: async ({ isPresentationMode, source }) => { + posthog.capture(`dashboard presentation mode toggled`, { is_presentation_mode: isPresentationMode, source }) + }, + reportDashboardDropdownNavigation: async ({ destIsShared }) => { + /* Triggered when a user navigates using the dropdown in the header. + destIsShared: whether the dashboard being navigated to is publicly shared + */ + posthog.capture(`dashboard dropdown navigated`, { destination_is_shared: destIsShared }) + }, + reportDashboardRenamed: async ({ originalLength, newLength }) => { + posthog.capture(`dashboard renamed`, { original_length: originalLength, new_length: newLength }) + }, + reportDashboardShareToggled: async ({ isShared }) => { + posthog.capture(`dashboard share toggled`, { is_shared: isShared }) + }, }, }) diff --git a/frontend/src/scenes/dashboard/DashboardHeader.tsx b/frontend/src/scenes/dashboard/DashboardHeader.tsx index 4ea7712b7e64a..209cd87fcbdc6 100644 --- a/frontend/src/scenes/dashboard/DashboardHeader.tsx +++ b/frontend/src/scenes/dashboard/DashboardHeader.tsx @@ -53,7 +53,7 @@ export function DashboardHeader(): JSX.Element { )} - } onClick={() => setIsOnEditMode(true)}> + } onClick={() => setIsOnEditMode(true, 'more_dropdown')}> Edit mode } onClick={togglePresentationMode}> @@ -87,7 +87,7 @@ export function DashboardHeader(): JSX.Element { type="link" data-attr="dashboard-edit-mode" icon={} - onClick={() => setIsOnEditMode(true)} + onClick={() => setIsOnEditMode(true, 'dashboard_header')} /> ) @@ -137,7 +141,7 @@ export function DashboardHeader(): JSX.Element { }} onKeyDown={(e) => { if (e.key === 'Enter') { - setIsOnEditMode(false) + setIsOnEditMode(false, 'rename_input') } }} /> diff --git a/frontend/src/scenes/dashboard/DashboardItems.tsx b/frontend/src/scenes/dashboard/DashboardItems.tsx index 9e4c466d28967..0efecc600b958 100644 --- a/frontend/src/scenes/dashboard/DashboardItems.tsx +++ b/frontend/src/scenes/dashboard/DashboardItems.tsx @@ -97,7 +97,7 @@ export function DashboardItems({ inSharedMode }: { inSharedMode: boolean }): JSX isDraggingRef={isDragging} inSharedMode={inSharedMode} isOnEditMode={isOnEditMode} - setEditMode={() => setIsOnEditMode(true)} + setEditMode={() => setIsOnEditMode(true, 'long_press')} index={index} />
diff --git a/frontend/src/scenes/dashboard/dashboardLogic.js b/frontend/src/scenes/dashboard/dashboardLogic.js index 0da6818152303..2d669182e75aa 100644 --- a/frontend/src/scenes/dashboard/dashboardLogic.js +++ b/frontend/src/scenes/dashboard/dashboardLogic.js @@ -27,7 +27,7 @@ export const dashboardLogic = kea({ updateContainerWidth: (containerWidth, columns) => ({ containerWidth, columns }), saveLayouts: true, updateItemColor: (id, color) => ({ id, color }), - setIsOnEditMode: (isOnEditMode) => ({ isOnEditMode }), + setIsOnEditMode: (isOnEditMode, source = null) => ({ isOnEditMode, source }), refreshAllDashboardItems: true, updateAndRefreshDashboard: true, }), @@ -306,7 +306,7 @@ export const dashboardLogic = kea({ actions.updateDashboard(filters) dashboardItemsModel.actions.refreshAllDashboardItems(filters) }, - setIsOnEditMode: ({ isOnEditMode }) => { + setIsOnEditMode: ({ isOnEditMode, source }) => { if (isOnEditMode) { clearDOMTextSelection() window.setTimeout(clearDOMTextSelection, 200) @@ -324,7 +324,7 @@ export const dashboardLogic = kea({ { type: 'info', autoClose: false, - onClick: () => actions.setIsOnEditMode(false), + onClick: () => actions.setIsOnEditMode(false, 'toast'), closeButton: false, className: 'drag-items-toast accent-border', } @@ -336,6 +336,7 @@ export const dashboardLogic = kea({ cache.draggingToastId = null } } + eventUsageLogic.actions.reportDashboardEditModeToggled(isOnEditMode, source) }, }), }) From 9411502f9f96115cd996cc4b0c5d8792d3db4ec4 Mon Sep 17 00:00:00 2001 From: Paolo D'Amico Date: Tue, 9 Mar 2021 16:39:05 -0800 Subject: [PATCH 15/21] report more events --- frontend/src/lib/utils/eventUsageLogic.ts | 9 ++++-- frontend/src/models/dashboardsModel.js | 15 ++++++++-- .../src/scenes/dashboard/DashboardHeader.tsx | 30 ++++++++++++++----- frontend/src/scenes/dashboard/Dashboards.tsx | 4 ++- .../src/scenes/dashboard/dashboardLogic.js | 5 ++++ 5 files changed, 50 insertions(+), 13 deletions(-) diff --git a/frontend/src/lib/utils/eventUsageLogic.ts b/frontend/src/lib/utils/eventUsageLogic.ts index 7e94f86ea64b6..dbba958767f13 100644 --- a/frontend/src/lib/utils/eventUsageLogic.ts +++ b/frontend/src/lib/utils/eventUsageLogic.ts @@ -53,11 +53,14 @@ export const eventUsageLogic = kea ({ isOnEditMode, source }), reportDashboardRefreshed: (lastRefreshed: string) => ({ lastRefreshed }), reportDashboardDateRangeChanged: (dateFrom?: string, dateTo?: string) => ({ dateFrom, dateTo }), - reportDashboardPinToggled: (pinned: boolean, source: 'more_dropdown' | 'main_nav' | 'dashboard_list') => ({ + reportDashboardPinToggled: (pinned: boolean, source: 'more_dropdown' | 'main_nav' | 'dashboards_list') => ({ pinned, source, }), - reportDashboardPresentationModeToggled: (isPresentationMode: boolean, source: 'more_dropdown' | 'hotkey') => ({ + reportDashboardPresentationModeToggled: ( + isPresentationMode: boolean, + source: 'more_dropdown' | 'hotkey' | 'dashboard_header' | 'browser' | null + ) => ({ isPresentationMode, source, }), @@ -265,7 +268,7 @@ export const eventUsageLogic = kea { - posthog.capture(`dashboard date range changed`, { pinned: pinned, source }) + posthog.capture(`dashboard pin toggled`, { pinned: pinned, source }) }, reportDashboardPresentationModeToggled: async ({ isPresentationMode, source }) => { posthog.capture(`dashboard presentation mode toggled`, { is_presentation_mode: isPresentationMode, source }) diff --git a/frontend/src/models/dashboardsModel.js b/frontend/src/models/dashboardsModel.js index 58ed6572f6090..e7e40f6df6ca4 100644 --- a/frontend/src/models/dashboardsModel.js +++ b/frontend/src/models/dashboardsModel.js @@ -2,6 +2,7 @@ import { kea } from 'kea' import { router } from 'kea-router' import api from 'lib/api' import { delay, idToKey } from 'lib/utils' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import React from 'react' import { toast } from 'react-toastify' @@ -12,6 +13,8 @@ export const dashboardsModel = kea({ // this is moved out of dashboardLogic, so that you can click "undo" on a item move when already // on another dashboard - both dashboards can listen to and share this event, even if one is not yet mounted updateDashboardItem: (item) => ({ item }), + pinDashboard: (id, source = null) => ({ id, source }), + unpinDashboard: (id, source = null) => ({ id, source }), }), loaders: () => ({ rawDashboards: [ @@ -50,8 +53,16 @@ export const dashboardsModel = kea({ await api.update(`api/dashboard/${id}`, { is_shared: isShared }), deleteDashboard: async ({ id }) => await api.update(`api/dashboard/${id}`, { deleted: true }), restoreDashboard: async ({ id }) => await api.update(`api/dashboard/${id}`, { deleted: false }), - pinDashboard: async (id) => await api.update(`api/dashboard/${id}`, { pinned: true }), - unpinDashboard: async (id) => await api.update(`api/dashboard/${id}`, { pinned: false }), + pinDashboard: async ({ id, source }) => { + const response = await api.update(`api/dashboard/${id}`, { pinned: true }) + eventUsageLogic.actions.reportDashboardPinToggled(true, source) + return response + }, + unpinDashboard: async ({ id, source }) => { + const response = await api.update(`api/dashboard/${id}`, { pinned: false }) + eventUsageLogic.actions.reportDashboardPinToggled(false, source) + return response + }, }, }), diff --git a/frontend/src/scenes/dashboard/DashboardHeader.tsx b/frontend/src/scenes/dashboard/DashboardHeader.tsx index 209cd87fcbdc6..92bccae84e514 100644 --- a/frontend/src/scenes/dashboard/DashboardHeader.tsx +++ b/frontend/src/scenes/dashboard/DashboardHeader.tsx @@ -19,6 +19,7 @@ import { FullScreen } from 'lib/components/FullScreen' import moment from 'moment' import { dashboardLogic } from 'scenes/dashboard/dashboardLogic' import { DashboardType } from '~/types' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' export function DashboardHeader(): JSX.Element { const { dashboard, isOnEditMode } = useValues(dashboardLogic) @@ -28,10 +29,16 @@ export function DashboardHeader(): JSX.Element { const [fullScreen, setFullScreen] = useState(false) const [showShareModal, setShowShareModal] = useState(false) const [newDashboardName, setNewDashboardName] = useState(dashboard.name) + const { reportDashboardPresentationModeToggled } = useActions(eventUsageLogic) - const togglePresentationMode = (): void => { - setFullScreen(!fullScreen) + const togglePresentationMode = ( + source: 'more_dropdown' | 'hotkey' | 'dashboard_header' | 'browser' | null, + newMode?: boolean + ): void => { + const _newMode = newMode !== undefined ? newMode : !fullScreen + setFullScreen(_newMode) triggerResizeAfterADelay() + reportDashboardPresentationModeToggled(_newMode, source) } const actionsDefault = ( @@ -56,15 +63,24 @@ export function DashboardHeader(): JSX.Element { } onClick={() => setIsOnEditMode(true, 'more_dropdown')}> Edit mode - } onClick={togglePresentationMode}> + } + onClick={() => togglePresentationMode('more_dropdown')} + > Presentation mode {dashboard.pinned ? ( - } onClick={() => unpinDashboard(dashboard.id)}> + } + onClick={() => unpinDashboard(dashboard.id, 'more_dropdown')} + > Unpin dashboard ) : ( - } onClick={() => pinDashboard(dashboard.id)}> + } + onClick={() => pinDashboard(dashboard.id, 'more_dropdown')} + > Pin dashboard )} @@ -102,7 +118,7 @@ export function DashboardHeader(): JSX.Element { const actionsPresentationMode = (