Skip to content

perf: creator Dashboard 2.0 – Unified Library, Analytics & Draft Management#446

Open
bibhupradhanofficial wants to merge 1 commit into
IndieHub25:mainfrom
bibhupradhanofficial:main
Open

perf: creator Dashboard 2.0 – Unified Library, Analytics & Draft Management#446
bibhupradhanofficial wants to merge 1 commit into
IndieHub25:mainfrom
bibhupradhanofficial:main

Conversation

@bibhupradhanofficial
Copy link
Copy Markdown

@bibhupradhanofficial bibhupradhanofficial commented Mar 2, 2026

IMPORTANT: Read this entire template and follow it fully.
Pull requests that do not follow this template may be closed and marked as blocked until updated.


Description

Issue Reference (e.g. Fixes #123): ---

Summary of Changes (high-level):

  • What did you change?
  • Why was this change needed?
  • Which part of the system is impacted (AI, Web, Smart Contracts, Docs, Infra, etc.)?

Context / Motivation:

  • What problem or use case does this PR solve?
  • Any relevant discussion, issue, or design doc links?

Type of Change

Select the relevant categories:

  • AI / Prompt Logic (Groq API integration, prompt design, story analysis, or LLM flows)
  • Web3 / Smart Contracts (Solidity, Monad SDK, Minting logic, or blockchain infra)
  • Frontend / UI (Next.js, Tailwind, shadcn components, accessibility)
  • Backend / API (API routes, services, workers, database models, jobs)
  • DevOps / Tooling (CI/CD, GitHub Actions, linting/formatting, build tooling)
  • Documentation (README, Wiki, architecture docs, code comments)
  • Testing (Unit, integration, e2e tests or test infra)
  • Bug Fix (Functional or security repair in existing behavior)
  • Refactor / Cleanup (No behavior change, only internal improvements)

Technical Checklist

Tick everything that applies. Leave non‑applicable items unchecked.

AI / Application Logic

  • I tested story generation end‑to‑end with a valid GROQ_API_KEY.
  • I verified that prompts, model names, and parameters are up to date with current Groq APIs.
  • I confirmed that error states and empty responses are handled gracefully (no unhandled exceptions).

Web3 / Smart Contracts

  • I verified contract logic on the Monad Testnet (deploy + basic flows).
  • I ran the smart contract tests in smart_contracts and they passed.
  • I checked for potential reentrancy / overflow / access‑control issues in new or edited contracts.

Frontend / UX / Accessibility

  • My changes follow the Progressive Disclosure (accordion / step‑based) UX where applicable.
  • I verified the UI in light and dark mode.
  • I checked keyboard navigation and focus states for interactive elements I touched.
  • I ensured accessible labels (aria-*, alt text) and semantic HTML for new UI.

Backend / Database

  • I ran backend startup locally without runtime errors.
  • I validated new API routes with both success and failure cases.
  • I considered database performance (indexes, query filters) for any new queries.
  • I confirmed that new logic respects existing retry/health‑check behavior where relevant.

Security & Privacy

  • No API keys, private keys, secrets, or .env files are committed.
  • I avoided logging sensitive data (tokens, secrets, full payloads with PII).
  • I considered common web vulnerabilities (XSS, CSRF, SSRF, injection) in my changes.

Code Quality

  • I ran npm run lint (or equivalent) and resolved reported issues.
  • I ran available tests for the areas I changed (frontend, backend, or contracts).
  • I kept functions/components focused and avoided large “god” modules where possible.
  • I updated or added types where necessary instead of using any by default.

Testing Evidence

Describe how you tested these changes. Include commands and logs where possible.

Environment: (e.g. local, Cloudflare Pages preview, Monad testnet)

Commands:
- npm run lint
- npm run test
- npx hardhat test
- npm run dev

Results / Logs:
- Paste relevant excerpts here…

If this PR affects UI flows, also describe the manual test steps you followed.


Visual Proof (for UI / UX changes)

Required for all visual changes. Attach screenshots or a short screen recording (GIF / video) that clearly shows:

  • Before vs After (if applicable)
  • Different breakpoints (mobile + desktop)
  • Light and dark mode if supported

Contributor Status

Tick all that apply to you for this PR:

  • I am an open source indie contributor.
  • I am an ECWoC’26 contributor.
  • I am an OSCG’26 contributor.
  • I am a SWOC’26 contributor.
  • I am a DSWOC'26 contributor.

Review & Impact

Breaking Changes

  • This PR introduces a breaking change (API / contract / DB).
  • If yes, I have documented migration steps in the description above.

Dependencies

  • I added or upgraded dependencies.
  • I explained why these dependencies are needed and checked for license compatibility.

Backward Compatibility / Migrations

  • Existing users can continue using GroqTales without manual steps.
  • If a migration is required, steps are clearly described (DB migrations, contract redeploys, etc.).

Final Acknowledgements (Mandatory or will be marked invalid)

You must check all of the following before requesting review. These are required:

  • I confirm that the information and code in this PR are my original work or appropriately credited, and I have the right to contribute them under this repository’s license.
  • I understand that by submitting this PR, I take full responsibility and accountability for the changes I am proposing.
  • I have read and agree to follow the project’s Code of Conduct, Security Policy, and Contribution Guidelines for all discussions and follow‑up on this PR.

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced creator dashboard with dedicated tabs for draft management, NFT tracking, and analytics.
    • Analytics overview displaying per-story engagement metrics (views, likes, bookmarks, mints).
    • Cloud draft synchronization with version history and search capabilities.
  • Bug Fixes

    • Improved tooltip stability in revenue charts.
    • Fixed slider interaction in royalty configuration.

@github-actions github-actions Bot requested a review from Drago-03 March 2, 2026 18:20
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 2, 2026

📝 Walkthrough

Walkthrough

Adds four new creator API routes for analytics, draft management, and NFT operations; reimplements the dashboard as a multi-tab interface with cloud data integration; introduces a versioned Draft data model with AI metadata; and refactors supporting utility functions for blockchain and local draft management.

Changes

Cohort / File(s) Summary
Creator API Routes
app/api/creator/analytics/route.ts, app/api/creator/drafts/route.ts, app/api/creator/drafts/[draftKey]/route.ts, app/api/creator/nfts/route.ts
New GET endpoints validating wallet input, querying drafts and stories from DB, aggregating user interactions for analytics, deriving NFT sync status with optional blockchain verification, and returning paginated/structured JSON responses.
Dashboard & Analytics UI
app/dashboard/page.tsx, components/dashboard/analytics/page.tsx
Rewritten dashboard page with multi-tab interface (drafts, NFTs, analytics), cloud data loading via APIs, local/cloud draft merging, and per-story engagement metrics display; updated analytics card data binding.
Draft Data Model
models/Draft.ts
Comprehensive Mongoose schema defining versioned drafts with snapshot history, AI metadata (pipelineState, suggestedEdits), owner references, timestamps, and compound indexing for owner/update-time queries.
Blockchain & Royalty Utilities
lib/blockchain.ts, lib/royalty-service.ts
Centralized provider creation in getProvider() helper and adjusted transaction checking; added creatorWallet field to DEFAULT_EARNINGS earnings shape.
Draft Manager & Component Polish
lib/story-draft-manager.ts, components/royalty/revenue-chart.tsx, components/royalty/royalty-config-form.tsx
New listDraftRecords() export for multi-record retrieval; defensive null checks in tooltip and slider handlers.
Test Improvements
tests/ai-security.test.ts
Added defensive destructuring guard in security logging test for robustness.

Sequence Diagram

sequenceDiagram
    participant Client as Dashboard Client
    participant API as Analytics Route
    participant DB as MongoDB
    participant Service as Aggregation Logic

    Client->>API: GET /api/creator/analytics?wallet=0x...
    API->>API: Validate wallet address
    API->>DB: Query stories by wallet<br/>(title, status, updatedAt, nftTokenId)
    DB-->>API: Story records
    API->>DB: Fetch UserInteraction records<br/>(VIEW, LIKE, BOOKMARK)
    DB-->>API: Interaction data
    API->>Service: Aggregate interactions by story
    Service->>Service: Calculate per-story metrics<br/>(views, likes, bookmarks, mints)
    Service-->>API: Aggregated results
    API->>Service: Compute totals across all stories
    Service-->>API: Overall totals
    API-->>Client: JSON: { totals, rows[] }
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

size/XL, Enhancement, Feature, API, Needs Review

Suggested reviewers

  • Drago-03

Poem

🐰 A dashboard hops with tabs so bright,
Analytics aggregate with might,
Drafts versioned, NFTs on chain,
Creator tools flow like spring rain!
Whiskers twitch with joy—what delight!

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR's actual changes implement Creator Dashboard 2.0 with analytics, draft management, and NFT features, but the linked issue #123 requires bumping testing dependencies (Jest 29.7.0→30.2.0 and 40 other packages). The code changes do not include any package.json or lockfile updates for testing dependencies. Either update package.json and lockfile to bump Jest and 40 related testing packages as required by issue #123, or update the PR to link to the correct issue tracking Dashboard 2.0 implementation.
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The PR description is the unfilled template with no actual summary, changes, context, type selection, testing evidence, or acknowledgements completed. Complete all required sections: provide issue reference, detailed summary of changes, context/motivation, select change types, document testing steps with logs, add visual proof for UI changes, and check all final acknowledgements.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'perf: creator Dashboard 2.0 – Unified Library, Analytics & Draft Management' clearly summarizes the main changes: a performance-focused Creator Dashboard with unified library, analytics, and draft management features. The code changes across multiple files (analytics route, drafts routes, NFTs route, dashboard page, and supporting utilities) align with this title.
Out of Scope Changes check ✅ Passed The PR includes extensive in-scope changes for Dashboard 2.0 (new API routes, dashboard page rewrite, UI components, draft model) and also makes several supporting changes (blockchain helper, royalty service, draft manager utilities, and test updates). All changes appear directly related to the dashboard/analytics feature objectives.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

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

🧹 Nitpick comments (1)
app/dashboard/page.tsx (1)

301-303: Consider lazy-loading tab data instead of fetching everything at connect time.

Fetching analytics, NFTs, and cloud drafts together increases initial load and backend pressure. Deferring NFTs/cloud drafts until their tabs are opened would improve perceived performance.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/dashboard/page.tsx` around lines 301 - 303, Currently fetchAnalytics(),
fetchNfts(), and fetchCloudDrafts() are all called on connect which causes heavy
initial load; change this to lazy-load per-tab by invoking fetchNfts() and
fetchCloudDrafts() only when their respective tab becomes active (e.g., on tab
click or when activeTab state changes) while keeping fetchAnalytics() on connect
if needed; add guards in the tab handlers or in useEffect tied to activeTab to
ensure each fetch runs only once (use flags like hasFetchedNfts /
hasFetchedCloudDrafts) and update the UI loading state accordingly.
🤖 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/api/creator/analytics/route.ts`:
- Around line 18-21: The route currently calls Story.find(...) and loads all
matching stories into stories, causing unbounded memory/CPU for large wallets;
modify the handler that calls Story.find to enforce a hard cap (e.g., limit
parameter) and add pagination/cursor support by accepting limit and cursor/query
params, applying .limit(limit) and a cursor filter (e.g., updatedAt/_id >
cursor) to the query, and return the next-cursor along with the page of stories
so downstream aggregation uses only that page; update the code paths that
aggregate over the returned story IDs to operate on the paged slice rather than
the entire set (refer to the Story.find(...).select(...).sort(...) usage and any
aggregation code that consumes stories).

In `@app/api/creator/drafts/`[draftKey]/route.ts:
- Around line 29-31: The code uses Date.now() as a fallback when
draft.current?.updatedAt is missing (variable currentUpdatedAt) — this makes
reads non-deterministic; replace the Date.now() fallback with a stable
alternative such as null or the top-level draft.updatedAt value instead, and
apply the same change to the other occurrence (the similar fallback at the
second site handling previous/other updatedAt around lines where
draft.current?.updatedAt is read). Ensure consumers of currentUpdatedAt (and the
other updatedAt variable) can accept null or the top-level draft.updatedAt and
adjust any downstream comparisons accordingly.

In `@app/api/creator/drafts/route.ts`:
- Around line 81-86: The pagination object in the drafts route can yield pages =
0 when total is 0; update the pages computation in the pagination return (where
pagination: { page, limit, total, pages: ... } is built in
app/api/creator/drafts/route.ts) to normalize to at least 1, e.g. replace
Math.ceil(total / limit) with Math.max(1, Math.ceil(total / limit)) so
paginators never receive 0 pages.
- Around line 18-20: The GET handlers in app/api/creator/drafts/route.ts and
app/api/creator/drafts/[draftKey]/route.ts currently trust a client-supplied
wallet query and filter by ownerWallet; change them to authenticate the caller
(via your existing session/JWT middleware or auth util) inside the GET handler,
derive the caller's wallet from the authenticated session, and reject the
request if no authenticated wallet is present; then use that authenticated
wallet to query drafts (instead of the query param) and for single-draft access
verify the draft.ownerWallet === authenticatedWallet (or return 403) when
resolving the draft by draftKey; remove or ignore the client-supplied wallet
query and add clear 401/403 responses for unauthenticated/unauthorized callers.

In `@app/api/creator/nfts/route.ts`:
- Around line 36-60: The on-chain check loop inside the candidates.map (where
txHash, tokenId, syncStatus are computed and checkTxStatus is called) must be
made bounded and fail-fast: wrap each checkTxStatus(txHash) call with a timeout
(e.g., reject after a few seconds) and run the checks with a concurrency limiter
or in small batches (instead of firing unbounded Promise.all across all
stories), and replace the empty catch with logging/propagating the error so
operational failures are visible; adjust logic to treat timed-out/rejected
checks as 'pending' or 'missing' as appropriate and still allow tokenId to be
updated when a successful response returns.

In `@app/dashboard/page.tsx`:
- Around line 111-133: The memoized localDrafts (useMemo block computing
listDraftRecords) can become stale because external mutations via
clearDraftRecord() and upsertDraftRecord() don't trigger the memo to recompute;
update the component to depend on a reactive signal that changes when drafts
storage mutates (e.g., introduce a draftsVersion state or useDraftsSubscription
hook) and include that signal in the useMemo dependency array along with
draftPipelineFilter and draftQuery so listDraftRecords(...) is re-run after
clearDraftRecord/upsertDraftRecord; reference the localDrafts useMemo,
listDraftRecords, clearDraftRecord, and upsertDraftRecord when adding the
reactive trigger.

---

Nitpick comments:
In `@app/dashboard/page.tsx`:
- Around line 301-303: Currently fetchAnalytics(), fetchNfts(), and
fetchCloudDrafts() are all called on connect which causes heavy initial load;
change this to lazy-load per-tab by invoking fetchNfts() and fetchCloudDrafts()
only when their respective tab becomes active (e.g., on tab click or when
activeTab state changes) while keeping fetchAnalytics() on connect if needed;
add guards in the tab handlers or in useEffect tied to activeTab to ensure each
fetch runs only once (use flags like hasFetchedNfts / hasFetchedCloudDrafts) and
update the UI loading state accordingly.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e381612 and 27fb439.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (13)
  • app/api/creator/analytics/route.ts
  • app/api/creator/drafts/[draftKey]/route.ts
  • app/api/creator/drafts/route.ts
  • app/api/creator/nfts/route.ts
  • app/dashboard/page.tsx
  • components/dashboard/analytics/page.tsx
  • components/royalty/revenue-chart.tsx
  • components/royalty/royalty-config-form.tsx
  • lib/blockchain.ts
  • lib/royalty-service.ts
  • lib/story-draft-manager.ts
  • models/Draft.ts
  • tests/ai-security.test.ts

Comment on lines +18 to +21
const stories = await Story.find({ authorWallet: wallet })
.select({ _id: 1, title: 1, status: 1, updatedAt: 1, nftTokenId: 1, nftTxHash: 1 })
.sort({ updatedAt: -1 })
.lean();
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Mar 2, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Bound this route to avoid unbounded query and aggregation load.

The current flow fetches all stories and aggregates over all IDs in one request. For large wallets, this can degrade latency and memory usage significantly. Add an enforced limit (and ideally pagination/cursor support).

💡 Proposed hard-cap fix
 export async function GET(request: NextRequest) {
   try {
     const { searchParams } = new URL(request.url);
     const wallet = (searchParams.get('wallet') || '').trim().toLowerCase();
+    const limit = Math.min(
+      Math.max(Number(searchParams.get('limit') || 200), 1),
+      500
+    );

@@
     const stories = await Story.find({ authorWallet: wallet })
       .select({ _id: 1, title: 1, status: 1, updatedAt: 1, nftTokenId: 1, nftTxHash: 1 })
       .sort({ updatedAt: -1 })
+      .limit(limit)
       .lean();

Also applies to: 30-45

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/creator/analytics/route.ts` around lines 18 - 21, The route currently
calls Story.find(...) and loads all matching stories into stories, causing
unbounded memory/CPU for large wallets; modify the handler that calls Story.find
to enforce a hard cap (e.g., limit parameter) and add pagination/cursor support
by accepting limit and cursor/query params, applying .limit(limit) and a cursor
filter (e.g., updatedAt/_id > cursor) to the query, and return the next-cursor
along with the page of stories so downstream aggregation uses only that page;
update the code paths that aggregate over the returned story IDs to operate on
the paged slice rather than the entire set (refer to the
Story.find(...).select(...).sort(...) usage and any aggregation code that
consumes stories).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@bibhupradhanofficial Bound the route

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.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment thread app/api/creator/drafts/[draftKey]/route.ts
Comment on lines +18 to +20
if (!wallet || !VALIDATION_PATTERNS.walletAddress.test(wallet)) {
return NextResponse.json({ error: 'Invalid wallet address' }, { status: 400 });
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Mar 2, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Checking for middleware files ==="
fd 'middleware.ts' --type f

echo
echo "=== Listing creator API route files ==="
fd 'route.ts' 'app/api/creator' --type f

echo
echo "=== Searching for auth/session verification in creator APIs ==="
rg -n --type=ts -C3 'getServerSession|auth\(|verify|jwt|clerk|siwe|signature|session' 'app/api/creator' 2>/dev/null || echo "No auth patterns found"

echo
echo "=== Checking wallet-param based access in draft routes ==="
rg -n --type=ts -C3 'searchParams\.get.*wallet|ownerWallet|walletAddress' 'app/api/creator/drafts' 2>/dev/null || echo "Pattern not found, checking entire file"

echo
echo "=== Full content of drafts/route.ts to understand auth context ==="
wc -l app/api/creator/drafts/route.ts && head -50 app/api/creator/drafts/route.ts

Repository: IndieHub25/GroqTales

Length of output: 6903


Implement authentication and caller-ownership verification for draft access.

The GET endpoints at app/api/creator/drafts/route.ts (lines 18-20, 24-25) and app/api/creator/drafts/[draftKey]/route.ts (lines 12, 15-17, 24) accept a wallet address as a query parameter and retrieve drafts with matching ownerWallet, but provide no authentication or ownership verification. This allows any caller to access another creator's drafts by passing a valid wallet address.

Instead of filtering by client-supplied wallet, these endpoints must:

  1. Authenticate the caller (via session, JWT, or other mechanism)
  2. Verify the caller owns the wallet they're querying, or reject access
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/creator/drafts/route.ts` around lines 18 - 20, The GET handlers in
app/api/creator/drafts/route.ts and app/api/creator/drafts/[draftKey]/route.ts
currently trust a client-supplied wallet query and filter by ownerWallet; change
them to authenticate the caller (via your existing session/JWT middleware or
auth util) inside the GET handler, derive the caller's wallet from the
authenticated session, and reject the request if no authenticated wallet is
present; then use that authenticated wallet to query drafts (instead of the
query param) and for single-draft access verify the draft.ownerWallet ===
authenticatedWallet (or return 403) when resolving the draft by draftKey; remove
or ignore the client-supplied wallet query and add clear 401/403 responses for
unauthenticated/unauthorized callers.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@bibhupradhanofficial implement authentication with already set up supabase setup

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.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment thread app/api/creator/drafts/route.ts
Comment on lines +36 to +60
const payload = await Promise.all(candidates.map(async (story: any) => {
const txHash = story.nftTxHash || undefined;
let tokenId = story.nftTokenId || undefined;
const status = story.status || 'draft';
let syncStatus: 'missing' | 'pending' | 'confirmed' | 'failed' =
status === 'failed'
? 'failed'
: tokenId
? 'confirmed'
: txHash || status === 'publishing'
? 'pending'
: 'missing';

if (txHash && syncStatus === 'pending') {
try {
const chainStatus = await checkTxStatus(txHash);
if (chainStatus.status === 'confirmed') {
syncStatus = 'confirmed';
tokenId = tokenId || (chainStatus as any).tokenId || undefined;
} else if (chainStatus.status === 'reverted') {
syncStatus = 'failed';
}
} catch {
}
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Mar 2, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Guard on-chain checks with bounded execution and timeout.

This endpoint can fire one blockchain status request per story in parallel, with no timeout. Under larger wallets, this can overload RPC and tie up request handling. Also, silent catch hides operational failures.

🛡️ Safer pattern (timeout + controlled execution)
-    const payload = await Promise.all(candidates.map(async (story: any) => {
+    const withTimeout = <T,>(p: Promise<T>, ms: number) =>
+      Promise.race([
+        p,
+        new Promise<never>((_, reject) =>
+          setTimeout(() => reject(new Error('tx-status-timeout')), ms)
+        ),
+      ]);
+
+    const payload = [];
+    for (const story of candidates) {
@@
-        if (txHash && syncStatus === 'pending') {
+        if (txHash && syncStatus === 'pending') {
           try {
-            const chainStatus = await checkTxStatus(txHash);
+            const chainStatus = await withTimeout(checkTxStatus(txHash), 5000);
             if (chainStatus.status === 'confirmed') {
               syncStatus = 'confirmed';
               tokenId = tokenId || (chainStatus as any).tokenId || undefined;
             } else if (chainStatus.status === 'reverted') {
               syncStatus = 'failed';
             }
-          } catch {
+          } catch (error) {
+            console.warn(`[creator/nfts] tx status check failed for ${txHash}:`, error);
           }
         }
@@
-      }));
+      payload.push({
+        storyId: story._id.toString(),
+        title: story.title || 'Untitled',
+        status,
+        ipfsHash: story.ipfsHash || undefined,
+        nftTxHash: txHash,
+        nftTokenId: tokenId,
+        syncStatus,
+        explorer,
+        updatedAt: story.updatedAt ? new Date(story.updatedAt).getTime() : Date.now(),
+      });
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/creator/nfts/route.ts` around lines 36 - 60, The on-chain check loop
inside the candidates.map (where txHash, tokenId, syncStatus are computed and
checkTxStatus is called) must be made bounded and fail-fast: wrap each
checkTxStatus(txHash) call with a timeout (e.g., reject after a few seconds) and
run the checks with a concurrency limiter or in small batches (instead of firing
unbounded Promise.all across all stories), and replace the empty catch with
logging/propagating the error so operational failures are visible; adjust logic
to treat timed-out/rejected checks as 'pending' or 'missing' as appropriate and
still allow tokenId to be updated when a successful response returns.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment thread app/dashboard/page.tsx
Comment on lines +111 to 133
const localDrafts = useMemo(() => {
const normalizedQuery = draftQuery.trim().toLowerCase();
return listDraftRecords((record) => {
if (draftPipelineFilter !== "all" && record.aiMetadata?.pipelineState !== draftPipelineFilter) {
return false;
}
if (!normalizedQuery) {
return true;
}
const haystack = [
record.current?.title,
record.current?.genre,
record.storyType,
record.storyFormat,
record.draftKey,
]
.filter(Boolean)
.join(" ")
.toLowerCase();
return haystack.includes(normalizedQuery);
});
}, [draftPipelineFilter, draftQuery]);

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Mar 2, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Local draft mutations won’t reliably refresh the rendered list.

listDraftRecords() is memoized only by query/filter, but clearDraftRecord() and upsertDraftRecord() mutate external storage without updating component state. This can leave stale draft cards visible until an unrelated re-render.

✅ Minimal reactive fix
 export default function DashboardPage() {
@@
+  const [draftStoreVersion, setDraftStoreVersion] = useState(0);
@@
   const localDrafts = useMemo(() => {
@@
-  }, [draftPipelineFilter, draftQuery]);
+  }, [draftPipelineFilter, draftQuery, draftStoreVersion]);
@@
   const deleteDraft = (draft: StoryDraftRecord) => {
     clearDraftRecord(draft.draftKey);
+    setDraftStoreVersion((v) => v + 1);
     toast({
       title: "Draft deleted",
       description: draft.current?.title?.trim() ? draft.current.title : "Untitled draft",
     });
   };
@@
       const payload = (await response.json()) as StoryDraftRecord;
       const imported = upsertDraftRecord(payload);
+      setDraftStoreVersion((v) => v + 1);
       openDraft(imported);

Also applies to: 330-336, 352-353

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/dashboard/page.tsx` around lines 111 - 133, The memoized localDrafts
(useMemo block computing listDraftRecords) can become stale because external
mutations via clearDraftRecord() and upsertDraftRecord() don't trigger the memo
to recompute; update the component to depend on a reactive signal that changes
when drafts storage mutates (e.g., introduce a draftsVersion state or
useDraftsSubscription hook) and include that signal in the useMemo dependency
array along with draftPipelineFilter and draftQuery so listDraftRecords(...) is
re-run after clearDraftRecord/upsertDraftRecord; reference the localDrafts
useMemo, listDraftRecords, clearDraftRecord, and upsertDraftRecord when adding
the reactive trigger.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@bibhupradhanofficial update the component state so the list gets refreshed

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.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Copy Markdown
Member

@Drago-03 Drago-03 left a comment

Choose a reason for hiding this comment

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

Update with the requested changes and resolve conflicts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants