Skip to content

Stellar transaction history explorer integration#368

Merged
Luluameh merged 2 commits into
LightForgeHub:mainfrom
LawalRahman:Stellar-Transaction-History-Explorer-Integration
Jun 26, 2026
Merged

Stellar transaction history explorer integration#368
Luluameh merged 2 commits into
LightForgeHub:mainfrom
LawalRahman:Stellar-Transaction-History-Explorer-Integration

Conversation

@LawalRahman

@LawalRahman LawalRahman commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Closes #300
Closes #299
Closes #304
Closes #305

Summary by CodeRabbit

  • New Features
    • Added an expert browsing experience with search, category filters, and profile cards.
    • Added expert profile pages with booking entry points and richer profile details.
    • Added a session funding wizard, video-call interface, and transaction history explorer views.
  • Bug Fixes
    • Improved empty and not-found states for expert, session, and transaction views.
  • Documentation
    • Added quick-start and implementation guides with demo links, usage notes, and testing steps.
  • Chores
    • Expanded mock data and shared data shapes to support the new experience.

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds shared data types and mock records, expert browsing and profile pages, a 3-step session funding modal, a video-call session page and demo, a transaction history explorer, and three documentation guides for the implemented flows.

Changes

SkillSphere feature rollout

Layer / File(s) Summary
Shared data and explorer helpers
utils/types/types.ts, utils/data/mock-data.ts, src/utils/explorer.ts
Session, Expert, Review, and Transaction shapes are expanded, mock sessions/experts/transactions are updated, and explorer URL, hash, clipboard, and network helpers are added.
Expert browse and profile UI
src/app/explore-experts/page.tsx, src/components/marketplace/ExpertCard.tsx, src/components/profile/ExpertDetails.tsx, src/app/explore-experts/[id]/page.tsx
The experts listing now filters by search text and category, renders expert cards, and the dynamic profile route loads expert data, handles missing states, and wires booking into marketplace navigation.
Funding wizard and demo
src/components/marketplace/FundSessionModal.tsx, src/app/ui-demo/fund-session/page.tsx
The session funding modal runs through duration, confirmation, and success states, and the demo page opens it with example props and usage text.
Video-call session flow
src/components/session/VideoCall.tsx, src/app/session/[id]/page.tsx, src/app/ui-demo/video-call/page.tsx
The video-call component renders picture-in-picture and full-screen layouts, the session route handles not-found and end-call states with a delayed dashboard redirect, and the demo page showcases both modes.
Transaction history and demo
src/components/dashboard/SessionHistory.tsx, src/app/ui-demo/transactions/page.tsx
The session history component links rows to Stellar Explorer transactions with copy-to-clipboard and network badges, and the demo page presents the explorer helpers alongside the mounted history view.
Release docs and quick-start guides
FEATURES_IMPLEMENTATION.md, IMPLEMENTATION_COMPLETE.md, QUICK_START.md
The documentation files describe the completed flows, demo URLs, data shapes, integration notes, and next steps.

Sequence Diagram(s)

sequenceDiagram
  participant SessionPage
  participant VideoCall
  participant NextRouter
  SessionPage->>VideoCall: render with session and expert props
  VideoCall->>SessionPage: onEndCall()
  SessionPage->>SessionPage: set isSessionEnded
  SessionPage->>NextRouter: push /dashboard?sessionId=...&status=completed
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

  • Issue 299 — The new expert profile route and ExpertDetails UI match the expert profile routing and display changes described for that issue.

Possibly related PRs

Poem

I hopped through profiles, soft and bright,
Then found the session stars at night.
With hashes, calls, and modal cheer,
SkillSphere blooms from gear to gear.
A carrot toast for code done right!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches one implemented feature in the PR, but it is narrower than the full multi-feature changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

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

🔧 Biome (2.5.0)
utils/data/mock-data.ts

File contains syntax errors that prevent linting: Line 414: Expected a property, a shorthand property, a getter, a setter, or a method but instead found ']'.

🔧 ESLint

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

ESLint install failed: one or more packages not found in the registry.


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

🧹 Nitpick comments (5)
IMPLEMENTATION_COMPLETE.md (1)

12-12: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Soften "Production-Ready" to "Demo-Ready" or "UI-Complete".

The funding flow and video call lack actual wallet/WebRTC integration. "Production-Ready" overstates readiness for features that are UI-complete with mocked backends. Consider "UI Complete — Ready for Integration" to set accurate expectations.

🤖 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 `@IMPLEMENTATION_COMPLETE.md` at line 12, Update the status wording in
IMPLEMENTATION_COMPLETE.md so it no longer says “Production-Ready”; change it to
a more accurate label like “Demo-Ready,” “UI-Complete,” or “UI Complete — Ready
for Integration” to reflect that the funding flow and video call are still
mocked and not fully integrated. Keep the change limited to the status text
associated with the completion summary.
src/utils/explorer.ts (1)

53-57: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Unhandled clipboard promise.

navigator.clipboard.writeText returns a Promise that is fire-and-forget here; a rejection (e.g. denied permission, non-secure context) surfaces as an unhandled rejection and callers can't react. Consider returning the promise or catching the error.

♻️ Optional
-export function copyHashToClipboard(hash: string): void {
-  if (typeof window !== 'undefined' && navigator.clipboard) {
-    navigator.clipboard.writeText(hash);
-  }
-}
+export async function copyHashToClipboard(hash: string): Promise<boolean> {
+  if (typeof window !== 'undefined' && navigator.clipboard) {
+    try {
+      await navigator.clipboard.writeText(hash);
+      return true;
+    } catch {
+      return false;
+    }
+  }
+  return false;
+}
🤖 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 `@src/utils/explorer.ts` around lines 53 - 57, The clipboard helper in
copyHashToClipboard currently ignores the Promise from
navigator.clipboard.writeText, which can create unhandled rejections and hides
failures from callers. Update copyHashToClipboard to either return the writeText
Promise or handle the rejection explicitly, while keeping the existing
window/navigator guard so callers can await or catch errors as needed.
src/components/dashboard/SessionHistory.tsx (1)

68-72: 📐 Maintainability & Code Quality | 🔵 Trivial

Use next/image or add an onError fallback for avatars.

The project extends next/core-web-vitals in .eslintrc.js, which implicitly enables the @next/next/no-img-element rule. Using a native <img> tag will trigger a lint warning and misses automatic image optimizations (format conversion, resizing, lazy loading). Additionally, the current implementation lacks an onError handler, leaving broken expertAvatar URLs without a graceful fallback.

import NextImage from "next/image";

// ...

<NextImage
  src={session.expertAvatar}
  alt={session.expertName}
  width={32}
  height={32}
  className="w-8 h-8 rounded-full object-cover"
  onError={(e) => {
    (e.currentTarget.src = "/avatars/default.png");
  }}
/>
🤖 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 `@src/components/dashboard/SessionHistory.tsx` around lines 68 - 72, The
SessionHistory avatar rendering currently uses a native img element, which
conflicts with the next/core-web-vitals lint rule and lacks image optimizations.
Update the avatar markup in SessionHistory to use next/image (or otherwise add a
robust fallback), keeping the existing session.expertAvatar/session.expertName
usage, and include an onError fallback so broken avatar URLs fall back to a
default image instead of rendering a broken image.
src/app/ui-demo/video-call/page.tsx (1)

10-12: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

handleEndCall is unused.

The onEndCall prop is wired inline as () => setIsFullScreen(false) (Line 23), so this handleEndCall (and its alert) is dead code. Either remove it or pass it to onEndCall.

🤖 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 `@src/app/ui-demo/video-call/page.tsx` around lines 10 - 12, The handleEndCall
function in the video call page is dead code because onEndCall is already wired
inline, so either remove handleEndCall and its alert or reuse it by passing it
to the onEndCall prop on the VideoCall component. Use the handleEndCall symbol
to locate the unused callback and ensure the onEndCall wiring is consistent.
src/app/session/\[id\]/page.tsx (1)

131-147: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Prefer next/image and guard the optional seeker avatar.

Raw <img> tags forgo Next.js image optimization. Additionally, session.seekerAvatar (Line 144) appears optional; an undefined src renders a broken image. Consider next/image with a fallback avatar.

🤖 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 `@src/app/session/`\[id\]/page.tsx around lines 131 - 147, The session page
currently uses raw image tags in the Expert/Seeker cards, and the seeker avatar
source can be undefined. Update the image rendering in the session details
component to use Next.js Image handling instead of plain img, and add a fallback
avatar when session.seekerAvatar is missing so the Seeker card never renders a
broken image. Use the existing expert/seeker display block in page.tsx to locate
the change.
🤖 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 `@FEATURES_IMPLEMENTATION.md`:
- Around line 418-428: The Stellar SDK example uses an invalid package name, so
update the integration snippet in the Stellar SDK section to import from the
correct package. Replace the `@stellar/js-sdk` reference in the `explorer.ts`
example with `@stellar/stellar-sdk` (or `stellar-sdk` if keeping compatibility
with older versions), and make sure the `Keypair`, `TransactionBuilder`, and
`Server` import line reflects the valid package name.
- Around line 62-66: Update the duration selection documentation to remove any
“USD ↔ XLM conversion” wording and describe the behavior accurately: the modal’s
amount calculation in the duration selection flow is just a duration-based price
formula rendered as XLM, not a real currency conversion. Use the existing “Step
1: Duration Selection” section to replace the misleading claim with wording that
matches the actual `amount = (hourlyRate * duration) / 60` and
`${amount.toFixed(2)} XLM` behavior.

In `@QUICK_START.md`:
- Around line 308-318: Update the reference table in QUICK_START.md so the
TypeScript interfaces and mock data entries point to the actual paths used
elsewhere in the project. Specifically, change the entries for the types and
mock-data files to match the real locations referenced by the existing
`@/utils/`... imports, and keep the rest of the table unchanged.

In `@src/app/explore-experts/`\[id\]/page.tsx:
- Around line 21-33: The explore-experts page effect is leaving stale derived
state behind when params.id changes, so a previous "Expert not found" error can
block a later valid profile from rendering. In the React.useEffect in page.tsx,
reset the page state at the start of each lookup by clearing error and setting
loading true before checking mockExperts, then update expert/error and loading
based on the current params.id result so the valid expert can render after
client-side navigation.
- Around line 1-33: Convert ExpertProfilePage in page.tsx from a client
component to an async Server Component so params can be used directly and
missing experts can call notFound() immediately instead of setting local
error/loading state. Remove use client, useState, and useEffect from
ExpertProfilePage, look up the expert from mockExperts on the server, and return
notFound() when no match is found. Keep ExpertDetails as the client-rendered
child, and if handleBookClick depends on sessionStorage or useRouter, extract
that interaction into a separate client component.

In `@src/app/explore-experts/page.tsx`:
- Around line 44-52: The search input in the explore experts page lacks an
accessible name and is only identified by its placeholder. Update the main
filter control in the relevant input element to include a proper <label> or an
aria-label so screen readers can reliably identify it, while keeping the
existing search behavior and visual layout intact.
- Around line 14-18: Update the search logic in filteredExperts so skill-only
queries are matched as well; the current predicate in the explore-experts page
only checks expert.name and expert.category. Extend the matchesSearch condition
to also search expert.skills (for example by checking each skill against the
lowercased searchTerm) and keep the existing selectedCategory filter behavior
unchanged. Use the filteredExperts block and the search UI text near the
skill-search promise to verify the behavior stays consistent.

In `@src/app/session/`\[id\]/page.tsx:
- Around line 8-21: `SessionPage` is treating `params` as a synchronous object,
but in Next.js 15 dynamic route params are async and must be unwrapped first.
Update the `SessionPageProps` shape and the `SessionPage` component to use
`React.use()` on `params`, then read `id` from the resolved value before using
it for `mockSessions.find` and the redirect logic so all `params.id` access uses
the unwrapped value.

In `@src/app/ui-demo/video-call/page.tsx`:
- Line 113: The JSX text in the UI demo page uses raw double quotes and will
trip react/no-unescaped-entities. Update the paragraph in the page component so
the quoted phrase uses escaped entities or curly quotes, keeping the text in the
same JSX element and preserving the meaning.

In `@src/components/marketplace/ExpertCard.tsx`:
- Around line 14-18: The ExpertCard CTA is currently rendered as a nested
interactive element inside the Link anchor, which creates invalid interactive
nesting. Update ExpertCard so the call-to-action around the expert card is not a
button inside the Link; either change the CTA in the card footer to a
non-interactive styled span/div or move the Link from the whole card onto the
CTA itself. Use the existing ExpertCard and Link structure to locate the card
markup and the CTA block near the bottom of the component.

In `@src/components/marketplace/FundSessionModal.tsx`:
- Around line 23-25: The modal state in FundSessionModal is not being reset when
isOpen becomes false, so currentStep, duration, and the deferred timeout can
leak into the next open. Update the component’s close/unmount handling to reset
the local state back to the initial duration step and clear any pending
setTimeout callback when the modal closes; use the existing state setters and
the timeout-related logic around the open/close flow in FundSessionModal to
locate the fix.
- Around line 27-29: The session ID in FundSessionModal is being regenerated on
each render, which can cause handleConfirm to use a different value than the
success UI and onSuccess handoff. Make sessionId stable for the entire funding
flow by initializing it once and preserving it across renders, then ensure
handleConfirm and the success screen both read the same stored value when
passing data through onSuccess and rendering the confirmation state.

In `@src/components/profile/ExpertDetails.tsx`:
- Around line 7-10: The ExpertDetails CTA is rendered even when onBookClick is
missing, leaving a non-functional “Book Session” control. Update ExpertDetails
and its button render path so the CTA only appears or is enabled when
onBookClick is provided, using the existing ExpertDetailsProps and onBookClick
props to gate the button behavior consistently across the affected render block.

In `@src/utils/explorer.ts`:
- Around line 8-11: The testnet explorer link in EXPLORER_URLS is using the
wrong Stellar.Expert host/path and will produce broken transaction URLs. Update
the testnet entry in EXPLORER_URLS to use the verified
`stellar.expert/explorer/testnet/tx` format so any hash appended by the explorer
helper resolves correctly.

In `@utils/data/mock-data.ts`:
- Around line 416-466: The mock session-to-transaction links are mismatched: the
entries in mockTransactions use sessionId values that do not line up with the
session records sharing the same hash. Update the affected mockTransactions
objects so each transaction’s sessionId matches the corresponding session’s
transactionHash (including the entries for ids 2, 3, and 4), and verify the mock
data stays consistent with the session/transaction join logic used by the demo.
- Around line 408-416: The expert object in mock-data.ts is left unterminated,
causing the parse error before mockTransactions. In the mock expert entry with
id 4, close the object properly by adding the missing pastReviews field
(matching the other expert objects) and the closing brace before the array
terminator so the experts array is valid.

---

Nitpick comments:
In `@IMPLEMENTATION_COMPLETE.md`:
- Line 12: Update the status wording in IMPLEMENTATION_COMPLETE.md so it no
longer says “Production-Ready”; change it to a more accurate label like
“Demo-Ready,” “UI-Complete,” or “UI Complete — Ready for Integration” to reflect
that the funding flow and video call are still mocked and not fully integrated.
Keep the change limited to the status text associated with the completion
summary.

In `@src/app/session/`\[id\]/page.tsx:
- Around line 131-147: The session page currently uses raw image tags in the
Expert/Seeker cards, and the seeker avatar source can be undefined. Update the
image rendering in the session details component to use Next.js Image handling
instead of plain img, and add a fallback avatar when session.seekerAvatar is
missing so the Seeker card never renders a broken image. Use the existing
expert/seeker display block in page.tsx to locate the change.

In `@src/app/ui-demo/video-call/page.tsx`:
- Around line 10-12: The handleEndCall function in the video call page is dead
code because onEndCall is already wired inline, so either remove handleEndCall
and its alert or reuse it by passing it to the onEndCall prop on the VideoCall
component. Use the handleEndCall symbol to locate the unused callback and ensure
the onEndCall wiring is consistent.

In `@src/components/dashboard/SessionHistory.tsx`:
- Around line 68-72: The SessionHistory avatar rendering currently uses a native
img element, which conflicts with the next/core-web-vitals lint rule and lacks
image optimizations. Update the avatar markup in SessionHistory to use
next/image (or otherwise add a robust fallback), keeping the existing
session.expertAvatar/session.expertName usage, and include an onError fallback
so broken avatar URLs fall back to a default image instead of rendering a broken
image.

In `@src/utils/explorer.ts`:
- Around line 53-57: The clipboard helper in copyHashToClipboard currently
ignores the Promise from navigator.clipboard.writeText, which can create
unhandled rejections and hides failures from callers. Update copyHashToClipboard
to either return the writeText Promise or handle the rejection explicitly, while
keeping the existing window/navigator guard so callers can await or catch errors
as needed.
🪄 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: 8e932fc1-d4ed-49ba-9e69-27218f150bc3

📥 Commits

Reviewing files that changed from the base of the PR and between 0f3a673 and 67d80dd.

📒 Files selected for processing (17)
  • FEATURES_IMPLEMENTATION.md
  • IMPLEMENTATION_COMPLETE.md
  • QUICK_START.md
  • src/app/explore-experts/\[id\]/page.tsx
  • src/app/explore-experts/page.tsx
  • src/app/session/\[id\]/page.tsx
  • src/app/ui-demo/fund-session/page.tsx
  • src/app/ui-demo/transactions/page.tsx
  • src/app/ui-demo/video-call/page.tsx
  • src/components/dashboard/SessionHistory.tsx
  • src/components/marketplace/ExpertCard.tsx
  • src/components/marketplace/FundSessionModal.tsx
  • src/components/profile/ExpertDetails.tsx
  • src/components/session/VideoCall.tsx
  • src/utils/explorer.ts
  • utils/data/mock-data.ts
  • utils/types/types.ts

Comment on lines +62 to +66
**Step 1: Duration Selection**
- Preset options: 30, 60, 90 minutes
- Custom duration input (15-240 minutes)
- Real-time price calculation
- Price breakdown display

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

Remove or correct "USD ↔ XLM conversion" claims.

The modal calculates amount = (hourlyRate * duration) / 60 and renders ${amount.toFixed(2)} XLM. There is no currency conversion; the code simply labels a dollar amount as XLM. This documentation inaccurately suggests real conversion logic exists.

🤖 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 `@FEATURES_IMPLEMENTATION.md` around lines 62 - 66, Update the duration
selection documentation to remove any “USD ↔ XLM conversion” wording and
describe the behavior accurately: the modal’s amount calculation in the duration
selection flow is just a duration-based price formula rendered as XLM, not a
real currency conversion. Use the existing “Step 1: Duration Selection” section
to replace the misleading claim with wording that matches the actual `amount =
(hourlyRate * duration) / 60` and `${amount.toFixed(2)} XLM` behavior.

Comment on lines +418 to +428
### Stellar SDK Integration Points

The `explorer.ts` utility is ready for full Stellar.js integration:
```typescript
import { Keypair, TransactionBuilder, Server } from '@stellar/js-sdk';

// Use explorer URLs with real transaction hashes from ledger
const hash = result.hash; // From submitted transaction
const explorerUrl = formatExplorerUrl(hash, network);
```

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

Fix incorrect Stellar SDK package name.

@stellar/js-sdk is not a valid package. The correct import is from @stellar/stellar-sdk (or stellar-sdk for older versions).

🤖 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 `@FEATURES_IMPLEMENTATION.md` around lines 418 - 428, The Stellar SDK example
uses an invalid package name, so update the integration snippet in the Stellar
SDK section to import from the correct package. Replace the `@stellar/js-sdk`
reference in the `explorer.ts` example with `@stellar/stellar-sdk` (or
`stellar-sdk` if keeping compatibility with older versions), and make sure the
`Keypair`, `TransactionBuilder`, and `Server` import line reflects the valid
package name.

Comment thread QUICK_START.md
Comment on lines +308 to +318
| File | Purpose |
|------|---------|
| `src/utils/explorer.ts` | Stellar.Expert URL formatting |
| `src/types/types.ts` | TypeScript interfaces |
| `src/data/mock-data.ts` | Mock experts, sessions, transactions |
| `src/components/profile/ExpertDetails.tsx` | Expert profile display |
| `src/components/marketplace/ExpertCard.tsx` | Expert listing card |
| `src/components/marketplace/FundSessionModal.tsx` | Funding wizard |
| `src/components/session/VideoCall.tsx` | Video call interface |
| `src/components/dashboard/SessionHistory.tsx` | Transaction history |

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

Correct file paths in reference table.

Lines 311–312 list src/types/types.ts and src/data/mock-data.ts, but the actual paths are src/utils/types/types.ts and src/utils/data/mock-data.ts (confirmed by line 325's @/utils/data/mock-data import and the project structure elsewhere).

🤖 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 `@QUICK_START.md` around lines 308 - 318, Update the reference table in
QUICK_START.md so the TypeScript interfaces and mock data entries point to the
actual paths used elsewhere in the project. Specifically, change the entries for
the types and mock-data files to match the real locations referenced by the
existing `@/utils/`... imports, and keep the rest of the table unchanged.

Comment on lines +1 to +33
"use client";

import React, { useState } from 'react';
import { useRouter } from 'next/navigation';
import ExpertDetails from '@/components/profile/ExpertDetails';
import { mockExperts } from '@/utils/data/mock-data';
import { Expert } from '@/utils/types/types';

interface ExpertProfilePageProps {
params: {
id: string;
};
}

export default function ExpertProfilePage({ params }: ExpertProfilePageProps) {
const router = useRouter();
const [expert, setExpert] = useState<Expert | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);

React.useEffect(() => {
// Simulate fetching expert data
const expertId = params.id;
const foundExpert = mockExperts.find((e) => e.id === expertId);

if (foundExpert) {
setExpert(foundExpert);
setLoading(false);
} else {
setError('Expert not found');
setLoading(false);
}
}, [params.id]);

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:

cat package.json | grep -A 5 '"next"'

Repository: LightForgeHub/SkillSphere-Dapp

Length of output: 309


🏁 Script executed:

head -n 30 src/components/profile/ExpertDetails.tsx

Repository: LightForgeHub/SkillSphere-Dapp

Length of output: 1244


🏁 Script executed:

cat src/app/explore-experts/[id]/page.tsx

Repository: LightForgeHub/SkillSphere-Dapp

Length of output: 240


🏁 Script executed:

fd --full-path "page.tsx" src/app/explore-experts

Repository: LightForgeHub/SkillSphere-Dapp

Length of output: 241


🏁 Script executed:

cat -n "src/app/explore-experts/\[id\]/page.tsx"

Repository: LightForgeHub/SkillSphere-Dapp

Length of output: 3995


Refactor to Server Component to handle missing experts with notFound()

Converting this page to an async Server Component allows synchronous resolution of the params and immediate invocation of notFound() for missing experts. This ensures the route returns a proper 404 status and avoids rendering a 200 OK page with a user-facing “not found” fallback after hydration. Since mockExperts is a server-safe module and ExpertDetails is already a Client Component, this refactor is safe and aligns with Next.js 15 App Router conventions.

Suggested implementation
-"use client";
-
-import React, { useState } from 'react';
-import { useRouter } from 'next/navigation';
+import { notFound } from 'next/navigation';
 import ExpertDetails from '`@/components/profile/ExpertDetails`';
 import { mockExperts } from '`@/utils/data/mock-data`';
-
-interface ExpertProfilePageProps {
-  params: {
-    id: string;
-  };
+import { Expert } from '`@/utils/types/types`';
+
+interface ExpertProfilePageProps {
+  params: Promise<{ id: string }>;
 }
 
-export default function ExpertProfilePage({ params }: ExpertProfilePageProps) {
-  const router = useRouter();
-  const [expert, setExpert] = useState<Expert | null>(null);
-  const [loading, setLoading] = useState(true);
-  const [error, setError] = useState<string | null>(null);
-
-  React.useEffect(() => {
-    // Simulate fetching expert data
-    const expertId = params.id;
-    const foundExpert = mockExperts.find((e) => e.id === expertId);
-
-    if (foundExpert) {
-      setExpert(foundExpert);
-      setLoading(false);
-    } else {
-      setError('Expert not found');
-      setLoading(false);
-    }
-  }, [params.id]);
-
-  if (loading) {
-    // ... loading UI
+export default async function ExpertProfilePage({ params }: ExpertProfilePageProps) {
+  const { id } = await params;
+  const expert = mockExperts.find((e) => e.id === id);
+
+  if (!expert) {
+    notFound();
+  }
+
+  const handleBookClick = () => {
+    // Client-side logic can stay here or move to a child client component
+    'use client';
+    // Note: This handler needs to be moved to a client wrapper if it uses sessionStorage
+    sessionStorage.setItem('selectedExpertId', expert.id);
+    sessionStorage.setItem('selectedExpertName', expert.name);
+    // Navigate to marketplace
+  };
-  }
-
-  if (error || !expert) {
-    // ... error UI
-  }
-
-  const handleBookClick = () => {
-    sessionStorage.setItem('selectedExpertId', expert.id);
-    sessionStorage.setItem('selectedExpertName', expert.name);
-    router.push('/marketplace?action=book&expertId=' + expert.id);
-  };
-
   return (
-    // ... main wrapper
-    <ExpertDetails expert={expert} onBookClick={handleBookClick} />
+    <div className="min-h-screen w-full text-foreground">
+      <ExpertDetails expert={expert} />
+    </div>
   );
 }

You may need to extract the handleBookClick logic into a client child component within ExpertDetails since sessionStorage and router from next/navigation are client-side APIs.

🤖 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 `@src/app/explore-experts/`\[id\]/page.tsx around lines 1 - 33, Convert
ExpertProfilePage in page.tsx from a client component to an async Server
Component so params can be used directly and missing experts can call notFound()
immediately instead of setting local error/loading state. Remove use client,
useState, and useEffect from ExpertProfilePage, look up the expert from
mockExperts on the server, and return notFound() when no match is found. Keep
ExpertDetails as the client-rendered child, and if handleBookClick depends on
sessionStorage or useRouter, extract that interaction into a separate client
component.

Comment on lines +21 to +33
React.useEffect(() => {
// Simulate fetching expert data
const expertId = params.id;
const foundExpert = mockExperts.find((e) => e.id === expertId);

if (foundExpert) {
setExpert(foundExpert);
setLoading(false);
} else {
setError('Expert not found');
setLoading(false);
}
}, [params.id]);

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

Reset the derived page state before each params.id lookup.

If a user hits a missing profile first and then navigates to a valid one client-side, Line 30 leaves error latched, so the if (error || !expert) branch still wins and the valid expert never renders.

Suggested fix
  React.useEffect(() => {
-    // Simulate fetching expert data
+    setLoading(true);
+    setError(null);
+    setExpert(null);
+
+    // Simulate fetching expert data
     const expertId = params.id;
     const foundExpert = mockExperts.find((e) => e.id === expertId);

     if (foundExpert) {
       setExpert(foundExpert);
-      setLoading(false);
     } else {
       setError('Expert not found');
-      setLoading(false);
     }
+    setLoading(false);
   }, [params.id]);
📝 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
React.useEffect(() => {
// Simulate fetching expert data
const expertId = params.id;
const foundExpert = mockExperts.find((e) => e.id === expertId);
if (foundExpert) {
setExpert(foundExpert);
setLoading(false);
} else {
setError('Expert not found');
setLoading(false);
}
}, [params.id]);
React.useEffect(() => {
setLoading(true);
setError(null);
setExpert(null);
// Simulate fetching expert data
const expertId = params.id;
const foundExpert = mockExperts.find((e) => e.id === expertId);
if (foundExpert) {
setExpert(foundExpert);
} else {
setError('Expert not found');
}
setLoading(false);
}, [params.id]);
🤖 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 `@src/app/explore-experts/`\[id\]/page.tsx around lines 21 - 33, The
explore-experts page effect is leaving stale derived state behind when params.id
changes, so a previous "Expert not found" error can block a later valid profile
from rendering. In the React.useEffect in page.tsx, reset the page state at the
start of each lookup by clearing error and setting loading true before checking
mockExperts, then update expert/error and loading based on the current params.id
result so the valid expert can render after client-side navigation.

Comment on lines +27 to +29
const hourlyRate = parseInt(expertHourlyRate?.replace(/\D/g, '') || '50');
const amount = (hourlyRate * duration) / 60;
const sessionId = `SESSION_${Date.now()}`;

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

Make the session ID stable for the whole funding flow.

sessionId is regenerated on every render, but handleConfirm closes over an older value. That lets the success screen show a different ID from the one sent through onSuccess, which breaks the handoff to the session room.

Suggested fix
 import React, { useState } from 'react';
@@
   const [currentStep, setCurrentStep] = useState<Step>('duration');
   const [duration, setDuration] = useState<number>(60); // minutes
   const [isProcessing, setIsProcessing] = useState(false);
+  const [sessionId, setSessionId] = useState<string | null>(null);
@@
-  const sessionId = `SESSION_${Date.now()}`;
-
   const handleDurationChange = (value: number) => {
     setDuration(value);
   };
@@
   const handleConfirm = async () => {
     setIsProcessing(true);
     // Simulate wallet transaction
     await new Promise((resolve) => setTimeout(resolve, 2000));
+    const newSessionId = `SESSION_${Date.now()}`;
+    setSessionId(newSessionId);
     setIsProcessing(false);
     setCurrentStep('success');
@@
     setTimeout(() => {
-      onSuccess?.(sessionId);
+      onSuccess?.(newSessionId);
       onClose();
     }, 2000);
   };
@@
-                  <p>Session ID: <span className="font-mono font-semibold">{sessionId}</span></p>
+                  <p>Session ID: <span className="font-mono font-semibold">{sessionId}</span></p>

Also applies to: 35-46, 193-200

🤖 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 `@src/components/marketplace/FundSessionModal.tsx` around lines 27 - 29, The
session ID in FundSessionModal is being regenerated on each render, which can
cause handleConfirm to use a different value than the success UI and onSuccess
handoff. Make sessionId stable for the entire funding flow by initializing it
once and preserving it across renders, then ensure handleConfirm and the success
screen both read the same stored value when passing data through onSuccess and
rendering the confirmation state.

Comment on lines +7 to +10
interface ExpertDetailsProps {
expert: Expert;
onBookClick?: () => void;
}

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

Don't render a dead “Book Session” CTA when onBookClick is absent.

The prop is optional on Line 9, but the button is always enabled. Any consumer that omits the handler gets a control that does nothing.

Suggested fix
-            <button
-              onClick={onBookClick}
-              className="w-full md:w-auto px-8 py-3 bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700 rounded-lg font-semibold transition-all duration-300 transform hover:scale-105"
-            >
-              Book Session
-            </button>
+            {onBookClick && (
+              <button
+                type="button"
+                onClick={onBookClick}
+                className="w-full md:w-auto px-8 py-3 bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700 rounded-lg font-semibold transition-all duration-300 transform hover:scale-105"
+              >
+                Book Session
+              </button>
+            )}

Also applies to: 80-86

🤖 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 `@src/components/profile/ExpertDetails.tsx` around lines 7 - 10, The
ExpertDetails CTA is rendered even when onBookClick is missing, leaving a
non-functional “Book Session” control. Update ExpertDetails and its button
render path so the CTA only appears or is enabled when onBookClick is provided,
using the existing ExpertDetailsProps and onBookClick props to gate the button
behavior consistently across the affected render block.

Comment thread src/utils/explorer.ts
Comment on lines +8 to +11
const EXPLORER_URLS = {
testnet: 'https://testnet.stellar.expert/tx',
mainnet: 'https://stellar.expert/explorer/mainnet/tx',
} as const;

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

🌐 Web query:

What is the correct Stellar.Expert URL format for viewing a testnet transaction by hash?

💡 Result:

The correct Stellar.Expert URL format for viewing a testnet transaction by its hash is: https://stellar.expert/explorer/testnet/tx/{transaction_hash} In this URL structure, {transaction_hash} should be replaced with the actual hash of the transaction you wish to view [1][2][3].

Citations:


Correct the testnet explorer URL to prevent broken links.

The current testnet URL https://testnet.stellar.expert/tx uses an incorrect host and path structure. The verified Stellar.Expert format for testnet transactions is https://stellar.expert/explorer/testnet/tx/{hash}. Using the current URL will result in 404 errors when appending transaction hashes.

🔧 Fix
 const EXPLORER_URLS = {
-  testnet: 'https://testnet.stellar.expert/tx',
+  testnet: 'https://stellar.expert/explorer/testnet/tx',
   mainnet: 'https://stellar.expert/explorer/mainnet/tx',
 } as const;
📝 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
const EXPLORER_URLS = {
testnet: 'https://testnet.stellar.expert/tx',
mainnet: 'https://stellar.expert/explorer/mainnet/tx',
} as const;
const EXPLORER_URLS = {
testnet: 'https://stellar.expert/explorer/testnet/tx',
mainnet: 'https://stellar.expert/explorer/mainnet/tx',
} as const;
🤖 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 `@src/utils/explorer.ts` around lines 8 - 11, The testnet explorer link in
EXPLORER_URLS is using the wrong Stellar.Expert host/path and will produce
broken transaction URLs. Update the testnet entry in EXPLORER_URLS to use the
verified `stellar.expert/explorer/testnet/tx` format so any hash appended by the
explorer helper resolves correctly.

Comment thread utils/data/mock-data.ts
Comment on lines 408 to +416
availability: true,
bio: 'Backend systems engineer with expertise in microservices, APIs, and distributed systems. 5+ years building scalable infrastructure.',
skills: ['Python', 'Go', 'Kubernetes', 'Docker', 'GraphQL', 'PostgreSQL', 'Redis'],
responseTime: '< 1 hour',
totalSessions: 198,
walletAddress: 'GB3YXVGWVF5MJXGQ3HHHLVXQMIJLYVRQAXDDBJ5FJKPIHSJTQR6SWVBO',
];

export const mockTransactions: Transaction[] = [

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 | 🔴 Critical | ⚡ Quick win

Syntax error: expert id 4 object is never closed.

The object literal opened at line 400 is missing its closing } (and the pastReviews array present on every other expert), so ]; at line 414 closes the array against an unterminated object. This is the parse error Biome reports and will fail the build.

🐛 Proposed fix
         walletAddress: 'GB3YXVGWVF5MJXGQ3HHHLVXQMIJLYVRQAXDDBJ5FJKPIHSJTQR6SWVBO',
+        pastReviews: []
+    }
 ];
📝 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
availability: true,
bio: 'Backend systems engineer with expertise in microservices, APIs, and distributed systems. 5+ years building scalable infrastructure.',
skills: ['Python', 'Go', 'Kubernetes', 'Docker', 'GraphQL', 'PostgreSQL', 'Redis'],
responseTime: '< 1 hour',
totalSessions: 198,
walletAddress: 'GB3YXVGWVF5MJXGQ3HHHLVXQMIJLYVRQAXDDBJ5FJKPIHSJTQR6SWVBO',
];
export const mockTransactions: Transaction[] = [
availability: true,
bio: 'Backend systems engineer with expertise in microservices, APIs, and distributed systems. 5+ years building scalable infrastructure.',
skills: ['Python', 'Go', 'Kubernetes', 'Docker', 'GraphQL', 'PostgreSQL', 'Redis'],
responseTime: '< 1 hour',
totalSessions: 198,
walletAddress: 'GB3YXVGWVF5MJXGQ3HHHLVXQMIJLYVRQAXDDBJ5FJKPIHSJTQR6SWVBO',
pastReviews: []
}
];
export const mockTransactions: Transaction[] = [
🧰 Tools
🪛 Biome (2.5.0)

[error] 414-414: Expected a property, a shorthand property, a getter, a setter, or a method but instead found ']'.

(parse)

🤖 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 `@utils/data/mock-data.ts` around lines 408 - 416, The expert object in
mock-data.ts is left unterminated, causing the parse error before
mockTransactions. In the mock expert entry with id 4, close the object properly
by adding the missing pastReviews field (matching the other expert objects) and
the closing brace before the array terminator so the experts array is valid.

Source: Linters/SAST tools

Comment thread utils/data/mock-data.ts
Comment on lines +416 to 466
export const mockTransactions: Transaction[] = [
{
id: '1',
hash: 'a123456789abcdef1234567890abcdef1234567890abcdef1234567890abcd',
type: 'deposit',
amount: '50 XLM',
date: '2025-06-10',
status: 'completed',
network: 'testnet',
sessionId: '2'
},
{
id: '2',
hash: 'b234567890abcdef1234567890abcdef1234567890abcdef1234567890abce',
type: 'settlement',
amount: '75 XLM',
date: '2025-06-05',
status: 'completed',
network: 'testnet',
sessionId: '3'
},
{
id: '3',
hash: 'c345678901bcdef1234567890abcdef1234567890abcdef1234567890abcef',
type: 'deposit',
amount: '60 XLM',
date: '2025-06-01',
status: 'completed',
network: 'mainnet',
sessionId: '4'
},
{
id: '4',
hash: 'd456789012cdef1234567890abcdef1234567890abcdef1234567890abcdf0',
type: 'withdrawal',
amount: '100 XLM',
date: '2025-05-28',
status: 'pending',
network: 'testnet'
},
{
id: '5',
hash: 'e567890123def1234567890abcdef1234567890abcdef1234567890abce01f',
type: 'deposit',
amount: '120 XLM',
date: '2025-05-20',
status: 'completed',
network: 'mainnet',
sessionId: '1'
},
];

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 | 🟡 Minor | ⚡ Quick win

Mock session/transaction links are inconsistent.

The transactionHash on each session doesn't agree with the sessionId carried by the transaction sharing that hash. For example, session 2 has hash b234…abce, but the transaction with that hash (mockTransactions id 2) points to sessionId: '3'. Same offset applies to sessions 3 and 4. Realign these so demo flows that join sessions to transactions resolve correctly.

🤖 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 `@utils/data/mock-data.ts` around lines 416 - 466, The mock
session-to-transaction links are mismatched: the entries in mockTransactions use
sessionId values that do not line up with the session records sharing the same
hash. Update the affected mockTransactions objects so each transaction’s
sessionId matches the corresponding session’s transactionHash (including the
entries for ids 2, 3, and 4), and verify the mock data stays consistent with the
session/transaction join logic used by the demo.

@Luluameh
Luluameh merged commit 956efa4 into LightForgeHub:main Jun 26, 2026
1 check 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

2 participants