Compare commits

...

16 Commits

28 changed files with 1499 additions and 884 deletions

70
package-lock.json generated
View File

@ -18,10 +18,12 @@
"animejs": "^3.2.2",
"bootstrap": "^5.1.3",
"dayjs": "^1.11.13",
"framer-motion": "^12.4.13",
"graphql": "^16.10.0",
"imagesloaded": "^5.0.0",
"isotope-layout": "^3.0.6",
"jarallax": "^2.2.1",
"motion": "^12.4.13",
"next": "15.2.0-canary.30",
"payload": "^3.20.0",
"photoswipe": "^5.4.4",
@ -8092,6 +8094,33 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/framer-motion": {
"version": "12.4.13",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.4.13.tgz",
"integrity": "sha512-JHSXIdL7WOTCSEb2UUurHURV85pWTn6UIg+iWLBhH5SFndbjni8CEQcxwsBwOs3RHZ83TkE4xoxb9cHsFPY9yQ==",
"license": "MIT",
"dependencies": {
"motion-dom": "^12.4.11",
"motion-utils": "^12.4.10",
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@ -10289,6 +10318,47 @@
"license": "MIT",
"peer": true
},
"node_modules/motion": {
"version": "12.4.13",
"resolved": "https://registry.npmjs.org/motion/-/motion-12.4.13.tgz",
"integrity": "sha512-8ehpE6Sd8ack6jLLzweW6RwCBQoASf+yVu8aUPFNKHsJIVejJaBPGsMiswcpfzHeCusZ/ztNIKbgoEn7ruJaOw==",
"license": "MIT",
"dependencies": {
"framer-motion": "^12.4.13",
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/motion-dom": {
"version": "12.4.11",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.4.11.tgz",
"integrity": "sha512-wstlyV3pktgFjqsjbXMo1NX9hQD9XTVqxQNvfc+FREAgxr3GVzgWIEKvbyyNlki3J1jmmh+et9X3aCKeqFPcxA==",
"license": "MIT",
"dependencies": {
"motion-utils": "^12.4.10"
}
},
"node_modules/motion-utils": {
"version": "12.4.10",
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.4.10.tgz",
"integrity": "sha512-NPwZd94V013SwRf++jMrk2+HEBgPkeIE2RiOzhAuuQlqxMJPkKt/LXVh6Upl+iN8oarSGD2dlY5/bqgsYXDABA==",
"license": "MIT"
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",

View File

@ -14,6 +14,7 @@
"tsc": "tsc"
},
"dependencies": {
"@next/third-parties": "^15.2.2",
"@payloadcms/db-postgres": "^3.20.0",
"@payloadcms/next": "^3.20.0",
"@payloadcms/payload-cloud": "^3.20.0",
@ -24,10 +25,12 @@
"animejs": "^3.2.2",
"bootstrap": "^5.1.3",
"dayjs": "^1.11.13",
"framer-motion": "^12.4.13",
"graphql": "^16.10.0",
"imagesloaded": "^5.0.0",
"isotope-layout": "^3.0.6",
"jarallax": "^2.2.1",
"motion": "^12.4.13",
"next": "15.2.0-canary.30",
"payload": "^3.20.0",
"photoswipe": "^5.4.4",

View File

@ -47,8 +47,9 @@
--bs-black-rgb: 0, 0, 0;
--bs-body-color-rgb: 33, 37, 41;
--bs-body-bg-rgb: 255, 255, 255;
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans",
"Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--bs-font-sans-serif:
system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
--bs-body-font-family: var(--bs-font-sans-serif);
@ -70,7 +71,6 @@
}
body {
margin: 0;
font-family: var(--bs-body-font-family);
font-size: var(--bs-body-font-size);
font-weight: var(--bs-body-font-weight);
line-height: var(--bs-body-line-height);
@ -172,10 +172,7 @@ address {
font-style: normal;
line-height: inherit;
}
ol,
ul {
padding-left: 2rem;
}
dl,
ol,
ul {
@ -2535,7 +2532,8 @@ textarea.form-control.is-valid {
.was-validated .form-select:valid:not([multiple]):not([size]),
.was-validated .form-select:valid:not([multiple])[size="1"] {
padding-right: 4.125rem;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),
background-image:
url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),
url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
background-position:
right 0.75rem center,
@ -2634,7 +2632,8 @@ textarea.form-control.is-invalid {
.was-validated .form-select:invalid:not([multiple]):not([size]),
.was-validated .form-select:invalid:not([multiple])[size="1"] {
padding-right: 4.125rem;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),
background-image:
url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),
url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
background-position:
right 0.75rem center,

View File

@ -20,13 +20,12 @@
.dark .light-mode-logo {
display: none;
}
@media (min-width: 1025px) {
/* @media (min-width: 1025px) {
.mn-has-sub:hover + *,
.mn-has-sub + *:hover {
display: block !important;
z-index: 1;
}
}
} */
.fadeInText {
animation: animationFadeText 0.5s linear 0s 1;
@ -87,7 +86,7 @@ a {
}
.mn-sub {
transition: max-height 0.3s ease-in-out;
transition: max-height;
}
.mn-sub.open {

View File

@ -1,6 +1,4 @@
.theme-slick {
--font-global: "Plus Jakarta Sans", sans-serif;
--font-alt: "Plus Jakarta Sans", sans-serif;
--section-padding-y: 50px;
--color-teal-1: #64b3b4;
--color-dark-1: #1e2432;
@ -66,14 +64,15 @@
--gradient-gray-light-1: linear-gradient(0deg, #f7f9fc 0%, #fff 100%);
--gradient-gray-light-2: linear-gradient(0deg, #fff 0%, #f7f9fc 100%);
--border-radius-default: 10px;
--box-shadow: 0px 5px 10px 0px rgba(30, 36, 50, 0.05), 0px 1px 1px 0px rgba(30, 36, 50, 0.03),
--box-shadow:
0px 5px 10px 0px rgba(30, 36, 50, 0.05), 0px 1px 1px 0px rgba(30, 36, 50, 0.03),
0px 3px 5px 0px rgba(30, 36, 50, 0.03);
--box-shadow-strong: 0px 5px 10px 0px rgba(30, 36, 50, 0.08), 0px 1px 1px 0px rgba(30, 36, 50, 0.06),
--box-shadow-strong:
0px 5px 10px 0px rgba(30, 36, 50, 0.08), 0px 1px 1px 0px rgba(30, 36, 50, 0.06),
0px 3px 5px 0px rgba(30, 36, 50, 0.06);
--box-shadow-block: 0px 10px 30px 0px rgba(30, 36, 50, 0.07), 0px 0px 1px 0px rgba(30, 36, 50, 0.1);
--box-shadow-block-strong: 0px 15px 50px 0px rgba(30, 36, 50, 0.14), 0px 0px 1px 0px rgba(30, 36, 50, 0.15);
color: var(--color-dark-1);
font-family: var(--font-global);
font-size: 17px;
font-weight: 400;
line-height: 1.725;
@ -201,9 +200,6 @@
.theme-slick .inner-nav ul li {
margin: 0 18px;
}
.theme-slick .inner-nav > ul > li > a {
opacity: 0.7;
}
.theme-slick .inner-nav ul li a {
position: relative;
}
@ -233,11 +229,7 @@
.theme-slick .mobile-on .desktop-nav ul {
background: rgba(30, 36, 50, 0.99);
}
.theme-slick .mobile-on .desktop-nav ul li a,
.theme-slick .inner-nav ul li .mn-sub li a,
.theme-slick .mn-group-title {
color: rgba(255, 255, 255, 0.9) !important;
}
.theme-slick .form label {
margin-bottom: 15px;
font-size: 18px;

View File

@ -238,10 +238,10 @@ Primary use: Multipurpose Template
--gradient-primary-alpha-2: linear-gradient(90deg, transparent 13%, var(--color-primary-1) 60%);
--border-radius-default: 4px;
--border-radius-large: 30px;
--box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.05), 0px 1px 1px 0px rgba(0, 0, 0, 0.03),
0px 3px 5px 0px rgba(0, 0, 0, 0.03);
--box-shadow-strong: 0px 5px 10px 0px rgba(0, 0, 0, 0.08), 0px 1px 1px 0px rgba(0, 0, 0, 0.06),
0px 3px 5px 0px rgba(0, 0, 0, 0.06);
--box-shadow:
0px 5px 10px 0px rgba(0, 0, 0, 0.05), 0px 1px 1px 0px rgba(0, 0, 0, 0.03), 0px 3px 5px 0px rgba(0, 0, 0, 0.03);
--box-shadow-strong:
0px 5px 10px 0px rgba(0, 0, 0, 0.08), 0px 1px 1px 0px rgba(0, 0, 0, 0.06), 0px 3px 5px 0px rgba(0, 0, 0, 0.06);
--box-shadow-block: 0px 3px 50px 0px rgba(0, 0, 0, 0.05);
--box-shadow-block-strong: 0px 3px 50px 0px rgba(0, 0, 0, 0.15);
--transition-default: all 0.27s cubic-bezier(0, 0, 0.58, 1);
@ -746,7 +746,6 @@ video {
body {
color: var(--color-dark-1);
font-family: var(--font-global);
font-size: 18px;
font-weight: 400;
letter-spacing: normal;
@ -3835,7 +3834,6 @@ a.logo:hover {
.inner-nav ul {
margin: auto;
font-size: 17px;
font-weight: 500;
text-align: center;
letter-spacing: 0;
line-height: 1.3;
@ -3880,7 +3878,6 @@ a.logo:hover {
display: table-cell;
vertical-align: middle;
height: var(--menu-bar-height);
opacity: 0.65;
}
.main-nav:not(.mobile-on).small-height .inner-nav > ul > li > a {
height: var(--menu-bar-height-scrolled) !important;
@ -3925,7 +3922,7 @@ a.logo:hover {
transition: var(--transition-default);
}
.mn-sub {
display: none;
/* opacity: 0; */
width: 220px;
position: absolute;
top: 100%;
@ -3979,7 +3976,6 @@ a.logo:hover {
border-left: none;
border-right: none;
letter-spacing: 0;
color: var(--color-gray-light-5) !important;
outline-offset: -2px !important;
transition: var(--transition-default);
}
@ -3990,7 +3986,6 @@ a.logo:hover {
.inner-nav ul li .mn-sub li a:hover,
.mn-sub li a.active {
background: rgba(255, 255, 255, 0.09);
color: var(--color-gray-light-1) !important;
}
.mn-sub:not(.mn-has-multi) li ul {
left: 100%;
@ -4324,20 +4319,11 @@ a.logo:hover {
backdrop-filter: blur(10px);
}
.main-nav.dark .inner-nav ul > li > a {
font-size: 18px;
color: #fff;
opacity: 0.78;
font-weight: 500;
line-height: 1.2em;
padding: 10px;
}
.main-nav.dark .inner-nav ul > li > a:hover,
.main-nav.dark .inner-nav ul > li > a.active {
color: #fff;
opacity: 1 !important;
}
.main-nav.dark .inner-nav ul li .mn-sub li a {
opacity: 1;
}
.main-nav.dark .main-nav-icon-cart {
opacity: 1;
}
@ -4361,13 +4347,13 @@ a.logo:hover {
font-size: 18px;
font-weight: 700;
text-decoration: none;
color: rgba(255, 255, 255, 0.9);
/* color: rgba(255, 255, 255, 0.9); */
}
.main-nav.dark .mn-sub {
/* .main-nav.dark .mn-sub {
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
} */
.main-nav.dark .mi-chevron-down,
.main-nav.dark.body-scrolled .inner-nav ul > li > a:hover .mi-chevron-down,
.main-nav.dark.body-scrolled .inner-nav ul > li > a.active .mi-chevron-down {

View File

@ -2,22 +2,106 @@ 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 const metadata = {
title: "Blog - Cochise Oncology",
description: "Blog - Cochise Oncology",
};
const metaDesc =
"Explore the latest insights, news, and patient resources on the Cochise Oncology blog. Stay informed about cancer treatments, patient support services, and wellness tips. Read our expert articles today.";
export async function generateMetadata(): Promise<Metadata> {
const metadata = await getDefaultMetadata();
metadata.title = `Blog - ${metadata.openGraph?.siteName}`;
metadata.description = metaDesc;
return metadata;
}
export default async function BlogPage({ searchParams }: { searchParams?: Promise<{ page?: string; s?: string }> }) {
const params = await searchParams;
const paramsPage = params?.page;
const paramsSearch = params?.s;
const page = sanitizePageNumber(paramsPage);
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 jsonLd = {
"@context": "https://schema.org",
"@graph": [
{
"@type": "WebPage",
"@id": fullUrl,
url: fullUrl,
name: `Blog - ${siteName}`,
isPartOf: {
"@id": `${mainUrl}/#website`,
},
datePublished: "2024-07-18T16:42:14+00:00",
dateModified: "2025-02-27T01:20:38+00:00",
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`,
caption: siteName,
},
image: {
"@id": `${mainUrl}/#/schema/logo/image/`,
},
},
],
};
return (
<>
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
<HeroOther title="Blog" />
<section className="page-section" id="blog">

View File

@ -2,14 +2,24 @@ import { BeforeFooterBlock } from "@/components/Blocks/BeforeFooter";
import { BlogCardItemSkeleton } from "@/components/Blogs/BlogCardItem";
import Blogs from "@/components/Blogs/Blogs";
import HeroOther from "@/components/HeroOther";
import { fetchBlogCategoryBySlug } from "@/services/payload/blog";
import { getDefaultMetadata } from "@/utils/metadata";
import { sanitizePageNumber } from "@/utils/sanitize";
import { Metadata } from "next";
// import { headers } from "next/headers";
import { headers } from "next/headers";
import { notFound } from "next/navigation";
import { Suspense } from "react";
export async function generateMetadata(): Promise<Metadata> {
export async function generateMetadata({ params }: { params: Promise<{ path: string[0] }> }): Promise<Metadata> {
const path = (await params)?.path;
const paramsCategory = path?.[0] ?? "";
const metadata = await getDefaultMetadata();
const category = await fetchBlogCategoryBySlug(paramsCategory);
if (!!category?.data) {
metadata.title = `${category.data.name} Archives - ${metadata.openGraph?.siteName}`;
}
return metadata;
}
@ -21,19 +31,92 @@ export default async function CategoryPage({
searchParams?: Promise<{ page?: string; s?: string }>;
}) {
const path = (await params)?.path;
// const headersList = await headers();
// const paramsCategory = path?.[0] ?? "";
const paramsCategory = path?.[0] ?? "";
const paramsPage = path?.[2] ?? "";
const paramsSearch = (await searchParams)?.s;
const page = sanitizePageNumber(paramsPage);
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 category = await fetchBlogCategoryBySlug(paramsCategory);
if (!category) {
notFound();
}
const jsonLd = {
"@context": "https://schema.org",
"@graph": [
{
"@type": "CollectionPage",
"@id": fullUrl,
url: fullUrl,
name: `${category.data.name} - ${siteName}`,
isPartOf: {
"@id": `${mainUrl}/#website`,
},
primaryImageOfPage: {
"@id": `${fullUrl}#primaryimage`,
},
image: {
"@id": `${fullUrl}#primaryimage`,
},
inLanguage: "en-US",
},
{
"@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`,
caption: siteName,
},
image: {
"@id": `${mainUrl}/#/schema/logo/image/`,
},
},
],
};
return (
<>
<HeroOther title="Blog" />
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
<HeroOther title={`Category: ${category.data.name}`} />
<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">
<form action={`/category/${paramsCategory}`} method="GET">
<div className="input-group">
<input
type="text"
@ -52,7 +135,7 @@ export default async function CategoryPage({
</div>
<Suspense fallback={<Loading />}>
<Blogs preset="categories" page={page} search={paramsSearch} />
<Blogs preset="categories" categoryId={category.data.id} page={page} search={paramsSearch} />
</Suspense>
</section>

View File

@ -8,14 +8,27 @@ 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 { Roboto, Noto_Sans } from "next/font/google";
import "photoswipe/dist/photoswipe.css";
import "react-modal-video/css/modal-video.css";
import "swiper/css";
import "swiper/css/effect-fade";
import "tippy.js/dist/tippy.css";
const roboto = Roboto({ subsets: ["latin"] });
const roboto = Roboto({
weight: ["400", "500", "600", "700", "800"],
style: ["normal", "italic"],
subsets: ["latin"],
display: "swap",
variable: "--font-roboto",
});
const notoSans = Noto_Sans({
weight: ["400", "500", "600"],
style: ["normal", "italic"],
subsets: ["latin"],
display: "swap",
variable: "--font-noto-sans",
});
export async function generateMetadata(): Promise<Metadata> {
const metadata = await getDefaultMetadata();
@ -28,9 +41,9 @@ export default function MainLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en" className={`no-mobile no-touch ${roboto.className}`}>
<html lang="en" className={`no-mobile no-touch ${roboto.variable} ${notoSans.variable}`}>
<InitialScript />
<body className="appear-animate body">
<body className="appear-animate body font-fontMain">
<div className="theme-slick">
<div className="page" id="top">
<Header links={navMenuData} />

View File

@ -111,7 +111,7 @@ export default async function Home() {
{
"@type": "Organization",
"@id": `${fullUrl}#organization`,
name: "Cochise Oncology",
name: siteName,
url: fullUrl,
logo: {
"@type": "ImageObject",

View File

@ -0,0 +1,157 @@
import { BeforeFooterBlock } from "@/components/Blocks/BeforeFooter";
import { BlogCardItemSkeleton } from "@/components/Blogs/BlogCardItem";
import Blogs from "@/components/Blogs/Blogs";
import HeroOther from "@/components/HeroOther";
import { fetchBlogTagBySlug } from "@/services/payload/blog";
import { getDefaultMetadata } from "@/utils/metadata";
import { sanitizePageNumber } from "@/utils/sanitize";
import { Metadata } from "next";
import { headers } from "next/headers";
import { notFound } from "next/navigation";
import { Suspense } from "react";
export async function generateMetadata({ params }: { params: Promise<{ path: string[0] }> }): Promise<Metadata> {
const path = (await params)?.path;
const paramsTag = path?.[0] ?? "";
const metadata = await getDefaultMetadata();
const tag = await fetchBlogTagBySlug(paramsTag);
if (!!tag?.data) {
metadata.title = `${tag.data.name} Archives - ${metadata.openGraph?.siteName}`;
}
return metadata;
}
export default async function TagPage({
params,
searchParams,
}: {
params?: Promise<{ path: string[] }>;
searchParams?: Promise<{ page?: string; s?: string }>;
}) {
const path = (await params)?.path;
const paramsTag = path?.[0] ?? "";
const paramsPage = path?.[2] ?? "";
const paramsSearch = (await searchParams)?.s;
const page = sanitizePageNumber(paramsPage);
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 tag = await fetchBlogTagBySlug(paramsTag);
if (!tag) {
notFound();
}
const jsonLd = {
"@context": "https://schema.org",
"@graph": [
{
"@type": "CollectionPage",
"@id": fullUrl,
url: fullUrl,
name: `${tag.data.name} - ${siteName}`,
isPartOf: {
"@id": `${mainUrl}/#website`,
},
primaryImageOfPage: {
"@id": `${fullUrl}#primaryimage`,
},
image: {
"@id": `${fullUrl}#primaryimage`,
},
inLanguage: "en-US",
},
{
"@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`,
caption: siteName,
},
image: {
"@id": `${mainUrl}/#/schema/logo/image/`,
},
},
],
};
return (
<>
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
<HeroOther title={`Tag: ${tag.data.name}`} />
<section className="page-section" id="blog">
<div className="w-full max-w-7xl mx-auto px-4 md:px-8">
<form action={`/tag/${paramsTag}`} 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="tags" tagId={tag.data.id} 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

@ -33,7 +33,6 @@
body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
}
@layer components {
@ -63,6 +62,10 @@ body {
.ext-btn-shadow-sm-primary8 {
@apply bg-extColorPrimary8 text-white hover:text-white hover:bg-extColorPrimary6 transition-colors;
}
.shadow-nav {
@apply !shadow-[0px_0px_10px_0px_rgba(0,0,0,0.24)];
}
}
.bg-gradient {

View File

@ -47,8 +47,8 @@ export function BeforeFooterBlock({ title, description, buttonText, showLinier =
)}
<div className="max-w-4xl mx-auto mt-12">
<h2 className="text-4xl font-semibold mb-4">{title ?? placeholderTitle}</h2>
<p className="text-lg leading-relaxed mb-6">{description ?? placeholderDescription}</p>
<h4 className="text-4xl font-medium mb-3">{title ?? placeholderTitle}</h4>
<span className="text-lg leading-relaxed mb-6">{description ?? placeholderDescription}</span>
<div className="pt-5">
<Link
href="/contact"

View File

@ -7,7 +7,7 @@ export function ContentBlock(props: any) {
<div className="container relative">
<div className="row">
{/* Content */}
<div className="col-md-10 offset-md-1 col-lg-8 offset-lg-2">
<div className="col-md-10 offset-md-1 col-lg-10 offset-lg-1">
{/* Post */}
<div className="blog-item mb-10">
<div className="blog-item-body">

View File

@ -100,7 +100,7 @@ export default async function BlogDetail({ slug }: BlogDetailProps) {
<div className="container relative">
<div className="row">
{/* Content */}
<div className="col-md-10 offset-md-1 col-lg-8 offset-lg-2">
<div className="col-md-10 offset-md-1 col-lg-10 offset-lg-1">
{/* Post */}
<div className="blog-item mb-80 mb-xs-40">
<div className="blog-item-body">
@ -133,7 +133,7 @@ export function BlogDetailContentSkeleton() {
<div className="container relative">
<div className="row">
{/* Content */}
<div className="col-md-10 offset-md-1 col-lg-8 offset-lg-2">
<div className="col-md-10 offset-md-1 col-lg-10 offset-lg-1">
{/* Post */}
<div className="blog-item mb-80 mb-xs-40">
<div className="blog-item-body">

View File

@ -3,14 +3,37 @@ import { fetchBlog } from "@/services/payload/blog";
import { BlogCardItem } from "./BlogCardItem";
import { sanitizeBlogContentIntoStringPreview } from "@/utils/sanitize";
export interface BlogsProps {
preset?: "blogs" | "categories";
export type BlogsProps = {
page: number;
search?: string;
}
} & (
| {
preset?: "blogs";
}
| {
preset?: "categories";
categoryId: number;
}
| {
preset?: "tags";
tagId: number;
}
);
export default async function Blogs({ page, search, preset = "blogs" }: BlogsProps) {
const data = await fetchBlog({ page, search });
export default async function Blogs({ page, search, ...params }: BlogsProps) {
const fetchBlogParams: Parameters<typeof fetchBlog>[0] = {
page,
search,
};
if (params.preset === "categories") {
fetchBlogParams.categoryId = params.categoryId;
}
if (params.preset === "tags") {
fetchBlogParams.tagId = params.tagId;
}
const data = await fetchBlog(fetchBlogParams);
if (!data?.totalDocs) return <></>;
@ -42,7 +65,7 @@ export default async function Blogs({ page, search, preset = "blogs" }: BlogsPro
hasNextPage={data.hasNextPage}
hasPreviousPage={data.hasPrevPage}
totalPages={data.totalPages}
usePathParams={preset === "blogs" ? false : true}
usePathParams={params.preset === "blogs" ? false : true}
/>
</div>
)}

View File

@ -1,4 +1,6 @@
import { fetchContact } from "@/services/payload/contact";
import { FaClock, FaFax, FaMapMarkerAlt } from "react-icons/fa";
import { GiRotaryPhone } from "react-icons/gi";
import { Suspense } from "react";
export default function Contact() {
@ -32,21 +34,7 @@ async function ContactWithData() {
{/* Contact Item */}
<div className="col-sm-6 mb-xs-30 d-flex align-items-stretch">
<div className="alt-features-item border-left mt-0 wow fadeScaleIn" data-wow-delay=".3s">
<div className="alt-features-icon">
<svg
width={24}
height={24}
viewBox="0 0 24 24"
fill="currentColor"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fillRule="evenodd"
clipRule="evenodd"
>
<path d="M12 2C8.134 2 5 5.134 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.866-3.134-7-7-7zm0 9.5c-1.381 0-2.5-1.119-2.5-2.5s1.119-2.5 2.5-2.5 2.5 1.119 2.5 2.5-1.119 2.5-2.5 2.5z" />
</svg>
</div>
<FaMapMarkerAlt className="text-4xl m-5" />
<h4 className="alt-features-title">Location</h4>
<div className="alt-features-descr clearlinks">
<div>
@ -61,20 +49,7 @@ async function ContactWithData() {
{/* Contact Item */}
<div className="col-sm-6 d-flex align-items-stretch">
<div className="alt-features-item border-left mt-0 wow fadeScaleIn" data-wow-delay=".5s">
<div className="alt-features-icon">
<svg
width={24}
height={24}
viewBox="0 0 24 24"
fill="currentColor"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fillRule="evenodd"
clipRule="evenodd"
>
<path d="M17 2H7C5.9 2 5 2.9 5 4v16c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 17H7V5h10v14zM12 16c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1z" />
</svg>
</div>
<GiRotaryPhone className="text-4xl m-5" />
<h4 className="alt-features-title">Phone</h4>
<div className="alt-features-descr clearlinks">
<div>
@ -89,20 +64,7 @@ async function ContactWithData() {
{/* Contact Item */}
<div className="col-sm-6 mb-xs-30 d-flex align-items-stretch">
<div className="alt-features-item border-left mt-0 wow fadeScaleIn" data-wow-delay=".3s">
<div className="alt-features-icon">
<svg
width={24}
height={24}
viewBox="0 0 24 24"
fill="currentColor"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fillRule="evenodd"
clipRule="evenodd"
>
<path d="M12 2C6.486 2 2 6.486 2 12s4.486 10 10 10 10-4.486 10-10S17.514 2 12 2zm0 18c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8zm1-8h4v2h-6V7h2v5z" />
</svg>
</div>
<FaClock className="text-4xl m-5" />
<h4 className="alt-features-title">Hours</h4>
<div className="alt-features-descr clearlinks">
{Array.isArray(contact?.hours) &&
@ -118,20 +80,7 @@ async function ContactWithData() {
{/* Contact Item */}
<div className="col-sm-6 mb-xs-30 d-flex align-items-stretch">
<div className="alt-features-item border-left mt-0 wow fadeScaleIn" data-wow-delay=".3s">
<div className="alt-features-icon">
<svg
width={24}
height={24}
viewBox="0 0 24 24"
fill="currentColor"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fillRule="evenodd"
clipRule="evenodd"
>
<path d="M12 2C6.486 2 2 6.486 2 12s4.486 10 10 10 10-4.486 10-10S17.514 2 12 2zm0 18c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8zm1-12h-2v2h2V8zm0 4h-2v6h2v-6zm-4-2H7v2h2v-2zm6 0h-2v2h2v-2z" />
</svg>
</div>
<FaFax className="text-4xl m-5" />
<h4 className="alt-features-title">Fax</h4>
<div className="alt-features-descr clearlinks">
<div>

View File

@ -11,8 +11,8 @@ 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} />
<span className="social-nav flex text-2xl text-white space-x-3 lg:space-x-1">
<Image src="/assets/icons/phone.png" alt="Phone" width={30} height={25} />
<b className="text-[17px]">(520) 803-6644</b>
</span>
),
@ -20,7 +20,7 @@ const shareIcons: Record<string, any> = {
facebook: {
link: "https://www.facebook.com/p/Cochise-Oncology-61556262839823",
dom: (
<span className="social-nav flex gap-3 text-2xl lg:text-white">
<span className="social-nav flex text-2xl text-white space-x-3 lg:space-x-1">
<FaFacebook size={25} />
<b className="text-[17px] lg:hidden">Facebook</b>
</span>
@ -29,7 +29,7 @@ const shareIcons: Record<string, any> = {
linkedin: {
link: "https://linkedin.com/company/cochise-oncology",
dom: (
<span className="social-nav flex gap-3 text-2xl lg:text-white">
<span className="social-nav flex text-2xl text-white space-x-3 lg:space-x-1">
<FaLinkedin size={25} />
<b className="text-[17px] lg:hidden">Linkedin</b>
</span>
@ -41,23 +41,13 @@ export default function Header({ links }: { links: typeof navMenuData }) {
return (
<nav className="main-nav dark stick-fixed wow-menubar wch-unset relative">
<div className="main-nav-sub full-wrapper">
{/* Logo (* Add your text or image to the link tag. Use SVG or PNG image format.
If you use a PNG logo image, the image resolution must be equal 200% of the visible logo
image size for support of retina screens. See details in the template documentation. *) */}
<div className="nav-logo-wrap local-scroll">
<div className="nav-logo-wrap local-scroll lg:hidden">
<Link href={`/`} className="logo">
<Image
src="/assets/images/demo-slick/logo-dark.webp"
alt="Your Company Logo"
width={105}
height={34}
className="light-mode-logo"
/>
<Image
src="/assets/images/demo-slick/logo-dark.webp"
alt="Your Company Logo"
width={405}
height={34}
alt="Company Logo"
width={250}
height={150}
className="dark-mode-logo"
/>
</Link>
@ -70,30 +60,41 @@ export default function Header({ links }: { links: typeof navMenuData }) {
{/* Main Menu */}
<div className="inner-nav desktop-nav">
<ul className="clearlist local-scroll">
{/* Item With Sub */}
<li className="!hidden lg:!block">
<Link href={`/`} className="logo">
<Image
src="/assets/images/demo-slick/logo-dark.webp"
alt="Company Logo"
width={165}
height={100}
className="dark-mode-logo"
/>
</Link>
</li>
<HeaderNav links={links} />
{/* End Item With Sub */}
</ul>
<ul className="items-end clearlist flex flex-col justify-center h-full">
<li>
<div className="flex flex-col items-center h-full w-full content-center justify-center gap-2">
<div className="flex justify-center">
<a
href="/contact"
className="bg-extColorPrimary6 hover:bg-extColorPrimary8 px-3 py-2 rounded-full text-white font-semibold hover:cursor-pointer hover:scale-[1.15] mt-3 lg:mt-0 transition-all duration-300"
className="bg-extColorPrimary6 hover:bg-extColorPrimary8 px-4 py-2 rounded-full text-white font-semibold hover:cursor-pointer hover:scale-[1.15] mt-3 lg:mt-0 transition-all duration-300"
>
REQUEST CONSULTATION
</a>
</div>
<div className="flex justify-between w-full flex-col lg:flex-row">
{Object.keys(shareIcons).map((k, idx) => {
return (
<li key={idx} className="!p-0 !m-0">
<div key={idx} className="!p-0 !m-0">
<a className="cursor-pointer text-white" href={shareIcons[k].link} target="_blank">
{shareIcons[k].dom}
</a>
</li>
</div>
);
})}
</div>
</div>
</li>
</ul>
</div>

View File

@ -7,9 +7,13 @@ import { scrollToElement } from "@/utils/scrollToElement";
import { toggleMobileMenu } from "@/utils/toggleMobileMenu";
import Link from "next/link";
import { Fragment, useEffect, useRef, useState } from "react";
import { FaCaretDown, FaCaretRight } from "react-icons/fa";
export default function HeaderNav({ links, animateY = false }: { links: typeof navMenuData; animateY?: boolean }) {
const [hoveredNavItem, setHoveredNavItem] = useState("");
const [hoveredSubNavItem, setHoveredSubNavItem] = useState("");
const [isDropdownOpen, setIsDropdownOpen] = useState([""]);
const [isSubDropdownOpen, setIsSubDropdownOpen] = useState("");
const dropdownRef = useRef(null);
const toggleDropdown = (section: string[]) => {
@ -40,7 +44,10 @@ export default function HeaderNav({ links, animateY = false }: { links: typeof n
<Fragment key={index}>
<li>
{!Array.isArray(link?.child) && (
<Link href={link.href}>
<Link
href={link.href}
className="!text-white hover:!text-extColorPrimary3 !text-[20px] !transition-all !duration-500"
>
{animateY ? (
<span className="btn-animate-y">
<span className="btn-animate-y-1">{link.text}</span>
@ -55,35 +62,54 @@ export default function HeaderNav({ links, animateY = false }: { links: typeof n
)}
{Array.isArray(link?.child) && (
<>
<Link href={link?.href ?? "#"} className="mn-has-sub flex justify-between">
{link.text} <i className="mi-chevron-down" onClick={() => toggleDropdown([link.text])} />
<div onMouseOver={() => setHoveredNavItem(`${index}`)} onMouseLeave={() => setHoveredNavItem("")}>
<Link
href={link?.href ?? "#"}
className="mn-has-sub !flex justify-between items-center !text-white hover:!text-extColorPrimary3 !text-[20px] !transition-all !duration-500"
onClick={() => toggleDropdown([link.text])}
>
{link.text} <FaCaretDown className="ml-2" />
</Link>
<ul
className={`mn-sub to-right ${isDropdownOpen.includes(link.text) && "open"} !bg-extColorPrimary8`}
className={`font-fontSecondary mn-sub to-right mt-2 ${isDropdownOpen.includes(link.text) && "open"} !bg-extColorPrimary8 ${hoveredNavItem.includes(`${index}`) ? "lg:visible lg:opacity-100 lg:z-10" : "lg:invisible lg:opacity-0"} !transition-all !duration-300 lg:shadow-nav`}
ref={dropdownRef}
>
{link.child.map((subLink: any, subLinkIdx: number) => (
<li key={subLinkIdx}>
<li
key={subLinkIdx}
onMouseOver={() => setHoveredSubNavItem(`${index}-${subLinkIdx}`)}
onMouseLeave={() => setHoveredSubNavItem("")}
>
{!Array.isArray(subLink?.child) && (
<Link href={subLink?.href} onClick={() => toggleMobileMenu()}>
<Link
href={subLink?.href}
onClick={() => toggleMobileMenu()}
className="!text-[14px] !text-white hover:!bg-extColorPrimary5 hover:!text-extColorPrimary3 !transition-all !duration-500"
>
{subLink?.text}
</Link>
)}
{Array.isArray(subLink?.child) && (
<>
<Link href={subLink?.href ?? "#"} className="mn-has-sub !flex !justify-between">
<span>{subLink.text}</span>
<i className="mi-chevron-down" onClick={() => toggleDropdown([link.text, subLink.text])} />
<Link
href={subLink?.href ?? "#"}
className="mn-has-sub !flex !justify-between !items-center !text-white hover:!bg-extColorPrimary5 hover:!text-extColorPrimary3 transition-all duration-500"
onClick={() => setIsSubDropdownOpen(`${index}-${subLinkIdx}`)}
>
<span className="!text-[14px]">{subLink.text}</span>
<FaCaretRight className="ml-2" />
</Link>
<ul
className={`mn-sub to-right ${isDropdownOpen.includes(subLink.text) && "open"} !bg-extColorPrimary8`}
className={`mn-sub to-right ${isSubDropdownOpen.includes(`${index}-${subLinkIdx}`) && "open"} ${hoveredSubNavItem.includes(`${index}-${subLinkIdx}`) ? "lg:visible lg:opacity-100 lg:z-10" : "lg:invisible lg:opacity-0"} !bg-extColorPrimary8 !transition-all !duration-300 lg:shadow-nav`}
>
{subLink.child.map((subLink2: any, subLinkIdx2: number) => (
<li key={subLinkIdx2}>
<Link href={subLink2?.href} onClick={() => toggleMobileMenu()}>
<Link
href={subLink2?.href}
className="!text-[14px] !items-center !text-white hover:!bg-extColorPrimary5 hover:!text-extColorPrimary3 transition-all duration-500"
onClick={() => toggleMobileMenu()}
>
{subLink2?.text}
</Link>
</li>
@ -94,12 +120,12 @@ export default function HeaderNav({ links, animateY = false }: { links: typeof n
</li>
))}
</ul>
</>
</div>
)}
</li>
{index != links.length - 1 && (
<div
className="text-white bg-white h-[1px] md:h-[25px] mx-2 opacity-[0.3]"
className="text-white bg-white h-[1px] lg:h-[25px] mx-2 opacity-[0.3]"
style={{ border: "1px solid white" }}
/>
)}

View File

@ -16,14 +16,14 @@ const shareIcons: Record<string, any> = {
export default function HeroOther({ title, img, createdAt, shareUrl }: HeroOtherProps) {
return (
<section className="page-section bg-dark-1 bg-gradient-gray-dark-1 light-content bg-scroll overflow-hidden !py-20 md:!pt-52 md:!pb-28 relative min-h-[48vh]">
<section className="page-section bg-dark-1 bg-gradient-gray-dark-1 light-content bg-scroll overflow-hidden !py-20 !pt-52 md:!pb-28 relative min-h-[48vh]">
{/* <!-- Background Shape --> */}
<div className="bg-shape-1 w-full h-full opacity-50 bg-contain">
<Image src={img || "/assets/images/hero-default.webp"} alt="" fill className="object-cover" />
</div>
{/* <!-- End Background Shape --> */}
<div className="container position-relative pt-sm-40 text-center">
<div className="container position-relative text-center">
<div className="row">
<div className="col-md-10 offset-md-1 col-lg-8 offset-lg-2">
<h1 className="text-5xl !font-medium mb-10 leading-[1.5]">{title ?? ""}</h1>

View File

@ -5,6 +5,7 @@ import { init_wow } from "@/utils/initWow";
import { parallaxMouseMovement, parallaxScroll } from "@/utils/parallax";
import { usePathname } from "next/navigation";
import { useEffect } from "react";
import { GoogleTagManager } from "@next/third-parties/google";
export default function InitialScript() {
const path = usePathname();
@ -36,5 +37,9 @@ export default function InitialScript() {
}
}, []);
return <></>;
return (
<>
<GoogleTagManager gtmId="GTM-XYZ" />
</>
);
}

View File

@ -28,8 +28,13 @@ export default function Pagination({
const searchParams = new URLSearchParams(url.search);
if (usePathParams) {
searchParams.set("page", `${page}`);
window.location.href = `${pathName}/page/${page}/?${searchParams}`;
let updatedPath = "";
if (pathName.includes("/page")) {
updatedPath = pathName.replace(/\/page\/\d+/, `/page/${page}`);
} else {
updatedPath = `${pathName}/page/${page}`;
}
window.location.href = `${updatedPath}?${searchParams}`;
} else {
searchParams.set("page", `${page}`);
window.location.href = `${pathName}/?${searchParams}`;

View File

@ -6,10 +6,65 @@
* and re-run `payload generate:types` to regenerate this file.
*/
/**
* Supported timezones in IANA format.
*
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "supportedTimezones".
*/
export type SupportedTimezones =
| 'Pacific/Midway'
| 'Pacific/Niue'
| 'Pacific/Honolulu'
| 'Pacific/Rarotonga'
| 'America/Anchorage'
| 'Pacific/Gambier'
| 'America/Los_Angeles'
| 'America/Tijuana'
| 'America/Denver'
| 'America/Phoenix'
| 'America/Chicago'
| 'America/Guatemala'
| 'America/New_York'
| 'America/Bogota'
| 'America/Caracas'
| 'America/Santiago'
| 'America/Buenos_Aires'
| 'America/Sao_Paulo'
| 'Atlantic/South_Georgia'
| 'Atlantic/Azores'
| 'Atlantic/Cape_Verde'
| 'Europe/London'
| 'Europe/Berlin'
| 'Africa/Lagos'
| 'Europe/Athens'
| 'Africa/Cairo'
| 'Europe/Moscow'
| 'Asia/Riyadh'
| 'Asia/Dubai'
| 'Asia/Baku'
| 'Asia/Karachi'
| 'Asia/Tashkent'
| 'Asia/Calcutta'
| 'Asia/Dhaka'
| 'Asia/Almaty'
| 'Asia/Jakarta'
| 'Asia/Bangkok'
| 'Asia/Shanghai'
| 'Asia/Singapore'
| 'Asia/Tokyo'
| 'Asia/Seoul'
| 'Australia/Sydney'
| 'Pacific/Guam'
| 'Pacific/Noumea'
| 'Pacific/Auckland'
| 'Pacific/Fiji';
export interface Config {
auth: {
users: UserAuthOperations;
};
blocks: {};
collections: {
users: User;
media: Media;

View File

@ -5,10 +5,11 @@ import { getPayload, Where } from "payload";
type FetchBlogParams = {
page?: number;
search?: string;
categorySlug?: string;
categoryId?: number;
tagId?: number;
};
export async function fetchBlog({ page, search = "", categorySlug = "" }: FetchBlogParams = {}) {
export async function fetchBlog({ page, search = "", categoryId, tagId }: FetchBlogParams = {}) {
const payload = await getPayload({ config: payloadConfig });
const queryCondition: Where = {};
@ -17,9 +18,14 @@ export async function fetchBlog({ page, search = "", categorySlug = "" }: FetchB
contains: search,
};
}
if (!!categorySlug) {
if (!!categoryId) {
queryCondition["categories"] = {
equals: 9,
equals: categoryId,
};
}
if (!!tagId) {
queryCondition["tags"] = {
equals: tagId,
};
}
@ -70,3 +76,35 @@ export async function fetchBlogDetail(slug: string | undefined) {
imgUrl,
};
}
export async function fetchBlogCategoryBySlug(slug: string) {
const payload = await getPayload({ config: payloadConfig });
const category = await payload.find({
collection: "blogCategories",
where: {
slug: { equals: slug },
},
});
if (!category?.docs?.[0]) return null;
return {
data: category.docs[0],
};
}
export async function fetchBlogTagBySlug(slug: string) {
const payload = await getPayload({ config: payloadConfig });
const tag = await payload.find({
collection: "blogTags",
where: {
slug: { equals: slug },
},
});
if (!tag?.docs?.[0]) return null;
return {
data: tag.docs[0],
};
}

View File

@ -21,7 +21,6 @@ export async function getDefaultMetadata(): Promise<Metadata> {
},
openGraph: {
siteName,
title: siteName,
description: metaDesc,
type: "website",
locale: "en_US",

View File

@ -4,7 +4,7 @@ export const toggleMobileMenu = () => {
const desktop_nav = document.querySelector(".desktop-nav");
if (desktop_nav.classList.contains("js-opened")) {
desktop_nav.style.maxHeight = "0px";
desktop_nav.style.maxHeight = "";
desktop_nav.classList.remove("js-opened");
mobile_nav.classList.remove("active");
} else {
@ -18,7 +18,7 @@ export const closeMobileMenu = () => {
const desktop_nav = document.querySelector(".desktop-nav");
if (desktop_nav.classList.contains("js-opened")) {
desktop_nav.style.maxHeight = "0px";
desktop_nav.style.maxHeight = "";
desktop_nav.classList.remove("js-opened");
mobile_nav.classList.remove("active");
}

View File

@ -8,6 +8,10 @@ export default {
],
theme: {
extend: {
fontFamily: {
fontMain: ["var(--font-noto-sans)"],
fontSecondary: ["var(--font-roboto)"],
},
colors: {
background: "var(--background)",
foreground: "var(--foreground)",

1485
yarn.lock

File diff suppressed because it is too large Load Diff