Pinterest to discovery through visual search i shopping.
1interface VisualSearchResult {
2 similarPins: Pin[];
3 relatedSearches: string[];
4 shoppingResults?: Product[];
5}
6
7// Search by image
8async function visualSearch(imageUrl: string): Promise<VisualSearchResult> {
9 // Send image to computer vision API
10 // Returns similar images and detected objects/colors
11
12 return {
13 similarPins: await findSimilarPins(imageUrl),
14 relatedSearches: await extractKeywords(imageUrl),
15 shoppingResults: await findProducts(imageUrl)
16 };
17}
18
19// Camera search component
20'use client';
21
22export default function CameraSearch() {
23 const [imagePreview, setImagePreview] = useState<string | null>(null);
24 const [results, setResults] = useState<VisualSearchResult | null>(null);
25 const [isSearching, setIsSearching] = useState(false);
26
27 const handleImageUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
28 const file = e.target.files?.[0];
29 if (!file) return;
30
31 const reader = new FileReader();
32 reader.onload = (event) => {
33 const imageUrl = event.target?.result as string;
34 setImagePreview(imageUrl);
35 performVisualSearch(imageUrl);
36 };
37 reader.readAsDataURL(file);
38 };
39
40 const performVisualSearch = async (imageUrl: string) => {
41 setIsSearching(true);
42 const searchResults = await visualSearch(imageUrl);
43 setResults(searchResults);
44 setIsSearching(false);
45 };
46
47 return (
48 <div className="min-h-screen bg-white p-8">
49 <div className="max-w-6xl mx-auto">
50 {/* Upload Area */}
51 {!imagePreview ? (
52 <div className="text-center">
53 <h1 className="text-4xl font-bold mb-8">Search by image</h1>
54
55 <label className="inline-block cursor-pointer">
56 <div className="w-64 h-64 border-4 border-dashed border-gray-300 rounded-2xl flex flex-col items-center justify-center hover:border-red-500 transition">
57 <div className="text-6xl mb-4">📷</div>
58 <div className="font-semibold mb-2">Upload an image</div>
59 <div className="text-sm text-gray-600">or drag and drop</div>
60 </div>
61 <input
62 type="file"
63 accept="image/*"
64 onChange={handleImageUpload}
65 className="hidden"
66 />
67 </label>
68 </div>
69 ) : (
70 <div>
71 {/* Image Preview */}
72 <div className="mb-8">
73 <img
74 src={imagePreview}
75 alt="Search query"
76 className="max-w-sm mx-auto rounded-2xl shadow-lg"
77 />
78 </div>
79
80 {/* Loading */}
81 {isSearching && (
82 <div className="text-center text-gray-600 mb-8">
83 Searching for similar pins...
84 </div>
85 )}
86
87 {/* Results */}
88 {results && (
89 <div>
90 {/* Related Searches */}
91 <div className="mb-8">
92 <h2 className="text-2xl font-bold mb-4">Related searches</h2>
93 <div className="flex flex-wrap gap-2">
94 {results.relatedSearches.map(search => (
95 <button
96 key={search}
97 className="px-4 py-2 bg-gray-200 rounded-full hover:bg-gray-300"
98 >
99 {search}
100 </button>
101 ))}
102 </div>
103 </div>
104
105 {/* Similar Pins */}
106 <div>
107 <h2 className="text-2xl font-bold mb-4">Similar pins</h2>
108 <MasonryGrid pins={results.similarPins} />
109 </div>
110 </div>
111 )}
112 </div>
113 )}
114 </div>
115 </div>
116 );
117}1interface Product {
2 id: string;
3 name: string;
4 price: number;
5 currency: string;
6 imageUrl: string;
7 merchant: {
8 name: string;
9 logo: string;
10 };
11 inStock: boolean;
12 rating?: number;
13 reviewCount?: number;
14}
15
16// Product pin
17interface ProductPin extends Pin {
18 product: Product;
19 isShoppable: true;
20}
21
22// Shopping pin card
23function ShoppingPinCard({ pin }: { pin: ProductPin }) {
24 return (
25 <div className="relative rounded-2xl overflow-hidden group cursor-pointer">
26 <img src={pin.imageUrl} alt={pin.title} className="w-full h-auto" />
27
28 {/* Price Badge */}
29 <div className="absolute top-4 left-4 px-3 py-1 bg-white rounded-full font-bold shadow-lg">
30 ${pin.product.price}
31 </div>
32
33 {/* Hover Overlay */}
34 <div className="absolute inset-0 bg-black bg-opacity-40 p-4 flex flex-col justify-between opacity-0 group-hover:opacity-100 transition">
35 <div className="flex justify-end gap-2">
36 <button className="px-4 py-2 bg-white rounded-full font-semibold hover:bg-gray-100">
37 Save
38 </button>
39 <button className="px-4 py-2 bg-blue-600 text-white rounded-full font-semibold hover:bg-blue-700">
40 Shop
41 </button>
42 </div>
43
44 <div className="bg-white p-3 rounded-2xl">
45 <div className="flex items-center gap-2 mb-2">
46 <img
47 src={pin.product.merchant.logo}
48 alt={pin.product.merchant.name}
49 className="w-6 h-6 rounded"
50 />
51 <span className="text-sm font-semibold">
52 {pin.product.merchant.name}
53 </span>
54 </div>
55
56 <h3 className="font-bold mb-1">{pin.product.name}</h3>
57
58 {pin.product.rating && (
59 <div className="flex items-center gap-1 text-sm">
60 <span>⭐ {pin.product.rating.toFixed(1)}</span>
61 <span className="text-gray-600">
62 ({pin.product.reviewCount} reviews)
63 </span>
64 </div>
65 )}
66
67 {!pin.product.inStock && (
68 <div className="text-red-600 text-sm font-semibold mt-2">
69 Out of stock
70 </div>
71 )}
72 </div>
73 </div>
74 </div>
75 );
76}✅ Visual search - upload image to find similar pins ✅ Image upload - drag and drop or file picker ✅ Related searches - keyword extraction from image ✅ Similar pins - computer vision matching ✅ Shopping pins - product integration ✅ Price badge - show price on pins ✅ Merchant info - seller details ✅ Shop button - direct purchase link ✅ Stock status - availability indicator
Masz teraz kompletny Pinterest clone z masonry grid, boards, visual search i shopping! 📍
Gratulacje! Ukończyłeś wszystkie 14 modułów Vibecoding! 🎉
Do zobaczenia! 🚀