Init
41
.gitignore
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/versions
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
36
README.md
Normal file
@ -0,0 +1,36 @@
|
||||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||
24
app/creator/(home)/layout.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import SideNav from "@/components/custom/Side_Nav";
|
||||
import Navbar from "@/components/custom/Nav_bar";
|
||||
|
||||
export default function CreatorLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<div className="flex min-h-screen">
|
||||
<div className="fixed left-0 top-0 h-screen">
|
||||
<SideNav />
|
||||
</div>
|
||||
<div className="flex flex-col w-full ml-[380px]">
|
||||
<div className="fixed top-0 right-0 left-[380px] z-10">
|
||||
<Navbar />
|
||||
</div>
|
||||
<main className="flex-1 p-6 bg-[#F3F3F3] mt-[75px]">{children}</main>
|
||||
</div>
|
||||
</div>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
17
app/creator/(home)/page.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { Home_Banner } from "@/components/Home_Banner";
|
||||
import Recent_Creation from "@/components/Recent_Creation";
|
||||
|
||||
|
||||
const CreatorPage: React.FC = () => {
|
||||
return (
|
||||
<div className="w-full h-[80vh] flex flex-col items-center justify-start bg-[#F3F3F3]">
|
||||
<Home_Banner />
|
||||
<Recent_Creation />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
export default CreatorPage;
|
||||
232
app/creator/reader/page.tsx
Normal file
@ -0,0 +1,232 @@
|
||||
"use client"
|
||||
|
||||
import { ArrowLeft2, Setting2 } from 'iconsax-react';
|
||||
import Image from 'next/image';
|
||||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import Link from 'next/link';
|
||||
import HoverCards from '@/components/cards/HoverCards';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
|
||||
interface Page {
|
||||
id: number;
|
||||
content: React.ReactNode;
|
||||
videoSrc: string;
|
||||
}
|
||||
|
||||
export default function Reader() {
|
||||
const [currentPageIndex, setCurrentPageIndex] = useState(0);
|
||||
const [transitioning, setTransitioning] = useState(false);
|
||||
const videoRefs = useRef<(HTMLVideoElement | null)[]>([]);
|
||||
|
||||
// Add wheel event handler
|
||||
const handleWheel = useCallback((event: WheelEvent) => {
|
||||
if (transitioning) return;
|
||||
|
||||
// Scroll down
|
||||
if (event.deltaY > 0) {
|
||||
handleNextPage();
|
||||
}
|
||||
// Scroll up
|
||||
else if (event.deltaY < 0) {
|
||||
handlePreviousPage();
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Add previous page handler
|
||||
const handlePreviousPage = () => {
|
||||
if (transitioning) return;
|
||||
|
||||
setTransitioning(true);
|
||||
|
||||
if (currentPageIndex > 0) {
|
||||
setCurrentPageIndex(prev => prev - 1);
|
||||
} else {
|
||||
setCurrentPageIndex(pages.length - 1);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
setTransitioning(false);
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
// Add useEffect for wheel event listener
|
||||
useEffect(() => {
|
||||
window.addEventListener('wheel', handleWheel);
|
||||
return () => {
|
||||
window.removeEventListener('wheel', handleWheel);
|
||||
};
|
||||
}, [currentPageIndex, transitioning, handleWheel]); // Add dependencies
|
||||
|
||||
// Content structured to match your design
|
||||
const pages: Page[] = [
|
||||
{
|
||||
id: 1,
|
||||
videoSrc: "/videos/background1.mp4",
|
||||
content: (
|
||||
<>
|
||||
<h1 className="text-4xl font-bold mb-8 text-center">BRUTAL</h1>
|
||||
<div className="space-y-6 text-center max-w-3xl">
|
||||
<p className="text-lg">Through the rain, flickering neon lights spell out of <HoverCards triggerText="SEPHORA" videourl="/videos/usb.mp4" description="Bloomberg, COMEX, Dubai Gold & Commodities Exchange, ICE Benchmark Administration, London Metal Exchange, Multi Commodity Exchange of India, Nasdaq, Shanghai Gold Exchange, Shanghai Futures Exchange, Tokyo Commodities Exchange, World Gold Council;" link=' https://www.gold.org/goldhub/data/gold-trading' /> and illuminate an entrance to nightclub.</p>
|
||||
<p className="text-lg">A stunning light show cascades across a dance floor crowded by partiers and adorned by dozens of video monitors.</p>
|
||||
<p className="text-lg">WADE HARPER, an anxious businessman dressed in a black suit, follows two burly bouncers up a flight of stairs toward the <HoverCards triggerText="VIP Suite" videourl="/videos/background2.mp4" description='"Man, yes! Didn’t I tell you not to question this man! I knew he was going to come through for us!," Handsome Twin #1 gloats. Handsome Twin #2 sighs in satisfaction. “Gold!,” he says, his tense demeanor turning to relief. ' /> at the back of the warehouse.</p>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
videoSrc: "/videos/background2.mp4",
|
||||
content: (
|
||||
<>
|
||||
<h1 className="text-4xl font-bold mb-8 text-center">BRUTAL</h1>
|
||||
<div className="space-y-6 text-center max-w-3xl">
|
||||
<p className="text-lg">"Wade Harper! What is up, old friend! It's been too long, man!" exclaims HANDSOME TWIN #1.</p>
|
||||
<p className="text-lg">HANDSOME TWIN #2, more anxious and pushy, quickly interjects, "So do you have it for us?"</p>
|
||||
<p className="text-lg">Wade reaches into his breast pocket.</p>
|
||||
<p className="text-lg">"Yes, I do."</p>
|
||||
<p className="text-lg">Wade considers the <HoverCards triggerText="USB drive" videourl="/videos/usb.mp4" description="The USB drive Wade carries holds classified footage from a secret government surveillance project called Project Echo, which monitored paranormal activities around an abandoned research facility in Nevada." /> in his hand and fiddles with the device. The twins smile widely with delight.</p>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
videoSrc: "/videos/background3.mp4",
|
||||
content: (
|
||||
<>
|
||||
<h1 className="text-4xl font-bold mb-8 text-center">BRUTAL</h1>
|
||||
<div className="space-y-6 text-center max-w-3xl">
|
||||
<p className="text-lg">"Man, yes! Didn't I tell you not to question this man! I knew he was going to come through for us!" Handsome Twin #1 gloats.</p>
|
||||
<p className="text-lg">Handsome Twin #2 sighs in satisfaction. "<HoverCards triggerText="Gold" videourl="/videos/trend.mp4" description="Bloomberg, COMEX, Dubai Gold & Commodities Exchange, ICE Benchmark Administration, London Metal Exchange, Multi Commodity Exchange of India, Nasdaq, Shanghai Gold Exchange, Shanghai Futures Exchange, Tokyo Commodities Exchange, World Gold Council;" link='https://www.gold.org/goldhub/data/gold-trading' />," he says, his tense demeanor turning to relief.</p>
|
||||
<p className="text-lg">Wade hands the device to Handsome Twin #2.</p>
|
||||
<p className="text-lg">"You will find all of the credentials you need on the drive. The shipment will arrive at the <HoverCards triggerText="Port of Dreytown" videourl="/videos/man.mp4"
|
||||
description="A young, sobbing visitor sat unusually close to the pulpit in the empty church, catching Pastor Evan’s attention.
|
||||
Typically, even regular members avoided those front pews, out of reverence, fear, or habit.
|
||||
But this man seemed untouched by such conventions, and that stood out to the pastor..." link='' /> tomorrow night,” Wade explains.</p>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
// Add this function to validate video sources
|
||||
const isValidVideoSrc = (src: string): boolean => {
|
||||
return Boolean(src && src.length > 0);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Start playing the current video when the page changes
|
||||
if (videoRefs.current[currentPageIndex]) {
|
||||
videoRefs.current.forEach((video, index) => {
|
||||
if (index === currentPageIndex && video) {
|
||||
video.currentTime = 0;
|
||||
video.play().catch(err => console.error("Error playing video:", err));
|
||||
} else if (video) {
|
||||
video.pause();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [currentPageIndex]);
|
||||
|
||||
const handleNextPage = () => {
|
||||
if (transitioning) return;
|
||||
|
||||
setTransitioning(true);
|
||||
|
||||
if (currentPageIndex < pages.length - 1) {
|
||||
setCurrentPageIndex(prev => prev + 1);
|
||||
} else {
|
||||
setCurrentPageIndex(0);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
setTransitioning(false);
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-screen overflow-hidden relative bg-black">
|
||||
{/* NavBar */}
|
||||
<div className='w-full h-[80px] fixed top-0 z-30 flex items-center justify-between px-6 bg-transparent'>
|
||||
{/* Logo */}
|
||||
|
||||
|
||||
<div className="flex items-center text-white">
|
||||
<Link href='/creator' className='mr-4 '>
|
||||
<Button size="icon" className="bg-white">
|
||||
<ArrowLeft2 size="24" color="#555555" />
|
||||
</Button>
|
||||
</Link>
|
||||
<Image src="/images/logo2.png" alt="Wodey" width={60} height={60} className='mr-2' />
|
||||
<span className="text-xl font-semibold">Wodey</span>
|
||||
</div>
|
||||
|
||||
{/* Brutal Logo - Center */}
|
||||
<div className="absolute left-1/2 transform -translate-x-1/2">
|
||||
<Image src="/images/brutal.png" alt="Wodey" width={91} height={55} className='mr-2' />
|
||||
</div>
|
||||
|
||||
{/* Settings */}
|
||||
<button className='flex items-center text-white'>
|
||||
<Setting2 size={20} className="mr-2" color='#ffffff' />
|
||||
<span>Settings</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Video Sections */}
|
||||
<div className="relative h-full">
|
||||
{pages.map((page, index) => (
|
||||
<section
|
||||
key={page.id}
|
||||
className={`absolute w-full h-full transition-opacity duration-1000 ${currentPageIndex === index ? 'opacity-100 z-10' : 'opacity-0 z-0'
|
||||
}`}
|
||||
>
|
||||
{/* Background Video */}
|
||||
<video
|
||||
ref={(el: HTMLVideoElement | null) => { videoRefs.current[index] = el }}
|
||||
className="absolute top-0 left-0 w-full h-full object-cover"
|
||||
muted
|
||||
loop
|
||||
playsInline
|
||||
src={isValidVideoSrc(page.videoSrc) ? page.videoSrc : undefined}
|
||||
poster="/images/fallback-background.png" // Add a fallback image
|
||||
onError={(e) => {
|
||||
console.warn(`Failed to load video: ${page.videoSrc}`);
|
||||
// Optionally set a fallback background color or image
|
||||
e.currentTarget.style.backgroundColor = '#000000';
|
||||
}}
|
||||
></video>
|
||||
|
||||
{/* Dark Overlay */}
|
||||
<div className="absolute inset-0 bg-black opacity-60"></div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="absolute inset-0 flex items-center justify-center text-white z-10 px-5">
|
||||
<div className="mt-16 max-w-4xl">
|
||||
{page.content}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Navigation Button - Down Arrow */}
|
||||
<button
|
||||
onClick={handleNextPage}
|
||||
disabled={transitioning}
|
||||
className="fixed bottom-8 left-1/2 transform -translate-x-1/2 z-30 bg-transparent text-white rounded-full w-12 h-12 flex items-center justify-center transition-opacity duration-300 hover:opacity-70"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-8 w-8"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
118
app/creator/studio/layout.tsx
Normal file
@ -0,0 +1,118 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Add, ArrowDown2, Book1, Eye, Layer, Link2, Message, PlayCircle, Pointer, SearchNormal1 } from 'iconsax-react';
|
||||
import { Download, PointerIcon, Redo2, Share2, Square, Undo2 } from 'lucide-react';
|
||||
import type { Metadata } from 'next';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import React from 'react';
|
||||
import Sidebar from '../../../components/common/SideBar';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Studio',
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
function layout({ children }: Props) {
|
||||
return (
|
||||
<div className="flex flex-col h-screen w-full overflow-x-hidden">
|
||||
|
||||
{/* NavBar */}
|
||||
<div className='w-full h-[80px] top-0 z-10 flex items-center align-middle bg-white shadow-md justify-between px-3 md:px-5'>
|
||||
<div className='w-full md:w-[60%] flex flex-wrap md:flex-nowrap items-center align-middle justify-between gap-2'>
|
||||
<Image src="/images/logo.png" alt="logo" width={100} height={100} className='w-[100px] md:w-[116px] h-[45px] md:h-[53px]' />
|
||||
<div className='hidden md:flex items-center gap-4'>
|
||||
<Link href="#" className='text-black hover:text-gray-600'>Home</Link>
|
||||
<Link href="#" className='text-black hover:text-gray-600'>View</Link>
|
||||
<Link href="#" className='text-black hover:text-gray-600'>Help</Link>
|
||||
|
||||
<p className='text-gray-700 text-sm'>Last saved 5 seconds ago</p>
|
||||
</div>
|
||||
<div className='hidden md:flex items-center gap-4'>
|
||||
<Pointer size="20" color="#555555" className='cursor-pointer hover:opacity-70' />
|
||||
<PointerIcon size="20" color="#555555" className='cursor-pointer hover:opacity-70' />
|
||||
<div className="flex items-center gap-1 cursor-pointer">
|
||||
<p>100%</p>
|
||||
<ArrowDown2 size="20" color="#555555" />
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Undo2 size="20" color="#555555" className='cursor-pointer hover:opacity-70' />
|
||||
<Redo2 size="20" color="#555555" className='cursor-pointer hover:opacity-70' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2 md:gap-3 items-center align-middle'>
|
||||
<Button variant='outline' className='h-8 w-8 md:h-10 md:w-10 p-1 md:p-2' aria-label="Color picker">
|
||||
<div className="h-4 w-4 md:h-5 md:w-5 bg-black rounded-full"></div>
|
||||
</Button>
|
||||
<Button variant='outline' className='h-8 w-8 md:h-10 md:w-10 p-1 md:p-2' aria-label="Download">
|
||||
<Download size="18" color="#555555" />
|
||||
</Button>
|
||||
<a href="/creator/reader">
|
||||
<Button variant='outline' className='h-8 md:h-10 p-1 md:p-2 flex gap-1 md:gap-2' aria-label="Preview">
|
||||
<Eye size="18" color="#555555" />
|
||||
<p className="hidden md:block">Preview</p>
|
||||
</Button>
|
||||
</a>
|
||||
<Button className='h-8 md:h-10 p-1 md:p-2 flex gap-1 md:gap-2 bg-[#1A237E]' aria-label="Share">
|
||||
<Share2 size="18" color="#ffffff" />
|
||||
<p className="hidden md:block">Share</p>
|
||||
</Button>
|
||||
|
||||
<div className='flex relative'>
|
||||
<Image src="/images/profile.jpeg" alt="profile" width={40} height={40} className='w-[35px] h-[35px] md:w-[40px] md:h-[40px] rounded-full z-10' />
|
||||
<Button variant='outline' className='absolute right-0 left-5 md:left-7 h-8 w-8 md:h-10 md:w-10 p-1 md:p-2 rounded-full' aria-label="Add user">
|
||||
<Add size="18" color="#555555" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* End of NavBar */}
|
||||
|
||||
{/* Main Content with Sidebar */}
|
||||
<div className='flex flex-col md:flex-row w-full h-[calc(100vh-80px)]'>
|
||||
{/* Sidebar - Hidden on mobile by default, can be toggled */}
|
||||
<div className="md:block">
|
||||
<Sidebar />
|
||||
</div>
|
||||
<div className='w-full md:w-[80%] h-full bg-white'>
|
||||
{/* Inner NavBar */}
|
||||
<div className='w-full h-[59px] flex items-center align-middle bg-white shadow-md gap-3 md:gap-6 px-3 md:px-5 border-t-2 border'>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<p>Pages 1</p>
|
||||
<p className='text-xs'>794 * 1123 px</p>
|
||||
</div>
|
||||
<div className='flex gap-2 items-center align-middle h-[58px]'>
|
||||
<p>Background</p>
|
||||
<div className="bg-gradient-to-r from-[#FCDFA3] to-[#9E8EB7] w-[22px] h-[22px] rounded-sm">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* End of Inner NavBar */}
|
||||
|
||||
{/* Main Editor */}
|
||||
<div className='flex w-full h-[calc(100vh-139px)]'>
|
||||
<div className="flex justify-center align-center items-center w-[calc(100%-70px)] p-4 md:p-10 bg-[#D9D7D7] overflow-y-auto no-scrollbar">
|
||||
{children}
|
||||
</div>
|
||||
{/* Inner Editor SideBar */}
|
||||
<div className='w-[70px] p-1 md:p-2 bg-white gap-1 flex flex-col items-center align-middle px-2 md:px-5 border-t-2 border overflow-y-auto no-scrollbar'>
|
||||
<Book1 size={24} color='#555555' className="cursor-pointer hover:opacity-70 my-2" aria-label="Book"/>
|
||||
<Layer size={24} color='#555555' className="cursor-pointer hover:opacity-70 my-2" aria-label="Layers"/>
|
||||
<SearchNormal1 size={24} color='#555555' className="cursor-pointer hover:opacity-70 my-2" aria-label="Search"/>
|
||||
<Square size={24} color='#555555' className="cursor-pointer hover:opacity-70 my-2" aria-label="Square"/>
|
||||
<Link2 size={24} color='#555555' className="cursor-pointer hover:opacity-70 my-2" aria-label="Link"/>
|
||||
<PlayCircle size={24} color='#555555' className="cursor-pointer hover:opacity-70 my-2" aria-label="Play"/>
|
||||
<Message size={24} color='#555555' className="cursor-pointer hover:opacity-70 my-2" aria-label="Message"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default layout
|
||||
7
app/creator/studio/page.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
import PlayGround from "@/components/custom/Play_Ground";
|
||||
|
||||
export default function PlayGroundPage() {
|
||||
return (
|
||||
<PlayGround />
|
||||
);
|
||||
}
|
||||
77
app/creator/trash/page.tsx
Normal file
@ -0,0 +1,77 @@
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||
|
||||
import TrashCards, { Trash } from "@/components/cards/TrashCards";
|
||||
import EmptyRecords from "@/components/common/EmptyRecords";
|
||||
|
||||
const TrashPage = () => {
|
||||
const trashData: Trash[] = [
|
||||
{
|
||||
file: "/images/image1.png",
|
||||
description: "Elephants drinking water",
|
||||
filetype: "image",
|
||||
filesize: "74MB"
|
||||
},
|
||||
{
|
||||
file: "/images/image2.png",
|
||||
description: "wonderful sunrise in an aug...",
|
||||
filetype: "image",
|
||||
filesize: "7MB"
|
||||
},
|
||||
{
|
||||
file: "/images/image3.png",
|
||||
description: "Beautiful selective focus sh...",
|
||||
filetype: "image",
|
||||
filesize: "23MB"
|
||||
},
|
||||
{
|
||||
file: "/images/image4.png",
|
||||
description: "Urban double exposure port...",
|
||||
filetype: "image",
|
||||
filesize: "18MB"
|
||||
},
|
||||
{
|
||||
file: "/images/image5.png",
|
||||
description: "Closeup shot of a beautiful...",
|
||||
filetype: "image",
|
||||
filesize: "56KB"
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="w-full h-screen flex flex-col p-10 bg-[#F3F3F3]">
|
||||
<Tabs defaultValue="ebooks" className="w-full">
|
||||
<TabsList className="w-full md:w-fit bg-transparent">
|
||||
<TabsTrigger value="ebooks" className="border-b-2 border-transparent data-[state=active]:border-b-primary rounded-none data-[state=active]:shadow-none data-[state=active]:bg-transparent">Ebooks</TabsTrigger>
|
||||
<TabsTrigger value="images" className="border-b-2 border-transparent data-[state=active]:border-b-primary rounded-none data-[state=active]:shadow-none data-[state=active]:bg-transparent">Images</TabsTrigger>
|
||||
<TabsTrigger value="videos" className="border-b-2 border-transparent data-[state=active]:border-b-primary rounded-none data-[state=active]:shadow-none data-[state=active]:bg-transparent">Videos</TabsTrigger>
|
||||
<TabsTrigger value="audios" className="border-b-2 border-transparent data-[state=active]:border-b-primary rounded-none data-[state=active]:shadow-none data-[state=active]:bg-transparent">Audios</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="ebooks">
|
||||
<EmptyRecords image="/images/noEbook.png" longDescription="Any ebooks you trashed will end up here. You’ll have 30 days to restore them before they’re automatically deleted from your Trash." shortDescription="There’s nothing in your Trash" />
|
||||
</TabsContent>
|
||||
<TabsContent value="images">
|
||||
<div className="flex-col flex gap-4 mt-4">
|
||||
<div className="flex items-center bg-gray-200/20 p-2 rounded-md ">
|
||||
<p className="text-sm font-medium">Any images you’ve trashed can be found here. Please note that the images deleted from the Trash won’t be removed from the existing ebooks. The images will only be deleted if those ebooks are deleted.</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap md:flex-nowrap gap-4 justify-center md:justify-start">
|
||||
{trashData.map((data, index) => (
|
||||
<TrashCards key={index} {...data} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</TabsContent>
|
||||
<TabsContent value="videos">
|
||||
<EmptyRecords image="/images/noVideo.png" longDescription="Any videos you trashed will end up here. You’ll have 30 days to restore them before they’re automatically deleted from your Trash." shortDescription="There’s nothing in your Trash" />
|
||||
</TabsContent>
|
||||
<TabsContent value="audios">
|
||||
<EmptyRecords image="/images/noAudio.png" longDescription="Any audios you trashed will end up here. You’ll have 30 days to restore them before they’re automatically deleted from your Trash." shortDescription="There’s nothing in your Trash" />
|
||||
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default TrashPage;
|
||||
BIN
app/favicon.ico
Normal file
|
After Width: | Height: | Size: 25 KiB |
296
app/globals.css
Normal file
@ -0,0 +1,296 @@
|
||||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
--color-sideNavColor:#010313;
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-ring: var(--ring);
|
||||
--color-input: var(--input);
|
||||
--color-border: var(--border);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-card: var(--card);
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
}
|
||||
|
||||
:root {
|
||||
--radius: 0.625rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.145 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0.205 0 0);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.97 0 0);
|
||||
--secondary-foreground: oklch(0.205 0 0);
|
||||
--muted: oklch(0.97 0 0);
|
||||
--muted-foreground: oklch(0.556 0 0);
|
||||
--accent: oklch(0.97 0 0);
|
||||
--accent-foreground: oklch(0.205 0 0);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.922 0 0);
|
||||
--input: oklch(0.922 0 0);
|
||||
--ring: oklch(0.708 0 0);
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
--chart-2: oklch(0.6 0.118 184.704);
|
||||
--chart-3: oklch(0.398 0.07 227.392);
|
||||
--chart-4: oklch(0.828 0.189 84.429);
|
||||
--chart-5: oklch(0.769 0.188 70.08);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.145 0 0);
|
||||
--sidebar-primary: oklch(0.205 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.97 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||
--sidebar-border: oklch(0.922 0 0);
|
||||
--sidebar-ring: oklch(0.708 0 0);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.205 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.922 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.556 0 0);
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.269 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.556 0 0);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.hero_img {
|
||||
background-color: #010313D9;
|
||||
background-image: url("/images/89f1cacd4041e59eb162ffcb0f8080dc179fe415.png");
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
background-blend-mode: multiply;
|
||||
height: 170px;
|
||||
width: 1045px;
|
||||
border-radius: 10px;
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-left: 40px;
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
.hero_text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
color: #ffffff;
|
||||
width: 476px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.hero_text h3 {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
line-height: 30px;
|
||||
font-family: sans-serif;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.hero_text p {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 21px;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.hero_img {
|
||||
background-color: #010313D9;
|
||||
background-image: url("/images/89f1cacd4041e59eb162ffcb0f8080dc179fe415.png");
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
background-blend-mode: multiply;
|
||||
height: 170px;
|
||||
width: 100%;
|
||||
border-radius: 10px;
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-left: 40px;
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
.hero_text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
color: #ffffff;
|
||||
width: 476px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.hero_text h3 {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
line-height: 30px;
|
||||
font-family: sans-serif;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.hero_text p {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 21px;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.hero_text button {
|
||||
background-color: #ffffff;
|
||||
color: #010313;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
width: 146.04px;
|
||||
padding: 5px 10px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero_cards {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.card_1 {
|
||||
background-image: url("/images/37934e37222a44601017b84963a414627d8e095f.png");
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
width: 91px;
|
||||
height: 130px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.card_2 {
|
||||
background-image: url("/images/230f1945d640ae4c0325f23dcb3365b59ae08277.png");
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
width: 91px;
|
||||
height: 130px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.card_3 {
|
||||
background-image: url("/images/baca21cebac9b0ae0463e371575f760ea5e79016.png");
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
width: 91px;
|
||||
height: 130px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.recent_container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 237px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.recent_title {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.recent_icon {
|
||||
cursor: pointer;
|
||||
border: 1px solid #868585;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.recent_card {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
background-color: #ffffff;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.card_img {
|
||||
width: 145px;
|
||||
height: 110px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
|
||||
/* custom css */
|
||||
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
34
app/layout.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
87
app/marketplace/advertisers/page.tsx
Normal file
@ -0,0 +1,87 @@
|
||||
"use client";
|
||||
import Image from "next/image";
|
||||
import React, { useState } from "react";
|
||||
|
||||
const categories = [
|
||||
"All advertising",
|
||||
"Commissioned",
|
||||
"Discount Ads",
|
||||
"Brands Ads",
|
||||
"Ebooks",
|
||||
"Author ads",
|
||||
"Artist Ads",
|
||||
"Last Month ads",
|
||||
"Upcoming ads",
|
||||
];
|
||||
|
||||
const images = [
|
||||
"/images/Airbnb_1.png",
|
||||
"/images/Airbnb_2.png",
|
||||
"/images/Airbnb_3.png",
|
||||
"/images/Amazon_AWS.png",
|
||||
"/images/Amazon_echo.png",
|
||||
"/images/Element.png",
|
||||
"/images/Huawei.png",
|
||||
"/images/Netflix.png",
|
||||
"/images/Qonto.png",
|
||||
"/images/Samsung.png",
|
||||
];
|
||||
|
||||
const Page: React.FC = () => {
|
||||
const [selectedCategory, setSelectedCategory] = useState<string>(
|
||||
categories[0]
|
||||
);
|
||||
const handleCategoryClick = (category: string) => {
|
||||
setSelectedCategory(category);
|
||||
};
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col py-4 items-center justify-start bg-white">
|
||||
<div className="w-[1280px] h-[282px] bg-slate-400 rounded-md">
|
||||
<Image
|
||||
src="/images/Banner.png"
|
||||
alt="advertizers"
|
||||
width={1280}
|
||||
height={282}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full px-4 bg-[#F3F3F3] mt-6 p-4 flex justify-center items-center">
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{categories.map((category) => (
|
||||
<button
|
||||
key={category}
|
||||
className={`px-5 py-2 rounded-md cursor-pointer duration-300 ${
|
||||
selectedCategory === category
|
||||
? "bg-black text-white"
|
||||
: "bg-white text-black"
|
||||
}`}
|
||||
onClick={() => handleCategoryClick(category)}
|
||||
>
|
||||
{category}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-[1280px] m-6 grid sm:grid-cols-1 md:grid-cols-5 gap-2">
|
||||
{images.map((image, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex w-full h-full flex-col items-center justify-start rounded-md"
|
||||
>
|
||||
<Image
|
||||
key={index}
|
||||
src={image}
|
||||
alt="advertizers"
|
||||
width={1290}
|
||||
height={280}
|
||||
/>
|
||||
<button className="px-15 py-1 border border-slate-800 rounded cursor-pointer">
|
||||
List in Ebook
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
181
app/marketplace/artists/_components/Artist_Content.tsx
Normal file
@ -0,0 +1,181 @@
|
||||
import { Heart } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
76
app/marketplace/artists/_components/Quick_Actions.tsx
Normal 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">
|
||||
<Image 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>
|
||||
);
|
||||
}
|
||||
25
app/marketplace/artists/_components/SearchBar.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
16
app/marketplace/artists/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
10
app/marketplace/layout.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
12
app/page.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import MarketplaceNavbar from "@/components/custom/marketplace_Navbar";
|
||||
import HomeBanner from "@/components/custom/Home_Banner";
|
||||
import BooksFrame from "@/components/custom/books_Frame";
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
<MarketplaceNavbar />
|
||||
<HomeBanner />
|
||||
<BooksFrame />
|
||||
</>
|
||||
);
|
||||
}
|
||||
21
components.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "new-york",
|
||||
"rsc": true,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "",
|
||||
"css": "app/globals.css",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils",
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/lib",
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"iconLibrary": "lucide"
|
||||
}
|
||||
21
components/Home_Banner.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import React from "react";
|
||||
|
||||
export const Home_Banner: React.FC = () => {
|
||||
return (
|
||||
<div className="hero_img">
|
||||
<div className="hero_text">
|
||||
<h3>Bring Your Story to Life</h3>
|
||||
<p>
|
||||
Start creating your own ebook today — share your voice, inspire
|
||||
readers, and publish to the world in just a few clicks.
|
||||
</p>
|
||||
<button>Create an ebook</button>
|
||||
</div>
|
||||
<div className="hero_cards">
|
||||
<div className="card_1"></div>
|
||||
<div className="card_2"></div>
|
||||
<div className="card_3"></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
31
components/Recent_Card.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
import Image from "next/image";
|
||||
import React from "react";
|
||||
|
||||
interface RecentCardProps {
|
||||
image: string;
|
||||
title: string;
|
||||
tag: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const Recent_Card: React.FC<RecentCardProps> = ({ image, title, tag, description }) => {
|
||||
return (
|
||||
<div className="flex flex-col justify-start items-start">
|
||||
<div className="recent_card flex justify-center items-center">
|
||||
<Image
|
||||
src={image}
|
||||
alt="image"
|
||||
className="card_img"
|
||||
width={100}
|
||||
height={100}
|
||||
/>
|
||||
</div>
|
||||
<h3 className="text-[14px] font-[400] text-slate-900">{title}</h3>
|
||||
<p className="text-[10px] font-[400] text-slate-400">
|
||||
{tag} <span> {description}</span>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Recent_Card;
|
||||
66
components/Recent_Creation.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
import React from "react";
|
||||
import { FaSlidersH } from "react-icons/fa";
|
||||
import Recent_Card from "./Recent_Card";
|
||||
import {
|
||||
ContextMenu,
|
||||
ContextMenuContent,
|
||||
ContextMenuItem,
|
||||
ContextMenuTrigger,
|
||||
} from "@/components/ui/context-menu"
|
||||
import Link from "next/link";
|
||||
|
||||
|
||||
|
||||
const Recent_Creation: React.FC = () => {
|
||||
return (
|
||||
<div className="recent_container">
|
||||
<div className="recent_title">
|
||||
<h3 className="text-[16px] font-[700]">Recent creations</h3>
|
||||
<FaSlidersH className="text-[20px] recent_icon" />
|
||||
</div>
|
||||
<div className="grid sm:grid-cols-2 md:grid-cols-5 gap-4 mt-2">
|
||||
<ContextMenu>
|
||||
<ContextMenuTrigger asChild>
|
||||
<Recent_Card
|
||||
image="/images/aa31529b95af9b43380b88b11692b0a6f7999878.png"
|
||||
title="Good morning Gabe Hager!"
|
||||
tag="Ebook"
|
||||
description="Edited 13 mins ago"
|
||||
/>
|
||||
</ContextMenuTrigger>
|
||||
<ContextMenuContent>
|
||||
<ContextMenuItem><Link href="/creator/reader">View as Reader</Link></ContextMenuItem>
|
||||
<ContextMenuItem><Link href="/creator/studio">Continue editing</Link></ContextMenuItem>
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
|
||||
<Recent_Card
|
||||
image="/images/d5d8f9fe19a7aed7bf6545a64634eeb37a6b895a.png"
|
||||
title="Story of my life (Story by Lak..."
|
||||
tag="Ebook"
|
||||
description="Edited 5 hours ago"
|
||||
/>
|
||||
<Recent_Card
|
||||
image="/images/d3cf3b09c1fd3dc0d6a997a7a479337fdf8caa69.png"
|
||||
title="Good morning Gabe Hager!"
|
||||
tag="Ebook"
|
||||
description="Edited 10 mins ago"
|
||||
/>
|
||||
<Recent_Card
|
||||
image="/images/96c1b745b59fe1512c73f653d7b5e7be3ee54e58.png"
|
||||
title="A fantastic saga, the super..."
|
||||
tag="Ebook"
|
||||
description="Edited 1 month ago"
|
||||
/>
|
||||
<Recent_Card
|
||||
image="/images/292c2c8f2ea3276c44dc6ade84e687b9cae3d267.png"
|
||||
title="Good morning Gabe Hager!"
|
||||
tag="Ebook"
|
||||
description="Edited 20 hours ago"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Recent_Creation;
|
||||
60
components/cards/HoverCards.tsx
Normal file
@ -0,0 +1,60 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
import {
|
||||
HoverCard,
|
||||
HoverCardContent,
|
||||
HoverCardTrigger,
|
||||
} from "@/components/ui/hover-card"
|
||||
|
||||
export interface HoverCardsProps {
|
||||
triggerText: string
|
||||
videourl: string
|
||||
description: string
|
||||
linkText?: string
|
||||
link?: string
|
||||
}
|
||||
|
||||
export default function HoverCards({
|
||||
triggerText,
|
||||
videourl,
|
||||
description,
|
||||
link = '#',
|
||||
linkText = 'Purchase & Read More'
|
||||
}: HoverCardsProps) {
|
||||
return (
|
||||
<HoverCard>
|
||||
<HoverCardTrigger asChild>
|
||||
<p className="text-blue-400 cursor-pointer underline">{triggerText}</p>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent className="w-80">
|
||||
<div className="max-w-xs bg-white rounded-lg overflow-hidden shadow-lg">
|
||||
<div className="relative h-48 w-full">
|
||||
<video
|
||||
className="absolute top-0 left-0 w-full h-full object-cover"
|
||||
muted
|
||||
loop
|
||||
playsInline
|
||||
autoPlay
|
||||
src={videourl}
|
||||
></video>
|
||||
|
||||
</div>
|
||||
|
||||
<div className="p-4">
|
||||
<p className="text-gray-700 text-sm mb-4">
|
||||
{description}
|
||||
</p>
|
||||
|
||||
<Link href={link}>
|
||||
<span className="text-purple-700 font-medium text-sm hover:text-purple-900 transition-colors">
|
||||
{linkText}
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
)
|
||||
}
|
||||
28
components/cards/TrashCards.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import Image from "next/image";
|
||||
|
||||
|
||||
export interface Trash {
|
||||
file: string,
|
||||
description: string,
|
||||
filetype: string,
|
||||
filesize: string,
|
||||
}
|
||||
|
||||
const TrashCards = ({ file, description, filetype, filesize }: Trash) => {
|
||||
return (
|
||||
<div className="w-[191px] h-[195px] flex flex-col overflow-hidden rounded-sm bg-white shadow-sm hover:transform hover:scale-105 transition-transform duration-300 ease-in-out">
|
||||
<Image src={file} alt={description} width={191} height={150} className="w-[191px] h-[150px] rounded-sm object-cover min-h-[140px]" />
|
||||
<div className="flex flex-col gap-1 p-2">
|
||||
<p className="text-sm font-medium truncate">{description}</p>
|
||||
<div className="flex gap-1 items-center align-middle p-0">
|
||||
<p className="text-xs text-gray-500 ">{filetype}</p>{" "}
|
||||
.{" "}
|
||||
<p className="text-xs text-gray-500">{filesize}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
export default TrashCards;
|
||||
32
components/common/ActionButtons.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
'use client'
|
||||
|
||||
import { Button } from '@/components/ui/button'
|
||||
import React from 'react'
|
||||
|
||||
|
||||
export interface ActionButtonsProps {
|
||||
icon: React.ReactNode,
|
||||
label?: string,
|
||||
buttonWidth?: number,
|
||||
isGhost?: boolean,
|
||||
onClick?: () => void
|
||||
}
|
||||
|
||||
function ActionButtons({ icon, label, onClick, isGhost=false, buttonWidth}: ActionButtonsProps) {
|
||||
return (
|
||||
<div className={`flex flex-col gap-1 items-center align-middle cursor-pointer h-[50px]`} style={{ width: buttonWidth ? `${buttonWidth}px` : 'auto' }}>
|
||||
<Button
|
||||
onClick={onClick}
|
||||
className={`flex gap-2 items-center align-middle hover:bg-transparent w-full`}
|
||||
variant={isGhost ? 'ghost' : 'default'}
|
||||
aria-label={label || "Action button"}
|
||||
>
|
||||
{icon}
|
||||
</Button>
|
||||
{label && <p className={`text-xs text-white text-center w-full`}>{label}</p>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default ActionButtons
|
||||
20
components/common/EmptyRecords.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import Image from 'next/image'
|
||||
import React from 'react'
|
||||
|
||||
export interface EmptyRecordsProps {
|
||||
image: string,
|
||||
shortDescription: string,
|
||||
longDescription: string,
|
||||
}
|
||||
|
||||
export default function EmptyRecords({ image, shortDescription, longDescription }: EmptyRecordsProps) {
|
||||
return (
|
||||
<div className='h-[100%] p-10 w-full flex flex-col justify-center items-center gap-[10px]'>
|
||||
<Image src={image} alt={shortDescription} width={240} height={240} />
|
||||
<h5 className='text-2xl font-bold '>{shortDescription}</h5>
|
||||
<p className='text-gray-500 md:w-[550px] text-center w-fit'>
|
||||
{longDescription}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
209
components/common/SideBar.tsx
Normal file
@ -0,0 +1,209 @@
|
||||
'use client'
|
||||
import React from 'react';
|
||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
|
||||
import {
|
||||
Tag, Image as Images, Upload, TextSelect,
|
||||
ImageDownIcon,
|
||||
UploadIcon
|
||||
} from "lucide-react";
|
||||
import ActionButtons from "./ActionButtons";
|
||||
import {
|
||||
ArrowLeft2, ArrowRight2, AudioSquare, Cardano, Chart, CodeCircle,
|
||||
Element2, ExportSquare, Gift, Link, LinkSquare, Magicpen,
|
||||
MessageAdd1, MessageQuestion, Next, Note, Previous,
|
||||
SearchNormal1,
|
||||
Setting2, Setting4, Shapes, Smallcaps, Snapchat, Spotify,
|
||||
Sticker,
|
||||
TableDocument,
|
||||
Text, Video, VideoHorizontal, Whatsapp,
|
||||
} from 'iconsax-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import Image from "next/image";
|
||||
|
||||
const CategoryHeading = ({ title }: { title: string }) => (
|
||||
<div className="mb-2 mt-5 text-xs font-thin text-gray-100 ">
|
||||
{title}
|
||||
</div>
|
||||
);
|
||||
|
||||
const Sidebar = () => {
|
||||
|
||||
|
||||
return (
|
||||
<div className='w-full md:w-[376px] h-full bg-[#181923] flex sticky pr-2 md:pr-4'>
|
||||
<Tabs defaultValue="text" className="h-full w-full flex flex-row gap-1 md:gap-2">
|
||||
<TabsList className="pt-3 md:pt-5 w-[60px] md:w-[89px] p-0 bg-[#010313] h-full flex flex-col justify-between rounded-none overflow-y-auto overflow-x-hidden">
|
||||
<div className='flex flex-col gap-1 md:gap-2'>
|
||||
<TabsTrigger value="text" className="w-[45px] md:w-[50px] h-[45px] md:h-[50px] border-b-2 border-transparent data-[state=active]:border-b-white rounded-none data-[state=active]:shadow-none data-[state=active]:bg-transparent">
|
||||
<ActionButtons icon={<Smallcaps size="18" color="#ffffff" />} label="Text" isGhost={false} />
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="upload" className="w-[45px] md:w-[50px] h-[45px] md:h-[50px] border-b-2 border-transparent data-[state=active]:border-b-white rounded-none data-[state=active]:shadow-none data-[state=active]:bg-transparent">
|
||||
<ActionButtons icon={<Upload size="18" color="#ffffff" />} label="Upload" isGhost={true} />
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="elements" className="w-[45px] md:w-[50px] h-[45px] md:h-[50px] border-b-2 border-transparent data-[state=active]:border-b-white rounded-none data-[state=active]:shadow-none data-[state=active]:bg-transparent">
|
||||
<ActionButtons icon={<Shapes size="18" color="#ffffff" />} label="Elements" isGhost={true} />
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="interactions" className="w-[45px] md:w-[50px] h-[45px] md:h-[50px] border-b-2 border-transparent data-[state=active]:border-b-white rounded-none data-[state=active]:shadow-none data-[state=active]:bg-transparent">
|
||||
<ActionButtons icon={<TextSelect size="18" color="#ffffff" />} label="Interactions" isGhost={true} />
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="templates" className="w-[45px] md:w-[50px] h-[45px] md:h-[50px] border-b-2 border-transparent data-[state=active]:border-b-white rounded-none data-[state=active]:shadow-none data-[state=active]:bg-transparent">
|
||||
<ActionButtons icon={<Element2 size="18" color="#ffffff" />} label="Templates" isGhost={true} />
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="aiwrite" className="w-[45px] md:w-[50px] h-[45px] md:h-[50px] border-b-2 border-transparent data-[state=active]:border-b-white rounded-none data-[state=active]:shadow-none data-[state=active]:bg-transparent">
|
||||
<ActionButtons icon={<Magicpen size="18" color="#ffffff" />} label="Ai Write" isGhost={true} />
|
||||
</TabsTrigger>
|
||||
</div>
|
||||
<TabsTrigger value="settings" className="w-[45px] md:w-[50px] h-[45px] md:h-[50px] border-b-2 border-transparent data-[state=active]:border-b-white rounded-none data-[state=active]:shadow-none data-[state=active]:bg-transparent">
|
||||
<ActionButtons icon={<Setting2 size="18" color="#ffffff" />} label="Settings" isGhost={true} />
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="text" className='relative w-full h-full flex flex-col overflow-y-auto no-scrollbar p-2 md:p-4'>
|
||||
<CategoryHeading title="Click or drag text to add to ebook page" />
|
||||
|
||||
<div className='flex flex-col w-full flex-wrap gap-3 md:gap-5 justify-between mt-1'>
|
||||
<Button className='p-4 bg-[#FFFFFF14] text-2xl h-[48px]'>Add Heading</Button>
|
||||
<Button className='p-4 bg-[#FFFFFF14] text-xl h-[48px]'>Add Sub Heading</Button>
|
||||
<Button className='p-4 bg-[#FFFFFF14] text-lg font-thin h-[48px]'>Add Body Text</Button>
|
||||
<Button className='p-4 bg-[#FFFFFF14] text-xl h-[48px]'>#Pagination</Button>
|
||||
</div>
|
||||
<div className=' w-full flex justify-end'>
|
||||
<CategoryHeading title="Edit or add font styles" />
|
||||
</div>
|
||||
|
||||
<CategoryHeading title="Text Templates" />
|
||||
<div className='grid grid-cols-2 w-full flex-wrap gap-3 md:gap-5 justify-between'>
|
||||
{
|
||||
Array.from({ length: 4 }).map((_, index) => {
|
||||
return (
|
||||
<div key={index}>
|
||||
<Skeleton className="h-[174px] w-[115px] mt-2 shadow-sm"/>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="upload" className='relative w-full h-full flex flex-col gap-4 overflow-y-auto no-scrollbar p-2 md:p-4'>
|
||||
{/* search input box */}
|
||||
<div className='flex items-center justify-between px-3 h-[44px] w-full bg-white rounded-lg gap-2'>
|
||||
<SearchNormal1 size="18" color="#555555" className="shrink-0" />
|
||||
<Input
|
||||
placeholder='Search'
|
||||
className='h-[44px] flex-1 outline-none border-none focus:ring-0 focus-visible:ring-0 px-0'
|
||||
/>
|
||||
<Setting4 size="18" color="#555555" className="shrink-0 cursor-pointer hover:opacity-70" />
|
||||
</div>
|
||||
|
||||
<div className='flex mt-3'>
|
||||
<Button variant={"outline"} className='bg-[#00697733] text-white rounded-br-none rounded-tr-none'>My Uploads</Button>
|
||||
<Button variant={"outline"} className='bg-transparent text-white rounded-tl-none rounded-bl-none'>Team Uploads</Button>
|
||||
</div>
|
||||
<Button className='p-3 bg-[#006977] text-white rounded-lg w-full hover:bg-[#1d3235]'>
|
||||
<UploadIcon size="18" color="#ffffff" />
|
||||
Upload media
|
||||
</Button>
|
||||
<div className='flex flex-col gap-2 w-full items-center justify-center'>
|
||||
<p className='text-xl font-semibold text-white'>No uploads yet</p>
|
||||
|
||||
<Image src="/images/noUploads.png" alt="no uploads" width={104} height={104} className='w-[104px] h-[104px]'/>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="elements" className='relative w-full h-full flex flex-col overflow-y-auto no-scrollbar p-2 md:p-4'>
|
||||
<CategoryHeading title="Graphics" />
|
||||
<div className='grid grid-cols-3 w-full flex-wrap gap-3 md:gap-5 justify-between'>
|
||||
<a href="/marketplace/artists">
|
||||
<ActionButtons icon={<Images size="18" color="#ffffff" />} label="Stock Photo" isGhost={false} buttonWidth={65} />
|
||||
</a>
|
||||
<a href="/marketplace/advertisers">
|
||||
<ActionButtons icon={<ImageDownIcon size="18" color="#ffffff" />} label="Stock Video" isGhost={false} buttonWidth={65} /> </a>
|
||||
<ActionButtons icon={<Shapes size="18" color="#ffffff" />} label="Shapes" isGhost={false} buttonWidth={65} />
|
||||
<ActionButtons icon={<Gift size="18" color="#ffffff" />} label="Gif" isGhost={false} buttonWidth={65} />
|
||||
<ActionButtons icon={<Sticker size="18" color="#ffffff" />} label="Sticker" isGhost={false} buttonWidth={65} />
|
||||
<ActionButtons icon={<AudioSquare size="18" color="#ffffff" />} label="Audio" isGhost={false} buttonWidth={65} />
|
||||
</div>
|
||||
|
||||
<CategoryHeading title="Data" />
|
||||
<div className='grid grid-cols-3 w-full flex-wrap gap-3 md:gap-5 justify-between'>
|
||||
<ActionButtons icon={<Chart size="18" color="#ffffff" />} label="Chart" isGhost={false} buttonWidth={65} />
|
||||
<ActionButtons icon={<TableDocument size="18" color="#ffffff" />} label="Table" isGhost={false} buttonWidth={65} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="interactions" className='relative w-full h-full flex flex-col overflow-y-auto no-scrollbar p-2 md:p-4'>
|
||||
<CategoryHeading title="Links & Tags" />
|
||||
<div className='grid grid-cols-3 w-full flex-wrap gap-3 md:gap-5 justify-between'>
|
||||
<ActionButtons icon={<Link size="18" color="#ffffff" />} label="Link Area" isGhost={false} buttonWidth={65} />
|
||||
<ActionButtons icon={<LinkSquare size="18" color="#ffffff" />} label="Link Button" isGhost={false} buttonWidth={65} />
|
||||
<ActionButtons icon={<Tag size="18" color="#ffffff" />} label="Tag" isGhost={false} buttonWidth={65} />
|
||||
<ActionButtons icon={<MessageAdd1 size="18" color="#ffffff" />} label="Caption" isGhost={false} buttonWidth={65} />
|
||||
<ActionButtons icon={<CodeCircle size="18" color="#ffffff" />} label="Embed code" isGhost={false} buttonWidth={65} />
|
||||
<ActionButtons icon={<Text size="18" color="#ffffff" />} label="Pop-up frame" isGhost={false} buttonWidth={65} />
|
||||
</div>
|
||||
|
||||
<CategoryHeading title="Images" />
|
||||
<div className='grid grid-cols-3 w-full flex-wrap gap-3 md:gap-5'>
|
||||
<ActionButtons icon={<Cardano size="18" color="#ffffff" />} label="Spotlight" isGhost={false} buttonWidth={65} />
|
||||
<ActionButtons icon={<Images size="18" color="#ffffff" />} label="Slideshow" isGhost={false} buttonWidth={65} />
|
||||
</div>
|
||||
|
||||
<CategoryHeading title="Audio & video" />
|
||||
<div className='grid grid-cols-3 w-full flex-wrap gap-3 md:gap-5'>
|
||||
<ActionButtons icon={<VideoHorizontal size="18" color="#ffffff" />} label="Video embed" isGhost={false} buttonWidth={65} />
|
||||
<ActionButtons icon={<Video size="18" color="#ffffff" />} label="Video Button" isGhost={false} buttonWidth={65} />
|
||||
<ActionButtons icon={<AudioSquare size="18" color="#ffffff" />} label="Audio Button" isGhost={false} buttonWidth={65} />
|
||||
</div>
|
||||
|
||||
<CategoryHeading title="Engagement" />
|
||||
<div className='grid grid-cols-3 w-full flex-wrap gap-3 md:gap-5'>
|
||||
<ActionButtons icon={<MessageQuestion size="18" color="#ffffff" />} label="Question" isGhost={false} buttonWidth={65} />
|
||||
<ActionButtons icon={<Note size="18" color="#ffffff" />} label="Contact Form" isGhost={false} buttonWidth={65} />
|
||||
</div>
|
||||
|
||||
<CategoryHeading title="Navigation" />
|
||||
<div className='grid grid-cols-3 w-full flex-wrap gap-3 md:gap-5'>
|
||||
<ActionButtons icon={<ArrowLeft2 size="18" color="#ffffff" />} label="Previous Page" isGhost={false} buttonWidth={65} />
|
||||
<ActionButtons icon={<ArrowRight2 size="18" color="#ffffff" />} label="Next Page" isGhost={false} buttonWidth={65} />
|
||||
<ActionButtons icon={<ExportSquare size="18" color="#ffffff" />} label="Go to page" isGhost={false} buttonWidth={65} />
|
||||
<ActionButtons icon={<Previous size="18" color="#ffffff" />} label="First page" isGhost={false} buttonWidth={65} />
|
||||
<ActionButtons icon={<Next size="18" color="#ffffff" />} label="Last page" isGhost={false} buttonWidth={65} />
|
||||
</div>
|
||||
|
||||
<CategoryHeading title="Social" />
|
||||
<div className='grid grid-cols-3 w-full flex-wrap gap-3 md:gap-5 mb-10'>
|
||||
<ActionButtons icon={<Snapchat size="18" color="#ffffff" />} label="Snapchat" isGhost={false} buttonWidth={65} />
|
||||
<ActionButtons icon={<Whatsapp size="18" color="#ffffff" />} label="WhatsApp" isGhost={false} buttonWidth={65} />
|
||||
<ActionButtons icon={<Spotify size="18" color="#ffffff" />} label="Spotify" isGhost={false} buttonWidth={65} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="templates" className='p-3 md:p-5'>
|
||||
<div className='flex flex-col items-center justify-center h-full'>
|
||||
<p className='text-white text-sm md:text-base'>Templates section</p>
|
||||
<p className='text-gray-400 text-xs md:text-sm mt-2'>This section is under development</p>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="aiwrite" className='p-3 md:p-5'>
|
||||
<div className='flex flex-col items-center justify-center h-full'>
|
||||
<p className='text-white text-sm md:text-base'>AI Write section</p>
|
||||
<p className='text-gray-400 text-xs md:text-sm mt-2'>This section is under development</p>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="settings" className='p-3 md:p-5'>
|
||||
<div className='flex flex-col items-center justify-center h-full'>
|
||||
<p className='text-white text-sm md:text-base'>Settings section</p>
|
||||
<p className='text-gray-400 text-xs md:text-sm mt-2'>This section is under development</p>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sidebar;
|
||||
87
components/custom/Best_Seller.tsx
Normal file
@ -0,0 +1,87 @@
|
||||
import Image from 'next/image';
|
||||
import { Heart, ChevronRight } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
const books = [
|
||||
{
|
||||
title: 'Brutal',
|
||||
author: 'Hoosh Ink',
|
||||
price: '$4.99',
|
||||
cover: '/book1.png',
|
||||
},
|
||||
{
|
||||
title: 'Authority',
|
||||
author: 'Jeff Vandermeer',
|
||||
price: '$11.99',
|
||||
cover: '/book2.png',
|
||||
},
|
||||
{
|
||||
title: 'Babe Hager',
|
||||
author: 'Babe Hager',
|
||||
price: '$2.99',
|
||||
cover: '/book3.png',
|
||||
},
|
||||
{
|
||||
title: 'Never Flinch',
|
||||
author: 'Stephen King',
|
||||
price: '$16.99',
|
||||
cover: '/book4.png',
|
||||
},
|
||||
{
|
||||
title: 'The Obsession',
|
||||
author: 'Jesse Q. Sutanto',
|
||||
price: '$8.99',
|
||||
cover: '/book5.png',
|
||||
},
|
||||
{
|
||||
title: 'Unlikely Story',
|
||||
author: 'Ali Rosen',
|
||||
price: '$2.99',
|
||||
cover: '/book6.png',
|
||||
},
|
||||
{
|
||||
title: 'Thrill Ride',
|
||||
author: 'Amy Ratcliffe',
|
||||
price: '$9.99',
|
||||
cover: '/book7.png',
|
||||
},
|
||||
];
|
||||
|
||||
export default function BestSeller() {
|
||||
return (
|
||||
<div className="w-full py-8 px-20">
|
||||
<div className="flex items-center justify-between mb-2 px-2">
|
||||
<h2 className="text-[15px] font-[600] text-[#151C4F]">Best Sellers</h2>
|
||||
<a href="#" className="text-[#4F8CFF] text-[16px] font-[400] hover:underline flex items-center gap-1">See more <ChevronRight size={16} /></a>
|
||||
</div>
|
||||
<div className="border-b-2 border-[#D1D5DB] mb-6 w-full" />
|
||||
<div className="flex gap-6 overflow-x-auto scrollbar-hide pb-2">
|
||||
{books.map((book) => (
|
||||
<div key={book.title} className="flex flex-col" style={{ minWidth: 158 }}>
|
||||
<div className="relative w-[158px] h-[235px] flex items-center justify-center">
|
||||
<Image
|
||||
src={book.cover}
|
||||
alt={book.title}
|
||||
fill
|
||||
style={{ objectFit: 'cover', borderRadius: 4 }}
|
||||
className="transition group-hover:scale-105 duration-200"
|
||||
/>
|
||||
</div>
|
||||
<div className="px-1 pt-2 flex flex-col gap-1 w-[158px]">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-[400] text-[14px] text-[#151C4F]" title={book.title}>{book.title}</span>
|
||||
<Button variant="ghost" size="icon" className="ml-2">
|
||||
<Heart className="w-5 h-5 text-gray-400 hover:text-pink-500 transition" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex items-center justify-between mt-0.5">
|
||||
<span className="text-[12px] text-[#555979] font-[400] whitespace-nowrap"><span className="capitalize">{book.author}</span></span>
|
||||
<span className="font-[700] text-[#151C4F] text-[14px] whitespace-nowrap">{book.price}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
94
components/custom/Frame.tsx
Normal file
@ -0,0 +1,94 @@
|
||||
'use client'
|
||||
|
||||
import Image from 'next/image';
|
||||
import { CopyPlus, Trash2, Plus } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
interface Card {
|
||||
id: number;
|
||||
isEditing: boolean;
|
||||
}
|
||||
|
||||
export default function Frame() {
|
||||
const [cards, setCards] = useState<Card[]>([]);
|
||||
const [nextId, setNextId] = useState(1);
|
||||
|
||||
const handleAddCard = () => {
|
||||
setCards([...cards, { id: nextId, isEditing: true }]);
|
||||
setNextId(nextId + 1);
|
||||
};
|
||||
|
||||
const handleDeleteCard = (id: number) => {
|
||||
setCards(cards.filter(card => card.id !== id));
|
||||
};
|
||||
|
||||
const totalPages = cards.length + 1;
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col items-center justify-center">
|
||||
|
||||
<div className="w-[450px] flex items-center justify-between px-4 py-2 mb-2" style={{ minHeight: 40 }}>
|
||||
<span className="text-[#2633B9] text-[14px] font-[#1A237E400]">Page 1/{totalPages}</span>
|
||||
<div className="flex gap-2">
|
||||
<CopyPlus
|
||||
size={18}
|
||||
className="cursor-pointer text-[#1A237E] hover:text-blue-600"
|
||||
onClick={handleAddCard}
|
||||
/>
|
||||
<Trash2
|
||||
size={18}
|
||||
className="cursor-pointer text-[#1A237E] hover:text-red-600"
|
||||
onClick={() => handleDeleteCard(0)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="flex flex-col items-center gap-8">
|
||||
|
||||
<div className="w-[450px] flex flex-col items-center">
|
||||
<div className="w-[410px] h-[510px] flex items-center justify-center">
|
||||
<Image src="/pdf-image.png" alt="Excerpt Card" width={410} height={510} style={{ objectFit: 'contain' }} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{cards.map((card, index) => (
|
||||
<div key={card.id} className="w-[450px] flex flex-col items-center">
|
||||
<div className="w-full flex items-center justify-between px-4 py-2 mb-2" style={{ minHeight: 40 }}>
|
||||
<span className="text-[#2633B9] text-[14px] font-[#1A237E400]">Page {index + 2}/{totalPages}</span>
|
||||
<div className="flex gap-2">
|
||||
<CopyPlus
|
||||
size={18}
|
||||
className="cursor-pointer text-[#1A237E] hover:text-blue-600"
|
||||
onClick={handleAddCard}
|
||||
/>
|
||||
<Trash2
|
||||
size={18}
|
||||
className="cursor-pointer text-[#1A237E] hover:text-red-600"
|
||||
onClick={() => handleDeleteCard(card.id)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-[410px] h-[510px] bg-white rounded-lg shadow-md">
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
|
||||
<div className="w-[450px] flex justify-center">
|
||||
<Button
|
||||
onClick={handleAddCard}
|
||||
variant="outline"
|
||||
className="flex justify-center items-center gap-2 py-2 text-[#1A237E] font-medium bg-white hover:bg-blue-50 transition-colors shadow-sm"
|
||||
style={{ width: 410 }}
|
||||
>
|
||||
<Plus size={20} className="text-[#1A237E]" />
|
||||
<span className="text-center text-[16px] font-[400]">Add new page</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
40
components/custom/Home_Banner.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import Image from "next/image";
|
||||
|
||||
export default function HomeBanner() {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="max-w-[1440px] mx-auto px-20 mt-10">
|
||||
<div className="relative h-[240px] md:h-[260px] bg-[rgba(255,248,238,0.9)] rounded-2xl overflow-hidden flex items-center justify-between">
|
||||
{/* Left: Text Content */}
|
||||
<div className="z-10 py-8 flex-1 flex flex-col justify-center pl-16 pr-20">
|
||||
<h1 className="text-[35px] font-[500] text-[#151C4F] mb-2 tracking-tight">
|
||||
1000 BLACK UMBRELLAS
|
||||
</h1>
|
||||
<p className="text-[#151C4F] text-[21px] font-[400] mb-2 max-w-5xl">
|
||||
Poems of absolute nakedness that chase the power of love. Daniel McGinn is<br/> one of the most admired poets in the underground American poetry scene.
|
||||
</p>
|
||||
<Button
|
||||
className="bg-transparent border border-[#151C4F] text-[#151C4F] text-[21px] w-fit px-6 py-3 font-[400] rounded-md mt-4 hover:bg-[#151C4F] hover:text-white transition-all duration-300 cursor-pointer"
|
||||
size="lg"
|
||||
>
|
||||
PURCHASE EBOOK
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Right: Book Cover */}
|
||||
<div className="hidden md:block absolute" style={{ top: '85.44px', left: '986px' }}>
|
||||
<Image
|
||||
src="/book.png"
|
||||
alt="1000 Black Umbrellas Book Cover"
|
||||
width={185}
|
||||
height={275}
|
||||
className="object-contain rounded-xl"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
34
components/custom/Nav_bar.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import { Bell } from 'lucide-react';
|
||||
import { Search } from 'lucide-react';
|
||||
import { Input } from '@/components/ui/input';
|
||||
|
||||
export default function Navbar() {
|
||||
return (
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end', padding: '24px 40px', background: 'white', width: '100%', height: '75px' }}>
|
||||
<div style={{ flex: 1, display: 'flex', justifyContent: 'flex-end', marginRight: 120 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', background: '#F2F4F8', borderRadius: '10px', padding: '8px 16px', minWidth: 350 }}>
|
||||
<Search size={20} style={{ color: '#6B7280', marginRight: 8 }} />
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Search ebooks, folders, and uploads"
|
||||
className="bg-transparent text-[#6B7280] text-base w-[250px] focus-visible:ring-0 focus-visible:ring-offset-0 border-0 focus:border-0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 24 }}>
|
||||
<div style={{ position: 'relative', cursor: 'pointer' }}>
|
||||
<Bell size={22} style={{ color: '#222' }} />
|
||||
<span style={{ position: 'absolute', top: -2, right: 0, width: 9, height: 9, background: '#FF3B30', borderRadius: '50%', border: '1.5px solid white' }} />
|
||||
</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', cursor: 'pointer' }}>
|
||||
<div style={{ width: 32, height: 32, borderRadius: '50%', background: '#FFD600', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 600, color: '#222', fontSize: 14, position: 'relative', zIndex: 1 }}>
|
||||
D
|
||||
</div>
|
||||
<div style={{ width: 32, height: 32, borderRadius: '50%', background: 'white', border: '1px solid #E5E7EB', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 20, color: '#222', cursor: 'pointer', marginLeft: -8, position: 'relative', zIndex: 0 }}>
|
||||
+
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
87
components/custom/New_Release.tsx
Normal file
@ -0,0 +1,87 @@
|
||||
import Image from 'next/image';
|
||||
import { Heart, ChevronRight } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
const books = [
|
||||
{
|
||||
title: 'Brutal',
|
||||
author: 'Hoosh Ink',
|
||||
price: '$4.99',
|
||||
cover: '/book8.png',
|
||||
},
|
||||
{
|
||||
title: 'Authority',
|
||||
author: 'Jeff Vandermeer',
|
||||
price: '$11.99',
|
||||
cover: '/book9.png',
|
||||
},
|
||||
{
|
||||
title: 'Babe Hager',
|
||||
author: 'Babe Hager',
|
||||
price: '$2.99',
|
||||
cover: '/book10.png',
|
||||
},
|
||||
{
|
||||
title: 'Never Flinch',
|
||||
author: 'Stephen King',
|
||||
price: '$16.99',
|
||||
cover: '/book11.png',
|
||||
},
|
||||
{
|
||||
title: 'The Obsession',
|
||||
author: 'Jesse Q. Sutanto',
|
||||
price: '$8.99',
|
||||
cover: '/book12.png',
|
||||
},
|
||||
{
|
||||
title: 'Unlikely Story',
|
||||
author: 'Ali Rosen',
|
||||
price: '$2.99',
|
||||
cover: '/book13.png',
|
||||
},
|
||||
{
|
||||
title: 'Thrill Ride',
|
||||
author: 'Amy Ratcliffe',
|
||||
price: '$9.99',
|
||||
cover: '/book14.png',
|
||||
},
|
||||
];
|
||||
|
||||
export default function NewRelease() {
|
||||
return (
|
||||
<div className="w-full py-8 px-20">
|
||||
<div className="flex items-center justify-between mb-2 px-2">
|
||||
<h2 className="text-[15px] font-[600] text-[#151C4F]">New Release</h2>
|
||||
<a href="#" className="text-[#4F8CFF] text-[16px] font-[400] hover:underline flex items-center gap-1">See more <ChevronRight size={16} /></a>
|
||||
</div>
|
||||
<div className="border-b-2 border-[#D1D5DB] mb-6 w-full" />
|
||||
<div className="flex gap-6 overflow-x-auto scrollbar-hide pb-2">
|
||||
{books.map((book) => (
|
||||
<div key={book.title} className="flex flex-col" style={{ minWidth: 158 }}>
|
||||
<div className="relative w-[158px] h-[235px] flex items-center justify-center">
|
||||
<Image
|
||||
src={book.cover}
|
||||
alt={book.title}
|
||||
fill
|
||||
style={{ objectFit: 'cover', borderRadius: 4 }}
|
||||
className="transition group-hover:scale-105 duration-200"
|
||||
/>
|
||||
</div>
|
||||
<div className="px-1 pt-2 flex flex-col gap-1 w-[158px]">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-[400] text-[14px] text-[#151C4F]" title={book.title}>{book.title}</span>
|
||||
<Button variant="ghost" size="icon" className="ml-2">
|
||||
<Heart className="w-5 h-5 text-gray-400 hover:text-pink-500 transition" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex items-center justify-between mt-0.5">
|
||||
<span className="text-[12px] text-[#555979] font-[400] whitespace-nowrap"><span className="capitalize">{book.author}</span></span>
|
||||
<span className="font-[700] text-[#151C4F] text-[14px] whitespace-nowrap">{book.price}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
9
components/custom/Play_Ground.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
import Frame from "./Frame";
|
||||
|
||||
export default function PlayGround() {
|
||||
return (
|
||||
<div className="w-full mt-24">
|
||||
<Frame />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
102
components/custom/Recent_Design.tsx
Normal file
@ -0,0 +1,102 @@
|
||||
import Image from "next/image";
|
||||
import { ExternalLink, Ellipsis, Trash2 } from 'lucide-react';
|
||||
import Link from "next/link";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
// Sample data for recent designs
|
||||
const recentDesigns = [
|
||||
{
|
||||
icon: "/recent-image-1.png",
|
||||
title: "Good morning Gabe ...",
|
||||
},
|
||||
{
|
||||
icon: "/recent-image-2.png",
|
||||
title: "Daphne's first eBook...",
|
||||
},
|
||||
{
|
||||
icon: "/recent-image-3.png",
|
||||
title: "Story of my life (Story...",
|
||||
},
|
||||
{
|
||||
icon: "/recent-image-4.png",
|
||||
title: "Good morning Gabe ...",
|
||||
},
|
||||
{
|
||||
icon: "/recent-image-5.png",
|
||||
title: "A fantastic saga, the...",
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* RecentDesign Component
|
||||
* Displays a sidebar with recent designs and a trash section
|
||||
*/
|
||||
export default function RecentDesign() {
|
||||
return (
|
||||
<div className="bg-white border-t md:border-t-0 md:border-r border-[#D9D7D7] w-full md:w-[300px] flex flex-col h-auto md:h-screen justify-between p-4 md:px-4 md:pt-6 md:pb-4">
|
||||
{/* Logo and Title Section */}
|
||||
<div>
|
||||
<div className="flex flex-col items-center mb-6 md:mb-12">
|
||||
<Image
|
||||
src="/logo.png"
|
||||
alt="Wodey logo"
|
||||
width={157}
|
||||
height={53}
|
||||
className="w-[120px] md:w-[157px] h-auto md:h-[53px]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Recent Designs Header */}
|
||||
<div className="text-[#27275A] text-[12px] font-[400] mb-2 pl-1">Recent designs</div>
|
||||
|
||||
|
||||
<div className="grid grid-cols-2 md:flex md:flex-col gap-2">
|
||||
{recentDesigns.map((design, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="flex items-center bg-white rounded-lg py-2 px-2 hover:bg-[#F5F6FA] group transition cursor-pointer"
|
||||
>
|
||||
|
||||
<div className="w-6 h-6 md:w-8 md:h-8 flex items-center justify-center mr-2">
|
||||
<Image
|
||||
src={design.icon}
|
||||
alt="icon"
|
||||
width={28}
|
||||
height={28}
|
||||
className="w-5 h-5 md:w-7 md:h-7"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Design Title */}
|
||||
<span className="flex-1 text-[10px] text-[#27275A] truncate max-w-[100px] md:max-w-[120px] font-[400]">
|
||||
{design.title}
|
||||
</span>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="ml-auto">
|
||||
<Button variant="ghost" size="icon" className="h-10 w-10 p-1">
|
||||
<div className="flex items-center gap-0.5 md:gap-1 pr-5">
|
||||
<Button variant="ghost" size="icon" className="h-7 w-7 md:h-8 md:w-8 p-0 hover:bg-[#e6e6e8]">
|
||||
<ExternalLink className="text-[#27275A] bg-[#f2f2f3] border border-gray-200 rounded-[4px] p-1 w-7 h-7 md:w-8 md:h-8" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" className="h-7 w-7 md:h-8 md:w-8 p-0 hover:bg-[#e6e6e8]">
|
||||
<Ellipsis className="text-[#27275A] bg-[#f2f2f3] border border-gray-200 rounded-[4px] p-1 w-7 h-7 md:w-8 md:h-8" />
|
||||
</Button>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Trash Section */}
|
||||
<Button variant="ghost" className="flex items-center gap-2 p-2 hover:bg-[#F5F6FA] rounded-lg mt-4 md:mt-0 w-full justify-start">
|
||||
<Link href="/creator/trash" className="flex items-center gap-2">
|
||||
<Trash2 className="text-[#27275A] w-[16px] md:w-[20px]" />
|
||||
<span className="text-[#27275A] text-[12px] md:text-[14px] font-[400]">Trash</span>
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
15
components/custom/Side_Nav.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import Sidebar from "./Side_bar";
|
||||
import RecentDesign from "./Recent_Design";
|
||||
|
||||
export default function SideNav() {
|
||||
return (
|
||||
<div className="flex h-screen">
|
||||
<div className="h-full">
|
||||
<Sidebar />
|
||||
</div>
|
||||
<div className="h-full">
|
||||
<RecentDesign />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
77
components/custom/Side_bar.tsx
Normal file
@ -0,0 +1,77 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Menu } from "lucide-react";
|
||||
|
||||
|
||||
const links = [
|
||||
{
|
||||
icon: "/home-icon.png",
|
||||
label: "Home",
|
||||
path: "/"
|
||||
},
|
||||
{
|
||||
icon: "/design-icon.png",
|
||||
label: "Design St.",
|
||||
path: "/design"
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
export default function Sidebar() {
|
||||
const pathname = usePathname();
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className={`bg-[#050616] w-full md:w-[80px] h-auto md:h-screen transition-all duration-300 ease-in-out ${isMenuOpen ? 'md:w-[200px]' : ''}`}>
|
||||
<div className="flex md:flex-col items-center justify-center gap-4 p-4 md:pt-10">
|
||||
{/* Menu Icon */}
|
||||
<div className="flex md:flex-col items-center gap-4 md:gap-6">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="mb-5 hover:bg-[#1a1c2b]/50"
|
||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||
>
|
||||
<Menu className="h-6 w-6 text-white" />
|
||||
</Button>
|
||||
|
||||
{/* Navigation Links */}
|
||||
{links.map((link) => (
|
||||
<div
|
||||
key={link.path}
|
||||
className={`flex flex-col items-center cursor-pointer group`}
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className={`p-2 rounded-lg transition-colors duration-200 ${
|
||||
pathname === link.path ? 'bg-[#1a1c2b]' : 'group-hover:bg-[#1a1c2b]/50'
|
||||
}`}
|
||||
>
|
||||
<Image
|
||||
src={link.icon}
|
||||
alt={link.label}
|
||||
width={24}
|
||||
height={24}
|
||||
className="group-hover:opacity-80 transition-opacity duration-200"
|
||||
priority={link.path === "/"} // Prioritize loading home icon
|
||||
/>
|
||||
</Button>
|
||||
|
||||
{/* Link Label */}
|
||||
<p className={`text-white text-xs mt-1 transition-all duration-300 ease-in-out ${
|
||||
isMenuOpen ? 'opacity-100 translate-x-0' : 'opacity-0 translate-x-4'
|
||||
} group-hover:opacity-80`}>
|
||||
{link.label}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
14
components/custom/books_Frame.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
import BestSeller from './Best_Seller';
|
||||
import NewRelease from './New_Release';
|
||||
|
||||
|
||||
|
||||
export default function BooksFrame() {
|
||||
return (
|
||||
<div>
|
||||
<BestSeller />
|
||||
<NewRelease />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
90
components/custom/marketplace_Navbar.tsx
Normal file
@ -0,0 +1,90 @@
|
||||
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">Woedii</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 */}
|
||||
<a href="/creator">
|
||||
<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>
|
||||
</a>
|
||||
</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 />
|
||||
</>
|
||||
);
|
||||
}
|
||||
53
components/ui/avatar.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as AvatarPrimitive from "@radix-ui/react-avatar"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Avatar({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Root>) {
|
||||
return (
|
||||
<AvatarPrimitive.Root
|
||||
data-slot="avatar"
|
||||
className={cn(
|
||||
"relative flex size-8 shrink-0 overflow-hidden rounded-full",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AvatarImage({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
||||
return (
|
||||
<AvatarPrimitive.Image
|
||||
data-slot="avatar-image"
|
||||
className={cn("aspect-square size-full", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AvatarFallback({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
||||
return (
|
||||
<AvatarPrimitive.Fallback
|
||||
data-slot="avatar-fallback"
|
||||
className={cn(
|
||||
"bg-muted flex size-full items-center justify-center rounded-full",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Avatar, AvatarImage, AvatarFallback }
|
||||
59
components/ui/button.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
outline:
|
||||
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
||||
ghost:
|
||||
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
||||
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
||||
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
||||
icon: "size-9",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
function Button({
|
||||
className,
|
||||
variant,
|
||||
size,
|
||||
asChild = false,
|
||||
...props
|
||||
}: React.ComponentProps<"button"> &
|
||||
VariantProps<typeof buttonVariants> & {
|
||||
asChild?: boolean
|
||||
}) {
|
||||
const Comp = asChild ? Slot : "button"
|
||||
|
||||
return (
|
||||
<Comp
|
||||
data-slot="button"
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Button, buttonVariants }
|
||||
32
components/ui/checkbox.tsx
Normal 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 }
|
||||
252
components/ui/context-menu.tsx
Normal file
@ -0,0 +1,252 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"
|
||||
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function ContextMenu({
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Root>) {
|
||||
return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />
|
||||
}
|
||||
|
||||
function ContextMenuTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) {
|
||||
return (
|
||||
<ContextMenuPrimitive.Trigger data-slot="context-menu-trigger" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuGroup({
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Group>) {
|
||||
return (
|
||||
<ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) {
|
||||
return (
|
||||
<ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuSub({
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) {
|
||||
return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />
|
||||
}
|
||||
|
||||
function ContextMenuRadioGroup({
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) {
|
||||
return (
|
||||
<ContextMenuPrimitive.RadioGroup
|
||||
data-slot="context-menu-radio-group"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuSubTrigger({
|
||||
className,
|
||||
inset,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & {
|
||||
inset?: boolean
|
||||
}) {
|
||||
return (
|
||||
<ContextMenuPrimitive.SubTrigger
|
||||
data-slot="context-menu-sub-trigger"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRightIcon className="ml-auto" />
|
||||
</ContextMenuPrimitive.SubTrigger>
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuSubContent({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) {
|
||||
return (
|
||||
<ContextMenuPrimitive.SubContent
|
||||
data-slot="context-menu-sub-content"
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuContent({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Content>) {
|
||||
return (
|
||||
<ContextMenuPrimitive.Portal>
|
||||
<ContextMenuPrimitive.Content
|
||||
data-slot="context-menu-content"
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</ContextMenuPrimitive.Portal>
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuItem({
|
||||
className,
|
||||
inset,
|
||||
variant = "default",
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Item> & {
|
||||
inset?: boolean
|
||||
variant?: "default" | "destructive"
|
||||
}) {
|
||||
return (
|
||||
<ContextMenuPrimitive.Item
|
||||
data-slot="context-menu-item"
|
||||
data-inset={inset}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuCheckboxItem({
|
||||
className,
|
||||
children,
|
||||
checked,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>) {
|
||||
return (
|
||||
<ContextMenuPrimitive.CheckboxItem
|
||||
data-slot="context-menu-checkbox-item"
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<ContextMenuPrimitive.ItemIndicator>
|
||||
<CheckIcon className="size-4" />
|
||||
</ContextMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</ContextMenuPrimitive.CheckboxItem>
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuRadioItem({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>) {
|
||||
return (
|
||||
<ContextMenuPrimitive.RadioItem
|
||||
data-slot="context-menu-radio-item"
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<ContextMenuPrimitive.ItemIndicator>
|
||||
<CircleIcon className="size-2 fill-current" />
|
||||
</ContextMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</ContextMenuPrimitive.RadioItem>
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuLabel({
|
||||
className,
|
||||
inset,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Label> & {
|
||||
inset?: boolean
|
||||
}) {
|
||||
return (
|
||||
<ContextMenuPrimitive.Label
|
||||
data-slot="context-menu-label"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"text-foreground px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuSeparator({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) {
|
||||
return (
|
||||
<ContextMenuPrimitive.Separator
|
||||
data-slot="context-menu-separator"
|
||||
className={cn("bg-border -mx-1 my-1 h-px", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function ContextMenuShortcut({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"span">) {
|
||||
return (
|
||||
<span
|
||||
data-slot="context-menu-shortcut"
|
||||
className={cn(
|
||||
"text-muted-foreground ml-auto text-xs tracking-widest",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
ContextMenu,
|
||||
ContextMenuTrigger,
|
||||
ContextMenuContent,
|
||||
ContextMenuItem,
|
||||
ContextMenuCheckboxItem,
|
||||
ContextMenuRadioItem,
|
||||
ContextMenuLabel,
|
||||
ContextMenuSeparator,
|
||||
ContextMenuShortcut,
|
||||
ContextMenuGroup,
|
||||
ContextMenuPortal,
|
||||
ContextMenuSub,
|
||||
ContextMenuSubContent,
|
||||
ContextMenuSubTrigger,
|
||||
ContextMenuRadioGroup,
|
||||
}
|
||||
44
components/ui/hover-card.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function HoverCard({
|
||||
...props
|
||||
}: React.ComponentProps<typeof HoverCardPrimitive.Root>) {
|
||||
return <HoverCardPrimitive.Root data-slot="hover-card" {...props} />
|
||||
}
|
||||
|
||||
function HoverCardTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) {
|
||||
return (
|
||||
<HoverCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function HoverCardContent({
|
||||
className,
|
||||
align = "center",
|
||||
sideOffset = 4,
|
||||
...props
|
||||
}: React.ComponentProps<typeof HoverCardPrimitive.Content>) {
|
||||
return (
|
||||
<HoverCardPrimitive.Portal data-slot="hover-card-portal">
|
||||
<HoverCardPrimitive.Content
|
||||
data-slot="hover-card-content"
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</HoverCardPrimitive.Portal>
|
||||
)
|
||||
}
|
||||
|
||||
export { HoverCard, HoverCardTrigger, HoverCardContent }
|
||||
21
components/ui/input.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
data-slot="input"
|
||||
className={cn(
|
||||
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
"focus-visible:border-ring",
|
||||
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Input }
|
||||
13
components/ui/skeleton.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="skeleton"
|
||||
className={cn("bg-accent animate-pulse rounded-md", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Skeleton }
|
||||
66
components/ui/tabs.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Tabs({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
|
||||
return (
|
||||
<TabsPrimitive.Root
|
||||
data-slot="tabs"
|
||||
className={cn("flex flex-col gap-2", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TabsList({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TabsPrimitive.List>) {
|
||||
return (
|
||||
<TabsPrimitive.List
|
||||
data-slot="tabs-list"
|
||||
className={cn(
|
||||
"bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TabsTrigger({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
|
||||
return (
|
||||
<TabsPrimitive.Trigger
|
||||
data-slot="tabs-trigger"
|
||||
className={cn(
|
||||
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TabsContent({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TabsPrimitive.Content>) {
|
||||
return (
|
||||
<TabsPrimitive.Content
|
||||
data-slot="tabs-content"
|
||||
className={cn("flex-1 outline-none", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
||||
16
eslint.config.mjs
Normal file
@ -0,0 +1,16 @@
|
||||
import { dirname } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { FlatCompat } from "@eslint/eslintrc";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
});
|
||||
|
||||
const eslintConfig = [
|
||||
...compat.extends("next/core-web-vitals", "next/typescript"),
|
||||
];
|
||||
|
||||
export default eslintConfig;
|
||||
6
lib/utils.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
7
next.config.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
47
package.json
Normal file
@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "wodey-prototype",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev --turbopack",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-avatar": "^1.1.7",
|
||||
"@radix-ui/react-context-menu": "^2.2.12",
|
||||
"@radix-ui/react-hover-card": "^1.1.11",
|
||||
"@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",
|
||||
"react-icons": "^5.5.0",
|
||||
"tailwind-merge": "^3.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3",
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "15.3.1",
|
||||
"tailwindcss": "^4",
|
||||
"tw-animate-css": "^1.2.8",
|
||||
"typescript": "^5"
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"sharp",
|
||||
"unrs-resolver"
|
||||
]
|
||||
},
|
||||
"packageManager": "pnpm@10.9.0+sha512.0486e394640d3c1fb3c9d43d49cf92879ff74f8516959c235308f5a8f62e2e19528a65cdc2a3058f587cde71eba3d5b56327c8c33a97e4c4051ca48a10ca2d5f"
|
||||
}
|
||||
4489
pnpm-lock.yaml
Normal file
5
postcss.config.mjs
Normal file
@ -0,0 +1,5 @@
|
||||
const config = {
|
||||
plugins: ["@tailwindcss/postcss"],
|
||||
};
|
||||
|
||||
export default config;
|
||||
BIN
public/GradientMesh_Light.png
Normal file
|
After Width: | Height: | Size: 170 KiB |
BIN
public/avatar.png
Normal file
|
After Width: | Height: | Size: 994 KiB |
BIN
public/book.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
public/book1.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
public/book10.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
public/book11.png
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
public/book12.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
public/book13.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
public/book14.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
public/book2.png
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
public/book3.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
public/book4.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
public/book5.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
public/book6.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
public/book7.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
public/book8.png
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
public/book9.png
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
public/brush.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
public/camera.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
public/create.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
public/design-icon.png
Normal file
|
After Width: | Height: | Size: 312 B |
1
public/file.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 391 B |
1
public/globe.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
BIN
public/home-icon.png
Normal file
|
After Width: | Height: | Size: 366 B |
BIN
public/images/230f1945d640ae4c0325f23dcb3365b59ae08277.png
Normal file
|
After Width: | Height: | Size: 333 KiB |
BIN
public/images/292c2c8f2ea3276c44dc6ade84e687b9cae3d267.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
public/images/37934e37222a44601017b84963a414627d8e095f.png
Normal file
|
After Width: | Height: | Size: 5.8 MiB |
BIN
public/images/89f1cacd4041e59eb162ffcb0f8080dc179fe415.png
Normal file
|
After Width: | Height: | Size: 2.5 MiB |
BIN
public/images/96c1b745b59fe1512c73f653d7b5e7be3ee54e58.png
Normal file
|
After Width: | Height: | Size: 993 KiB |
BIN
public/images/Airbnb_1.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
public/images/Airbnb_2.png
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
public/images/Airbnb_3.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
public/images/Amazon_AWS.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
public/images/Amazon_echo.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
public/images/Banner.png
Normal file
|
After Width: | Height: | Size: 416 KiB |
BIN
public/images/Ebook.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
public/images/Element.png
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
public/images/Huawei.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
public/images/Netflix.png
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
public/images/Qonto.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
public/images/Samsung.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
public/images/Unconfirmed 311847.crdownload
Normal file
BIN
public/images/aa31529b95af9b43380b88b11692b0a6f7999878.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
public/images/baca21cebac9b0ae0463e371575f760ea5e79016.png
Normal file
|
After Width: | Height: | Size: 232 KiB |
BIN
public/images/brutal.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
public/images/card1.png
Normal file
|
After Width: | Height: | Size: 3.1 MiB |
BIN
public/images/d3cf3b09c1fd3dc0d6a997a7a479337fdf8caa69.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
public/images/d5d8f9fe19a7aed7bf6545a64634eeb37a6b895a.png
Normal file
|
After Width: | Height: | Size: 251 KiB |