Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## github Address
https://github.com/junyi04/OBar

## github page Address


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
Expand Down
10 changes: 10 additions & 0 deletions app/about/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export default function Loading() {
return (
<div className="min-h-screen bg-black flex items-center justify-center">
<div className="flex flex-col items-center gap-4">
<div className="w-12 h-12 border-4 border-pink-500 border-t-transparent rounded-full animate-spin"></div>
<p className="text-gray-400">로딩 중...</p>
</div>
</div>
);
}
16 changes: 9 additions & 7 deletions app/about/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { HeroSection } from '@/components/about/HeroSection';
import { MixologySection } from '@/components/about/MixologySection';
import { BrandStorySection } from '@/components/about/BrandStorySection';
import { CTASection } from '@/components/about/CTASection';
import type { Metadata } from "next";
import BrandStorySection from "@/components/about/BrandStorySection";
import MixologySection from "@/components/about/MixologySection";

export const metadata: Metadata = {
title: "Obar - About Us",
description: "Learn about Obar's story, values, and commitment to premium cocktails.",
};

export default function AboutPage() {
return (
<>
<HeroSection />
<MixologySection />
<BrandStorySection />
<CTASection />
<MixologySection />
</>
);
}
26 changes: 26 additions & 0 deletions app/dashboard/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"use client";

export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<div className="min-h-screen bg-black flex items-center justify-center px-4">
<div className="text-center">
<h2 className="text-3xl font-bold text-white mb-4">문제가 발생했습니다</h2>
<p className="text-gray-400 mb-8">
{error.message || "Dashboard를 불러올 수 없습니다."}
</p>
<button
onClick={() => reset()}
className="px-6 py-3 bg-pink-500 text-white rounded-lg hover:bg-pink-600 transition"
>
다시 시도
</button>
</div>
</div>
);
}
30 changes: 30 additions & 0 deletions app/dashboard/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export default function Loading() {
return (
<div className="min-h-screen bg-black py-12 px-4">
<div className="max-w-7xl mx-auto">
<div className="animate-pulse space-y-8">
<div className="h-10 bg-gray-800 rounded w-1/3"></div>
<div className="h-4 bg-gray-800 rounded w-1/2"></div>

<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
{[...Array(4)].map((_, i) => (
<div key={i} className="h-24 bg-gray-800 rounded"></div>
))}
</div>

<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{[...Array(2)].map((_, i) => (
<div key={i} className="h-64 bg-gray-800 rounded"></div>
))}
</div>

<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
{[...Array(8)].map((_, i) => (
<div key={i} className="h-80 bg-gray-800 rounded"></div>
))}
</div>
</div>
</div>
</div>
);
}
38 changes: 23 additions & 15 deletions app/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
import { StatCard } from "@/components/dashboard/StatCard";
import { SeachBar } from "@/components/dashboard/SearchBar";
import { CocktailMapping } from "@/components/dashboard/CocktailMapping";
import type { Metadata } from "next";
import SearchBar from "@/components/dashboard/SearchBar";
import CategoryFilter from "@/components/dashboard/CategoryFilter";
import StatsCards from "@/components/dashboard/StatsCards";
import ChartSection from "@/components/dashboard/ChartSection";
import CocktailGrid from "@/components/dashboard/CocktailGrid";

export const metadata: Metadata = {
title: "Obar - Cocktail Dashboard",
description: "Explore our collection of premium cocktails and discover your favorite.",
};

export default function DashboardPage() {
return (
<main className="container mx-auto px-4 py-8">
<h1 className="text-4xl font-bold text-slate-100 my-6">Welcome to the OBar!</h1>
<div className="min-h-screen bg-black py-12 px-4 md:px-8">
<div className="max-w-7xl mx-auto">
<h1 className="text-4xl font-bold text-white mb-2">Cocktail Dashboard</h1>
<p className="text-gray-400 mb-8">
Explore our collection of premium cocktails and discover your favorite
</p>

{/* 통계 카드 영역 */}
<StatCard />

{/* 검색창 영역 */}
<SeachBar />

{/* 칵테일 리스트 */}
<CocktailMapping />

</main>
<StatsCards />
<ChartSection />
<SearchBar />
<CategoryFilter />
<CocktailGrid />
</div>
</div>
);
}
36 changes: 36 additions & 0 deletions app/detail/[id]/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"use client";

import Link from "next/link";

export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<div className="min-h-screen bg-black flex items-center justify-center px-4">
<div className="text-center">
<h2 className="text-3xl font-bold text-white mb-4">칵테일을 찾을 수 없습니다</h2>
<p className="text-gray-400 mb-8">
{error.message || "요청하신 칵테일 정보를 불러올 수 없습니다."}
</p>
<div className="flex gap-4 justify-center">
<button
onClick={() => reset()}
className="px-6 py-3 bg-pink-500 text-white rounded-lg hover:bg-pink-600 transition"
>
다시 시도
</button>
<Link
href="/dashboard"
className="px-6 py-3 border border-pink-500 text-pink-500 rounded-lg hover:bg-pink-500 hover:text-white transition"
>
대시보드로 돌아가기
</Link>
</div>
</div>
</div>
);
}
23 changes: 23 additions & 0 deletions app/detail/[id]/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export default function Loading() {
return (
<div className="min-h-screen bg-black py-12 px-4">
<div className="max-w-6xl mx-auto">
<div className="animate-pulse space-y-8">
<div className="h-8 bg-gray-800 rounded w-1/4"></div>

<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
<div className="h-96 bg-gray-800 rounded"></div>
<div className="space-y-4">
<div className="h-12 bg-gray-800 rounded"></div>
<div className="h-24 bg-gray-800 rounded"></div>
<div className="h-32 bg-gray-800 rounded"></div>
</div>
</div>

<div className="h-64 bg-gray-800 rounded"></div>
<div className="h-80 bg-gray-800 rounded"></div>
</div>
</div>
</div>
);
}
78 changes: 44 additions & 34 deletions app/detail/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,51 @@
'use client';

import { usePathname } from 'next/navigation';
import cocktailData from "@/lib/cocktails.json";
import { useEffect, useState } from 'react';
import { Cocktail } from '@/types/cocktails';
import { ImageCocktail } from '@/components/detail/ImageCocktail';
import { HeaderCocktail } from '@/components/detail/HeaderCocktail';
import { IngredientCocktail } from '@/components/detail/IngredientCocktail';
import { InstructionCocktail } from '@/components/detail/InstructionCocktail';

export default function DetailPage() {
const pathname = usePathname();
const id = pathname?.split('/').pop();
const [cocktail, setCocktail] = useState<Cocktail | null>(null);

useEffect(() => {
if (id) {
const foundCocktail = cocktailData.find((c) => c.id === Number(id));
setCocktail(foundCocktail || null);
}
}, [id]);
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import cocktailsData from "@/lib/cocktails.json";
import CocktailHero from "@/components/detail/CocktailHero";
import RecipeSection from "@/components/detail/RecipeSection";
import RelatedCocktails from "@/components/detail/RelatedCocktails";

interface DetailPageProps {
params: Promise<{ id: string }>;
}

export async function generateMetadata({
params,
}: DetailPageProps): Promise<Metadata> {
const { id } = await params;
const cocktail = cocktailsData.cocktails.find((c) => String(c.id) === id);

if (!cocktail) {
return <p>Loading...</p>;
return { title: "Cocktail Not Found" };
}

return {
title: `${cocktail.name} - Obar`,
description: cocktail.description,
};
}

export function generateStaticParams() {
return cocktailsData.cocktails.map((cocktail) => ({
id: String(cocktail.id),
}));
}

export default async function DetailPage({ params }: DetailPageProps) {
const { id } = await params;
const cocktail = cocktailsData.cocktails.find((c) => String(c.id) === id);

if (!cocktail) {
notFound();
}

return (
<main className="container mx-auto p-4 bg-slate-950 text-slate-200">
{/* 상단 */}
<div className="flex flex-col items-center gap-12 lg:grid lg:grid-cols-[auto_1fr] lg:items-start lg:gap-16 max-w-4xl mx-auto">
<ImageCocktail image={cocktail.image} name={cocktail.name} />
<HeaderCocktail cocktail={cocktail} />
<div className="min-h-screen bg-black py-12 px-4 md:px-8">
<div className="max-w-6xl mx-auto">
<CocktailHero cocktail={cocktail} />
<RecipeSection cocktail={cocktail} />
<RelatedCocktails currentId={cocktail.id} />
</div>

{/* 하단 */}
<IngredientCocktail ingredients={cocktail.ingredients} />
<InstructionCocktail instructions={cocktail.instructions} />
</main>
</div>
);
};
}
14 changes: 14 additions & 0 deletions app/detail/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { Metadata } from "next";

export const metadata: Metadata = {
title: "Obar - Cocktail Details",
description: "Explore detailed information about our premium cocktails.",
};

export default function DetailLayout({
children,
}: {
children: React.ReactNode;
}) {
return <>{children}</>;
}
45 changes: 21 additions & 24 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { Header } from "@/components/Header";
import Header from "@/components/Header";
import Footer from "@/components/Footer";

const geistSans = Geist({
variable: "--font-geist-sans",
Expand All @@ -14,36 +15,32 @@ const geistMono = Geist_Mono({
});

export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
title: "Obar - Premium Cocktail Experience",
description: "Discover premium cocktails at Obar. OH, bar. Over the ordinary.",
keywords: "cocktail, bar, mixology, premium drinks",
authors: [{ name: "Obar" }],
openGraph: {
title: "Obar - Premium Cocktail Experience",
description: "Discover premium cocktails at Obar",
type: "website",
url: "https://obar.com",
},
};
interface RootLayoutProps {
children: React.ReactNode;
about: React.ReactNode;
dashboard: React.ReactNode;
detail: React.ReactNode;
}

export default function RootLayout({
children,
about,
dashboard,
detail,
}: RootLayoutProps) {
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<html lang="ko">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased min-h-screen flex flex-col`}
className={`${geistSans.variable} ${geistMono.variable} antialiased bg-black text-white`}
>
<main className="flex-grow flex flex-col">
<Header />
{children}

<section>{about}</section>
<section>{dashboard}</section>
<section>{detail}</section>
</main>
<Header />
<main className="min-h-screen">{children}</main>
<Footer />
</body>
</html>
);
}
}
Loading