feat: implement proper i18n translations for Serbian and English

- Add comprehensive Serbian translation file (sr.json) with all UI strings
- Add comprehensive English translation file (en.json) with all UI strings
- Update Serbian root pages (/, /products, /products/[slug], /about, /contact) to use getTranslations()
- Update English pages (/en/*) to use getTranslations()
- Replace all hardcoded strings with translation keys
This commit is contained in:
Unchained
2026-03-23 18:28:00 +02:00
parent f72f32fe60
commit bcc51ce282
11 changed files with 700 additions and 577 deletions

View File

@@ -1,117 +1,113 @@
import { getTranslations } from "next-intl/server";
import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer";
export const metadata = {
title: "About - ManoonOils",
description: "Learn about ManoonOils - our story, mission, and commitment to natural beauty.",
};
export async function generateMetadata() {
return {
title: "O nama - ManoonOils",
description: "Saznajte više o ManoonOils - naša priča, misija i posvećenost prirodnoj lepoti.",
};
}
export default async function AboutPage() {
const t = await getTranslations("About");
export default function AboutPage() {
return (
<>
<Header />
<main className="min-h-screen bg-white">
{/* Page Header */}
<div className="pt-[104px]">
<div className="container py-12 md:py-16">
<div className="max-w-2xl mx-auto text-center">
<span className="text-xs uppercase tracking-[0.2em] text-[#666666] mb-3 block">Our Story</span>
<span className="text-xs uppercase tracking-[0.2em] text-[#666666] mb-3 block">
{t("subtitle")}
</span>
<h1 className="text-4xl md:text-5xl font-medium tracking-tight">
About ManoonOils
{t("title")}
</h1>
</div>
</div>
</div>
{/* Hero Image */}
<div className="relative h-[400px] md:h-[500px] overflow-hidden">
<img
src="https://images.unsplash.com/photo-1608571423902-eed4a5ad8108?q=80&w=2000&auto=format&fit=crop"
alt="Natural oils production"
alt="Proizvodnja prirodnih ulja"
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-black/20" />
</div>
{/* Content */}
<section className="py-16 md:py-24">
<div className="container">
<div className="max-w-3xl mx-auto">
{/* Introduction */}
<div className="mb-16">
<p className="text-xl md:text-2xl text-[#1a1a1a] leading-relaxed mb-8">
ManoonOils was born from a passion for natural beauty and the belief
that the best skincare comes from nature itself.
{t("intro")}
</p>
<p className="text-[#666666] leading-relaxed">
We believe in the power of natural ingredients. Every oil in our
collection is carefully selected for its unique properties and
benefits. From nourishing oils that restore hair vitality to serums
that rejuvenate skin, we craft each product with love and attention
to detail.
{t("intro2")}
</p>
</div>
{/* Values Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-12 mb-16">
<div className="p-6 bg-[#f8f9fa]">
<h3 className="text-lg font-medium mb-3">Natural Ingredients</h3>
<h3 className="text-lg font-medium mb-3">
{t("naturalIngredients")}
</h3>
<p className="text-[#666666] text-sm leading-relaxed">
We use only the finest natural ingredients, sourced ethically and sustainably
from trusted suppliers around the world.
{t("naturalIngredientsDesc")}
</p>
</div>
<div className="p-6 bg-[#f8f9fa]">
<h3 className="text-lg font-medium mb-3">Cruelty-Free</h3>
<h3 className="text-lg font-medium mb-3">
{t("crueltyFree")}
</h3>
<p className="text-[#666666] text-sm leading-relaxed">
Our products are never tested on animals. We believe in beauty
without compromise.
{t("crueltyFreeDesc")}
</p>
</div>
<div className="p-6 bg-[#f8f9fa]">
<h3 className="text-lg font-medium mb-3">Sustainable Packaging</h3>
<h3 className="text-lg font-medium mb-3">
{t("sustainablePackaging")}
</h3>
<p className="text-[#666666] text-sm leading-relaxed">
We use eco-friendly packaging materials and minimize waste
throughout our production process.
{t("sustainablePackagingDesc")}
</p>
</div>
<div className="p-6 bg-[#f8f9fa]">
<h3 className="text-lg font-medium mb-3">Handcrafted Quality</h3>
<h3 className="text-lg font-medium mb-3">
{t("handcraftedQuality")}
</h3>
<p className="text-[#666666] text-sm leading-relaxed">
Every bottle is handcrafted in small batches to ensure
the highest quality and freshness.
{t("handcraftedQualityDesc")}
</p>
</div>
</div>
{/* Mission */}
<div className="text-center py-12 border-t border-b border-[#e5e5e5]">
<span className="text-caption text-[#666666] mb-4 block">Our Mission</span>
<span className="text-caption text-[#666666] mb-4 block">
{t("mission")}
</span>
<blockquote className="text-2xl md:text-3xl font-medium tracking-tight">
&ldquo;To provide premium quality, natural products that enhance
your daily beauty routine.&rdquo;
&ldquo;{t("missionQuote")}&rdquo;
</blockquote>
</div>
{/* Story Section */}
<div className="mt-16">
<h2 className="text-2xl font-medium mb-6">Handmade with Love</h2>
<h2 className="text-2xl font-medium mb-6">
{t("handmadeTitle")}
</h2>
<p className="text-[#666666] leading-relaxed mb-6">
Every bottle of ManoonOils is handcrafted with care. We small-batch
produce our products to ensure the highest quality and freshness.
When you use ManoonOils, you can feel confident that you&apos;re using
something made with genuine care and expertise.
{t("handmadeText1")}
</p>
<p className="text-[#666666] leading-relaxed">
Our journey began with a simple question: how can we create products
that truly nurture both hair and skin? Today, we continue to innovate
while staying true to our commitment to natural, effective beauty solutions.
{t("handmadeText2")}
</p>
</div>
</div>
</div>
</section>
</main>
<div className="pt-16">
<Footer />

View File

@@ -1,11 +1,13 @@
"use client";
import { useState } from "react";
import { useTranslations } from "next-intl";
import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer";
import { Mail, MapPin, Truck, Check } from "lucide-react";
export default function ContactPage() {
const t = useTranslations("Contact");
const [formData, setFormData] = useState({
name: "",
email: "",
@@ -22,31 +24,31 @@ export default function ContactPage() {
<>
<Header />
<main className="min-h-screen bg-white">
{/* Page Header */}
<div className="pt-[104px]">
<div className="container py-12 md:py-16">
<div className="max-w-2xl mx-auto text-center">
<span className="text-xs uppercase tracking-[0.2em] text-[#666666] mb-3 block">Get in Touch</span>
<span className="text-xs uppercase tracking-[0.2em] text-[#666666] mb-3 block">
{t("subtitle")}
</span>
<h1 className="text-4xl md:text-5xl font-medium tracking-tight mb-4">
Contact Us
{t("title")}
</h1>
<p className="text-[#666666]">
Have questions? We&apos;d love to hear from you.
{t("getInTouchDesc")}
</p>
</div>
</div>
</div>
{/* Contact Section */}
<section className="py-12 md:py-16">
<div className="container">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-20">
{/* Contact Info */}
<div>
<h2 className="text-2xl font-medium mb-6">Get in Touch</h2>
<h2 className="text-2xl font-medium mb-6">
{t("getInTouch")}
</h2>
<p className="text-[#666666] mb-8 leading-relaxed">
We&apos;re here to help! Whether you have questions about our products,
need assistance with an order, or just want to say hello, we&apos;d love to hear from you.
{t("getInTouchDesc")}
</p>
<div className="space-y-6">
@@ -55,9 +57,9 @@ export default function ContactPage() {
<Mail className="w-5 h-5 text-[#666666]" strokeWidth={1.5} />
</div>
<div>
<h3 className="font-medium mb-1">Email</h3>
<h3 className="font-medium mb-1">{t("email")}</h3>
<p className="text-[#666666] text-sm">hello@manoonoils.com</p>
<p className="text-[#999999] text-xs mt-1">We reply within 24 hours</p>
<p className="text-[#999999] text-xs mt-1">{t("emailReply")}</p>
</div>
</div>
@@ -66,9 +68,9 @@ export default function ContactPage() {
<Truck className="w-5 h-5 text-[#666666]" strokeWidth={1.5} />
</div>
<div>
<h3 className="font-medium mb-1">Shipping</h3>
<p className="text-[#666666] text-sm">Free shipping over 3,000 RSD</p>
<p className="text-[#999999] text-xs mt-1">Delivered within 2-5 business days</p>
<h3 className="font-medium mb-1">{t("shippingTitle")}</h3>
<p className="text-[#666666] text-sm">{t("freeShipping")}</p>
<p className="text-[#999999] text-xs mt-1">{t("deliveryTime")}</p>
</div>
</div>
@@ -77,31 +79,30 @@ export default function ContactPage() {
<MapPin className="w-5 h-5 text-[#666666]" strokeWidth={1.5} />
</div>
<div>
<h3 className="font-medium mb-1">Location</h3>
<p className="text-[#666666] text-sm">Serbia</p>
<p className="text-[#999999] text-xs mt-1">Shipping nationwide</p>
<h3 className="font-medium mb-1">{t("location")}</h3>
<p className="text-[#666666] text-sm">{t("locationDesc")}</p>
<p className="text-[#999999] text-xs mt-1">{t("worldwideShipping")}</p>
</div>
</div>
</div>
</div>
{/* Contact Form */}
<div className="bg-[#f8f9fa] p-8 md:p-10">
{submitted ? (
<div className="text-center py-12">
<div className="w-16 h-16 rounded-full bg-green-100 flex items-center justify-center mx-auto mb-4">
<Check className="w-8 h-8 text-green-600" strokeWidth={1.5} />
</div>
<h3 className="text-xl font-medium mb-2">Thank You!</h3>
<h3 className="text-xl font-medium mb-2">{t("thankYou")}</h3>
<p className="text-[#666666]">
Your message has been sent. We&apos;ll get back to you soon.
{t("thankYouDesc")}
</p>
</div>
) : (
<form onSubmit={handleSubmit} className="space-y-6">
<div>
<label htmlFor="name" className="block text-sm font-medium mb-2">
Name
{t("name")}
</label>
<input
type="text"
@@ -110,13 +111,13 @@ export default function ContactPage() {
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
className="w-full px-4 py-3 bg-white border border-[#e5e5e5] focus:outline-none focus:border-black transition-colors"
placeholder="Your name"
placeholder={t("namePlaceholder")}
/>
</div>
<div>
<label htmlFor="email" className="block text-sm font-medium mb-2">
Email
{t("emailField")}
</label>
<input
type="email"
@@ -125,13 +126,13 @@ export default function ContactPage() {
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
className="w-full px-4 py-3 bg-white border border-[#e5e5e5] focus:outline-none focus:border-black transition-colors"
placeholder="your@email.com"
placeholder={t("emailPlaceholder")}
/>
</div>
<div>
<label htmlFor="message" className="block text-sm font-medium mb-2">
Message
{t("message")}
</label>
<textarea
id="message"
@@ -140,7 +141,7 @@ export default function ContactPage() {
value={formData.message}
onChange={(e) => setFormData({ ...formData, message: e.target.value })}
className="w-full px-4 py-3 bg-white border border-[#e5e5e5] focus:outline-none focus:border-black transition-colors resize-none"
placeholder="How can we help you?"
placeholder={t("messagePlaceholder")}
/>
</div>
@@ -148,7 +149,7 @@ export default function ContactPage() {
type="submit"
className="w-full py-4 bg-black text-white text-sm uppercase tracking-[0.1em] font-medium hover:bg-[#333333] transition-colors"
>
Send Message
{t("sendMessage")}
</button>
</form>
)}
@@ -157,32 +158,19 @@ export default function ContactPage() {
</div>
</section>
{/* FAQ Section */}
<section className="py-16 md:py-24 border-t border-[#e5e5e5]">
<div className="container">
<div className="max-w-3xl mx-auto">
<h2 className="text-2xl font-medium text-center mb-12">
Frequently Asked Questions
{t("faqTitle")}
</h2>
<div className="space-y-6">
{[
{
q: "How long does shipping take?",
a: "Orders are typically delivered within 2-5 business days for domestic shipping. You'll receive a tracking number once your order ships."
},
{
q: "Are your products 100% natural?",
a: "Yes! All our oils are 100% natural, cold-pressed, and free from any additives, preservatives, or artificial fragrances."
},
{
q: "What is your return policy?",
a: "We accept returns within 14 days of delivery for unopened products. Please contact us if you have any issues with your order."
},
{
q: "Do you offer wholesale?",
a: "Yes, we offer wholesale pricing for bulk orders. Please contact us at hello@manoonoils.com for more information."
}
{ q: t("faq1q"), a: t("faq1a") },
{ q: t("faq2q"), a: t("faq2a") },
{ q: t("faq3q"), a: t("faq3a") },
{ q: t("faq4q"), a: t("faq4a") },
].map((faq, index) => (
<div key={index} className="border-b border-[#e5e5e5] pb-6">
<h3 className="font-medium mb-2">{faq.q}</h3>

View File

@@ -1,12 +1,17 @@
import { getTranslations } from "next-intl/server";
import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer";
export const metadata = {
export async function generateMetadata() {
return {
title: "About - ManoonOils",
description: "Learn about ManoonOils - our story, mission, and commitment to natural beauty.",
};
};
}
export default async function AboutPage() {
const t = await getTranslations("About");
export default function AboutPage() {
return (
<>
<Header />
@@ -14,9 +19,11 @@ export default function AboutPage() {
<div className="pt-[104px]">
<div className="container py-12 md:py-16">
<div className="max-w-2xl mx-auto text-center">
<span className="text-xs uppercase tracking-[0.2em] text-[#666666] mb-3 block">Our Story</span>
<span className="text-xs uppercase tracking-[0.2em] text-[#666666] mb-3 block">
{t("subtitle")}
</span>
<h1 className="text-4xl md:text-5xl font-medium tracking-tight">
About ManoonOils
{t("title")}
</h1>
</div>
</div>
@@ -36,69 +43,66 @@ export default function AboutPage() {
<div className="max-w-3xl mx-auto">
<div className="mb-16">
<p className="text-xl md:text-2xl text-[#1a1a1a] leading-relaxed mb-8">
ManoonOils was born from a passion for natural beauty and the belief
that the best skincare comes from nature itself.
{t("intro")}
</p>
<p className="text-[#666666] leading-relaxed">
We believe in the power of natural ingredients. Every oil in our
collection is carefully selected for its unique properties and
benefits. From nourishing oils that restore hair vitality to serums
that rejuvenate skin, we craft each product with love and attention
to detail.
{t("intro2")}
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-12 mb-16">
<div className="p-6 bg-[#f8f9fa]">
<h3 className="text-lg font-medium mb-3">Natural Ingredients</h3>
<h3 className="text-lg font-medium mb-3">
{t("naturalIngredients")}
</h3>
<p className="text-[#666666] text-sm leading-relaxed">
We use only the finest natural ingredients, sourced ethically and sustainably
from trusted suppliers around the world.
{t("naturalIngredientsDesc")}
</p>
</div>
<div className="p-6 bg-[#f8f9fa]">
<h3 className="text-lg font-medium mb-3">Cruelty-Free</h3>
<h3 className="text-lg font-medium mb-3">
{t("crueltyFree")}
</h3>
<p className="text-[#666666] text-sm leading-relaxed">
Our products are never tested on animals. We believe in beauty
without compromise.
{t("crueltyFreeDesc")}
</p>
</div>
<div className="p-6 bg-[#f8f9fa]">
<h3 className="text-lg font-medium mb-3">Sustainable Packaging</h3>
<h3 className="text-lg font-medium mb-3">
{t("sustainablePackaging")}
</h3>
<p className="text-[#666666] text-sm leading-relaxed">
We use eco-friendly packaging materials and minimize waste
throughout our production process.
{t("sustainablePackagingDesc")}
</p>
</div>
<div className="p-6 bg-[#f8f9fa]">
<h3 className="text-lg font-medium mb-3">Handcrafted Quality</h3>
<h3 className="text-lg font-medium mb-3">
{t("handcraftedQuality")}
</h3>
<p className="text-[#666666] text-sm leading-relaxed">
Every bottle is handcrafted in small batches to ensure
the highest quality and freshness.
{t("handcraftedQualityDesc")}
</p>
</div>
</div>
<div className="text-center py-12 border-t border-b border-[#e5e5e5]">
<span className="text-caption text-[#666666] mb-4 block">Our Mission</span>
<span className="text-caption text-[#666666] mb-4 block">
{t("mission")}
</span>
<blockquote className="text-2xl md:text-3xl font-medium tracking-tight">
&ldquo;To provide premium quality, natural products that enhance
your daily beauty routine.&rdquo;
&ldquo;{t("missionQuote")}&rdquo;
</blockquote>
</div>
<div className="mt-16">
<h2 className="text-2xl font-medium mb-6">Handmade with Love</h2>
<h2 className="text-2xl font-medium mb-6">
{t("handmadeTitle")}
</h2>
<p className="text-[#666666] leading-relaxed mb-6">
Every bottle of ManoonOils is handcrafted with care. We small-batch
produce our products to ensure the highest quality and freshness.
When you use ManoonOils, you can feel confident that you&apos;re using
something made with genuine care and expertise.
{t("handmadeText1")}
</p>
<p className="text-[#666666] leading-relaxed">
Our journey began with a simple question: how can we create products
that truly nurture both hair and skin? Today, we continue to innovate
while staying true to our commitment to natural, effective beauty solutions.
{t("handmadeText2")}
</p>
</div>
</div>

View File

@@ -1,11 +1,13 @@
"use client";
import { useState } from "react";
import { useTranslations } from "next-intl";
import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer";
import { Mail, MapPin, Truck, Check } from "lucide-react";
export default function ContactPage() {
const t = useTranslations("Contact");
const [formData, setFormData] = useState({
name: "",
email: "",
@@ -25,12 +27,14 @@ export default function ContactPage() {
<div className="pt-[104px]">
<div className="container py-12 md:py-16">
<div className="max-w-2xl mx-auto text-center">
<span className="text-xs uppercase tracking-[0.2em] text-[#666666] mb-3 block">Get in Touch</span>
<span className="text-xs uppercase tracking-[0.2em] text-[#666666] mb-3 block">
{t("subtitle")}
</span>
<h1 className="text-4xl md:text-5xl font-medium tracking-tight mb-4">
Contact Us
{t("title")}
</h1>
<p className="text-[#666666]">
Have questions? We&apos;d love to hear from you.
{t("getInTouchDesc")}
</p>
</div>
</div>
@@ -40,10 +44,11 @@ export default function ContactPage() {
<div className="container">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-20">
<div>
<h2 className="text-2xl font-medium mb-6">Get in Touch</h2>
<h2 className="text-2xl font-medium mb-6">
{t("getInTouch")}
</h2>
<p className="text-[#666666] mb-8 leading-relaxed">
We&apos;re here to help! Whether you have questions about our products,
need assistance with an order, or just want to say hello, we&apos;d love to hear from you.
{t("getInTouchDesc")}
</p>
<div className="space-y-6">
@@ -52,9 +57,9 @@ export default function ContactPage() {
<Mail className="w-5 h-5 text-[#666666]" strokeWidth={1.5} />
</div>
<div>
<h3 className="font-medium mb-1">Email</h3>
<h3 className="font-medium mb-1">{t("email")}</h3>
<p className="text-[#666666] text-sm">hello@manoonoils.com</p>
<p className="text-[#999999] text-xs mt-1">We reply within 24 hours</p>
<p className="text-[#999999] text-xs mt-1">{t("emailReply")}</p>
</div>
</div>
@@ -63,9 +68,9 @@ export default function ContactPage() {
<Truck className="w-5 h-5 text-[#666666]" strokeWidth={1.5} />
</div>
<div>
<h3 className="font-medium mb-1">Shipping</h3>
<p className="text-[#666666] text-sm">Free shipping over £50</p>
<p className="text-[#999999] text-xs mt-1">Delivered within 2-5 business days</p>
<h3 className="font-medium mb-1">{t("shippingTitle")}</h3>
<p className="text-[#666666] text-sm">{t("freeShipping")}</p>
<p className="text-[#999999] text-xs mt-1">{t("deliveryTime")}</p>
</div>
</div>
@@ -74,9 +79,9 @@ export default function ContactPage() {
<MapPin className="w-5 h-5 text-[#666666]" strokeWidth={1.5} />
</div>
<div>
<h3 className="font-medium mb-1">Location</h3>
<p className="text-[#666666] text-sm">Serbia</p>
<p className="text-[#999999] text-xs mt-1">Shipping worldwide</p>
<h3 className="font-medium mb-1">{t("location")}</h3>
<p className="text-[#666666] text-sm">{t("locationDesc")}</p>
<p className="text-[#999999] text-xs mt-1">{t("worldwideShipping")}</p>
</div>
</div>
</div>
@@ -88,16 +93,16 @@ export default function ContactPage() {
<div className="w-16 h-16 rounded-full bg-green-100 flex items-center justify-center mx-auto mb-4">
<Check className="w-8 h-8 text-green-600" strokeWidth={1.5} />
</div>
<h3 className="text-xl font-medium mb-2">Thank You!</h3>
<h3 className="text-xl font-medium mb-2">{t("thankYou")}</h3>
<p className="text-[#666666]">
Your message has been sent. We&apos;ll get back to you soon.
{t("thankYouDesc")}
</p>
</div>
) : (
<form onSubmit={handleSubmit} className="space-y-6">
<div>
<label htmlFor="name" className="block text-sm font-medium mb-2">
Name
{t("name")}
</label>
<input
type="text"
@@ -106,13 +111,13 @@ export default function ContactPage() {
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
className="w-full px-4 py-3 bg-white border border-[#e5e5e5] focus:outline-none focus:border-black transition-colors"
placeholder="Your name"
placeholder={t("namePlaceholder")}
/>
</div>
<div>
<label htmlFor="email" className="block text-sm font-medium mb-2">
Email
{t("emailField")}
</label>
<input
type="email"
@@ -121,13 +126,13 @@ export default function ContactPage() {
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
className="w-full px-4 py-3 bg-white border border-[#e5e5e5] focus:outline-none focus:border-black transition-colors"
placeholder="your@email.com"
placeholder={t("emailPlaceholder")}
/>
</div>
<div>
<label htmlFor="message" className="block text-sm font-medium mb-2">
Message
{t("message")}
</label>
<textarea
id="message"
@@ -136,7 +141,7 @@ export default function ContactPage() {
value={formData.message}
onChange={(e) => setFormData({ ...formData, message: e.target.value })}
className="w-full px-4 py-3 bg-white border border-[#e5e5e5] focus:outline-none focus:border-black transition-colors resize-none"
placeholder="How can we help you?"
placeholder={t("messagePlaceholder")}
/>
</div>
@@ -144,7 +149,7 @@ export default function ContactPage() {
type="submit"
className="w-full py-4 bg-black text-white text-sm uppercase tracking-[0.1em] font-medium hover:bg-[#333333] transition-colors"
>
Send Message
{t("sendMessage")}
</button>
</form>
)}
@@ -157,27 +162,15 @@ export default function ContactPage() {
<div className="container">
<div className="max-w-3xl mx-auto">
<h2 className="text-2xl font-medium text-center mb-12">
Frequently Asked Questions
{t("faqTitle")}
</h2>
<div className="space-y-6">
{[
{
q: "How long does shipping take?",
a: "Orders are typically delivered within 2-5 business days for domestic shipping. You'll receive a tracking number once your order ships."
},
{
q: "Are your products 100% natural?",
a: "Yes! All our oils are 100% natural, cold-pressed, and free from any additives, preservatives, or artificial fragrances."
},
{
q: "What is your return policy?",
a: "We accept returns within 14 days of delivery for unopened products. Please contact us if you have any issues with your order."
},
{
q: "Do you offer wholesale?",
a: "Yes, we offer wholesale pricing for bulk orders. Please contact us at hello@manoonoils.com for more information."
}
{ q: t("faq1q"), a: t("faq1a") },
{ q: t("faq2q"), a: t("faq2a") },
{ q: t("faq3q"), a: t("faq3a") },
{ q: t("faq4q"), a: t("faq4a") },
].map((faq, index) => (
<div key={index} className="border-b border-[#e5e5e5] pb-6">
<h3 className="font-medium mb-2">{faq.q}</h3>

View File

@@ -1,4 +1,5 @@
import { getProducts } from "@/lib/saleor";
import { getTranslations } from "next-intl/server";
import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer";
import HeroVideo from "@/components/home/HeroVideo";
@@ -10,13 +11,17 @@ import BeforeAfterGallery from "@/components/home/BeforeAfterGallery";
import ProblemSection from "@/components/home/ProblemSection";
import HowItWorks from "@/components/home/HowItWorks";
export const metadata = {
export async function generateMetadata() {
return {
title: "ManoonOils - Premium Natural Oils for Hair & Skin",
description:
"Discover our premium collection of natural oils for hair and skin care. Handmade with love using only the finest ingredients.",
};
description: "Discover our premium collection of natural oils for hair and skin care.",
};
}
export default async function EnglishHomepage() {
const t = await getTranslations("Home");
const tBenefits = await getTranslations("Benefits");
let products: any[] = [];
try {
products = await getProducts("UK");
@@ -44,19 +49,19 @@ export default async function EnglishHomepage() {
<BeforeAfterGallery />
<div id="main_content" className="scroll-mt-[72px] lg:scroll-mt-[72px]">
<div id="main-content" className="scroll-mt-[72px] lg:scroll-mt-[72px]">
{hasProducts && (
<section className="py-24 px-4 sm:px-6 lg:px-8 bg-white">
<div className="max-w-7xl mx-auto">
<div className="text-center mb-16">
<span className="text-xs uppercase tracking-[0.2em] text-[#666666] mb-3 block">
Our Collection
{t("collection")}
</span>
<h2 className="text-3xl md:text-4xl font-medium mb-4">
Premium Natural Oils
{t("premiumOils")}
</h2>
<p className="text-[#666666] max-w-xl mx-auto">
Cold-pressed, pure, and natural oils for your daily beauty routine
{t("oilsDescription")}
</p>
</div>
@@ -71,7 +76,7 @@ export default async function EnglishHomepage() {
href="/en/products"
className="inline-block text-sm uppercase tracking-[0.1em] border-b border-black pb-1 hover:text-[#666666] hover:border-[#666666] transition-colors"
>
View All Products
{t("viewAll")}
</a>
</div>
</div>
@@ -85,25 +90,22 @@ export default async function EnglishHomepage() {
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-20 items-center">
<div>
<span className="text-xs uppercase tracking-[0.2em] text-[#666666] mb-3 block">
Our Story
{t("ourStory")}
</span>
<h2 className="text-3xl md:text-4xl font-medium mb-6">
Handmade with Love
{t("handmadeWithLove")}
</h2>
<p className="text-[#666666] mb-6 leading-relaxed">
Every bottle of ManoonOils is crafted with care using traditional
methods passed down through generations. We source only the finest
organic ingredients to bring you oils that nourish both hair and skin.
{t("storyText1")}
</p>
<p className="text-[#666666] mb-8 leading-relaxed">
Our commitment to purity means no additives, no preservatives -
just nature&apos;s goodness in its most potent form.
{t("storyText2")}
</p>
<a
href="/en/about"
className="inline-block text-sm uppercase tracking-[0.1em] border-b border-black pb-1 hover:text-[#666666] hover:border-[#666666] transition-colors"
>
Learn More
{t("learnMore")}
</a>
</div>
<div className="relative aspect-[4/3] bg-[#e8f0f5] rounded-lg overflow-hidden">
@@ -121,10 +123,10 @@ export default async function EnglishHomepage() {
<div className="max-w-7xl mx-auto">
<div className="text-center mb-16">
<span className="text-xs uppercase tracking-[0.3em] text-[#c9a962] mb-4 block font-medium">
Why Choose Us
{t("whyChooseUs")}
</span>
<h2 className="text-3xl md:text-4xl lg:text-5xl font-medium text-[#1a1a1a]">
The Manoon Difference
{t("manoonDifference")}
</h2>
<div className="w-24 h-1 bg-gradient-to-r from-[#c9a962] to-[#FFD700] mx-auto mt-6 rounded-full" />
</div>
@@ -132,8 +134,8 @@ export default async function EnglishHomepage() {
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 lg:gap-8">
{[
{
title: "100% Natural",
description: "Pure, cold-pressed oils with no additives or preservatives. Just nature's goodness.",
title: tBenefits("natural"),
description: tBenefits("naturalDesc"),
icon: (
<svg className="w-10 h-10" viewBox="0 0 24 24" fill="none">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" fill="#7eb89e"/>
@@ -142,8 +144,8 @@ export default async function EnglishHomepage() {
),
},
{
title: "Handcrafted",
description: "Each batch is carefully prepared by hand to ensure the highest quality.",
title: tBenefits("handcrafted"),
description: tBenefits("handcraftedDesc"),
icon: (
<svg className="w-10 h-10" viewBox="0 0 24 24" fill="none">
<path stroke="#c9a962" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" d="M15.182 15.182a4.5 4.5 0 01-6.364 0M21 12a9 9 0 11-18 0 9 9 0 0118 0zM9.75 9.75c0 .414-.168.75-.375.75S9 10.164 9 9.75 9.168 9 9.375 9s.375.336.375.75zm-.375 0h.008v.015h-.008V9.75zm5.625 0c0 .414-.168.75-.375.75s-.375-.336-.375-.75.168-.75.375-.75.375.336.375.75zm-.375 0h.008v.015h-.008V9.75z"/>
@@ -151,8 +153,8 @@ export default async function EnglishHomepage() {
),
},
{
title: "Sustainable",
description: "Ethically sourced ingredients and eco-friendly packaging for a better planet.",
title: tBenefits("sustainable"),
description: tBenefits("sustainableDesc"),
icon: (
<svg className="w-10 h-10" viewBox="0 0 24 24" fill="none">
<path stroke="#e8967a" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" d="M12.75 3.03v.568c0 .334.148.65.405.864l1.068.89c.442.369.535 1.01.216 1.49l-.51.766a2.25 2.25 0 01-1.161.886l-.143.048a1.107 1.107 0 00-.57 1.664c.369.555.169 1.307-.427 1.605L9 13.125l.423 1.059a.956.956 0 11-1.652.928l-.714-.093a1.125 1.125 0 00-1.906.172L4.5 15.75l-.612.153M12.75 3.031l.002-.004m0 0a8.955 8.955 0 00-4.943.834 8.974 8.974 0 004.943.834m4.943-.834a8.955 8.955 0 00-4.943-.834c2.687 0 5.18.948 7.161 2.664a8.974 8.974 0 014.943-.834z"/>
@@ -179,25 +181,25 @@ export default async function EnglishHomepage() {
<div className="max-w-7xl mx-auto">
<div className="max-w-2xl mx-auto text-center">
<span className="text-xs uppercase tracking-[0.2em] text-white/60 mb-3 block">
Stay Connected
{t("stayConnected")}
</span>
<h2 className="text-3xl md:text-4xl lg:text-5xl font-medium mb-6">
Join Our Community
{t("joinCommunity")}
</h2>
<p className="text-white/70 mb-10 mx-auto text-lg">
Subscribe to receive exclusive offers, beauty tips, and be the first to know about new products.
{t("newsletterText")}
</p>
<form className="flex flex-col sm:flex-row items-stretch justify-center max-w-md mx-auto gap-0">
<input
type="email"
placeholder="Enter your email"
placeholder={t("emailPlaceholder")}
className="flex-1 min-w-0 px-5 h-14 bg-white/10 border border-white/20 border-b-0 sm:border-b border-r-0 sm:border-r border-white/20 text-white placeholder:text-white/50 focus:border-white focus:outline-none transition-colors text-base text-center sm:text-left rounded-t sm:rounded-l sm:rounded-tr-none"
/>
<button
type="submit"
className="px-8 h-14 bg-white text-black text-sm uppercase tracking-[0.1em] font-medium hover:bg-white/90 transition-colors whitespace-nowrap flex-shrink-0 rounded-b sm:rounded-r sm:rounded-bl-none"
>
Subscribe
{t("subscribe")}
</button>
</form>
</div>

View File

@@ -1,15 +1,19 @@
import { getProducts } from "@/lib/saleor";
import { getTranslations } from "next-intl/server";
import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer";
import ProductCard from "@/components/product/ProductCard";
import { ChevronDown } from "lucide-react";
export const metadata = {
export async function generateMetadata() {
return {
title: "Products - ManoonOils",
description: "Browse our collection of premium natural oils for hair and skin care.",
};
};
}
export default async function EnglishProductsPage() {
const t = await getTranslations("Products");
const products = await getProducts("UK");
return (
@@ -23,26 +27,26 @@ export default async function EnglishProductsPage() {
<div className="flex flex-col md:flex-row md:items-end md:justify-between gap-4">
<div>
<span className="text-xs uppercase tracking-[0.2em] text-[#666666] mb-2 block">
Our Collection
{t("collection")}
</span>
<h1 className="text-3xl md:text-4xl font-medium">
All Products
{t("allProducts")}
</h1>
</div>
<div className="flex items-center gap-3">
<span className="text-sm text-[#666666]">
{products.length} products
{t("productsCount", { count: products.length })}
</span>
<div className="relative">
<select
className="appearance-none bg-transparent border border-[#e5e5e5] pl-4 pr-10 py-2 text-sm focus:outline-none focus:border-black cursor-pointer"
defaultValue="featured"
>
<option value="featured">Featured</option>
<option value="newest">Newest</option>
<option value="price-low">Price: Low to High</option>
<option value="price-high">Price: High to Low</option>
<option value="featured">{t("featured")}</option>
<option value="newest">{t("newest")}</option>
<option value="price-low">{t("priceLow")}</option>
<option value="price-high">{t("priceHigh")}</option>
</select>
<ChevronDown className="absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 pointer-events-none text-[#666666]" />
</div>
@@ -56,10 +60,10 @@ export default async function EnglishProductsPage() {
{products.length === 0 ? (
<div className="text-center py-20">
<p className="text-[#666666] mb-4">
No products available
{t("noProducts")}
</p>
<p className="text-sm text-[#999999]">
Please check back later for new arrivals.
{t("checkBack")}
</p>
</div>
) : (

View File

@@ -1,4 +1,5 @@
import { getProducts } from "@/lib/saleor";
import { getTranslations } from "next-intl/server";
import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer";
import HeroVideo from "@/components/home/HeroVideo";
@@ -10,19 +11,23 @@ import BeforeAfterGallery from "@/components/home/BeforeAfterGallery";
import ProblemSection from "@/components/home/ProblemSection";
import HowItWorks from "@/components/home/HowItWorks";
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 using only the finest ingredients.",
};
export async function generateMetadata() {
const t = await getTranslations("Home");
return {
title: "ManoonOils - Premium prirodna ulja za negu kose i kože",
description: "Otkrijte našu premium kolekciju prirodnih ulja za negu kose i kože.",
};
}
export default async function Homepage() {
const t = await getTranslations("Home");
const tBenefits = await getTranslations("Benefits");
let products: any[] = [];
try {
products = await getProducts("SR");
} catch (e) {
// Fallback for build time when API is unavailable
console.log('Failed to fetch products during build');
console.log("Failed to fetch products during build");
}
const featuredProducts = products?.slice(0, 4) || [];
@@ -33,97 +38,81 @@ export default async function Homepage() {
<Header />
<main className="min-h-screen bg-white">
{/* Hero Section with Video Background */}
<HeroVideo />
{/* As Seen In */}
<AsSeenIn />
{/* Testimonials Section */}
<ProductReviews />
{/* Trust Badges */}
<TrustBadges />
{/* Problem Section - Create empathy */}
<ProblemSection />
{/* Before/After Gallery */}
<BeforeAfterGallery />
{/* Main Content */}
<div id="main-content" className="scroll-mt-[72px] lg:scroll-mt-[72px]">
{/* Products Grid Section */}
{hasProducts && (
<section className="py-24 px-4 sm:px-6 lg:px-8 bg-white">
<div className="max-w-7xl mx-auto">
{/* Section Header */}
<div className="text-center mb-16">
<span className="text-xs uppercase tracking-[0.2em] text-[#666666] mb-3 block">
Our Collection
{t("collection")}
</span>
<h2 className="text-3xl md:text-4xl font-medium mb-4">
Premium Natural Oils
{t("premiumOils")}
</h2>
<p className="text-[#666666] max-w-xl mx-auto">
Cold-pressed, pure, and natural oils for your daily beauty routine
{t("oilsDescription")}
</p>
</div>
{/* Products Grid */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 lg:gap-8">
{featuredProducts.map((product, index) => (
<ProductCard key={product.id} product={product} index={index} locale="SR" />
))}
</div>
{/* View All Link */}
<div className="text-center mt-12">
<a
href="/products"
className="inline-block text-sm uppercase tracking-[0.1em] border-b border-black pb-1 hover:text-[#666666] hover:border-[#666666] transition-colors"
>
View All Products
{t("viewAll")}
</a>
</div>
</div>
</section>
)}
{/* How It Works */}
<HowItWorks />
{/* Brand Story Section */}
<section className="py-24 px-4 sm:px-6 lg:px-8 bg-[#f8f9fa]">
<div className="max-w-7xl mx-auto">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-20 items-center">
<div>
<span className="text-xs uppercase tracking-[0.2em] text-[#666666] mb-3 block">
Our Story
{t("ourStory")}
</span>
<h2 className="text-3xl md:text-4xl font-medium mb-6">
Handmade with Love
{t("handmadeWithLove")}
</h2>
<p className="text-[#666666] mb-6 leading-relaxed">
Every bottle of ManoonOils is crafted with care using traditional
methods passed down through generations. We source only the finest
organic ingredients to bring you oils that nourish both hair and skin.
{t("storyText1")}
</p>
<p className="text-[#666666] mb-8 leading-relaxed">
Our commitment to purity means no additives, no preservatives -
just nature&apos;s goodness in its most potent form.
{t("storyText2")}
</p>
<a
href="/about"
className="inline-block text-sm uppercase tracking-[0.1em] border-b border-black pb-1 hover:text-[#666666] hover:border-[#666666] transition-colors"
>
Learn More
{t("learnMore")}
</a>
</div>
<div className="relative aspect-[4/3] bg-[#e8f0f5] rounded-lg overflow-hidden">
<img
src="https://images.unsplash.com/photo-1608571423902-eed4a5ad8108?q=80&w=800&auto=format&fit=crop"
alt="Natural oils production"
alt="Proizvodnja prirodnih ulja"
className="w-full h-full object-cover"
/>
</div>
@@ -131,15 +120,14 @@ export default async function Homepage() {
</div>
</section>
{/* Benefits Section */}
<section className="py-24 px-4 sm:px-6 lg:px-8 bg-gradient-to-b from-white to-[#faf9f7]">
<div className="max-w-7xl mx-auto">
<div className="text-center mb-16">
<span className="text-xs uppercase tracking-[0.3em] text-[#c9a962] mb-4 block font-medium">
Why Choose Us
{t("whyChooseUs")}
</span>
<h2 className="text-3xl md:text-4xl lg:text-5xl font-medium text-[#1a1a1a]">
The Manoon Difference
{t("manoonDifference")}
</h2>
<div className="w-24 h-1 bg-gradient-to-r from-[#c9a962] to-[#FFD700] mx-auto mt-6 rounded-full" />
</div>
@@ -147,8 +135,8 @@ export default async function Homepage() {
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 lg:gap-8">
{[
{
title: "100% Natural",
description: "Pure, cold-pressed oils with no additives or preservatives. Just nature's goodness.",
title: tBenefits("natural"),
description: tBenefits("naturalDesc"),
icon: (
<svg className="w-10 h-10" viewBox="0 0 24 24" fill="none">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" fill="#7eb89e"/>
@@ -157,8 +145,8 @@ export default async function Homepage() {
),
},
{
title: "Handcrafted",
description: "Each batch is carefully prepared by hand to ensure the highest quality.",
title: tBenefits("handcrafted"),
description: tBenefits("handcraftedDesc"),
icon: (
<svg className="w-10 h-10" viewBox="0 0 24 24" fill="none">
<path stroke="#c9a962" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" d="M15.182 15.182a4.5 4.5 0 01-6.364 0M21 12a9 9 0 11-18 0 9 9 0 0118 0zM9.75 9.75c0 .414-.168.75-.375.75S9 10.164 9 9.75 9.168 9 9.375 9s.375.336.375.75zm-.375 0h.008v.015h-.008V9.75zm5.625 0c0 .414-.168.75-.375.75s-.375-.336-.375-.75.168-.75.375-.75.375.336.375.75zm-.375 0h.008v.015h-.008V9.75z"/>
@@ -166,8 +154,8 @@ export default async function Homepage() {
),
},
{
title: "Sustainable",
description: "Ethically sourced ingredients and eco-friendly packaging for a better planet.",
title: tBenefits("sustainable"),
description: tBenefits("sustainableDesc"),
icon: (
<svg className="w-10 h-10" viewBox="0 0 24 24" fill="none">
<path stroke="#e8967a" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" d="M12.75 3.03v.568c0 .334.148.65.405.864l1.068.89c.442.369.535 1.01.216 1.49l-.51.766a2.25 2.25 0 01-1.161.886l-.143.048a1.107 1.107 0 00-.57 1.664c.369.555.169 1.307-.427 1.605L9 13.125l.423 1.059a.956.956 0 11-1.652.928l-.714-.093a1.125 1.125 0 00-1.906.172L4.5 15.75l-.612.153M12.75 3.031l.002-.004m0 0a8.955 8.955 0 00-4.943.834 8.974 8.974 0 004.943.834m4.943-.834a8.955 8.955 0 00-4.943-.834c2.687 0 5.18.948 7.161 2.664a8.974 8.974 0 014.943-.834z"/>
@@ -190,31 +178,29 @@ export default async function Homepage() {
</div>
</section>
{/* Newsletter Section */}
<section className="py-28 lg:py-32 px-4 sm:px-6 lg:px-8 bg-[#1a1a1a] text-white">
<div className="max-w-7xl mx-auto">
<div className="max-w-2xl mx-auto text-center">
<span className="text-xs uppercase tracking-[0.2em] text-white/60 mb-3 block">
Stay Connected
{t("stayConnected")}
</span>
<h2 className="text-3xl md:text-4xl lg:text-5xl font-medium mb-6">
Join Our Community
{t("joinCommunity")}
</h2>
<p className="text-white/70 mb-10 mx-auto text-lg">
Subscribe to receive exclusive offers, beauty tips, and be the first to know about new products.
{t("newsletterText")}
</p>
{/* Newsletter Form - Centered */}
<form className="flex flex-col sm:flex-row items-stretch justify-center max-w-md mx-auto gap-0">
<input
type="email"
placeholder="Enter your email"
placeholder={t("emailPlaceholder")}
className="flex-1 min-w-0 px-5 h-14 bg-white/10 border border-white/20 border-b-0 sm:border-b border-r-0 sm:border-r border-white/20 text-white placeholder:text-white/50 focus:border-white focus:outline-none transition-colors text-base text-center sm:text-left rounded-t sm:rounded-l sm:rounded-tr-none"
/>
<button
type="submit"
className="px-8 h-14 bg-white text-black text-sm uppercase tracking-[0.1em] font-medium hover:bg-white/90 transition-colors whitespace-nowrap flex-shrink-0 rounded-b sm:rounded-r sm:rounded-bl-none"
>
Subscribe
{t("subscribe")}
</button>
</form>
</div>

View File

@@ -1,27 +1,21 @@
import { getProductBySlug, getProducts, getLocalizedProduct } from "@/lib/saleor";
import { getTranslations } from "next-intl/server";
import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer";
import ProductDetail from "@/components/product/ProductDetail";
import type { Product } from "@/types/saleor";
interface ProductPageProps {
params: Promise<{ slug: string; locale?: string }>;
params: Promise<{ slug: string }>;
}
// Generate static params for all products
export async function generateStaticParams() {
try {
const products = await getProducts("SR", 100);
const params: Array<{ slug: string; locale: string }> = [];
const params: Array<{ slug: string }> = [];
products.forEach((product: Product) => {
// Serbian slug
params.push({ slug: product.slug, locale: "sr" });
// English slug (if translation exists)
if (product.translation?.slug) {
params.push({ slug: product.translation.slug, locale: "en" });
}
params.push({ slug: product.slug });
});
return params;
@@ -31,39 +25,27 @@ export async function generateStaticParams() {
}
export async function generateMetadata({ params }: ProductPageProps) {
const { slug, locale = "sr" } = await params;
const product = await getProductBySlug(slug, locale.toUpperCase());
const { slug } = await params;
const product = await getProductBySlug(slug, "SR");
if (!product) {
return {
title: locale === "en" ? "Product Not Found" : "Proizvod nije pronađen",
title: "Proizvod nije pronađen",
};
}
const localized = getLocalizedProduct(product, locale.toUpperCase());
const localized = getLocalizedProduct(product, "SR");
return {
title: localized.name,
description: localized.seoDescription || localized.description?.slice(0, 160),
alternates: {
canonical: `/products/${product.slug}`,
languages: {
"sr": `/products/${product.slug}`,
"en": product.translation?.slug ? `/products/${product.translation.slug}` : `/products/${product.slug}`,
},
},
openGraph: {
title: localized.name,
description: localized.seoDescription || localized.description?.slice(0, 160),
images: product.media?.[0]?.url ? [product.media[0].url] : [],
type: 'website',
},
};
}
export default async function ProductPage({ params }: ProductPageProps) {
const { slug, locale = "sr" } = await params;
const product = await getProductBySlug(slug, locale.toUpperCase());
const t = await getTranslations("Product");
const { slug } = await params;
const product = await getProductBySlug(slug, "SR");
if (!product) {
return (
@@ -72,18 +54,16 @@ export default async function ProductPage({ params }: ProductPageProps) {
<main className="min-h-screen bg-white">
<div className="pt-[180px] lg:pt-[200px] pb-20 text-center px-4">
<h1 className="text-2xl font-medium mb-4">
{locale === "en" ? "Product not found" : "Proizvod nije pronađen"}
{t("notFound")}
</h1>
<p className="text-[#666666] mb-8">
{locale === "en"
? "The product you're looking for doesn't exist or has been removed."
: "Proizvod koji tražite ne postoji ili je uklonjen."}
{t("notFoundDesc")}
</p>
<a
href="/products"
className="inline-block px-8 py-3 bg-black text-white text-sm uppercase tracking-[0.1em] hover:bg-[#333333] transition-colors"
>
{locale === "en" ? "Browse Products" : "Pregledaj proizvode"}
{t("browseProducts")}
</a>
</div>
</main>
@@ -92,20 +72,13 @@ export default async function ProductPage({ params }: ProductPageProps) {
);
}
// Determine language based on which slug matched
const isEnglishSlug = slug === product.translation?.slug;
const currentLocale = isEnglishSlug ? "EN" : "SR";
// Get related products (same category or just other products)
let relatedProducts: Product[] = [];
try {
const allProducts = await getProducts(currentLocale, 8);
const allProducts = await getProducts("SR", 8);
relatedProducts = allProducts
.filter((p: Product) => p.id !== product.id)
.slice(0, 4);
} catch (e) {
// Ignore error, just won't show related products
}
} catch (e) {}
return (
<>
@@ -114,7 +87,7 @@ export default async function ProductPage({ params }: ProductPageProps) {
<ProductDetail
product={product}
relatedProducts={relatedProducts}
locale={currentLocale}
locale="SR"
/>
</main>
<Footer />

View File

@@ -1,64 +1,52 @@
import { getProducts } from "@/lib/saleor";
import { getTranslations } from "next-intl/server";
import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer";
import ProductCard from "@/components/product/ProductCard";
import { ChevronDown } from "lucide-react";
export const metadata = {
title: "Products - ManoonOils",
description: "Browse our collection of premium natural oils for hair and skin care.",
};
interface ProductsPageProps {
params: Promise<{ locale: string }>;
export async function generateMetadata() {
return {
title: "Proizvodi - ManoonOils",
description: "Pregledajte našu kolekciju premium prirodnih ulja za negu kose i kože.",
};
}
export default async function ProductsPage({ params }: ProductsPageProps) {
const { locale = "sr" } = await params;
const products = await getProducts(locale.toUpperCase());
const localeUpper = locale.toUpperCase();
export default async function ProductsPage() {
const t = await getTranslations("Products");
const products = await getProducts("SR");
return (
<>
<Header />
<main className="min-h-screen bg-white">
{/* Page Header */}
<div className="pt-[72px] lg:pt-[72px]">
<div className="border-b border-[#e5e5e5]">
<div className="container py-8 md:py-12">
<div className="flex flex-col md:flex-row md:items-end md:justify-between gap-4">
<div>
<span className="text-xs uppercase tracking-[0.2em] text-[#666666] mb-2 block">
{localeUpper === "EN" ? "Our Collection" : "Naša kolekcija"}
{t("collection")}
</span>
<h1 className="text-3xl md:text-4xl font-medium">
{localeUpper === "EN" ? "All Products" : "Svi Proizvodi"}
{t("allProducts")}
</h1>
</div>
{/* Sort Dropdown */}
<div className="flex items-center gap-3">
<span className="text-sm text-[#666666]">
{products.length} {localeUpper === "EN" ? "products" : "proizvoda"}
{t("productsCount", { count: products.length })}
</span>
<div className="relative">
<select
className="appearance-none bg-transparent border border-[#e5e5e5] pl-4 pr-10 py-2 text-sm focus:outline-none focus:border-black cursor-pointer"
defaultValue="featured"
>
<option value="featured">
{localeUpper === "EN" ? "Featured" : "Istaknuto"}
</option>
<option value="newest">
{localeUpper === "EN" ? "Newest" : "Najnovije"}
</option>
<option value="price-low">
{localeUpper === "EN" ? "Price: Low to High" : "Cena: Rastuće"}
</option>
<option value="price-high">
{localeUpper === "EN" ? "Price: High to Low" : "Cena: Opadajuće"}
</option>
<option value="featured">{t("featured")}</option>
<option value="newest">{t("newest")}</option>
<option value="price-low">{t("priceLow")}</option>
<option value="price-high">{t("priceHigh")}</option>
</select>
<ChevronDown className="absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 pointer-events-none text-[#666666]" />
</div>
@@ -67,18 +55,15 @@ export default async function ProductsPage({ params }: ProductsPageProps) {
</div>
</div>
{/* Products Grid */}
<section className="py-12 md:py-16">
<div className="container">
{products.length === 0 ? (
<div className="text-center py-20">
<p className="text-[#666666] mb-4">
{localeUpper === "EN" ? "No products available" : "Nema dostupnih proizvoda"}
{t("noProducts")}
</p>
<p className="text-sm text-[#999999]">
{localeUpper === "EN"
? "Please check back later for new arrivals."
: "Molimo proverite ponovo kasnije za nove proizvode."}
{t("checkBack")}
</p>
</div>
) : (
@@ -88,7 +73,7 @@ export default async function ProductsPage({ params }: ProductsPageProps) {
key={product.id}
product={product}
index={index}
locale={localeUpper}
locale="SR"
/>
))}
</div>

View File

@@ -10,24 +10,40 @@
"title": "Premium Natural Oils",
"subtitle": "For hair and skin care"
},
"ticker": {
"text": "Free shipping on orders over 3000 RSD • Natural ingredients • Cruelty-free • Handmade with love"
"collection": "Our Collection",
"premiumOils": "Premium Natural Oils",
"oilsDescription": "Cold-pressed, pure, and natural oils for your daily beauty routine",
"viewAll": "View All Products",
"ourStory": "Our Story",
"handmadeWithLove": "Handmade with Love",
"storyText1": "Every bottle of ManoonOils is crafted with care using traditional methods passed down through generations. We source only the finest organic ingredients to bring you oils that nourish both hair and skin.",
"storyText2": "Our commitment to purity means no additives, no preservatives - just nature's goodness in its most potent form.",
"learnMore": "Learn More",
"whyChooseUs": "Why Choose Us",
"manoonDifference": "The Manoon Difference",
"stayConnected": "Stay Connected",
"joinCommunity": "Join Our Community",
"newsletterText": "Subscribe to receive exclusive offers, beauty tips, and be the first to know about new products.",
"emailPlaceholder": "Enter your email",
"subscribe": "Subscribe"
},
"products": {
"title": "Our Products"
"Benefits": {
"natural": "100% Natural",
"naturalDesc": "Pure, cold-pressed oils with no additives or preservatives. Just nature's goodness.",
"handcrafted": "Handcrafted",
"handcraftedDesc": "Each batch is carefully prepared by hand to ensure the highest quality.",
"sustainable": "Sustainable",
"sustainableDesc": "Ethically sourced ingredients and eco-friendly packaging for a better planet."
},
"bestsellers": {
"title": "Best Sellers"
},
"story": {
"title": "Our Story",
"description": "ManoonOils was born from a passion for natural beauty..."
},
"newsletter": {
"title": "Stay Updated",
"placeholder": "Enter your email",
"button": "Subscribe"
}
"Products": {
"allProducts": "All Products",
"productsCount": "{count} products",
"featured": "Featured",
"newest": "Newest",
"priceLow": "Price: Low to High",
"priceHigh": "Price: High to Low",
"noProducts": "No products available",
"checkBack": "Please check back later for new arrivals."
},
"Product": {
"addToCart": "Add to Cart",
@@ -35,19 +51,99 @@
"details": "Details",
"ingredients": "Ingredients",
"usage": "How to Use",
"related": "You May Also Like"
"related": "You May Also Like",
"notFound": "Product not found",
"notFoundDesc": "The product you're looking for doesn't exist or has been removed.",
"browseProducts": "Browse Products"
},
"Cart": {
"title": "Your Cart",
"empty": "Your cart is empty",
"emptyDesc": "Looks like you haven't added anything to your cart yet.",
"continueShopping": "Continue Shopping",
"checkout": "Checkout",
"subtotal": "Subtotal",
"remove": "Remove"
"shipping": "Shipping",
"shippingCalc": "Calculated at checkout",
"total": "Total",
"freeShipping": "Free shipping on orders over {amount}",
"remove": "Remove",
"processes": "Processing...",
"cartEmpty": "Your cart is empty"
},
"About": {
"title": "About Us",
"subtitle": "Our Story",
"intro": "ManoonOils was born from a passion for natural beauty and the belief that the best skincare comes from nature itself.",
"intro2": "We believe in the power of natural ingredients. Every oil in our collection is carefully selected for its unique properties and benefits. From nourishing oils that restore hair vitality to serums that rejuvenate skin, we craft each product with love and attention to detail.",
"naturalIngredients": "Natural Ingredients",
"naturalIngredientsDesc": "We use only the finest natural ingredients, sourced ethically and sustainably from trusted suppliers around the world.",
"crueltyFree": "Cruelty-Free",
"crueltyFreeDesc": "Our products are never tested on animals. We believe in beauty without compromise.",
"sustainablePackaging": "Sustainable Packaging",
"sustainablePackagingDesc": "We use eco-friendly packaging materials and minimize waste throughout our production process.",
"handcraftedQuality": "Handcrafted Quality",
"handcraftedQualityDesc": "Every bottle is handcrafted in small batches to ensure the highest quality and freshness.",
"mission": "Our Mission",
"missionQuote": "\"To provide premium quality, natural products that enhance your daily beauty routine.\"",
"handmadeTitle": "Handmade with Love",
"handmadeText1": "Every bottle of ManoonOils is handcrafted with care. We small-batch produce our products to ensure the highest quality and freshness. When you use ManoonOils, you can feel confident that you're using something made with genuine care and expertise.",
"handmadeText2": "Our journey began with a simple question: how can we create products that truly nurture both hair and skin? Today, we continue to innovate while staying true to our commitment to natural, effective beauty solutions."
},
"Contact": {
"title": "Contact",
"subtitle": "Get in Touch",
"getInTouch": "Get in Touch",
"getInTouchDesc": "We're here to help! Whether you have questions about our products, need assistance with an order, or just want to say hello, we'd love to hear from you.",
"email": "Email",
"emailReply": "We reply within 24 hours",
"shippingTitle": "Shipping",
"freeShipping": "Free shipping over £50",
"deliveryTime": "Delivered within 2-5 business days",
"location": "Location",
"locationDesc": "Serbia",
"worldwideShipping": "Shipping worldwide",
"name": "Name",
"namePlaceholder": "Your name",
"emailField": "Email",
"emailPlaceholder": "your@email.com",
"message": "Message",
"messagePlaceholder": "How can we help you?",
"sendMessage": "Send Message",
"thankYou": "Thank You!",
"thankYouDesc": "Your message has been sent. We'll get back to you soon.",
"faqTitle": "Frequently Asked Questions",
"faq1q": "How long does shipping take?",
"faq1a": "Orders are typically delivered within 2-5 business days for domestic shipping. You'll receive a tracking number once your order ships.",
"faq2q": "Are your products 100% natural?",
"faq2a": "Yes! All our oils are 100% natural, cold-pressed, and free from any additives, preservatives, or artificial fragrances.",
"faq3q": "What is your return policy?",
"faq3a": "We accept returns within 14 days of delivery for unopened products. Please contact us if you have any issues with your order.",
"faq4q": "Do you offer wholesale?",
"faq4a": "Yes, we offer wholesale pricing for bulk orders. Please contact us at hello@manoonoils.com for more information."
},
"Footer": {
"quickLinks": "Quick Links",
"customerService": "Customer Service",
"copyright": "All rights reserved."
"contact": "Contact",
"shipping": "Shipping",
"returns": "Returns",
"faq": "FAQ",
"followUs": "Follow Us",
"newsletter": "Newsletter",
"newsletterDesc": "Subscribe to our newsletter for exclusive offers and updates.",
"copyright": "All rights reserved.",
"allRights": "© {year} ManoonOils."
},
"Common": {
"loading": "Loading...",
"error": "An error occurred",
"tryAgain": "Try again",
"close": "Close",
"back": "Back",
"next": "Next",
"previous": "Previous",
"search": "Search",
"noResults": "No results found"
}
}

View File

@@ -10,24 +10,40 @@
"title": "Premium prirodna ulja",
"subtitle": "Za negu kose i kože"
},
"ticker": {
"text": "Besplatna dostava za porudžbine preko 3000 RSD • Prirodni sastojci • Bez okrutnosti • Ručno sa ljubavlju"
"collection": "Naša kolekcija",
"premiumOils": "Premium prirodna ulja",
"oilsDescription": "Hladno ceđena, čista i prirodna ulja za vašu svakodnevnuBeauty rutinu",
"viewAll": "Pogledaj sve proizvode",
"ourStory": "Naša priča",
"handmadeWithLove": "Ručno sa ljubavlju",
"storyText1": "Svaka boca ManoonOils je izrađena sa pažnjom koristeći tradicionalne metode koje se prenose kroz generacije. Nabavljamo samo najkvalitetnije organske sastojke da bismo vam doneli ulja koja neguju kosu i kožu.",
"storyText2": "Naša posvećenost čistoći znači bez aditiva, bez konzervanasa - samo dobrobit prirode u njenom najpotentnijem obliku.",
"learnMore": "Saznajte više",
"whyChooseUs": "Zašto nas izabrati",
"manoonDifference": "Manoon razlika",
"stayConnected": "Ostanite povezani",
"joinCommunity": "Pridružite se našoj zajednici",
"newsletterText": "Pretplatite se da biste primali ekskluzivne ponude, Beauty savete i budite prvi koji ćete saznati za nove proizvode.",
"emailPlaceholder": "Unesite vaš email",
"subscribe": "Pretplatite se"
},
"products": {
"title": "Naši proizvodi"
"Benefits": {
"natural": "100% Prirodno",
"naturalDesc": "Čista, hladno ceđena ulja bez aditiva ili konzervanasa. Samo dobrobit prirode.",
"handcrafted": "Ručno pravljeno",
"handcraftedDesc": "Svaka serija je pažljivo pripremljena ručno kako bi se osigurao najviši kvalitet.",
"sustainable": "Održivo",
"sustainableDesc": "Etički nabavljeni sastojci i ekološka ambalaža za bolju planetu."
},
"bestsellers": {
"title": "Najprodavaniji"
},
"story": {
"title": "Naša priča",
"description": "ManoonOils je rođen iz strasti za prirodnu lepotu..."
},
"newsletter": {
"title": "Ostanite u toku",
"placeholder": "Unesite vaš email",
"button": "Pretplati se"
}
"Products": {
"allProducts": "Svi proizvodi",
"productsCount": "{count} proizvoda",
"featured": "Istaknuto",
"newest": "Najnovije",
"priceLow": "Cena: Rastuće",
"priceHigh": "Cena: Opadajuće",
"noProducts": "Nema dostupnih proizvoda",
"checkBack": "Molimo proverite ponovo kasnije za nove proizvode."
},
"Product": {
"addToCart": "Dodaj u korpu",
@@ -35,19 +51,99 @@
"details": "Detalji",
"ingredients": "Sastojci",
"usage": "Način upotrebe",
"related": "Takođe će vam se svideti"
"related": "Takođe će vam se svideti",
"notFound": "Proizvod nije pronađen",
"notFoundDesc": "Proizvod koji tražite ne postoji ili je uklonjen.",
"browseProducts": "Pregledaj proizvode"
},
"Cart": {
"title": "Vaša korpa",
"empty": "Vaša korpa je prazna",
"emptyDesc": "Izgleda da još uvek niste dodali ništa u korpu.",
"continueShopping": "Nastavite kupovinu",
"checkout": "Kupovina",
"subtotal": "Ukupno",
"remove": "Ukloni"
"shipping": "Dostava",
"shippingCalc": "Racunato pri kupovini",
"total": "Ukupno",
"freeShipping": "Besplatna dostava za porudžbine preko {amount}",
"remove": "Ukloni",
"processes": "Obrađuje se...",
"cartEmpty": "Vaša korpa je prazna"
},
"About": {
"title": "O nama",
"subtitle": "Naša priča",
"intro": "ManoonOils je rođen iz strasti za prirodnu lepotu i verovanja da najbolja nega kože dolazi od same prirode.",
"intro2": "Verujemo u moć prirodnih sastojaka. Svako ulje u našoj kolekciji je pažljivo odabrano zbog svojih jedinstvenih svojstava i prednosti. Od hranljivih ulja koja obnavljaju vitalnost kose, do seruma koji podmlađuju kožu, svaki proizvod pravimo sa ljubavlju i pažnjom prema detaljima.",
"naturalIngredients": "Prirodni sastojci",
"naturalIngredientsDesc": "Koristimo samo najfinije prirodne sastojke, etički i održivo nabavljene od pouzdanih dobavljača širom sveta.",
"crueltyFree": "Bez okrutnosti",
"crueltyFreeDesc": "Naši proizvodi se nikada ne testiraju na životinjama. Verujemo u lepotu bez kompromisa.",
"sustainablePackaging": "Održiva ambalaža",
"sustainablePackagingDesc": "Koristimo ekološke materijale za pakovanje i minimizujemo otpad tokom celokupnog proizvodnog procesa.",
"handcraftedQuality": "Ručna izrada",
"handcraftedQualityDesc": "Svaka boca je ručno napravljena u malim serijama kako bi se osigurao najviši kvalitet i svežina.",
"mission": "Naša misija",
"missionQuote": "\"Da pružimo proizvode premium kvaliteta, prirodne proizvode koji unapređuju vašu svakodnevnuBeauty rutinu.\"",
"handmadeTitle": "Ručno sa ljubavlju",
"handmadeText1": "Svaka boca ManoonOils je ručno napravljena sa brigom. Proizvodimo proizvode u malim serijama kako bi osigurali najviši kvalitet i svežinu. Kada koristite ManoonOils, možete biti sigurni da koristite nešto napravljeno sa iskrenom brigom i stručnošću.",
"handmadeText2": "Naše putovanje je počelo jednostavnim pitanjem: kako možemo stvoriti proizvode koji zaista neguju kosu i kožu? Danas nastavljamo da inoviramo dok ostajemo verni našoj posvećenosti prirodnim, efikasnim Beauty rešenjima."
},
"Contact": {
"title": "Kontakt",
"subtitle": "Stupite u kontakt",
"getInTouch": "Stupite u kontakt",
"getInTouchDesc": "Tu smo da pomognemo! Bilo da imate pitanja o našim proizvodima, trebate pomoć sa narudžbinom, ili jednostavno želite reći zdravo, voleli bismo da čujemo od vas.",
"email": "Email",
"emailReply": "Odgovaramo u roku od 24 sata",
"shippingTitle": "Dostava",
"freeShipping": "Besplatna dostava preko 3.000 RSD",
"deliveryTime": "Isporučeno u roku od 2-5 radnih dana",
"location": "Lokacija",
"locationDesc": "Srbija",
"worldwideShipping": "Isporuka širom sveta",
"name": "Ime",
"namePlaceholder": "Vaše ime",
"emailField": "Email",
"emailPlaceholder": "vas@email.com",
"message": "Poruka",
"messagePlaceholder": "Kako možemo da vam pomognemo?",
"sendMessage": "Pošalji poruku",
"thankYou": "Hvala vam!",
"thankYouDesc": "Vaša poruka je poslata. Javićemo vam se uskoro.",
"faqTitle": "Često postavljana pitanja",
"faq1q": "Koliko dugo traje dostava?",
"faq1a": "Narudžbe se obično isporučuju u roku od 2-5 radnih dana za domaću dostavu. Dobićete broj za praćenje čim vaša narudžbina bude poslata.",
"faq2q": "Da li su vaši proizvodi 100% prirodni?",
"faq2a": "Da! Sva naša ulja su 100% prirodna, hladno ceđena i bez ikakvih aditiva, konzervanasa ili veštačkih mirisa.",
"faq3q": "Koja je vaša politika povrata?",
"faq3a": "Prihvatamo povrate u roku od 14 dana od isporuke za neotvorene proizvode. Molimo kontaktirajte nas ako imate bilo kakvih problema sa narudžbinom.",
"faq4q": "Da li nudite veleprodaju?",
"faq4a": "Da, nudimo veleprodajne cene za narudžbine u velikim količinama. Molimo kontaktirajte nas na hello@manoonoils.com za više informacija."
},
"Footer": {
"quickLinks": "Brze veze",
"customerService": "Korisnička podrška",
"copyright": "Sva prava zadržana."
"contact": "Kontakt",
"shipping": "Dostava",
"returns": "Povrat",
"faq": "Česta pitanja",
"followUs": "Pratite nas",
"newsletter": "Newsletter",
"newsletterDesc": "Pretplatite se na naš newsletter za ekskluzivne ponude i novosti.",
"copyright": "Sva prava zadržana.",
"allRights": "© {year} ManoonOils."
},
"Common": {
"loading": "Učitavanje...",
"error": "Došlo je do greške",
"tryAgain": "Pokušajte ponovo",
"close": "Zatvori",
"back": "Nazad",
"next": "Sledeće",
"previous": "Prethodno",
"search": "Pretraga",
"noResults": "Nema rezultata"
}
}