user stats
This commit is contained in:
parent
2c749dd07f
commit
0b3b690c10
|
@ -20,21 +20,30 @@ export default {
|
||||||
},
|
},
|
||||||
itemGrowth: async (parent, args, { models }) => {
|
itemGrowth: async (parent, args, { models }) => {
|
||||||
return await models.$queryRaw(
|
return await models.$queryRaw(
|
||||||
`SELECT date_trunc('month', created_at) AS time, count(*) as num
|
`SELECT date_trunc('month', created_at) AS time, count("parentId") as comments,
|
||||||
|
count("subName") as jobs, count(*)-count("parentId")-count("subName") as posts
|
||||||
FROM "Item"
|
FROM "Item"
|
||||||
WHERE date_trunc('month', now_utc()) <> date_trunc('month', created_at)
|
WHERE date_trunc('month', now_utc()) <> date_trunc('month', created_at)
|
||||||
GROUP BY time
|
GROUP BY time
|
||||||
ORDER BY time ASC`)
|
ORDER BY time ASC`)
|
||||||
},
|
},
|
||||||
spentGrowth: async (parent, args, { models }) => {
|
spentGrowth: async (parent, args, { models }) => {
|
||||||
|
// add up earn for each month
|
||||||
|
// add up non-self votes/tips for posts and comments
|
||||||
|
|
||||||
return await models.$queryRaw(
|
return await models.$queryRaw(
|
||||||
`SELECT date_trunc('month', created_at) AS time, sum(sats) as num
|
`SELECT date_trunc('month', "ItemAct".created_at) AS time,
|
||||||
|
sum(CASE WHEN act = 'STREAM' THEN sats ELSE 0 END) as jobs,
|
||||||
|
sum(CASE WHEN act = 'VOTE' AND "Item"."userId" = "ItemAct"."userId" THEN sats ELSE 0 END) as fees,
|
||||||
|
sum(CASE WHEN act = 'BOOST' THEN sats ELSE 0 END) as boost,
|
||||||
|
sum(CASE WHEN act = 'TIP' THEN sats ELSE 0 END) as tips
|
||||||
FROM "ItemAct"
|
FROM "ItemAct"
|
||||||
WHERE date_trunc('month', now_utc()) <> date_trunc('month', created_at)
|
JOIN "Item" on "ItemAct"."itemId" = "Item".id
|
||||||
|
WHERE date_trunc('month', now_utc()) <> date_trunc('month', "ItemAct".created_at)
|
||||||
GROUP BY time
|
GROUP BY time
|
||||||
ORDER BY time ASC`)
|
ORDER BY time ASC`)
|
||||||
},
|
},
|
||||||
earnedGrowth: async (parent, args, { models }) => {
|
earnerGrowth: async (parent, args, { models }) => {
|
||||||
return await models.$queryRaw(
|
return await models.$queryRaw(
|
||||||
`SELECT time, count(distinct user_id) as num
|
`SELECT time, count(distinct user_id) as num
|
||||||
FROM
|
FROM
|
||||||
|
@ -48,6 +57,101 @@ export default {
|
||||||
WHERE date_trunc('month', now_utc()) <> date_trunc('month', created_at))) u
|
WHERE date_trunc('month', now_utc()) <> date_trunc('month', created_at))) u
|
||||||
GROUP BY time
|
GROUP BY time
|
||||||
ORDER BY time ASC`)
|
ORDER BY time ASC`)
|
||||||
|
},
|
||||||
|
stackedGrowth: async (parent, args, { models }) => {
|
||||||
|
return await models.$queryRaw(
|
||||||
|
`SELECT time, sum(airdrop) as airdrops, sum(post) as posts, sum(comment) as comments
|
||||||
|
FROM
|
||||||
|
((SELECT date_trunc('month', "ItemAct".created_at) AS time, 0 as airdrop,
|
||||||
|
CASE WHEN "Item"."parentId" IS NULL THEN 0 ELSE sats END as comment,
|
||||||
|
CASE WHEN "Item"."parentId" IS NULL THEN sats ELSE 0 END as post
|
||||||
|
FROM "ItemAct"
|
||||||
|
JOIN "Item" on "ItemAct"."itemId" = "Item".id AND "Item"."userId" <> "ItemAct"."userId"
|
||||||
|
WHERE date_trunc('month', now_utc()) <> date_trunc('month', "ItemAct".created_at) AND
|
||||||
|
"ItemAct".act IN ('VOTE', 'TIP'))
|
||||||
|
UNION ALL
|
||||||
|
(SELECT date_trunc('month', created_at) AS time, msats / 1000 as airdrop, 0 as post, 0 as comment
|
||||||
|
FROM "Earn"
|
||||||
|
WHERE date_trunc('month', now_utc()) <> date_trunc('month', created_at))) u
|
||||||
|
GROUP BY time
|
||||||
|
ORDER BY time ASC`)
|
||||||
|
},
|
||||||
|
registrationsWeekly: async (parent, args, { models }) => {
|
||||||
|
return await models.item.count({
|
||||||
|
where: {
|
||||||
|
createdAt: {
|
||||||
|
gte: new Date(new Date().setDate(new Date().getDate() - 7))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
activeWeekly: async (parent, args, { models }) => {
|
||||||
|
const [{ active }] = await models.$queryRaw(
|
||||||
|
`SELECT count(DISTINCT "userId") as active
|
||||||
|
FROM "ItemAct"
|
||||||
|
WHERE created_at >= now_utc() - interval '1 week'`
|
||||||
|
)
|
||||||
|
return active
|
||||||
|
},
|
||||||
|
earnersWeekly: async (parent, args, { models }) => {
|
||||||
|
const [{ earners }] = await models.$queryRaw(
|
||||||
|
`SELECT count(distinct user_id) as earners
|
||||||
|
FROM
|
||||||
|
((SELECT "Item"."userId" as user_id
|
||||||
|
FROM "ItemAct"
|
||||||
|
JOIN "Item" on "ItemAct"."itemId" = "Item".id AND "Item"."userId" <> "ItemAct"."userId"
|
||||||
|
WHERE "ItemAct".created_at >= now_utc() - interval '1 week')
|
||||||
|
UNION ALL
|
||||||
|
(SELECT "userId" as user_id
|
||||||
|
FROM "Earn"
|
||||||
|
WHERE created_at >= now_utc() - interval '1 week')) u`)
|
||||||
|
return earners
|
||||||
|
},
|
||||||
|
itemsWeekly: async (parent, args, { models }) => {
|
||||||
|
const [stats] = await models.$queryRaw(
|
||||||
|
`SELECT json_build_array(
|
||||||
|
json_build_object('name', 'comments', 'value', count("parentId")),
|
||||||
|
json_build_object('name', 'job', 'value', count("subName")),
|
||||||
|
json_build_object('name', 'posts', 'value', count(*)-count("parentId")-count("subName"))) as array
|
||||||
|
FROM "Item"
|
||||||
|
WHERE created_at >= now_utc() - interval '1 week'`)
|
||||||
|
|
||||||
|
return stats?.array
|
||||||
|
},
|
||||||
|
spentWeekly: async (parent, args, { models }) => {
|
||||||
|
const [stats] = await models.$queryRaw(
|
||||||
|
`SELECT json_build_array(
|
||||||
|
json_build_object('name', 'jobs', 'value', sum(CASE WHEN act = 'STREAM' THEN sats ELSE 0 END)),
|
||||||
|
json_build_object('name', 'fees', 'value', sum(CASE WHEN act = 'VOTE' AND "Item"."userId" = "ItemAct"."userId" THEN sats ELSE 0 END)),
|
||||||
|
json_build_object('name', 'boost', 'value', sum(CASE WHEN act = 'BOOST' THEN sats ELSE 0 END)),
|
||||||
|
json_build_object('name', 'tips', 'value', sum(CASE WHEN act = 'TIP' THEN sats ELSE 0 END))) as array
|
||||||
|
FROM "ItemAct"
|
||||||
|
JOIN "Item" on "ItemAct"."itemId" = "Item".id
|
||||||
|
WHERE "ItemAct".created_at >= now_utc() - interval '1 week'`)
|
||||||
|
|
||||||
|
return stats?.array
|
||||||
|
},
|
||||||
|
stackedWeekly: async (parent, args, { models }) => {
|
||||||
|
const [stats] = await models.$queryRaw(
|
||||||
|
`SELECT json_build_array(
|
||||||
|
json_build_object('name', 'airdrops', 'value', sum(airdrop)),
|
||||||
|
json_build_object('name', 'posts', 'value', sum(post)),
|
||||||
|
json_build_object('name', 'comments', 'value', sum(comment))
|
||||||
|
) as array
|
||||||
|
FROM
|
||||||
|
((SELECT 0 as airdrop,
|
||||||
|
CASE WHEN "Item"."parentId" IS NULL THEN 0 ELSE sats END as comment,
|
||||||
|
CASE WHEN "Item"."parentId" IS NULL THEN sats ELSE 0 END as post
|
||||||
|
FROM "ItemAct"
|
||||||
|
JOIN "Item" on "ItemAct"."itemId" = "Item".id AND "Item"."userId" <> "ItemAct"."userId"
|
||||||
|
WHERE "ItemAct".created_at >= now_utc() - interval '1 week' AND
|
||||||
|
"ItemAct".act IN ('VOTE', 'TIP'))
|
||||||
|
UNION ALL
|
||||||
|
(SELECT msats / 1000 as airdrop, 0 as post, 0 as comment
|
||||||
|
FROM "Earn"
|
||||||
|
WHERE created_at >= now_utc() - interval '1 week')) u`)
|
||||||
|
|
||||||
|
return stats?.array
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,48 @@ export default gql`
|
||||||
extend type Query {
|
extend type Query {
|
||||||
registrationGrowth: [TimeNum!]!
|
registrationGrowth: [TimeNum!]!
|
||||||
activeGrowth: [TimeNum!]!
|
activeGrowth: [TimeNum!]!
|
||||||
itemGrowth: [TimeNum!]!
|
itemGrowth: [ItemGrowth!]!
|
||||||
spentGrowth: [TimeNum!]!
|
spentGrowth: [SpentGrowth!]!
|
||||||
earnedGrowth: [TimeNum!]!
|
stackedGrowth: [StackedGrowth!]!
|
||||||
|
earnerGrowth: [TimeNum!]!
|
||||||
|
|
||||||
|
registrationsWeekly: Int!
|
||||||
|
activeWeekly: Int!
|
||||||
|
earnersWeekly: Int!
|
||||||
|
itemsWeekly: [NameValue!]!
|
||||||
|
spentWeekly: [NameValue!]!
|
||||||
|
stackedWeekly: [NameValue!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type TimeNum {
|
type TimeNum {
|
||||||
time: String!
|
time: String!
|
||||||
num: Int!
|
num: Int!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NameValue {
|
||||||
|
name: String!
|
||||||
|
value: Int!
|
||||||
|
}
|
||||||
|
|
||||||
|
type ItemGrowth {
|
||||||
|
time: String!
|
||||||
|
jobs: Int!
|
||||||
|
posts: Int!
|
||||||
|
comments: Int!
|
||||||
|
}
|
||||||
|
|
||||||
|
type StackedGrowth {
|
||||||
|
time: String!
|
||||||
|
airdrops: Int!
|
||||||
|
posts: Int!
|
||||||
|
comments: Int!
|
||||||
|
}
|
||||||
|
|
||||||
|
type SpentGrowth {
|
||||||
|
time: String!
|
||||||
|
jobs: Int!
|
||||||
|
fees: Int!
|
||||||
|
boost: Int!
|
||||||
|
tips: Int!
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|
|
@ -96,9 +96,9 @@ const AnalyticsPopover = (
|
||||||
visitors
|
visitors
|
||||||
</a>
|
</a>
|
||||||
<span className='mx-2 text-dark'> \ </span>
|
<span className='mx-2 text-dark'> \ </span>
|
||||||
<Link href='/usage' passHref>
|
<Link href='/users/forever' passHref>
|
||||||
<a className='text-dark d-inline-flex'>
|
<a className='text-dark d-inline-flex'>
|
||||||
usage
|
users
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
</Popover.Content>
|
</Popover.Content>
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { Nav, Navbar } from 'react-bootstrap'
|
||||||
|
import styles from './header.module.css'
|
||||||
|
|
||||||
|
export function UsageHeader () {
|
||||||
|
const router = useRouter()
|
||||||
|
return (
|
||||||
|
<Navbar className='pt-0'>
|
||||||
|
<Nav
|
||||||
|
className={`${styles.navbarNav} justify-content-around`}
|
||||||
|
activeKey={router.asPath}
|
||||||
|
>
|
||||||
|
<Nav.Item>
|
||||||
|
<Link href='/users/week' passHref>
|
||||||
|
<Nav.Link
|
||||||
|
className={styles.navLink}
|
||||||
|
>
|
||||||
|
week
|
||||||
|
</Nav.Link>
|
||||||
|
</Link>
|
||||||
|
</Nav.Item>
|
||||||
|
<Nav.Item>
|
||||||
|
<Link href='/users/forever' passHref>
|
||||||
|
<Nav.Link
|
||||||
|
className={styles.navLink}
|
||||||
|
>
|
||||||
|
forever
|
||||||
|
</Nav.Link>
|
||||||
|
</Link>
|
||||||
|
</Nav.Item>
|
||||||
|
</Nav>
|
||||||
|
</Navbar>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,92 +0,0 @@
|
||||||
import { gql } from '@apollo/client'
|
|
||||||
import { getGetServerSideProps } from '../api/ssrApollo'
|
|
||||||
import Layout from '../components/layout'
|
|
||||||
import { LineChart, Line, XAxis, YAxis, Tooltip, Legend, ResponsiveContainer } from 'recharts'
|
|
||||||
import { Col, Row } from 'react-bootstrap'
|
|
||||||
import { formatSats } from '../lib/format'
|
|
||||||
|
|
||||||
export const getServerSideProps = getGetServerSideProps(
|
|
||||||
gql`
|
|
||||||
{
|
|
||||||
registrationGrowth {
|
|
||||||
time
|
|
||||||
num
|
|
||||||
}
|
|
||||||
activeGrowth {
|
|
||||||
time
|
|
||||||
num
|
|
||||||
}
|
|
||||||
itemGrowth {
|
|
||||||
time
|
|
||||||
num
|
|
||||||
}
|
|
||||||
spentGrowth {
|
|
||||||
time
|
|
||||||
num
|
|
||||||
}
|
|
||||||
earnedGrowth {
|
|
||||||
time
|
|
||||||
num
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
|
|
||||||
const dateFormatter = timeStr => {
|
|
||||||
const date = new Date(timeStr)
|
|
||||||
return `${('0' + (date.getMonth() + 2)).slice(-2)}/${String(date.getFullYear()).slice(-2)}`
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Growth ({
|
|
||||||
data: { registrationGrowth, activeGrowth, itemGrowth, spentGrowth, earnedGrowth }
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<Layout>
|
|
||||||
<Row className='mt-3'>
|
|
||||||
<Col>
|
|
||||||
<GrowthLineChart data={registrationGrowth} xName='month' yName='registrations' />
|
|
||||||
</Col>
|
|
||||||
<Col>
|
|
||||||
<GrowthLineChart data={activeGrowth} xName='month' yName='active users' />
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row className='mt-3'>
|
|
||||||
<Col>
|
|
||||||
<GrowthLineChart data={itemGrowth} xName='month' yName='items' />
|
|
||||||
</Col>
|
|
||||||
<Col>
|
|
||||||
<GrowthLineChart data={spentGrowth} xName='month' yName='sats spent' />
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row className='mt-3'>
|
|
||||||
<Col>
|
|
||||||
<GrowthLineChart data={earnedGrowth} xName='month' yName='earning users' />
|
|
||||||
</Col>
|
|
||||||
<Col />
|
|
||||||
</Row>
|
|
||||||
</Layout>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function GrowthLineChart ({ data, xName, yName }) {
|
|
||||||
return (
|
|
||||||
<ResponsiveContainer width='100%' height={300} minWidth={300}>
|
|
||||||
<LineChart
|
|
||||||
data={data}
|
|
||||||
margin={{
|
|
||||||
top: 5,
|
|
||||||
right: 5,
|
|
||||||
left: 0,
|
|
||||||
bottom: 0
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<XAxis
|
|
||||||
dataKey='time' tickFormatter={dateFormatter} name={xName}
|
|
||||||
tick={{ fill: 'var(--theme-grey)' }}
|
|
||||||
/>
|
|
||||||
<YAxis tickFormatter={formatSats} tick={{ fill: 'var(--theme-grey)' }} />
|
|
||||||
<Tooltip labelFormatter={dateFormatter} contentStyle={{ color: 'var(--theme-color)', backgroundColor: 'var(--theme-body)' }} />
|
|
||||||
<Legend />
|
|
||||||
<Line type='monotone' dataKey='num' name={yName} stroke='var(--secondary)' />
|
|
||||||
</LineChart>
|
|
||||||
</ResponsiveContainer>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -0,0 +1,147 @@
|
||||||
|
import { gql } from '@apollo/client'
|
||||||
|
import { getGetServerSideProps } from '../../api/ssrApollo'
|
||||||
|
import Layout from '../../components/layout'
|
||||||
|
import { LineChart, Line, XAxis, YAxis, Tooltip, Legend, ResponsiveContainer, AreaChart, Area } from 'recharts'
|
||||||
|
import { Col, Row } from 'react-bootstrap'
|
||||||
|
import { formatSats } from '../../lib/format'
|
||||||
|
import { UsageHeader } from '../../components/usage-header'
|
||||||
|
|
||||||
|
export const getServerSideProps = getGetServerSideProps(
|
||||||
|
gql`
|
||||||
|
{
|
||||||
|
registrationGrowth {
|
||||||
|
time
|
||||||
|
num
|
||||||
|
}
|
||||||
|
activeGrowth {
|
||||||
|
time
|
||||||
|
num
|
||||||
|
}
|
||||||
|
itemGrowth {
|
||||||
|
time
|
||||||
|
jobs
|
||||||
|
comments
|
||||||
|
posts
|
||||||
|
}
|
||||||
|
spentGrowth {
|
||||||
|
time
|
||||||
|
jobs
|
||||||
|
fees
|
||||||
|
boost
|
||||||
|
tips
|
||||||
|
}
|
||||||
|
stackedGrowth {
|
||||||
|
time
|
||||||
|
posts
|
||||||
|
comments
|
||||||
|
airdrops
|
||||||
|
}
|
||||||
|
earnerGrowth {
|
||||||
|
time
|
||||||
|
num
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
const dateFormatter = timeStr => {
|
||||||
|
const date = new Date(timeStr)
|
||||||
|
return `${('0' + (date.getMonth() + 2)).slice(-2)}/${String(date.getFullYear()).slice(-2)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Growth ({
|
||||||
|
data: { registrationGrowth, activeGrowth, itemGrowth, spentGrowth, earnerGrowth, stackedGrowth }
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<UsageHeader />
|
||||||
|
<Row>
|
||||||
|
<Col className='mt-3'>
|
||||||
|
<div className='text-center text-muted font-weight-bold invisible'>earning users</div>
|
||||||
|
<GrowthLineChart data={earnerGrowth} xName='month' yName='earning users' />
|
||||||
|
</Col>
|
||||||
|
<Col className='mt-3'>
|
||||||
|
<div className='text-center text-muted font-weight-bold'>stacking</div>
|
||||||
|
<GrowthAreaChart data={stackedGrowth} xName='month' />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Col className='mt-3'>
|
||||||
|
<div className='text-center text-muted font-weight-bold'>items</div>
|
||||||
|
<GrowthAreaChart data={itemGrowth} xName='month' />
|
||||||
|
</Col>
|
||||||
|
<Col className='mt-3'>
|
||||||
|
<div className='text-center text-muted font-weight-bold'>spending</div>
|
||||||
|
<GrowthAreaChart data={spentGrowth} xName='month' yName='sats spent' />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Col className='mt-3'>
|
||||||
|
<div className='text-center text-muted font-weight-bold invisible'>registrations</div>
|
||||||
|
<GrowthLineChart data={registrationGrowth} xName='month' yName='registrations' />
|
||||||
|
</Col>
|
||||||
|
<Col className='mt-3'>
|
||||||
|
<div className='text-center text-muted font-weight-bold invisible'>active users</div>
|
||||||
|
<GrowthLineChart data={activeGrowth} xName='month' yName='interactive users' />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Layout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const COLORS = [
|
||||||
|
'var(--secondary)',
|
||||||
|
'var(--info)',
|
||||||
|
'var(--success)',
|
||||||
|
'var(--boost)',
|
||||||
|
'var(--grey)'
|
||||||
|
]
|
||||||
|
|
||||||
|
function GrowthAreaChart ({ data, xName, title }) {
|
||||||
|
return (
|
||||||
|
<ResponsiveContainer width='100%' height={300} minWidth={300}>
|
||||||
|
<AreaChart
|
||||||
|
data={data}
|
||||||
|
margin={{
|
||||||
|
top: 5,
|
||||||
|
right: 5,
|
||||||
|
left: 0,
|
||||||
|
bottom: 0
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<XAxis
|
||||||
|
dataKey='time' tickFormatter={dateFormatter} name={xName}
|
||||||
|
tick={{ fill: 'var(--theme-grey)' }}
|
||||||
|
/>
|
||||||
|
<YAxis tickFormatter={formatSats} tick={{ fill: 'var(--theme-grey)' }} />
|
||||||
|
<Tooltip labelFormatter={dateFormatter} contentStyle={{ color: 'var(--theme-color)', backgroundColor: 'var(--theme-body)' }} />
|
||||||
|
<Legend />
|
||||||
|
{Object.keys(data[0]).filter(v => v !== 'time' && v !== '__typename').map((v, i) =>
|
||||||
|
<Area key={v} type='monotone' dataKey={v} name={v} stackId='1' stroke={COLORS[i]} fill={COLORS[i]} />)}
|
||||||
|
</AreaChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function GrowthLineChart ({ data, xName, yName }) {
|
||||||
|
return (
|
||||||
|
<ResponsiveContainer width='100%' height={300} minWidth={300}>
|
||||||
|
<LineChart
|
||||||
|
data={data}
|
||||||
|
margin={{
|
||||||
|
top: 5,
|
||||||
|
right: 5,
|
||||||
|
left: 0,
|
||||||
|
bottom: 0
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<XAxis
|
||||||
|
dataKey='time' tickFormatter={dateFormatter} name={xName}
|
||||||
|
tick={{ fill: 'var(--theme-grey)' }}
|
||||||
|
/>
|
||||||
|
<YAxis tickFormatter={formatSats} tick={{ fill: 'var(--theme-grey)' }} />
|
||||||
|
<Tooltip labelFormatter={dateFormatter} contentStyle={{ color: 'var(--theme-color)', backgroundColor: 'var(--theme-body)' }} />
|
||||||
|
<Legend />
|
||||||
|
<Line type='monotone' dataKey='num' name={yName} stroke='var(--secondary)' />
|
||||||
|
</LineChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
import { gql } from '@apollo/client'
|
||||||
|
import { getGetServerSideProps } from '../../api/ssrApollo'
|
||||||
|
import Layout from '../../components/layout'
|
||||||
|
import { Tooltip, ResponsiveContainer, PieChart, Pie, Cell } from 'recharts'
|
||||||
|
import { Col, Row } from 'react-bootstrap'
|
||||||
|
import { UsageHeader } from '../../components/usage-header'
|
||||||
|
|
||||||
|
export const getServerSideProps = getGetServerSideProps(
|
||||||
|
gql`
|
||||||
|
{
|
||||||
|
registrationsWeekly
|
||||||
|
activeWeekly
|
||||||
|
earnersWeekly
|
||||||
|
itemsWeekly {
|
||||||
|
name
|
||||||
|
value
|
||||||
|
}
|
||||||
|
spentWeekly {
|
||||||
|
name
|
||||||
|
value
|
||||||
|
}
|
||||||
|
stackedWeekly {
|
||||||
|
name
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
export default function Growth ({
|
||||||
|
data: {
|
||||||
|
registrationsWeekly, activeWeekly, itemsWeekly, spentWeekly,
|
||||||
|
stackedWeekly, earnersWeekly
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<UsageHeader />
|
||||||
|
<Row>
|
||||||
|
<Col className='mt-3'>
|
||||||
|
<div className='text-center text-muted font-weight-bold'>registrations</div>
|
||||||
|
<h3 className='text-center'>{registrationsWeekly}</h3>
|
||||||
|
</Col>
|
||||||
|
<Col className='mt-3'>
|
||||||
|
<div className='text-center text-muted font-weight-bold'>interactive users</div>
|
||||||
|
<h3 className='text-center'>{activeWeekly}</h3>
|
||||||
|
</Col>
|
||||||
|
<Col className='mt-3'>
|
||||||
|
<div className='text-center text-muted font-weight-bold'>earners</div>
|
||||||
|
<h3 className='text-center'>{earnersWeekly}</h3>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Col className='mt-3'>
|
||||||
|
<div className='text-center text-muted font-weight-bold'>items</div>
|
||||||
|
<GrowthPieChart data={itemsWeekly} />
|
||||||
|
</Col>
|
||||||
|
<Col className='mt-3'>
|
||||||
|
<div className='text-center text-muted font-weight-bold'>stacked</div>
|
||||||
|
<GrowthPieChart data={stackedWeekly} />
|
||||||
|
</Col>
|
||||||
|
<Col className='mt-3'>
|
||||||
|
<div className='text-center text-muted font-weight-bold'>spent</div>
|
||||||
|
<GrowthPieChart data={spentWeekly} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Layout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const COLORS = [
|
||||||
|
'var(--secondary)',
|
||||||
|
'var(--info)',
|
||||||
|
'var(--success)',
|
||||||
|
'var(--boost)',
|
||||||
|
'var(--grey)'
|
||||||
|
]
|
||||||
|
|
||||||
|
function GrowthPieChart ({ data }) {
|
||||||
|
return (
|
||||||
|
<ResponsiveContainer width='100%' height={250} minWidth={200}>
|
||||||
|
<PieChart margin={{ top: 5, right: 5, bottom: 5, left: 5 }}>
|
||||||
|
<Pie
|
||||||
|
dataKey='value'
|
||||||
|
isAnimationActive={false}
|
||||||
|
data={data}
|
||||||
|
cx='50%'
|
||||||
|
cy='50%'
|
||||||
|
outerRadius={80}
|
||||||
|
fill='var(--secondary)'
|
||||||
|
label
|
||||||
|
>
|
||||||
|
{
|
||||||
|
data.map((entry, index) => (
|
||||||
|
<Cell key={`cell-${index}`} fill={COLORS[index]} />
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</Pie>
|
||||||
|
<Tooltip />
|
||||||
|
</PieChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue