diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index e93756d..17722f5 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -7,8 +7,14 @@ import { } from './components/RoleBasedSidebar'; import WorkspaceView from './components/WorkspaceView'; + import { CartProvider } from './context/CartContext'; +// Auth Pages +import Login from './pages/shared/Login'; +import Signup from './pages/shared/Signup'; +import SellerVerification from './pages/shared/SellerVerification'; + // Buyer Pages import MapDiscovery from './pages/buyer/MapDiscovery'; import Discover from './pages/buyer/Discover'; @@ -18,6 +24,7 @@ import ProductDetail from './pages/buyer/ProductDetail'; import Cart from './pages/buyer/Cart'; import Checkout from './pages/buyer/Checkout'; import Orders from './pages/buyer/Orders'; +import BuyerProfile from './pages/buyer/BuyerProfile'; // Seller Pages import Dashboard from './pages/seller/dashboard'; @@ -34,10 +41,43 @@ import Users from './pages/admin/users'; import Logs from './pages/admin/logs'; import Disputes from './pages/admin/disputes'; +// Admin Pages +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 = () => ( + + + } />} > + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + } />}> + } /> + + + } />}> + } /> + + + + } /> + } /> + } /> + {/* 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 */} +
+ + + + + + + + + + + + {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/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 */} +
+
+
+ {initials} +
+
+ +
+
+
+
+
+
+ +

+ {formData.firstName || formData.lastName ? `${formData.firstName} ${formData.lastName}` : 'Guest User'} +

+

+ Verified Buyer +

+ +
+ +
+
+
+ +
+ {formData.email || 'No email provided'} +
+
+
+ +
+ {formData.phone || 'No phone provided'} +
+
+ +
+ + +
+
+ + {/* Right Form Content */} +
+
+ + {/* Personal Information */} +
+
+
+ +
+

Personal Details

+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + {/* Delivery Address & Location */} +
+
+
+ +
+

Delivery Address

+
+ +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ +
+
+ + Drag pin to adjust +
+
+ + + + +
+
+
+ Deliver Here + + + +
+
+
+ + {/* Verification Document */} +
+
+
+ +
+

Identity Verification

+
+
+ +

For secure transactions, we require a valid government-issued ID.

+ +
+
+
+ +
+
+ +
+

PNG, JPG, PDF up to 10MB

+ + {formData.idFile && ( +
+ + {formData.idFile.name} +
+ )} +
+
+
+
+ +
+ +
+ +
+
+
+
+ ); +}; + +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 */} + +
+

+ Don't have an account?{' '} + + Create one now + +

+
+ +
+
+
+ ); +}; + +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. +

+
+ + + +
+
+
+ ); + } + + 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."} +

+
+ +
e.preventDefault()} className="space-y-8"> + + {/* Step 1: Personal Identity */} + {step === 1 && ( +
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + +
+
+ )} + + {/* Step 2: Store Basics */} + {step === 2 && ( +
+
+ + +
+ +
+ + +
+ +
+ +