Skip to content

Spaces UI - #12

Draft
calebroseland wants to merge 18 commits into
masterfrom
docs-wire-spaces-api
Draft

Spaces UI#12
calebroseland wants to merge 18 commits into
masterfrom
docs-wire-spaces-api

Conversation

@calebroseland

Copy link
Copy Markdown
Member

Summary

Space CRUD UI, Space Shell, Page Tree, etc

Ticket Link

Soft-merge of MM-69892 (PR #7, nang2049): the editor slice in
webapp_globals and a stub PageEditor mounted at the page/draft routes,
so the spaces-API wiring can build around the editor and avoid later
conflicts.

Applied as a net diff onto master since PR #7 was cut from the pre-merge
scaffolding branch. Two divergences resolved:
- Dropped PR #7's navigation.ts change: master's merged draft-route fix
  already exposes the isDraft the editor consumes (single
  [DOCS_DRAFT_ROUTE, DOCS_ROUTE] match instead of two useRouteMatch calls).
- webapp_globals: took PR #7's superset React type imports; kept master's
  useBootstrapDocs in docs_root (3-way merged cleanly with the isDraft add).
Replace the mock data source with an API-backed DocsDataSource over the
plugin's /api/v1 routes (server/api.go), following the Playbooks
fetch(url, Client4.getOptions()) idiom. Spaces are created, listed
(team-scoped by backing-channel membership), and left against the real
server; the switcher fans out per team for cross-team results.

- client/rest: doGet/doPost/doDelete + paginated listAll ({items,has_more})
- data: async DocsDataSource + apiDataSource; remove mock fixtures
- store: fetchSpaces/fetchAllSpaces/createSpace/leaveSpace async thunks;
  drop the team_id hashing stand-in (the server scopes lists by team)
- opaque server ids: drop slug-as-id and isSlugAvailable; the slug stays a
  client-only vanity field, format-validated only
- recent: client-side view history (data/recent_spaces) as the seam for a
  later server last_viewed_at proxy; page count omitted for MVP
- leave space: wired to member removal, navigates home when viewing it
- visibility stays client-only, maps to server view_access later (PR #10)

Addresses PR #2 feedback (team_id hashing, slug assumption, leaveSpace
wiring). Targets current master; forward-compatible with PR #10.
Adds the docs.editor.* ids from the stub editor and drops the removed
url.taken and page-placeholder ids plus the stale favorites ids.
The slug/URL field became dead weight once spaces route by opaque server
id: it was collected and format-validated but never sent or used. Remove
it from the create-space modal and the form/schema — slug is gone from
CreateSpaceInput, the form values, and the Zod schema (with its Url*
errors and slugify helper). The UrlInput form-control component and its
test are kept for reuse. Re-extract en.json (drops the url.* messages).
Custom emoji/icon picking is deferred, so a space without a custom icon
renders a generic compass glyph (utils/space_icon: <SpaceIcon>, using the
product's file-text-outline) everywhere a space icon shows — sidebar, home
cards, main content, switcher, and the create-space name field — so a
freshly created space never appears blank. A space with a custom emoji
renders that character.

Switcher: rename the "Recent docs" group to "Recent", and dedupe — a space
already listed under Recent is no longer repeated under "Your spaces".
Reverts 13b2db3 (soft-merge of PR #7's stub editor): the mounted editor
wasn't working. Drops the page_editor component, the editor slice from
webapp_globals, and the pageId->PageEditor routing in docs_main_content
(back to the space/page placeholder). Later work on the touched files
(SpaceIcon, useRecordSpaceView) is preserved. Re-extract en.json.
Implements the space main-content view (Figma Space Home) as a new
space_view component set, replacing the routed-space placeholder:

- SpaceTitleBar: favorite, space icon + title + menu, member count, space
  details, Share
- PageBar: page-tree toggle + "Pages", "Updated <relative>" (host
  Timestamp), comments, Edit, overflow, expand
- PageHero: rounded banner with the space icon/title, description (with a
  placeholder when empty), and a pages/members/views stats row
- Page body: a placeholder until the editor is mounted

Uses Mattermost theme tokens and compass icons. Data-less bits are honest
placeholders for now: the stat counts and title-bar member count render an
em dash (await the pages/members/views APIs), member avatars are omitted,
and the controls are visual scaffolding wired in later passes.
Feeds the Space Home stats and title-bar member count with real data.

- data: add listSpaceMembers over GET /spaces/{id}/members; normalize the
  page-list summaries (no body) to Page for the store
- store: new spaceMembers slice (user ids per space) + fetchSpaceMembers;
  fetchPages now stores pages for the count (reused by the page tree later)
- selectors: getSpaceMemberIds; page count derives from getPagesForSpace
- hooks: useSpaceStats loads both on space mount and returns the counts
- space view: real PAGES and MEMBERS counts in the hero and the title-bar
  member count

Views has no server source yet, so that stat stays an em dash.
Space view build-out plus a raw-<button> cleanup.

- buttons: replace the raw <button>s in the space title/page bars with the
  shared Button — icon buttons use emphasis="quaternary" + the compass
  `btn-icon` class (the core pattern), Edit uses SecondaryButton
- avatars: expose the host Avatar via webapp_globals (as a wrapper that
  no-ops when the host doesn't publish it, so callers need no null check);
  useSpaceMemberProfiles resolves member ids to profiles (fetching missing
  ones); MemberAvatars renders the overlapping stack + "+N" in the hero
- share: ShareSpaceModal — real member list with avatars/names and a
  functional Copy link; the add-people search, per-member role, and space
  visibility/access are scaffolding (capabilities + view_access land with
  PR #10). Wired to the title-bar Share button.
Adds the space page-tree panel (Figma) as a middle column in the space
view, backed by the real page + move APIs.

- store: buildPageTree (parent_id/sort_order, orphans as roots) +
  buildDescendantMap; reindexAfterMove (0-based sibling renumber mirroring
  the server) + MOVED_PAGE; movePage thunk (optimistic move, reconcile on
  success, re-fetch on failure) and createPage thunk
- data: DocsDataSource.movePage (PATCH .../move) and createPage (POST);
  restPatch
- collapse: per-user localStorage tracker (data/collapsed_pages) +
  useCollapsedPages, mirroring the recent-spaces seam
- ui: PageTreePanel (264px column: Pages header, add-page, tree) and the
  recursive PageTreeNode (disclosure chevron, page/folder glyph, indent,
  active highlight, navigate/collapse)
- dnd: pragmatic-drag-and-drop reused from spaces_sidebar — a 3-zone
  hitbox (reorder above/below, reparent onto center), descendant-drop
  guard, and a monitor resolving the drop into (parent_id, sibling_index)
- layout: space view is now the title bar over [tree panel][content
  column]; the page-bar Pages button toggles the panel
- tests: buildPageTree/descendants and reindexAfterMove/MOVED_PAGE

Design's role/visibility remain PR #10 scaffolding; the page body is still
a placeholder until the editor is mounted.
Adds a real debounced people-search pipeline and a Base UI Autocomplete
combobox for the Share modal, gated on a named permission so nothing
misleading ships.

There is no server add-member API yet (roles/view-access + capabilities
land with PR #10), so a live "add people" control could only hold
selections client-side. The picker renders only when canManageMembers.

- store/permissions: getSpacePermissions(state, spaceId) selector +
  useSpacePermissions(spaceId) hook — the get/use split from core's
  channel_bookmarks/utils — returning named SpacePermissions
  {canManageMembers}; the seam PR #10's capability set drops into
- hooks/user_search: useUserSearch — 300ms-debounced searchProfiles scoped
  to the current team, mapped to MemberProfile, excludes already-shown ids
- share_space_modal/people_picker: Base UI Autocomplete (server-driven
  list, host Avatar rows, empty/searching states)
- share_space_modal: render the picker only when canManageMembers
- permissions: a DEV override forces canManageMembers on so the add-people
  picker is exercisable before PR #10's capability set exists. The get/use
  permission hooks are unchanged; revert the default to false (and wire the
  real capability source) before shipping.
- space title bar: the member count is now a button that opens the Share
  modal, the same as the Share button.
The shared Button is accent-only: every emphasis (quaternary etc.) colors
with --button-bg, and its rule follows .btn-icon in core's _buttons.scss,
so `<Button emphasis='quaternary' className='btn-icon'>` renders the
canonical icon markup but in accent blue instead of neutral grey.

Add a :global override that raises .btn-icon's specificity over the
emphasis so icon buttons stay neutral (center-channel-color), loaded as a
side effect via the button wrapper so it applies everywhere. Marked
PORT-TO-CORE — fold into core's .btn-icon rule and delete when it lands.
Button styling:
- Pages toggle and Edit are neutral ghost buttons now (not accent-blue /
  bordered secondary). Edit is an icon+label ghost (pencil + "Edit").
- Members count and the favorite star use btn-xs, closer to core's
  channel-header favorite toggle; Members reads neutral.
- Generalize the port-to-core neutral treatment to also cover labeled
  quaternary ghost buttons via a `docs-btn-neutral` marker, alongside the
  existing `.btn-icon` rule (renamed button_icon_neutral -> button_neutral).

Rename components/form-controls -> components/form_controls (underscore,
matching the rest of the tree), updating all imports.
btn-quaternary hovers with an accent-blue wash; the neutral icon/label
ghost buttons now use the grey channel-header hover instead.
Space management from the title bar, backed by the real update/archive APIs.

- data/store: updateSpace (PATCH /spaces/{id}) and deleteSpace (DELETE)
  data-source methods + thunks; UpdateSpacePatch type
- Space Settings modal (space_settings_modal): tabbed (Info / Permissions /
  Configuration / Archive). Info edits name+description via updateSpace;
  Archive confirms then deleteSpace + goHome. Permissions/Configuration are
  PR #10 scaffolding (view_access, roles, external sharing).
- Space Info panel (space_info): read-only RHS mirroring core's Channel Info
  — icon/title, description, members (avatars), page count + created time;
  renders as the right column of the space view.
- title bar: the space name opens a dropdown menu (Space info, Members, Copy
  link, Space settings, Leave, Archive; Settings/Archive gated on
  canManageMembers). The details button is now the Info-panel toggle.

Built via subagents from an on-disk plan; reviewed and gates re-run here.
Neutral toolbar buttons now mirror core's .btn-icon / channel-header icon:
grey base, light-grey hover, light-blue press (:active, declared after
:hover so a hovered press reads blue), and persistent blue when toggled on
(.active). The Info button gets `active` while its panel is open (alongside
aria-pressed) so the toggled state reads deep blue.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant