Skip to content

Panel P1.2: event workspace, event settings, organization screen - #64

Merged
thevladbog merged 30 commits into
mainfrom
panel/p1.2-workspace-settings
Jul 15, 2026
Merged

Panel P1.2: event workspace, event settings, organization screen#64
thevladbog merged 30 commits into
mainfrom
panel/p1.2-workspace-settings

Conversation

@thevladbog

@thevladbog thevladbog commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Summary

  • Event workspace (/events/$eventId): readiness-pipeline rail, header with launch-check-in gate (locked until ready, opens a coming-soon dialog once unlocked), Overview panel ("what's next" + 4 stat tiles) — replaces the P1.1 stub.
  • Event Settings (/events/$eventId/settings): anchor-rail with scroll-spy (ported useScrollSpy from the console app) across four scoped-save cards — General, Fonts (multipart upload), API keys (show-once secret reveal), Danger zone (typed-confirmation delete by event name).
  • Organization screen (/organization): tenant settings form with role-based read-only gating — replaces the placeholder.
  • Zero backend changes — every endpoint consumed here already existed and was contract-tested in P0.3/P1.1; schema.d.ts drift check confirms zero diff.

Process

Built via subagent-driven-development: 9 tasks, each with a fresh implementer + independent task reviewer (5 tasks needed a fix round for Critical/Important findings — two credential/race-condition bugs caught and fixed: a stray API-key secret that could resurface after a cancel-during-create race, and an equivalent race in event deletion), followed by a final whole-branch review (on the most capable available model) that caught and fixed 3 additional cross-task issues: a stale-form-state bug on tenant switch, dead type-safety casts, and a destructive-flow error-handling inconsistency. Full task-by-task trail in .superpowers/sdd/progress.md (search "PANEL P1.2").

Test plan

  • npm test -w panel — 181/181 passing (41 files)
  • npm test -w @idento/ui — 131/131 passing
  • cd backend && OPENAPI_COVERAGE=1 go test ./... -count=1 — 288 tests, unaffected
  • npm run typecheck -w panel && npm run build -w panel
  • cd panel && npx eslint . — clean
  • npm run generate:api -w panel && git diff --exit-code -- panel/src/shared/api/schema.d.ts — zero drift confirmed
  • docker build --load -f panel/Dockerfile -t idento-panel:p1.2-verify . — succeeds
  • Manual smoke test of workspace/settings/organization screens in a browser (not yet done in this session)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added event workspace overview with readiness progress, next steps, statistics, and check-in launch gating.
    • Added event settings for general details, fonts, API keys, and event deletion.
    • Added organization settings with admin-only editing and validation.
    • Added localized English and Russian interface text.
  • Bug Fixes

    • Improved date-range formatting and confirmation-action safeguards.
    • Added clearer loading, validation, permission, and API error states.

CI Bot and others added 22 commits July 15, 2026 00:13
…organization)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pure, props-driven WorkspaceRail for the event workspace's left rail
(board 1f): progress header + 5-segment bar, Overview/Settings nav
links, five locked numbered step rows (icon + sr-only status text per
WCAG 1.4.1), an always-locked Check-in row, and the amber unlock-hint
notice. Also exports ReadinessCell's STEP_LABEL_KEYS for reuse here.

Task 2 wires this into the real workspace layout route, including the
/events/$eventId/settings route this component's Settings Link
type-casts past for now.
Address two Important review findings on WorkspaceRail (commit 5b91fd0):

- StepRow's status icon was hardcoded text-muted-foreground for every
  status, so a done step's checkmark rendered gray instead of the
  success color the board mock shows. Moved icon shape + color into one
  STEP_STATUS_ICON lookup so they can't drift apart again.
- The unlock hint's `readiness?.ready !== true` condition was also true
  during loading (readiness undefined), asserting "not ready yet"
  before the component actually knew the answer. Changed to
  `readiness !== undefined && readiness.ready !== true` so the hint is
  absent while loading and only appears once readiness has loaded and
  ready === false.

Added test coverage for the per-status icon color mapping and for hint
absence during the undefined/loading state.
Restructures the flat /events/$eventId stub route into a layout route
(EventWorkspaceLayout) with an overview index child and a settings child
(both placeholders for Tasks 3/4), mounting the Task 1 WorkspaceRail
alongside the routed content.

- Header row: event name, UTC-pinned date-range pill, and a launch-check-in
  button gated on readiness — locked (disabled, sr-only reason text per
  WCAG 1.4.1) vs enabled (opens a coming-soon dialog; the real 3-step
  ceremony is P4).
- Extracted LiveStrip's formatDateRange into features/events/eventDates.ts
  so the header reuses the same UTC-pinned formatting instead of
  duplicating it.
- Fixed a latent WorkspaceRail bug this surfaced: the Overview Link had no
  activeOptions, so TanStack Router's default fuzzy active-matching treated
  it as active on the sibling /settings route too (a string-prefix match),
  stomping the explicit aria-current with its own. Added
  activeOptions={{ exact: true }}.
- Deleted the now-superseded EventWorkspaceStub + its test.

router.tsx edit is scoped to swapping eventStubRoute for the new layout +
children in the addChildren(...) call; the /register beforeLoad and
protectedBeforeLoad guards are untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Builds the workspace index route's Overview panel (board 1f §4), replacing
the Task 2 placeholder: a "What's next" card surfacing up to two not-done
readiness steps in fixed pipeline order (attendees→badge→staff→equipment,
zones excluded) as locked/coming-soon rows, or an all-ready message, plus a
4-tile stat grid (Attendees/Zones/Staff readiness counts, Checked-in from
stats) with independent per-tile loading/error handling so no query failure
ever renders a fabricated zero.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adapted from web/src/hooks/useScrollSpy.ts: panel scrolls the WINDOW
(AppShell's <main> has no scroll-container styling) rather than a
styled <main>, so this port uses root: null (IntersectionObserver's
viewport default) instead of the source's closest('main') resolution.
Test ports the source's self-contained MockIntersectionObserver
(jsdom has none) without touching panel's global test/setup.ts.
… card

Board 6a's Event Settings page: a left anchor rail (General/Fonts/API
keys/Danger zone, per task-brief reconciliation #5 narrowing the
board's 7-item rail) with scroll-spy active-highlighting via the
ported useScrollSpy hook, and stacked card sections. Danger zone's
rail link is always styled text-destructive regardless of scroll
position, not just when active.

GeneralCard is the only real card this task builds (Fonts/API
keys/Danger zone are inline placeholders for Tasks 5-7): name/starts/
ends/location fields with dirty-tracking and a scoped PATCH that sends
only the fields the user actually changed. Location can be explicitly
cleared to "" (PATCH's *string pointer semantics: present-empty-string
sets empty), but dates cannot — the backend treats a nil/absent date
as "leave unchanged" with no way to express clearing, so emptying a
previously-set date input disables Save and shows a muted note instead
of silently dropping the clear or sending a value that wouldn't work.
Zod validation reuses CreateEventDialog's exact message keys. Success
invalidates the event query (["get", "/api/events/{id}", { params:
{ path: { id } } }], matching $api's actual generated key shape) so
the header/rail resync too.

Swaps EventWorkspaceLayout's WorkspaceSettingsPlaceholder for the real
EventSettingsPage at the settings route.
Guards against calling setSaved on an unmounted component if a save
succeeds right before the user navigates away from the settings page.
Adds FontsCard to Event Settings: lists uploaded fonts (name, UPLOADED
pill, format/size/date caption), removes via a tier-1 destructive
ConfirmDialog + DELETE + list invalidation, and uploads new fonts
immediately on file pick via a dashed drop-zone with an amber license
disclaimer always visible. Uses a per-call openapi-fetch bodySerializer
to send multipart/form-data, verified against the installed
openapi-fetch source (no manual Content-Type override needed — the
client already skips the JSON header when the body is FormData).

Swaps EventSettingsPage's inline FontsCardPlaceholder for the real
card. Adds settingsFont* i18n keys to en/ru.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Also records the still-uncommitted Task 4 review outcome and Task 5
completion note that were already present in the working tree.
…revoke error handling

Critical: close-during-pending on the create dialog let a late POST
response's plain_key resurface unlabeled on the next dialog open, because
createKey.reset() on close only detaches the mutation observer — it does
not cancel the in-flight request or stop onSuccess from firing. Guard
onSuccess with a ref that tracks whether the dialog is still open when
the response lands.

Minor: the revoke mutation had no onError handling and the ConfirmDialog's
confirm button wasn't disabled during the request, so a failed revoke
failed silently and could be double-clicked into firing twice. Add an
inline i18n'd error (cleared on the next attempt, matching GeneralCard/
FontsCard's established convention) and a new confirmDisabled prop on the
shared ConfirmDialog primitive.
DangerZoneCard replaces the EventSettingsPage placeholder: red-tinted
Card (border-destructive/30, text-destructive title) with one row
("Delete this event") whose destructive Button opens @idento/ui's
typed-confirmation ConfirmDialog tier, keyed on the event's actual
name (not a slug or "DELETE" literal). onConfirm calls
DELETE /api/events/{id} (verified 204 No Content against schema.d.ts
and backend/internal/handler/events.go's c.NoContent call — this
endpoint's status code claim holds, unlike Tasks 5/6's font/api-key
DELETEs); on success it invalidates the ["get", "/api/events"] list
query (Task 4's confirmed key shape, so Home refreshes) and navigates
to "/". On failure the dialog auto-closes (same convention as
ApiKeysCard's revoke flow) and an inline i18n'd error renders in the
card; the mutation itself is reset on the dialog's open->closed
transition, with a separate deleteError flag holding the visible
error so that reset doesn't race away the message the user just saw.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…alog open on retry

Two reviewer-confirmed Important findings on DangerZoneCard (commit 006b1c4):

1. Cancelling the confirm dialog while a DELETE was in flight didn't abort
   anything — a late onSuccess/onError still force-navigated to Home or
   surfaced an error for a delete the user believed they'd cancelled. Fixed
   with a deleteAbortedRef guard, mirroring ApiKeysCard's createAbortedRef
   fix for the same race class, flipped only on user-driven closes
   (Cancel/Escape/overlay), never on the success-driven close+navigate.

2. The dialog auto-closed on a failed DELETE, wiping the typed
   confirmation input and forcing a full retype on every transient
   failure. Now the dialog stays open on failure, shows the error inline
   via a dynamic ConfirmDialog description (reusing the existing
   settingsDeleteError key), and preserves the typed name for a same-dialog
   retry. Mutation-reset-on-close now happens only on an explicit user
   close, not on every close.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Single-card tenant settings form (name/website/contact email/logo URL)
styled to match GeneralCard's dirty-tracked, scoped-save pattern. Role
comes from the getTenant response's own `role` field (no extra query);
non-admins get all-disabled inputs plus a read-only notice and no save
action. Clearing an optional field deliberately PUTs an explicit "" per
the endpoint's pointer-partial-update semantics. Wires organizationRoute
to the new page (single surgical swap in router.tsx).
Task 9: dead i18n key sweep (none found, both already clean) and
hardcoded-string sweep (none found) across all Tasks 1-8 files. Full
verification suite green: backend go test, panel typecheck/test/build/lint,
@idento/ui test/typecheck, openapi schema drift check (clean), docker build.
Document src/shared/hooks/ as the home for cross-cutting hooks in
panel/AGENTS.md (introduced this phase for useScrollSpy).
Task 9's final verification missed that settingsKeyCreatedAt/settingsKeyLastUsed
(added in Task 6) were never wired into ApiKeysCard.tsx's key list, which
rendered as a bare grid with no header row at all. Add a header row above
the list, reusing settingsKeyName/settingsKeyCreatedAt/settingsKeyLastUsed
and sharing the same grid template as the rows so headers stay aligned with
their columns. The key-preview and actions columns get empty header cells
(no existing label key for either, consistent with a pure icon/button
actions column not needing a forced label).
- OrganizationPage: add key={tenant.id} to OrganizationForm so it remounts
  (fresh useState initializers) when OrgSwitcher changes the active tenant
  without navigating away — without this, a stale form could PUT the
  previous org's values into the newly-selected tenant's record.
- WorkspaceRail: remove the stale `as never` casts on the Settings Link now
  that /events/$eventId/settings is a real registered route, restoring
  compiler route-id checking for this link. Update WorkspaceRail.test.tsx's
  comment, which referenced the now-removed cast and a not-yet-registered
  route.
- EventSettingsPage: fix a stale comment claiming Danger zone was still a
  placeholder — all four settings cards are real, fully-implemented
  components.
…ering

- FontsCard: mirror ApiKeysCard's revoke-error handling into the delete-font
  flow — onError sets an inline i18n'd error (settingsFontRemoveError, added
  to en.json/ru.json) and closes the confirm dialog so it's visible;
  confirmDisabled={deleteFont.isPending} prevents a double-fired DELETE from
  rapid re-clicks; the error resets whenever a new remove-confirmation is
  opened or a subsequent delete succeeds. Adds two tests mirroring
  ApiKeysCard.test.tsx's revoke-error/pending-disable cases.
- DangerZoneCard: run queryClient.invalidateQueries unconditionally in
  deleteEvent's onSuccess, before the deleteAbortedRef check — the abort
  guard must only suppress user-visible UI reactions (dialog close,
  navigate), never cache-correctness operations, since a cancelled dialog
  doesn't actually cancel the in-flight server-side delete. Updates the
  existing regression test, which previously asserted the buggy
  no-invalidation behavior.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 15, 2026 01:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot added documentation Improvements or additions to documentation panel shared-ui labels Jul 15, 2026
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@thevladbog, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 29 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 872cef0c-1605-4fc4-ba50-6d6d921f49c2

📥 Commits

Reviewing files that changed from the base of the PR and between 43ab6a2 and f380e94.

📒 Files selected for processing (13)
  • .superpowers/sdd/progress.md
  • docs/superpowers/plans/2026-07-15-panel-p1.2-workspace-settings.md
  • panel/src/features/workspace/WorkspaceOverview.test.tsx
  • panel/src/features/workspace/settings/ApiKeysCard.test.tsx
  • panel/src/features/workspace/settings/ApiKeysCard.tsx
  • panel/src/features/workspace/settings/DangerZoneCard.test.tsx
  • panel/src/features/workspace/settings/DangerZoneCard.tsx
  • panel/src/features/workspace/settings/FontsCard.test.tsx
  • panel/src/features/workspace/settings/FontsCard.tsx
  • panel/src/features/workspace/settings/GeneralCard.test.tsx
  • panel/src/features/workspace/settings/GeneralCard.tsx
  • panel/src/shared/hooks/useScrollSpy.test.ts
  • panel/src/shared/hooks/useScrollSpy.ts
📝 Walkthrough

Walkthrough

This PR replaces the event workspace and organization placeholders with nested workspace routes, readiness-driven overview content, event settings cards, and role-gated organization editing. It also adds shared date and scroll-spy utilities, expanded translations, confirmation-button control, comprehensive tests, and implementation documentation.

Changes

Panel P1.2 workspace rollout

Layer / File(s) Summary
Implementation plan and progress ledger
.superpowers/sdd/progress.md, docs/superpowers/plans/..., panel/AGENTS.md
Documents the workspace/settings rollout, implementation constraints, completed tasks, verification status, and shared hook placement guidance.
Nested workspace shell and readiness rail
panel/src/app/router.tsx, panel/src/features/workspace/*, panel/src/features/events/eventDates.ts, panel/src/features/home/*, packages/ui/src/components/confirm-dialog.tsx
Adds nested overview/settings routes, event header states, readiness navigation, gated launch behavior, shared date formatting, and caller-controlled confirmation disabling.
Workspace overview
panel/src/features/workspace/WorkspaceOverview.tsx, panel/src/features/workspace/WorkspaceOverview.test.tsx
Renders readiness-derived next steps and statistics with loading, ready, skipped-zone, and query-error states.
Settings scaffold and general editing
panel/src/features/workspace/settings/EventSettingsPage.tsx, panel/src/features/workspace/settings/GeneralCard.tsx, panel/src/shared/hooks/useScrollSpy.ts
Adds the four-section settings page, anchor navigation, viewport scroll tracking, validated general fields, scoped PATCH updates, and date-clear restrictions.
Fonts and API keys
panel/src/features/workspace/settings/FontsCard.tsx, panel/src/features/workspace/settings/ApiKeysCard.tsx
Adds font upload/removal and API-key creation, one-time secret reveal, copying, masking, revocation, error handling, and query invalidation.
Danger zone and organization
panel/src/features/workspace/settings/DangerZoneCard.tsx, panel/src/features/organization/OrganizationPage.tsx
Adds cancellation-safe typed event deletion and replaces organization management with admin-gated validation and scoped tenant updates.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant EventWorkspaceLayout
  participant WorkspaceOverview
  participant EventSettingsPage
  participant API

  User->>EventWorkspaceLayout: Open event workspace route
  EventWorkspaceLayout->>API: Fetch event and readiness
  API-->>EventWorkspaceLayout: Event and readiness data
  EventWorkspaceLayout->>WorkspaceOverview: Render matched overview outlet
  WorkspaceOverview->>API: Fetch stats and zones
  API-->>WorkspaceOverview: Statistics and zones data
  User->>EventSettingsPage: Open settings route
  EventSettingsPage->>API: Fetch event settings data
  API-->>EventSettingsPage: Event settings data
Loading

Possibly related PRs

  • thevladbog/idento#59: Introduces the typed-confirmation ConfirmDialog that this PR extends with confirmDisabled.
  • thevladbog/idento#60: Adds router scaffolding that this PR extends with nested workspace and organization pages.

Suggested labels: web

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main addition of the PR: the P1.2 panel workspace, settings, and organization screens.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch panel/p1.2-workspace-settings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🧹 Nitpick comments (1)
panel/src/features/workspace/WorkspaceOverview.tsx (1)

153-157: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider extracting this chip into @idento/ui.

As per coding guidelines, you should use UI primitives only from @idento/ui and never re-implement them inside the app. Consider extracting this locked/muted chip into a Badge or StatusPill variant in @idento/ui rather than styling a bare <span>.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@panel/src/features/workspace/WorkspaceOverview.tsx` around lines 153 - 157,
Extract the locked “coming soon” chip from the WorkspaceOverview markup into a
reusable Badge or StatusPill variant in `@idento/ui`, then replace the bare span
in WorkspaceOverview with that UI primitive while preserving the lock icon,
translation text, and muted styling.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/superpowers/plans/2026-07-15-panel-p1.2-workspace-settings.md`:
- Line 139: Update the wording in the workspace settings plan’s stat-tile error
behavior description to use “error-styled caption” instead of “error styled
caption,” without changing the surrounding behavior or terminology.
- Line 26: Update the plan’s per-commit verification gate and the repeated gate
near Line 280 to retain the repository-mandated npm run lint -w panel command,
while listing cd panel && npx eslint . only as an additional fallback for the
documented output issue. Ensure the panel verification requirements also include
npm test -w panel, npm run typecheck -w panel, and npm run build -w panel from
the repository root.

In `@panel/src/features/workspace/settings/ApiKeysCard.test.tsx`:
- Around line 258-292: Make the abort guard request-scoped rather than relying
on the resettable *_AbortedRef, so responses from an older request cannot affect
a newly reopened dialog. Update ApiKeysCard to prevent stale plainKey updates
and DangerZoneCard to prevent stale navigation or error handling, while
preserving current-session behavior. Extend the reopen-before-settle coverage in
panel/src/features/workspace/settings/ApiKeysCard.test.tsx:258-292 and
panel/src/features/workspace/settings/DangerZoneCard.test.tsx:202-240; both
sites require regression tests for the stale-response scenario.

In `@panel/src/features/workspace/settings/ApiKeysCard.tsx`:
- Line 52: Replace the boolean createAbortedRef session guard with a
monotonically changing session identifier in the create-key dialog flow. Capture
the current identifier when starting the request, and have onSuccess update
setPlainKey only when its captured identifier still matches the active session;
increment or replace the identifier when closing and reopening so an in-flight
response from a prior session remains stale.
- Around line 116-122: Update handleCopy to await navigator.clipboard.writeText
and only set copied and schedule the reset timeout after the write succeeds;
catch or otherwise handle rejected clipboard writes without marking the key as
copied.

In `@panel/src/features/workspace/settings/DangerZoneCard.tsx`:
- Line 48: The delete confirmation flow must ignore responses from a previously
cancelled DELETE after the dialog is reopened. Update deleteAbortedRef and the
handlers around the “Delete event” action so each delete session/request has
distinct identity, and ensure onSuccess/onError only act for the current session
rather than resetting a shared flag that stale responses can observe. Preserve
the existing cancellation behavior for the active delete.

In `@panel/src/features/workspace/settings/FontsCard.tsx`:
- Around line 82-101: The FontsCard upload flow currently hardcodes
license_accepted as "true" without collecting consent. Add an actionable
license-acceptance checkbox state near the existing license notice, require it
before allowing file selection or upload, pass the state as licenseAccepted into
the upload body, and disable the drop-zone until checked.

In `@panel/src/features/workspace/settings/GeneralCard.tsx`:
- Around line 75-96: Update the patchEvent mutation flow and updateField so an
in-flight PATCH response cannot overwrite newer edits. Track the edit/request
version or otherwise validate that the onSuccess result belongs to the latest
form state before applying toFormState, setBaseline, setForm, and setSaved;
preserve reset() for clearing stale mutation state.

In `@panel/src/features/workspace/WorkspaceOverview.test.tsx`:
- Around line 192-198: Update the “/ 200” assertion in the affected
WorkspaceOverview test to await within(tiles).findByText("/ 200") and assert the
resolved element with toBeInTheDocument(), rather than asserting the Promise
with toBeTruthy().

In `@panel/src/shared/hooks/useScrollSpy.ts`:
- Around line 17-54: Update the useScrollSpy effect’s trySetup flow to avoid
indefinitely scheduling requestAnimationFrame retries when sectionIds are absent
from the DOM. Use a MutationObserver or bounded retry mechanism to detect
sections that mount later, and ensure the chosen observer or retry resources are
cleaned up in the effect teardown alongside the existing IntersectionObserver.

---

Nitpick comments:
In `@panel/src/features/workspace/WorkspaceOverview.tsx`:
- Around line 153-157: Extract the locked “coming soon” chip from the
WorkspaceOverview markup into a reusable Badge or StatusPill variant in
`@idento/ui`, then replace the bare span in WorkspaceOverview with that UI
primitive while preserving the lock icon, translation text, and muted styling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0dfef7ed-49ac-41d6-91db-1956d6232fb4

📥 Commits

Reviewing files that changed from the base of the PR and between 88b9f73 and 43ab6a2.

📒 Files selected for processing (32)
  • .superpowers/sdd/progress.md
  • docs/superpowers/plans/2026-07-15-panel-p1.2-workspace-settings.md
  • packages/ui/src/components/confirm-dialog.tsx
  • panel/AGENTS.md
  • panel/src/app/router.tsx
  • panel/src/features/events/EventWorkspaceStub.test.tsx
  • panel/src/features/events/EventWorkspaceStub.tsx
  • panel/src/features/events/eventDates.ts
  • panel/src/features/home/LiveStrip.tsx
  • panel/src/features/home/ReadinessCell.tsx
  • panel/src/features/organization/OrganizationPage.test.tsx
  • panel/src/features/organization/OrganizationPage.tsx
  • panel/src/features/workspace/EventWorkspaceLayout.test.tsx
  • panel/src/features/workspace/EventWorkspaceLayout.tsx
  • panel/src/features/workspace/WorkspaceOverview.test.tsx
  • panel/src/features/workspace/WorkspaceOverview.tsx
  • panel/src/features/workspace/WorkspaceRail.test.tsx
  • panel/src/features/workspace/WorkspaceRail.tsx
  • panel/src/features/workspace/settings/ApiKeysCard.test.tsx
  • panel/src/features/workspace/settings/ApiKeysCard.tsx
  • panel/src/features/workspace/settings/DangerZoneCard.test.tsx
  • panel/src/features/workspace/settings/DangerZoneCard.tsx
  • panel/src/features/workspace/settings/EventSettingsPage.test.tsx
  • panel/src/features/workspace/settings/EventSettingsPage.tsx
  • panel/src/features/workspace/settings/FontsCard.test.tsx
  • panel/src/features/workspace/settings/FontsCard.tsx
  • panel/src/features/workspace/settings/GeneralCard.test.tsx
  • panel/src/features/workspace/settings/GeneralCard.tsx
  • panel/src/shared/hooks/useScrollSpy.test.ts
  • panel/src/shared/hooks/useScrollSpy.ts
  • panel/src/shared/i18n/en.json
  • panel/src/shared/i18n/ru.json
💤 Files with no reviewable changes (2)
  • panel/src/features/events/EventWorkspaceStub.test.tsx
  • panel/src/features/events/EventWorkspaceStub.tsx

Comment thread docs/superpowers/plans/2026-07-15-panel-p1.2-workspace-settings.md
Comment thread docs/superpowers/plans/2026-07-15-panel-p1.2-workspace-settings.md Outdated
Comment thread panel/src/features/workspace/settings/ApiKeysCard.test.tsx
Comment thread panel/src/features/workspace/settings/ApiKeysCard.tsx Outdated
Comment thread panel/src/features/workspace/settings/ApiKeysCard.tsx
Comment thread panel/src/features/workspace/settings/DangerZoneCard.tsx Outdated
Comment thread panel/src/features/workspace/settings/FontsCard.tsx
Comment thread panel/src/features/workspace/settings/GeneralCard.tsx
Comment thread panel/src/features/workspace/WorkspaceOverview.test.tsx Outdated
Comment thread panel/src/shared/hooks/useScrollSpy.ts
…write

A PATCH response landing after a newer, still-unsaved edit could silently
clobber the user's latest typing — reset() on keystroke only detaches the
mutation observer, it doesn't cancel the in-flight request. Gate
setBaseline/setForm on an edit-version captured at submit time via onMutate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CI Bot and others added 7 commits July 15, 2026 05:05
createAbortedRef/deleteAbortedRef were plain booleans re-armed on every
reopen, so a second cancel-then-reopen cycle let a stale response from the
FIRST (already-abandoned) request slip past the guard after the reopen —
in ApiKeysCard this could leak an old, abandoned key's plaintext secret; in
DangerZoneCard it could force-navigate or surface a stale error. Replace
both with monotonically-incrementing session refs, captured at mutate-time
via onMutate and compared exactly in onSuccess/onError, so a reopen can
never "un-stale" a response tied to a previously-closed session.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
handleCopy fired navigator.clipboard.writeText() without awaiting or
catching it, so a rejected write (e.g. permission blocked) still flipped
the button to "Copied" — misleading the user into thinking the one-time
secret was actually saved to their clipboard. Only set copied/schedule the
reset on a resolved write.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
license_accepted: "true" was sent unconditionally on every upload, and the
license notice was inert text — "accepted" never reflected real consent.
Turn the notice into a real checkbox; disable the upload input until it's
checked; wire the upload body's license_accepted from the actual state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…r test

expect(within(tiles).findByText("/ 200")).toBeTruthy() asserted on an
un-awaited Promise, which is always truthy — the check passed regardless
of whether the element ever rendered. Await and assert on the resolved
element instead, and strengthen the adjacent unasserted await the same way.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…mount

If none of sectionIds ever appear in the DOM (e.g. the caller stuck in a
persistently-failing loading state), trySetup rescheduled itself via
requestAnimationFrame forever — an unbounded ~60fps busy-loop for as long
as the component stayed mounted. Cap retries at 150 (~2.5s), then stop
rescheduling and leave activeId at its last value.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Compound-adjective grammar fix, no code impact.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation panel shared-ui

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants