Skip to content

Commit

Permalink
feat: add billing UI plans, billing account and subscriptions for adm…
Browse files Browse the repository at this point in the history
…in panel (#479)

* chore: add and replace table column

* feat: add plan list

* feat: add billing accounts for org
  • Loading branch information
Praveen Yadav authored Jan 31, 2024
1 parent e244c5c commit 551ecc6
Show file tree
Hide file tree
Showing 20 changed files with 712 additions and 30 deletions.
4 changes: 4 additions & 0 deletions ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ const navigationItems: NavigationItemsTypes[] = [
name: "Groups",
to: `/groups`,
},
{
name: "Plans",
to: `/plans`,
},
{
name: "Roles",
to: `/roles`,
Expand Down
48 changes: 48 additions & 0 deletions ui/src/containers/billingplans.list/columns.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { V1Beta1Plan } from "@raystack/frontier";
import type { ColumnDef } from "@tanstack/react-table";
import { createColumnHelper } from "@tanstack/react-table";
import { Link } from "react-router-dom";

const columnHelper = createColumnHelper<V1Beta1Plan>();
export const getColumns: (
plans: V1Beta1Plan[]
) => ColumnDef<V1Beta1Plan, any>[] = (plans: V1Beta1Plan[]) => {
return [
columnHelper.accessor("id", {
header: "ID",
//@ts-ignore
filterVariant: "text",
cell: ({ row, getValue }) => {
return <Link to={`/plans/${row.getValue("id")}`}>{getValue()}</Link>;
},
}),
{
header: "Title",
accessorKey: "title",
filterVariant: "text",
cell: (info) => info.getValue(),
},
{
header: "Interval",
accessorKey: "interval",
filterVariant: "text",
cell: (info) => info.getValue(),
footer: (props) => props.column.id,
},
{
header: "Create At",
accessorKey: "created_at",
meta: {
headerFilter: false,
},
cell: (info) =>
new Date(info.getValue() as Date).toLocaleString("en", {
month: "long",
day: "numeric",
year: "numeric",
}),

footer: (props) => props.column.id,
},
];
};
44 changes: 44 additions & 0 deletions ui/src/containers/billingplans.list/details.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Flex, Grid, Text } from "@raystack/apsara";
import { usePlan } from ".";
export default function PlanDetails() {
const { plan } = usePlan();

return (
<Flex
direction="column"
gap="large"
style={{
width: "320px",
height: "calc(100vh - 60px)",
borderLeft: "1px solid var(--border-base)",
padding: "var(--pd-16)",
}}
>
<Text size={4}>{plan?.name}</Text>
<Flex direction="column" gap="large">
<Grid columns={2} gap="small">
<Text size={1}>Name</Text>
<Text size={1}>{plan?.name}</Text>
</Grid>
<Grid columns={2} gap="small">
<Text size={1}>Interval</Text>
<Text size={1}>{plan?.interval}</Text>
</Grid>
<Grid columns={2} gap="small">
<Text size={1}>Created At</Text>
<Text size={1}>
{new Date(plan?.created_at as any).toLocaleString("en", {
month: "long",
day: "numeric",
year: "numeric",
})}
</Text>
</Grid>
</Flex>
</Flex>
);
}

const css = {
row: { height: "32px", display: "flex", alignItems: "center" },
};
17 changes: 17 additions & 0 deletions ui/src/containers/billingplans.list/header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { DataTable, useTable } from "@raystack/apsara";
import { useNavigate } from "react-router-dom";
import PageHeader from "~/components/page-header";

export const PlanHeader = ({ header }: any) => {
const navigate = useNavigate();
const { filteredColumns, table } = useTable();
const isFiltered = filteredColumns.length > 0;

return (
<PageHeader title={header.title} breadcrumb={header?.breadcrumb || []}>
{isFiltered ? <DataTable.ClearFilter /> : <DataTable.FilterOptions />}
<DataTable.ViewOptions />
<DataTable.GloabalSearch placeholder="Search plans..." />
</PageHeader>
);
};
79 changes: 79 additions & 0 deletions ui/src/containers/billingplans.list/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { DataTable, EmptyState, Flex } from "@raystack/apsara";
import { useFrontier } from "@raystack/frontier/react";
import { useEffect, useState } from "react";
import { Outlet, useOutletContext, useParams } from "react-router-dom";

import { V1Beta1Plan } from "@raystack/frontier";
import { reduceByKey } from "~/utils/helper";
import { getColumns } from "./columns";
import { PlanHeader } from "./header";

const pageHeader = {
title: "Plans",
breadcrumb: [],
};

type ContextType = { plan: V1Beta1Plan | null };
export default function PlanList() {
const { client } = useFrontier();
const [plans, setPlans] = useState([]);

useEffect(() => {
async function getAllPlans() {
const {
// @ts-ignore
data: { plans },
} = await client?.frontierServiceListPlans();
setPlans(plans);
}
getAllPlans();
}, []);

let { planId } = useParams();

const userMapByName = reduceByKey(plans ?? [], "id");

const tableStyle = plans?.length
? { width: "100%" }
: { width: "100%", height: "100%" };

return (
<Flex direction="row" style={{ height: "100%", width: "100%" }}>
<DataTable
data={plans ?? []}
// @ts-ignore
columns={getColumns(plans)}
emptyState={noDataChildren}
parentStyle={{ height: "calc(100vh - 60px)" }}
style={tableStyle}
>
<DataTable.Toolbar>
<PlanHeader header={pageHeader} />
<DataTable.FilterChips style={{ paddingTop: "16px" }} />
</DataTable.Toolbar>
<DataTable.DetailContainer>
<Outlet
context={{
user: planId ? userMapByName[planId] : null,
}}
/>
</DataTable.DetailContainer>
</DataTable>
</Flex>
);
}

export function usePlan() {
return useOutletContext<ContextType>();
}

export const noDataChildren = (
<EmptyState>
<div className="svg-container"></div>
<h3>0 plan created</h3>
</EmptyState>
);

export const TableDetailContainer = ({ children }: any) => (
<div>{children}</div>
);
10 changes: 5 additions & 5 deletions ui/src/containers/groups.list/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ export const getColumns: (
},
}),
{
header: "Name",
accessorKey: "name",
header: "Title",
accessorKey: "title",
cell: (info: any) => info.getValue(),
filterVariant: "text",
},
{
header: "Slug",
accessorKey: "slug",
cell: (info: any) => info.getValue(),
header: "Organization Id",
accessorKey: "org_id",
cell: (info) => info.getValue(),
filterVariant: "text",
},
{
Expand Down
4 changes: 4 additions & 0 deletions ui/src/containers/groups.list/details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export default function GroupDetails() {
<Text size={1}>Name</Text>
<Text size={1}>{group?.name}</Text>
</Grid>
<Grid columns={2} gap="small">
<Text size={1}>Organization Id</Text>
<Text size={1}>{group?.org_id}</Text>
</Grid>
<Grid columns={2} gap="small">
<Text size={1}>Created At</Text>
<Text size={1}>
Expand Down
71 changes: 71 additions & 0 deletions ui/src/containers/organisations.list/billingaccounts/columns.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { V1Beta1BillingAccount } from "@raystack/frontier";
import type { ColumnDef } from "@tanstack/react-table";
import { createColumnHelper } from "@tanstack/react-table";
import { Link, useParams } from "react-router-dom";

const columnHelper = createColumnHelper<V1Beta1BillingAccount>();
export const getColumns: (
billingAccounts: V1Beta1BillingAccount[]
) => ColumnDef<V1Beta1BillingAccount, any>[] = (
billingAccounts: V1Beta1BillingAccount[]
) => {
let { organisationId } = useParams();
return [
columnHelper.accessor("id", {
header: "ID",
//@ts-ignore
filterVariant: "text",
cell: ({ row, getValue }) => {
return (
<Link
to={`/organisations/${organisationId}/billingaccounts/${row.getValue(
"id"
)}`}
>
{getValue()}
</Link>
);
},
}),
{
header: "Organization Id",
accessorKey: "org_id",
cell: (info) => info.getValue(),
filterVariant: "text",
},
{
header: "Title",
accessorKey: "name",
cell: (info) => info.getValue(),
filterVariant: "text",
},
{
header: "Provider",
accessorKey: "provider",
cell: (info) => info.getValue(),
filterVariant: "text",
},
{
header: "State",
accessorKey: "state",
cell: (info) => info.getValue(),
filterVariant: "text",
},

{
header: "Create At",
accessorKey: "created_at",
meta: {
headerFilter: false,
},
cell: (info) =>
new Date(info.getValue() as Date).toLocaleString("en", {
month: "long",
day: "numeric",
year: "numeric",
}),

footer: (props) => props.column.id,
},
];
};
69 changes: 69 additions & 0 deletions ui/src/containers/organisations.list/billingaccounts/details.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Flex, Grid, Text } from "@raystack/apsara";
import { NavLink, useParams } from "react-router-dom";
import { usebillingaccount } from ".";

export default function BillingAccountDetails() {
const { billingaccount } = usebillingaccount();
let { organisationId, billingaccountId } = useParams();
return (
<Flex
direction="column"
gap="large"
style={{
width: "320px",
height: "calc(100vh - 60px)",
borderLeft: "1px solid var(--border-base)",
padding: "var(--pd-16)",
}}
>
<Text size={4}>{billingaccount?.name}</Text>
<Flex direction="column" gap="large">
<Grid columns={2} gap="small">
<Text size={1}>Name</Text>
<Text size={1}>{billingaccount?.name}</Text>
</Grid>
<Grid columns={2} gap="small">
<Text size={1}>Subscriptions</Text>
<Text size={1}>
<NavLink
to={`/organisations/${organisationId}/billingaccounts/${billingaccountId}/subscriptions`}
style={{
display: "flex",
alignItems: "center",
flexDirection: "row",
}}
>
Go to subscriptions
</NavLink>
</Text>
</Grid>
<Grid columns={2} gap="small">
<Text size={1}>Organization Id</Text>
<Text size={1}>{billingaccount?.org_id}</Text>
</Grid>
<Grid columns={2} gap="small">
<Text size={1}>Provider</Text>
<Text size={1}>{billingaccount?.provider}</Text>
</Grid>
<Grid columns={2} gap="small">
<Text size={1}>State</Text>
<Text size={1}>{billingaccount?.state}</Text>
</Grid>
<Grid columns={2} gap="small">
<Text size={1}>Created At</Text>
<Text size={1}>
{new Date(billingaccount?.created_at as any).toLocaleString("en", {
month: "long",
day: "numeric",
year: "numeric",
})}
</Text>
</Grid>
</Flex>
</Flex>
);
}

const css = {
row: { height: "32px", display: "flex", alignItems: "center" },
};
Loading

0 comments on commit 551ecc6

Please sign in to comment.