680 lines
32 KiB
TypeScript
680 lines
32 KiB
TypeScript
// // app/brands/BrandsClient.tsx
|
|
// 'use client';
|
|
|
|
// import React, { useState, useEffect } from 'react';
|
|
|
|
// type Brand = {
|
|
// id: string;
|
|
// name: string;
|
|
// logo?: string;
|
|
// dropship: boolean;
|
|
// };
|
|
|
|
// export default function BrandsClient({ brands }: { brands: Brand[] }) {
|
|
// const [search, setSearch] = useState('');
|
|
// const [selectedIds, setSelectedIds] = useState<string[]>([]);
|
|
// const [toast, setToast] = useState('');
|
|
// const [isScrolled, setIsScrolled] = useState(false);
|
|
// const [showDropshipOnly, setShowDropshipOnly] = useState(false);
|
|
|
|
// useEffect(() => {
|
|
// const handleScroll = () => {
|
|
// setIsScrolled(window.scrollY > 10);
|
|
// };
|
|
// window.addEventListener('scroll', handleScroll);
|
|
// return () => window.removeEventListener('scroll', handleScroll);
|
|
// }, []);
|
|
|
|
// const filteredBrands = brands.filter((b) => {
|
|
// const matchesSearch = b.name.toLowerCase().includes(search.toLowerCase());
|
|
// const matchesDropship = showDropshipOnly ? b.dropship : true;
|
|
// return matchesSearch && matchesDropship;
|
|
// });
|
|
|
|
// const allFilteredSelected = filteredBrands.length > 0 && filteredBrands.every((b) => selectedIds.includes(b.id));
|
|
|
|
// const toggleSelect = (id: string) => {
|
|
// setSelectedIds((prev) => (prev.includes(id) ? prev.filter((i) => i !== id) : [...prev, id]));
|
|
// };
|
|
|
|
// const toggleSelectAll = () => {
|
|
// console.log('Toggling select all', filteredBrands);
|
|
// const ids = filteredBrands.map((b) => b.id);
|
|
// if (allFilteredSelected) {
|
|
// setSelectedIds((prev) => prev.filter((id) => !ids.includes(id)));
|
|
// } else {
|
|
// setSelectedIds((prev) => Array.from(new Set([...prev, ...ids])));
|
|
// }
|
|
// };
|
|
|
|
// const getSelectedStatusText = () => {
|
|
// if (selectedIds.length === 0) return 'No brands selected';
|
|
// if (selectedIds.length === 1) return '1 brand selected';
|
|
// return `${selectedIds.length} brands selected`;
|
|
// };
|
|
|
|
// const USER_ID = '6ac40b29-8c50-4800-9ece-62b44b69019a'; // static user id
|
|
|
|
// const handleSave = async () => {
|
|
// const payload = {
|
|
// userid: USER_ID,
|
|
// brands: brands
|
|
// .filter((b) => selectedIds.includes(b.id))
|
|
// .map((b) => ({
|
|
// id: b.id,
|
|
// name: b.name,
|
|
// logo: b.logo,
|
|
// dropship: b.dropship,
|
|
// })),
|
|
// };
|
|
|
|
// try {
|
|
// const res = await fetch('https://ebay.backend.data4autos.com/api/brands/bulk-insert', {
|
|
// method: 'POST',
|
|
// headers: {
|
|
// 'Content-Type': 'application/json',
|
|
// },
|
|
// body: JSON.stringify(payload),
|
|
// });
|
|
|
|
// const data = await res.json();
|
|
// console.log('Save response:', data);
|
|
|
|
// // Show beautiful message
|
|
// setToast(`${data.message} (Code: ${data.code}, User: ${data.userid})`);
|
|
// setTimeout(() => setToast(''), 4000);
|
|
// } catch (error) {
|
|
// console.error('Error saving brands:', error);
|
|
// setToast('Failed to save collections. Please try again.');
|
|
// setTimeout(() => setToast(''), 4000);
|
|
// }
|
|
// };
|
|
|
|
// // Function to generate filter status text
|
|
// const getFilterStatusText = () => {
|
|
// if (filteredBrands.length === brands.length && !search && !showDropshipOnly) {
|
|
// return `Showing all ${brands.length} brands`;
|
|
// }
|
|
|
|
// let status = `Showing ${filteredBrands.length} of ${brands.length} brands`;
|
|
|
|
// if (search && showDropshipOnly) {
|
|
// status += ` matching "${search}" and dropship only`;
|
|
// } else if (search) {
|
|
// status += ` matching "${search}"`;
|
|
// } else if (showDropshipOnly) {
|
|
// status += ` (dropship only)`;
|
|
// }
|
|
|
|
// return status;
|
|
// };
|
|
|
|
// return (
|
|
// <div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-200">
|
|
// {/* Enhanced Fixed Header */}
|
|
// <div className={`fixed top-0 left-0 w-full z-20 transition-all duration-300 ${isScrolled ? 'bg-white/95 backdrop-blur-md shadow-lg py-3' : 'bg-white/80 backdrop-blur-sm py-4'}`}>
|
|
// <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
// <div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
|
|
// <div className="flex flex-col">
|
|
// <h1 className="text-2xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent">Data4Autos Turn14 Brands</h1>
|
|
// <p className="text-sm text-gray-500 mt-1">{getFilterStatusText()}</p>
|
|
// <p className="text-sm font-medium text-blue-600 mt-1">{getSelectedStatusText()}</p>
|
|
// </div>
|
|
|
|
// <div className="flex flex-col sm:flex-row gap-3 items-stretch sm:items-center">
|
|
// <div className="relative flex-grow">
|
|
// <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
// <svg className="h-5 w-5 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
|
|
// <path
|
|
// fillRule="evenodd"
|
|
// d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
|
|
// clipRule="evenodd"
|
|
// />
|
|
// </svg>
|
|
// </div>
|
|
// <input
|
|
// type="text"
|
|
// value={search}
|
|
// onChange={(e) => setSearch(e.target.value)}
|
|
// placeholder="Search brands…"
|
|
// className="block w-full pl-10 pr-3 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors"
|
|
// />
|
|
// </div>
|
|
|
|
// <div className="flex flex-wrap items-center gap-3">
|
|
// <label className="flex items-center gap-2 text-sm font-medium text-gray-700 cursor-pointer select-none">
|
|
// <div className="relative">
|
|
// <input type="checkbox" checked={showDropshipOnly} onChange={() => setShowDropshipOnly(!showDropshipOnly)} className="sr-only" />
|
|
// <div className={`block w-10 h-6 rounded-full transition-colors ${showDropshipOnly ? 'bg-blue-600' : 'bg-gray-300'}`}></div>
|
|
// <div className={`absolute left-1 top-1 bg-white w-4 h-4 rounded-full transition-transform ${showDropshipOnly ? 'transform translate-x-4' : ''}`}></div>
|
|
// </div>
|
|
// Dropship Only
|
|
// </label>
|
|
|
|
// <label className="flex items-center gap-2 text-sm font-medium text-gray-700 cursor-pointer select-none">
|
|
// <div className="relative">
|
|
// <input type="checkbox" checked={allFilteredSelected} onChange={toggleSelectAll} className="sr-only" />
|
|
// <div className={`block w-10 h-6 rounded-full transition-colors ${allFilteredSelected ? 'bg-blue-600' : 'bg-gray-300'}`}></div>
|
|
// <div className={`absolute left-1 top-1 bg-white w-4 h-4 rounded-full transition-transform ${allFilteredSelected ? 'transform translate-x-4' : ''}`}></div>
|
|
// </div>
|
|
// Select All
|
|
// </label>
|
|
|
|
// <button
|
|
// onClick={handleSave}
|
|
// className="px-5 py-2.5 bg-gradient-to-r from-blue-600 to-purple-600 text-white font-medium rounded-lg hover:from-blue-700 hover:to-purple-700 transition-all duration-300 transform hover:-translate-y-0.5 disabled:opacity-50 disabled:transform-none disabled:cursor-not-allowed flex items-center gap-2 shadow-md hover:shadow-lg"
|
|
// >
|
|
// <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
// <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M5 13l4 4L19 7"></path>
|
|
// </svg>
|
|
// Save Collections
|
|
// </button>
|
|
// </div>
|
|
// </div>
|
|
// </div>
|
|
// </div>
|
|
// </div>
|
|
|
|
// {/* Brand Grid */}
|
|
// <div className="pt-32 pb-12 px-4 sm:px-6 lg:px-8 max-w-7xl mx-auto">
|
|
// {filteredBrands.length > 0 ? (
|
|
// <div className="grid grid-cols-1 xs:grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-5">
|
|
// {/* {filteredBrands.map((brand, index) => ( */}
|
|
// {[...filteredBrands]
|
|
// .sort((a, b) => {
|
|
// const aSelected = selectedIds.includes(a.id);
|
|
// const bSelected = selectedIds.includes(b.id);
|
|
// if (aSelected && !bSelected) return -1;
|
|
// if (!aSelected && bSelected) return 1;
|
|
// return 0;
|
|
// })
|
|
// .map((brand, index) => (
|
|
// <div
|
|
// key={brand.id}
|
|
// className="bg-white rounded-xl shadow-md overflow-hidden hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1.5 relative group"
|
|
// style={{ animationDelay: `${index * 0.05}s` }}
|
|
// onClick={() => toggleSelect(brand.id)}
|
|
// >
|
|
// <div className="absolute top-3 right-3 z-10">
|
|
// <label className="inline-flex items-center">
|
|
// <input type="checkbox" checked={selectedIds.includes(brand.id)} onChange={() => toggleSelect(brand.id)} className="absolute opacity-0 h-0 w-0" />
|
|
// <span
|
|
// className={`checkmark w-6 h-6 rounded-md border-2 flex items-center justify-center transition-all ${selectedIds.includes(brand.id) ? 'bg-blue-600 border-blue-600' : 'bg-white border-gray-300 group-hover:border-blue-400'}`}
|
|
// >
|
|
// {selectedIds.includes(brand.id) && (
|
|
// <svg className="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
// <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="3" d="M5 13l4 4L19 7"></path>
|
|
// </svg>
|
|
// )}
|
|
// </span>
|
|
// </label>
|
|
// </div>
|
|
|
|
// {brand.dropship && (
|
|
// <div className="absolute top-3 left-3 z-10">
|
|
// <span className="inline-flex items-center px-2 py-1 bg-green-100 text-green-800 text-xs font-medium rounded-md">Dropship</span>
|
|
// </div>
|
|
// )}
|
|
|
|
// <div className="p-5 flex flex-col items-center h-full">
|
|
// <div className="w-28 h-28 flex items-center justify-center p-2 bg-gray-50 rounded-lg mb-4">
|
|
// <img
|
|
// src={brand.logo || 'https://cdn.shopify.com/s/files/1/0757/9955/files/no-image_280x@2x.png'}
|
|
// alt={brand.name}
|
|
// className="max-w-full max-h-full object-contain"
|
|
// />
|
|
// </div>
|
|
// <p className="text-center font-medium text-gray-800 mt-auto">{brand.name}</p>
|
|
// </div>
|
|
// </div>
|
|
// ))}
|
|
// </div>
|
|
// ) : (
|
|
// <div className="text-center py-20">
|
|
// <div className="inline-block p-4 bg-white rounded-xl shadow-md">
|
|
// <svg className="w-16 h-16 mx-auto text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
// <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
// </svg>
|
|
// <h3 className="mt-4 text-xl font-medium text-gray-700">No brands found</h3>
|
|
// <p className="mt-2 text-gray-500">Try adjusting your search query or filter settings</p>
|
|
// </div>
|
|
// </div>
|
|
// )}
|
|
// </div>
|
|
|
|
// {/* Enhanced Toast Notification */}
|
|
// {/* {toast && (
|
|
// <div className="fixed bottom-6 right-6 bg-green-600 text-white px-6 py-3 rounded-xl shadow-lg z-30 animate-fade-in-up">
|
|
// <div className="flex items-center gap-3">
|
|
// <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
// <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M5 13l4 4L19 7"></path>
|
|
// </svg>
|
|
// <span>{toast}</span>
|
|
// </div>
|
|
// </div>
|
|
// )} */}
|
|
|
|
// {toast && (
|
|
// <div className="fixed bottom-6 right-6 bg-gradient-to-r from-green-500 to-emerald-600 text-white px-6 py-4 rounded-2xl shadow-2xl z-30 animate-fade-in-up">
|
|
// <div className="flex items-center gap-3">
|
|
// <svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
// <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M5 13l4 4L19 7"></path>
|
|
// </svg>
|
|
// <span className="font-medium">{toast}</span>
|
|
// </div>
|
|
// </div>
|
|
// )}
|
|
|
|
// {/* Add custom animations */}
|
|
// <style jsx global>{`
|
|
// @keyframes fadeInUp {
|
|
// from {
|
|
// opacity: 0;
|
|
// transform: translate3d(0, 40px, 0);
|
|
// }
|
|
// to {
|
|
// opacity: 1;
|
|
// transform: translate3d(0, 0, 0);
|
|
// }
|
|
// }
|
|
// .animate-fade-in-up {
|
|
// animation: fadeInUp 0.5s ease-out;
|
|
// }
|
|
// .checkmark {
|
|
// transition: all 0.2s ease;
|
|
// }
|
|
// `}</style>
|
|
// </div>
|
|
// );
|
|
// }
|
|
'use client';
|
|
|
|
import { getAccessToken_client } from '@/utils/apiHelper_client';
|
|
import axios from 'axios';
|
|
import { useRouter } from 'next/navigation';
|
|
import React, { useState, useEffect } from 'react';
|
|
|
|
type Brand = {
|
|
id: string;
|
|
name: string;
|
|
logo?: string;
|
|
dropship: boolean;
|
|
};
|
|
|
|
|
|
|
|
|
|
async function fetchBrands(accessToken: string): Promise<Brand[]> {
|
|
const resp = await fetch('https://turn14.data4autos.com/v1/brands', {
|
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
cache: 'no-store',
|
|
});
|
|
|
|
if (!resp.ok) {
|
|
throw new Error(`Failed to fetch brands: ${resp.statusText}`);
|
|
}
|
|
|
|
const data = await resp.json();
|
|
return data.data || [];
|
|
}
|
|
|
|
|
|
export default function BrandsClient() {
|
|
|
|
const router = useRouter()
|
|
|
|
const [brands, setBrands] = useState<Brand[]>([]);
|
|
const [search, setSearch] = useState('');
|
|
const [selectedIds, setSelectedIds] = useState<string[]>([]);
|
|
const [toast, setToast] = useState('');
|
|
const [isScrolled, setIsScrolled] = useState(false);
|
|
const [showDropshipOnly, setShowDropshipOnly] = useState(false);
|
|
const [payment, setPayment] = useState<any>(null);
|
|
|
|
|
|
const userId = sessionStorage.getItem('USERID');
|
|
|
|
useEffect(() => {
|
|
const role = localStorage.getItem("user_role");
|
|
const sessionId = localStorage.getItem("payment_session");
|
|
|
|
// ✅ Admins and Partners can access directly (skip payment check)
|
|
if (role === "admin" || role === "partner") {
|
|
return;
|
|
}
|
|
|
|
// 🚫 If no payment session, redirect to pricing
|
|
if (!sessionId) {
|
|
router.push("/pricing");
|
|
return;
|
|
}
|
|
|
|
// ✅ Otherwise, check payment details
|
|
const fetchPaymentDetails = async () => {
|
|
try {
|
|
const res: any = await axios.get(
|
|
"https://ebay.backend.data4autos.com/api/payment/details",
|
|
{ params: { session_id: sessionId } }
|
|
);
|
|
setPayment(res.data.payment);
|
|
} catch (err) {
|
|
console.error("Error fetching payment details:", err);
|
|
}
|
|
};
|
|
|
|
fetchPaymentDetails();
|
|
}, [router]);
|
|
|
|
|
|
useEffect(() => {
|
|
const fetchUserBrands = async () => {
|
|
try {
|
|
//console.log('Fetching access token...'); // Debugging line
|
|
const accessToken = await getAccessToken_client();
|
|
//console.log('Access Token:', accessToken); // Debugging line
|
|
|
|
const brands = accessToken ? await fetchBrands(accessToken) : [];
|
|
setBrands(brands);
|
|
const res = await fetch(`https://ebay.backend.data4autos.com/api/brands/${userId}`, {
|
|
method: 'GET',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
// Optionally add Authorization: `Bearer ${accessToken}` if needed
|
|
},
|
|
});
|
|
|
|
const data = await res.json();
|
|
console.log('GET response:', data);
|
|
|
|
// Extract selected brand IDs from the response
|
|
const userSelectedIds = data.map((b: any) => String(b.brandid)); // brandid from your response
|
|
setSelectedIds(userSelectedIds);
|
|
|
|
// Optional: show toast
|
|
setToast(`Loaded ${userSelectedIds.length} selected brands`);
|
|
setTimeout(() => setToast(''), 4000);
|
|
} catch (error) {
|
|
console.error('Error fetching brands:', error);
|
|
setToast('Failed to load user brands');
|
|
setTimeout(() => setToast(''), 4000);
|
|
}
|
|
};
|
|
|
|
fetchUserBrands();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const handleScroll = () => {
|
|
setIsScrolled(window.scrollY > 10);
|
|
};
|
|
window.addEventListener('scroll', handleScroll);
|
|
return () => window.removeEventListener('scroll', handleScroll);
|
|
}, []);
|
|
|
|
const filteredBrands = brands.filter((b) => {
|
|
const matchesSearch = b.name.toLowerCase().includes(search.toLowerCase());
|
|
const matchesDropship = showDropshipOnly ? b.dropship : true;
|
|
return matchesSearch && matchesDropship;
|
|
});
|
|
|
|
const allFilteredSelected = filteredBrands.length > 0 && filteredBrands.every((b) => selectedIds.includes(b.id));
|
|
|
|
const toggleSelect = (id: string) => {
|
|
setSelectedIds((prev) => (prev.includes(id) ? prev.filter((i) => i !== id) : [...prev, id]));
|
|
};
|
|
|
|
const toggleSelectAll = () => {
|
|
const ids = filteredBrands.map((b) => b.id);
|
|
if (allFilteredSelected) {
|
|
setSelectedIds((prev) => prev.filter((id) => !ids.includes(id)));
|
|
} else {
|
|
setSelectedIds((prev) => Array.from(new Set([...prev, ...ids])));
|
|
}
|
|
};
|
|
|
|
const getSelectedStatusText = () => {
|
|
if (selectedIds.length === 0) return 'No brands selected';
|
|
if (selectedIds.length === 1) return '1 brand selected';
|
|
return `${selectedIds.length} brands selected`;
|
|
};
|
|
|
|
//const userId = sessionStorage.getItem('USERID'); // dynamic user id
|
|
|
|
const handleSave = async () => {
|
|
const payload = {
|
|
userid: userId,
|
|
brands: brands
|
|
.filter((b) => selectedIds.includes(b.id))
|
|
.map((b) => ({
|
|
id: b.id,
|
|
name: b.name,
|
|
logo: b.logo,
|
|
dropship: b.dropship,
|
|
})),
|
|
};
|
|
|
|
try {
|
|
const res = await fetch('https://ebay.backend.data4autos.com/api/brands/bulk-insert', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(payload),
|
|
});
|
|
|
|
const data = await res.json();
|
|
setToast(`${data.message} (Code: ${data.code}, User: ${data.userid})`);
|
|
setTimeout(() => setToast(''), 4000);
|
|
} catch (error) {
|
|
console.error('Error saving brands:', error);
|
|
setToast('Failed to save collections. Please try again.');
|
|
setTimeout(() => setToast(''), 4000);
|
|
}
|
|
};
|
|
|
|
const getFilterStatusText = () => {
|
|
if (filteredBrands.length === brands.length && !search && !showDropshipOnly) {
|
|
return `Showing all ${brands.length} brands`;
|
|
}
|
|
|
|
let status = `Showing ${filteredBrands.length} of ${brands.length} brands`;
|
|
if (search && showDropshipOnly) {
|
|
status += ` matching "${search}" and dropship only`;
|
|
} else if (search) {
|
|
status += ` matching "${search}"`;
|
|
} else if (showDropshipOnly) {
|
|
status += ` (dropship only)`;
|
|
}
|
|
return status;
|
|
};
|
|
|
|
return (
|
|
<div className="bg-gradient-to-br from-[#00d1ff]/10 via-white to-[#00d1ff]/20">
|
|
{/* Sticky Header (below default Header) */}
|
|
<div
|
|
className={`sticky top-14 z-10 transition-all duration-300 ${isScrolled ? 'bg-white/95 backdrop-blur-md shadow-lg py-3' : 'bg-white/80 backdrop-blur-sm py-4'
|
|
}`}
|
|
>
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
|
|
<div className="flex flex-col">
|
|
<h1 className="text-2xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-[#00d1ff]">
|
|
Data4Autos Turn14 Brands
|
|
</h1>
|
|
<p className="text-sm text-gray-500 mt-1">{getFilterStatusText()}</p>
|
|
<p className="text-sm font-medium text-[#00d1ff] mt-1">{getSelectedStatusText()}</p>
|
|
</div>
|
|
|
|
<div className="flex flex-col sm:flex-row gap-3 items-stretch sm:items-center">
|
|
<div className="relative flex-grow">
|
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
<svg className="h-5 w-5 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
|
|
<path
|
|
fillRule="evenodd"
|
|
d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
|
|
clipRule="evenodd"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
<input
|
|
type="text"
|
|
value={search}
|
|
onChange={(e) => setSearch(e.target.value)}
|
|
placeholder="Search brands…"
|
|
className="block w-full pl-10 pr-3 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors"
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex flex-wrap items-center gap-3">
|
|
<label className="flex items-center gap-2 text-sm font-medium text-gray-700 cursor-pointer select-none">
|
|
<div className="relative">
|
|
<input
|
|
type="checkbox"
|
|
checked={showDropshipOnly}
|
|
onChange={() => setShowDropshipOnly(!showDropshipOnly)}
|
|
className="sr-only"
|
|
/>
|
|
<div className={`block w-10 h-6 rounded-full transition-colors ${showDropshipOnly ? 'bg-[#00d1ff]' : 'bg-gray-300'}`}></div>
|
|
<div
|
|
className={`absolute left-1 top-1 bg-white w-4 h-4 rounded-full transition-transform ${showDropshipOnly ? 'transform translate-x-4' : ''
|
|
}`}
|
|
></div>
|
|
</div>
|
|
Dropship Only
|
|
</label>
|
|
|
|
<label className="flex items-center gap-2 text-sm font-medium text-gray-700 cursor-pointer select-none">
|
|
<div className="relative">
|
|
<input type="checkbox" checked={allFilteredSelected} onChange={toggleSelectAll} className="sr-only" />
|
|
<div className={`block w-10 h-6 rounded-full transition-colors ${allFilteredSelected ? 'bg-[#00d1ff]' : 'bg-gray-300'}`}></div>
|
|
<div
|
|
className={`absolute left-1 top-1 bg-white w-4 h-4 rounded-full transition-transform ${allFilteredSelected ? 'transform translate-x-4' : ''
|
|
}`}
|
|
></div>
|
|
</div>
|
|
Select All
|
|
</label>
|
|
|
|
<button
|
|
onClick={handleSave}
|
|
className="px-5 py-2.5 bg-[#00d1ff] text-white font-medium rounded-lg hover:from-blue-700 hover:to-purple-700 transition-all duration-300 transform hover:-translate-y-0.5 disabled:opacity-50 disabled:transform-none disabled:cursor-not-allowed flex items-center gap-2 shadow-md hover:shadow-lg"
|
|
>
|
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M5 13l4 4L19 7"></path>
|
|
</svg>
|
|
Save Collections
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Brand Grid */}
|
|
<div className={`pb-12 px-4 sm:px-6 lg:px-8 max-w-7xl mx-auto ${isScrolled ? 'pt-[100px]' : 'pt-16'}`}>
|
|
{filteredBrands.length > 0 ? (
|
|
<div className="grid grid-cols-1 xs:grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-5">
|
|
{[...filteredBrands]
|
|
.sort((a, b) => {
|
|
const aSelected = selectedIds.includes(a.id);
|
|
const bSelected = selectedIds.includes(b.id);
|
|
if (aSelected && !bSelected) return -1;
|
|
if (!aSelected && bSelected) return 1;
|
|
return 0;
|
|
})
|
|
.map((brand, index) => (
|
|
<div
|
|
key={brand.id}
|
|
className="bg-white rounded-xl shadow-md overflow-hidden hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1.5 relative group"
|
|
style={{ animationDelay: `${index * 0.05}s` }}
|
|
onClick={() => toggleSelect(brand.id)}
|
|
>
|
|
<div className="absolute top-3 right-3 z-10">
|
|
<label className="inline-flex items-center">
|
|
<input
|
|
type="checkbox"
|
|
checked={selectedIds.includes(brand.id)}
|
|
onChange={() => toggleSelect(brand.id)}
|
|
className="absolute opacity-0 h-0 w-0"
|
|
/>
|
|
<span
|
|
className={`checkmark w-6 h-6 rounded-md border-2 flex items-center justify-center transition-all ${selectedIds.includes(brand.id) ? 'bg-[#00d1ff] border-[#00d1ff]' : 'bg-white border-gray-300 group-hover:border-blue-400'
|
|
}`}
|
|
>
|
|
{selectedIds.includes(brand.id) && (
|
|
<svg className="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="3" d="M5 13l4 4L19 7"></path>
|
|
</svg>
|
|
)}
|
|
</span>
|
|
</label>
|
|
</div>
|
|
|
|
{brand.dropship && (
|
|
<div className="absolute top-3 left-3 z-10">
|
|
<span className="inline-flex items-center px-2 py-1 bg-green-100 text-green-800 text-xs font-medium rounded-md">Dropship</span>
|
|
</div>
|
|
)}
|
|
|
|
<div className="p-5 flex flex-col items-center h-full">
|
|
<div className="w-28 h-28 flex items-center justify-center p-2 bg-gray-50 rounded-lg mb-4">
|
|
<img
|
|
src={brand.logo || 'https://cdn.shopify.com/s/files/1/0757/9955/files/no-image_280x@2x.png'}
|
|
alt={brand.name}
|
|
className="max-w-full max-h-full object-contain"
|
|
/>
|
|
</div>
|
|
<p className="text-center font-medium text-gray-800 mt-auto">{brand.name}</p>
|
|
|
|
<p className="text-center font-medium text-gray-800 mt-auto">ID : {brand.id}</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div className="text-center py-20">
|
|
<div className="inline-block p-4 bg-white rounded-xl shadow-md">
|
|
<svg className="w-16 h-16 mx-auto text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
</svg>
|
|
<h3 className="mt-4 text-xl font-medium text-gray-700">No brands found</h3>
|
|
<p className="mt-2 text-gray-500">Try adjusting your search query or filter settings</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Toast Notification */}
|
|
{toast && (
|
|
<div className="fixed bottom-6 right-6 bg-gradient-to-r from-green-500 to-emerald-600 text-white px-6 py-4 rounded-2xl shadow-2xl z-30 animate-fade-in-up">
|
|
<div className="flex items-center gap-3">
|
|
<svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M5 13l4 4L19 7"></path>
|
|
</svg>
|
|
<span className="font-medium">{toast}</span>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Custom Animations */}
|
|
<style jsx global>{`
|
|
@keyframes fadeInUp {
|
|
from {
|
|
opacity: 0;
|
|
transform: translate3d(0, 40px, 0);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translate3d(0, 0, 0);
|
|
}
|
|
}
|
|
.animate-fade-in-up {
|
|
animation: fadeInUp 0.5s ease-out;
|
|
}
|
|
.checkmark {
|
|
transition: all 0.2s ease;
|
|
}
|
|
`}</style>
|
|
</div>
|
|
);
|
|
} |