diff --git a/app/[lang]/blog/page.tsx b/app/[lang]/blog/page.tsx index 00d9fbe..ed5e06a 100644 --- a/app/[lang]/blog/page.tsx +++ b/app/[lang]/blog/page.tsx @@ -7,8 +7,8 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: Loc const { lang } = await params; const dict = await getDictionary(lang); return { - title: `${dict.blog.title} | ${dict.nav.brandName}${dict.nav.brandSub}`, - description: dict.blog.desc, + title: dict.meta.blog.title, + description: dict.meta.blog.desc, }; } diff --git a/app/[lang]/contact/page.tsx b/app/[lang]/contact/page.tsx index 2220707..15b7a07 100644 --- a/app/[lang]/contact/page.tsx +++ b/app/[lang]/contact/page.tsx @@ -6,8 +6,8 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: Loc const { lang } = await params; const dict = await getDictionary(lang); return { - title: `${dict.contact.title} | ${dict.nav.brandName}${dict.nav.brandSub}`, - description: dict.contact.desc, + title: dict.meta.contact.title, + description: dict.meta.contact.desc, }; } diff --git a/app/[lang]/expertise/[slug]/page.tsx b/app/[lang]/expertise/[slug]/page.tsx new file mode 100644 index 0000000..cefd883 --- /dev/null +++ b/app/[lang]/expertise/[slug]/page.tsx @@ -0,0 +1,49 @@ +import { getDictionary } from "@/get-dictionary"; +import type { Locale } from "@/i18n-config"; +import WorkClient from "@/components/WorkClient"; +import { getProjects } from "@/app/actions"; +import { slugify } from "@/lib/utils"; +import { notFound } from "next/navigation"; + +export async function generateMetadata({ params }: { params: Promise<{ lang: Locale, slug: string }> }) { + const { lang, slug } = await params; + const dict = await getDictionary(lang); + const projects = await getProjects(); + + const filteredProjects = projects.filter((p: any) => slugify(p.tag) === slug); + if (filteredProjects.length === 0) return { title: "Not Found" }; + + const tagName = filteredProjects[0].tag; + const titleStr = lang === "tr" ? `${tagName} Ajansı` : `${tagName} Agency`; + + return { + title: `${titleStr} | ${dict.nav.brandName}`, + description: `${titleStr} - ${dict.work.desc}`, + }; +} + +export default async function ExpertisePage({ params }: { params: Promise<{ lang: Locale, slug: string }> }) { + const { lang, slug } = await params; + const dict = await getDictionary(lang); + const projects = await getProjects(); + + const filteredProjects = projects.filter((p: any) => slugify(p.tag) === slug); + + if (filteredProjects.length === 0) { + notFound(); + } + + const tagName = filteredProjects[0].tag; + const titleStr = lang === "tr" ? `${tagName} Ajansı` : `${tagName} Agency`; + + // Clone dict to overwrite the work title specifically for this page + const customDict = { + ...dict, + work: { + ...dict.work, + title: titleStr + } + }; + + return ; +} diff --git a/app/[lang]/faq/page.tsx b/app/[lang]/faq/page.tsx index 979efe5..0234a33 100644 --- a/app/[lang]/faq/page.tsx +++ b/app/[lang]/faq/page.tsx @@ -6,8 +6,8 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: Loc const { lang } = await params; const dict = await getDictionary(lang); return { - title: `${dict.faq.title} | ${dict.nav.brandName}${dict.nav.brandSub}`, - description: dict.faq.desc, + title: dict.meta.faq.title, + description: dict.meta.faq.desc, }; } diff --git a/app/[lang]/layout.tsx b/app/[lang]/layout.tsx index e4b7d11..9fa1805 100644 --- a/app/[lang]/layout.tsx +++ b/app/[lang]/layout.tsx @@ -21,41 +21,48 @@ const ibmPlexMono = IBM_Plex_Mono({ variable: "--font-ibm-plex-mono", }); -export const metadata: Metadata = { - metadataBase: new URL('https://ayris.tech'), - title: "Ayris Tech | Forging the Future with AI & Blockchain", - description: "Ayris Tech delivers enterprise-grade digital transformation. We build the impossible for the next generation of business using AI, Blockchain, and modern web technologies.", - keywords: ["AI Solutions", "Blockchain Development", "Mobile App", "Web Development", "Digital Transformation"], - openGraph: { - title: "Ayris Tech | Forging the Future with AI & Blockchain", - description: "Ayris Tech delivers enterprise-grade digital transformation. We build the impossible for the next generation of business using AI, Blockchain, and modern web technologies.", - url: 'https://ayris.tech', - siteName: 'Ayris Tech', - images: [ - { - url: '/og-image.jpg', - width: 1200, - height: 630, - alt: 'Ayris Tech - AI & Blockchain Solutions' - } - ], - locale: 'en_US', - type: 'website', - }, - twitter: { - card: 'summary_large_image', - title: "Ayris Tech | Forging the Future with AI & Blockchain", - description: "Ayris Tech delivers enterprise-grade digital transformation.", - images: ['/og-image.jpg'], - }, - alternates: { - canonical: '/', - languages: { - 'en': '/en', - 'tr': '/tr', +import { getDictionary } from "@/get-dictionary"; + +export async function generateMetadata({ params }: { params: Promise<{ lang: Locale }> }): Promise { + const { lang } = await params; + const dict = await getDictionary(lang); + + return { + metadataBase: new URL('https://ayris.tech'), + title: dict.meta.home.title, + description: dict.meta.home.desc, + keywords: ["AI Solutions", "Blockchain Development", "Mobile App", "Web Development", "Digital Transformation"], + openGraph: { + title: dict.meta.home.title, + description: dict.meta.home.desc, + url: 'https://ayris.tech', + siteName: 'Ayris Tech', + images: [ + { + url: '/og-image.jpg', + width: 1200, + height: 630, + alt: 'Ayris Tech - AI & Blockchain Solutions' + } + ], + locale: lang === 'tr' ? 'tr_TR' : 'en_US', + type: 'website', }, - }, -}; + twitter: { + card: 'summary_large_image', + title: dict.meta.home.title, + description: dict.meta.home.desc, + images: ['/og-image.jpg'], + }, + alternates: { + canonical: '/', + languages: { + 'en': '/en', + 'tr': '/tr', + }, + }, + }; +} const jsonLd = { '@context': 'https://schema.org', diff --git a/app/[lang]/page.tsx b/app/[lang]/page.tsx index a6042c4..bcc3fb9 100644 --- a/app/[lang]/page.tsx +++ b/app/[lang]/page.tsx @@ -6,8 +6,8 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: Loc const { lang } = await params; const dict = await getDictionary(lang); return { - title: `${dict.nav.brandName}${dict.nav.brandSub} | ${dict.hero.badge}`, - description: dict.hero.desc, + title: dict.meta.home.title, + description: dict.meta.home.desc, }; } diff --git a/app/[lang]/process/page.tsx b/app/[lang]/process/page.tsx index e97eec1..d48ef35 100644 --- a/app/[lang]/process/page.tsx +++ b/app/[lang]/process/page.tsx @@ -6,8 +6,8 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: Loc const { lang } = await params; const dict = await getDictionary(lang); return { - title: `${dict.process.title} | ${dict.nav.brandName}${dict.nav.brandSub}`, - description: dict.process.desc, + title: dict.meta.process.title, + description: dict.meta.process.desc, }; } diff --git a/app/[lang]/services/page.tsx b/app/[lang]/services/page.tsx index 7b5996d..89bef04 100644 --- a/app/[lang]/services/page.tsx +++ b/app/[lang]/services/page.tsx @@ -6,8 +6,8 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: Loc const { lang } = await params; const dict = await getDictionary(lang); return { - title: `${dict.services.title} | ${dict.nav.brandName}${dict.nav.brandSub}`, - description: dict.services.desc, + title: dict.meta.services.title, + description: dict.meta.services.desc, }; } diff --git a/app/[lang]/work/page.tsx b/app/[lang]/work/page.tsx index aebe7b7..858ac98 100644 --- a/app/[lang]/work/page.tsx +++ b/app/[lang]/work/page.tsx @@ -7,8 +7,8 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: Loc const { lang } = await params; const dict = await getDictionary(lang); return { - title: `${dict.work.title} | ${dict.nav.brandName}${dict.nav.brandSub}`, - description: dict.work.desc, + title: dict.meta.work.title, + description: dict.meta.work.desc, }; } diff --git a/app/actions.ts b/app/actions.ts index 3485990..7bf35ee 100644 --- a/app/actions.ts +++ b/app/actions.ts @@ -11,6 +11,7 @@ export async function getProjects() { try { return await prisma.project.findMany({ orderBy: { num: "asc" }, + take: 50, }); } catch (error) { console.error("Error fetching projects:", error); diff --git a/app/sitemap.ts b/app/sitemap.ts index 67a8b8e..d6d2769 100644 --- a/app/sitemap.ts +++ b/app/sitemap.ts @@ -1,9 +1,11 @@ import { MetadataRoute } from 'next'; +import { getProjects } from '@/app/actions'; +import { slugify } from '@/lib/utils'; -export default function sitemap(): MetadataRoute.Sitemap { +export default async function sitemap(): Promise { const baseUrl = 'https://ayris.tech'; - return [ + const staticUrls: MetadataRoute.Sitemap = [ { url: `${baseUrl}`, lastModified: new Date(), @@ -47,4 +49,32 @@ export default function sitemap(): MetadataRoute.Sitemap { priority: 0.8, }, ]; + + try { + const projects = await getProjects(); + const uniqueTags = Array.from(new Set(projects.map((p: any) => p.tag))); + + const expertiseUrls: MetadataRoute.Sitemap = uniqueTags.flatMap((tag) => { + if (typeof tag !== 'string') return []; + const slug = slugify(tag); + return [ + { + url: `${baseUrl}/en/expertise/${slug}`, + lastModified: new Date(), + changeFrequency: 'monthly', + priority: 0.7, + }, + { + url: `${baseUrl}/tr/expertise/${slug}`, + lastModified: new Date(), + changeFrequency: 'monthly', + priority: 0.7, + } + ]; + }); + + return [...staticUrls, ...expertiseUrls]; + } catch(e) { + return staticUrls; + } } diff --git a/components/HomeClient.tsx b/components/HomeClient.tsx index e88e523..d02a3a9 100644 --- a/components/HomeClient.tsx +++ b/components/HomeClient.tsx @@ -10,10 +10,10 @@ import { } from "framer-motion"; import { useRef, useState, useEffect, useCallback } from "react"; import Header from "@/components/Header"; -import Footer from "@/components/Footer"; import type { Locale } from "@/i18n-config"; -import Link from "next/link"; -import { blogPosts } from "@/data/blog"; +import dynamic from "next/dynamic"; + +const HomeClientBelowFold = dynamic(() => import('./HomeClientBelowFold')); // ── EASING ── const expo: [number, number, number, number] = [0.16, 1, 0.3, 1]; @@ -181,42 +181,7 @@ export default function HomeClient({ lang, dict }: { lang: Locale; dict: any }) const heroY = useTransform(scrollYProgress, [0, 1], ["0%", "25%"]); const heroOpacity = useTransform(scrollYProgress, [0, 0.7], [1, 0]); - const [activeFaq, setActiveFaq] = useState(null); - const [formSent, setFormSent] = useState(false); - const [hoverCase, setHoverCase] = useState(null); - - const serviceIcons = [, , , ]; - const serviceCodes = ["AI / ML", "BC / WEB3", "MOB / DART", "WEB / NEXT"]; - - const services = dict.services.items - ? Object.entries(dict.services.items as Record).map(([, val], idx) => ({ - icon: serviceIcons[idx], - code: serviceCodes[idx], - title: val.title, - desc: val.desc, - items: [ - ["Predictive Models", "NLP Pipelines", "Computer Vision", "Agentic Workflows"], - ["Smart Contract Audit", "DeFi Architecture", "Tokenomics Design", "Cross-chain Bridges"], - ["Universal Codebase", "Offline Syncing", "Biometric Protocols", "Core Bundle Optimization"], - ["Next.js App Router", "Server Components", "Headless Commerce", "Modular API Mesh"], - ][idx], - })) - : []; - - const caseStudies = (dict.work.items as any[]).map((item: any, idx: number) => ({ - ...item, - tech: [ - ["Python", "TensorFlow", "Next.js", "PostgreSQL"], - ["Solidity", "React", "IPFS", "Node.js"], - ["Flutter", "Firebase", "Django", "AWS"], - ["Next.js", "Stripe", "Redis", "Vercel"], - ][idx] || ["React", "TypeScript", "Node.js"], - accent: ["#FFE600", "#FF4500", "#0A0A0A", "#00CC77"][idx] || "#FFE600", - })); - - const processes = dict.process.items as any[]; - const featuredPosts = blogPosts; - const faqs = dict.faq.items as any[]; + return (
@@ -318,546 +283,7 @@ export default function HomeClient({ lang, dict }: { lang: Locale; dict: any })
- {/* ──────────────── MARQUEE 1 ──────────────── */} - - - {/* ──────────────── STATS ──────────────── */} -
-
- {[ - { val: 48, suffix: "+", label: dict.stats.deliveries }, - { val: 98, suffix: "%", label: dict.stats.retention }, - { val: 12, suffix: "+", label: dict.stats.regions }, - { val: 1, suffix: "wk", label: lang === "tr" ? "MVP Süresi" : "Avg Cycle To MVP" }, - ].map((s, idx) => ( - -
- -
-
- {s.label} -
-
- ))} -
-
- - {/* ──────────────── SERVICES ──────────────── */} -
-
- -
-
- -

- -

-
-
-

- {dict.services.desc} -

-
-
- -
- {services.map((s, idx) => ( - - {/* Hover accent left bar */} - - -
-
- {s.icon} -
- - {s.code} - -
- -

- {s.title} -

-

- {s.desc} -

- -
- {s.items?.map((item: string) => ( - - {item} - - ))} -
- - {/* Number watermark */} -
- {String(idx + 1).padStart(2, "0")} -
-
- ))} -
-
-
- - {/* ──────────────── MARQUEE 2 ──────────────── */} - - - {/* ──────────────── CASE STUDIES ──────────────── */} -
-
- -
- -

- -

-
- -
- {caseStudies.map((study: any, idx: number) => ( - setHoverCase(idx)} - onHoverEnd={() => setHoverCase(null)} - initial={{ opacity: 0, y: 20 }} - whileInView={{ opacity: 1, y: 0 }} - viewport={{ once: true }} - transition={{ delay: idx * 0.08 }} - > -
- {/* Index */} -
- {study.num} -
- - {/* Title */} -
-
- - ↗ - -

- {study.title} -

-
-
- - {/* Tag */} -
- - {study.tag} - -
- - {/* Description */} -
-

- {study.desc} -

-
- - {/* Arrow */} -
- - - -
-
- - {/* Expanded tech stack */} - - {hoverCase === idx && ( - - STACK: - {study.tech.map((t: string) => ( - - {t} - - ))} - - )} - -
- ))} -
- - - - {dict.work.analyzeBtn} - - -
-
- - {/* ──────────────── PROCESS ──────────────── */} -
-
- -
- -

- -

-
- -
- {processes.map((p: any, idx: number) => ( - -
- {p.num} -
- -
- -
- {p.timeframe} -
-

- {p.title} -

-

- {p.desc} -

-
-
- ))} -
-
-
- - {/* ──────────────── BLOG FEATURED ──────────────── */} -
-
- -
-
- -

- -

-
- - {dict.blog.viewAll} → - -
- -
- {featuredPosts.map((post, idx) => { - const content = post[lang] || post.en; - const accent = ["#FFE600", "#FF4500", "#00CC77"][idx] || "#FFE600"; - return ( - -
- - {content.category} - - - {post.date} - -
- -

- {content.title} -

- -

- {content.excerpt} -

- -
-
- {post.author} - {post.authorRole} -
- - - - - - -
- - {/* Top line category accent */} -
- - ); - })} -
-
-
- - {/* ──────────────── FAQ ──────────────── */} -
-
- -
- -

- -

-
- -
- {faqs.map((faq: any, idx: number) => ( - - - - - {activeFaq === idx && ( - -

- {faq.a} -

-
- )} -
-
- ))} -
-
-
- - {/* ──────────────── CONTACT ──────────────── */} -
-
- -
-
- -

- -

-
-
-

- {dict.contact.desc} -

-
-
- -
- - {/* Form */} -
- - {!formSent ? ( - { e.preventDefault(); setFormSent(true); }} - exit={{ opacity: 0, y: -10 }} - transition={{ duration: 0.3 }} - > -
- {[ - { label: dict.contact.formName, placeholder: dict.contact.formNamePlaceholder, type: "text" }, - { label: dict.contact.formEmail, placeholder: dict.contact.formEmailPlaceholder, type: "email" }, - { label: dict.contact.formCompany, placeholder: dict.contact.formCompanyPlaceholder, type: "text" }, - { label: dict.contact.formTrack, type: "select", options: ["$5k – $20k", "$20k – $50k", "$50k – $100k", "$100k+"] }, - ].map((field, fi) => ( -
- - {field.type === "select" ? ( - - ) : ( - - )} -
- ))} -
- -
- -