summaryrefslogtreecommitdiffhomepage
path: root/cloud
diff options
context:
space:
mode:
authorJay V <[email protected]>2025-08-30 15:28:35 -0400
committerJay V <[email protected]>2025-08-30 15:28:35 -0400
commitd3bbaa141ca74853d5f4c9f2a13575adedf824d8 (patch)
tree083bde744bf5ea23f01f2d4b18c8bbffcd4e9358 /cloud
parent8714f235090ca7977662c36bdff2413bbe200943 (diff)
downloadopencode-d3bbaa141ca74853d5f4c9f2a13575adedf824d8.tar.gz
opencode-d3bbaa141ca74853d5f4c9f2a13575adedf824d8.zip
ignore: cloud
Diffstat (limited to 'cloud')
-rw-r--r--cloud/app/src/routes/workspace.css3
-rw-r--r--cloud/app/src/routes/workspace.tsx20
-rw-r--r--cloud/app/src/routes/workspace/[id].css3
-rw-r--r--cloud/app/src/routes/workspace/[id].tsx195
4 files changed, 64 insertions, 157 deletions
diff --git a/cloud/app/src/routes/workspace.css b/cloud/app/src/routes/workspace.css
index e18b410ee..e378ef461 100644
--- a/cloud/app/src/routes/workspace.css
+++ b/cloud/app/src/routes/workspace.css
@@ -54,7 +54,10 @@
a,
button {
+ appearance: none;
+ background: none;
border: none;
+ cursor: pointer;
padding: 0;
color: var(--color-text);
text-decoration: underline;
diff --git a/cloud/app/src/routes/workspace.tsx b/cloud/app/src/routes/workspace.tsx
index 6876ae962..f75af8e74 100644
--- a/cloud/app/src/routes/workspace.tsx
+++ b/cloud/app/src/routes/workspace.tsx
@@ -1,7 +1,20 @@
import "./workspace.css"
import { useAuthSession } from "~/context/auth.session"
import { IconLogo } from "../component/icon"
-import { action, redirect, RouteSectionProps } from "@solidjs/router"
+import { withActor } from "~/context/auth.withActor"
+import "./workspace.css"
+import { query, action, redirect, createAsync, RouteSectionProps } from "@solidjs/router"
+import { User } from "@opencode/cloud-core/user.js"
+import { Actor } from "@opencode/cloud-core/actor.js"
+
+const getUserInfo = query(async () => {
+ "use server"
+ return withActor(async () => {
+ const actor = Actor.assert("user")
+ const user = await User.fromID(actor.properties.userID)
+ return { user }
+ })
+}, "userInfo")
const logout = action(async () => {
"use server"
@@ -17,6 +30,7 @@ const logout = action(async () => {
})
export default function WorkspaceLayout(props: RouteSectionProps) {
+ const userInfo = createAsync(() => getUserInfo())
return (
<main data-page="workspace">
<header data-component="workspace-header">
@@ -26,7 +40,9 @@ export default function WorkspaceLayout(props: RouteSectionProps) {
</a>
</div>
<div data-slot="header-actions">
- <span>[email protected]</span>
+ {userInfo() &&
+ <span>{userInfo()!.user.email}</span>
+ }
<form action={logout} method="post">
<button type="submit" formaction={logout}>Logout</button>
</form>
diff --git a/cloud/app/src/routes/workspace/[id].css b/cloud/app/src/routes/workspace/[id].css
index d29f5af32..651cae627 100644
--- a/cloud/app/src/routes/workspace/[id].css
+++ b/cloud/app/src/routes/workspace/[id].css
@@ -168,13 +168,13 @@
[data-slot="api-keys-section"] {
[data-slot="create-form"] {
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);
input {
+ flex: 1;
padding: var(--space-2) var(--space-3);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-sm);
@@ -196,7 +196,6 @@
[data-slot="form-actions"] {
display: flex;
gap: var(--space-2);
- justify-content: flex-end;
}
}
diff --git a/cloud/app/src/routes/workspace/[id].tsx b/cloud/app/src/routes/workspace/[id].tsx
index 70f1a7cdb..a03811c17 100644
--- a/cloud/app/src/routes/workspace/[id].tsx
+++ b/cloud/app/src/routes/workspace/[id].tsx
@@ -63,116 +63,8 @@ const createPortalUrl = action(async (returnUrl: string) => {
return withActor(() => Billing.generatePortalUrl({ returnUrl }))
}, "portalUrl")
-const dummyUsageData = [
- {
- model: "claude-3-5-sonnet-20241022",
- inputTokens: 1250,
- outputTokens: 890,
- reasoningTokens: 150,
- cacheReadTokens: 0,
- cacheWriteTokens: 45,
- cost: 12340000,
- timeCreated: new Date("2025-01-28T10:30:00Z"),
- },
- {
- model: "claude-3-haiku-20240307",
- inputTokens: 2100,
- outputTokens: 450,
- reasoningTokens: null,
- cacheReadTokens: 120,
- cacheWriteTokens: 0,
- cost: 5670000,
- timeCreated: new Date("2025-01-27T15:22:00Z"),
- },
- {
- model: "claude-3-5-sonnet-20241022",
- inputTokens: 850,
- outputTokens: 1200,
- reasoningTokens: 220,
- cacheReadTokens: 30,
- cacheWriteTokens: 15,
- cost: 18990000,
- timeCreated: new Date("2025-01-27T09:15:00Z"),
- },
- {
- model: "claude-3-opus-20240229",
- inputTokens: 3200,
- outputTokens: 1800,
- reasoningTokens: 400,
- cacheReadTokens: 0,
- cacheWriteTokens: 100,
- cost: 45670000,
- timeCreated: new Date("2025-01-26T14:45:00Z"),
- },
- {
- model: "claude-3-haiku-20240307",
- inputTokens: 650,
- outputTokens: 280,
- reasoningTokens: null,
- cacheReadTokens: 200,
- cacheWriteTokens: 0,
- cost: 2340000,
- timeCreated: new Date("2025-01-25T16:18:00Z"),
- },
-]
-
-const dummyPaymentData = [
- {
- id: "pay_1Ab2Cd3Ef4Gh5678",
- amount: 2000000000,
- timeCreated: new Date("2025-01-28T14:32:00Z"),
- },
- {
- id: "pay_9Ij8Kl7Mn6Op5432",
- amount: 1000000000,
- timeCreated: new Date("2025-01-25T09:18:00Z"),
- },
- {
- id: "pay_5Qr4St3Uv2Wx1098",
- amount: 5000000000,
- timeCreated: new Date("2025-01-20T16:45:00Z"),
- },
- {
- id: "pay_7Yz6Ab5Cd4Ef3210",
- amount: 1500000000,
- timeCreated: new Date("2025-01-15T11:22:00Z"),
- },
- {
- id: "pay_3Gh2Ij1Kl0Mn9876",
- amount: 3000000000,
- timeCreated: new Date("2025-01-10T13:55:00Z"),
- },
-]
-
-const dummyApiKeyData = [
- {
- id: "key_1Ab2Cd3Ef4Gh5678",
- name: "Production API",
- key: "oc_live_sk_1Ab2Cd3Ef4Gh567890123456789012345678901234567890",
- timeCreated: new Date("2025-01-28T14:32:00Z"),
- timeUsed: new Date("2025-01-29T09:15:00Z"),
- },
- {
- id: "key_9Ij8Kl7Mn6Op5432",
- name: "Development Key",
- key: "oc_test_sk_9Ij8Kl7Mn6Op543210987654321098765432109876543210",
- timeCreated: new Date("2025-01-25T09:18:00Z"),
- timeUsed: null,
- },
- {
- id: "key_5Qr4St3Uv2Wx1098",
- name: "CI/CD Pipeline",
- key: "oc_live_sk_5Qr4St3Uv2Wx109876543210987654321098765432109876",
- timeCreated: new Date("2025-01-20T16:45:00Z"),
- timeUsed: new Date("2025-01-28T12:30:00Z"),
- },
-]
-
export default function() {
const actor = createAsync(() => getActor())
- onMount(() => {
- console.log("MOUNTED", actor())
- })
/////////////////
// Keys section
@@ -342,9 +234,8 @@ export default function() {
</button>
</Show>
<div data-slot="api-keys-table">
- {/* when={keys()?.length */}
<Show
- when={dummyApiKeyData.length > 0}
+ when={keys()?.length}
fallback={
<div data-slot="empty-state">
<p>Create an opencode Gateway API key</p>
@@ -361,8 +252,7 @@ export default function() {
</tr>
</thead>
<tbody>
- <For each={dummyApiKeyData}>
- {/* Real data: keys() */}
+ <For each={keys()!}>
{(key) => (
<tr>
<td data-slot="key-name">{key.name}</td>
@@ -424,45 +314,6 @@ export default function() {
</div>
</section>
- {/* Payments Section */}
- <Show when={dummyPaymentData.length > 0}>
- {/* Real data condition: billingInfo() && billingInfo()!.payments.length > 0 */}
- <section data-slot="payments-section">
- <div data-slot="section-title">
- <h2>Payments History</h2>
- <p>Recent payment transactions.</p>
- </div>
- <div data-slot="payments-table">
- <table data-slot="payments-table-element">
- <thead>
- <tr>
- <th>Date</th>
- <th>Payment ID</th>
- <th>Amount</th>
- </tr>
- </thead>
- <tbody>
- <For each={dummyPaymentData}>
- {/* Real data: billingInfo()?.payments */}
- {(payment) => {
- const date = new Date(payment.timeCreated)
- return (
- <tr>
- <td data-slot="payment-date" title={formatDateUTC(date)}>
- {formatDateForTable(date)}
- </td>
- <td data-slot="payment-id">{payment.id}</td>
- <td data-slot="payment-amount">${((payment.amount ?? 0) / 100000000).toFixed(2)}</td>
- </tr>
- )
- }}
- </For>
- </tbody>
- </table>
- </div>
- </section>
- </Show>
-
{/* Usage Section */}
<section data-slot="usage-section">
<div data-slot="section-title">
@@ -471,7 +322,7 @@ export default function() {
</div>
<div data-slot="usage-table">
<Show
- when={dummyUsageData.length > 0}
+ when={billingInfo() && billingInfo()!.usage.length > 0}
fallback={
<div data-slot="empty-state">
<p>Make your first API call to get started.</p>
@@ -488,7 +339,7 @@ export default function() {
</tr>
</thead>
<tbody>
- <For each={dummyUsageData}>
+ <For each={billingInfo()!.usage}>
{(usage) => {
const totalTokens = usage.inputTokens + usage.outputTokens + (usage.reasoningTokens || 0)
const date = new Date(usage.timeCreated)
@@ -509,6 +360,44 @@ export default function() {
</Show>
</div>
</section>
+
+ {/* Payments Section */}
+ <Show when={billingInfo() && billingInfo()!.payments.length > 0}>
+ <section data-slot="payments-section">
+ <div data-slot="section-title">
+ <h2>Payments History</h2>
+ <p>Recent payment transactions.</p>
+ </div>
+ <div data-slot="payments-table">
+ <table data-slot="payments-table-element">
+ <thead>
+ <tr>
+ <th>Date</th>
+ <th>Payment ID</th>
+ <th>Amount</th>
+ </tr>
+ </thead>
+ <tbody>
+ <For each={billingInfo()!.payments}>
+ {(payment) => {
+ const date = new Date(payment.timeCreated)
+ return (
+ <tr>
+ <td data-slot="payment-date" title={formatDateUTC(date)}>
+ {formatDateForTable(date)}
+ </td>
+ <td data-slot="payment-id">{payment.id}</td>
+ <td data-slot="payment-amount">${((payment.amount ?? 0) / 100000000).toFixed(2)}</td>
+ </tr>
+ )
+ }}
+ </For>
+ </tbody>
+ </table>
+ </div>
+ </section>
+ </Show>
+
</div>
</div>
)