diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..9ddb0ad
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,27 @@
+FROM node:22-alpine AS base
+WORKDIR /app
+
+FROM base AS deps
+COPY package.json package-lock.json* ./
+RUN npm ci
+
+FROM base AS builder
+COPY --from=deps /app/node_modules ./node_modules
+COPY . .
+RUN npm run build
+
+FROM base AS runner
+ENV NODE_ENV=production
+
+RUN addgroup --system --gid 1001 nodejs || true
+RUN adduser --system --uid 1001 nextjs || true
+
+COPY --from=builder /app/public ./public
+COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
+COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
+
+USER nextjs
+EXPOSE 3000
+ENV PORT=3000
+ENV HOSTNAME="0.0.0.0"
+CMD ["node", "server.js"]
diff --git a/next.config.ts b/next.config.ts
index 9df45bd..4629d1a 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -1,6 +1,10 @@
+import createNextIntlPlugin from "next-intl/plugin";
import type { NextConfig } from "next";
+const withNextIntl = createNextIntlPlugin();
+
const nextConfig: NextConfig = {
+ output: 'standalone',
images: {
remotePatterns: [
{
@@ -17,4 +21,4 @@ const nextConfig: NextConfig = {
},
};
-export default nextConfig;
+export default withNextIntl(nextConfig);
diff --git a/src/app/about/page.tsx b/src/app/[locale]/about/page.tsx
similarity index 100%
rename from src/app/about/page.tsx
rename to src/app/[locale]/about/page.tsx
diff --git a/src/app/contact/page.tsx b/src/app/[locale]/contact/page.tsx
similarity index 100%
rename from src/app/contact/page.tsx
rename to src/app/[locale]/contact/page.tsx
diff --git a/src/app/[locale]/page.tsx b/src/app/[locale]/page.tsx
new file mode 100644
index 0000000..450ab84
--- /dev/null
+++ b/src/app/[locale]/page.tsx
@@ -0,0 +1,67 @@
+import { getProducts } from "@/lib/woocommerce";
+import Header from "@/components/layout/Header";
+import Footer from "@/components/layout/Footer";
+import ProductCard from "@/components/product/ProductCard";
+
+export const metadata = {
+ title: "ManoonOils - Premium Natural Oils for Hair & Skin",
+ description: "Discover our premium collection of natural oils for hair and skin care. Handmade with love.",
+};
+
+export default async function Homepage() {
+ const products = await getProducts();
+ const publishedProducts = products.filter((p) => p.status === "publish").slice(0, 4);
+
+ return (
+
+
+
+ {/* Hero Section */}
+
+
+
+ ManoonOils
+
+
+ Premium Natural Oils for Hair & Skin
+
+
+ Shop Now
+
+
+
+
+ {/* Products Section */}
+ {publishedProducts.length > 0 && (
+
+
+
Our Products
+
+ {publishedProducts.map((product, index) => (
+
+ ))}
+
+
+
+ )}
+
+ {/* About Teaser */}
+
+
+
Natural & Pure
+
+ Our oils are crafted with love using only the finest natural ingredients.
+
+
+ Learn More
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/[locale]/products/[slug]/page.tsx b/src/app/[locale]/products/[slug]/page.tsx
new file mode 100644
index 0000000..e0a6e58
--- /dev/null
+++ b/src/app/[locale]/products/[slug]/page.tsx
@@ -0,0 +1,77 @@
+import { getProducts } from "@/lib/woocommerce";
+import Header from "@/components/layout/Header";
+import Footer from "@/components/layout/Footer";
+
+export async function generateStaticParams() {
+ try {
+ const products = await getProducts();
+ return products.map((product) => ({
+ slug: product.slug || product.id.toString(),
+ }));
+ } catch {
+ return [];
+ }
+}
+
+export default async function ProductPage({ params }: { params: Promise<{ slug: string }> }) {
+ const { slug } = await params;
+ let product = null;
+
+ try {
+ const products = await getProducts();
+ product = products.find((p) => (p.slug || p.id.toString()) === slug);
+ } catch (e) {
+ // Fallback
+ }
+
+ if (!product) {
+ return (
+
+
+
+
Product not found
+
+
+
+ );
+ }
+
+ const image = product.images?.[0]?.src || '/placeholder.jpg';
+ const price = product.sale_price || product.price;
+
+ return (
+
+
+
+
+
+
+
+

+
+
+
+
{product.name}
+
+
{price} RSD
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/products/page.tsx b/src/app/[locale]/products/page.tsx
similarity index 100%
rename from src/app/products/page.tsx
rename to src/app/[locale]/products/page.tsx
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 720b84b..1cd2e40 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -2,16 +2,25 @@ import "./globals.css";
import type { Metadata } from "next";
export const metadata: Metadata = {
- title: "ManoonOils - Premium Natural Oils for Hair & Skin",
+ title: {
+ default: "ManoonOils - Premium Natural Oils for Hair & Skin",
+ template: "%s | ManoonOils",
+ },
description: "Discover our premium collection of natural oils for hair and skin care. Handmade with love.",
robots: "index, follow",
+ openGraph: {
+ title: "ManoonOils - Premium Natural Oils for Hair & Skin",
+ description: "Discover our premium collection of natural oils for hair and skin care.",
+ type: "website",
+ locale: "en_US",
+ },
};
export default function RootLayout({
children,
-}: Readonly<{
+}: {
children: React.ReactNode;
-}>) {
+}) {
return (
diff --git a/src/app/page.tsx b/src/app/page.tsx
deleted file mode 100644
index 3093177..0000000
--- a/src/app/page.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import { getProducts } from "@/lib/woocommerce";
-import Header from "@/components/layout/Header";
-import Footer from "@/components/layout/Footer";
-import Hero from "@/components/home/Hero";
-import TickerBar from "@/components/home/TickerBar";
-import ProductShowcase from "@/components/home/ProductShowcase";
-import BrandStory from "@/components/home/BrandStory";
-import Testimonials from "@/components/home/Testimonials";
-import Newsletter from "@/components/home/Newsletter";
-
-export default async function Home() {
- const products = await getProducts();
-
- const publishedProducts = products.filter(
- (p) => p.status === "publish" && p.stock_status === "instock"
- );
-
- return (
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/src/app/products/[slug]/page.tsx b/src/app/products/[slug]/page.tsx
deleted file mode 100644
index 3fa6091..0000000
--- a/src/app/products/[slug]/page.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import { notFound } from "next/navigation";
-import { getProductBySlug, getProducts, formatPrice, getProductImage } from "@/lib/woocommerce";
-import Header from "@/components/layout/Header";
-import Footer from "@/components/layout/Footer";
-import ProductDetail from "@/components/product/ProductDetail";
-
-interface Props {
- params: Promise<{ slug: string }>;
-}
-
-export async function generateMetadata({ params }: Props) {
- const { slug } = await params;
- const product = await getProductBySlug(slug);
-
- if (!product) {
- return { title: "Product Not Found" };
- }
-
- return {
- title: `${product.name} - ManoonOils`,
- description: product.short_description || product.description.slice(0, 160),
- };
-}
-
-export default async function ProductPage({ params }: Props) {
- const { slug } = await params;
- const product = await getProductBySlug(slug);
-
- if (!product) {
- notFound();
- }
-
- const allProducts = await getProducts();
- const relatedProducts = allProducts
- .filter((p) => p.id !== product.id && p.status === "publish")
- .slice(0, 4);
-
- return (
-
-
-
-
-
- );
-}
diff --git a/src/app/robots.ts b/src/app/robots.ts
index 3ca300e..4393ae3 100644
--- a/src/app/robots.ts
+++ b/src/app/robots.ts
@@ -11,5 +11,6 @@ export default function robots(): MetadataRoute.Robots {
},
],
sitemap: `${baseUrl}/sitemap.xml`,
+ host: baseUrl,
};
}
diff --git a/src/components/cart/CartDrawer.tsx b/src/components/cart/CartDrawer.tsx
index b2d4132..cf0f6ae 100644
--- a/src/components/cart/CartDrawer.tsx
+++ b/src/components/cart/CartDrawer.tsx
@@ -47,7 +47,7 @@ export default function CartDrawer() {
Your cart is empty
diff --git a/src/components/home/Hero.tsx b/src/components/home/Hero.tsx
index 2071c0d..75cbae7 100644
--- a/src/components/home/Hero.tsx
+++ b/src/components/home/Hero.tsx
@@ -48,7 +48,7 @@ export default function Hero() {
transition={{ duration: 0.8, delay: 0.8 }}
>
Shop Now
diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx
index 1a70402..7a5dc8d 100644
--- a/src/components/layout/Footer.tsx
+++ b/src/components/layout/Footer.tsx
@@ -18,17 +18,17 @@ export default function Footer() {
Quick Links
-
-
+
Products
-
-
+
About Us
-
-
+
Contact
@@ -39,12 +39,12 @@ export default function Footer() {
Customer Service
-
-
+
Shipping Info
-
-
+
Returns
diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx
index a829c02..b3dca51 100644
--- a/src/components/layout/Header.tsx
+++ b/src/components/layout/Header.tsx
@@ -36,13 +36,13 @@ export default function Header() {
diff --git a/src/components/layout/MobileMenu.tsx b/src/components/layout/MobileMenu.tsx
index 73c40f3..df6ec49 100644
--- a/src/components/layout/MobileMenu.tsx
+++ b/src/components/layout/MobileMenu.tsx
@@ -9,10 +9,10 @@ interface MobileMenuProps {
export default function MobileMenu({ onClose }: MobileMenuProps) {
const menuItems = [
- { href: "/", label: "Home" },
- { href: "/products", label: "Products" },
- { href: "/about", label: "About" },
- { href: "/contact", label: "Contact" },
+ { href: "/en", label: "Home" },
+ { href: "/en/products", label: "Products" },
+ { href: "/en/about", label: "About" },
+ { href: "/en/contact", label: "Contact" },
];
return (
diff --git a/src/i18n/en.json b/src/i18n/messages/en.json
similarity index 100%
rename from src/i18n/en.json
rename to src/i18n/messages/en.json
diff --git a/src/i18n/sr.json b/src/i18n/messages/sr.json
similarity index 100%
rename from src/i18n/sr.json
rename to src/i18n/messages/sr.json
diff --git a/src/i18n/request.tsx b/src/i18n/request.tsx
new file mode 100644
index 0000000..187cb7b
--- /dev/null
+++ b/src/i18n/request.tsx
@@ -0,0 +1,15 @@
+import { getRequestConfig } from 'next-intl/server';
+import { routing } from './routing';
+
+export default getRequestConfig(async ({ requestLocale }) => {
+ let locale = await requestLocale;
+
+ if (!locale || !routing.locales.includes(locale as any)) {
+ locale = routing.defaultLocale;
+ }
+
+ return {
+ locale,
+ messages: (await import(`./messages/${locale}.json`)).default
+ };
+});
diff --git a/src/i18n/routing.ts b/src/i18n/routing.ts
new file mode 100644
index 0000000..7e9ea89
--- /dev/null
+++ b/src/i18n/routing.ts
@@ -0,0 +1,6 @@
+import { defineRouting } from 'next-intl/routing';
+
+export const routing = defineRouting({
+ locales: ['en', 'sr'],
+ defaultLocale: 'en'
+});
diff --git a/src/middleware.ts b/src/middleware.ts
index 3b6eab6..c628e1d 100644
--- a/src/middleware.ts
+++ b/src/middleware.ts
@@ -4,7 +4,6 @@ import { locales } from "./i18n";
export default createMiddleware({
locales,
defaultLocale: "en",
- localePrefix: "always",
});
export const config = {