Skip to content

Integrate Rewards page with backend Discord roles and achievements#475

Merged
senamakel merged 4 commits intotinyhumansai:mainfrom
senamakel:feat/rewards-page-backend-336
Apr 10, 2026
Merged

Integrate Rewards page with backend Discord roles and achievements#475
senamakel merged 4 commits intotinyhumansai:mainfrom
senamakel:feat/rewards-page-backend-336

Conversation

@senamakel
Copy link
Copy Markdown
Member

@senamakel senamakel commented Apr 10, 2026

Summary

  • Replace the Rewards page's client-only Discord role heuristics with a backend-backed rewards snapshot fetched from /rewards/me
  • Add a dedicated rewardsApi client plus normalized rewards types for Discord linkage, role sync state, achievements, and server-tracked metrics
  • Update the Rewards UI to show loading, error, and conservative fallback states without claiming unlocks when backend sync is unavailable
  • Add page and API unit tests covering normalized payloads and failure handling
  • Consume the backend reward contract that is already available on tinyhumansai/backend:develop

Problem

  • app/src/pages/Rewards.tsx inferred reward progress from local message counts, Redux channel state, and profile fields instead of canonical backend data
  • That could disagree with actual Discord role assignment and persisted achievements, and it had no explicit state for backend sync failures or partially linked Discord accounts
  • Issue [Feature] Integrate Rewards page with backend for Discord roles and achievements #336 calls for backend-aligned rewards and achievements while keeping the page useful when offline or unsynced

Solution

  • Add app/src/services/api/rewardsApi.ts and app/src/types/rewards.ts to normalize the backend rewards payload into stable app-side types
  • Rework app/src/pages/Rewards.tsx to fetch the rewards snapshot on load and render Discord membership, role-sync status, achievement progress, and credit amounts from backend data
  • Keep the Rewards page conservative on failure by showing connection guidance and an error banner instead of false unlocks
  • Add Vitest coverage for payload normalization and page rendering/error states
  • Reuse the backend /rewards/me contract that is already present in tinyhumansai/backend, so no backend changes are included in this PR

Submission Checklist

  • Unit tests — Vitest (app/) and/or cargo test (core) for logic you add or change
  • E2E / integration — Where behavior is user-visible or crosses UI → Tauri → sidecar → JSON-RPC; use existing harnesses (app/test/e2e, mock backend, tests/json_rpc_e2e.rs as appropriate)
  • N/A — If truly not applicable, say why (e.g. change is documentation-only)
  • Doc comments/// / //! (Rust), JSDoc or brief file/module headers (TS) on public APIs and non-obvious modules
  • Inline comments — Where logic, invariants, or edge cases aren’t clear from names alone (keep them grep-friendly; avoid restating the code)

(Any feature related checklist can go in here)

Impact

  • Desktop Rewards UI now reflects backend-tracked Discord reward state instead of local heuristics
  • No core/Tauri transport changes in this PR; it uses the existing authenticated backend client from the app
  • Backend dependency: expects the /rewards/me endpoint already available on tinyhumansai/backend:develop

Related

Summary by CodeRabbit

Release Notes

  • New Features

    • Rewards system now dynamically fetches achievement data from backend on page load.
    • Added loading indicators and error handling with fallback messaging when sync is unavailable.
    • Achievement cards now display Discord role assignment status and credit amounts.
  • Tests

    • Added comprehensive test coverage for rewards API integration and UI states.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 10, 2026

Warning

Rate limit exceeded

@senamakel has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 6 minutes and 47 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 6 minutes and 47 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6642b26a-bf7e-4863-89d1-59c9bdba6c9d

📥 Commits

Reviewing files that changed from the base of the PR and between 1ce5857 and c923536.

📒 Files selected for processing (3)
  • app/src/pages/Rewards.tsx
  • app/src/services/api/__tests__/rewardsApi.test.ts
  • app/src/services/api/rewardsApi.ts
📝 Walkthrough

Walkthrough

The Rewards page has been refactored to fetch achievement and Discord role data from the backend via a new rewardsApi.getMyRewards() endpoint instead of deriving it locally from client state. A new API service layer with safe normalization functions and comprehensive type definitions support the backend integration, complete with loading and error handling.

Changes

Cohort / File(s) Summary
Rewards Page Component
app/src/pages/Rewards.tsx, app/src/pages/__tests__/Rewards.test.tsx
Replaced local rewards derivation with backend-driven RewardsSnapshot fetching. Introduced async loading/error states, updated progress and role rendering to use snapshot data, and added per-role Discord status and credit amount UI. Tests verify loading state, data rendering, and error handling with fallback UI.
Rewards API & Type Definitions
app/src/services/api/rewardsApi.ts, app/src/services/api/__tests__/rewardsApi.test.ts, app/src/types/rewards.ts
Introduced new RewardsSnapshot and RewardsAchievement types with Discord membership/role status enums. Implemented rewardsApi.getMyRewards() with safe normalization helpers (asRecord, asNumber, asStringOrNull) to coerce unknown API payloads into typed structures with fallback defaults. Tests verify normalization and API call behavior.

Sequence Diagram

sequenceDiagram
    participant RC as Rewards Component
    participant API as rewardsApi
    participant Backend as Backend (/rewards/me)
    participant Normalize as normalizeRewardsSnapshot

    RC->>RC: Component mounts
    RC->>RC: Set isLoading=true
    RC->>API: getMyRewards()
    API->>Backend: GET /rewards/me
    alt Success
        Backend-->>API: RewardsSnapshot JSON
        API->>Normalize: normalizeRewardsSnapshot(payload)
        Normalize-->>API: RewardsSnapshot (typed & validated)
        API-->>RC: RewardsSnapshot
        RC->>RC: Set snapshot, isLoading=false
        RC->>RC: Render achievements & summary
    else Error
        Backend-->>API: Error response
        API-->>RC: Rejection
        RC->>RC: Set error, isLoading=false
        RC->>RC: Show error alert + sync pending fallback
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 Backend rewards hop in today,
No more guessing what Discord will say,
Achievements fetch, fallbacks in place,
Data flows safe with normalization grace,
Our rewards page now shines so bright! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Integrate Rewards page with backend Discord roles and achievements' directly and accurately summarizes the main objective of the changeset: replacing client-derived rewards logic with backend-driven data fetched from a new /rewards/me endpoint.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/pages/Rewards.tsx`:
- Around line 99-103: The UI uses two different sources for totals causing
inconsistent progress: keep a single computed total and unlocked count used
everywhere. Replace uses of totalCount and unlockedCount with a unified total =
snapshot?.summary.totalCount ?? rewardRoles.length ?? 0 and unlocked =
snapshot?.summary.unlockedCount ?? rewardRoles.filter(r => r.unlocked).length
(or similar) and then compute progressWidth = total > 0 ? (unlocked / total) *
100 : 0; update all references (rewardRoles, unlockedCount, totalCount,
progressWidth) to use these unified values so the count and progress bar remain
consistent.
- Around line 58-64: The synchronous state resets inside the useEffect (the
calls to setIsLoading(true) and setError(null) at the start of loadRewards)
should be removed because they run synchronously before the first await and
violate the react-hooks/set-state-in-effect rule; edit the useEffect/loadRewards
block to eliminate those two immediate setter calls (or, if you need to reset
state only when the async begins, move them to just before the first await
inside the async function) and keep cancellation handling (cancelled flag) and
the existing try/catch/finally logic in functions loadRewards/useEffect intact.

In `@app/src/services/api/rewardsApi.ts`:
- Around line 20-44: normalizeAchievement is converting malformed credit amounts
into 0 by calling asNumber directly; change the logic for creditAmountUsd so it
returns null unless the raw value is a valid finite number (e.g. check typeof
raw.creditAmountUsd === 'number' or if it's a string, attempt
Number(raw.creditAmountUsd) and verify Number.isFinite(parsed) before calling
asNumber). Update the creditAmountUsd assignment in normalizeAchievement to
perform this validation and only call asNumber when the parsed value is a valid
finite number; otherwise set creditAmountUsd to null.
- Around line 92-100: The getMyRewards method in rewardsApi currently calls
apiClient.get<ApiResponse<unknown>>('/rewards/me') and immediately passes
response.data to normalizeRewardsSnapshot; you must validate the
ApiResponse.success flag before using response.data. Update getMyRewards (and
other API methods that follow this pattern) to check response.data.success (or
have apiClient.get throw when success is false), and if success is false throw
or return a rejected error that includes the backend error message (from
response.data.error) so callers aren’t silently shown empty data; look for the
symbols rewardsApi, getMyRewards, apiClient.get, ApiResponse, and
normalizeRewardsSnapshot to implement this change.
🪄 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: 491fa44b-8728-4775-9d5a-f33ec17da697

📥 Commits

Reviewing files that changed from the base of the PR and between 38eb934 and 1ce5857.

📒 Files selected for processing (5)
  • app/src/pages/Rewards.tsx
  • app/src/pages/__tests__/Rewards.test.tsx
  • app/src/services/api/__tests__/rewardsApi.test.ts
  • app/src/services/api/rewardsApi.ts
  • app/src/types/rewards.ts

…ackend-336

# Conflicts:
#	app/src/pages/Rewards.tsx
…ogic

- Removed redundant loading state management in the Rewards component.
- Updated the calculation of unlocked and total rewards to derive values from the achievements array when necessary, enhancing accuracy.
- Adjusted the display logic to reflect the new calculation method for better clarity.

Enhancements in the rewards API include improved error handling for backend failures, ensuring robust user feedback during data retrieval. Added a utility function to validate numeric values, enhancing data normalization for achievements. Updated tests to cover new error handling scenarios and ensure consistent behavior across the rewards API.
- Adjusted the formatting of the unlocked rewards calculation for better clarity and maintainability.
- Ensured consistent code style in the Rewards component, enhancing overall readability.

These changes contribute to a cleaner codebase and facilitate future modifications.
@senamakel senamakel merged commit d5822ff into tinyhumansai:main Apr 10, 2026
8 of 9 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