Home page: can't pick or create a workspace when creating a new board #79

Open
opened 2026-04-27 12:20:49 +00:00 by AhmedHanafy725 · 3 comments
Member

Bug

The boards home page (/web/home.html) doesn't expose any way to organize boards into workspaces from the user-facing flow:

  1. The workspace dropdown at the top is wired up purely as a filter on the board list. It lists existing workspaces but never offers an option to create a new one. workspace.create is reachable only from the admin dashboard at /index.html -- a normal user landing on the home page can't create a workspace at all.
  2. The "New Board" button silently reuses the value of that filter dropdown as the new board's workspace_id. There is no per-board workspace picker -- if the user has "All Workspaces" selected, the board is created without any workspace; if they have a workspace selected, every new board they make goes there with no chance to choose otherwise.
  3. The same dropdown doing two unrelated jobs (filter the list AND set the workspace for the next created board) is the structural cause -- it's not obvious from the UI that selecting a filter also affects future board creation.

Net effect: a user clicking "New Board" cannot intentionally pick which workspace the board goes in, and cannot create a workspace from the page they're on.

Reproduction

  1. Open the home page (/web/home) as a user with no admin access to the dashboard.
  2. Notice the workspace dropdown only lists "All Workspaces" + any workspaces that already exist -- there is no "+ New workspace" entry.
  3. Click "New Board". A board is created and you're navigated to it. There is no prompt or modal asking which workspace the board belongs to.

Affected file

  • crates/hero_whiteboard_ui/templates/web/home.html
    • workspace dropdown markup (around lines 9-13)
    • loadWorkspaces (53-72)
    • onWorkspaceChange (74-80)
    • createNewBoard (141-152)

No server / SDK / openrpc / DB changes are needed -- workspace.create, workspace.list, and board.create (with optional workspace_id) already exist in the JSON-RPC surface (see static/openrpc.json and static/js/dashboard.js:305 for the existing admin call).

Expected behavior

  • The user can create a workspace directly from the home page (without going through the admin dashboard).
  • When creating a new board, the user can choose which workspace it goes into (including "no workspace") and can create a new workspace inline as part of the same flow.
  • The filter dropdown remains a filter for the visible board list and no longer doubles as the implicit workspace for new boards.

Fix sketch

Replace the silent createNewBoard flow with a small modal (matching the existing rename modal at lines 25-44) that asks for:

  • Board name (default "Untitled Board", editable).
  • Workspace, via a dropdown that lists existing workspaces, plus options for "No workspace" and "+ New workspace...". Picking the latter reveals an inline name field.
  • Create button that, if a new workspace was requested, calls workspace.create first and then board.create with the resulting workspace_id.

Also add the same "+ New workspace..." sentinel option to the top-of-page filter dropdown so users can create a workspace without first clicking "New Board". Selecting it prompts for a name, calls workspace.create, refreshes the list, and selects the new workspace.

The existing filter dropdown's behavior (filter the visible board list) should be preserved. Optionally pre-select the currently filtered workspace as the default in the new-board modal so users who are already scoped to a workspace get the expected default.

Acceptance criteria

  • Clicking "New Board" opens a modal with a board-name input and a workspace picker (existing workspaces + "No workspace" + "+ New workspace...").
  • If "+ New workspace..." is chosen in the modal, an inline name input appears; on submit the new workspace is created via workspace.create and the board is created with its id.
  • Cancel in the modal does nothing (no board, no workspace created).
  • The home-page filter dropdown also offers a "+ New workspace..." sentinel option that prompts for a name and creates a workspace via workspace.create, refreshes the list, selects the new workspace, and reloads boards. Cancelling reverts the dropdown to the previous selection so it doesn't get stuck on the sentinel.
  • The currently selected filter workspace, if any, is the default workspace in the new-board modal.
  • Filter behavior is unchanged: selecting an existing workspace still filters the board list.
  • No regression for users who only have "All Workspaces".
  • No JS console errors.
  • The change is contained to crates/hero_whiteboard_ui/templates/web/home.html (no server / SDK / openrpc edits).
## Bug The boards home page (`/web/home.html`) doesn't expose any way to organize boards into workspaces from the user-facing flow: 1. The workspace dropdown at the top is wired up purely as a *filter* on the board list. It lists existing workspaces but never offers an option to create a new one. `workspace.create` is reachable only from the admin dashboard at `/index.html` -- a normal user landing on the home page can't create a workspace at all. 2. The "New Board" button silently reuses the value of that filter dropdown as the new board's `workspace_id`. There is no per-board workspace picker -- if the user has "All Workspaces" selected, the board is created without any workspace; if they have a workspace selected, every new board they make goes there with no chance to choose otherwise. 3. The same dropdown doing two unrelated jobs (filter the list AND set the workspace for the next created board) is the structural cause -- it's not obvious from the UI that selecting a filter also affects future board creation. Net effect: a user clicking "New Board" cannot intentionally pick which workspace the board goes in, and cannot create a workspace from the page they're on. ## Reproduction 1. Open the home page (`/web/home`) as a user with no admin access to the dashboard. 2. Notice the workspace dropdown only lists "All Workspaces" + any workspaces that already exist -- there is no "+ New workspace" entry. 3. Click "New Board". A board is created and you're navigated to it. There is no prompt or modal asking which workspace the board belongs to. ## Affected file - `crates/hero_whiteboard_ui/templates/web/home.html` - workspace dropdown markup (around lines 9-13) - `loadWorkspaces` (53-72) - `onWorkspaceChange` (74-80) - `createNewBoard` (141-152) No server / SDK / openrpc / DB changes are needed -- `workspace.create`, `workspace.list`, and `board.create` (with optional `workspace_id`) already exist in the JSON-RPC surface (see `static/openrpc.json` and `static/js/dashboard.js:305` for the existing admin call). ## Expected behavior - The user can create a workspace directly from the home page (without going through the admin dashboard). - When creating a new board, the user can choose which workspace it goes into (including "no workspace") and can create a new workspace inline as part of the same flow. - The filter dropdown remains a filter for the visible board list and no longer doubles as the implicit workspace for new boards. ## Fix sketch Replace the silent `createNewBoard` flow with a small modal (matching the existing rename modal at lines 25-44) that asks for: - Board name (default "Untitled Board", editable). - Workspace, via a dropdown that lists existing workspaces, plus options for "No workspace" and "+ New workspace...". Picking the latter reveals an inline name field. - Create button that, if a new workspace was requested, calls `workspace.create` first and then `board.create` with the resulting `workspace_id`. Also add the same "+ New workspace..." sentinel option to the top-of-page filter dropdown so users can create a workspace without first clicking "New Board". Selecting it prompts for a name, calls `workspace.create`, refreshes the list, and selects the new workspace. The existing filter dropdown's behavior (filter the visible board list) should be preserved. Optionally pre-select the currently filtered workspace as the default in the new-board modal so users who are already scoped to a workspace get the expected default. ## Acceptance criteria - [ ] Clicking "New Board" opens a modal with a board-name input and a workspace picker (existing workspaces + "No workspace" + "+ New workspace..."). - [ ] If "+ New workspace..." is chosen in the modal, an inline name input appears; on submit the new workspace is created via `workspace.create` and the board is created with its id. - [ ] Cancel in the modal does nothing (no board, no workspace created). - [ ] The home-page filter dropdown also offers a "+ New workspace..." sentinel option that prompts for a name and creates a workspace via `workspace.create`, refreshes the list, selects the new workspace, and reloads boards. Cancelling reverts the dropdown to the previous selection so it doesn't get stuck on the sentinel. - [ ] The currently selected filter workspace, if any, is the default workspace in the new-board modal. - [ ] Filter behavior is unchanged: selecting an existing workspace still filters the board list. - [ ] No regression for users who only have "All Workspaces". - [ ] No JS console errors. - [ ] The change is contained to `crates/hero_whiteboard_ui/templates/web/home.html` (no server / SDK / openrpc edits).
Author
Member

Implementation Spec for Issue #79

Objective

Let users pick or create a workspace for a new board directly from the home page, and let them create a workspace from the page-level dropdown without having to use the admin dashboard.

Requirements

  • New Board flow opens a modal that asks for board name + target workspace.
  • The workspace picker in the modal lists existing workspaces, plus (no workspace) and + New workspace....
  • Choosing + New workspace... reveals an inline name input. On submit, workspace.create is called first, then board.create with the resulting workspace_id.
  • The page-level filter dropdown gains a + New workspace... sentinel option that prompts for a name, calls workspace.create, refreshes the dropdown, and selects the new workspace. Cancelling reverts the dropdown to the previous selection.
  • The filter dropdown's existing behavior (filter visible boards) is preserved.
  • The currently selected filter workspace is the default in the new-board modal.
  • Cancel in either flow performs no RPC and creates nothing.
  • All-static-asset change; no server / SDK / openrpc edits.

Files to Modify

  • crates/hero_whiteboard_ui/templates/web/home.html — workspace dropdown markup, loadWorkspaces, onWorkspaceChange, createNewBoard, plus a new modal and helper functions.

Implementation Plan

Step 1: Add + New workspace... to the filter dropdown

Files: crates/hero_whiteboard_ui/templates/web/home.html

  • In loadWorkspaces (lines 53-72), after appending existing workspace options, append a sentinel <option value="__new__">+ New workspace...</option>.
  • In onWorkspaceChange (lines 74-80), if the selected value is __new__, immediately revert the visible selection to currentWorkspaceId (so cancel doesn't strand the dropdown), then prompt(...) for a name, call rpcCall('workspace.create', { name }), await it, then call loadWorkspaces(), set currentWorkspaceId to the new id, persist to localStorage, and call loadBoards(). On error, alert the message.
  • This is a small, self-contained change and is the simpler half of the issue.

Dependencies: none.

Step 2: Add a New-Board modal with workspace picker + inline workspace creation

Files: crates/hero_whiteboard_ui/templates/web/home.html

  • Add a modal block in the template body styled like the existing rename modal (lines 25-44). Contents:
    • Header New Board.
    • Name input (default Untitled Board, focused on open).
    • Workspace <select id="new-board-ws-select"> populated with (no workspace) + every existing workspace + + New workspace....
    • A hidden <div id="new-board-new-ws"> containing a New workspace name input. Visible only when the picker value is __new__.
    • Cancel + Create buttons.
  • Replace createNewBoard (lines 141-152) with openNewBoardModal() that pre-fills the name field with Untitled Board, populates the workspace select (reusing the workspace list cached from loadWorkspaces), pre-selects currentWorkspaceId if any, hides the new-workspace input, and shows the modal.
  • Wire closeNewBoardModal() and a select-change handler that toggles the new-workspace name input visibility.
  • Implement submitNewBoard():
    • Read board name (trim, fallback to Untitled Board if empty).
    • Read selected workspace value.
    • If __new__, read the new-workspace name (trim). If empty, alert and bail.
    • If __new__, await rpcCall('workspace.create', { name }) and use its id. Otherwise, parse the value if non-empty, leave undefined if (no workspace).
    • Build params = { name }, set params.workspace_id = wsId only when defined.
    • await rpcCall('board.create', params), then window.location.href = WB_BASE + '/board/' + board.id (matches existing flow).
    • Wrap in try/catch; on error, alert the message and keep the modal open. The existing fallback (/board/local-... on RPC failure) is intentionally not reused here because failing to create a workspace shouldn't drop the user into a local board.
  • Cache the workspaces list returned by loadWorkspaces in a module-scope variable (var workspacesCache = []) so the new-board modal can populate without an extra RPC each time.
  • Hook onclick="openNewBoardModal()" on the existing New Board button (line 14-16) instead of createNewBoard().
  • Hook Escape/Enter on the modal (matching the rename modal handler at lines 313-316) so Enter submits and Escape cancels.

Dependencies: Step 1 (Step 2 reuses the workspacesCache populated by loadWorkspaces; Step 1's edits to loadWorkspaces are a good place to also populate the cache).

Acceptance Criteria

  • Clicking New Board opens a modal with a board-name input and a workspace picker.
  • The workspace picker lists existing workspaces, plus (no workspace) and + New workspace....
  • Selecting + New workspace... reveals an inline name input. On Create, workspace.create is called first, then board.create with the new workspace's id.
  • Cancel in the modal closes it without creating anything.
  • Pre-selected workspace defaults to whatever the filter dropdown currently shows; if the filter is All Workspaces, the modal defaults to (no workspace).
  • The filter dropdown gains a + New workspace... sentinel option that prompts for a name and creates a workspace via workspace.create. Cancelling reverts the dropdown.
  • After creating a workspace via either flow, the dropdown is refreshed and (where appropriate) selects the new workspace.
  • Filter behavior is unchanged: selecting an existing workspace still filters the board list.
  • No regression for users who only have All Workspaces.
  • No JS console errors.
  • Change is contained to crates/hero_whiteboard_ui/templates/web/home.html.

Notes

  • workspace.create is already in static/openrpc.json:69 and used by static/js/dashboard.js:305. No server changes are required.
  • board.create already accepts an optional workspace_id (static/openrpc.json:195).
  • Keep the styling consistent with the existing rename modal so the result looks native (use var(--wb-...) CSS variables, match button classes).
  • Don't introduce a new file or framework — stay vanilla DOM + the existing rpcCall helper from static/js/whiteboard/rpc.js.
## Implementation Spec for Issue #79 ### Objective Let users pick or create a workspace for a new board directly from the home page, and let them create a workspace from the page-level dropdown without having to use the admin dashboard. ### Requirements - New Board flow opens a modal that asks for board name + target workspace. - The workspace picker in the modal lists existing workspaces, plus `(no workspace)` and `+ New workspace...`. - Choosing `+ New workspace...` reveals an inline name input. On submit, `workspace.create` is called first, then `board.create` with the resulting `workspace_id`. - The page-level filter dropdown gains a `+ New workspace...` sentinel option that prompts for a name, calls `workspace.create`, refreshes the dropdown, and selects the new workspace. Cancelling reverts the dropdown to the previous selection. - The filter dropdown's existing behavior (filter visible boards) is preserved. - The currently selected filter workspace is the default in the new-board modal. - Cancel in either flow performs no RPC and creates nothing. - All-static-asset change; no server / SDK / openrpc edits. ### Files to Modify - `crates/hero_whiteboard_ui/templates/web/home.html` — workspace dropdown markup, `loadWorkspaces`, `onWorkspaceChange`, `createNewBoard`, plus a new modal and helper functions. ### Implementation Plan #### Step 1: Add `+ New workspace...` to the filter dropdown Files: `crates/hero_whiteboard_ui/templates/web/home.html` - In `loadWorkspaces` (lines 53-72), after appending existing workspace options, append a sentinel `<option value="__new__">+ New workspace...</option>`. - In `onWorkspaceChange` (lines 74-80), if the selected value is `__new__`, immediately revert the visible selection to `currentWorkspaceId` (so cancel doesn't strand the dropdown), then `prompt(...)` for a name, call `rpcCall('workspace.create', { name })`, await it, then call `loadWorkspaces()`, set `currentWorkspaceId` to the new id, persist to localStorage, and call `loadBoards()`. On error, `alert` the message. - This is a small, self-contained change and is the simpler half of the issue. Dependencies: none. #### Step 2: Add a New-Board modal with workspace picker + inline workspace creation Files: `crates/hero_whiteboard_ui/templates/web/home.html` - Add a modal block in the template body styled like the existing rename modal (lines 25-44). Contents: - Header `New Board`. - `Name` input (default `Untitled Board`, focused on open). - `Workspace` `<select id="new-board-ws-select">` populated with `(no workspace)` + every existing workspace + `+ New workspace...`. - A hidden `<div id="new-board-new-ws">` containing a `New workspace name` input. Visible only when the picker value is `__new__`. - Cancel + Create buttons. - Replace `createNewBoard` (lines 141-152) with `openNewBoardModal()` that pre-fills the name field with `Untitled Board`, populates the workspace select (reusing the workspace list cached from `loadWorkspaces`), pre-selects `currentWorkspaceId` if any, hides the new-workspace input, and shows the modal. - Wire `closeNewBoardModal()` and a select-change handler that toggles the new-workspace name input visibility. - Implement `submitNewBoard()`: - Read board name (trim, fallback to `Untitled Board` if empty). - Read selected workspace value. - If `__new__`, read the new-workspace name (trim). If empty, `alert` and bail. - If `__new__`, `await rpcCall('workspace.create', { name })` and use its id. Otherwise, parse the value if non-empty, leave undefined if `(no workspace)`. - Build `params = { name }`, set `params.workspace_id = wsId` only when defined. - `await rpcCall('board.create', params)`, then `window.location.href = WB_BASE + '/board/' + board.id` (matches existing flow). - Wrap in try/catch; on error, `alert` the message and keep the modal open. The existing fallback (`/board/local-...` on RPC failure) is intentionally not reused here because failing to create a workspace shouldn't drop the user into a local board. - Cache the workspaces list returned by `loadWorkspaces` in a module-scope variable (`var workspacesCache = []`) so the new-board modal can populate without an extra RPC each time. - Hook `onclick="openNewBoardModal()"` on the existing `New Board` button (line 14-16) instead of `createNewBoard()`. - Hook Escape/Enter on the modal (matching the rename modal handler at lines 313-316) so Enter submits and Escape cancels. Dependencies: Step 1 (Step 2 reuses the `workspacesCache` populated by `loadWorkspaces`; Step 1's edits to `loadWorkspaces` are a good place to also populate the cache). ### Acceptance Criteria - [ ] Clicking `New Board` opens a modal with a board-name input and a workspace picker. - [ ] The workspace picker lists existing workspaces, plus `(no workspace)` and `+ New workspace...`. - [ ] Selecting `+ New workspace...` reveals an inline name input. On Create, `workspace.create` is called first, then `board.create` with the new workspace's id. - [ ] Cancel in the modal closes it without creating anything. - [ ] Pre-selected workspace defaults to whatever the filter dropdown currently shows; if the filter is `All Workspaces`, the modal defaults to `(no workspace)`. - [ ] The filter dropdown gains a `+ New workspace...` sentinel option that prompts for a name and creates a workspace via `workspace.create`. Cancelling reverts the dropdown. - [ ] After creating a workspace via either flow, the dropdown is refreshed and (where appropriate) selects the new workspace. - [ ] Filter behavior is unchanged: selecting an existing workspace still filters the board list. - [ ] No regression for users who only have `All Workspaces`. - [ ] No JS console errors. - [ ] Change is contained to `crates/hero_whiteboard_ui/templates/web/home.html`. ### Notes - `workspace.create` is already in `static/openrpc.json:69` and used by `static/js/dashboard.js:305`. No server changes are required. - `board.create` already accepts an optional `workspace_id` (`static/openrpc.json:195`). - Keep the styling consistent with the existing rename modal so the result looks native (use `var(--wb-...)` CSS variables, match button classes). - Don't introduce a new file or framework — stay vanilla DOM + the existing `rpcCall` helper from `static/js/whiteboard/rpc.js`.
Author
Member

Test Results

  • cargo test --workspace --lib: 4 lib targets, 0 tests / 0 passed / 0 failed each (no Rust unit tests for these crates).
  • cargo clippy --workspace -- -D warnings: clean.
  • cargo check --workspace: clean (template and embedded JS compile via Askama).

This is an HTML-template-only change. Manual verification recommended:

  1. Open the home page and click the workspace dropdown — confirm a + New workspace... option exists at the bottom.
  2. Pick + New workspace..., enter a name, confirm the workspace is created and selected; cancel via Escape and confirm the dropdown reverts.
  3. Click New Board — confirm a modal opens with name and workspace fields.
  4. Pick + New workspace... in the modal, enter a name plus a board name, click Create. Confirm the workspace is created, then the board is created in it, and the page navigates to the new board.
  5. Repeat with (no workspace) and with an existing workspace selected; confirm the board lands in the right workspace.
  6. Cancel in the modal — confirm nothing is created.
## Test Results - `cargo test --workspace --lib`: 4 lib targets, 0 tests / 0 passed / 0 failed each (no Rust unit tests for these crates). - `cargo clippy --workspace -- -D warnings`: clean. - `cargo check --workspace`: clean (template and embedded JS compile via Askama). This is an HTML-template-only change. Manual verification recommended: 1. Open the home page and click the workspace dropdown — confirm a `+ New workspace...` option exists at the bottom. 2. Pick `+ New workspace...`, enter a name, confirm the workspace is created and selected; cancel via Escape and confirm the dropdown reverts. 3. Click `New Board` — confirm a modal opens with name and workspace fields. 4. Pick `+ New workspace...` in the modal, enter a name plus a board name, click Create. Confirm the workspace is created, then the board is created in it, and the page navigates to the new board. 5. Repeat with `(no workspace)` and with an existing workspace selected; confirm the board lands in the right workspace. 6. Cancel in the modal — confirm nothing is created.
Author
Member

Implementation Summary

Changes

  • crates/hero_whiteboard_ui/templates/web/home.html (+146 / -18)
    • Added a New Board modal (board-name input + workspace picker + inline new-workspace name input).
    • Replaced the silent createNewBoard() flow with openNewBoardModal() / submitNewBoard() driven by the new modal. submitNewBoard calls workspace.create first when + New workspace... is selected, then board.create with the resulting workspace_id.
    • Added a + New workspace... sentinel option to the existing top-of-page workspace filter dropdown. Picking it reverts the visible selection so a cancel doesn't strand the dropdown, prompts for a name, calls workspace.create, refreshes the list, and selects the new workspace.
    • Cached the workspace list (workspacesCache) in loadWorkspaces so the new-board modal can populate without an extra RPC every time it opens.
    • Pre-selects the currently filtered workspace as the default in the new-board modal.
    • Hooked Enter/Escape on the new-board modal so Enter submits and Escape cancels (matches the rename modal's keyboard handling).

Why this lives entirely in one template

The JSON-RPC surface already supports everything needed: workspace.create (static/openrpc.json:69, used today only by the admin dashboard at static/js/dashboard.js:305) and board.create with optional workspace_id (static/openrpc.json:195). No server / SDK / openrpc / DB changes.

Verification

  • cargo test --workspace --lib passes (4 lib targets, 0 tests).
  • cargo clippy --workspace -- -D warnings: clean.
  • cargo check --workspace: clean (Askama compiled the template successfully).

Notes / caveats

  • Template-only change; HTML, embedded JS, and CSS variables only.
  • The modal styling reuses the existing rename modal's pattern for visual consistency.
  • Filter behavior is preserved: selecting an existing workspace in the top dropdown still filters the visible board list.
  • If RPC fails during new-workspace creation the modal stays open and an alert surfaces the error, so the user can retry without losing their typed board name.
## Implementation Summary ### Changes - `crates/hero_whiteboard_ui/templates/web/home.html` (+146 / -18) - Added a `New Board` modal (board-name input + workspace picker + inline new-workspace name input). - Replaced the silent `createNewBoard()` flow with `openNewBoardModal() / submitNewBoard()` driven by the new modal. `submitNewBoard` calls `workspace.create` first when `+ New workspace...` is selected, then `board.create` with the resulting `workspace_id`. - Added a `+ New workspace...` sentinel option to the existing top-of-page workspace filter dropdown. Picking it reverts the visible selection so a cancel doesn't strand the dropdown, prompts for a name, calls `workspace.create`, refreshes the list, and selects the new workspace. - Cached the workspace list (`workspacesCache`) in `loadWorkspaces` so the new-board modal can populate without an extra RPC every time it opens. - Pre-selects the currently filtered workspace as the default in the new-board modal. - Hooked Enter/Escape on the new-board modal so Enter submits and Escape cancels (matches the rename modal's keyboard handling). ### Why this lives entirely in one template The JSON-RPC surface already supports everything needed: `workspace.create` (`static/openrpc.json:69`, used today only by the admin dashboard at `static/js/dashboard.js:305`) and `board.create` with optional `workspace_id` (`static/openrpc.json:195`). No server / SDK / openrpc / DB changes. ### Verification - `cargo test --workspace --lib` passes (4 lib targets, 0 tests). - `cargo clippy --workspace -- -D warnings`: clean. - `cargo check --workspace`: clean (Askama compiled the template successfully). ### Notes / caveats - Template-only change; HTML, embedded JS, and CSS variables only. - The modal styling reuses the existing rename modal's pattern for visual consistency. - Filter behavior is preserved: selecting an existing workspace in the top dropdown still filters the visible board list. - If RPC fails during new-workspace creation the modal stays open and an alert surfaces the error, so the user can retry without losing their typed board name.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_whiteboard#79
No description provided.