update: pricing plan with usage base

This commit is contained in:
Karrix 2024-01-21 15:35:14 +08:00 committed by BennyKok
parent 8a134ed39e
commit ac8f6e2808
5 changed files with 152 additions and 7 deletions

View File

@ -1,6 +1,11 @@
import { checkMarkIcon } from "../const/Icon";
import { checkMarkIcon, crossMarkIcon } from "../const/Icon";
import { cn } from "@/lib/utils";
import { getPricing } from "@/server/linkToPricing";
import {
getPricing,
getSubscriptionItem,
getUsage,
setUsage,
} from "@/server/linkToPricing";
import { useEffect, useState } from "react";
type Tier = {
@ -11,10 +16,18 @@ type Tier = {
description: string;
features: string[];
featured: boolean;
priority?: TierPriority;
};
enum TierPriority {
Free = "free",
Pro = "pro",
Enterprise = "enterprise",
}
export default function PricingList() {
const [productTiers, setProductTiers] = useState<Tier[]>();
const [userUsageId, setUserUsageId] = useState<number>(0);
useEffect(() => {
(async () => {
@ -40,26 +53,81 @@ export default function PricingList() {
name: item.attributes.name,
id: item.id,
href: item.attributes.buy_now_url,
priceMonthly: item.attributes.price_formatted.split("/")[0],
priceMonthly:
item.attributes.price_formatted.split("/")[0] == "Usage-based"
? "$20.00"
: item.attributes.price_formatted.split("/")[0],
description: description,
features: features,
// if name contains pro, it's featured
featured: item.attributes.name.toLowerCase().includes("pro"),
// give priority if name contain in enum
priority: Object.values(TierPriority).find((priority) =>
item.attributes.name.toLowerCase().includes(priority)
),
};
});
console.log(newProductTiers);
// sort newProductTiers by priority
newProductTiers.sort((a, b) => {
if (!a.priority) return 1;
if (!b.priority) return -1;
return (
Object.values(TierPriority).indexOf(a.priority) -
Object.values(TierPriority).indexOf(b.priority)
);
});
setProductTiers(newProductTiers);
})();
}, []);
useEffect(() => {
(async () => {
// const currentUser = await getUserData();
const userUsage = await getUsage();
const userSubscription = await getSubscriptionItem();
// const setUserUsage = await setUsage(236561, 10);
// console.log(currentUser);
console.log(userSubscription);
})();
}, []);
const setUserUsage = async (id: number, quantity: number) => {
try {
const response = await setUsage(id, quantity);
console.log(response);
} catch (error) {
console.error(error);
}
};
return (
<div className="relative isolate px-6 py-24 lg:px-8">
<div className="mx-auto max-w-2xl text-center lg:max-w-4xl">
<h2 className="text-base font-semibold leading-7 text-indigo-600">
Pricing
</h2>
<div className="flex flex-col">
<button
className="mt-2 text-base font-semibold leading-7 text-indigo-600 bg-black"
onClick={() => {
setUserUsage(192385, 10);
}}
>
Set
</button>
<button className="mt-2 text-base font-semibold leading-7 text-indigo-600 bg-slate-400">
Get
</button>
</div>
<p className="mt-2 text-4xl font-bold tracking-tight text-gray-900 sm:text-5xl">
The right price for you, whoever you are
</p>
@ -68,7 +136,7 @@ export default function PricingList() {
Qui iusto aut est earum eos quae. Eligendi est at nam aliquid ad quo
reprehenderit in aliquid fugiat dolorum voluptatibus.
</p>
<div className="mx-auto mt-16 grid max-w-lg grid-cols-1 items-center gap-y-6 sm:mt-20 sm:gap-y-0 lg:max-w-4xl lg:grid-cols-2">
<div className="mx-auto mt-16 grid max-w-lg grid-cols-1 items-center gap-y-6 sm:mt-20 sm:gap-y-0 lg:max-w-4xl lg:grid-cols-2 xl:max-w-6xl xl:grid-cols-3">
{productTiers &&
productTiers.map((tier, tierIdx) => (
<div
@ -107,9 +175,9 @@ export default function PricingList() {
{tier.features.map((feature) => (
<li key={feature} className="flex gap-x-3">
<div className="flex justify-center items-center">
{checkMarkIcon}
{feature.includes("[x]") ? crossMarkIcon : checkMarkIcon}
</div>
{feature}
{feature.replace("[x]", "")}
</li>
))}
</ul>

View File

@ -13,3 +13,25 @@ export const checkMarkIcon = (
/>
</svg>
);
export const crossMarkIcon = (
<svg
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
width="20"
height="20"
viewBox="0 0 48 48"
>
<path
fill="#F44336"
d="M21.5 4.5H26.501V43.5H21.5z"
transform="rotate(45.001 24 24)"
/>
<path
fill="#F44336"
d="M21.5 4.5H26.5V43.501H21.5z"
transform="rotate(135.008 24 24)"
/>
</svg>
);

View File

@ -85,6 +85,13 @@ export function Navbar() {
</div>
<div className="flex flex-row items-center gap-2">
{isDesktop && <NavbarMenu />}
<Button
asChild
variant="link"
className="rounded-full aspect-square p-2 mr-4"
>
<a href="/pricing">Pricing</a>
</Button>
<Button
asChild
variant="link"

View File

@ -8,6 +8,7 @@ import {
text,
timestamp,
uuid,
real,
} from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
@ -331,8 +332,22 @@ export const apiKeyTable = dbSchema.table("api_keys", {
updated_at: timestamp("updated_at").defaultNow().notNull(),
});
export const userUsageTable = dbSchema.table("user_usage", {
id: uuid("id").primaryKey().defaultRandom().notNull(),
user_id: text("user_id")
.references(() => usersTable.id, {
onDelete: "cascade",
})
.notNull(),
usage_time: real("usage_time").default(0).notNull(),
created_at: timestamp("created_at").defaultNow().notNull(),
updated_at: timestamp("updated_at").defaultNow().notNull(),
});
export type UserType = InferSelectModel<typeof usersTable>;
export type WorkflowType = InferSelectModel<typeof workflowTable>;
export type MachineType = InferSelectModel<typeof machinesTable>;
export type WorkflowVersionType = InferSelectModel<typeof workflowVersionTable>;
export type DeploymentType = InferSelectModel<typeof deploymentsTable>;
export type UserUsageType = InferSelectModel<typeof userUsageTable>;

View File

@ -10,3 +10,36 @@ export async function getPricing() {
return products;
}
export async function getUsage() {
const usageRecord = await ls.getUsageRecords();
return usageRecord;
}
export async function setUsage(id: number, quantity: number) {
const setUsage = await ls.createUsageRecord({
subscriptionItemId: id,
quantity: quantity,
});
return setUsage;
}
export async function getSubscription() {
const subscription = await ls.getSubscriptions();
return subscription;
}
export async function getSubscriptionItem() {
const subscriptionItem = await ls.getSubscriptionItems();
return subscriptionItem;
}
export async function getUserData() {
const user = await ls.getUser();
return user;
}