{label}
+{value}
+{detail}
diff --git a/soroban-client/__tests__/components/Header.test.tsx b/soroban-client/__tests__/components/Header.test.tsx
index c3c2a431..d5942aa2 100644
--- a/soroban-client/__tests__/components/Header.test.tsx
+++ b/soroban-client/__tests__/components/Header.test.tsx
@@ -1,6 +1,7 @@
import React from 'react';
-import { render, screen, fireEvent } from '@testing-library/react';
+import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import Header from '../../components/Header';
+import { ThemeProvider } from '@/contexts/ThemeContext';
import { useWallet } from '@/contexts/WalletContext';
// Mock Wallet Context Hook
@@ -13,6 +14,33 @@ jest.mock('next/navigation', () => ({
}));
describe('Header Component', () => {
+ beforeEach(() => {
+ window.localStorage.clear();
+
+ Object.defineProperty(window, 'matchMedia', {
+ writable: true,
+ value: jest.fn().mockImplementation((query: string) => ({
+ matches: false,
+ media: query,
+ onchange: null,
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ addListener: jest.fn(),
+ removeListener: jest.fn(),
+ dispatchEvent: jest.fn(),
+ })),
+ });
+
+ document.documentElement.classList.remove('dark');
+ });
+
+ const renderHeader = () =>
+ render(
+
+
Analytics
+
Track page views, wallet connections, batch ticket purchases, conversion
trends, and revenue signals without leaving the CrowdPass interface.
- Launch your decentralized event with secure ticketing.
-
- Launch a new CrowdPass experience with on-chain pricing, inventory, and
- organizer ownership.
+
+ Launch a new CrowdPass experience with on-chain pricing, inventory,
+ and organizer ownership.
Loading… Loading… No attendees yet. No attendees yet.
+
{error}
{event.event_type} {event.event_type}
+
{event.tickets_sold.toString()}
Sold Sold
+
{event.total_tickets.toString()}
Total Total {formatXLM(revenue)} Revenue {formatXLM(revenue)} Revenue Start: {formatDate(event.start_date)} End: {formatDate(event.end_date)} Price: {formatXLM(event.ticket_price)} Connect your wallet to manage your events. Connect your wallet to manage your events.
+
{address?.substring(0, 6)}…{address?.slice(-4)}
{value} {label} {value} {label} Loading events… {error} You haven't created any events yet. You haven't created any events yet. Events Events
+
Quantity selection now supports group purchases, discounted batch pricing,
and wallet-connected checkout for on-chain ticket reservations.
+
Analytics
+
Track page views, wallet connections, batch ticket purchases, conversion
trends, and revenue signals without leaving the CrowdPass interface.
+
Launch a new CrowdPass experience with on-chain pricing, inventory,
and organizer ownership.
@@ -176,25 +176,25 @@ export default function CreateEventPage() {
@@ -204,13 +204,13 @@ export default function CreateEventPage() {
@@ -220,11 +220,11 @@ export default function CreateEventPage() {
diff --git a/soroban-client/app/dashboard/page.tsx b/soroban-client/app/dashboard/page.tsx
index daefa3e9..931b1fff 100644
--- a/soroban-client/app/dashboard/page.tsx
+++ b/soroban-client/app/dashboard/page.tsx
@@ -66,24 +66,24 @@ function AttendeesModal({
}, [eventId, readerAccount]);
return (
- Loading… Loading… No attendees yet. No attendees yet.
+
{error}
{event.event_type} {event.event_type}
+
{event.tickets_sold.toString()}
Sold Sold
+
{event.total_tickets.toString()}
Total Total {formatXLM(revenue)} Revenue {formatXLM(revenue)} Revenue Start: {formatDate(event.start_date)} End: {formatDate(event.end_date)} Price: {formatXLM(event.ticket_price)} Connect your wallet to manage your events. Connect your wallet to manage your events.
+
{address?.substring(0, 6)}…{address?.slice(-4)}
{value} {label} {value} {label} Loading events… {error} You haven't created any events yet. You haven't created any events yet. Browse Browse
+
Search and filter live listings from the EventManager contract, review dates
and inventory, then confirm purchases with your wallet (Freighter by default).
The same experience is available at{" "}
- /browse.
+ /browse.
Marketplace Marketplace
+
List your unused tickets for resale or discover great deals from other fans.
All transfers are secured by smart contracts with built-in price caps to prevent scalping.
Connect your wallet to see all your event tickets and access your entry codes. Connect your wallet to see all your event tickets and access your entry codes. My Wallet
- Manage your on-chain event tickets, view details, and generate verification QR codes for entry.
- My Wallet
+ Manage your on-chain event tickets, view details, and generate verification QR codes for entry.
+ Your ticket history is currently empty. Your ticket history is currently empty. #{ticket.id} • {ticket.event_type} #{ticket.id} • {ticket.event_type}
- Scan participant QR codes to verify ticket ownership and authenticity via cryptographic signatures and on-chain records.
-
+ Scan participant QR codes to verify ticket ownership and authenticity via cryptographic signatures and on-chain records.
+ Waiting for scan... Verification Status Verification Status On-Chain {onChainValid === true ? "Confirmed" : onChainValid === false ? "Failed" : "Checking..."} Signature {scanResult?.sig ? "Verified" : "Missing"} On-Chain {onChainValid === true ? "Confirmed" : onChainValid === false ? "Failed" : "Checking..."} Signature {scanResult?.sig ? "Verified" : "Missing"} {t("p1")} {t("p2")} {t("p3")}
+
Usage Trends
Local analytics snapshot Local analytics snapshot
+
Platform Mix
+
Organizer Dashboard
+
Tickets by Event
{label} {value} {detail} {label} {value} {detail}
+
{total} events indexed
{updatedAt > 0 && (
<> · updated {new Date(updatedAt).toLocaleTimeString()}>
@@ -59,7 +59,7 @@ export default function ContractEventFeed() {
onChange={(e) =>
setStatusFilter(e.target.value as typeof statusFilter)
}
- className="rounded-lg border border-white/10 bg-[#2a2a2a] px-3 py-1.5 text-sm text-white focus:outline-none"
+ className="rounded-lg border border-zinc-300 bg-white px-3 py-1.5 text-sm text-zinc-900 focus:outline-none dark:border-white/10 dark:bg-[#2a2a2a] dark:text-white"
>
@@ -73,7 +73,7 @@ export default function ContractEventFeed() {
onChange={(e) =>
setTypeFilter(e.target.value as typeof typeFilter)
}
- className="rounded-lg border border-white/10 bg-[#2a2a2a] px-3 py-1.5 text-sm text-white focus:outline-none"
+ className="rounded-lg border border-zinc-300 bg-white px-3 py-1.5 text-sm text-zinc-900 focus:outline-none dark:border-white/10 dark:bg-[#2a2a2a] dark:text-white"
>
{(Object.keys(EVENT_TYPE_LABELS) as ContractEventType[]).map((t) => (
@@ -85,7 +85,7 @@ export default function ContractEventFeed() {
+
Listing uses{" "}
- Loading events… Loading events…
+
Event #{event.id}
+
{event.event_type}
{feedback.message}
Hash:{" "}
{feedback.tx.hash}
@@ -373,15 +371,15 @@ export default function EventCatalog() {
aria-modal="true"
aria-labelledby="event-detail-title"
>
-
+
Event #{selected.id}
+
{selected.event_type}
+
This event was canceled. Purchases are disabled.
+
Sold out.
{feedback.message}
+
{t(`${feature.key}.description`)}
+
Organizer and platform insights in one dashboard
-
- Create New Event
-
- Create Event
- Create Event
+ Attendees
-
{attendees.map((addr) => (
Update Event
- Update Event
+ {event.theme}
- {event.theme}
+ Organizer Dashboard
- Organizer Dashboard
+ Organizer Dashboard
- Organizer Dashboard
+
+
Buy multiple tickets in a single flow
-
+
Organizer and platform insights in one dashboard
- Create Event
- Create Event
+ Attendees
- Attendees
+
{attendees.map((addr) => (
Update Event
- Update Event
+ {event.theme}
- {event.theme}
+ Organizer Dashboard
- Organizer Dashboard
+ Organizer Dashboard
- Organizer Dashboard
+
+
Discover events and buy tickets on-chain
-
+
Buy and sell tickets peer-to-peer
- Your Ticket Wallet is Private
- Your Ticket Wallet is Private
+ Your Field Access
- Your Field Access
+ {ticket.theme}
- {ticket.theme}
+ Ticket Verifier
- Ticket Verifier
+
- {verificationStatus === "verifying" ?
- {verificationStatus === "verifying" ? "Analyzing..." :
- verificationStatus === "valid" ? "Access Granted" : "Access Denied"}
-
-
+
{t("title")}
- Page traffic
+ Page traffic
What users do most
+ What users do most
Event performance
+ Event performance
{event.name}
-
+ {event.name}
+
{event.conversion}% conversion
Sales distribution
+ Sales distribution
Contract Event Feed
- Contract Event Feed
+ NEXT_PUBLIC_EVENT_MANAGER_CONTRACT is
+ NEXT_PUBLIC_EVENT_MANAGER_CONTRACT is
configured.
-
+
-
+
{events.map((ev) => (
- Type
Event ID
Status
@@ -121,9 +121,9 @@ export default function ContractEventFeed() {
Tx
+
{ev.ledger}
-
+
{new Date(ev.ledgerClosedAt).toLocaleString()}
@@ -146,7 +146,7 @@ export default function ContractEventFeed() {
href={`https://stellar.expert/explorer/testnet/tx/${ev.txHash}`}
target="_blank"
rel="noopener noreferrer"
- className="font-mono text-xs text-sky-400 hover:underline"
+ className="font-mono text-xs text-sky-600 hover:underline dark:text-sky-400"
>
{ev.txHash.slice(0, 8)}…
diff --git a/soroban-client/components/EventCatalog.tsx b/soroban-client/components/EventCatalog.tsx
index 3728ed53..7d152c49 100644
--- a/soroban-client/components/EventCatalog.tsx
+++ b/soroban-client/components/EventCatalog.tsx
@@ -206,9 +206,9 @@ export default function EventCatalog() {
return (
+
NEXT_PUBLIC_EVENT_MANAGER_CONTRACT
{" "}
to your deployed EventManager contract id to enable purchases.
@@ -216,9 +216,9 @@ export default function EventCatalog() {
)}
{simulationHint && (
-
+
NEXT_PUBLIC_SOROBAN_SIM_SOURCE
{" "}
for read simulation. Connect a wallet to use your address instead.
@@ -227,7 +227,7 @@ export default function EventCatalog() {
+
{event.theme}
+
{selected.theme}
@@ -392,7 +390,7 @@ export default function EventCatalog() {
setSelected(null);
setFeedback(null);
}}
- className="rounded-xl p-2 text-zinc-400 hover:bg-white/10 hover:text-white"
+ className="rounded-xl p-2 text-zinc-500 transition hover:bg-zinc-100 hover:text-zinc-950 dark:text-zinc-400 dark:hover:bg-white/10 dark:hover:text-white"
aria-label="Close"
>
+
{selected.is_canceled ? (
-
+
{t("title")}
-
+
{t(`${feature.key}.title`)}
-