Remove /en routes to fix build - using sr locale only

This commit is contained in:
Unchained
2026-03-06 16:24:06 +02:00
parent 5df87cbb9d
commit 7d23176b6a
10 changed files with 184 additions and 366 deletions

View File

@@ -3,7 +3,7 @@ WORKDIR /app
FROM base AS deps
COPY package.json package-lock.json* ./
RUN npm ci
RUN npm install
FROM base AS builder
COPY --from=deps /app/node_modules ./node_modules

View File

@@ -0,0 +1,72 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: storefront
namespace: manoonoils
labels:
app: storefront
spec:
replicas: 1
selector:
matchLabels:
app: storefront
template:
metadata:
labels:
app: storefront
spec:
containers:
- name: storefront
image: node:22-alpine
workingDir: /app
command:
- sh
- -c
- |
if [ ! -d ".git" ]; then
echo "Cloning repository..."
apk add --no-cache git openssh-client
mkdir -p ~/.ssh
ssh-keyscan -p 222 100.74.155.73 >> ~/.ssh/known_hosts 2>/dev/null || true
GIT_SSH_COMMAND='ssh -p 222 -o StrictHostKeyChecking=accept-new' git clone ssh://git@100.74.155.73:222/unchained/manoon-headless.git /app
else
echo "Pulling latest changes..."
git pull
fi
echo "Installing dependencies..."
npm ci --legacy-peer-deps
echo "Building..."
npm run build
echo "Starting..."
npm start
env:
- name: NODE_ENV
value: "production"
- name: PORT
value: "3000"
- name: HOSTNAME
value: "0.0.0.0"
- name: NEXT_PUBLIC_WOOCOMMERCE_URL
value: "https://manoonoils.com"
- name: NEXT_PUBLIC_WOOCOMMERCE_CONSUMER_KEY
value: "ck_6a62a2ac8fa8d50e4757bf3b35c9d052dbbcf09f"
- name: NEXT_PUBLIC_WOOCOMMERCE_CONSUMER_SECRET
value: "cs_0ea41d2c8fc232d1e609e559ea8561d02c4406ee"
ports:
- containerPort: 3000
startupProbe:
httpGet:
path: /
port: 3000
periodSeconds: 10
failureThreshold: 60
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "1000m"

100
k8s/deployment-simple.yaml Normal file
View File

@@ -0,0 +1,100 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: storefront
namespace: manoonoils
labels:
app: storefront
spec:
replicas: 1
selector:
matchLabels:
app: storefront
template:
metadata:
labels:
app: storefront
spec:
initContainers:
- name: build
image: node:22-alpine
workingDir: /app
command:
- sh
- -c
- |
echo "Installing dependencies..."
npm ci --legacy-peer-deps
echo "Building Next.js app..."
npm run build
echo "Build complete!"
env:
- name: NEXT_PUBLIC_WOOCOMMERCE_URL
value: "https://manoonoils.com"
- name: NEXT_PUBLIC_WOOCOMMERCE_CONSUMER_KEY
value: "ck_6a62a2ac8fa8d50e4757bf3b35c9d052dbbcf09f"
- name: NEXT_PUBLIC_WOOCOMMERCE_CONSUMER_SECRET
value: "cs_0ea41d2c8fc232d1e609e559ea8561d02c4406ee"
- name: NODE_ENV
value: "production"
volumeMounts:
- name: app-code
mountPath: /app
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "1000m"
containers:
- name: storefront
image: node:22-alpine
workingDir: /app
command:
- sh
- -c
- |
echo "Starting Next.js..."
npm start
env:
- name: NODE_ENV
value: "production"
- name: PORT
value: "3000"
- name: HOSTNAME
value: "0.0.0.0"
ports:
- containerPort: 3000
protocol: TCP
startupProbe:
httpGet:
path: /
port: 3000
periodSeconds: 10
failureThreshold: 30
readinessProbe:
httpGet:
path: /
port: 3000
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /
port: 3000
periodSeconds: 10
failureThreshold: 3
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
volumeMounts:
- name: app-code
mountPath: /app
volumes:
- name: app-code
emptyDir: {}

10
package-lock.json generated
View File

@@ -11,6 +11,7 @@
"@woocommerce/woocommerce-rest-api": "^1.0.2",
"clsx": "^2.1.1",
"framer-motion": "^12.34.4",
"lucide-react": "^0.577.0",
"next": "16.1.6",
"next-intl": "^4.8.3",
"react": "19.2.3",
@@ -5615,6 +5616,15 @@
"yallist": "^3.0.2"
}
},
"node_modules/lucide-react": {
"version": "0.577.0",
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.577.0.tgz",
"integrity": "sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A==",
"license": "ISC",
"peerDependencies": {
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/magic-string": {
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",

View File

@@ -12,6 +12,7 @@
"@woocommerce/woocommerce-rest-api": "^1.0.2",
"clsx": "^2.1.1",
"framer-motion": "^12.34.4",
"lucide-react": "^0.577.0",
"next": "16.1.6",
"next-intl": "^4.8.3",
"react": "19.2.3",

View File

@@ -1,66 +0,0 @@
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 default function AboutPage() {
return (
<main className="min-h-screen pt-16 md:pt-20">
<Header />
<section className="py-20 px-4">
<div className="max-w-4xl mx-auto">
<h1 className="text-4xl md:text-5xl font-serif text-center mb-8">
Our Story
</h1>
<div className="prose prose-lg max-w-none text-foreground-muted space-y-6">
<p>
ManoonOils was born from a passion for natural beauty and the belief
that the best skincare comes from nature itself. Our journey began with
a simple question: how can we create products that truly nurture both
hair and skin?
</p>
<p>
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>
<h2 className="font-serif text-2xl text-foreground mt-8 mb-4">
Our Mission
</h2>
<p>
Our mission is to provide premium quality, natural products that
enhance your daily beauty routine. We are committed to:
</p>
<ul className="list-disc pl-6 space-y-2">
<li>Using only the finest natural ingredients</li>
<li>Cruelty-free and ethical production</li>
<li>Sustainable packaging practices</li>
<li>Transparency in our formulations</li>
</ul>
<h2 className="font-serif text-2xl text-foreground mt-8 mb-4">
Handmade with Love
</h2>
<p>
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.
</p>
</div>
</div>
</section>
<Footer />
</main>
);
}

View File

@@ -1,114 +0,0 @@
"use client";
import { useState } from "react";
import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer";
export default function ContactPage() {
const [formData, setFormData] = useState({
name: "",
email: "",
message: "",
});
const [submitted, setSubmitted] = useState(false);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
setSubmitted(true);
};
return (
<main className="min-h-screen pt-16 md:pt-20">
<Header />
<section className="py-20 px-4">
<div className="max-w-2xl mx-auto">
<h1 className="text-4xl md:text-5xl font-serif text-center mb-8">
Contact Us
</h1>
<p className="text-foreground-muted text-center mb-12">
Have questions? We'd love to hear from you.
</p>
{submitted ? (
<div className="bg-green-50 text-green-700 p-6 text-center">
<p className="text-lg">Thank you for your message!</p>
<p className="mt-2">We'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 border border-border focus:outline-none focus:border-foreground"
/>
</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 border border-border focus:outline-none focus:border-foreground"
/>
</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 border border-border focus:outline-none focus:border-foreground resize-none"
/>
</div>
<button
type="submit"
className="w-full py-3 bg-foreground text-white hover:bg-accent-dark transition-colors"
>
Send Message
</button>
</form>
)}
<div className="mt-16 pt-8 border-t border-border/30">
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 text-center">
<div>
<h3 className="font-serif mb-2">Email</h3>
<p className="text-foreground-muted">hello@manoonoils.com</p>
</div>
<div>
<h3 className="font-serif mb-2">Shipping</h3>
<p className="text-foreground-muted">Free over 3000 RSD</p>
</div>
<div>
<h3 className="font-serif mb-2">Location</h3>
<p className="text-foreground-muted">Serbia</p>
</div>
</div>
</div>
</div>
</section>
<Footer />
</main>
);
}

View File

@@ -1,67 +0,0 @@
import { getProducts } from "@/lib/woocommerce";
import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer";
import ProductCard from "@/components/product/ProductCard";
export const metadata = {
title: "ManoonOils - Premium Natural Oils for Hair & Skin",
description: "Discover our premium collection of natural oils for hair and skin care. Handmade with love.",
};
export default async function Homepage() {
const products = await getProducts();
const publishedProducts = products.filter((p) => p.status === "publish").slice(0, 4);
return (
<main className="min-h-screen">
<Header />
{/* Hero Section */}
<section className="relative h-[80vh] flex items-center justify-center bg-gradient-to-b from-white to-background-ice">
<div className="text-center px-4">
<h1 className="text-5xl md:text-7xl font-serif mb-6">
ManoonOils
</h1>
<p className="text-xl md:text-2xl text-foreground-muted mb-8">
Premium Natural Oils for Hair & Skin
</p>
<a
href="/products"
className="inline-block bg-foreground text-white px-8 py-4 text-lg font-medium hover:bg-opacity-90 transition-all"
>
Shop Now
</a>
</div>
</section>
{/* Products Section */}
{publishedProducts.length > 0 && (
<section className="py-20 px-4">
<div className="max-w-7xl mx-auto">
<h2 className="text-4xl font-serif text-center mb-12">Our Products</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-8">
{publishedProducts.map((product, index) => (
<ProductCard key={product.id} product={product} index={index} />
))}
</div>
</div>
</section>
)}
{/* About Teaser */}
<section className="py-20 px-4 bg-background-ice">
<div className="max-w-3xl mx-auto text-center">
<h2 className="text-3xl font-serif mb-6">Natural & Pure</h2>
<p className="text-lg text-foreground-muted mb-8">
Our oils are crafted with love using only the finest natural ingredients.
</p>
<a href="/about" className="text-foreground border-b border-foreground pb-1">
Learn More
</a>
</div>
</section>
<Footer />
</main>
);
}

View File

@@ -1,77 +0,0 @@
import { getProducts } from "@/lib/woocommerce";
import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer";
export async function generateStaticParams() {
try {
const products = await getProducts();
return products.map((product) => ({
slug: product.slug || product.id.toString(),
}));
} catch {
return [];
}
}
export default async function ProductPage({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
let product = null;
try {
const products = await getProducts();
product = products.find((p) => (p.slug || p.id.toString()) === slug);
} catch (e) {
// Fallback
}
if (!product) {
return (
<main className="min-h-screen">
<Header />
<div className="pt-24 text-center">
<h1 className="text-2xl">Product not found</h1>
</div>
<Footer />
</main>
);
}
const image = product.images?.[0]?.src || '/placeholder.jpg';
const price = product.sale_price || product.price;
return (
<main className="min-h-screen">
<Header />
<section className="pt-24 pb-20 px-4">
<div className="max-w-7xl mx-auto">
<div className="grid grid-cols-1 md:grid-cols-2 gap-12">
<div className="relative aspect-[4/5] bg-background-ice overflow-hidden">
<img
src={image}
alt={product.name}
className="object-cover w-full h-full"
/>
</div>
<div className="flex flex-col">
<h1 className="text-4xl font-serif mb-4">{product.name}</h1>
<div className="text-2xl mb-6">{price} RSD</div>
<div className="prose max-w-none mb-8" dangerouslySetInnerHTML={{ __html: product.description || '' }} />
<button
className="inline-block bg-foreground text-white px-8 py-4 text-lg font-medium text-center hover:bg-opacity-90 transition-all"
>
Add to Cart
</button>
</div>
</div>
</div>
</section>
<Footer />
</main>
);
}

View File

@@ -1,41 +0,0 @@
import { getProducts } from "@/lib/woocommerce";
import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer";
import ProductCard from "@/components/product/ProductCard";
export const metadata = {
title: "Products - ManoonOils",
description: "Browse our collection of premium natural oils for hair and skin care.",
};
export default async function ProductsPage() {
const products = await getProducts();
const publishedProducts = products.filter((p) => p.status === "publish");
return (
<main className="min-h-screen pt-16 md:pt-20">
<Header />
<section className="py-20 px-4">
<div className="max-w-7xl mx-auto">
<h1 className="text-4xl md:text-5xl font-serif text-center mb-16">
All Products
</h1>
{publishedProducts.length === 0 ? (
<p className="text-center text-foreground-muted">No products available</p>
) : (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-8">
{publishedProducts.map((product, index) => (
<ProductCard key={product.id} product={product} index={index} />
))}
</div>
)}
</div>
</section>
<Footer />
</main>
);
}