Replace drawer with vaul
This commit is contained in:
@@ -1,11 +1,5 @@
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import {
|
import { Drawer as VaulDrawer } from "vaul";
|
||||||
Drawer,
|
|
||||||
DrawerContent,
|
|
||||||
DrawerDescription,
|
|
||||||
DrawerHeader,
|
|
||||||
DrawerTitle,
|
|
||||||
} from "@/components/ui/drawer";
|
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import { Card } from "@/components/ui/card";
|
import { Card } from "@/components/ui/card";
|
||||||
@@ -188,409 +182,411 @@ export function ProductDetail({ productId, onClose }: ProductDetailProps) {
|
|||||||
if (!productId) return null;
|
if (!productId) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer open={!!productId} onOpenChange={(open) => !open && onClose()}>
|
<VaulDrawer.Root open={!!productId} onOpenChange={(open) => !open && onClose()} direction="right">
|
||||||
<DrawerContent className="h-[90vh] md:h-[90vh] overflow-y-auto">
|
<VaulDrawer.Portal>
|
||||||
<DrawerHeader>
|
<VaulDrawer.Content className="fixed right-0 top-0 h-full w-[90%] max-w-[800px] bg-background p-6 shadow-lg">
|
||||||
<DrawerTitle>
|
<div className="mb-8">
|
||||||
{isLoading ? (
|
<VaulDrawer.Title className="text-2xl font-bold">
|
||||||
<Skeleton className="h-8 w-[200px]" />
|
|
||||||
) : (
|
|
||||||
product?.title
|
|
||||||
)}
|
|
||||||
</DrawerTitle>
|
|
||||||
<DrawerDescription>
|
|
||||||
{isLoading ? (
|
|
||||||
"\u00A0"
|
|
||||||
) : (
|
|
||||||
`SKU: ${product?.SKU} | Stock: ${product?.stock_quantity}`
|
|
||||||
)}
|
|
||||||
</DrawerDescription>
|
|
||||||
</DrawerHeader>
|
|
||||||
|
|
||||||
<div className="px-4 pb-8">
|
|
||||||
<Tabs defaultValue="overview" className="w-full">
|
|
||||||
<TabsList className="w-full justify-start mb-4 sticky top-0 bg-background z-10">
|
|
||||||
<TabsTrigger value="overview">Overview</TabsTrigger>
|
|
||||||
<TabsTrigger value="inventory">Inventory</TabsTrigger>
|
|
||||||
<TabsTrigger value="sales">Sales</TabsTrigger>
|
|
||||||
<TabsTrigger value="purchase">Purchase History</TabsTrigger>
|
|
||||||
<TabsTrigger value="financial">Financial</TabsTrigger>
|
|
||||||
<TabsTrigger value="vendor">Vendor</TabsTrigger>
|
|
||||||
</TabsList>
|
|
||||||
|
|
||||||
<TabsContent value="overview" className="space-y-4">
|
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="space-y-4">
|
<Skeleton className="h-8 w-[200px]" />
|
||||||
<Skeleton className="h-24 w-full" />
|
|
||||||
<Skeleton className="h-24 w-full" />
|
|
||||||
</div>
|
|
||||||
) : (
|
) : (
|
||||||
<div className="grid grid-cols-2 gap-4">
|
product?.title
|
||||||
<Card className="p-4">
|
|
||||||
<h3 className="font-semibold mb-2">Basic Information</h3>
|
|
||||||
<dl className="space-y-2">
|
|
||||||
<div>
|
|
||||||
<dt className="text-sm text-muted-foreground">Brand</dt>
|
|
||||||
<dd>{product?.brand || "N/A"}</dd>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<dt className="text-sm text-muted-foreground">Vendor</dt>
|
|
||||||
<dd>{product?.vendor || "N/A"}</dd>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<dt className="text-sm text-muted-foreground">Categories</dt>
|
|
||||||
<dd className="flex flex-wrap gap-2">
|
|
||||||
{product?.categories?.map(category => (
|
|
||||||
<span key={category} className="inline-flex items-center rounded-md bg-muted px-2 py-1 text-xs font-medium ring-1 ring-inset ring-muted">
|
|
||||||
{category}
|
|
||||||
</span>
|
|
||||||
)) || "N/A"}
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<dt className="text-sm text-muted-foreground">Tags</dt>
|
|
||||||
<dd className="flex flex-wrap gap-2">
|
|
||||||
{product?.tags?.map(tag => (
|
|
||||||
<span key={tag} className="inline-flex items-center rounded-md bg-muted px-2 py-1 text-xs font-medium ring-1 ring-inset ring-muted">
|
|
||||||
{tag}
|
|
||||||
</span>
|
|
||||||
)) || "N/A"}
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
</dl>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card className="p-4">
|
|
||||||
<h3 className="font-semibold mb-2">Pricing</h3>
|
|
||||||
<dl className="space-y-2">
|
|
||||||
<div>
|
|
||||||
<dt className="text-sm text-muted-foreground">Price</dt>
|
|
||||||
<dd>${formatPrice(product?.price)}</dd>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<dt className="text-sm text-muted-foreground">Regular Price</dt>
|
|
||||||
<dd>${formatPrice(product?.regular_price)}</dd>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<dt className="text-sm text-muted-foreground">Cost Price</dt>
|
|
||||||
<dd>${formatPrice(product?.cost_price)}</dd>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<dt className="text-sm text-muted-foreground">Landing Cost</dt>
|
|
||||||
<dd>${formatPrice(product?.landing_cost_price)}</dd>
|
|
||||||
</div>
|
|
||||||
</dl>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card className="p-4">
|
|
||||||
<h3 className="font-semibold mb-2">Stock Status</h3>
|
|
||||||
<dl className="space-y-2">
|
|
||||||
<div>
|
|
||||||
<dt className="text-sm text-muted-foreground">Current Stock</dt>
|
|
||||||
<dd className="text-2xl font-semibold">{product?.stock_quantity}</dd>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<dt className="text-sm text-muted-foreground">Status</dt>
|
|
||||||
<dd>{product?.metrics?.stock_status}</dd>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<dt className="text-sm text-muted-foreground">Days of Stock</dt>
|
|
||||||
<dd>{product?.metrics?.days_of_inventory} days</dd>
|
|
||||||
</div>
|
|
||||||
</dl>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card className="p-4">
|
|
||||||
<h3 className="font-semibold mb-2">Sales Velocity</h3>
|
|
||||||
<dl className="space-y-2">
|
|
||||||
<div>
|
|
||||||
<dt className="text-sm text-muted-foreground">Daily Sales</dt>
|
|
||||||
<dd>{product?.metrics?.daily_sales_avg?.toFixed(1)} units</dd>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<dt className="text-sm text-muted-foreground">Weekly Sales</dt>
|
|
||||||
<dd>{product?.metrics?.weekly_sales_avg?.toFixed(1)} units</dd>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<dt className="text-sm text-muted-foreground">Monthly Sales</dt>
|
|
||||||
<dd>{product?.metrics?.monthly_sales_avg?.toFixed(1)} units</dd>
|
|
||||||
</div>
|
|
||||||
</dl>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card className="p-4">
|
|
||||||
<h3 className="font-semibold mb-2">Sales Trend</h3>
|
|
||||||
<div className="h-64">
|
|
||||||
<ResponsiveContainer width="100%" height="100%">
|
|
||||||
<LineChart data={combinedData?.monthly_sales || []}>
|
|
||||||
<CartesianGrid strokeDasharray="3 3" />
|
|
||||||
<XAxis dataKey="month" />
|
|
||||||
<YAxis />
|
|
||||||
<Tooltip />
|
|
||||||
<Line type="monotone" dataKey="quantity" stroke="#8884d8" name="Quantity" />
|
|
||||||
<Line type="monotone" dataKey="revenue" stroke="#82ca9d" name="Revenue" />
|
|
||||||
</LineChart>
|
|
||||||
</ResponsiveContainer>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</TabsContent>
|
</VaulDrawer.Title>
|
||||||
|
<VaulDrawer.Description className="text-muted-foreground">
|
||||||
<TabsContent value="inventory" className="space-y-4">
|
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<Skeleton className="h-48 w-full" />
|
"\u00A0"
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-4">
|
`SKU: ${product?.SKU} | Stock: ${product?.stock_quantity}`
|
||||||
<Card className="p-4">
|
|
||||||
<h3 className="font-semibold mb-2">Current Stock</h3>
|
|
||||||
<dl className="grid grid-cols-3 gap-4">
|
|
||||||
<div>
|
|
||||||
<dt className="text-sm text-muted-foreground">Stock Quantity</dt>
|
|
||||||
<dd className="text-2xl font-semibold">{product?.stock_quantity}</dd>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<dt className="text-sm text-muted-foreground">Days of Inventory</dt>
|
|
||||||
<dd className="text-2xl font-semibold">{product?.metrics?.days_of_inventory || 0}</dd>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<dt className="text-sm text-muted-foreground">Status</dt>
|
|
||||||
<dd className="text-2xl font-semibold">{product?.metrics?.stock_status || "N/A"}</dd>
|
|
||||||
</div>
|
|
||||||
</dl>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card className="p-4">
|
|
||||||
<h3 className="font-semibold mb-2">Stock Thresholds</h3>
|
|
||||||
<dl className="grid grid-cols-3 gap-4">
|
|
||||||
<div>
|
|
||||||
<dt className="text-sm text-muted-foreground">Reorder Point</dt>
|
|
||||||
<dd>{product?.metrics?.reorder_point || 0}</dd>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<dt className="text-sm text-muted-foreground">Safety Stock</dt>
|
|
||||||
<dd>{product?.metrics?.safety_stock || 0}</dd>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<dt className="text-sm text-muted-foreground">ABC Class</dt>
|
|
||||||
<dd>{product?.metrics?.abc_class || "N/A"}</dd>
|
|
||||||
</div>
|
|
||||||
</dl>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</TabsContent>
|
</VaulDrawer.Description>
|
||||||
|
</div>
|
||||||
|
|
||||||
<TabsContent value="sales" className="space-y-4">
|
<div className="pb-8">
|
||||||
{isLoading ? (
|
<Tabs defaultValue="overview" className="w-full">
|
||||||
<Skeleton className="h-96 w-full" />
|
<TabsList className="w-full justify-start mb-4 sticky top-0 bg-background z-10">
|
||||||
) : (
|
<TabsTrigger value="overview">Overview</TabsTrigger>
|
||||||
<div className="space-y-4">
|
<TabsTrigger value="inventory">Inventory</TabsTrigger>
|
||||||
<Card className="p-4">
|
<TabsTrigger value="sales">Sales</TabsTrigger>
|
||||||
<h3 className="font-semibold mb-2">Recent Orders</h3>
|
<TabsTrigger value="purchase">Purchase History</TabsTrigger>
|
||||||
<Table>
|
<TabsTrigger value="financial">Financial</TabsTrigger>
|
||||||
<TableHeader>
|
<TabsTrigger value="vendor">Vendor</TabsTrigger>
|
||||||
<TableRow>
|
</TabsList>
|
||||||
<TableHead>Date</TableHead>
|
|
||||||
<TableHead>Order #</TableHead>
|
<TabsContent value="overview" className="space-y-4">
|
||||||
<TableHead>Customer</TableHead>
|
{isLoading ? (
|
||||||
<TableHead>Quantity</TableHead>
|
<div className="space-y-4">
|
||||||
<TableHead>Price</TableHead>
|
<Skeleton className="h-24 w-full" />
|
||||||
<TableHead>Status</TableHead>
|
<Skeleton className="h-24 w-full" />
|
||||||
</TableRow>
|
</div>
|
||||||
</TableHeader>
|
) : (
|
||||||
<TableBody>
|
<div className="grid grid-cols-2 gap-4">
|
||||||
{combinedData?.recent_orders?.map((order: NonNullable<Product['recent_orders']>[number]) => (
|
<Card className="p-4">
|
||||||
<TableRow key={order.order_number}>
|
<h3 className="font-semibold mb-2">Basic Information</h3>
|
||||||
<TableCell>{order.date}</TableCell>
|
<dl className="space-y-2">
|
||||||
<TableCell>{order.order_number}</TableCell>
|
<div>
|
||||||
<TableCell>{order.customer}</TableCell>
|
<dt className="text-sm text-muted-foreground">Brand</dt>
|
||||||
<TableCell>{order.quantity}</TableCell>
|
<dd>{product?.brand || "N/A"}</dd>
|
||||||
<TableCell>${formatPrice(order.price)}</TableCell>
|
</div>
|
||||||
<TableCell>{order.status}</TableCell>
|
<div>
|
||||||
</TableRow>
|
<dt className="text-sm text-muted-foreground">Vendor</dt>
|
||||||
))}
|
<dd>{product?.vendor || "N/A"}</dd>
|
||||||
{(!combinedData?.recent_orders || combinedData.recent_orders.length === 0) && (
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt className="text-sm text-muted-foreground">Categories</dt>
|
||||||
|
<dd className="flex flex-wrap gap-2">
|
||||||
|
{product?.categories?.map(category => (
|
||||||
|
<span key={category} className="inline-flex items-center rounded-md bg-muted px-2 py-1 text-xs font-medium ring-1 ring-inset ring-muted">
|
||||||
|
{category}
|
||||||
|
</span>
|
||||||
|
)) || "N/A"}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt className="text-sm text-muted-foreground">Tags</dt>
|
||||||
|
<dd className="flex flex-wrap gap-2">
|
||||||
|
{product?.tags?.map(tag => (
|
||||||
|
<span key={tag} className="inline-flex items-center rounded-md bg-muted px-2 py-1 text-xs font-medium ring-1 ring-inset ring-muted">
|
||||||
|
{tag}
|
||||||
|
</span>
|
||||||
|
)) || "N/A"}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card className="p-4">
|
||||||
|
<h3 className="font-semibold mb-2">Pricing</h3>
|
||||||
|
<dl className="space-y-2">
|
||||||
|
<div>
|
||||||
|
<dt className="text-sm text-muted-foreground">Price</dt>
|
||||||
|
<dd>${formatPrice(product?.price)}</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt className="text-sm text-muted-foreground">Regular Price</dt>
|
||||||
|
<dd>${formatPrice(product?.regular_price)}</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt className="text-sm text-muted-foreground">Cost Price</dt>
|
||||||
|
<dd>${formatPrice(product?.cost_price)}</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt className="text-sm text-muted-foreground">Landing Cost</dt>
|
||||||
|
<dd>${formatPrice(product?.landing_cost_price)}</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card className="p-4">
|
||||||
|
<h3 className="font-semibold mb-2">Stock Status</h3>
|
||||||
|
<dl className="space-y-2">
|
||||||
|
<div>
|
||||||
|
<dt className="text-sm text-muted-foreground">Current Stock</dt>
|
||||||
|
<dd className="text-2xl font-semibold">{product?.stock_quantity}</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt className="text-sm text-muted-foreground">Status</dt>
|
||||||
|
<dd>{product?.metrics?.stock_status}</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt className="text-sm text-muted-foreground">Days of Stock</dt>
|
||||||
|
<dd>{product?.metrics?.days_of_inventory} days</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card className="p-4">
|
||||||
|
<h3 className="font-semibold mb-2">Sales Velocity</h3>
|
||||||
|
<dl className="space-y-2">
|
||||||
|
<div>
|
||||||
|
<dt className="text-sm text-muted-foreground">Daily Sales</dt>
|
||||||
|
<dd>{product?.metrics?.daily_sales_avg?.toFixed(1)} units</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt className="text-sm text-muted-foreground">Weekly Sales</dt>
|
||||||
|
<dd>{product?.metrics?.weekly_sales_avg?.toFixed(1)} units</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt className="text-sm text-muted-foreground">Monthly Sales</dt>
|
||||||
|
<dd>{product?.metrics?.monthly_sales_avg?.toFixed(1)} units</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card className="p-4">
|
||||||
|
<h3 className="font-semibold mb-2">Sales Trend</h3>
|
||||||
|
<div className="h-64">
|
||||||
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
|
<LineChart data={combinedData?.monthly_sales || []}>
|
||||||
|
<CartesianGrid strokeDasharray="3 3" />
|
||||||
|
<XAxis dataKey="month" />
|
||||||
|
<YAxis />
|
||||||
|
<Tooltip />
|
||||||
|
<Line type="monotone" dataKey="quantity" stroke="#8884d8" name="Quantity" />
|
||||||
|
<Line type="monotone" dataKey="revenue" stroke="#82ca9d" name="Revenue" />
|
||||||
|
</LineChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
<TabsContent value="inventory" className="space-y-4">
|
||||||
|
{isLoading ? (
|
||||||
|
<Skeleton className="h-48 w-full" />
|
||||||
|
) : (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<Card className="p-4">
|
||||||
|
<h3 className="font-semibold mb-2">Current Stock</h3>
|
||||||
|
<dl className="grid grid-cols-3 gap-4">
|
||||||
|
<div>
|
||||||
|
<dt className="text-sm text-muted-foreground">Stock Quantity</dt>
|
||||||
|
<dd className="text-2xl font-semibold">{product?.stock_quantity}</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt className="text-sm text-muted-foreground">Days of Inventory</dt>
|
||||||
|
<dd className="text-2xl font-semibold">{product?.metrics?.days_of_inventory || 0}</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt className="text-sm text-muted-foreground">Status</dt>
|
||||||
|
<dd className="text-2xl font-semibold">{product?.metrics?.stock_status || "N/A"}</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card className="p-4">
|
||||||
|
<h3 className="font-semibold mb-2">Stock Thresholds</h3>
|
||||||
|
<dl className="grid grid-cols-3 gap-4">
|
||||||
|
<div>
|
||||||
|
<dt className="text-sm text-muted-foreground">Reorder Point</dt>
|
||||||
|
<dd>{product?.metrics?.reorder_point || 0}</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt className="text-sm text-muted-foreground">Safety Stock</dt>
|
||||||
|
<dd>{product?.metrics?.safety_stock || 0}</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt className="text-sm text-muted-foreground">ABC Class</dt>
|
||||||
|
<dd>{product?.metrics?.abc_class || "N/A"}</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
<TabsContent value="sales" className="space-y-4">
|
||||||
|
{isLoading ? (
|
||||||
|
<Skeleton className="h-96 w-full" />
|
||||||
|
) : (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<Card className="p-4">
|
||||||
|
<h3 className="font-semibold mb-2">Recent Orders</h3>
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={6} className="text-center text-muted-foreground">
|
<TableHead>Date</TableHead>
|
||||||
No recent orders
|
<TableHead>Order #</TableHead>
|
||||||
</TableCell>
|
<TableHead>Customer</TableHead>
|
||||||
|
<TableHead>Quantity</TableHead>
|
||||||
|
<TableHead>Price</TableHead>
|
||||||
|
<TableHead>Status</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)}
|
</TableHeader>
|
||||||
</TableBody>
|
<TableBody>
|
||||||
</Table>
|
{combinedData?.recent_orders?.map((order: NonNullable<Product['recent_orders']>[number]) => (
|
||||||
</Card>
|
<TableRow key={order.order_number}>
|
||||||
|
<TableCell>{order.date}</TableCell>
|
||||||
|
<TableCell>{order.order_number}</TableCell>
|
||||||
|
<TableCell>{order.customer}</TableCell>
|
||||||
|
<TableCell>{order.quantity}</TableCell>
|
||||||
|
<TableCell>${formatPrice(order.price)}</TableCell>
|
||||||
|
<TableCell>{order.status}</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
{(!combinedData?.recent_orders || combinedData.recent_orders.length === 0) && (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={6} className="text-center text-muted-foreground">
|
||||||
|
No recent orders
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</Card>
|
||||||
|
|
||||||
<Card className="p-4">
|
<Card className="p-4">
|
||||||
<h3 className="font-semibold mb-2">Monthly Sales Trend</h3>
|
<h3 className="font-semibold mb-2">Monthly Sales Trend</h3>
|
||||||
<div className="h-64">
|
<div className="h-64">
|
||||||
<ResponsiveContainer width="100%" height="100%">
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
<LineChart data={combinedData?.monthly_sales || []}>
|
<LineChart data={combinedData?.monthly_sales || []}>
|
||||||
<CartesianGrid strokeDasharray="3 3" />
|
<CartesianGrid strokeDasharray="3 3" />
|
||||||
<XAxis dataKey="month" />
|
<XAxis dataKey="month" />
|
||||||
<YAxis yAxisId="left" />
|
<YAxis yAxisId="left" />
|
||||||
<YAxis yAxisId="right" orientation="right" />
|
<YAxis yAxisId="right" orientation="right" />
|
||||||
<Tooltip />
|
<Tooltip />
|
||||||
<Line yAxisId="left" type="monotone" dataKey="quantity" stroke="#8884d8" name="Quantity" />
|
<Line yAxisId="left" type="monotone" dataKey="quantity" stroke="#8884d8" name="Quantity" />
|
||||||
<Line yAxisId="right" type="monotone" dataKey="revenue" stroke="#82ca9d" name="Revenue" />
|
<Line yAxisId="right" type="monotone" dataKey="revenue" stroke="#82ca9d" name="Revenue" />
|
||||||
</LineChart>
|
</LineChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="purchase" className="space-y-4">
|
<TabsContent value="purchase" className="space-y-4">
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<Skeleton className="h-96 w-full" />
|
<Skeleton className="h-96 w-full" />
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<Card className="p-4">
|
<Card className="p-4">
|
||||||
<h3 className="font-semibold mb-2">Recent Purchase Orders</h3>
|
<h3 className="font-semibold mb-2">Recent Purchase Orders</h3>
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
|
||||||
<TableHead>Date</TableHead>
|
|
||||||
<TableHead>PO #</TableHead>
|
|
||||||
<TableHead>Ordered</TableHead>
|
|
||||||
<TableHead>Received</TableHead>
|
|
||||||
<TableHead>Status</TableHead>
|
|
||||||
<TableHead>Lead Time</TableHead>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{combinedData?.recent_purchases?.map((po: NonNullable<Product['recent_purchases']>[number]) => (
|
|
||||||
<TableRow key={po.po_id}>
|
|
||||||
<TableCell>{po.date}</TableCell>
|
|
||||||
<TableCell>{po.po_id}</TableCell>
|
|
||||||
<TableCell>{po.ordered}</TableCell>
|
|
||||||
<TableCell>{po.received}</TableCell>
|
|
||||||
<TableCell>{po.status}</TableCell>
|
|
||||||
<TableCell>{po.lead_time_days ? `${po.lead_time_days} days` : 'N/A'}</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
{(!combinedData?.recent_purchases || combinedData.recent_purchases.length === 0) && (
|
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={6} className="text-center text-muted-foreground">
|
<TableHead>Date</TableHead>
|
||||||
No recent purchase orders
|
<TableHead>PO #</TableHead>
|
||||||
</TableCell>
|
<TableHead>Ordered</TableHead>
|
||||||
|
<TableHead>Received</TableHead>
|
||||||
|
<TableHead>Status</TableHead>
|
||||||
|
<TableHead>Lead Time</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)}
|
</TableHeader>
|
||||||
</TableBody>
|
<TableBody>
|
||||||
</Table>
|
{combinedData?.recent_purchases?.map((po: NonNullable<Product['recent_purchases']>[number]) => (
|
||||||
</Card>
|
<TableRow key={po.po_id}>
|
||||||
</div>
|
<TableCell>{po.date}</TableCell>
|
||||||
)}
|
<TableCell>{po.po_id}</TableCell>
|
||||||
</TabsContent>
|
<TableCell>{po.ordered}</TableCell>
|
||||||
|
<TableCell>{po.received}</TableCell>
|
||||||
|
<TableCell>{po.status}</TableCell>
|
||||||
|
<TableCell>{po.lead_time_days ? `${po.lead_time_days} days` : 'N/A'}</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
{(!combinedData?.recent_purchases || combinedData.recent_purchases.length === 0) && (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={6} className="text-center text-muted-foreground">
|
||||||
|
No recent purchase orders
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="financial" className="space-y-4">
|
<TabsContent value="financial" className="space-y-4">
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<Skeleton className="h-48 w-full" />
|
<Skeleton className="h-48 w-full" />
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<Card className="p-4">
|
<Card className="p-4">
|
||||||
<h3 className="font-semibold mb-2">Financial Overview</h3>
|
<h3 className="font-semibold mb-2">Financial Overview</h3>
|
||||||
<dl className="grid grid-cols-3 gap-4">
|
<dl className="grid grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<dt className="text-sm text-muted-foreground">Gross Profit</dt>
|
<dt className="text-sm text-muted-foreground">Gross Profit</dt>
|
||||||
<dd className="text-2xl font-semibold">${formatPrice(product?.metrics.gross_profit)}</dd>
|
<dd className="text-2xl font-semibold">${formatPrice(product?.metrics.gross_profit)}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt className="text-sm text-muted-foreground">GMROI</dt>
|
<dt className="text-sm text-muted-foreground">GMROI</dt>
|
||||||
<dd className="text-2xl font-semibold">{product?.metrics.gmroi.toFixed(2)}</dd>
|
<dd className="text-2xl font-semibold">{product?.metrics.gmroi.toFixed(2)}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt className="text-sm text-muted-foreground">Margin %</dt>
|
<dt className="text-sm text-muted-foreground">Margin %</dt>
|
||||||
<dd className="text-2xl font-semibold">{product?.metrics.avg_margin_percent.toFixed(2)}%</dd>
|
<dd className="text-2xl font-semibold">{product?.metrics.avg_margin_percent.toFixed(2)}%</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card className="p-4">
|
<Card className="p-4">
|
||||||
<h3 className="font-semibold mb-2">Cost Breakdown</h3>
|
<h3 className="font-semibold mb-2">Cost Breakdown</h3>
|
||||||
<dl className="grid grid-cols-2 gap-4">
|
<dl className="grid grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<dt className="text-sm text-muted-foreground">Cost of Goods Sold</dt>
|
<dt className="text-sm text-muted-foreground">Cost of Goods Sold</dt>
|
||||||
<dd>${formatPrice(product?.metrics.cost_of_goods_sold)}</dd>
|
<dd>${formatPrice(product?.metrics.cost_of_goods_sold)}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt className="text-sm text-muted-foreground">Landing Cost</dt>
|
<dt className="text-sm text-muted-foreground">Landing Cost</dt>
|
||||||
<dd>${formatPrice(product?.landing_cost_price)}</dd>
|
<dd>${formatPrice(product?.landing_cost_price)}</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card className="p-4">
|
<Card className="p-4">
|
||||||
<h3 className="font-semibold mb-2">Profit Margin Trend</h3>
|
<h3 className="font-semibold mb-2">Profit Margin Trend</h3>
|
||||||
<div className="h-64">
|
<div className="h-64">
|
||||||
<ResponsiveContainer width="100%" height="100%">
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
<LineChart data={combinedData?.monthly_sales || []}>
|
<LineChart data={combinedData?.monthly_sales || []}>
|
||||||
<CartesianGrid strokeDasharray="3 3" />
|
<CartesianGrid strokeDasharray="3 3" />
|
||||||
<XAxis dataKey="month" />
|
<XAxis dataKey="month" />
|
||||||
<YAxis domain={[0, 100]} />
|
<YAxis domain={[0, 100]} />
|
||||||
<Tooltip />
|
<Tooltip />
|
||||||
<Line type="monotone" dataKey="profit_margin" stroke="#82ca9d" name="Profit Margin %" />
|
<Line type="monotone" dataKey="profit_margin" stroke="#82ca9d" name="Profit Margin %" />
|
||||||
</LineChart>
|
</LineChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="vendor" className="space-y-4">
|
<TabsContent value="vendor" className="space-y-4">
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<Skeleton className="h-48 w-full" />
|
<Skeleton className="h-48 w-full" />
|
||||||
) : product?.vendor_performance ? (
|
) : product?.vendor_performance ? (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<Card className="p-4">
|
<Card className="p-4">
|
||||||
<h3 className="font-semibold mb-2">Vendor Performance</h3>
|
<h3 className="font-semibold mb-2">Vendor Performance</h3>
|
||||||
<dl className="grid grid-cols-3 gap-4">
|
<dl className="grid grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<dt className="text-sm text-muted-foreground">On-Time Delivery</dt>
|
<dt className="text-sm text-muted-foreground">On-Time Delivery</dt>
|
||||||
<dd className="text-2xl font-semibold">{product.vendor_performance.on_time_delivery_rate.toFixed(1)}%</dd>
|
<dd className="text-2xl font-semibold">{product.vendor_performance.on_time_delivery_rate.toFixed(1)}%</dd>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt className="text-sm text-muted-foreground">Order Fill Rate</dt>
|
<dt className="text-sm text-muted-foreground">Order Fill Rate</dt>
|
||||||
<dd className="text-2xl font-semibold">{product.vendor_performance.order_fill_rate.toFixed(1)}%</dd>
|
<dd className="text-2xl font-semibold">{product.vendor_performance.order_fill_rate.toFixed(1)}%</dd>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt className="text-sm text-muted-foreground">Avg Lead Time</dt>
|
<dt className="text-sm text-muted-foreground">Avg Lead Time</dt>
|
||||||
<dd className="text-2xl font-semibold">{product.vendor_performance.avg_lead_time_days} days</dd>
|
<dd className="text-2xl font-semibold">{product.vendor_performance.avg_lead_time_days} days</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card className="p-4">
|
<Card className="p-4">
|
||||||
<h3 className="font-semibold mb-2">Order History</h3>
|
<h3 className="font-semibold mb-2">Order History</h3>
|
||||||
<dl className="grid grid-cols-2 gap-4">
|
<dl className="grid grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<dt className="text-sm text-muted-foreground">Total Orders</dt>
|
<dt className="text-sm text-muted-foreground">Total Orders</dt>
|
||||||
<dd>{product.vendor_performance.total_orders}</dd>
|
<dd>{product.vendor_performance.total_orders}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt className="text-sm text-muted-foreground">Late Orders</dt>
|
<dt className="text-sm text-muted-foreground">Late Orders</dt>
|
||||||
<dd>{product.vendor_performance.total_late_orders}</dd>
|
<dd>{product.vendor_performance.total_late_orders}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt className="text-sm text-muted-foreground">Total Purchase Value</dt>
|
<dt className="text-sm text-muted-foreground">Total Purchase Value</dt>
|
||||||
<dd>${formatPrice(product.vendor_performance.total_purchase_value)}</dd>
|
<dd>${formatPrice(product.vendor_performance.total_purchase_value)}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt className="text-sm text-muted-foreground">Avg Order Value</dt>
|
<dt className="text-sm text-muted-foreground">Avg Order Value</dt>
|
||||||
<dd>${formatPrice(product.vendor_performance.avg_order_value)}</dd>
|
<dd>${formatPrice(product.vendor_performance.avg_order_value)}</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-center text-muted-foreground">No vendor performance data available</div>
|
<div className="text-center text-muted-foreground">No vendor performance data available</div>
|
||||||
)}
|
)}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
</DrawerContent>
|
</VaulDrawer.Content>
|
||||||
</Drawer>
|
</VaulDrawer.Portal>
|
||||||
|
</VaulDrawer.Root>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
{"root":["./src/app.tsx","./src/config.ts","./src/main.tsx","./src/vite-env.d.ts","./src/components/analytics/categoryperformance.tsx","./src/components/analytics/priceanalysis.tsx","./src/components/analytics/profitanalysis.tsx","./src/components/analytics/stockanalysis.tsx","./src/components/analytics/vendorperformance.tsx","./src/components/dashboard/inventorystats.tsx","./src/components/dashboard/overview.tsx","./src/components/dashboard/recentsales.tsx","./src/components/dashboard/salesbycategory.tsx","./src/components/dashboard/trendingproducts.tsx","./src/components/layout/appsidebar.tsx","./src/components/layout/mainlayout.tsx","./src/components/products/producteditdialog.tsx","./src/components/products/productfilters.tsx","./src/components/products/producttable.tsx","./src/components/products/producttableskeleton.tsx","./src/components/settings/calculationsettings.tsx","./src/components/settings/datamanagement.tsx","./src/components/settings/performancemetrics.tsx","./src/components/settings/stockmanagement.tsx","./src/components/ui/alert-dialog.tsx","./src/components/ui/alert.tsx","./src/components/ui/avatar.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/calendar.tsx","./src/components/ui/card.tsx","./src/components/ui/date-range-picker.tsx","./src/components/ui/dialog.tsx","./src/components/ui/drawer.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/pagination.tsx","./src/components/ui/popover.tsx","./src/components/ui/progress.tsx","./src/components/ui/select.tsx","./src/components/ui/separator.tsx","./src/components/ui/sheet.tsx","./src/components/ui/sidebar.tsx","./src/components/ui/skeleton.tsx","./src/components/ui/sonner.tsx","./src/components/ui/switch.tsx","./src/components/ui/table.tsx","./src/components/ui/tabs.tsx","./src/components/ui/tooltip.tsx","./src/hooks/use-mobile.tsx","./src/lib/utils.ts","./src/pages/analytics.tsx","./src/pages/dashboard.tsx","./src/pages/import.tsx","./src/pages/login.tsx","./src/pages/orders.tsx","./src/pages/products.tsx","./src/pages/purchaseorders.tsx","./src/pages/settings.tsx"],"version":"5.6.3"}
|
{"root":["./src/app.tsx","./src/config.ts","./src/main.tsx","./src/vite-env.d.ts","./src/components/analytics/categoryperformance.tsx","./src/components/analytics/priceanalysis.tsx","./src/components/analytics/profitanalysis.tsx","./src/components/analytics/stockanalysis.tsx","./src/components/analytics/vendorperformance.tsx","./src/components/dashboard/inventoryhealthsummary.tsx","./src/components/dashboard/inventorystats.tsx","./src/components/dashboard/keymetricscharts.tsx","./src/components/dashboard/lowstockalerts.tsx","./src/components/dashboard/overview.tsx","./src/components/dashboard/recentsales.tsx","./src/components/dashboard/salesbycategory.tsx","./src/components/dashboard/trendingproducts.tsx","./src/components/dashboard/vendorperformance.tsx","./src/components/layout/appsidebar.tsx","./src/components/layout/mainlayout.tsx","./src/components/products/productdetail.tsx","./src/components/products/producteditdialog.tsx","./src/components/products/productfilters.tsx","./src/components/products/producttable.tsx","./src/components/products/producttableskeleton.tsx","./src/components/settings/calculationsettings.tsx","./src/components/settings/configuration.tsx","./src/components/settings/datamanagement.tsx","./src/components/settings/performancemetrics.tsx","./src/components/settings/stockmanagement.tsx","./src/components/ui/alert-dialog.tsx","./src/components/ui/alert.tsx","./src/components/ui/avatar.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/calendar.tsx","./src/components/ui/card.tsx","./src/components/ui/command.tsx","./src/components/ui/date-range-picker.tsx","./src/components/ui/dialog.tsx","./src/components/ui/drawer.tsx","./src/components/ui/dropdown-menu.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/pagination.tsx","./src/components/ui/popover.tsx","./src/components/ui/progress.tsx","./src/components/ui/select.tsx","./src/components/ui/separator.tsx","./src/components/ui/sheet.tsx","./src/components/ui/sidebar.tsx","./src/components/ui/skeleton.tsx","./src/components/ui/sonner.tsx","./src/components/ui/switch.tsx","./src/components/ui/table.tsx","./src/components/ui/tabs.tsx","./src/components/ui/tooltip.tsx","./src/hooks/use-mobile.tsx","./src/lib/utils.ts","./src/pages/analytics.tsx","./src/pages/dashboard.tsx","./src/pages/import.tsx","./src/pages/login.tsx","./src/pages/orders.tsx","./src/pages/products.tsx","./src/pages/purchaseorders.tsx","./src/pages/settings.tsx"],"version":"5.6.3"}
|
||||||
183
inventory/vite.config.js
Normal file
183
inventory/vite.config.js
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
|
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||||
|
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||||
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
|
function step(op) {
|
||||||
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
|
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||||
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
|
switch (op[0]) {
|
||||||
|
case 0: case 1: t = op; break;
|
||||||
|
case 4: _.label++; return { value: op[1], done: false };
|
||||||
|
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||||
|
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||||
|
default:
|
||||||
|
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||||
|
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||||
|
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||||
|
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||||
|
if (t[2]) _.ops.pop();
|
||||||
|
_.trys.pop(); continue;
|
||||||
|
}
|
||||||
|
op = body.call(thisArg, _);
|
||||||
|
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||||
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
import path from "path";
|
||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import react from '@vitejs/plugin-react';
|
||||||
|
import { loadEnv } from "vite";
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig(function (_a) {
|
||||||
|
var mode = _a.mode;
|
||||||
|
var env = loadEnv(mode, process.cwd(), "");
|
||||||
|
var isDev = mode === 'development';
|
||||||
|
return {
|
||||||
|
plugins: [
|
||||||
|
react(),
|
||||||
|
{
|
||||||
|
name: 'copy-build',
|
||||||
|
closeBundle: function () { return __awaiter(void 0, void 0, void 0, function () {
|
||||||
|
var sourcePath, targetPath, error_1;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
if (!!isDev) return [3 /*break*/, 6];
|
||||||
|
sourcePath = path.resolve(__dirname, 'build');
|
||||||
|
targetPath = path.resolve(__dirname, '../inventory-server/frontend/build');
|
||||||
|
_a.label = 1;
|
||||||
|
case 1:
|
||||||
|
_a.trys.push([1, 5, , 6]);
|
||||||
|
return [4 /*yield*/, fs.ensureDir(path.dirname(targetPath))];
|
||||||
|
case 2:
|
||||||
|
_a.sent();
|
||||||
|
return [4 /*yield*/, fs.remove(targetPath)];
|
||||||
|
case 3:
|
||||||
|
_a.sent();
|
||||||
|
return [4 /*yield*/, fs.copy(sourcePath, targetPath)];
|
||||||
|
case 4:
|
||||||
|
_a.sent();
|
||||||
|
console.log('Build files copied successfully to server directory!');
|
||||||
|
return [3 /*break*/, 6];
|
||||||
|
case 5:
|
||||||
|
error_1 = _a.sent();
|
||||||
|
console.error('Error copying build files:', error_1);
|
||||||
|
process.exit(1);
|
||||||
|
return [3 /*break*/, 6];
|
||||||
|
case 6: return [2 /*return*/];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}); }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
define: {
|
||||||
|
'process.env.NODE_ENV': JSON.stringify(mode)
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": path.resolve(__dirname, "./src"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
host: "0.0.0.0",
|
||||||
|
port: 5173,
|
||||||
|
proxy: {
|
||||||
|
"/api": {
|
||||||
|
target: "https://inventory.kent.pw",
|
||||||
|
changeOrigin: true,
|
||||||
|
secure: false,
|
||||||
|
ws: true,
|
||||||
|
xfwd: true,
|
||||||
|
cookieDomainRewrite: "",
|
||||||
|
withCredentials: true,
|
||||||
|
rewrite: function (path) { return path.replace(/^\/api/, "/api"); },
|
||||||
|
configure: function (proxy, _options) {
|
||||||
|
proxy.on("error", function (err, req, res) {
|
||||||
|
console.log("API proxy error:", err);
|
||||||
|
res.writeHead(500, {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
});
|
||||||
|
res.end(JSON.stringify({ error: "Proxy Error", message: err.message }));
|
||||||
|
});
|
||||||
|
proxy.on("proxyReq", function (proxyReq, req, _res) {
|
||||||
|
console.log("Outgoing request to API:", {
|
||||||
|
method: req.method,
|
||||||
|
url: req.url,
|
||||||
|
headers: proxyReq.getHeaders(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
proxy.on("proxyRes", function (proxyRes, req, _res) {
|
||||||
|
console.log("API Proxy response:", {
|
||||||
|
statusCode: proxyRes.statusCode,
|
||||||
|
url: req.url,
|
||||||
|
headers: proxyRes.headers,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"/auth-inv": {
|
||||||
|
target: "https://inventory.kent.pw",
|
||||||
|
changeOrigin: true,
|
||||||
|
secure: false,
|
||||||
|
ws: true,
|
||||||
|
xfwd: true,
|
||||||
|
cookieDomainRewrite: {
|
||||||
|
"inventory.kent.pw": "localhost"
|
||||||
|
},
|
||||||
|
withCredentials: true,
|
||||||
|
onProxyReq: function (proxyReq, req) {
|
||||||
|
// Add origin header to match CORS policy
|
||||||
|
proxyReq.setHeader('Origin', 'http://localhost:5173');
|
||||||
|
},
|
||||||
|
rewrite: function (path) { return path.replace(/^\/auth-inv/, "/auth-inv"); },
|
||||||
|
configure: function (proxy, _options) {
|
||||||
|
proxy.on("error", function (err, req, res) {
|
||||||
|
console.log("Auth proxy error:", err);
|
||||||
|
res.writeHead(500, {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
});
|
||||||
|
res.end(JSON.stringify({ error: "Proxy Error", message: err.message }));
|
||||||
|
});
|
||||||
|
proxy.on("proxyReq", function (proxyReq, req, _res) {
|
||||||
|
console.log("Outgoing request to Auth:", {
|
||||||
|
method: req.method,
|
||||||
|
url: req.url,
|
||||||
|
headers: proxyReq.getHeaders(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
proxy.on("proxyRes", function (proxyRes, req, _res) {
|
||||||
|
console.log("Auth Proxy response:", {
|
||||||
|
statusCode: proxyRes.statusCode,
|
||||||
|
url: req.url,
|
||||||
|
headers: proxyRes.headers,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
outDir: "build",
|
||||||
|
sourcemap: true,
|
||||||
|
rollupOptions: {
|
||||||
|
output: {
|
||||||
|
manualChunks: {
|
||||||
|
vendor: ["react", "react-dom", "react-router-dom"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user