Skip to content

Family Review + Child Profile Page Tanstack DB Migration and related fixes#93

Merged
rk234 merged 10 commits into
mainfrom
tanstack-db-migration
May 24, 2026
Merged

Family Review + Child Profile Page Tanstack DB Migration and related fixes#93
rk234 merged 10 commits into
mainfrom
tanstack-db-migration

Conversation

@rk234
Copy link
Copy Markdown
Member

@rk234 rk234 commented May 21, 2026

Pull Request

Description

  • migrate complex staff review mutations to tanstack db
  • allow editing of tracking number by staff
  • fix price editing on review page
  • make approve and publish default
  • add claim queries

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Performance improvement
  • Other (please describe):

Related Issues (put task name here from notion)

Closes #73

Screenshots (If it is a front end feature screenshot is required)

Additional Notes

Summary by CodeRabbit

  • New Features

    • Added gift-claim support showing donor info and allowing claim tracking number updates.
    • Coordinated transactional editing for children, families, and gifts for safer multi-entity saves.
  • Refactor

    • Replaced many mutation-based flows with a collections/transaction-driven architecture.
    • Components now use explicit field-change handlers and controlled editing flows.
  • Bug Fixes

    • Uploaded child photos now use cache-busted URLs so updates appear immediately.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

📝 Walkthrough

Walkthrough

Replace per-entity React Query mutations with a TanStack DB collections factory and provider; add claim server endpoints and query keys; refactor profile and review components/routes to use transaction-backed drafts and live queries; remove legacy mutation hooks.

Changes

TanStack DB Collections Migration

Layer / File(s) Summary
Collections infra & router wiring
app/package.json, app/src/collections/context.tsx, app/src/collections/preload.ts, app/src/integrations/tanstack-query/root-provider.tsx, app/src/router.tsx
Add workspace deps and Collection provider, preload helpers, drop root Provider, and wrap routes with CollectionsProvider.
Collections factory: children, families, gifts
app/src/collections/factory.ts
New collections factory with allowlists, persist helpers, per-collection queryKey/queryFn, insert/update persistence, and transaction creators for child/family/gifts.
Queries for claims
app/src/queries/claim.ts, app/src/queries/index.ts
Add claimQueries.byChildId and register it in the global queries export.
Server: claims endpoints & schema relaxations
app/src/server/functions/child.ts, app/src/server/functions/family.ts, app/src/server/functions/profile.ts, app/src/server/services/childPhotoService.server.ts
Add getClaimsWithDonorByChildId, updateClaimTrackingNumber, GiftClaimDetails; remove .strict() from update schemas; append cache-busting param to uploaded photo URLs.
Utils: claim guards & date formatting
app/src/lib/utils.ts, app/src/server/functions/gifts.ts
Add isDonorClaim type-guard and formatISODate; reuse isDonorClaim in gifts module.
Child-profile component refactors
app/src/components/child-profile/ChildHeader.tsx, app/src/components/child-profile/ChildInfo.tsx, app/src/components/child-profile/ChildSidebar.tsx, app/src/components/child-profile/ConfirmUnpublishModal.tsx
Remove local edited-state props; accept onChildFieldChange/onFamilyFieldChange/onAddressFieldChange/onPublishedChange callbacks and use transaction-aware UI patterns (useTransition, errorMessage).
Gift UI: claim-driven and transactions
app/src/components/child-profile/GiftInfoCard.tsx, app/src/components/child-profile/GiftInfoSection.tsx, app/src/components/child-profile/SelectedGifts.tsx
GiftInfoCard now accepts optional claim?: GiftClaimDetails, stages edits in createGiftsTransaction, optionally updates claim tracking, and renders donor/tracking/date from claim; SelectedGifts is controlled via onGiftToggle.
Child profile route -> transactions
app/src/routes/_authenticated/staff/child/$childId.tsx
Preload collection queries, useLiveQuery for child/family/gifts, manage tx refs for draft edits, commit/rollback all txs, and coordinate add-gift via insert placeholder and useTransition.
Review UI & route refactor
app/src/components/review/ChildCard.tsx, app/src/components/review/GuardianInfoCard.tsx, app/src/components/review/ReviewActionPanel.tsx, app/src/routes/_authenticated/staff/review/$familyId.tsx
Switch review components to useCollections/useLiveQuery and transactions, ReviewActionPanel persists reviewStatus through collections and accepts authUser, and review route renders read-only children/guardian with live queries.
Remove deprecated mutation hooks
app/src/hooks/mutations/*
Removed legacy React Query mutation hooks (create/update gifts, children, families, review status, upload picture); persistence moved into collections layer.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • ArnavGupta23

Poem

🐰 I stitched the drafts into a tidy row,

Transactions hum where mutations used to grow.
Claims and gifts now speak in one clear song,
Photo URLs refreshed, and checks move along.
Hooray — the rabbit hops; the data gardens strong!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.92% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive Several changes extend beyond the core Tanstack DB migration: dependency additions (@react-email/render, @tanstack/query-db-collection), new claim query infrastructure, shipment tracking edits, price editing fixes, and schema validation relaxations (.strict() removal). Clarify whether claim tracking, dependency updates, and schema validation changes are intentional extensions or unrelated to the core migration objective.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main changes: migrating child profile and family review pages to use Tanstack DB for state management instead of local optimistic updates.
Linked Issues check ✅ Passed The PR fully implements the core objective from issue #73: refactoring complex optimistic update flows (child profile, family review, gift editing) to use Tanstack DB transactions and collections for automatic syncing.

✏️ 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 tanstack-db-migration

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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.

@rk234
Copy link
Copy Markdown
Member Author

rk234 commented May 22, 2026

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 22, 2026

✅ Actions performed

Full review triggered.

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

🧹 Nitpick comments (2)
app/src/lib/serverValidation.ts (1)

45-50: ⚡ Quick win

Remove redundant instanceof Error check.

Since error is now typed as Error, the instanceof check at line 48 will always be true and can be removed.

♻️ Proposed fix
 export function getServerValidationIssues(
   error: Error,
 ): Array<ValidationIssue> {
-  if (!(error instanceof Error)) {
-    return [];
-  }
-
   try {
     const parsed = JSON.parse(error.message);
🤖 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 `@app/src/lib/serverValidation.ts` around lines 45 - 50, In
getServerValidationIssues, remove the redundant runtime guard "if (!(error
instanceof Error)) { return []; }" because the parameter is already typed as
Error; delete that conditional and its early return so the function directly
processes the provided error (keeping existing logic that constructs/returns
Array<ValidationIssue>), and ensure no other code paths relied on that branch.
app/src/components/review/GuardianInfoCard.tsx (1)

232-236: 💤 Low value

"Guardian comments" shows stale data while editing.

The readonly display at line 235 reads family.privateNotes (the original prop) rather than formState.privateNotes. While editing, users see the old value here even though their changes are staged in formState. If this is intentional (showing "saved" vs "pending"), consider adding a visual indicator; otherwise, use formState.privateNotes.

🤖 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 `@app/src/components/review/GuardianInfoCard.tsx` around lines 232 - 236, The
readonly "Guardian comments" display is currently rendering the original prop
family.privateNotes instead of the editable state; update the rendering in
GuardianInfoCard (the <p> showing "Guardian comments:") to use
formState.privateNotes so the UI reflects staged edits, or if you intentionally
want to show saved vs pending values add a clear visual indicator (e.g., "Saved"
vs "Pending") and keep family.privateNotes for the saved value while showing
formState.privateNotes as the pending draft.
🤖 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 `@app/src/collections/factory.ts`:
- Around line 471-480: The createGiftsTransaction function currently only
processes update mutations (using isUpdateMutation) so insert mutations are
ignored; update the mutation loop in createGiftsTransaction to detect insert
mutations (e.g., isInsertMutation) and call the corresponding persistence
routine for inserts (e.g., persistGiftInsert or a newly implemented persist
function) in addition to the existing persistGiftUpdate call, ensuring both
insert and update branches are awaited, and keep the subsequent
invalidateGiftDerivedCaches call as-is so derived caches are invalidated after
all mutations are persisted.
- Around line 188-203: The photo upload is committed before the child record
update, causing partial state if updateChild fails; instead, when isDataUrl is
true call uploadChildPictureStaff first to obtain photoUrl but do not call
mutation.collection.utils.writeUpdate yet; include that photoUrl in the updates
object you build (i.e. stop excluding "photoUrl" from UPDATABLE_CHILD_FIELDS
when isDataUrl and add the returned photoUrl into updates), then await
updateChild({ data: { childId, updates } }); only after updateChild succeeds
call mutation.collection.utils.writeUpdate({ id: childId, photoUrl }) (or write
the full updates) so the photo change is persisted atomically from the client
perspective; reference functions: uploadChildPictureStaff, updateChild,
mutation.collection.utils.writeUpdate, UPDATABLE_CHILD_FIELDS, modified,
isDataUrl, photoUrl, childId.

In `@app/src/components/child-profile/ConfirmUnpublishModal.tsx`:
- Around line 37-39: The DialogDescription currently always says "This will
remove the profile from the storefront" in ConfirmUnpublishModal.tsx which is
incorrect for the publish action; update the component to render a conditional
description based on the action (e.g., if publishing show "This will add the
profile to the storefront." otherwise keep "This will remove the profile from
the storefront.") by branching where DialogDescription is rendered (use the
existing prop or state that indicates the action, e.g., isPublishing /
isUnpublishing or actionType) so the displayed message matches the operation.

In `@app/src/components/review/ChildCard.tsx`:
- Around line 480-489: The price handler writes directly to the DB
(collections.gifts.update) so price edits bypass the edit transaction and aren't
rolled back on Cancel; update the onPriceChange handler to validate with
parsePriceInput/hasValidListedPrice as before but call the existing editGift
transactional API (using the gift identifier, e.g., gift.id, and setting
listedPrice) instead of collections.gifts.update, awaiting the editGift call and
preserving the invalid-value toast.warning("Invalid price!"); this ensures price
changes go through the same transaction/undo flow as title/notes edits.
- Around line 238-240: The catch block in ChildCard.tsx currently only logs the
error ("Child review save failed") and provides no user feedback; update the
catch to surface the failure to the UI by either invoking the app's
toast/notification utility (e.g., showToast or similar) or by
rethrowing/propagating the error to the collection-level error handler used by
GuardianInfoCard and ReviewActionPanel; locate the save handler inside the
ChildCard component (the function containing the try/catch) and add a
user-facing notification with a clear message (e.g., "Failed to save child
review") and include the error details or pass the error to the common error
handler so the user sees the failure.

In `@app/src/components/review/GuardianInfoCard.tsx`:
- Around line 96-98: The catch block in the save handler of GuardianInfoCard
(where it currently calls console.error("Failed to save guardian info", error))
doesn't surface failures to users; mirror ChildCard's behavior by invoking
toast.error(...) with a clear message (e.g., "Failed to save guardian info") and
optionally include short error text, while still logging the full error to
console or processLogger for debugging; update the catch in the save function
(the try/catch that wraps the save logic in GuardianInfoCard) to call
toast.error in addition to the existing console.error.

In `@app/src/components/review/ReviewActionPanel.tsx`:
- Around line 63-93: runReviewUpdate currently updates only approved, held and
notes but ignores the audit fields lastReviewedAt and reviewedBy passed in
nextStatus, so those values are never persisted; update the
collections.families.update draft inside runReviewUpdate to also set
draft.reviewStatus.lastReviewedAt = nextStatus.lastReviewedAt and
draft.reviewStatus.reviewedBy = nextStatus.reviewedBy (respecting the
held/approved branches as needed) so the audit fields from nextStatus are saved
when the transaction commits.

---

Nitpick comments:
In `@app/src/components/review/GuardianInfoCard.tsx`:
- Around line 232-236: The readonly "Guardian comments" display is currently
rendering the original prop family.privateNotes instead of the editable state;
update the rendering in GuardianInfoCard (the <p> showing "Guardian comments:")
to use formState.privateNotes so the UI reflects staged edits, or if you
intentionally want to show saved vs pending values add a clear visual indicator
(e.g., "Saved" vs "Pending") and keep family.privateNotes for the saved value
while showing formState.privateNotes as the pending draft.

In `@app/src/lib/serverValidation.ts`:
- Around line 45-50: In getServerValidationIssues, remove the redundant runtime
guard "if (!(error instanceof Error)) { return []; }" because the parameter is
already typed as Error; delete that conditional and its early return so the
function directly processes the provided error (keeping existing logic that
constructs/returns Array<ValidationIssue>), and ensure no other code paths
relied on that branch.
🪄 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: 5ca4a765-3853-4c12-9c18-9f065034a147

📥 Commits

Reviewing files that changed from the base of the PR and between a1fa123 and 7952823.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (31)
  • app/package.json
  • app/src/collections/context.tsx
  • app/src/collections/factory.ts
  • app/src/collections/preload.ts
  • app/src/components/child-profile/ChildHeader.tsx
  • app/src/components/child-profile/ChildInfo.tsx
  • app/src/components/child-profile/ChildSidebar.tsx
  • app/src/components/child-profile/ConfirmUnpublishModal.tsx
  • app/src/components/child-profile/GiftInfoCard.tsx
  • app/src/components/child-profile/GiftInfoSection.tsx
  • app/src/components/child-profile/SelectedGifts.tsx
  • app/src/components/review/ChildCard.tsx
  • app/src/components/review/GuardianInfoCard.tsx
  • app/src/components/review/ReviewActionPanel.tsx
  • app/src/hooks/mutations/useCreateGift.ts
  • app/src/hooks/mutations/useUpdateChild.ts
  • app/src/hooks/mutations/useUpdateFamily.ts
  • app/src/hooks/mutations/useUpdateFamilyReviewStatus.ts
  • app/src/hooks/mutations/useUpdateGift.ts
  • app/src/hooks/mutations/useUploadChildPicture.ts
  • app/src/integrations/tanstack-query/root-provider.tsx
  • app/src/lib/serverValidation.ts
  • app/src/queries/claim.ts
  • app/src/queries/index.ts
  • app/src/router.tsx
  • app/src/routes/_authenticated/staff/child/$childId.tsx
  • app/src/routes/_authenticated/staff/review/$familyId.tsx
  • app/src/server/functions/child.ts
  • app/src/server/functions/family.ts
  • app/src/server/functions/profile.ts
  • app/src/server/services/childPhotoService.server.ts
💤 Files with no reviewable changes (6)
  • app/src/hooks/mutations/useUploadChildPicture.ts
  • app/src/hooks/mutations/useUpdateFamilyReviewStatus.ts
  • app/src/hooks/mutations/useUpdateGift.ts
  • app/src/hooks/mutations/useUpdateChild.ts
  • app/src/hooks/mutations/useUpdateFamily.ts
  • app/src/hooks/mutations/useCreateGift.ts

Comment thread app/src/collections/factory.ts
Comment thread app/src/collections/factory.ts
Comment thread app/src/components/child-profile/ConfirmUnpublishModal.tsx
Comment thread app/src/components/review/ChildCard.tsx
Comment thread app/src/components/review/ChildCard.tsx
Comment thread app/src/components/review/GuardianInfoCard.tsx
Comment thread app/src/components/review/ReviewActionPanel.tsx
@rk234
Copy link
Copy Markdown
Member Author

rk234 commented May 24, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 24, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.

🧹 Nitpick comments (1)
app/src/server/functions/child.ts (1)

1142-1159: 💤 Low value

Consider validating claim existence before update.

The function updates the claim document directly without verifying it exists. If an invalid claimId is passed, Firestore's update() will throw an error that may not have a clear message. A quick existence check improves error clarity.

Proposed enhancement
   .handler(async ({ data }) => {
     const { claimId, trackingNumber } = data;
     const db = getServerDB();
+    const claimDoc = await db.claims.doc(claimId).get();
+    if (!claimDoc.exists) {
+      throw new Error("Claim not found");
+    }
     await db.claims.doc(claimId).update({
       "purchaseConfirmation.trackingNumber": trackingNumber,
     });
   });
🤖 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 `@app/src/server/functions/child.ts` around lines 1142 - 1159, In
updateClaimTrackingNumber: before calling db.claims.doc(claimId).update(...),
fetch the document snapshot (e.g., db.claims.doc(claimId).get()) and check
snapshot.exists; if it does not exist, return/throw a clear error (bad
request/not found) indicating the claimId is invalid, otherwise proceed to call
.update(...) as before; ensure the error path uses the same middleware/response
conventions used elsewhere in the service.
🤖 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.

Nitpick comments:
In `@app/src/server/functions/child.ts`:
- Around line 1142-1159: In updateClaimTrackingNumber: before calling
db.claims.doc(claimId).update(...), fetch the document snapshot (e.g.,
db.claims.doc(claimId).get()) and check snapshot.exists; if it does not exist,
return/throw a clear error (bad request/not found) indicating the claimId is
invalid, otherwise proceed to call .update(...) as before; ensure the error path
uses the same middleware/response conventions used elsewhere in the service.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 47136726-cae2-45bc-bc62-ecf16d893382

📥 Commits

Reviewing files that changed from the base of the PR and between 7952823 and 8dafdc1.

📒 Files selected for processing (10)
  • app/src/collections/factory.ts
  • app/src/components/child-profile/ConfirmUnpublishModal.tsx
  • app/src/components/child-profile/GiftInfoCard.tsx
  • app/src/components/review/ChildCard.tsx
  • app/src/components/review/GuardianInfoCard.tsx
  • app/src/components/review/ReviewActionPanel.tsx
  • app/src/lib/utils.ts
  • app/src/routes/_authenticated/staff/child/$childId.tsx
  • app/src/server/functions/child.ts
  • app/src/server/functions/gifts.ts

@rk234 rk234 marked this pull request as ready for review May 24, 2026 01:55
@rk234 rk234 merged commit db4fcd1 into main May 24, 2026
4 checks passed
@rk234 rk234 deleted the tanstack-db-migration branch May 24, 2026 01:57
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.

Migrate complex optimsitic updates to Tanstack DB for automatic syncing

1 participant