new temporal folder to test
This commit is contained in:
407
frontend-web-free/src/components/vendors/EditVendorModal.jsx
vendored
Normal file
407
frontend-web-free/src/components/vendors/EditVendorModal.jsx
vendored
Normal file
@@ -0,0 +1,407 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { base44 } from "@/api/base44Client";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Building2, Save, Loader2, X } from "lucide-react";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
|
||||
export default function EditVendorModal({ vendor, open, onClose }) {
|
||||
const queryClient = useQueryClient();
|
||||
const { toast } = useToast();
|
||||
|
||||
const [vendorData, setVendorData] = useState({
|
||||
vendor_number: "",
|
||||
legal_name: "",
|
||||
doing_business_as: "",
|
||||
tax_id: "",
|
||||
business_type: "LLC",
|
||||
primary_contact_name: "",
|
||||
primary_contact_email: "",
|
||||
primary_contact_phone: "",
|
||||
billing_address: "",
|
||||
service_address: "",
|
||||
coverage_regions: [],
|
||||
eligible_roles: [],
|
||||
insurance_expiry: "",
|
||||
approval_status: "approved",
|
||||
is_active: true,
|
||||
notes: ""
|
||||
});
|
||||
|
||||
const [regionInput, setRegionInput] = useState("");
|
||||
const [roleInput, setRoleInput] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
if (vendor) {
|
||||
setVendorData({
|
||||
vendor_number: vendor.vendorNumber || vendor.vendor_number || "",
|
||||
legal_name: vendor.name || vendor.legal_name || "",
|
||||
doing_business_as: vendor.doing_business_as || "",
|
||||
tax_id: vendor.tax_id || "",
|
||||
business_type: vendor.business_type || "LLC",
|
||||
primary_contact_name: vendor.primary_contact_name || "",
|
||||
primary_contact_email: vendor.primary_contact_email || `contact@${vendor.name?.toLowerCase().replace(/\s+/g, '').replace(/[()&]/g, '')}.com`,
|
||||
primary_contact_phone: vendor.primary_contact_phone || "(555) 123-4567",
|
||||
billing_address: vendor.billing_address || "",
|
||||
service_address: vendor.service_address || "",
|
||||
coverage_regions: vendor.coverage_regions || [vendor.region] || [],
|
||||
eligible_roles: vendor.eligible_roles || [vendor.specialty] || [],
|
||||
insurance_expiry: vendor.insurance_expiry || "",
|
||||
approval_status: vendor.approval_status || "approved",
|
||||
is_active: vendor.is_active !== undefined ? vendor.is_active : true,
|
||||
notes: vendor.notes || ""
|
||||
});
|
||||
}
|
||||
}, [vendor]);
|
||||
|
||||
const updateVendorMutation = useMutation({
|
||||
mutationFn: (data) => {
|
||||
// For now, just show success toast since we're using mock data
|
||||
return Promise.resolve(data);
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['vendors'] });
|
||||
toast({
|
||||
title: "Vendor Updated",
|
||||
description: "Vendor information has been successfully updated",
|
||||
});
|
||||
onClose();
|
||||
},
|
||||
});
|
||||
|
||||
const handleAddRegion = () => {
|
||||
if (regionInput.trim() && !vendorData.coverage_regions.includes(regionInput.trim())) {
|
||||
setVendorData({
|
||||
...vendorData,
|
||||
coverage_regions: [...vendorData.coverage_regions, regionInput.trim()]
|
||||
});
|
||||
setRegionInput("");
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveRegion = (index) => {
|
||||
setVendorData({
|
||||
...vendorData,
|
||||
coverage_regions: vendorData.coverage_regions.filter((_, i) => i !== index)
|
||||
});
|
||||
};
|
||||
|
||||
const handleAddRole = () => {
|
||||
if (roleInput.trim() && !vendorData.eligible_roles.includes(roleInput.trim())) {
|
||||
setVendorData({
|
||||
...vendorData,
|
||||
eligible_roles: [...vendorData.eligible_roles, roleInput.trim()]
|
||||
});
|
||||
setRoleInput("");
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveRole = (index) => {
|
||||
setVendorData({
|
||||
...vendorData,
|
||||
eligible_roles: vendorData.eligible_roles.filter((_, i) => i !== index)
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
updateVendorMutation.mutate(vendorData);
|
||||
};
|
||||
|
||||
if (!vendor) return null;
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onClose}>
|
||||
<DialogContent className="max-w-4xl max-h-[90vh] overflow-hidden p-0">
|
||||
<DialogHeader className="px-6 pt-6 pb-4 border-b border-slate-200">
|
||||
<DialogTitle className="flex items-center gap-2 text-2xl">
|
||||
<Building2 className="w-6 h-6 text-[#0A39DF]" />
|
||||
Edit Vendor: {vendor.name}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="overflow-y-auto max-h-[calc(90vh-180px)] px-6 pb-6">
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
{/* Basic Information */}
|
||||
<Card className="border-slate-200 shadow-sm">
|
||||
<CardHeader className="bg-gradient-to-br from-slate-50 to-white border-b">
|
||||
<CardTitle className="text-base">Basic Information</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="p-4 space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label htmlFor="vendor_number" className="text-sm">Vendor Number</Label>
|
||||
<Input
|
||||
id="vendor_number"
|
||||
value={vendorData.vendor_number}
|
||||
disabled
|
||||
className="bg-slate-100 text-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="approval_status" className="text-sm">Status</Label>
|
||||
<Select
|
||||
value={vendorData.approval_status}
|
||||
onValueChange={(value) => setVendorData({...vendorData, approval_status: value})}
|
||||
>
|
||||
<SelectTrigger className="text-sm">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="pending">Pending</SelectItem>
|
||||
<SelectItem value="approved">Approved</SelectItem>
|
||||
<SelectItem value="suspended">Suspended</SelectItem>
|
||||
<SelectItem value="terminated">Terminated</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="md:col-span-2">
|
||||
<Label htmlFor="legal_name" className="text-sm">Legal Business Name *</Label>
|
||||
<Input
|
||||
id="legal_name"
|
||||
placeholder="e.g., ABC Staffing LLC"
|
||||
value={vendorData.legal_name}
|
||||
onChange={(e) => setVendorData({...vendorData, legal_name: e.target.value})}
|
||||
required
|
||||
className="text-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="md:col-span-2">
|
||||
<Label htmlFor="doing_business_as" className="text-sm">Doing Business As (DBA)</Label>
|
||||
<Input
|
||||
id="doing_business_as"
|
||||
placeholder="e.g., ABC Staff"
|
||||
value={vendorData.doing_business_as}
|
||||
onChange={(e) => setVendorData({...vendorData, doing_business_as: e.target.value})}
|
||||
className="text-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="tax_id" className="text-sm">Federal Tax ID / EIN</Label>
|
||||
<Input
|
||||
id="tax_id"
|
||||
placeholder="12-3456789"
|
||||
value={vendorData.tax_id}
|
||||
onChange={(e) => setVendorData({...vendorData, tax_id: e.target.value})}
|
||||
className="text-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="business_type" className="text-sm">Business Type</Label>
|
||||
<Select
|
||||
value={vendorData.business_type}
|
||||
onValueChange={(value) => setVendorData({...vendorData, business_type: value})}
|
||||
>
|
||||
<SelectTrigger className="text-sm">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="Corporation">Corporation</SelectItem>
|
||||
<SelectItem value="LLC">LLC</SelectItem>
|
||||
<SelectItem value="Partnership">Partnership</SelectItem>
|
||||
<SelectItem value="Sole Proprietorship">Sole Proprietorship</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Contact Information */}
|
||||
<Card className="border-slate-200 shadow-sm">
|
||||
<CardHeader className="bg-gradient-to-br from-slate-50 to-white border-b">
|
||||
<CardTitle className="text-base">Primary Contact</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="p-4 space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="md:col-span-2">
|
||||
<Label htmlFor="primary_contact_name" className="text-sm">Contact Name</Label>
|
||||
<Input
|
||||
id="primary_contact_name"
|
||||
placeholder="John Doe"
|
||||
value={vendorData.primary_contact_name}
|
||||
onChange={(e) => setVendorData({...vendorData, primary_contact_name: e.target.value})}
|
||||
className="text-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="primary_contact_email" className="text-sm">Email *</Label>
|
||||
<Input
|
||||
id="primary_contact_email"
|
||||
type="email"
|
||||
placeholder="john@vendor.com"
|
||||
value={vendorData.primary_contact_email}
|
||||
onChange={(e) => setVendorData({...vendorData, primary_contact_email: e.target.value})}
|
||||
required
|
||||
className="text-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="primary_contact_phone" className="text-sm">Phone</Label>
|
||||
<Input
|
||||
id="primary_contact_phone"
|
||||
type="tel"
|
||||
placeholder="(555) 123-4567"
|
||||
value={vendorData.primary_contact_phone}
|
||||
onChange={(e) => setVendorData({...vendorData, primary_contact_phone: e.target.value})}
|
||||
className="text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Coverage & Services */}
|
||||
<Card className="border-slate-200 shadow-sm">
|
||||
<CardHeader className="bg-gradient-to-br from-slate-50 to-white border-b">
|
||||
<CardTitle className="text-base">Coverage & Services</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="p-4 space-y-4">
|
||||
<div>
|
||||
<Label className="text-sm">Coverage Regions</Label>
|
||||
<div className="flex gap-2 mb-2">
|
||||
<Input
|
||||
placeholder="Add region (e.g., San Francisco Bay Area)"
|
||||
value={regionInput}
|
||||
onChange={(e) => setRegionInput(e.target.value)}
|
||||
onKeyPress={(e) => e.key === 'Enter' && (e.preventDefault(), handleAddRegion())}
|
||||
className="text-sm"
|
||||
/>
|
||||
<Button type="button" onClick={handleAddRegion} variant="outline" size="sm">
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{vendorData.coverage_regions.map((region, i) => (
|
||||
<span key={i} className="bg-blue-100 text-blue-700 px-2 py-1 rounded-full text-xs flex items-center gap-1">
|
||||
{region}
|
||||
<button type="button" onClick={() => handleRemoveRegion(i)} className="text-blue-900 hover:text-blue-700">×</button>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label className="text-sm">Eligible Roles</Label>
|
||||
<div className="flex gap-2 mb-2">
|
||||
<Input
|
||||
placeholder="Add role (e.g., Server, Bartender)"
|
||||
value={roleInput}
|
||||
onChange={(e) => setRoleInput(e.target.value)}
|
||||
onKeyPress={(e) => e.key === 'Enter' && (e.preventDefault(), handleAddRole())}
|
||||
className="text-sm"
|
||||
/>
|
||||
<Button type="button" onClick={handleAddRole} variant="outline" size="sm">
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{vendorData.eligible_roles.map((role, i) => (
|
||||
<span key={i} className="bg-purple-100 text-purple-700 px-2 py-1 rounded-full text-xs flex items-center gap-1">
|
||||
{role}
|
||||
<button type="button" onClick={() => handleRemoveRole(i)} className="text-purple-900 hover:text-purple-700">×</button>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
<div>
|
||||
<Label htmlFor="billing_address" className="text-sm">Billing Address</Label>
|
||||
<Textarea
|
||||
id="billing_address"
|
||||
rows={2}
|
||||
placeholder="123 Main St, San Francisco, CA 94102"
|
||||
value={vendorData.billing_address}
|
||||
onChange={(e) => setVendorData({...vendorData, billing_address: e.target.value})}
|
||||
className="text-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="service_address" className="text-sm">Service Address</Label>
|
||||
<Textarea
|
||||
id="service_address"
|
||||
rows={2}
|
||||
placeholder="456 Service St, San Francisco, CA 94103"
|
||||
value={vendorData.service_address}
|
||||
onChange={(e) => setVendorData({...vendorData, service_address: e.target.value})}
|
||||
className="text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Notes */}
|
||||
<Card className="border-slate-200 shadow-sm">
|
||||
<CardHeader className="bg-gradient-to-br from-slate-50 to-white border-b">
|
||||
<CardTitle className="text-base">Additional Information</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="p-4">
|
||||
<div>
|
||||
<Label htmlFor="notes" className="text-sm">Internal Notes</Label>
|
||||
<Textarea
|
||||
id="notes"
|
||||
rows={3}
|
||||
placeholder="Add any internal notes about this vendor..."
|
||||
value={vendorData.notes}
|
||||
onChange={(e) => setVendorData({...vendorData, notes: e.target.value})}
|
||||
className="text-sm"
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="flex justify-end gap-3 px-6 py-4 border-t border-slate-200 bg-slate-50">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
onClick={handleSubmit}
|
||||
disabled={updateVendorMutation.isPending}
|
||||
className="bg-[#0A39DF] hover:bg-[#0A39DF]/90"
|
||||
>
|
||||
{updateVendorMutation.isPending ? (
|
||||
<>
|
||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
||||
Updating...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Save className="w-4 h-4 mr-2" />
|
||||
Update Vendor
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user