Compare commits

..

3 Commits

16 changed files with 379 additions and 145 deletions

View File

@ -4,13 +4,13 @@ import { BeforeFooterBlock } from "@/components/Blocks/BeforeFooter";
export const metadata = {
title: "HomePage - Cochise Oncology",
description: "Resonance — One & Multi Page React Nextjs Creative Template",
description: "Cochise Oncology",
keywords: "Cochise Oncology, Oncology, Healthcare, Medical Services, Cancer Treatment",
author: "Cochise Oncology",
robots: "index, follow",
og: {
title: "HomePage - Cochise Oncology",
description: "Resonance — One & Multi Page React Nextjs Creative Template",
description: "Cochise Oncology",
type: "website",
url: "https://www.cochiseoncology.com",
image: "https://www.cochiseoncology.com/og-image.jpg",
@ -19,7 +19,7 @@ export const metadata = {
card: "summary_large_image",
site: "@CochiseOncology",
title: "HomePage - Cochise Oncology",
description: "Resonance — One & Multi Page React Nextjs Creative Template",
description: "Cochise Oncology",
image: "https://www.cochiseoncology.com/twitter-image.jpg",
},
};

View File

@ -1,8 +1,8 @@
import Image from "next/image";
export const metadata = {
title: "Slick About Dark || Resonance — One & Multi Page React Nextjs Creative Template",
description: "Resonance — One & Multi Page React Nextjs Creative Template",
title: "Slick About Dark || Cochise Oncology",
description: "Cochise Oncology",
};
const dark = true;
export default function SlickAboutPageDark() {

View File

@ -6,8 +6,8 @@ import Image from "next/image";
import Link from "next/link";
export const metadata = {
title: "Slick About Dark || Resonance — One & Multi Page React Nextjs Creative Template",
description: "Resonance — One & Multi Page React Nextjs Creative Template",
title: "Slick About Dark || Cochise Oncology",
description: "Cochise Oncology",
};
const dark = true;
export default function SlickAboutPageDark() {

View File

@ -6,8 +6,8 @@ import Image from "next/image";
import Link from "next/link";
export const metadata = {
title: "Slick About || Resonance — One & Multi Page React Nextjs Creative Template",
description: "Resonance — One & Multi Page React Nextjs Creative Template",
title: "Slick About || Cochise Oncology",
description: "Cochise Oncology",
};
const dark = false;

View File

@ -4,8 +4,8 @@ import Image from "next/image";
import Link from "next/link";
export const metadata = {
title: "Slick Portfolio || Resonance — One & Multi Page React Nextjs Creative Template",
description: "Resonance — One & Multi Page React Nextjs Creative Template",
title: "Slick Portfolio || Cochise Oncology",
description: "Cochise Oncology",
};
const dark = false;
export default function SlickPortfolioPage() {

View File

@ -5,8 +5,8 @@ import Image from "next/image";
import Link from "next/link";
export const metadata = {
title: "Slick Services || Resonance — One & Multi Page React Nextjs Creative Template",
description: "Resonance — One & Multi Page React Nextjs Creative Template",
title: "Slick Services || Cochise Oncology",
description: "Cochise Oncology",
};
const dark = false;

View File

@ -1,13 +1,15 @@
import { BeforeFooterBlock } from "@/components/Blocks/BeforeFooter";
import { GoogleReviewsBlock } from "@/components/Blocks/GoogleReviews";
import { GoogleReviewsSkeleton } from "@/components/Blocks/GoogleReviews/GoogleReviews";
import Image from "next/image";
import { Suspense } from "react";
export const metadata = {
title: "Testimonials - Cochise Oncology",
description: "Testimonials - Cochise Oncology",
};
export default function SlickAboutPage() {
export default async function TestimonialsPage() {
return (
<>
<section className="page-section bg-dark-1 bg-gradient-gray-dark-1 light-content bg-scroll overflow-hidden">
@ -26,7 +28,9 @@ export default function SlickAboutPage() {
</div>
</section>
<section className="page-section scrollSpysection" id="testimonials">
<GoogleReviewsBlock />
<Suspense fallback={<GoogleReviewsSkeleton />}>
<GoogleReviewsBlock />
</Suspense>
</section>
<BeforeFooterBlock />
</>

View File

@ -0,0 +1,188 @@
"use client";
import { CardStarRating, CardStarRatingProps, CardStarRatingSkeleton } from "@/components/Ratings/CardStarRating";
import StarRating from "@/components/Ratings/StarRating";
import Image from "next/image";
import { Navigation } from "swiper/modules";
import { Swiper, SwiperSlide } from "swiper/react";
export interface GoogleReviewProps {
data: {
ratingValue: number;
totalRating: number;
data: CardStarRatingProps["data"][];
};
}
export function GoogleReviews({ data }: GoogleReviewProps) {
return (
<section className="bg-scroll relative">
<div className="decoration-14" />
<div className="decoration-15" />
<div className="decoration-16 opacity-035 d-none d-md-block">
<Image src="/assets/images/demo-slick/decoration-2.svg" alt="Image Description" width={128} height={228} />
</div>
<div className="decoration-17 opacity-035 d-none d-md-block">
<Image src="/assets/images/demo-slick/decoration-2.svg" alt="Image Description" width={128} height={228} />
</div>
<div className="container px-6">
<h2 className="text-3xl font-bold text-center">Reviews</h2>
<div className="flex justify-center space-x-2">
<h2 className="text-xl text-center font-bol">{data.ratingValue}</h2>
<div className="mt-1">
<StarRating size={20} value={data.ratingValue} />
</div>
<div className="text-xs font-semibold mt-">Over {data.totalRating} Reviews</div>
</div>
<div className="mt-20 relative">
<Swiper
loop
spaceBetween={0}
slidesPerView={3}
breakpoints={{
900: {
slidesPerView: 3,
},
500: {
slidesPerView: 2, // When window width is <= 480px
},
0: {
slidesPerView: 1,
},
}}
modules={[Navigation]}
navigation={{
prevEl: ".snbp1",
nextEl: ".snbn1",
}}
watchSlidesProgress
resizeObserver
className="team-carousel owl-carousel owl-theme overflow-hidden position-static"
style={{
opacity: 1,
display: "block",
}}
>
{/* Group item */}
{data.data.map((rev, index) => (
<SwiperSlide className="owl-item py-2 mt-5" key={index}>
<div>
<CardStarRating data={rev} />
</div>
</SwiperSlide>
))}
<div className="owl-controls clickable absolute top-6 w-full">
<div className="owl-buttons">
<div className="owl-prev snbp1 left" role="button" tabIndex={0}>
<span className="visually-hidden">Previous Slide</span>
<i className="mi-arrow-left" aria-hidden="true"></i>
</div>
<div className="owl-next snbn1" role="button" tabIndex={0}>
<span className="visually-hidden">Next Slide</span>
<i className="mi-arrow-right" aria-hidden="true"></i>
</div>
</div>
</div>
{/* End Group item */}
</Swiper>
</div>
</div>
</section>
);
}
export function GoogleReviewsSkeleton() {
return (
<section className="bg-scroll relative">
<div className="decoration-14" />
<div className="decoration-15" />
<div className="decoration-16 opacity-035 d-none d-md-block">
<Image src="/assets/images/demo-slick/decoration-2.svg" alt="Image Description" width={128} height={228} />
</div>
<div className="decoration-17 opacity-035 d-none d-md-block">
<Image src="/assets/images/demo-slick/decoration-2.svg" alt="Image Description" width={128} height={228} />
</div>
<div className="container px-6">
<h2 className="text-3xl font-bold text-center">Reviews</h2>
<GoogleReviewsHeaderSkeleton />
<GoogleReviewsContentSkeleton />
</div>
</section>
);
}
function GoogleReviewsHeaderSkeleton() {
return (
<div className="animate-pulse flex flex-1 justify-center">
<div className="h-5 bg-gray-300 rounded w-1/3 mb-2"></div>
</div>
);
}
function GoogleReviewsContentSkeleton() {
return (
<div className="mt-20 relative">
<Swiper
loop
spaceBetween={0}
slidesPerView={3}
breakpoints={{
900: {
slidesPerView: 3,
},
500: {
slidesPerView: 2, // When window width is <= 480px
},
0: {
slidesPerView: 1,
},
}}
modules={[Navigation]}
navigation={{
prevEl: ".snbp1",
nextEl: ".snbn1",
}}
watchSlidesProgress
resizeObserver
className="team-carousel owl-carousel owl-theme overflow-hidden position-static"
style={{
opacity: 1,
display: "block",
}}
>
{/* Group item */}
<SwiperSlide className="owl-item py-2 mt-5">
<CardStarRatingSkeleton />
</SwiperSlide>
<SwiperSlide className="owl-item py-2 mt-5">
<CardStarRatingSkeleton />
</SwiperSlide>
<SwiperSlide className="owl-item py-2 mt-5">
<CardStarRatingSkeleton />
</SwiperSlide>
<SwiperSlide className="owl-item py-2 mt-5">
<CardStarRatingSkeleton />
</SwiperSlide>
<div className="owl-controls clickable absolute top-6 w-full">
<div className="owl-buttons">
<div className="owl-prev snbp1 left" role="button" tabIndex={0}>
<span className="visually-hidden">Previous Slide</span>
<i className="mi-arrow-left" aria-hidden="true"></i>
</div>
<div className="owl-next snbn1" role="button" tabIndex={0}>
<span className="visually-hidden">Next Slide</span>
<i className="mi-arrow-right" aria-hidden="true"></i>
</div>
</div>
</div>
{/* End Group item */}
</Swiper>
</div>
);
}

View File

@ -1,130 +1,66 @@
"use client";
import { fetchGoogleReviews } from "@/services/payload/google-reviews";
import { GoogleReviewProps, GoogleReviews } from "./GoogleReviews";
import { CardStarRating, CardStarRatingProps } from "@/components/Ratings/CardStarRating";
import StarRating from "@/components/Ratings/StarRating";
import Image from "next/image";
import { Navigation } from "swiper/modules";
import { Swiper, SwiperSlide } from "swiper/react";
// const data: CardStarRatingProps["data"][] = [
// {
// star: 5,
// description:
// "Today i did my cat scan , and met Raymond, he radiates hope and good will, i then met Larry who showed me the hospitality suite, i was overwhelmed by thier kindness, every one i have met so far has been truly amazing.while at physical therapy the doc said i had made a wise",
// },
// {
// star: 5,
// description: "Great and caring service. Completely satisfied.",
// },
// {
// star: 5,
// description: "Sincere and caring attitude...very professional and inspires confidence.",
// },
// {
// star: 5,
// description:
// "I've been going to cochise oncology for many years and everyone is always professional and take the time to explain everything",
// },
// {
// star: 5,
// description: "One fine person\n Very concerning. Glad to see her",
// },
// {
// star: 5,
// description:
// "Barbara was very professional and friendly. She shared all info with me and gave me a copy of my lab results.",
// },
// {
// star: 5,
// description:
// "Barbara is great! She listens and ask questions on other issues outside her area just to be sure youre well taken care of.",
// },
// {
// star: 5,
// description:
// "Today, at Cochise Oncology, I had an appointment with Dr. Reid Culton. He explained in great detail from the medical chart of the blood work and listen to my concerns. Dr. Reid Culton also answered my questions in great detail. Thank you.",
// },
// {
// star: 5,
// description:
// "I absolutely love the people that work at this place. From the front desk to radiation room. Everyone is loving and caring.",
// },
// ];
const data: CardStarRatingProps["data"][] = [
{
star: 5,
description:
"Today i did my cat scan , and met Raymond, he radiates hope and good will, i then met Larry who showed me the hospitality suite, i was overwhelmed by thier kindness, every one i have met so far has been truly amazing.while at physical therapy the doc said i had made a wise",
},
{
star: 5,
description: "Great and caring service. Completely satisfied.",
},
{
star: 5,
description: "Sincere and caring attitude...very professional and inspires confidence.",
},
{
star: 5,
description:
"I've been going to cochise oncology for many years and everyone is always professional and take the time to explain everything",
},
{
star: 5,
description: "One fine person\n Very concerning. Glad to see her",
},
{
star: 5,
description:
"Barbara was very professional and friendly. She shared all info with me and gave me a copy of my lab results.",
},
{
star: 5,
description:
"Barbara is great! She listens and ask questions on other issues outside her area just to be sure youre well taken care of.",
},
{
star: 5,
description:
"Today, at Cochise Oncology, I had an appointment with Dr. Reid Culton. He explained in great detail from the medical chart of the blood work and listen to my concerns. Dr. Reid Culton also answered my questions in great detail. Thank you.",
},
{
star: 5,
description:
"I absolutely love the people that work at this place. From the front desk to radiation room. Everyone is loving and caring.",
},
];
export async function GoogleReviewsBlock() {
const data = await fetchGoogleReviews();
if (!data?.id) return <></>;
export function GoogleReviewsBlock() {
return (
<section className="bg-scroll relative">
<div className="decoration-14" />
<div className="decoration-15" />
<div className="decoration-16 opacity-035 d-none d-md-block">
<Image src="/assets/images/demo-slick/decoration-2.svg" alt="Image Description" width={128} height={228} />
</div>
<div className="decoration-17 opacity-035 d-none d-md-block">
<Image src="/assets/images/demo-slick/decoration-2.svg" alt="Image Description" width={128} height={228} />
</div>
const dataReviews: GoogleReviewProps["data"] = {
data:
data.ratings?.map?.((r) => {
return {
star: r.ratingValue,
description: r.description,
};
}) ?? [],
ratingValue: data.ratingValue,
totalRating: data.totalRating,
};
<div className="container px-6">
<h2 className="text-3xl font-bold text-cente">Reviews</h2>
<div className="flex justify-center space-x-2">
<h2 className="text-xl text-center font-bol">4.8</h2>
<div className="mt-1">
<StarRating size={20} value={4.8} />
</div>
<div className="text-xs font-semibold mt-">Over 200 Reviews</div>
</div>
<div className="mt-20 relative">
<Swiper
loop
spaceBetween={0}
slidesPerView={3}
breakpoints={{
900: {
slidesPerView: 3,
},
500: {
slidesPerView: 2, // When window width is <= 480px
},
0: {
slidesPerView: 1,
},
}}
modules={[Navigation]}
navigation={{
prevEl: ".snbp1",
nextEl: ".snbn1",
}}
watchSlidesProgress
resizeObserver
className="team-carousel owl-carousel owl-theme overflow-hidden position-static"
style={{
opacity: 1,
display: "block",
}}
>
{/* Group item */}
{data.map((rev, index) => (
<SwiperSlide className="owl-item py-2 mt-5" key={index}>
<div>
<CardStarRating data={rev} />
</div>
</SwiperSlide>
))}
<div className="owl-controls clickable absolute top-6 w-full">
<div className="owl-buttons">
<div className="owl-prev snbp1 left" role="button" tabIndex={0}>
<span className="visually-hidden">Previous Slide</span>
<i className="mi-arrow-left" aria-hidden="true"></i>
</div>
<div className="owl-next snbn1" role="button" tabIndex={0}>
<span className="visually-hidden">Next Slide</span>
<i className="mi-arrow-right" aria-hidden="true"></i>
</div>
</div>
</div>
{/* End Group item */}
</Swiper>
</div>
</div>
</section>
);
return <GoogleReviews data={dataReviews} />;
}

View File

@ -19,3 +19,15 @@ export function CardStarRating({ data }: CardStarRatingProps) {
</div>
);
}
export function CardStarRatingSkeleton() {
return (
<div className="bg-white p-2 rounded-lg shadow-md animate-pulse">
<div className="flex justify-between">
<div className="h-5 bg-gray-300 rounded w-1/3 mb-2"></div>
<div className="h-5 bg-gray-300 rounded w-1/5 mb-2"></div>
</div>
<div className="h-16 bg-gray-300 rounded w-full mb-2"></div>
</div>
);
}

View File

@ -1,6 +1,6 @@
"use client";
import { Suspense } from "react";
import { GoogleReviewsBlock } from "./Blocks/GoogleReviews";
import { GoogleReviewsSkeleton } from "./Blocks/GoogleReviews/GoogleReviews";
export default function Testimonials() {
// const isotopContainer = useRef<any | null>(null);
@ -28,7 +28,9 @@ export default function Testimonials() {
<GoogleReviewsBlock />
</div>
</div> */}
<GoogleReviewsBlock />
<Suspense fallback={<GoogleReviewsSkeleton />}>
<GoogleReviewsBlock />
</Suspense>
</div>
);
}

View File

@ -0,0 +1,42 @@
import type { GlobalConfig } from "payload";
export const GoogleReviews: GlobalConfig = {
slug: "google-reviews",
fields: [
{
name: "ratingValue",
label: "Rating Value",
type: "number",
required: true,
defaultValue: 0,
},
{
name: "totalRating",
type: "number",
required: true,
defaultValue: 0,
},
{
label: "Ratings",
name: "ratings",
type: "array",
fields: [
{
name: "ratingValue",
label: "Rating Value",
type: "number",
required: true,
defaultValue: 0,
},
{
name: "description",
type: "textarea",
required: true,
},
],
},
],
admin: {
hideAPIURL: true,
},
};

View File

@ -42,8 +42,12 @@ export interface Config {
db: {
defaultIDType: number;
};
globals: {};
globalsSelect: {};
globals: {
'google-reviews': GoogleReview;
};
globalsSelect: {
'google-reviews': GoogleReviewsSelect<false> | GoogleReviewsSelect<true>;
};
locale: null;
user: User & {
collection: 'users';
@ -893,6 +897,42 @@ export interface PayloadMigrationsSelect<T extends boolean = true> {
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "google-reviews".
*/
export interface GoogleReview {
id: number;
ratingValue: number;
totalRating: number;
ratings?:
| {
ratingValue: number;
description: string;
id?: string | null;
}[]
| null;
updatedAt?: string | null;
createdAt?: string | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "google-reviews_select".
*/
export interface GoogleReviewsSelect<T extends boolean = true> {
ratingValue?: T;
totalRating?: T;
ratings?:
| T
| {
ratingValue?: T;
description?: T;
id?: T;
};
updatedAt?: T;
createdAt?: T;
globalType?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "auth".

View File

@ -24,6 +24,7 @@ import {
lexicalEditor,
LinkFeature,
} from "@payloadcms/richtext-lexical";
import { GoogleReviews } from "@/globals/GoogleReviews";
const filename = fileURLToPath(import.meta.url);
const dirname = path.dirname(filename);
@ -50,6 +51,7 @@ export default buildConfig({
theme: "dark",
},
collections: [Users, Media, Blogs, Pages, Teams, BlogCategories, BlogTags],
globals: [GoogleReviews],
secret: process.env.PAYLOAD_SECRET || "",
typescript: {
outputFile: path.resolve(dirname, "payload-types.ts"),

View File

@ -0,0 +1,8 @@
import payloadConfig from "@/payload.config";
import { getPayload } from "payload";
export async function fetchGoogleReviews() {
const payload = await getPayload({ config: payloadConfig });
const dataQuery = await payload.findGlobal({ slug: "google-reviews" });
return dataQuery;
}