From c3d52c68e7cc1c10aaabb9e2283cf88df1ed217f Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Fri, 22 Sep 2023 13:27:10 +0000 Subject: [PATCH 1/3] feat: add early adopters graph --- .../open/early-adopter-metrics.tsx | 59 +++++++++++++++++++ .../src/app/(marketing)/open/gh-metrics.tsx | 2 +- .../src/app/(marketing)/open/page.tsx | 33 ++++++++++- 3 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 apps/marketing/src/app/(marketing)/open/early-adopter-metrics.tsx diff --git a/apps/marketing/src/app/(marketing)/open/early-adopter-metrics.tsx b/apps/marketing/src/app/(marketing)/open/early-adopter-metrics.tsx new file mode 100644 index 000000000..b84deea37 --- /dev/null +++ b/apps/marketing/src/app/(marketing)/open/early-adopter-metrics.tsx @@ -0,0 +1,59 @@ +'use client'; + +import { HTMLAttributes } from 'react'; + +import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts'; + +import { formatMonth } from '@documenso/lib/client-only/format-month'; +import { cn } from '@documenso/ui/lib/utils'; + +import { EarlyAdoptersType } from './page'; + +export type MetricsDataKey = keyof Omit; +export type EarlyAdopterMetricsProps = HTMLAttributes & { + data: EarlyAdoptersType; + metricKey: MetricsDataKey; + title: string; + label: string; + chartHeight?: number; +}; + +export const EarlyAdopterMetrics = ({ + className, + data, + metricKey, + title, + label, + chartHeight = 400, + ...props +}: EarlyAdopterMetricsProps) => { + const formattedData = Object.keys(data) + .map((key) => ({ + month: formatMonth(key), + [metricKey]: data[key][metricKey], + })) + .reverse(); + + return ( +
+

{title}

+ +
+ + + + + [Number(value), label]} + cursor={{ fill: 'hsl(var(--primary) / 10%)' }} + /> + + + +
+
+ ); +}; diff --git a/apps/marketing/src/app/(marketing)/open/gh-metrics.tsx b/apps/marketing/src/app/(marketing)/open/gh-metrics.tsx index 5c0c79716..357c110c2 100644 --- a/apps/marketing/src/app/(marketing)/open/gh-metrics.tsx +++ b/apps/marketing/src/app/(marketing)/open/gh-metrics.tsx @@ -9,7 +9,7 @@ import { cn } from '@documenso/ui/lib/utils'; import { StargazersType } from './page'; -export type MetricsDataKey = 'stars' | 'forks' | 'mergedPRs' | 'openIssues'; +export type MetricsDataKey = keyof StargazersType[string]; export type GithubMetricProps = HTMLAttributes & { data: StargazersType; metricKey: MetricsDataKey; diff --git a/apps/marketing/src/app/(marketing)/open/page.tsx b/apps/marketing/src/app/(marketing)/open/page.tsx index 2d5bc2aa4..1740a65ad 100644 --- a/apps/marketing/src/app/(marketing)/open/page.tsx +++ b/apps/marketing/src/app/(marketing)/open/page.tsx @@ -4,6 +4,7 @@ import { MetricCard } from '~/app/(marketing)/open/metric-card'; import { SalaryBands } from '~/app/(marketing)/open/salary-bands'; import { CapTable } from './cap-table'; +import { EarlyAdopterMetrics } from './early-adopter-metrics'; import { FundingRaised } from './funding-raised'; import { GithubMetric } from './gh-metrics'; import { TeamMembers } from './team-members'; @@ -29,7 +30,16 @@ const ZStargazersLiveResponse = z.record( }), ); +const ZEarlyAdoptersResponse = z.record( + z.object({ + id: z.number(), + time: z.string().datetime(), + earlyAdopters: z.number(), + }), +); + export type StargazersType = z.infer; +export type EarlyAdoptersType = z.infer; // const ZOpenPullRequestsResponse = ZMergedPullRequestsResponse; @@ -65,6 +75,14 @@ export default async function OpenPage() { .then(async (res) => res.json()) .then((res) => ZStargazersLiveResponse.parse(res)); + const EARLY_ADOPTERS_DATA = await fetch('https://stargrazer-live.onrender.com/api/stats/stripe', { + headers: { + accept: 'application/json', + }, + }) + .then(async (res) => res.json()) + .then((res) => ZEarlyAdoptersResponse.parse(res)); + return (
@@ -110,6 +128,15 @@ export default async function OpenPage() { + + +
From eef63c9adcc6869665451ad94a7225ca3d29d63f Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Fri, 22 Sep 2023 14:08:25 +0000 Subject: [PATCH 2/3] refactor: metrics into reusable component --- .../open/{gh-metrics.tsx => bar-metrics.tsx} | 15 ++--- .../open/early-adopter-metrics.tsx | 59 ------------------- .../app/(marketing)/open/funding-raised.tsx | 12 ++-- .../src/app/(marketing)/open/page.tsx | 20 +++---- 4 files changed, 22 insertions(+), 84 deletions(-) rename apps/marketing/src/app/(marketing)/open/{gh-metrics.tsx => bar-metrics.tsx} (79%) delete mode 100644 apps/marketing/src/app/(marketing)/open/early-adopter-metrics.tsx diff --git a/apps/marketing/src/app/(marketing)/open/gh-metrics.tsx b/apps/marketing/src/app/(marketing)/open/bar-metrics.tsx similarity index 79% rename from apps/marketing/src/app/(marketing)/open/gh-metrics.tsx rename to apps/marketing/src/app/(marketing)/open/bar-metrics.tsx index 357c110c2..ec677ec83 100644 --- a/apps/marketing/src/app/(marketing)/open/gh-metrics.tsx +++ b/apps/marketing/src/app/(marketing)/open/bar-metrics.tsx @@ -7,18 +7,15 @@ import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recha import { formatMonth } from '@documenso/lib/client-only/format-month'; import { cn } from '@documenso/ui/lib/utils'; -import { StargazersType } from './page'; - -export type MetricsDataKey = keyof StargazersType[string]; -export type GithubMetricProps = HTMLAttributes & { - data: StargazersType; - metricKey: MetricsDataKey; +export type BarMetricProps> = HTMLAttributes & { + data: T; + metricKey: keyof T[string]; title: string; label: string; chartHeight?: number; }; -export const GithubMetric = ({ +export const BarMetric = >>({ className, data, metricKey, @@ -26,7 +23,7 @@ export const GithubMetric = ({ label, chartHeight = 400, ...props -}: GithubMetricProps) => { +}: BarMetricProps) => { const formattedData = Object.keys(data) .map((key) => ({ month: formatMonth(key), @@ -50,7 +47,7 @@ export const GithubMetric = ({ formatter={(value) => [Number(value), label]} cursor={{ fill: 'hsl(var(--primary) / 10%)' }} /> - + {' '}
diff --git a/apps/marketing/src/app/(marketing)/open/early-adopter-metrics.tsx b/apps/marketing/src/app/(marketing)/open/early-adopter-metrics.tsx deleted file mode 100644 index b84deea37..000000000 --- a/apps/marketing/src/app/(marketing)/open/early-adopter-metrics.tsx +++ /dev/null @@ -1,59 +0,0 @@ -'use client'; - -import { HTMLAttributes } from 'react'; - -import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts'; - -import { formatMonth } from '@documenso/lib/client-only/format-month'; -import { cn } from '@documenso/ui/lib/utils'; - -import { EarlyAdoptersType } from './page'; - -export type MetricsDataKey = keyof Omit; -export type EarlyAdopterMetricsProps = HTMLAttributes & { - data: EarlyAdoptersType; - metricKey: MetricsDataKey; - title: string; - label: string; - chartHeight?: number; -}; - -export const EarlyAdopterMetrics = ({ - className, - data, - metricKey, - title, - label, - chartHeight = 400, - ...props -}: EarlyAdopterMetricsProps) => { - const formattedData = Object.keys(data) - .map((key) => ({ - month: formatMonth(key), - [metricKey]: data[key][metricKey], - })) - .reverse(); - - return ( -
-

{title}

- -
- - - - - [Number(value), label]} - cursor={{ fill: 'hsl(var(--primary) / 10%)' }} - /> - - - -
-
- ); -}; diff --git a/apps/marketing/src/app/(marketing)/open/funding-raised.tsx b/apps/marketing/src/app/(marketing)/open/funding-raised.tsx index c6601f547..51857bb06 100644 --- a/apps/marketing/src/app/(marketing)/open/funding-raised.tsx +++ b/apps/marketing/src/app/(marketing)/open/funding-raised.tsx @@ -7,14 +7,14 @@ import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recha import { formatMonth } from '@documenso/lib/client-only/format-month'; import { cn } from '@documenso/ui/lib/utils'; -import { FUNDING_RAISED } from '~/app/(marketing)/open/data'; +export type FundingRaisedProps = HTMLAttributes & { + data: Record[]; +}; -export type FundingRaisedProps = HTMLAttributes; - -export const FundingRaised = ({ className, ...props }: FundingRaisedProps) => { - const formattedData = FUNDING_RAISED.map((item) => ({ +export const FundingRaised = ({ className, data, ...props }: FundingRaisedProps) => { + const formattedData = data.map((item) => ({ amount: Number(item.amount), - date: formatMonth(item.date), + date: formatMonth(item.date as string), })); return ( diff --git a/apps/marketing/src/app/(marketing)/open/page.tsx b/apps/marketing/src/app/(marketing)/open/page.tsx index 1740a65ad..8a8281d53 100644 --- a/apps/marketing/src/app/(marketing)/open/page.tsx +++ b/apps/marketing/src/app/(marketing)/open/page.tsx @@ -1,12 +1,12 @@ import { z } from 'zod'; +import { FUNDING_RAISED } from '~/app/(marketing)/open/data'; import { MetricCard } from '~/app/(marketing)/open/metric-card'; import { SalaryBands } from '~/app/(marketing)/open/salary-bands'; +import { BarMetric } from './bar-metrics'; import { CapTable } from './cap-table'; -import { EarlyAdopterMetrics } from './early-adopter-metrics'; import { FundingRaised } from './funding-raised'; -import { GithubMetric } from './gh-metrics'; import { TeamMembers } from './team-members'; export const revalidate = 3600; @@ -41,8 +41,6 @@ const ZEarlyAdoptersResponse = z.record( export type StargazersType = z.infer; export type EarlyAdoptersType = z.infer; -// const ZOpenPullRequestsResponse = ZMergedPullRequestsResponse; - export default async function OpenPage() { const { forks_count: forksCount, @@ -125,11 +123,11 @@ export default async function OpenPage() { - + - data={EARLY_ADOPTERS_DATA} metricKey="earlyAdopters" title="Early Adopters" @@ -137,7 +135,7 @@ export default async function OpenPage() { className="col-span-12 lg:col-span-6" /> - data={STARGAZERS_DATA} metricKey="stars" title="Github: Total Stars" @@ -145,7 +143,7 @@ export default async function OpenPage() { className="col-span-12 lg:col-span-6" /> - data={STARGAZERS_DATA} metricKey="mergedPRs" title="Github: Total Merged PRs" @@ -153,7 +151,8 @@ export default async function OpenPage() { chartHeight={300} className="col-span-12 lg:col-span-6" /> - data={STARGAZERS_DATA} metricKey="forks" title="Github: Total Forks" @@ -161,7 +160,8 @@ export default async function OpenPage() { chartHeight={300} className="col-span-12 lg:col-span-6" /> - data={STARGAZERS_DATA} metricKey="openIssues" title="Github: Total Open Issues" From d0fad4775af1aeb2a9a907fc98eaedca24928fb7 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Sat, 23 Sep 2023 12:02:37 +0000 Subject: [PATCH 3/3] feat: add extra info for the early adopters --- .../src/app/(marketing)/open/bar-metrics.tsx | 7 +++- .../src/app/(marketing)/open/page.tsx | 2 + .../src/app/(marketing)/open/tooltip.tsx | 40 +++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 apps/marketing/src/app/(marketing)/open/tooltip.tsx diff --git a/apps/marketing/src/app/(marketing)/open/bar-metrics.tsx b/apps/marketing/src/app/(marketing)/open/bar-metrics.tsx index ec677ec83..42295c0bf 100644 --- a/apps/marketing/src/app/(marketing)/open/bar-metrics.tsx +++ b/apps/marketing/src/app/(marketing)/open/bar-metrics.tsx @@ -13,6 +13,7 @@ export type BarMetricProps> = HTMLAttributes>>({ @@ -22,6 +23,7 @@ export const BarMetric = ) => { const formattedData = Object.keys(data) @@ -33,7 +35,10 @@ export const BarMetric = -

{title}

+
+

{title}

+ {extraInfo} +
diff --git a/apps/marketing/src/app/(marketing)/open/page.tsx b/apps/marketing/src/app/(marketing)/open/page.tsx index 8a8281d53..8d04e21b1 100644 --- a/apps/marketing/src/app/(marketing)/open/page.tsx +++ b/apps/marketing/src/app/(marketing)/open/page.tsx @@ -8,6 +8,7 @@ import { BarMetric } from './bar-metrics'; import { CapTable } from './cap-table'; import { FundingRaised } from './funding-raised'; import { TeamMembers } from './team-members'; +import { OpenPageTooltip } from './tooltip'; export const revalidate = 3600; @@ -133,6 +134,7 @@ export default async function OpenPage() { title="Early Adopters" label="Early Adopters" className="col-span-12 lg:col-span-6" + extraInfo={} /> diff --git a/apps/marketing/src/app/(marketing)/open/tooltip.tsx b/apps/marketing/src/app/(marketing)/open/tooltip.tsx new file mode 100644 index 000000000..e6bf48a94 --- /dev/null +++ b/apps/marketing/src/app/(marketing)/open/tooltip.tsx @@ -0,0 +1,40 @@ +import React from 'react'; + +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from '@documenso/ui/primitives/tooltip'; + +export function OpenPageTooltip() { + return ( + + + + + + + + +

+ August and earlier: Active subscribers. September and beyond: Numbers of active + subscriptions. +

+
+
+
+ ); +}