diff --git a/frontend/app/components/BugFinder/EventFilter/EventEditor.js b/frontend/app/components/BugFinder/EventFilter/EventEditor.js index def086416..29488a231 100644 --- a/frontend/app/components/BugFinder/EventFilter/EventEditor.js +++ b/frontend/app/components/BugFinder/EventFilter/EventEditor.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { DNDSource, DNDTarget } from 'Components/hocs/dnd'; +// import { DNDSource, DNDTarget } from 'Components/hocs/dnd'; import Event, { TYPES } from 'Types/filter/event'; import { operatorOptions } from 'Types/filter'; import { editEvent, removeEvent, clearEvents, applyFilter } from 'Duck/filters'; @@ -25,8 +25,8 @@ const getLabel = ({ type }) => { return getPlaceholder({ type }); }; -@DNDTarget('event') -@DNDSource('event') +// @DNDTarget('event') +// @DNDSource('event') @connect(state => ({ isLastEvent: state.getIn([ 'filters', 'appliedFilter', 'events' ]).size === 1, }), { editEvent, removeEvent, clearEvents, applyFilter }) diff --git a/frontend/app/components/BugFinder/EventFilter/EventFilter.js b/frontend/app/components/BugFinder/EventFilter/EventFilter.js index 5bc1d1b32..5adc5a42e 100644 --- a/frontend/app/components/BugFinder/EventFilter/EventFilter.js +++ b/frontend/app/components/BugFinder/EventFilter/EventFilter.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import { Input } from 'semantic-ui-react'; -import { DNDContext } from 'Components/hocs/dnd'; +// import { DNDContext } from 'Components/hocs/dnd'; import { addEvent, applyFilter, moveEvent, clearEvents, edit, addCustomFilter, addAttribute, setSearchQuery, setActiveFlow, setFilterOption @@ -45,7 +45,7 @@ import SaveFilterButton from 'Shared/SaveFilterButton'; setBlink, edit, }) -@DNDContext +// @DNDContext export default class EventFilter extends React.PureComponent { state = { search: '', showFilterModal: false, showPlacehoder: true } fetchEventList = debounce(this.props.fetchEventList, 500) diff --git a/frontend/app/components/Dashboard/NewDashboard.tsx b/frontend/app/components/Dashboard/NewDashboard.tsx index 46901a87f..5d51e5037 100644 --- a/frontend/app/components/Dashboard/NewDashboard.tsx +++ b/frontend/app/components/Dashboard/NewDashboard.tsx @@ -26,7 +26,10 @@ function NewDashboard(props) { }, []); useEffect(() => { - if (!dashboard || !dashboard.dashboardId) { + if (dashboardId) { + store.selectDashboardById(dashboardId); + } + if (!dashboardId) { if (dashboardId) { store.selectDashboardById(dashboardId); } else { @@ -35,19 +38,9 @@ function NewDashboard(props) { }); } } + }, []); - // if (dashboard) { - // if (dashboard.dashboardId !== dashboardId) { - // history.push(withSiteId(dashboardSelected(dashboard.dashboardId), parseInt(siteId))); - // } - - // history.replace(withSiteId(dashboardSelected(dashboard.dashboardId), parseInt(siteId))); - // } - - // console.log('dashboard', dashboard) - }, [dashboard]); - - console.log('rendering dashboard', props.match.params); + // console.log('rendering dashboard', props.match.params); return ( <> diff --git a/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx b/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx index d34eeb08d..d7f6e2fe4 100644 --- a/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx +++ b/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx @@ -1,18 +1,61 @@ -import React from 'react'; +import React, { useRef } from 'react'; import { useDashboardStore } from '../store/store'; import cn from 'classnames'; import { ItemMenu } from 'UI'; +import { useDrag, useDrop } from 'react-dnd'; function WidgetWrapper(props) { - const { widget } = props; - // const store: any = useDashboardStore(); - // const dashboard = store.selectedDashboard; - // const siteId = store.siteId; + const { widget, index, moveListItem } = props; + + // useDrag - the list item is draggable + const [{ opacity, isDragging }, dragRef] = useDrag({ + type: 'item', + item: { index }, + collect: (monitor) => ({ + isDragging: monitor.isDragging(), + opacity: monitor.isDragging() ? 0.5 : 1, + }), + }, [index]); + + // useDrop - the list item is also a drop area + const [spec, dropRef] = useDrop({ + accept: 'item', + drop: (item: any) => { + if (item.index === index) return; + moveListItem(item.index, index); + }, + // hover: (item: any, monitor: any) => { + // const dragIndex = item.index + // const hoverIndex = index + // const hoverBoundingRect = ref.current?.getBoundingClientRect() + // const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2 + // const hoverActualY = monitor.getClientOffset().y - hoverBoundingRect.top + + // // if dragging down, continue only when hover is smaller than middle Y + // if (dragIndex < hoverIndex && hoverActualY < hoverMiddleY) return + // // if dragging up, continue only when hover is bigger than middle Y + // if (dragIndex > hoverIndex && hoverActualY > hoverMiddleY) return + + // moveListItem(dragIndex, hoverIndex) + // item.index = hoverIndex + // }, + }, []) + + console.log('spec', spec) + + const ref: any = useRef(null) + const dragDropRef: any = dragRef(dropRef(ref)) return ( -
+
{/* */} -
+
{widget.name} - {widget.position}
-
- - -
-
- {list && list.map(item => )} +
+
+ + +
+
+ Right +
+
) } diff --git a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx new file mode 100644 index 000000000..1b1256793 --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { useDashboardStore } from '../../store/store'; +import WidgetWrapper from '../../WidgetWrapper'; +import { NoContent, Button, Loader } from 'UI'; +import { useObserver } from 'mobx-react-lite'; +// import { divider } from '../../Filters/filters.css'; + +function DashboardWidgetGrid(props) { + const store: any = useDashboardStore(); + const loading = store.isLoading; + const dashbaord = store.selectedDashboard; + const list = dashbaord.widgets; + return useObserver(() => ( + + +

Metrics helps you visualize trends from sessions captured by OpenReplay

+ +
+ } + > +
+ {list && list.map((item, index) => ( + dashbaord.swapWidgetPosition(dragIndex, hoverIndex)} + /> + ))} +
+ + + )); +} + +export default DashboardWidgetGrid; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/index.ts b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/index.ts new file mode 100644 index 000000000..410933285 --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/index.ts @@ -0,0 +1 @@ +export { default } from './DashboardWidgetGrid'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx index c25b2546c..ba6df6ce4 100644 --- a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx +++ b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx @@ -1,20 +1,24 @@ import { useObserver } from 'mobx-react-lite'; import React from 'react'; -import { Icon, NoContent, Label, Link } from 'UI'; +import { Icon, NoContent, Label, Link, Pagination } from 'UI'; import { useDashboardStore } from '../../store/store'; +import { getRE } from 'App/utils'; interface Props { } function MetricsList(props: Props) { const store: any = useDashboardStore(); const widgets = store.widgets; const lenth = widgets.length; - const currentPage = store.metricsPage; - const totalPages = widgets.length; + const currentPage = useObserver(() => store.metricsPage); + const metricsSearch = useObserver(() => store.metricsSearch); + + const filterRE = getRE(metricsSearch, 'i'); + const list = widgets.filter(w => filterRE.test(w.name)) + const totalPages = list.length; const pageSize = store.metricsPageSize; const start = (currentPage - 1) * pageSize; const end = currentPage * pageSize; - const list = widgets.slice(start, end); return useObserver(() => ( @@ -28,7 +32,7 @@ function MetricsList(props: Props) {
Last Modified
- {list.map((metric: any) => ( + {list.slice(start, end).map((metric: any) => (
@@ -55,6 +59,16 @@ function MetricsList(props: Props) {
))}
+ +
+ store.updateKey('metricsPage', page)} + limit={pageSize} + debounceRequest={100} + /> +
)); } diff --git a/frontend/app/components/Dashboard/components/MetricsSearch/MetricsSearch.tsx b/frontend/app/components/Dashboard/components/MetricsSearch/MetricsSearch.tsx new file mode 100644 index 000000000..45c209350 --- /dev/null +++ b/frontend/app/components/Dashboard/components/MetricsSearch/MetricsSearch.tsx @@ -0,0 +1,25 @@ +import { useObserver } from 'mobx-react-lite'; +import React from 'react'; +import { useDashboardStore } from '../../store/store'; +import { Icon } from 'UI'; + +function MetricsSearch(props) { + const store: any = useDashboardStore(); + const metricsSearch = useObserver(() => store.metricsSearch); + + + return useObserver(() => ( +
+ + store.updateKey(name, value)} + /> +
+ )); +} + +export default MetricsSearch; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/MetricsSearch/index.ts b/frontend/app/components/Dashboard/components/MetricsSearch/index.ts new file mode 100644 index 000000000..cf23f645d --- /dev/null +++ b/frontend/app/components/Dashboard/components/MetricsSearch/index.ts @@ -0,0 +1 @@ +export { default } from './MetricsSearch'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx b/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx index e00402d1f..b8ec01d25 100644 --- a/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx +++ b/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Button, PageTitle, Icon, Link } from 'UI'; import { withSiteId, dashboardMetricCreate } from 'App/routes'; import MetricsList from '../MetricsList'; +import MetricsSearch from '../MetricsSearch'; function MetricsView(props) { return ( @@ -9,11 +10,8 @@ function MetricsView(props) {
{/* */} -
- +
+
diff --git a/frontend/app/components/Dashboard/store/dashboard.ts b/frontend/app/components/Dashboard/store/dashboard.ts index c8f4a498b..dbc6d221f 100644 --- a/frontend/app/components/Dashboard/store/dashboard.ts +++ b/frontend/app/components/Dashboard/store/dashboard.ts @@ -104,6 +104,7 @@ export default class Dashboard { } swapWidgetPosition(positionA, positionB) { + console.log('swapWidgetPosition', positionA, positionB) const widgetA = this.widgets[positionA] const widgetB = this.widgets[positionB] this.widgets[positionA] = widgetB diff --git a/frontend/app/components/Dashboard/store/dashboardStore.ts b/frontend/app/components/Dashboard/store/dashboardStore.ts index 44b33f2f7..8373c3fe9 100644 --- a/frontend/app/components/Dashboard/store/dashboardStore.ts +++ b/frontend/app/components/Dashboard/store/dashboardStore.ts @@ -14,15 +14,12 @@ export default class DashboardStore { widgets: Widget[] = [] metricsPage: number = 1 metricsPageSize: number = 10 + metricsSearch: string = '' private client = new APIClient() constructor() { makeAutoObservable(this, { - dashboards: observable, - selectedDashboard: observable, - isLoading: observable, - resetCurrentWidget: action, addDashboard: action, removeDashboard: action, @@ -38,6 +35,7 @@ export default class DashboardStore { fromJson: action, setSiteId: action, editWidget: action, + updateKey: action, }) @@ -54,7 +52,7 @@ export default class DashboardStore { // this.selectedDashboard?.swapWidgetPosition(2, 0) // }, 3000) - for (let i = 0; i < 20; i++) { + for (let i = 0; i < 15; i++) { const widget: any= {}; widget.widgetId = `${i}` widget.name = `Widget ${i}`; @@ -84,6 +82,10 @@ export default class DashboardStore { } + updateKey(key: any, value: any) { + this[key] = value + } + resetCurrentWidget() { this.currentWidget = new Widget() } diff --git a/frontend/app/components/hocs/dnd.js b/frontend/app/components/hocs/dnd.js index dd2a4b784..3e4f42e8e 100644 --- a/frontend/app/components/hocs/dnd.js +++ b/frontend/app/components/hocs/dnd.js @@ -1,6 +1,6 @@ -import HTML5Backend from 'react-dnd-html5-backend'; +import { HTML5Backend } from 'react-dnd-html5-backend'; import { findDOMNode } from 'react-dom'; -import { DragSource, DropTarget, DragDropContext } from 'react-dnd'; +import { DragSource, DropTarget, DndContext } from 'react-dnd'; const cardSource = { beginDrag(props) { @@ -50,7 +50,7 @@ const cardTarget = { }, }; -export const DNDContext = DragDropContext(HTML5Backend); +export const DNDContext = DndContext(HTML5Backend); export const DNDSource = name => DragSource(name, cardSource, (connect, monitor) => ({ connectDragSource: connect.dragSource(), diff --git a/frontend/app/components/shared/EventFilter/EventEditor.js b/frontend/app/components/shared/EventFilter/EventEditor.js index f2d5dee8f..4fe2c5ee8 100644 --- a/frontend/app/components/shared/EventFilter/EventEditor.js +++ b/frontend/app/components/shared/EventFilter/EventEditor.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { DNDSource, DNDTarget } from 'Components/hocs/dnd'; +// import { DNDSource, DNDTarget } from 'Components/hocs/dnd'; import Event, { TYPES } from 'Types/filter/event'; import { operatorOptions } from 'Types/filter'; import { editEvent, removeEvent, clearEvents, applyFilter } from 'Duck/funnelFilters'; @@ -24,8 +24,8 @@ const getLabel = ({ type }) => { return getPlaceholder({ type }); }; -@DNDTarget('event') -@DNDSource('event') +// @DNDTarget('event') +// @DNDSource('event') @connect(state => ({ isLastEvent: state.getIn([ 'filters', 'appliedFilter', 'events' ]).size === 1, funnel: state.getIn(['funnels', 'instance']), diff --git a/frontend/app/components/shared/EventFilter/EventFilter.js b/frontend/app/components/shared/EventFilter/EventFilter.js index 9a852398e..ca3db8fc5 100644 --- a/frontend/app/components/shared/EventFilter/EventFilter.js +++ b/frontend/app/components/shared/EventFilter/EventFilter.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { DNDContext } from 'Components/hocs/dnd'; +// import { DNDContext } from 'Components/hocs/dnd'; import { addEvent, applyFilter, moveEvent, clearEvents, addCustomFilter, addAttribute, setSearchQuery, setActiveFlow, setFilterOption @@ -39,7 +39,7 @@ import CustomFilters from './CustomFilters'; updateFunnelFilters, refreshFunnel }) -@DNDContext +// @DNDContext export default class EventFilter extends React.PureComponent { state = { search: '', showFilterModal: false, showPlacehoder: true, showSaveModal: false } fetchEventList = debounce(this.props.fetchEventList, 500) diff --git a/frontend/app/initialize.js b/frontend/app/initialize.js index 6460a9eff..629ea33ad 100644 --- a/frontend/app/initialize.js +++ b/frontend/app/initialize.js @@ -9,6 +9,9 @@ import DashboardStore from './components/Dashboard/store'; import { DashboardStoreProvider } from './components/Dashboard/store/store'; import { ModalProvider } from './components/Modal/ModalContext'; import ModalRoot from './components/Modal/ModalRoot'; +import { HTML5Backend } from 'react-dnd-html5-backend' +import { DndProvider } from 'react-dnd' + document.addEventListener('DOMContentLoaded', () => { @@ -16,12 +19,14 @@ document.addEventListener('DOMContentLoaded', () => { render( ( - - - - - - + + + + + + + + ), document.getElementById('app'),