diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPercentage/CustomMetricPercentage.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPercentage/CustomMetricPercentage.tsx index 7e3681f45..ffce73783 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPercentage/CustomMetricPercentage.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPercentage/CustomMetricPercentage.tsx @@ -12,7 +12,7 @@ function CustomMetriPercentage(props: Props) { return (
{numberWithCommas(data.count)}
-
{`${parseInt(data.previousCount)} ( ${parseInt(data.countProgress).toFixed(1)}% )`}
+
{`${parseInt(data.previousCount || 0)} ( ${parseInt(data.countProgress || 0).toFixed(1)}% )`}
from previous period.
) diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestDomains/SlowestDomains.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestDomains/SlowestDomains.tsx index 9e2563752..2d74e2b39 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestDomains/SlowestDomains.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestDomains/SlowestDomains.tsx @@ -15,6 +15,7 @@ function SlowestDomains(props: Props) {
{metric.data.chart.map((item, i) => diff --git a/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx index 371815135..a999e0358 100644 --- a/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx +++ b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx @@ -17,8 +17,8 @@ function WidgetCategoryItem({ category, isSelected, onClick, selectedWidgetIds,
{category.description}
{selectedCategoryWidgetsCount > 0 && (
- unSelectCategory(category)} /> - {`Selected ${selectedCategoryWidgetsCount} of ${category.widgets.length}`} + {/* unSelectCategory(category)} /> */} + {`Selected ${selectedCategoryWidgetsCount} of ${category.widgets.length}`}
)}
@@ -29,6 +29,7 @@ function DashboardMetricSelection(props) { const { dashboardStore } = useStore(); let widgetCategories: any[] = useObserver(() => dashboardStore.widgetCategories); const [activeCategory, setActiveCategory] = React.useState(); + const [selectAllCheck, setSelectAllCheck] = React.useState(false); const selectedWidgetIds = useObserver(() => dashboardStore.selectedWidgets.map((widget: any) => widget.metricId)); useEffect(() => { @@ -39,10 +40,17 @@ function DashboardMetricSelection(props) { const handleWidgetCategoryClick = (category: any) => { setActiveCategory(category); + setSelectAllCheck(false); }; const toggleAllWidgets = ({ target: { checked }}) => { - dashboardStore.toggleAllSelectedWidgets(checked); + // dashboardStore.toggleAllSelectedWidgets(checked); + setSelectAllCheck(checked); + if (checked) { + dashboardStore.selectWidgetsByCategory(activeCategory.name); + } else { + dashboardStore.removeSelectedWidgetByCategory(activeCategory); + } } return useObserver(() => ( @@ -62,10 +70,10 @@ function DashboardMetricSelection(props) {
Showing past 7 days data for visual clue -
- - Select All -
+
)} diff --git a/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx index 4814ada0d..35277dc74 100644 --- a/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx +++ b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx @@ -16,6 +16,7 @@ interface Props { function DashboardModal(props) { const { history, siteId, dashboardId } = props; const { dashboardStore } = useStore(); + const selectedWidgetsCount = useObserver(() => dashboardStore.selectedWidgets.length); const { hideModal } = useModal(); const dashboard = useObserver(() => dashboardStore.dashboardInstance); const loading = useObserver(() => dashboardStore.isSaving); @@ -33,7 +34,7 @@ function DashboardModal(props) { return useObserver(() => (
@@ -53,15 +54,16 @@ function DashboardModal(props) { )} -
+
+ {selectedWidgetsCount} Widgets
)); diff --git a/frontend/app/components/Dashboard/components/DashboardSelectionModal/DashboardSelectionModal.tsx b/frontend/app/components/Dashboard/components/DashboardSelectionModal/DashboardSelectionModal.tsx index f7a2c46ac..830730d7b 100644 --- a/frontend/app/components/Dashboard/components/DashboardSelectionModal/DashboardSelectionModal.tsx +++ b/frontend/app/components/Dashboard/components/DashboardSelectionModal/DashboardSelectionModal.tsx @@ -29,7 +29,7 @@ function DashboardSelectionModal(props: Props) { return useObserver(() => ( -
{ 'Add to selected Dashboard' }
+
{ 'Add to selected dashboard' }
dashboardStore.selectedDashboard?.dashboardId); const dashboardsPicked = useObserver(() => dashboardStore.dashboards.slice(0, SHOW_COUNT)); const remainingDashboardsCount = dashboardStore.dashboards.length - SHOW_COUNT; + const isMetric = history.location.pathname.includes('metrics'); const redirect = (path) => { history.push(path); @@ -82,6 +83,7 @@ function DashboardSideMenu(props: Props) {
Create Dashboard } > -
+
setShowEditModal(false)} @@ -98,15 +98,16 @@ function DashboardView(props: Props) {
- More + {/* Options */} setExpanded(!expanded)} className="flex items-center cursor-pointer select-none" > - {expanded ? 'Collapse' : 'Expand'} + {expanded ? 'Close' : 'Edit'}
diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/TemplateOverlay.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/TemplateOverlay.tsx new file mode 100644 index 000000000..3b76f36de --- /dev/null +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/TemplateOverlay.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { Tooltip } from 'react-tippy'; + +function TemplateOverlay() { + return ( +
+ +
+ +
+ ); +} + +export default TemplateOverlay; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx index 6bb9ca875..2aadc8733 100644 --- a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx @@ -9,6 +9,7 @@ import { useStore } from 'App/mstore'; import LazyLoad from 'react-lazyload'; import { withRouter } from 'react-router-dom'; import { withSiteId, dashboardMetricDetails } from 'App/routes'; +import TemplateOverlay from './TemplateOverlay'; interface Props { className?: string; @@ -27,7 +28,7 @@ interface Props { function WidgetWrapper(props: Props) { const { dashboardStore } = useStore(); const { isWidget = false, active = false, index = 0, moveListItem = null, isPreview = false, isTemplate = false, dashboardId, siteId } = props; - const widget = useObserver(() => props.widget); + const widget: any = useObserver(() => props.widget); const [{ opacity, isDragging }, dragRef] = useDrag({ type: 'item', @@ -40,7 +41,6 @@ function WidgetWrapper(props: Props) { const [{ isOver, canDrop }, dropRef] = useDrop({ accept: 'item', - drop: (item: any) => { if (item.index === index) return; moveListItem(item.index, index); @@ -52,13 +52,14 @@ function WidgetWrapper(props: Props) { }) const onDelete = async () => { - if (await confirm({ - header: 'Confirm', - confirmButton: 'Yes, delete', - confirmation: `Are you sure you want to permanently delete the widget from this dashboard?` - })) { - dashboardStore.deleteDashboardWidget(dashboardId!, widget.widgetId); - } + dashboardStore.deleteDashboardWidget(dashboardId!, widget.widgetId); + // if (await confirm({ + // header: 'Confirm', + // confirmButton: 'Yes, delete', + // confirmation: `Are you sure you want to permanently delete the widget from this dashboard?` + // })) { + // dashboardStore.deleteDashboardWidget(dashboardId!, widget.widgetId); + // } } const onChartClick = () => { @@ -72,9 +73,8 @@ function WidgetWrapper(props: Props) { return useObserver(() => (
{}} > + {isTemplate && }

{widget.name}

{isWidget && ( @@ -92,9 +93,11 @@ function WidgetWrapper(props: Props) { items={[ { text: 'Edit', onClick: onChartClick, + disabled: widget.metricType === 'predefined', + disabledMessage: 'Cannot edit system generated metrics' }, { - text: 'Hide from view', + text: 'Remove from view', onClick: onDelete }, ]} diff --git a/frontend/app/components/Modal/ModalOverlay.tsx b/frontend/app/components/Modal/ModalOverlay.tsx index 85b314eec..4dad0ec61 100644 --- a/frontend/app/components/Modal/ModalOverlay.tsx +++ b/frontend/app/components/Modal/ModalOverlay.tsx @@ -6,7 +6,7 @@ function ModalOverlay({ children }) { let modal = useModal(); return ( -
+
modal.hideModal()} className={stl.overlay} diff --git a/frontend/app/components/ui/ItemMenu/ItemMenu.js b/frontend/app/components/ui/ItemMenu/ItemMenu.js index 2663a7b2a..841d9e4c9 100644 --- a/frontend/app/components/ui/ItemMenu/ItemMenu.js +++ b/frontend/app/components/ui/ItemMenu/ItemMenu.js @@ -2,7 +2,7 @@ import { Icon } from 'UI'; import styles from './itemMenu.css'; import OutsideClickDetectingDiv from 'Shared/OutsideClickDetectingDiv'; import cn from 'classnames'; - +import { Tooltip } from 'react-tippy'; export default class ItemMenu extends React.PureComponent { state = { displayed: false, @@ -20,7 +20,7 @@ export default class ItemMenu extends React.PureComponent { closeMenu = () => this.setState({ displayed: false }) render() { - const { items } = this.props; + const { items, label = "" } = this.props; const { displayed } = this.state; return ( @@ -28,33 +28,46 @@ export default class ItemMenu extends React.PureComponent { -
{ this.menuBtnRef = ref; } } - className={cn("w-10 h-10 cursor-pointer rounded-full flex items-center justify-center hover:bg-gray-light", { 'bg-gray-light' : displayed })} - onClick={ this.toggleMenu } - role="button" - > - +
+ {label && {label}} +
{ this.menuBtnRef = ref; } } + className={cn("w-10 h-10 rounded-full flex items-center justify-center hover:bg-gray-light", { 'bg-gray-light' : displayed })} + role="button" + > + +
- { items.filter(({ hidden }) => !hidden).map(({ onClick, text, icon }) => ( + { items.filter(({ hidden }) => !hidden).map(({ onClick, text, icon, disabled = false, disabledMessage = '' }) => (
{} } role="menuitem" tabIndex="-1" > - { icon && ( -
- + +
+ { icon && ( +
+ +
+ )} +
{ text }
- )} -
{ text }
+
))}
diff --git a/frontend/app/mstore/dashboardStore.ts b/frontend/app/mstore/dashboardStore.ts index dc8b4b999..fb33ac89e 100644 --- a/frontend/app/mstore/dashboardStore.ts +++ b/frontend/app/mstore/dashboardStore.ts @@ -31,6 +31,7 @@ export interface IDashboardSotre { fetchingDashboard: boolean sessionsLoading: boolean + selectWidgetsByCategory: (category: string) => void toggleAllSelectedWidgets: (isSelected: boolean) => void removeSelectedWidgetByCategory(category: string): void toggleWidgetSelection(widget: IWidget): void @@ -111,6 +112,7 @@ export default class DashboardStore implements IDashboardSotre { editWidget: action, updateKey: action, + selectWidgetsByCategory: action, toggleAllSelectedWidgets: action, removeSelectedWidgetByCategory: action, toggleWidgetSelection: action, @@ -138,6 +140,12 @@ export default class DashboardStore implements IDashboardSotre { } } + selectWidgetsByCategory(category: string) { + const selectedWidgetIds = this.selectedWidgets.map((widget: any) => widget.metricId); + const widgets = this.widgetCategories.find(cat => cat.name === category)?.widgets.filter(widget => !selectedWidgetIds.includes(widget.metricId)) + this.selectedWidgets = this.selectedWidgets.concat(widgets) || [] + } + removeSelectedWidgetByCategory = (category: any) => { const categoryWidgetIds = category.widgets.map(w => w.metricId) this.selectedWidgets = this.selectedWidgets.filter((widget: any) => !categoryWidgetIds.includes(widget.metricId));