+ } />
+ } />
+ } />
+
{/* Admin Routes */}
(
} />
} />
} />
+ } />
} />
diff --git a/frontend/src/components/RoleBasedSidebar.jsx b/frontend/src/components/RoleBasedSidebar.jsx
index 7fa1f17..901b221 100644
--- a/frontend/src/components/RoleBasedSidebar.jsx
+++ b/frontend/src/components/RoleBasedSidebar.jsx
@@ -15,6 +15,7 @@ import {
Truck,
DollarSign,
Compass,
+ User,
} from 'lucide-react';
const SidebarNavItem = ({
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 */}
+
+
+
+
+ Dispute ID
+ Artisan / Shop
+ Customer
+ Reason
+ Amount
+
+
+
+ {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' : ''}`}
+ >
+
+
+ {row.id}
+
+
+
+
+
+
+
+ {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 */}
+
+
+ Got it
+
+
+
+
+ );
+};
+
+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 (
+
+ );
+};
+
+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 (
+
+ );
+};
+
+const ApplicationsTable = ({ data, onFilterClick, onDownloadClick, onRowClick }) => {
+ return (
+
+ {/* Header */}
+
+
Applications Overview
+
+
+
+
+
+
+
+
+
+
+ {/* Table Content */}
+
+
+
+
+ Shop Name
+ Category
+ Applied On
+ Score
+ Status
+
+
+
+
+ {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' : ''}`}
+ >
+
+
+
+
+
+
+ {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.
+
+
+
+
+
+ Review Flags
+
+
+ Download Audit
+
+
+
+ );
+};
+
+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 (
+
+
+
+ Batch Verify ({count})
+
+
+ );
+};
+
+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 */}
+
+ onActionClick('Approve')}
+ className="flex items-center gap-2 text-[10px] font-sans font-bold tracking-widest uppercase text-neutral-dark/60 hover:text-neutral-dark transition-colors"
+ >
+
+ APPROVE
+
+ onActionClick('Reject')}
+ className="flex items-center gap-2 text-[10px] font-sans font-bold tracking-widest uppercase text-destructive hover:text-red-700 transition-colors"
+ >
+
+ REJECT
+
+ onActionClick('Escalate')}
+ className="flex items-center gap-2 text-[10px] font-sans font-bold tracking-widest uppercase text-neutral-dark/60 hover:text-neutral-dark transition-colors"
+ >
+
+ ESCALATE
+
+
+
+ {/* Primary Action */}
+
onActionClick('Bulk Resolve')}
+ className="ml-2 px-6 py-2.5 rounded-full bg-[#8C5233] hover:bg-[#7E4A2E] text-white text-[13px] font-sans font-bold transition-colors shadow-sm"
+ >
+ Bulk Resolve
+
+
+
+ );
+};
+
+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
+
+
+ All Regions
+
+
+
+
+
+
+
+
+ {/* Table Content */}
+
+
+
+
+ Category
+ Active Shops
+ Avg. Sales
+ Growth
+ Status
+
+
+
+
+ {data.map((row, i) => (
+
+
+
+
+
+
+ {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 (
+
+ );
+};
+
+const StatusDisplay = ({ status }) => {
+ if (status === 'ESCALATED') {
+ return (
+
+ );
+ }
+ return (
+
+ {status}
+
+ );
+};
+
+const ModerationTable = ({ data, onFilterClick, onDownloadClick, onReviewClick }) => {
+ return (
+
+ {/* Header */}
+
+
Moderation Queue
+
+
onFilterClick('Violation')}
+ className="flex items-center gap-2 px-3 py-1.5 rounded-md border border-neutral-dark/10 bg-neutral-dark/5 hover:bg-neutral-dark/10 text-[11px] font-sans font-bold text-neutral-dark transition-colors uppercase tracking-wider"
+ >
+ ALL VIOLATION TYPES
+
+
+
onFilterClick('Severity')}
+ className="flex items-center gap-2 px-3 py-1.5 rounded-md border border-neutral-dark/10 bg-neutral-dark/5 hover:bg-neutral-dark/10 text-[11px] font-sans font-bold text-neutral-dark transition-colors uppercase tracking-wider"
+ >
+ SEVERITY: HIGH FIRST
+
+
+
+
+
+
+
+
+
+ {/* Table Content */}
+
{/* Extra padding bottom for the floating bar */}
+
+
+
+ Product & ID
+ Shop Name
+ Violation
+ Flagged On
+ Severity
+ Status
+
+
+
+ {data.map((row, i) => (
+
+
+
+
+
+
+
+ {row.productName}
+
+
+ {row.id}
+
+
+
+
+
+ {row.shopName}
+
+
+
+
+
+
+
+ {row.flaggedDate}
+
+
+ {row.flaggedTime}
+
+
+
+
+
+
+
+
+ onReviewClick(row.productName)}
+ className="px-4 py-1.5 rounded-md border border-neutral-dark/20 text-neutral-dark text-[11px] font-sans font-bold hover:bg-neutral-dark/5 transition-colors shadow-sm bg-background shrink-0"
+ >
+ Review
+
+
+
+
+ ))}
+
+
+
+
+ {/* 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 */}
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ {/* Ellipsis */}
+
+ ...
+
+
+
+ 12
+
+
+ {/* 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
+
+
+
+
+ 7D
+
+
+ 30D
+
+
+ 90D
+
+
+
+
+ {/* 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 (
+
+ );
+};
+
+const RecentActivityCard = ({ activities, onViewAllClick }) => {
+ return (
+
+
+ Recent Activity
+
+
+
+ {activities.map((activity, i) => (
+
+ ))}
+
+
+
+
+ View Full Activity Log
+
+
+
+ );
+};
+
+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 */}
+
+
+ View All Activity
+
+
+
+
+ );
+};
+
+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 (
+
+ );
+};
+
+const TransactionLogsTable = ({ data, onFilterClick, onActionClick }) => {
+ return (
+
+ {/* Header */}
+
+
Recent Financial Activities
+
+
onFilterClick('Type')}
+ className="flex items-center gap-2 px-3 py-1.5 rounded-md border border-neutral-dark/10 bg-neutral-dark/5 hover:bg-neutral-dark/10 text-[11px] font-sans font-bold text-neutral-dark transition-colors uppercase tracking-wider"
+ >
+ ALL TYPES
+
+
+
onFilterClick('Status')}
+ className="flex items-center gap-2 px-3 py-1.5 rounded-md border border-neutral-dark/10 bg-neutral-dark/5 hover:bg-neutral-dark/10 text-[11px] font-sans font-bold text-neutral-dark transition-colors uppercase tracking-wider"
+ >
+ STATUS: ALL
+
+
+
+
+ 1-5 of 1,284
+
+
+
+
+ {/* Table Content */}
+
+
+
+
+ Date
+ Transaction ID
+ Shop Name
+ Type
+ Amount
+ Status
+ Actions
+
+
+
+ {data.map((row, i) => (
+
+
+
+ {row.date.split(',')[0]},
+
+
+ {row.date.split(',')[1]}
+
+
+
+
+ {row.id}
+
+
+
+
+ {row.shopName}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ onActionClick('View Details', row.id)}
+ className="text-neutral-dark/40 hover:text-neutral-dark transition-colors outline-none focus-visible:ring-2 focus-visible:ring-primary rounded-sm"
+ >
+
+
+
+
+
+ ))}
+
+
+
+
+ {/* 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 (
+
+ );
+};
+
+const UserDirectoryTable = ({ data, onFilterClick, onActionClick }) => {
+ return (
+
+ {/* Header */}
+
+
Directory
+
+
onFilterClick('Role')}
+ className="flex items-center gap-2 px-3 py-1.5 rounded-md border border-neutral-dark/10 bg-neutral-dark/5 hover:bg-neutral-dark/10 text-[11px] font-sans font-bold text-neutral-dark transition-colors uppercase tracking-wider"
+ >
+ ALL ROLES
+
+
+
onFilterClick('Status')}
+ className="flex items-center gap-2 px-3 py-1.5 rounded-md border border-neutral-dark/10 bg-neutral-dark/5 hover:bg-neutral-dark/10 text-[11px] font-sans font-bold text-neutral-dark transition-colors uppercase tracking-wider"
+ >
+ STATUS: ALL
+
+
+
+
+ 1-10 of 24,892
+
+
+
+
+ {/* Table Content */}
+
+
+
+
+ Name / Shop
+ Role
+ Status
+ Join Date
+ Actions
+
+
+
+ {data.map((row, i) => (
+
+
+
+
+
+
+
+ {row.name}
+
+
+ {row.subtext}
+
+
+
+
+
+
+
+
+
+
+ {row.joinDate}
+
+
+
+ {/* Common Edit Action */}
+
onActionClick('Edit', row.name)}
+ className="text-neutral-dark/40 hover:text-neutral-dark transition-colors outline-none focus-visible:ring-2 focus-visible:ring-primary rounded-sm"
+ >
+
+
+
+ {/* Role-specific Action */}
+ {row.role === 'SELLER' ? (
+
onActionClick('View Shop', row.name)}
+ className="text-[#8C5233] hover:text-[#7E4A2E] transition-colors outline-none focus-visible:ring-2 focus-visible:ring-primary rounded-sm"
+ >
+
+
+ ) : row.status === 'PENDING' ? (
+
onActionClick('Approve', row.name)}
+ className="text-primary hover:text-primary-dark transition-colors outline-none focus-visible:ring-2 focus-visible:ring-primary rounded-sm"
+ >
+
+
+ ) : (
+
/* Spacer if no action */
+ )}
+
+ {/* Status-specific Action (Suspend / Restore) */}
+ {row.status === 'SUSPENDED' ? (
+
onActionClick('Restore', row.name)}
+ className="text-primary hover:text-primary-dark transition-colors outline-none focus-visible:ring-2 focus-visible:ring-primary rounded-sm"
+ >
+
+
+ ) : (
+
onActionClick('Suspend', row.name)}
+ className="text-destructive hover:text-red-700 transition-colors outline-none focus-visible:ring-2 focus-visible:ring-primary rounded-sm"
+ >
+
+
+ )}
+
+
+
+ ))}
+
+
+
+
+ {/* Footer / Pagination */}
+
+
+ Showing 1 to 10 of 24,892 users
+
+
+
+
+ );
+};
+
+export default UserDirectoryTable;
diff --git a/frontend/src/index.css b/frontend/src/index.css
index 3965264..bf60557 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -431,6 +431,9 @@ h6 {
--input: rgba(54, 46, 37, 0.15);
--ring: #A0522D;
+ --muted: rgba(54, 46, 37, 0.05);
+ --muted-foreground: rgba(54, 46, 37, 0.55);
+
--chart-1: #A0522D;
--chart-2: #3D5A3E;
--chart-3: #8B6914;
@@ -488,6 +491,9 @@ h6 {
--input: rgba(245, 240, 232, 0.12);
--ring: #C8744C;
+ --muted: rgba(245, 240, 232, 0.05);
+ --muted-foreground: rgba(245, 240, 232, 0.55);
+
--sidebar: #241F1B;
--sidebar-foreground: #F5F0E8;
diff --git a/frontend/src/layouts/SharedSidebar.jsx b/frontend/src/layouts/SharedSidebar.jsx
index 1f7287a..82cdd4b 100644
--- a/frontend/src/layouts/SharedSidebar.jsx
+++ b/frontend/src/layouts/SharedSidebar.jsx
@@ -1,5 +1,5 @@
import { Link } from 'react-router-dom';
-import { User, X } from 'lucide-react';
+import { User, X, LogOut } from 'lucide-react';
/**
* SharedSidebar
@@ -65,7 +65,7 @@ export const SharedSidebar = ({ sidebarContent, isSidebarOpen, setIsSidebarOpen
{/* Profile footer */}
-
+
Profile
+
+
+
+ Sign Out
+
>
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;
diff --git a/frontend/src/pages/buyer/BuyerProfile.jsx b/frontend/src/pages/buyer/BuyerProfile.jsx
new file mode 100644
index 0000000..dea4d82
--- /dev/null
+++ b/frontend/src/pages/buyer/BuyerProfile.jsx
@@ -0,0 +1,300 @@
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { Upload, CheckCircle, Save, MapPin, User, FileText, LogOut, ShieldCheck, Mail, Phone, Camera } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import { Input } from '@/components/ui/input';
+import { Label } from '@/components/ui/label';
+import { Map, MapMarker, MapControls, MarkerContent, MarkerLabel } from '@/components/ui/map';
+
+const BuyerProfile = () => {
+ const navigate = useNavigate();
+ const [isSaved, setIsSaved] = useState(false);
+
+ const [formData, setFormData] = useState(() => {
+ const saved = localStorage.getItem('buyerProfile');
+ if (saved) {
+ try { return JSON.parse(saved); } catch (e) {}
+ }
+ return {
+ firstName: '',
+ middleName: '',
+ lastName: '',
+ email: '',
+ phone: '',
+ address: {
+ street: '',
+ city: '',
+ state: '',
+ zipCode: '',
+ },
+ idFile: null,
+ location: {
+ lng: 121.0215,
+ lat: 14.5995,
+ }
+ };
+ });
+
+ const [mapViewport, setMapViewport] = useState({
+ center: [formData.location.lng, formData.location.lat],
+ zoom: 12,
+ });
+
+ const handleInputChange = (e) => setFormData({ ...formData, [e.target.name]: e.target.value });
+
+ const handleAddressChange = (e) => setFormData({
+ ...formData,
+ address: { ...formData.address, [e.target.name]: e.target.value }
+ });
+
+ const handleFileChange = (e, field) => {
+ if (e.target.files && e.target.files[0]) {
+ setFormData({ ...formData, [field]: e.target.files[0] });
+ }
+ };
+
+ const handleDragEnd = (e) => setFormData({
+ ...formData,
+ location: { lng: e.lng, lat: e.lat }
+ });
+
+ const handleLocate = (coords) => {
+ setFormData({
+ ...formData,
+ location: { lng: coords.longitude, lat: coords.latitude }
+ });
+ setMapViewport({ center: [coords.longitude, coords.latitude], zoom: 15 });
+ };
+
+ const handleSave = (e) => {
+ e.preventDefault();
+ const dataToSave = { ...formData, idFile: null };
+ localStorage.setItem('buyerProfile', JSON.stringify(dataToSave));
+ setIsSaved(true);
+ setTimeout(() => setIsSaved(false), 3000);
+ };
+
+ const handleLogout = () => {
+ // Clear any auth tokens or user context here if they exist
+ navigate('/login');
+ };
+
+ const initials = `${formData.firstName?.charAt(0) || ''}${formData.lastName?.charAt(0) || ''}`.toUpperCase() || 'U';
+
+ return (
+
+ {/* Header Banner */}
+
+
+
+
+
+ {/* Left Sidebar Profile Summary */}
+
+
+
+ {/* Avatar */}
+
+
+
+ {formData.firstName || formData.lastName ? `${formData.firstName} ${formData.lastName}` : 'Guest User'}
+
+
+ Verified Buyer
+
+
+
+
+
+
+
+
+
+
{formData.email || 'No email provided'}
+
+
+
+
{formData.phone || 'No phone provided'}
+
+
+
+
+
+
+
+ Sign Out Account
+
+
+
+
+ {/* Right Form Content */}
+
+
+
+
+ );
+};
+
+export default BuyerProfile;
diff --git a/frontend/src/pages/buyer/Checkout.jsx b/frontend/src/pages/buyer/Checkout.jsx
index 82f7462..e22ec99 100644
--- a/frontend/src/pages/buyer/Checkout.jsx
+++ b/frontend/src/pages/buyer/Checkout.jsx
@@ -66,21 +66,29 @@ const Checkout = () => {
const [step, setStep] = useState('delivery'); // 'delivery', 'payment', 'review'
const [isProcessing, setIsProcessing] = useState(false);
const [paymentMethod, setPaymentMethod] = useState('card'); // 'card', 'gcash', 'cod'
- const [formData, setFormData] = useState({
- firstName: '',
- lastName: '',
- email: '',
- phone: '',
- address: '',
- city: '',
- province: '',
- zipCode: '',
- region: '',
- deliveryNotes: '',
- cardNumber: '',
- cardExpiry: '',
- cardCvv: '',
- cardholderName: ''
+ const [formData, setFormData] = useState(() => {
+ const savedProfile = localStorage.getItem('buyerProfile');
+ let parsed = null;
+ if (savedProfile) {
+ try { parsed = JSON.parse(savedProfile); } catch (e) {}
+ }
+
+ return {
+ firstName: parsed?.firstName || '',
+ lastName: parsed?.lastName || '',
+ email: parsed?.email || '',
+ phone: parsed?.phone || '',
+ address: parsed?.address?.street || '',
+ city: parsed?.address?.city || '',
+ province: parsed?.address?.state || '',
+ zipCode: parsed?.address?.zipCode || '',
+ region: '',
+ deliveryNotes: '',
+ cardNumber: '',
+ cardExpiry: '',
+ cardCvv: '',
+ cardholderName: parsed ? `${parsed.firstName} ${parsed.lastName}`.trim().toUpperCase() : ''
+ };
});
const handleChange = (e) => setFormData((p) => ({ ...p, [e.target.name]: e.target.value }));
diff --git a/frontend/src/pages/buyer/ProductDetail.jsx b/frontend/src/pages/buyer/ProductDetail.jsx
index 2bdedf2..96ba55a 100644
--- a/frontend/src/pages/buyer/ProductDetail.jsx
+++ b/frontend/src/pages/buyer/ProductDetail.jsx
@@ -57,7 +57,7 @@ const ProductDetail = () => {
return (
-
+
{/* Back button */}
{
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ // Handle login logic here
+ console.log('Login attempt', { email, password });
+ };
+
+ return (
+
+
+ {/* Decorative background elements */}
+
+
+
+
+
+
+
+
+
+ Welcome Back
+
+
+ Sign in to continue your journey.
+
+
+
+
+
+
+
+ );
+};
+
+export default Login;
\ No newline at end of file
diff --git a/frontend/src/pages/shared/SellerVerification.jsx b/frontend/src/pages/shared/SellerVerification.jsx
new file mode 100644
index 0000000..c6624a9
--- /dev/null
+++ b/frontend/src/pages/shared/SellerVerification.jsx
@@ -0,0 +1,403 @@
+import React, { useState } from 'react';
+import { Upload, MapPin, Store, FileText, CheckCircle, ArrowRight, ArrowLeft, ShieldCheck, Map as MapIcon, User } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import { Input } from '@/components/ui/input';
+import { Textarea } from '@/components/ui/textarea';
+import { Label } from '@/components/ui/label';
+import { Map, MapMarker, MapControls, MarkerContent, MarkerLabel } from '@/components/ui/map';
+import { Link } from 'react-router-dom';
+
+const SellerVerification = () => {
+ const [step, setStep] = useState(1);
+ const [isSubmitted, setIsSubmitted] = useState(false);
+ const [formData, setFormData] = useState({
+ firstName: '',
+ middleName: '',
+ lastName: '',
+ phone: '',
+ storeName: '',
+ category: '',
+ description: '',
+ address: {
+ street: '',
+ city: '',
+ state: '',
+ zipCode: '',
+ },
+ permitFile: null,
+ idFile: null,
+ location: {
+ lng: 121.0215, // Manila coordinates
+ lat: 14.5995,
+ }
+ });
+
+ const handleAddressChange = (e) => {
+ setFormData({
+ ...formData,
+ address: {
+ ...formData.address,
+ [e.target.name]: e.target.value
+ }
+ });
+ };
+
+ const [mapViewport, setMapViewport] = useState({
+ center: [121.0215, 14.5995],
+ zoom: 12,
+ });
+
+ const handleInputChange = (e) => {
+ setFormData({ ...formData, [e.target.name]: e.target.value });
+ };
+
+ const handleFileChange = (e, field) => {
+ if (e.target.files && e.target.files[0]) {
+ setFormData({ ...formData, [field]: e.target.files[0] });
+ }
+ };
+
+ const handleDragEnd = (e) => {
+ setFormData({
+ ...formData,
+ location: { lng: e.lng, lat: e.lat }
+ });
+ };
+
+ const handleLocate = (coords) => {
+ setFormData({
+ ...formData,
+ location: { lng: coords.longitude, lat: coords.latitude }
+ });
+ setMapViewport({
+ center: [coords.longitude, coords.latitude],
+ zoom: 15,
+ });
+ };
+
+ const nextStep = () => setStep(step + 1);
+ const prevStep = () => setStep(step - 1);
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ console.log('Seller Verification Submitted', formData);
+ setIsSubmitted(true);
+ };
+
+ if (isSubmitted) {
+ return (
+
+
+
+
Application Received
+
+ Thank you for applying to become a seller. We are reviewing your documents and store information. You will be notified once your store is approved.
+
+
+
+
+ Go to Dashboard
+
+
+
+
+
+ );
+ }
+
+ return (
+
+
+ {/* Decorative background elements */}
+
+
+
+
+
+
+
+
+
= 1 ? 'w-8 bg-primary/80' : 'w-4 bg-muted'}`} />
+
= 2 ? 'w-8 bg-primary/80' : 'w-4 bg-muted'}`} />
+
= 3 ? 'w-8 bg-primary/80' : 'w-4 bg-muted'}`} />
+
= 4 ? 'w-8 bg-primary/80' : 'w-4 bg-muted'}`} />
+
+
+
+
+
+ {step === 1 && "Seller Identity"}
+ {step === 2 && "Store Basics"}
+ {step === 3 && "Store Location"}
+ {step === 4 && "Verification Documents"}
+
+
+ {step === 1 && "Please provide your authentic legal identification details."}
+ {step === 2 && "Tell us about the craft you sell."}
+ {step === 3 && "Where can buyers find your physical store or workshop?"}
+ {step === 4 && "Upload necessary IDs and permits to verify your identity."}
+
+
+
+
+
+
+
+ );
+};
+
+export default SellerVerification;
diff --git a/frontend/src/pages/shared/Signup.jsx b/frontend/src/pages/shared/Signup.jsx
new file mode 100644
index 0000000..0fb2b16
--- /dev/null
+++ b/frontend/src/pages/shared/Signup.jsx
@@ -0,0 +1,192 @@
+import React, { useState } from 'react';
+import { Link } from 'react-router-dom';
+import { Mail, Lock, User, Store, ShoppingBag, ArrowRight } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import { Input } from '@/components/ui/input';
+import { Label } from '@/components/ui/label';
+
+const Signup = () => {
+ const [role, setRole] = useState('buyer'); // 'buyer' or 'seller'
+ const [formData, setFormData] = useState({
+ firstName: '',
+ middleName: '',
+ lastName: '',
+ email: '',
+ password: '',
+ });
+
+ const handleChange = (e) => {
+ setFormData({ ...formData, [e.target.name]: e.target.value });
+ };
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ // Handle signup logic here
+ console.log('Signup attempt', { ...formData, role });
+ };
+
+ return (
+
+
+ {/* Decorative background elements */}
+
+
+
+
+
+
+
+
+
+ Create an Account
+
+
+ Join Artisan Hub today.
+
+
+
+
+
+ {/* Role Toggle */}
+
+ setRole('buyer')}
+ className={`flex-1 flex items-center justify-center gap-2 py-2.5 text-xs font-bold font-sans rounded-lg transition-all duration-300 ${
+ role === 'buyer'
+ ? 'bg-white shadow-sm text-foreground border border-border/50'
+ : 'text-muted-foreground hover:text-foreground hover:bg-white/50'
+ }`}
+ >
+
+ Buyer
+
+ setRole('seller')}
+ className={`flex-1 flex items-center justify-center gap-2 py-2.5 text-xs font-bold font-sans rounded-lg transition-all duration-300 ${
+ role === 'seller'
+ ? 'bg-white shadow-sm text-foreground border border-border/50'
+ : 'text-muted-foreground hover:text-foreground hover:bg-white/50'
+ }`}
+ >
+
+ Seller
+
+
+
+
+
+
+
+
+
Email Address
+
+
+
+
+
+
+
+
Password
+
+
+
+
+
Must be at least 8 characters.
+
+
+
+
+ Create {role === 'seller' ? 'Seller ' : ''}Account
+
+
+
+
+
+ Already have an account?{' '}
+
+ Sign in securely
+
+
+
+
+
+
+
+ );
+};
+
+export default Signup;
\ No newline at end of file