This commit is contained in:
iqbal024 2025-03-06 10:51:51 +07:00
commit e6c1765fa7
14 changed files with 113 additions and 47 deletions

1
env
View File

@ -14,3 +14,4 @@ S3_SECRET_ACCESS_KEY=9b4e412850582aab32ffdadbe23bb7ea972b39aaf79c34da0c19d3fcafe
S3_REGION=ap-southeast-1 S3_REGION=ap-southeast-1
S3_ENDPOINT=https://jswmbraeandqttpcdfmj.supabase.co/storage/v1/s3 S3_ENDPOINT=https://jswmbraeandqttpcdfmj.supabase.co/storage/v1/s3
NEXT_PUBLIC_PAYLOAD_URL=http://localhost:3000 NEXT_PUBLIC_PAYLOAD_URL=http://localhost:3000
SITE_URL=http://localhost:3000

View File

@ -15,6 +15,7 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str
let publishedAt = ""; let publishedAt = "";
let updatedAt = ""; let updatedAt = "";
let imgUrl = ""; let imgUrl = "";
let createdByName = "";
const slug = (await params).slug; const slug = (await params).slug;
const blog = await fetchBlogDetail(slug); const blog = await fetchBlogDetail(slug);
@ -26,6 +27,9 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str
imgUrl = blog.imgUrl; imgUrl = blog.imgUrl;
publishedAt = blog.data.createdAt; publishedAt = blog.data.createdAt;
updatedAt = blog.data.updatedAt; updatedAt = blog.data.updatedAt;
if (!!blog?.data?.createdBy && typeof blog.data.createdBy !== "number") {
createdByName = blog.data.createdBy?.name ?? "";
}
} else { } else {
// check for page data when blog is not found // check for page data when blog is not found
const page = await fetchPageBySlug({ slug }); const page = await fetchPageBySlug({ slug });
@ -35,22 +39,35 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str
imgUrl = page.heroImg?.url; imgUrl = page.heroImg?.url;
publishedAt = page.createdAt; publishedAt = page.createdAt;
updatedAt = page.updatedAt; updatedAt = page.updatedAt;
if (!!page?.createdBy && typeof page.createdBy !== "number") {
createdByName = page?.createdBy?.name ?? "";
}
} }
} }
defaultMetadata.title = title; defaultMetadata.title = title;
defaultMetadata.description = description; defaultMetadata.description = description;
if (!!defaultMetadata.openGraph) { if (!!defaultMetadata.openGraph) {
defaultMetadata.openGraph.title = title;
// @ts-ignore // @ts-ignore
defaultMetadata.openGraph.type = "article"; defaultMetadata.openGraph.type = "article";
defaultMetadata.openGraph.description = description;
defaultMetadata.openGraph.title = title; defaultMetadata.openGraph.title = title;
defaultMetadata.openGraph.images = !!imgUrl ? { url: imgUrl } : undefined; defaultMetadata.openGraph.description = description;
defaultMetadata.openGraph.images = !!imgUrl ? [imgUrl] : undefined;
} }
defaultMetadata.twitter = {
card: "summary_large_image",
title: title,
description: description,
images: !!imgUrl ? [imgUrl] : undefined,
};
defaultMetadata.other = { defaultMetadata.other = {
"article:published_time": publishedAt, "article:published_time": publishedAt,
"article:modified_time": updatedAt, "article:modified_time": updatedAt,
"twitter:label1": "Written by",
"twitter:data1": !!createdByName ? createdByName : "Admin",
"twitter:label2": "Est. reading time",
"twitter:data2": "3 minutes",
}; };
return defaultMetadata; return defaultMetadata;

View File

@ -1,36 +1,45 @@
import { BeforeFooterBlock } from "@/components/Blocks/BeforeFooter"; import { BeforeFooterBlock } from "@/components/Blocks/BeforeFooter";
import HeroOther from "@/components/HeroOther"; import HeroOther from "@/components/HeroOther";
import { fetchTeamDetail } from "@/services/payload/team"; import { fetchTeamDetail } from "@/services/payload/team";
import { getDefaultMetadata } from "@/utils/metadata";
import { RichText } from "@payloadcms/richtext-lexical/react"; import { RichText } from "@payloadcms/richtext-lexical/react";
import Image from "next/image"; import Image from "next/image";
import { Metadata } from "next/types"; import { Metadata } from "next/types";
import { Suspense } from "react"; import { Suspense } from "react";
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> { export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
const name = "Cochise Oncology"; const defaultMetadata = await getDefaultMetadata();
const name = defaultMetadata.openGraph?.siteName ?? "";
let title = "Page"; let title = "Page";
let description = "Page";
let imgUrl = ""; let imgUrl = "";
const slug = (await params).slug; const slug = (await params).slug;
const blog = await fetchTeamDetail(decodeURIComponent(slug)); const team = await fetchTeamDetail(decodeURIComponent(slug));
// check for blog data // check for blog data
if (!!blog) { if (!!team) {
title = `${name} Staff - ${blog.data.name}`; title = `${team.data.name} - ${name}`;
description = `${name} Staff - ${blog.data.name}`; imgUrl = team.imgUrl;
imgUrl = blog.imgUrl;
} }
return { defaultMetadata.title = title;
if (!!defaultMetadata.openGraph) {
// @ts-ignore
defaultMetadata.openGraph.type = "article";
defaultMetadata.openGraph.title = title;
defaultMetadata.openGraph.images = !!imgUrl ? [imgUrl] : undefined;
}
defaultMetadata.twitter = {
card: "summary_large_image",
title: title, title: title,
description: description, images: !!imgUrl ? [imgUrl] : undefined,
openGraph: {
title: title,
description: description,
images: !!imgUrl ? { url: imgUrl } : undefined,
},
}; };
defaultMetadata.other = {
"twitter:label1": "Est. reading time",
"twitter:data1": "1 minute",
};
return defaultMetadata;
} }
export default async function BiographySinglePage({ params }: { params: Promise<{ slug: string }> }) { export default async function BiographySinglePage({ params }: { params: Promise<{ slug: string }> }) {

13
src/app/robots.ts Normal file
View File

@ -0,0 +1,13 @@
import type { MetadataRoute } from "next";
export default function robots(): MetadataRoute.Robots {
const siteUrl = process.env.SITE_URL || "http://localhost:3000";
return {
rules: {
userAgent: "*",
allow: "/",
},
sitemap: `${siteUrl}/sitemap.xml`,
};
}

18
src/app/sitemap.ts Normal file
View File

@ -0,0 +1,18 @@
import type { MetadataRoute } from "next";
export default function sitemap(): MetadataRoute.Sitemap {
const siteUrl = process.env.SITE_URL || "http://localhost:3000";
// Define your static routes
const routes: string[] = ["", "/blog"];
// Create sitemap entries for static routes
const staticRoutesSitemap = routes.map((route) => ({
url: `${siteUrl}${route}`,
lastModified: new Date(),
changeFrequency: "weekly" as const,
priority: route === "" ? 1 : 0.8,
}));
return [...staticRoutesSitemap];
}

View File

@ -97,5 +97,6 @@ export const Blogs: CollectionConfig = {
admin: { admin: {
hideAPIURL: true, hideAPIURL: true,
group: "Blogs", group: "Blogs",
useAsTitle: "title",
}, },
}; };

View File

@ -104,5 +104,6 @@ export const Pages: CollectionConfig = {
admin: { admin: {
hideAPIURL: true, hideAPIURL: true,
group: "General", group: "General",
useAsTitle: "title",
}, },
}; };

View File

@ -1,5 +1,6 @@
import type { CollectionConfig } from "payload"; import type { CollectionConfig } from "payload";
import { lexicalEditor } from "@payloadcms/richtext-lexical"; import { lexicalEditor } from "@payloadcms/richtext-lexical";
import formatSlug from "@/utils/payload/formatSlug";
export const Teams: CollectionConfig = { export const Teams: CollectionConfig = {
slug: "teams", slug: "teams",
@ -9,6 +10,13 @@ export const Teams: CollectionConfig = {
type: "text", type: "text",
required: true, required: true,
}, },
{
name: "slug",
type: "text",
hooks: {
beforeValidate: [formatSlug("name")],
},
},
{ {
name: "role", name: "role",
type: "text", type: "text",

View File

@ -2,6 +2,7 @@ import Team from "@/components/Team";
type Team = { type Team = {
id: number; id: number;
slug: string;
name: string; name: string;
role: string; role: string;
img: { url: string; alt: string }; img: { url: string; alt: string };

View File

@ -2,25 +2,32 @@
import Link from "next/link"; import Link from "next/link";
import { CardTeam } from "./Teams/CardTeam"; import { CardTeam } from "./Teams/CardTeam";
export default function Team({ data }: any) { type Team = {
id: number;
slug: string;
name: string;
role: string;
img: { url: string; alt: string };
biography: string;
};
export default function Team({ data }: { data: Team[] }) {
return ( return (
<div className="container"> <div className="container">
<div className="row"> <div className="flex justify-center">
<div className="col-md-12 offset-md-2 col-lg-6 offset-lg-3 text-center"> <h2 className="text-4xl mb-30 mb-sm-20">
<h2 className="section-title mb-30 mb-sm-20">
<span className="text-gray">Our</span> Team <span className="text-gray">Our</span> Team
<span className="text-gray">.</span> <span className="text-gray">.</span>
</h2> </h2>
</div> </div>
</div> <div className="grid grid-cols-1 md:grid-cols-3 xl:grid-cols-4 gap-x-5 gap-y-2">
<div className="grid grid-cols-4 gap-5">
{/* Team item */} {/* Team item */}
{data.map((member: any, index: any) => ( {data.map((member: any, index: any) => (
<div key={index} className="text-center"> <div key={index} className="flex flex-col text-center justify-between">
<CardTeam data={member} /> <CardTeam data={member} />
<Link href={`/biography/${member.name}`} passHref> <Link href={`/staff_member/${member.slug}`} passHref>
<button className="bg-[#64B3B4] text-white px-5 py-1 m-3 rounded-3xl hover:opacity-[0.7]"> <button className="bg-extColorPrimary6 text-white px-5 py-1 m-3 rounded-3xl hover:bg-extColorPrimary4 transition-colors duration-500">
Biography BIOGRAPHY
</button> </button>
</Link> </Link>
</div> </div>

View File

@ -21,20 +21,6 @@ export function CardTeam({ data }: CardTeamProps) {
data-wow-duration="1.2s" data-wow-duration="1.2s"
alt={`Image of ${data.name}`} alt={`Image of ${data.name}`}
/> />
<div className="team-item-detail">
<div className="team-social-links">
{[
{ platform: "Facebook", icon: "fa-facebook-f", url: "#" },
{ platform: "Twitter", icon: "fa-twitter", url: "#" },
{ platform: "Pinterest", icon: "fa-pinterest-p", url: "#" },
].map((social, idx) => (
<a key={idx} href={social.url} target="_blank" rel="noopener nofollow">
<div className="visually-hidden">{social.platform}</div>
<i className={social.icon} />
</a>
))}
</div>
</div>
</div> </div>
<div className="team-item-descr"> <div className="team-item-descr">
<div className="team-item-name">{data.name}</div> <div className="team-item-name">{data.name}</div>

View File

@ -319,6 +319,7 @@ export interface Page {
export interface Team { export interface Team {
id: number; id: number;
name: string; name: string;
slug?: string | null;
role: string; role: string;
img: number | Media; img: number | Media;
biography?: { biography?: {
@ -762,6 +763,7 @@ export interface PagesSelect<T extends boolean = true> {
*/ */
export interface TeamsSelect<T extends boolean = true> { export interface TeamsSelect<T extends boolean = true> {
name?: T; name?: T;
slug?: T;
role?: T; role?: T;
img?: T; img?: T;
biography?: T; biography?: T;

View File

@ -114,11 +114,14 @@ export default buildConfig({
fields: undefined, fields: undefined,
admin: { admin: {
group: "General", group: "General",
useAsTitle: "title",
}, },
}, },
formSubmissionOverrides: { formSubmissionOverrides: {
fields: undefined,
admin: { admin: {
group: "General", group: "General",
useAsTitle: "form",
}, },
}, },
}), }),

View File

@ -2,17 +2,16 @@ import payloadConfig from "@/payload.config";
import { formatDate } from "@/utils/datetime"; import { formatDate } from "@/utils/datetime";
import { getPayload } from "payload"; import { getPayload } from "payload";
export async function fetchTeamDetail(name: string | undefined) { export async function fetchTeamDetail(slug: string) {
const payload = await getPayload({ config: payloadConfig }); const payload = await getPayload({ config: payloadConfig });
const blogDataQuery = await payload.find({ const blogDataQuery = await payload.find({
collection: "teams", collection: "teams",
where: { where: {
name: { like: `%${name}%` }, slug: { equals: slug },
}, },
limit: 1, limit: 1,
pagination: false, pagination: false,
}); });
console.log("data", name);
if (!blogDataQuery?.docs?.[0]) return null; if (!blogDataQuery?.docs?.[0]) return null;