diff --git a/src/app/(main)/[slug]/page.tsx b/src/app/(main)/[slug]/page.tsx index 1e1da44..b6e529d 100644 --- a/src/app/(main)/[slug]/page.tsx +++ b/src/app/(main)/[slug]/page.tsx @@ -3,44 +3,57 @@ import HeroOther from "@/components/HeroOther"; import Page from "@/components/Pages/Page"; import { fetchBlogDetail } from "@/services/payload/blog"; import { fetchPageBySlug } from "@/services/payload/page"; +import { getDefaultMetadata } from "@/utils/metadata"; import { Metadata } from "next"; 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 publishedAt = ""; + let updatedAt = ""; let imgUrl = ""; const slug = (await params).slug; const blog = await fetchBlogDetail(slug); - // check for blog data if (!!blog) { + // check for blog data title = `${!!blog.data?.meta?.title ? blog.data?.meta?.title : blog.data.title} - ${name}`; description = `${!!blog.data?.meta?.description ? blog.data?.meta?.description : blog.data.title} - ${name}`; imgUrl = blog.imgUrl; - } - - // check for page data when blog is not found - if (!blog) { + publishedAt = blog.data.createdAt; + updatedAt = blog.data.updatedAt; + } else { + // check for page data when blog is not found const page = await fetchPageBySlug({ slug }); if (!!page) { title = `${!!page?.meta?.title ? page?.meta?.title : page.title} - ${name}`; description = `${!!page?.meta?.description ? page?.meta?.description : page.title} - ${name}`; imgUrl = page.heroImg?.url; + publishedAt = page.createdAt; + updatedAt = page.updatedAt; } } - return { - title: title, - description: description, - openGraph: { - title: title, - description: description, - images: !!imgUrl ? { url: imgUrl } : undefined, - }, + 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.other = { + "article:published_time": publishedAt, + "article:modified_time": updatedAt, }; + + return defaultMetadata; } export default async function SinglePage({ params }: { params: Promise<{ slug: string }> }) { diff --git a/src/app/(main)/layout.tsx b/src/app/(main)/layout.tsx index 01a8889..bba8bcd 100644 --- a/src/app/(main)/layout.tsx +++ b/src/app/(main)/layout.tsx @@ -3,9 +3,11 @@ import Footer from "@/components/Footer"; import Header from "@/components/Header"; import InitialScript from "@/components/InitialScript"; import { navMenuData } from "@/data/menu"; +import { getDefaultMetadata } from "@/utils/metadata"; import "@public/assets/css/styles.css"; import "jarallax/dist/jarallax.min.css"; +import { Metadata } from "next"; import { Roboto } from "next/font/google"; import "photoswipe/dist/photoswipe.css"; import "react-modal-video/css/modal-video.css"; @@ -15,6 +17,11 @@ import "tippy.js/dist/tippy.css"; const roboto = Roboto({ subsets: ["latin"] }); +export async function generateMetadata(): Promise { + const metadata = await getDefaultMetadata(); + return metadata; +} + export default function MainLayout({ children, }: Readonly<{ diff --git a/src/collections/BlogCategories.ts b/src/collections/BlogCategories.ts index 21cabf2..1f31904 100644 --- a/src/collections/BlogCategories.ts +++ b/src/collections/BlogCategories.ts @@ -1,4 +1,4 @@ -import formatSlug from "@/utils/formatSlug"; +import formatSlug from "@/utils/payload/formatSlug"; import type { CollectionConfig } from "payload"; export const BlogCategories: CollectionConfig = { diff --git a/src/collections/BlogTags.ts b/src/collections/BlogTags.ts index 04256cc..a7d623b 100644 --- a/src/collections/BlogTags.ts +++ b/src/collections/BlogTags.ts @@ -1,4 +1,4 @@ -import formatSlug from "@/utils/formatSlug"; +import formatSlug from "@/utils/payload/formatSlug"; import type { CollectionConfig } from "payload"; export const BlogTags: CollectionConfig = { diff --git a/src/collections/Blogs.ts b/src/collections/Blogs.ts index 6168629..f9bbd96 100644 --- a/src/collections/Blogs.ts +++ b/src/collections/Blogs.ts @@ -1,10 +1,16 @@ -import type { CollectionConfig } from "payload"; +import formatSlug from "@/utils/payload/formatSlug"; +import setAuthor from "@/utils/payload/setAuthor"; import { lexicalEditor } from "@payloadcms/richtext-lexical"; -import formatSlug from "@/utils/formatSlug"; +import type { CollectionConfig } from "payload"; export const Blogs: CollectionConfig = { slug: "blogs", labels: { plural: "Posts", singular: "Post" }, + versions: { + drafts: { + validate: true, + }, + }, fields: [ { name: "title", @@ -59,19 +65,32 @@ export const Blogs: CollectionConfig = { type: "textarea", }, { - name: "cannonical_url", - label: "Cannonical Url", + name: "canonical_url", + label: "Canonical Url", type: "text", }, ], }, { - name: "is_published", - label: "Published", - type: "checkbox", - defaultValue: true, + name: "createdBy", + type: "relationship", + relationTo: "users", + hooks: { + beforeChange: [setAuthor], + }, admin: { - position: "sidebar", + hidden: true, + }, + }, + { + name: "updatedBy", + type: "relationship", + relationTo: "users", + hooks: { + beforeChange: [setAuthor], + }, + admin: { + hidden: true, }, }, ], diff --git a/src/collections/Pages.ts b/src/collections/Pages.ts index fec04fe..fe7c380 100644 --- a/src/collections/Pages.ts +++ b/src/collections/Pages.ts @@ -7,11 +7,17 @@ import { GoogleReviewBlock } from "@/blocks/GoogleReview"; import { HorizontalImageContentBlock } from "@/blocks/HorizontalImageContent"; import { ImageSliderBlock } from "@/blocks/ImageSlider"; import { OurTeamBlock } from "@/blocks/OurTeam"; -import formatSlug from "@/utils/formatSlug"; +import formatSlug from "@/utils/payload/formatSlug"; +import setAuthor from "@/utils/payload/setAuthor"; import { CollectionConfig } from "payload"; export const Pages: CollectionConfig = { slug: "pages", + versions: { + drafts: { + validate: true, + }, + }, fields: [ { name: "title", @@ -29,9 +35,6 @@ export const Pages: CollectionConfig = { name: "slug", label: "Page Slug", type: "text", - admin: { - position: "sidebar", - }, hooks: { beforeValidate: [formatSlug("title")], }, @@ -75,6 +78,28 @@ export const Pages: CollectionConfig = { }, ], }, + { + name: "createdBy", + type: "relationship", + relationTo: "users", + hooks: { + beforeChange: [setAuthor], + }, + admin: { + hidden: true, + }, + }, + { + name: "updatedBy", + type: "relationship", + relationTo: "users", + hooks: { + beforeChange: [setAuthor], + }, + admin: { + hidden: true, + }, + }, ], admin: { hideAPIURL: true, diff --git a/src/collections/Users.ts b/src/collections/Users.ts index 773550d..bcd1a61 100644 --- a/src/collections/Users.ts +++ b/src/collections/Users.ts @@ -11,5 +11,10 @@ export const Users: CollectionConfig = { fields: [ // Email added by default // Add more fields as needed + { + name: "name", + label: "Name", + type: "text", + }, ], }; diff --git a/src/middleware.ts b/src/middleware.ts index bb27fd2..1d5dc2a 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -8,7 +8,10 @@ export function middleware(request: NextRequest) { const host = request.headers.get("x-forwarded-host") || request.nextUrl.hostname; const path = request.nextUrl.pathname + request.nextUrl.search; // Construct the full URL - const fullUrl = `${protocol}://${host}${path}`; + const mainUrl = `${protocol}://${host}`; + const fullUrl = `${mainUrl}${path}`; + + request.headers.set("x-main-url", mainUrl); request.headers.set("x-full-url", fullUrl); return NextResponse.next({ diff --git a/src/payload-types.ts b/src/payload-types.ts index 203e067..9987a52 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -83,6 +83,7 @@ export interface UserAuthOperations { */ export interface User { id: number; + name?: string | null; updatedAt: string; createdAt: string; email: string; @@ -143,11 +144,13 @@ export interface Blog { meta?: { title?: string | null; description?: string | null; - cannonical_url?: string | null; + canonical_url?: string | null; }; - is_published?: boolean | null; + createdBy?: (number | null) | User; + updatedBy?: (number | null) | User; updatedAt: string; createdAt: string; + _status?: ('draft' | 'published') | null; } /** * This interface was referenced by `Config`'s JSON-Schema @@ -303,8 +306,11 @@ export interface Page { description?: string | null; cannonical_url?: string | null; }; + createdBy?: (number | null) | User; + updatedBy?: (number | null) | User; updatedAt: string; createdAt: string; + _status?: ('draft' | 'published') | null; } /** * This interface was referenced by `Config`'s JSON-Schema @@ -593,6 +599,7 @@ export interface PayloadMigration { * via the `definition` "users_select". */ export interface UsersSelect { + name?: T; updatedAt?: T; createdAt?: T; email?: T; @@ -638,11 +645,13 @@ export interface BlogsSelect { | { title?: T; description?: T; - cannonical_url?: T; + canonical_url?: T; }; - is_published?: T; + createdBy?: T; + updatedBy?: T; updatedAt?: T; createdAt?: T; + _status?: T; } /** * This interface was referenced by `Config`'s JSON-Schema @@ -741,8 +750,11 @@ export interface PagesSelect { description?: T; cannonical_url?: T; }; + createdBy?: T; + updatedBy?: T; updatedAt?: T; createdAt?: T; + _status?: T; } /** * This interface was referenced by `Config`'s JSON-Schema diff --git a/src/services/payload/blog.ts b/src/services/payload/blog.ts index 410c398..9929eed 100644 --- a/src/services/payload/blog.ts +++ b/src/services/payload/blog.ts @@ -10,24 +10,11 @@ export async function fetchBlog(page: number | undefined, search: string = "") { pagination: true, limit: 6, where: !search - ? { - is_published: { - equals: true, - }, - } + ? undefined : { - and: [ - { - is_published: { - equals: true, - }, - }, - { - title: { - contains: search, - }, - }, - ], + title: { + contains: search, + }, }, }); @@ -51,9 +38,6 @@ export async function fetchBlogDetail(slug: string | undefined) { collection: "blogs", where: { slug: { equals: slug }, - is_published: { - equals: true, - }, }, limit: 1, pagination: false, diff --git a/src/utils/metadata.ts b/src/utils/metadata.ts new file mode 100644 index 0000000..e077916 --- /dev/null +++ b/src/utils/metadata.ts @@ -0,0 +1,21 @@ +import { Metadata } from "next"; +import { headers } from "next/headers"; + +export async function getDefaultMetadata(): Promise { + const headersList = await headers(); + const mainUrl = headersList.get("x-main-url") ?? ""; + const fullUrl = headersList.get("x-full-url") ?? ""; + + return { + metadataBase: new URL(mainUrl), + openGraph: { + type: "website", + locale: "en_US", + url: fullUrl, + siteName: "Cochise Oncology", + }, + alternates: { + canonical: "./", + }, + }; +} diff --git a/src/utils/formatSlug.ts b/src/utils/payload/formatSlug.ts similarity index 100% rename from src/utils/formatSlug.ts rename to src/utils/payload/formatSlug.ts diff --git a/src/utils/payload/setAuthor.ts b/src/utils/payload/setAuthor.ts new file mode 100644 index 0000000..f14e7e7 --- /dev/null +++ b/src/utils/payload/setAuthor.ts @@ -0,0 +1,17 @@ +import { FieldHook } from "payload"; + +const setAuthor: FieldHook = ({ value, req, data, field, operation }) => { + if (req.user && !!data) { + if (operation === "create") { + return req.user.id; + } else { + if (field.name === "updatedBy") { + return req.user.id; + } + } + } + + return value; +}; + +export default setAuthor;