From 74f9fcea8829d1fa7cb7c1f87230718605e54358 Mon Sep 17 00:00:00 2001 From: Jay V Date: Mon, 15 Sep 2025 19:02:47 -0400 Subject: [PATCH] ignore: zen --- cloud/app/src/routes/workspace/[id].css | 181 ++++++++++++++- cloud/app/src/routes/workspace/[id].tsx | 291 +++++++++++------------- 2 files changed, 306 insertions(+), 166 deletions(-) diff --git a/cloud/app/src/routes/workspace/[id].css b/cloud/app/src/routes/workspace/[id].css index 3f4db713..b08ae24d 100644 --- a/cloud/app/src/routes/workspace/[id].css +++ b/cloud/app/src/routes/workspace/[id].css @@ -269,9 +269,39 @@ } } - /* Balance Section */ - [data-component="balance-section"] { - [data-slot="balance"] { + /* Billing Section */ + [data-component="billing-section"] { + [data-slot="section-content"] { + display: flex; + flex-direction: column; + gap: var(--space-3); + } + + [data-slot="reload-error"] { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-4); + padding: var(--space-4); + border: 1px solid var(--color-border); + border-radius: var(--border-radius-sm); + + p { + color: var(--color-danger); + font-size: var(--font-size-sm); + line-height: 1.4; + margin: 0; + flex: 1; + } + + [data-slot="create-form"] { + display: flex; + gap: var(--space-2); + margin: 0; + flex-shrink: 0; + } + } + [data-slot="payment"] { display: flex; flex-direction: column; gap: var(--space-3); @@ -281,6 +311,85 @@ min-width: 14.5rem; width: fit-content; + [data-slot="credit-card"] { + padding: var(--space-3-5) var(--space-4); + background-color: var(--color-bg-surface); + border-radius: var(--border-radius-sm); + display: flex; + align-items: center; + justify-content: space-between; + + [data-slot="card-icon"] { + display: flex; + align-items: center; + } + + [data-slot="card-details"] { + display: flex; + align-items: baseline; + gap: var(--space-1); + + [data-slot="secret"] { + position: relative; + bottom: 2px; + font-size: var(--font-size-lg); + color: var(--color-text-muted); + font-weight: 400; + } + + [data-slot="number"] { + font-size: var(--font-size-3xl); + font-weight: 500; + color: var(--color-text); + } + } + } + + [data-slot="button-row"] { + display: flex; + gap: var(--space-2); + align-items: center; + + [data-slot="create-form"] { + margin: 0; + } + + /* Make Enable Billing button full width when it's the only button */ + > button { + flex: 1; + } + } + } + [data-slot="usage"] { + p { + font-size: var(--font-size-sm); + line-height: 1.5; + color: var(--color-text-secondary); + b { + font-weight: 600; + } + } + } + } + + /* Monthly Limit Section */ + [data-component="monthly-limit-section"] { + [data-slot="section-content"] { + display: flex; + flex-direction: column; + gap: var(--space-3); + } + + [data-slot="balance"] { + display: flex; + flex-direction: column; + gap: var(--space-3); + padding: var(--space-4); + border: 1px solid var(--color-border); + border-radius: var(--border-radius-sm); + min-width: 15rem; + width: fit-content; + [data-slot="amount"] { padding: var(--space-3-5) var(--space-4); background-color: var(--color-bg-surface); @@ -290,15 +399,6 @@ gap: var(--space-1); justify-content: flex-end; - &[data-state="danger"] { - [data-slot="value"] { - color: var(--color-danger); - } - [data-slot="currency"] { - color: var(--color-danger); - } - } - [data-slot="currency"] { position: relative; bottom: 2px; @@ -313,6 +413,63 @@ color: var(--color-text); } } + + [data-slot="create-form"] { + display: flex; + flex-direction: column; + gap: var(--space-3); + margin-top: var(--space-1); + + [data-slot="input-container"] { + display: flex; + flex-direction: column; + gap: var(--space-1); + } + + @media (max-width: 30rem) { + gap: var(--space-2); + } + + input { + flex: 1; + padding: var(--space-2) var(--space-3); + border: 1px solid var(--color-border); + border-radius: var(--border-radius-sm); + background-color: var(--color-bg); + color: var(--color-text); + font-size: var(--font-size-sm); + font-family: var(--font-mono); + + &:focus { + outline: none; + border-color: var(--color-accent); + } + + &::placeholder { + color: var(--color-text-disabled); + } + } + + [data-slot="form-actions"] { + display: flex; + gap: var(--space-2); + justify-content: flex-end; + } + + [data-slot="form-error"] { + color: var(--color-danger); + font-size: var(--font-size-sm); + margin-top: var(--space-1); + line-height: 1.4; + } + } + } + + [data-slot="usage-status"] { + font-size: var(--font-size-sm); + color: var(--color-text-secondary); + margin: 0; + line-height: 1.4; } } diff --git a/cloud/app/src/routes/workspace/[id].tsx b/cloud/app/src/routes/workspace/[id].tsx index 5844f1af..4d525b33 100644 --- a/cloud/app/src/routes/workspace/[id].tsx +++ b/cloud/app/src/routes/workspace/[id].tsx @@ -278,140 +278,122 @@ function KeyCreateForm() { ) } -function BalanceSection() { +function BillingSection() { const params = useParams() const balanceInfo = createAsync(() => getBillingInfo(params.id)) const createCheckoutUrlAction = useAction(createCheckoutUrl) const createCheckoutUrlSubmission = useSubmission(createCheckoutUrl) + const createSessionUrlAction = useAction(createSessionUrl) + const createSessionUrlSubmission = useSubmission(createSessionUrl) const disableReloadSubmission = useSubmission(disableReload) const reloadSubmission = useSubmission(reload) + const balanceAmount = createMemo(() => { + return ((balanceInfo()?.balance ?? 0) / 100000000).toFixed(2) + }) + return ( -
+
-

Balance

-

Add credits to your account.

+

Billing

+

Manage the payment method for your account.

-
-
{ - const balanceStr = ((balanceInfo()?.balance ?? 0) / 100000000).toFixed(2) - return balanceStr === "0.00" || balanceStr === "-0.00" ? "danger" : undefined - })()} - > - $ - - {(() => { - const balanceStr = ((balanceInfo()?.balance ?? 0) / 100000000).toFixed(2) - return balanceStr === "-0.00" ? "0.00" : balanceStr - })()} - -
- +
+ +
+

+ Reload failed at{" "} + {balanceInfo()?.timeReloadError!.toLocaleString("en-US", { + month: "short", + day: "numeric", + hour: "numeric", + minute: "2-digit", + second: "2-digit", + })}{" "} + . Reason: {balanceInfo()?.reloadError?.replace(/\.$/, "")}. Please update your payment method and try + again. +

+
+ + +
+
+
+
+
+
+ +
+
+ ----}> + •••• + {balanceInfo()?.paymentMethodLast4} + +
+
+
+ { + const baseUrl = window.location.href + const checkoutUrl = await createCheckoutUrlAction(params.id, baseUrl, baseUrl) + if (checkoutUrl) { + window.location.href = checkoutUrl + } + }} + > + {createCheckoutUrlSubmission.pending ? "Loading..." : "Enable Billing"} + + } + > -

You can continue using the API with the remaining credits.

- - } - > - <> -
-

- You will be automatically reloading $20 (+$1.23 processing fee) when your balance reaches{" "} - $5. -

-

You will be able to continue using the API with the remaining credits after disabling billing.

-
- - <> -

- Reload failed at{" "} - {balanceInfo()?.timeReloadError!.toLocaleString("en-US", { - month: "short", - day: "numeric", - hour: "numeric", - minute: "2-digit", - second: "2-digit", - })}{" "} - . Reason: {balanceInfo()?.reloadError?.replace(/\.$/, "")}. Please update your payment method and - try again. -

-
- - -
- -
-
- -
+ +
+
+
+ +

+ You have ${balanceAmount() === "-0.00" ? "0.00" : balanceAmount()} remaining in + your account. You can continue using the API with your remaining balance. +

+
+ +

+ Your current balance is ${balanceAmount() === "-0.00" ? "0.00" : balanceAmount()} + . It'll be reloaded to $20 (+$1.23 processing fee) when it reaches $5. +

+
+
- - - -
) } -function BalancePaymentForm() { - const params = useParams() - const createSessionUrlAction = useAction(createSessionUrl) - const createSessionUrlSubmission = useSubmission(createSessionUrl) - const balanceInfo = createAsync(() => getBillingInfo(params.id)) - - return ( - <> -
-

Payment Method

-
-
-
- - •••• - {balanceInfo()?.paymentMethodLast4} -
- -
- - ) -} - -function BalanceLimitForm() { +function MonthlyLimitSection() { const params = useParams() const submission = useSubmission(setMonthlyLimit) const [store, setStore] = createStore({ show: false }) @@ -444,19 +426,47 @@ function BalanceLimitForm() { } return ( - <> +

Monthly Limit

+

Set a monthly spending limit for your account.

-
-
- $ - {balanceInfo()?.monthlyLimit ?? "-"} +
+
+
+ {balanceInfo()?.monthlyLimit ? $ : null} + {balanceInfo()?.monthlyLimit ?? "-"} +
+ +
+ (input = r)} data-component="input" name="limit" type="number" placeholder="50" /> + + {(err) =>
{err()}
} +
+
+ +
+ + +
+ + } + > + +
- No spending limit set.

}> -

- Current usage for the month of {new Date().toLocaleDateString("en-US", { month: "long", timeZone: "UTC" })}{" "} - is $ + No spending limit set.

}> +

+ Current usage for {new Date().toLocaleDateString("en-US", { month: "long", timeZone: "UTC" })} is $ {(() => { const dateLastUsed = balanceInfo()?.timeMonthlyUsageUpdated if (!dateLastUsed) return "0" @@ -474,42 +484,11 @@ function BalanceLimitForm() { if (current !== lastUsed) return "0" return ((balanceInfo()?.monthlyUsage ?? 0) / 100000000).toFixed(2) })()} + .

- show()}> - {balanceInfo()?.monthlyLimit ? "Edit Spending Limit" : "Set Spending Limit"} - - } - > -
-
- (input = r)} - data-component="input" - name="limit" - type="number" - placeholder="Enter limit" - /> - - {(err) =>
{err()}
} -
-
- -
- - -
-
-
- +
) } @@ -570,7 +549,6 @@ function UsageSection() { function PaymentSection() { const params = useParams() const payments = createAsync(() => getPaymentsInfo(params.id)) - console.log("!#!@", payments()) return ( payments() && @@ -675,13 +653,13 @@ function NewUserSection() {
    +
  1. Enable billing
  2. Run opencode auth login and select opencode
  3. Paste your API key
  4. -
  5. Start opencode
  6. - Run /models to see available models + Start opencode and run /models to select a model
@@ -690,7 +668,9 @@ function NewUserSection() { ) } -export default function () { +export default function() { + const params = useParams() + return (
@@ -707,7 +687,10 @@ export default function () {
- + + getBillingInfo(params.id))()?.reload}> + +