Skip to content

feat: implement SenderDashboard with stream analytics and activity feed#575

Open
LawalRahman wants to merge 1 commit into
ritik4ever:mainfrom
LawalRahman:add-sender-dashboard-with-stream-creation-history-and-analytics
Open

feat: implement SenderDashboard with stream analytics and activity feed#575
LawalRahman wants to merge 1 commit into
ritik4ever:mainfrom
LawalRahman:add-sender-dashboard-with-stream-creation-history-and-analytics

Conversation

@LawalRahman

@LawalRahman LawalRahman commented Jun 24, 2026

Copy link
Copy Markdown
  • Add SenderDashboard component with stats cards for total streams, amount, active, completed
  • Implement bar chart visualization for streams by status (Scheduled, Active, Paused, Completed, Canceled)
  • Add recent activity feed showing last 10 events aggregated from sender's streams
  • Implement quick action buttons: Create Stream and Bulk Cancel
  • Add getSenderEvents() API helper function for aggregating events across streams
  • Include comprehensive Vitest test suite with 14 tests covering all features
  • Add MSW mock handlers for API endpoints with realistic test data fixtures

Closes #392

What changed

Testing done

Related issues

Closes #392

Checklist

  • I kept the change focused on the related issue.
  • I added or updated tests where useful.
  • I updated documentation where behavior changed.
  • I verified the app still builds or explained why verification was skipped.

Summary by CodeRabbit

  • New Features

    • Expanded the sender dashboard with summary metrics, a status breakdown chart, recent activity feed, and quick actions for creating and bulk-canceling streams.
    • Added clearer stream lists with selection checkboxes, improved status handling, and updated empty/loading states.
  • Bug Fixes

    • Improved dashboard refresh behavior so activity and stream data stay in sync after create, cancel, or bulk-cancel actions.
    • Enhanced error and wallet-connection messaging for a smoother experience.

- Add SenderDashboard component with stats cards for total streams, amount, active, completed
- Implement bar chart visualization for streams by status (Scheduled, Active, Paused, Completed, Canceled)
- Add recent activity feed showing last 10 events aggregated from sender's streams
- Implement quick action buttons: Create Stream and Bulk Cancel
- Add getSenderEvents() API helper function for aggregating events across streams
- Include comprehensive Vitest test suite with 14 tests covering all features
- Add MSW mock handlers for API endpoints with realistic test data fixtures

Closes ritik4ever#392
@vercel

vercel Bot commented Jun 24, 2026

Copy link
Copy Markdown

@LawalRahman is attempting to deploy a commit to the ritik4ever's projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Jun 24, 2026

Copy link
Copy Markdown

@LawalRahman Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

SenderDashboard is expanded with analytics cards, a Recharts streams-by-status bar chart, a recent activity feed, and bulk-cancel with row selection. A new getSenderEvents helper in api.ts aggregates per-stream history concurrently. Tests are expanded with MSW fixtures for the new endpoints, and three markdown documentation files are added.

Changes

SenderDashboard Analytics, Activity Feed & Bulk Cancel

Layer / File(s) Summary
getSenderEvents API helper
frontend/src/services/api.ts
Exports getSenderEvents(senderAddress, limit?) that fetches all sender streams, concurrently retrieves each stream's event history via getStreamHistory, silently swallows per-stream failures, aggregates, sorts descending by timestamp, slices to limit, and returns [] on any outer error.
Component state, helpers, and polling
frontend/src/components/SenderDashboard.tsx
Adds recharts bar-chart imports and named API imports; adds event/chart formatting helpers; introduces events, eventsLoading, and selectedStreams state; integrates getSenderEvents into the initial load effect and the 5-second polling interval; replaces per-status memos with unified useMemo-derived stats and chartData.
Cancel, bulk cancel, and create handlers
frontend/src/components/SenderDashboard.tsx
Adds handleCancel with confirmation and post-cancel event refresh; adds handleBulkCancel that concurrently cancels all selected streams, clears selection, and refreshes both streams and events; updates handleCreate to refresh events and close the form.
Dashboard UI sections and table rendering
frontend/src/components/SenderDashboard.tsx
Adds metrics cards, asset breakdown, BarChart status distribution section, quick-actions bulk-cancel button, recent activity feed with formatted labels and timestamps, select-all and per-row checkboxes, status badges, progress bars, conditional "Edit start time" and disabled "Cancel" buttons, and updates loading/error/empty/wallet-not-connected states.
Expanded Vitest/MSW test suite
frontend/src/components/SenderDashboard.test.tsx
Adds StreamEvent import, scheduled/paused/canceled stream fixtures, mockStreamEvent factory; updates MSW handlers to return paginated /api/streams and typed /api/streams/:id/history responses; adds "Enhanced Analytics & Activity" suite covering stats cards, asset breakdown, bar chart labels, activity feed aggregation/sorting, and bulk-cancel checkbox selection; reworks baseline tests; adds wallet-null and 500-error-code assertions.
Implementation and acceptance documentation
ACCEPTANCE_CRITERIA_VERIFICATION.md, SENDER_DASHBOARD_IMPLEMENTATION.md, SENDER_DASHBOARD_STRUCTURE.md
Three new markdown files documenting acceptance criteria (14-test plan, UX order, QA expectations, completion status), implementation summary (API helper contract, component changes, test coverage, dependencies, future ideas), and component structure (layout diagram, hierarchy, data flow, API endpoints, performance, accessibility).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant SenderDashboard
    participant getSenderEvents
    participant listStreams
    participant getStreamHistory
    participant cancelStream

    rect rgba(70, 130, 180, 0.5)
        Note over SenderDashboard: Mount / 5s poll
        SenderDashboard->>listStreams: listStreams({ sender })
        listStreams-->>SenderDashboard: streams[] + stats/chartData via useMemo
        SenderDashboard->>getSenderEvents: getSenderEvents(senderAddress, 10)
        getSenderEvents->>getStreamHistory: getStreamHistory(id) × N parallel
        getStreamHistory-->>getSenderEvents: StreamEvent[]
        getSenderEvents-->>SenderDashboard: sorted, sliced events[]
    end

    rect rgba(60, 179, 113, 0.5)
        Note over User,SenderDashboard: Bulk Cancel flow
        User->>SenderDashboard: check row checkboxes / select-all
        SenderDashboard->>SenderDashboard: update selectedStreams Set
        User->>SenderDashboard: click "Bulk Cancel"
        SenderDashboard->>cancelStream: cancelStream(id) × M parallel
        cancelStream-->>SenderDashboard: results
        SenderDashboard->>listStreams: refresh streams
        SenderDashboard->>getSenderEvents: refresh events
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • ritik4ever/stellar-stream#306: Modifies getStreamHistory and getStream signatures to accept an AbortSignal, directly affecting the history fetching called inside the new getSenderEvents helper.
  • ritik4ever/stellar-stream#481: Adds backend StreamEventType values and records completed events in stream history, which the new activity feed and completed-stream stats in this PR depend on.
  • ritik4ever/stellar-stream#567: Modifies SenderDashboard.tsx and SenderDashboard.test.tsx around the create-stream flow and metric rendering, overlapping directly with changes in this PR.

Poem

🐇 Hopping through streams with a chart in my paw,
I counted the active, the paused, and the more.
Bulk cancel? No problem — I check every box,
Then gather the events like eggs in my socks.
The feed sorts by timestamp, the stats light up bright,
This dashboard is done! ✅ What a beautiful night.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The implementation appears to use stream histories for stats/activity instead of the required /api/stats and /api/events?sender sources. Align SenderDashboard with the linked issue by fetching aggregate stats from /api/stats and recent activity from /api/events?sender, or update the acceptance criteria if the API changed.
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: SenderDashboard analytics and activity feed.
Out of Scope Changes check ✅ Passed The added docs, component, service, and tests all support the SenderDashboard feature and its acceptance criteria.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 12

Caution

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

⚠️ Outside diff range comments (1)
frontend/src/components/SenderDashboard.tsx (1)

170-182: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Prevent overlapping poll cycles.

setInterval starts a new async refresh every 5 seconds even if the previous listStreams / getSenderEvents cycle is still running. Slow requests can stack up and let older responses overwrite newer state.

Proposed guard
+    let polling = false;
     const interval = setInterval(async () => {
+      if (polling) return;
+      polling = true;
       try {
         const result = await listStreams({ sender: senderAddress });
         if (active) {
           setStreams(result.data);

           // Also refresh events
           const recentEvents = await getSenderEvents(senderAddress);
           if (active) {
             setEvents(recentEvents);
           }
         }
       } catch {
         // Silent fail on polling
+      } finally {
+        polling = false;
       }
     }, 5000);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/SenderDashboard.tsx` around lines 170 - 182, The
polling logic in SenderDashboard is using setInterval with async work, which can
overlap refresh cycles and allow stale responses to update state. Update the
polling effect around the listStreams and getSenderEvents calls to use a
re-entrancy guard or single-flight flag so a new cycle is skipped while the
previous one is still running. Keep the existing active check, but ensure only
one refresh runs at a time and that the interval cleanup still clears properly.
🧹 Nitpick comments (1)
frontend/src/components/SenderDashboard.test.tsx (1)

53-59: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Minor DRY: mockActiveStream is invoked twice per call.

Each helper calls mockActiveStream(id, sender) for the spread and again for ...progress, building two throwaway objects. Capture once for clarity.

♻️ Optional
-const mockPausedStream = (id: string, sender: string): Stream => ({
-  ...mockActiveStream(id, sender),
-  progress: {
-    ...mockActiveStream(id, sender).progress,
-    status: "paused",
-  },
-});
+const mockPausedStream = (id: string, sender: string): Stream => {
+  const base = mockActiveStream(id, sender);
+  return { ...base, progress: { ...base.progress, status: "paused" } };
+};

Also applies to: 73-79

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/SenderDashboard.test.tsx` around lines 53 - 59, The
mockPausedStream helper is calling mockActiveStream(id, sender) twice, creating
unnecessary duplicate objects. Update mockPausedStream (and the similar helper
referenced in the review) to call mockActiveStream once, store the result in a
local variable, and reuse it for both the spread and progress override so the
test data is clearer and avoids repeated work.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ACCEPTANCE_CRITERIA_VERIFICATION.md`:
- Around line 24-33: The markdown fences in ACCEPTANCE_CRITERIA_VERIFICATION
should use explicit language identifiers to satisfy MD040. Update each fenced
block in this document, including the ones around the table output and the other
referenced sections, to use a consistent language tag such as text. Focus on the
fenced code blocks shown in the diff and any matching blocks elsewhere in the
file so they all follow the same markdown lint-friendly format.
- Around line 78-101: The recent activity feed acceptance note is inconsistent
with the implemented data source, since getSenderEvents and SenderDashboard
aggregate stream history instead of using the stated /api/events?sender=
contract. Update the acceptance criteria text to either match the actual
implementation path or add an explicit signed-off deviation, and make sure the
status reflects the real contract used by frontend/src/services/api.ts and
frontend/src/components/SenderDashboard.tsx.

In `@frontend/src/components/SenderDashboard.test.tsx`:
- Around line 181-185: The SenderDashboard test is asserting the wrong formatted
total for stats.totalAmount. Update the amount expectation in
SenderDashboard.test.tsx to match the fixture sum rendered by the component’s
toLocaleString("en-US", { maximumFractionDigits: 2 }) formatting, and change the
assertion near the Total Amount Streamed card to expect 3,500 instead of 3000.
- Around line 229-259: The chart test in SenderDashboard.test.tsx is relying on
jsdom/Recharts layout rendering and text labels, which is brittle without the
same mocking used elsewhere. Update the "renders bar chart showing streams by
status" test to avoid asserting axis labels from the rendered chart, and instead
either mock the chart/recharts components locally or verify the underlying
status data passed into SenderDashboard/Streams by Status behavior using
existing helpers like setupSenderHandler and mock*Stream fixtures.

In `@frontend/src/components/SenderDashboard.tsx`:
- Around line 222-225: The `completedStreams` metric in `SenderDashboard` is
incorrectly counting canceled streams as completed, which inflates the completed
stat. Update the filtering logic used for `completedStreams` so it only includes
streams whose `progress.status` is exactly `"completed"` and excludes
`"canceled"`; check the related stats/card rendering and any other references in
`SenderDashboard` to keep the completed and canceled counts split correctly.
- Around line 275-284: The cancel handlers in SenderDashboard are treating
refresh failures as cancel failures because the mutation and post-success state
reloads are inside the same try; move the listStreams/getSenderEvents refresh
logic out of the mutation error path so a successful cancel still updates state
even if refresh fails. For the bulk flow in the same component, update the
Promise.all-based cancellation in the bulk handler to tolerate partial success,
then refresh streams/events and clear any selected IDs based on the completed
cancels rather than aborting on the first rejection.
- Around line 233-239: The chart color lookup in SenderDashboard’s chart data
rendering is using filtered indexes and capitalized labels, which causes
mismatched or gray bars. Update the chart data objects to carry the actual
lowercase status key alongside the display name in the chartData builder, then
have the <Cell> color lookup use that status key when calling statusColor
instead of relying on array position or the label text. Apply the same fix
anywhere else the same chart color mapping pattern is used, including the
repeated chart rendering block referenced in the comment.
- Around line 493-526: Quick Actions is gated on stats.activeStreams, but Bulk
Cancel should be driven by the same cancellable rows the table can actually
select. Update SenderDashboard to use a single cancellable stream list for both
the quick-actions section and selection state, so scheduled rows can still
surface Bulk Cancel when selected. Review the Quick Actions block and the row
selection logic around selectedStreamForBulkCancel, handleBulkCancel, and the
stream table rendering to ensure paused/scheduled streams are handled
consistently.
- Around line 94-97: The claimed activity label in SenderDashboard’s label
formatting can render undefined values because it always interpolates amount and
assetCode even when they are optional. Update the logic in the label switch for
the claimed case to conditionally include these fields only when present,
matching the pattern already used in the created case, so the activity text
never shows undefined.

In `@frontend/src/services/api.ts`:
- Around line 345-370: The getSenderEvents helper is using listStreams plus
per-stream getStreamHistory fan-out instead of the sender events endpoint
required by the review. Update getSenderEvents to fetch directly from
/api/events with the sender query parameter, keep the existing limit handling
for the last 10 items, and remove the N+1 stream/history aggregation logic so
the function returns the sender’s activity from the single endpoint.

In `@SENDER_DASHBOARD_IMPLEMENTATION.md`:
- Line 95: The test-count documentation is inconsistent across the added docs,
so align the verified number everywhere it appears. Update the count in
SENDER_DASHBOARD_IMPLEMENTATION.md and the other referenced sections to match
the actual PR artifact count, using the same wording consistently so the
summary, implementation notes, and test coverage references all agree.

In `@SENDER_DASHBOARD_STRUCTURE.md`:
- Around line 5-83: The fenced diagram/tree/flow blocks in this markdown file
are missing language identifiers, which triggers MD040. Update each affected
fenced block in SENDER_DASHBOARD_STRUCTURE.md to use a text/diagram-style
language tag consistently, and verify the blocks around the SenderDashboard
layout, the tree view, and the flow steps all have explicit fence languages
while keeping the content unchanged.

---

Outside diff comments:
In `@frontend/src/components/SenderDashboard.tsx`:
- Around line 170-182: The polling logic in SenderDashboard is using setInterval
with async work, which can overlap refresh cycles and allow stale responses to
update state. Update the polling effect around the listStreams and
getSenderEvents calls to use a re-entrancy guard or single-flight flag so a new
cycle is skipped while the previous one is still running. Keep the existing
active check, but ensure only one refresh runs at a time and that the interval
cleanup still clears properly.

---

Nitpick comments:
In `@frontend/src/components/SenderDashboard.test.tsx`:
- Around line 53-59: The mockPausedStream helper is calling mockActiveStream(id,
sender) twice, creating unnecessary duplicate objects. Update mockPausedStream
(and the similar helper referenced in the review) to call mockActiveStream once,
store the result in a local variable, and reuse it for both the spread and
progress override so the test data is clearer and avoids repeated work.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4fc5723b-643a-4b36-bd9e-8b997b0c3256

📥 Commits

Reviewing files that changed from the base of the PR and between 113a586 and 9275174.

📒 Files selected for processing (6)
  • ACCEPTANCE_CRITERIA_VERIFICATION.md
  • SENDER_DASHBOARD_IMPLEMENTATION.md
  • SENDER_DASHBOARD_STRUCTURE.md
  • frontend/src/components/SenderDashboard.test.tsx
  • frontend/src/components/SenderDashboard.tsx
  • frontend/src/services/api.ts

Comment on lines +24 to +33
```
┌──────────────────────────┬──────────────────────────┐
│ Total Streams Created │ Total Amount Streamed │
│ 4 │ 3,000 │
└──────────────────────────┴──────────────────────────┘
┌──────────────────────────┬──────────────────────────┐
│ Active Streams │ Completed/Canceled │
│ 2 │ 2 │
└──────────────────────────┴──────────────────────────┘
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add language identifiers to fenced code blocks (markdownlint MD040).

Use explicit fence languages for these blocks to keep lint clean and improve rendering consistency.

Suggested patch
-```
+```text
 ┌──────────────────────────┬──────────────────────────┐
 ...
-```
+```

-```
+```text
 Streams by Status
 ...
-```
+```

-```
+```text
 Recent Activity
 ...
-```
+```

-```
+```text
 Header:
 ...
-```
+```

Also applies to: 65-74, 119-130, 180-190

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

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

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ACCEPTANCE_CRITERIA_VERIFICATION.md` around lines 24 - 33, The markdown
fences in ACCEPTANCE_CRITERIA_VERIFICATION should use explicit language
identifiers to satisfy MD040. Update each fenced block in this document,
including the ones around the table output and the other referenced sections, to
use a consistent language tag such as text. Focus on the fenced code blocks
shown in the diff and any matching blocks elsewhere in the file so they all
follow the same markdown lint-friendly format.

Source: Linters/SAST tools

Comment on lines +78 to +101
### 3. ✅ Recent activity feed: last 10 events from `/api/events?sender=`

**Implemented in:**
- API Helper: `frontend/src/services/api.ts` (lines 336-370)
- Component: `frontend/src/components/SenderDashboard.tsx` (lines 354-394)

**API Function: `getSenderEvents(senderAddress, limit=10)`**
```typescript
export async function getSenderEvents(
senderAddress: string,
limit: number = 10
): Promise<StreamEvent[]> {
// 1. Fetch all streams for sender
// 2. Get event history for each stream
// 3. Aggregate all events
// 4. Sort by timestamp (newest first)
// 5. Return limited set
}
```

**Features:**
- Fetches from actual `/api/streams/:streamId/history` endpoints
- Aggregates events from all sender's streams
- Displays last 10 most recent events (sorted by timestamp descending)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Acceptance criterion for activity endpoint is not actually met as written.

This section states the feed is from /api/events?sender=, but the implemented path (via getSenderEvents) aggregates /api/streams/:streamId/history calls instead. Please either implement the required endpoint contract or explicitly document a signed-off deviation in acceptance status.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ACCEPTANCE_CRITERIA_VERIFICATION.md` around lines 78 - 101, The recent
activity feed acceptance note is inconsistent with the implemented data source,
since getSenderEvents and SenderDashboard aggregate stream history instead of
using the stated /api/events?sender= contract. Update the acceptance criteria
text to either match the actual implementation path or add an explicit
signed-off deviation, and make sure the status reflects the real contract used
by frontend/src/services/api.ts and frontend/src/components/SenderDashboard.tsx.

Comment on lines +181 to +185
expect(screen.getByText("Total Amount Streamed")).toBeInTheDocument();
const amountCard = screen
.getByText("Total Amount Streamed")
.closest("article");
expect(amountCard?.querySelector("strong")?.textContent).toContain("3000");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm the completed-stream fixture amount and that the rendered total is comma-formatted.
fd -t f 'SenderDashboard.test.tsx' --exec sed -n '60,80p' {}
# Inspect the amount-rendering line in the component
fd -t f 'SenderDashboard.tsx' frontend/src/components --exec rg -n 'toLocaleString' {}

Repository: ritik4ever/stellar-stream

Length of output: 848


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the fixture definitions and the stats assertion block.
sed -n '1,220p' frontend/src/components/SenderDashboard.test.tsx

# Show the stats rendering/aggregation area in the component.
sed -n '380,470p' frontend/src/components/SenderDashboard.tsx

Repository: ritik4ever/stellar-stream

Length of output: 9510


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the fixture definitions and the stats assertion block.
sed -n '1,220p' frontend/src/components/SenderDashboard.test.tsx

# Show the stats rendering/aggregation area in the component.
sed -n '380,470p' frontend/src/components/SenderDashboard.tsx

Repository: ritik4ever/stellar-stream

Length of output: 9510


Update the amount assertion to match the fixture total

stats.totalAmount is rendered with toLocaleString("en-US", { maximumFractionDigits: 2 }), and these fixtures add up to 3,500 (1000 + 1000 + 500 + 1000). toContain("3000") will not match; assert against "3,500" instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/SenderDashboard.test.tsx` around lines 181 - 185, The
SenderDashboard test is asserting the wrong formatted total for
stats.totalAmount. Update the amount expectation in SenderDashboard.test.tsx to
match the fixture sum rendered by the component’s toLocaleString("en-US", {
maximumFractionDigits: 2 }) formatting, and change the assertion near the Total
Amount Streamed card to expect 3,500 instead of 3000.

Comment on lines +229 to +259
it("renders bar chart showing streams by status", async () => {
const SENDER_CHART = "GSENDER_CHART";
const streams = [
mockActiveStream("s1", SENDER_CHART),
mockActiveStream("s2", SENDER_CHART),
mockScheduledStream("s3", SENDER_CHART),
mockPausedStream("s4", SENDER_CHART),
mockCompletedStream("s5", SENDER_CHART),
mockCanceledStream("s6", SENDER_CHART),
];
setupSenderHandler(streams, SENDER_CHART);
streams.forEach((s) => setupStreamHistoryHandler(s.id, []));

render(
<SenderDashboard
senderAddress={SENDER_CHART}
onEditStartTime={onEditStartTime}
/>
);

await waitFor(() =>
expect(screen.getByText("Streams by Status")).toBeInTheDocument()
);

// Verify chart is rendered (look for axis labels)
expect(screen.getByText("Scheduled")).toBeInTheDocument();
expect(screen.getByText("Active")).toBeInTheDocument();
expect(screen.getByText("Paused")).toBeInTheDocument();
expect(screen.getByText("Completed")).toBeInTheDocument();
expect(screen.getByText("Canceled")).toBeInTheDocument();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Look for ResizeObserver / dimension shims in test setup.
fd -t f -e ts -e tsx 'setup' frontend --exec rg -n -i 'resizeobserver|getBoundingClientRect|ResponsiveContainer' {}
rg -n -i 'resizeobserver' frontend/src 2>/dev/null

Repository: ritik4ever/stellar-stream

Length of output: 163


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Relevant test/setup files ==\n'
git ls-files frontend | rg '(^|/)(setup|test|tests|vitest|jest|jest\.setup|setupTests|config|SenderDashboard)\.(ts|tsx|js|jsx|mjs|cjs)$|(^|/)test'
printf '\n== Search for jsdom/layout shims ==\n'
rg -n -i 'ResizeObserver|getBoundingClientRect|matchMedia|offsetWidth|offsetHeight|clientWidth|clientHeight|ResponsiveContainer' frontend --glob '!**/node_modules/**'
printf '\n== Search for chart-related test helpers ==\n'
rg -n -i 'ResponsiveContainer|recharts|chart' frontend/src frontend/test frontend --glob '!**/node_modules/**'

Repository: ritik4ever/stellar-stream

Length of output: 50382


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Relevant test/setup files ==\n'
git ls-files frontend | rg '(^|/)(setup|test|tests|vitest|jest|jest\.setup|setupTests|config|SenderDashboard)\.(ts|tsx|js|jsx|mjs|cjs)$|(^|/)test'
printf '\n== Search for jsdom/layout shims ==\n'
rg -n -i 'ResizeObserver|getBoundingClientRect|matchMedia|offsetWidth|offsetHeight|clientWidth|clientHeight|ResponsiveContainer' frontend --glob '!**/node_modules/**'
printf '\n== Search for chart-related test helpers ==\n'
rg -n -i 'ResponsiveContainer|recharts|chart' frontend/src frontend/test frontend --glob '!**/node_modules/**'

Repository: ritik4ever/stellar-stream

Length of output: 50382


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Relevant test/setup files ==\n'
git ls-files frontend | rg '(^|/)(setup|test|tests|vitest|jest|jest\.setup|setupTests|config|SenderDashboard)\.(ts|tsx|js|jsx|mjs|cjs)$|(^|/)test'

printf '\n== Search for jsdom/layout shims ==\n'
rg -n -i 'ResizeObserver|getBoundingClientRect|matchMedia|offsetWidth|offsetHeight|clientWidth|clientHeight|ResponsiveContainer' frontend --glob '!**/node_modules/**'

printf '\n== Search for chart-related test helpers ==\n'
rg -n -i 'ResponsiveContainer|recharts|chart' frontend/src frontend/test frontend --glob '!**/node_modules/**'

Repository: ritik4ever/stellar-stream

Length of output: 50382


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== frontend/src/setupTests.ts =='
wc -l frontend/src/setupTests.ts
cat -n frontend/src/setupTests.ts | sed -n '1,220p'

echo '== frontend/src/setupTests.js =='
wc -l frontend/src/setupTests.js
cat -n frontend/src/setupTests.js | sed -n '1,220p'

echo '== frontend/src/components/SenderDashboard.tsx (chart area) =='
cat -n frontend/src/components/SenderDashboard.tsx | sed -n '430,520p'

echo '== frontend/src/components/StreamMetricsChart.test.tsx =='
cat -n frontend/src/components/StreamMetricsChart.test.tsx | sed -n '1,120p'

Repository: ritik4ever/stellar-stream

Length of output: 7437


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== frontend/src/setupTests.ts =='
wc -l frontend/src/setupTests.ts
cat -n frontend/src/setupTests.ts | sed -n '1,220p'

echo '== frontend/src/setupTests.js =='
wc -l frontend/src/setupTests.js
cat -n frontend/src/setupTests.js | sed -n '1,220p'

echo '== frontend/src/components/SenderDashboard.tsx (chart area) =='
cat -n frontend/src/components/SenderDashboard.tsx | sed -n '430,520p'

echo '== frontend/src/components/StreamMetricsChart.test.tsx =='
cat -n frontend/src/components/StreamMetricsChart.test.tsx | sed -n '1,120p'

Repository: ritik4ever/stellar-stream

Length of output: 7437


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== frontend/src/setupTests.ts =='
wc -l frontend/src/setupTests.ts
cat -n frontend/src/setupTests.ts | sed -n '1,220p'

echo '== frontend/src/setupTests.js =='
wc -l frontend/src/setupTests.js
cat -n frontend/src/setupTests.js | sed -n '1,220p'

echo '== frontend/src/components/SenderDashboard.tsx (chart area) =='
cat -n frontend/src/components/SenderDashboard.tsx | sed -n '430,520p'

echo '== frontend/src/components/StreamMetricsChart.test.tsx =='
cat -n frontend/src/components/StreamMetricsChart.test.tsx | sed -n '1,120p'

Repository: ritik4ever/stellar-stream

Length of output: 7437


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== frontend/src/setupTests.ts =='
wc -l frontend/src/setupTests.ts
cat -n frontend/src/setupTests.ts | sed -n '1,220p'

echo '== frontend/src/setupTests.js =='
wc -l frontend/src/setupTests.js
cat -n frontend/src/setupTests.js | sed -n '1,220p'

echo '== frontend/src/components/SenderDashboard.tsx (chart area) =='
cat -n frontend/src/components/SenderDashboard.tsx | sed -n '430,520p'

echo '== frontend/src/components/StreamMetricsChart.test.tsx =='
cat -n frontend/src/components/StreamMetricsChart.test.tsx | sed -n '1,120p'

Repository: ritik4ever/stellar-stream

Length of output: 7437


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== SenderDashboard.test.tsx around chart test =='
cat -n frontend/src/components/SenderDashboard.test.tsx | sed -n '1,320p'

echo '== Search for recharts mocks in SenderDashboard tests/setup =='
rg -n 'vi\.mock\("recharts"|mock\("recharts"|ResponsiveContainer|XAxis|YAxis|BarChart' frontend/src/components/SenderDashboard.test.tsx frontend/src/setupTests.ts frontend/src/setupTests.js frontend/src --glob '!**/node_modules/**'

Repository: ritik4ever/stellar-stream

Length of output: 13925


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== SenderDashboard.test.tsx around chart test =='
cat -n frontend/src/components/SenderDashboard.test.tsx | sed -n '1,320p'

echo '== Search for recharts mocks in SenderDashboard tests/setup =='
rg -n 'vi\.mock\("recharts"|mock\("recharts"|ResponsiveContainer|XAxis|YAxis|BarChart' frontend/src/components/SenderDashboard.test.tsx frontend/src/setupTests.ts frontend/src/setupTests.js frontend/src --glob '!**/node_modules/**'

Repository: ritik4ever/stellar-stream

Length of output: 13925


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== SenderDashboard.test.tsx around chart test =='
cat -n frontend/src/components/SenderDashboard.test.tsx | sed -n '1,320p'

echo '== Search for recharts mocks in SenderDashboard tests/setup =='
rg -n 'vi\.mock\("recharts"|mock\("recharts"|ResponsiveContainer|XAxis|YAxis|BarChart' frontend/src/components/SenderDashboard.test.tsx frontend/src/setupTests.ts frontend/src/setupTests.js frontend/src --glob '!**/node_modules/**'

Repository: ritik4ever/stellar-stream

Length of output: 13925


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== SenderDashboard.test.tsx around chart test =='
cat -n frontend/src/components/SenderDashboard.test.tsx | sed -n '1,320p'

echo '== Search for recharts mocks in SenderDashboard tests/setup =='
rg -n 'vi\.mock\("recharts"|mock\("recharts"|ResponsiveContainer|XAxis|YAxis|BarChart' frontend/src/components/SenderDashboard.test.tsx frontend/src/setupTests.ts frontend/src/setupTests.js frontend/src --glob '!**/node_modules/**'

Repository: ritik4ever/stellar-stream

Length of output: 13925


frontend/src/components/SenderDashboard.test.tsx:229-258 — Mock Recharts or avoid chart-label assertions here. frontend/src/setupTests.ts has no ResizeObserver/layout shim, and this test doesn’t mock recharts like StreamMetricsChart.test.tsx does. That makes the getByText("Scheduled" | "Active" | …) checks depend on jsdom chart rendering; assert the status values from the data instead, or stub the chart components locally.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/SenderDashboard.test.tsx` around lines 229 - 259, The
chart test in SenderDashboard.test.tsx is relying on jsdom/Recharts layout
rendering and text labels, which is brittle without the same mocking used
elsewhere. Update the "renders bar chart showing streams by status" test to
avoid asserting axis labels from the rendered chart, and instead either mock the
chart/recharts components locally or verify the underlying status data passed
into SenderDashboard/Streams by Status behavior using existing helpers like
setupSenderHandler and mock*Stream fixtures.

Comment on lines +94 to +97
case "created":
return `Stream created${amount ? ` (${amount} ${assetCode})` : ""}`;
case "claimed":
return `Claimed ${amount} ${assetCode}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Avoid rendering undefined in activity labels.

amount and assetCode are optional, but the claimed label always interpolates both; missing data renders Claimed undefined undefined.

Proposed fix
     case "created":
-      return `Stream created${amount ? ` (${amount} ${assetCode})` : ""}`;
+      return amount != null && assetCode
+        ? `Stream created (${amount} ${assetCode})`
+        : "Stream created";
     case "claimed":
-      return `Claimed ${amount} ${assetCode}`;
+      return amount != null && assetCode
+        ? `Claimed ${amount} ${assetCode}`
+        : "Claimed";
📝 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
case "created":
return `Stream created${amount ? ` (${amount} ${assetCode})` : ""}`;
case "claimed":
return `Claimed ${amount} ${assetCode}`;
case "created":
return amount != null && assetCode
? `Stream created (${amount} ${assetCode})`
: "Stream created";
case "claimed":
return amount != null && assetCode
? `Claimed ${amount} ${assetCode}`
: "Claimed";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/SenderDashboard.tsx` around lines 94 - 97, The
claimed activity label in SenderDashboard’s label formatting can render
undefined values because it always interpolates amount and assetCode even when
they are optional. Update the logic in the label switch for the claimed case to
conditionally include these fields only when present, matching the pattern
already used in the created case, so the activity text never shows undefined.

Comment on lines +275 to +284
try {
await cancelStream(id);
const result = await listStreams({ sender: senderAddress! });
setStreams(result.data);

// Refresh events
const recentEvents = await getSenderEvents(senderAddress!);
setEvents(recentEvents);
} catch (err) {
alert(err instanceof Error ? err.message : "Failed to cancel stream");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Refresh state after successful cancellations, including partial bulk success.

Both handlers put the mutation and refresh in the same try. If refresh fails after a cancel succeeds, the user sees a cancel failure. In bulk mode, Promise.all rejects on the first failure even after earlier streams may already be canceled, leaving stale rows and selected IDs.

Proposed bulk-cancel pattern
-      await Promise.all(selectedIds.map((id) => cancelStream(id)));
+      const results = await Promise.allSettled(
+        selectedIds.map((id) => cancelStream(id))
+      );
       const result = await listStreams({ sender: senderAddress! });
       setStreams(result.data);
       setSelectedStreamForBulkCancel(new Set());
 
       // Refresh events
       const recentEvents = await getSenderEvents(senderAddress!);
       setEvents(recentEvents);
+      const failedCount = results.filter((r) => r.status === "rejected").length;
+      if (failedCount > 0) {
+        alert(`Failed to cancel ${failedCount} stream(s).`);
+      }

Also applies to: 303-313

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/SenderDashboard.tsx` around lines 275 - 284, The
cancel handlers in SenderDashboard are treating refresh failures as cancel
failures because the mutation and post-success state reloads are inside the same
try; move the listStreams/getSenderEvents refresh logic out of the mutation
error path so a successful cancel still updates state even if refresh fails. For
the bulk flow in the same component, update the Promise.all-based cancellation
in the bulk handler to tolerate partial success, then refresh streams/events and
clear any selected IDs based on the completed cancels rather than aborting on
the first rejection.

Comment on lines +493 to +526
{/* Quick Action Buttons */}
{stats.activeStreams.length > 0 && (
<section
className="recipient-dashboard-section"
style={{ marginTop: "2rem" }}
>
<div
style={{
display: "flex",
gap: "1rem",
alignItems: "center",
}}
>
<h3 className="recipient-dashboard-section-title">
Quick Actions
</h3>
{selectedStreamForBulkCancel.size > 0 && (
<button
type="button"
className="btn-primary"
style={{
backgroundColor: "#ef4444",
padding: "0.5rem 1rem",
fontSize: "0.875rem",
marginLeft: "auto",
}}
onClick={handleBulkCancel}
>
Bulk Cancel ({selectedStreamForBulkCancel.size})
</button>
)}
</div>
</section>
)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use one cancellable stream list for quick actions and selection.

Quick Actions only renders when there is at least one active stream, but the table supports scheduled rows too; with only scheduled streams, users can select rows but never see Bulk Cancel. Paused streams are also counted in stats but are not rendered in either table.

Proposed direction
+  const cancellableStreams = [
+    ...stats.scheduledStreams,
+    ...stats.activeStreams,
+    ...stats.pausedStreams,
+  ];
-            {stats.activeStreams.length > 0 && (
+            {cancellableStreams.length > 0 && (
-                              (stats.activeStreams.length +
-                                stats.scheduledStreams.length)
+                              cancellableStreams.length
...
-                                  [...stats.activeStreams, ...stats.scheduledStreams].map(
-                                    (s) => s.id
-                                  )
+                                  cancellableStreams.map((s) => s.id)
...
-                      {[...stats.scheduledStreams, ...stats.activeStreams].map(
+                      {cancellableStreams.map(

Also applies to: 578-624

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/SenderDashboard.tsx` around lines 493 - 526, Quick
Actions is gated on stats.activeStreams, but Bulk Cancel should be driven by the
same cancellable rows the table can actually select. Update SenderDashboard to
use a single cancellable stream list for both the quick-actions section and
selection state, so scheduled rows can still surface Bulk Cancel when selected.
Review the Quick Actions block and the row selection logic around
selectedStreamForBulkCancel, handleBulkCancel, and the stream table rendering to
ensure paused/scheduled streams are handled consistently.

Comment on lines +345 to +370
export async function getSenderEvents(senderAddress: string, limit: number = 10): Promise<StreamEvent[]> {
try {
// First get all streams for the sender
const streamsResult = await listStreams({ sender: senderAddress });
const streams = streamsResult.data;

if (streams.length === 0) {
return [];
}

// Fetch events from each stream
const allEvents: StreamEvent[] = [];
const eventPromises = streams.map((stream) =>
getStreamHistory(stream.id)
.then((events) => allEvents.push(...events))
.catch(() => {
// Silent fail on individual stream event fetch
})
);

await Promise.all(eventPromises);

// Sort by timestamp descending (most recent first) and limit
return allEvents
.sort((a, b) => b.timestamp - a.timestamp)
.slice(0, limit);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Fetch sender activity from the sender events endpoint.

The linked requirement calls for /api/events?sender=<address> as the source for the last 10 activity items. This implementation fans out through listStreams and per-stream history, so it only covers the current streams page and adds N+1 requests on every dashboard refresh.

Proposed direction
 export async function getSenderEvents(senderAddress: string, limit: number = 10): Promise<StreamEvent[]> {
   try {
-    // First get all streams for the sender
-    const streamsResult = await listStreams({ sender: senderAddress });
-    const streams = streamsResult.data;
-
-    if (streams.length === 0) {
-      return [];
-    }
-
-    // Fetch events from each stream
-    const allEvents: StreamEvent[] = [];
-    const eventPromises = streams.map((stream) =>
-      getStreamHistory(stream.id)
-        .then((events) => allEvents.push(...events))
-        .catch(() => {
-          // Silent fail on individual stream event fetch
-        })
-    );
-
-    await Promise.all(eventPromises);
-
-    // Sort by timestamp descending (most recent first) and limit
-    return allEvents
+    const params = new URLSearchParams({
+      sender: senderAddress,
+      limit: String(limit),
+    });
+    const response = await fetch(`${API_BASE}/events?${params.toString()}`);
+    const body = await parseResponse<{ data: StreamEvent[] }>(response);
+    return body.data
       .sort((a, b) => b.timestamp - a.timestamp)
       .slice(0, limit);
   } catch {
     return [];
   }
 }
📝 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
export async function getSenderEvents(senderAddress: string, limit: number = 10): Promise<StreamEvent[]> {
try {
// First get all streams for the sender
const streamsResult = await listStreams({ sender: senderAddress });
const streams = streamsResult.data;
if (streams.length === 0) {
return [];
}
// Fetch events from each stream
const allEvents: StreamEvent[] = [];
const eventPromises = streams.map((stream) =>
getStreamHistory(stream.id)
.then((events) => allEvents.push(...events))
.catch(() => {
// Silent fail on individual stream event fetch
})
);
await Promise.all(eventPromises);
// Sort by timestamp descending (most recent first) and limit
return allEvents
.sort((a, b) => b.timestamp - a.timestamp)
.slice(0, limit);
export async function getSenderEvents(senderAddress: string, limit: number = 10): Promise<StreamEvent[]> {
try {
const params = new URLSearchParams({
sender: senderAddress,
limit: String(limit),
});
const response = await fetch(`${API_BASE}/events?${params.toString()}`);
const body = await parseResponse<{ data: StreamEvent[] }>(response);
return body.data
.sort((a, b) => b.timestamp - a.timestamp)
.slice(0, limit);
} catch {
return [];
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/services/api.ts` around lines 345 - 370, The getSenderEvents
helper is using listStreams plus per-stream getStreamHistory fan-out instead of
the sender events endpoint required by the review. Update getSenderEvents to
fetch directly from /api/events with the sender query parameter, keep the
existing limit handling for the last 10 items, and remove the N+1 stream/history
aggregation logic so the function returns the sender’s activity from the single
endpoint.

### 3. Comprehensive Test Suite (`SenderDashboard.test.tsx`)
**Location:** `frontend/src/components/SenderDashboard.test.tsx`

#### Test Coverage: 15+ Tests

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Test-count documentation is inconsistent with PR artifacts.

This file states “15+ tests”, while the PR summary and other added docs state 14. Please align to one verified count to avoid acceptance/reporting drift.

Also applies to: 168-168, 207-207

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@SENDER_DASHBOARD_IMPLEMENTATION.md` at line 95, The test-count documentation
is inconsistent across the added docs, so align the verified number everywhere
it appears. Update the count in SENDER_DASHBOARD_IMPLEMENTATION.md and the other
referenced sections to match the actual PR artifact count, using the same
wording consistently so the summary, implementation notes, and test coverage
references all agree.

Comment on lines +5 to +83
```
┌─────────────────────────────────────────────────────────────┐
│ SenderDashboard │
│ │
│ Header: "Sender Dashboard" [Create Stream Button] ──┐ │
│ Subtitle: "View your outgoing streams..." │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ STATS CARDS SECTION │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ │ │
│ │ │ Streams │ │ Total │ │ Active │ │Complet │ │ │
│ │ │ Created │ │ Amount │ │ Streams │ │ /Cancel│ │ │
│ │ │ 5 │ │ 3000 │ │ 3 │ │ 2 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ASSET BREAKDOWN CARDS │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ │ │
│ │ │Total USDC│ │ Total XLM│ │ │
│ │ │ 2500 │ │ 500 │ │ │
│ │ └──────────┘ └──────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ STREAMS BY STATUS - BAR CHART │ │
│ │ │ │
│ │ ▓ │ │
│ │ ▓ ▓ ▓ │ │
│ │ ┌────┼──────┼───┼──┬───┐ │ │
│ │ │Sch│Active│Pau│Com│Can│ │ │
│ │ └────┴──────┴───┴───┴───┘ │ │
│ │ 1 2 1 1 1 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ QUICK ACTIONS │ │
│ │ │ │
│ │ [Bulk Cancel (2)] ◄─ When streams selected │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ RECENT ACTIVITY │ │
│ │ │ │
│ │ • Stream paused Nov 24, 10:55 │ │
│ │ Stream: str...m1 │ │
│ │ │ │
│ │ • Stream resumed Nov 23, 09:30 │ │
│ │ Stream: str...m1 │ │
│ │ │ │
│ │ • Claimed 500 USDC Nov 22, 14:20 │ │
│ │ Stream: str...m1 │ │
│ │ │ │
│ │ • Stream created (1000 USDC) Nov 21, 11:00 │ │
│ │ Stream: str...m1 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ACTIVE & SCHEDULED STREAMS TABLE │ │
│ │ │ │
│ │ ☐ To Asset Total Status Progress Actions │ │
│ │ ☑ Gre... USDC 1000 Active 50% ✏️ Cancel │ │
│ │ ☐ Gre... USDC 500 Schedul 0% Edit Cancel │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ HISTORY - COMPLETED STREAMS TABLE │ │
│ │ │ │
│ │ To Asset Total Status │ │
│ │ Gre... USDC 1000 Completed │ │
│ │ Gre... USDC 500 Canceled │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add language identifiers to fenced code blocks (markdownlint MD040).

Please specify fence languages for the diagram/tree/flow blocks.

Suggested patch
-```
+```text
 ┌─────────────────────────────────────────────────────────────┐
 ...
-```
+```

-```
+```text
 SenderDashboard
 ...
-```
+```

-```
+```text
 1. Component Mount
 ...
-```
+```

Also applies to: 87-127, 131-153

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

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

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@SENDER_DASHBOARD_STRUCTURE.md` around lines 5 - 83, The fenced
diagram/tree/flow blocks in this markdown file are missing language identifiers,
which triggers MD040. Update each affected fenced block in
SENDER_DASHBOARD_STRUCTURE.md to use a text/diagram-style language tag
consistently, and verify the blocks around the SenderDashboard layout, the tree
view, and the flow steps all have explicit fence languages while keeping the
content unchanged.

Source: Linters/SAST tools

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.

Add SenderDashboard with stream creation history and analytics

1 participant