change(ui) - remove projects from client, and other fixes
This commit is contained in:
parent
d50d63ba7e
commit
bb6401b675
52 changed files with 265 additions and 212 deletions
|
|
@ -22,7 +22,7 @@ const FunnelIssueDetails = lazy(() => import('Components/Funnels/FunnelIssueDeta
|
|||
import WidgetViewPure from 'Components/Dashboard/components/WidgetView';
|
||||
import Header from 'Components/Header/Header';
|
||||
// import ResultsModal from 'Shared/Results/ResultsModal';
|
||||
import { fetchList as fetchIntegrationVariables } from 'Duck/customField';
|
||||
import { fetchList as fetchMetadata } from 'Duck/customField';
|
||||
import { fetchList as fetchSiteList } from 'Duck/site';
|
||||
import { fetchList as fetchAnnouncements } from 'Duck/announcements';
|
||||
import { fetchList as fetchAlerts } from 'Duck/alerts';
|
||||
|
|
@ -80,7 +80,7 @@ const ONBOARDING_REDIRECT_PATH = routes.onboarding(OB_DEFAULT_TAB);
|
|||
@withStore
|
||||
@withRouter
|
||||
@connect((state) => {
|
||||
const siteId = state.getIn([ 'user', 'siteId' ]);
|
||||
const siteId = state.getIn([ 'site', 'siteId' ]);
|
||||
const jwt = state.get('jwt');
|
||||
const changePassword = state.getIn([ 'user', 'account', 'changePassword' ]);
|
||||
const userInfoLoading = state.getIn([ 'user', 'fetchUserInfoRequest', 'loading' ]);
|
||||
|
|
@ -88,7 +88,7 @@ const ONBOARDING_REDIRECT_PATH = routes.onboarding(OB_DEFAULT_TAB);
|
|||
jwt,
|
||||
siteId,
|
||||
changePassword,
|
||||
sites: state.getIn([ 'user', 'client', 'sites' ]),
|
||||
sites: state.getIn([ 'site', 'list' ]),
|
||||
isLoggedIn: jwt !== null && !changePassword,
|
||||
loading: siteId === null || userInfoLoading,
|
||||
email: state.getIn([ 'user', 'account', 'email' ]),
|
||||
|
|
@ -103,7 +103,7 @@ const ONBOARDING_REDIRECT_PATH = routes.onboarding(OB_DEFAULT_TAB);
|
|||
fetchUserInfo,
|
||||
fetchTenants,
|
||||
setSessionPath,
|
||||
fetchIntegrationVariables,
|
||||
fetchMetadata,
|
||||
fetchSiteList,
|
||||
fetchAnnouncements,
|
||||
fetchAlerts,
|
||||
|
|
@ -124,17 +124,18 @@ class Router extends React.Component {
|
|||
fetchInitialData = () => {
|
||||
Promise.all([
|
||||
this.props.fetchUserInfo().then(() => {
|
||||
const { mstore } = this.props
|
||||
mstore.initClient();
|
||||
this.props.fetchIntegrationVariables()
|
||||
}),
|
||||
this.props.fetchSiteList().then(() => {
|
||||
setTimeout(() => {
|
||||
this.props.fetchAnnouncements();
|
||||
this.props.fetchAlerts();
|
||||
this.props.fetchWatchdogStatus();
|
||||
}, 100);
|
||||
}),
|
||||
this.props.fetchSiteList().then(() => {
|
||||
const { mstore } = this.props
|
||||
mstore.initClient();
|
||||
|
||||
setTimeout(() => {
|
||||
this.props.fetchMetadata()
|
||||
this.props.fetchAnnouncements();
|
||||
this.props.fetchAlerts();
|
||||
this.props.fetchWatchdogStatus();
|
||||
}, 100);
|
||||
})
|
||||
})
|
||||
])
|
||||
}
|
||||
|
||||
|
|
@ -197,25 +198,17 @@ class Router extends React.Component {
|
|||
{ onboarding &&
|
||||
<Redirect to={ withSiteId(ONBOARDING_REDIRECT_PATH, siteId)} />
|
||||
}
|
||||
{ siteIdList.length === 0 &&
|
||||
{/* { siteIdList.length === 0 &&
|
||||
<Redirect to={ routes.client(routes.CLIENT_TABS.SITES) } />
|
||||
}
|
||||
} */}
|
||||
|
||||
{/* DASHBOARD and Metrics */}
|
||||
<Route exact strict path={ withSiteId(METRICS_PATH, siteIdList) } component={ Dashboard } />
|
||||
<Route exact strict path={ withSiteId(METRICS_DETAILS, siteIdList) } component={ Dashboard } />
|
||||
|
||||
<Route exact strict path={ withSiteId(DASHBOARD_PATH, siteIdList) } component={ Dashboard } />
|
||||
<Route exact strict path={ withSiteId(DASHBOARD_SELECT_PATH, siteIdList) } component={ Dashboard } />
|
||||
<Route exact strict path={ withSiteId(DASHBOARD_METRIC_CREATE_PATH, siteIdList) } component={ Dashboard } />
|
||||
<Route exact strict path={ withSiteId(DASHBOARD_METRIC_DETAILS_PATH, siteIdList) } component={ Dashboard } />
|
||||
|
||||
|
||||
|
||||
{/* <Route exact strict path={ withSiteId(WIDGET_PATAH, siteIdList) } component={ Dashboard } />
|
||||
<Route exact strict path={ withSiteId(WIDGET_PATAH, siteIdList) } component={ Dashboard } />
|
||||
<Route exact strict path={ withSiteId(WIDGET_PATAH, siteIdList) } component={ Dashboard } />
|
||||
<Route exact strict path={ withSiteId(WIDGET_PATAH, siteIdList) } component={ Dashboard } />
|
||||
<Route exact strict path={ withSiteId(WIDGET_PATAH, siteIdList) } component={ Dashboard } /> */}
|
||||
|
||||
<Route exact strict path={ withSiteId(ASSIST_PATH, siteIdList) } component={ Assist } />
|
||||
<Route exact strict path={ withSiteId(ERRORS_PATH, siteIdList) } component={ Errors } />
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ export const clean = (obj, forbidenValues = [ undefined, '' ]) => {
|
|||
export default class APIClient {
|
||||
constructor() {
|
||||
const jwt = store.getState().get('jwt');
|
||||
const siteId = store.getState().getIn([ 'user', 'siteId' ]);
|
||||
const siteId = store.getState().getIn([ 'site', 'siteId' ]);
|
||||
this.init = {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
|
|
|
|||
|
|
@ -96,6 +96,6 @@ class Announcements extends React.Component {
|
|||
export default connect(state => ({
|
||||
announcements: state.getIn(['announcements', 'list']),
|
||||
loading: state.getIn(['announcements', 'fetchList', 'loading']),
|
||||
siteId: state.getIn([ 'user', 'siteId' ]),
|
||||
siteId: state.getIn([ 'site', 'siteId' ]),
|
||||
sites: state.getIn([ 'site', 'list' ]),
|
||||
}), { fetchList, setLastRead })(Announcements);
|
||||
|
|
@ -53,7 +53,7 @@ const allowedQueryKeys = [
|
|||
sources: state.getIn([ 'customFields', 'sources' ]),
|
||||
filterValues: state.get('filterValues'),
|
||||
favoriteList: state.getIn([ 'sessions', 'favoriteList' ]),
|
||||
currentProjectId: state.getIn([ 'user', 'siteId' ]),
|
||||
currentProjectId: state.getIn([ 'site', 'siteId' ]),
|
||||
sites: state.getIn([ 'site', 'list' ]),
|
||||
watchdogs: state.getIn(['watchdogs', 'list']),
|
||||
activeFlow: state.getIn([ 'filters', 'activeFlow' ]),
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ const SessionCaptureRate = props => {
|
|||
}
|
||||
|
||||
export default connect(state => ({
|
||||
currentProjectId: state.getIn([ 'user', 'siteId' ]),
|
||||
currentProjectId: state.getIn([ 'site', 'siteId' ]),
|
||||
captureRate: state.getIn(['watchdogs', 'captureRate']),
|
||||
loading: state.getIn(['watchdogs', 'savingCaptureRate', 'loading']),
|
||||
}), {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import { confirm } from 'UI/Confirmation';
|
|||
fields: state.getIn(['customFields', 'list']).sortBy(i => i.index),
|
||||
field: state.getIn(['customFields', 'instance']),
|
||||
loading: state.getIn(['customFields', 'fetchRequest', 'loading']),
|
||||
sites: state.getIn([ 'user', 'client', 'sites' ]),
|
||||
sites: state.getIn([ 'site', 'list' ]),
|
||||
errors: state.getIn([ 'customFields', 'saveRequest', 'errors' ]),
|
||||
}), {
|
||||
init,
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import SiteDropdown from 'Shared/SiteDropdown';
|
|||
import { save, init, edit, remove, fetchList } from 'Duck/integrations/actions';
|
||||
|
||||
@connect((state, { name, customPath }) => ({
|
||||
sites: state.getIn([ 'user', 'client', 'sites' ]),
|
||||
initialSiteId: state.getIn([ 'user', 'siteId' ]),
|
||||
sites: state.getIn([ 'site', 'list' ]),
|
||||
initialSiteId: state.getIn([ 'site', 'siteId' ]),
|
||||
list: state.getIn([ name, 'list' ]),
|
||||
config: state.getIn([ name, 'instance']),
|
||||
saving: state.getIn([ customPath || name, 'saveRequest', 'loading']),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { Input, Button, Label } from 'UI';
|
||||
import { save, edit, update , fetchList } from 'Duck/site';
|
||||
import { pushNewSite, setSiteId } from 'Duck/user';
|
||||
import { pushNewSite } from 'Duck/user';
|
||||
import { setSiteId } from 'Duck/site';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import styles from './siteForm.css';
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ class Webhooks extends React.PureComponent {
|
|||
title="No webhooks available."
|
||||
size="small"
|
||||
show={ noSlackWebhooks.size === 0 }
|
||||
icon
|
||||
animatedIcon="no-results"
|
||||
>
|
||||
<div className={ styles.list }>
|
||||
{ noSlackWebhooks.map(webhook => (
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
& .tabContent {
|
||||
background-color: white;
|
||||
padding: 25px;
|
||||
margin-top: -30px;
|
||||
/* margin-top: -30px; */
|
||||
margin-right: -20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,6 @@ const DashboardHeader = props => {
|
|||
export default connect(state => ({
|
||||
period: state.getIn([ 'dashboard', 'period' ]),
|
||||
platform: state.getIn([ 'dashboard', 'platform' ]),
|
||||
currentProjectId: state.getIn([ 'user', 'siteId' ]),
|
||||
currentProjectId: state.getIn([ 'site', 'siteId' ]),
|
||||
sites: state.getIn([ 'site', 'list' ]),
|
||||
}), { setPeriod, setPlatform })(DashboardHeader)
|
||||
|
|
|
|||
|
|
@ -3,18 +3,16 @@ import withPageTitle from 'HOCs/withPageTitle';
|
|||
import { observer, useObserver } from "mobx-react-lite";
|
||||
import { useStore } from 'App/mstore';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import {
|
||||
dashboardSelected,
|
||||
withSiteId,
|
||||
} from 'App/routes';
|
||||
import DashboardSideMenu from './components/DashboardSideMenu';
|
||||
import { Loader } from 'UI';
|
||||
import DashboardRouter from './components/DashboardRouter';
|
||||
import cn from 'classnames';
|
||||
|
||||
function NewDashboard(props) {
|
||||
const { history, match: { params: { siteId, dashboardId, metricId } } } = props;
|
||||
const { dashboardStore } = useStore();
|
||||
const loading = useObserver(() => dashboardStore.isLoading);
|
||||
const isMetricDetails = history.location.pathname.includes('/metrics/') || history.location.pathname.includes('/metric/');
|
||||
|
||||
useEffect(() => {
|
||||
dashboardStore.fetchList().then((resp) => {
|
||||
|
|
@ -32,20 +30,18 @@ function NewDashboard(props) {
|
|||
});
|
||||
}, [siteId]);
|
||||
|
||||
return (
|
||||
return useObserver(() => (
|
||||
<Loader loading={loading}>
|
||||
<div className="page-margin container-90">
|
||||
<div className="side-menu">
|
||||
<div className={cn("side-menu", { 'hidden' : isMetricDetails })}>
|
||||
<DashboardSideMenu siteId={siteId} />
|
||||
</div>
|
||||
<div className="side-menu-margined">
|
||||
<div className={cn({ "side-menu-margined" : !isMetricDetails, "container-70" : isMetricDetails })}>
|
||||
<DashboardRouter siteId={siteId} />
|
||||
</div>
|
||||
</div>
|
||||
</Loader>
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
export default withPageTitle('New Dashboard')(
|
||||
withRouter(observer(NewDashboard))
|
||||
);
|
||||
export default withPageTitle('New Dashboard')(withRouter(NewDashboard));
|
||||
|
|
@ -41,5 +41,5 @@ function SideMenuSection({ title, items, onItemClick, setShowAlerts, siteId }) {
|
|||
SideMenuSection.displayName = "SideMenuSection";
|
||||
|
||||
export default connect(state => ({
|
||||
siteId: state.getIn([ 'user', 'siteId' ])
|
||||
siteId: state.getIn([ 'site', 'siteId' ])
|
||||
}), { setShowAlerts })(SideMenuSection);
|
||||
|
|
@ -6,16 +6,17 @@ import { LineChart, Line, Legend } from 'recharts';
|
|||
interface Props {
|
||||
data: any;
|
||||
params: any;
|
||||
seriesMap: any;
|
||||
// seriesMap: any;
|
||||
colors: any;
|
||||
onClick?: (event, index) => void;
|
||||
}
|
||||
function CustomMetriLineChart(props: Props) {
|
||||
const { data, params, seriesMap = [], colors, onClick = () => null } = props;
|
||||
const { data = { chart: [], namesMap: [] }, params, colors, onClick = () => null } = props;
|
||||
|
||||
return (
|
||||
<ResponsiveContainer height={ 240 } width="100%">
|
||||
<LineChart
|
||||
data={ data }
|
||||
data={ data.chart }
|
||||
margin={Styles.chartMargins}
|
||||
// syncId={ showSync ? "domainsErrors_4xx" : undefined }
|
||||
onClick={onClick}
|
||||
|
|
@ -37,18 +38,18 @@ function CustomMetriLineChart(props: Props) {
|
|||
/>
|
||||
<Legend />
|
||||
<Tooltip {...Styles.tooltip} />
|
||||
{ seriesMap.map((key, index) => (
|
||||
{ Array.isArray(data.namesMap) && data.namesMap.map((key, index) => (
|
||||
<Line
|
||||
key={key}
|
||||
name={key}
|
||||
type="monotone"
|
||||
dataKey={key}
|
||||
stroke={colors[index]}
|
||||
fillOpacity={ 1 }
|
||||
strokeWidth={ 2 }
|
||||
strokeOpacity={ 0.6 }
|
||||
// fill="url(#colorCount)"
|
||||
dot={false}
|
||||
key={key}
|
||||
name={key}
|
||||
type="monotone"
|
||||
dataKey={key}
|
||||
stroke={colors[index]}
|
||||
fillOpacity={ 1 }
|
||||
strokeWidth={ 2 }
|
||||
strokeOpacity={ 0.6 }
|
||||
// fill="url(#colorCount)"
|
||||
dot={false}
|
||||
/>
|
||||
))}
|
||||
</LineChart>
|
||||
|
|
|
|||
|
|
@ -16,24 +16,24 @@ function CustomMetricOverviewChart(props: Props) {
|
|||
|
||||
return (
|
||||
<div className="relative -mx-4">
|
||||
<div className="absolute flex items-start flex-col justify-center inset-0 p-3">
|
||||
<div className="absolute flex items-start flex-col justify-start inset-0 p-3">
|
||||
<div className="mb-2 flex items-center" >
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<CountBadge
|
||||
// title={subtext}
|
||||
count={ countView(Math.round(data.value), data.unit) }
|
||||
change={ data.progress || 0 }
|
||||
unit={ data.unit }
|
||||
// className={textClass}
|
||||
/>
|
||||
<CountBadge
|
||||
// title={subtext}
|
||||
count={ countView(Math.round(data.value), data.unit) }
|
||||
change={ data.progress || 0 }
|
||||
unit={ data.unit }
|
||||
// className={textClass}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ResponsiveContainer height={ 100 } width="100%">
|
||||
<AreaChart
|
||||
data={ data.chart }
|
||||
margin={ {
|
||||
top: 85, right: 0, left: 0, bottom: 5,
|
||||
top: 50, right: 0, left: 0, bottom: 5,
|
||||
} }
|
||||
>
|
||||
{gradientDef}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ function CustomMetriPercentage(props: Props) {
|
|||
return (
|
||||
<div className="flex flex-col items-center justify-center" style={{ height: '240px'}}>
|
||||
<div className="text-6xl">{numberWithCommas(data.count)}</div>
|
||||
<div className="text-lg mt-6">{`${parseInt(data.previousCount).toFixed(1)} ( ${parseInt(data.countProgress).toFixed(1)}% )`}</div>
|
||||
<div className="text-lg mt-6">{`${parseInt(data.previousCount)} ( ${parseInt(data.countProgress).toFixed(1)}% )`}</div>
|
||||
<div className="color-gray-medium">from previous period.</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ function CustomMetricWidget(props: Props) {
|
|||
<CustomMetriLineChart
|
||||
data={ data }
|
||||
params={ params }
|
||||
seriesMap={ seriesMap }
|
||||
// seriesMap={ seriesMap }
|
||||
colors={ colors }
|
||||
onClick={ clickHandler }
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ function CustomMetricWidget(props: Props) {
|
|||
{ metric.viewType === 'lineChart' && (
|
||||
<CustomMetriLineChart
|
||||
data={data}
|
||||
seriesMap={seriesMap}
|
||||
// seriesMap={seriesMap}
|
||||
colors={colors}
|
||||
params={params}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ function CPULoad(props: Props) {
|
|||
name="Avg"
|
||||
type="monotone"
|
||||
unit="%"
|
||||
dataKey="avgCpu"
|
||||
dataKey="value"
|
||||
stroke={Styles.colors[0]}
|
||||
fillOpacity={ 1 }
|
||||
strokeWidth={ 2 }
|
||||
|
|
|
|||
|
|
@ -32,14 +32,13 @@ function Crashes(props: Props) {
|
|||
{...Styles.yaxis}
|
||||
allowDecimals={false}
|
||||
tickFormatter={val => Styles.tickFormatter(val)}
|
||||
label={{ ...Styles.axisLabelLeft, value: "CPU Load (%)" }}
|
||||
label={{ ...Styles.axisLabelLeft, value: "Number of Crashes" }}
|
||||
/>
|
||||
<Tooltip {...Styles.tooltip} />
|
||||
<Area
|
||||
name="Crashes"
|
||||
type="monotone"
|
||||
unit="%"
|
||||
dataKey="avgCpu"
|
||||
dataKey="avg"
|
||||
stroke={Styles.colors[0]}
|
||||
fillOpacity={ 1 }
|
||||
strokeWidth={ 2 }
|
||||
|
|
|
|||
|
|
@ -59,14 +59,14 @@ function DomBuildingTime(props: Props) {
|
|||
{...Styles.yaxis}
|
||||
allowDecimals={false}
|
||||
tickFormatter={val => Styles.tickFormatter(val)}
|
||||
label={{ ...Styles.axisLabelLeft, value: "CPU Load (%)" }}
|
||||
label={{ ...Styles.axisLabelLeft, value: "DOM Build Time (ms)" }}
|
||||
/>
|
||||
<Tooltip {...Styles.tooltip} />
|
||||
<Area
|
||||
name="Avg"
|
||||
type="monotone"
|
||||
unit="%"
|
||||
dataKey="avgCpu"
|
||||
// unit="%"
|
||||
dataKey="avg"
|
||||
stroke={Styles.colors[0]}
|
||||
fillOpacity={ 1 }
|
||||
strokeWidth={ 2 }
|
||||
|
|
|
|||
|
|
@ -37,13 +37,13 @@ function FPS(props: Props) {
|
|||
{...Styles.yaxis}
|
||||
allowDecimals={false}
|
||||
tickFormatter={val => Styles.tickFormatter(val)}
|
||||
label={{ ...Styles.axisLabelLeft, value: "CPU Load (%)" }}
|
||||
label={{ ...Styles.axisLabelLeft, value: "Frames Per Second" }}
|
||||
/>
|
||||
<Tooltip {...Styles.tooltip} />
|
||||
<Area
|
||||
name="Avg"
|
||||
type="monotone"
|
||||
dataKey="avgFps"
|
||||
dataKey="avg"
|
||||
stroke={Styles.colors[0]}
|
||||
fillOpacity={ 1 }
|
||||
strokeWidth={ 2 }
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ function MemoryConsumption(props: Props) {
|
|||
name="Avg"
|
||||
unit=" mb"
|
||||
type="monotone"
|
||||
dataKey="avgFps"
|
||||
dataKey="avg"
|
||||
stroke={Styles.colors[0]}
|
||||
fillOpacity={ 1 }
|
||||
strokeWidth={ 2 }
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ function DashboardView(props: Props) {
|
|||
</div>
|
||||
<div className="flex items-center">
|
||||
<div className="flex items-center">
|
||||
<span className="mr-2 color-gray-medium">Time Range</span>
|
||||
{/* <span className="mr-2 color-gray-medium">Time Range</span> */}
|
||||
<DateRange
|
||||
rangeValue={period.rangeName}
|
||||
startDate={period.start}
|
||||
|
|
@ -97,18 +97,21 @@ function DashboardView(props: Props) {
|
|||
/>
|
||||
</div>
|
||||
<div className="mx-4" />
|
||||
<ItemMenu
|
||||
items={[
|
||||
{
|
||||
text: 'Edit',
|
||||
onClick: onEdit
|
||||
},
|
||||
{
|
||||
text: 'Delete Dashboard',
|
||||
onClick: onDelete
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<div className="flex items-center">
|
||||
<span className="mr-1 color-gray-medium">More</span>
|
||||
<ItemMenu
|
||||
items={[
|
||||
{
|
||||
text: 'Edit',
|
||||
onClick: onEdit
|
||||
},
|
||||
{
|
||||
text: 'Delete Dashboard',
|
||||
onClick: onDelete
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<DashboardWidgetGrid
|
||||
|
|
|
|||
|
|
@ -9,9 +9,11 @@ import { Loader } from 'UI';
|
|||
import { useStore } from 'App/mstore';
|
||||
import WidgetPredefinedChart from '../WidgetPredefinedChart';
|
||||
import CustomMetricOverviewChart from 'App/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricOverviewChart';
|
||||
import { getStartAndEndTimestampsByDensity } from 'Types/dashboard/helper';
|
||||
interface Props {
|
||||
metric: any;
|
||||
isWidget?: boolean
|
||||
onClick?: () => void;
|
||||
}
|
||||
function WidgetChart(props: Props) {
|
||||
const { isWidget = false, metric } = props;
|
||||
|
|
@ -19,12 +21,32 @@ function WidgetChart(props: Props) {
|
|||
const period = useObserver(() => dashboardStore.period);
|
||||
const colors = Styles.customMetricColors;
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [seriesMap, setSeriesMap] = useState<any>([]);
|
||||
const params = { density: 28 }
|
||||
const isOverviewWidget = metric.metricType === 'predefined' && metric.viewType === 'overview';
|
||||
const params = { density: isOverviewWidget ? 7 : 70 }
|
||||
const metricParams = { ...params }
|
||||
const prevMetricRef = useRef<any>();
|
||||
const [data, setData] = useState<any>(metric.data);
|
||||
|
||||
const onChartClick = (event: any) => {
|
||||
if (event) {
|
||||
const payload = event.activePayload[0].payload;
|
||||
const timestamp = payload.timestamp;
|
||||
const periodTimestamps = metric.metricType === 'timeseries' ?
|
||||
getStartAndEndTimestampsByDensity(timestamp, period.start, period.end, params.density) :
|
||||
period.toTimestamps();
|
||||
|
||||
// const activeWidget = {
|
||||
// widget: metric,
|
||||
// period: period,
|
||||
// ...periodTimestamps,
|
||||
// timestamp: payload.timestamp,
|
||||
// index,
|
||||
// }
|
||||
|
||||
// props.setActiveWidget(activeWidget);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (prevMetricRef.current && prevMetricRef.current.name !== metric.name) {
|
||||
prevMetricRef.current = metric;
|
||||
|
|
@ -33,8 +55,8 @@ function WidgetChart(props: Props) {
|
|||
prevMetricRef.current = metric;
|
||||
|
||||
setLoading(true);
|
||||
const data = isWidget ? { ...params } : { ...metricParams, ...metric.toJson() };
|
||||
dashboardStore.fetchMetricChartData(metric, data, isWidget).then((res: any) => {
|
||||
const payload = isWidget ? { ...params } : { ...metricParams, ...metric.toJson() };
|
||||
dashboardStore.fetchMetricChartData(metric, payload, isWidget).then((res: any) => {
|
||||
setData(res);
|
||||
}).finally(() => {
|
||||
setLoading(false);
|
||||
|
|
@ -42,10 +64,10 @@ function WidgetChart(props: Props) {
|
|||
}, [period]);
|
||||
|
||||
const renderChart = () => {
|
||||
const { metricType, viewType, predefinedKey } = metric;
|
||||
const { metricType, viewType } = metric;
|
||||
|
||||
if (metricType === 'predefined') {
|
||||
if (viewType === 'overview') {
|
||||
if (isOverviewWidget) {
|
||||
return <CustomMetricOverviewChart data={data} />
|
||||
}
|
||||
return <WidgetPredefinedChart data={data} predefinedKey={metric.predefinedKey} />
|
||||
|
|
@ -55,16 +77,16 @@ function WidgetChart(props: Props) {
|
|||
if (viewType === 'lineChart') {
|
||||
return (
|
||||
<CustomMetriLineChart
|
||||
data={metric.data}
|
||||
seriesMap={seriesMap}
|
||||
data={data}
|
||||
colors={colors}
|
||||
params={params}
|
||||
onClick={onChartClick}
|
||||
/>
|
||||
)
|
||||
} else if (viewType === 'progress') {
|
||||
return (
|
||||
<CustomMetricPercentage
|
||||
data={metric.data[0]}
|
||||
data={data[0]}
|
||||
colors={colors}
|
||||
params={params}
|
||||
/>
|
||||
|
|
@ -74,12 +96,12 @@ function WidgetChart(props: Props) {
|
|||
|
||||
if (metricType === 'table') {
|
||||
if (viewType === 'table') {
|
||||
return <CustomMetricTable metric={metric} data={metric.data[0]} />;
|
||||
return <CustomMetricTable metric={metric} data={data[0]} />;
|
||||
} else if (viewType === 'pieChart') {
|
||||
return (
|
||||
<CustomMetricPieChart
|
||||
metric={metric}
|
||||
data={metric.data[0]}
|
||||
data={data[0]}
|
||||
colors={colors}
|
||||
params={params}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ interface Props {
|
|||
function WidgetSessions(props: Props) {
|
||||
const { className = '' } = props;
|
||||
const { dashboardStore } = useStore();
|
||||
const period = useObserver(() => dashboardStore.period);
|
||||
const filter = useObserver(() => dashboardStore.drillDownFilter);
|
||||
const widget = dashboardStore.currentWidget;
|
||||
|
||||
const range = period.toTimestamps()
|
||||
const startTime = DateTime.fromMillis(range.startTimestamp).toFormat('LLL dd, yyyy HH:mm a');
|
||||
const endTime = DateTime.fromMillis(range.endTimestamp).toFormat('LLL dd, yyyy HH:mm a');
|
||||
// const range = period.toTimestamps()
|
||||
const startTime = DateTime.fromMillis(filter.startTimestamp).toFormat('LLL dd, yyyy HH:mm a');
|
||||
const endTime = DateTime.fromMillis(filter.endTimestamp).toFormat('LLL dd, yyyy HH:mm a');
|
||||
|
||||
return useObserver(() => (
|
||||
<div className={cn(className)}>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ function WidgetView(props: Props) {
|
|||
|
||||
return useObserver(() => (
|
||||
<Loader loading={loading}>
|
||||
<div className="relative">
|
||||
<div className="relative pb-10">
|
||||
<BackLink onClick={onBackHandler} vertical className="absolute" style={{ left: '-50px', top: '0px' }} />
|
||||
<div className="bg-white rounded border">
|
||||
<div className="p-4 flex justify-between items-center">
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ function WidgetWrapper(props: Props) {
|
|||
|
||||
const onChartClick = () => {
|
||||
if (!isWidget || widget.metricType === 'predefined') return;
|
||||
|
||||
props.history.push(withSiteId(dashboardMetricDetails(dashboardId, widget.metricId),siteId));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ export default connect((state, props) => {
|
|||
funnelId: props.match.params.funnelId,
|
||||
activeStages: state.getIn(['funnels', 'activeStages']),
|
||||
funnelFilters: state.getIn(['funnels', 'funnelFilters']),
|
||||
siteId: state.getIn([ 'user', 'siteId' ]),
|
||||
siteId: state.getIn([ 'site', 'siteId' ]),
|
||||
liveFilters: state.getIn(['funnelFilters', 'appliedFilter']),
|
||||
}
|
||||
}, {
|
||||
|
|
|
|||
|
|
@ -33,5 +33,5 @@ function FunnelDropdown(props) {
|
|||
export default connect((state, props) => ({
|
||||
funnels: state.getIn(['funnels', 'list']),
|
||||
funnel: state.getIn(['funnels', 'instance']),
|
||||
siteId: state.getIn([ 'user', 'siteId' ]),
|
||||
siteId: state.getIn([ 'site', 'siteId' ]),
|
||||
}), { })(withRouter(FunnelDropdown))
|
||||
|
|
|
|||
|
|
@ -39,5 +39,5 @@ export default connect((state, props) => ({
|
|||
issue: state.getIn(['funnels', 'issue']),
|
||||
issueId: props.match.params.issueId,
|
||||
funnelId: props.match.params.funnelId,
|
||||
siteId: state.getIn([ 'user', 'siteId' ]),
|
||||
siteId: state.getIn([ 'site', 'siteId' ]),
|
||||
}), { fetchIssue, setNavRef, resetIssue })(withRouter(FunnelIssueDetails))
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ export default connect(state => ({
|
|||
list: state.getIn(['funnels', 'issues']),
|
||||
criticalIssuesCount: state.getIn(['funnels', 'criticalIssuesCount']),
|
||||
loading: state.getIn(['funnels', 'fetchIssuesRequest', 'loading']),
|
||||
siteId: state.getIn([ 'user', 'siteId' ]),
|
||||
siteId: state.getIn([ 'site', 'siteId' ]),
|
||||
funnel: state.getIn(['funnels', 'instance']),
|
||||
activeStages: state.getIn(['funnels', 'activeStages']),
|
||||
funnelFilters: state.getIn(['funnels', 'funnelFilters']),
|
||||
|
|
|
|||
|
|
@ -28,5 +28,5 @@ function FunnelList(props) {
|
|||
|
||||
export default connect(state => ({
|
||||
list: state.getIn(['funnels', 'list']),
|
||||
siteId: state.getIn([ 'user', 'siteId' ]),
|
||||
siteId: state.getIn([ 'site', 'siteId' ]),
|
||||
}))(withRouter(FunnelList))
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ export default withRouter(connect(
|
|||
state => ({
|
||||
account: state.getIn([ 'user', 'account' ]),
|
||||
appearance: state.getIn([ 'user', 'account', 'appearance' ]),
|
||||
siteId: state.getIn([ 'user', 'siteId' ]),
|
||||
siteId: state.getIn([ 'site', 'siteId' ]),
|
||||
sites: state.getIn([ 'site', 'list' ]),
|
||||
showAlerts: state.getIn([ 'dashboard', 'showAlerts' ]),
|
||||
boardingCompletion: state.getIn([ 'dashboard', 'boardingCompletion' ])
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ const styles = {
|
|||
};
|
||||
|
||||
@connect(state => ({
|
||||
siteId: state.getIn([ 'user', 'siteId' ]),
|
||||
siteId: state.getIn([ 'site', 'siteId' ]),
|
||||
boarding: state.getIn([ 'dashboard', 'boarding' ]),
|
||||
boardingCompletion: state.getIn([ 'dashboard', 'boardingCompletion' ]),
|
||||
}), {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { setSiteId } from 'Duck/user';
|
||||
import { setSiteId } from 'Duck/site';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { hasSiteId, siteChangeAvaliable } from 'App/routes';
|
||||
import { STATUS_COLOR_MAP, GREEN } from 'Types/site';
|
||||
|
|
@ -19,7 +19,7 @@ import { withStore } from 'App/mstore'
|
|||
@withRouter
|
||||
@connect(state => ({
|
||||
sites: state.getIn([ 'site', 'list' ]),
|
||||
siteId: state.getIn([ 'user', 'siteId' ]),
|
||||
siteId: state.getIn([ 'site', 'siteId' ]),
|
||||
account: state.getIn([ 'user', 'account' ]),
|
||||
}), {
|
||||
setSiteId,
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ function StackEvents({
|
|||
|
||||
export default connect(state => ({
|
||||
hintIsHidden: state.getIn(['components', 'player', 'hiddenHints', 'stack']) ||
|
||||
!state.getIn([ 'user', 'client', 'sites' ]).some(s => s.stackIntegrations),
|
||||
!state.getIn([ 'site', 'list' ]).some(s => s.stackIntegrations),
|
||||
}), {
|
||||
hideHint
|
||||
})(StackEvents);
|
||||
|
|
@ -52,6 +52,6 @@ function AutoplayTimer({ nextId, siteId, history }) {
|
|||
|
||||
|
||||
export default withRouter(connect(state => ({
|
||||
siteId: state.getIn([ 'user', 'siteId' ]),
|
||||
siteId: state.getIn([ 'site', 'siteId' ]),
|
||||
nextId: parseInt(state.getIn([ 'sessions', 'nextId' ])),
|
||||
}))(AutoplayTimer))
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ const ASSIST_ROUTE = assistRoute();
|
|||
issuesFetched: state.getIn([ 'issues', 'issuesFetched' ]),
|
||||
local: state.getIn(['sessions', 'timezone']),
|
||||
funnelRef: state.getIn(['funnels', 'navRef']),
|
||||
siteId: state.getIn([ 'user', 'siteId' ]),
|
||||
siteId: state.getIn([ 'site', 'siteId' ]),
|
||||
metaList: state.getIn(['customFields', 'list']).map(i => i.key),
|
||||
closedLive: !!state.getIn([ 'sessions', 'errors' ]) || (isAssist && !session.live),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { connectPlayer } from 'Player';
|
||||
import { connectPlayer, jump } from 'Player';
|
||||
import { NoContent, Tabs } from 'UI';
|
||||
import withEnumToggle from 'HOCs/withEnumToggle';
|
||||
import { hideHint } from 'Duck/components/player';
|
||||
|
|
@ -18,7 +18,7 @@ const TABS = [ ALL, ...typeList ].map(tab =>({ text: tab, key: tab }));
|
|||
}))
|
||||
@connect(state => ({
|
||||
hintIsHidden: state.getIn(['components', 'player', 'hiddenHints', 'stack']) ||
|
||||
!state.getIn([ 'user', 'client', 'sites' ]).some(s => s.stackIntegrations),
|
||||
!state.getIn([ 'site', 'list' ]).some(s => s.stackIntegrations),
|
||||
}), {
|
||||
hideHint
|
||||
})
|
||||
|
|
@ -66,7 +66,11 @@ export default class StackEvents extends React.PureComponent {
|
|||
>
|
||||
<Autoscroll>
|
||||
{ filteredStackEvents.map(userEvent => (
|
||||
<UserEvent key={ userEvent.key } userEvent={ userEvent }/>
|
||||
<UserEvent
|
||||
key={ userEvent.key }
|
||||
userEvent={ userEvent }
|
||||
onJump={ () => jump(userEvent.time) }
|
||||
/>
|
||||
))}
|
||||
</Autoscroll>
|
||||
</NoContent>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import cn from 'classnames';
|
||||
import { OPENREPLAY, SENTRY, DATADOG, STACKDRIVER } from 'Types/session/stackEvent';
|
||||
import { Modal, Icon, SlideModal } from 'UI';
|
||||
import { Modal, Icon, SlideModal, IconButton } from 'UI';
|
||||
import withToggle from 'HOCs/withToggle';
|
||||
import Sentry from './Sentry';
|
||||
import JsonViewer from './JsonViewer';
|
||||
|
|
@ -54,34 +54,42 @@ export default class UserEvent extends React.PureComponent {
|
|||
return !!this.props.userEvent.payload;
|
||||
}
|
||||
|
||||
onClickDetails = (e) => {
|
||||
e.stopPropagation();
|
||||
this.props.switchOpen();
|
||||
}
|
||||
|
||||
renderContent(modalTrigger) {
|
||||
const { userEvent } = this.props;
|
||||
//const message = this.getEventMessage();
|
||||
return (
|
||||
<div
|
||||
data-scroll-item={ userEvent.isRed() }
|
||||
onClick={ this.props.switchOpen } //
|
||||
className={
|
||||
cn(
|
||||
stl.userEvent,
|
||||
this.getLevelClassname(),
|
||||
{ [ stl.modalTrigger ]: modalTrigger }
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className={ stl.infoWrapper }>
|
||||
<div
|
||||
className={ stl.title }
|
||||
>
|
||||
<Icon { ...this.getIconProps() } />
|
||||
{ userEvent.name }
|
||||
</div>
|
||||
{ /* message &&
|
||||
<div className={ stl.message }>
|
||||
{ message }
|
||||
</div> */
|
||||
}
|
||||
</div>
|
||||
// onClick={ this.props.switchOpen } //
|
||||
onClick={ this.props.onJump } //
|
||||
className={
|
||||
cn(
|
||||
"group",
|
||||
stl.userEvent,
|
||||
this.getLevelClassname(),
|
||||
{ [ stl.modalTrigger ]: modalTrigger }
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className={ stl.infoWrapper }>
|
||||
<div className={ stl.title } >
|
||||
<Icon { ...this.getIconProps() } />
|
||||
{ userEvent.name }
|
||||
</div>
|
||||
{ /* message &&
|
||||
<div className={ stl.message }>
|
||||
{ message }
|
||||
</div> */
|
||||
}
|
||||
<div className="invisible self-end ml-auto group-hover:visible">
|
||||
<IconButton size="small" plain onClick={this.onClickDetails} label="DETAILS" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -91,15 +99,15 @@ export default class UserEvent extends React.PureComponent {
|
|||
if (this.ifNeedModal()) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<SlideModal
|
||||
//title="Add Custom Field"
|
||||
size="middle"
|
||||
isDisplayed={ this.props.open }
|
||||
content={ this.props.open && this.renderPopupContent() }
|
||||
onClose={ this.props.switchOpen }
|
||||
/>
|
||||
{ this.renderContent(true) }
|
||||
</React.Fragment>
|
||||
<SlideModal
|
||||
//title="Add Custom Field"
|
||||
size="middle"
|
||||
isDisplayed={ this.props.open }
|
||||
content={ this.props.open && this.renderPopupContent() }
|
||||
onClose={ this.props.switchOpen }
|
||||
/>
|
||||
{ this.renderContent(true) }
|
||||
</React.Fragment>
|
||||
//<Modal
|
||||
// trigger={ this.renderContent(true) }
|
||||
// content={ this.renderPopupContent() }
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { withRouter } from 'react-router-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import { withSiteId } from 'App/routes';
|
||||
import { setSiteId } from 'Duck/user';
|
||||
import { setSiteId } from 'Duck/site';
|
||||
|
||||
export default BaseComponent =>
|
||||
@withRouter
|
||||
@connect((state, props) => ({
|
||||
urlSiteId: props.match.params.siteId,
|
||||
siteId: state.getIn([ 'user', 'siteId' ]),
|
||||
siteId: state.getIn([ 'site', 'siteId' ]),
|
||||
}), {
|
||||
setSiteId,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { withSiteId } from 'App/routes';
|
||||
import { setSiteId } from 'Duck/user';
|
||||
import { setSiteId } from 'Duck/site';
|
||||
|
||||
export default BaseComponent =>
|
||||
@connect((state, props) => ({
|
||||
urlSiteId: props.match.params.siteId,
|
||||
siteId: state.getIn([ 'user', 'siteId' ]),
|
||||
siteId: state.getIn([ 'site', 'siteId' ]),
|
||||
}), {
|
||||
setSiteId,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ const SESSIONS_ROUTE = sessionsRoute();
|
|||
// )
|
||||
@connect(state => ({
|
||||
timezone: state.getIn(['sessions', 'timezone']),
|
||||
siteId: state.getIn([ 'user', 'siteId' ]),
|
||||
siteId: state.getIn([ 'site', 'siteId' ]),
|
||||
}), { toggleFavorite, setSessionPath })
|
||||
@withRouter
|
||||
export default class SessionItem extends React.PureComponent {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ const SiteDropdown = ({ contextName="", sites, onChange, value }) => {
|
|||
const options = sites.map(site => ({ value: site.id, text: site.host })).toJS();
|
||||
return (
|
||||
<Select
|
||||
name={ `${ contextName }_site` }
|
||||
name={ `${ contextName }_site` }
|
||||
placeholder="Select Site"
|
||||
options={ options }
|
||||
value={ value }
|
||||
|
|
@ -17,5 +17,5 @@ const SiteDropdown = ({ contextName="", sites, onChange, value }) => {
|
|||
SiteDropdown.displayName = "SiteDropdown";
|
||||
|
||||
export default connect(state => ({
|
||||
sites: state.getIn([ 'user', 'client', 'sites' ]),
|
||||
sites: state.getIn([ 'site', 'list' ]),
|
||||
}))(SiteDropdown);
|
||||
|
|
@ -15,5 +15,5 @@ const OpenReplayLink = ({ siteId, to, className="", dispatch, ...other }) => (
|
|||
OpenReplayLink.displayName = 'OpenReplayLink';
|
||||
|
||||
export default connect((state, props) => ({
|
||||
siteId: props.siteId || state.getIn([ 'user', 'siteId' ])
|
||||
siteId: props.siteId || state.getIn([ 'site', 'siteId' ])
|
||||
}))(OpenReplayLink);
|
||||
|
|
@ -145,7 +145,7 @@ export default connect(state => ({
|
|||
loading: state.getIn(['funnels', 'fetchListRequest', 'loading']),
|
||||
activeFlow: state.getIn([ 'filters', 'activeFlow' ]),
|
||||
activeTab: state.getIn([ 'sessions', 'activeTab' ]),
|
||||
siteId: state.getIn([ 'user', 'siteId' ]),
|
||||
siteId: state.getIn([ 'site', 'siteId' ]),
|
||||
events: state.getIn([ 'filters', 'appliedFilter', 'events' ]),
|
||||
filters: state.getIn([ 'search', 'instance', 'filters' ]),
|
||||
}), {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ import {
|
|||
import { createRequestReducer } from './funcTools/request';
|
||||
import { Map, List, fromJS } from "immutable";
|
||||
|
||||
const SITE_ID_STORAGE_KEY = "__$user-siteId$__";
|
||||
const storedSiteId = localStorage.getItem(SITE_ID_STORAGE_KEY);
|
||||
|
||||
const name = 'project';
|
||||
const idKey = 'id';
|
||||
const itemInListUpdater = createItemInListUpdater(idKey)
|
||||
|
|
@ -26,13 +29,17 @@ const itemInListUpdater = createItemInListUpdater(idKey)
|
|||
const EDIT_GDPR = 'sites/EDIT_GDPR';
|
||||
const SAVE_GDPR = 'sites/SAVE_GDPR';
|
||||
const FETCH_GDPR = 'sites/FETCH_GDPR';
|
||||
const FETCH_LIST = 'sites/FETCH_LIST';
|
||||
const SET_SITE_ID = 'sites/SET_SITE_ID';
|
||||
const FETCH_GDPR_SUCCESS = success(FETCH_GDPR);
|
||||
const SAVE_GDPR_SUCCESS = success(SAVE_GDPR);
|
||||
const FETCH_LIST_SUCCESS = success(FETCH_LIST);
|
||||
|
||||
const initialState = Map({
|
||||
list: List(),
|
||||
instance: fromJS(),
|
||||
remainingSites: undefined,
|
||||
siteId: null,
|
||||
});
|
||||
|
||||
const reducer = (state = initialState, action = {}) => {
|
||||
|
|
@ -44,10 +51,18 @@ const reducer = (state = initialState, action = {}) => {
|
|||
case SAVE_GDPR_SUCCESS:
|
||||
const gdpr = GDPR(action.data);
|
||||
return state.setIn([ 'instance', 'gdpr' ], gdpr);
|
||||
// return state.update('list', itemInListUpdater({
|
||||
// [ idKey ] : state.getIn([ 'instance', idKey ]),
|
||||
// gdpr,
|
||||
// })).setIn([ 'instance', 'gdpr' ], gdpr);
|
||||
case FETCH_LIST_SUCCESS:
|
||||
let siteId = state.get("siteId");
|
||||
if (!siteId) {
|
||||
siteId = !!action.data.find(s => s.projectId === storedSiteId)
|
||||
? storedSiteId
|
||||
: action.data[0].projectId;
|
||||
}
|
||||
console.log('siteId asd', siteId)
|
||||
return state.set('list', List(action.data.map(Site))).set('siteId', siteId);
|
||||
case SET_SITE_ID:
|
||||
localStorage.setItem(SITE_ID_STORAGE_KEY, action.siteId)
|
||||
return state.set('siteId', action.siteId);
|
||||
}
|
||||
return state;
|
||||
};
|
||||
|
|
@ -73,13 +88,27 @@ export function saveGDPR(siteId, gdpr) {
|
|||
};
|
||||
}
|
||||
|
||||
export const fetchList = createFetchList(name);
|
||||
export function fetchList() {
|
||||
return {
|
||||
types: array(FETCH_LIST),
|
||||
call: client => client.get('/projects'),
|
||||
};
|
||||
}
|
||||
|
||||
// export const fetchList = createFetchList(name);
|
||||
export const init = createInit(name);
|
||||
export const edit = createEdit(name);
|
||||
export const save = createSave(name);
|
||||
export const update = createUpdate(name);
|
||||
export const remove = createRemove(name);
|
||||
|
||||
export function setSiteId(siteId) {
|
||||
return {
|
||||
type: SET_SITE_ID,
|
||||
siteId,
|
||||
};
|
||||
}
|
||||
|
||||
export default mergeReducers(
|
||||
reducer,
|
||||
createCRUDReducer(name, Site, idKey),
|
||||
|
|
|
|||
|
|
@ -18,12 +18,8 @@ export const UPDATE_PASSWORD = new RequestTypes('user/UPDATE_PASSWORD');
|
|||
const PUT_CLIENT = new RequestTypes('user/PUT_CLIENT');
|
||||
|
||||
const PUSH_NEW_SITE = 'user/PUSH_NEW_SITE';
|
||||
const SET_SITE_ID = 'user/SET_SITE_ID';
|
||||
const SET_ONBOARDING = 'user/SET_ONBOARDING';
|
||||
|
||||
const SITE_ID_STORAGE_KEY = "__$user-siteId$__";
|
||||
const storedSiteId = localStorage.getItem(SITE_ID_STORAGE_KEY);
|
||||
|
||||
const initialState = Map({
|
||||
client: Client(),
|
||||
account: Account(),
|
||||
|
|
@ -32,20 +28,13 @@ const initialState = Map({
|
|||
passwordErrors: List(),
|
||||
tenants: [],
|
||||
authDetails: {},
|
||||
onboarding: false
|
||||
onboarding: false,
|
||||
sites: List()
|
||||
});
|
||||
|
||||
const setClient = (state, data) => {
|
||||
const client = Client(data);
|
||||
let siteId = state.get("siteId");
|
||||
if (!siteId) {
|
||||
siteId = !!client.sites.find(s => s.id === storedSiteId)
|
||||
? storedSiteId
|
||||
: client.getIn([ 'sites', 0, 'id' ]);
|
||||
}
|
||||
return state
|
||||
.set('client', client)
|
||||
.set('siteId', siteId);
|
||||
return state.set('client', client)
|
||||
}
|
||||
|
||||
const reducer = (state = initialState, action = {}) => {
|
||||
|
|
@ -80,11 +69,8 @@ const reducer = (state = initialState, action = {}) => {
|
|||
return state.mergeIn([ 'client' ], action.params);
|
||||
case FETCH_CLIENT.SUCCESS:
|
||||
return setClient(state, action.data);
|
||||
case SET_SITE_ID:
|
||||
localStorage.setItem(SITE_ID_STORAGE_KEY, action.siteId)
|
||||
return state.set('siteId', action.siteId);
|
||||
case PUSH_NEW_SITE:
|
||||
return state.updateIn([ 'client', 'sites' ], list =>
|
||||
return state.updateIn([ 'site', 'list' ], list =>
|
||||
list.push(action.newSite));
|
||||
case SET_ONBOARDING:
|
||||
return state.set('onboarding', action.state)
|
||||
|
|
@ -185,13 +171,6 @@ export function updateAppearance(appearance) {
|
|||
};
|
||||
}
|
||||
|
||||
export function setSiteId(siteId) {
|
||||
return {
|
||||
type: SET_SITE_ID,
|
||||
siteId,
|
||||
};
|
||||
}
|
||||
|
||||
export function pushNewSite(newSite) {
|
||||
return {
|
||||
type: PUSH_NEW_SITE,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { dashboardService, metricService } from "App/services";
|
|||
import { toast } from 'react-toastify';
|
||||
import Period, { LAST_24_HOURS, LAST_7_DAYS } from 'Types/app/period';
|
||||
import { getChartFormatter } from 'Types/dashboard/helper';
|
||||
import Filter from "./types/filter";
|
||||
|
||||
export interface IDashboardSotre {
|
||||
dashboards: IDashboard[]
|
||||
|
|
@ -14,6 +15,7 @@ export interface IDashboardSotre {
|
|||
startTimestamp: number
|
||||
endTimestamp: number
|
||||
period: Period
|
||||
drillDownFilter: Filter
|
||||
|
||||
siteId: any
|
||||
currentWidget: Widget
|
||||
|
|
@ -73,6 +75,7 @@ export default class DashboardStore implements IDashboardSotre {
|
|||
widgetCategories: any[] = []
|
||||
widgets: Widget[] = []
|
||||
period: Period = Period({ rangeName: LAST_7_DAYS })
|
||||
drillDownFilter: Filter = new Filter()
|
||||
startTimestamp: number = 0
|
||||
endTimestamp: number = 0
|
||||
|
||||
|
|
@ -114,6 +117,10 @@ export default class DashboardStore implements IDashboardSotre {
|
|||
|
||||
fetchMetricChartData: action
|
||||
})
|
||||
|
||||
const drillDownPeriod = Period({ rangeName: LAST_7_DAYS }).toTimestamps();
|
||||
this.drillDownFilter.updateKey('startTimestamp', drillDownPeriod.startTimestamp)
|
||||
this.drillDownFilter.updateKey('endTimestamp', drillDownPeriod.endTimestamp)
|
||||
}
|
||||
|
||||
toggleAllSelectedWidgets(isSelected: boolean) {
|
||||
|
|
@ -434,7 +441,9 @@ export default class DashboardStore implements IDashboardSotre {
|
|||
// metric.setData(_data)
|
||||
// resolve(_data);
|
||||
|
||||
const _data = {}
|
||||
const _data = {
|
||||
...data,
|
||||
}
|
||||
if (data.hasOwnProperty('chart')) {
|
||||
_data['chart'] = getChartFormatter(this.period)(data.chart)
|
||||
_data['namesMap'] = data.chart
|
||||
|
|
@ -461,10 +470,9 @@ export default class DashboardStore implements IDashboardSotre {
|
|||
}
|
||||
|
||||
metric.setData(_data)
|
||||
resolve({ ...data, ..._data });
|
||||
resolve(_data);
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.log('err', err)
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
import { filter } from "App/components/BugFinder/ManageFilters/savedFilterList.css"
|
||||
import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx"
|
||||
import { FilterKey, FilterType } from 'Types/filter/filterType'
|
||||
import { filtersMap } from 'Types/filter/newFilter'
|
||||
import FilterItem from "./filterItem"
|
||||
|
||||
// console.log('filtersMap', filtersMap)
|
||||
|
||||
export default class Filter {
|
||||
public static get ID_KEY():string { return "filterId" }
|
||||
name: string = ''
|
||||
filters: FilterItem[] = []
|
||||
eventsOrder: string = 'then'
|
||||
startTimestamp: number = 0
|
||||
endTimestamp: number = 0
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this, {
|
||||
|
|
@ -53,6 +51,17 @@ export default class Filter {
|
|||
return this
|
||||
}
|
||||
|
||||
toJsonDrilldown() {
|
||||
const json = {
|
||||
name: this.name,
|
||||
filters: this.filters.map(i => i.toJson()),
|
||||
eventsOrder: this.eventsOrder,
|
||||
startTimestamp: this.startTimestamp,
|
||||
endTimestamp: this.endTimestamp,
|
||||
}
|
||||
return json
|
||||
}
|
||||
|
||||
toJson() {
|
||||
const json = {
|
||||
name: this.name,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { List } from 'immutable';
|
||||
// import { List } from 'immutable';
|
||||
import Record from 'Types/Record';
|
||||
import Site from 'Types/site';
|
||||
import { DateTime } from 'luxon';
|
||||
// import Site from 'Types/site';
|
||||
// import { DateTime } from 'luxon';
|
||||
import LoggerOptions from './loggerOptions';
|
||||
|
||||
export default Record({
|
||||
|
|
@ -10,7 +10,7 @@ export default Record({
|
|||
tenantId: undefined,
|
||||
tenantKey: '',
|
||||
name: undefined,
|
||||
sites: List(),
|
||||
// sites: List(),
|
||||
optOut: true,
|
||||
edition: '',
|
||||
}, {
|
||||
|
|
@ -21,6 +21,6 @@ export default Record({
|
|||
}) => ({
|
||||
...rest,
|
||||
loggerOptions: LoggerOptions(loggerOptions),
|
||||
sites: List(projects).map(Site),
|
||||
// sites: List(projects).map(Site),
|
||||
}),
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue