Components
All shared UI lives under frontend/src/components/. Pages only import from here — no cross-page imports.
SiteNav
components/SiteNav.tsx
Public top navigation bar, used on all public-facing pages (/campaigns, /campaign/:id, /trust-score, /s/:username, etc.).
Props:
{ activePath?: string }Features:
- Verifluence logo (links to
/) - Role-based right-side controls: sign-in button when unauthenticated, avatar menu with profile / sign-out when streamer session is active
- Mobile hamburger menu
- Active link highlight driven by
activePath - Handles sign-out via
AuthContext
CampaignCardPublic
components/CampaignCardPublic.tsx
Campaign card shown to unauthenticated visitors and in the public marketplace. Built with Flowbite Button and Badge components; uses semantic Tailwind classes throughout.
Props:
{
campaign: Campaign;
onApply: (id: string) => void;
applying?: boolean;
}Card layout (top → bottom):
- Status badge — absolute top-right corner (
StatusBadge type="campaign") - Operator avatar + name, category badge
- Campaign name (2-line clamp)
- Platform badges, GEOs, language chip, duration
- Budget progress bar
- Requirements row (min followers, viewers)
- Apply button — Flowbite
<Button>withArrowRighticon, disabled / loading states
Apply button states:
| State | Appearance |
|---|---|
| Default | Filled violet "Apply now →" |
| Applying | Loading spinner + "Applying…" |
| Closed / expired | Disabled gray "Closed" |
| Already applied | Disabled green "Re-apply" |
CampaignCardVerified
components/CampaignCardVerified.tsx
Authenticated-streamer variant of the campaign card. Extends CampaignCardPublic with application status awareness — shows "Applied ✓" if the streamer has already applied to this campaign.
StatusBadge
components/StatusBadge.tsx
Unified status pill component. Renders a Flowbite Badge with the correct colour for each status string.
Props:
{
type: "campaign" | "deal" | "offer" | "negotiation" | "submission" | "kyc";
status: string;
size?: "sm" | "md";
}Campaign status → colour:
| Status | Colour |
|---|---|
prepared / draft | Gray — "Draft" |
funded | Amber — "Funded" |
in_progress | Blue — "In Progress" |
completed | Green — "Completed" |
cancelled | Red — "Cancelled" |
FundDonut
components/FundDonut.tsx
SVG donut chart showing campaign budget allocation in four segments:
| Segment | Colour |
|---|---|
| Rewarded (paid out) | Green |
| Escrowed (allocated, pending) | Violet |
| Refunded | Amber |
| Unallocated (remaining) | Gray |
Props:
{
size: number; // diameter in px (e.g. 32, 36, 72)
rewarded: number;
escrowed: number;
refunded: number;
total: number;
showLegend?: boolean; // full legend below chart (72px variant)
}Used at three sizes: 32 px (ActivePage list), 36 px (CampaignsPage list), 72 px (CampaignsPage detail panel).
FundCampaignPanel
components/FundCampaignPanel.tsx
On-chain escrow deposit flow. Shown inside the CampaignsPage detail view when a campaign has status prepared and no escrow_id yet.
Flow:
- Connect MetaMask wallet
- Select deposit amount (pre-filled to
budget_cap_usdc) - Approve ERC-20 spend allowance
- Call
deposit()on the Escrow contract - Show tx hash + confirmation
InviteCampaignModal
components/InviteCampaignModal.tsx
Operator modal for inviting a specific streamer to one of their campaigns. Fully migrated to Flowbite Modal + semantic token colours.
Props:
{
streamer: { id: number; username: string };
onClose: () => void;
}Sections:
- Campaign selector (list of operator's active campaigns)
- Terms form (pre-filled from campaign minimums, all 5 negotiable fields)
- Custom invite message textarea
- Offer terms summary banner
- Submit / Cancel buttons
NegTermEditor
components/NegTermEditor.tsx
Bilateral negotiation terms form. Renders all five negotiation fields (timeframe_days, deliveries, min_viewers, min_duration, payment_per_stream) with per-field agree/propose controls and colour-coded agreement state.
Used inside NegotiationCard (streamer view) and OperatorNegotiationCard.
StreamerAvatar
components/StreamerAvatar.tsx
Avatar component with graceful fallback.
Priority: Kick profile image → initials circle (styled with semantic tokens).
Props:
{
username: string;
avatarUrl?: string | null;
size?: number; // px, default 40
}AppFooter
components/AppFooter.tsx
Site footer. Nav links to key public pages, social links, copyright line.
VFFooter.tsxis a legacy alias kept for backward compatibility — new code should importAppFooter.
Subdirectory components
components/dashboard/
| Component | Purpose |
|---|---|
ActivityRow | Single item in the streamer activity feed |
EarningsTile | Summary tile (Total Earned, This Month, etc.) |
components/deals/
| Component | Purpose |
|---|---|
DealCard | Streamer deal summary card |
OperatorDealCard | Operator deal summary card |
DealAllocationCard | Slot allocation stepper + submission panel |
AllocationStepper | Visual slot progress bar |
SlotRow | Single slot row inside DealAllocationCard |
StreamSubmissionRow | Submission attempt row with status badge |
FundsDonut | Inline mini donut for deal payout progress |
OnChainOpsList | List of on-chain allocation operations |
AgreedTermsBadges | Compact badge row for agreed deal terms |
components/negotiations/
| Component | Purpose |
|---|---|
NegotiationCard | Streamer-side negotiation card |
OperatorNegotiationCard | Operator-side negotiation card |
NegStatusBanner | Status banner at top of negotiation view |
EmptyNegotiationsState | Empty state illustration + CTA |
components/offers/
Offer list items and per-role offer cards.
components/personal/
Profile settings sub-panels (channel list, bio editor, visibility toggles).
components/streamer/delivery/
Delivery proof submission panel and tracked session picker.
Flowbite component usage
The following Flowbite React components are in active use:
| Component | Imported from |
|---|---|
Button | flowbite-react |
Badge | flowbite-react |
Alert | flowbite-react |
Spinner | flowbite-react |
Modal, ModalBody, ModalHeader, ModalFooter | flowbite-react |
Table, TableHead, TableHeadCell, TableBody, TableRow, TableCell | flowbite-react |
Tooltip | flowbite-react |
Icons come from flowbite-react-icons (imported individually):
import { ArrowRight, CheckCircle, UserCircle } from "flowbite-react-icons/outline";Flowbite dark mode
Flowbite respects the html.dark class. Combined with the semantic token system, dark mode requires no per-component overrides — set the class and every surface adapts.