Skip to content

Frontend[UI]: Layout with global components#57

Merged
JoelVR17 merged 1 commit intoTrustless-Work:developfrom
emmi-lili:components/layout
Mar 11, 2026
Merged

Frontend[UI]: Layout with global components#57
JoelVR17 merged 1 commit intoTrustless-Work:developfrom
emmi-lili:components/layout

Conversation

@emmi-lili
Copy link
Contributor

@emmi-lili emmi-lili commented Mar 11, 2026

Creating PR for layout with global components.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added ROI section with a dedicated dashboard for managing campaigns.
    • Implemented campaign search and filtering functionality.
    • Added ability to claim ROI from active campaigns with status tracking.
    • Updated navigation to include new ROI menu item.
  • Documentation

    • Added planning document outlining future wallet integration strategy.

@coderabbitai
Copy link

coderabbitai bot commented Mar 11, 2026

📝 Walkthrough

Walkthrough

This pull request introduces a new ROI (Return on Investment) feature for the investor tokenization application. It adds a complete ROI module with layout, page, multiple UI components, type definitions, and mock data. Additionally, the root layout is updated for light mode with suppressed hydration warnings, the navigation bar includes a new ROI link, and a planning document outlines a future wallet kit migration.

Changes

Cohort / File(s) Summary
Layout & Configuration
apps/investor-tokenization/src/app/layout.tsx, apps/investor-tokenization/src/app/roi/layout.tsx
Updated root layout to light mode with suppressHydrationWarning; added new ROI layout wrapper that provides RoiDashboardShell context.
ROI Page
apps/investor-tokenization/src/app/roi/page.tsx
New client-side ROI page component with state management for search text and filter status; computes filtered campaign list and handles claim ROI callbacks.
ROI Core Infrastructure
apps/investor-tokenization/src/features/roi/types/campaign.types.ts, apps/investor-tokenization/src/features/roi/data/mock-campaigns.ts, apps/investor-tokenization/src/features/roi/roi-dashboard-shell.tsx
New type definitions (Campaign, CampaignStatus), mock campaign data array, and ROI dashboard shell component wrapping navigation and sidebar layout.
ROI UI Components – Header & Search
apps/investor-tokenization/src/features/roi/components/roi-header.tsx, apps/investor-tokenization/src/features/roi/components/campaign-search.tsx
Header component with search control and notifications; search input component with icon and accessible aria-label.
ROI UI Components – Filtering & Toolbar
apps/investor-tokenization/src/features/roi/components/campaign-filter.tsx, apps/investor-tokenization/src/features/roi/components/campaign-toolbar.tsx
Dropdown filter with predefined status options (all, ready, pending, closed); toolbar wrapper that manages filter state propagation.
ROI UI Components – Campaign Display
apps/investor-tokenization/src/features/roi/components/campaign-card.tsx, apps/investor-tokenization/src/features/roi/components/campaign-list.tsx, apps/investor-tokenization/src/features/roi/components/campaign-status-badge.tsx
Card component rendering campaign details with status badge and claim button; list component mapping campaigns to cards; status badge with icon and styling.
ROI UI Components – Actions & Profile
apps/investor-tokenization/src/features/roi/components/claim-roi-button.tsx, apps/investor-tokenization/src/features/roi/components/investor-profile-card.tsx
Button component for claiming ROI with callback; profile card displaying investor avatar, name, and label.
Navigation Update
apps/investor-tokenization/src/components/shared/Navbar.tsx
Added ROI navigation link with TrendingUp icon to floating dock menu.
Documentation
docs/PLAN_WALLET_KIT.md
Planning document detailing strategy and roadmap for replacing custom wallet-kit with official Wallet Kit block from Trustless Work.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant RoiPage as ROI Page
    participant RoiHeader as ROI Header
    participant CampaignToolbar as Campaign Toolbar
    participant CampaignList as Campaign List
    participant CampaignCard as Campaign Card
    participant ClaimButton as Claim ROI Button

    User->>RoiPage: Visit /roi page
    RoiPage->>RoiHeader: Render with search state
    RoiPage->>CampaignToolbar: Render with filter state
    
    User->>RoiHeader: Enter search text
    RoiHeader->>RoiPage: Update searchValue
    RoiPage->>RoiPage: Recompute filtered campaigns
    
    User->>CampaignToolbar: Change status filter
    CampaignToolbar->>RoiPage: Update filterValue
    RoiPage->>RoiPage: Recompute filtered campaigns
    
    RoiPage->>CampaignList: Render with filtered campaigns
    CampaignList->>CampaignCard: Render each campaign
    CampaignCard->>ClaimButton: Render with campaignId
    
    User->>ClaimButton: Click Claim ROI
    ClaimButton->>RoiPage: Invoke handleClaimRoi callback
    RoiPage->>RoiPage: Log claimed campaign ID
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Hops of joy across the fields,
ROI dashboards, what wealth it yields!
Campaigns filter, search, and claim,
Investor returns in this game,
A feature carved with care and grace,
New components in their place!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is vague and generic, using non-descriptive terms like 'Layout with global components' that don't convey the specific meaningful changes in the changeset. Consider a more specific title like 'Add ROI dashboard layout and campaign management components' to better describe the primary changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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.

@JoelVR17 JoelVR17 self-assigned this Mar 11, 2026
Copy link
Contributor

@JoelVR17 JoelVR17 left a comment

Choose a reason for hiding this comment

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

Great job!

Copy link

@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 (4)
apps/investor-tokenization/src/features/roi/data/mock-campaigns.ts (1)

1-1: Use the @/ alias for this local import.

This relative import bypasses the frontend import convention and makes later moves/renames noisier than they need to be. As per coding guidelines, "Use path alias @/* mapping to ./src/* for imports".

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

In `@apps/investor-tokenization/src/features/roi/data/mock-campaigns.ts` at line
1, Replace the relative import in mock-campaigns.ts with the project path-alias:
change the "../types/campaign.types" import to use the "@/…" alias that maps to
./src, e.g. import the Campaign type from "@/features/roi/types/campaign.types"
(update the import statement in the file that currently references Campaign).
apps/investor-tokenization/src/features/roi/components/campaign-card.tsx (1)

15-23: Hardcoded locale in currency formatting.

The formatMinInvest function hardcodes "en-US" locale. Consider using undefined to let Intl.NumberFormat use the user's default locale, or accept locale as a parameter for flexibility.

💡 Optional locale-aware formatting
-function formatMinInvest(cents: number, currency: string): string {
+function formatMinInvest(cents: number, currency: string, locale?: string): string {
   const value = cents / 100;
-  return new Intl.NumberFormat("en-US", {
+  return new Intl.NumberFormat(locale, {
     style: "currency",
     currency,
     minimumFractionDigits: 0,
     maximumFractionDigits: 0,
   }).format(value);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/investor-tokenization/src/features/roi/components/campaign-card.tsx`
around lines 15 - 23, The currency formatter in function formatMinInvest
hardcodes the "en-US" locale; change it to use the runtime/default locale or
accept a locale parameter. Update formatMinInvest(cents: number, currency:
string[, locale?: string]) to call new Intl.NumberFormat(locale ?? undefined, {
style: "currency", currency, minimumFractionDigits: 0, maximumFractionDigits: 0
}) so it uses the user's locale when none is provided (or thread through a
locale argument from callers).
apps/investor-tokenization/src/features/roi/components/campaign-list.tsx (1)

9-19: Consider adding an empty state.

When campaigns is empty, the component renders an empty <ul>. Consider showing a user-friendly message when no campaigns match the current filters.

💡 Optional empty state handling
 export function CampaignList({ campaigns, onClaimRoi }: CampaignListProps) {
+  if (campaigns.length === 0) {
+    return (
+      <p className="text-center text-muted-foreground py-8">
+        No campaigns found.
+      </p>
+    );
+  }
+
   return (
     <ul className="flex flex-col gap-4 list-none p-0 m-0">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/investor-tokenization/src/features/roi/components/campaign-list.tsx`
around lines 9 - 19, The CampaignList currently renders an empty <ul> when the
campaigns prop is empty; update the CampaignList component to detect when
campaigns.length === 0 (or !campaigns || campaigns.length === 0) and render a
user-friendly empty state instead of an empty list — e.g., return a centered
message or an <EmptyState /> placeholder with guidance like "No campaigns match
your filters" while preserving the existing list rendering for non-empty arrays;
reference the CampaignList function and the campaigns prop to locate where to
add this conditional rendering.
apps/investor-tokenization/src/app/roi/page.tsx (1)

23-25: Simplify filtering once CampaignFilter values are aligned.

The .toLowerCase() call is necessary due to the case mismatch between filter values and CampaignStatus. Once the filter values are updated to uppercase (per the comment on campaign-filter.tsx), this can be simplified:

♻️ Proposed simplification (after filter fix)
     if (filter !== "all") {
-      list = list.filter((c) => c.status.toLowerCase() === filter);
+      list = list.filter((c) => c.status === filter);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/investor-tokenization/src/app/roi/page.tsx` around lines 23 - 25, The
current filter compares c.status.toLowerCase() to filter to work around casing;
once CampaignFilter values are updated to match CampaignStatus (uppercase per
campaign-filter.tsx), simplify the condition in
apps/investor-tokenization/src/app/roi/page.tsx by removing .toLowerCase() and
compare c.status === filter (i.e., update the list.filter callback that
currently reads list.filter((c) => c.status.toLowerCase() === filter) to use
direct equality), ensuring the 'filter' variable and CampaignStatus enums are
the same case.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/investor-tokenization/src/features/roi/components/campaign-filter.tsx`:
- Around line 32-36: The SelectTrigger component's hardcoded "text-black" in its
className overrides dark mode; update the class string passed to SelectTrigger
(the cn(...) call) to replace "text-black" with "text-foreground" so it respects
theme colors, keeping the rest of the classes and the existing cn(className)
composition intact.
- Around line 12-17: filterOptions uses lowercase values that don't match the
CampaignStatus union, breaking type-safety; update the values in filterOptions
(the array declared as filterOptions) so the campaign state entries use the
canonical uppercase strings "READY", "PENDING", "CLOSED" while keeping the "all"
option as-is, and then update the filter usage to compare directly via c.status
=== filter (and ensure the filter variable/type is CampaignStatus | "all") so
you no longer need .toLowerCase().

In
`@apps/investor-tokenization/src/features/roi/components/campaign-status-badge.tsx`:
- Around line 5-21: The badge always renders a green check for every state and
accepts a plain string; update CampaignStatusBadge to be status-aware by
defining a CampaignStatus union type (e.g., "READY" | "PENDING" | "CLOSED") and
change CampaignStatusBadgeProps.status to that type, then compute the badge
appearance inside CampaignStatusBadge (use a switch or a mapping keyed by
status) to return appropriate class names and icon components (e.g., green Check
for READY, yellow Clock/Spinner for PENDING, gray X/Lock for CLOSED) and merge
those classes with the existing cn call and className prop; ensure you provide a
safe default/fallback for unexpected values and update any imports/usages of
CampaignStatusBadge accordingly.

In `@apps/investor-tokenization/src/features/roi/roi-dashboard-shell.tsx`:
- Around line 13-15: The ROI_NAV_ITEMS entry has a label/destination mismatch:
the item { href: "/", label: "Manage Campaigns", icon: Megaphone } should point
to the actual Manage Campaigns route or its correct label; update ROI_NAV_ITEMS
so the label matches the href (either change href to the Manage Campaigns route
path used elsewhere or change label to "Invest"), e.g. locate ROI_NAV_ITEMS in
roi-dashboard-shell.tsx and adjust the first entry's href or label so that the
label "Manage Campaigns" leads to the Manage Campaigns route and "/" remains
labeled "Invest" consistently.
- Around line 24-25: The ROI shell is being mounted as a fullscreen overlay due
to the outer div's "fixed inset-0 z-[60]" class (and a similar block at lines
47-50), which leaves global header/dock underneath and prevents internal
scrolling; remove or replace the positioning so the ROI route renders in normal
flow (e.g., drop "fixed inset-0 z-[60]" and use a non-fixed container like
"relative" or no positioning) or, if you intend it to own chrome, make it a
full-page layout that also accounts for header/dock offsets and provides an
internal scrollable container (add a wrapper with explicit top/bottom offsets
and overflow-auto). Update the div wrapping SidebarProvider (and the duplicate
block) to ensure content can scroll when taller than the viewport and that
global header/dock are either covered intentionally or remain visible as part of
the app chrome.

In `@apps/investor-tokenization/src/features/roi/types/campaign.types.ts`:
- Around line 1-10: The CampaignStatus union in this file is out-of-sync with
the canonical domain enum; update the CampaignStatus type to use the domain
values DRAFT | ACTIVE | FUNDED | PAUSED | CLOSED (and replace the current
"READY" and "PENDING") so the Campaign type accepts real payloads, and ensure
any local references to CampaignStatus (e.g., in the Campaign type or UI
conversion helpers) are adjusted or mapped to the canonical values instead of
the old READY/PENDING tokens.

In `@docs/PLAN_WALLET_KIT.md`:
- Around line 61-63: The fenced code block containing "❌ Missing initial setup.
Run 'trustless-work init'..." should be tagged with a language to satisfy
markdownlint MD040; update the opening fence from ``` to ```text in the block in
PLAN_WALLET_KIT.md so the example remains unchanged but the linter error is
resolved.

---

Nitpick comments:
In `@apps/investor-tokenization/src/app/roi/page.tsx`:
- Around line 23-25: The current filter compares c.status.toLowerCase() to
filter to work around casing; once CampaignFilter values are updated to match
CampaignStatus (uppercase per campaign-filter.tsx), simplify the condition in
apps/investor-tokenization/src/app/roi/page.tsx by removing .toLowerCase() and
compare c.status === filter (i.e., update the list.filter callback that
currently reads list.filter((c) => c.status.toLowerCase() === filter) to use
direct equality), ensuring the 'filter' variable and CampaignStatus enums are
the same case.

In `@apps/investor-tokenization/src/features/roi/components/campaign-card.tsx`:
- Around line 15-23: The currency formatter in function formatMinInvest
hardcodes the "en-US" locale; change it to use the runtime/default locale or
accept a locale parameter. Update formatMinInvest(cents: number, currency:
string[, locale?: string]) to call new Intl.NumberFormat(locale ?? undefined, {
style: "currency", currency, minimumFractionDigits: 0, maximumFractionDigits: 0
}) so it uses the user's locale when none is provided (or thread through a
locale argument from callers).

In `@apps/investor-tokenization/src/features/roi/components/campaign-list.tsx`:
- Around line 9-19: The CampaignList currently renders an empty <ul> when the
campaigns prop is empty; update the CampaignList component to detect when
campaigns.length === 0 (or !campaigns || campaigns.length === 0) and render a
user-friendly empty state instead of an empty list — e.g., return a centered
message or an <EmptyState /> placeholder with guidance like "No campaigns match
your filters" while preserving the existing list rendering for non-empty arrays;
reference the CampaignList function and the campaigns prop to locate where to
add this conditional rendering.

In `@apps/investor-tokenization/src/features/roi/data/mock-campaigns.ts`:
- Line 1: Replace the relative import in mock-campaigns.ts with the project
path-alias: change the "../types/campaign.types" import to use the "@/…" alias
that maps to ./src, e.g. import the Campaign type from
"@/features/roi/types/campaign.types" (update the import statement in the file
that currently references Campaign).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 23bbe689-76f9-43aa-970b-22b9036ba90f

📥 Commits

Reviewing files that changed from the base of the PR and between 554b9c4 and 296be5a.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (17)
  • apps/investor-tokenization/src/app/layout.tsx
  • apps/investor-tokenization/src/app/roi/layout.tsx
  • apps/investor-tokenization/src/app/roi/page.tsx
  • apps/investor-tokenization/src/components/shared/Navbar.tsx
  • apps/investor-tokenization/src/features/roi/components/campaign-card.tsx
  • apps/investor-tokenization/src/features/roi/components/campaign-filter.tsx
  • apps/investor-tokenization/src/features/roi/components/campaign-list.tsx
  • apps/investor-tokenization/src/features/roi/components/campaign-search.tsx
  • apps/investor-tokenization/src/features/roi/components/campaign-status-badge.tsx
  • apps/investor-tokenization/src/features/roi/components/campaign-toolbar.tsx
  • apps/investor-tokenization/src/features/roi/components/claim-roi-button.tsx
  • apps/investor-tokenization/src/features/roi/components/investor-profile-card.tsx
  • apps/investor-tokenization/src/features/roi/components/roi-header.tsx
  • apps/investor-tokenization/src/features/roi/data/mock-campaigns.ts
  • apps/investor-tokenization/src/features/roi/roi-dashboard-shell.tsx
  • apps/investor-tokenization/src/features/roi/types/campaign.types.ts
  • docs/PLAN_WALLET_KIT.md

Comment on lines +12 to +17
const filterOptions = [
{ value: "all", label: "All Campaigns" },
{ value: "ready", label: "Ready" },
{ value: "pending", label: "Pending" },
{ value: "closed", label: "Closed" },
];
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Filter values don't match CampaignStatus type.

The filter options use lowercase values ("ready", "pending", "closed") while CampaignStatus is defined as "READY" | "PENDING" | "CLOSED". This inconsistency forces the page to use .toLowerCase() for comparison and loses type safety.

Consider aligning with the canonical type:

🔧 Proposed fix to use uppercase values
 const filterOptions = [
   { value: "all", label: "All Campaigns" },
-  { value: "ready", label: "Ready" },
-  { value: "pending", label: "Pending" },
-  { value: "closed", label: "Closed" },
+  { value: "READY", label: "Ready" },
+  { value: "PENDING", label: "Pending" },
+  { value: "CLOSED", label: "Closed" },
 ];

This allows the page to filter directly: c.status === filter without case conversion.

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

In `@apps/investor-tokenization/src/features/roi/components/campaign-filter.tsx`
around lines 12 - 17, filterOptions uses lowercase values that don't match the
CampaignStatus union, breaking type-safety; update the values in filterOptions
(the array declared as filterOptions) so the campaign state entries use the
canonical uppercase strings "READY", "PENDING", "CLOSED" while keeping the "all"
option as-is, and then update the filter usage to compare directly via c.status
=== filter (and ensure the filter variable/type is CampaignStatus | "all") so
you no longer need .toLowerCase().

Comment on lines +32 to +36
<SelectTrigger
className={cn(
"w-[180px] rounded-lg border border-input bg-background h-9 text-sm font-medium text-black",
className
)}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Hardcoded text-black may break dark mode.

The text-black class will render black text regardless of the theme. Use text-foreground instead to respect the theme's color scheme.

🎨 Proposed fix
       <SelectTrigger
         className={cn(
-          "w-[180px] rounded-lg border border-input bg-background h-9 text-sm font-medium text-black",
+          "w-[180px] rounded-lg border border-input bg-background h-9 text-sm font-medium text-foreground",
           className
         )}
       >
📝 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
<SelectTrigger
className={cn(
"w-[180px] rounded-lg border border-input bg-background h-9 text-sm font-medium text-black",
className
)}
<SelectTrigger
className={cn(
"w-[180px] rounded-lg border border-input bg-background h-9 text-sm font-medium text-foreground",
className
)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/investor-tokenization/src/features/roi/components/campaign-filter.tsx`
around lines 32 - 36, The SelectTrigger component's hardcoded "text-black" in
its className overrides dark mode; update the class string passed to
SelectTrigger (the cn(...) call) to replace "text-black" with "text-foreground"
so it respects theme colors, keeping the rest of the classes and the existing
cn(className) composition intact.

Comment on lines +5 to +21
type CampaignStatusBadgeProps = {
status: string;
className?: string;
};

export function CampaignStatusBadge({ status, className }: CampaignStatusBadgeProps) {
return (
<Badge
className={cn(
"rounded-md bg-emerald-500 text-white border-0 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide gap-1 [&_svg]:size-3",
className
)}
>
<Check className="size-3" />
{status}
</Badge>
);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Badge doesn't differentiate between campaign statuses.

The badge always renders as green with a check icon regardless of whether the status is "READY", "PENDING", or "CLOSED". This creates a misleading UX where pending/closed campaigns appear as ready.

Additionally, status is typed as string instead of CampaignStatus, losing type safety.

🔧 Proposed fix with status-aware styling
+import type { CampaignStatus } from "../types/campaign.types";
+import { Clock, Check, XCircle } from "lucide-react";
+
+const statusConfig: Record<CampaignStatus, { icon: typeof Check; className: string }> = {
+  READY: { icon: Check, className: "bg-emerald-500 text-white" },
+  PENDING: { icon: Clock, className: "bg-yellow-500 text-white" },
+  CLOSED: { icon: XCircle, className: "bg-gray-500 text-white" },
+};
+
 type CampaignStatusBadgeProps = {
-  status: string;
+  status: CampaignStatus;
   className?: string;
 };
 
 export function CampaignStatusBadge({ status, className }: CampaignStatusBadgeProps) {
+  const config = statusConfig[status];
+  const Icon = config.icon;
+
   return (
     <Badge
       className={cn(
-        "rounded-md bg-emerald-500 text-white border-0 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide gap-1 [&_svg]:size-3",
+        "rounded-md border-0 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide gap-1 [&_svg]:size-3",
+        config.className,
         className
       )}
     >
-      <Check className="size-3" />
+      <Icon className="size-3" />
       {status}
     </Badge>
   );
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/investor-tokenization/src/features/roi/components/campaign-status-badge.tsx`
around lines 5 - 21, The badge always renders a green check for every state and
accepts a plain string; update CampaignStatusBadge to be status-aware by
defining a CampaignStatus union type (e.g., "READY" | "PENDING" | "CLOSED") and
change CampaignStatusBadgeProps.status to that type, then compute the badge
appearance inside CampaignStatusBadge (use a switch or a mapping keyed by
status) to return appropriate class names and icon components (e.g., green Check
for READY, yellow Clock/Spinner for PENDING, gray X/Lock for CLOSED) and merge
those classes with the existing cn call and className prop; ensure you provide a
safe default/fallback for unexpected values and update any imports/usages of
CampaignStatusBadge accordingly.

Comment on lines +13 to +15
const ROI_NAV_ITEMS = [
{ href: "/", label: "Manage Campaigns", icon: Megaphone },
{ href: "/roi", label: "ROI", icon: TrendingUp },
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix the sidebar label/destination mismatch.

href="/" routes back to the investor landing page, but this item is labeled "Manage Campaigns". In this app, / is already surfaced as "Invest" in apps/investor-tokenization/src/components/shared/Navbar.tsx, so the label currently points users to a different destination than it describes.

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

In `@apps/investor-tokenization/src/features/roi/roi-dashboard-shell.tsx` around
lines 13 - 15, The ROI_NAV_ITEMS entry has a label/destination mismatch: the
item { href: "/", label: "Manage Campaigns", icon: Megaphone } should point to
the actual Manage Campaigns route or its correct label; update ROI_NAV_ITEMS so
the label matches the href (either change href to the Manage Campaigns route
path used elsewhere or change label to "Invest"), e.g. locate ROI_NAV_ITEMS in
roi-dashboard-shell.tsx and adjust the first entry's href or label so that the
label "Manage Campaigns" leads to the Manage Campaigns route and "/" remains
labeled "Invest" consistently.

Comment on lines +24 to +25
<div className="fixed inset-0 z-[60] flex min-h-svh bg-background">
<SidebarProvider>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid mounting the ROI shell as a fullscreen overlay.

fixed inset-0 z-[60] turns this route layout into an overlay on top of the root layout instead of normal page content. That leaves the global header/dock mounted underneath, and because there is no inner scroll container here, ROI content taller than the viewport will become unreachable. This should either render in normal flow or own both the route chrome and scrolling.

Also applies to: 47-50

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

In `@apps/investor-tokenization/src/features/roi/roi-dashboard-shell.tsx` around
lines 24 - 25, The ROI shell is being mounted as a fullscreen overlay due to the
outer div's "fixed inset-0 z-[60]" class (and a similar block at lines 47-50),
which leaves global header/dock underneath and prevents internal scrolling;
remove or replace the positioning so the ROI route renders in normal flow (e.g.,
drop "fixed inset-0 z-[60]" and use a non-fixed container like "relative" or no
positioning) or, if you intend it to own chrome, make it a full-page layout that
also accounts for header/dock offsets and provides an internal scrollable
container (add a wrapper with explicit top/bottom offsets and overflow-auto).
Update the div wrapping SidebarProvider (and the duplicate block) to ensure
content can scroll when taller than the viewport and that global header/dock are
either covered intentionally or remain visible as part of the app chrome.

Comment on lines +1 to +10
export type CampaignStatus = "READY" | "PENDING" | "CLOSED";

export type Campaign = {
id: string;
title: string;
description: string;
status: CampaignStatus;
loansCompleted: number;
minInvestCents: number;
currency: string;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Align CampaignStatus with the domain enum.

The ROI feature introduces "READY" | "PENDING" | "CLOSED", but the shared Campaign model uses DRAFT | ACTIVE | FUNDED | PAUSED | CLOSED. The first real payload with ACTIVE, FUNDED, or PAUSED will not fit this type, so either reuse the canonical enum values here or add an explicit UI-mapping layer on top of them. Based on learnings, Campaign model should have status enum with values: DRAFT, ACTIVE, FUNDED, PAUSED, CLOSED.

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

In `@apps/investor-tokenization/src/features/roi/types/campaign.types.ts` around
lines 1 - 10, The CampaignStatus union in this file is out-of-sync with the
canonical domain enum; update the CampaignStatus type to use the domain values
DRAFT | ACTIVE | FUNDED | PAUSED | CLOSED (and replace the current "READY" and
"PENDING") so the Campaign type accepts real payloads, and ensure any local
references to CampaignStatus (e.g., in the Campaign type or UI conversion
helpers) are adjusted or mapped to the canonical values instead of the old
READY/PENDING tokens.

Comment on lines +61 to +63
```
❌ Missing initial setup. Run 'trustless-work init' first to install dependencies and create .twblocks.json (uiBase).
```
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add a language to the fenced error example.

This block currently trips markdownlint (MD040). Tagging it as text keeps the doc lint-clean without changing the content.

📝 Proposed fix
-     ```
+     ```text
      ❌ Missing initial setup. Run 'trustless-work init' first to install dependencies and create .twblocks.json (uiBase).
      ```
📝 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
```
❌ Missing initial setup. Run 'trustless-work init' first to install dependencies and create .twblocks.json (uiBase).
```
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 61-61: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

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

In `@docs/PLAN_WALLET_KIT.md` around lines 61 - 63, The fenced code block
containing "❌ Missing initial setup. Run 'trustless-work init'..." should be
tagged with a language to satisfy markdownlint MD040; update the opening fence
from ``` to ```text in the block in PLAN_WALLET_KIT.md so the example remains
unchanged but the linter error is resolved.

Copy link
Contributor

@armandocodecr armandocodecr left a comment

Choose a reason for hiding this comment

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

looks good

@JoelVR17 JoelVR17 merged commit cf2238d into Trustless-Work:develop Mar 11, 2026
1 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants