diff --git a/app/api/upload/route.ts b/app/api/upload/route.ts new file mode 100644 index 0000000..f6fad40 --- /dev/null +++ b/app/api/upload/route.ts @@ -0,0 +1,32 @@ +import { NextRequest, NextResponse } from 'next/server' +import cloudinary from '@/lib/cloudinary' + +export async function POST(request: NextRequest) { + try { + const formData = await request.formData() + const file = formData.get('file') as File + + if (!file) { + return NextResponse.json({ error: 'Dosya bulunamadı.' }, { status: 400 }) + } + + // Convert file to buffer + const arrayBuffer = await file.arrayBuffer() + const buffer = Buffer.from(arrayBuffer) + const base64 = `data:${file.type};base64,${buffer.toString('base64')}` + + // Upload to Cloudinary + const result = await cloudinary.uploader.upload(base64, { + folder: 'ayristech/projects', + transformation: [ + { width: 1200, crop: 'limit' }, + { quality: 'auto', fetch_format: 'auto' } + ] + }) + + return NextResponse.json({ url: result.secure_url }) + } catch (error) { + console.error('Upload error:', error) + return NextResponse.json({ error: 'Yükleme başarısız oldu.' }, { status: 500 }) + } +} diff --git a/components/AdminClient.tsx b/components/AdminClient.tsx index edb742b..52e5f1f 100644 --- a/components/AdminClient.tsx +++ b/components/AdminClient.tsx @@ -95,6 +95,18 @@ export default function AdminClient({ const [newGalleryItem, setNewGalleryItem] = useState(""); const [formWebsite, setFormWebsite] = useState(""); + const [uploadingImage, setUploadingImage] = useState(false); + const [uploadingGallery, setUploadingGallery] = useState(false); + + const handleFileUpload = async (file: File) => { + const fd = new FormData(); + fd.append('file', file); + const res = await fetch('/api/upload', { method: 'POST', body: fd }); + const data = await res.json(); + if (!res.ok) throw new Error(data.error || 'Yükleme başarısız'); + return data.url; + }; + // Blog CRUD State const [posts, setPosts] = useState(initialBlogPosts); const [blogSearchQuery, setBlogSearchQuery] = useState(""); @@ -1103,13 +1115,37 @@ export default function AdminClient({
- setFormImage(e.target.value)} - placeholder="https://images.unsplash.com/..." - className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]" - /> +
+ setFormImage(e.target.value)} + placeholder="https://images.unsplash.com/..." + className="flex-grow font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]" + /> + +
@@ -1205,10 +1241,32 @@ export default function AdminClient({ placeholder="https://images.unsplash.com/..." className="flex-grow font-mono text-[9px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]" /> + diff --git a/components/WorkClient.tsx b/components/WorkClient.tsx index 98d712b..6cd0b6f 100644 --- a/components/WorkClient.tsx +++ b/components/WorkClient.tsx @@ -120,6 +120,22 @@ export default function WorkClient({

{study.desc}

+ + {study.gallery && study.gallery.length > 0 && ( +
+ {study.gallery.slice(0, 3).map((src: string, i: number) => ( +
+ {`${study.title} +
+
+ ))} +
+ )} +
{(study.tech as string[]).map((t) => ( diff --git a/components/WorkDetailClient.tsx b/components/WorkDetailClient.tsx index 07a0cbc..c0503a3 100644 --- a/components/WorkDetailClient.tsx +++ b/components/WorkDetailClient.tsx @@ -431,7 +431,7 @@ export default function WorkDetailClient({ lang, dict, project, prev, next }: Pr {`${project.title}
diff --git a/lib/cloudinary.ts b/lib/cloudinary.ts new file mode 100644 index 0000000..d4a2d66 --- /dev/null +++ b/lib/cloudinary.ts @@ -0,0 +1,9 @@ +import { v2 as cloudinary } from 'cloudinary' + +cloudinary.config({ + cloud_name: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME, + api_key: process.env.CLOUDINARY_API_KEY, + api_secret: process.env.CLOUDINARY_API_SECRET, +}) + +export default cloudinary diff --git a/next-env.d.ts b/next-env.d.ts index 9edff1c..c4b7818 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/next.config.mjs b/next.config.mjs index 8c8bab6..64d3e3b 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,6 +1,18 @@ /** @type {import('next').NextConfig} */ const nextConfig = { output: "standalone", + images: { + remotePatterns: [ + { + protocol: "https", + hostname: "images.unsplash.com", + }, + { + protocol: "https", + hostname: "res.cloudinary.com", + }, + ], + }, }; export default nextConfig; diff --git a/package-lock.json b/package-lock.json index d0a69ed..75d3235 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@prisma/adapter-pg": "^7.8.0", "@prisma/client": "^7.8.0", + "cloudinary": "^2.10.0", "framer-motion": "^12.40.0", "next": "16.2.6", "pg": "^8.21.0", @@ -1722,6 +1723,18 @@ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/cloudinary": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/cloudinary/-/cloudinary-2.10.0.tgz", + "integrity": "sha512-sY09kYg7wprkndAOjZBAYqFZqwL+SxnEGcAvksOvFA+5upnFn949UjkEkHKNSwkBtW/xRDd0p6NgbSXZcxkI3w==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.23" + }, + "engines": { + "node": ">=9" + } + }, "node_modules/confbox": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", @@ -2357,6 +2370,12 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, "node_modules/long": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", diff --git a/package.json b/package.json index 9dcd9a2..4d12477 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "dependencies": { "@prisma/adapter-pg": "^7.8.0", "@prisma/client": "^7.8.0", + "cloudinary": "^2.10.0", "framer-motion": "^12.40.0", "next": "16.2.6", "pg": "^8.21.0",