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.