From 0d13390f345beed9a3d9ebd6ff8ad2a5c6ea38a3 Mon Sep 17 00:00:00 2001 From: Thanakarn Klangkasame <77600906+Simulationable@users.noreply.github.com> Date: Fri, 31 Oct 2025 16:57:00 +0700 Subject: [PATCH] Add Jenkinsfile --- Jenkinsfile | 109 +++++++++++ src/app/(public)/about/page.tsx | 61 +++--- src/app/(public)/companies/page.tsx | 77 ++++++-- src/app/(public)/contact/page.tsx | 119 +++++++++--- src/app/(public)/distribution/page.tsx | 58 ++++-- src/app/(public)/founders/page.tsx | 89 +++++++-- src/app/(public)/future/page.tsx | 93 +++++++-- src/app/(public)/milestones/page.tsx | 70 +++++-- src/app/(public)/performance/page.tsx | 91 +++++---- src/app/(public)/products/page.tsx | 60 +++++- src/app/components/Footer.tsx | 114 +++++++++-- src/app/components/Nav.tsx | 171 ++++++++++++---- src/app/components/ProductGrid.tsx | 80 ++++++-- src/app/components/Section.tsx | 42 ++-- src/app/components/StatCard.tsx | 31 ++- src/app/components/Timeline.tsx | 78 ++++++-- src/app/fonts.ts | 10 + src/app/layout.tsx | 45 ++--- src/app/page.tsx | 259 +++++++++++++++++++------ 19 files changed, 1294 insertions(+), 363 deletions(-) create mode 100644 Jenkinsfile diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..3e1d2a4 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,109 @@ +pipeline { + agent any + options { + ansiColor('xterm'); timestamps(); disableConcurrentBuilds() + timeout(time: 25, unit: 'MINUTES') + buildDiscarder(logRotator(numToKeepStr: '20', artifactNumToKeepStr: '10')) + } + + environment { + REGISTRY = 'registry.aetherframe.tech' + IMAGE = 'simulationable/amrez-nova-web-services-app' // 👈 ชื่ออิมเมจให้ตรง repo นี้ + APP_PORT = '3000' // Next.js in container + HOST_PORT = '5007' // 👈 พอร์ต host (กันชนตัวอื่น) + NODE_ENV = 'production' + NEXT_TELEMETRY_DISABLED = '1' + DOCKER_BUILDKIT = '0' + DOCKER_CLI_HINTS = 'false' + } + + stages { + stage('Checkout'){ steps { checkout scm } } + + stage('Preflight') { + steps { + sh ''' + set -eux + echo "== Sys =="; whoami || true; uname -a || true + echo "== Git =="; git rev-parse --abbrev-ref HEAD; git rev-parse --short=12 HEAD + echo "== Docker =="; docker version; docker info || true + echo "== Registry =="; getent hosts ${REGISTRY} || true; curl -sS -I https://${REGISTRY}/v2/ || true + ''' + } + } + + stage('Docker Build'){ + steps { + script { + def branch = (env.BRANCH_NAME ?: env.GIT_BRANCH ?: sh(script: 'git rev-parse --abbrev-ref HEAD', returnStdout: true).trim()) + .replaceFirst(/^origin\\//,'').toLowerCase() + def commit = sh(script: 'git rev-parse --short=12 HEAD', returnStdout: true).trim() + def tag = "${branch}-${env.BUILD_NUMBER}" + env.IMAGE_TAG = tag + + sh """ + set -eux + docker build --pull \ + --label org.opencontainers.image.revision="${commit}" \ + -t ${REGISTRY}/${IMAGE}:${tag} \ + -t ${REGISTRY}/${IMAGE}:latest \ + . + docker images ${REGISTRY}/${IMAGE} --digests || true + """ + } + } + } + + stage('Registry Check') { + steps { + sh ''' + set -eux + curl -sS -I https://${REGISTRY}/v2/ || true + docker images ${REGISTRY}/${IMAGE} --digests || true + ''' + } + } + + stage('Docker Push'){ + steps { + withCredentials([usernamePassword(credentialsId:'reg-creds', usernameVariable:'REG_USER', passwordVariable:'REG_PASS')]){ + sh ''' + set -eux + docker logout ${REGISTRY} || true + echo "$REG_PASS" | docker login ${REGISTRY} -u "$REG_USER" --password-stdin + docker push ${REGISTRY}/${IMAGE}:${IMAGE_TAG} + docker push ${REGISTRY}/${IMAGE}:latest + curl -sS -u "$REG_USER:$REG_PASS" https://${REGISTRY}/v2/${IMAGE}/tags/list || true + docker logout ${REGISTRY} || true + ''' + } + } + } + + stage('Deploy (same host)'){ + when { branch 'main' } + steps { + sh """ + set -eux + docker rm -f amrez-nova-web || true + docker run -d --name amrez-nova-web \\ + -p 127.0.0.1:${HOST_PORT}:${APP_PORT} \\ + -e NODE_ENV=${NODE_ENV} \\ + -e PORT=${APP_PORT} \\ + --restart=always \\ + ${REGISTRY}/${IMAGE}:latest + sleep 2 + docker ps --no-trunc | sed -n '1,5p' || true + docker logs --tail=200 amrez-nova-web || true + (curl -fsS http://127.0.0.1:${HOST_PORT}/ || true) + """ + } + } + } + + post { + success { echo "✅ Deployed at http://127.0.0.1:${HOST_PORT}" } + failure { echo "❌ Failed — ดูสเตจ Preflight/Push/Deploy" } + always { cleanWs() } + } +} diff --git a/src/app/(public)/about/page.tsx b/src/app/(public)/about/page.tsx index 163b1d1..6d626b3 100644 --- a/src/app/(public)/about/page.tsx +++ b/src/app/(public)/about/page.tsx @@ -1,24 +1,41 @@ -import Section from "../../components/Section"; -import { company } from "../../lib/data"; +// src/app/about/page.tsx +import Section from "@/app/components/Section"; +import {company} from "@/app/lib/data"; export default function AboutPage() { - return ( -
-
-
-

Vision

-

{company.vision}

-
-
-

Mission

-

{company.mission}

-
-
-
-

Company

-

{company.about}

-

Source: Company profile

-
-
- ); -} \ No newline at end of file + return ( +
+
+ {/* Vision */} +
+

+ Vision +

+

+ {company.vision} +

+
+ + {/* Mission */} +
+

+ Mission +

+

+ {company.mission} +

+
+
+ + {/* Company */} +
+

+ Company +

+

+ {company.about} +

+
+
+ ); +} diff --git a/src/app/(public)/companies/page.tsx b/src/app/(public)/companies/page.tsx index 6ed953b..d54c83b 100644 --- a/src/app/(public)/companies/page.tsx +++ b/src/app/(public)/companies/page.tsx @@ -1,17 +1,64 @@ -import Section from "../../components/Section"; -import { groupCompanies } from "../../lib/data"; +// src/app/companies/page.tsx +import Section from "@/app/components/Section"; +import Link from "next/link"; +import {groupCompanies} from "@/app/lib/data"; export default function CompaniesPage() { - return ( -
-
- {groupCompanies.map((c, i) => ( -
-

{c.name}

-

{c.focus}

-
- ))} -
-
- ); -} \ No newline at end of file + return ( +
+
+ {groupCompanies.map((c: any, i: number) => ( +
+
+ {c.logo ? ( + {c.name} + ) : ( +
+ {(c.name?.[0] ?? "•").toUpperCase()} +
+ )} +
+

+ {c.name} +

+ {c.focus && ( +

{c.focus}

+ )} +
+
+ + {(c.url || c.href) && ( +
+ {c.url ? ( + + Visit site → + + ) : ( + + Learn more → + + )} +
+ )} +
+ ))} +
+
+ ); +} diff --git a/src/app/(public)/contact/page.tsx b/src/app/(public)/contact/page.tsx index 5b8e107..d619ace 100644 --- a/src/app/(public)/contact/page.tsx +++ b/src/app/(public)/contact/page.tsx @@ -1,26 +1,101 @@ +// src/app/contact/page.tsx "use client"; -import Section from "../../components/Section"; -import { useState } from "react"; +import Section from "@/app/components/Section"; +import {useState} from "react"; export default function ContactPage() { - const [sent, setSent] = useState(false); - return ( -
- {!sent ? ( -
{e.preventDefault(); setSent(true);}}> - - - - -

This is a demo form. Hook your API or email service here.

-
- ) : ( -
-

Thanks!

-

We‘ll be in touch shortly.

-
- )} -
- ); -} \ No newline at end of file + const [sent, setSent] = useState(false); + + const field = + "w-full rounded-xl border border-black/10 bg-white px-4 py-3 text-neutral-900 placeholder:text-neutral-400 " + + "focus:outline-none focus:ring-2 focus:ring-neutral-900/10 focus:border-neutral-300"; + + return ( +
+ {!sent ? ( +
{ + e.preventDefault(); + setSent(true); + }} + noValidate + style={{fontFamily: "var(--font-dbheavent, ui-sans-serif, system-ui)"}} + > +
+
+ + +
+
+ + +
+
+ +
+ +