feat: add featured flag to project, display featured projects on home page
This commit is contained in:
+3
-1
@@ -1,6 +1,7 @@
|
|||||||
import { getDictionary } from "@/get-dictionary";
|
import { getDictionary } from "@/get-dictionary";
|
||||||
import type { Locale } from "@/i18n-config";
|
import type { Locale } from "@/i18n-config";
|
||||||
import HomeClient from "@/components/HomeClient";
|
import HomeClient from "@/components/HomeClient";
|
||||||
|
import { getFeaturedProjects } from "../actions";
|
||||||
|
|
||||||
export async function generateMetadata({ params }: { params: Promise<{ lang: Locale }> }) {
|
export async function generateMetadata({ params }: { params: Promise<{ lang: Locale }> }) {
|
||||||
const { lang } = await params;
|
const { lang } = await params;
|
||||||
@@ -17,6 +18,7 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: Loc
|
|||||||
export default async function Page({ params }: { params: Promise<{ lang: Locale }> }) {
|
export default async function Page({ params }: { params: Promise<{ lang: Locale }> }) {
|
||||||
const { lang } = await params;
|
const { lang } = await params;
|
||||||
const dict = await getDictionary(lang);
|
const dict = await getDictionary(lang);
|
||||||
|
const featuredProjects = await getFeaturedProjects();
|
||||||
|
|
||||||
return <HomeClient lang={lang} dict={dict} />;
|
return <HomeClient lang={lang} dict={dict} featuredProjects={featuredProjects} />;
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-1
@@ -19,9 +19,22 @@ export async function getProjects() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getFeaturedProjects() {
|
||||||
|
try {
|
||||||
|
return await prisma.project.findMany({
|
||||||
|
where: { featured: true },
|
||||||
|
orderBy: { num: "asc" },
|
||||||
|
take: 4,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching featured projects:", error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function saveProject(data: any) {
|
export async function saveProject(data: any) {
|
||||||
try {
|
try {
|
||||||
const { id, num, slug, title, tag, desc, spec, year, client, duration, tech, challenge, solution, results, image, gallery, website } = data;
|
const { id, num, slug, title, tag, desc, spec, year, client, duration, tech, challenge, solution, results, image, gallery, website, featured } = data;
|
||||||
|
|
||||||
let project;
|
let project;
|
||||||
if (id) {
|
if (id) {
|
||||||
@@ -45,6 +58,7 @@ export async function saveProject(data: any) {
|
|||||||
image: image || "",
|
image: image || "",
|
||||||
gallery: gallery || [],
|
gallery: gallery || [],
|
||||||
website: website || "",
|
website: website || "",
|
||||||
|
featured: featured || false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -73,6 +87,7 @@ export async function saveProject(data: any) {
|
|||||||
image: image || "",
|
image: image || "",
|
||||||
gallery: gallery || [],
|
gallery: gallery || [],
|
||||||
website: website || "",
|
website: website || "",
|
||||||
|
featured: featured || false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ interface ProjectData {
|
|||||||
image: string;
|
image: string;
|
||||||
gallery?: string[];
|
gallery?: string[];
|
||||||
website?: string;
|
website?: string;
|
||||||
|
featured?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PartnerData {
|
interface PartnerData {
|
||||||
@@ -94,6 +95,7 @@ export default function AdminClient({
|
|||||||
const [formGallery, setFormGallery] = useState<string[]>([]);
|
const [formGallery, setFormGallery] = useState<string[]>([]);
|
||||||
const [newGalleryItem, setNewGalleryItem] = useState("");
|
const [newGalleryItem, setNewGalleryItem] = useState("");
|
||||||
const [formWebsite, setFormWebsite] = useState("");
|
const [formWebsite, setFormWebsite] = useState("");
|
||||||
|
const [formFeatured, setFormFeatured] = useState(false);
|
||||||
|
|
||||||
const [uploadingImage, setUploadingImage] = useState(false);
|
const [uploadingImage, setUploadingImage] = useState(false);
|
||||||
const [uploadingGallery, setUploadingGallery] = useState(false);
|
const [uploadingGallery, setUploadingGallery] = useState(false);
|
||||||
@@ -178,6 +180,7 @@ export default function AdminClient({
|
|||||||
setFormGallery(p.gallery ? [...p.gallery] : []);
|
setFormGallery(p.gallery ? [...p.gallery] : []);
|
||||||
setNewGalleryItem("");
|
setNewGalleryItem("");
|
||||||
setFormWebsite(p.website || "");
|
setFormWebsite(p.website || "");
|
||||||
|
setFormFeatured(p.featured || false);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Open modal for project adding
|
// Open modal for project adding
|
||||||
@@ -200,6 +203,7 @@ export default function AdminClient({
|
|||||||
setFormGallery([]);
|
setFormGallery([]);
|
||||||
setNewGalleryItem("");
|
setNewGalleryItem("");
|
||||||
setFormWebsite("");
|
setFormWebsite("");
|
||||||
|
setFormFeatured(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Save project
|
// Save project
|
||||||
@@ -237,6 +241,7 @@ export default function AdminClient({
|
|||||||
image: formImage,
|
image: formImage,
|
||||||
gallery: formGallery,
|
gallery: formGallery,
|
||||||
website: formWebsite,
|
website: formWebsite,
|
||||||
|
featured: formFeatured,
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await saveProject(projectDataToSave);
|
const res = await saveProject(projectDataToSave);
|
||||||
@@ -1157,6 +1162,18 @@ export default function AdminClient({
|
|||||||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Öne Çıkan Proje (Ana Sayfa İçin)</label>
|
||||||
|
<div className="flex items-center h-[30px]">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={formFeatured}
|
||||||
|
onChange={(e) => setFormFeatured(e.target.checked)}
|
||||||
|
className="w-4 h-4 cursor-pointer"
|
||||||
|
/>
|
||||||
|
<span className="ml-2 font-mono text-[10px] text-[#0A0A0A]">Bu bizim kendi projemiz (Ana sayfada göster)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ function Label({ children }: { children: React.ReactNode }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── MAIN COMPONENT ──
|
// ── MAIN COMPONENT ──
|
||||||
export default function HomeClient({ lang, dict }: { lang: Locale; dict: any }) {
|
export default function HomeClient({ lang, dict, featuredProjects }: { lang: Locale; dict: any; featuredProjects?: any[] }) {
|
||||||
const heroRef = useRef<HTMLDivElement>(null);
|
const heroRef = useRef<HTMLDivElement>(null);
|
||||||
const { scrollYProgress } = useScroll({ target: heroRef, offset: ["start start", "end start"] });
|
const { scrollYProgress } = useScroll({ target: heroRef, offset: ["start start", "end start"] });
|
||||||
const heroY = useTransform(scrollYProgress, [0, 1], ["0%", "25%"]);
|
const heroY = useTransform(scrollYProgress, [0, 1], ["0%", "25%"]);
|
||||||
@@ -283,7 +283,7 @@ export default function HomeClient({ lang, dict }: { lang: Locale; dict: any })
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<HomeClientBelowFold lang={lang} dict={dict} />
|
<HomeClientBelowFold lang={lang} dict={dict} featuredProjects={featuredProjects} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ function Label({ children }: { children: React.ReactNode }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function HomeClientBelowFold({ lang, dict }: { lang: Locale; dict: any }) {
|
export default function HomeClientBelowFold({ lang, dict, featuredProjects }: { lang: Locale; dict: any; featuredProjects?: any[] }) {
|
||||||
const [activeFaq, setActiveFaq] = useState<number | null>(null);
|
const [activeFaq, setActiveFaq] = useState<number | null>(null);
|
||||||
const [formSent, setFormSent] = useState(false);
|
const [formSent, setFormSent] = useState(false);
|
||||||
const [hoverCase, setHoverCase] = useState<number | null>(null);
|
const [hoverCase, setHoverCase] = useState<number | null>(null);
|
||||||
@@ -169,9 +169,10 @@ export default function HomeClientBelowFold({ lang, dict }: { lang: Locale; dict
|
|||||||
}))
|
}))
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const caseStudies = (dict.work.items as any[]).map((item: any, idx: number) => ({
|
const rawCases = featuredProjects && featuredProjects.length > 0 ? featuredProjects : (dict.work.items as any[]);
|
||||||
|
const caseStudies = rawCases.map((item: any, idx: number) => ({
|
||||||
...item,
|
...item,
|
||||||
tech: [
|
tech: item.tech || [
|
||||||
["Python", "TensorFlow", "Next.js", "PostgreSQL"],
|
["Python", "TensorFlow", "Next.js", "PostgreSQL"],
|
||||||
["Solidity", "React", "IPFS", "Node.js"],
|
["Solidity", "React", "IPFS", "Node.js"],
|
||||||
["Flutter", "Firebase", "Django", "AWS"],
|
["Flutter", "Firebase", "Django", "AWS"],
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ model Project {
|
|||||||
image String @default("")
|
image String @default("")
|
||||||
gallery String[] @default([])
|
gallery String[] @default([])
|
||||||
website String @default("")
|
website String @default("")
|
||||||
|
featured Boolean @default(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
model BlogPost {
|
model BlogPost {
|
||||||
|
|||||||
Reference in New Issue
Block a user