mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 00:03:33 +10:00
feat: show monthly new users
This commit is contained in:
@ -19,7 +19,8 @@
|
||||
"@hookform/resolvers": "^3.1.0",
|
||||
"contentlayer": "^0.3.4",
|
||||
"framer-motion": "^10.12.8",
|
||||
"lucide-react": "^0.277.0",
|
||||
"lucide-react": "^0.279.0",
|
||||
"luxon": "^3.4.0",
|
||||
"micro": "^10.0.1",
|
||||
"next": "14.0.0",
|
||||
"next-auth": "4.24.3",
|
||||
|
||||
@ -40,9 +40,9 @@ export const BarMetric = <T extends Record<string, Record<keyof T[string], unkno
|
||||
<span>{extraInfo}</span>
|
||||
</div>
|
||||
|
||||
<div className="border-border mt-2.5 flex flex-1 items-center justify-center rounded-2xl border pr-2 shadow-sm hover:shadow">
|
||||
<div className="border-border mt-2.5 flex flex-1 items-center justify-center rounded-2xl border p-6 pl-2 pt-12 shadow-sm hover:shadow">
|
||||
<ResponsiveContainer width="100%" height={chartHeight}>
|
||||
<BarChart data={formattedData} margin={{ top: 30, right: 20 }}>
|
||||
<BarChart data={formattedData}>
|
||||
<XAxis dataKey="month" />
|
||||
<YAxis />
|
||||
<Tooltip
|
||||
@ -55,7 +55,13 @@ export const BarMetric = <T extends Record<string, Record<keyof T[string], unkno
|
||||
formatter={(value) => [Number(value), label]}
|
||||
cursor={{ fill: 'hsl(var(--primary) / 10%)' }}
|
||||
/>
|
||||
<Bar dataKey={metricKey as string} fill="hsl(var(--primary))" label={label} />{' '}
|
||||
<Bar
|
||||
dataKey={metricKey as string}
|
||||
maxBarSize={60}
|
||||
fill="hsl(var(--primary))"
|
||||
label={label}
|
||||
radius={[4, 4, 0, 0]}
|
||||
/>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
|
||||
@ -21,7 +21,7 @@ export const FundingRaised = ({ className, data, ...props }: FundingRaisedProps)
|
||||
<div className={cn('flex flex-col', className)} {...props}>
|
||||
<h3 className="px-4 text-lg font-semibold">Total Funding Raised</h3>
|
||||
|
||||
<div className="border-border mt-2.5 flex flex-1 flex-col items-center justify-center rounded-2xl border p-4 shadow-sm hover:shadow">
|
||||
<div className="border-border mt-2.5 flex flex-1 flex-col items-center justify-center rounded-2xl border p-6 pl-2 pt-12 shadow-sm hover:shadow">
|
||||
<ResponsiveContainer width="100%" height={400}>
|
||||
<BarChart data={formattedData} margin={{ top: 40, right: 40, bottom: 20, left: 40 }}>
|
||||
<XAxis dataKey="date" />
|
||||
@ -51,7 +51,13 @@ export const FundingRaised = ({ className, data, ...props }: FundingRaisedProps)
|
||||
]}
|
||||
cursor={{ fill: 'hsl(var(--primary) / 10%)' }}
|
||||
/>
|
||||
<Bar dataKey="amount" fill="hsl(var(--primary))" label="Amount Raised" />
|
||||
<Bar
|
||||
dataKey="amount"
|
||||
fill="hsl(var(--primary))"
|
||||
label="Amount Raised"
|
||||
maxBarSize={60}
|
||||
radius={[4, 4, 0, 0]}
|
||||
/>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
'use client';
|
||||
|
||||
import { DateTime } from 'luxon';
|
||||
import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';
|
||||
|
||||
import { GetUserMonthlyGrowthResult } from '@documenso/lib/server-only/user/get-user-monthly-growth';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
|
||||
export type MonthlyUsersChartProps = {
|
||||
className?: string;
|
||||
data: GetUserMonthlyGrowthResult;
|
||||
};
|
||||
|
||||
export const MonthlyUsersChart = ({ className, data }: MonthlyUsersChartProps) => {
|
||||
const formattedData = [...data].reverse().map(({ month, count }) => {
|
||||
return {
|
||||
month: DateTime.fromFormat(month, 'yyyy-MM').toFormat('LLL'),
|
||||
count: Number(count),
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={cn('flex flex-col', className)}>
|
||||
<div className="flex items-center px-4">
|
||||
<h3 className="text-lg font-semibold">Monthly New Users</h3>
|
||||
</div>
|
||||
|
||||
<div className="border-border mt-2.5 flex flex-1 items-center justify-center rounded-2xl border p-6 pl-2 pt-12 shadow-sm hover:shadow">
|
||||
<ResponsiveContainer width="100%" height={400}>
|
||||
<BarChart data={formattedData}>
|
||||
<XAxis dataKey="month" />
|
||||
<YAxis />
|
||||
|
||||
<Tooltip
|
||||
formatter={(value) => [Number(value).toLocaleString('en-US'), 'Total Users']}
|
||||
cursor={{ fill: 'hsl(var(--primary) / 10%)' }}
|
||||
/>
|
||||
|
||||
<Bar
|
||||
dataKey="count"
|
||||
fill="hsl(var(--primary))"
|
||||
radius={[4, 4, 0, 0]}
|
||||
maxBarSize={60}
|
||||
label="New Users"
|
||||
/>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -1,5 +1,7 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { getUserMonthlyGrowth } from '@documenso/lib/server-only/user/get-user-monthly-growth';
|
||||
|
||||
import { FUNDING_RAISED } from '~/app/(marketing)/open/data';
|
||||
import { MetricCard } from '~/app/(marketing)/open/metric-card';
|
||||
import { SalaryBands } from '~/app/(marketing)/open/salary-bands';
|
||||
@ -7,11 +9,14 @@ import { SalaryBands } from '~/app/(marketing)/open/salary-bands';
|
||||
import { BarMetric } from './bar-metrics';
|
||||
import { CapTable } from './cap-table';
|
||||
import { FundingRaised } from './funding-raised';
|
||||
import { MonthlyUsersChart } from './monthly-users-chart';
|
||||
import { TeamMembers } from './team-members';
|
||||
import { OpenPageTooltip } from './tooltip';
|
||||
|
||||
export const revalidate = 3600;
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
const ZGithubStatsResponse = z.object({
|
||||
stargazers_count: z.number(),
|
||||
forks_count: z.number(),
|
||||
@ -43,14 +48,20 @@ export type StargazersType = z.infer<typeof ZStargazersLiveResponse>;
|
||||
export type EarlyAdoptersType = z.infer<typeof ZEarlyAdoptersResponse>;
|
||||
|
||||
export default async function OpenPage() {
|
||||
const GITHUB_HEADERS: Record<string, string> = {
|
||||
accept: 'application/vnd.github.v3+json',
|
||||
};
|
||||
|
||||
if (process.env.NEXT_PRIVATE_GITHUB_TOKEN) {
|
||||
GITHUB_HEADERS.authorization = `Bearer ${process.env.NEXT_PRIVATE_GITHUB_TOKEN}`;
|
||||
}
|
||||
|
||||
const {
|
||||
forks_count: forksCount,
|
||||
open_issues: openIssues,
|
||||
stargazers_count: stargazersCount,
|
||||
} = await fetch('https://api.github.com/repos/documenso/documenso', {
|
||||
headers: {
|
||||
accept: 'application/vnd.github.v3+json',
|
||||
},
|
||||
headers: GITHUB_HEADERS,
|
||||
})
|
||||
.then(async (res) => res.json())
|
||||
.then((res) => ZGithubStatsResponse.parse(res));
|
||||
@ -58,9 +69,7 @@ export default async function OpenPage() {
|
||||
const { total_count: mergedPullRequests } = await fetch(
|
||||
'https://api.github.com/search/issues?q=repo:documenso/documenso/+is:pr+merged:>=2010-01-01&page=0&per_page=1',
|
||||
{
|
||||
headers: {
|
||||
accept: 'application/vnd.github.v3+json',
|
||||
},
|
||||
headers: GITHUB_HEADERS,
|
||||
},
|
||||
)
|
||||
.then(async (res) => res.json())
|
||||
@ -82,6 +91,8 @@ export default async function OpenPage() {
|
||||
.then(async (res) => res.json())
|
||||
.then((res) => ZEarlyAdoptersResponse.parse(res));
|
||||
|
||||
const MONTHLY_USERS = await getUserMonthlyGrowth();
|
||||
|
||||
return (
|
||||
<div className="mx-auto mt-6 max-w-screen-lg sm:mt-12">
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
@ -122,7 +133,7 @@ export default async function OpenPage() {
|
||||
|
||||
<TeamMembers className="col-span-12" />
|
||||
|
||||
<SalaryBands className="col-span-12 lg:col-span-6" />
|
||||
<SalaryBands className="col-span-12" />
|
||||
|
||||
<FundingRaised data={FUNDING_RAISED} className="col-span-12 lg:col-span-6" />
|
||||
|
||||
@ -172,6 +183,8 @@ export default async function OpenPage() {
|
||||
className="col-span-12 lg:col-span-6"
|
||||
/>
|
||||
|
||||
<MonthlyUsersChart data={MONTHLY_USERS} className="col-span-12 lg:col-span-6" />
|
||||
|
||||
<div className="col-span-12 mt-12 flex flex-col items-center justify-center">
|
||||
<h2 className="text-2xl font-bold">Where's the rest?</h2>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user