+ {/* Current Status */}
-
-
Current Order Status
-
- {orderInfoItems.map((item, index) => (
-
+
+
+
- ))}
+
+
+
+ {currentStatus}
+
+
+ {currentStatusInfo.description}
+
+
+ Last updated: {formatDateTime(order.status_histories?.[0]?.created_at)}
+
+
+
-
- {order?.status_histories?.[0]?.status ===
- Order_Status_Enum.Canceled &&
Order Has Been Cancelled
}
-
-
- >
- );
-};
+ {/* Status Flow Information Dialog */}
+
+
+ )
+}
diff --git a/packages/features/Reports/OrderStatusDistribution.tsx b/packages/features/Reports/OrderStatusDistribution.tsx
new file mode 100644
index 00000000..de504087
--- /dev/null
+++ b/packages/features/Reports/OrderStatusDistribution.tsx
@@ -0,0 +1,35 @@
+
+import { Card } from "ui";
+import { BarChart, Bar, PieChart, Pie, LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from "recharts"
+import { getRevenueByProduct, getOrderStatusStats, getClientProfitability, getClientSizeStats } from "@sahil/features/Reports/utils";
+
+
+
+export const OrderStatusDistribution = ({ orders }) => {
+ const statusData = getOrderStatusStats(orders)
+ return (
+
+
+
Order Status Distribution
+
Overview of order statuses
+
+
+
+
+
+
+
+
+
+ )
+ }
+
\ No newline at end of file
diff --git a/packages/features/Reports/RevenueByProduct.tsx b/packages/features/Reports/RevenueByProduct.tsx
new file mode 100644
index 00000000..b0775f2f
--- /dev/null
+++ b/packages/features/Reports/RevenueByProduct.tsx
@@ -0,0 +1,27 @@
+
+import { Card } from "ui";
+import { BarChart, Bar, PieChart, Pie, LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from "recharts"
+import { getRevenueByProduct, getOrderStatusStats, getClientProfitability, getClientSizeStats } from "@sahil/features/Reports/utils";
+
+
+export const RevenueByProduct = ({ orders }) => {
+ const revenueData = getRevenueByProduct(orders)
+ return (
+
+
+
Revenue by Product
+
Distribution of revenue across different products
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+ }
\ No newline at end of file
diff --git a/packages/features/Reports/TopClientsByRevenue.tsx b/packages/features/Reports/TopClientsByRevenue.tsx
new file mode 100644
index 00000000..2da3908d
--- /dev/null
+++ b/packages/features/Reports/TopClientsByRevenue.tsx
@@ -0,0 +1,28 @@
+
+import { Card } from "ui";
+import { BarChart, Bar, PieChart, Pie, LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from "recharts"
+import { getRevenueByProduct, getOrderStatusStats, getClientProfitability, getClientSizeStats } from "@sahil/features/Reports/utils";
+
+
+export const TopClientsByRevenue = ({ orders }) => {
+ const clientProfitability = getClientProfitability(orders)
+ return (
+
+
+
Top Clients by Revenue
+
Most profitable clients
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+ }
+
\ No newline at end of file
diff --git a/packages/features/Reports/utils.tsx b/packages/features/Reports/utils.tsx
new file mode 100644
index 00000000..bbf7a0e0
--- /dev/null
+++ b/packages/features/Reports/utils.tsx
@@ -0,0 +1,110 @@
+import { startOfWeek, endOfWeek, startOfMonth, endOfMonth, startOfQuarter, endOfQuarter, startOfYear, endOfYear, isWithinInterval } from "date-fns"
+
+export interface Order {
+ id: string
+ status: string
+ created_at: string
+ business: {
+ id: string
+ name: string
+ type: string
+ }
+ order_items: {
+ price: number
+ product: {
+ name: string
+ price: number
+ quantity: number
+ discount: number | null
+ }
+ }[]
+}
+
+// Get revenue per product type
+export const getRevenueByProduct = (orders: Order[]) => {
+ const productRevenue = orders.reduce((acc: { [key: string]: number }, order) => {
+ order.order_items.forEach(item => {
+ const productName = item.product.name
+ const revenue = item.product.price * item.product.quantity
+ acc[productName] = (acc[productName] || 0) + revenue
+ })
+ return acc
+ }, {})
+
+ return Object.entries(productRevenue).map(([name, revenue]) => ({
+ name,
+ revenue,
+ }))
+}
+
+// Get order status statistics
+export const getOrderStatusStats = (orders: Order[]) => {
+ const stats = orders.reduce((acc: { [key: string]: number }, order) => {
+ acc[order.status] = (acc[order.status] || 0) + 1
+ return acc
+ }, {})
+
+ return Object.entries(stats).map(([status, count]) => ({
+ status,
+ count,
+ }))
+}
+
+// Get most profitable clients
+export const getClientProfitability = (orders: Order[]) => {
+ const clientRevenue = orders.reduce((acc: { [key: string]: number }, order) => {
+ const totalOrderValue = order.order_items.reduce((sum, item) => {
+ return sum + (item.product.price * item.product.quantity)
+ }, 0)
+
+ acc[order.business.name] = (acc[order.business.name] || 0) + totalOrderValue
+ return acc
+ }, {})
+
+ return Object.entries(clientRevenue)
+ .map(([name, revenue]) => ({
+ name,
+ revenue,
+ }))
+ .sort((a, b) => b.revenue - a.revenue)
+}
+
+// Get client size distribution based on order volume
+export const getClientSizeStats = (orders: Order[]) => {
+ // Count orders per client
+ const orderCounts = orders.reduce((acc: { [key: string]: number }, order) => {
+ acc[order.business.name] = (acc[order.business.name] || 0) + 1
+ return acc
+ }, {})
+
+ // Categorize clients by size
+ const sizeCategories = {
+ 'Small (1-5)': 0,
+ 'Medium (6-15)': 0,
+ 'Large (16-30)': 0,
+ 'Enterprise (30+)': 0,
+ }
+
+ Object.values(orderCounts).forEach(count => {
+ if (count <= 5) sizeCategories['Small (1-5)']++
+ else if (count <= 15) sizeCategories['Medium (6-15)']++
+ else if (count <= 30) sizeCategories['Large (16-30)']++
+ else sizeCategories['Enterprise (30+)']++
+ })
+
+ return Object.entries(sizeCategories).map(([name, value]) => ({
+ name,
+ value,
+ }))
+}
+
+// Filter data by date range (reused from original code)
+export const filterDataByDateRange = (data: Order[], dateRange: DateRange | undefined) => {
+ if (!dateRange?.from || !dateRange?.to || !data?.length) return data
+
+ return data.filter(item => {
+ const itemDate = new Date(item.created_at)
+ return isWithinInterval(itemDate, { start: dateRange.from!, end: dateRange.to! })
+ })
+}
+
diff --git a/packages/features/Shared/CollectionControls.tsx b/packages/features/Shared/CollectionControls.tsx
new file mode 100644
index 00000000..fb323532
--- /dev/null
+++ b/packages/features/Shared/CollectionControls.tsx
@@ -0,0 +1,124 @@
+import { useState } from "react";
+import { useFetchSupplierOrders } from "@sahil/lib/hooks/suppliers";
+import { useGetUserById } from "@sahil/lib/hooks/users";
+import { useUserSuppliers } from "@sahil/lib/hooks/useUserOrganizations";
+import { Card } from "ui";
+import { getRevenueByProduct, getOrderStatusStats, getClientProfitability, getClientSizeStats } from "../Reports/utils";
+import { SelectOption, CustomSelect } from "./CustomSelect";
+import { HiCalendar, HiMagnifyingGlass, HiOutlineCalendarDays } from 'react-icons/hi2';
+
+type DateRange = 'today' | 'week' | 'month' | 'custom';
+type SortOption = 'date_desc' | 'date_asc' | 'status' | 'customer';
+type OrderStatus = 'CONFIRMED' | 'CANCELLED' | 'PENDING' | 'PROCESSING' | 'DELIVERED';
+
+interface FilterState {
+ search: string;
+ status: OrderStatus | '';
+ dateRange: DateRange;
+ customDateRange?: { from: Date; to: Date };
+ sortBy: SortOption;
+}
+
+const initialFilters: FilterState = {
+ search: '',
+ status: '',
+ dateRange: 'week',
+ sortBy: 'date_desc'
+};
+
+export const CollectionControls = ({user}) => {
+ ;
+
+ const { data: currentUser, loading: userLoading } = useGetUserById(user?.id);
+
+ console.log("currentUser", currentUser);
+
+ const [dateRange, setDateRange] = useState({ from: new Date(), to: new Date() })
+
+
+ const statusOptions: SelectOption[] = [
+ { value: '', label: 'All Status' },
+ ...(['CONFIRMED', 'CANCELLED', 'PENDING', 'PROCESSING', 'DELIVERED'] as OrderStatus[]).map(status => ({
+ value: status,
+ label: status,
+ icon:
+ }))
+ ]
+
+ const dateRangeOptions: SelectOption[] = [
+ { value: 'today', label: 'Today' },
+ { value: 'week', label: 'This Week' },
+ { value: 'month', label: 'This Month' },
+ { value: 'custom', label: 'Custom Range' },
+ ]
+
+ const sortOptions: SelectOption[] = [
+ { value: 'date_desc', label: 'Newest First' },
+ { value: 'date_asc', label: 'Oldest First' },
+ { value: 'status', label: 'By Status' },
+ { value: 'customer', label: 'By Customer' },
+ ]
+
+
+
+ const supplierOrders = useFetchSupplierOrders();
+ console.log(supplierOrders);
+
+ const [filters, setFilters] = useState
(initialFilters);
+ const [activeBusiness, setActiveBusiness] = useState(null);
+ console.log("currentUser", currentUser);
+
+ const {
+ suppliers,
+ activeSupplier,
+ switchSupplier,
+ loading: suppliersLoading
+ } = useUserSuppliers(
+ user?.id,
+ user?.role
+ );
+
+ console.log(suppliers);
+ return (
+
+
+
+
+
+ setFilters({ ...filters, search: e.target.value })}
+ className="input input-bordered w-full pl-10"
+ />
+
+
+
setFilters({ ...filters, status: value as OrderStatus })}
+ options={statusOptions}
+ placeholder="Filter by status"
+ width="180px"
+ />
+
+ setFilters({ ...filters, dateRange: value as DateRange })}
+ options={dateRangeOptions}
+ placeholder="Select date range"
+ width="180px"
+ />
+
+ setFilters({ ...filters, sortBy: value as SortOption })}
+ options={sortOptions}
+ placeholder="Sort by"
+ width="150px"
+ />
+
+
+
+ )
+ }
\ No newline at end of file
diff --git a/packages/ui/components/IconButton.tsx b/packages/ui/components/IconButton.tsx
index c483c7ce..08a622f2 100644
--- a/packages/ui/components/IconButton.tsx
+++ b/packages/ui/components/IconButton.tsx
@@ -9,7 +9,7 @@ export type IconButtonProps = IconProps & {
};
export const IconButton: FC = ({ className, icon, title, onClick }) => {
- const merged = twMerge("btn btn-xs btn-square", className);
+ const merged = twMerge("btn btn-xs btn-circle btn-ghost", className);
return (