Compare commits

..

No commits in common. "c7dcb7e7b4b8a0b8c0d5802119275e66ffff7461" and "f3d8e300462e7ab37c3a7cdaadda9a4058265ab5" have entirely different histories.

30 changed files with 131 additions and 586 deletions

17
env Normal file
View File

@ -0,0 +1,17 @@
SERVICE_SUPABASESERVICE_KEY=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzdXBhYmFzZSIsImlhdCI6MTczODQ3MzA2MCwiZXhwIjo0ODk0MTQ2NjYwLCJyb2xlIjoic2VydmljZV9yb2xlIn0.nRpZfD6Uur9yzCT_BTgLH9DLWsCcCiAgWKenJ_qYu5M\
SERVICE_SUPABASEANON_KEY=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzdXBhYmFzZSIsImlhdCI6MTczODQ3MzA2MCwiZXhwIjo0ODk0MTQ2NjYwLCJyb2xlIjoiYW5vbiJ9.WXJvhzD1gD6Kxlq8PLFAcpaVFmh9h3xbJHh9oow2IFQ
SUPABASE_URL=https://supabasekong-n00g8kwoos4skc0gw44k8sks.dev3vds1.link
# payload
DATABASE_URI=postgresql://postgres.jswmbraeandqttpcdfmj:qDY4C35XaRsmW6dW@aws-0-ap-southeast-1.pooler.supabase.com:5432/postgres
PAYLOAD_SECRET=1b7204f9ed5f7ab09706fc51
S3_BUCKET=cochise-bucket
S3_ACCESS_KEY_ID=97394cd503ae9973efa25168ed980e51
S3_SECRET_ACCESS_KEY=9b4e412850582aab32ffdadbe23bb7ea972b39aaf79c34da0c19d3fcafe11dd0
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

View File

@ -11,7 +11,7 @@
--color-gray-1: #697582;
--color-gray-2: #747f8c;
--color-gray-3: #8a95a2;
--color-primary-1: #00898b;
--color-primary-1: #1872e8;
--color-primary-1-a: #2b87ff;
--color-primary-light-1: #e3effe;
--color-primary-light-1-a: #bcd1f1;

View File

@ -191,7 +191,7 @@ Primary use: Multipurpose Template
--color-gray-light-6: #bbb;
--color-dark-mode-gray-1: rgba(255, 255, 255, 0.7);
--color-dark-mode-gray-2: rgba(255, 255, 255, 0.1275);
--color-primary-1: #00898b;
--color-primary-1: #4567ed;
--color-primary-1-a: #375ae3;
--color-primary-light-1: #e3effe;
--color-primary-light-1-a: #bcd1f1;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@ -16,7 +16,6 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str
let updatedAt = "";
let imgUrl = "";
let createdByName = "";
let canonicalUrl = "";
const slug = (await params).slug;
const blog = await fetchBlogDetail(slug);
@ -28,9 +27,6 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str
imgUrl = blog.imgUrl;
publishedAt = blog.data.createdAt;
updatedAt = blog.data.updatedAt;
if (!!blog.data?.meta?.canonical_url) {
canonicalUrl = blog.data.meta.canonical_url;
}
if (!!blog?.data?.createdBy && typeof blog.data.createdBy !== "number") {
createdByName = blog.data.createdBy?.name ?? "";
}
@ -43,9 +39,7 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str
imgUrl = page.heroImg?.url;
publishedAt = page.createdAt;
updatedAt = page.updatedAt;
if (!!page.meta?.canonical_url) {
canonicalUrl = page.meta.canonical_url;
}
if (!!page?.createdBy && typeof page.createdBy !== "number") {
createdByName = page?.createdBy?.name ?? "";
}
@ -61,9 +55,6 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str
defaultMetadata.openGraph.description = description;
defaultMetadata.openGraph.images = !!imgUrl ? [imgUrl] : undefined;
}
if (!!defaultMetadata.alternates && !!canonicalUrl) {
defaultMetadata.alternates.canonical = canonicalUrl;
}
defaultMetadata.twitter = {
card: "summary_large_image",
title: title,

View File

@ -1,74 +0,0 @@
import { BeforeFooterBlock } from "@/components/Blocks/BeforeFooter";
import { BlogCardItemSkeleton } from "@/components/Blogs/BlogCardItem";
import Blogs from "@/components/Blogs/Blogs";
import HeroOther from "@/components/HeroOther";
import { getDefaultMetadata } from "@/utils/metadata";
import { sanitizePageNumber } from "@/utils/sanitize";
import { Metadata } from "next";
// import { headers } from "next/headers";
import { Suspense } from "react";
export async function generateMetadata(): Promise<Metadata> {
const metadata = await getDefaultMetadata();
return metadata;
}
export default async function CategoryPage({
params,
searchParams,
}: {
params?: Promise<{ path: string[] }>;
searchParams?: Promise<{ page?: string; s?: string }>;
}) {
const path = (await params)?.path;
// const headersList = await headers();
// const paramsCategory = path?.[0] ?? "";
const paramsPage = path?.[2] ?? "";
const paramsSearch = (await searchParams)?.s;
const page = sanitizePageNumber(paramsPage);
return (
<>
<HeroOther title="Blog" />
<section className="page-section" id="blog">
<div className="w-full max-w-7xl mx-auto px-4 md:px-8">
<form action={`category/chemotherapy/page/1`} method="GET">
<div className="input-group">
<input
type="text"
name="s"
defaultValue={paramsSearch ?? ""}
placeholder="Search blog..."
className="input-lg input-circle form-control h-12"
/>
<div className="input-group-append px-2">
<button className="btn btn-info text-white h-12 px-5" type="submit">
Search
</button>
</div>
</div>
</form>
</div>
<Suspense fallback={<Loading />}>
<Blogs preset="categories" page={page} search={paramsSearch} />
</Suspense>
</section>
<BeforeFooterBlock />
</>
);
}
function Loading() {
return (
<div className="container position-relative">
<div className="row">
<BlogCardItemSkeleton />
<BlogCardItemSkeleton />
<BlogCardItemSkeleton />
</div>
</div>
);
}

View File

@ -1,6 +1,10 @@
"use client";
// "use client";
import Hero from "@/components/Hero";
// const ParallaxContainer = dynamic(() => import("@/components/ParallaxContainer"), {
// ssr: false,
// });
export default function HomeBgVideo() {
return (
<>

View File

@ -1,139 +1,38 @@
import Parallax from "./home-bg-video/page";
import Homepage from "@/components/Homepage";
import { BeforeFooterBlock } from "@/components/Blocks/BeforeFooter";
import { Metadata, Viewport } from "next";
import { getDefaultMetadata } from "@/utils/metadata";
import HomeBgVideo from "@/components/HomeBgVideo";
import { headers } from "next/headers";
export async function generateMetadata(): Promise<Metadata> {
const defaultMetadata = await getDefaultMetadata();
defaultMetadata.title = `Homepage - ${defaultMetadata.openGraph?.siteName}`;
defaultMetadata.keywords = "Cochise Oncology, Oncology, Healthcare, Medical Services, Cancer Treatment";
export const metadata = {
title: "HomePage - Cochise Oncology",
description: "Cochise Oncology",
keywords: "Cochise Oncology, Oncology, Healthcare, Medical Services, Cancer Treatment",
author: "Cochise Oncology",
robots: "index, follow",
og: {
title: "HomePage - Cochise Oncology",
description: "Cochise Oncology",
type: "website",
url: "https://www.cochiseoncology.com",
image: "https://www.cochiseoncology.com/og-image.jpg",
},
twitter: {
card: "summary_large_image",
site: "@CochiseOncology",
title: "HomePage - Cochise Oncology",
description: "Cochise Oncology",
image: "https://www.cochiseoncology.com/twitter-image.jpg",
},
};
if (!!defaultMetadata?.openGraph) {
defaultMetadata.openGraph.images = ["/assets/images/cochise-welcome.png"];
}
return defaultMetadata;
}
export const viewport: Viewport = {
export const viewport = {
width: "device-width",
initialScale: 1,
};
export default async function Home() {
const headersList = await headers();
const fullUrl = headersList.get("x-full-url");
const siteName = headersList.get("x-site-name");
const metaDesc = headersList.get("x-meta-desc");
const jsonLd = {
"@context": "https://schema.org",
"@graph": [
{
"@type": "WebPage",
"@id": fullUrl,
url: fullUrl,
name: `Homepage - ${siteName}`,
isPartOf: {
"@id": `${fullUrl}#website`,
},
about: {
"@id": `${fullUrl}#organization`,
},
primaryImageOfPage: {
"@id": `${fullUrl}#primaryimage`,
},
image: {
"@id": `${fullUrl}#primaryimage`,
},
thumbnailUrl: [`${fullUrl}assets/images/cochise-welcome.png`],
datePublished: "2024-07-18T16:29:29+00:00",
dateModified: "2025-02-24T21:01:50+00:00",
description: metaDesc,
breadcrumb: {
"@id": `${fullUrl}#breadcrumb`,
},
inLanguage: "en-US",
potentialAction: [
{
"@type": "ReadAction",
target: [fullUrl],
},
],
},
{
"@type": "ImageObject",
inLanguage: "en-US",
"@id": `${fullUrl}#primaryimage`,
url: `${fullUrl}assets/images/cochise-welcome.png`,
contentUrl: `${fullUrl}assets/images/cochise-welcome.png`,
width: 940,
height: 788,
},
{
"@type": "BreadcrumbList",
"@id": `${fullUrl}#breadcrumb`,
itemListElement: [
{
"@type": "ListItem",
position: 1,
name: "Home",
},
],
},
{
"@type": "WebSite",
"@id": `${fullUrl}#website`,
url: fullUrl,
name: siteName,
description: "",
publisher: {
"@id": `${fullUrl}#organization`,
},
potentialAction: [
{
"@type": "SearchAction",
target: {
"@type": "EntryPoint",
urlTemplate: `${fullUrl}?s={search_term_string}`,
},
"query-input": {
"@type": "PropertyValueSpecification",
valueRequired: true,
valueName: "search_term_string",
},
},
],
inLanguage: "en-US",
},
{
"@type": "Organization",
"@id": `${fullUrl}#organization`,
name: "Cochise Oncology",
url: fullUrl,
logo: {
"@type": "ImageObject",
inLanguage: "en-US",
"@id": `${fullUrl}#/schema/logo/image/`,
url: `${fullUrl}assets/images/cochise-welcome.png`,
contentUrl: `${fullUrl}assets/images/cochise-welcome.png`,
width: 500,
height: 195,
caption: siteName,
},
image: {
"@id": `${fullUrl}#/schema/logo/image/`,
},
},
],
};
export default function Home() {
return (
<>
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
<HomeBgVideo />
<Parallax />
<Homepage />
<BeforeFooterBlock />
</>

View File

@ -3,9 +3,7 @@ import HeroOther from "@/components/HeroOther";
import { fetchTeamDetail } from "@/services/payload/team";
import { getDefaultMetadata } from "@/utils/metadata";
import { RichText } from "@payloadcms/richtext-lexical/react";
import { headers } from "next/headers";
import Image from "next/image";
import { notFound } from "next/navigation";
import { Metadata } from "next/types";
import { Suspense } from "react";
@ -47,94 +45,17 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str
export default async function BiographySinglePage({ params }: { params: Promise<{ slug: string }> }) {
const slug = (await params).slug;
const data = await fetchTeamDetail(decodeURIComponent(slug));
if (!data?.data) {
notFound();
}
const headersList = await headers();
const mainUrl = headersList.get("x-main-url");
const fullUrl = headersList.get("x-full-url");
const siteName = headersList.get("x-site-name");
const metaDesc = headersList.get("x-meta-desc");
const jsonLd = {
"@context": "https://schema.org",
"@graph": [
{
"@type": "WebPage",
"@id": fullUrl,
url: fullUrl,
name: `${data?.data?.name ?? ""} - ${siteName}`,
isPartOf: {
"@id": `${mainUrl}/#website`,
},
datePublished: data?.data?.createdAt,
dateModified: data?.data?.updatedAt,
description: metaDesc,
breadcrumb: {
"@id": `${fullUrl}#breadcrumb`,
},
inLanguage: "en-US",
potentialAction: [
{
"@type": "ReadAction",
target: [fullUrl],
},
],
},
{
"@type": "WebSite",
"@id": `${mainUrl}/#website`,
url: `${mainUrl}/`,
name: siteName,
description: "",
publisher: {
"@id": `${mainUrl}/#organization`,
},
potentialAction: [
{
"@type": "SearchAction",
target: {
"@type": "EntryPoint",
urlTemplate: `${mainUrl}/?s={search_term_string}`,
},
"query-input": {
"@type": "PropertyValueSpecification",
valueRequired: true,
valueName: "search_term_string",
},
},
],
inLanguage: "en-US",
},
{
"@type": "Organization",
"@id": `${mainUrl}/#organization`,
name: siteName,
url: `${mainUrl}/`,
logo: {
"@type": "ImageObject",
thumbnailUrl: data.imgUrl,
inLanguage: "en-US",
"@id": `${mainUrl}/#primaryimage`,
url: data.imgUrl,
contentUrl: data.imgUrl,
width: 500,
height: 195,
caption: siteName,
},
image: {
"@id": `${mainUrl}/#primaryimage`,
},
},
],
};
if (!data?.data)
return (
<>
<BeforeFooterBlock />
</>
);
return (
<>
<Suspense fallback={<Loading />}>
<>
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
<HeroOther
img={"/assets/images/full-width-images/section-bg-13.jpeg"}
title={data.data.name}

View File

@ -59,10 +59,6 @@ body {
.ext-btn-shadow-sm-primary4 {
@apply bg-extColorPrimary4 text-white hover:text-white hover:bg-extColorPrimary6 transition-colors;
}
.ext-btn-shadow-sm-primary8 {
@apply bg-extColorPrimary8 text-white hover:text-white hover:bg-extColorPrimary6 transition-colors;
}
}
.bg-gradient {

View File

@ -72,8 +72,8 @@ export const Pages: CollectionConfig = {
type: "textarea",
},
{
name: "canonical_url",
label: "Canonical Url",
name: "cannonical_url",
label: "Cannonical Url",
type: "text",
},
],

View File

@ -52,7 +52,7 @@ export function BeforeFooterBlock({ title, description, buttonText, showLinier =
<div className="pt-5">
<Link
href="/contact"
className="inline-block bg-extColorPrimary6 hover:bg-extColorPrimary8 text-white py-3 px-6 hover:scale-105 rounded-full text-lg shadow-sm transition-all duration-500"
className="inline-block bg-extColorPrimary6 hover:bg-extColorPrimary8 text-white py-3 px-6 rounded-full text-lg shadow-lg transition-all duration-500"
>
{buttonText ?? placeholderButtonText}
</Link>

View File

@ -32,9 +32,9 @@ export function GoogleReviews({ data }: GoogleReviewProps) {
<div className="flex justify-center items-center space-x-2">
<h2 className="text-xl text-center font-bol mt-[15px]">{data.ratingValue}</h2>
<div className="mt-1">
<StarRating size={30} value={data.ratingValue} />
<StarRating size={20} value={data.ratingValue} />
</div>
<div className="font-medium">Over {data.totalRating} Reviews</div>
<div className="text-xs font-medium">Over {data.totalRating} Reviews</div>
</div>
<div className="mt-20 relative">

View File

@ -5,7 +5,7 @@ import { fetchBlog } from "@/services/payload/blog";
import { sanitizeBlogContentIntoStringPreview } from "@/utils/sanitize";
export default async function Blog() {
const data = await fetchBlog();
const data = await fetchBlog(undefined);
if (!data?.totalDocs) return <></>;
return (

View File

@ -31,7 +31,7 @@ export function BlogCardItem({ data }: BlogCardItemProps) {
<a href={`/${data.slug}/`}>{data.title}</a>
</h2>
<div className="flex justify-center mt-2">
<a href={`/${data.slug}/`} className="ext-btn-shadow-sm ext-btn-shadow-sm-primary8">
<a href={`/${data.slug}/`} className="ext-btn-shadow-sm ext-btn-shadow-sm-primary4">
Continue Reading
</a>
</div>

View File

@ -13,7 +13,6 @@ export interface BlogDetailProps {
export default async function BlogDetail({ slug }: BlogDetailProps) {
const data = await fetchBlogDetail(slug);
const headersList = await headers();
const mainUrl = headersList.get("x-main-url");
const fullUrl = headersList.get("x-full-url");
const shareUrl = {
@ -26,73 +25,8 @@ export default async function BlogDetail({ slug }: BlogDetailProps) {
notFound();
}
let author = {};
if (!!data?.data?.createdBy && typeof data?.data?.createdBy !== "number") {
author = {
author: {
name: data?.data?.createdBy?.name,
"@id": `${mainUrl}/#/schema/person/${data?.data?.createdBy?.id}`,
},
};
}
const jsonLd = {
"@context": "https://schema.org",
"@graph": [
{
"@type": "Article",
"@id": `${fullUrl}#article`,
isPartOf: {
"@id": fullUrl,
},
...author,
headline: data?.data?.title ?? "",
datePublished: data?.createdAt,
dateModified: data?.updatedAt,
mainEntityOfPage: {
"@id": fullUrl,
},
publisher: { "@id": `${mainUrl}/#organization` },
image: {
"@id": `${fullUrl}#primaryimage`,
},
thumbnailUrl: data?.imgUrl ?? "",
inLanguage: "en-US",
},
{
"@type": "WebPage",
"@id": fullUrl,
url: fullUrl,
name: `${data?.data?.title ?? ""} - Cochise Oncology`,
isPartOf: { "@id": `${mainUrl}/#website` },
primaryImageOfPage: {
"@id": `${fullUrl}#primaryimage`,
},
image: {
"@id": `${fullUrl}#primaryimage`,
},
thumbnailUrl: data?.imgUrl ?? "",
datePublished: data?.createdAt,
dateModified: data?.updatedAt,
description: data?.data?.meta?.description,
breadcrumb: {
"@id": `${fullUrl}#breadcrumb`,
},
inLanguage: "en-US",
potentialAction: [
{
"@type": "ReadAction",
target: [fullUrl],
},
],
},
],
};
return (
<>
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
<HeroOther title={data.data.title} createdAt={data?.createdAt} shareUrl={shareUrl} />
{/* Section */}

View File

@ -4,13 +4,12 @@ import { BlogCardItem } from "./BlogCardItem";
import { sanitizeBlogContentIntoStringPreview } from "@/utils/sanitize";
export interface BlogsProps {
preset?: "blogs" | "categories";
page: number;
search?: string;
}
export default async function Blogs({ page, search, preset = "blogs" }: BlogsProps) {
const data = await fetchBlog({ page, search });
export default async function Blogs({ page, search }: BlogsProps) {
const data = await fetchBlog(page, search);
if (!data?.totalDocs) return <></>;
@ -42,7 +41,6 @@ export default async function Blogs({ page, search, preset = "blogs" }: BlogsPro
hasNextPage={data.hasNextPage}
hasPreviousPage={data.hasPrevPage}
totalPages={data.totalPages}
usePathParams={preset === "blogs" ? false : true}
/>
</div>
)}

View File

@ -1,6 +1,5 @@
import Image from "next/image";
import { FaClock, FaFacebookF, FaFax, FaLinkedinIn, FaMapMarkerAlt } from "react-icons/fa";
import { GiRotaryPhone } from "react-icons/gi";
import { FaClock, FaFacebook, FaFax, FaLinkedin, FaMapMarkerAlt, FaPhone } from "react-icons/fa";
import ScrollToTop from "./ScrollToTop";
import { fetchContact } from "@/services/payload/contact";
import { Suspense } from "react";
@ -26,11 +25,11 @@ async function FooterWithData() {
backgroundImage: "linear-gradient(172deg, #798D90 0%, #C48853 100%)",
}}
>
<div className="container mx-auto flex flex-wrap justify-around items-start px-6">
<div className="container mx-auto flex flex-wrap justify-between items-start px-6">
<div className="w-full md:w-1/4 mb-6 md:mb-0 flex flex-col items-start">
<Image src="/assets/images/demo-slick/logo-dark.webp" alt="Cochise Oncology Logo" width={363} height={138} />
<p className="mt-4 mb-2">© {new Date().getFullYear()} All Rights Reserved</p>
<a href="/privacy-policy" className="text text-extColorPrimary8 font-semibold">
<p className="text-sm mt-3">© {new Date().getFullYear()} All Rights Reserved</p>
<a href="/privacy-policy" className="text-sm text-orange-300 font-semibold">
Privacy Policy
</a>
</div>
@ -38,8 +37,8 @@ async function FooterWithData() {
<div className="w-full md:w-1/3 mb-6 md:mb-0">
<h3 className="text-lg font-semibold mb-4">Contact Us</h3>
<ul className="space-y-4 border-gray-400 pl-0">
<li className="flex items-center space-x-4 border-b-[0.8px] border-[#b69a89] pb-2">
<FaMapMarkerAlt className="text-2xl text-white-300" />
<li className="flex items-center space-x-4 border-b border-gray-500 pb-2">
<FaMapMarkerAlt className="text-2xl text-gray-300" />
<div className="leading-tight">
<a
href={contact?.location?.href ?? ""}
@ -50,49 +49,49 @@ async function FooterWithData() {
{contact?.location?.street ?? ""}
</a>
<br />
<span className="text-sm text-white-300">{contact?.fullLocation ?? ""}</span>
<span className="text-sm text-gray-300">{contact?.fullLocation ?? ""}</span>
</div>
</li>
<li className="flex items-center space-x-4 border-b-[0.8px] border-[#b69a89] pb-2">
<GiRotaryPhone className="text-2xl text-white-300" />
<li className="flex items-center space-x-4 border-b border-gray-500 pb-2">
<FaPhone className="text-2xl text-gray-300" />
<a
href={`tel:${contact?.phone ?? ""}`}
target="_blank"
rel="noopener noreferrer"
className="text-lg text-white text-[18px]"
className="text-lg text-white"
>
{contact?.phone ?? ""}
</a>
</li>
<li className="flex items-center space-x-4 border-b-[0.8px] border-[#b69a89] pb-2">
<FaFax className="text-2xl text-white-300" />
<li className="flex items-center space-x-4 border-b border-gray-500 pb-2">
<FaFax className="text-2xl text-gray-300" />
<a
href={`tel:${contact?.fax ?? ""}`}
target="_blank"
rel="noopener noreferrer"
className="text-lg text-white text-[18px]"
className="text-lg text-white"
>
Fax: {contact?.fax ?? ""}
</a>
</li>
<li className="flex items-center space-x-4 border-b-[0.8px] border-[#b69a89] pb-2">
<FaFacebookF className="text-2xl text-white-300" />
<li className="flex items-center space-x-4 border-b border-gray-500 pb-2">
<FaFacebook className="text-2xl text-gray-300" />
<a
href={contact?.facebook ?? ""}
target="_blank"
rel="noopener noreferrer"
className="text-lg text-white text-[18px]"
className="text-lg text-white"
>
Facebook
</a>
</li>
<li className="flex items-center space-x-4">
<FaLinkedinIn className="text-2xl text-white-300" />
<FaLinkedin className="text-2xl text-gray-300" />
<a
href={contact?.linkedin ?? ""}
target="_blank"
rel="noopener noreferrer"
className="text-lg text-white text-[18px]"
className="text-lg text-white"
>
Linkedin
</a>
@ -127,7 +126,7 @@ function FooterSkeleton() {
backgroundImage: "linear-gradient(172deg, #798D90 0%, #C48853 100%)",
}}
>
<div className="container mx-auto flex flex-wrap justify-around items-start px-6">
<div className="container mx-auto flex flex-wrap justify-between items-start px-6">
<div className="w-full md:w-1/4 mb-6 md:mb-0 flex flex-col items-start">
<Image src="/assets/images/demo-slick/logo-dark.webp" alt="Cochise Oncology Logo" width={363} height={138} />
<p className="text-sm mt-3">© {new Date().getFullYear()} All Rights Reserved</p>
@ -136,28 +135,28 @@ function FooterSkeleton() {
<div className="w-full md:w-1/3 mb-6 md:mb-0 animate-pulse">
<h3 className="text-lg font-semibold mb-4">Contact Us</h3>
<ul className="space-y-4 border-gray-400 pl-0">
<li className="flex items-center space-x-4 border-b-[0.8px] border-[#b69a89] pb-2">
<li className="flex items-center space-x-4 border-b border-gray-500 pb-2">
<FaMapMarkerAlt className="text-2xl text-gray-300" />
<div className="h-2 bg-gray-300 rounded flex-1"></div>
<div className="h-2 bg-gray-300 rounded flex-1"></div>
</li>
<li className="flex items-center space-x-4 border-b-[0.8px] border-[#b69a89] pb-2">
<GiRotaryPhone className="text-2xl text-gray-300" />
<li className="flex items-center space-x-4 border-b border-gray-500 pb-2">
<FaPhone className="text-2xl text-gray-300" />
<div className="h-2 bg-gray-300 rounded flex-1"></div>
<div className="h-2 bg-gray-300 rounded flex-1"></div>
</li>
<li className="flex items-center space-x-4 border-b-[0.8px] border-[#b69a89] pb-2">
<li className="flex items-center space-x-4 border-b border-gray-500 pb-2">
<FaFax className="text-2xl text-gray-300" />
<div className="h-2 bg-gray-300 rounded flex-1"></div>
<div className="h-2 bg-gray-300 rounded flex-1"></div>
</li>
<li className="flex items-center space-x-4 border-b-[0.8px] border-[#b69a89]">
<FaFacebookF className="text-2xl text-gray-300" />
<li className="flex items-center space-x-4">
<FaFacebook className="text-2xl text-gray-300" />
<div className="h-2 bg-gray-300 rounded flex-1"></div>
<div className="h-2 bg-gray-300 rounded flex-1"></div>
</li>
<li className="flex items-center space-x-4">
<FaLinkedinIn className="text-2xl text-gray-300" />
<FaLinkedin className="text-2xl text-gray-300" />
<div className="h-2 bg-gray-300 rounded flex-1"></div>
<div className="h-2 bg-gray-300 rounded flex-1"></div>
</li>
@ -168,8 +167,8 @@ function FooterSkeleton() {
<h3 className="text-lg font-semibold mb-4">Business Hours</h3>
<div className="flex items-center space-x-2">
<FaClock className="text-xl" />
<div className="h-2 bg-white-300 rounded flex-1"></div>
<div className="h-2 bg-white-300 rounded flex-1"></div>
<div className="h-2 bg-gray-300 rounded flex-1"></div>
<div className="h-2 bg-gray-300 rounded flex-1"></div>
</div>
</div>
</div>

View File

@ -11,27 +11,27 @@ const shareIcons: Record<string, any> = {
phone: {
link: "tel:+15208036644",
dom: (
<span className="social-nav flex gap-1 text-2xl lg:text-white">
<Image src="/assets/icons/phone.png" alt="Image Description" width={25} height={22} />
<b className="text-[17px]">(520) 803-6644</b>
<span className="social-nav flex gap-1 text-2xl lg:text-[#00898b]">
<Image src="/assets/icons/phone.png" alt="Image Description" width={25} height={25} />
<b className="text-sm">(520) 803-6644</b>
</span>
),
},
facebook: {
link: "https://www.facebook.com/p/Cochise-Oncology-61556262839823",
dom: (
<span className="social-nav flex gap-3 text-2xl lg:text-white">
<FaFacebook size={25} />
<b className="text-[17px] lg:hidden">Facebook</b>
<span className="social-nav flex gap-3 text-2xl lg:text-[#00898b]">
<FaFacebook />
<b className="text-sm lg:hidden">Facebook</b>
</span>
),
},
linkedin: {
link: "https://linkedin.com/company/cochise-oncology",
dom: (
<span className="social-nav flex gap-3 text-2xl lg:text-white">
<FaLinkedin size={25} />
<b className="text-[17px] lg:hidden">Linkedin</b>
<span className="social-nav flex gap-3 text-2xl lg:text-[#00898b]">
<FaLinkedin />
<b className="text-sm lg:hidden">Linkedin</b>
</span>
),
},
@ -78,7 +78,7 @@ export default function Header({ links }: { links: typeof navMenuData }) {
<div className="flex flex-col items-center h-full w-full content-center justify-center gap-2">
<a
href="/contact"
className="bg-extColorPrimary8 px-3 py-2 rounded-full text-white font-bold hover:cursor-pointer hover:scale-[1.15] mt-3 lg:mt-0 transition-transform duration-300"
className="bg-[#00898b] px-3 py-2 rounded-full text-white font-bold hover:cursor-pointer hover:bg-[#00abad] scale-[1.02] mt-3 lg:mt-0"
>
REQUEST CONSULTATION
</a>
@ -86,7 +86,7 @@ export default function Header({ links }: { links: typeof navMenuData }) {
{Object.keys(shareIcons).map((k, idx) => {
return (
<li key={idx} className="!p-0 !m-0">
<a className="cursor-pointer text-white" href={shareIcons[k].link} target="_blank">
<a className="cursor-pointer" href={shareIcons[k].link} target="_blank">
{shareIcons[k].dom}
</a>
</li>

View File

@ -60,10 +60,7 @@ export default function HeaderNav({ links, animateY = false }: { links: typeof n
{link.text} <i className="mi-chevron-down" onClick={() => toggleDropdown([link.text])} />
</Link>
<ul
className={`mn-sub to-right ${isDropdownOpen.includes(link.text) && "open"} !bg-extColorPrimary8`}
ref={dropdownRef}
>
<ul className={`mn-sub to-right ${isDropdownOpen.includes(link.text) && "open"}`} ref={dropdownRef}>
{link.child.map((subLink: any, subLinkIdx: number) => (
<li key={subLinkIdx}>
{!Array.isArray(subLink?.child) && (
@ -78,9 +75,7 @@ export default function HeaderNav({ links, animateY = false }: { links: typeof n
<i className="mi-chevron-down" onClick={() => toggleDropdown([link.text, subLink.text])} />
</Link>
<ul
className={`mn-sub to-right ${isDropdownOpen.includes(subLink.text) && "open"} !bg-extColorPrimary8`}
>
<ul className={`mn-sub to-right ${isDropdownOpen.includes(subLink.text) && "open"}`}>
{subLink.child.map((subLink2: any, subLinkIdx2: number) => (
<li key={subLinkIdx2}>
<Link href={subLink2?.href} onClick={() => toggleMobileMenu()}>

View File

@ -24,8 +24,7 @@ export default function Hero() {
<p className="text-xl">Southern Arizonas Only Complete Cancer Treatment Center in Sierra Vista.</p>
<a
href="/contact"
// className="btn btn-mod btn-border-w btn-large btn-round align-middle w-full md:w-1/2 lg:w-1/4 hover:opacity-[0.5]"
className="inline-block bg-extColorPrimary6 hover:bg-extColorPrimary8 text-white py-1 px-6 hover:scale-105 rounded-full text-lg shadow-lg transition-all duration-500"
className="btn btn-mod btn-border-w btn-large btn-round align-middle w-full md:w-1/2 lg:w-1/4 hover:opacity-[0.5]"
data-btn-animate="y"
>
Request Consultation

View File

@ -123,15 +123,15 @@ export default function homepage({ dark = false }) {
<div className="row wow fadeInUp">
<div className="col-12 col-lg-6 offset-lg-6 col-xl-6 offset-xl-6">
<h2 className="section-title mb-40 mb-sm-30">A Cancer Treatment Center of Excellence</h2>
<p className="">
<p className="text-sm">
Get compassionate care and excellent medical services from COCHISE ONCOLOGY in Sierra Vista, Arizona. We
are the largest full-service cancer treatment center between Albuquerque, New Mexico and Tucson,
Arizona. Our services cover radiation treatments as well as chemotherapy, and we also have an infusion
center. We offer support groups as well as free hospitality housing for patients undergoing treatment.
Read on to learn more about our establishment, or reach out to us via phone or email to make an inquiry.
</p>
<h2 className="section-title mb-40 mb-sm-30">The Mission of Cochise Oncology</h2>
<p className="">
<h4>The Mission of Cochise Oncology</h4>
<p className="text-sm">
To provide excellence in cancer and patient care in a comprehensive center for the local community.
Optimal cancer treatment places the patient firstfrom the patients first greeting with the front
office staff to the trust they develop with the physicians and the whole treatment team. We believe our

View File

@ -3,7 +3,6 @@ import { RenderBlocks } from "@/components/Blocks/RenderBlocks";
import BlogDetail from "@/components/Blogs/BlogDetail";
import { fetchPageBySlug } from "@/services/payload/page";
import HeroOther from "@/components/HeroOther";
import { headers } from "next/headers";
export interface PageProps {
slug: string | undefined;
@ -12,7 +11,6 @@ export interface PageProps {
export default async function Page({ slug }: PageProps) {
const page = await fetchPageBySlug({ slug });
// if page is a blog post
if (!page) {
return (
<>
@ -22,88 +20,8 @@ export default async function Page({ slug }: PageProps) {
);
}
// if page is not a blog post
const headersList = await headers();
const mainUrl = headersList.get("x-main-url");
const fullUrl = headersList.get("x-full-url");
const siteName = headersList.get("x-site-name");
const metaDesc = !!page?.meta?.description ? page.meta?.description : headersList.get("x-meta-desc");
const jsonLd = {
"@context": "https://schema.org",
"@graph": [
{
"@type": "WebPage",
"@id": fullUrl,
url: fullUrl,
name: `${page.meta?.title ?? ""} - ${siteName}`,
isPartOf: {
"@id": `${mainUrl}/#website`,
},
datePublished: page?.createdAt,
dateModified: page?.updatedAt,
description: metaDesc,
breadcrumb: {
"@id": `${fullUrl}#breadcrumb`,
},
inLanguage: "en-US",
potentialAction: [
{
"@type": "ReadAction",
target: [fullUrl],
},
],
},
{
"@type": "WebSite",
"@id": `${mainUrl}/#website`,
url: `${mainUrl}/`,
name: siteName,
description: "",
publisher: {
"@id": `${mainUrl}/#organization`,
},
potentialAction: [
{
"@type": "SearchAction",
target: {
"@type": "EntryPoint",
urlTemplate: `${mainUrl}/?s={search_term_string}`,
},
"query-input": {
"@type": "PropertyValueSpecification",
valueRequired: true,
valueName: "search_term_string",
},
},
],
inLanguage: "en-US",
},
{
"@type": "Organization",
"@id": `${mainUrl}/#organization`,
name: siteName,
url: `${mainUrl}/`,
logo: {
"@type": "ImageObject",
inLanguage: "en-US",
"@id": `${mainUrl}/#/schema/logo/image/`,
url: `${mainUrl}/assets/images/logo-dark.webp`,
contentUrl: `${mainUrl}/assets/images/logo-dark.webp`,
width: 500,
height: 195,
caption: siteName,
},
image: {
"@id": `${mainUrl}/#/schema/logo/image/`,
},
},
],
};
return (
<>
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
<HeroOther title={page.title} img={page?.heroImg?.url ?? ""} />
<RenderBlocks blocks={page.layout} />
</>

View File

@ -7,16 +7,9 @@ interface PaginationProps {
hasPreviousPage: boolean;
hasNextPage: boolean;
totalPages: number;
usePathParams?: boolean;
}
export default function Pagination({
page,
hasPreviousPage,
hasNextPage,
totalPages,
usePathParams = false,
}: PaginationProps) {
export default function Pagination({ page, hasPreviousPage, hasNextPage, totalPages }: PaginationProps) {
const activePage = page;
const pathName = usePathname();
@ -24,16 +17,11 @@ export default function Pagination({
const handlePageChange = (page: string | number) => {
if (typeof page === "string") return;
if (typeof window === "undefined") return;
const url = new URL(window.location.href);
const searchParams = new URLSearchParams(url.search);
if (usePathParams) {
searchParams.set("page", `${page}`);
window.location.href = `${pathName}/page/${page}/?${searchParams}`;
} else {
searchParams.set("page", `${page}`);
window.location.href = `${pathName}/?${searchParams}`;
}
searchParams.set("page", `${page}`);
window.location.href = `${pathName}/?${searchParams}`;
};
const getPageNumbers = () => {

View File

@ -1,6 +1,5 @@
import Image from "next/image";
import StarRating from "./StarRating";
import { limitString } from "@/utils/general";
export interface CardStarRatingProps {
data: {
@ -13,10 +12,10 @@ export function CardStarRating({ data }: CardStarRatingProps) {
return (
<div className="bg-white p-2 rounded-lg shadow-md">
<div className="flex justify-between">
<StarRating size={30} value={data.star} />
<Image src="/assets/images/google-provider.svg" width={30} height={30} alt="" />
<StarRating size={20} value={data.star} />
<Image src="/assets/images/google-provider.svg" width={20} height={20} alt="" />
</div>
<p className="p-1 mt-4 text-[22px]">{limitString(data.description)}</p>
<p className="text-xs pl-1 mt-4">{data.description}</p>
</div>
);
}

View File

@ -7,17 +7,12 @@ export function middleware(request: NextRequest) {
const protocol = request.headers.get("x-forwarded-proto") || "http"; // Default to 'http' if not provided
const host = request.headers.get("x-forwarded-host") || request.nextUrl.hostname;
const path = request.nextUrl.pathname + request.nextUrl.search;
const siteName = "Cochise Oncology";
const metaDesc =
"Get compassionate care and excellent medical services from COCHISE ONCOLOGY in Sierra Vista, Arizona. We are the largest full-service cancer treatment center between Albuquerque, New Mexico and Tucson, Arizona.";
// Construct the full URL
const mainUrl = `${protocol}://${host}`;
const fullUrl = `${mainUrl}${path}`;
request.headers.set("x-main-url", mainUrl);
request.headers.set("x-full-url", fullUrl);
request.headers.set("x-site-name", siteName);
request.headers.set("x-meta-desc", metaDesc);
return NextResponse.next({
request: {

View File

@ -304,7 +304,7 @@ export interface Page {
meta?: {
title?: string | null;
description?: string | null;
canonical_url?: string | null;
cannonical_url?: string | null;
};
createdBy?: (number | null) | User;
updatedBy?: (number | null) | User;
@ -749,7 +749,7 @@ export interface PagesSelect<T extends boolean = true> {
| {
title?: T;
description?: T;
canonical_url?: T;
cannonical_url?: T;
};
createdBy?: T;
updatedBy?: T;

View File

@ -1,34 +1,21 @@
import payloadConfig from "@/payload.config";
import { formatDate } from "@/utils/datetime";
import { getPayload, Where } from "payload";
import { getPayload } from "payload";
type FetchBlogParams = {
page?: number;
search?: string;
categorySlug?: string;
};
export async function fetchBlog({ page, search = "", categorySlug = "" }: FetchBlogParams = {}) {
export async function fetchBlog(page: number | undefined, search: string = "") {
const payload = await getPayload({ config: payloadConfig });
const queryCondition: Where = {};
if (!!search) {
queryCondition["title"] = {
contains: search,
};
}
if (!!categorySlug) {
queryCondition["categories"] = {
equals: 9,
};
}
const blogDataQuery = await payload.find({
collection: "blogs",
page,
pagination: true,
limit: 9,
where: queryCondition,
limit: 6,
where: !search
? undefined
: {
title: {
contains: search,
},
},
});
const formattedData = blogDataQuery.docs.map((item) => {
@ -60,13 +47,11 @@ export async function fetchBlogDetail(slug: string | undefined) {
const data = blogDataQuery?.docs?.[0];
const createdAt = formatDate(data.createdAt);
const updatedAt = formatDate(data.updatedAt);
const imgUrl = typeof data.img !== "number" ? (data?.img?.url ?? "") : "";
return {
data,
createdAt,
updatedAt,
imgUrl,
};
}

View File

@ -1,3 +0,0 @@
export function limitString(text: string) {
return `${text.length > 100 ? `${text.slice(0, 100)}...` : text}`;
}

View File

@ -5,30 +5,14 @@ export async function getDefaultMetadata(): Promise<Metadata> {
const headersList = await headers();
const mainUrl = headersList.get("x-main-url") ?? "";
const fullUrl = headersList.get("x-full-url") ?? "";
const siteName = headersList.get("x-site-name") ?? "";
const metaDesc = headersList.get("x-meta-desc") ?? "";
return {
metadataBase: new URL(mainUrl),
title: siteName,
description: metaDesc,
robots: {
index: true,
follow: true,
"max-image-preview": "large",
"max-snippet": -1,
"max-video-preview": -1,
},
openGraph: {
siteName,
title: siteName,
description: metaDesc,
type: "website",
locale: "en_US",
url: fullUrl,
},
twitter: {
card: "summary_large_image",
siteName: "Cochise Oncology",
},
alternates: {
canonical: "./",