Spaces:
Sleeping
Sleeping
| import { useState } from "react"; | |
| import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; | |
| import { Button } from "@/components/ui/button"; | |
| import { Input } from "@/components/ui/input"; | |
| import { Label } from "@/components/ui/label"; | |
| import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; | |
| import { Separator } from "@/components/ui/separator"; | |
| import { ArrowLeft, Save, Printer } from "lucide-react"; | |
| import { Link } from "react-router-dom"; | |
| import { useToast } from "@/hooks/use-toast"; | |
| const Invoice = () => { | |
| const { toast } = useToast(); | |
| const [formData, setFormData] = useState({ | |
| partyName: "", | |
| date: new Date().toISOString().split('T')[0], | |
| serialNumber: "", | |
| particular: "", | |
| lotNumber: "", | |
| bags: "", | |
| grossWeight: "40", | |
| rate: "", | |
| }); | |
| const [charges, setCharges] = useState({ | |
| bardhana: true, | |
| hamali: false, | |
| adhath: 0, | |
| cess: 2, | |
| gaadiBharni: "", | |
| }); | |
| const calculateNetWeight = () => { | |
| const bags = parseFloat(formData.bags) || 0; | |
| const grossPerBag = parseFloat(formData.grossWeight) || 40; | |
| return bags * (grossPerBag - 1); | |
| }; | |
| const calculateBasicAmount = () => { | |
| const netWeight = calculateNetWeight(); | |
| const rate = parseFloat(formData.rate) || 0; | |
| return netWeight * rate; | |
| }; | |
| const calculateCharges = () => { | |
| const bags = parseFloat(formData.bags) || 0; | |
| const basicAmount = calculateBasicAmount(); | |
| let total = basicAmount; | |
| const breakdown = { | |
| bardhana: charges.bardhana ? bags * 18 : 0, | |
| hamali: charges.hamali ? bags * 6 : 0, | |
| adhath: (charges.adhath / 100) * basicAmount, | |
| cess: (charges.cess / 100) * basicAmount, | |
| gaadiBharni: parseFloat(charges.gaadiBharni) || 0, | |
| }; | |
| total += breakdown.bardhana + breakdown.hamali + breakdown.adhath + breakdown.cess + breakdown.gaadiBharni; | |
| return { breakdown, total }; | |
| }; | |
| const handleSave = () => { | |
| toast({ | |
| title: "Invoice Saved", | |
| description: "Invoice has been saved successfully.", | |
| }); | |
| }; | |
| const handlePrint = () => { | |
| window.print(); | |
| }; | |
| const { breakdown, total } = calculateCharges(); | |
| return ( | |
| <div className="min-h-screen bg-background"> | |
| {/* Header */} | |
| <header className="border-b bg-card print:hidden"> | |
| <div className="container mx-auto px-4 py-4"> | |
| <div className="flex items-center justify-between"> | |
| <div className="flex items-center gap-4"> | |
| <Link to="/"> | |
| <Button variant="ghost" size="icon"> | |
| <ArrowLeft className="h-5 w-5" /> | |
| </Button> | |
| </Link> | |
| <div> | |
| <h1 className="text-2xl font-bold text-foreground">New Invoice</h1> | |
| <p className="text-sm text-muted-foreground">Create sales invoice</p> | |
| </div> | |
| </div> | |
| <div className="flex gap-2"> | |
| <Button variant="outline" onClick={handlePrint}> | |
| <Printer className="h-4 w-4 mr-2" /> | |
| </Button> | |
| <Button onClick={handleSave}> | |
| <Save className="h-4 w-4 mr-2" /> | |
| Save | |
| </Button> | |
| </div> | |
| </div> | |
| </div> | |
| </header> | |
| {/* Main Content */} | |
| <main className="container mx-auto px-4 py-8"> | |
| <div className="grid gap-6 lg:grid-cols-3"> | |
| {/* Invoice Form */} | |
| <div className="lg:col-span-2"> | |
| <Card> | |
| <CardHeader> | |
| <CardTitle>Invoice Details</CardTitle> | |
| </CardHeader> | |
| <CardContent className="space-y-6"> | |
| {/* Basic Info */} | |
| <div className="grid gap-4 md:grid-cols-3"> | |
| <div className="space-y-2"> | |
| <Label htmlFor="serialNumber">Serial Number</Label> | |
| <Input | |
| id="serialNumber" | |
| value={formData.serialNumber} | |
| onChange={(e) => setFormData({ ...formData, serialNumber: e.target.value })} | |
| placeholder="INV-001" | |
| /> | |
| </div> | |
| <div className="space-y-2"> | |
| <Label htmlFor="date">Date</Label> | |
| <Input | |
| id="date" | |
| type="date" | |
| value={formData.date} | |
| onChange={(e) => setFormData({ ...formData, date: e.target.value })} | |
| /> | |
| </div> | |
| <div className="space-y-2"> | |
| <Label htmlFor="lotNumber">Lot Number</Label> | |
| <Input | |
| id="lotNumber" | |
| value={formData.lotNumber} | |
| onChange={(e) => setFormData({ ...formData, lotNumber: e.target.value })} | |
| placeholder="LOT-123" | |
| /> | |
| </div> | |
| </div> | |
| <div className="space-y-2"> | |
| <Label htmlFor="partyName">Party Name</Label> | |
| <Input | |
| id="partyName" | |
| value={formData.partyName} | |
| onChange={(e) => setFormData({ ...formData, partyName: e.target.value })} | |
| placeholder="Enter party name" | |
| /> | |
| </div> | |
| <Separator /> | |
| {/* Product Details */} | |
| <div className="space-y-4"> | |
| <h3 className="font-semibold">Product Details</h3> | |
| <div className="grid gap-4 md:grid-cols-2"> | |
| <div className="space-y-2"> | |
| <Label htmlFor="particular">Variety</Label> | |
| <Select | |
| value={formData.particular} | |
| onValueChange={(value) => setFormData({ ...formData, particular: value })} | |
| > | |
| <SelectTrigger id="particular"> | |
| <SelectValue placeholder="Select variety" /> | |
| </SelectTrigger> | |
| <SelectContent> | |
| <SelectItem value="teja">Teja</SelectItem> | |
| <SelectItem value="garuda">Garuda</SelectItem> | |
| <SelectItem value="341">341</SelectItem> | |
| <SelectItem value="other">Other</SelectItem> | |
| </SelectContent> | |
| </Select> | |
| </div> | |
| <div className="space-y-2"> | |
| <Label htmlFor="bags">Number of Bags</Label> | |
| <Input | |
| id="bags" | |
| type="number" | |
| value={formData.bags} | |
| onChange={(e) => setFormData({ ...formData, bags: e.target.value })} | |
| placeholder="10" | |
| /> | |
| </div> | |
| </div> | |
| <div className="grid gap-4 md:grid-cols-3"> | |
| <div className="space-y-2"> | |
| <Label htmlFor="grossWeight">Gross Weight (kg/bag)</Label> | |
| <Input | |
| id="grossWeight" | |
| type="number" | |
| value={formData.grossWeight} | |
| onChange={(e) => setFormData({ ...formData, grossWeight: e.target.value })} | |
| placeholder="40" | |
| /> | |
| </div> | |
| <div className="space-y-2"> | |
| <Label>Net Weight (kg/bag)</Label> | |
| <Input | |
| value={(parseFloat(formData.grossWeight) || 40) - 1} | |
| disabled | |
| className="bg-muted" | |
| /> | |
| </div> | |
| <div className="space-y-2"> | |
| <Label htmlFor="rate">Rate (₹/kg)</Label> | |
| <Input | |
| id="rate" | |
| type="number" | |
| value={formData.rate} | |
| onChange={(e) => setFormData({ ...formData, rate: e.target.value })} | |
| placeholder="200" | |
| /> | |
| </div> | |
| </div> | |
| </div> | |
| <Separator /> | |
| {/* Additional Charges */} | |
| <div className="space-y-4"> | |
| <h3 className="font-semibold">Additional Charges</h3> | |
| <div className="space-y-3"> | |
| <div className="flex items-center justify-between"> | |
| <div className="flex items-center gap-2"> | |
| <input | |
| type="checkbox" | |
| id="bardhana" | |
| checked={charges.bardhana} | |
| onChange={(e) => setCharges({ ...charges, bardhana: e.target.checked })} | |
| className="h-4 w-4" | |
| /> | |
| <Label htmlFor="bardhana">Bardhana (₹18/bag)</Label> | |
| </div> | |
| <span className="text-sm text-muted-foreground">₹{breakdown.bardhana.toFixed(2)}</span> | |
| </div> | |
| <div className="flex items-center justify-between"> | |
| <div className="flex items-center gap-2"> | |
| <input | |
| type="checkbox" | |
| id="hamali" | |
| checked={charges.hamali} | |
| onChange={(e) => setCharges({ ...charges, hamali: e.target.checked })} | |
| className="h-4 w-4" | |
| /> | |
| <Label htmlFor="hamali">Hamali (₹6/bag)</Label> | |
| </div> | |
| <span className="text-sm text-muted-foreground">₹{breakdown.hamali.toFixed(2)}</span> | |
| </div> | |
| <div className="flex items-center justify-between gap-4"> | |
| <Label htmlFor="adhath">Adhath (%)</Label> | |
| <div className="flex items-center gap-2"> | |
| <Input | |
| id="adhath" | |
| type="number" | |
| value={charges.adhath} | |
| onChange={(e) => setCharges({ ...charges, adhath: parseFloat(e.target.value) || 0 })} | |
| className="w-20" | |
| min="0" | |
| max="6" | |
| /> | |
| <span className="text-sm text-muted-foreground">₹{breakdown.adhath.toFixed(2)}</span> | |
| </div> | |
| </div> | |
| <div className="flex items-center justify-between gap-4"> | |
| <Label htmlFor="cess">Cess (%)</Label> | |
| <div className="flex items-center gap-2"> | |
| <Input | |
| id="cess" | |
| type="number" | |
| value={charges.cess} | |
| onChange={(e) => setCharges({ ...charges, cess: parseFloat(e.target.value) || 0 })} | |
| className="w-20" | |
| min="0" | |
| max="6" | |
| /> | |
| <span className="text-sm text-muted-foreground">₹{breakdown.cess.toFixed(2)}</span> | |
| </div> | |
| </div> | |
| <div className="flex items-center justify-between gap-4"> | |
| <Label htmlFor="gaadiBharni">Gaadi Bharni</Label> | |
| <div className="flex items-center gap-2"> | |
| <Input | |
| id="gaadiBharni" | |
| type="number" | |
| value={charges.gaadiBharni} | |
| onChange={(e) => setCharges({ ...charges, gaadiBharni: e.target.value })} | |
| className="w-20" | |
| placeholder="0" | |
| /> | |
| <span className="text-sm text-muted-foreground">₹{breakdown.gaadiBharni.toFixed(2)}</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| </div> | |
| {/* Invoice Summary */} | |
| <div className="lg:col-span-1"> | |
| <Card className="sticky top-4"> | |
| <CardHeader> | |
| <CardTitle>Summary</CardTitle> | |
| </CardHeader> | |
| <CardContent className="space-y-4"> | |
| <div className="space-y-2 text-sm"> | |
| <div className="flex justify-between"> | |
| <span className="text-muted-foreground">Bags:</span> | |
| <span className="font-medium">{formData.bags || 0}</span> | |
| </div> | |
| <div className="flex justify-between"> | |
| <span className="text-muted-foreground">Gross Weight:</span> | |
| <span className="font-medium"> | |
| {(parseFloat(formData.bags) || 0) * (parseFloat(formData.grossWeight) || 40)} kg | |
| </span> | |
| </div> | |
| <div className="flex justify-between"> | |
| <span className="text-muted-foreground">Net Weight:</span> | |
| <span className="font-medium">{calculateNetWeight()} kg</span> | |
| </div> | |
| <Separator /> | |
| <div className="flex justify-between text-base"> | |
| <span className="font-medium">Basic Amount:</span> | |
| <span className="font-bold">₹{calculateBasicAmount().toFixed(2)}</span> | |
| </div> | |
| <Separator /> | |
| <div className="flex justify-between text-lg font-bold text-primary"> | |
| <span>Total Amount:</span> | |
| <span>₹{total.toFixed(2)}</span> | |
| </div> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| </div> | |
| </div> | |
| </main> | |
| </div> | |
| ); | |
| }; | |
| export default Invoice; | |