fix(ui): card issues and avg time in path analysis
This commit is contained in:
parent
c7f013589d
commit
e497aa3554
6 changed files with 56 additions and 34 deletions
|
|
@ -57,7 +57,7 @@ function CardIssues() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClick = (issue: any) => {
|
const handleClick = (issue?: any) => {
|
||||||
showModal(<SessionsModal issue={issue} list={[]} />, { right: true, width: 900 });
|
showModal(<SessionsModal issue={issue} list={[]} />, { right: true, width: 900 });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -89,7 +89,7 @@ function CardIssues() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Button variant='text-primary'>All Sessions</Button>
|
<Button variant='text-primary' onClick={() => handleClick()}>All Sessions</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,13 +25,15 @@ function SessionsModal(props: Props) {
|
||||||
|
|
||||||
const fetchSessions = async (filter: any) => {
|
const fetchSessions = async (filter: any) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
filter.filters = [
|
filter.filters = [];
|
||||||
{
|
|
||||||
|
if (issue) {
|
||||||
|
filter.filters.push({
|
||||||
type: 'issue',
|
type: 'issue',
|
||||||
operator: 'is',
|
operator: 'is',
|
||||||
value: [issue.type]
|
value: [issue.type]
|
||||||
}
|
});
|
||||||
];
|
}
|
||||||
const res = await metricService.fetchSessions(null, filter);
|
const res = await metricService.fetchSessions(null, filter);
|
||||||
console.log('res', res);
|
console.log('res', res);
|
||||||
setList(res[0].sessions.map((item: any) => new Session().fromJson(item)));
|
setList(res[0].sessions.map((item: any) => new Session().fromJson(item)));
|
||||||
|
|
@ -43,15 +45,10 @@ function SessionsModal(props: Props) {
|
||||||
fetchSessions({ ...dashboardStore.drillDownFilter, ...metricStore.instance.toJson(), limit: 10, page: page });
|
fetchSessions({ ...dashboardStore.drillDownFilter, ...metricStore.instance.toJson(), limit: 10, page: page });
|
||||||
}, [page]);
|
}, [page]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchSessions({ ...dashboardStore.drillDownFilter, ...metricStore.instance.toJson(), limit: 10, page: 1 });
|
|
||||||
}, [props.issue]);
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='bg-white h-screen'>
|
<div className='bg-white h-screen'>
|
||||||
<Modal.Header title='Sessions'>
|
<Modal.Header title='Sessions'>
|
||||||
Sessions with selected issue
|
{issue ? 'Sessions with selected issue' : 'All sessions'}
|
||||||
</Modal.Header>
|
</Modal.Header>
|
||||||
<Loader loading={loading}>
|
<Loader loading={loading}>
|
||||||
<NoContent show={length == 0} title='No data!'>
|
<NoContent show={length == 0} title='No data!'>
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ function NodeButton(props: Props) {
|
||||||
return (
|
return (
|
||||||
<div className='relative'>
|
<div className='relative'>
|
||||||
<Popover content={
|
<Popover content={
|
||||||
<div className='bg-white rounded w-fit mt-1 text-xs'>
|
<div className='bg-white rounded mt-1 text-xs'>
|
||||||
<div className='border-b py-1 px-2 flex items-center'>
|
<div className='border-b py-1 px-2 flex items-center'>
|
||||||
<div className='w-6 shrink-0'>
|
<div className='w-6 shrink-0'>
|
||||||
<Icon name='link-45deg' size={18} />
|
<Icon name='link-45deg' size={18} />
|
||||||
|
|
@ -30,14 +30,17 @@ function NodeButton(props: Props) {
|
||||||
</div>
|
</div>
|
||||||
<div className='ml-1 font-medium'>Continuing {Math.round(payload.value)}%</div>
|
<div className='ml-1 font-medium'>Continuing {Math.round(payload.value)}%</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='border-b py-1 px-2 flex items-center'>
|
{payload.avgTimeFromPrevious && (
|
||||||
<div className='w-6 shrink-0'>
|
<div className='border-b py-1 px-2 flex items-center'>
|
||||||
<Icon name='clock-history' size={16} />
|
<div className='w-6 shrink-0'>
|
||||||
|
<Icon name='clock-history' size={16} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='ml-1 font-medium'>
|
||||||
|
Average time from previous step <span>{payload.avgTimeFromPrevious}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='ml-1 font-medium'>
|
)}
|
||||||
Average time from previous step <span>{payload.avgTimeFromPrevious}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
} title={<div className='text-sm'>Title</div>}>
|
} title={<div className='text-sm'>Title</div>}>
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import CustomNode from './CustomNode';
|
||||||
import { NoContent } from 'UI';
|
import { NoContent } from 'UI';
|
||||||
|
|
||||||
interface Node {
|
interface Node {
|
||||||
id: number; // Assuming you missed this from your interface
|
idd: number;
|
||||||
name: string;
|
name: string;
|
||||||
eventType: string;
|
eventType: string;
|
||||||
avgTimeFromPrevious: number | null;
|
avgTimeFromPrevious: number | null;
|
||||||
|
|
|
||||||
|
|
@ -3,20 +3,41 @@
|
||||||
import { DateTime, Duration } from 'luxon'; // TODO
|
import { DateTime, Duration } from 'luxon'; // TODO
|
||||||
import { Timezone } from 'MOBX/types/sessionSettings';
|
import { Timezone } from 'MOBX/types/sessionSettings';
|
||||||
|
|
||||||
export const durationFormatted = (duration: Duration):string => {
|
/**
|
||||||
if (duration.as('minutes') < 1) { // show in seconds
|
* Formats a given duration.
|
||||||
duration = duration.toFormat('s\'s\'');
|
*
|
||||||
} else if (duration.as('hours') < 1) { // show in minutes
|
* @param {Duration | number} inputDuration - The duration to format. Can be a Duration object or a number representing milliseconds.
|
||||||
duration = duration.toFormat('m\'m\'s\'s');
|
* @returns {string} - Formatted duration string.
|
||||||
} else if (duration.as('days') < 1) { // show in hours and minutes
|
*
|
||||||
duration = duration.toFormat('h\'h\'m\'m');
|
* @example
|
||||||
} else if (duration.as('months') < 1) { // show in days and hours
|
*
|
||||||
duration = duration.toFormat('d\'d\'h\'h');
|
* // Format a duration from a Duration object
|
||||||
|
* const duration1 = Duration.fromObject({ hours: 2, minutes: 30 });
|
||||||
|
* console.log(durationFormatted(duration1)); // Outputs: "2h30m"
|
||||||
|
*
|
||||||
|
* // Format a duration from milliseconds
|
||||||
|
* console.log(durationFormatted(55000)); // Outputs: "55s"
|
||||||
|
*/
|
||||||
|
export const durationFormatted = (inputDuration: Duration | number): string => {
|
||||||
|
let duration: Duration;
|
||||||
|
|
||||||
|
if (inputDuration instanceof Duration) {
|
||||||
|
duration = inputDuration;
|
||||||
} else {
|
} else {
|
||||||
duration = duration.toFormat('m\'m\'s\'s\'');
|
duration = Duration.fromMillis(inputDuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
return duration;
|
if (duration.as('minutes') < 1) { // show in seconds
|
||||||
|
return duration.toFormat('s\'s\'');
|
||||||
|
} else if (duration.as('hours') < 1) { // show in minutes
|
||||||
|
return duration.toFormat('m\'m\'s\'s\'');
|
||||||
|
} else if (duration.as('days') < 1) { // show in hours and minutes
|
||||||
|
return duration.toFormat('h\'h\'m\'m\'');
|
||||||
|
} else if (duration.as('months') < 1) { // show in days and hours
|
||||||
|
return duration.toFormat('d\'d\'h\'h\'');
|
||||||
|
} else {
|
||||||
|
return duration.toFormat('m\'m\'s\'s\'');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export function durationFromMsFormatted(ms: number): string {
|
export function durationFromMsFormatted(ms: number): string {
|
||||||
|
|
@ -44,7 +65,7 @@ export const durationFormattedFull = (duration: Duration): string => {
|
||||||
duration = d + (d > 1 ? ' days' : ' day');
|
duration = d + (d > 1 ? ' days' : ' day');
|
||||||
} else {
|
} else {
|
||||||
let d = Math.trunc(duration.as('months'));
|
let d = Math.trunc(duration.as('months'));
|
||||||
duration = d + (d > 1 ? ' months' : ' month');;
|
duration = d + (d > 1 ? ' months' : ' month');
|
||||||
}
|
}
|
||||||
|
|
||||||
return duration;
|
return duration;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import { getChartFormatter } from 'Types/dashboard/helper';
|
||||||
import FilterItem from './filterItem';
|
import FilterItem from './filterItem';
|
||||||
import { filtersMap } from 'Types/filter/newFilter';
|
import { filtersMap } from 'Types/filter/newFilter';
|
||||||
import Issue from '../types/issue';
|
import Issue from '../types/issue';
|
||||||
|
import { durationFormatted } from 'App/date';
|
||||||
|
|
||||||
export class InsightIssue {
|
export class InsightIssue {
|
||||||
icon: string;
|
icon: string;
|
||||||
|
|
@ -264,12 +265,12 @@ export default class Widget {
|
||||||
if (this.metricType === USER_PATH) {
|
if (this.metricType === USER_PATH) {
|
||||||
_data['nodes'] = data.nodes.map((s: any) => ({
|
_data['nodes'] = data.nodes.map((s: any) => ({
|
||||||
...s,
|
...s,
|
||||||
|
avgTimeFromPrevious: s.avgTimeFromPrevious ? durationFormatted(s.avgTimeFromPrevious) : null,
|
||||||
idd: Math.random().toString(36).substring(7),
|
idd: Math.random().toString(36).substring(7),
|
||||||
}));
|
}));
|
||||||
_data['links'] = data.links.map((s: any) => ({
|
_data['links'] = data.links.map((s: any) => ({
|
||||||
...s,
|
...s,
|
||||||
id: Math.random().toString(36).substring(7),
|
id: Math.random().toString(36).substring(7),
|
||||||
// value: Math.round(s.value),
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Object.assign(this.data, _data);
|
Object.assign(this.data, _data);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue