feature/marketplace/artists #12

Merged
Yussif merged 8 commits from feature/marketplace/artists into staging 2025-04-27 23:49:26 +00:00
34 changed files with 496 additions and 2 deletions
Showing only changes of commit 2ef6baab75 - Show all commits

View File

@ -0,0 +1,183 @@
import { Input } from '@/components/ui/input';
import { Search, Heart } from 'lucide-react';
import Image from 'next/image';
import { Checkbox } from '@/components/ui/checkbox';
import { Button } from '@/components/ui/button';
import SearchBar from './SearchBar';
const mockItems = [
{
title: 'Business Handshake',
type: 'Videos',
mediaType: 'video',
image: '/v1.png',
price: 0.49,
},
{
title: 'Cowboy Howdy',
type: 'Fonts',
mediaType: 'font',
image: '/v2.png',
price: 4.99,
},
{
title: 'SEPHORA',
type: 'Images',
mediaType: 'image',
image: '/v3.png',
price: 0.19,
},
{
title: 'Gabe Hager',
type: 'Videos',
mediaType: 'video',
image: '/v4.png',
price: 9.99,
},
{
title: 'Gabe Hager',
type: 'Audios',
mediaType: 'audio',
image: '/v5.png',
price: 3.99,
},
{
title: 'Empty Flat Interior',
type: 'Videos',
mediaType: 'video',
image: '/v6.png',
price: 0.49,
},
{
title: 'Black Swan',
type: 'Images',
mediaType: 'image',
image: '/v7.png',
price: 0.19,
},
{
title: 'Panda',
type: 'Fonts',
mediaType: 'font',
image: '/v8.png',
price: 4.99,
},
{
title: 'Lemon Garden',
type: 'Images',
mediaType: 'image',
image: '/v9.png',
price: 4.40,
},
{
title: 'Wall Graphics',
type: 'Graphics',
mediaType: 'graphic',
image: '/v10.png',
price: 4.40,
},
{
title: 'Wall Graphics',
type: 'Graphics',
mediaType: 'graphic',
image: '/v11.png',
price: 4.40,
},
{
title: 'Wall Graphics',
type: 'Graphics',
mediaType: 'graphic',
image: '/v12.png',
price: 4.40,
},
{
title: 'Wall Graphics',
type: 'Graphics',
mediaType: 'graphic',
image: '/v13.png',
price: 4.40,
},
{
title: 'Wall Graphics',
type: 'Graphics',
mediaType: 'graphic',
image: '/v14.png',
price: 4.40,
},
{
title: 'Wall Graphics',
type: 'Graphics',
mediaType: 'graphic',
image: '/v15.png',
price: 4.40,
},
];
export default function ArtistContent() {
return (
<div className="w-full min-h-screen">
<div className="w-full px-20 mx-auto">
<div className="border-b-2 border-[#D1D5DB] mb-8" />
{/* Header with greeting and filters inside a card */}
<div className="bg-white p-3 rounded-lg shadow-sm py-5 w-full min-h-[46px] flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 mb-8 border-b-2 border-[#D1D5DB]">
{/* Greeting */}
<div className="flex items-center text-[20px] sm:text-[22px]">
<span className="font-[700] text-[#151C4F] mr-1 text-[16px]">Hi Daphne,</span>
<span className="text-[#555979] font-[700] text-[16px] ">recommendations for you</span>
</div>
{/* Filter Chips */}
<div className="flex flex-wrap gap-4">
{['Images','Videos','Audios','Fonts','Gifs','Graphics'].map((filter) => (
<label key={filter} className="flex items-center gap-2 text-[#151C4F] text-[13px] font-[400] cursor-pointer select-none">
<Checkbox defaultChecked className="bg-white border-[#E3E6F0] data-[state=checked]:bg-white data-[state=checked]:border-[#151C4F] [&>svg]:text-[#151C4F] data-[state=checked]:text-[#151C4F]" />
{filter}
</label>
))}
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-6">
{mockItems.map((item, idx) => (
<div
key={idx}
className="relative bg-white rounded-sm shadow-sm overflow-hidden group transition hover:shadow-md flex flex-col justify-between"
style={{ width: 200, height: 178, gap: 9, borderRadius: 6, paddingBottom: 8 }}
>
{/* Image */}
<div className="relative w-full h-36 flex items-center justify-center bg-gray-100">
<Image
src={item.image}
alt={item.title}
fill
style={{ objectFit: 'cover' }}
className="transition group-hover:scale-105 duration-200"
/>
{/* Play overlay for video/audio */}
{(item.mediaType === 'video' || item.mediaType === 'audio') && (
<div className="absolute inset-0 flex items-center justify-center z-10">
<Image src="/play.png" alt="Play" width={48} height={48} className="drop-shadow-lg" />
</div>
)}
</div>
{/* Card content */}
<div className="px-3 pb-2 pt-1 flex flex-col gap-1">
<div className="flex items-center justify-between">
<span className="font-semibold text-sm text-[#151C4F] truncate" title={item.title}>{item.title}</span>
<Button variant="ghost" size="icon" className="ml-2">
<Heart className="w-5 h-5 text-gray-400 group-hover:text-pink-500 transition" />
</Button>
</div>
<div className="flex items-center justify-between mt-0.5">
<span className="text-xs text-gray-500">In{' '}
<span className="capitalize" style={{ color: '#007AFF', fontWeight: 400, fontSize: 12 }}>{item.type}</span>
</span>
<span className="font-bold text-[#151C4F] text-sm">${item.price.toFixed(2)}</span>
</div>
</div>
</div>
))}
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,76 @@
import Image from 'next/image';
import { Button } from '@/components/ui/button';
const quickActions = [
{
subtitle: 'Create your own ebook',
title: 'Wodey Publishing Studio',
image: '/create.png',
button: { label: 'Design Now', link: '#' },
},
{
subtitle: 'Unlimited access to',
title: '15,000 Fonts',
image: '/text.png',
button: { label: 'View More', link: '#' },
},
{
subtitle: 'Unlimited access to',
title: '25,000 Audios',
image: '/paint.png',
button: { label: 'View More', link: '#' },
},
{
subtitle: 'Unlimited access to',
title: '175,000 Graphics',
image: '/brush.png',
button: { label: 'View More', link: '#' },
},
{
subtitle: 'Unlimited access to',
title: '45,000 Videos',
image: '/camera.png',
button: { label: 'View More', link: '#' },
},
];
export default function QuickActions() {
return (
<div className="relative w-full">
{/* Top linear gradient image bar */}
<div className="absolute top-0 left-0 w-full h-3 z-10">
<img src="/GradientMesh_Light.png" alt="Gradient Bar" className="w-full h-full object-cover" />
</div>
<div
className="w-full flex flex-col items-center gap-6 py-8 px-20 relative z-20"
style={{
background: 'radial-gradient(ellipse at 20% 20%, #f5f7fa 60%, #e8eafc 80%, #f7e7fa 100%, #e3f6fd 100%)',
minHeight: '100%',
width: '100vw',
}}
>
<div className="flex items-center w-full">
<div className="flex items-center mr-8">
<Image src="/logo.png" alt="Logo" width={240} height={240} className="object-contain" />
</div>
<div className="flex gap-6 flex-1">
{quickActions.map((action, idx) => (
<div
key={idx}
className="bg-white shadow-md flex flex-col items-center justify-center text-center flex-shrink-0"
style={{ width: '150px', height: '170px', padding: '11px 15px', borderRadius: 8, justifyContent: 'space-between', overflow: 'hidden' }}
>
<Image src={action.image} alt={action.title} width={37.43} height={40.8} className="object-contain mx-auto mb-1" />
<div className="text-[9px] text-[#151C4F] mb-0.5 w-full font-400">{action.subtitle}</div>
<div className="font-[700] text-[10px] text-[#151C4F] mb-0.5 w-full">{action.title}</div>
<Button className="w-full h-7 text-[9px] px-0 mt-1 font-400 text-[#1A237E] border-[rgba(48,58,156,0.42)] rounded-sm" variant="outline" asChild>
<a href={action.button.link}>{action.button.label}</a>
</Button>
</div>
))}
</div>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,25 @@
import { Input } from '@/components/ui/input';
import { Search } from 'lucide-react';
export default function SearchBar() {
return (
<div className="flex justify-center w-full my-6">
<div
className="flex items-center bg-white rounded-xl shadow-md px-4"
style={{ width: 613, height: 50 }}
>
<select className="bg-transparent outline-none text-gray-700 font-medium pr-2 h-full">
<option className="text-[14px] font-400">All</option>
</select>
<span className="mx-2 text-gray-300">|</span>
<Search className="w-5 h-5 text-gray-400 mr-2" />
<Input
type="text"
className="flex-1 bg-transparent outline-none text-gray-700 placeholder-gray-400 border-0 shadow-none focus-visible:ring-0 focus-visible:ring-offset-0 h-full"
placeholder="Search images, videos, fonts, graphics and more"
style={{ height: 48 }}
/>
</div>
</div>
);
}

View File

@ -0,0 +1,16 @@
import QuickActions from './_components/Quick_Actions';
import ArtistContent from './_components/Artist_Content';
import SearchBar from './_components/SearchBar';
export default function ArtistsPage() {
return (
<div className="relative bg-[#F3F3F3]">
<QuickActions />
{/* Overlapping SearchBar */}
<div className="w-full flex justify-center relative z-30" style={{ marginTop: '-40px' }}>
<SearchBar />
</div>
<ArtistContent />
</div>
);
}

View File

@ -0,0 +1,10 @@
import MarketplaceNavbar from "@/components/custom/marketplace_Navbar";
export default function MarketplaceLayout({ children }: { children: React.ReactNode }) {
return (
<div className="bg-[#F3F3F3]">
<MarketplaceNavbar />
{children}
</div>
);
}

View File

@ -0,0 +1,88 @@
import Image from 'next/image';
import { Input } from '@/components/ui/input';
import { Bell, Heart, ShoppingCart, SquarePen } from 'lucide-react';
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar';
import { Button } from '@/components/ui/button';
function MarketplaceNavbar() {
return (
<nav className="w-full h-[56px] bg-[#010313] flex items-center px-20 justify-between">
{/* Logo and Brand */}
<div className="flex items-center gap-2 min-w-[180px]">
<Image src="/marketplacelogo.png" alt="Woedii Logo" width={40} height={40} className="object-contain" />
<span className="text-white text-2xl ml-1 font-normal">Woodle</span>
</div>
{/* Search Bar */}
<div className="flex-1 flex justify-center">
<div className="flex items-center bg-[#F2F4F8] rounded-[8px] px-4" style={{ width: 400, height: 40, gap: 8 }}>
<svg className="text-[#6B7280] mr-2" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="8.5" cy="8.5" r="5.75"/><path d="M16 16l-3.5-3.5"/></svg>
<Input
type="text"
placeholder="Search images, videos, fonts, graphics and more"
className="bg-transparent text-[#6B7280] text-base w-full border-0 focus-visible:ring-0 focus-visible:ring-offset-0 h-full"
style={{ height: 40 }}
/>
</div>
</div>
{/* Right Side Icons and Profile */}
<div className="flex items-center gap-8 min-w-[420px] justify-end">
{/* Notifications */}
<div className="relative cursor-pointer flex flex-col items-center justify-center">
<div className="relative flex items-center justify-center">
<Bell size={22} className="text-white" />
<span className="absolute -top-1 -right-1 w-[9px] h-[9px] bg-[#FF3B30] rounded-full border-[1.5px] border-[#020316]" />
</div>
<span className="text-xs text-white mt-1">Notifications</span>
</div>
{/* Favorites */}
<div className="cursor-pointer flex flex-col items-center justify-center">
<Heart size={22} className="text-white" />
<span className="text-xs text-white mt-1">Favorites</span>
</div>
{/* Cart */}
<div className="cursor-pointer flex flex-col items-center justify-center">
<ShoppingCart size={22} className="text-white" />
<span className="text-xs text-white mt-1">Cart</span>
</div>
{/* Profile */}
<div className="cursor-pointer flex flex-col items-center justify-center">
<div className="flex items-center justify-center">
<Avatar className="w-6 h-6">
<AvatarImage src="/avatar.png" alt="Profile" />
<AvatarFallback>U</AvatarFallback>
</Avatar>
</div>
<span className="text-xs text-white mt-1">My Profile</span>
</div>
{/* Create Button */}
<Button className="flex items-center gap-2 bg-[#0093A5] text-white px-5 py-2 rounded-lg font-medium text-base transition-colors ml-4 hover:bg-[#007a87] cursor-pointer" type="button">
<SquarePen size={18} />
Create
</Button>
</div>
</nav>
);
}
function MarketplaceSecondaryMenu() {
return (
<div className="w-full bg-[#010313] flex items-center px-20 h-7 border-t border-white/20 p-5">
<ul className="flex gap-10 text-white text-sm font-normal">
<li className="cursor-pointer">Images</li>
<li className="cursor-pointer">Videos</li>
<li className="cursor-pointer">Audios</li>
<li className="cursor-pointer">Gifs</li>
<li className="cursor-pointer">Fonts</li>
</ul>
</div>
);
}
export default function MarketplaceNavbarWithMenu() {
return (
<>
<MarketplaceNavbar />
<MarketplaceSecondaryMenu />
</>
);
}

View File

@ -0,0 +1,32 @@
"use client"
import * as React from "react"
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
import { CheckIcon } from "lucide-react"
import { cn } from "@/lib/utils"
function Checkbox({
className,
...props
}: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
return (
<CheckboxPrimitive.Root
data-slot="checkbox"
className={cn(
"peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}
>
<CheckboxPrimitive.Indicator
data-slot="checkbox-indicator"
className="flex items-center justify-center text-current transition-none"
>
<CheckIcon className="size-3.5" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
)
}
export { Checkbox }

View File

@ -9,14 +9,15 @@
"lint": "next lint"
},
"dependencies": {
"lucide-react": "^0.503.0",
"next": "15.3.1",
"@radix-ui/react-avatar": "^1.1.7",
"@radix-ui/react-checkbox": "^1.2.3",
"@radix-ui/react-slot": "^1.2.0",
"@radix-ui/react-tabs": "^1.1.9",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"iconsax-react": "^0.0.8",
"lucide-react": "^0.503.0",
"next": "15.3.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"tailwind-merge": "^3.2.0"

View File

@ -11,6 +11,9 @@ importers:
'@radix-ui/react-avatar':
specifier: ^1.1.7
version: 1.1.7(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@radix-ui/react-checkbox':
specifier: ^1.2.3
version: 1.2.3(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@radix-ui/react-slot':
specifier: ^1.2.0
version: 1.2.0(@types/react@19.1.2)(react@19.1.0)
@ -345,6 +348,19 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-checkbox@1.2.3':
resolution: {integrity: sha512-pHVzDYsnaDmBlAuwim45y3soIN8H4R7KbkSVirGhXO+R/kO2OLCe0eucUEbddaTcdMHHdzcIGHtZSMSQlA+apw==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-collection@1.1.4':
resolution: {integrity: sha512-cv4vSf7HttqXilDnAnvINd53OTl1/bjUYVZrkFnA7nwmY9Ob2POUy0WY0sfqBAe1s5FyKsyceQlqiEGPYNTadg==}
peerDependencies:
@ -500,6 +516,24 @@ packages:
'@types/react':
optional: true
'@radix-ui/react-use-previous@1.1.1':
resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-use-size@1.1.1':
resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@rtsao/scc@1.1.0':
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
@ -2160,6 +2194,22 @@ snapshots:
'@types/react': 19.1.2
'@types/react-dom': 19.1.2(@types/react@19.1.2)
'@radix-ui/react-checkbox@1.2.3(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
dependencies:
'@radix-ui/primitive': 1.1.2
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.2)(react@19.1.0)
'@radix-ui/react-context': 1.1.2(@types/react@19.1.2)(react@19.1.0)
'@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.2)(react@19.1.0)
'@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.2)(react@19.1.0)
'@radix-ui/react-use-size': 1.1.1(@types/react@19.1.2)(react@19.1.0)
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
optionalDependencies:
'@types/react': 19.1.2
'@types/react-dom': 19.1.2(@types/react@19.1.2)
'@radix-ui/react-collection@1.1.4(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.2)(react@19.1.0)
@ -2290,6 +2340,19 @@ snapshots:
optionalDependencies:
'@types/react': 19.1.2
'@radix-ui/react-use-previous@1.1.1(@types/react@19.1.2)(react@19.1.0)':
dependencies:
react: 19.1.0
optionalDependencies:
'@types/react': 19.1.2
'@radix-ui/react-use-size@1.1.1(@types/react@19.1.2)(react@19.1.0)':
dependencies:
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.2)(react@19.1.0)
react: 19.1.0
optionalDependencies:
'@types/react': 19.1.2
'@rtsao/scc@1.1.0': {}
'@rushstack/eslint-patch@1.11.0': {}

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

BIN
public/avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 KiB

BIN
public/brush.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
public/camera.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
public/create.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
public/marketplacelogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
public/paint.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
public/play.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 B

BIN
public/text.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
public/titlelogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
public/v1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
public/v10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
public/v11.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
public/v12.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

BIN
public/v13.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

BIN
public/v14.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

BIN
public/v15.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
public/v2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
public/v3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
public/v4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
public/v5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
public/v6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
public/v7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
public/v8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
public/v9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB