Skip to content

Zaptam Admin Dashboard ​

Complete documentation of admin features, pages, and capabilities.


Overview ​

The admin dashboard provides tools for platform management, user moderation, verification review, and analytics.

Tech Stack:

  • React 18 + Vite
  • TanStack Query (data fetching)
  • Tailwind CSS
  • Lucide Icons

Access Levels:

RoleLevelCapabilities
USER1No admin access
CURATOR2View users, review verifications/reports
ADMIN3Everything above + suspend/ban users, process payouts
SUPER_ADMIN4Full access

Dashboard Structure ​

/admin
├── Dashboard.tsx       # Main overview with stats
├── Users.tsx          # User management
├── Verifications.tsx  # Review verification requests
├── Reports.tsx        # Handle user reports
├── Transactions.tsx   # View all transactions
└── (Other pages)

Dashboard Page ​

File: admin/src/pages/Dashboard.tsx

Stats Overview ​

typescript
interface DashboardStats {
  users: {
    total: number;
    male: number;
    female: number;
    active: number;
    pending: number;
  };
  pendingVerifications: number;
  pendingReports: number;
  totalMatches: number;
}

Stat Cards ​

tsx
const stats = [
  {
    label: 'Total Users',
    value: data?.users.total || 0,
    icon: Users,
    color: 'bg-blue-500/10 text-blue-400',
    change: '+12%',
    up: true,
  },
  {
    label: 'Active Users',
    value: data?.users.active || 0,
    icon: TrendingUp,
    color: 'bg-green-500/10 text-green-400',
    change: '+8%',
    up: true,
  },
  {
    label: 'Pending Verifications',
    value: data?.pendingVerifications || 0,
    icon: Shield,
    color: 'bg-yellow-500/10 text-yellow-400',
    change: '-5%',
    up: false,
  },
  {
    label: 'Pending Reports',
    value: data?.pendingReports || 0,
    icon: AlertTriangle,
    color: 'bg-red-500/10 text-red-400',
    change: '+2',
    up: true,
  },
  {
    label: 'Total Matches',
    value: data?.totalMatches || 0,
    icon: Heart,
    color: 'bg-pink-500/10 text-pink-400',
    change: '+15%',
    up: true,
  },
];

User Breakdown ​

Visual breakdown showing:

  • Male users count + percentage bar
  • Female users count + percentage bar
  • Pending approval count + percentage bar

Quick Actions ​

Links to common tasks:

  • Review Verifications (with pending count)
  • Handle Reports (with pending count)

API Integration ​

File: admin/src/services/api.ts

typescript
import axios from 'axios';

const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000';

export const api = axios.create({
  baseURL: API_URL,
  headers: {
    'Content-Type': 'application/json',
  },
});

// Add auth token to requests
api.interceptors.request.use((config) => {
  const token = localStorage.getItem('accessToken');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

Users Page ​

File: admin/src/pages/Users.tsx

Features ​

  1. Search - Filter by alias, phone, or email
  2. Pagination - 20 users per page
  3. Status Indicators - Color-coded badges
  4. Quick Actions - Dropdown menu per user

User Table Columns ​

ColumnDescription
UserAlias + phone number
GenderMALE (blue) / FEMALE (pink)
StatusACTIVE (green), PENDING (yellow), SUSPENDED (orange), BANNED (red)
TrustTrust score number
VerificationLevel with shield icon
JoinedRegistration date
ActionsDropdown menu

Query ​

typescript
const { data, isLoading } = useQuery({
  queryKey: ['admin-users', page, search],
  queryFn: async () => {
    const params = new URLSearchParams({ 
      page: String(page), 
      limit: '20' 
    });
    if (search) params.append('search', search);
    const response = await api.get(`/api/admin/users?${params}`);
    return response.data;
  },
});

User Actions (ADMIN+ only) ​

  • View Profile
  • Suspend User
  • Ban User
  • Recalculate Trust Score

Verifications Page ​

File: admin/src/pages/Verifications.tsx

Verification Card ​

tsx
<div className="card">
  <div className="flex items-start justify-between">
    {/* User info */}
    <div className="flex items-start gap-4">
      {/* Type icon with color */}
      <div className={`w-12 h-12 rounded-xl ${typeColor}`}>
        <Shield className={`w-6 h-6 ${iconColor}`} />
      </div>
      
      <div>
        {/* User name + gender badge */}
        <h3>{user.alias || 'Anonymous'}</h3>
        <span className="badge">{user.gender}</span>
        
        {/* Verification type + current level */}
        <p>{type} Verification - Current Level: {user.verificationLevel}</p>
        
        {/* Submission time */}
        <p>Submitted {formatDate(createdAt)}</p>
        
        {/* Document count */}
        <p>{documents.length} document(s) attached</p>
      </div>
    </div>
    
    {/* Action buttons */}
    <div className="flex gap-2">
      <button onClick={() => handleReview(id, false)}>
        <X /> {/* Reject */}
      </button>
      <button onClick={() => handleReview(id, true)}>
        <Check /> {/* Approve */}
      </button>
    </div>
  </div>
</div>

Review Action ​

typescript
const reviewMutation = useMutation({
  mutationFn: async ({ id, approved, notes }) => {
    return api.patch(`/api/admin/verifications/${id}`, { approved, notes });
  },
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ['admin-verifications'] });
    queryClient.invalidateQueries({ queryKey: ['dashboard'] });
  },
});

Type Colors ​

TypeBackgroundIcon Color
IDENTITYbg-blue-500/10text-blue-400
WORTHbg-green-500/10text-green-400
VALUEbg-purple-500/10text-purple-400

Reports Page ​

File: admin/src/pages/Reports.tsx

Report Card ​

tsx
<div className="card">
  {/* Type + Status badges */}
  <span className={getTypeColor(type)}>{type.replace('_', ' ')}</span>
  <span className={getStatusColor(status)}>{status}</span>
  
  {/* Reporter and Reported info */}
  <div className="grid grid-cols-2">
    <div>
      <p>Reporter</p>
      <p>{reporter.alias}</p>
      <p>{reporter.phoneNumber}</p>
    </div>
    <div>
      <p>Reported User</p>
      <p>{reported.alias}</p>
      <p>Trust: {reported.trustScore}</p>
    </div>
  </div>
  
  {/* Description */}
  <p>{description}</p>
  
  {/* Timestamp */}
  <p>Reported {formatDate(createdAt)}</p>
  
  {/* Actions for PENDING reports */}
  {status === 'PENDING' && (
    <div>
      <button onClick={() => handleReport(id, 'dismiss')}>
        <X /> {/* Dismiss */}
      </button>
      <button onClick={() => handleReport(id, 'resolve')}>
        <AlertTriangle /> {/* Resolve + Apply Penalty */}
      </button>
    </div>
  )}
</div>

Handle Report ​

typescript
const handleMutation = useMutation({
  mutationFn: async ({ id, status, applyPenalty }) => {
    return api.patch(`/api/admin/reports/${id}`, { 
      status, 
      applyPenalty, 
      penaltyAmount: applyPenalty ? 10 : 0 
    });
  },
});

const handleReport = (id: string, action: 'resolve' | 'dismiss') => {
  const status = action === 'resolve' ? 'RESOLVED' : 'DISMISSED';
  const applyPenalty = action === 'resolve';  // Only apply penalty on resolve
  
  if (confirm(`Are you sure you want to ${action} this report?`)) {
    handleMutation.mutate({ id, status, applyPenalty });
  }
};

Type Colors ​

TypeColor
BLACKMAILbg-red-500/10 text-red-400
HARASSMENTbg-orange-500/10 text-orange-400
FAKE_PROFILEbg-yellow-500/10 text-yellow-400
INAPPROPRIATE_CONTENTbg-purple-500/10 text-purple-400
OTHERbg-gray-500/10 text-gray-400

Transactions Page ​

File: admin/src/pages/Transactions.tsx

Transaction Table ​

ColumnDescription
TypeIcon + label (CREDIT_PURCHASE, EARNING, WITHDRAWAL, etc.)
UserAlias + phone
Amount+/- with color
StatusCOMPLETED (green), PENDING (yellow), FAILED (red)
DateTransaction timestamp

Type Icons & Colors ​

typescript
const getTypeIcon = (type: string) => {
  if (['EARNING', 'CREDIT_PURCHASE'].includes(type)) {
    return <ArrowDownRight className="text-green-400" />;  // Money IN
  }
  return <ArrowUpRight className="text-red-400" />;  // Money OUT
};

Amount Display ​

tsx
<span className={
  ['EARNING', 'CREDIT_PURCHASE'].includes(tx.type)
    ? 'text-green-400'
    : 'text-red-400'
}>
  {['EARNING', 'CREDIT_PURCHASE'].includes(tx.type) ? '+' : '-'}
  ${tx.amount} {tx.currency}
</span>

Admin API Endpoints ​

Dashboard ​

GET /api/admin/dashboard

Returns user counts, pending items.

Users ​

GET /api/admin/users
GET /api/admin/users/:id
PATCH /api/admin/users/:id           # ADMIN+
POST /api/admin/users/:id/suspend    # ADMIN+
POST /api/admin/users/:id/ban        # ADMIN+
POST /api/admin/users/:id/recalculate-trust

Verifications ​

GET /api/admin/verifications
PATCH /api/admin/verifications/:id

Reports ​

GET /api/admin/reports
GET /api/admin/reports/:id
PATCH /api/admin/reports/:id

Transactions & Payouts ​

GET /api/admin/transactions
GET /api/admin/payouts
PATCH /api/admin/payouts/:id    # ADMIN+

Analytics ​

GET /api/admin/analytics?days=30

Analytics Endpoint ​

File: api/src/controllers/admin/analytics.controller.ts

Available Data ​

typescript
// User growth over time
const userGrowth = await prisma.$queryRaw`
  SELECT DATE(created_at) as date, COUNT(*) as count
  FROM users
  WHERE created_at >= ${startDate}
  GROUP BY DATE(created_at)
  ORDER BY date ASC
`;

// Matches over time
const matchGrowth = await prisma.$queryRaw`
  SELECT DATE(created_at) as date, COUNT(*) as count
  FROM interests
  WHERE status = 'ACCEPTED' AND created_at >= ${startDate}
  GROUP BY DATE(created_at)
  ORDER BY date ASC
`;

// Verification stats by status
const verificationStats = await prisma.verification.groupBy({
  by: ['status'],
  _count: true,
});

// Report stats by type
const reportStats = await prisma.report.groupBy({
  by: ['type'],
  _count: true,
});

Response ​

json
{
  "success": true,
  "data": {
    "userGrowth": [
      { "date": "2024-01-01", "count": 50 },
      { "date": "2024-01-02", "count": 45 },
      ...
    ],
    "matchGrowth": [
      { "date": "2024-01-01", "count": 25 },
      ...
    ],
    "verificationStats": [
      { "status": "PENDING", "count": 15 },
      { "status": "APPROVED", "count": 200 },
      { "status": "REJECTED", "count": 30 }
    ],
    "reportStats": [
      { "type": "HARASSMENT", "count": 10 },
      { "type": "FAKE_PROFILE", "count": 5 },
      ...
    ]
  }
}

Component Library ​

StatCard ​

tsx
<div className="card">
  <div className="flex items-center gap-4">
    <div className={`w-12 h-12 rounded-xl ${stat.color}`}>
      <stat.icon className="w-6 h-6" />
    </div>
    <div>
      <p className="text-sm text-gray-400">{stat.label}</p>
      <p className="text-2xl font-bold">{stat.value}</p>
    </div>
  </div>
  <div className="mt-4 flex items-center gap-1 text-sm">
    {stat.up ? <TrendingUp /> : <TrendingDown />}
    <span>{stat.change}</span>
    <span className="text-gray-500">vs last month</span>
  </div>
</div>

Badge ​

tsx
<span className={cn(
  "px-2 py-1 rounded text-sm",
  status === 'ACTIVE' && 'bg-green-500/10 text-green-400',
  status === 'PENDING' && 'bg-yellow-500/10 text-yellow-400',
  status === 'SUSPENDED' && 'bg-orange-500/10 text-orange-400',
  status === 'BANNED' && 'bg-red-500/10 text-red-400',
)}>
  {status}
</span>

DataTable ​

Loading state with skeleton:

tsx
{isLoading ? (
  [...Array(5)].map((_, i) => (
    <tr key={i}>
      <td colSpan={7} className="p-4">
        <div className="h-8 bg-dark-700 rounded animate-pulse" />
      </td>
    </tr>
  ))
) : /* data rows */}

Admin Actions Summary ​

ActionMin RoleDescription
View DashboardCURATORSee platform stats
View UsersCURATORBrowse user list
View User DetailCURATORSee user profile + history
Update UserADMINChange status, trust, verification
Suspend UserADMINSet status to SUSPENDED
Ban UserADMINSet status to BANNED, revoke tokens
Recalculate TrustCURATORRefresh trust score
View VerificationsCURATORSee pending verifications
Review VerificationCURATORApprove or reject
View ReportsCURATORSee all reports
Handle ReportCURATORResolve or dismiss
View TransactionsCURATORSee all transactions
View PayoutsCURATORSee withdrawal requests
Process PayoutADMINApprove or reject withdrawals
View AnalyticsCURATORSee growth charts

Security Considerations ​

  1. Role Validation - All admin routes check requireMinRole('CURATOR')
  2. Action Logging - Important actions include adminId in reference
  3. Token Revocation - Banning a user revokes all their tokens
  4. Confirmation Prompts - Destructive actions require confirmation

Future Enhancements ​

  1. Activity Logs - Track all admin actions
  2. Bulk Actions - Select multiple users/reports
  3. Export Data - CSV/Excel exports
  4. Real-time Updates - WebSocket for live stats
  5. Email Templates - Send notifications to users
  6. Advanced Filters - Date ranges, multiple filters

One chat. Everything done.