add bd, pages: adt, create, profile
This commit is contained in:
parent
a2d2ff4e01
commit
7b40ce2c1a
109
app/(root)/adt/[id]/page.tsx
Normal file
109
app/(root)/adt/[id]/page.tsx
Normal file
@ -0,0 +1,109 @@
|
||||
import React from 'react';
|
||||
// import { useParams } from 'next/navigation';
|
||||
import { MapPin, Calendar, Phone, MessageCircle, Share2, Flag, Heart } from 'lucide-react';
|
||||
import { adts } from '@/data/adt';
|
||||
import { prisma } from '@/prisma/prisma-client';
|
||||
import Header from '@/components/Header';
|
||||
import { notFound } from 'next/navigation';
|
||||
|
||||
export default async function AdtPage({params: { id } }: { params: { id: string } }) {
|
||||
const adt = await prisma.adt.findUnique({
|
||||
where: {
|
||||
id: Number(id),
|
||||
},
|
||||
include: {
|
||||
user: true
|
||||
}
|
||||
})
|
||||
|
||||
if (!adt) {
|
||||
return notFound();
|
||||
}
|
||||
|
||||
const user = adt.user
|
||||
|
||||
// const { id } = params();
|
||||
// const adt = adts.find(l => l.id === id) || adts[0];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<div className="lg:col-span-2">
|
||||
<div className="bg-white rounded-xl shadow-sm overflow-hidden">
|
||||
<img src={adt.image} alt={adt.title} className="w-full h-[400px] object-cover" />
|
||||
<div className="p-6">
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<h1 className="text-2xl font-semibold">{adt.title}</h1>
|
||||
<span className="text-2xl font-bold text-indigo-600">{adt.price}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 text-gray-500 mb-6">
|
||||
<div className="flex items-center gap-1">
|
||||
<MapPin className="h-5 w-5" />
|
||||
<span>{adt.location}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Calendar className="h-5 w-5" />
|
||||
<span>{String(adt.createdAt)}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 className="font-semibold text-lg mb-3">Description</h2>
|
||||
<p className="text-gray-600 mb-6">
|
||||
This is a detailed description of the listing. It includes all the important information
|
||||
about the item, its condition, and any special features or considerations.
|
||||
</p>
|
||||
|
||||
<div className="flex gap-3">
|
||||
<button className="flex items-center gap-2 px-4 py-2 rounded-lg border border-gray-200 hover:bg-gray-50">
|
||||
<Share2 className="h-5 w-5" />
|
||||
<span>Share</span>
|
||||
</button>
|
||||
<button className="flex items-center gap-2 px-4 py-2 rounded-lg border border-gray-200 hover:bg-gray-50">
|
||||
<Flag className="h-5 w-5" />
|
||||
<span>Report</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="lg:col-span-1">
|
||||
<div className="bg-white rounded-xl shadow-sm p-6 sticky top-24">
|
||||
<div className="flex items-center gap-4 mb-6">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?auto=format&fit=crop&w=100"
|
||||
alt="Seller"
|
||||
className="w-12 h-12 rounded-full"
|
||||
/>
|
||||
<div>
|
||||
<h3 className="font-semibold">{user.name}</h3>
|
||||
<p className="text-sm text-gray-500">Member {String(user.createdAt)}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
{/* TODO всплывающее окно для показа номера */}
|
||||
|
||||
<button className="w-full flex items-center justify-center gap-2 bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700">
|
||||
<Phone className="h-5 w-5" />
|
||||
<span>Show Phone Number</span>
|
||||
</button>
|
||||
<button className="w-full flex items-center justify-center gap-2 bg-white border border-indigo-600 text-indigo-600 px-4 py-2 rounded-lg hover:bg-indigo-50">
|
||||
<MessageCircle className="h-5 w-5" />
|
||||
<span>Send Message (In development)</span>
|
||||
</button>
|
||||
<button className="w-full flex items-center justify-center gap-2 bg-white border border-gray-200 px-4 py-2 rounded-lg hover:bg-gray-50">
|
||||
<Heart className="h-5 w-5" />
|
||||
<span>Save to Favorites (In development)</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
105
app/(root)/adt/create/page.tsx
Normal file
105
app/(root)/adt/create/page.tsx
Normal file
@ -0,0 +1,105 @@
|
||||
import React from 'react';
|
||||
import { ImagePlus, X } from 'lucide-react';
|
||||
|
||||
export default function CreateListing() {
|
||||
return (
|
||||
<main className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||||
<h1 className="text-2xl font-semibold mb-6">Create New Listing</h1>
|
||||
|
||||
<form className="space-y-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Title
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="w-full px-4 py-2 rounded-lg border border-gray-200 focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500"
|
||||
placeholder="Enter listing title"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Category
|
||||
</label>
|
||||
<select className="w-full px-4 py-2 rounded-lg border border-gray-200 focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500">
|
||||
<option>Select a category</option>
|
||||
<option>Vehicles</option>
|
||||
<option>Real Estate</option>
|
||||
<option>Electronics</option>
|
||||
<option>Fashion</option>
|
||||
<option>Jobs</option>
|
||||
<option>Sports</option>
|
||||
<option>Art</option>
|
||||
<option>Books</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Price
|
||||
</label>
|
||||
<div className="relative">
|
||||
<span className="absolute left-4 top-2 text-gray-500">$</span>
|
||||
<input
|
||||
type="number"
|
||||
className="w-full pl-8 pr-4 py-2 rounded-lg border border-gray-200 focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500"
|
||||
placeholder="0.00"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Description
|
||||
</label>
|
||||
<textarea
|
||||
rows={4}
|
||||
className="w-full px-4 py-2 rounded-lg border border-gray-200 focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500"
|
||||
placeholder="Describe your item..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Photos
|
||||
</label>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 gap-4">
|
||||
<button className="aspect-square rounded-lg border-2 border-dashed border-gray-300 flex flex-col items-center justify-center hover:border-indigo-500 hover:bg-indigo-50">
|
||||
<ImagePlus className="h-8 w-8 text-gray-400" />
|
||||
<span className="mt-2 text-sm text-gray-500">Add Photo</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Location
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="w-full px-4 py-2 rounded-lg border border-gray-200 focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500"
|
||||
placeholder="Enter location"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
type="submit"
|
||||
className="flex-1 bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700"
|
||||
>
|
||||
Create Listing
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="px-4 py-2 rounded-lg border border-gray-200 hover:bg-gray-50"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
25
app/(root)/layout.tsx
Normal file
25
app/(root)/layout.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import type { Metadata } from "next";
|
||||
import localFont from "next/font/local";
|
||||
import "../globals.css";
|
||||
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Bazar",
|
||||
description: "Bazar",
|
||||
};
|
||||
|
||||
export default function HomeLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body
|
||||
// className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
39
app/(root)/page.tsx
Normal file
39
app/(root)/page.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
// "use client"
|
||||
|
||||
import Categories from "@/components/Categories";
|
||||
import Header from "@/components/Header";
|
||||
import ListingCard from "@/components/ListingCard";
|
||||
import { adts } from "@/data/adt";
|
||||
import { prisma } from "@/prisma/prisma-client";
|
||||
import Image from "next/image";
|
||||
|
||||
export default async function Home() {
|
||||
const adts = await prisma.adt.findMany()
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="min-h-screen bg-gray-100">
|
||||
<Header />
|
||||
<Categories />
|
||||
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h2 className="text-xl font-semibold">Featured Listings</h2>
|
||||
<div className="flex gap-2">
|
||||
<select className="px-4 py-2 rounded-lg border border-gray-200 bg-white">
|
||||
<option>Most Recent</option>
|
||||
<option>Price: Low to High</option>
|
||||
<option>Price: High to Low</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{adts.map((adt) => (
|
||||
<ListingCard key={adt.id} title={adt.title} image={adt.image} price={adt.price} location={adt.location} date={String(adt.createdAt)} id={adt.id} />
|
||||
))}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
66
app/(root)/profile/page.tsx
Normal file
66
app/(root)/profile/page.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
import React from 'react';
|
||||
import { Settings, Package, Heart, Bell } from 'lucide-react';
|
||||
import ListingCard from '@/components/ListingCard';
|
||||
import { adts } from '@/data/adt';
|
||||
import Header from '@/components/Header';
|
||||
|
||||
export default function Profile() {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
|
||||
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div className="bg-white rounded-xl shadow-sm mb-8">
|
||||
<div className="relative h-48 rounded-t-xl bg-gradient-to-r from-indigo-500 to-purple-600">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?auto=format&fit=crop&w=150"
|
||||
alt="Profile"
|
||||
className="absolute -bottom-12 left-8 w-24 h-24 rounded-full border-4 border-white"
|
||||
/>
|
||||
</div>
|
||||
<div className="pt-16 pb-8 px-8">
|
||||
<div className="flex justify-between items-start">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold">John Doe</h1>
|
||||
<p className="text-gray-500">San Francisco, CA</p>
|
||||
</div>
|
||||
<button className="flex items-center gap-2 px-4 py-2 rounded-lg border border-gray-200 hover:bg-gray-50">
|
||||
<Settings className="h-5 w-5" />
|
||||
<span>Edit Profile</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t">
|
||||
<nav className="flex divide-x">
|
||||
<button className="flex-1 px-4 py-3 text-indigo-600 border-b-2 border-indigo-600">
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Package className="h-5 w-5" />
|
||||
<span>My Listings</span>
|
||||
</div>
|
||||
</button>
|
||||
<button className="flex-1 px-4 py-3 text-gray-500 hover:text-gray-700">
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Heart className="h-5 w-5" />
|
||||
<span>Favorites</span>
|
||||
</div>
|
||||
</button>
|
||||
<button className="flex-1 px-4 py-3 text-gray-500 hover:text-gray-700">
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Bell className="h-5 w-5" />
|
||||
<span>Notifications</span>
|
||||
</div>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{adts.slice(0, 3).map((adt) => (
|
||||
<ListingCard key={adt.id} {...adt} />
|
||||
))}
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
|
||||
}
|
||||
8
app/api/category/route.ts
Normal file
8
app/api/category/route.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { prisma } from "@/prisma/prisma-client"
|
||||
import { NextResponse } from "next/server"
|
||||
|
||||
export async function GET() {
|
||||
const ingredients = await prisma.category.findMany()
|
||||
|
||||
return NextResponse.json(ingredients)
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@ -2,20 +2,12 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
color: var(--foreground);
|
||||
background: var(--background);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,21 +2,9 @@ import type { Metadata } from "next";
|
||||
import localFont from "next/font/local";
|
||||
import "./globals.css";
|
||||
|
||||
const geistSans = localFont({
|
||||
src: "./fonts/GeistVF.woff",
|
||||
variable: "--font-geist-sans",
|
||||
weight: "100 900",
|
||||
});
|
||||
const geistMono = localFont({
|
||||
src: "./fonts/GeistMonoVF.woff",
|
||||
variable: "--font-geist-mono",
|
||||
weight: "100 900",
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
|
||||
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
@ -26,7 +14,7 @@ export default function RootLayout({
|
||||
return (
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
// className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
</body>
|
||||
|
||||
101
app/page.tsx
101
app/page.tsx
@ -1,101 +0,0 @@
|
||||
import Image from "next/image";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
||||
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/next.svg"
|
||||
alt="Next.js logo"
|
||||
width={180}
|
||||
height={38}
|
||||
priority
|
||||
/>
|
||||
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
|
||||
<li className="mb-2">
|
||||
Get started by editing{" "}
|
||||
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
|
||||
app/page.tsx
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li>Save and see your changes instantly.</li>
|
||||
</ol>
|
||||
|
||||
<div className="flex gap-4 items-center flex-col sm:flex-row">
|
||||
<a
|
||||
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/vercel.svg"
|
||||
alt="Vercel logomark"
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
Deploy now
|
||||
</a>
|
||||
<a
|
||||
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Read our docs
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/file.svg"
|
||||
alt="File icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Learn
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/window.svg"
|
||||
alt="Window icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Examples
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/globe.svg"
|
||||
alt="Globe icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Go to nextjs.org →
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
21
components.json
Normal file
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": "tailwind.config.ts",
|
||||
"css": "app/globals.css",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": false,
|
||||
"prefix": ""
|
||||
},
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils",
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/lib",
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"iconLibrary": "lucide"
|
||||
}
|
||||
42
components/Categories.tsx
Normal file
42
components/Categories.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
// "use client"
|
||||
import React from 'react';
|
||||
import { Car, Home, Laptop, Shirt, Briefcase, Dumbbell, Palette, Book } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import { prisma } from '@/prisma/prisma-client';
|
||||
|
||||
const categories = [
|
||||
{ name: 'Vehicles', icon: Car },
|
||||
{ name: 'Real Estate', icon: Home },
|
||||
{ name: 'Electronics', icon: Laptop },
|
||||
{ name: 'Fashion', icon: Shirt },
|
||||
{ name: 'Jobs', icon: Briefcase },
|
||||
{ name: 'Sports', icon: Dumbbell },
|
||||
{ name: 'Art', icon: Palette },
|
||||
{ name: 'Books', icon: Book },
|
||||
];
|
||||
|
||||
export default async function Categories() {
|
||||
const categories = await prisma.category.findMany();
|
||||
|
||||
return (
|
||||
<div className="py-8 bg-gray-50">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<h2 className="text-xl font-semibold mb-6">Browse Categories</h2>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 md:grid-cols-8 gap-4">
|
||||
{categories.map((category) => {
|
||||
// const Icon = category.icon;
|
||||
return (
|
||||
<button
|
||||
key={category.name}
|
||||
className="flex flex-col items-center p-4 bg-white rounded-xl hover:shadow-md transition-shadow"
|
||||
>
|
||||
{/* <Icon className="h-8 w-8 text-indigo-600 mb-2" /> */}
|
||||
<span className="text-sm text-gray-700">{category.name}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
53
components/Header.tsx
Normal file
53
components/Header.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
"use client"
|
||||
import React from 'react';
|
||||
import { Search, PlusCircle, Bell, User } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function Header() {
|
||||
return (
|
||||
<header className="sticky top-0 z-50 bg-white shadow-sm">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex justify-between items-center h-16">
|
||||
<div className="flex items-center">
|
||||
<Link href="/" className="text-2xl font-bold text-indigo-600">
|
||||
MarketSpot
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 max-w-2xl mx-8">
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search listings..."
|
||||
className="w-full pl-10 pr-4 py-2 rounded-lg border border-gray-200 focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500"
|
||||
/>
|
||||
<Search className="absolute left-3 top-2.5 h-5 w-5 text-gray-400" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<Link
|
||||
href="/adt/create"
|
||||
className="flex items-center gap-2 bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700 transition-colors"
|
||||
>
|
||||
<PlusCircle className="h-5 w-5" />
|
||||
<span>Post Ad</span>
|
||||
</Link>
|
||||
<button className="relative p-2 hover:bg-gray-100 rounded-full">
|
||||
<Bell className="h-6 w-6 text-gray-600" />
|
||||
<span className="absolute top-0 right-0 h-4 w-4 bg-red-500 rounded-full text-xs text-white flex items-center justify-center">
|
||||
2
|
||||
</span>
|
||||
</button>
|
||||
<Link
|
||||
href="/profile"
|
||||
className="p-2 hover:bg-gray-100 rounded-full"
|
||||
>
|
||||
<User className="h-6 w-6 text-gray-600" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
51
components/ListingCard.tsx
Normal file
51
components/ListingCard.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
"use client"
|
||||
|
||||
import React from 'react';
|
||||
import { Heart, MapPin } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
// import { Link } from 'next/navigation';
|
||||
|
||||
interface ListingCardProps {
|
||||
id: string;
|
||||
title: string;
|
||||
price?: string;
|
||||
location?: string;
|
||||
image?: string;
|
||||
date: string;
|
||||
}
|
||||
|
||||
export default function ListingCard({ id, title, price, location, image, date }: ListingCardProps) {
|
||||
return (
|
||||
<Link href={`/adt/${id}`} className="block">
|
||||
<div className="bg-white rounded-xl shadow-sm hover:shadow-md transition-shadow">
|
||||
<div className="relative aspect-[4/3]">
|
||||
<img
|
||||
src={image}
|
||||
alt={title}
|
||||
className="w-full h-full object-cover rounded-t-xl"
|
||||
/>
|
||||
<button
|
||||
className="absolute top-3 right-3 p-2 bg-white/90 rounded-full hover:bg-white"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
// Handle favorite toggle
|
||||
}}
|
||||
>
|
||||
<Heart className="h-5 w-5 text-gray-600" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<div className="flex justify-between items-start mb-2">
|
||||
<h3 className="text-lg font-medium text-gray-900 line-clamp-2">{title}</h3>
|
||||
<span className="text-lg font-semibold text-indigo-600">{price}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-sm text-gray-500">
|
||||
<MapPin className="h-4 w-4" />
|
||||
<span>{location}</span>
|
||||
</div>
|
||||
<div className="mt-2 text-sm text-gray-400">{date}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
57
components/ui/button.tsx
Normal file
57
components/ui/button.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
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-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-neutral-950 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 dark:focus-visible:ring-neutral-300",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"bg-neutral-900 text-neutral-50 shadow hover:bg-neutral-900/90 dark:bg-neutral-50 dark:text-neutral-900 dark:hover:bg-neutral-50/90",
|
||||
destructive:
|
||||
"bg-red-500 text-neutral-50 shadow-sm hover:bg-red-500/90 dark:bg-red-900 dark:text-neutral-50 dark:hover:bg-red-900/90",
|
||||
outline:
|
||||
"border border-neutral-200 bg-white shadow-sm hover:bg-neutral-100 hover:text-neutral-900 dark:border-neutral-800 dark:bg-neutral-950 dark:hover:bg-neutral-800 dark:hover:text-neutral-50",
|
||||
secondary:
|
||||
"bg-neutral-100 text-neutral-900 shadow-sm hover:bg-neutral-100/80 dark:bg-neutral-800 dark:text-neutral-50 dark:hover:bg-neutral-800/80",
|
||||
ghost: "hover:bg-neutral-100 hover:text-neutral-900 dark:hover:bg-neutral-800 dark:hover:text-neutral-50",
|
||||
link: "text-neutral-900 underline-offset-4 hover:underline dark:text-neutral-50",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2",
|
||||
sm: "h-8 rounded-md px-3 text-xs",
|
||||
lg: "h-10 rounded-md px-8",
|
||||
icon: "h-9 w-9",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
export interface ButtonProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {
|
||||
asChild?: boolean
|
||||
}
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "button"
|
||||
return (
|
||||
<Comp
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
)
|
||||
Button.displayName = "Button"
|
||||
|
||||
export { Button, buttonVariants }
|
||||
74
data/adt.ts
Normal file
74
data/adt.ts
Normal file
@ -0,0 +1,74 @@
|
||||
export const adts = [
|
||||
{
|
||||
id: '1',
|
||||
title: "2020 Tesla Model 3 Long Range",
|
||||
price: "$41,999",
|
||||
location: "San Francisco, CA",
|
||||
image: "https://images.unsplash.com/photo-1560958089-b8a1929cea89?auto=format&fit=crop&w=800",
|
||||
date: "Posted 2 hours ago"
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: "Modern Studio Apartment in Downtown",
|
||||
price: "$2,200/mo",
|
||||
location: "Seattle, WA",
|
||||
image: "https://images.unsplash.com/photo-1522708323590-d24dbb6b0267?auto=format&fit=crop&w=800",
|
||||
date: "Posted 5 hours ago"
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: "MacBook Pro M2 16-inch",
|
||||
price: "$2,499",
|
||||
location: "Austin, TX",
|
||||
image: "https://images.unsplash.com/photo-1517336714731-489689fd1ca8?auto=format&fit=crop&w=800",
|
||||
date: "Posted 1 day ago"
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
title: "Vintage Leather Jacket",
|
||||
price: "$299",
|
||||
location: "Portland, OR",
|
||||
image: "https://images.unsplash.com/photo-1551028719-00167b16eac5?auto=format&fit=crop&w=800",
|
||||
date: "Posted 2 days ago"
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
title: "Professional DSLR Camera Kit",
|
||||
price: "$1,899",
|
||||
location: "New York, NY",
|
||||
image: "https://images.unsplash.com/photo-1516035069371-29a1b244cc32?auto=format&fit=crop&w=800",
|
||||
date: "Posted 3 days ago"
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
title: "Handcrafted Wooden Dining Table",
|
||||
price: "$899",
|
||||
location: "Denver, CO",
|
||||
image: "https://images.unsplash.com/photo-1577140917170-285929fb55b7?auto=format&fit=crop&w=800",
|
||||
date: "Posted 4 days ago"
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
title: "Smart Bluetooth Speaker",
|
||||
price: "$99",
|
||||
location: "Los Angeles, CA",
|
||||
image: "https://images.unsplash.com/photo-1517336714731-489689fd1ca8?auto=format&fit=crop&w=800",
|
||||
date: "Posted 5 days ago"
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
title: "Luxury Designer Watch",
|
||||
price: "$1,299",
|
||||
location: "Chicago, IL",
|
||||
image: "https://images.unsplash.com/photo-1551028719-00167b16eac5?auto=format&fit=crop&w=800",
|
||||
date: "Posted 6 days ago"
|
||||
},
|
||||
{
|
||||
id: '9',
|
||||
title: "Gaming Laptop",
|
||||
price: "$1,499",
|
||||
location: "Miami, FL",
|
||||
image: "https://images.unsplash.com/photo-1516035069371-29a1b244cc32?auto=format&fit=crop&w=800",
|
||||
date: "Posted 7 days ago"
|
||||
}
|
||||
];
|
||||
6
lib/utils.ts
Normal file
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))
|
||||
}
|
||||
868
package-lock.json
generated
868
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
27
package.json
27
package.json
@ -6,21 +6,38 @@
|
||||
"dev": "next dev --turbopack",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
"lint": "next lint",
|
||||
"prisma:push": "prisma db push",
|
||||
"prisma:studio": "prisma studio",
|
||||
"prisma:seed": "prisma db seed"
|
||||
},
|
||||
"prisma": {
|
||||
"seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "^5.22.0",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@types/bcrypt": "^5.0.2",
|
||||
"axios": "^1.7.7",
|
||||
"bcrypt": "^5.1.1",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.460.0",
|
||||
"next": "15.0.3",
|
||||
"prisma": "^5.22.0",
|
||||
"react": "19.0.0-rc-66855b96-20241106",
|
||||
"react-dom": "19.0.0-rc-66855b96-20241106",
|
||||
"next": "15.0.3"
|
||||
"tailwind-merge": "^2.5.4",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "15.0.3",
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "15.0.3"
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
|
||||
101
prisma/constant.ts
Normal file
101
prisma/constant.ts
Normal file
@ -0,0 +1,101 @@
|
||||
export const categories = [
|
||||
{
|
||||
name: "Vehicles",
|
||||
},
|
||||
{
|
||||
name: "Real Estate",
|
||||
},
|
||||
{
|
||||
name: "Electronics",
|
||||
},
|
||||
{
|
||||
name: "Fashion",
|
||||
},
|
||||
{
|
||||
name: "Sports",
|
||||
},
|
||||
{
|
||||
name: "Art",
|
||||
},
|
||||
{
|
||||
name: "Books",
|
||||
},
|
||||
{
|
||||
name: "etc",
|
||||
},
|
||||
]
|
||||
|
||||
export const adts = [
|
||||
{
|
||||
title: "2020 Tesla Model 3 Long Range",
|
||||
price: "$41,999",
|
||||
location: "San Francisco, CA",
|
||||
image: "https://images.unsplash.com/photo-1560958089-b8a1929cea89?auto=format&fit=crop&w=800",
|
||||
// date: "Posted 2 hours ago"
|
||||
userId: 1
|
||||
},
|
||||
{
|
||||
title: "Modern Studio Apartment in Downtown",
|
||||
price: "$2,200/mo",
|
||||
location: "Seattle, WA",
|
||||
image: "https://images.unsplash.com/photo-1522708323590-d24dbb6b0267?auto=format&fit=crop&w=800",
|
||||
// date: "Posted 5 hours ago"
|
||||
userId: 1
|
||||
},
|
||||
{
|
||||
title: "MacBook Pro M2 16-inch",
|
||||
price: "$2,499",
|
||||
location: "Austin, TX",
|
||||
image: "https://images.unsplash.com/photo-1517336714731-489689fd1ca8?auto=format&fit=crop&w=800",
|
||||
// date: "Posted 1 day ago"
|
||||
userId: 1
|
||||
},
|
||||
{
|
||||
title: "Vintage Leather Jacket",
|
||||
price: "$299",
|
||||
location: "Portland, OR",
|
||||
image: "https://images.unsplash.com/photo-1551028719-00167b16eac5?auto=format&fit=crop&w=800",
|
||||
// date: "Posted 2 days ago"
|
||||
userId: 2
|
||||
},
|
||||
{
|
||||
title: "Professional DSLR Camera Kit",
|
||||
price: "$1,899",
|
||||
location: "New York, NY",
|
||||
image: "https://images.unsplash.com/photo-1516035069371-29a1b244cc32?auto=format&fit=crop&w=800",
|
||||
// date: "Posted 3 days ago"
|
||||
userId: 1
|
||||
},
|
||||
{
|
||||
title: "Handcrafted Wooden Dining Table",
|
||||
price: "$899",
|
||||
location: "Denver, CO",
|
||||
image: "https://images.unsplash.com/photo-1577140917170-285929fb55b7?auto=format&fit=crop&w=800",
|
||||
// date: "Posted 4 days ago"
|
||||
userId: 2
|
||||
},
|
||||
{
|
||||
title: "Smart Bluetooth Speaker",
|
||||
price: "$99",
|
||||
location: "Los Angeles, CA",
|
||||
image: "https://images.unsplash.com/photo-1517336714731-489689fd1ca8?auto=format&fit=crop&w=800",
|
||||
// date: "Posted 5 days ago"
|
||||
userId: 1
|
||||
},
|
||||
{
|
||||
title: "Luxury Designer Watch",
|
||||
price: "$1,299",
|
||||
location: "Chicago, IL",
|
||||
image: "https://images.unsplash.com/photo-1551028719-00167b16eac5?auto=format&fit=crop&w=800",
|
||||
// date: "Posted 6 days ago"
|
||||
userId: 1
|
||||
},
|
||||
{
|
||||
title: "Gaming Laptop",
|
||||
price: "$1,499",
|
||||
location: "Miami, FL",
|
||||
image: "https://images.unsplash.com/photo-1516035069371-29a1b244cc32?auto=format&fit=crop&w=800",
|
||||
// date: "Posted 7 days ago"
|
||||
userId: 1
|
||||
}
|
||||
];
|
||||
13
prisma/prisma-client.ts
Normal file
13
prisma/prisma-client.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
const prismaClientSingleton = () => {
|
||||
return new PrismaClient()
|
||||
}
|
||||
|
||||
declare const globalThis: {
|
||||
prismaGlobal: ReturnType<typeof prismaClientSingleton>;
|
||||
} & typeof global;
|
||||
|
||||
export const prisma = globalThis.prismaGlobal ?? prismaClientSingleton()
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') globalThis.prismaGlobal = prisma
|
||||
62
prisma/schema.prisma
Normal file
62
prisma/schema.prisma
Normal file
@ -0,0 +1,62 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("POSTGRES_URL")
|
||||
directUrl = env("POSTGRES_URL_NON_POOLING")
|
||||
|
||||
}
|
||||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
email String @unique
|
||||
name String?
|
||||
password String
|
||||
|
||||
role Role @default(USER)
|
||||
|
||||
adts Adt[]
|
||||
// favoriteAdts Adt[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
}
|
||||
|
||||
model Adt {
|
||||
id Int @id @default(autoincrement())
|
||||
|
||||
title String
|
||||
description String?
|
||||
price String?
|
||||
location String?
|
||||
image String?
|
||||
status Status @default(CHECKING)
|
||||
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
userId Int
|
||||
|
||||
categories Category[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Category {
|
||||
id Int @id @default(autoincrement())
|
||||
name String
|
||||
adts Adt[]
|
||||
}
|
||||
|
||||
enum Role {
|
||||
USER
|
||||
ADMIN
|
||||
}
|
||||
|
||||
enum Status {
|
||||
CHECKING
|
||||
PUBLISHED
|
||||
CLOSED
|
||||
}
|
||||
67
prisma/seed.ts
Normal file
67
prisma/seed.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import { adts, categories } from "./constant";
|
||||
import { prisma } from "./prisma-client";
|
||||
import { hashSync } from "bcrypt";
|
||||
|
||||
|
||||
async function up() {
|
||||
await prisma.user.createMany({
|
||||
data: [
|
||||
{
|
||||
name: "user",
|
||||
email: "j@j.com",
|
||||
password: hashSync("123456", 10),
|
||||
// verified: new Date(),
|
||||
role: "USER",
|
||||
},
|
||||
{
|
||||
name: "user2",
|
||||
email: "da@j.com",
|
||||
password: hashSync("123456", 10),
|
||||
// verified: new Date(),
|
||||
role: "USER",
|
||||
},
|
||||
{
|
||||
name: "admin",
|
||||
email: "d@j.com",
|
||||
password: hashSync("123456", 10),
|
||||
// verified: new Date(),
|
||||
role: "ADMIN",
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
await prisma.category.createMany({
|
||||
data: categories
|
||||
});
|
||||
|
||||
await prisma.adt.createMany({
|
||||
data: adts
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async function down() {
|
||||
await prisma.$executeRaw`TRUNCATE TABLE "User" RESTART IDENTITY CASCADE`;
|
||||
await prisma.$executeRaw`TRUNCATE TABLE "Category" RESTART IDENTITY CASCADE`;
|
||||
await prisma.$executeRaw`TRUNCATE TABLE "Adt" RESTART IDENTITY CASCADE`;
|
||||
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
await down();
|
||||
await up();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
.then(async() => {
|
||||
await prisma.$disconnect()
|
||||
})
|
||||
.catch(async (e) => {
|
||||
console.error(e);
|
||||
await prisma.$disconnect()
|
||||
process.exit(1)
|
||||
})
|
||||
9
services/adt.ts
Normal file
9
services/adt.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { Adt } from "@prisma/client"
|
||||
import { axiosInstance } from "./instance"
|
||||
import { ApiRoutes } from "./constant";
|
||||
|
||||
export const search = async (query: string): Promise<Adt[]> => {
|
||||
const {data} = await axiosInstance.get<Adt[]>(ApiRoutes.ADT, {params: {query}});
|
||||
|
||||
return data;
|
||||
}
|
||||
7
services/api-client.ts
Normal file
7
services/api-client.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import * as adts from './adt';
|
||||
import * as categorys from './category';
|
||||
|
||||
export const Api = {
|
||||
adts,
|
||||
categorys
|
||||
}
|
||||
9
services/category.ts
Normal file
9
services/category.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { Category } from "@prisma/client"
|
||||
import { axiosInstance } from "./instance"
|
||||
import { ApiRoutes } from "./constant";
|
||||
|
||||
export const getAll = async (): Promise<Category[]> => {
|
||||
const {data} = await axiosInstance.get<Category[]>(ApiRoutes.CATEGORY);
|
||||
|
||||
return data;
|
||||
}
|
||||
5
services/constant.tsx
Normal file
5
services/constant.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
export enum ApiRoutes {
|
||||
SEARCH_PRODUCTS = 'products/search',
|
||||
CATEGORY = 'category',
|
||||
ADT = 'adt',
|
||||
}
|
||||
5
services/instance.ts
Normal file
5
services/instance.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import axios from "axios";
|
||||
|
||||
export const axiosInstance = axios.create({
|
||||
baseURL: process.env.NEXT_PUBLIC_API_URL,
|
||||
});
|
||||
@ -1,6 +1,7 @@
|
||||
import type { Config } from "tailwindcss";
|
||||
|
||||
export default {
|
||||
darkMode: ["class"],
|
||||
content: [
|
||||
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
@ -9,10 +10,15 @@ export default {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
background: "var(--background)",
|
||||
foreground: "var(--foreground)",
|
||||
background: 'var(--background)',
|
||||
foreground: 'var(--foreground)'
|
||||
},
|
||||
borderRadius: {
|
||||
lg: 'var(--radius)',
|
||||
md: 'calc(var(--radius) - 2px)',
|
||||
sm: 'calc(var(--radius) - 4px)'
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
plugins: [require("tailwindcss-animate")],
|
||||
} satisfies Config;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user