diff options
37 files changed, 809 insertions, 212 deletions
diff --git a/packages/console/app/src/lib/beta.ts b/packages/console/app/src/lib/beta.ts deleted file mode 100644 index d60a735ee..000000000 --- a/packages/console/app/src/lib/beta.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { query } from "@solidjs/router" -import { Resource } from "@opencode-ai/console-resource" - -export const beta = query(async (workspaceID?: string) => { - "use server" - return Resource.App.stage === "production" ? workspaceID === "wrk_01K46JDFR0E75SG2Q8K172KF3Y" : true -}, "beta") diff --git a/packages/console/app/src/routes/workspace.tsx b/packages/console/app/src/routes/workspace.tsx index ac394f585..f87123d31 100644 --- a/packages/console/app/src/routes/workspace.tsx +++ b/packages/console/app/src/routes/workspace.tsx @@ -8,16 +8,16 @@ import { WorkspacePicker } from "./workspace-picker" import { withActor } from "~/context/auth.withActor" import { User } from "@opencode-ai/console-core/user.js" import { Actor } from "@opencode-ai/console-core/actor.js" -import { beta } from "~/lib/beta" +import { querySessionInfo } from "./workspace/common" -const getUserInfo = query(async (workspaceID: string) => { +const getUserEmail = query(async (workspaceID: string) => { "use server" return withActor(async () => { const actor = Actor.assert("user") const email = await User.getAccountEmail(actor.properties.userID) - return { email } + return email }, workspaceID) -}, "userInfo") +}, "userEmail") const logout = action(async () => { "use server" @@ -37,8 +37,8 @@ const logout = action(async () => { export default function WorkspaceLayout(props: RouteSectionProps) { const params = useParams() - const userInfo = createAsync(() => getUserInfo(params.id)) - const isBeta = createAsync(() => beta(params.id)) + const userEmail = createAsync(() => getUserEmail(params.id)) + const sessionInfo = createAsync(() => querySessionInfo(params.id)) return ( <main data-page="workspace"> <header data-component="workspace-header"> @@ -48,10 +48,10 @@ export default function WorkspaceLayout(props: RouteSectionProps) { </A> </div> <div data-slot="header-actions"> - <Show when={isBeta()}> + <Show when={sessionInfo()?.isBeta}> <WorkspacePicker /> </Show> - <span data-slot="user">{userInfo()?.email}</span> + <span data-slot="user">{userEmail()}</span> <form action={logout} method="post"> <button type="submit" formaction={logout}> Logout diff --git a/packages/console/app/src/routes/workspace/[id].css b/packages/console/app/src/routes/workspace/[id].css index 8b318a19f..399d7e737 100644 --- a/packages/console/app/src/routes/workspace/[id].css +++ b/packages/console/app/src/routes/workspace/[id].css @@ -1,115 +1,63 @@ -[data-page="workspace-[id]"] { - max-width: 64rem; - padding: var(--space-10) var(--space-4); - margin: 0 auto; - width: 100%; - display: flex; - flex-direction: column; - gap: var(--space-10); - - @media (max-width: 30rem) { - padding-top: var(--space-4); - padding-bottom: var(--space-4); - - gap: var(--space-8); - } - - [data-slot="sections"] { - display: flex; - flex-direction: column; - gap: var(--space-16); - - @media (max-width: 30rem) { - gap: var(--space-8); - } - - section { - display: flex; - flex-direction: column; - gap: var(--space-8); - - @media (max-width: 30rem) { - gap: var(--space-6); - } - - /* Section titles */ - [data-slot="section-title"] { - display: flex; - flex-direction: column; - gap: var(--space-1); - - h2 { - font-size: var(--font-size-md); - font-weight: 600; - line-height: 1.2; - letter-spacing: -0.03125rem; - margin: 0; - color: var(--color-text-secondary); - text-transform: uppercase; - - @media (max-width: 30rem) { - font-size: var(--font-size-md); - } - } - - p { - line-height: 1.5; - font-size: var(--font-size-md); - color: var(--color-text-muted); +[data-page="workspace"] { + line-height: 1; +} - a { - color: var(--color-text-muted); - } +/* Workspace Layout */ +[data-component="workspace-container"] { + display: flex; + height: calc(100vh - 73px); +} - @media (max-width: 30rem) { - font-size: var(--font-size-sm); - } - } - } +[data-component="workspace-nav"] { + width: 240px; + flex-shrink: 0; + border-right: 1px solid var(--color-border); + padding: var(--space-6) var(--space-4); + display: flex; + flex-direction: column; + gap: var(--space-2); + + [data-nav-button] { + padding: var(--space-3) var(--space-4); + border-radius: var(--border-radius-sm); + color: var(--color-text-muted); + text-decoration: none; + font-size: var(--font-size-sm); + font-weight: 500; + transition: all 0.15s ease; + + &:hover { + background-color: var(--color-surface-hover); + color: var(--color-text); } - section:not(:last-child) { - border-bottom: 1px solid var(--color-border); - padding-bottom: var(--space-16); - @media (max-width: 30rem) { - padding-bottom: var(--space-8); - } + &.active { + background-color: var(--color-surface-hover); + color: var(--color-text); } } +} - /* Title section */ - [data-component="title-section"] { - display: flex; - flex-direction: column; - gap: var(--space-2); - padding-bottom: var(--space-8); - border-bottom: 1px solid var(--color-border); - - @media (max-width: 30rem) { - padding-bottom: var(--space-6); - } - - h1 { - font-size: var(--font-size-2xl); - font-weight: 500; - line-height: 1.2; - letter-spacing: -0.03125rem; - margin: 0; - text-transform: uppercase; +[data-component="workspace-content"] { + flex: 1; + padding: var(--space-6) var(--space-8); + overflow-y: auto; - @media (max-width: 30rem) { - font-size: var(--font-size-xl); - } - } + @media (max-width: 48rem) { + padding: var(--space-6) var(--space-4); + } +} - p { - line-height: 1.5; - font-size: var(--font-size-md); - color: var(--color-text-muted); +@media (max-width: 48rem) { + [data-component="workspace-container"] { + flex-direction: column; + } - a { - color: var(--color-text-muted); - } - } + [data-component="workspace-nav"] { + width: 100%; + flex-direction: row; + border-right: none; + border-bottom: 1px solid var(--color-border); + padding: var(--space-4); } -} +}
\ No newline at end of file diff --git a/packages/console/app/src/routes/workspace/[id].tsx b/packages/console/app/src/routes/workspace/[id].tsx index 15aeb57a0..1da24dc32 100644 --- a/packages/console/app/src/routes/workspace/[id].tsx +++ b/packages/console/app/src/routes/workspace/[id].tsx @@ -1,70 +1,35 @@ -import "./[id].css" -import { MonthlyLimitSection } from "./monthly-limit-section" -import { NewUserSection } from "./new-user-section" -import { BillingSection } from "./billing-section" -import { PaymentSection } from "./payment-section" -import { UsageSection } from "./usage-section" -import { KeySection } from "./key-section" -import { MemberSection } from "./member-section" -import { SettingsSection } from "./settings-section" -import { ModelSection } from "./model-section" -import { ProviderSection } from "./provider-section" import { Show } from "solid-js" -import { createAsync, query, useParams } from "@solidjs/router" -import { Actor } from "@opencode-ai/console-core/actor.js" -import { withActor } from "~/context/auth.withActor" -import { User } from "@opencode-ai/console-core/user.js" -import { beta } from "~/lib/beta" - -const getUserInfo = query(async (workspaceID: string) => { - "use server" - return withActor(async () => { - const actor = Actor.assert("user") - const user = await User.fromID(actor.properties.userID) - return { - isAdmin: user?.role === "admin", - } - }, workspaceID) -}, "user.get") +import { createAsync, RouteSectionProps, useParams, A } from "@solidjs/router" +import { querySessionInfo } from "./common" +import "./[id].css" -export default function () { +export default function WorkspaceLayout(props: RouteSectionProps) { const params = useParams() - const userInfo = createAsync(() => getUserInfo(params.id)) - const isBeta = createAsync(() => beta(params.id)) - + const userInfo = createAsync(() => querySessionInfo(params.id)) return ( - <div data-page="workspace-[id]"> - <section data-component="title-section"> - <h1>Zen</h1> - <p> - Curated list of models provided by opencode.{" "} - <a target="_blank" href="/docs/zen"> - Learn more - </a> - . - </p> - </section> - - <div data-slot="sections"> - <NewUserSection /> - <KeySection /> - <Show when={isBeta()}> - <MemberSection /> - </Show> - <Show when={userInfo()?.isAdmin}> - <Show when={isBeta()}> - <SettingsSection /> - <ModelSection /> - <ProviderSection /> + <main data-page="workspace"> + <div data-component="workspace-container"> + <nav data-component="workspace-nav"> + <A href={`/workspace/${params.id}`} end activeClass="active" data-nav-button> + Zen + </A> + <Show when={userInfo()?.isAdmin}> + <A href={`/workspace/${params.id}/billing`} activeClass="active" data-nav-button> + Billing + </A> </Show> - <BillingSection /> - <MonthlyLimitSection /> - </Show> - <UsageSection /> - <Show when={userInfo()?.isAdmin}> - <PaymentSection /> - </Show> + <A href={`/workspace/${params.id}/keys`} activeClass="active" data-nav-button> + API Keys + </A> + <A href={`/workspace/${params.id}/members`} activeClass="active" data-nav-button> + Members + </A> + <A href={`/workspace/${params.id}/settings`} activeClass="active" data-nav-button> + Settings + </A> + </nav> + <div data-component="workspace-content">{props.children}</div> </div> - </div> + </main> ) } diff --git a/packages/console/app/src/routes/workspace/billing-section.module.css b/packages/console/app/src/routes/workspace/[id]/billing/billing-section.module.css index 0bb5709cb..0bb5709cb 100644 --- a/packages/console/app/src/routes/workspace/billing-section.module.css +++ b/packages/console/app/src/routes/workspace/[id]/billing/billing-section.module.css diff --git a/packages/console/app/src/routes/workspace/billing-section.tsx b/packages/console/app/src/routes/workspace/[id]/billing/billing-section.tsx index 295ad3396..295ad3396 100644 --- a/packages/console/app/src/routes/workspace/billing-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/billing/billing-section.tsx diff --git a/packages/console/app/src/routes/workspace/[id]/billing/index.css b/packages/console/app/src/routes/workspace/[id]/billing/index.css new file mode 100644 index 000000000..5124c78cf --- /dev/null +++ b/packages/console/app/src/routes/workspace/[id]/billing/index.css @@ -0,0 +1,116 @@ +[data-page="workspace-[id]"] { + max-width: 64rem; + padding: var(--space-10) var(--space-4); + margin: 0 auto; + width: 100%; + display: flex; + flex-direction: column; + gap: var(--space-10); + + @media (max-width: 30rem) { + padding-top: var(--space-4); + padding-bottom: var(--space-4); + + gap: var(--space-8); + } + + [data-slot="sections"] { + display: flex; + flex-direction: column; + gap: var(--space-16); + + @media (max-width: 30rem) { + gap: var(--space-8); + } + + section { + display: flex; + flex-direction: column; + gap: var(--space-8); + + @media (max-width: 30rem) { + gap: var(--space-6); + } + + /* Section titles */ + [data-slot="section-title"] { + display: flex; + flex-direction: column; + gap: var(--space-1); + + h2 { + font-size: var(--font-size-md); + font-weight: 600; + line-height: 1.2; + letter-spacing: -0.03125rem; + margin: 0; + color: var(--color-text-secondary); + text-transform: uppercase; + + @media (max-width: 30rem) { + font-size: var(--font-size-md); + } + } + + p { + line-height: 1.5; + font-size: var(--font-size-md); + color: var(--color-text-muted); + + a { + color: var(--color-text-muted); + } + + @media (max-width: 30rem) { + font-size: var(--font-size-sm); + } + } + } + } + + section:not(:last-child) { + border-bottom: 1px solid var(--color-border); + padding-bottom: var(--space-16); + + @media (max-width: 30rem) { + padding-bottom: var(--space-8); + } + } + } + + /* Title section */ + [data-component="title-section"] { + display: flex; + flex-direction: column; + gap: var(--space-2); + padding-bottom: var(--space-8); + border-bottom: 1px solid var(--color-border); + + @media (max-width: 30rem) { + padding-bottom: var(--space-6); + } + + h1 { + font-size: var(--font-size-2xl); + font-weight: 500; + line-height: 1.2; + letter-spacing: -0.03125rem; + margin: 0; + text-transform: uppercase; + + @media (max-width: 30rem) { + font-size: var(--font-size-xl); + } + } + + p { + line-height: 1.5; + font-size: var(--font-size-md); + color: var(--color-text-muted); + + a { + color: var(--color-text-muted); + } + } + } +}
\ No newline at end of file diff --git a/packages/console/app/src/routes/workspace/[id]/billing/index.tsx b/packages/console/app/src/routes/workspace/[id]/billing/index.tsx new file mode 100644 index 000000000..913d4f92f --- /dev/null +++ b/packages/console/app/src/routes/workspace/[id]/billing/index.tsx @@ -0,0 +1,24 @@ +import "./index.css" +import { MonthlyLimitSection } from "./monthly-limit-section" +import { BillingSection } from "./billing-section" +import { PaymentSection } from "./payment-section" +import { Show } from "solid-js" +import { createAsync, useParams } from "@solidjs/router" +import { querySessionInfo } from "../../common" + +export default function () { + const params = useParams() + const userInfo = createAsync(() => querySessionInfo(params.id)) + + return ( + <div data-page="workspace-[id]"> + <div data-slot="sections"> + <Show when={userInfo()?.isAdmin}> + <BillingSection /> + <MonthlyLimitSection /> + <PaymentSection /> + </Show> + </div> + </div> + ) +} diff --git a/packages/console/app/src/routes/workspace/monthly-limit-section.module.css b/packages/console/app/src/routes/workspace/[id]/billing/monthly-limit-section.module.css index 02de058e4..02de058e4 100644 --- a/packages/console/app/src/routes/workspace/monthly-limit-section.module.css +++ b/packages/console/app/src/routes/workspace/[id]/billing/monthly-limit-section.module.css diff --git a/packages/console/app/src/routes/workspace/monthly-limit-section.tsx b/packages/console/app/src/routes/workspace/[id]/billing/monthly-limit-section.tsx index dbeda115c..dbeda115c 100644 --- a/packages/console/app/src/routes/workspace/monthly-limit-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/billing/monthly-limit-section.tsx diff --git a/packages/console/app/src/routes/workspace/payment-section.module.css b/packages/console/app/src/routes/workspace/[id]/billing/payment-section.module.css index 2e1afe78b..2e1afe78b 100644 --- a/packages/console/app/src/routes/workspace/payment-section.module.css +++ b/packages/console/app/src/routes/workspace/[id]/billing/payment-section.module.css diff --git a/packages/console/app/src/routes/workspace/payment-section.tsx b/packages/console/app/src/routes/workspace/[id]/billing/payment-section.tsx index c35a50660..d3520bea4 100644 --- a/packages/console/app/src/routes/workspace/payment-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/billing/payment-section.tsx @@ -1,8 +1,8 @@ import { Billing } from "@opencode-ai/console-core/billing.js" import { query, action, useParams, createAsync, useAction } from "@solidjs/router" -import { For } from "solid-js" +import { For, Show } from "solid-js" import { withActor } from "~/context/auth.withActor" -import { formatDateUTC, formatDateForTable } from "./common" +import { formatDateUTC, formatDateForTable } from "../common" import styles from "./payment-section.module.css" const getPaymentsInfo = query(async (workspaceID: string) => { @@ -19,7 +19,6 @@ const downloadReceipt = action(async (workspaceID: string, paymentID: string) => export function PaymentSection() { const params = useParams() - // ORIGINAL CODE - COMMENTED OUT FOR TESTING const payments = createAsync(() => getPaymentsInfo(params.id)) const downloadReceiptAction = useAction(downloadReceipt) @@ -58,8 +57,7 @@ export function PaymentSection() { // ] return ( - payments() && - payments()!.length > 0 && ( + <Show when={payments() && payments()!.length > 0}> <section class={styles.root}> <div data-slot="section-title"> <h2>Payments History</h2> @@ -109,6 +107,6 @@ export function PaymentSection() { </table> </div> </section> - ) + </Show> ) } diff --git a/packages/console/app/src/routes/workspace/[id]/common.tsx b/packages/console/app/src/routes/workspace/[id]/common.tsx new file mode 100644 index 000000000..f85fd8423 --- /dev/null +++ b/packages/console/app/src/routes/workspace/[id]/common.tsx @@ -0,0 +1,25 @@ +export function formatDateForTable(date: Date) { + const options: Intl.DateTimeFormatOptions = { + day: "numeric", + month: "short", + hour: "numeric", + minute: "2-digit", + hour12: true, + } + return date.toLocaleDateString("en-GB", options).replace(",", ",") +} + +export function formatDateUTC(date: Date) { + const options: Intl.DateTimeFormatOptions = { + weekday: "short", + year: "numeric", + month: "short", + day: "numeric", + hour: "numeric", + minute: "2-digit", + second: "2-digit", + timeZoneName: "short", + timeZone: "UTC", + } + return date.toLocaleDateString("en-US", options) +} diff --git a/packages/console/app/src/routes/workspace/[id]/index.css b/packages/console/app/src/routes/workspace/[id]/index.css new file mode 100644 index 000000000..5124c78cf --- /dev/null +++ b/packages/console/app/src/routes/workspace/[id]/index.css @@ -0,0 +1,116 @@ +[data-page="workspace-[id]"] { + max-width: 64rem; + padding: var(--space-10) var(--space-4); + margin: 0 auto; + width: 100%; + display: flex; + flex-direction: column; + gap: var(--space-10); + + @media (max-width: 30rem) { + padding-top: var(--space-4); + padding-bottom: var(--space-4); + + gap: var(--space-8); + } + + [data-slot="sections"] { + display: flex; + flex-direction: column; + gap: var(--space-16); + + @media (max-width: 30rem) { + gap: var(--space-8); + } + + section { + display: flex; + flex-direction: column; + gap: var(--space-8); + + @media (max-width: 30rem) { + gap: var(--space-6); + } + + /* Section titles */ + [data-slot="section-title"] { + display: flex; + flex-direction: column; + gap: var(--space-1); + + h2 { + font-size: var(--font-size-md); + font-weight: 600; + line-height: 1.2; + letter-spacing: -0.03125rem; + margin: 0; + color: var(--color-text-secondary); + text-transform: uppercase; + + @media (max-width: 30rem) { + font-size: var(--font-size-md); + } + } + + p { + line-height: 1.5; + font-size: var(--font-size-md); + color: var(--color-text-muted); + + a { + color: var(--color-text-muted); + } + + @media (max-width: 30rem) { + font-size: var(--font-size-sm); + } + } + } + } + + section:not(:last-child) { + border-bottom: 1px solid var(--color-border); + padding-bottom: var(--space-16); + + @media (max-width: 30rem) { + padding-bottom: var(--space-8); + } + } + } + + /* Title section */ + [data-component="title-section"] { + display: flex; + flex-direction: column; + gap: var(--space-2); + padding-bottom: var(--space-8); + border-bottom: 1px solid var(--color-border); + + @media (max-width: 30rem) { + padding-bottom: var(--space-6); + } + + h1 { + font-size: var(--font-size-2xl); + font-weight: 500; + line-height: 1.2; + letter-spacing: -0.03125rem; + margin: 0; + text-transform: uppercase; + + @media (max-width: 30rem) { + font-size: var(--font-size-xl); + } + } + + p { + line-height: 1.5; + font-size: var(--font-size-md); + color: var(--color-text-muted); + + a { + color: var(--color-text-muted); + } + } + } +}
\ No newline at end of file diff --git a/packages/console/app/src/routes/workspace/[id]/index.tsx b/packages/console/app/src/routes/workspace/[id]/index.tsx new file mode 100644 index 000000000..1345bf40f --- /dev/null +++ b/packages/console/app/src/routes/workspace/[id]/index.tsx @@ -0,0 +1,39 @@ +import "./index.css" +import { NewUserSection } from "./new-user-section" +import { UsageSection } from "./usage-section" +import { MemberSection } from "./members/member-section" +import { SettingsSection } from "./settings/settings-section" +import { ModelSection } from "./model-section" +import { ProviderSection } from "./provider-section" +import { Show } from "solid-js" +import { createAsync, useParams } from "@solidjs/router" +import { querySessionInfo } from "../common" + +export default function () { + const params = useParams() + const userInfo = createAsync(() => querySessionInfo(params.id)) + + return ( + <div data-page="workspace-[id]"> + <section data-component="title-section"> + <h1>Zen</h1> + <p> + Curated list of models provided by opencode.{" "} + <a target="_blank" href="/docs/zen"> + Learn more + </a> + . + </p> + </section> + + <div data-slot="sections"> + <NewUserSection /> + <Show when={userInfo()?.isAdmin}> + <ModelSection /> + <ProviderSection /> + </Show> + <UsageSection /> + </div> + </div> + ) +} diff --git a/packages/console/app/src/routes/workspace/[id]/keys/index.css b/packages/console/app/src/routes/workspace/[id]/keys/index.css new file mode 100644 index 000000000..5124c78cf --- /dev/null +++ b/packages/console/app/src/routes/workspace/[id]/keys/index.css @@ -0,0 +1,116 @@ +[data-page="workspace-[id]"] { + max-width: 64rem; + padding: var(--space-10) var(--space-4); + margin: 0 auto; + width: 100%; + display: flex; + flex-direction: column; + gap: var(--space-10); + + @media (max-width: 30rem) { + padding-top: var(--space-4); + padding-bottom: var(--space-4); + + gap: var(--space-8); + } + + [data-slot="sections"] { + display: flex; + flex-direction: column; + gap: var(--space-16); + + @media (max-width: 30rem) { + gap: var(--space-8); + } + + section { + display: flex; + flex-direction: column; + gap: var(--space-8); + + @media (max-width: 30rem) { + gap: var(--space-6); + } + + /* Section titles */ + [data-slot="section-title"] { + display: flex; + flex-direction: column; + gap: var(--space-1); + + h2 { + font-size: var(--font-size-md); + font-weight: 600; + line-height: 1.2; + letter-spacing: -0.03125rem; + margin: 0; + color: var(--color-text-secondary); + text-transform: uppercase; + + @media (max-width: 30rem) { + font-size: var(--font-size-md); + } + } + + p { + line-height: 1.5; + font-size: var(--font-size-md); + color: var(--color-text-muted); + + a { + color: var(--color-text-muted); + } + + @media (max-width: 30rem) { + font-size: var(--font-size-sm); + } + } + } + } + + section:not(:last-child) { + border-bottom: 1px solid var(--color-border); + padding-bottom: var(--space-16); + + @media (max-width: 30rem) { + padding-bottom: var(--space-8); + } + } + } + + /* Title section */ + [data-component="title-section"] { + display: flex; + flex-direction: column; + gap: var(--space-2); + padding-bottom: var(--space-8); + border-bottom: 1px solid var(--color-border); + + @media (max-width: 30rem) { + padding-bottom: var(--space-6); + } + + h1 { + font-size: var(--font-size-2xl); + font-weight: 500; + line-height: 1.2; + letter-spacing: -0.03125rem; + margin: 0; + text-transform: uppercase; + + @media (max-width: 30rem) { + font-size: var(--font-size-xl); + } + } + + p { + line-height: 1.5; + font-size: var(--font-size-md); + color: var(--color-text-muted); + + a { + color: var(--color-text-muted); + } + } + } +}
\ No newline at end of file diff --git a/packages/console/app/src/routes/workspace/[id]/keys/index.tsx b/packages/console/app/src/routes/workspace/[id]/keys/index.tsx new file mode 100644 index 000000000..0fd3cdbd3 --- /dev/null +++ b/packages/console/app/src/routes/workspace/[id]/keys/index.tsx @@ -0,0 +1,12 @@ +import "./index.css" +import { KeySection } from "./key-section" + +export default function () { + return ( + <div data-page="workspace-[id]"> + <div data-slot="sections"> + <KeySection /> + </div> + </div> + ) +} diff --git a/packages/console/app/src/routes/workspace/key-section.module.css b/packages/console/app/src/routes/workspace/[id]/keys/key-section.module.css index 6a1d0c85f..6a1d0c85f 100644 --- a/packages/console/app/src/routes/workspace/key-section.module.css +++ b/packages/console/app/src/routes/workspace/[id]/keys/key-section.module.css diff --git a/packages/console/app/src/routes/workspace/key-section.tsx b/packages/console/app/src/routes/workspace/[id]/keys/key-section.tsx index 3b7e399aa..22b82ae05 100644 --- a/packages/console/app/src/routes/workspace/key-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/keys/key-section.tsx @@ -4,7 +4,7 @@ import { IconCopy, IconCheck } from "~/component/icon" import { Key } from "@opencode-ai/console-core/key.js" import { withActor } from "~/context/auth.withActor" import { createStore } from "solid-js/store" -import { formatDateUTC, formatDateForTable } from "./common" +import { formatDateUTC, formatDateForTable } from "../common" import styles from "./key-section.module.css" import { Actor } from "@opencode-ai/console-core/actor.js" diff --git a/packages/console/app/src/routes/workspace/[id]/members/index.css b/packages/console/app/src/routes/workspace/[id]/members/index.css new file mode 100644 index 000000000..5124c78cf --- /dev/null +++ b/packages/console/app/src/routes/workspace/[id]/members/index.css @@ -0,0 +1,116 @@ +[data-page="workspace-[id]"] { + max-width: 64rem; + padding: var(--space-10) var(--space-4); + margin: 0 auto; + width: 100%; + display: flex; + flex-direction: column; + gap: var(--space-10); + + @media (max-width: 30rem) { + padding-top: var(--space-4); + padding-bottom: var(--space-4); + + gap: var(--space-8); + } + + [data-slot="sections"] { + display: flex; + flex-direction: column; + gap: var(--space-16); + + @media (max-width: 30rem) { + gap: var(--space-8); + } + + section { + display: flex; + flex-direction: column; + gap: var(--space-8); + + @media (max-width: 30rem) { + gap: var(--space-6); + } + + /* Section titles */ + [data-slot="section-title"] { + display: flex; + flex-direction: column; + gap: var(--space-1); + + h2 { + font-size: var(--font-size-md); + font-weight: 600; + line-height: 1.2; + letter-spacing: -0.03125rem; + margin: 0; + color: var(--color-text-secondary); + text-transform: uppercase; + + @media (max-width: 30rem) { + font-size: var(--font-size-md); + } + } + + p { + line-height: 1.5; + font-size: var(--font-size-md); + color: var(--color-text-muted); + + a { + color: var(--color-text-muted); + } + + @media (max-width: 30rem) { + font-size: var(--font-size-sm); + } + } + } + } + + section:not(:last-child) { + border-bottom: 1px solid var(--color-border); + padding-bottom: var(--space-16); + + @media (max-width: 30rem) { + padding-bottom: var(--space-8); + } + } + } + + /* Title section */ + [data-component="title-section"] { + display: flex; + flex-direction: column; + gap: var(--space-2); + padding-bottom: var(--space-8); + border-bottom: 1px solid var(--color-border); + + @media (max-width: 30rem) { + padding-bottom: var(--space-6); + } + + h1 { + font-size: var(--font-size-2xl); + font-weight: 500; + line-height: 1.2; + letter-spacing: -0.03125rem; + margin: 0; + text-transform: uppercase; + + @media (max-width: 30rem) { + font-size: var(--font-size-xl); + } + } + + p { + line-height: 1.5; + font-size: var(--font-size-md); + color: var(--color-text-muted); + + a { + color: var(--color-text-muted); + } + } + } +}
\ No newline at end of file diff --git a/packages/console/app/src/routes/workspace/[id]/members/index.tsx b/packages/console/app/src/routes/workspace/[id]/members/index.tsx new file mode 100644 index 000000000..5845e144c --- /dev/null +++ b/packages/console/app/src/routes/workspace/[id]/members/index.tsx @@ -0,0 +1,12 @@ +import "./index.css" +import { MemberSection } from "./member-section" + +export default function () { + return ( + <div data-page="workspace-[id]"> + <div data-slot="sections"> + <MemberSection /> + </div> + </div> + ) +} diff --git a/packages/console/app/src/routes/workspace/member-section.module.css b/packages/console/app/src/routes/workspace/[id]/members/member-section.module.css index 16b6ff8d2..16b6ff8d2 100644 --- a/packages/console/app/src/routes/workspace/member-section.module.css +++ b/packages/console/app/src/routes/workspace/[id]/members/member-section.module.css diff --git a/packages/console/app/src/routes/workspace/member-section.tsx b/packages/console/app/src/routes/workspace/[id]/members/member-section.tsx index b13e8e5ed..b13e8e5ed 100644 --- a/packages/console/app/src/routes/workspace/member-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/members/member-section.tsx diff --git a/packages/console/app/src/routes/workspace/model-section.module.css b/packages/console/app/src/routes/workspace/[id]/model-section.module.css index 5a98c9b15..5a98c9b15 100644 --- a/packages/console/app/src/routes/workspace/model-section.module.css +++ b/packages/console/app/src/routes/workspace/[id]/model-section.module.css diff --git a/packages/console/app/src/routes/workspace/model-section.tsx b/packages/console/app/src/routes/workspace/[id]/model-section.tsx index 4128b4a2c..4128b4a2c 100644 --- a/packages/console/app/src/routes/workspace/model-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/model-section.tsx diff --git a/packages/console/app/src/routes/workspace/new-user-section.module.css b/packages/console/app/src/routes/workspace/[id]/new-user-section.module.css index 2edc7cc14..2edc7cc14 100644 --- a/packages/console/app/src/routes/workspace/new-user-section.module.css +++ b/packages/console/app/src/routes/workspace/[id]/new-user-section.module.css diff --git a/packages/console/app/src/routes/workspace/new-user-section.tsx b/packages/console/app/src/routes/workspace/[id]/new-user-section.tsx index b694801cc..b694801cc 100644 --- a/packages/console/app/src/routes/workspace/new-user-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/new-user-section.tsx diff --git a/packages/console/app/src/routes/workspace/provider-section.module.css b/packages/console/app/src/routes/workspace/[id]/provider-section.module.css index 5f18862f5..5f18862f5 100644 --- a/packages/console/app/src/routes/workspace/provider-section.module.css +++ b/packages/console/app/src/routes/workspace/[id]/provider-section.module.css diff --git a/packages/console/app/src/routes/workspace/provider-section.tsx b/packages/console/app/src/routes/workspace/[id]/provider-section.tsx index 856b3a6a2..856b3a6a2 100644 --- a/packages/console/app/src/routes/workspace/provider-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/provider-section.tsx diff --git a/packages/console/app/src/routes/workspace/[id]/settings/index.css b/packages/console/app/src/routes/workspace/[id]/settings/index.css new file mode 100644 index 000000000..5124c78cf --- /dev/null +++ b/packages/console/app/src/routes/workspace/[id]/settings/index.css @@ -0,0 +1,116 @@ +[data-page="workspace-[id]"] { + max-width: 64rem; + padding: var(--space-10) var(--space-4); + margin: 0 auto; + width: 100%; + display: flex; + flex-direction: column; + gap: var(--space-10); + + @media (max-width: 30rem) { + padding-top: var(--space-4); + padding-bottom: var(--space-4); + + gap: var(--space-8); + } + + [data-slot="sections"] { + display: flex; + flex-direction: column; + gap: var(--space-16); + + @media (max-width: 30rem) { + gap: var(--space-8); + } + + section { + display: flex; + flex-direction: column; + gap: var(--space-8); + + @media (max-width: 30rem) { + gap: var(--space-6); + } + + /* Section titles */ + [data-slot="section-title"] { + display: flex; + flex-direction: column; + gap: var(--space-1); + + h2 { + font-size: var(--font-size-md); + font-weight: 600; + line-height: 1.2; + letter-spacing: -0.03125rem; + margin: 0; + color: var(--color-text-secondary); + text-transform: uppercase; + + @media (max-width: 30rem) { + font-size: var(--font-size-md); + } + } + + p { + line-height: 1.5; + font-size: var(--font-size-md); + color: var(--color-text-muted); + + a { + color: var(--color-text-muted); + } + + @media (max-width: 30rem) { + font-size: var(--font-size-sm); + } + } + } + } + + section:not(:last-child) { + border-bottom: 1px solid var(--color-border); + padding-bottom: var(--space-16); + + @media (max-width: 30rem) { + padding-bottom: var(--space-8); + } + } + } + + /* Title section */ + [data-component="title-section"] { + display: flex; + flex-direction: column; + gap: var(--space-2); + padding-bottom: var(--space-8); + border-bottom: 1px solid var(--color-border); + + @media (max-width: 30rem) { + padding-bottom: var(--space-6); + } + + h1 { + font-size: var(--font-size-2xl); + font-weight: 500; + line-height: 1.2; + letter-spacing: -0.03125rem; + margin: 0; + text-transform: uppercase; + + @media (max-width: 30rem) { + font-size: var(--font-size-xl); + } + } + + p { + line-height: 1.5; + font-size: var(--font-size-md); + color: var(--color-text-muted); + + a { + color: var(--color-text-muted); + } + } + } +}
\ No newline at end of file diff --git a/packages/console/app/src/routes/workspace/[id]/settings/index.tsx b/packages/console/app/src/routes/workspace/[id]/settings/index.tsx new file mode 100644 index 000000000..972154aa3 --- /dev/null +++ b/packages/console/app/src/routes/workspace/[id]/settings/index.tsx @@ -0,0 +1,12 @@ +import "./index.css" +import { SettingsSection } from "./settings-section" + +export default function () { + return ( + <div data-page="workspace-[id]"> + <div data-slot="sections"> + <SettingsSection /> + </div> + </div> + ) +} diff --git a/packages/console/app/src/routes/workspace/settings-section.module.css b/packages/console/app/src/routes/workspace/[id]/settings/settings-section.module.css index e3a5ad508..e3a5ad508 100644 --- a/packages/console/app/src/routes/workspace/settings-section.module.css +++ b/packages/console/app/src/routes/workspace/[id]/settings/settings-section.module.css diff --git a/packages/console/app/src/routes/workspace/settings-section.tsx b/packages/console/app/src/routes/workspace/[id]/settings/settings-section.tsx index 0fc0158da..0fc0158da 100644 --- a/packages/console/app/src/routes/workspace/settings-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/settings/settings-section.tsx diff --git a/packages/console/app/src/routes/workspace/usage-section.module.css b/packages/console/app/src/routes/workspace/[id]/usage-section.module.css index 1a772ba87..1a772ba87 100644 --- a/packages/console/app/src/routes/workspace/usage-section.module.css +++ b/packages/console/app/src/routes/workspace/[id]/usage-section.module.css diff --git a/packages/console/app/src/routes/workspace/usage-section.tsx b/packages/console/app/src/routes/workspace/[id]/usage-section.tsx index 9f65fe5f7..9f65fe5f7 100644 --- a/packages/console/app/src/routes/workspace/usage-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/usage-section.tsx diff --git a/packages/console/app/src/routes/workspace/common.tsx b/packages/console/app/src/routes/workspace/common.tsx index f85fd8423..d1f1aba81 100644 --- a/packages/console/app/src/routes/workspace/common.tsx +++ b/packages/console/app/src/routes/workspace/common.tsx @@ -1,25 +1,14 @@ -export function formatDateForTable(date: Date) { - const options: Intl.DateTimeFormatOptions = { - day: "numeric", - month: "short", - hour: "numeric", - minute: "2-digit", - hour12: true, - } - return date.toLocaleDateString("en-GB", options).replace(",", ",") -} +import { Resource } from "@opencode-ai/console-resource" +import { Actor } from "@opencode-ai/console-core/actor.js" +import { query } from "@solidjs/router" +import { withActor } from "~/context/auth.withActor" -export function formatDateUTC(date: Date) { - const options: Intl.DateTimeFormatOptions = { - weekday: "short", - year: "numeric", - month: "short", - day: "numeric", - hour: "numeric", - minute: "2-digit", - second: "2-digit", - timeZoneName: "short", - timeZone: "UTC", - } - return date.toLocaleDateString("en-US", options) -} +export const querySessionInfo = query(async (workspaceID: string) => { + "use server" + return withActor(() => { + return { + isAdmin: Actor.userRole() === "admin", + isBeta: Resource.App.stage === "production" ? workspaceID === "wrk_01K46JDFR0E75SG2Q8K172KF3Y" : true, + } + }, workspaceID) +}, "session.get") diff --git a/packages/console/app/src/routes/workspace/index.tsx b/packages/console/app/src/routes/workspace/index.tsx deleted file mode 100644 index e69de29bb..000000000 --- a/packages/console/app/src/routes/workspace/index.tsx +++ /dev/null |
