From f6d9cd7903488b0d5042939e1c2de9210c94416c Mon Sep 17 00:00:00 2001 From: mr-mangoranda Date: Sat, 20 Jun 2026 01:53:01 +0800 Subject: [PATCH] refractor(admin): transferred all files to admin branch --- frontend/src/App.jsx | 14 +- .../components/admin/ActiveDisputeQueue.jsx | 103 +++++++++ .../src/components/admin/AdminActionModal.jsx | 63 ++++++ frontend/src/components/admin/AdminHeader.jsx | 55 +++++ .../src/components/admin/AdminStatCard.jsx | 28 +++ .../components/admin/ApplicationsTable.jsx | 122 +++++++++++ .../src/components/admin/AuditSummaryCard.jsx | 36 ++++ .../src/components/admin/BatchVerifyFAB.jsx | 18 ++ .../components/admin/BulkActionOverlay.jsx | 61 ++++++ .../admin/CategoryPerformanceTable.jsx | 93 ++++++++ .../components/admin/DisputeReasonsCard.jsx | 49 +++++ .../src/components/admin/ModerationTable.jsx | 175 +++++++++++++++ frontend/src/components/admin/Pagination.jsx | 40 ++++ .../src/components/admin/PayoutHealthCard.jsx | 49 +++++ .../components/admin/PlatformGrowthChart.jsx | 76 +++++++ .../components/admin/RecentActivityCard.jsx | 57 +++++ .../src/components/admin/SystemAlerts.jsx | 53 +++++ .../components/admin/TransactionLogsTable.jsx | 144 +++++++++++++ .../components/admin/UserDirectoryTable.jsx | 162 ++++++++++++++ frontend/src/pages/admin/disputes.jsx | 192 +++++++++++++++++ frontend/src/pages/admin/logs.jsx | 168 +++++++++++++++ frontend/src/pages/admin/moderate.jsx | 161 ++++++++++++++ frontend/src/pages/admin/overview.jsx | 201 ++++++++++++++++++ frontend/src/pages/admin/users.jsx | 147 +++++++++++++ frontend/src/pages/admin/verify.jsx | 158 ++++++++++++++ 25 files changed, 2424 insertions(+), 1 deletion(-) create mode 100644 frontend/src/components/admin/ActiveDisputeQueue.jsx create mode 100644 frontend/src/components/admin/AdminActionModal.jsx create mode 100644 frontend/src/components/admin/AdminHeader.jsx create mode 100644 frontend/src/components/admin/AdminStatCard.jsx create mode 100644 frontend/src/components/admin/ApplicationsTable.jsx create mode 100644 frontend/src/components/admin/AuditSummaryCard.jsx create mode 100644 frontend/src/components/admin/BatchVerifyFAB.jsx create mode 100644 frontend/src/components/admin/BulkActionOverlay.jsx create mode 100644 frontend/src/components/admin/CategoryPerformanceTable.jsx create mode 100644 frontend/src/components/admin/DisputeReasonsCard.jsx create mode 100644 frontend/src/components/admin/ModerationTable.jsx create mode 100644 frontend/src/components/admin/Pagination.jsx create mode 100644 frontend/src/components/admin/PayoutHealthCard.jsx create mode 100644 frontend/src/components/admin/PlatformGrowthChart.jsx create mode 100644 frontend/src/components/admin/RecentActivityCard.jsx create mode 100644 frontend/src/components/admin/SystemAlerts.jsx create mode 100644 frontend/src/components/admin/TransactionLogsTable.jsx create mode 100644 frontend/src/components/admin/UserDirectoryTable.jsx create mode 100644 frontend/src/pages/admin/disputes.jsx create mode 100644 frontend/src/pages/admin/logs.jsx create mode 100644 frontend/src/pages/admin/moderate.jsx create mode 100644 frontend/src/pages/admin/overview.jsx create mode 100644 frontend/src/pages/admin/users.jsx create mode 100644 frontend/src/pages/admin/verify.jsx diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 5494806..2d2af51 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -2,11 +2,23 @@ import { BrowserRouter, Routes, Route } from 'react-router-dom'; import RootLayout from './layouts/RootLayout'; import { BuyerSidebar, SellerSidebar, AdminSidebar } from './components/RoleBasedSidebar'; import WorkspaceView from './components/WorkspaceView'; +import Overview from './pages/admin/overview'; +import Verify from './pages/admin/verify'; +import Moderate from './pages/admin/moderate'; +import Users from './pages/admin/users'; +import Logs from './pages/admin/logs'; +import Disputes from './pages/admin/disputes'; const App = () => ( - } />}> + } />} > + } /> + } /> + } /> + } /> + } /> + } /> } /> diff --git a/frontend/src/components/admin/ActiveDisputeQueue.jsx b/frontend/src/components/admin/ActiveDisputeQueue.jsx new file mode 100644 index 0000000..aad0851 --- /dev/null +++ b/frontend/src/components/admin/ActiveDisputeQueue.jsx @@ -0,0 +1,103 @@ +import React from 'react'; +import { ArrowUpDown, SlidersHorizontal } from 'lucide-react'; +import Pagination from './Pagination'; + +const ReasonBadge = ({ reason }) => { + return ( + + {reason} + + ); +}; + +const ActiveDisputeQueue = ({ data, onRowClick, onFilterClick, onSortClick }) => { + return ( +
+ {/* Header */} +
+

Active Dispute Queue

+
+ + +
+ + 1-4 of 24 + +
+
+ + {/* Table Content */} +
+ + + + + + + + + + + + {data.map((row, i) => ( + onRowClick(row.id)} + className={`border-b border-neutral-dark/5 hover:bg-neutral-dark/5 transition-colors cursor-pointer group/row ${i === data.length - 1 ? 'border-b-0' : ''}`} + > + + + + + + + ))} + +
Dispute IDArtisan / ShopCustomerReasonAmount
+ + {row.id} + + +
+ {row.shopName} +
+ + {row.shopName} + +
+ + {row.customerName} + + + + + + {row.amount} + +
+
+ + {/* Footer / Pagination */} +
+
+ Showing 1-4 of 24 open disputes +
+
+ +
+
+
+ ); +}; + +export default ActiveDisputeQueue; diff --git a/frontend/src/components/admin/AdminActionModal.jsx b/frontend/src/components/admin/AdminActionModal.jsx new file mode 100644 index 0000000..90f832a --- /dev/null +++ b/frontend/src/components/admin/AdminActionModal.jsx @@ -0,0 +1,63 @@ +import React, { useEffect } from 'react'; +import { X, Info } from 'lucide-react'; + +const AdminActionModal = ({ isOpen, onClose, title, message }) => { + useEffect(() => { + if (isOpen) { + document.body.style.overflow = 'hidden'; + } else { + document.body.style.overflow = 'unset'; + } + return () => { + document.body.style.overflow = 'unset'; + }; + }, [isOpen]); + + if (!isOpen) return null; + + return ( +
+ {/* Backdrop */} +
+ + {/* Modal Dialog */} +
+ {/* Header */} +
+

+ + {title} +

+ +
+ + {/* Content */} +
+

+ {message || "This feature is currently under development for the Admin Portal. Check back later!"} +

+
+ + {/* Footer */} +
+ +
+
+
+ ); +}; + +export default AdminActionModal; diff --git a/frontend/src/components/admin/AdminHeader.jsx b/frontend/src/components/admin/AdminHeader.jsx new file mode 100644 index 0000000..6185d77 --- /dev/null +++ b/frontend/src/components/admin/AdminHeader.jsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { Search, Bell, HelpCircle } from 'lucide-react'; + +const AdminHeader = ({ + searchPlaceholder = "Search for shops, orders, or reports...", + primaryActionText = "Export Report", + onPrimaryActionClick, + secondaryActionText, + secondaryActionIcon: SecondaryActionIcon, + onSecondaryActionClick +}) => { + return ( +
+ {/* Search Bar */} +
+ + +
+ + {/* Right Controls */} +
+
+ {secondaryActionText && ( + + )} + +
+ + +
+
+ ); +}; + +export default AdminHeader; diff --git a/frontend/src/components/admin/AdminStatCard.jsx b/frontend/src/components/admin/AdminStatCard.jsx new file mode 100644 index 0000000..1ff34ad --- /dev/null +++ b/frontend/src/components/admin/AdminStatCard.jsx @@ -0,0 +1,28 @@ +import React from 'react'; + +const AdminStatCard = ({ title, value, subtext, subtextColor = 'text-neutral-dark/60', icon: Icon, iconBgClass = 'bg-[#F5EDE8]', iconColorClass = 'text-primary', accentClass = '' }) => { + return ( +
+
+

+ {title} +

+ {Icon && ( +
+ +
+ )} +
+ +
+
+ {value} +
+
+ {subtext} +
+
+
+ ); +}; +export default AdminStatCard; \ No newline at end of file diff --git a/frontend/src/components/admin/ApplicationsTable.jsx b/frontend/src/components/admin/ApplicationsTable.jsx new file mode 100644 index 0000000..bbac8a5 --- /dev/null +++ b/frontend/src/components/admin/ApplicationsTable.jsx @@ -0,0 +1,122 @@ +import React from 'react'; +import { Filter, Download, ChevronRight } from 'lucide-react'; +import Pagination from './Pagination'; + +const StatusBadge = ({ status }) => { + const isFlagged = status === 'FLAGGED'; + return ( + + {status} + + ); +}; + +const ScoreBar = ({ score }) => { + const isLow = score < 50; + const colorClass = isLow ? 'bg-destructive' : 'bg-[#8C5233]'; + + return ( +
+
+ {score} +
+
+
+
+
+ ); +}; + +const ApplicationsTable = ({ data, onFilterClick, onDownloadClick, onRowClick }) => { + return ( +
+ {/* Header */} +
+

Applications Overview

+
+ + +
+
+ + {/* Table Content */} +
+ + + + + + + + + + + + + {data.map((row, i) => ( + onRowClick(row.name)} + className={`border-b border-neutral-dark/5 hover:bg-neutral-dark/5 transition-colors group/row cursor-pointer ${i === data.length - 1 ? 'border-b-0' : ''}`} + > + + + + + + + + ))} + +
Shop NameCategoryApplied OnScoreStatus
+
+ +
+
+
+ {row.name} +
+
+ {row.id} +
+
+
+ {row.category} + + {row.appliedOn} + + + + + + +
+
+ + {/* Footer / Pagination */} +
+
+ Showing 4 of 42 pending shops +
+
+ +
+
+
+ ); +}; + +export default ApplicationsTable; diff --git a/frontend/src/components/admin/AuditSummaryCard.jsx b/frontend/src/components/admin/AuditSummaryCard.jsx new file mode 100644 index 0000000..e320b85 --- /dev/null +++ b/frontend/src/components/admin/AuditSummaryCard.jsx @@ -0,0 +1,36 @@ +import React from 'react'; + +const AuditSummaryCard = ({ onReviewClick, onDownloadClick }) => { + return ( +
+ {/* Background Graphic Suggestion */} +
+ +
+

+ Automated Audit Summary +

+

+ The platform AI has analyzed the last 500 transactions. No systemic anomalies detected, but 2 shops are flagged for manual review based on volume spikes. +

+
+ +
+ + +
+
+ ); +}; + +export default AuditSummaryCard; diff --git a/frontend/src/components/admin/BatchVerifyFAB.jsx b/frontend/src/components/admin/BatchVerifyFAB.jsx new file mode 100644 index 0000000..b1776c1 --- /dev/null +++ b/frontend/src/components/admin/BatchVerifyFAB.jsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { BadgeCheck } from 'lucide-react'; + +const BatchVerifyFAB = ({ count = 5, onClick }) => { + return ( + + ); +}; + +export default BatchVerifyFAB; diff --git a/frontend/src/components/admin/BulkActionOverlay.jsx b/frontend/src/components/admin/BulkActionOverlay.jsx new file mode 100644 index 0000000..8bb2a4e --- /dev/null +++ b/frontend/src/components/admin/BulkActionOverlay.jsx @@ -0,0 +1,61 @@ +import React from 'react'; +import { Check, XCircle, CornerRightUp } from 'lucide-react'; + +const BulkActionOverlay = ({ selectedCount = 4, onActionClick }) => { + if (selectedCount === 0) return null; + + return ( +
+
+ + {/* Selection Count */} +
+
+ {selectedCount} +
+ + SELECTED + +
+ + {/* Divider */} +
+ + {/* Quick Actions */} +
+ + + +
+ + {/* Primary Action */} + +
+
+ ); +}; + +export default BulkActionOverlay; diff --git a/frontend/src/components/admin/CategoryPerformanceTable.jsx b/frontend/src/components/admin/CategoryPerformanceTable.jsx new file mode 100644 index 0000000..1e639fe --- /dev/null +++ b/frontend/src/components/admin/CategoryPerformanceTable.jsx @@ -0,0 +1,93 @@ +import React from 'react'; +import { Filter, ChevronDown, ChevronRight } from 'lucide-react'; + +const StatusBadge = ({ status }) => { + const isTrending = status === 'TRENDING'; + return ( + + {status} + + ); +}; + +const CategoryPerformanceTable = ({ data, onViewDetailedAnalyticsClick, onAllRegionsClick }) => { + return ( +
+ {/* Header */} +
+

Shop Performance by Category

+
+ + +
+
+ + {/* Table Content */} +
+ + + + + + + + + + + + + {data.map((row, i) => ( + + + + + + + + + ))} + +
CategoryActive ShopsAvg. SalesGrowthStatus
+
+ +
+ + {row.category.name} + +
+ {row.activeShops} + + {row.avgSales} + + + {row.growth} + + + + + +
+
+ + {/* Footer Link */} +
+ + View Detailed Analytics + +
+
+ ); +}; + +export default CategoryPerformanceTable; diff --git a/frontend/src/components/admin/DisputeReasonsCard.jsx b/frontend/src/components/admin/DisputeReasonsCard.jsx new file mode 100644 index 0000000..18e5473 --- /dev/null +++ b/frontend/src/components/admin/DisputeReasonsCard.jsx @@ -0,0 +1,49 @@ +import React from 'react'; + +const ReasonBar = ({ label, percentage }) => { + return ( +
+
+ + {label} + + + {percentage}% + +
+
+
+
+
+ ); +}; + +const DisputeReasonsCard = ({ data, trend }) => { + return ( +
+

+ Dispute Reasons +

+ +
+ {data.map((item, i) => ( + + ))} +
+ +
+

+ Trend Analysis +

+

+ {trend} +

+
+
+ ); +}; + +export default DisputeReasonsCard; diff --git a/frontend/src/components/admin/ModerationTable.jsx b/frontend/src/components/admin/ModerationTable.jsx new file mode 100644 index 0000000..06a9d54 --- /dev/null +++ b/frontend/src/components/admin/ModerationTable.jsx @@ -0,0 +1,175 @@ +import React from 'react'; +import { ChevronDown, Download } from 'lucide-react'; +import Pagination from './Pagination'; + +const ViolationBadge = ({ type }) => { + let styleClass = "bg-neutral-dark/10 text-neutral-dark/60"; // Default + if (type === 'COPYRIGHT') { + styleClass = "bg-[#F8E2DF] text-destructive"; // Pink/Red + } else if (type === 'QUALITY') { + styleClass = "bg-[#FDF3E1] text-[#B7791F]"; // Yellow/Orange + } else if (type === 'PROHIBITED') { + styleClass = "bg-[#E2E8F0] text-[#4A5568]"; // Gray + } + + return ( + + {type} + + ); +}; + +const SeverityBar = ({ score }) => { + let colorClass = "bg-[#CBD5E1] text-[#64748B]"; // Default gray (<30) + if (score >= 80) { + colorClass = "bg-destructive text-destructive"; // High (red) + } else if (score >= 40) { + colorClass = "bg-[#ED8936] text-[#DD6B20]"; // Medium (orange) + } + + return ( +
+
+ {score} +
+
+
+
+
+ ); +}; + +const StatusDisplay = ({ status }) => { + if (status === 'ESCALATED') { + return ( +
+
+ + ESCALATED + +
+ ); + } + return ( + + {status} + + ); +}; + +const ModerationTable = ({ data, onFilterClick, onDownloadClick, onReviewClick }) => { + return ( +
+ {/* Header */} +
+

Moderation Queue

+
+ + +
+ +
+
+ + {/* Table Content */} +
{/* Extra padding bottom for the floating bar */} + + + + + + + + + + + + + {data.map((row, i) => ( + + + + + + + + + ))} + +
Product & IDShop NameViolationFlagged OnSeverityStatus
+
+ {row.productName} +
+
+
+ {row.productName} +
+
+ {row.id} +
+
+
+ + {row.shopName} + + + + +
+ {row.flaggedDate} +
+
+ {row.flaggedTime} +
+
+ + +
+ + +
+
+
+ + {/* Footer / Pagination */} +
+
+ Showing 1 to 10 of 124 tickets +
+
+ +
+
+
+ ); +}; + +export default ModerationTable; diff --git a/frontend/src/components/admin/Pagination.jsx b/frontend/src/components/admin/Pagination.jsx new file mode 100644 index 0000000..a4ed84a --- /dev/null +++ b/frontend/src/components/admin/Pagination.jsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { ChevronLeft, ChevronRight } from 'lucide-react'; + +const Pagination = () => { + return ( +
+ {/* Previous Button */} + + + {/* Page Numbers */} + + + + + {/* Ellipsis */} + + ... + + + + + {/* Next Button */} + +
+ ); +}; + +export default Pagination; diff --git a/frontend/src/components/admin/PayoutHealthCard.jsx b/frontend/src/components/admin/PayoutHealthCard.jsx new file mode 100644 index 0000000..b923174 --- /dev/null +++ b/frontend/src/components/admin/PayoutHealthCard.jsx @@ -0,0 +1,49 @@ +import React from 'react'; + +const HealthBar = ({ label, percentage }) => { + const isHealthy = percentage >= 95; + const colorClass = isHealthy ? "bg-[#16A34A] text-[#16A34A]" : "bg-destructive text-destructive"; + + return ( +
+
+ + {label} + + + {percentage}% + +
+
+
+
+
+ ); +}; + +const PayoutHealthCard = ({ data, lastUpdated }) => { + return ( +
+

+ Payout Method Health +

+ +
+ {data.map((item, i) => ( + + ))} +
+ +
+ + Last updated: {lastUpdated} + +
+
+ ); +}; + +export default PayoutHealthCard; diff --git a/frontend/src/components/admin/PlatformGrowthChart.jsx b/frontend/src/components/admin/PlatformGrowthChart.jsx new file mode 100644 index 0000000..1f5930c --- /dev/null +++ b/frontend/src/components/admin/PlatformGrowthChart.jsx @@ -0,0 +1,76 @@ +import React from 'react'; + +const PlatformGrowthChart = ({ data }) => { + // data is expected to be an array of objects: { label: 'OCT 01', value: 40, isHighlighted: false, tooltip: null } + const maxVal = Math.max(...data.map(d => d.value), 1); + + return ( +
+
+
+

+ PLATFORM GROWTH +

+

+ Merchant acquisition vs Transaction volume +

+
+
+ + + +
+
+ + {/* Bar Chart Area */} +
+
+ {data.map((item, i) => { + const heightPercent = (item.value / maxVal) * 100; + return ( +
+ {/* Tooltip for highlighted bar */} + {item.isHighlighted && item.tooltip && ( +
+
+ PEAK PERFORMANCE +
+
+ {item.tooltip.value} +
+
+ {item.tooltip.date} +
+ {/* Arrow down */} +
+
+ )} +
+ ); + })} +
+ + {/* X-Axis Labels */} +
+ {data.map((item, i) => ( + + {item.showLabel ? item.label : ''} + + ))} +
+
+
+ ); +}; + +export default PlatformGrowthChart; diff --git a/frontend/src/components/admin/RecentActivityCard.jsx b/frontend/src/components/admin/RecentActivityCard.jsx new file mode 100644 index 0000000..48fa3fa --- /dev/null +++ b/frontend/src/components/admin/RecentActivityCard.jsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { CheckCircle, MessageSquare } from 'lucide-react'; + +const ActivityItem = ({ type, text, timeAgo }) => { + const Icon = type === 'resolution' ? CheckCircle : MessageSquare; + const iconColor = type === 'resolution' ? 'text-[#8C5233]' : 'text-destructive'; + const iconBg = type === 'resolution' ? 'bg-[#8C5233]/10' : 'bg-destructive/10'; + + return ( +
+
+ +
+
+

+

+ {timeAgo} +

+
+
+ ); +}; + +const RecentActivityCard = ({ activities, onViewAllClick }) => { + return ( +
+

+ Recent Activity +

+ +
+ {activities.map((activity, i) => ( + + ))} +
+ +
+ +
+
+ ); +}; + +export default RecentActivityCard; diff --git a/frontend/src/components/admin/SystemAlerts.jsx b/frontend/src/components/admin/SystemAlerts.jsx new file mode 100644 index 0000000..e08955f --- /dev/null +++ b/frontend/src/components/admin/SystemAlerts.jsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { MoreVertical, ArrowRight } from 'lucide-react'; + +const SystemAlerts = ({ alerts, onViewAllClick }) => { + return ( +
+
+

+ SYSTEM ALERTS +

+ +
+ +
+ {alerts.map((alert, index) => ( +
+ {/* Indicator Dot */} +
+
+
+ {/* Content */} +
+

+ {alert.title} +

+

+ {alert.message} +

+
+ {alert.timeAgo} +
+
+
+ ))} +
+ + {/* Footer Link */} +
+ +
+
+ ); +}; + +export default SystemAlerts; diff --git a/frontend/src/components/admin/TransactionLogsTable.jsx b/frontend/src/components/admin/TransactionLogsTable.jsx new file mode 100644 index 0000000..d59395c --- /dev/null +++ b/frontend/src/components/admin/TransactionLogsTable.jsx @@ -0,0 +1,144 @@ +import React from 'react'; +import { ChevronDown, Eye } from 'lucide-react'; +import Pagination from './Pagination'; + +const TypeBadge = ({ type }) => { + return ( + + {type} + + ); +}; + +const AmountDisplay = ({ amount, type }) => { + let colorClass = "text-neutral-dark"; + if (type === 'SALE') colorClass = "text-[#16A34A]"; // Green + if (type === 'REFUND') colorClass = "text-destructive"; // Red + + return ( + + {amount} + + ); +}; + +const StatusDisplay = ({ status }) => { + let colorClass = "bg-[#16A34A] text-[#16A34A]"; // COMPLETED (Green) + if (status === 'PENDING') colorClass = "bg-[#D97706] text-[#D97706]"; + if (status === 'DISPUTED') colorClass = "bg-destructive text-destructive"; + + return ( +
+
+ + {status} + +
+ ); +}; + +const TransactionLogsTable = ({ data, onFilterClick, onActionClick }) => { + return ( +
+ {/* Header */} +
+

Recent Financial Activities

+
+ + +
+ + 1-5 of 1,284 + +
+
+ + {/* Table Content */} +
+ + + + + + + + + + + + + + {data.map((row, i) => ( + + + + + + + + + + ))} + +
DateTransaction IDShop NameTypeAmountStatusActions
+
+ {row.date.split(',')[0]}, +
+
+ {row.date.split(',')[1]} +
+
+ + {row.id} + + + + {row.shopName} + + + + + + + + +
+ +
+
+
+ + {/* Footer / Pagination */} +
+
+ Showing 1 to 5 of 1,284 transactions +
+
+ +
+
+
+ ); +}; + +export default TransactionLogsTable; diff --git a/frontend/src/components/admin/UserDirectoryTable.jsx b/frontend/src/components/admin/UserDirectoryTable.jsx new file mode 100644 index 0000000..073265b --- /dev/null +++ b/frontend/src/components/admin/UserDirectoryTable.jsx @@ -0,0 +1,162 @@ +import React from 'react'; +import { ChevronDown, Pencil, Store, Ban, CheckCircle, RefreshCw } from 'lucide-react'; +import Pagination from './Pagination'; + +const RoleBadge = ({ role }) => { + return ( + + {role} + + ); +}; + +const StatusDisplay = ({ status }) => { + let colorClass = "bg-primary text-primary"; + if (status === 'PENDING') colorClass = "bg-[#D97706] text-[#D97706]"; + if (status === 'SUSPENDED') colorClass = "bg-destructive text-destructive"; + + return ( +
+
+ + {status} + +
+ ); +}; + +const UserDirectoryTable = ({ data, onFilterClick, onActionClick }) => { + return ( +
+ {/* Header */} +
+

Directory

+
+ + +
+ + 1-10 of 24,892 + +
+
+ + {/* Table Content */} +
+ + + + + + + + + + + + {data.map((row, i) => ( + + + + + + + + ))} + +
Name / ShopRoleStatusJoin DateActions
+
+ {row.name} +
+
+
+ {row.name} +
+
+ {row.subtext} +
+
+
+ + + + + {row.joinDate} + +
+ {/* Common Edit Action */} + + + {/* Role-specific Action */} + {row.role === 'SELLER' ? ( + + ) : row.status === 'PENDING' ? ( + + ) : ( +
/* Spacer if no action */ + )} + + {/* Status-specific Action (Suspend / Restore) */} + {row.status === 'SUSPENDED' ? ( + + ) : ( + + )} +
+
+
+ + {/* Footer / Pagination */} +
+
+ Showing 1 to 10 of 24,892 users +
+
+ +
+
+
+ ); +}; + +export default UserDirectoryTable; diff --git a/frontend/src/pages/admin/disputes.jsx b/frontend/src/pages/admin/disputes.jsx new file mode 100644 index 0000000..eaba21a --- /dev/null +++ b/frontend/src/pages/admin/disputes.jsx @@ -0,0 +1,192 @@ +import React, { useState } from 'react'; +import { Scale, Clock, BadgeCheck, AlertCircle, TrendingUp, Filter } from 'lucide-react'; +import AdminHeader from '../../components/admin/AdminHeader'; +import AdminStatCard from '../../components/admin/AdminStatCard'; +import ActiveDisputeQueue from '../../components/admin/ActiveDisputeQueue'; +import DisputeReasonsCard from '../../components/admin/DisputeReasonsCard'; +import RecentActivityCard from '../../components/admin/RecentActivityCard'; +import AdminActionModal from '../../components/admin/AdminActionModal'; + +// --- Dummy Data (Data Contract for Backend) --- +const pageData = { + stats: { + open: { value: '24', subtext: '12% VS LAST WEEK' }, + avgResolution: { value: '3.2 Days', subtext: '-0.4 DAYS IMPROVEMENT' }, + resolutionRate: { value: '94%', subtext: 'TARGET: 90%' }, + appeals: { value: '8', subtext: 'REQUIRES SENIOR ADMIN' } + }, + queue: [ + { + id: '#DISP-9021', + shopName: 'Terra Ceramics', + shopImg: 'https://images.unsplash.com/photo-1610701596007-11502861dcfa?w=100&q=80', + customerName: 'Elena Rodriguez', + reason: 'DAMAGED', + amount: '$145.00' + }, + { + id: '#DISP-8942', + shopName: 'Loom & Leaf', + shopImg: 'https://images.unsplash.com/photo-1590736969955-71cc94801759?w=100&q=80', + customerName: 'Jameson Blake', + reason: 'DELIVERY', + amount: '$89.20' + }, + { + id: '#DISP-8710', + shopName: 'Silver Lining', + shopImg: 'https://images.unsplash.com/photo-1611085583191-a3b181a88401?w=100&q=80', + customerName: 'Sarah Jenkins', + reason: 'QUALITY', + amount: '$320.00' + }, + { + id: '#DISP-8604', + shopName: 'Kiln & Kin', + shopImg: 'https://images.unsplash.com/photo-1578749556568-bc2c40e68b61?w=100&q=80', + customerName: 'David Miller', + reason: 'DAMAGED', + amount: '$64.00' + } + ], + reasons: [ + { reason: 'DAMAGED ITEM', percentage: 42 }, + { reason: 'ITEM NOT RECEIVED', percentage: 28 }, + { reason: 'QUALITY ISSUE', percentage: 20 } + ], + trendAnalysis: "Damaged item reports are up by 8%. Recommend updating 'Packaging Guidelines'.", + activities: [ + { + type: 'resolution', + text: '#DISP-8590 was resolved in favor of the customer.', + timeAgo: '2 hours ago' + }, + { + type: 'message', + text: 'Elena R. added a photo to dispute #DISP-9021.', + timeAgo: '4 hours ago' + } + ] +}; + +const DisputesPage = () => { + const [modalState, setModalState] = useState({ isOpen: false, title: '', message: '' }); + + const openModal = (title, message) => { + setModalState({ isOpen: true, title, message }); + }; + + const closeModal = () => { + setModalState(prev => ({ ...prev, isOpen: false })); + }; + + return ( +
+ openModal('Export Report', 'Download comprehensive dispute records.')} + secondaryActionText="Filters" + secondaryActionIcon={Filter} + onSecondaryActionClick={() => openModal('Advanced Filters', 'Select date ranges and status filters.')} + /> + + {/* Header Section */} +
+
+

+ Disputes & Complaints +

+

+ Review and resolve customer conflicts within the artisan marketplace. +

+
+
+ + {/* Stats Grid */} +
+ + + {pageData.stats.open.subtext} + + } + icon={Scale} + /> + + {pageData.stats.avgResolution.subtext} + + } + icon={Clock} + /> + + {pageData.stats.resolutionRate.subtext} + + } + icon={BadgeCheck} + /> + + {pageData.stats.appeals.subtext} + + } + icon={AlertCircle} + iconBgClass="bg-[#F8E2DF]" + iconColorClass="text-destructive" + accentClass="border-l-4 border-l-destructive" + /> +
+ + {/* Main Content Area (2-column layout) */} +
+ + {/* Left Column: Active Dispute Queue */} +
+ openModal('Filter Queue', 'Filter table by dispute reason or amount.')} + onSortClick={() => openModal('Sort Queue', 'Sort table by newest or oldest.')} + onRowClick={(id) => openModal('Review Dispute', `Opening full workspace for dispute ${id}.`)} + /> +
+ + {/* Right Column: Sidebar Cards */} +
+ + openModal('Activity Log', 'Viewing complete timeline of dispute events.')} + /> +
+ +
+ + {/* Action Modal */} + +
+ ); +}; + +export default DisputesPage; \ No newline at end of file diff --git a/frontend/src/pages/admin/logs.jsx b/frontend/src/pages/admin/logs.jsx new file mode 100644 index 0000000..9eca169 --- /dev/null +++ b/frontend/src/pages/admin/logs.jsx @@ -0,0 +1,168 @@ +import React, { useState } from 'react'; +import { Banknote, CheckCircle, Hourglass, AlertCircle, TrendingUp, Calendar } from 'lucide-react'; +import AdminHeader from '../../components/admin/AdminHeader'; +import AdminStatCard from '../../components/admin/AdminStatCard'; +import TransactionLogsTable from '../../components/admin/TransactionLogsTable'; +import AuditSummaryCard from '../../components/admin/AuditSummaryCard'; +import PayoutHealthCard from '../../components/admin/PayoutHealthCard'; +import AdminActionModal from '../../components/admin/AdminActionModal'; + +// --- Dummy Data (Data Contract for Backend) --- +const pageData = { + stats: { + volume: { value: 'P142,850.00', subtext: '12% increase' }, + successRate: { value: '99.4%', subtext: 'STABLE GROWTH' }, + pending: { value: 'P12,400.00', subtext: '86 PROCESSING' }, + disputes: { value: '14', subtext: 'IMMEDIATE ACTION' } + }, + transactions: [ + { + date: 'Oct 24, 2023', + id: 'TXN-9021834', + shopName: 'Ancient Earth Crafts', + type: 'SALE', + amount: '+P245.00', + status: 'COMPLETED' + }, + { + date: 'Oct 23, 2023', + id: 'TXN-9021835', + shopName: 'Indigo Loom Textiles', + type: 'PAYOUT', + amount: '-P1,200.00', + status: 'PENDING' + }, + { + date: 'Oct 23, 2023', + id: 'TXN-9021836', + shopName: 'Obsidian Fire Glass', + type: 'SALE', + amount: '+P68.00', + status: 'DISPUTED' + }, + { + date: 'Oct 22, 2023', + id: 'TXN-9021837', + shopName: 'The Iron Forge', + type: 'REFUND', + amount: '-P142.50', + status: 'COMPLETED' + } + ], + payoutHealth: [ + { name: 'BANK TRANSFER (ACH)', health: 99.8 }, + { name: 'STRIPE CONNECT', health: 98.9 }, + { name: 'PAYPAL GLOBAL', health: 94.2 } + ] +}; + +const LogsPage = () => { + const [modalState, setModalState] = useState({ isOpen: false, title: '', message: '' }); + const openModal = (title, message) => { + setModalState({ isOpen: true, title, message }); + }; + const closeModal = () => { + setModalState(prev => ({ ...prev, isOpen: false })); + }; + return ( +
+ openModal('Export CSV', 'Download comprehensive financial records.')} + secondaryActionText="Filters" + secondaryActionIcon={Calendar} + onSecondaryActionClick={() => openModal('Advanced Filters', 'Select date ranges and transaction types.')} + /> + {/* Header Section */} +
+
+

+ Transaction Logs +

+

+ Monitor and audit all platform financial activity from artisanal crafts to raw material sales. +

+
+
+ {/* Stats Grid */} +
+ + + {pageData.stats.volume.subtext} + + } + icon={Banknote} + /> + + {pageData.stats.successRate.subtext} + + } + icon={CheckCircle} + /> + + {pageData.stats.pending.subtext} + + } + icon={Hourglass} + /> + + {pageData.stats.disputes.subtext} + + } + icon={AlertCircle} + iconBgClass="bg-[#F8E2DF]" + iconColorClass="text-destructive" + accentClass="border-l-4 border-l-destructive" + /> +
+ {/* Main Table Content */} +
+ openModal(`${filterName} Filter`, `Select options to filter the logs by ${filterName}.`)} + onActionClick={(actionName, txnId) => openModal(`Action: ${actionName}`, `Viewing full details for transaction ${txnId}.`)} + /> +
+ {/* Bottom Widgets */} +
+
+ openModal('Review Flags', 'Opening the AI audit flags queue for manual review.')} + onDownloadClick={() => openModal('Download Audit', 'Generating the full automated audit PDF report.')} + /> +
+
+ +
+
+ {/* Action Modal */} + +
+ ); +}; +export default LogsPage; \ No newline at end of file diff --git a/frontend/src/pages/admin/moderate.jsx b/frontend/src/pages/admin/moderate.jsx new file mode 100644 index 0000000..7602dfb --- /dev/null +++ b/frontend/src/pages/admin/moderate.jsx @@ -0,0 +1,161 @@ +import React, { useState } from 'react'; +import { + Hourglass, + Flag, + Clock, + CheckCircle, + TrendingDown +} from 'lucide-react'; +import AdminHeader from '../../components/admin/AdminHeader'; +import AdminStatCard from '../../components/admin/AdminStatCard'; +import ModerationTable from '../../components/admin/ModerationTable'; +// import BulkActionOverlay from '../../components/admin/BulkActionOverlay'; +import AdminActionModal from '../../components/admin/AdminActionModal'; + +// --- Dummy Data (Data Contract for Backend) --- +const pageData = { + stats: { + pending: { value: '124', subtext: '12% less', isPositive: false }, + flagged: { value: '42', subtext: '8 urgent escalations' }, + avgTime: { value: '4.2h', subtext: 'Within SLA (6h)' }, + resolved: { value: '89', subtext: 'TARGET MET' } + }, + tickets: [ + { + productName: 'Hand-Carved Earthen Vase', + id: '#PRD-9842', + image: 'https://images.unsplash.com/photo-1610701596007-11502861dcfa?w=400&q=80', + shopName: 'Ancient Earth Crafts', + violation: 'COPYRIGHT', + flaggedDate: 'Oct 24, 2023', + flaggedTime: '10:42 AM', + severity: 88, + status: 'ESCALATED' + }, + { + productName: 'Indigo Weave Runner', + id: '#PRD-7210', + image: 'https://images.unsplash.com/photo-1596526131083-e8c633c948d2?w=400&q=80', + shopName: 'Studio Indigo', + violation: 'QUALITY', + flaggedDate: 'Oct 24, 2023', + flaggedTime: '09:15 AM', + severity: 45, + status: 'UNDER REVIEW' + }, + { + productName: 'Botanical Soap Set', + id: '#PRD-3301', + image: 'https://images.unsplash.com/photo-1600857062241-98e5dba7f214?w=400&q=80', + shopName: "Nature's Lather", + violation: 'PROHIBITED', + flaggedDate: 'Oct 23, 2023', + flaggedTime: '04:30 PM', + severity: 12, + status: 'PENDING' + } + ] +}; + +const Moderate = () => { + const [modalState, setModalState] = useState({ isOpen: false, title: '', message: '' }); + + const openModal = (title, message) => { + setModalState({ isOpen: true, title, message }); + }; + + const closeModal = () => { + setModalState(prev => ({ ...prev, isOpen: false })); + }; + + return ( +
+ openModal('Bulk Resolve', 'The bulk resolution wizard will open here.')} + /> + + {/* Header Section */} +
+
+

+ Product Moderation +

+

+ Review and manage reported items from the Artisan community. +

+
+
+ + {/* Stats Grid */} +
+ + + {pageData.stats.pending.subtext} + + } + icon={Hourglass} + /> + + {pageData.stats.flagged.subtext} + + } + icon={Flag} + iconBgClass="bg-[#F8E2DF]" + iconColorClass="text-destructive" + /> + + + {pageData.stats.resolved.subtext} + + } + icon={CheckCircle} + /> +
+ + {/* Main Table Content */} +
+ openModal(`${filterName} Filter`, `Select options to filter the moderation queue by ${filterName}.`)} + onDownloadClick={() => openModal('Download Report', 'Download the current moderation queue data.')} + onReviewClick={(productName) => openModal('Review Ticket', `Review full moderation details for ${productName}.`)} + /> +
+ + {/* Floating Bulk Action Overlay (Mockup State) */} + {/* openModal(`Bulk Action: ${actionName}`, `Applying ${actionName} to 4 selected tickets.`)} + /> */} + + {/* Action Modal */} + +
+ ); +}; + +export default Moderate; diff --git a/frontend/src/pages/admin/overview.jsx b/frontend/src/pages/admin/overview.jsx new file mode 100644 index 0000000..5d52134 --- /dev/null +++ b/frontend/src/pages/admin/overview.jsx @@ -0,0 +1,201 @@ +import React, { useState } from 'react'; +import { + Store, + Banknote, + BadgeCheck, + AlertTriangle, + TrendingUp, + TrendingDown, + Armchair, + Gem, + Palette +} from 'lucide-react'; +import AdminHeader from '../../components/admin/AdminHeader'; +import AdminStatCard from '../../components/admin/AdminStatCard'; +import PlatformGrowthChart from '../../components/admin/PlatformGrowthChart'; +import SystemAlerts from '../../components/admin/SystemAlerts'; +import CategoryPerformanceTable from '../../components/admin/CategoryPerformanceTable'; +import AdminActionModal from '../../components/admin/AdminActionModal'; + +// --- Dummy Data (Data Contract for Backend) --- +const pageData = { + stats: { + shops: { value: '4,281', subtext: '+12%', isPositive: true }, + gmv: { value: 'P1.2M', subtext: '+8.4%', isPositive: true }, + verifications: { value: '84', subtext: 'REVIEW QUEUE' }, + disputes: { value: '12', subtext: '-2.1%', isPositive: false } + }, + growthData: [ + { label: 'OCT 01', showLabel: true, value: 30 }, + { label: '', showLabel: false, value: 40 }, + { label: '', showLabel: false, value: 35 }, + { label: 'OCT 10', showLabel: true, value: 50 }, + { label: '', showLabel: false, value: 65 }, + { + label: '', showLabel: false, value: 100, + isHighlighted: true, + tooltip: { value: 'P42,800.00', date: 'Oct 12, 2023' } + }, + { label: 'OCT 20', showLabel: true, value: 55 }, + { label: '', showLabel: false, value: 45 }, + { label: '', showLabel: false, value: 75 }, + { label: 'OCT 30', showLabel: true, value: 60 } + ], + systemAlerts: [ + { + title: 'New verification request', + message: 'The "Highland Loom" shop has submitted premium papers.', + timeAgo: '2 MINUTES AGO', + isUrgent: false + }, + { + title: 'Dispute Escalation', + message: 'Order #8821 from "Ceramic Echoes" marked as undelivered.', + timeAgo: '45 MINUTES AGO', + isUrgent: true + }, + { + title: 'Server maintenance', + message: 'Database optimization scheduled for 02:00 UTC.', + timeAgo: '2 HOURS AGO', + isUrgent: false + } + ], + categoryPerformance: [ + { + category: { name: 'Handcrafted Furniture', icon: Armchair }, + activeShops: '1,120', + avgSales: 'P4,250', + growth: '+14.2%', + status: 'TRENDING' + }, + { + category: { name: 'Artisanal Jewelry', icon: Gem }, + activeShops: '845', + avgSales: 'P1,980', + growth: '+5.8%', + status: 'STABLE' + }, + { + category: { name: 'Ceramics & Pottery', icon: Palette }, + activeShops: '562', + avgSales: 'P2,110', + growth: '-1.2%', + status: 'STABLE' + } + ] +}; + +const Overview = () => { + const [modalState, setModalState] = useState({ isOpen: false, title: '', message: '' }); + + const openModal = (title, message) => { + setModalState({ isOpen: true, title, message }); + }; + + const closeModal = () => { + setModalState(prev => ({ ...prev, isOpen: false })); + }; + + return ( +
+ openModal('Export Report', 'The report generation and export feature is coming soon.')} /> + + {/* Header Section */} +
+
+

+ System Overview +

+

+ Real-time health and performance metrics for the Artisan Hub ecosystem. +

+
+
+ + {/* Stats Grid */} +
+ + + {pageData.stats.shops.subtext} + + } + icon={Store} + /> + + + {pageData.stats.gmv.subtext} + + } + icon={Banknote} + /> + + {pageData.stats.verifications.subtext} + + } + icon={BadgeCheck} + /> + + + {pageData.stats.disputes.subtext} + + } + icon={AlertTriangle} + iconBgClass="bg-[#F8E2DF]" + iconColorClass="text-destructive" + /> +
+ + {/* Main Content Layout */} +
+ {/* Left Column (Chart) */} +
+ +
+ + {/* Right Column (Alerts) */} +
+ openModal('System Activity Log', 'Full system activity log and filters will be displayed here.')} + /> +
+
+ + {/* Bottom Layout (Table) */} +
+ openModal('Region Filter', 'Geographic filtering options will be available soon.')} + onViewDetailedAnalyticsClick={() => openModal('Detailed Analytics', 'Comprehensive category analytics dashboard is under construction.')} + /> +
+ + {/* Action Modal */} + +
+ ); +}; + +export default Overview; \ No newline at end of file diff --git a/frontend/src/pages/admin/users.jsx b/frontend/src/pages/admin/users.jsx new file mode 100644 index 0000000..885d1a8 --- /dev/null +++ b/frontend/src/pages/admin/users.jsx @@ -0,0 +1,147 @@ +import React, { useState } from 'react'; +import { + Users, + Store, + Ban, + UserPlus, + TrendingUp +} from 'lucide-react'; +import AdminHeader from '../../components/admin/AdminHeader'; +import AdminStatCard from '../../components/admin/AdminStatCard'; +import UserDirectoryTable from '../../components/admin/UserDirectoryTable'; +import AdminActionModal from '../../components/admin/AdminActionModal'; + +// --- Dummy Data (Data Contract for Backend) --- +const pageData = { + stats: { + totalUsers: { value: '24,892', subtext: '12% increase' }, + activeShops: { value: '1,402', subtext: 'Active Now' }, + suspended: { value: '43', subtext: 'Immediate Action' }, + newReg: { value: '128', subtext: 'LAST 24H' } + }, + directory: [ + { + name: 'Aurelia James', + subtext: 'EARTH & FIRE STUDIO', + avatar: 'https://images.unsplash.com/photo-1544005313-94ddf0286df2?w=400&q=80', + role: 'SELLER', + status: 'VERIFIED', + joinDate: 'Oct 12, 2023' + }, + { + name: 'Marcus Smith', + subtext: 'MARCUS.VIBE@EMAIL.COM', + avatar: 'https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?w=400&q=80', + role: 'CUSTOMER', + status: 'PENDING', + joinDate: 'Jan 05, 2024' + }, + { + name: 'Liam Weaver', + subtext: 'THE LOOM & HIDE', + avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400&q=80', + role: 'SELLER', + status: 'SUSPENDED', + joinDate: 'Nov 22, 2023' + } + ] +}; + +const UsersPage = () => { + const [modalState, setModalState] = useState({ isOpen: false, title: '', message: '' }); + + const openModal = (title, message) => { + setModalState({ isOpen: true, title, message }); + }; + + const closeModal = () => { + setModalState(prev => ({ ...prev, isOpen: false })); + }; + + return ( +
+ openModal('Export Data', 'Download comprehensive user and store reports.')} + secondaryActionText="Add New User" + secondaryActionIcon={UserPlus} + onSecondaryActionClick={() => openModal('Add New User', 'The user creation wizard will open here.')} + /> + + {/* Header Section */} +
+
+

+ User & Store Management +

+

+ Oversee Artisan Hub's ecosystem of creators and customers. +

+
+
+ + {/* Stats Grid */} +
+ + + {pageData.stats.totalUsers.subtext} + + } + icon={Users} + /> + + + {pageData.stats.suspended.subtext} + + } + icon={Ban} + iconBgClass="bg-[#F8E2DF]" + iconColorClass="text-destructive" + /> + + {pageData.stats.newReg.subtext} + + } + icon={UserPlus} + /> +
+ + {/* Main Table Content */} +
+ openModal(`${filterName} Filter`, `Select options to filter the user directory by ${filterName}.`)} + onActionClick={(actionName, userName) => openModal(`Action: ${actionName}`, `Applying ${actionName} to user ${userName}.`)} + /> +
+ + {/* Action Modal */} + +
+ ); +}; + +export default UsersPage; \ No newline at end of file diff --git a/frontend/src/pages/admin/verify.jsx b/frontend/src/pages/admin/verify.jsx new file mode 100644 index 0000000..425526d --- /dev/null +++ b/frontend/src/pages/admin/verify.jsx @@ -0,0 +1,158 @@ +import React, { useState } from 'react'; +import { + ClipboardList, + Clock, + Percent, + BadgeCheck, + TrendingUp, + Palette, + Gem, + Hammer, + Scissors +} from 'lucide-react'; +import AdminHeader from '../../components/admin/AdminHeader'; +import AdminStatCard from '../../components/admin/AdminStatCard'; +import ApplicationsTable from '../../components/admin/ApplicationsTable'; +import BatchVerifyFAB from '../../components/admin/BatchVerifyFAB'; +import AdminActionModal from '../../components/admin/AdminActionModal'; + +// --- Dummy Data (Data Contract for Backend) --- +const pageData = { + stats: { + pending: { value: '42', subtext: '+5', isPositive: true }, + avgReviewTime: { value: '18 hrs', subtext: 'Goal: 12h' }, + approvalRate: { value: '76%', subtext: 'Target: 80%' }, + verifiedToday: { value: '12', subtext: 'DAILY STATS' } + }, + applications: [ + { + name: 'Earthbound Arts', + id: '#SHOP-9041', + icon: Palette, + category: 'Ceramics & Clay', + appliedOn: 'Oct 24, 2023', + score: 92, + status: 'UNDER REVIEW' + }, + { + name: 'Silver Lining Studio', + id: '#SHOP-8822', + icon: Gem, + category: 'Jewelry Design', + appliedOn: 'Oct 25, 2023', + score: 48, + status: 'FLAGGED' + }, + { + name: 'Wildwood Craft', + id: '#SHOP-9128', + icon: Hammer, + category: 'Woodworking', + appliedOn: 'Oct 26, 2023', + score: 85, + status: 'NEW' + }, + { + name: 'Northern Fabrics', + id: '#SHOP-9233', + icon: Scissors, + category: 'Textiles', + appliedOn: 'Oct 26, 2023', + score: 78, + status: 'NEW' + } + ] +}; + +const Verify = () => { + const [modalState, setModalState] = useState({ isOpen: false, title: '', message: '' }); + + const openModal = (title, message) => { + setModalState({ isOpen: true, title, message }); + }; + + const closeModal = () => { + setModalState(prev => ({ ...prev, isOpen: false })); + }; + + return ( +
+ openModal('Export Report', 'Verification queue reports can be exported here.')} /> + + {/* Header Section */} +
+
+

+ Verified Shops Queue +

+

+ Review and verify pending shop applications to maintain marketplace quality. +

+
+
+ + {/* Stats Grid */} +
+ + + {pageData.stats.pending.subtext} + + } + icon={ClipboardList} + /> + + + + {pageData.stats.verifiedToday.subtext} + + } + icon={BadgeCheck} + /> +
+ + {/* Main Table Content */} +
+ openModal('Filter Applications', 'Filter by status, category, or application date.')} + onDownloadClick={() => openModal('Download Queue', 'Download the current verification queue data.')} + onRowClick={(shopName) => openModal('Review Application', `Review full application details for ${shopName}.`)} + /> +
+ + {/* Action Modal */} + + + {/* Floating Action Button */} + openModal('Batch Verify', 'Select multiple shops to verify them simultaneously.')} + /> +
+ ); +}; + +export default Verify;