Skip to content

Commit

Permalink
clients/web: add Subscribers page
Browse files Browse the repository at this point in the history
  • Loading branch information
frankie567 committed Oct 31, 2023
1 parent e6fe909 commit 23eca68
Show file tree
Hide file tree
Showing 4 changed files with 252 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import SubscribersPage from '@/components/Subscriptions/SubscribersPage'
import { getServerSideAPI } from '@/utils/api'
import { Platforms } from '@polar-sh/sdk'
import { Metadata, ResolvingMetadata } from 'next'
import { DataTableSearchParams, parseSearchParams } from 'polarkit/datatable'

export async function generateMetadata(
{
params,
}: {
params: { organization: string }
},
parent: ResolvingMetadata,
): Promise<Metadata> {
return {
title: `${params.organization}`, // " | Polar is added by the template"
}
}

export default async function Page({
params,
searchParams,
}: {
params: { organization: string }
searchParams: DataTableSearchParams
}) {
const api = getServerSideAPI()
const organization = await api.organizations.lookup({
organizationName: params.organization,
platform: Platforms.GITHUB,
})

const { pagination, sorting } = parseSearchParams(searchParams, [
{ id: 'started_at', desc: true },
])

return (
<SubscribersPage
organization={organization}
pagination={pagination}
sorting={sorting}
/>
)
}
182 changes: 182 additions & 0 deletions clients/apps/web/src/components/Subscriptions/SubscribersPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
'use client'

import { DashboardBody } from '@/components/Layout/MaintainerLayout'
import {
Organization,
PolarSubscriptionSchemasUser,
Subscription,
SubscriptionStatus,
SubscriptionTierType,
} from '@polar-sh/sdk'
import { useRouter } from 'next/navigation'
import { api } from 'polarkit'
import {
Avatar,
DataTable,
DataTableColumnDef,
DataTableColumnHeader,
FormattedDateTime,
} from 'polarkit/components/ui/atoms'
import {
DataTablePaginationState,
DataTableSortingState,
getAPIParams,
serializeSearchParams,
} from 'polarkit/datatable'
import React, { useEffect, useState } from 'react'
import { subscriptionStatusDisplayNames, tiersTypeDisplayNames } from './utils'

interface SubscribersPageProps {
organization: Organization
pagination: DataTablePaginationState
sorting: DataTableSortingState
}

const SubscribersPage: React.FC<SubscribersPageProps> = ({
organization,
pagination,
sorting,
}) => {
const router = useRouter()
const [subscriptions, setSubscriptions] = useState<
Subscription[] | undefined
>()
const [pageCount, setPageCount] = useState<number | undefined>()

const setPagination = (
updaterOrValue:
| DataTablePaginationState
| ((old: DataTablePaginationState) => DataTablePaginationState),
) => {
const updatedPagination =
typeof updaterOrValue === 'function'
? updaterOrValue(pagination)
: updaterOrValue

router.push(
`/maintainer/${
organization.name
}/subscriptions/subscribers?${serializeSearchParams(
updatedPagination,
sorting,
)}`,
)
}

const setSorting = (
updaterOrValue:
| DataTableSortingState
| ((old: DataTableSortingState) => DataTableSortingState),
) => {
const updatedSorting =
typeof updaterOrValue === 'function'
? updaterOrValue(sorting)
: updaterOrValue

router.push(
`/maintainer/${
organization.name
}/subscriptions/subscribers?${serializeSearchParams(
pagination,
updatedSorting,
)}`,
)
}

useEffect(() => {
api.subscriptions
.searchSubscriptions({
platform: organization.platform,
organizationName: organization.name,
...getAPIParams(pagination, sorting),
})
.then((subscriptions) => {
setSubscriptions(subscriptions.items || [])
setPageCount(subscriptions.pagination.max_page)
})
}, [organization, pagination, sorting])

const columns: DataTableColumnDef<Subscription>[] = [
{
accessorKey: 'user',
enableSorting: true,
header: ({ column }) => (
<DataTableColumnHeader column={column} title="User" />
),
cell: (props) => {
const user = props.getValue() as PolarSubscriptionSchemasUser
return (
<div className="flex flex-row gap-2">
<Avatar avatar_url={user.avatar_url} name={user.username} />
<div className="fw-medium">{user.username}</div>
</div>
)
},
},
{
accessorKey: 'status',
enableSorting: true,
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Status" />
),
cell: (props) =>
subscriptionStatusDisplayNames[props.getValue() as SubscriptionStatus],
},
{
accessorKey: 'started_at',
enableSorting: true,
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Subscription date" />
),
cell: (props) => (
<FormattedDateTime datetime={props.getValue() as string} />
),
},
{
accessorKey: 'current_period_end',
enableSorting: true,
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Renewal date" />
),
cell: (props) => (
<FormattedDateTime datetime={props.getValue() as string} />
),
},
{
accessorKey: 'subscription_tier.type',
id: 'subscription_tier_type',
enableSorting: true,
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Tier group" />
),
cell: (props) =>
tiersTypeDisplayNames[props.getValue() as SubscriptionTierType],
},
{
accessorKey: 'subscription_tier.name',
id: 'subscription_tier',
enableSorting: true,
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Tier" />
),
},
]

return (
<DashboardBody>
{subscriptions && pageCount !== undefined && (
<DataTable
columns={columns}
data={subscriptions}
pageCount={pageCount}
pagination={pagination}
onPaginationChange={setPagination}
sorting={sorting}
onSortingChange={setSorting}
/>
)}
</DashboardBody>
)
}

export default SubscribersPage
21 changes: 21 additions & 0 deletions clients/apps/web/src/components/Subscriptions/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ScheduleOutlined,
} from '@mui/icons-material'
import {
SubscriptionStatus,
SubscriptionTier,
SubscriptionTierBenefit,
SubscriptionTierType,
Expand Down Expand Up @@ -34,6 +35,14 @@ const defaultSubscriptionTiersByType: SubscriptionTiersByType = {
[SubscriptionTierType.BUSINESS]: [],
}

export const tiersTypeDisplayNames: {
[key in SubscriptionTierType]: string
} = {
[SubscriptionTierType.HOBBY]: 'Hobby',
[SubscriptionTierType.PRO]: 'Pro',
[SubscriptionTierType.BUSINESS]: 'Business',
}

export const getSubscriptionTiersByType = (tiers: SubscriptionTier[]) =>
tiers.reduce(
(acc: SubscriptionTiersByType, subscriptionTier: SubscriptionTier) => {
Expand Down Expand Up @@ -66,3 +75,15 @@ export const resolveBenefitIcon = (
return <CheckOutlined className={className} fontSize="small" />
}
}

export const subscriptionStatusDisplayNames: {
[key in SubscriptionStatus]: string
} = {
[SubscriptionStatus.INCOMPLETE]: 'Incomplete',
[SubscriptionStatus.INCOMPLETE_EXPIRED]: 'Incomplete',
[SubscriptionStatus.TRIALING]: 'Trialing',
[SubscriptionStatus.ACTIVE]: 'Active',
[SubscriptionStatus.PAST_DUE]: 'Past due',
[SubscriptionStatus.CANCELED]: 'Canceled',
[SubscriptionStatus.UNPAID]: 'Unpaid',
}
5 changes: 5 additions & 0 deletions clients/packages/polarkit/src/components/ui/atoms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
} from './Card'
import CopyToClipboardInput from './CopyToClipboardInput'
import CountryPicker from './CountryPicker'
import { DataTable, DataTableColumnDef } from './DataTable'
import { DataTableColumnHeader } from './DataTableColumnHeader'
import FormattedDateTime from './FormattedDateTime'
import Input from './Input'
import IssueBodyRenderer from './IssueBodyRenderer'
Expand All @@ -36,9 +38,12 @@ import Switch from './Switch'
import { Tabs, TabsContent, TabsList, TabsTrigger } from './Tabs'
import TextArea from './TextArea'

export type { DataTableColumnDef }
export {
Alert,
Button,
DataTable,
DataTableColumnHeader,
ShadowBox,
ShadowBoxOnLg,
ShadowListGroup,
Expand Down

0 comments on commit 23eca68

Please sign in to comment.