|
@@ -4,6 +4,8 @@ import { Billing } from "@opencode/console-core/billing.js"
|
|
|
import { withActor } from "~/context/auth.withActor"
|
|
import { withActor } from "~/context/auth.withActor"
|
|
|
import { IconCreditCard } from "~/component/icon"
|
|
import { IconCreditCard } from "~/component/icon"
|
|
|
import styles from "./billing-section.module.css"
|
|
import styles from "./billing-section.module.css"
|
|
|
|
|
+import { Database, eq } from "@opencode/console-core/drizzle/index.js"
|
|
|
|
|
+import { BillingTable } from "@opencode/console-core/schema/billing.sql.js"
|
|
|
|
|
|
|
|
const createCheckoutUrl = action(async (workspaceID: string, successUrl: string, cancelUrl: string) => {
|
|
const createCheckoutUrl = action(async (workspaceID: string, successUrl: string, cancelUrl: string) => {
|
|
|
"use server"
|
|
"use server"
|
|
@@ -17,12 +19,23 @@ const reload = action(async (form: FormData) => {
|
|
|
return json(await withActor(() => Billing.reload(), workspaceID), { revalidate: getBillingInfo.key })
|
|
return json(await withActor(() => Billing.reload(), workspaceID), { revalidate: getBillingInfo.key })
|
|
|
}, "billing.reload")
|
|
}, "billing.reload")
|
|
|
|
|
|
|
|
-const disableReload = action(async (form: FormData) => {
|
|
|
|
|
|
|
+const setReload = action(async (form: FormData) => {
|
|
|
"use server"
|
|
"use server"
|
|
|
const workspaceID = form.get("workspaceID")?.toString()
|
|
const workspaceID = form.get("workspaceID")?.toString()
|
|
|
if (!workspaceID) return { error: "Workspace ID is required" }
|
|
if (!workspaceID) return { error: "Workspace ID is required" }
|
|
|
- return json(await withActor(() => Billing.disableReload(), workspaceID), { revalidate: getBillingInfo.key })
|
|
|
|
|
-}, "billing.disableReload")
|
|
|
|
|
|
|
+ const reload = form.get("reload")?.toString() === "true"
|
|
|
|
|
+ return json(
|
|
|
|
|
+ await Database.use((tx) =>
|
|
|
|
|
+ tx
|
|
|
|
|
+ .update(BillingTable)
|
|
|
|
|
+ .set({
|
|
|
|
|
+ reload,
|
|
|
|
|
+ })
|
|
|
|
|
+ .where(eq(BillingTable.workspaceID, workspaceID)),
|
|
|
|
|
+ ),
|
|
|
|
|
+ { revalidate: getBillingInfo.key },
|
|
|
|
|
+ )
|
|
|
|
|
+}, "billing.setReload")
|
|
|
|
|
|
|
|
const createSessionUrl = action(async (workspaceID: string, returnUrl: string) => {
|
|
const createSessionUrl = action(async (workspaceID: string, returnUrl: string) => {
|
|
|
"use server"
|
|
"use server"
|
|
@@ -44,7 +57,7 @@ export function BillingSection() {
|
|
|
const createCheckoutUrlSubmission = useSubmission(createCheckoutUrl)
|
|
const createCheckoutUrlSubmission = useSubmission(createCheckoutUrl)
|
|
|
const createSessionUrlAction = useAction(createSessionUrl)
|
|
const createSessionUrlAction = useAction(createSessionUrl)
|
|
|
const createSessionUrlSubmission = useSubmission(createSessionUrl)
|
|
const createSessionUrlSubmission = useSubmission(createSessionUrl)
|
|
|
- const disableReloadSubmission = useSubmission(disableReload)
|
|
|
|
|
|
|
+ const setReloadSubmission = useSubmission(setReload)
|
|
|
const reloadSubmission = useSubmission(reload)
|
|
const reloadSubmission = useSubmission(reload)
|
|
|
|
|
|
|
|
// DUMMY DATA FOR TESTING - UNCOMMENT ONE OF THE SCENARIOS BELOW
|
|
// DUMMY DATA FOR TESTING - UNCOMMENT ONE OF THE SCENARIOS BELOW
|
|
@@ -89,6 +102,10 @@ export function BillingSection() {
|
|
|
return ((balanceInfo()?.balance ?? 0) / 100000000).toFixed(2)
|
|
return ((balanceInfo()?.balance ?? 0) / 100000000).toFixed(2)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+ const hasBalance = createMemo(() => {
|
|
|
|
|
+ return (balanceInfo()?.balance ?? 0) > 0 && balanceAmount() !== "0.00"
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
return (
|
|
return (
|
|
|
<section class={styles.root}>
|
|
<section class={styles.root}>
|
|
|
<div data-slot="section-title">
|
|
<div data-slot="section-title">
|
|
@@ -136,19 +153,32 @@ export function BillingSection() {
|
|
|
<Show
|
|
<Show
|
|
|
when={balanceInfo()?.reload}
|
|
when={balanceInfo()?.reload}
|
|
|
fallback={
|
|
fallback={
|
|
|
- <button
|
|
|
|
|
- data-color="primary"
|
|
|
|
|
- disabled={createCheckoutUrlSubmission.pending}
|
|
|
|
|
- onClick={async () => {
|
|
|
|
|
- const baseUrl = window.location.href
|
|
|
|
|
- const checkoutUrl = await createCheckoutUrlAction(params.id, baseUrl, baseUrl)
|
|
|
|
|
- if (checkoutUrl) {
|
|
|
|
|
- window.location.href = checkoutUrl
|
|
|
|
|
- }
|
|
|
|
|
- }}
|
|
|
|
|
|
|
+ <Show
|
|
|
|
|
+ when={hasBalance()}
|
|
|
|
|
+ fallback={
|
|
|
|
|
+ <button
|
|
|
|
|
+ data-color="primary"
|
|
|
|
|
+ disabled={createCheckoutUrlSubmission.pending}
|
|
|
|
|
+ onClick={async () => {
|
|
|
|
|
+ const baseUrl = window.location.href
|
|
|
|
|
+ const checkoutUrl = await createCheckoutUrlAction(params.id, baseUrl, baseUrl)
|
|
|
|
|
+ if (checkoutUrl) {
|
|
|
|
|
+ window.location.href = checkoutUrl
|
|
|
|
|
+ }
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ {createCheckoutUrlSubmission.pending ? "Loading..." : "Enable Billing"}
|
|
|
|
|
+ </button>
|
|
|
|
|
+ }
|
|
|
>
|
|
>
|
|
|
- {createCheckoutUrlSubmission.pending ? "Loading..." : "Enable Billing"}
|
|
|
|
|
- </button>
|
|
|
|
|
|
|
+ <form action={setReload} method="post" data-slot="create-form">
|
|
|
|
|
+ <input type="hidden" name="workspaceID" value={params.id} />
|
|
|
|
|
+ <input type="hidden" name="reload" value="true" />
|
|
|
|
|
+ <button data-color="primary" type="submit" disabled={setReloadSubmission.pending}>
|
|
|
|
|
+ {setReloadSubmission.pending ? "Enabling..." : "Enable Billing"}
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </form>
|
|
|
|
|
+ </Show>
|
|
|
}
|
|
}
|
|
|
>
|
|
>
|
|
|
<button
|
|
<button
|
|
@@ -164,10 +194,11 @@ export function BillingSection() {
|
|
|
>
|
|
>
|
|
|
{createSessionUrlSubmission.pending ? "Loading..." : "Manage Payment Methods"}
|
|
{createSessionUrlSubmission.pending ? "Loading..." : "Manage Payment Methods"}
|
|
|
</button>
|
|
</button>
|
|
|
- <form action={disableReload} method="post" data-slot="create-form">
|
|
|
|
|
|
|
+ <form action={setReload} method="post" data-slot="create-form">
|
|
|
<input type="hidden" name="workspaceID" value={params.id} />
|
|
<input type="hidden" name="workspaceID" value={params.id} />
|
|
|
- <button data-color="ghost" type="submit" disabled={disableReloadSubmission.pending}>
|
|
|
|
|
- {disableReloadSubmission.pending ? "Disabling..." : "Disable"}
|
|
|
|
|
|
|
+ <input type="hidden" name="reload" value="false" />
|
|
|
|
|
+ <button data-color="ghost" type="submit" disabled={setReloadSubmission.pending}>
|
|
|
|
|
+ {setReloadSubmission.pending ? "Disabling..." : "Disable"}
|
|
|
</button>
|
|
</button>
|
|
|
</form>
|
|
</form>
|
|
|
</Show>
|
|
</Show>
|
|
@@ -176,7 +207,7 @@ export function BillingSection() {
|
|
|
<div data-slot="usage">
|
|
<div data-slot="usage">
|
|
|
<Show when={!balanceInfo()?.reload}>
|
|
<Show when={!balanceInfo()?.reload}>
|
|
|
<Show
|
|
<Show
|
|
|
- when={!(balanceAmount() === "0.00" || balanceAmount() === "-0.00")}
|
|
|
|
|
|
|
+ when={hasBalance()}
|
|
|
fallback={
|
|
fallback={
|
|
|
<p>
|
|
<p>
|
|
|
We'll load <b>$20</b> (+$1.23 processing fee) and reload it when it reaches <b>$5</b>.
|
|
We'll load <b>$20</b> (+$1.23 processing fee) and reload it when it reaches <b>$5</b>.
|