Skip to content

Commit 95f7f73

Browse files
Merge pull request #135 from devxtra-community/dev
feat:gig list page ui
2 parents bf72092 + 983f4e6 commit 95f7f73

3 files changed

Lines changed: 141 additions & 75 deletions

File tree

apps/web/app/gig-details/page.tsx

Lines changed: 86 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -217,68 +217,98 @@ function GigDetailsContent() {
217217
{/* Content Wrapper */}
218218
<div>
219219
{/* Header & Mobile Action */}
220-
<div className="flex flex-row items-center justify-between gap-3 sm:gap-6 mb-8">
221-
<Link href={`/influencer-profile-page?id=${influencer?._id}`} className="flex items-center gap-3 sm:gap-4 hover:opacity-80 transition-opacity bg-white p-2.5 sm:p-4 rounded-2xl sm:rounded-3xl border border-slate-100 shadow-sm flex-1 min-w-0 group">
222-
<div className="relative w-12 h-12 sm:w-16 sm:h-16 rounded-full overflow-hidden border-2 border-emerald-100 shadow-sm bg-emerald-50 flex items-center justify-center font-bold text-emerald-600 text-lg sm:text-xl">
223-
{avatar ? (
224-
<>
225-
<Image
226-
src={avatar}
227-
alt={influencer?.fullName || "Influencer"}
228-
fill
229-
unoptimized
230-
className="object-cover"
231-
onError={(e) => {
232-
const target = e.target as HTMLImageElement;
233-
target.style.display = 'none';
234-
const fallback = target.nextElementSibling as HTMLElement;
235-
if (fallback) fallback.style.display = 'block';
236-
}}
237-
/>
238-
<span className="hidden">{(influencer?.fullName || "I").charAt(0).toUpperCase()}</span>
239-
</>
240-
) : (
241-
(influencer?.fullName || "I").charAt(0).toUpperCase()
242-
)}
243-
</div>
244-
<div className="min-w-0 flex-1">
245-
<div className="flex items-center gap-2">
246-
<h1 className="text-lg sm:text-xl font-bold text-gray-900 truncate">{influencer?.fullName || "Unknown Creator"}</h1>
247-
{influencer?.isVerified && (
248-
<span className="bg-[#E6F6ED] text-[#0CAF60] text-[10px] sm:text-xs font-bold px-2 py-0.5 rounded-full flex items-center gap-1">
249-
<CheckCircle2 className="w-3 h-3 fill-current" />
250-
Verified
251-
</span>
220+
<div className="flex flex-row items-stretch justify-between gap-3 sm:gap-4 mb-12 lg:hidden">
221+
<Link
222+
href={`/influencer-profile-page?id=${influencer?._id}`}
223+
className="flex items-center gap-2 sm:gap-4 hover:opacity-80 transition-opacity bg-white p-3 sm:p-5 rounded-[2rem] border border-slate-100 shadow-sm basis-0 grow min-w-0 group"
224+
>
225+
<div className="relative w-10 h-10 sm:w-16 sm:h-16 rounded-full overflow-hidden border-2 border-emerald-100 shadow-sm bg-emerald-50 flex items-center justify-center font-bold text-emerald-600 text-base sm:text-xl shrink-0">
226+
{avatar ? (
227+
<>
228+
<Image
229+
src={avatar}
230+
alt={influencer?.fullName || "Influencer"}
231+
fill
232+
unoptimized
233+
className="object-cover"
234+
onError={(e) => {
235+
const target = e.target as HTMLImageElement;
236+
target.style.display = 'none';
237+
const fallback = target.nextElementSibling as HTMLElement;
238+
if (fallback) fallback.style.display = 'block';
239+
}}
240+
/>
241+
<span className="hidden">{(influencer?.fullName || "I").charAt(0).toUpperCase()}</span>
242+
</>
243+
) : (
244+
(influencer?.fullName || "I").charAt(0).toUpperCase()
252245
)}
253246
</div>
254-
<div className="text-[10px] sm:text-xs text-gray-500 flex items-center gap-2 mt-0.5 font-medium">
255-
<span>{influencer.categories.join(" & ")}</span>
256-
<span className="w-1 h-1 bg-gray-300 rounded-full"></span>
257-
<span className="flex items-center text-gray-700">
258-
<Star className="w-3.5 h-3.5 fill-yellow-400 text-yellow-400 mr-1" />
259-
<span className="font-bold">New</span>
260-
</span>
247+
<div className="min-w-0 flex-1">
248+
<div className="flex items-center gap-1.5">
249+
<h1 className="text-xs sm:text-lg font-black text-gray-900 truncate tracking-tight">{influencer?.fullName?.split(' ')[0] || "Creator"}</h1>
250+
{influencer?.isVerified && (
251+
<CheckCircle2 className="w-3 h-3 text-[#0CAF60] fill-current shrink-0" />
252+
)}
253+
</div>
254+
<p className="text-[9px] sm:text-xs text-emerald-600 font-black uppercase tracking-wider truncate">Profile</p>
261255
</div>
256+
</Link>
257+
258+
<div className="basis-0 grow min-w-0">
259+
<button
260+
onClick={() => {
261+
if (!user) {
262+
router.push(`/login?redirect=${encodeURIComponent(window.location.pathname + window.location.search)}`);
263+
return;
264+
}
265+
setIsModalOpen(true);
266+
}}
267+
disabled={!isInitialized || alreadyRequested || isRequestSuccess}
268+
className="w-full h-full bg-emerald-500 hover:bg-emerald-600 disabled:bg-slate-300 disabled:cursor-not-allowed disabled:shadow-none text-white px-3 sm:px-6 rounded-[2rem] font-bold text-[13px] sm:text-lg transition-all shadow-lg flex items-center justify-center gap-1 sm:gap-2 group"
269+
>
270+
<span className="truncate">
271+
{alreadyRequested ? "Already Requested" : "Request Booking"}
272+
</span>
273+
{!alreadyRequested && <ChevronRight className="w-4 h-4 sm:w-6 sm:h-6 group-hover:translate-x-1 transition-transform shrink-0" />}
274+
</button>
262275
</div>
263-
</Link>
276+
</div>
264277

265-
{/* Mobile/Tablet Request Button */}
266-
<div className="lg:hidden">
267-
<button
268-
onClick={() => {
269-
if (!user) {
270-
router.push(`/login?redirect=${encodeURIComponent(window.location.pathname + window.location.search)}`);
271-
return;
272-
}
273-
setIsModalOpen(true);
274-
}}
275-
disabled={!isInitialized || alreadyRequested || isRequestSuccess}
276-
className="h-12 sm:h-14 px-8 bg-emerald-500 hover:bg-emerald-600 disabled:bg-slate-200 disabled:text-slate-400 text-white rounded-2xl font-bold text-sm sm:text-base transition-all shadow-lg shadow-emerald-500/20 whitespace-nowrap"
278+
{/* Desktop Header Only */}
279+
<div className="hidden lg:flex items-center gap-6 mb-8">
280+
<Link
281+
href={`/influencer-profile-page?id=${influencer?._id}`}
282+
className="flex items-center gap-4 hover:opacity-80 transition-opacity bg-white p-4 rounded-3xl border border-slate-100 shadow-sm group"
277283
>
278-
{alreadyRequested ? "Requested" : "Request Now"}
279-
</button>
284+
<div className="relative w-16 h-16 rounded-full overflow-hidden border-2 border-emerald-100 shadow-sm bg-emerald-50 flex items-center justify-center font-bold text-emerald-600 text-xl">
285+
{avatar ? (
286+
<Image src={avatar} alt={influencer?.fullName || "Influencer"} fill unoptimized className="object-cover" />
287+
) : (
288+
(influencer?.fullName || "I").charAt(0).toUpperCase()
289+
)}
290+
</div>
291+
<div>
292+
<div className="flex items-center gap-2">
293+
<h1 className="text-xl font-bold text-gray-900">{influencer?.fullName || "Unknown Creator"}</h1>
294+
{influencer?.isVerified && (
295+
<span className="bg-[#E6F6ED] text-[#0CAF60] text-xs font-bold px-2 py-0.5 rounded-full flex items-center gap-1">
296+
<CheckCircle2 className="w-3 h-3 fill-current" />
297+
Verified
298+
</span>
299+
)}
300+
</div>
301+
<div className="text-xs text-gray-500 flex items-center gap-2 mt-0.5 font-medium">
302+
<span>{influencer.categories.join(" & ")}</span>
303+
<span className="w-1 h-1 bg-gray-300 rounded-full"></span>
304+
<span className="flex items-center text-gray-700 font-bold">
305+
<Star className="w-3.5 h-3.5 fill-yellow-400 text-yellow-400 mr-1" />
306+
New
307+
</span>
308+
</div>
309+
</div>
310+
</Link>
280311
</div>
281-
</div>
282312

283313
<h2 className="text-4xl font-black text-gray-900 leading-tight mb-5 pr-8">
284314
{gig.title}
@@ -439,7 +469,7 @@ function GigDetailsContent() {
439469
setIsModalOpen(true);
440470
}}
441471
disabled={!isInitialized || alreadyRequested || isRequestSuccess}
442-
className="w-full bg-emerald-500 hover:bg-emerald-600 disabled:bg-slate-300 disabled:cursor-not-allowed disabled:shadow-none text-white py-5 rounded-2xl font-black text-lg transition-all shadow-lg flex items-center justify-center gap-3 group"
472+
className="w-full bg-emerald-500 hover:bg-emerald-600 disabled:bg-slate-300 disabled:cursor-not-allowed disabled:shadow-none text-white py-5 rounded-2xl font-bold text-lg xl:text-xl transition-all shadow-lg flex items-center justify-center gap-3 group"
443473
>
444474
{alreadyRequested ? "Already Requested" : "Request Booking"}
445475
{!alreadyRequested && <ChevronRight className="w-6 h-6 group-hover:translate-x-1 transition-transform" />}

apps/web/app/gig-list/page.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,24 @@ export default function ExploreGigs() {
260260
const [committedSearch, setCommittedSearch] = useState<string>("");
261261
const [committedMaxPrice, setCommittedMaxPrice] = useState<number>(MAX_PRICE_LIMIT);
262262
const [isFilterOpen, setIsFilterOpen] = useState<boolean>(false);
263+
const [isNavbarVisible, setIsNavbarVisible] = useState<boolean>(true);
264+
const [lastScrollY, setLastScrollY] = useState<number>(0);
265+
266+
useEffect(() => {
267+
const handleScroll = () => {
268+
const currentScrollY = window.scrollY;
269+
if (currentScrollY < 10) {
270+
setIsNavbarVisible(true);
271+
} else if (currentScrollY > lastScrollY) {
272+
setIsNavbarVisible(false);
273+
} else {
274+
setIsNavbarVisible(true);
275+
}
276+
setLastScrollY(currentScrollY);
277+
};
278+
window.addEventListener("scroll", handleScroll, { passive: true });
279+
return () => window.removeEventListener("scroll", handleScroll);
280+
}, [lastScrollY]);
263281

264282
const fetchGigs = useCallback(async (
265283
opts: {
@@ -382,7 +400,7 @@ export default function ExploreGigs() {
382400
</div>
383401

384402
{/* Premium Minimal Filter System */}
385-
<div className="sticky top-24 z-40 mb-12">
403+
<div className={`sticky z-40 mb-12 transition-all duration-500 ${!isNavbarVisible ? "top-6" : "top-32"}`}>
386404
<div className="flex items-center gap-3 md:gap-4">
387405
{/* Unified Search Bar */}
388406
<div className="relative flex-1 group">

0 commit comments

Comments
 (0)