Admin: refactor counters with DashCounter component
This commit is contained in:
parent
5f08091856
commit
ee443158b6
|
@ -0,0 +1,57 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { FormattedNumber } from 'react-intl';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { Text } from 'soapbox/components/ui';
|
||||||
|
import { isNumber } from 'soapbox/utils/numbers';
|
||||||
|
|
||||||
|
interface IDashCounter {
|
||||||
|
count: number | undefined
|
||||||
|
label: React.ReactNode
|
||||||
|
to?: string
|
||||||
|
percent?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Displays a (potentially clickable) dashboard statistic. */
|
||||||
|
const DashCounter: React.FC<IDashCounter> = ({ count, label, to = '#', percent = false }) => {
|
||||||
|
|
||||||
|
if (!isNumber(count)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
className='bg-gray-200 dark:bg-gray-800 p-4 rounded flex flex-col items-center space-y-2 hover:-translate-y-1 transition-transform cursor-pointer'
|
||||||
|
to={to}
|
||||||
|
>
|
||||||
|
<Text align='center' size='2xl' weight='medium'>
|
||||||
|
<FormattedNumber
|
||||||
|
value={count}
|
||||||
|
style={percent ? 'unit' : undefined}
|
||||||
|
unit={percent ? 'percent' : undefined}
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
<Text align='center'>
|
||||||
|
{label}
|
||||||
|
</Text>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IDashCounters {
|
||||||
|
children: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Wrapper container for dash counters. */
|
||||||
|
const DashCounters: React.FC<IDashCounters> = ({ children }) => {
|
||||||
|
return (
|
||||||
|
<div className='grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-2 mb-4 mt-8'>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
DashCounter,
|
||||||
|
DashCounters,
|
||||||
|
};
|
|
@ -1,15 +1,13 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FormattedMessage, FormattedNumber } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { getSubscribersCsv, getUnsubscribersCsv, getCombinedCsv } from 'soapbox/actions/email-list';
|
import { getSubscribersCsv, getUnsubscribersCsv, getCombinedCsv } from 'soapbox/actions/email-list';
|
||||||
import { Text } from 'soapbox/components/ui';
|
|
||||||
import { useAppDispatch, useOwnAccount, useFeatures, useInstance } from 'soapbox/hooks';
|
import { useAppDispatch, useOwnAccount, useFeatures, useInstance } from 'soapbox/hooks';
|
||||||
import sourceCode from 'soapbox/utils/code';
|
import sourceCode from 'soapbox/utils/code';
|
||||||
import { download } from 'soapbox/utils/download';
|
import { download } from 'soapbox/utils/download';
|
||||||
import { parseVersion } from 'soapbox/utils/features';
|
import { parseVersion } from 'soapbox/utils/features';
|
||||||
import { isNumber } from 'soapbox/utils/numbers';
|
|
||||||
|
|
||||||
|
import { DashCounter, DashCounters } from '../components/dashcounter';
|
||||||
import RegistrationModePicker from '../components/registration-mode-picker';
|
import RegistrationModePicker from '../components/registration-mode-picker';
|
||||||
|
|
||||||
const Dashboard: React.FC = () => {
|
const Dashboard: React.FC = () => {
|
||||||
|
@ -46,64 +44,37 @@ const Dashboard: React.FC = () => {
|
||||||
const domainCount = instance.stats.get('domain_count');
|
const domainCount = instance.stats.get('domain_count');
|
||||||
|
|
||||||
const mau = instance.pleroma.getIn(['stats', 'mau']) as number | undefined;
|
const mau = instance.pleroma.getIn(['stats', 'mau']) as number | undefined;
|
||||||
const retention = (userCount && mau) ? Math.round(mau / userCount * 100) : null;
|
const retention = (userCount && mau) ? Math.round(mau / userCount * 100) : undefined;
|
||||||
|
|
||||||
if (!account) return null;
|
if (!account) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='dashcounters mt-8'>
|
<DashCounters>
|
||||||
{isNumber(mau) && (
|
<DashCounter
|
||||||
<div className='dashcounter'>
|
count={mau}
|
||||||
<Text align='center' size='2xl' weight='medium'>
|
label={<FormattedMessage id='admin.dashcounters.mau_label' defaultMessage='monthly active users' />}
|
||||||
<FormattedNumber value={mau} />
|
/>
|
||||||
</Text>
|
<DashCounter
|
||||||
<Text align='center'>
|
to='/soapbox/admin/users'
|
||||||
<FormattedMessage id='admin.dashcounters.mau_label' defaultMessage='monthly active users' />
|
count={userCount}
|
||||||
</Text>
|
label={<FormattedMessage id='admin.dashcounters.user_count_label' defaultMessage='total users' />}
|
||||||
</div>
|
/>
|
||||||
)}
|
<DashCounter
|
||||||
{isNumber(userCount) && (
|
count={retention}
|
||||||
<Link className='dashcounter' to='/soapbox/admin/users'>
|
label={<FormattedMessage id='admin.dashcounters.retention_label' defaultMessage='user retention' />}
|
||||||
<Text align='center' size='2xl' weight='medium'>
|
percent
|
||||||
<FormattedNumber value={userCount} />
|
/>
|
||||||
</Text>
|
<DashCounter
|
||||||
<Text align='center'>
|
to='/timeline/local'
|
||||||
<FormattedMessage id='admin.dashcounters.user_count_label' defaultMessage='total users' />
|
count={statusCount}
|
||||||
</Text>
|
label={<FormattedMessage id='admin.dashcounters.status_count_label' defaultMessage='posts' />}
|
||||||
</Link>
|
/>
|
||||||
)}
|
<DashCounter
|
||||||
{isNumber(retention) && (
|
count={domainCount}
|
||||||
<div className='dashcounter'>
|
label={<FormattedMessage id='admin.dashcounters.domain_count_label' defaultMessage='peers' />}
|
||||||
<Text align='center' size='2xl' weight='medium'>
|
/>
|
||||||
{retention}%
|
</DashCounters>
|
||||||
</Text>
|
|
||||||
<Text align='center'>
|
|
||||||
<FormattedMessage id='admin.dashcounters.retention_label' defaultMessage='user retention' />
|
|
||||||
</Text>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{isNumber(statusCount) && (
|
|
||||||
<Link className='dashcounter' to='/timeline/local'>
|
|
||||||
<Text align='center' size='2xl' weight='medium'>
|
|
||||||
<FormattedNumber value={statusCount} />
|
|
||||||
</Text>
|
|
||||||
<Text align='center'>
|
|
||||||
<FormattedMessage id='admin.dashcounters.status_count_label' defaultMessage='posts' />
|
|
||||||
</Text>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
{isNumber(domainCount) && (
|
|
||||||
<div className='dashcounter'>
|
|
||||||
<Text align='center' size='2xl' weight='medium'>
|
|
||||||
<FormattedNumber value={domainCount} />
|
|
||||||
</Text>
|
|
||||||
<Text align='center'>
|
|
||||||
<FormattedMessage id='admin.dashcounters.domain_count_label' defaultMessage='peers' />
|
|
||||||
</Text>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{account.admin && <RegistrationModePicker />}
|
{account.admin && <RegistrationModePicker />}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
.dashcounters {
|
|
||||||
@apply grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-2 mb-4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashcounter {
|
|
||||||
@apply bg-gray-200 dark:bg-gray-800 p-4 rounded flex flex-col items-center space-y-2 hover:-translate-y-1 transition-transform cursor-pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashwidgets {
|
.dashwidgets {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
Loading…
Reference in New Issue