Skip to content

Commit dc12f23

Browse files
authored
Merge pull request #121 from GoBuildOrg/dev2
Dev2
2 parents b7ce1fd + adf5028 commit dc12f23

4 files changed

Lines changed: 248 additions & 3 deletions

File tree

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
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;

src/integrations/supabase/types.ts

72.1 KB
Binary file not shown.

src/pages/categories/Suppliers.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useNavigate, useSearchParams } from "react-router-dom";
55
import Navbar from "@/components/Navbar";
66
import Footer from "@/components/Footer";
77
import { Loader2, CheckCircle2, Smile } from "lucide-react";
8-
8+
import SupplierProducts from "@/components/SupplierProducts";
99
const MaterialSupplierPage = () => {
1010
const { user } = useAuth();
1111
const navigate = useNavigate();
@@ -373,7 +373,7 @@ const MaterialSupplierPage = () => {
373373
)}
374374
</section>
375375
)}
376-
376+
<SupplierProducts/>
377377
{/* Supplier Section */}
378378
<section className="max-w-6xl mx-auto px-4 md:px-0 mt-16 mb-8 text-center">
379379
<h2 className="text-3xl md:text-4xl font-extrabold text-gray-900">

supabase/.temp/cli-latest

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v2.54.11
1+
v2.62.10

0 commit comments

Comments
 (0)