1
0
Fork 0
freesewing/packages/react/components/Stats/index.mjs

166 lines
4.7 KiB
JavaScript
Raw Normal View History

// Dependencies
import { linkClasses, formatNumber, orderBy, clone } from '@freesewing/utils'
// Hooks
import React, { useState, useEffect } from 'react'
import { useBackend } from '@freesewing/react/hooks/useBackend'
// Components
import { Spinner } from '@freesewing/react/components/Spinner'
import { Link as WebLink } from '@freesewing/react/components/Link'
import { ChartWrapper } from '@freesewing/react/components/Echart'
const meta = {
title: 'FreeSewing by numbers',
description: 'Some high-level numbers about Freesewing',
}
const option = {
tooltip: {
trigger: 'axis',
show: true,
axisPointer: {
type: 'line',
lineStyle: {
type: 'dashed',
},
},
},
title: {
left: 'center',
},
grid: {
left: '40',
right: '60',
containLabel: true,
},
toolbox: {
feature: {
saveAsImage: {},
magicType: {
type: ['line', 'bar'],
},
},
},
yAxis: {
type: 'value',
},
}
export const Stats = ({ Link = false }) => {
if (!Link) Link = WebLink
const [stats, setStats] = useState()
const [error, setError] = useState(false)
const backend = useBackend()
useEffect(() => {
getStats(backend, setStats, setError)
}, [])
if (!stats)
return (
<div className="tw-text-center tw-mt-12 tw-w-full">
<Spinner />
</div>
)
const designTop = orderBy(
Object.entries(stats.designs).map(([design, count]) => ({ design, count })),
'count',
'desc'
).slice(0, 50)
const optionD = clone(option)
optionD.title.text = 'Top 50 FreeSewing Designs'
optionD.xAxis = {
type: 'category',
data: designTop.map((entry) => entry.design),
name: 'Design',
}
optionD.series = [
{
data: designTop.map((entry) => entry.count),
type: 'bar',
},
]
const optionU = clone(option)
optionU.title.text = 'Top 25 FreeSewing Users'
optionU.xAxis = {
type: 'category',
data: stats.topUsers.map((user) => user.username),
name: 'User',
}
optionU.series = [
{
data: stats.topUsers.map((user) => user.calls),
type: 'bar',
},
]
return (
<>
<div className="tw-max-w-7xl tw-mx-auto tw-my-12 tw-px-4">
<div className="tw-grid tw-grid-cols-1 md:tw-grid-cols-2 tw-gap-2">
<Stat title="Users" value={stats.user} />
<Stat title="Patterns" value={stats.pattern} />
<Stat title="Measurements Sets" value={stats.set} />
<Stat title="Curated Sets" value={stats.curatedSet} />
<Stat title="Bookmarks" value={stats.bookmark} />
<Stat title="API Keys" value={stats.apikey} />
<Stat title="JWT Calls" value={stats.activity.jwt} desc="Total Number Seen" />
<Stat title="API Key Calls" value={stats.activity.key} desc="Total Number Seen" />
</div>
<h2>Top Users</h2>
<ChartWrapper option={optionU} />
<small className="tw-ml-4 tw-py-1 tw-opacity-80">
<b>Note:</b> Ordered by JWT calls made to the FreeSewing backend
</small>
<div className="tw-max-h-96 tw-overflow-scroll">
<ol className="tw-list tw-list-inside tw-list-decimal tw-ml-4">
{stats.topUsers.map((u) => (
<li key={u.id}>
<Link href={`/users/?id=${u.id}`} className={linkClasses}>
{u.username}
</Link>
: {formatNumber(u.calls)}
</li>
))}
</ol>
</div>
<h2>Top Designs</h2>
<ChartWrapper option={optionD} />
<small className="tw-ml-4 tw-py-1 tw-opacity-80">
<b>Note:</b> Ordered by patterns stored in the FreeSewing backend
</small>
<div className="tw-max-h-96 tw-overflow-scroll">
<ol className="tw-list tw-list-inside tw-list-decimal tw-ml-4">
{Object.entries(stats.designs).map(([d, c]) => (
<li key={d}>
<Link href={`/designs/${d}`} className={linkClasses}>
<span className="tw-capitalize">{d}</span>
</Link>
: {formatNumber(c)}
</li>
))}
</ol>
</div>
</div>
</>
)
}
const Stat = ({ title, value, desc = 'Total Number Stored' }) => (
<div className="tw-daisy-stats tw-shadow">
<div className="tw-daisy-stat">
<div className="tw-daisy-stat-title">{title}</div>
<div className="tw-daisy-stat-value">{formatNumber(value)}</div>
<div className="tw-daisy-stat-desc">{desc}</div>
</div>
</div>
)
const getStats = async (backend, setStats, setError) => {
const result = await backend.getStats()
if (result[0] === 200 && result[1]) setStats(result[1])
else setError(true)
}