diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index cae85eb..e93756d 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,6 +1,10 @@ import { BrowserRouter, Routes, Route } from 'react-router-dom'; import RootLayout from './layouts/RootLayout'; -import { BuyerSidebar, SellerSidebar, AdminSidebar } from './components/RoleBasedSidebar'; +import { + BuyerSidebar, + SellerSidebar, + AdminSidebar, +} from './components/RoleBasedSidebar'; import WorkspaceView from './components/WorkspaceView'; import { CartProvider } from './context/CartContext'; @@ -18,27 +22,50 @@ import Orders from './pages/buyer/Orders'; // Seller Pages import Dashboard from './pages/seller/dashboard'; import Catalog from './pages/seller/catalog'; +import Inventory from './pages/seller/inventory'; +import SellerOrders from './pages/seller/orders'; +import Earnings from './pages/seller/earnings'; + +// 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 */} } />} > + } /> + } /> + } /> + } /> + } /> + } /> } /> + {/* Seller Routes */} } />} > } /> } /> + } /> + } /> + } /> } /> + {/* Buyer Routes */} } />} diff --git a/frontend/src/components/RoleBasedSidebar.jsx b/frontend/src/components/RoleBasedSidebar.jsx index 540b620..7fa1f17 100644 --- a/frontend/src/components/RoleBasedSidebar.jsx +++ b/frontend/src/components/RoleBasedSidebar.jsx @@ -114,7 +114,7 @@ export const SellerSidebar = () => ( icon={Store} /> @@ -133,7 +133,7 @@ export const AdminSidebar = () => (
    { + // Prevent scrolling when modal is open + 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. Check back later!"} +

    +
    + + {/* Footer */} +
    + +
    +
    +
    + ); +}; + +export default ActionModal; diff --git a/frontend/src/components/seller/AlertBanner.jsx b/frontend/src/components/seller/AlertBanner.jsx new file mode 100644 index 0000000..68a61fe --- /dev/null +++ b/frontend/src/components/seller/AlertBanner.jsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { TriangleAlert } from 'lucide-react'; + +const AlertBanner = ({ title, message, buttonText, onClick, variant = 'danger', icon: Icon = TriangleAlert }) => { + // Danger variant (Red/Pink) + let containerClass = "bg-[#FCF1F0] border border-[#EAC2BE]"; + let iconContainerClass = "bg-[#F8E2DF]"; + let iconColor = "text-destructive"; + let buttonClass = "bg-[#C85746] hover:bg-destructive text-white"; + + // Warning variant (Beige/Brown) + if (variant === 'warning') { + containerClass = "bg-[#F3EBE3] border border-[#E3D4C4]"; + iconContainerClass = "bg-[#EBDDD0]"; + iconColor = "text-[#8C5233]"; + buttonClass = "bg-[#8C5233] hover:bg-[#7E4A2E] text-white"; + } + + return ( +
    +
    +
    + +
    +
    +

    + {title} +

    +

    + {message} +

    +
    +
    + +
    + ); +}; + +export default AlertBanner; diff --git a/frontend/src/components/seller/DailyShipmentsChart.jsx b/frontend/src/components/seller/DailyShipmentsChart.jsx new file mode 100644 index 0000000..147fe8e --- /dev/null +++ b/frontend/src/components/seller/DailyShipmentsChart.jsx @@ -0,0 +1,44 @@ +import React from 'react'; + +const DailyShipmentsChart = ({ data }) => { + // data is expected to be an array of objects: { label: 'MON', value: 40, isHighlighted: false } + const maxVal = Math.max(...data.map(d => d.value), 1); + + return ( +
    +
    +

    + DAILY SHIPMENTS +

    +

    + Last 7 Days +

    +
    + + {/* Bar Chart Area */} +
    +
    + {data.map((item, i) => { + const heightPercent = (item.value / maxVal) * 100; + return ( +
    + ); + })} +
    + + {/* X-Axis Labels */} +
    + {data.map((item, i) => ( + {item.label} + ))} +
    +
    +
    + ); +}; + +export default DailyShipmentsChart; diff --git a/frontend/src/components/seller/DashboardFAB.jsx b/frontend/src/components/seller/DashboardFAB.jsx index 3901fa4..4edf162 100644 --- a/frontend/src/components/seller/DashboardFAB.jsx +++ b/frontend/src/components/seller/DashboardFAB.jsx @@ -1,9 +1,12 @@ import React from 'react'; import { Plus } from 'lucide-react'; -const DashboardFAB = () => { +const DashboardFAB = ({ onClick }) => { return ( - ); diff --git a/frontend/src/components/seller/DashboardHeader.jsx b/frontend/src/components/seller/DashboardHeader.jsx index a287e19..ad49e0d 100644 --- a/frontend/src/components/seller/DashboardHeader.jsx +++ b/frontend/src/components/seller/DashboardHeader.jsx @@ -1,7 +1,15 @@ import React from 'react'; -import { Search, Bell, MessageSquare } from 'lucide-react'; +import { Search, Bell, MessageSquare, Settings } from 'lucide-react'; -const DashboardHeader = () => { +const DashboardHeader = ({ + user = { + name: 'Habib Mangoranda', + role: 'Master Potter', + initials: 'HM' + }, + searchPlaceholder = 'Search orders, products...', + showSettings = false +}) => { return (
    {/* Search Bar */} @@ -12,7 +20,7 @@ const DashboardHeader = () => {
    @@ -21,20 +29,26 @@ const DashboardHeader = () => { - + {showSettings ? ( + + ) : ( + + )} {/* User Profile */}
    -
    Habib Mangoranda
    -
    Master Potter
    +
    {user.name}
    +
    {user.role}
    Elena Rodriguez
    diff --git a/frontend/src/components/seller/DeliveryPerformance.jsx b/frontend/src/components/seller/DeliveryPerformance.jsx new file mode 100644 index 0000000..e93af73 --- /dev/null +++ b/frontend/src/components/seller/DeliveryPerformance.jsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { Clock } from 'lucide-react'; + +const DeliveryPerformance = ({ onTimeRate, avgFulfillmentTime, performanceMessage }) => { + return ( +
    +

    + DELIVERY PERFORMANCE +

    + + {/* On-Time Rate */} +
    +
    + On-Time Delivery Rate + {onTimeRate}% +
    +
    +
    +
    +
    + + {/* Avg Fulfillment Time */} +
    +
    +
    Avg. Fulfillment Time
    +
    {avgFulfillmentTime} Days
    +
    +
    + +
    +
    + + {/* Message */} +
    +

    + "{performanceMessage}" +

    +
    +
    + ); +}; + +export default DeliveryPerformance; diff --git a/frontend/src/components/seller/InventoryOptimizer.jsx b/frontend/src/components/seller/InventoryOptimizer.jsx new file mode 100644 index 0000000..d2714e6 --- /dev/null +++ b/frontend/src/components/seller/InventoryOptimizer.jsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { Sparkles } from 'lucide-react'; + +const InventoryOptimizer = ({ title, description, buttonText, onOptimize }) => { + return ( +
    +

    + {title} +

    +

    + {description} +

    + + +
    + ); +}; + +export default InventoryOptimizer; diff --git a/frontend/src/components/seller/InventoryStatCard.jsx b/frontend/src/components/seller/InventoryStatCard.jsx new file mode 100644 index 0000000..8921039 --- /dev/null +++ b/frontend/src/components/seller/InventoryStatCard.jsx @@ -0,0 +1,29 @@ +import React from 'react'; + +const InventoryStatCard = ({ title, value, subtext, subtextColor = 'text-neutral-dark/60', icon: Icon, iconBgClass = 'bg-[#F5EDE8]' }) => { + return ( +
    +
    +

    + {title} +

    + {Icon && ( +
    + +
    + )} +
    + +
    +
    + {value} +
    +
    + {subtext} +
    +
    +
    + ); +}; + +export default InventoryStatCard; diff --git a/frontend/src/components/seller/InventoryTable.jsx b/frontend/src/components/seller/InventoryTable.jsx new file mode 100644 index 0000000..ce36093 --- /dev/null +++ b/frontend/src/components/seller/InventoryTable.jsx @@ -0,0 +1,103 @@ +import React from 'react'; +import { Filter, Download } from 'lucide-react'; +import Pagination from './Pagination'; // Reuse the Pagination component + +const StatusBadge = ({ status }) => { + let badgeStyle = "bg-[#EBE5D9] text-neutral-dark/70"; // IN STOCK + if (status === 'LOW STOCK') { + badgeStyle = "bg-[#F8E2DF] text-[#C85746]"; + } else if (status === 'OUT OF STOCK') { + badgeStyle = "bg-[#F8E2DF] text-destructive"; + } + + return ( + + {status.replace(' ', '\n')} + + ); +}; + +const InventoryTable = ({ items, totalItems, currentlyShowing }) => { + return ( +
    + {/* Header */} +
    +

    Stock Inventory List

    +
    + + +
    +
    + + {/* Table Content */} +
    + + + + + + + + + + + + + {items.map((item, i) => ( + + + + + + + + + ))} + +
    Item DetailsSKUCategoryCurrent StockReorder PointStatus
    +
    +
    + {item.name} +
    +
    +

    {item.name}

    +

    {item.description}

    +
    +
    +
    +
    + {item.sku.split('-').map((part, index) => ( + + {part}{index < item.sku.split('-').length - 1 ? '-' : ''}
    +
    + ))} +
    +
    + + {item.category} + + + + {item.currentStock} pcs + + + {item.reorderPoint ? `${item.reorderPoint} pcs` : ''} + + +
    +
    + + {/* Footer / Pagination */} +
    +
    + Showing {currentlyShowing} of {totalItems} items +
    +
    + +
    +
    +
    + ); +}; + +export default InventoryTable; diff --git a/frontend/src/components/seller/OrderQueueTable.jsx b/frontend/src/components/seller/OrderQueueTable.jsx new file mode 100644 index 0000000..52ddff3 --- /dev/null +++ b/frontend/src/components/seller/OrderQueueTable.jsx @@ -0,0 +1,87 @@ +import React from 'react'; +import { Filter, MoreVertical } from 'lucide-react'; +import Pagination from './Pagination'; + +const StatusBadge = ({ status }) => { + let badgeStyle = "bg-[#EBE5D9] text-[#8C5233]"; // default PENDING / READY FOR PICKUP + if (status === 'PROCESSING') { + badgeStyle = "bg-[#D9E2EB] text-[#4A6478]"; + } + + return ( + + {status} + + ); +}; + +const OrderQueueTable = ({ orders, totalOrders, currentlyShowing }) => { + return ( +
    + {/* Header */} +
    +

    Recent Orders Queue

    +
    + + +
    +
    + + {/* Table Content */} +
    + + + + + + + + + + + + {orders.map((order, i) => ( + + + + + + + + ))} + +
    Order IDDateCustomerProductStatus
    + + {order.id.split('-').map((part, index) => ( + + {part}{index < order.id.split('-').length - 1 ? '-' : ''}
    +
    + ))} +
    +
    + {order.date.replace(', ', ',\n')} + + + {order.customer.replace(' ', '\n')} + + + {order.product} + + +
    +
    + + {/* Footer / Pagination */} +
    +
    + Showing {currentlyShowing} of {totalOrders} pending orders +
    +
    + +
    +
    +
    + ); +}; + +export default OrderQueueTable; diff --git a/frontend/src/components/seller/PayoutDetails.jsx b/frontend/src/components/seller/PayoutDetails.jsx new file mode 100644 index 0000000..a4a8f70 --- /dev/null +++ b/frontend/src/components/seller/PayoutDetails.jsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { Building2, ArrowRight } from 'lucide-react'; + +const PayoutDetails = ({ nextPayoutDate, scheduleType, bankName, accountEnding, onManageClick }) => { + return ( +
    +

    + PAYOUT DETAILS +

    + + {/* Next Scheduled Payout */} +
    +

    + NEXT SCHEDULED PAYOUT +

    +
    +
    + {nextPayoutDate} +
    +
    + {scheduleType} +
    +
    +
    + + {/* Linked Bank Account */} +
    +

    + LINKED BANK ACCOUNT +

    +
    +
    + +
    +
    +
    + {bankName} +
    +
    + Checking **** {accountEnding} +
    +
    +
    +
    + + {/* Manage Link */} +
    + +
    +
    + ); +}; + +export default PayoutDetails; diff --git a/frontend/src/components/seller/RestockForecast.jsx b/frontend/src/components/seller/RestockForecast.jsx new file mode 100644 index 0000000..9f3b4a0 --- /dev/null +++ b/frontend/src/components/seller/RestockForecast.jsx @@ -0,0 +1,46 @@ +import React from 'react'; + +const RestockForecast = ({ data }) => { + // data is expected to be an array of objects: { label: 'MON', value: 40 } + const maxVal = Math.max(...data.map(d => d.value), 1); + + return ( +
    +
    +
    +

    Restock Forecast

    +

    Next 30 Days

    +
    + +
    + ANALYTIC
    S +
    +
    + + {/* Bar Chart Area */} +
    +
    + {data.map((item, i) => { + const heightPercent = (item.value / maxVal) * 100; + return ( +
    + ); + })} +
    + + {/* X-Axis Labels */} +
    + {data.map((item, i) => ( + {item.label} + ))} +
    +
    +
    + ); +}; + +export default RestockForecast; diff --git a/frontend/src/components/seller/RevenueTrendChart.jsx b/frontend/src/components/seller/RevenueTrendChart.jsx new file mode 100644 index 0000000..b3c052b --- /dev/null +++ b/frontend/src/components/seller/RevenueTrendChart.jsx @@ -0,0 +1,51 @@ +import React from 'react'; + +const RevenueTrendChart = ({ data }) => { + // data expected to be an array of objects representing weeks: + // { label: 'W1', bars: [40, 60, 50] } // values 0-100 representing height % + // One bar can have isHighlighted: true: { value: 90, isHighlighted: true } + + return ( +
    +
    +
    +

    + REVENUE TREND +

    +

    + Last 30 Days +

    +
    + +
    + + {/* Bar Chart Area */} +
    +
    + {data.map((week, i) => ( +
    + {week.bars.map((bar, j) => ( +
    + ))} +
    + ))} +
    + + {/* X-Axis Labels */} +
    + {data.map((week, i) => ( + {week.label} + ))} +
    +
    +
    + ); +}; + +export default RevenueTrendChart; diff --git a/frontend/src/components/seller/TransactionsTable.jsx b/frontend/src/components/seller/TransactionsTable.jsx new file mode 100644 index 0000000..b26f8ea --- /dev/null +++ b/frontend/src/components/seller/TransactionsTable.jsx @@ -0,0 +1,92 @@ +import React from 'react'; +import { Filter } from 'lucide-react'; + +const StatusBadge = ({ status, type }) => { + let badgeStyle = "bg-[#D9E2EB] text-[#4A6478]"; // Default COMPLETED + + if (status === 'PROCESSING') { + badgeStyle = "bg-[#EBE5D9] text-[#8C5233]"; + } else if (status === 'COMPLETED' && type === 'Refund') { + badgeStyle = "bg-[#F8E2DF] text-[#C85746]"; + } + + return ( + + {status} + + ); +}; + +const TransactionsTable = ({ transactions }) => { + return ( +
    + {/* Header */} +
    +

    Recent Transactions

    +
    + + +
    +
    + + {/* Table Content */} +
    + + + + + + + + + + + + + {transactions.map((txn, i) => ( + + + + + + + + + ))} + +
    DateTransaction IDTypeOrder IDStatusAmount
    + {txn.date.replace(', ', ',\n')} + + + {txn.id} + + + {txn.type} + + + {txn.orderId || '—'} + + + + + + {txn.amount} + +
    +
    + + {/* Footer */} +
    + + View All Transactions + +
    +
    + ); +}; + +export default TransactionsTable; diff --git a/frontend/src/pages/seller/catalog.jsx b/frontend/src/pages/seller/catalog.jsx index be4ff7c..9b3ccb1 100644 --- a/frontend/src/pages/seller/catalog.jsx +++ b/frontend/src/pages/seller/catalog.jsx @@ -1,10 +1,11 @@ -import React from 'react'; import { Plus } from 'lucide-react'; import DashboardHeader from '../../components/seller/DashboardHeader'; import CatalogFilterBar from '../../components/seller/CatalogFilterBar'; import ProductCard from '../../components/seller/ProductCard'; import Pagination from '../../components/seller/Pagination'; import DashboardFAB from '../../components/seller/DashboardFAB'; +import ActionModal from '../../components/seller/ActionModal'; +import { useState } from 'react'; const dummyProducts = [ { @@ -82,6 +83,16 @@ const dummyProducts = [ ]; const Catalog = () => { + 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 (
    @@ -98,7 +109,10 @@ const Catalog = () => {
    {/* Add Product Button */} - @@ -115,7 +129,15 @@ const Catalog = () => { - + openModal('Quick Actions', 'Quick action shortcuts will be available in a future update.')} /> + + {/* Action Modal */} +
    ); }; diff --git a/frontend/src/pages/seller/dashboard.jsx b/frontend/src/pages/seller/dashboard.jsx index fd15b5c..bfd3274 100644 --- a/frontend/src/pages/seller/dashboard.jsx +++ b/frontend/src/pages/seller/dashboard.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import { Calendar, ChevronDown } from 'lucide-react'; import DashboardHeader from '../../components/seller/DashboardHeader'; import StatCard from '../../components/seller/StatCard'; @@ -6,8 +5,20 @@ import SalesPerformance from '../../components/seller/SalesPerformance'; import TopProducts from '../../components/seller/TopProducts'; import RecentOrders from '../../components/seller/RecentOrders'; import DashboardFAB from '../../components/seller/DashboardFAB'; +import ActionModal from '../../components/seller/ActionModal'; +import { useState } from 'react'; const Dashboard = () => { + 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 (
    @@ -24,7 +35,10 @@ const Dashboard = () => {
    {/* Date Picker Button */} -
    {/* Floating Action Button */} - + openModal('Quick Actions', 'Quick action shortcuts will be available in a future update.')} /> + + {/* Action Modal */} +
    ); }; diff --git a/frontend/src/pages/seller/earnings.jsx b/frontend/src/pages/seller/earnings.jsx new file mode 100644 index 0000000..071b2af --- /dev/null +++ b/frontend/src/pages/seller/earnings.jsx @@ -0,0 +1,188 @@ +import { Wallet, Banknote, Landmark, Clock, CheckCircle, TrendingUp } from 'lucide-react'; +import DashboardHeader from '../../components/seller/DashboardHeader'; +import InventoryStatCard from '../../components/seller/InventoryStatCard'; +import TransactionsTable from '../../components/seller/TransactionsTable'; +import RevenueTrendChart from '../../components/seller/RevenueTrendChart'; +import PayoutDetails from '../../components/seller/PayoutDetails'; +import ActionModal from '../../components/seller/ActionModal'; +import { useState } from 'react'; + +// --- Dummy Data (Data Contract for Backend) --- +const pageData = { + userProfile: { + name: 'Julian Marks', + role: 'Master Weaver', + initials: 'JM' + }, + stats: { + revenue: { value: 'P24,850.40' }, + available: { value: 'P4,120.00' }, + pending: { value: 'P1,840.50', subtext: '3-5 days arrival' }, + lastPayout: { value: 'P3,200.00', subtext: 'Oct 12, 2023' } + }, + recentTransactions: [ + { + id: 'TXN-9021834', + date: 'Oct 24, 2023', + type: 'Sale', + orderId: '#AH-22941', + status: 'COMPLETED', + amount: '+P120.00' + }, + { + id: 'TXN-9021822', + date: 'Oct 23, 2023', + type: 'Payout', + orderId: null, + status: 'PROCESSING', + amount: '-P3,200.00' + }, + { + id: 'TXN-9021810', + date: 'Oct 22, 2023', + type: 'Sale', + orderId: '#AH-22938', + status: 'COMPLETED', + amount: '+P45.50' + }, + { + id: 'TXN-9021795', + date: 'Oct 21, 2023', + type: 'Refund', + orderId: '#AH-22912', + status: 'COMPLETED', + amount: '-P24.00' + } + ], + revenueTrend: [ + { label: 'W1', bars: [45, 65, 55] }, + { label: 'W2', bars: [85, 75, 95] }, + { label: 'W3', bars: [60, 70, { value: 100, isHighlighted: true }] }, + { label: 'W4', bars: [80] } + ], + payoutInfo: { + nextPayoutDate: 'October 28, 2023', + scheduleType: 'Weekly Schedule', + bankName: 'Artisans National Bank', + accountEnding: '5821' + } +}; + +const Earnings = () => { + 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 ( +
    + + + {/* Header Section */} +
    +
    +

    + Earnings & Payouts +

    +

    + Monitor your business growth, manage withdrawals, and track transactions. +

    +
    + + {/* Action Buttons */} +
    + +
    +
    + + {/* Stats Grid */} +
    + + + +12.5% + + } + icon={Banknote} + /> + openModal('Withdraw Funds', 'Withdrawal processing will be available soon.')} + className="text-[#8C5233] hover:text-[#7E4A2E] hover:underline cursor-pointer transition-all" + > + WITHDRAW NOW + + } + icon={Landmark} + iconBgClass="bg-[#F8E2DF]" + /> + + +
    + + {/* Main Content Layout */} +
    + {/* Left Column (Table) */} +
    + +
    + + {/* Right Column (Sidebar Cards) */} +
    + + openModal('Manage Payout Methods', 'Bank account management settings will be added in a future update.')} + /> +
    +
    + + {/* Action Modal */} + +
    + ); +}; + +export default Earnings; \ No newline at end of file diff --git a/frontend/src/pages/seller/inventory.jsx b/frontend/src/pages/seller/inventory.jsx new file mode 100644 index 0000000..898a15b --- /dev/null +++ b/frontend/src/pages/seller/inventory.jsx @@ -0,0 +1,215 @@ +import { Users, RotateCcw, Box, TrendingDown, AlertCircle, MapPin } from 'lucide-react'; +import DashboardHeader from '../../components/seller/DashboardHeader'; +import AlertBanner from '../../components/seller/AlertBanner'; +import InventoryStatCard from '../../components/seller/InventoryStatCard'; +import InventoryTable from '../../components/seller/InventoryTable'; +import RestockForecast from '../../components/seller/RestockForecast'; +import InventoryOptimizer from '../../components/seller/InventoryOptimizer'; +import ActionModal from '../../components/seller/ActionModal'; +import { useState } from 'react'; + +// --- Dummy Data (Data Contract for Backend) --- +const pageData = { + userProfile: { + name: 'Julian Marks', + role: 'Master Weaver', + initials: 'JM' + }, + alert: { + title: 'Critical Action Required: Low Stock Alert', + message: '4 items have fallen below their reorder points. Restock recommended immediately.', + buttonText: 'Review Items' + }, + stats: { + totalSkus: { value: '1,284', subtext: '+12 this month', subtextColor: 'text-neutral-dark/50' }, + lowStock: { value: '04', subtext: 'Alerting now', subtextColor: 'text-[#C85746]' }, + outOfStock: { value: '01', subtext: 'Needs removal', subtextColor: 'text-destructive' }, + warehouses: { value: '03', subtext: 'Global hubs', subtextColor: 'text-neutral-dark/50' } + }, + inventoryItems: [ + { + sku: 'DY-IND-001', + name: 'Organic Indigo Dye', + description: 'Natural Fermentation Base', + category: 'TEXTILE', + currentStock: 120, + reorderPoint: 50, + status: 'IN STOCK', + image: 'https://placehold.co/100x100/1E3A8A/FFFFFF?text=Indigo' + }, + { + sku: 'TX-SIL-202', + name: 'Hand-Spun Raw Silk', + description: 'Natural Ivory, Grade A', + category: 'TEXTILE', + currentStock: 12, + reorderPoint: 20, + status: 'LOW STOCK', + image: 'https://placehold.co/100x100/FDE68A/78350F?text=Silk' + }, + { + sku: 'CL-TER-509', + name: 'Terracotta Clay', + description: 'Fine Grit, Red Earth', + category: 'CLAY', + currentStock: 150, + reorderPoint: 50, + status: 'IN STOCK', + image: 'https://placehold.co/100x100/9A3412/FFFFFF?text=Clay' + }, + { + sku: 'WD-TEA-991', + name: 'Reclaimed Teak Planks', + description: 'Aged 20yrs, 2" thickness', + category: 'WOOD', + currentStock: 0, + reorderPoint: 10, + status: 'OUT OF STOCK', + image: 'https://placehold.co/100x100/A8A29E/44403C?text=Teak' + } + ], + forecast: [ + { label: 'MON', value: 30 }, + { label: 'TUE', value: 25 }, + { label: 'WED', value: 45 }, + { label: 'THU', value: 15 }, + { label: 'FRI', value: 35 }, + { label: 'SAT', value: 25 }, + { label: 'SUN', value: 50 } + ], + optimizer: { + title: 'Inventory Optimizer', + description: 'You can save up to 12% on shipping by consolidating your next 3 supplier orders.', + buttonText: 'Optimize Orders' + } +}; + +const Inventory = () => { + 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 ( +
    + + + {/* Header Section */} +
    +
    +

    + Inventory Management +

    +

    + Monitor, restock, and manage your artisan craft supplies. +

    +
    + + {/* Action Buttons */} +
    + + +
    +
    + + {/* Alert Banner */} +
    + openModal('Review Items', 'Low stock items review panel will open here.')} + /> +
    + + {/* Stats Grid */} +
    + + + + +
    + + {/* Table Section */} +
    + +
    + + {/* Bottom Grid */} +
    +
    + +
    +
    + openModal('Optimize Orders', 'The inventory optimizer tool is currently under construction.')} + /> +
    +
    + + {/* Action Modal */} + +
    + ); +}; + +export default Inventory; \ No newline at end of file diff --git a/frontend/src/pages/seller/orders.jsx b/frontend/src/pages/seller/orders.jsx new file mode 100644 index 0000000..b15673a --- /dev/null +++ b/frontend/src/pages/seller/orders.jsx @@ -0,0 +1,202 @@ +import { Settings, Printer, CircleAlert, ClipboardList, Package, Truck, CheckCircle } from 'lucide-react'; +import DashboardHeader from '../../components/seller/DashboardHeader'; +import AlertBanner from '../../components/seller/AlertBanner'; +import InventoryStatCard from '../../components/seller/InventoryStatCard'; +import OrderQueueTable from '../../components/seller/OrderQueueTable'; +import DailyShipmentsChart from '../../components/seller/DailyShipmentsChart'; +import DeliveryPerformance from '../../components/seller/DeliveryPerformance'; +import ActionModal from '../../components/seller/ActionModal'; +import { useState } from 'react'; + +// --- Dummy Data (Data Contract for Backend) --- +const pageData = { + userProfile: { + name: 'Julian Marks', + role: 'Master Weaver', + initials: 'JM' + }, + alert: { + title: '3 Priority Orders', + message: 'Ship today to meet your 2-day delivery guarantee. Late shipments may impact seller ratings.', + buttonText: 'View Priority Queue', + variant: 'warning' + }, + stats: { + pending: { value: '14', subtext: '-2%', subtextColor: 'text-destructive' }, + awaiting: { value: '5', subtext: '+12%', subtextColor: 'text-[#8C5233]' }, + transit: { value: '42', subtext: '+5%', subtextColor: 'text-[#8C5233]' }, + delivered: { value: '128', subtext: '+18%', subtextColor: 'text-[#8C5233]' } + }, + recentOrders: [ + { + id: '#ORD-9821', + date: 'Oct 24, 2023', + customer: 'Sarah Jenkins', + product: '2x Organic Indigo Dye', + status: 'PENDING' + }, + { + id: '#ORD-9819', + date: 'Oct 23, 2023', + customer: 'Michael Chen', + product: '1x Hand-Spun Raw Silk', + status: 'PROCESSING' + }, + { + id: '#ORD-9815', + date: 'Oct 22, 2023', + customer: 'Emma Watson', + product: '4x Hand-Carved Spools', + status: 'READY FOR PICKUP' + }, + { + id: '#ORD-9812', + date: 'Oct 22, 2023', + customer: 'Robert Downey', + product: '1x Linen Weaving Kit', + status: 'PENDING' + } + ], + shipmentsData: [ + { label: 'MON', value: 20, isHighlighted: false }, + { label: 'TUE', value: 30, isHighlighted: false }, + { label: 'WED', value: 25, isHighlighted: false }, + { label: 'THU', value: 40, isHighlighted: false }, + { label: 'FRI', value: 35, isHighlighted: false }, + { label: 'SAT', value: 45, isHighlighted: false }, + { label: 'SUN', value: 15, isHighlighted: true } + ], + performance: { + onTimeRate: 98.5, + avgFulfillmentTime: '1.2', + message: 'Excellent performance this week.' + } +}; + +const Orders = () => { + 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 ( +
    + + + {/* Header Section */} +
    +
    +

    + Order Fulfilment +

    +

    + Process, ship, and track your artisan orders. +

    +
    + + {/* Action Buttons */} +
    + + +
    +
    + + {/* Alert Banner */} +
    + openModal('Priority Queue', 'The priority queue view is being prepared.')} + /> +
    + + {/* Stats Grid */} +
    + + + + +
    + + {/* Main Content Layout */} +
    + {/* Left Column (Table) */} +
    + +
    + + {/* Right Column (Sidebar Cards) */} +
    + + +
    +
    + + {/* Action Modal */} + +
    + ); +}; + +export default Orders; \ No newline at end of file