user stats
This commit is contained in:
		
							parent
							
								
									2c749dd07f
								
							
						
					
					
						commit
						0b3b690c10
					
				@ -20,21 +20,30 @@ export default {
 | 
			
		||||
    },
 | 
			
		||||
    itemGrowth: async (parent, args, { models }) => {
 | 
			
		||||
      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"
 | 
			
		||||
        WHERE date_trunc('month', now_utc()) <> date_trunc('month', created_at)
 | 
			
		||||
        GROUP BY time
 | 
			
		||||
        ORDER BY time ASC`)
 | 
			
		||||
    },
 | 
			
		||||
    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(
 | 
			
		||||
        `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"
 | 
			
		||||
        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
 | 
			
		||||
        ORDER BY time ASC`)
 | 
			
		||||
    },
 | 
			
		||||
    earnedGrowth: async (parent, args, { models }) => {
 | 
			
		||||
    earnerGrowth: async (parent, args, { models }) => {
 | 
			
		||||
      return await models.$queryRaw(
 | 
			
		||||
        `SELECT time, count(distinct user_id) as num
 | 
			
		||||
        FROM
 | 
			
		||||
@ -48,6 +57,101 @@ export default {
 | 
			
		||||
          WHERE date_trunc('month', now_utc()) <> date_trunc('month', created_at))) u
 | 
			
		||||
        GROUP BY time
 | 
			
		||||
        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 {
 | 
			
		||||
    registrationGrowth: [TimeNum!]!
 | 
			
		||||
    activeGrowth: [TimeNum!]!
 | 
			
		||||
    itemGrowth: [TimeNum!]!
 | 
			
		||||
    spentGrowth: [TimeNum!]!
 | 
			
		||||
    earnedGrowth: [TimeNum!]!
 | 
			
		||||
    itemGrowth: [ItemGrowth!]!
 | 
			
		||||
    spentGrowth: [SpentGrowth!]!
 | 
			
		||||
    stackedGrowth: [StackedGrowth!]!
 | 
			
		||||
    earnerGrowth: [TimeNum!]!
 | 
			
		||||
 | 
			
		||||
    registrationsWeekly: Int!
 | 
			
		||||
    activeWeekly: Int!
 | 
			
		||||
    earnersWeekly: Int!
 | 
			
		||||
    itemsWeekly: [NameValue!]!
 | 
			
		||||
    spentWeekly: [NameValue!]!
 | 
			
		||||
    stackedWeekly: [NameValue!]!
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  type TimeNum {
 | 
			
		||||
    time: String!
 | 
			
		||||
    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
 | 
			
		||||
      </a>
 | 
			
		||||
      <span className='mx-2 text-dark'> \ </span>
 | 
			
		||||
      <Link href='/usage' passHref>
 | 
			
		||||
      <Link href='/users/forever' passHref>
 | 
			
		||||
        <a className='text-dark d-inline-flex'>
 | 
			
		||||
          usage
 | 
			
		||||
          users
 | 
			
		||||
        </a>
 | 
			
		||||
      </Link>
 | 
			
		||||
    </Popover.Content>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										35
									
								
								components/usage-header.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								components/usage-header.js
									
									
									
									
									
										Normal file
									
								
							@ -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>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										147
									
								
								pages/users/forever.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								pages/users/forever.js
									
									
									
									
									
										Normal file
									
								
							@ -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>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										101
									
								
								pages/users/week.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								pages/users/week.js
									
									
									
									
									
										Normal file
									
								
							@ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user