diff options
| author | Frank <[email protected]> | 2025-09-12 14:22:40 -0400 |
|---|---|---|
| committer | Frank <[email protected]> | 2025-09-12 14:22:42 -0400 |
| commit | c6c153de95668b6e3a3e08e122cad06b8e0f40e6 (patch) | |
| tree | 17235e011ede1804b8ad554ddcf9b29b6b9e82a5 /cloud/app/src | |
| parent | 417e8f619cea713e307c1efbf0b56964834b3731 (diff) | |
| download | opencode-c6c153de95668b6e3a3e08e122cad06b8e0f40e6.tar.gz opencode-c6c153de95668b6e3a3e08e122cad06b8e0f40e6.zip | |
wip: zen
Diffstat (limited to 'cloud/app/src')
| -rw-r--r-- | cloud/app/src/routes/workspace/[id].css | 14 | ||||
| -rw-r--r-- | cloud/app/src/routes/workspace/[id].tsx | 33 |
2 files changed, 41 insertions, 6 deletions
diff --git a/cloud/app/src/routes/workspace/[id].css b/cloud/app/src/routes/workspace/[id].css index 2d9b0e638..6cf8a0cff 100644 --- a/cloud/app/src/routes/workspace/[id].css +++ b/cloud/app/src/routes/workspace/[id].css @@ -115,11 +115,18 @@ [data-component="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); + [data-slot="input-container"] { + display: flex; + flex-direction: column; + gap: var(--space-1); + } + @media (max-width: 30rem) { gap: var(--space-2); } @@ -148,6 +155,13 @@ display: flex; gap: var(--space-2); } + + [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="api-keys-table"] { diff --git a/cloud/app/src/routes/workspace/[id].tsx b/cloud/app/src/routes/workspace/[id].tsx index 222e51a15..39db5cc40 100644 --- a/cloud/app/src/routes/workspace/[id].tsx +++ b/cloud/app/src/routes/workspace/[id].tsx @@ -49,7 +49,13 @@ const createKey = action(async (form: FormData) => { const workspaceID = form.get("workspaceID")?.toString() if (!workspaceID) return { error: "Workspace ID is required" } return json( - withActor(() => Key.create({ name }), workspaceID), + withActor( + () => + Key.create({ name }) + .then((data) => ({ data })) + .catch((e) => ({ error: e.message })), + workspaceID, + ), { revalidate: listKeys.key }, ) }, "key.create") @@ -185,19 +191,27 @@ function KeySection() { function KeyCreateForm() { const params = useParams() const submission = useSubmission(createKey) - const [store, setStore] = createStore({ - show: false, - }) + const [store, setStore] = createStore({ show: false }) let input: HTMLInputElement createEffect(() => { - if (!submission.pending && submission.result) { + // @ts-expect-error + if (!submission.pending && submission.result?.data) { hide() } }) 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 + for (let i = 0; i < 3; i++) { + submission.clear() + if (!submission.result) break + } setStore("show", true) input.focus() } @@ -216,7 +230,14 @@ function KeyCreateForm() { } > <form action={createKey} method="post" data-slot="create-form"> - <input ref={(r) => (input = r)} data-component="input" name="name" type="text" placeholder="Enter key name" /> + <div data-slot="input-container"> + <input ref={(r) => (input = r)} data-component="input" name="name" type="text" placeholder="Enter key name" /> + {/* @ts-expect-error */} + <Show when={submission.result?.error}> + {/* @ts-expect-error */} + <div data-slot="form-error">{submission.result.error}</div> + </Show> + </div> <input type="hidden" name="workspaceID" value={params.id} /> <div data-slot="form-actions"> <button type="reset" data-color="ghost" onClick={() => hide()}> |
