← Back to all products
$10
Subscription Dashboard
Dashboard for managing subscribers, revenue, churn, and plan analytics.
TypeScriptMarkdownJSONReact
📁 File Structure 16 files
subscription-dashboard/
├── LICENSE
├── README.md
├── package.json
├── security-notes.md
├── src/
│ ├── App.tsx
│ ├── components/
│ │ ├── ChurnChart.tsx
│ │ ├── MRRChart.tsx
│ │ ├── MetricCards.tsx
│ │ ├── SubscriberTable.tsx
│ │ └── TierBreakdown.tsx
│ ├── hooks/
│ │ └── useSubscriptionData.ts
│ ├── index.css
│ ├── main.tsx
│ └── types/
│ └── index.ts
├── test/
│ └── dashboard.test.ts
└── tsconfig.json
📖 Documentation Preview README excerpt
Subscription Dashboard
Sub Protocol Product #6 — CryptoForge Store #5
Real-time subscription analytics dashboard built with React, TypeScript, and Recharts. Connects directly to the MembershipNFT contract to display MRR, churn analysis, tier distribution, and subscriber management.
Features
- Real-time KPI cards: active subscribers, MRR, churn rate, ARPU
- MRR trend area chart with new vs churned revenue
- Churn analysis composed chart (bars + line)
- Donut chart for tier distribution breakdown
- Sortable, filterable, paginated subscriber table
- CryptoForge dark industrial theme (--cf-* variables)
- Responsive layout with Tailwind CSS
- Loading skeletons for all components
Quick Start
# Install dependencies
npm install
# Configure environment
cp .env.example .env
# Set VITE_MEMBERSHIP_CONTRACT, VITE_RPC_URL, VITE_CHAIN_ID
# Development
npm run dev
# Build for production
npm run build
# Run tests
npm test
Architecture
src/
├── App.tsx # Main dashboard layout
├── main.tsx # React entry point
├── index.css # Tailwind + CryptoForge theme
├── components/
│ ├── MetricCards.tsx # KPI summary cards
│ ├── MRRChart.tsx # MRR area chart (Recharts)
│ ├── ChurnChart.tsx # Churn composed chart
│ ├── SubscriberTable.tsx # Paginated subscriber table
│ └── TierBreakdown.tsx # Tier donut chart + stats
├── hooks/
│ └── useSubscriptionData.ts # Contract data fetching hook
└── types/
└── index.ts # TypeScript type definitions
Environment Variables
| Variable | Description |
|---|
... continues with setup instructions, usage examples, and more.
📄 Code Sample .ts preview
src/hooks/useSubscriptionData.ts
// ╔══════════════════════════════════════════════════════════════════════╗
// ║ SUBSCRIPTION DASHBOARD — CONTRACT HOOK ║
// ║ Sub Protocol #6 — CryptoForge ║
// ╚══════════════════════════════════════════════════════════════════════╝
import { useState, useEffect, useCallback } from "react";
import { ethers } from "ethers";
import type {
Subscriber,
TierMetrics,
DashboardMetrics,
MRRDataPoint,
ChurnDataPoint,
ContractConfig,
SubscriptionStatus,
} from "../types";
const MEMBERSHIP_ABI = [
"function totalSupply() view returns (uint256)",
"function tokenByIndex(uint256 index) view returns (uint256)",
"function ownerOf(uint256 tokenId) view returns (address)",
"function getTier(uint256 tokenId) view returns (uint256 tierId, string name, uint256 price, uint256 duration, uint256 gracePeriod, uint256 maxSupply, uint256 minted)",
"function getExpiration(uint256 tokenId) view returns (uint256)",
"function isActive(uint256 tokenId) view returns (bool)",
"function tierCount() view returns (uint256)",
"event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)",
];
/**
* Hook to fetch and compute subscription analytics from the MembershipNFT contract.
*
* @param config - Contract address, chain ID, and RPC URL
* @returns Dashboard metrics, subscriber list, chart data, loading/error state
*/
export function useSubscriptionData(config: ContractConfig) {
const [subscribers, setSubscribers] = useState<Subscriber[]>([]);
const [tierMetrics, setTierMetrics] = useState<TierMetrics[]>([]);
const [dashboardMetrics, setDashboardMetrics] = useState<DashboardMetrics | null>(null);
const [mrrHistory, setMrrHistory] = useState<MRRDataPoint[]>([]);
const [churnHistory, setChurnHistory] = useState<ChurnDataPoint[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const TIER_COLORS = ["#FF6B00", "#3B82F6", "#F59E0B", "#10B981", "#8B5CF6"];
const fetchData = useCallback(async () => {
try {
setLoading(true);
setError(null);
# ... 142 more lines ...