Skip to content

frontend betschemas done - #72

Merged
reijjo merged 1 commit into
mainfrom
front
May 14, 2026
Merged

frontend betschemas done#72
reijjo merged 1 commit into
mainfrom
front

Conversation

@reijjo

@reijjo reijjo commented May 14, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • Documentation

    • Expanded project structure guides for frontend and backend development
    • Clarified directory layouts and development organization
  • Refactor

    • Strengthened type safety for user roles across the application
    • Reorganized betting data structures with improved validation and consistency

@coderabbitai

coderabbitai Bot commented May 14, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

This PR unifies role and bet data models across frontend and backend by replacing loose string/untyped representations with strongly-typed schemas and enums. The frontend adopts Zod-based validation schemas for bets and auth roles; the backend consolidates role representation into a domain enum, removes string type casts from database queries, and threads the typed role through JWT claims and middleware. Documentation clarifies the new module structure.

Changes

Role Type System Unification

Layer / File(s) Summary
Role enum foundation and User struct
rust-server/src/types.rs
Backend defines UserRole enum with Serde and sqlx::Type annotations for GUEST, PAID, BOSS; updates User.role from String to UserRole.
Auth response and claim types
rust-server/src/features/auth/types.rs, rust-server/src/features/auth/tokens/jwt.rs
MeResponse, LoginResponse, LoginSessionResult structs and AccessClaims update role field to UserRole; sign_access_token signature changes to accept role: UserRole.
Auth service and handlers
rust-server/src/features/auth/service.rs, rust-server/src/features/auth/handlers/me.rs
JWT creation uses user.role directly (eliminating redundant clones); me handler explicitly types row reads as row.get::<UserRole, _>("role").
Auth middleware
rust-server/src/middleware/auth.rs
AuthUser.role changes to UserRole; require_role now accepts &[UserRole] allowlist with direct membership test instead of string iteration.
Database queries
rust-server/src/db/queries.rs
User lookups by email, username, and ID remove role::text AS role casting; queries now select role directly, allowing sqlx to decode into typed UserRole.
Frontend UserRole schema
client/src/features/auth/types.ts
Defines userRoleSchema Zod enum and exports UserRole TypeScript type for frontend use; getMe return type updated to use UserRole.
Documentation updates
rust-server/AGENTS.md, rust-server/documentation.md, README.md
Backend structure docs clarify src/types.rs houses shared domain types like UserRole; top-level README expands project layout.

Bet Schema Definition and Data Model Migration

Layer / File(s) Summary
Bet schema definition
client/src/features/bets/schemas.ts
Introduces Zod schemas for BetStatus (enum), BetType (enum), BetDetails (object with nested fields and arrays), and Bet (top-level with betDetails array); exports inferred TypeScript types for each.
Bet constants and form types
client/src/features/bets/constants.ts, client/src/features/bets/types.ts
BET_STATUS_LABELS and BET_TYPE_LABELS map enums to display strings; BetFormValues and BetDetailsFormValues derive from schemas via Omit<>, removing id and foreign-key fields for form input.
Component import consolidation
client/src/components/ui/bets/BetStatusBall.tsx, client/src/lib/utils/betHelpers.ts
Update BetStatus imports from @/lib/types/bets to @/features/bets/schemas; component logic remains unchanged.
Dashboard bet display refactor
client/src/features/dashboard/components/big-cards/monthly-latest/LatestBet.tsx, client/src/features/dashboard/components/big-cards/monthly-latest/LatestCard.tsx
LatestBet now reads display metadata from bet.betDetails[0] and uses bet.betFinalOdds; LatestCard updates type import and expands placeholder data to match full Bet schema shape with nested details.
Remove legacy types
client/src/lib/types/bets.ts
Deletes old BetStatus, BetType, Bet, BetDetails type definitions; all consumers now import from @/features/bets/schemas.
Documentation updates
client/AGENTS.md, client/documentation.md, README.md
Frontend structure docs clarify route-local _components/ and how src/features/ and src/components/ are organized; updates src/lib/ documentation to match new structure.

Sequence Diagram(s)

sequenceDiagram
    participant Browser as Browser
    participant FrontendAPI as Frontend (getMe)
    participant Backend as Backend (Auth Service)
    participant Database as Database
    participant JWT as JWT / Middleware

    Browser->>FrontendAPI: Request user data
    FrontendAPI->>Backend: GET /api/me with token
    
    Backend->>JWT: Verify AccessClaims with role: UserRole
    JWT-->>Backend: Valid claims (role typed as enum)
    
    Backend->>Database: SELECT * FROM users WHERE id=?
    Database-->>Backend: User { role: UserRole } (no string cast)
    
    Backend-->>FrontendAPI: MeResponse { role: UserRole }
    FrontendAPI-->>Browser: Update UI with typed role (GUEST|PAID|BOSS)
Loading
sequenceDiagram
    participant Component as Dashboard Component
    participant Store as Data Store
    participant BetService as Bet Service
    participant Formatter as Bet Constants

    Component->>BetService: Request latest bets
    BetService-->>Component: Bet[] with betDetails[]

    Component->>Component: Read from bet.betDetails[0]
    Component->>Formatter: Look up BET_STATUS_LABELS[bet.status]
    Formatter-->>Component: Display label (e.g., "Won")
    
    Component->>Component: Use bet.betFinalOdds for calculation
    Component-->>Store: Render with typed metadata and odds
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Reasoning: This refactoring touches 20+ files across frontend and backend, affecting core auth and bet data flows. While the pattern is consistent (string → enum/Zod type), you'll need to verify:

  • Each import migration and type source correctness across disconnected file trees
  • SQL row decoding and sqlx type binding correctness (role::text removal, explicit type parameters)
  • Schema shape changes in dashboard placeholders align with new betDetails nesting
  • Middleware role check logic (string-in-array → enum membership) is semantically equivalent
  • All auth handlers and services thread the typed UserRole without stranding unconverted code paths

Professional Dev Wisdom

This PR teaches a valuable lesson: Type-driven refactoring at scale requires consistency across stack layers. You've done something pro here:

  1. Define types at the source, not the boundary. Rather than letting each service own its own string representation of "role," you've placed the source-of-truth enum in shared backend types and mirrored it on the frontend via Zod. This prevents drift and makes serialization automatic.

  2. Schemas as contracts, not afterthoughts. Your betSchema isn't just a Zod validator—it's now the single definition of the Bet shape. That means forms, API responses, and internal logic all speak the same language. No more "wait, does Bet have odds or betFinalOdds?"

  3. Remove the cast, not the problem. Deleting role::text AS role from queries isn't just cleanup—it pushes responsibility to sqlx to handle the type conversion at the right layer (the database driver), where it can fail loudly if the schema changes. That's better than silently converting everything to strings and hoping.

  4. Middleware simplification follows from type safety. Your require_role function changed from allowed.contains(&auth.role.as_str()) logic to allowed.contains(&auth.role). That's not just prettier—it's impossible to mistype a role string now.

Next level: Consider whether your form-value types (BetFormValues) should also have a Zod schema (e.g., betFormSchema) so your form handlers can validate inputs the same way your API does. That keeps frontend and backend validation in sync.

Possibly Related PRs

  • reijjo/tarpit2.0#69: Introduced the dashboard's LatestBet and BetStatusBall components; this PR updates their type imports and data shape handling.
  • reijjo/tarpit2.0#58: Modified the same src/db/queries.rs user lookup queries; this PR removes the role::text casting that may have been introduced there.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'frontend betschemas done' is vague and generic, lacking clarity about the actual scope of changes made to the codebase. Consider a more descriptive title that reflects the main work, such as 'Migrate bet types and schemas from lib/types to features/bets' or 'Refactor bet schemas and introduce UserRole enum'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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 front

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 and usage tips.

@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: 5

Caution

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

⚠️ Outside diff range comments (1)
rust-server/src/middleware/auth.rs (1)

57-64: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Small mentor nudge on require_role.

The implementation is idiomatic — allowed.contains(&auth.role) is exactly the right shape now that UserRole: Eq + Copy, and it reads better than the old string slice version. Two seasoned-dev habits worth adopting here:

  1. Drop #[allow(dead_code)] the moment you have a first caller. That attribute is a future-proofing band-aid; if it lingers, it hides genuinely dead helpers from the compiler. A common pattern is to introduce the helper in the same PR that wires the first protected route, so the lint stays honest.
  2. Consider an ergonomic call-site macro/helper later. Once you have several protected routes, writing require_role(&auth, &[UserRole::Boss, UserRole::Paid])?; at the top of every handler gets repetitive. A tower/axum-style layer or extractor wrapper (e.g. AuthUser<{Role::Boss}>) is a great next-level move — but only worth doing once the duplication actually shows up. YAGNI until then.

No blocker — just markers on the road to "pro" 🙂.

🤖 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 `@rust-server/src/middleware/auth.rs` around lines 57 - 64, Remove the
unnecessary #[allow(dead_code)] on the require_role function so the compiler
will warn if it becomes unused; locate the require_role(auth: &AuthUser,
allowed: &[UserRole]) -> Result<(), AppError> helper and simply delete the
attribute line above it (keep the function body as-is), and keep in mind later
you can replace repeated call-sites like require_role(&auth, &[UserRole::Boss,
UserRole::Paid])? with a layer/extractor or macro once duplication appears.
🤖 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 `@client/documentation.md`:
- Line 14: Update the tree header entry for "lib/" to match the actual contents
described later: replace "API clients, auth helpers, hooks, schemas, stores,
types, utils" with the accurate list used by the code (e.g., "auth, constants,
hooks, stores, types, utils") so the top-level line for "lib/" is consistent
with the detailed section below; edit the string in client/documentation.md
where the tree header line contains "lib/" to reflect the new wording.

In `@client/src/features/bets/schemas.ts`:
- Around line 43-45: The numeric bet fields allow invalid negatives—update the
Zod schemas for odds, stake, homeScore, and awayScore so they enforce domain
bounds: use z.number().positive() for stake and odds (must be >0), and use
z.number().int().nonnegative() for homeScore and awayScore (scores >=0); apply
these changes in the same schema objects where the symbols odds, stake,
homeScore, and awayScore are defined so runtime validation rejects negative or
fractional scores and zero/negative stakes/odds.

In
`@client/src/features/dashboard/components/big-cards/monthly-latest/LatestBet.tsx`:
- Line 13: The betDetails array schema currently allows empty arrays which lets
LatestBet (where detail is read via const detail = bet.betDetails[0]) render
blank fields; update the Zod definition (the betDetails array created from
betDetailsSchema) to require at least one entry by adding .min(1) to the
z.array(...) declaration, or alternatively add explicit handling in the
LatestBet component to check bet.betDetails.length before accessing index 0 and
render a clear fallback/error UI; target the betDetailsSchema array definition
and the LatestBet access site when making the change.

In `@client/src/lib/auth/getMe.ts`:
- Line 4: The `/me` response currently assumes backend returns a valid UserRole
but res.json() can contain invalid values; add runtime validation with Zod
before returning. Define a Zod schema (e.g., MeSchema) that validates at least
data.role as the union or enum matching UserRole, parse the fetched payload (the
object returned by res.json()) inside the getMe function, and throw or handle
validation errors if parsing fails; return the parsed/validated result instead
of the raw payload so callers receive a type-safe object. Ensure you reference
and validate data.role and adjust the function return flow in getMe accordingly.

In `@rust-server/src/features/auth/tokens/jwt.rs`:
- Around line 8-20: Changing AccessClaims.role to UserRole will break existing
tokens; update the JWT schema to be versioned: add a ver: u8 field to
AccessClaims, set ver = 1 in sign_access_token, and update verify_access_token
to accept tokens with ver == 1 while treating missing/other versions as
expired/unauthorized (so clients are forced to refresh rather than failing to
deserialize), or alternatively implement a deploy-time secret roll or shortened
TTL before this change — pick one strategy and implement it. Also, preserve
diagnostic errors from jsonwebtoken by logging the original error with
tracing::error! inside sign_access_token and verify_access_token before mapping
to AppError::internal/unauthorized so you can debug signing/verifying failures.
Ensure references: AccessClaims, sign_access_token, verify_access_token,
UserRole, and AppError are updated accordingly.

---

Outside diff comments:
In `@rust-server/src/middleware/auth.rs`:
- Around line 57-64: Remove the unnecessary #[allow(dead_code)] on the
require_role function so the compiler will warn if it becomes unused; locate the
require_role(auth: &AuthUser, allowed: &[UserRole]) -> Result<(), AppError>
helper and simply delete the attribute line above it (keep the function body
as-is), and keep in mind later you can replace repeated call-sites like
require_role(&auth, &[UserRole::Boss, UserRole::Paid])? with a layer/extractor
or macro once duplication appears.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a79ebcee-67c2-41b4-b91f-b86c3b0ce3c1

📥 Commits

Reviewing files that changed from the base of the PR and between cfa4388 and 9689832.

📒 Files selected for processing (22)
  • README.md
  • client/AGENTS.md
  • client/documentation.md
  • client/src/components/ui/bets/BetStatusBall.tsx
  • client/src/features/auth/types.ts
  • client/src/features/bets/constants.ts
  • client/src/features/bets/schemas.ts
  • client/src/features/bets/types.ts
  • client/src/features/dashboard/components/big-cards/monthly-latest/LatestBet.tsx
  • client/src/features/dashboard/components/big-cards/monthly-latest/LatestCard.tsx
  • client/src/lib/auth/getMe.ts
  • client/src/lib/types/bets.ts
  • client/src/lib/utils/betHelpers.ts
  • rust-server/AGENTS.md
  • rust-server/documentation.md
  • rust-server/src/db/queries.rs
  • rust-server/src/features/auth/handlers/me.rs
  • rust-server/src/features/auth/service.rs
  • rust-server/src/features/auth/tokens/jwt.rs
  • rust-server/src/features/auth/types.rs
  • rust-server/src/middleware/auth.rs
  • rust-server/src/types.rs
💤 Files with no reviewable changes (1)
  • client/src/lib/types/bets.ts

Comment thread client/documentation.md
Comment thread client/src/features/bets/schemas.ts
Comment thread client/src/lib/auth/getMe.ts
Comment thread rust-server/src/features/auth/tokens/jwt.rs
@reijjo
reijjo merged commit e9f9a09 into main May 14, 2026
4 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request May 27, 2026
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