diff --git a/env b/env index f8d6860..1ec99f5 100644 --- a/env +++ b/env @@ -14,3 +14,4 @@ S3_SECRET_ACCESS_KEY=9b4e412850582aab32ffdadbe23bb7ea972b39aaf79c34da0c19d3fcafe S3_REGION=ap-southeast-1 S3_ENDPOINT=https://jswmbraeandqttpcdfmj.supabase.co/storage/v1/s3 NEXT_PUBLIC_PAYLOAD_URL=http://localhost:3000 +SITE_URL=http://localhost:3000 diff --git a/src/app/(main)/[slug]/page.tsx b/src/app/(main)/[slug]/page.tsx index b6e529d..37e8955 100644 --- a/src/app/(main)/[slug]/page.tsx +++ b/src/app/(main)/[slug]/page.tsx @@ -15,6 +15,7 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str let publishedAt = ""; let updatedAt = ""; let imgUrl = ""; + let createdByName = ""; const slug = (await params).slug; const blog = await fetchBlogDetail(slug); @@ -26,6 +27,9 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str imgUrl = blog.imgUrl; publishedAt = blog.data.createdAt; updatedAt = blog.data.updatedAt; + if (!!blog?.data?.createdBy && typeof blog.data.createdBy !== "number") { + createdByName = blog.data.createdBy?.name ?? ""; + } } else { // check for page data when blog is not found const page = await fetchPageBySlug({ slug }); @@ -35,22 +39,35 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str imgUrl = page.heroImg?.url; publishedAt = page.createdAt; updatedAt = page.updatedAt; + + if (!!page?.createdBy && typeof page.createdBy !== "number") { + createdByName = page?.createdBy?.name ?? ""; + } } } defaultMetadata.title = title; defaultMetadata.description = description; if (!!defaultMetadata.openGraph) { - defaultMetadata.openGraph.title = title; // @ts-ignore defaultMetadata.openGraph.type = "article"; - defaultMetadata.openGraph.description = description; 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 = { "article:published_time": publishedAt, "article:modified_time": updatedAt, + "twitter:label1": "Written by", + "twitter:data1": !!createdByName ? createdByName : "Admin", + "twitter:label2": "Est. reading time", + "twitter:data2": "3 minutes", }; return defaultMetadata; diff --git a/src/app/(main)/biography/[slug]/page.tsx b/src/app/(main)/staff_member/[slug]/page.tsx similarity index 79% rename from src/app/(main)/biography/[slug]/page.tsx rename to src/app/(main)/staff_member/[slug]/page.tsx index 1daf0bc..a5b242a 100644 --- a/src/app/(main)/biography/[slug]/page.tsx +++ b/src/app/(main)/staff_member/[slug]/page.tsx @@ -1,36 +1,45 @@ import { BeforeFooterBlock } from "@/components/Blocks/BeforeFooter"; import HeroOther from "@/components/HeroOther"; import { fetchTeamDetail } from "@/services/payload/team"; +import { getDefaultMetadata } from "@/utils/metadata"; import { RichText } from "@payloadcms/richtext-lexical/react"; import Image from "next/image"; import { Metadata } from "next/types"; import { Suspense } from "react"; export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise { - const name = "Cochise Oncology"; + const defaultMetadata = await getDefaultMetadata(); + const name = defaultMetadata.openGraph?.siteName ?? ""; let title = "Page"; - let description = "Page"; let imgUrl = ""; const slug = (await params).slug; - const blog = await fetchTeamDetail(decodeURIComponent(slug)); + const team = await fetchTeamDetail(decodeURIComponent(slug)); // check for blog data - if (!!blog) { - title = `${name} Staff - ${blog.data.name}`; - description = `${name} Staff - ${blog.data.name}`; - imgUrl = blog.imgUrl; + if (!!team) { + title = `${team.data.name} - ${name}`; + imgUrl = team.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, - description: description, - openGraph: { - title: title, - description: description, - images: !!imgUrl ? { url: imgUrl } : undefined, - }, + images: !!imgUrl ? [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 }> }) { diff --git a/src/app/robots.ts b/src/app/robots.ts new file mode 100644 index 0000000..642e42c --- /dev/null +++ b/src/app/robots.ts @@ -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`, + }; +} diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts new file mode 100644 index 0000000..bd835a7 --- /dev/null +++ b/src/app/sitemap.ts @@ -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]; +} diff --git a/src/collections/Blogs.ts b/src/collections/Blogs.ts index f9bbd96..55437d6 100644 --- a/src/collections/Blogs.ts +++ b/src/collections/Blogs.ts @@ -97,5 +97,6 @@ export const Blogs: CollectionConfig = { admin: { hideAPIURL: true, group: "Blogs", + useAsTitle: "title", }, }; diff --git a/src/collections/Pages.ts b/src/collections/Pages.ts index fe7c380..f8a6588 100644 --- a/src/collections/Pages.ts +++ b/src/collections/Pages.ts @@ -104,5 +104,6 @@ export const Pages: CollectionConfig = { admin: { hideAPIURL: true, group: "General", + useAsTitle: "title", }, }; diff --git a/src/collections/Teams.ts b/src/collections/Teams.ts index f3748eb..65dea1c 100644 --- a/src/collections/Teams.ts +++ b/src/collections/Teams.ts @@ -1,5 +1,6 @@ import type { CollectionConfig } from "payload"; import { lexicalEditor } from "@payloadcms/richtext-lexical"; +import formatSlug from "@/utils/payload/formatSlug"; export const Teams: CollectionConfig = { slug: "teams", @@ -9,6 +10,13 @@ export const Teams: CollectionConfig = { type: "text", required: true, }, + { + name: "slug", + type: "text", + hooks: { + beforeValidate: [formatSlug("name")], + }, + }, { name: "role", type: "text", diff --git a/src/components/Blocks/OurTeam/index.tsx b/src/components/Blocks/OurTeam/index.tsx index 3340c2f..aca1d93 100644 --- a/src/components/Blocks/OurTeam/index.tsx +++ b/src/components/Blocks/OurTeam/index.tsx @@ -2,6 +2,7 @@ import Team from "@/components/Team"; type Team = { id: number; + slug: string; name: string; role: string; img: { url: string; alt: string }; diff --git a/src/components/Team.tsx b/src/components/Team.tsx index 573a036..2ef6f5d 100644 --- a/src/components/Team.tsx +++ b/src/components/Team.tsx @@ -2,25 +2,32 @@ import Link from "next/link"; 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 (
-
-
-

- Our Team - . -

-
+
+

+ Our Team + . +

-
+
{/* Team item */} {data.map((member: any, index: any) => ( -
+
- -
diff --git a/src/components/Teams/CardTeam.tsx b/src/components/Teams/CardTeam.tsx index bdc8552..bab7387 100644 --- a/src/components/Teams/CardTeam.tsx +++ b/src/components/Teams/CardTeam.tsx @@ -21,20 +21,6 @@ export function CardTeam({ data }: CardTeamProps) { data-wow-duration="1.2s" alt={`Image of ${data.name}`} /> -
-
- {[ - { platform: "Facebook", icon: "fa-facebook-f", url: "#" }, - { platform: "Twitter", icon: "fa-twitter", url: "#" }, - { platform: "Pinterest", icon: "fa-pinterest-p", url: "#" }, - ].map((social, idx) => ( - -
{social.platform}
- -
- ))} -
-
{data.name}
diff --git a/src/payload-types.ts b/src/payload-types.ts index 9987a52..036a1c9 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -319,6 +319,7 @@ export interface Page { export interface Team { id: number; name: string; + slug?: string | null; role: string; img: number | Media; biography?: { @@ -762,6 +763,7 @@ export interface PagesSelect { */ export interface TeamsSelect { name?: T; + slug?: T; role?: T; img?: T; biography?: T; diff --git a/src/payload.config.ts b/src/payload.config.ts index b400fda..9271142 100644 --- a/src/payload.config.ts +++ b/src/payload.config.ts @@ -114,11 +114,14 @@ export default buildConfig({ fields: undefined, admin: { group: "General", + useAsTitle: "title", }, }, formSubmissionOverrides: { + fields: undefined, admin: { group: "General", + useAsTitle: "form", }, }, }), diff --git a/src/services/payload/team.ts b/src/services/payload/team.ts index 2d96fcd..7cf3f5b 100644 --- a/src/services/payload/team.ts +++ b/src/services/payload/team.ts @@ -2,17 +2,16 @@ import payloadConfig from "@/payload.config"; import { formatDate } from "@/utils/datetime"; import { getPayload } from "payload"; -export async function fetchTeamDetail(name: string | undefined) { +export async function fetchTeamDetail(slug: string) { const payload = await getPayload({ config: payloadConfig }); const blogDataQuery = await payload.find({ collection: "teams", where: { - name: { like: `%${name}%` }, + slug: { equals: slug }, }, limit: 1, pagination: false, }); - console.log("data", name); if (!blogDataQuery?.docs?.[0]) return null;