feat(assistants): viewer/editor share permissions UI (#113)#384
Merged
Conversation
Consumes the backend contract shipped in #383: surfaces a per-share permission toggle in the share dialog, an "Editor" badge + Edit affordance on shared-with-me cards, an editor banner on the form, and an owner-only gate on the Share button. - Dialog: per-row "Can view / Can edit" select on existing shares, a "permission for new people" toggle, and onSave delta-detection that distinguishes adds, removes, and permission changes on already-shared emails — each dispatched to the correct backend endpoint (POST / DELETE / PATCH). - List: shared-with-me cards with userPermission='editor' now show an Editor badge and an Edit button alongside Chat. - Form: surfaces "Shared by {owner}" banner for editors; Share button is owner-only. - AssistantSharesResponse.sharedWith is now ShareEntry[] across the service / api / dialog (matches the backend's PR-1 shape change). - Vitest: existing service spec migrated to the new shape; new dialog spec covers the delta algorithm (adds / removes / permission upgrades / mixed) via DI tokens per project convention. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Brings the share dialog in line with the list/form design language captured in .claude/skills/tailwind-ui/references/app-conventions.md: rounded-2xl, blue accent (was indigo), focus:ring-2 focus:ring-blue-500, dark:bg-gray-800 inputs, flat <section> blocks divided by border-t. - Header avatar: blue-100 chip (was indigo). - URL row + add-people + current-shares are three flat sections, no individually-bordered cards. - "Currently shared with" became a single rounded-2xl divide-y ul (was a stack of bordered rows), with an empty-state when nothing is shared. - Mode toggle is now a proper segmented tablist with role="tab" and aria-selected, accented in blue. - Permission default-for-new-people moved into the section header so it's contextually attached to the add controls. - Save/Cancel actions reorder cleanly on mobile + desktop and use the shared blue/ghost button tokens. - Search results render skeleton rows (animate-pulse) while searching, with role="status" sr-only text — matches the connector-chip skeleton pattern already in the assistant editor. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
loadShares() runs in the constructor and shares() starts empty, so the
empty-state ("Not shared with anyone yet") was painting while the fetch
was in flight on dialogs for SHARED assistants. Renders a skeleton ul
matching the real row layout (email + permission select + delete) while
loadingShares() is true; suppresses the count chip until the fetch
resolves.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Shares skeleton: drop from 3 rows to 1; the loading state was visually heavier than the real list it was previewing. - Tabs: selected state now flips font-medium → font-semibold alongside the existing blue underline + text color, so the active tab reads at a glance. -mb-px on each tab makes the active 2px underline overlap the container's 1px bottom border cleanly (no gray line peeking through). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The native chevron sat too close to the rounded-2xl edge. Switch px-2.5 → pl-2.5 pr-7 on both permission selects (the per-share row select and the "default for new people" select) so the chevron has breathing room without changing the left padding. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The active tab used border-blue-600 alongside a base border-transparent. Both are border-color utilities at the same specificity, so whichever Tailwind emitted later in the stylesheet won — in practice the transparent base, leaving no visible underline. Switch to bottom-only border-b-transparent / border-b-blue-600 so the active state targets border-bottom-color exclusively; no cascade collision with the base. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Confirmed via devtools: border-b-blue-600 was on the DOM but computed border-bottom-color was rgba(0,0,0,0). Same-specificity class collision with border-b-transparent — Tailwind's emit order put the base last and the conditional class lost the cascade. Move all active styling onto aria-selected:* utilities so the active selector becomes [aria-selected="true"], which has attribute-selector specificity and beats the base. As a bonus the tabs now use one declarative class string instead of four parallel [class.x] bindings. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Native select chevrons sit at a browser-fixed offset from the right edge regardless of padding-right, so pr-7 / pr-8 just pushed the text away from a chevron still crowded against the rounded-2xl corner. Switch both permission selects to appearance-none + an overlaid heroChevronDown icon. The wrapper handles positioning so the chevron clears the rounded corner cleanly. pointer-events-none on the icon so clicks still hit the native select. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Both fell out of the share-assistant-dialog redesign on PR #384 and will bite future work the same way if undocumented: - Tabs: conditional [class.border-b-blue-600] loses the cascade to a base border-b-transparent at the same specificity. Use the aria-selected: variant so the active rule has attribute-selector specificity. - Selects: native chevrons sit at a browser-fixed offset from the right edge regardless of padding-right; with rounded-2xl they crowd the corner. appearance-none + overlaid heroChevronDown gives reliable positioning. Adds a Tabs subsection, a Select example under Form pages, and a "Common gotchas" section that names both failure modes and their DevTools symptoms. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR 2 of 2 for #113 — consumes the backend contract shipped in #383.
Summary
Why this lands now
PR 1 changed `AssistantSharesResponse.sharedWith` from `string[]` to `ShareEntry[]` — the SPA was reading the old shape until this PR. Merging promptly closes that gap.
Test plan
Manual test script
Setup: two accounts. Alice owns an assistant; Bob has no shares yet.
🤖 Generated with Claude Code