diff options
| author | Frank <[email protected]> | 2025-09-19 14:08:02 -0400 |
|---|---|---|
| committer | Frank <[email protected]> | 2025-09-19 14:16:53 -0400 |
| commit | 9420d80b73add9f65d5ae7469e5eb82bdc710894 (patch) | |
| tree | 880405e710c063740df9f653db9ac78f910056d7 /packages/console/app/src/component | |
| parent | c21161b75e627ec1af22947f465ee72027918d31 (diff) | |
| download | opencode-9420d80b73add9f65d5ae7469e5eb82bdc710894.tar.gz opencode-9420d80b73add9f65d5ae7469e5eb82bdc710894.zip | |
zen: data share
Diffstat (limited to 'packages/console/app/src/component')
| -rw-r--r-- | packages/console/app/src/component/workspace/privacy-section.module.css | 114 | ||||
| -rw-r--r-- | packages/console/app/src/component/workspace/privacy-section.tsx | 98 |
2 files changed, 212 insertions, 0 deletions
diff --git a/packages/console/app/src/component/workspace/privacy-section.module.css b/packages/console/app/src/component/workspace/privacy-section.module.css new file mode 100644 index 000000000..0bb5709cb --- /dev/null +++ b/packages/console/app/src/component/workspace/privacy-section.module.css @@ -0,0 +1,114 @@ +.root { + [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); + padding: var(--space-4); + border: 1px solid var(--color-border); + border-radius: var(--border-radius-sm); + min-width: 14.5rem; + width: fit-content; + + @media (max-width: 30rem) { + width: 100%; + } + + [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; + color: var(--color-text-muted); + } + + [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; + + @media (max-width: 30rem) { + flex-direction: column; + + > button { + width: 100%; + } + } + + [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; + } + } + } +} diff --git a/packages/console/app/src/component/workspace/privacy-section.tsx b/packages/console/app/src/component/workspace/privacy-section.tsx new file mode 100644 index 000000000..12dd3b203 --- /dev/null +++ b/packages/console/app/src/component/workspace/privacy-section.tsx @@ -0,0 +1,98 @@ +import { json, query, action, useParams, createAsync, useSubmission } from "@solidjs/router" +import { withActor } from "~/context/auth.withActor" +import styles from "./billing-section.module.css" +import { Database, eq } from "@opencode/console-core/drizzle/index.js" +import { WorkspaceTable } from "@opencode/console-core/schema/workspace.sql.js" +import { Show } from "solid-js" + +const updateShare = action(async (form: FormData) => { + "use server" + const workspaceID = form.get("workspaceID")?.toString() + if (!workspaceID) return { error: "Workspace ID is required" } + const dataShare = form.get("dataShare")?.toString() === "true" ? true : null + return json( + await withActor(() => { + return Database.use((tx) => + tx + .update(WorkspaceTable) + .set({ + dataShare, + }) + .where(eq(WorkspaceTable.id, workspaceID)), + ) + }, workspaceID), + { revalidate: getWorkspaceInfo.key }, + ) +}, "workspace.disableShare") + +const getWorkspaceInfo = query(async (workspaceID: string) => { + "use server" + return withActor(() => { + return Database.use((tx) => + tx + .select({ + dataShare: WorkspaceTable.dataShare, + }) + .from(WorkspaceTable) + .where(eq(WorkspaceTable.id, workspaceID)) + .then((r) => r[0]), + ) + }, workspaceID) +}, "workspace.get") + +export function PrivacySection() { + const params = useParams() + const workspaceInfo = createAsync(() => getWorkspaceInfo(params.id)) + const updateShareSubmission = useSubmission(updateShare) + + return ( + <section class={styles.root}> + <div data-slot="section-title"> + <h2>Privacy controls</h2> + <p> + Some providers offer data-sharing programs. If you opt in, you voluntarily <b>share your usage data</b> with + them, which they may use to improve their services, including <b>model training</b>. + </p> + <br /> + <p> + By opting in, you gain access to <b>discounted pricing</b> from the provider. You can opt in or out at any + time. + </p> + <br /> + <p> + <a target="_blank" href="/docs/zen"> + Learn more + </a> + </p> + </div> + <Show when={workspaceInfo()?.dataShare}> + <div data-slot="payment"> + <div data-slot="credit-card"> + <div data-slot="card-details"> + <span data-slot="number">You are currently opted in to the data-sharing program.</span> + </div> + </div> + </div> + </Show> + <div data-slot="section-content"> + <div data-slot="payment"> + <div data-slot="button-row"> + <form action={updateShare} method="post" data-slot="create-form"> + <input type="hidden" name="workspaceID" value={params.id} /> + <input type="hidden" name="dataShare" value={workspaceInfo()?.dataShare ? "false" : "true"} /> + <button data-color="ghost" type="submit" disabled={updateShareSubmission.pending}> + {workspaceInfo()?.dataShare + ? updateShareSubmission.pending + ? "Opting out..." + : "Opt out" + : updateShareSubmission.pending + ? "Opting in..." + : "Opt in"} + </button> + </form> + </div> + </div> + </div> + </section> + ) +} |
