Tweak layout on settings page

This commit is contained in:
2025-01-10 14:34:11 -05:00
parent 619f4058c6
commit 0a7b05f52c
2 changed files with 186 additions and 101 deletions

View 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 }

View File

@@ -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>