|
| 1 | +import React, { useEffect, useState } from "react"; |
| 2 | +import { Loader2, CheckCircle } from "lucide-react"; |
| 3 | +import { supabase } from "@/integrations/supabase/client"; |
| 4 | + |
| 5 | +// Type for your table |
| 6 | +type SupplierProduct = { |
| 7 | + id: number; |
| 8 | + productName: string | null; |
| 9 | + productCost: number | null; |
| 10 | + brandName: string | null; |
| 11 | + proID: string | null; |
| 12 | + image: string | null; |
| 13 | + created_at: string; |
| 14 | +}; |
| 15 | + |
| 16 | +const SupplierProducts: React.FC = () => { |
| 17 | + const [products, setProducts] = useState<SupplierProduct[]>([]); |
| 18 | + const [loading, setLoading] = useState<boolean>(true); |
| 19 | + |
| 20 | + // Modal States |
| 21 | + const [showForm, setShowForm] = useState(false); |
| 22 | + const [selectedProduct, setSelectedProduct] = useState<SupplierProduct | null>(null); |
| 23 | + |
| 24 | + const [formData, setFormData] = useState({ |
| 25 | + name: "", |
| 26 | + mobile: "", |
| 27 | + quantity: "", |
| 28 | + }); |
| 29 | + |
| 30 | + const [submitting, setSubmitting] = useState(false); |
| 31 | + const [showSuccessPopup, setShowSuccessPopup] = useState(false); |
| 32 | + |
| 33 | + const fetchProducts = async () => { |
| 34 | + setLoading(true); |
| 35 | + |
| 36 | + const { data, error } = await supabase.from("supplierProduct").select("*"); |
| 37 | + |
| 38 | + if (error) { |
| 39 | + console.error("Error fetching supplier products:", error); |
| 40 | + } else { |
| 41 | + setProducts(data as SupplierProduct[]); |
| 42 | + } |
| 43 | + |
| 44 | + setLoading(false); |
| 45 | + }; |
| 46 | + |
| 47 | + useEffect(() => { |
| 48 | + fetchProducts(); |
| 49 | + }, []); |
| 50 | + |
| 51 | + const openForm = (product: SupplierProduct) => { |
| 52 | + setSelectedProduct(product); |
| 53 | + setShowForm(true); |
| 54 | + }; |
| 55 | + |
| 56 | + // Submit Booking Request |
| 57 | + const handleSubmit = async () => { |
| 58 | + if (!formData.name || !formData.mobile || !formData.quantity) { |
| 59 | + alert("Please fill all fields."); |
| 60 | + return; |
| 61 | + } |
| 62 | + |
| 63 | + setSubmitting(true); |
| 64 | + |
| 65 | + const { error } = await supabase.from("supplierPorductRequest").insert([ |
| 66 | + { |
| 67 | + name: formData.name, |
| 68 | + phone_No: formData.mobile, |
| 69 | + productName: selectedProduct?.productName, |
| 70 | + Quantity: formData.quantity, |
| 71 | + }, |
| 72 | + ]); |
| 73 | + |
| 74 | + setSubmitting(false); |
| 75 | + |
| 76 | + if (error) { |
| 77 | + console.error("Error inserting:", error); |
| 78 | + alert("Error while booking."); |
| 79 | + } else { |
| 80 | + // clear form |
| 81 | + setShowForm(false); |
| 82 | + setFormData({ name: "", mobile: "", quantity: "" }); |
| 83 | + |
| 84 | + // show success popup |
| 85 | + setShowSuccessPopup(true); |
| 86 | + |
| 87 | + // auto-hide in 2.5 seconds |
| 88 | + setTimeout(() => { |
| 89 | + setShowSuccessPopup(false); |
| 90 | + }, 2500); |
| 91 | + } |
| 92 | + }; |
| 93 | + |
| 94 | + return ( |
| 95 | + <div> |
| 96 | + {/* Page Heading */} |
| 97 | + <section className="max-w-6xl mx-auto px-4 md:px-0 mt-16 mb-8 text-center"> |
| 98 | + <h2 className="text-3xl md:text-4xl font-extrabold text-gray-900"> |
| 99 | + Different Material Available |
| 100 | + </h2> |
| 101 | + <p className="text-gray-600 mt-2 text-lg"> |
| 102 | + Explore Different type of Material |
| 103 | + </p> |
| 104 | + </section> |
| 105 | + |
| 106 | + {/* Product List */} |
| 107 | + <section className="max-w-6xl mx-auto px-4 md:px-0 mt-6 mb-20"> |
| 108 | + {loading ? ( |
| 109 | + <div className="flex justify-center items-center h-40"> |
| 110 | + <Loader2 className="animate-spin w-8 h-8 text-blue-600" /> |
| 111 | + </div> |
| 112 | + ) : products.length === 0 ? ( |
| 113 | + <p className="text-center text-gray-500">No products found.</p> |
| 114 | + ) : ( |
| 115 | + <div className="grid gap-8 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3"> |
| 116 | + {products.map((item) => ( |
| 117 | + <div |
| 118 | + key={item.id} |
| 119 | + className="bg-white rounded-2xl shadow-md hover:shadow-lg transition-transform transform hover:scale-105 overflow-hidden flex flex-col" |
| 120 | + > |
| 121 | + <img |
| 122 | + src={item.image || "/placeholder.jpg"} |
| 123 | + alt={item.productName || "Product Image"} |
| 124 | + className="w-full h-56 sm:h-64 object-cover" |
| 125 | + /> |
| 126 | + |
| 127 | + <div className="p-5 flex flex-col justify-between flex-1"> |
| 128 | + <div> |
| 129 | + <h3 className="text-xl font-bold mb-1 text-gray-900 line-clamp-2"> |
| 130 | + {item.productName} |
| 131 | + </h3> |
| 132 | + |
| 133 | + <p className="text-gray-700 text-sm mb-2"> |
| 134 | + Brand: <span className="font-semibold">{item.brandName}</span> |
| 135 | + </p> |
| 136 | + |
| 137 | + <p className="text-gray-900 font-bold text-lg mb-4"> |
| 138 | + ₹ {item.productCost} |
| 139 | + </p> |
| 140 | + </div> |
| 141 | + |
| 142 | + <button |
| 143 | + onClick={() => openForm(item)} |
| 144 | + className="mt-auto bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition" |
| 145 | + > |
| 146 | + Book a Service |
| 147 | + </button> |
| 148 | + </div> |
| 149 | + </div> |
| 150 | + ))} |
| 151 | + </div> |
| 152 | + )} |
| 153 | + </section> |
| 154 | + |
| 155 | + {/* Booking Form Popup */} |
| 156 | + {showForm && selectedProduct && ( |
| 157 | + <div className="fixed inset-0 bg-black bg-opacity-40 flex justify-center items-center z-50"> |
| 158 | + <div className="bg-white p-6 rounded-xl w-96 shadow-xl"> |
| 159 | + <h2 className="text-xl font-bold mb-4">Book a Service</h2> |
| 160 | + |
| 161 | + <form className="space-y-3"> |
| 162 | + |
| 163 | + <input |
| 164 | + type="text" |
| 165 | + placeholder="Your Name" |
| 166 | + className="w-full border rounded-lg p-2" |
| 167 | + value={formData.name} |
| 168 | + onChange={(e) => |
| 169 | + setFormData({ ...formData, name: e.target.value }) |
| 170 | + } |
| 171 | + /> |
| 172 | + |
| 173 | + <input |
| 174 | + type="text" |
| 175 | + placeholder="Mobile Number" |
| 176 | + className="w-full border rounded-lg p-2" |
| 177 | + value={formData.mobile} |
| 178 | + onChange={(e) => |
| 179 | + setFormData({ ...formData, mobile: e.target.value }) |
| 180 | + } |
| 181 | + /> |
| 182 | + |
| 183 | + <input |
| 184 | + type="text" |
| 185 | + value={selectedProduct.productName || ""} |
| 186 | + className="w-full border rounded-lg p-2 bg-gray-100" |
| 187 | + disabled |
| 188 | + /> |
| 189 | + |
| 190 | + <input |
| 191 | + type="number" |
| 192 | + placeholder="Quantity" |
| 193 | + className="w-full border rounded-lg p-2" |
| 194 | + value={formData.quantity} |
| 195 | + onChange={(e) => |
| 196 | + setFormData({ ...formData, quantity: e.target.value }) |
| 197 | + } |
| 198 | + /> |
| 199 | + |
| 200 | + <button |
| 201 | + type="button" |
| 202 | + onClick={handleSubmit} |
| 203 | + disabled={submitting} |
| 204 | + className="w-full bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700" |
| 205 | + > |
| 206 | + {submitting ? "Submitting..." : "Submit Request"} |
| 207 | + </button> |
| 208 | + |
| 209 | + <button |
| 210 | + type="button" |
| 211 | + onClick={() => setShowForm(false)} |
| 212 | + className="w-full bg-gray-300 text-black py-2 rounded-lg" |
| 213 | + > |
| 214 | + Close |
| 215 | + </button> |
| 216 | + </form> |
| 217 | + </div> |
| 218 | + </div> |
| 219 | + )} |
| 220 | + |
| 221 | + {/* SUCCESS POPUP */} |
| 222 | + {showSuccessPopup && ( |
| 223 | + <div className="fixed bottom-6 right-6 bg-green-600 text-white py-4 px-6 rounded-xl shadow-xl flex items-center gap-3 animate-fade-in z-50"> |
| 224 | + <CheckCircle className="w-6 h-6" /> |
| 225 | + <span className="text-lg font-medium"> |
| 226 | + Request submitted successfully! We will contact you soon. |
| 227 | + </span> |
| 228 | + </div> |
| 229 | + )} |
| 230 | + |
| 231 | + {/* Fade animation */} |
| 232 | + <style>{` |
| 233 | + @keyframes fadeIn { |
| 234 | + from { opacity: 0; transform: translateY(20px); } |
| 235 | + to { opacity: 1; transform: translateY(0); } |
| 236 | + } |
| 237 | + .animate-fade-in { |
| 238 | + animation: fadeIn 0.4s ease-out; |
| 239 | + } |
| 240 | + `}</style> |
| 241 | + </div> |
| 242 | + ); |
| 243 | +}; |
| 244 | + |
| 245 | +export default SupplierProducts; |
0 commit comments