Skip to content

fix(mapping): UI updates after match, persist character name - #25

Merged
guarzo merged 3 commits into
mainfrom
fix/mapping-ui-update
May 5, 2026
Merged

fix(mapping): UI updates after match, persist character name#25
guarzo merged 3 commits into
mainfrom
fix/mapping-ui-update

Conversation

@guarzo

@guarzo guarzo commented May 5, 2026

Copy link
Copy Markdown
Owner

Summary

  • Associations API returns charId/charName (frontend was reading undefined charId), fixing the "unknown" name and matched characters still showing as selectable until manual refresh.
  • AssociateCharacter now persists charName; POST/DELETE responses include {success, message} so the frontend's optimistic update path actually fires — no more refresh-to-see-the-match.
  • Remove duplicate w.WriteHeader in CreateAssociation (was logging superfluous response.WriteHeader call).
  • Migrate 5 MUI Dialogs from deprecated PaperProps to slotProps.paper (MUI v9) — silences the React DOM unknown-attribute warning.

Test plan

  • Drag a character onto a user file: name appears immediately, character disappears from the unassociated list, no manual refresh needed.
  • Reload — match persists with correct character name (not "unknown").
  • No superfluous response.WriteHeader log line on associate.
  • No PaperProps React warning in devtools.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Character name is now captured and returned with associations for clearer records.
  • Improvements

    • Association actions now return richer, consistent JSON responses (success/message plus charId and charName).
    • Dialog styling updated for consistent appearance across account, character, confirm, setup, and skill‑plan modals.
  • Tests

    • Updated tests to validate inclusion of character name in association requests.

…perfluous WriteHeader

- Associations API now returns `charId`/`charName` (was `characterId` only),
  fixing the "unknown" name and the matched character still appearing in
  the unassociated list until refresh.
- `AssociateCharacter` persists `charName`; handler accepts it from POST body.
- POST/DELETE responses include `{success, message}` so the frontend's
  optimistic update branch actually runs (was returning 204/no body).
- Remove duplicate `w.WriteHeader` in CreateAssociation that was logging
  "superfluous response.WriteHeader call".
- Migrate 5 MUI Dialogs from deprecated `PaperProps` to `slotProps.paper`
  to silence React DOM unknown-attribute warning under MUI v9.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 5, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: dbbefecb-b3ba-4f76-9288-15b8031e58b9

📥 Commits

Reviewing files that changed from the base of the PR and between e332fae and 0da2f70.

📒 Files selected for processing (1)
  • internal/services/account/account_management_service.go

📝 Walkthrough

Walkthrough

Association flows now accept and persist a new charName field and return it in JSON responses; interface and mocks updated. Multiple React MUI Dialog components were migrated from PaperProps to slotProps.paper for styling wiring.

Changes

Character Association Enhancement

Layer / File(s) Summary
Interface Contract
internal/services/interfaces/account_management.go, internal/testutil/mock_interfaces.go
AssociateCharacter signature expanded to (userId, charId, charName string); mocks updated to include charName in m.Called(...).
Service Data / Persistence
internal/services/account/account_management_service.go
AssociateCharacter(userId, charId, charName) added; existing associations always update UserId and update CharName only if charName != ""; new associations persist CharName. Storage load/save errors wrapped with contextual fmt.Errorf.
Handler / API Mapping
internal/handlers/associations.go
ListAssociations emits charId (was characterId) and charName. CreateAssociation accepts charName, calls service with (userId, characterId, charName), and returns JSON { success, message, userId, charId, charName }. DeleteAssociation now returns JSON { success, message } instead of HTTP 204.
Frontend Client & Tests
renderer/src/api/accountsApi.js, renderer/src/api/accountsApi.test.js
associateCharacter now accepts and sends charName in the POST body; tests updated to expect charName in request payload.

MUI Dialog Styling Migration

Layer / File(s) Summary
Dialog Prop Rewiring
renderer/src/components/common/AccountPromptModal.jsx, renderer/src/components/common/CharacterDetailModal.jsx, renderer/src/components/common/CustomConfirmDialog.jsx, renderer/src/components/setup/FirstRunDialog.jsx, renderer/src/components/skillplan/AddSkillPlanModal.jsx
Replaced PaperProps={{ sx: ... }} with slotProps={{ paper: { sx: ... } }} (and preserved className where present) for Dialog components; sx values unchanged.
sequenceDiagram
  participant Client as Browser UI
  participant API as HTTP Handler (/api/associations)
  participant Service as AccountManagementService
  participant DB as Database

  Client->>API: POST /api/associations { userId, characterId, charName }
  API->>Service: AssociateCharacter(userId, characterId, charName)
  alt association exists
    Service->>DB: update association (UserId, CharName if non-empty)
  else new association
    Service->>DB: insert association (UserId, CharId, CharName)
  end
  Service-->>API: result { success, message, userId, charId, charName }
  API-->>Client: 200 JSON { success, message, userId, charId, charName }
Loading

🎯 3 (Moderate) | ⏱️ ~20 minutes

🐰 I hop through code with a tiny cheer,
I tuck charName where friends appear,
PaperProps swapped for shiny slots,
New JSON returns tie up the knots,
A carrot-sized update—swift and clear.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.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 directly reflects the main changes: UI persistence of character name associations and fixing mapping-related issues after character matching.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/mapping-ui-update
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix/mapping-ui-update

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

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/services/account/account_management_service.go (1)

349-374: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Guard AssociateCharacter with the service mutex to prevent lost updates.

This method mutates shared persisted state via a read-modify-write cycle without locking. Concurrent association requests can overwrite each other.

🔧 Proposed fix
 func (s *AccountManagementService) AssociateCharacter(userId, charId, charName string) error {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+
 	accountData, err := s.storage.LoadAccountData()
 	if err != nil {
 		return err
 	}
🤖 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 `@internal/services/account/account_management_service.go` around lines 349 -
374, AssociateCharacter performs a read-modify-write on persisted account data
without synchronization, causing lost updates under concurrency; guard the
entire operation by adding/using a mutex on AccountManagementService (e.g., a
sync.Mutex or sync.RWMutex field like mu) and call s.mu.Lock() before
loading/modifying/saving (with defer s.mu.Unlock()) so the sequence around
storage.LoadAccountData and storage.SaveAccountData is atomic; if the service
lacks a mutex field, add one to the struct and use it in AssociateCharacter to
serialize concurrent calls.
🤖 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.

Outside diff comments:
In `@internal/services/account/account_management_service.go`:
- Around line 349-374: AssociateCharacter performs a read-modify-write on
persisted account data without synchronization, causing lost updates under
concurrency; guard the entire operation by adding/using a mutex on
AccountManagementService (e.g., a sync.Mutex or sync.RWMutex field like mu) and
call s.mu.Lock() before loading/modifying/saving (with defer s.mu.Unlock()) so
the sequence around storage.LoadAccountData and storage.SaveAccountData is
atomic; if the service lacks a mutex field, add one to the struct and use it in
AssociateCharacter to serialize concurrent calls.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 353606cf-24aa-4b00-94ea-694d8db174b0

📥 Commits

Reviewing files that changed from the base of the PR and between 7950cb0 and b8f158f.

📒 Files selected for processing (10)
  • internal/handlers/associations.go
  • internal/services/account/account_management_service.go
  • internal/services/interfaces/account_management.go
  • internal/testutil/mock_interfaces.go
  • renderer/src/api/accountsApi.js
  • renderer/src/components/common/AccountPromptModal.jsx
  • renderer/src/components/common/CharacterDetailModal.jsx
  • renderer/src/components/common/CustomConfirmDialog.jsx
  • renderer/src/components/setup/FirstRunDialog.jsx
  • renderer/src/components/skillplan/AddSkillPlanModal.jsx

…update test

- Take s.mu in AssociateCharacter and UnassociateCharacter to prevent
  lost updates from concurrent read-modify-write on persisted account data
  (CodeRabbit review).
- Update accountsApi test to assert charName in the POST body now that the
  client sends it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/services/account/account_management_service.go (1)

353-356: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add contextual wrapping and error logs in association persistence paths.

These storage failures are returned raw, which loses operation context and reduces observability in production incidents.

Proposed patch
 func (s *AccountManagementService) AssociateCharacter(userId, charId, charName string) error {
 	s.mu.Lock()
 	defer s.mu.Unlock()

 	accountData, err := s.storage.LoadAccountData()
 	if err != nil {
-		return err
+		s.logger.Errorf("AssociateCharacter: failed to load account data: %v", err)
+		return fmt.Errorf("associate character: load account data: %w", err)
 	}
@@
-			return s.storage.SaveAccountData(accountData)
+			if err := s.storage.SaveAccountData(accountData); err != nil {
+				s.logger.Errorf("AssociateCharacter: failed to save updated association for charId=%s userId=%s: %v", charId, userId, err)
+				return fmt.Errorf("associate character: save updated association: %w", err)
+			}
+			return nil
 		}
 	}
@@
-	return s.storage.SaveAccountData(accountData)
+	if err := s.storage.SaveAccountData(accountData); err != nil {
+		s.logger.Errorf("AssociateCharacter: failed to save new association for charId=%s userId=%s: %v", charId, userId, err)
+		return fmt.Errorf("associate character: save new association: %w", err)
+	}
+	return nil
 }

 func (s *AccountManagementService) UnassociateCharacter(userId, charId string) error {
 	s.mu.Lock()
 	defer s.mu.Unlock()

 	accountData, err := s.storage.LoadAccountData()
 	if err != nil {
-		return err
+		s.logger.Errorf("UnassociateCharacter: failed to load account data: %v", err)
+		return fmt.Errorf("unassociate character: load account data: %w", err)
 	}
@@
-	return s.storage.SaveAccountData(accountData)
+	if err := s.storage.SaveAccountData(accountData); err != nil {
+		s.logger.Errorf("UnassociateCharacter: failed to save account data for charId=%s userId=%s: %v", charId, userId, err)
+		return fmt.Errorf("unassociate character: save account data: %w", err)
+	}
+	return nil
 }

As per coding guidelines, "Use structured errors with proper logging in Go backend code" and "Use logrus with appropriate log levels for logging in Go backend code".

Also applies to: 365-366, 376-377, 383-386, 396-397

🤖 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 `@internal/services/account/account_management_service.go` around lines 353 -
356, The storage calls like s.storage.LoadAccountData() (and the other storage
calls in the association persistence paths) return raw errors; update each call
to log a structured message with logrus including context and the error (e.g.,
logrus.WithError(err).WithFields(...).Error("failed to load account data") ),
and return a wrapped error using fmt.Errorf("failed to load account data: %w",
err) (or errors.Wrapf) so callers retain context; apply the same pattern to the
other storage calls referenced (the similar s.storage.* calls in the association
persistence code paths).
🤖 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.

Outside diff comments:
In `@internal/services/account/account_management_service.go`:
- Around line 353-356: The storage calls like s.storage.LoadAccountData() (and
the other storage calls in the association persistence paths) return raw errors;
update each call to log a structured message with logrus including context and
the error (e.g., logrus.WithError(err).WithFields(...).Error("failed to load
account data") ), and return a wrapped error using fmt.Errorf("failed to load
account data: %w", err) (or errors.Wrapf) so callers retain context; apply the
same pattern to the other storage calls referenced (the similar s.storage.*
calls in the association persistence code paths).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 02ace0cc-b7ca-4c21-8ebe-d845f3952734

📥 Commits

Reviewing files that changed from the base of the PR and between b8f158f and e332fae.

📒 Files selected for processing (3)
  • .claude/settings.local.json
  • internal/services/account/account_management_service.go
  • renderer/src/api/accountsApi.test.js

Per CodeRabbit review: storage calls in AssociateCharacter,
UnassociateCharacter, and GetAssociations now wrap errors with
fmt.Errorf("...: %w", err) so the handler's existing log line includes
which storage operation failed. Skipped the suggestion to add structured
service-level logging since the handlers already log with context — adding
service logs would just duplicate them.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@guarzo
guarzo merged commit 30309e6 into main May 5, 2026
2 checks passed
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