diff --git a/GITHUB_ISSUES_AND_SCF_ROADMAP.md b/GITHUB_ISSUES_AND_SCF_ROADMAP.md new file mode 100644 index 0000000..1abfd97 --- /dev/null +++ b/GITHUB_ISSUES_AND_SCF_ROADMAP.md @@ -0,0 +1,195 @@ +# SkillSphere — Open-Source GitHub Issues & SCF Submission Roadmap + +This document serves as the comprehensive open-source contributor guide, issue directory, and Stellar Community Fund (SCF) roadmap for **SkillSphere** — a decentralized, peer-to-peer knowledge marketplace with real-time per-second streaming payments powered by Stellar and Soroban smart contracts. + +--- + +## 🏛️ 1. Project Architecture & Integration Flow + +SkillSphere consists of three core open-source repositories designed for seamless interaction: + +``` ++-----------------------------------------------------------------------------------+ +| SkillSphere-Dapp | +| (Next.js 15 App Router, React 19, TailwindCSS, @stellar/freighter-api) | ++----------------------------------------+------------------------------------------+ + | + +-------------------------+-------------------------+ + | | + v v ++----------------------------------------+ +----------------------------------+ +| SkillSphere (Backend) | | SkillSphere-Contracts | +| (Node.js/TS, Express, Apollo GraphQL, | | (Rust + Soroban Smart Contracts: | +| Prisma PostgreSQL, Event Indexer) | | Vault, Reputation, Calendar, ID)| ++----------------------------------------+ +----------------------------------+ +``` + +### Component Roles: +1. **`SkillSphere-Contracts`**: Soroban smart contracts handling trustless escrow lockup, continuous per-second value release, instant refunds, on-chain user identity registration, and immutable reputation scoring. +2. **`SkillSphere` (Backend)**: Off-chain auxiliary service that indexes Soroban RPC event logs into PostgreSQL, powers full-text expert search, and provides WebRTC signaling for video consultations. +3. **`SkillSphere-Dapp` (Frontend)**: Web application providing expert discovery, wallet connection, session booking, WebRTC video calling with live per-second payment timer overlays, and transaction tracking. + +--- + +## ⚡ 2. Wallet Connection & Network Alignment (Testnet & Mainnet) + +The dApp frontend (`SkillSphere-Dapp`) is integrated with `@stellar/freighter-api` via [`WalletProvider.tsx`](file:///c:/Users/TOSHIBA/Documents/SkillSphere-Dapp/src/providers/WalletProvider.tsx). + +### Key Integration Highlights: +- **Freighter Wallet Integration**: Connects directly to Freighter browser extension to retrieve public key addresses (`G...`) without requiring central accounts or password storage. +- **Network Polling & Auto-Detection**: Continuously polls Freighter network state every 3 seconds to determine active network (`TESTNET`, `PUBLIC` / Mainnet, `FUTURENET`). +- **Horizon XLM Balance Sync**: Connects to official Horizon RPC nodes (`https://horizon-testnet.stellar.org` & `https://horizon.stellar.org`) to fetch live native account balances. +- **Network Mismatch Warning Banner**: Displays an alert banner on top of the UI whenever a user's Freighter network is set differently than the application environment (e.g. prompt to switch from Mainnet to Testnet). +- **Sandbox Mock Switcher**: Includes a built-in sandbox mock profile toggle ([`DevToolsSwitcher.tsx`](file:///c:/Users/TOSHIBA/Documents/SkillSphere-Dapp/src/components/ui/DevToolsSwitcher.tsx)) allowing open-source contributors and SCF reviewers to test client and expert personas without requiring a live browser extension. + +--- + +## 📋 3. Detailed Open-Source GitHub Issues Directory + +Below is a curated set of 10 GitHub issue templates ready for creation across your repositories. + +--- + +### 📜 Category A: `SkillSphere-Contracts` (Rust / Soroban Smart Contracts) + +#### **Issue #1: Implement Dynamic Rate-Limit Escrow Locking in `payment-vault-contract`** +- **Labels**: `enhancement`, `good first issue`, `soroban`, `rust` +- **Context**: Prevent clients from creating multiple rapid escrow locks for the same session ID due to frontend retries or network latency. +- **Tasks**: + 1. Add `min_session_interval` configuration in contract persistent storage. + 2. Validate that the elapsed time between consecutive escrow locks for a given session ID exceeds `min_session_interval`. + 3. Emit Soroban event: `EscrowLocked(session_id, client, expert, rate_per_second, total_deposit)`. +- **Suggested Files**: `contracts/payment-vault-contract/src/contract.rs`, `storage.rs`, `events.rs` +- **Acceptance Criteria**: Unit tests in `test.rs` confirm rapid duplicate lock calls revert with `Error::RateLimitExceeded`. + +--- + +#### **Issue #2: Add Multi-Asset Token Support (Stellar Asset Contract / USDC) to Escrow Vault** +- **Labels**: `feature`, `smart-contracts`, `scf-priority` +- **Context**: Expand the payment vault to accept Stellar Asset Contracts (SAC) such as USDC alongside native XLM. +- **Tasks**: + 1. Modify `initialize_session_vault` to accept an asset `Address` parameter. + 2. Implement cross-contract invocations using `token::Client::new(&env, &token_address).transfer(...)`. + 3. Support multi-token streaming rate calculations based on asset decimal precision. +- **Suggested Files**: `contracts/payment-vault-contract/src/contract.rs`, `types.rs` +- **Acceptance Criteria**: Integration test demonstrates depositing and streaming custom Stellar tokens (e.g., testnet USDC). + +--- + +#### **Issue #3: Automated Dispute Penalty Slashing in `reputation-scoring-contract`** +- **Labels**: `feature`, `security`, `soroban` +- **Context**: When a dispute is resolved in favor of the client, expert reputation scores should automatically decrease based on dispute severity. +- **Tasks**: + 1. Add `penalize_expert(expert: Address, penalty_points: u32)` function restricted to authorized dispute arbitrator contracts. + 2. Update expert tier thresholds (`ExpertTier::Verified`, `ExpertTier::TopRated`, `ExpertTier::Suspended`). + 3. Emit event `ReputationPenalized(expert, penalty_points, new_score)`. +- **Suggested Files**: `contracts/reputation-scoring-contract/src/contract.rs`, `lib.rs` +- **Acceptance Criteria**: `cargo test` confirms proper deduction of points and automatic tier demotion. + +--- + +#### **Issue #4: WASM Binary Footprint & Gas Profile Optimization** +- **Labels**: `performance`, `ci/cd`, `rust` +- **Context**: Optimize compiled Soroban WASM binaries to minimize transaction footprint and gas execution costs on Stellar mainnet. +- **Tasks**: + 1. Configure release profile options in root `Cargo.toml` (`opt-level = "z"`, `codegen-units = 1`, `panic = "abort"`). + 2. Profile gas usage using `soroban-cli` contract invocation metrics. +- **Suggested Files**: `Cargo.toml`, `.github/workflows/ci.yml` +- **Acceptance Criteria**: Reduced WASM file size below 50KB per contract module. + +--- + +### ⚙️ Category B: `SkillSphere` (Backend & Indexer API) + +#### **Issue #5: Soroban Event Stream Consumer for Real-Time Payment Indexing** +- **Labels**: `feature`, `backend`, `indexer` +- **Context**: Index contract events from Stellar RPC into PostgreSQL so expert earnings and session histories are queried instantly off-chain. +- **Tasks**: + 1. Create a Stellar RPC event poller in `backend/src/indexer.ts` fetching `getEvents` filtered by contract ID. + 2. Decode Soroban XDR event topics and data into TypeScript objects. + 3. Save transactions and update session status in PostgreSQL via Prisma. +- **Suggested Files**: `backend/src/indexer.ts`, `backend/prisma/schema.prisma` +- **Acceptance Criteria**: Emitted `PaymentStreamed` events update database records within 2 seconds. + +--- + +#### **Issue #6: WebRTC Peer-to-Peer Signaling Server Heartbeat & Disconnect Timeout** +- **Labels**: `enhancement`, `websockets`, `backend` +- **Context**: Ensure video call sessions handle sudden peer disconnects gracefully and trigger contract auto-settlement. +- **Tasks**: + 1. Add ping/pong heartbeat intervals (10s) in Socket.IO signaling server. + 2. If a peer drops connection without sending `EndSession`, wait 60 seconds grace period then trigger fallback settlement. +- **Suggested Files**: `backend/src/socket/signaling.ts` +- **Acceptance Criteria**: Simulated network disconnect triggers `PeerDisconnected` notification and auto-settlement fallback. + +--- + +#### **Issue #7: GraphQL Subscriptions for Live Consultation Session Status** +- **Labels**: `enhancement`, `graphql`, `backend` +- **Context**: Provide live real-time updates to client and expert UI dashboards when session status changes. +- **Tasks**: + 1. Define Apollo Server GraphQL subscriptions for `sessionUpdated(sessionId: ID!)`. + 2. Publish events when indexer detects escrow funding or settlement transactions on-chain. +- **Suggested Files**: `backend/src/graphql/schema.ts`, `backend/src/graphql/resolvers.ts` +- **Acceptance Criteria**: Frontend subscription receives real-time payload upon escrow confirmation. + +--- + +### 🎨 Category C: `SkillSphere-Dapp` (Next.js Frontend) + +#### **Issue #8: Real-Time Freighter Transaction Signing Stepper for Escrow Lockup** +- **Labels**: `feature`, `wallet`, `frontend` +- **Context**: Connect [`FundSessionModal.tsx`](file:///c:/Users/TOSHIBA/Documents/SkillSphere-Dapp/src/components/marketplace/FundSessionModal.tsx) to sign and submit Soroban transactions directly using Freighter. +- **Tasks**: + 1. Construct contract call XDR using `@stellar/stellar-sdk` and `useSorobanTx` hook. + 2. Prompt Freighter signature via `signTransaction()`. + 3. Show step-by-step progress using [`TxProgressStepper.tsx`](file:///c:/Users/TOSHIBA/Documents/SkillSphere-Dapp/src/components/ui/TxProgressStepper.tsx) (Preparing -> Signing -> Submitting -> Confirmed). +- **Suggested Files**: `src/components/marketplace/FundSessionModal.tsx`, `src/hooks/useSorobanTx.ts` +- **Acceptance Criteria**: Successful transaction displays live Stellar Explorer transaction hash link. + +--- + +#### **Issue #9: On-Chain Dispute Resolution & Appeal Submission UI** +- **Labels**: `feature`, `ui`, `frontend` +- **Context**: Allow knowledge seekers or experts to submit a dispute claim for reviewed sessions. +- **Tasks**: + 1. Create an appeal submission modal using [`AppealForm.tsx`](file:///c:/Users/TOSHIBA/Documents/SkillSphere-Dapp/src/components/session/AppealForm.tsx). + 2. Upload evidence metadata and invoke `submit_dispute` on the `reputation-scoring-contract`. +- **Suggested Files**: `src/components/session/AppealForm.tsx`, `src/app/dashboard/support/page.tsx` +- **Acceptance Criteria**: Submitted disputes appear in the `/admin/disputes` dashboard view. + +--- + +#### **Issue #10: Multi-Currency Exchange Rate Conversion & Live Wallet Switcher** +- **Labels**: `enhancement`, `ui`, `frontend` +- **Context**: Display session prices in XLM alongside fiat equivalence (USD, EUR, GBP, JPY) using real-time price feeds. +- **Tasks**: + 1. Wire [`useCurrency.ts`](file:///c:/Users/TOSHIBA/Documents/SkillSphere-Dapp/src/hooks/useCurrency.ts) to public exchange rate API. + 2. Allow instant switching between currencies in [`Navbar.tsx`](file:///c:/Users/TOSHIBA/Documents/SkillSphere-Dapp/src/components/layout/Navbar.tsx). +- **Suggested Files**: `src/hooks/useCurrency.ts`, `src/components/layout/Navbar.tsx` +- **Acceptance Criteria**: Toggling currency dropdown updates rates smoothly across expert cards and booking modals. + +--- + +## 🚀 4. Stellar Community Fund (SCF) Milestone Strategy & Presentation + +To present a compelling proposal to **Stellar Community Fund (SCF)** reviewers: + +### Recommended SCF Milestone Structure + +| Milestone | Deliverables | Key Deliverable Proofs | +|---|---|---| +| **Milestone 1: Core Contracts & Escrow Streaming** | - Soroban `payment-vault` & `identity-registry` contracts.
- Automated dispute penalty mechanisms.
- Unit & integration test suites. | - Smart contract WASM builds.
- GitHub test suite reports (`cargo test`).
- Soroban CLI invocation scripts. | +| **Milestone 2: Dapp UI, Wallet Integration & WebRTC** | - Next.js frontend with Freighter Wallet integration.
- Per-second streaming payment timer overlay.
- 3-step escrow funding wizard. | - Live dApp deployment link.
- Interactive demo flows (`/explore-experts`, `/ui-demo/video-call`). | +| **Milestone 3: Indexer, Analytics & Mainnet Readiness** | - Event indexer syncing Soroban events to PostgreSQL.
- On-chain transaction history explorer.
- Production security audits & mainnet deployment. | - Indexed GraphQL API endpoints.
- Mainnet contract deployment transaction hashes. | + +--- + +### Live Interactive Demo Routes for Reviewers + +Include direct links to these pre-configured prototype routes in your SCF application pitch: + +- 🔍 **Expert Directory**: `http://localhost:3000/explore-experts` — Browse verified experts, categories, and per-minute rates. +- 💳 **Escrow Funding Wizard**: `http://localhost:3000/ui-demo/fund-session` — Interactive 3-step wallet funding simulation. +- 📹 **Live Consultation Room**: `http://localhost:3000/ui-demo/video-call` — WebRTC call interface with live per-second payment counter. +- 📑 **Stellar Transaction Explorer**: `http://localhost:3000/ui-demo/transactions` — On-chain escrow lock and settlement receipts. diff --git a/package.json b/package.json index f8d658e..92f0251 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,13 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev --turbopack", + "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint", "indexer": "tsx src/indexer/index.ts", - "test:indexer": "tsx tests/indexer.spec.ts" + "test:indexer": "tsx tests/indexer.spec.ts", + "test:validation": "npx tsx tests/validation.spec.ts" }, "dependencies": { "@hookform/resolvers": "^3.10.0", diff --git a/pages/test.tsx b/pages/test.tsx deleted file mode 100644 index 30f623f..0000000 --- a/pages/test.tsx +++ /dev/null @@ -1,13 +0,0 @@ -"use client"; - -import React from "react"; -import LoadingAndEmptyDemo from "../src/components/__demo__/LoadingAndEmptyDemo"; - -export default function TestPage() { - return ( -
-

Test: Loading & Empty states

- -
- ); -} diff --git a/src/app/favicon.ico b/public/favicon.ico similarity index 100% rename from src/app/favicon.ico rename to public/favicon.ico diff --git a/src/app/marketplace/page.tsx b/src/app/marketplace/page.tsx index 909c5b3..5405e69 100644 --- a/src/app/marketplace/page.tsx +++ b/src/app/marketplace/page.tsx @@ -1,15 +1,17 @@ -import HottestCollections from "@/components/marketplace/HottestCollections"; -import NFTGrid from "@/components/marketplace/NFTGrid"; -import PlatformStats from "@/components/marketplace/PlatformStats"; +"use client"; + import TrendingExpert from "@/components/marketplace/trendingExpert"; +import PlatformStats from "@/components/marketplace/PlatformStats"; +import ExploreExpertsPage from "@/app/explore-experts/page"; export default function MarketplacePage() { - return ( -
- - - - -
- ) + return ( +
+ + +
+ +
+
+ ); } \ No newline at end of file diff --git a/src/app/page.tsx b/src/app/page.tsx index 1d718f2..89d30eb 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,14 +1,16 @@ import { HeroSection } from "@/components/home/sections/HeroSection"; +import { FeaturedExpertsSection } from "@/components/home/sections/FeaturedExpertsSection"; +import { HowItWorksSection } from "@/components/home/sections/HowItWorksSection"; +import { ValuePillarsSection } from "@/components/home/sections/ValuePillarsSection"; import { CTASection } from "@/components/home/sections/CTASection"; -import { DiscoverNFTsSection } from "@/components/home/sections/DiscoverNFTsSection"; -import { TopRatedArtist } from "@/components/home/sections/TopRatedArtist"; export default function Home() { return ( <> - - + + + ); diff --git a/src/app/register-expert/page.tsx b/src/app/register-expert/page.tsx new file mode 100644 index 0000000..d732c6b --- /dev/null +++ b/src/app/register-expert/page.tsx @@ -0,0 +1,74 @@ +"use client"; + +import React from "react"; +import RegistrationForm from "@/components/profile/RegistrationForm"; +import Link from "next/link"; +import { ArrowLeft, Sparkles, ShieldCheck, Zap } from "lucide-react"; + +export default function RegisterExpertPage() { + return ( +
+
+ {/* Navigation & Breadcrumbs */} +
+ + + Back to Experts + + + Issue #437 + +
+ + {/* Hero Banner */} +
+
+ + Join SkillSphere as a Verified Expert +
+

+ Monetize Your Knowledge in Real-Time +

+

+ Set your per-second streaming rate and connect your Stellar wallet to get paid directly via trustless escrow contracts during 1-on-1 consultations. +

+
+ + {/* Registration Form with Zod Validation */} + { + console.log("Expert registration submitted:", data); + }} + /> + + {/* Feature Highlights */} +
+
+ +

Per-Second Payouts

+

+ Funds stream directly to your Stellar account every second during active WebRTC calls. +

+
+
+ +

Zero Custody Risk

+

+ Non-custodial Soroban escrow ensures both seeker and expert are protected at all times. +

+
+
+ +

Reputation Badges

+

+ Earn on-chain Soulbound reputation tokens for high-rated consultations and milestones. +

+
+
+
+
+ ); +} diff --git a/src/components/CreateWalletModal.tsx b/src/components/CreateWalletModal.tsx index 3476367..08a969e 100644 --- a/src/components/CreateWalletModal.tsx +++ b/src/components/CreateWalletModal.tsx @@ -1,7 +1,7 @@ "use client"; import React, { useEffect, useRef, useState } from "react"; -import { X, CheckCircle, AlertCircle } from "lucide-react"; +import { X, CheckCircle, AlertCircle, Wallet, ShieldCheck } from "lucide-react"; import { useWallet } from "@/providers/WalletProvider"; type ModalState = "idle" | "connecting" | "connected" | "error"; @@ -12,18 +12,20 @@ interface Props { } export default function CreateWalletModal({ open, onClose }: Props) { + const { connect, address, walletType, error: walletError } = useWallet(); const [state, setState] = useState("idle"); + const [method, setMethod] = useState(null); + const [errorMsg, setErrorMsg] = useState(null); const backdropRef = useRef(null); - const { connect, error } = useWallet(); - // reset when opened useEffect(() => { if (open) { setState("idle"); + setMethod(null); + setErrorMsg(null); } }, [open]); - // Escape to close useEffect(() => { function onKey(e: KeyboardEvent) { if (e.key === "Escape") onClose(); @@ -38,89 +40,139 @@ export default function CreateWalletModal({ open, onClose }: Props) { if (e.target === backdropRef.current) onClose(); } - async function handleConnect() { + async function handleConnect(walletName: "Freighter" | "Lobstr" | "Albedo") { + setMethod(walletName); setState("connecting"); - const connected = await connect(); - setState(connected ? "connected" : "error"); + setErrorMsg(null); + + const success = await connect(); + + if (success) { + setState("connected"); + setTimeout(() => onClose(), 1000); + } else { + setState("error"); + setErrorMsg("Connection request was cancelled or declined in wallet."); + } } return (
-
- - -

Connect Your Wallet

-

Choose a wallet to connect to SkillSphere.

- -
- {/* Default / Options */} +
+ {/* Header */} +
+
+ +

Connect Wallet

+
+ +
+ +

+ Select a Stellar wallet to connect and stream per-second consultation payments on SkillSphere. +

+ +
{state === "idle" && ( -
- + + {/* Lobstr */} + + + {/* Albedo */} +
)} - {/* Connecting */} {state === "connecting" && ( -
-
-
-
+
+
-
Connecting to Freighter…
-
Please approve the connection in your wallet.
+

Connecting to {method}…

+

+ Please approve the access request in your {method} wallet popup. +

)} - {/* Connected */} {state === "connected" && ( -
- +
+
-
Connected
-
Freighter is now connected.
+

{method || walletType} Connected!

+

+ {address} +

)} - {/* Error */} {state === "error" && ( -
- -
-
Connection failed
-
- {error ?? "Unable to connect to Freighter. Please try again."} -
+
+ +
+

Connection Cancelled

+

+ {errorMsg || walletError || `Connection request to ${method} was rejected or closed.`} +

+
)}
-
- {state === "error" && ( - - )} - +
+
diff --git a/src/components/dashboard/Header.tsx b/src/components/dashboard/Header.tsx index 4c0b1a4..f424117 100644 --- a/src/components/dashboard/Header.tsx +++ b/src/components/dashboard/Header.tsx @@ -23,7 +23,12 @@ export default function Header({ useEffect(() => { const v = safeLocalStorage.get("dashboard_profile") - if (v) setProfile(JSON.parse(v)) + if (v) { + try { + const parsed = JSON.parse(v) + if (parsed && typeof parsed === "object") setProfile(parsed) + } catch {} + } }, []) useEffect(() => { diff --git a/src/components/dashboard/SessionHistory.tsx b/src/components/dashboard/SessionHistory.tsx index 108b971..06d8cc5 100644 --- a/src/components/dashboard/SessionHistory.tsx +++ b/src/components/dashboard/SessionHistory.tsx @@ -33,7 +33,7 @@ export default function SessionHistory() {

Session History

-

View your past sessions and transaction details on Stellar Explorer.

+

View your past sessions and transaction details on Stellar Explorer.

- - - + {/* Desktop Nav Links */} +
+ {!isLandingPage && ( + + Home - - ) : ( - <> - - - {address && ( - - )} - - - -
- -
- Profile -
- - )} -
+ )} + + Explore Experts + + + Marketplace + + + Dashboard + + + FAQ's + +
- {/* Mobile Menu Button */} - -
+ {/* Desktop Right Actions */} +
+ - {/* Mobile Menu */} - {mobileMenuOpen && ( -
-
- Theme - -
-
- - -
-
- {!isLandingPage && ( - Home - )} - Explore Experts - Community - FAQ's - {isLandingPage ? ( <> - - - - - ) : ( -
- - {address && ( + {balance && ( + + {balance} XLM + + )} - )} -
-
- Profile - Profile -
-
+
+ ) : ( + // Connect wallet button + )} + +
+ + {/* Mobile Menu Toggle */} +
- )} -
- - setWalletOpen(false)} /> -
+ + {/* Mobile Dropdown */} + {mobileMenuOpen && ( +
+ + Explore Experts + + + Marketplace + + + Dashboard + + + FAQ's + + +
+ {address ? ( + + ) : ( + + )} +
+
+ )} +
+ + + setWalletOpen(false)} /> +
); } diff --git a/src/components/home/sections/CTASection.tsx b/src/components/home/sections/CTASection.tsx index 2021d2a..cb28141 100644 --- a/src/components/home/sections/CTASection.tsx +++ b/src/components/home/sections/CTASection.tsx @@ -1,3 +1,5 @@ +"use client"; + import Image from "next/image"; export function CTASection() { diff --git a/src/components/home/sections/DiscoverNFTsSection.tsx b/src/components/home/sections/DiscoverNFTsSection.tsx deleted file mode 100644 index 07c8909..0000000 --- a/src/components/home/sections/DiscoverNFTsSection.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { NFTCard } from "../ui/NFTCard"; -import { Eye } from "lucide-react" - -const nftData = [ - { - id: 1, - image: "/imagePlaceholder1.png", - title: "Distant Galaxy", - creatorName: "MoonDancer", - creatorAvatar: "/nftAvatar1.png", - price: "1.63 ETH", - highestBid: "0.33 wETH", - }, - { - id: 2, - image: "/imagePlaceholder2.png", - title: "Life On Edena", - creatorName: "NebulaKid", - creatorAvatar: "/nftAvatar2.png", - price: "1.63 ETH", - highestBid: "0.33 wETH", - }, - { - id: 3, - image: "/imagePlaceholder3.png", - title: "AstroFiction", - creatorName: "Spaceone", - creatorAvatar: "/nftAvatar3.png", - price: "1.63 ETH", - highestBid: "0.33 wETH", - }, -]; - -export function DiscoverNFTsSection() { - return ( -
-
- {/* Header */} -
-
-

- Discover More NFTs -

-

- Explore new trending NFTs -

-
- - -
- - {/* NFT Cards Grid */} -
- {nftData.map((nft) => ( - - ))} -
-
-
- ); -} diff --git a/src/components/home/sections/FeaturedExpertsSection.tsx b/src/components/home/sections/FeaturedExpertsSection.tsx new file mode 100644 index 0000000..d2eafb9 --- /dev/null +++ b/src/components/home/sections/FeaturedExpertsSection.tsx @@ -0,0 +1,147 @@ +"use client"; + +import React from "react"; +import Link from "next/link"; +import { Star, Clock, ArrowRight, ShieldCheck, Video } from "lucide-react"; +import { mockExperts } from "@/utils/data/mock-data"; + +export function FeaturedExpertsSection() { + // Take top 3 verified experts + const featured = mockExperts.slice(0, 3); + + return ( +
+ {/* Background glow accents */} +
+ +
+ {/* Header */} +
+
+
+ + Verified On-Chain Experts +
+

+ Top Rated Knowledge Partners +

+

+ Connect with vetted industry specialists for real-time video consultations. Stream payments per minute directly on Stellar. +

+
+ + + Explore All Experts + + +
+ + {/* Expert Cards Grid */} +
+ {featured.map((expert) => { + const perMinuteRate = (expert.hourlyRate / 60).toFixed(2); + return ( +
+
+ {/* Top Header: Avatar & Category */} +
+
+ {expert.name} + {expert.verified && ( +
+ +
+ )} +
+ + + {expert.category} + +
+ + {/* Name & Title */} +

+ {expert.name} +

+

+ {expert.bio || expert.title} +

+ + {/* Skills tags */} +
+ {expert.skills.slice(0, 3).map((skill, idx) => ( + + {skill} + + ))} + {expert.skills.length > 3 && ( + + +{expert.skills.length - 3} more + + )} +
+
+ + {/* Footer: Rating, Rate & Action */} +
+
+
+ + {expert.rating.toFixed(1)} + + ({expert.reviewsCount || 12} reviews) + +
+ +
+ + {expert.responseTime || "< 1 hour"} +
+
+ +
+
+

+ Rate +

+

+ ${expert.hourlyRate}/hr{" "} + + (~${perMinuteRate}/min) + +

+
+ + +
+
+
+ ); + })} +
+
+
+ ); +} diff --git a/src/components/home/sections/HeroSection.tsx b/src/components/home/sections/HeroSection.tsx index 54da3f3..c504c7b 100644 --- a/src/components/home/sections/HeroSection.tsx +++ b/src/components/home/sections/HeroSection.tsx @@ -1,3 +1,5 @@ +"use client"; + import Image from "next/image"; export function HeroSection() { diff --git a/src/components/home/sections/HowItWorksSection.tsx b/src/components/home/sections/HowItWorksSection.tsx new file mode 100644 index 0000000..b167bc8 --- /dev/null +++ b/src/components/home/sections/HowItWorksSection.tsx @@ -0,0 +1,85 @@ +"use client"; + +import React from "react"; +import { Wallet, ShieldCheck, PlayCircle, RefreshCw } from "lucide-react"; + +export function HowItWorksSection() { + const steps = [ + { + number: "01", + icon: Wallet, + title: "Connect Stellar Wallet", + description: + "Connect your Freighter Wallet on Stellar Testnet. No central account registration required.", + }, + { + number: "02", + icon: ShieldCheck, + title: "Lock Session Escrow", + description: + "Choose your consultation duration. Your budget is securely locked into a Soroban smart contract vault.", + }, + { + number: "03", + icon: PlayCircle, + title: "Stream & Settle", + description: + "Enter the WebRTC video call. Payments stream per second while value is delivered. Unused funds refund instantly.", + }, + ]; + + return ( +
+ {/* Glow background */} +
+ +
+ {/* Title */} +
+
+ + Continuous Value Exchange +
+

+ How SkillSphere Works +

+

+ Say goodbye to upfront hourly blocks and 20% platform commissions. Pay strictly per second while consulting with verified specialists. +

+
+ + {/* Steps Grid */} +
+ {steps.map((step, index) => { + const Icon = step.icon; + return ( +
+ {/* Number Badge */} +
+ + {step.number} + +
+ +
+
+ +
+

+ {step.title} +

+

+ {step.description} +

+
+
+ ); + })} +
+
+
+ ); +} diff --git a/src/components/home/sections/TopRatedArtist.tsx b/src/components/home/sections/TopRatedArtist.tsx deleted file mode 100644 index db227f5..0000000 --- a/src/components/home/sections/TopRatedArtist.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import { CreatorCard } from "../ui/CreatorCard"; -import { Rocket } from "lucide-react"; - -const creators = [ - { - id: 1, - avatar: "/avatarPlaceholder1.jpg", - name: "Keepitreal", - sales: "34.53 ETH", - }, - { - id: 2, - avatar: "/avatarPlaceholder2.png", - name: "DigiLab", - sales: "34.53 ETH", - }, - { - id: 3, - avatar: "/avatarPlaceholder3.png", - name: "GravityOne", - sales: "34.53 ETH", - }, - { - id: 4, - avatar: "/avatarPlaceholder4.png", - name: "Juanie", - sales: "34.53 ETH", - }, - { - id: 5, - avatar: "/avatarPlaceholder5.jpg", - name: "BlueWhale", - sales: "34.53 ETH", - }, - { - id: 6, - avatar: "/avatarPlaceholder6.png", - name: "Mr Fox", - sales: "34.53 ETH", - }, - { - id: 7, - avatar: "/avatarPlaceholder7.jpg", - name: "Shroomie", - sales: "34.53 ETH", - }, - { - id: 8, - avatar: "/avatarPlaceholder8.jpg", - name: "Robotica", - sales: "34.53 ETH", - }, - { - id: 9, - avatar: "/avatarPlaceholder9.png", - name: "RustyRobot", - sales: "34.53 ETH", - }, - { - id: 10, - avatar: "/avatarPlaceholder10.jpg", - name: "Animakid", - sales: "34.53 ETH", - }, - { - id: 11, - avatar: "/avatarPlaceholder11.png", - name: "Dotgu", - sales: "34.53 ETH", - }, - { - id: 12, - avatar: "/avatarPlaceholder12.png", - name: "Ghiblier", - sales: "34.53 ETH", - }, -]; - -export function TopRatedArtist() { - return ( -
- -
- {/* Header */} -
-
-

- Top creators -

-

- Checkout Top Rated Creators on the NFT Marketplace -

-
-
-
- -
-
-
- - {/* Grid */} -
- {creators.map((creator, index) => ( - - ))} -
-
-
- ); -} diff --git a/src/components/home/sections/ValuePillarsSection.tsx b/src/components/home/sections/ValuePillarsSection.tsx new file mode 100644 index 0000000..def9e24 --- /dev/null +++ b/src/components/home/sections/ValuePillarsSection.tsx @@ -0,0 +1,70 @@ +"use client"; + +import React from "react"; +import { Zap, ShieldCheck, Award, Globe } from "lucide-react"; + +export function ValuePillarsSection() { + const pillars = [ + { + icon: Zap, + title: "Per-Second Payment Streams", + description: + "No more paying for full 60-minute blocks when you only need a 10-minute code review or strategy check. Money streams continuously while value flows.", + }, + { + icon: ShieldCheck, + title: "Trustless Soroban Escrow", + description: + "Funds are held safely in open-source Smart Contract vaults on Stellar. When the session ends, unspent funds return instantly to your wallet.", + }, + { + icon: Award, + title: "Immutable On-Chain Reputation", + description: + "Expert ratings and dispute metrics are calculated programmatically on-chain. Reputation cannot be deleted, bought, or manipulated.", + }, + { + icon: Globe, + title: "Global Micro-Consultations", + description: + "Borderlessly connect with verified mentors, software architects, and consultants globally without cross-border banking delays.", + }, + ]; + + return ( +
+
+
+

+ Why Knowledge Seekers & Experts Choose SkillSphere +

+

+ Re-architecting human expertise exchange around fairness, speed, and real-time streaming payments. +

+
+ +
+ {pillars.map((item, idx) => { + const Icon = item.icon; + return ( +
+
+ +
+

+ {item.title} +

+

+ {item.description} +

+
+ ); + })} +
+
+
+ ); +} diff --git a/src/components/home/ui/CreatorCard.tsx b/src/components/home/ui/CreatorCard.tsx deleted file mode 100644 index c78d410..0000000 --- a/src/components/home/ui/CreatorCard.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import Image from "next/image"; - -interface CreatorCardProps { - avatar: string; - name: string; - totalSales: string; - rank: number; -} - -export function CreatorCard({ - avatar, - name, - totalSales, - rank, -}: CreatorCardProps) { - return ( -
- {/* Ranking Number */} -
-

- {rank} -

-
- - {/* Avatar */} -
- {name} -
- - {/* Creator Info */} -
-

- {name} -

-
-

- Total Sales: -

-

{totalSales}

-
-
-
- ); -} - -// Skeleton component for loading states -export function CreatorCardSkeleton() { - return ( -
-
-
-
-
-
-
-
- ); -} diff --git a/src/components/home/ui/NFTCard.tsx b/src/components/home/ui/NFTCard.tsx deleted file mode 100644 index ec0d9c1..0000000 --- a/src/components/home/ui/NFTCard.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import Image from "next/image"; - -interface NFTCardProps { - image: string; - title: string; - creatorName: string; - creatorAvatar: string; - price: string; - highestBid: string; -} - -export function NFTCard({ - image, - title, - creatorName, - creatorAvatar, - price, - highestBid, -}: NFTCardProps) { - return ( -
- {/* Image Section */} -
- {`${title} -
-
- - {/* Info Section */} -
- {/* Title and Creator */} -
-

- {title} -

-
-
- {creatorName} -
-

- {creatorName} -

-
-
- - {/* Price and Bid */} -
-
-

- Price -

-

- {price} -

-
-
-

- Highest Bid -

-

- {highestBid} -

-
-
-
-
- ); -} - - -// Skeleton component for loading states -export function NFTCardSkeleton() { - return ( -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ); -} diff --git a/src/components/marketplace/HottestCollections.tsx b/src/components/marketplace/HottestCollections.tsx deleted file mode 100644 index c2396df..0000000 --- a/src/components/marketplace/HottestCollections.tsx +++ /dev/null @@ -1,154 +0,0 @@ -'use client'; - -import { useState, useEffect } from 'react'; // Added useEffect -import Image from 'next/image'; -import { Flame } from 'lucide-react'; -import { FilterDropdown } from './FilterDropdown'; -import { Collection } from '../../../utils/types/types'; -import { mockCollections } from '../../../utils/data/mock-data'; - - -const CollectionRow = ({ collection }: { collection: Collection }) => { - return ( -
-
- {/* Collection Info */} -
-
- {collection.name} -
-
-
- {collection.name} -
- {collection.name} -
-
- - {/* Number of Items */} -
- - Items: - {collection.items} - -
- - {/* Starting Price */} -
- - Starting price: - {collection.price} ETH - -
-
-
- ); -}; - -export default function HottestCollections() { - const [sortBy, setSortBy] = useState('Popular'); - const [priceFilter, setPriceFilter] = useState('Price'); - const [dayFilter, setDayFilter] = useState('Day'); - const [filteredCollections, setFilteredCollections] = useState(mockCollections); - - const sortOptions = ['Popular', 'Trending', 'Recent', 'Top Rated']; - const priceOptions = ['Price', 'Low to High', 'High to Low']; - const dayOptions = ['Day', 'Week', 'Month', 'All Time']; - - useEffect(() => { - const result = [...mockCollections]; - - // Apply price filter - if (priceFilter === 'Low to High') { - result.sort((a, b) => parseFloat(a.price) - parseFloat(b.price)); - } else if (priceFilter === 'High to Low') { - result.sort((a, b) => parseFloat(b.price) - parseFloat(a.price)); - } - - // Apply sort filter - if (sortBy === 'Top Rated') { - result.sort((a, b) => b.items - a.items); - } else if (sortBy === 'Recent') { - // Assuming newer collections have higher IDs - result.sort((a, b) => b.id - a.id); - } else if (sortBy === 'Popular') { - result.sort((a, b) => b.items - a.items); - } - - // Apply day filter (if you have dates) - if (dayFilter !== 'Day') { - // For now, it just returns all collections - } - - setFilteredCollections(result); - }, [sortBy, priceFilter, dayFilter]); - - return ( -
-
- {/* Header */} -
-
-
- -
-

- Hottest Collections {/* Fixed typo: "Hotest" to "Hottest" */} -

-
- - {/* Filters */} -
- - - -
-
- - {/* Table Header - Desktop Only */} -
-
Collections
-
Number of items
-
Starting price
-
- - {/* Collections List */} -
- {filteredCollections.length > 0 ? ( - filteredCollections.map((collection) => ( - - )) - ) : ( -
- No collections found with the current filters. -
- )} -
-
-
- ); -} \ No newline at end of file diff --git a/src/components/marketplace/NFTCard.tsx b/src/components/marketplace/NFTCard.tsx deleted file mode 100644 index 7dfdd38..0000000 --- a/src/components/marketplace/NFTCard.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { Star, Users } from "lucide-react"; -import Image from "next/image"; -import { NFT } from "../../../utils/types/types"; - - - -export const NFTCard = ({ nft }: { nft: NFT }) => { - return ( -
- {/* Image */} -
- {nft.title} -
- - {/* Content */} -
- {/* Title and Time */} -
-

{nft.title}

- -
- - {/* Rating */} -
- {nft.timeLeft} -
-
- - {nft.rating} -
-
- - {nft.reviews} -
-
-
- - {/* Creator */} -
-
- {nft.creator} -
-
-

{nft.creator}

-

: {nft.creatorRole}

-
-
- - {/* Price and Button */} -
-
- {nft.price} ETH -
- -
-
-
- ); -}; diff --git a/src/components/marketplace/NFTGrid.tsx b/src/components/marketplace/NFTGrid.tsx deleted file mode 100644 index fa9a769..0000000 --- a/src/components/marketplace/NFTGrid.tsx +++ /dev/null @@ -1,94 +0,0 @@ -'use client'; - -import { useState } from 'react'; -import * as React from 'react'; -import { FilterDropdown } from './FilterDropdown'; -import { NFTCard } from './NFTCard'; -import { mocknftData } from '../../../utils/data/mock-data'; - - - - -export default function NFTGrid() { - const [category, setCategory] = useState('All Categories'); - const [sortBy, setSortBy] = useState('Popular'); - const [priceFilter, setPriceFilter] = useState('Price'); - const [filteredNFTs, setFilteredNFTs] = useState(mocknftData); - - const categoryOptions = ['All Categories', 'Science Lab', 'Aoen.Net', 'Flow Dymenisty', 'Centry Networks']; - const sortOptions = ['Popular', 'Recent', 'Price: Low to High', 'Price: High to Low', 'Most Liked']; - const priceOptions = ['Price', 'Under 1 ETH', '1-2 ETH', '2-3 ETH', 'Above 3 ETH']; - - // Apply filters - React.useEffect(() => { - let result = [...mocknftData]; - - // Filter by category - if (category !== 'All Categories') { - result = result.filter(nft => nft.title === category); - } - - // Filter by price range - if (priceFilter === 'Under 1 ETH') { - result = result.filter(nft => parseFloat(nft.price) < 1); - } else if (priceFilter === '1-2 ETH') { - result = result.filter(nft => parseFloat(nft.price) >= 1 && parseFloat(nft.price) < 2); - } else if (priceFilter === '2-3 ETH') { - result = result.filter(nft => parseFloat(nft.price) >= 2 && parseFloat(nft.price) < 3); - } else if (priceFilter === 'Above 3 ETH') { - result = result.filter(nft => parseFloat(nft.price) >= 3); - } - - // Sort - if (sortBy === 'Price: Low to High') { - result.sort((a, b) => parseFloat(a.price) - parseFloat(b.price)); - } else if (sortBy === 'Price: High to Low') { - result.sort((a, b) => parseFloat(b.price) - parseFloat(a.price)); - } else if (sortBy === 'Most Liked') { - result.sort((a, b) => b.rating - a.rating); - } - - setFilteredNFTs(result); - }, [category, sortBy, priceFilter]); - - return ( -
-
- {/* Header */} -
-

NFTS

- - {/* Filters */} -
- - - -
-
- - {/* NFT Grid */} - {filteredNFTs.length > 0 ? ( -
- {filteredNFTs.map((nft) => ( - - ))} -
- ) : ( -
-

No NFTs found matching your filters.

- -
- )} -
-
- ); -} \ No newline at end of file diff --git a/src/components/profile/RegistrationForm.tsx b/src/components/profile/RegistrationForm.tsx index fe14476..c8eaa20 100644 --- a/src/components/profile/RegistrationForm.tsx +++ b/src/components/profile/RegistrationForm.tsx @@ -1,90 +1,511 @@ "use client"; +import React, { useState } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; -import { Input } from "@/components/ui/Input"; -import { Button } from "@/components/ui/Button"; +import { + User, + Briefcase, + DollarSign, + Globe, + Github, + Twitter, + Tag, + Clock, + Sparkles, + AlertCircle, + CheckCircle2, + Wallet, +} from "lucide-react"; import { expertRegistrationSchema, type ExpertRegistrationData, + calculateHourlyRate, + calculateMinuteRate, + parseTags, } from "@/utils/validation"; +import { useWallet } from "@/providers/WalletProvider"; interface RegistrationFormProps { - onSubmit: (data: ExpertRegistrationData) => void; + onSubmit?: (data: ExpertRegistrationData) => void | Promise; + defaultValues?: Partial; } -export default function RegistrationForm({ onSubmit }: RegistrationFormProps) { +const SUGGESTED_SKILLS = [ + "Soroban", + "Rust", + "Smart Contracts", + "Stellar", + "TypeScript", + "WebRTC", + "DeFi", + "Zero-Knowledge", + "Security Audits", + "Next.js", +]; + +export default function RegistrationForm({ + onSubmit, + defaultValues, +}: RegistrationFormProps) { + const { address } = useWallet(); + const [isSubmitting, setIsSubmitting] = useState(false); + const [isSuccess, setIsSuccess] = useState(false); + const { register, handleSubmit, - formState: { errors, isValid }, + watch, + setValue, + formState: { errors, isValid, isDirty }, } = useForm({ resolver: zodResolver(expertRegistrationSchema), mode: "onChange", + defaultValues: { + displayName: defaultValues?.displayName || "", + title: defaultValues?.title || "", + bio: defaultValues?.bio || "", + tags: defaultValues?.tags || "", + ratePerSecond: defaultValues?.ratePerSecond ?? 0.003, + yearsOfExperience: defaultValues?.yearsOfExperience ?? 3, + portfolioUrl: defaultValues?.portfolioUrl || "", + githubUrl: defaultValues?.githubUrl || "", + twitterUrl: defaultValues?.twitterUrl || "", + languages: defaultValues?.languages || "English", + }, }); + const watchedRate = watch("ratePerSecond"); + const watchedBio = watch("bio") || ""; + const watchedTags = watch("tags") || ""; + const activeTags = parseTags(watchedTags); + + const hourlyRate = calculateHourlyRate(watchedRate); + const minuteRate = calculateMinuteRate(watchedRate); + + function handleAddTag(tag: string) { + if (activeTags.includes(tag)) return; + if (activeTags.length >= 10) return; + const newTags = [...activeTags, tag].join(", "); + setValue("tags", newTags, { shouldValidate: true, shouldDirty: true }); + } + + function handleRemoveTag(tagToRemove: string) { + const newTags = activeTags.filter((t) => t !== tagToRemove).join(", "); + setValue("tags", newTags, { shouldValidate: true, shouldDirty: true }); + } + + async function handleFormSubmit(data: ExpertRegistrationData) { + setIsSubmitting(true); + try { + if (onSubmit) { + await onSubmit(data); + } + setIsSuccess(true); + } catch (err) { + console.error("Submission failed:", err); + } finally { + setIsSubmitting(false); + } + } + return ( -
-

Expert Registration

- - {/* Display Name */} -
- - - {errors.displayName && ( -

{errors.displayName.message}

- )} -
+
+ {/* Form Header */} +
+
+
+ +
+
+

+ Expert Profile Registration +

+

+ Set up your verified expert profile to offer 1-on-1 consultations with real-time per-second streaming payments on Stellar. +

+
+
- {/* Bio */} -
- -