Skip to content

perf: implemented Unified Library, Analytics & Draft Management#369

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

perf: implemented Unified Library, Analytics & Draft Management#369
bibhupradhanofficial wants to merge 1 commit into
IndieHub25:mainfrom
bibhupradhanofficial:main

Conversation

@bibhupradhanofficial
Copy link
Copy Markdown

@bibhupradhanofficial bibhupradhanofficial commented Feb 14, 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, Vercel 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 OSGC’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

    • Redesigned Creator Dashboard with tabbed interface for Drafts, NFTs, and Analytics
    • Analytics view displaying story performance metrics (views, likes, bookmarks, mints)
    • Enhanced draft management with cloud sync, import, and delete functionality
    • NFT publishing status tracking with blockchain explorer integration
    • Wallet-based authentication for dashboard access
  • Bug Fixes

    • Improved error handling and defensive checks in data fetching and UI components

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 14, 2026

📝 Walkthrough

Walkthrough

This pull request introduces a comprehensive creator dashboard system with four new API routes for analytics, draft management, and NFT tracking, completely redesigns the dashboard UI with tabbed navigation and data-driven state management, and adds supporting infrastructure including a new Mongoose Draft model, utility function updates, and defensive code patterns across components.

Changes

Cohort / File(s) Summary
Creator API Routes
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
Four new Next.js route handlers for creator data: analytics endpoint aggregates user interactions (views, likes, bookmarks) across stories; drafts endpoints retrieve individual drafts and list paginated drafts with search/filter; NFTs endpoint provides stories with blockchain synchronization status and explorer URLs.
Dashboard Redesign
app/dashboard/page.tsx
Major rewrite replacing minimal layout with feature-rich tabbed interface (Drafts, NFTs, Analytics). Adds extensive state management for cloud/local drafts, NFT data, and analytics metrics; implements data fetching hooks with error handling; includes draft actions (open, import, delete); adds summary metrics grid with skeleton loading.
Draft Data Model
models/Draft.ts
New Mongoose model with TypeScript interfaces: IDraftSnapshot (content, metadata), IDraftVersion (with reason field), IDraftAiMetadata (pipeline state), and IDraft (document with versions and AI metadata). Includes timestamps, indexing on ownerWallet/updatedAt.
Blockchain & Utility Updates
lib/blockchain.ts, lib/royalty-service.ts, lib/story-draft-manager.ts
Refactored provider creation into centralized getProvider() function in blockchain.ts; added creatorWallet field to DEFAULT_EARNINGS in royalty-service.ts; introduced listDraftRecords() export for querying local draft store with optional filtering.
Component & Test Updates
components/dashboard/analytics/page.tsx, components/royalty/revenue-chart.tsx, components/royalty/royalty-config-form.tsx, tests/ai-security.test.ts
Updated analytics metric source from totalTokensUsed to totalWordCount; added defensive payload access pattern in revenue-chart tooltip; refactored slider handler to accept array values; improved test log access with safe guard pattern.

Sequence Diagram(s)

sequenceDiagram
    participant User as User<br/>(Browser)
    participant Dashboard as Dashboard<br/>Page
    participant AnalyticsAPI as Analytics<br/>API
    participant DraftsAPI as Drafts<br/>API
    participant NFTAPI as NFTs<br/>API
    participant Database as MongoDB
    participant Blockchain as Blockchain<br/>(RPC)

    User->>Dashboard: Load dashboard with wallet
    
    par Fetch Analytics
        Dashboard->>AnalyticsAPI: GET /api/creator/analytics?wallet=0x...
        AnalyticsAPI->>Database: Find stories by wallet
        Database-->>AnalyticsAPI: Story records
        AnalyticsAPI->>Database: Aggregate UserInteractions (views/likes/bookmarks)
        Database-->>AnalyticsAPI: Aggregated metrics
        AnalyticsAPI-->>Dashboard: Totals + per-story metrics
    and Fetch Drafts
        Dashboard->>DraftsAPI: GET /api/creator/drafts?wallet=0x...
        DraftsAPI->>Database: Query drafts with pagination
        Database-->>DraftsAPI: Draft list with versions
        DraftsAPI-->>Dashboard: Paginated drafts
    and Fetch NFTs
        Dashboard->>NFTAPI: GET /api/creator/nfts?wallet=0x...
        NFTAPI->>Database: Find stories by author
        NFTAPI->>Blockchain: Check transaction status (if pending)
        Blockchain-->>NFTAPI: Tx receipt + status
        NFTAPI-->>Dashboard: Stories with NFT sync status
    end
    
    Dashboard->>Dashboard: Merge cloud + local drafts
    Dashboard->>Dashboard: Render tabbed interface
    Dashboard-->>User: Display Analytics/Drafts/NFTs tabs
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

size/XL, Enhancement, Feature, Hard, OSGC'26

Suggested reviewers

  • Drago-03

Poem

🐰 A dashboard springs to life, with tabs so bright,
Analytics dance, drafts take flight!
From blockchain calls to MongoDB's call,
The creator's toolkit now stands tall.
Warrens of data, metrics in sight,
Hoppy updates bringing delight! 🎉

🚥 Pre-merge checks | ✅ 1 | ❌ 5
❌ Failed checks (5 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description follows the required template structure and includes completed technical checklists for frontend/UX/accessibility changes, code quality lint checks, and contributor acknowledgements; however, the Summary of Changes and Context/Motivation sections remain as template placeholders without actual implementation details. Fill in the Summary of Changes section with what was actually implemented (new API routes for analytics/drafts/NFTs, dashboard redesign) and add Context/Motivation explaining why these features were built, along with any relevant issue/design links.
Linked Issues check ⚠️ Warning The linked issue #123 requires updating Jest dependencies from v29.x to v30.x and verifying test suite compatibility, but the raw_summary shows no changes to jest.config.ts, test files beyond two defensive pattern updates, or dependency updates in package.json. Either update jest and 40+ related dependencies as specified in #123, run the full test suite to verify v30 compatibility, and update the lockfile; or clarify if this PR does not address issue #123.
Out of Scope Changes check ⚠️ Warning The PR introduces new API routes (analytics, drafts, NFTs), a redesigned dashboard, and utility functions/models that are not related to Jest dependency updates specified in linked issue #123, indicating scope misalignment. Clarify the relationship between this PR and issue #123. If the Jest updates are missing, create a separate PR for dependency updates. If this PR is independent, remove the linked issue reference or update it to reflect the actual feature work.
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.
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (10 files):

⚔️ app/dashboard/page.tsx (content)
⚔️ components/community-feed.tsx (content)
⚔️ components/dashboard/analytics/page.tsx (content)
⚔️ components/royalty/revenue-chart.tsx (content)
⚔️ components/royalty/royalty-config-form.tsx (content)
⚔️ lib/blockchain.ts (content)
⚔️ lib/royalty-service.ts (content)
⚔️ lib/story-draft-manager.ts (content)
⚔️ server/package-lock.json (content)
⚔️ tests/ai-security.test.ts (content)

These conflicts must be resolved before merging into main.
Resolve conflicts locally and push changes to this branch.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title describes implementing unified library, analytics & draft management features, which aligns with the major changes across analytics routes, draft API endpoints, dashboard redesign, and data management utilities.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
⚔️ Resolve merge conflicts (beta)
  • Auto-commit resolved conflicts to branch main
  • Post resolved changes as copyable diffs 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

🤖 Fix all issues with AI agents
In `@app/api/creator/analytics/route.ts`:
- Around line 7-14: The GET handler currently only validates wallet format
(using VALIDATION_PATTERNS.walletAddress) but does not authenticate/authorize
the caller; update the handler to require and validate an authentication
token/session/signature (e.g., check a session cookie, JWT in Authorization
header, or verify a signed message) and ensure the authenticated identity
matches the requested wallet before returning analytics (otherwise return
401/403 via NextResponse); apply the same pattern to the handlers in
app/api/creator/drafts/route.ts, app/api/creator/drafts/[draftKey]/route.ts, and
app/api/creator/nfts/route.ts so each route (their GET functions) verifies auth
and owner equality with the wallet param.
- Around line 18-21: The query using
Story.find(...).select(...).sort(...).lean() fetches all stories for a wallet
into memory (variable stories) which can OOM for prolific creators; modify the
handler that calls Story.find to accept pagination parameters (e.g., page/limit
or cursor) and apply .limit(n).skip(page*limit) or a cursor-based filter to
Story.find, and update downstream aggregation logic that iterates over stories
to operate on a single page (or to stream/process incrementally) so analytics
run only on the bounded result set; ensure default limit is set (e.g., 50–100)
and expose pagination params in the API surface used by the dashboard.

In `@app/api/creator/nfts/route.ts`:
- Around line 36-60: The current implementation maps over candidates and calls
checkTxStatus for every pending tx concurrently (inside payload creation), which
can overwhelm RPC providers and also silently swallows errors; modify the flow
so pending tx checks are performed with bounded concurrency (e.g., process
candidates in chunks of ~5–10 or use a concurrency limiter when invoking
checkTxStatus) and avoid awaiting all RPC calls at once for payload generation,
cache or persist confirmed tokenId/status to skip re-checks, and replace the
empty catch with a processLogger.warn (or similar) that logs the txHash and the
caught error so RPC/connectivity issues are visible; key symbols to update:
candidates mapping that builds payload, the checkTxStatus calls, txHash,
tokenId, and syncStatus handling.

In `@app/dashboard/page.tsx`:
- Around line 308-328: openDraft currently writes draft metadata to localStorage
but does not include the revision counter, so after importCloudDraft calls
upsertDraftRecord and then openDraft the localDrafts memo can remain stale;
update openDraft (and the call site in importCloudDraft where upsertDraftRecord
is invoked) to include and increment the same revision counter used elsewhere
(the revision bump used in deleteDraft) when writing "storyCreationData" to
localStorage so localDrafts sees the change and the newly imported draft appears
without a full remount.
- Around line 330-336: The UI doesn't refresh because localDrafts (useMemo) only
depends on [draftPipelineFilter, draftQuery]; add a revision/trigger state
(e.g., draftRevision via useState) and include it in the dependency arrays for
localDrafts and draftCards so they recompute when changed, then increment that
revision inside deleteDraft immediately after calling
clearDraftRecord(draft.draftKey) to force a re-render of draft cards.

In `@models/Draft.ts`:
- Line 82: The schema options currently set { timestamps: true, strict: false }
on the Draft schema allow arbitrary fields to be persisted; update the schema
options used when creating DraftSchema (the Schema/Model definition in
models/Draft.ts) to remove strict: false or change it to a stricter policy
(e.g., omit strict to use default strict: true or set strict: 'throw') so
unknown fields are rejected; adjust any dependent code that relied on dynamic
fields if necessary.
🧹 Nitpick comments (8)
app/api/creator/nfts/route.ts (1)

3-3: Use path alias for consistency.

All other imports in this file use the @/ path alias, but Story uses a fragile relative path.

Proposed fix
-import Story from '../../../../models/Story';
+import Story from '@/models/Story';
models/Draft.ts (1)

69-69: Redundant index: true alongside unique: true.

unique: true already creates a unique index on draftKey, so the explicit index: true is unnecessary.

Proposed fix
-    draftKey: { type: String, required: true, unique: true, trim: true, index: true },
+    draftKey: { type: String, required: true, unique: true, trim: true },
app/api/creator/drafts/route.ts (2)

30-38: Regex-based text search on multiple fields without indexes can be slow.

The $or with $regex on five fields (Lines 32–37) will perform collection scans unless text indexes or compound indexes exist for draftKey, current.title, current.genre, storyType, and storyFormat. For small datasets this is acceptable, but consider adding a MongoDB text index if this endpoint will be hit frequently or the collection grows.


88-90: Consider logging the caught error before returning a generic 500.

The error variable is caught but never logged. In a serverless/Next.js environment, silent 500s make debugging difficult. Even a console.error would help surface issues in production logs.

🔧 Proposed fix
   } catch (error) {
+    console.error('[GET /api/creator/drafts]', error);
     return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
   }
app/api/creator/drafts/[draftKey]/route.ts (1)

29-31: Inconsistent fallback: Date.now() here vs. null for top-level timestamps.

currentUpdatedAt falls back to Date.now() (Line 31) when the field is missing, but createdAt and updatedAt at Lines 66–67 fall back to null. The same inconsistency appears in the version mapping (Line 55). This can mislead clients into thinking a draft was just updated when the field simply doesn't exist.

Consider using null consistently:

♻️ Proposed fix
     const currentUpdatedAt = draft.current?.updatedAt
       ? new Date(draft.current.updatedAt).getTime()
-      : Date.now();
+      : null;
-        updatedAt: version.updatedAt ? new Date(version.updatedAt).getTime() : Date.now(),
+        updatedAt: version.updatedAt ? new Date(version.updatedAt).getTime() : null,
app/dashboard/page.tsx (3)

111-132: Double-filtering local drafts: query filter applied redundantly.

localDrafts (Line 111) already filters by both draftPipelineFilter and draftQuery. Then draftCards (Line 193) applies matchesQuery to localDrafts again. The pipeline filter is not re-checked in draftCards for local drafts because localDrafts already excluded non-matching records. However, the query check is applied twice — once in localDrafts and again in draftCards at Line 194.

This is not a bug but is wasted computation. Consider either removing the filter from localDrafts (let draftCards handle all filtering) or removing the redundant matchesQuery check in the local-drafts loop of draftCards.

Also applies to: 134-221


466-474: Error messages use text-muted-foreground — may be too subtle for errors.

Errors from analytics, NFTs, and cloud drafts are displayed in muted text, making them easy to miss. Consider using a more visible treatment (e.g., text-destructive or a bordered alert component) so users understand something is wrong.

♻️ Proposed change
-            {analyticsError && (
-              <div className="text-sm text-muted-foreground">{analyticsError}</div>
-            )}
-            {nftsError && <div className="text-sm text-muted-foreground">{nftsError}</div>}
-            {cloudDraftsError && <div className="text-sm text-muted-foreground">{cloudDraftsError}</div>}
+            {analyticsError && (
+              <div className="text-sm text-destructive">{analyticsError}</div>
+            )}
+            {nftsError && <div className="text-sm text-destructive">{nftsError}</div>}
+            {cloudDraftsError && <div className="text-sm text-destructive">{cloudDraftsError}</div>}

476-476: Avoid as any cast — use the union type directly.

♻️ Proposed fix
-        <Tabs value={tab} onValueChange={(value) => setTab(value as any)}>
+        <Tabs value={tab} onValueChange={(value) => setTab(value as "drafts" | "nfts" | "analytics")}>

Comment on lines +7 to +14
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const wallet = (searchParams.get('wallet') || '').trim().toLowerCase();

if (!wallet || !VALIDATION_PATTERNS.walletAddress.test(wallet)) {
return NextResponse.json({ error: 'Invalid wallet address' }, { status: 400 });
}
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.

⚠️ Potential issue | 🟠 Major

No authentication — any caller can query analytics for any wallet.

All four /api/creator/* routes validate the wallet format but never verify that the caller actually owns the wallet (no session/JWT/signature check). This means any user can fetch analytics, drafts, and NFTs for any wallet address. If this data is considered private, an authentication layer is needed.

This concern applies equally to app/api/creator/drafts/route.ts, app/api/creator/drafts/[draftKey]/route.ts, and app/api/creator/nfts/route.ts.

🤖 Prompt for AI Agents
In `@app/api/creator/analytics/route.ts` around lines 7 - 14, The GET handler
currently only validates wallet format (using VALIDATION_PATTERNS.walletAddress)
but does not authenticate/authorize the caller; update the handler to require
and validate an authentication token/session/signature (e.g., check a session
cookie, JWT in Authorization header, or verify a signed message) and ensure the
authenticated identity matches the requested wallet before returning analytics
(otherwise return 401/403 via NextResponse); apply the same pattern to the
handlers in app/api/creator/drafts/route.ts,
app/api/creator/drafts/[draftKey]/route.ts, and app/api/creator/nfts/route.ts so
each route (their GET functions) verifies auth and owner equality with the
wallet param.

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

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Unbounded query: all stories for a wallet are fetched without limit.

If a prolific creator has thousands of stories, this query returns them all into memory, and the subsequent aggregation scans all their interactions. Consider adding a reasonable upper bound or pagination if the analytics response is meant to be consumed incrementally by the dashboard.

🤖 Prompt for AI Agents
In `@app/api/creator/analytics/route.ts` around lines 18 - 21, The query using
Story.find(...).select(...).sort(...).lean() fetches all stories for a wallet
into memory (variable stories) which can OOM for prolific creators; modify the
handler that calls Story.find to accept pagination parameters (e.g., page/limit
or cursor) and apply .limit(n).skip(page*limit) or a cursor-based filter to
Story.find, and update downstream aggregation logic that iterates over stories
to operate on a single page (or to stream/process incrementally) so analytics
run only on the bounded result set; ensure default limit is set (e.g., 50–100)
and expose pagination params in the API surface used by the dashboard.

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

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Unbounded Promise.all for on-chain status checks can overwhelm the RPC provider.

Every pending NFT triggers a concurrent checkTxStatus RPC call. For a creator with many pending NFTs, this could hit rate limits or cause timeouts. Consider batching or limiting concurrency (e.g., processing in chunks of 5–10), or caching confirmed statuses so they aren't re-checked.

Also, the empty catch block on lines 58–59 silently swallows RPC errors, making it hard to diagnose blockchain connectivity issues. Consider logging at warn level.

Sketch: limit concurrency and add logging
+    // Process in batches to avoid RPC rate limiting
+    const BATCH_SIZE = 5;
+    for (let i = 0; i < candidates.length; i += BATCH_SIZE) {
+      const batch = candidates.slice(i, i + BATCH_SIZE);
+      await Promise.all(batch.map(async (story: any) => { /* ... */ }));
+    }

         } catch {
+          console.warn(`[NFTs] Failed to check tx status for ${txHash}`);
         }
🤖 Prompt for AI Agents
In `@app/api/creator/nfts/route.ts` around lines 36 - 60, The current
implementation maps over candidates and calls checkTxStatus for every pending tx
concurrently (inside payload creation), which can overwhelm RPC providers and
also silently swallows errors; modify the flow so pending tx checks are
performed with bounded concurrency (e.g., process candidates in chunks of ~5–10
or use a concurrency limiter when invoking checkTxStatus) and avoid awaiting all
RPC calls at once for payload generation, cache or persist confirmed
tokenId/status to skip re-checks, and replace the empty catch with a
processLogger.warn (or similar) that logs the txHash and the caught error so
RPC/connectivity issues are visible; key symbols to update: candidates mapping
that builds payload, the checkTxStatus calls, txHash, tokenId, and syncStatus
handling.

Comment thread app/dashboard/page.tsx
Comment on lines +308 to +328
const openDraft = (draft: StoryDraftRecord) => {
try {
setActiveDraftKey(draft.draftKey);
localStorage.setItem(
"storyCreationData",
JSON.stringify({
type: draft.storyType || "text",
format: draft.storyFormat || "free",
draftKey: draft.draftKey,
timestamp: Date.now(),
})
);
router.push("/create");
} catch {
toast({
title: "Unable to open draft",
description: "Please try again.",
variant: "destructive",
});
}
}, []);
};
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.

⚠️ Potential issue | 🟡 Minor

openDraft also needs the revision counter for consistency.

After importCloudDraft calls upsertDraftRecord (Line 352) and then openDraft, the local draft store is updated. If the user navigates back to the dashboard without a full remount, the same stale-memo issue applies — the newly imported draft won't appear in localDrafts. The revision counter fix from the deleteDraft comment should also bump after upsertDraftRecord in importCloudDraft.

🤖 Prompt for AI Agents
In `@app/dashboard/page.tsx` around lines 308 - 328, openDraft currently writes
draft metadata to localStorage but does not include the revision counter, so
after importCloudDraft calls upsertDraftRecord and then openDraft the
localDrafts memo can remain stale; update openDraft (and the call site in
importCloudDraft where upsertDraftRecord is invoked) to include and increment
the same revision counter used elsewhere (the revision bump used in deleteDraft)
when writing "storyCreationData" to localStorage so localDrafts sees the change
and the newly imported draft appears without a full remount.

Comment thread app/dashboard/page.tsx
Comment on lines +330 to 336
const deleteDraft = (draft: StoryDraftRecord) => {
clearDraftRecord(draft.draftKey);
toast({
title: "Draft deleted",
description: draft.current?.title?.trim() ? draft.current.title : "Untitled draft",
});
};
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.

⚠️ Potential issue | 🔴 Critical

Bug: UI does not update after deleting a local draft.

deleteDraft calls clearDraftRecord to remove the draft from localStorage, but localDrafts (useMemo at Line 111) only depends on [draftPipelineFilter, draftQuery]. Since neither dependency changes, the memo doesn't recompute, and the deleted draft card remains visible until the user changes a filter or navigates away.

A simple fix is to introduce a counter/trigger state that increments on delete and is included in the dependency arrays of both localDrafts and draftCards.

🐛 Proposed fix

Add a revision counter:

+ const [localDraftRevision, setLocalDraftRevision] = useState(0);

Update the dependency arrays:

  const localDrafts = useMemo(() => {
    // ... existing code
- }, [draftPipelineFilter, draftQuery]);
+ }, [draftPipelineFilter, draftQuery, localDraftRevision]);
- }, [cloudDrafts, draftPipelineFilter, draftQuery, localDrafts]);
+ }, [cloudDrafts, draftPipelineFilter, draftQuery, localDrafts, localDraftRevision]);

Bump the counter on delete:

  const deleteDraft = (draft: StoryDraftRecord) => {
    clearDraftRecord(draft.draftKey);
+   setLocalDraftRevision((r) => r + 1);
    toast({
      title: "Draft deleted",
      description: draft.current?.title?.trim() ? draft.current.title : "Untitled draft",
    });
  };
🤖 Prompt for AI Agents
In `@app/dashboard/page.tsx` around lines 330 - 336, The UI doesn't refresh
because localDrafts (useMemo) only depends on [draftPipelineFilter, draftQuery];
add a revision/trigger state (e.g., draftRevision via useState) and include it
in the dependency arrays for localDrafts and draftCards so they recompute when
changed, then increment that revision inside deleteDraft immediately after
calling clearDraftRecord(draft.draftKey) to force a re-render of draft cards.

Comment thread models/Draft.ts
lastEditedByAIAt: { type: Date, default: null },
},
},
{ timestamps: true, strict: false }
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.

⚠️ Potential issue | 🟠 Major

strict: false disables schema validation for unknown fields.

This allows arbitrary, unvalidated fields to be persisted to MongoDB. Unless there's a specific need for dynamic/ad-hoc fields, this is a data integrity risk — typos in field names, injected fields from unsanitized input, etc. will silently be stored.

Consider removing it (Mongoose defaults to strict: true) or switching to strict: 'throw' if you want to catch unexpected fields.

Proposed fix
-  { timestamps: true, strict: false }
+  { timestamps: true }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{ timestamps: true, strict: false }
{ timestamps: true }
🤖 Prompt for AI Agents
In `@models/Draft.ts` at line 82, The schema options currently set { timestamps:
true, strict: false } on the Draft schema allow arbitrary fields to be
persisted; update the schema options used when creating DraftSchema (the
Schema/Model definition in models/Draft.ts) to remove strict: false or change it
to a stricter policy (e.g., omit strict to use default strict: true or set
strict: 'throw') so unknown fields are rejected; adjust any dependent code that
relied on dynamic fields if necessary.

@Drago-03
Copy link
Copy Markdown
Member

Right now the PR description is basically the raw template, there’s no real summary of what you shipped or why. For something this big (new creator APIs, dashboard redesign, Draft model, NFT sync, etc.) that’s a blocker.

Create a new PR focused on “Creator Dashboard 2.0 – Unified Library, Analytics & Draft Management” with:

  • a filled “Summary of Changes” that clearly lists the main features you added
  • a short “Context / Motivation” explaining which issues it addresses and what problem it solves

Until that’s done, I can’t treat this as merge‑ready, even if the implementation looks promising.

@Drago-03 Drago-03 closed this Feb 17, 2026
@Drago-03 Drago-03 added invalid This doesn't seem right Blocked Work cannot proceed due to a blocker labels Feb 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Blocked Work cannot proceed due to a blocker invalid This doesn't seem right size/XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants