Tweak layout on settings page
This commit is contained in:
59
inventory/src/components/ui/alert.tsx
Normal file
59
inventory/src/components/ui/alert.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const alertVariants = cva(
|
||||||
|
"relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: "bg-background text-foreground",
|
||||||
|
destructive:
|
||||||
|
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const Alert = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
|
||||||
|
>(({ className, variant, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
role="alert"
|
||||||
|
className={cn(alertVariants({ variant }), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
Alert.displayName = "Alert"
|
||||||
|
|
||||||
|
const AlertTitle = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLHeadingElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<h5
|
||||||
|
ref={ref}
|
||||||
|
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
AlertTitle.displayName = "AlertTitle"
|
||||||
|
|
||||||
|
const AlertDescription = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLParagraphElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-sm [&_p]:leading-relaxed", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
AlertDescription.displayName = "AlertDescription"
|
||||||
|
|
||||||
|
export { Alert, AlertTitle, AlertDescription }
|
||||||
@@ -4,7 +4,9 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
|
|||||||
import { Progress } from "@/components/ui/progress";
|
import { Progress } from "@/components/ui/progress";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||||
import { Loader2, RefreshCw, Upload, X } from "lucide-react";
|
import { Loader2, RefreshCw, Upload, X } from "lucide-react";
|
||||||
|
import { Separator } from "@/components/ui/separator";
|
||||||
import config from '../config';
|
import config from '../config';
|
||||||
|
|
||||||
interface ImportProgress {
|
interface ImportProgress {
|
||||||
@@ -350,9 +352,11 @@ export function Settings() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{progress.error && (
|
{progress.error && (
|
||||||
<div className="text-sm text-red-500">
|
<Alert variant="destructive" className="mt-2">
|
||||||
Error: {progress.error}
|
<AlertDescription>
|
||||||
</div>
|
{progress.error}
|
||||||
|
</AlertDescription>
|
||||||
|
</Alert>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -364,52 +368,14 @@ export function Settings() {
|
|||||||
<h1 className="text-2xl font-bold">Settings</h1>
|
<h1 className="text-2xl font-bold">Settings</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
<div className="max-w-[400px] space-y-4">
|
||||||
|
{/* Update CSV Card */}
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>Data Management</CardTitle>
|
<CardTitle>Update CSV Files</CardTitle>
|
||||||
<CardDescription>Update and import CSV data files</CardDescription>
|
<CardDescription>Download the latest CSV data files</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent>
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="products-limit">Products Import Limit (0 for no limit)</Label>
|
|
||||||
<Input
|
|
||||||
id="products-limit"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
value={limits.products}
|
|
||||||
onChange={(e) => setLimits(prev => ({ ...prev, products: parseInt(e.target.value) || 0 }))}
|
|
||||||
disabled={isUpdating || isImporting}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="orders-limit">Orders Import Limit (0 for no limit)</Label>
|
|
||||||
<Input
|
|
||||||
id="orders-limit"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
value={limits.orders}
|
|
||||||
onChange={(e) => setLimits(prev => ({ ...prev, orders: parseInt(e.target.value) || 0 }))}
|
|
||||||
disabled={isUpdating || isImporting}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="purchase-orders-limit">Purchase Orders Import Limit (0 for no limit)</Label>
|
|
||||||
<Input
|
|
||||||
id="purchase-orders-limit"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
value={limits.purchaseOrders}
|
|
||||||
onChange={(e) => setLimits(prev => ({ ...prev, purchaseOrders: parseInt(e.target.value) || 0 }))}
|
|
||||||
disabled={isUpdating || isImporting}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button
|
<Button
|
||||||
className="flex-1"
|
className="flex-1"
|
||||||
@@ -439,6 +405,59 @@ export function Settings() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{isUpdating && renderProgress()}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Import Data Card */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Import Data</CardTitle>
|
||||||
|
<CardDescription>Import current CSV files into database. Set limits to 0 to import all records.</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-6">
|
||||||
|
<div className="grid grid-cols-3 gap-4">
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="products-limit" className="text-xs">Products Limit</Label>
|
||||||
|
<Input
|
||||||
|
id="products-limit"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
value={limits.products}
|
||||||
|
onChange={(e) => setLimits(prev => ({ ...prev, products: parseInt(e.target.value) || 0 }))}
|
||||||
|
disabled={isUpdating || isImporting}
|
||||||
|
className="[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
|
||||||
|
placeholder="0 for no limit"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="orders-limit" className="text-xs">Orders Limit</Label>
|
||||||
|
<Input
|
||||||
|
id="orders-limit"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
value={limits.orders}
|
||||||
|
onChange={(e) => setLimits(prev => ({ ...prev, orders: parseInt(e.target.value) || 0 }))}
|
||||||
|
disabled={isUpdating || isImporting}
|
||||||
|
className="[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
|
||||||
|
placeholder="0 for no limit"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="purchase-orders-limit" className="text-xs">PO Limit</Label>
|
||||||
|
<Input
|
||||||
|
id="purchase-orders-limit"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
value={limits.purchaseOrders}
|
||||||
|
onChange={(e) => setLimits(prev => ({ ...prev, purchaseOrders: parseInt(e.target.value) || 0 }))}
|
||||||
|
disabled={isUpdating || isImporting}
|
||||||
|
className="[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
|
||||||
|
placeholder="0 for no limit"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button
|
<Button
|
||||||
className="flex-1"
|
className="flex-1"
|
||||||
@@ -448,7 +467,7 @@ export function Settings() {
|
|||||||
{isImporting ? (
|
{isImporting ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||||
Importing Data...
|
Importing...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@@ -468,8 +487,15 @@ export function Settings() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{(isUpdating || isImporting || progress) && renderProgress()}
|
{isImporting && renderProgress()}
|
||||||
</div>
|
|
||||||
|
|
||||||
|
{/* Show progress outside cards if neither operation is running but we have progress state */}
|
||||||
|
{!isUpdating && !isImporting && progress && (
|
||||||
|
<>
|
||||||
|
{renderProgress()}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user