diff --git a/frontend/app/components/Dashboard/components/AddCardSelectionModal.tsx b/frontend/app/components/Dashboard/components/AddCardSelectionModal.tsx new file mode 100644 index 000000000..e9bb29a64 --- /dev/null +++ b/frontend/app/components/Dashboard/components/AddCardSelectionModal.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import {Card, Col, Modal, Row, Typography} from "antd"; +import {Grid2X2, Plus} from "lucide-react"; +import NewDashboardModal from "Components/Dashboard/components/DashboardList/NewDashModal"; + +interface Props { + open: boolean; + onClose?: () => void; +} + +function AddCardSelectionModal(props: Props) { + const [open, setOpen] = React.useState(false); + const [isLibrary, setIsLibrary] = React.useState(false); + + const onCloseModal = () => { + setOpen(false); + // props.onClose && props.onClose(); + } + + const onClick = (isLibrary: boolean) => { + setIsLibrary(isLibrary); + setOpen(true); + } + return ( + + + + onClick(true)}> +
+ + Add from library +

Select from 12 available

+
+
+ + + onClick(false)}> +
+ +

Create New Card

+
+
+ +
+ + {open && } +
+ ); +} + +export default AddCardSelectionModal; diff --git a/frontend/app/components/Dashboard/components/DashboardHeader/DashboardHeader.tsx b/frontend/app/components/Dashboard/components/DashboardHeader/DashboardHeader.tsx index 072e2bf44..29950a0cd 100644 --- a/frontend/app/components/Dashboard/components/DashboardHeader/DashboardHeader.tsx +++ b/frontend/app/components/Dashboard/components/DashboardHeader/DashboardHeader.tsx @@ -1,134 +1,145 @@ import React from 'react'; import Breadcrumb from 'Shared/Breadcrumb'; -import { withSiteId } from 'App/routes'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; -import { Button, PageTitle, confirm, Tooltip } from 'UI'; +import {withSiteId} from 'App/routes'; +import {withRouter, RouteComponentProps} from 'react-router-dom'; +import {Button, PageTitle, confirm, Tooltip} from 'UI'; import SelectDateRange from 'Shared/SelectDateRange'; -import { useStore } from 'App/mstore'; -import { useModal } from 'App/components/Modal'; +import {useStore} from 'App/mstore'; +import {useModal} from 'App/components/Modal'; import DashboardOptions from '../DashboardOptions'; import withModal from 'App/components/Modal/withModal'; -import { observer } from 'mobx-react-lite'; +import {observer} from 'mobx-react-lite'; import DashboardEditModal from '../DashboardEditModal'; import AddCardModal from '../AddCardModal'; +import AddCardSelectionModal from "Components/Dashboard/components/AddCardSelectionModal"; interface IProps { - dashboardId: string; - siteId: string; - renderReport?: any; + dashboardId: string; + siteId: string; + renderReport?: any; } type Props = IProps & RouteComponentProps; const MAX_CARDS = 29; -function DashboardHeader(props: Props) { - const { siteId, dashboardId } = props; - const { dashboardStore } = useStore(); - const { showModal } = useModal(); - const [focusTitle, setFocusedInput] = React.useState(true); - const [showEditModal, setShowEditModal] = React.useState(false); - const period = dashboardStore.period; - const dashboard: any = dashboardStore.selectedDashboard; - const canAddMore: boolean = dashboard?.widgets?.length <= MAX_CARDS; +function AddCard(props: { disabled: boolean }) { + const [open, setOpen] = React.useState(false); - const onEdit = (isTitle: boolean) => { - dashboardStore.initDashboard(dashboard); - setFocusedInput(isTitle); - setShowEditModal(true); - }; - - const onDelete = async () => { - if ( - await confirm({ - header: 'Confirm', - confirmButton: 'Yes, delete', - confirmation: `Are you sure you want to permanently delete this Dashboard?`, - }) - ) { - dashboardStore.deleteDashboard(dashboard).then(() => { - props.history.push(withSiteId(`/dashboard`, siteId)); - }); - } - }; - return ( -
- setShowEditModal(false)} - focusTitle={focusTitle} - /> - -
-
- - {dashboard?.name} - - } - onDoubleClick={() => onEdit(true)} - className="mr-3 select-none border-b border-b-borderColor-transparent hover:border-dotted hover:border-gray-medium cursor-pointer" - /> -
-
- + // showModal(, {right: true}) + return <> + - -
-
- dashboardStore.setPeriod(period)} - right={true} - /> -
-
-
- -
-
-
-
- {/* @ts-ignore */} - -

onEdit(false)} - > - {dashboard?.description || 'Describe the purpose of this dashboard'} -

-
-
- ); + setOpen(false)}/> + ; +} + +function DashboardHeader(props: Props) { + const {siteId, dashboardId} = props; + const {dashboardStore} = useStore(); + const {showModal} = useModal(); + const [focusTitle, setFocusedInput] = React.useState(true); + const [showEditModal, setShowEditModal] = React.useState(false); + const period = dashboardStore.period; + + const dashboard: any = dashboardStore.selectedDashboard; + const canAddMore: boolean = dashboard?.widgets?.length <= MAX_CARDS; + + const onEdit = (isTitle: boolean) => { + dashboardStore.initDashboard(dashboard); + setFocusedInput(isTitle); + setShowEditModal(true); + }; + + const onDelete = async () => { + if ( + await confirm({ + header: 'Confirm', + confirmButton: 'Yes, delete', + confirmation: `Are you sure you want to permanently delete this Dashboard?`, + }) + ) { + dashboardStore.deleteDashboard(dashboard).then(() => { + props.history.push(withSiteId(`/dashboard`, siteId)); + }); + } + }; + return ( +
+ setShowEditModal(false)} + focusTitle={focusTitle} + /> + +
+
+ + {dashboard?.name} + + } + onDoubleClick={() => onEdit(true)} + className="mr-3 select-none border-b border-b-borderColor-transparent hover:border-dotted hover:border-gray-medium cursor-pointer" + /> +
+
+ +
+
+ dashboardStore.setPeriod(period)} + right={true} + /> +
+
+
+ +
+
+
+
+ {/* @ts-ignore */} + +

onEdit(false)} + > + {dashboard?.description || 'Describe the purpose of this dashboard'} +

+
+
+
+ ); } export default withRouter(withModal(observer(DashboardHeader))); diff --git a/frontend/app/components/Dashboard/components/DashboardList/Header.tsx b/frontend/app/components/Dashboard/components/DashboardList/Header.tsx index afc478e49..5b0062ea4 100644 --- a/frontend/app/components/Dashboard/components/DashboardList/Header.tsx +++ b/frontend/app/components/Dashboard/components/DashboardList/Header.tsx @@ -1,62 +1,47 @@ -import { PlusOutlined } from '@ant-design/icons'; -import { Button } from 'antd'; -import { observer } from 'mobx-react-lite'; +import {PlusOutlined} from '@ant-design/icons'; +import {Button} from 'antd'; +import {observer} from 'mobx-react-lite'; import React from 'react'; -import { useStore } from 'App/mstore'; -import { withSiteId } from 'App/routes'; -import { PageTitle } from 'UI'; +import {PageTitle} from 'UI'; import DashboardSearch from './DashboardSearch'; import NewDashboardModal from './NewDashModal'; -function Header({ history, siteId }: { history: any; siteId: string }) { - const [showModal, setShowModal] = React.useState(false); - const { dashboardStore } = useStore(); +function Header() { + const [showModal, setShowModal] = React.useState(true); - const onSaveDashboard = () => { - dashboardStore.initDashboard(); - dashboardStore - .save(dashboardStore.dashboardInstance) - .then(async (syncedDashboard) => { - dashboardStore.selectDashboardById(syncedDashboard.dashboardId); - history.push( - withSiteId(`/dashboard/${syncedDashboard.dashboardId}`, siteId) - ); - }); - }; + const onAddDashboardClick = () => { + setShowModal(true); + }; - const onAddDashboardClick = () => { - setShowModal(true); - }; + const onClose = () => { + setShowModal(false); + }; - const onClose = () => { - setShowModal(false); - }; - - return ( - <> -
-
- -
-
- -
-
- -
-
-
- - - ); + return ( + <> +
+
+ +
+
+ +
+
+ +
+
+
+ + + ); } export default observer(Header); diff --git a/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/CardsLibrary.tsx b/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/CardsLibrary.tsx new file mode 100644 index 000000000..3465b71e6 --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/CardsLibrary.tsx @@ -0,0 +1,76 @@ +import React, {useEffect, useMemo} from 'react'; +import {useStore} from "App/mstore"; +import WidgetWrapper from "Components/Dashboard/components/WidgetWrapper/WidgetWrapper"; +import {observer} from "mobx-react-lite"; +import {Loader} from "UI"; +import WidgetChart from "Components/Dashboard/components/WidgetChart/WidgetChart"; +import LazyLoad from 'react-lazyload'; +import {Card} from "antd"; +import {CARD_CATEGORIES} from "Components/Dashboard/components/DashboardList/NewDashModal/ExampleCards"; + +const CARD_TYPES_MAP = CARD_CATEGORIES.reduce((acc: any, category: any) => { + acc[category.key] = category.types; + return acc; +}, {}); + +interface Props { + category?: string; + selectedList: any; + onCard: (metricId: number) => void; +} + +function CardsLibrary(props: Props) { + const {selectedList} = props; + const {metricStore, dashboardStore} = useStore(); + + const cards = useMemo(() => { + return metricStore.filteredCards.filter((card: any) => { + return CARD_TYPES_MAP[props.category || 'default'].includes(card.metricType); + }); + }, [metricStore.filteredCards, props.category]); + + useEffect(() => { + metricStore.fetchList(); + }, []); + + const onItemClick = (metricId: number) => { + props.onCard(metricId); + } + + return ( + +
+ {cards.map((metric: any) => ( + +
onItemClick(metric.metricId)}> + + + + + +
+
+ ))} +
+
+ ); +} + +export default observer(CardsLibrary); diff --git a/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/CreateCard.tsx b/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/CreateCard.tsx index 0444d163c..7ca9a826f 100644 --- a/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/CreateCard.tsx +++ b/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/CreateCard.tsx @@ -7,6 +7,7 @@ import {useStore} from "App/mstore"; import {CLICKMAP} from "App/constants/card"; import {renderClickmapThumbnail} from "Components/Dashboard/components/WidgetForm/renderMap"; import WidgetPreview from "Components/Dashboard/components/WidgetPreview/WidgetPreview"; +import {number} from "Player/player/localStorage"; const getTitleByType = (type: string) => { switch (type) { @@ -26,10 +27,10 @@ function CreateCard(props: Props) { const history = useHistory(); const {metricStore, dashboardStore, aiFiltersStore} = useStore(); const metric = metricStore.instance; - const siteId = history.location.pathname.split('/')[1]; + const siteId: string = history.location.pathname.split('/')[1]; + const dashboardId: string = history.location.pathname.split('/')[4]; // const title = getTitleByType(metric.metricType) - const createNewDashboard = async () => { dashboardStore.initDashboard(); return await dashboardStore diff --git a/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/ExampleCards.tsx b/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/ExampleCards.tsx new file mode 100644 index 000000000..618a31ab4 --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/ExampleCards.tsx @@ -0,0 +1,142 @@ +import ExampleFunnel from "./Examples/Funnel"; +import ExamplePath from "./Examples/Path"; +import ExampleTrend from "./Examples/Trend"; +import ExampleCount from "./Examples/Count"; +import PerfBreakdown from "./Examples/PerfBreakdown"; +import SlowestDomain from "./Examples/SlowestDomain"; +import SessionsByErrors from "./Examples/SessionsByErrors"; +import SessionsByIssues from "./Examples/SessionsByIssues"; +import ByBrowser from "./Examples/SessionsBy/ByBrowser"; +import BySystem from "./Examples/SessionsBy/BySystem"; +import ByCountry from "./Examples/SessionsBy/ByCountry"; +import ByUrl from "./Examples/SessionsBy/ByUrl"; +import {ERRORS, FUNNEL, PERFORMANCE, TABLE, TIMESERIES, USER_PATH, WEB_VITALS} from "App/constants/card"; +import {FilterKey} from "Types/filter/filterType"; +import {Activity, BarChart, TableCellsMerge, TrendingUp} from "lucide-react"; + +const TYPE = { + FUNNEL: 'funnel', + PATH_FINDER: 'path-finder', + TREND: 'trend', + SESSIONS_BY: 'sessions-by', + BREAKDOWN: 'breakdown', + SLOWEST_DOMAIN: 'slowest-domain', + SESSIONS_BY_ERRORS: 'sessions-by-errors', + SESSIONS_BY_ISSUES: 'sessions-by-issues', + SESSIONS_BY_BROWSER: 'sessions-by-browser', + SESSIONS_BY_SYSTEM: 'sessions-by-system', + SESSIONS_BY_COUNTRY: 'sessions-by-country', + SESSIONS_BY_URL: 'sessions-by-url', +} + +export const CARD_CATEGORIES = [ + { + key: 'product-analytics', label: 'Product Analytics', icon: TrendingUp, types: [USER_PATH, ERRORS] + }, + {key: 'performance-monitoring', label: 'Performance Monitoring', icon: Activity, types: [TIMESERIES]}, + {key: 'web-analytics', label: 'Web Analytics', icon: BarChart, types: [TABLE]}, + {key: 'core-web-vitals', label: 'Core Web Vitals', icon: TableCellsMerge, types: [WEB_VITALS]} +]; + +export interface CardType { + title: string; + key: string; + cardType: string; + category: string; + example: any; + metricOf?: string; +} + +export const CARD_LIST: CardType[] = [ + { + title: 'Funnel', + key: TYPE.FUNNEL, + cardType: FUNNEL, + category: CARD_CATEGORIES[0].key, + example: ExampleFunnel, + }, + { + title: 'Path Finder', + key: TYPE.PATH_FINDER, + cardType: USER_PATH, + category: CARD_CATEGORIES[0].key, + example: ExamplePath, + }, + { + title: 'Trend', + key: TYPE.TREND, + cardType: TIMESERIES, + category: CARD_CATEGORIES[0].key, + example: ExampleTrend, + }, + { + title: 'Sessions by', + key: TYPE.SESSIONS_BY, + cardType: TABLE, + metricOf: 'userBrowser', + category: CARD_CATEGORIES[0].key, + example: ExampleCount, + }, + + { + title: 'Breakdown', + key: TYPE.BREAKDOWN, + cardType: PERFORMANCE, + category: CARD_CATEGORIES[1].key, + example: PerfBreakdown, + }, + { + title: 'Slowest Domain', + key: TYPE.SLOWEST_DOMAIN, + cardType: TIMESERIES, + category: CARD_CATEGORIES[1].key, + example: SlowestDomain, + }, + { + title: 'Sessions by Errors', + key: TYPE.SESSIONS_BY_ERRORS, + cardType: TIMESERIES, + category: CARD_CATEGORIES[1].key, + example: SessionsByErrors, + }, + { + title: 'Sessions by Issues', + key: TYPE.SESSIONS_BY_ISSUES, + cardType: TIMESERIES, + category: CARD_CATEGORIES[1].key, + example: SessionsByIssues, + }, + + { + title: 'Sessions by Browser', + key: TYPE.SESSIONS_BY_BROWSER, + cardType: TABLE, + metricOf: FilterKey.USER_BROWSER, + category: CARD_CATEGORIES[2].key, + example: ByBrowser, + }, + { + title: 'Sessions by System', + key: TYPE.SESSIONS_BY_SYSTEM, + cardType: TABLE, + metricOf: FilterKey.USER_OS, + category: CARD_CATEGORIES[2].key, + example: BySystem, + }, + { + title: 'Sessions by Country', + key: TYPE.SESSIONS_BY_COUNTRY, + cardType: TABLE, + metricOf: FilterKey.USER_COUNTRY, + category: CARD_CATEGORIES[2].key, + example: ByCountry, + }, + { + title: 'Sessions by URL', + key: TYPE.SESSIONS_BY_URL, + cardType: TABLE, + metricOf: FilterKey.LOCATION, + category: CARD_CATEGORIES[2].key, + example: ByUrl, + }, +] diff --git a/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/NewDashboardModal.tsx b/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/NewDashboardModal.tsx index f414aea5b..9c0eaad8f 100644 --- a/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/NewDashboardModal.tsx +++ b/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/NewDashboardModal.tsx @@ -2,35 +2,29 @@ import React, {useEffect} from 'react'; import {Modal} from 'antd'; import SelectCard from './SelectCard'; import CreateCard from "Components/Dashboard/components/DashboardList/NewDashModal/CreateCard"; -import {useStore} from "App/mstore"; -import {TIMESERIES} from "App/constants/card"; interface NewDashboardModalProps { onClose: () => void; open: boolean; + isAddingFromLibrary?: boolean; + isCreatingNewCard?: boolean; } -const NewDashboardModal: React.FC = ({onClose, open}) => { +const NewDashboardModal: React.FC = ({ + onClose, + open, + isAddingFromLibrary = false, + isCreatingNewCard = false + }) => { const [step, setStep] = React.useState(0); - const [selectedCard, setSelectedCard] = React.useState('trend-single'); - const {metricStore} = useStore(); - const onCard = (card: any) => { + const onCard = () => { setStep(step + 1); - // setSelectedCard(card); - // console.log('Selected card:', card) - console.log('Selected card:', card) - metricStore.merge({ - name: card.title, - }); - metricStore.changeType(card.cardType); }; - const [modalOpen, setModalOpen] = React.useState(false); - useEffect(() => { return () => { - setStep(1); + setStep(0); } }, [open]); @@ -39,7 +33,7 @@ const NewDashboardModal: React.FC = ({onClose, open}) =>
- {step === 0 && } + {step === 0 && } {step === 1 && setStep(0)}/>}
diff --git a/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/SelectCard.tsx b/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/SelectCard.tsx index 0e4af66f3..9c21a58ed 100644 --- a/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/SelectCard.tsx +++ b/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/SelectCard.tsx @@ -1,228 +1,109 @@ import React, {useMemo} from 'react'; -import {Segmented} from 'antd'; +import {Button, Segmented} from 'antd'; +import {CARD_LIST, CARD_CATEGORIES, CardType} from './ExampleCards'; +import {useStore} from 'App/mstore'; import Option from './Option'; -// import ProductAnalytics from './Examples/ProductAnalytics'; -// import PerformanceMonitoring from './Examples/PerformanceMonitoring'; -// import WebAnalytics from './Examples/WebAnalytics'; -// import CoreWebVitals from './Examples/CoreWebVitals'; -import {TrendingUp, Activity, BarChart, TableCellsMerge} from "lucide-react"; -import ExampleFunnel from "Components/Dashboard/components/DashboardList/NewDashModal/Examples/Funnel"; -import ExamplePath from "Components/Dashboard/components/DashboardList/NewDashModal/Examples/Path"; -import ExampleTrend from "Components/Dashboard/components/DashboardList/NewDashModal/Examples/Trend"; -import ExampleCount from "Components/Dashboard/components/DashboardList/NewDashModal/Examples/Count"; -import PerfBreakdown from "Components/Dashboard/components/DashboardList/NewDashModal/Examples/PerfBreakdown"; -import SlowestDomain from "Components/Dashboard/components/DashboardList/NewDashModal/Examples/SlowestDomain"; -import SessionsByErrors from "Components/Dashboard/components/DashboardList/NewDashModal/Examples/SessionsByErrors"; -import SessionsByIssues from "Components/Dashboard/components/DashboardList/NewDashModal/Examples/SessionsByIssues"; -import ByBrowser from "Components/Dashboard/components/DashboardList/NewDashModal/Examples/SessionsBy/ByBrowser"; -import BySystem from "Components/Dashboard/components/DashboardList/NewDashModal/Examples/SessionsBy/BySystem"; -import ByCountry from "Components/Dashboard/components/DashboardList/NewDashModal/Examples/SessionsBy/ByCountry"; -import ByUrl from "Components/Dashboard/components/DashboardList/NewDashModal/Examples/SessionsBy/ByUrl"; -import {ERRORS, FUNNEL, TIMESERIES, USER_PATH} from "App/constants/card"; +import CardsLibrary from "Components/Dashboard/components/DashboardList/NewDashModal/CardsLibrary"; interface SelectCardProps { onClose: () => void; - onCard: (card: any) => void; + onCard: () => void; + isLibrary?: boolean; } -const CARD_CATEGORY = { - PRODUCT_ANALYTICS: 'product-analytics', - PERFORMANCE_MONITORING: 'performance-monitoring', - WEB_ANALYTICS: 'web-analytics', - CORE_WEB_VITALS: 'core-web-vitals', -} - -const segmentedOptions = [ - {label: 'Product Analytics', Icon: TrendingUp, value: CARD_CATEGORY.PRODUCT_ANALYTICS}, - {label: 'Performance Monitoring', Icon: Activity, value: CARD_CATEGORY.PERFORMANCE_MONITORING}, - {label: 'Web Analytics', Icon: BarChart, value: CARD_CATEGORY.WEB_ANALYTICS}, - {label: 'Core Web Vitals', Icon: TableCellsMerge, value: CARD_CATEGORY.CORE_WEB_VITALS}, -]; - -const TYPE = { - FUNNEL: 'funnel', - PATH_FINDER: 'path-finder', - TREND: 'trend', - SESSIONS_BY: 'sessions-by', - BREAKDOWN: 'breakdown', - SLOWEST_DOMAIN: 'slowest-domain', - SESSIONS_BY_ERRORS: 'sessions-by-errors', - SESSIONS_BY_ISSUES: 'sessions-by-issues', - SESSIONS_BY_BROWSER: 'sessions-by-browser', - SESSIONS_BY_SYSTEM: 'sessions-by-system', - SESSIONS_BY_COUNTRY: 'sessions-by-country', - SESSIONS_BY_URL: 'sessions-by-url', -} - -const CARD_TYPE_MAP = { - [TYPE.FUNNEL]: FUNNEL, - [TYPE.PATH_FINDER]: USER_PATH, - [TYPE.TREND]: TIMESERIES, - [TYPE.SESSIONS_BY]: TIMESERIES, - [TYPE.BREAKDOWN]: TIMESERIES, - [TYPE.SLOWEST_DOMAIN]: TIMESERIES, - [TYPE.SESSIONS_BY_ERRORS]: ERRORS, - [TYPE.SESSIONS_BY_ISSUES]: TIMESERIES, - [TYPE.SESSIONS_BY_BROWSER]: TIMESERIES, - [TYPE.SESSIONS_BY_SYSTEM]: TIMESERIES, - [TYPE.SESSIONS_BY_COUNTRY]: TIMESERIES, - [TYPE.SESSIONS_BY_URL]: TIMESERIES, -} - -export const CARD_LIST = [ - { - title: 'Funnel', - key: TYPE.FUNNEL, - cardType: FUNNEL, - category: CARD_CATEGORY.PRODUCT_ANALYTICS, - example: ExampleFunnel, - }, - { - title: 'Path Finder', - key: TYPE.PATH_FINDER, - cardType: USER_PATH, - category: CARD_CATEGORY.PRODUCT_ANALYTICS, - example: ExamplePath, - }, - { - title: 'Trend', - key: TYPE.TREND, - cardType: TIMESERIES, - category: CARD_CATEGORY.PRODUCT_ANALYTICS, - example: ExampleTrend, - }, - { - title: 'Sessions by', - key: TYPE.SESSIONS_BY, - cardType: TIMESERIES, - category: CARD_CATEGORY.PRODUCT_ANALYTICS, - example: ExampleCount, - }, - { - title: 'Breakdown', - key: TYPE.BREAKDOWN, - cardType: TIMESERIES, - category: CARD_CATEGORY.PERFORMANCE_MONITORING, - example: PerfBreakdown, - }, - { - title: 'Slowest Domain', - key: TYPE.SLOWEST_DOMAIN, - cardType: TIMESERIES, - category: CARD_CATEGORY.PERFORMANCE_MONITORING, - example: SlowestDomain, - }, - { - title: 'Sessions by Errors', - key: TYPE.SESSIONS_BY_ERRORS, - cardType: TIMESERIES, - category: CARD_CATEGORY.PERFORMANCE_MONITORING, - example: SessionsByErrors, - }, - { - title: 'Sessions by Issues', - key: TYPE.SESSIONS_BY_ISSUES, - cardType: TIMESERIES, - category: CARD_CATEGORY.PERFORMANCE_MONITORING, - example: SessionsByIssues, - }, - - { - title: 'Sessions by Browser', - key: TYPE.SESSIONS_BY_BROWSER, - cardType: TIMESERIES, - category: CARD_CATEGORY.WEB_ANALYTICS, - example: ByBrowser, - }, - { - title: 'Sessions by System', - key: TYPE.SESSIONS_BY_SYSTEM, - cardType: TIMESERIES, - category: CARD_CATEGORY.WEB_ANALYTICS, - example: BySystem, - }, - { - title: 'Sessions by Country', - key: TYPE.SESSIONS_BY_COUNTRY, - cardType: TIMESERIES, - category: CARD_CATEGORY.WEB_ANALYTICS, - example: ByCountry, - }, - { - title: 'Sessions by URL', - key: TYPE.SESSIONS_BY_URL, - cardType: TIMESERIES, - category: CARD_CATEGORY.WEB_ANALYTICS, - example: ByUrl, - }, - - // { - // title: 'Breakdown', - // key: TYPE.BREAKDOWN, - // category: CARD_CATEGORY.CORE_WEB_VITALS, - // example: PerfBreakdown, - // }, - // { - // title: 'Slowest Domain', - // key: TYPE.SLOWEST_DOMAIN, - // category: CARD_CATEGORY.CORE_WEB_VITALS, - // example: SlowestDomain, - // }, - // { - // title: 'Sessions by Issues', - // key: TYPE.SESSIONS_BY_ISSUES, - // category: CARD_CATEGORY.CORE_WEB_VITALS, - // example: SessionsByIssues, - // }, - // { - // title: 'Sessions by Errors', - // key: TYPE.SESSIONS_BY_ISSUES, - // category: CARD_CATEGORY.CORE_WEB_VITALS, - // example: SessionsByErrors, - // }, -] - -const SelectCard: React.FC = (props: SelectCardProps) => { +const SelectCard: React.FC = ({onCard, isLibrary = false}) => { const [selected, setSelected] = React.useState('product-analytics'); - // const item = getSelectedItem(selected, onCard); - - const onCard = (card: string) => { - const _card = CARD_LIST.find((c) => c.key === card); - props.onCard(_card); - // props.onClose(); - } + const [selectedCards, setSelectedCards] = React.useState([]); + const {metricStore, dashboardStore} = useStore(); + const dashboardId = window.location.pathname.split('/')[4]; - const item = useMemo(() => { + const handleCardSelection = (card: string) => { + const selectedCard = CARD_LIST.find((c) => c.key === card) as CardType; + metricStore.merge({ + metricType: selectedCard.cardType, + name: selectedCard.title, + metricOf: selectedCard.metricOf, + }); + onCard(); + }; + + const cardItems = useMemo(() => { return CARD_LIST.filter((card) => card.category === selected).map((card) => (
- +
)); }, [selected]); + const onCardClick = (cardId: number) => { + if (selectedCards.includes(cardId)) { + setSelectedCards(selectedCards.filter((id) => id !== cardId)); + } else { + setSelectedCards([...selectedCards, cardId]); + } + } + + const onAddSelected = () => { + console.log(selectedCards); + dashboardStore.addWidgetToDashboard(dashboardId, selectedCards); + } + return ( <> -
-
- Select your first card type to add to the dashboard -
-
-
- ({ - label:
-
- {item} -
+
+ + {isLibrary ? : + } ); }; +interface HeaderProps { + selectedCount?: number, + onAdd?: () => void; +} + +const Header: React.FC = ({selectedCount = 0, onAdd = () => null}) => ( +
+
+ Select your first card type to add to the dashboard +
+
+ {selectedCount > 0 ? ( + + ) : ''} +
+
+); + +interface CategorySelectorProps { + setSelected: React.Dispatch>; +} + +const CategorySelector: React.FC = ({setSelected}) => ( + ({ + label:
+ ); } diff --git a/frontend/app/components/Dashboard/components/WidgetForm/CardBuilder.tsx b/frontend/app/components/Dashboard/components/WidgetForm/CardBuilder.tsx index 6a978cf7d..205e66d2d 100644 --- a/frontend/app/components/Dashboard/components/WidgetForm/CardBuilder.tsx +++ b/frontend/app/components/Dashboard/components/WidgetForm/CardBuilder.tsx @@ -5,7 +5,7 @@ import {metricOf, issueOptions, issueCategories} from 'App/constants/filterOptio import {FilterKey} from 'Types/filter/filterType'; import {withSiteId, dashboardMetricDetails, metricDetails} from 'App/routes'; import {Icon, confirm} from 'UI'; -import {Card, Input, Space, Button} from 'antd'; +import {Card, Input, Space, Button, Segmented} from 'antd'; import {AudioWaveform} from "lucide-react"; import FilterSeries from '../FilterSeries'; import Select from 'Shared/Select'; @@ -21,6 +21,8 @@ import { import {useParams} from 'react-router-dom'; import {useHistory} from "react-router"; +const tableOptions = metricOf.filter((i) => i.type === 'table'); + const AIInput = ({value, setValue, placeholder, onEnter}) => ( (
); -const MetricOptions = ({metric, writeOption}) => { +const MetricTabs = ({metric, writeOption}: any) => { + if (![TABLE].includes(metric.metricType)) return null; + + const onChange = (value: string) => { + console.log('value', value); + writeOption({ + value: { + value + }, name: 'metricOf' + }); + } + + return ( + + ) +} + +const MetricOptions = ({metric, writeOption}: any) => { const isUserPath = metric.metricType === USER_PATH; return ( @@ -198,7 +217,6 @@ interface CardBuilderProps { const CardBuilder = observer((props: CardBuilderProps) => { const history = useHistory(); const {siteId, dashboardId, metricId} = props; - console.log('siteId', siteId); const {metricStore, dashboardStore, aiFiltersStore} = useStore(); const [aiQuery, setAiQuery] = useState(''); const [aiAskChart, setAiAskChart] = useState(''); @@ -209,6 +227,8 @@ const CardBuilder = observer((props: CardBuilderProps) => { const isPredefined = [ERRORS, PERFORMANCE, RESOURCE_MONITORING, WEB_VITALS].includes(metric.metricType); const testingKey = localStorage.getItem('__mauricio_testing_access') === 'true'; + console.log('metric', metric); + useEffect(() => { if (metric && !initialInstance) setInitialInstance(metric.toJson()); @@ -276,6 +296,8 @@ const CardBuilder = observer((props: CardBuilderProps) => { {/* metric={metric}*/} {/* writeOption={writeOption}*/} {/*/>*/} + {metric.metricType === USER_PATH && } {isPredefined && } {testingKey && ( diff --git a/frontend/app/mstore/dashboardStore.ts b/frontend/app/mstore/dashboardStore.ts index 379ce94cf..691016047 100644 --- a/frontend/app/mstore/dashboardStore.ts +++ b/frontend/app/mstore/dashboardStore.ts @@ -359,34 +359,33 @@ export default class DashboardStore { }); } - deleteDashboardWidget(dashboardId: string, widgetId: string) { + async deleteDashboardWidget(dashboardId: string, widgetId: string) { this.isDeleting = true; - return dashboardService - .deleteWidget(dashboardId, widgetId) - .then(() => { - toast.success('Dashboard updated successfully'); - runInAction(() => { - this.selectedDashboard?.removeWidget(widgetId); - }); - }) - .finally(() => { - this.isDeleting = false; + try { + await dashboardService + .deleteWidget(dashboardId, widgetId); + toast.success('Dashboard updated successfully'); + runInAction(() => { + this.selectedDashboard?.removeWidget(widgetId); }); + } finally { + this.isDeleting = false; + } } - addWidgetToDashboard(dashboard: Dashboard, metricIds: any): Promise { + async addWidgetToDashboard(dashboard: Dashboard, metricIds: any): Promise { this.isSaving = true; - return dashboardService - .addWidget(dashboard, metricIds) - .then((response) => { + try { + try { + const response = await dashboardService + .addWidget(dashboard, metricIds); toast.success('Card added to dashboard.'); - }) - .catch(() => { + } catch { toast.error('Card could not be added.'); - }) - .finally(() => { - this.isSaving = false; - }); + } + } finally { + this.isSaving = false; + } } setPeriod(period: any) {