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>
<h1 className="text-4xl md:text-5xl font-medium tracking-tight">
About ManoonOils
</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"
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.
</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.
</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>
<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.
</p>
</div>
<div className="p-6 bg-[#f8f9fa]">
<h3 className="text-lg font-medium mb-3">Cruelty-Free</h3>
<p className="text-[#666666] text-sm leading-relaxed">
Our products are never tested on animals. We believe in beauty
without compromise.
</p>
</div>
<div className="p-6 bg-[#f8f9fa]">
<h3 className="text-lg font-medium mb-3">Sustainable Packaging</h3>
<p className="text-[#666666] text-sm leading-relaxed">
We use eco-friendly packaging materials and minimize waste
throughout our production process.
</p>
</div>
<div className="p-6 bg-[#f8f9fa]">
<h3 className="text-lg font-medium mb-3">Handcrafted Quality</h3>
<p className="text-[#666666] text-sm leading-relaxed">
Every bottle is handcrafted in small batches to ensure
the highest quality and freshness.
</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>
<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;
</blockquote>
</div>
{/* Story Section */}
<div className="mt-16">
<h2 className="text-2xl font-medium mb-6">Handmade with Love</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.
</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.
</p>
<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">
{t("subtitle")}
</span>
<h1 className="text-4xl md:text-5xl font-medium tracking-tight">
{t("title")}
</h1>
</div>
</div>
</div>
</section>
<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="Proizvodnja prirodnih ulja"
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-black/20" />
</div>
<section className="py-16 md:py-24">
<div className="container">
<div className="max-w-3xl mx-auto">
<div className="mb-16">
<p className="text-xl md:text-2xl text-[#1a1a1a] leading-relaxed mb-8">
{t("intro")}
</p>
<p className="text-[#666666] leading-relaxed">
{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">
{t("naturalIngredients")}
</h3>
<p className="text-[#666666] text-sm leading-relaxed">
{t("naturalIngredientsDesc")}
</p>
</div>
<div className="p-6 bg-[#f8f9fa]">
<h3 className="text-lg font-medium mb-3">
{t("crueltyFree")}
</h3>
<p className="text-[#666666] text-sm leading-relaxed">
{t("crueltyFreeDesc")}
</p>
</div>
<div className="p-6 bg-[#f8f9fa]">
<h3 className="text-lg font-medium mb-3">
{t("sustainablePackaging")}
</h3>
<p className="text-[#666666] text-sm leading-relaxed">
{t("sustainablePackagingDesc")}
</p>
</div>
<div className="p-6 bg-[#f8f9fa]">
<h3 className="text-lg font-medium mb-3">
{t("handcraftedQuality")}
</h3>
<p className="text-[#666666] text-sm leading-relaxed">
{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">
{t("mission")}
</span>
<blockquote className="text-2xl md:text-3xl font-medium tracking-tight">
&ldquo;{t("missionQuote")}&rdquo;
</blockquote>
</div>
<div className="mt-16">
<h2 className="text-2xl font-medium mb-6">
{t("handmadeTitle")}
</h2>
<p className="text-[#666666] leading-relaxed mb-6">
{t("handmadeText1")}
</p>
<p className="text-[#666666] leading-relaxed">
{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,179 +24,165 @@ 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>
<h1 className="text-4xl md:text-5xl font-medium tracking-tight mb-4">
Contact Us
</h1>
<p className="text-[#666666]">
Have questions? We&apos;d love to hear from you.
</p>
<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">
{t("subtitle")}
</span>
<h1 className="text-4xl md:text-5xl font-medium tracking-tight mb-4">
{t("title")}
</h1>
<p className="text-[#666666]">
{t("getInTouchDesc")}
</p>
</div>
</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>
<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.
</p>
<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">
<div>
<h2 className="text-2xl font-medium mb-6">
{t("getInTouch")}
</h2>
<p className="text-[#666666] mb-8 leading-relaxed">
{t("getInTouchDesc")}
</p>
<div className="space-y-6">
<div className="flex items-start gap-4">
<div className="w-12 h-12 rounded-full bg-[#f8f9fa] flex items-center justify-center flex-shrink-0">
<Mail className="w-5 h-5 text-[#666666]" strokeWidth={1.5} />
<div className="space-y-6">
<div className="flex items-start gap-4">
<div className="w-12 h-12 rounded-full bg-[#f8f9fa] flex items-center justify-center flex-shrink-0">
<Mail className="w-5 h-5 text-[#666666]" strokeWidth={1.5} />
</div>
<div>
<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">{t("emailReply")}</p>
</div>
</div>
<div>
<h3 className="font-medium mb-1">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>
</div>
</div>
<div className="flex items-start gap-4">
<div className="w-12 h-12 rounded-full bg-[#f8f9fa] flex items-center justify-center flex-shrink-0">
<Truck className="w-5 h-5 text-[#666666]" strokeWidth={1.5} />
<div className="flex items-start gap-4">
<div className="w-12 h-12 rounded-full bg-[#f8f9fa] flex items-center justify-center flex-shrink-0">
<Truck className="w-5 h-5 text-[#666666]" strokeWidth={1.5} />
</div>
<div>
<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>
<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>
</div>
</div>
<div className="flex items-start gap-4">
<div className="w-12 h-12 rounded-full bg-[#f8f9fa] flex items-center justify-center flex-shrink-0">
<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>
<div className="flex items-start gap-4">
<div className="w-12 h-12 rounded-full bg-[#f8f9fa] flex items-center justify-center flex-shrink-0">
<MapPin className="w-5 h-5 text-[#666666]" strokeWidth={1.5} />
</div>
<div>
<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>
</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 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">{t("thankYou")}</h3>
<p className="text-[#666666]">
{t("thankYouDesc")}
</p>
</div>
<h3 className="text-xl font-medium mb-2">Thank You!</h3>
<p className="text-[#666666]">
Your message has been sent. We&apos;ll get back to you soon.
</p>
</div>
) : (
<form onSubmit={handleSubmit} className="space-y-6">
<div>
<label htmlFor="name" className="block text-sm font-medium mb-2">
Name
</label>
<input
type="text"
id="name"
required
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"
/>
</div>
<div>
<label htmlFor="email" className="block text-sm font-medium mb-2">
Email
</label>
<input
type="email"
id="email"
required
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"
/>
</div>
<div>
<label htmlFor="message" className="block text-sm font-medium mb-2">
Message
</label>
<textarea
id="message"
required
rows={5}
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?"
/>
</div>
<button
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
</button>
</form>
)}
) : (
<form onSubmit={handleSubmit} className="space-y-6">
<div>
<label htmlFor="name" className="block text-sm font-medium mb-2">
{t("name")}
</label>
<input
type="text"
id="name"
required
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={t("namePlaceholder")}
/>
</div>
<div>
<label htmlFor="email" className="block text-sm font-medium mb-2">
{t("emailField")}
</label>
<input
type="email"
id="email"
required
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={t("emailPlaceholder")}
/>
</div>
<div>
<label htmlFor="message" className="block text-sm font-medium mb-2">
{t("message")}
</label>
<textarea
id="message"
required
rows={5}
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={t("messagePlaceholder")}
/>
</div>
<button
type="submit"
className="w-full py-4 bg-black text-white text-sm uppercase tracking-[0.1em] font-medium hover:bg-[#333333] transition-colors"
>
{t("sendMessage")}
</button>
</form>
)}
</div>
</div>
</div>
</div>
</section>
</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
</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."
}
].map((faq, index) => (
<div key={index} className="border-b border-[#e5e5e5] pb-6">
<h3 className="font-medium mb-2">{faq.q}</h3>
<p className="text-[#666666] text-sm leading-relaxed">{faq.a}</p>
</div>
))}
<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">
{t("faqTitle")}
</h2>
<div className="space-y-6">
{[
{ 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>
<p className="text-[#666666] text-sm leading-relaxed">{faq.a}</p>
</div>
))}
</div>
</div>
</div>
</div>
</section>
</section>
</main>
<div className="pt-16">
<Footer />
</div>

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 = {
title: "About - ManoonOils",
description: "Learn about ManoonOils - our story, mission, and commitment to natural beauty.",
};
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,15 +141,15 @@ 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>
<button
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>
@@ -189,7 +182,7 @@ export default function ContactPage() {
</div>
</section>
</main>
<div className="pt-16">
<Footer />
</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,13 +11,17 @@ 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() {
return {
title: "ManoonOils - Premium Natural Oils for Hair & Skin",
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,21 +1,25 @@
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.",
};
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 (
<>
<Header />
<main className="min-h-screen bg-white">
<div className="pt-[72px] lg:pt-[72px]">
<div className="border-b border-[#e5e5e5]">
@@ -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
<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,18 +60,18 @@ 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>
) : (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 lg:gap-8">
{products.map((product, index) => (
<ProductCard
key={product.id}
product={product}
<ProductCard
key={product.id}
product={product}
index={index}
locale="EN"
/>
@@ -78,7 +82,7 @@ export default async function EnglishProductsPage() {
</section>
</div>
</main>
<div className="pt-16">
<Footer />
</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,120 +11,108 @@ 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) || [];
const hasProducts = featuredProducts.length > 0;
return (
<>
<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"/>
@@ -175,8 +163,8 @@ export default async function Homepage() {
),
},
].map((benefit, index) => (
<div
key={index}
<div
key={index}
className="relative text-center p-8 bg-white rounded-3xl shadow-lg border border-[#f0ede8] hover:shadow-2xl hover:border-[#c9a962]/30 transition-all duration-500 group"
>
<div className="w-20 h-20 mx-auto mb-6 rounded-2xl bg-gradient-to-br from-[#faf9f7] to-[#f5f0e8] flex items-center justify-center shadow-md border border-[#e8e4dc] group-hover:border-[#c9a962]/50 transition-colors duration-300">
@@ -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,29 +1,23 @@
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;
} catch (e) {
return [];
@@ -31,40 +25,28 @@ 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
<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,29 +72,22 @@ 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 (
<>
<Header />
<main className="min-h-screen bg-white">
<ProductDetail
product={product}
<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
<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,28 +55,25 @@ 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>
) : (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 lg:gap-8">
{products.map((product, index) => (
<ProductCard
key={product.id}
product={product}
<ProductCard
key={product.id}
product={product}
index={index}
locale={localeUpper}
locale="SR"
/>
))}
</div>
@@ -97,7 +82,7 @@ export default async function ProductsPage({ params }: ProductsPageProps) {
</section>
</div>
</main>
<div className="pt-16">
<Footer />
</div>