feat: integrate RapidAPI Instagram feed, apply Bodoni Moda font globally, disable prisma query logs, and fix UI minor issues

This commit is contained in:
mstfyldz
2026-06-09 22:45:18 +03:00
parent 0c510c95b5
commit f5b17e6e0d
8 changed files with 98 additions and 39 deletions
+6 -11
View File
@@ -1,17 +1,12 @@
import type { Metadata } from "next"; import type { Metadata } from "next";
import { Playfair_Display, Lato } from "next/font/google"; import { Bodoni_Moda } from "next/font/google";
import "../globals.css"; import "../globals.css";
import { prisma } from "@/lib/db"; import { prisma } from "@/lib/db";
const playfair = Playfair_Display({ const bodoni = Bodoni_Moda({
variable: "--font-playfair", variable: "--font-bodoni",
subsets: ["latin"], subsets: ["latin"],
}); display: "swap",
const lato = Lato({
variable: "--font-lato",
subsets: ["latin"],
weight: ["300", "400", "700", "900"],
}); });
export async function generateStaticParams() { export async function generateStaticParams() {
@@ -147,14 +142,14 @@ export default async function RootLayout({
}; };
return ( return (
<html lang={lang} className="scroll-smooth"> <html lang={lang} className={`scroll-smooth ${bodoni.variable}`}>
<head> <head>
<script <script
type="application/ld+json" type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/> />
</head> </head>
<body className={`${lato.variable} ${playfair.variable} antialiased`}> <body className="antialiased font-sans">
{children} {children}
</body> </body>
</html> </html>
+42 -3
View File
@@ -9,15 +9,54 @@ import Footer from "@/components/Footer";
import { getDictionary } from "../dictionaries"; import { getDictionary } from "../dictionaries";
import { prisma } from "@/lib/db"; import { prisma } from "@/lib/db";
async function getInstagramData() {
try {
const res = await fetch('https://instagram120.p.rapidapi.com/api/instagram/posts', {
method: 'POST',
headers: {
'x-rapidapi-key': process.env.RAPIDAPI_KEY || '',
'x-rapidapi-host': 'instagram120.p.rapidapi.com',
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: 'moy_akyaka',
maxId: ''
}),
next: { revalidate: 86400 } // 24 hours
});
if (!res.ok) {
console.error('Instagram verisi çekilemedi:', await res.text());
return [];
}
const data = await res.json();
const edges = data?.result?.edges || [];
// Get the first 4 posts and map them
return edges.slice(0, 4).map((edge: any) => ({
id: edge.node.id || edge.node.code,
url: `https://www.instagram.com/p/${edge.node.code}/`,
imageUrl: edge.node.image_versions2?.candidates?.[0]?.url || edge.node.display_uri || '',
caption: edge.node.caption?.text || '',
isVideo: edge.node.media_type === 2
}));
} catch (error) {
console.error('Instagram API hatası:', error);
return [];
}
}
export default async function Home({ params }: { params: Promise<{ lang: string }> }) { export default async function Home({ params }: { params: Promise<{ lang: string }> }) {
const { lang } = await params; const { lang } = await params;
const dict = await getDictionary(lang); const dict = await getDictionary(lang);
const [dbPhotos, dbServices, heroMediaList, dbSettings] = await Promise.all([ const [dbPhotos, dbServices, heroMediaList, dbSettings, instagramPosts] = await Promise.all([
prisma.gallery.findMany({ orderBy: { createdAt: 'desc' } }), prisma.gallery.findMany({ orderBy: { createdAt: 'desc' } }),
prisma.service.findMany({ orderBy: { createdAt: 'asc' } }), prisma.service.findMany({ orderBy: { createdAt: 'asc' } }),
prisma.heroMedia.findMany(), prisma.heroMedia.findMany(),
prisma.siteSettings.findFirst() prisma.siteSettings.findFirst(),
getInstagramData()
]); ]);
const dbHeroMedia = heroMediaList.length > 0 ? heroMediaList[0] : null; const dbHeroMedia = heroMediaList.length > 0 ? heroMediaList[0] : null;
@@ -29,7 +68,7 @@ export default async function Home({ params }: { params: Promise<{ lang: string
<About dict={dict} /> <About dict={dict} />
<Services dict={dict} dbServices={dbServices} /> <Services dict={dict} dbServices={dbServices} />
<Gallery dict={dict} dbPhotos={dbPhotos} /> <Gallery dict={dict} dbPhotos={dbPhotos} />
<InstagramFeed dict={dict} /> <InstagramFeed dict={dict} posts={instagramPosts} />
<Contact dict={dict} dbSettings={dbSettings} /> <Contact dict={dict} dbSettings={dbSettings} />
<Footer dict={dict} dbSettings={dbSettings} /> <Footer dict={dict} dbSettings={dbSettings} />
</main> </main>
+2 -2
View File
@@ -16,8 +16,8 @@
--color-brand-white: #FFFFFF; --color-brand-white: #FFFFFF;
--color-brand-black: #171717; --color-brand-black: #171717;
--font-serif: var(--font-playfair); --font-serif: var(--font-bodoni);
--font-sans: var(--font-lato); --font-sans: var(--font-bodoni);
} }
:root { :root {
+1
View File
@@ -37,6 +37,7 @@ export default function Header({ dict, lang, dbSettings }: { dict: any, lang: st
width={120} width={120}
height={48} height={48}
className={`h-12 w-auto transition-all duration-300 ${(!isScrolled && !dbSettings?.logoUrl) ? "brightness-0 invert opacity-90" : ""}`} className={`h-12 w-auto transition-all duration-300 ${(!isScrolled && !dbSettings?.logoUrl) ? "brightness-0 invert opacity-90" : ""}`}
style={{ width: "auto" }}
/> />
</a> </a>
+1 -1
View File
@@ -59,7 +59,7 @@ export default function Hero({ dict, dbHeroMedia }: { dict: any, dbHeroMedia?: a
/> />
{/* Main Title — word-by-word reveal */} {/* Main Title — word-by-word reveal */}
<h1 className="text-[4.5rem] md:text-[8rem] lg:text-[11rem] font-serif text-white leading-none tracking-tight mb-6"> <h1 className="text-[4.5rem] md:text-[6rem] lg:text-[10rem] font-serif text-white leading-none tracking-tight mb-6">
{words.map((word, i) => ( {words.map((word, i) => (
<span key={word} className="inline-block overflow-hidden"> <span key={word} className="inline-block overflow-hidden">
<motion.span <motion.span
+44 -21
View File
@@ -1,11 +1,12 @@
"use client"; "use client";
const reels = [ export interface InstagramPost {
{ id: 1, url: "https://www.instagram.com/reel/C97iqFqoSyl/embed" }, id: string;
{ id: 2, url: "https://www.instagram.com/reel/C-slzOcIQB3/embed" }, url: string;
{ id: 3, url: "https://www.instagram.com/reel/DLM8ng6MOKf/embed" }, imageUrl: string;
{ id: 4, url: "https://www.instagram.com/reel/DX-BDErM5qd/embed" }, caption: string;
]; isVideo: boolean;
}
function InstagramIcon() { function InstagramIcon() {
return ( return (
@@ -17,7 +18,7 @@ function InstagramIcon() {
); );
} }
export default function InstagramFeed({ dict }: { dict: any }) { export default function InstagramFeed({ dict, posts = [] }: { dict: any, posts?: InstagramPost[] }) {
return ( return (
<section className="py-24 bg-sand-light text-sea-dark overflow-hidden"> <section className="py-24 bg-sand-light text-sea-dark overflow-hidden">
<div className="container mx-auto px-6 max-w-7xl"> <div className="container mx-auto px-6 max-w-7xl">
@@ -48,24 +49,46 @@ export default function InstagramFeed({ dict }: { dict: any }) {
{/* Reels grid */} {/* Reels grid */}
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 md:gap-6"> <div className="grid grid-cols-2 lg:grid-cols-4 gap-4 md:gap-6">
{reels.map((reel) => ( {posts.map((post) => (
<div <a
key={reel.id} key={post.id}
className="group relative bg-white rounded-2xl overflow-hidden shadow-sm hover:shadow-xl hover:shadow-sea-dark/10 transition-all duration-500 hover:-translate-y-1" href={post.url}
target="_blank"
rel="noopener noreferrer"
className="group relative bg-white rounded-2xl overflow-hidden shadow-sm hover:shadow-xl hover:shadow-sea-dark/10 transition-all duration-500 hover:-translate-y-1 block"
> >
{/* Thin coral top border accent */} {/* Thin coral top border accent */}
<div className="absolute top-0 left-0 right-0 h-[2px] bg-gradient-to-r from-coral/0 via-coral to-coral/0 opacity-0 group-hover:opacity-100 transition-opacity duration-500 z-10" /> <div className="absolute top-0 left-0 right-0 h-[2px] bg-gradient-to-r from-coral/0 via-coral to-coral/0 opacity-0 group-hover:opacity-100 transition-opacity duration-500 z-10" />
<iframe <div className="relative aspect-[4/5] w-full overflow-hidden bg-sand-dark/10">
src={reel.url} <img
className="w-full h-[520px] md:h-[580px]" src={post.imageUrl || ''}
style={{ border: "none", display: "block" }} alt={post.caption ? post.caption.substring(0, 50) : 'Instagram Post'}
scrolling="no" className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700"
frameBorder="0" />
allow="encrypted-media"
title={`Instagram Reel ${reel.id}`} {/* Hover Overlay */}
/> <div className="absolute inset-0 bg-sea-dark/40 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-center justify-center">
</div> <div className="text-white transform scale-90 group-hover:scale-100 transition-transform duration-300">
{post.isVideo ? (
<svg className="w-12 h-12" fill="currentColor" viewBox="0 0 24 24">
<path d="M8 5v14l11-7z" />
</svg>
) : (
<InstagramIcon />
)}
</div>
</div>
</div>
{post.caption && (
<div className="p-4">
<p className="text-sm text-sea-dark/80 line-clamp-2">
{post.caption}
</p>
</div>
)}
</a>
))} ))}
</div> </div>
+1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -7,7 +7,7 @@ const globalForPrisma = globalThis as unknown as {
export const prisma = export const prisma =
globalForPrisma.prisma ?? globalForPrisma.prisma ??
new PrismaClient({ new PrismaClient({
log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'], log: process.env.NODE_ENV === 'development' ? ['error', 'warn'] : ['error'],
}); });
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma; if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;