summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorFrank <[email protected]>2025-10-10 13:45:06 -0400
committerFrank <[email protected]>2025-10-10 13:45:06 -0400
commitd83af721a6e6269481186df80d498aa1213a6e59 (patch)
treefad37aa7f5337a615a73e5a70f4ce8109f79f6f6
parent0bc00bef32a334cd44005a1dc160c728801d3ba6 (diff)
downloadopencode-d83af721a6e6269481186df80d498aa1213a6e59.tar.gz
opencode-d83af721a6e6269481186df80d498aa1213a6e59.zip
wip: zen style api keys
-rw-r--r--packages/console/app/src/routes/workspace/[id]/keys/key-section.module.css20
-rw-r--r--packages/console/app/src/routes/workspace/[id]/keys/key-section.tsx79
2 files changed, 52 insertions, 47 deletions
diff --git a/packages/console/app/src/routes/workspace/[id]/keys/key-section.module.css b/packages/console/app/src/routes/workspace/[id]/keys/key-section.module.css
index 6a1d0c85f..ad20f1fa6 100644
--- a/packages/console/app/src/routes/workspace/[id]/keys/key-section.module.css
+++ b/packages/console/app/src/routes/workspace/[id]/keys/key-section.module.css
@@ -1,4 +1,11 @@
.root {
+ [data-slot="title-row"] {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: var(--space-4);
+ }
+
[data-component="empty-state"] {
padding: var(--space-20) var(--space-6);
text-align: center;
@@ -150,6 +157,7 @@
}
@media (max-width: 40rem) {
+
th,
td {
padding: var(--space-2) var(--space-3);
@@ -157,16 +165,22 @@
}
th {
- &:nth-child(3) /* Date */ {
+ &:nth-child(3)
+
+ /* Date */
+ {
display: none;
}
}
td {
- &:nth-child(3) /* Date */ {
+ &:nth-child(3)
+
+ /* Date */
+ {
display: none;
}
}
}
}
-}
+} \ No newline at end of file
diff --git a/packages/console/app/src/routes/workspace/[id]/keys/key-section.tsx b/packages/console/app/src/routes/workspace/[id]/keys/key-section.tsx
index 22b82ae05..87c1541d6 100644
--- a/packages/console/app/src/routes/workspace/[id]/keys/key-section.tsx
+++ b/packages/console/app/src/routes/workspace/[id]/keys/key-section.tsx
@@ -43,8 +43,9 @@ const listKeys = query(async (workspaceID: string) => {
return withActor(() => Key.list(), workspaceID)
}, "key.list")
-export function KeyCreateForm() {
+export function KeySection() {
const params = useParams()
+ const keys = createAsync(() => listKeys(params.id))
const submission = useSubmission(createKey)
const [store, setStore] = createStore({ show: false })
@@ -52,22 +53,17 @@ export function KeyCreateForm() {
createEffect(() => {
if (!submission.pending && submission.result && !submission.result.error) {
- hide()
+ setStore("show", false)
}
})
function show() {
- // submission.clear() does not clear the result in some cases, ie.
- // 1. Create key with empty name => error shows
- // 2. Put in a key name and creates the key => form hides
- // 3. Click add key button again => form shows with the same error if
- // submission.clear() is called only once
while (true) {
submission.clear()
if (!submission.result) break
}
setStore("show", true)
- input.focus()
+ setTimeout(() => input?.focus(), 0)
}
function hide() {
@@ -75,46 +71,41 @@ export function KeyCreateForm() {
}
return (
- <Show
- when={store.show}
- fallback={
- <button data-color="primary" onClick={() => show()}>
- Create API Key
- </button>
- }
- >
- <form action={createKey} method="post" data-slot="create-form">
- <div data-slot="input-container">
- <input ref={(r) => (input = r)} data-component="input" name="name" type="text" placeholder="Enter key name" />
- <Show when={submission.result && submission.result.error}>
- {(err) => <div data-slot="form-error">{err()}</div>}
- </Show>
- </div>
- <input type="hidden" name="workspaceID" value={params.id} />
- <div data-slot="form-actions">
- <button type="reset" data-color="ghost" onClick={() => hide()}>
- Cancel
- </button>
- <button type="submit" data-color="primary" disabled={submission.pending}>
- {submission.pending ? "Creating..." : "Create"}
- </button>
- </div>
- </form>
- </Show>
- )
-}
-
-export function KeySection() {
- const params = useParams()
- const keys = createAsync(() => listKeys(params.id))
-
- return (
<section class={styles.root}>
<div data-slot="section-title">
<h2>API Keys</h2>
- <p>Manage your API keys for accessing opencode services.</p>
+ <div data-slot="title-row">
+ <p>Manage your API keys for accessing opencode services.</p>
+ <button data-color="primary" onClick={() => show()}>
+ Create API Key
+ </button>
+ </div>
</div>
- <KeyCreateForm />
+ <Show when={store.show}>
+ <form action={createKey} method="post" data-slot="create-form">
+ <div data-slot="input-container">
+ <input
+ ref={(r) => (input = r)}
+ data-component="input"
+ name="name"
+ type="text"
+ placeholder="Enter key name"
+ />
+ <Show when={submission.result && submission.result.error}>
+ {(err) => <div data-slot="form-error">{err()}</div>}
+ </Show>
+ </div>
+ <input type="hidden" name="workspaceID" value={params.id} />
+ <div data-slot="form-actions">
+ <button type="reset" data-color="ghost" onClick={() => hide()}>
+ Cancel
+ </button>
+ <button type="submit" data-color="primary" disabled={submission.pending}>
+ {submission.pending ? "Creating..." : "Create"}
+ </button>
+ </div>
+ </form>
+ </Show>
<div data-slot="api-keys-table">
<Show
when={keys()?.length}