Co-authored-by: tobi <tobi.smethurst@protonmail.com> Reviewed-on: https://codeberg.org/superseriousbusiness/masto-fe-standalone/pulls/88 Co-authored-by: Zoë Bijl <moiety@noreply.codeberg.org> Co-committed-by: Zoë Bijl <moiety@noreply.codeberg.org>
156 lines
4.8 KiB
JavaScript
156 lines
4.8 KiB
JavaScript
import PropTypes from "prop-types";
|
|
import { PureComponent } from "react";
|
|
|
|
import { FormattedMessage, FormattedNumber, FormattedDate } from "react-intl";
|
|
|
|
import classNames from "classnames";
|
|
|
|
import api from "mastodon/api";
|
|
import { roundTo10 } from "mastodon/utils/numbers";
|
|
|
|
const dateForCohort = cohort => {
|
|
const timeZone = "UTC";
|
|
switch(cohort.frequency) {
|
|
case "day":
|
|
return <FormattedDate value={cohort.period} month='long' day='2-digit' timeZone={timeZone} />;
|
|
default:
|
|
return <FormattedDate value={cohort.period} month='long' year='numeric' timeZone={timeZone} />;
|
|
}
|
|
};
|
|
|
|
export default class Retention extends PureComponent {
|
|
|
|
static propTypes = {
|
|
start_at: PropTypes.string,
|
|
end_at: PropTypes.string,
|
|
frequency: PropTypes.string,
|
|
};
|
|
|
|
state = {
|
|
loading: true,
|
|
data: null,
|
|
};
|
|
|
|
componentDidMount () {
|
|
const { start_at, end_at, frequency } = this.props;
|
|
|
|
api().post("/api/v1/admin/retention", { start_at, end_at, frequency }).then(res => {
|
|
this.setState({
|
|
loading: false,
|
|
data: res.data,
|
|
});
|
|
}).catch(err => {
|
|
console.error(err);
|
|
});
|
|
}
|
|
|
|
render () {
|
|
const { loading, data } = this.state;
|
|
const { frequency } = this.props;
|
|
|
|
let content;
|
|
|
|
if (loading) {
|
|
content = <FormattedMessage id='loading_indicator.label' defaultMessage='Loading...' />;
|
|
} else {
|
|
content = (
|
|
<table className='retention__table'>
|
|
<thead>
|
|
<tr>
|
|
<th>
|
|
<div className='retention__table__date retention__table__label'>
|
|
<FormattedMessage id='admin.dashboard.retention.cohort' defaultMessage='Sign-up month' />
|
|
</div>
|
|
</th>
|
|
|
|
<th>
|
|
<div className='retention__table__number retention__table__label'>
|
|
<FormattedMessage id='admin.dashboard.retention.cohort_size' defaultMessage='New users' />
|
|
</div>
|
|
</th>
|
|
|
|
{data[0].data.slice(1).map((retention, i) => (
|
|
<th key={retention.date}>
|
|
<div className='retention__table__number retention__table__label'>
|
|
{i + 1}
|
|
</div>
|
|
</th>
|
|
))}
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>
|
|
<div className='retention__table__date retention__table__average'>
|
|
<FormattedMessage id='admin.dashboard.retention.average' defaultMessage='Average' />
|
|
</div>
|
|
</td>
|
|
|
|
<td>
|
|
<div className='retention__table__size'>
|
|
<FormattedNumber value={data.reduce((sum, cohort, i) => sum + ((cohort.data[0].value * 1) - sum) / (i + 1), 0)} maximumFractionDigits={0} />
|
|
</div>
|
|
</td>
|
|
|
|
{data[0].data.slice(1).map((retention, i) => {
|
|
const average = data.reduce((sum, cohort, k) => cohort.data[i + 1] ? sum + (cohort.data[i + 1].rate - sum)/(k + 1) : sum, 0);
|
|
|
|
return (
|
|
<td key={retention.date}>
|
|
<div className={classNames("retention__table__box", "retention__table__average", `retention__table__box--${roundTo10(average * 100)}`)}>
|
|
<FormattedNumber value={average} style='percent' />
|
|
</div>
|
|
</td>
|
|
);
|
|
})}
|
|
</tr>
|
|
</thead>
|
|
|
|
<tbody>
|
|
{data.slice(0, -1).map(cohort => (
|
|
<tr key={cohort.period}>
|
|
<td>
|
|
<div className='retention__table__date'>
|
|
{dateForCohort(cohort)}
|
|
</div>
|
|
</td>
|
|
|
|
<td>
|
|
<div className='retention__table__size'>
|
|
<FormattedNumber value={cohort.data[0].value} />
|
|
</div>
|
|
</td>
|
|
|
|
{cohort.data.slice(1).map(retention => (
|
|
<td key={retention.date}>
|
|
<div className={classNames("retention__table__box", `retention__table__box--${roundTo10(retention.rate * 100)}`)}>
|
|
<FormattedNumber value={retention.rate} style='percent' />
|
|
</div>
|
|
</td>
|
|
))}
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
);
|
|
}
|
|
|
|
let title = null;
|
|
switch(frequency) {
|
|
case "day":
|
|
title = <FormattedMessage id='admin.dashboard.daily_retention' defaultMessage='User retention rate by day after sign-up' />;
|
|
break;
|
|
default:
|
|
title = <FormattedMessage id='admin.dashboard.monthly_retention' defaultMessage='User retention rate by month after sign-up' />;
|
|
}
|
|
|
|
return (
|
|
<div className='retention'>
|
|
<h4>{title}</h4>
|
|
|
|
{content}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
}
|