-
-
Notifications
You must be signed in to change notification settings - Fork 120
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add territory to analytics selectors * implement territory analytics, revert user satistics header * fix linting errors * disallow some territory names * fix linting error * minor adjustments to header * escape input * 404 on non-existant sub * exclude unused queries depending on sub select --------- Co-authored-by: Keyan <[email protected]> Co-authored-by: k00b <[email protected]>
- Loading branch information
1 parent
5de9d92
commit 73170ba
Showing
10 changed files
with
280 additions
and
122 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { useRouter } from 'next/router' | ||
import { Select, DatePicker } from './form' | ||
import { useSubs } from './sub-select' | ||
import { WHENS } from '@/lib/constants' | ||
import { whenToFrom } from '@/lib/time' | ||
import styles from './sub-select.module.css' | ||
import classNames from 'classnames' | ||
|
||
export function SubAnalyticsHeader ({ pathname = null }) { | ||
const router = useRouter() | ||
|
||
const path = pathname || 'stackers' | ||
|
||
const select = async values => { | ||
const { sub, when, ...query } = values | ||
|
||
if (when !== 'custom') { delete query.from; delete query.to } | ||
if (query.from && !query.to) return | ||
|
||
await router.push({ | ||
|
||
pathname: `/${path}/${sub}/${when}`, | ||
query | ||
}) | ||
} | ||
|
||
const when = router.query.when || 'day' | ||
const sub = router.query.sub || 'all' | ||
|
||
const subs = useSubs({ prependSubs: ['all'], sub, appendSubs: [], filterSubs: () => true }) | ||
|
||
return ( | ||
<div className='text-muted fw-bold my-0 d-flex align-items-center flex-wrap'> | ||
<div className='text-muted fw-bold mb-2 d-flex align-items-center'> | ||
stacker analytics in | ||
<Select | ||
groupClassName='mb-0 mx-2' | ||
className={classNames(styles.subSelect, styles.subSelectSmall)} | ||
name='sub' | ||
size='sm' | ||
items={subs} | ||
value={sub} | ||
noForm | ||
onChange={(formik, e) => { | ||
const range = when === 'custom' ? { from: router.query.from, to: router.query.to } : {} | ||
select({ sub: e.target.value, when, ...range }) | ||
}} | ||
/> | ||
for | ||
<Select | ||
groupClassName='mb-0 mx-2' | ||
className='w-auto' | ||
name='when' | ||
size='sm' | ||
items={WHENS} | ||
value={when} | ||
noForm | ||
onChange={(formik, e) => { | ||
const range = e.target.value === 'custom' ? { from: whenToFrom(when), to: Date.now() } : {} | ||
select({ sub, when: e.target.value, ...range }) | ||
}} | ||
/> | ||
</div> | ||
{when === 'custom' && | ||
<DatePicker | ||
noForm | ||
fromName='from' | ||
toName='to' | ||
className='p-0 px-2 mb-0' | ||
onChange={(formik, [from, to], e) => { | ||
select({ sub, when, from: from.getTime(), to: to.getTime() }) | ||
}} | ||
from={router.query.from} | ||
to={router.query.to} | ||
when={when} | ||
/>} | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
import { gql, useQuery } from '@apollo/client' | ||
import { getGetServerSideProps } from '@/api/ssrApollo' | ||
import Layout from '@/components/layout' | ||
import Col from 'react-bootstrap/Col' | ||
import Row from 'react-bootstrap/Row' | ||
import { SubAnalyticsHeader } from '@/components/sub-analytics-header' | ||
import { useRouter } from 'next/router' | ||
import dynamic from 'next/dynamic' | ||
import PageLoading from '@/components/page-loading' | ||
import { WhenAreaChartSkeleton, WhenComposedChartSkeleton, WhenLineChartSkeleton } from '@/components/charts-skeletons' | ||
|
||
const WhenAreaChart = dynamic(() => import('@/components/charts').then(mod => mod.WhenAreaChart), { | ||
loading: () => <WhenAreaChartSkeleton /> | ||
}) | ||
const WhenLineChart = dynamic(() => import('@/components/charts').then(mod => mod.WhenLineChart), { | ||
loading: () => <WhenLineChartSkeleton /> | ||
}) | ||
const WhenComposedChart = dynamic(() => import('@/components/charts').then(mod => mod.WhenComposedChart), { | ||
loading: () => <WhenComposedChartSkeleton /> | ||
}) | ||
|
||
const GROWTH_QUERY = gql` | ||
query Growth($when: String!, $from: String, $to: String, $sub: String, $subSelect: Boolean = false) | ||
{ | ||
registrationGrowth(when: $when, from: $from, to: $to) @skip(if: $subSelect) { | ||
time | ||
data { | ||
name | ||
value | ||
} | ||
} | ||
itemGrowth(when: $when, from: $from, to: $to) @skip(if: $subSelect) { | ||
time | ||
data { | ||
name | ||
value | ||
} | ||
} | ||
spendingGrowth(when: $when, from: $from, to: $to) @skip(if: $subSelect) { | ||
time | ||
data { | ||
name | ||
value | ||
} | ||
} | ||
spenderGrowth(when: $when, from: $from, to: $to) @skip(if: $subSelect) { | ||
time | ||
data { | ||
name | ||
value | ||
} | ||
} | ||
stackingGrowth(when: $when, from: $from, to: $to) @skip(if: $subSelect) { | ||
time | ||
data { | ||
name | ||
value | ||
} | ||
} | ||
stackerGrowth(when: $when, from: $from, to: $to) @skip(if: $subSelect) { | ||
time | ||
data { | ||
name | ||
value | ||
} | ||
} | ||
itemGrowthSubs(when: $when, from: $from, to: $to, sub: $sub) @include(if: $subSelect) { | ||
time | ||
data { | ||
name | ||
value | ||
} | ||
} | ||
revenueGrowthSubs(when: $when, from: $from, to: $to, sub: $sub) @include(if: $subSelect) { | ||
time | ||
data { | ||
name | ||
value | ||
} | ||
} | ||
}` | ||
|
||
const variablesFunc = vars => ({ ...vars, subSelect: vars.sub !== 'all' }) | ||
export const getServerSideProps = getGetServerSideProps({ query: GROWTH_QUERY, variables: variablesFunc }) | ||
|
||
export default function Growth ({ ssrData }) { | ||
const router = useRouter() | ||
const { when, from, to, sub } = router.query | ||
|
||
const { data } = useQuery(GROWTH_QUERY, { variables: { when, from, to, sub, subSelect: sub !== 'all' } }) | ||
if (!data && !ssrData) return <PageLoading /> | ||
|
||
const { | ||
registrationGrowth, | ||
itemGrowth, | ||
spendingGrowth, | ||
spenderGrowth, | ||
stackingGrowth, | ||
stackerGrowth, | ||
itemGrowthSubs, | ||
revenueGrowthSubs | ||
} = data || ssrData | ||
|
||
if (sub === 'all') { | ||
return ( | ||
<Layout> | ||
<SubAnalyticsHeader /> | ||
<Row> | ||
<Col className='mt-3'> | ||
<div className='text-center text-muted fw-bold'>stackers</div> | ||
<WhenLineChart data={stackerGrowth} /> | ||
</Col> | ||
<Col className='mt-3'> | ||
<div className='text-center text-muted fw-bold'>stacking</div> | ||
<WhenAreaChart data={stackingGrowth} /> | ||
</Col> | ||
</Row> | ||
<Row> | ||
<Col className='mt-3'> | ||
<div className='text-center text-muted fw-bold'>spenders</div> | ||
<WhenLineChart data={spenderGrowth} /> | ||
</Col> | ||
<Col className='mt-3'> | ||
<div className='text-center text-muted fw-bold'>spending</div> | ||
<WhenAreaChart data={spendingGrowth} /> | ||
</Col> | ||
</Row> | ||
<Row> | ||
<Col className='mt-3'> | ||
<div className='text-center text-muted fw-bold'>registrations</div> | ||
<WhenAreaChart data={registrationGrowth} /> | ||
</Col> | ||
<Col className='mt-3'> | ||
<div className='text-center text-muted fw-bold'>items</div> | ||
<WhenComposedChart data={itemGrowth} areaNames={['posts', 'comments', 'jobs']} areaAxis='left' lineNames={['comments/posts', 'territories']} lineAxis='right' barNames={['zaps']} /> | ||
</Col> | ||
</Row> | ||
</Layout> | ||
) | ||
} else { | ||
return ( | ||
<Layout> | ||
<SubAnalyticsHeader /> | ||
<Row> | ||
<Col className='mt-3'> | ||
<div className='text-center text-muted fw-bold'>items</div> | ||
<WhenLineChart data={itemGrowthSubs} /> | ||
</Col> | ||
<Col className='mt-3'> | ||
<div className='text-center text-muted fw-bold'>sats</div> | ||
<WhenLineChart data={revenueGrowthSubs} /> | ||
</Col> | ||
</Row> | ||
</Layout> | ||
) | ||
} | ||
} |
Oops, something went wrong.