Show failed runs per step
[MAILPOET-5460]
This commit is contained in:
@ -38,6 +38,39 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mailpoet-automation-analytics-step-failed {
|
||||||
|
bottom: 0;
|
||||||
|
display: none;
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
justify-content: center;
|
||||||
|
left: -100px;
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
border-top: 1px dashed $color-gutenberg-grey-600;
|
||||||
|
content: '';
|
||||||
|
height: 0;
|
||||||
|
left: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 600px) {
|
||||||
|
.mailpoet-automation-analytics-step-failed {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mailpoet-automation-analytics-step-footer {
|
.mailpoet-automation-analytics-step-footer {
|
||||||
background: $color-wp-gray-0;
|
background: $color-wp-gray-0;
|
||||||
border-top: 1px solid $color-poet-gray-dividers;
|
border-top: 1px solid $color-poet-gray-dividers;
|
||||||
@ -47,10 +80,17 @@
|
|||||||
margin-bottom: -12px;
|
margin-bottom: -12px;
|
||||||
margin-left: -12px;
|
margin-left: -12px;
|
||||||
width: calc(100% + 24px);
|
width: calc(100% + 24px);
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin: 0;
|
|
||||||
padding: 6px 0;
|
padding: 6px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mailpoet-automation-analytics-step-footer,
|
||||||
|
.mailpoet-automation-analytics-step-failed {
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: $color-gutenberg-grey-800;
|
color: $color-gutenberg-grey-800;
|
||||||
|
@ -7,6 +7,52 @@ import { locale } from '../../../config';
|
|||||||
import { Step } from '../../../../../../editor/components/automation/types';
|
import { Step } from '../../../../../../editor/components/automation/types';
|
||||||
import { openTab } from '../../../navigation/open_tab';
|
import { openTab } from '../../../navigation/open_tab';
|
||||||
|
|
||||||
|
const compactFormatter = Intl.NumberFormat(locale.toString(), {
|
||||||
|
notation: 'compact',
|
||||||
|
});
|
||||||
|
const percentFormatter = Intl.NumberFormat(locale.toString(), {
|
||||||
|
style: 'percent',
|
||||||
|
});
|
||||||
|
|
||||||
|
function FailedStep({ step }: { step: Step }): JSX.Element | null {
|
||||||
|
const { section } = useSelect(
|
||||||
|
(s) =>
|
||||||
|
({
|
||||||
|
section: s(storeName).getSection('automation_flow'),
|
||||||
|
} as {
|
||||||
|
section: AutomationFlowSection;
|
||||||
|
}),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const { data } = section;
|
||||||
|
|
||||||
|
const failed = data.step_data?.failed;
|
||||||
|
const value = failed !== undefined ? failed[step.id] ?? 0 : 0;
|
||||||
|
if (!value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const percent =
|
||||||
|
data.step_data.total > 0
|
||||||
|
? Math.round((value / data.step_data.total) * 100)
|
||||||
|
: 0;
|
||||||
|
const formattedValue = compactFormatter.format(value);
|
||||||
|
const formattedPercent = percentFormatter.format(percent / 100);
|
||||||
|
return (
|
||||||
|
<div className="mailpoet-automation-analytics-step-failed">
|
||||||
|
<p>
|
||||||
|
{formattedPercent} ({formattedValue})
|
||||||
|
<span>
|
||||||
|
{
|
||||||
|
// translators: "failed" as in "100 automation runs failed at this step".
|
||||||
|
__('failed', 'mailpoet')
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function StepFooter({ step }: { step: Step }): JSX.Element | null {
|
export function StepFooter({ step }: { step: Step }): JSX.Element | null {
|
||||||
const { section } = useSelect(
|
const { section } = useSelect(
|
||||||
(s) =>
|
(s) =>
|
||||||
@ -29,37 +75,36 @@ export function StepFooter({ step }: { step: Step }): JSX.Element | null {
|
|||||||
? Math.round((value / data.step_data.total) * 100)
|
? Math.round((value / data.step_data.total) * 100)
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
const formattedValue = Intl.NumberFormat(locale.toString(), {
|
const formattedValue = compactFormatter.format(value);
|
||||||
notation: 'compact',
|
const formattedPercent = percentFormatter.format(percent / 100);
|
||||||
}).format(value);
|
|
||||||
const formattedPercent = Intl.NumberFormat(locale.toString(), {
|
|
||||||
style: 'percent',
|
|
||||||
}).format(percent / 100);
|
|
||||||
return (
|
return (
|
||||||
<Tooltip text={__('View subscribers', 'mailpoet')}>
|
<>
|
||||||
<div className="mailpoet-automation-analytics-step-footer">
|
<FailedStep step={step} />
|
||||||
<p>
|
<Tooltip text={__('View subscribers', 'mailpoet')}>
|
||||||
<a
|
<div className="mailpoet-automation-analytics-step-footer">
|
||||||
href={addQueryArgs(window.location.href, {
|
<p>
|
||||||
tab: 'automation-subscribers',
|
<a
|
||||||
})}
|
href={addQueryArgs(window.location.href, {
|
||||||
onClick={(e) => {
|
tab: 'automation-subscribers',
|
||||||
e.preventDefault();
|
})}
|
||||||
openTab('subscribers', {
|
onClick={(e) => {
|
||||||
filters: { status: [], step: [step.id] },
|
e.preventDefault();
|
||||||
});
|
openTab('subscribers', {
|
||||||
}}
|
filters: { status: [], step: [step.id] },
|
||||||
>
|
});
|
||||||
{formattedPercent} ({formattedValue}){' '}
|
}}
|
||||||
<span>
|
>
|
||||||
{
|
{formattedPercent} ({formattedValue}){' '}
|
||||||
// translators: "waiting" as in "100 people are waiting for this step".
|
<span>
|
||||||
__('waiting', 'mailpoet')
|
{
|
||||||
}
|
// translators: "waiting" as in "100 people are waiting for this step".
|
||||||
</span>
|
__('waiting', 'mailpoet')
|
||||||
</a>
|
}
|
||||||
</p>
|
</span>
|
||||||
</div>
|
</a>
|
||||||
</Tooltip>
|
</p>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -156,6 +156,7 @@ export type SubscriberSection = Section & {
|
|||||||
export type StepFlowData = {
|
export type StepFlowData = {
|
||||||
total: number;
|
total: number;
|
||||||
waiting: Record<string, number> | undefined;
|
waiting: Record<string, number> | undefined;
|
||||||
|
failed: Record<string, number> | undefined;
|
||||||
flow: Record<string, number> | undefined;
|
flow: Record<string, number> | undefined;
|
||||||
};
|
};
|
||||||
export type AutomationFlowSectionData = SectionData & {
|
export type AutomationFlowSectionData = SectionData & {
|
||||||
|
@ -45,6 +45,25 @@ class StepStatisticController {
|
|||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getFailedStatistics(Automation $automation, Query $query): array {
|
||||||
|
$rawData = $this->automationRunStorage->getAutomationStepStatisticForTimeFrame(
|
||||||
|
$automation->getId(),
|
||||||
|
AutomationRun::STATUS_FAILED,
|
||||||
|
$query->getAfter(),
|
||||||
|
$query->getBefore()
|
||||||
|
);
|
||||||
|
|
||||||
|
$data = [];
|
||||||
|
foreach ($automation->getSteps() as $step) {
|
||||||
|
foreach ($rawData as $rawDatum) {
|
||||||
|
if ($rawDatum['next_step_id'] === $step->getId()) {
|
||||||
|
$data[$step->getId()] = (int)$rawDatum['count'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
public function getFlowStatistics(Automation $automation, Query $query): array {
|
public function getFlowStatistics(Automation $automation, Query $query): array {
|
||||||
$statistics = $this->automationRunLogStorage->getAutomationRunStatisticsForAutomationInTimeFrame(
|
$statistics = $this->automationRunLogStorage->getAutomationRunStatisticsForAutomationInTimeFrame(
|
||||||
$automation->getId(),
|
$automation->getId(),
|
||||||
|
@ -65,6 +65,7 @@ class AutomationFlowEndpoint extends Endpoint {
|
|||||||
);
|
);
|
||||||
|
|
||||||
$waitingData = $this->stepStatisticController->getWaitingStatistics($automation, $query);
|
$waitingData = $this->stepStatisticController->getWaitingStatistics($automation, $query);
|
||||||
|
$failedData = $this->stepStatisticController->getFailedStatistics($automation, $query);
|
||||||
try {
|
try {
|
||||||
$flowData = $this->stepStatisticController->getFlowStatistics($automation, $query);
|
$flowData = $this->stepStatisticController->getFlowStatistics($automation, $query);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@ -76,6 +77,9 @@ class AutomationFlowEndpoint extends Endpoint {
|
|||||||
if ($waitingData) {
|
if ($waitingData) {
|
||||||
$stepData['waiting'] = $waitingData;
|
$stepData['waiting'] = $waitingData;
|
||||||
}
|
}
|
||||||
|
if ($failedData) {
|
||||||
|
$stepData['failed'] = $failedData;
|
||||||
|
}
|
||||||
if ($flowData) {
|
if ($flowData) {
|
||||||
$stepData['flow'] = $flowData;
|
$stepData['flow'] = $flowData;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user