summaryrefslogtreecommitdiffhomepage
path: root/cloud/app/src
diff options
context:
space:
mode:
authorFrank <[email protected]>2025-09-12 14:22:40 -0400
committerFrank <[email protected]>2025-09-12 14:22:42 -0400
commitc6c153de95668b6e3a3e08e122cad06b8e0f40e6 (patch)
tree17235e011ede1804b8ad554ddcf9b29b6b9e82a5 /cloud/app/src
parent417e8f619cea713e307c1efbf0b56964834b3731 (diff)
downloadopencode-c6c153de95668b6e3a3e08e122cad06b8e0f40e6.tar.gz
opencode-c6c153de95668b6e3a3e08e122cad06b8e0f40e6.zip
wip: zen
Diffstat (limited to 'cloud/app/src')
-rw-r--r--cloud/app/src/routes/workspace/[id].css14
-rw-r--r--cloud/app/src/routes/workspace/[id].tsx33
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()}>