From 3a08a76b3feed05f4b176f25e3dc798f3e5095be Mon Sep 17 00:00:00 2001 From: RizqiSyahrendra Date: Fri, 28 Feb 2025 19:07:37 +0700 Subject: [PATCH] fix: layout and footer, - restructure layout and footer - footer (contact) data from payload --- src/app/(main)/layout.tsx | 62 ++++++---------------- src/collections/Media.ts | 1 + src/collections/Pages.ts | 4 ++ src/collections/Teams.ts | 1 + src/collections/Users.ts | 1 + src/components/Footer.tsx | 89 +++++++++++++++++++++++++------- src/components/Header.tsx | 2 + src/components/InitialScript.tsx | 40 ++++++++++++++ src/components/ScrollToTop.tsx | 14 +++++ src/globals/Contacts.ts | 74 ++++++++++++++++++++++++++ src/globals/GoogleReviews.ts | 1 + src/payload-types.ts | 56 ++++++++++++++++++++ src/payload.config.ts | 11 +++- src/services/payload/contact.ts | 25 +++++++++ 14 files changed, 316 insertions(+), 65 deletions(-) create mode 100644 src/components/InitialScript.tsx create mode 100644 src/components/ScrollToTop.tsx create mode 100644 src/globals/Contacts.ts create mode 100644 src/services/payload/contact.ts diff --git a/src/app/(main)/layout.tsx b/src/app/(main)/layout.tsx index b984640..75bed91 100644 --- a/src/app/(main)/layout.tsx +++ b/src/app/(main)/layout.tsx @@ -1,59 +1,29 @@ -"use client"; -import { usePathname } from "next/navigation"; -import { useEffect } from "react"; -import { init_wow } from "@/utils/initWow"; -import { parallaxMouseMovement, parallaxScroll } from "@/utils/parallax"; -import { headerChangeOnScroll } from "@/utils/changeHeaderOnScroll"; -import Header from "@/components/Header"; -import Footer from "@/components/Footer"; -import { navMenuData } from "@/data/menu"; import "@/app/globals.css"; -import "swiper/css"; -import "jarallax/dist/jarallax.min.css"; -import "swiper/css/effect-fade"; -import "react-modal-video/css/modal-video.css"; -import "photoswipe/dist/photoswipe.css"; -import "tippy.js/dist/tippy.css"; +import Footer, { FooterSkeleton } from "@/components/Footer"; +import Header from "@/components/Header"; +import InitialScript from "@/components/InitialScript"; +import { navMenuData } from "@/data/menu"; + import "@public/assets/css/styles.css"; +import "jarallax/dist/jarallax.min.css"; import { Roboto } from "next/font/google"; +import "photoswipe/dist/photoswipe.css"; +import { Suspense } from "react"; +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"] }); + export default function MainLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { - const path = usePathname(); - - useEffect(() => { - init_wow(); - parallaxMouseMovement(); - const mainNav = document.querySelector(".main-nav"); - if (mainNav?.classList.contains("transparent")) { - mainNav.classList.add("js-transparent"); - } else if (!mainNav?.classList?.contains("dark")) { - mainNav?.classList.add("js-no-transparent-white"); - } - - window.addEventListener("scroll", headerChangeOnScroll); - parallaxScroll(); - return () => { - window.removeEventListener("scroll", headerChangeOnScroll); - }; - }, [path]); - - useEffect(() => { - if (typeof window !== "undefined") { - // Import the script only on the client side - // @ts-ignore - import("bootstrap/dist/js/bootstrap.esm").then(() => { - // Module is imported, you can access any exported functionality if - }); - } - }, []); - return ( +
@@ -61,7 +31,9 @@ export default function MainLayout({
{children}
-
+ }> +
+
diff --git a/src/collections/Media.ts b/src/collections/Media.ts index cbaf14d..2a5a07f 100644 --- a/src/collections/Media.ts +++ b/src/collections/Media.ts @@ -15,5 +15,6 @@ export const Media: CollectionConfig = { upload: true, admin: { hideAPIURL: true, + group: "General", }, }; diff --git a/src/collections/Pages.ts b/src/collections/Pages.ts index 1287427..b5d4bff 100644 --- a/src/collections/Pages.ts +++ b/src/collections/Pages.ts @@ -72,4 +72,8 @@ export const Pages: CollectionConfig = { ], }, ], + admin: { + hideAPIURL: true, + group: "General", + }, }; diff --git a/src/collections/Teams.ts b/src/collections/Teams.ts index 68636e2..f3748eb 100644 --- a/src/collections/Teams.ts +++ b/src/collections/Teams.ts @@ -30,5 +30,6 @@ export const Teams: CollectionConfig = { admin: { hideAPIURL: true, useAsTitle: "name", + group: "General", }, }; diff --git a/src/collections/Users.ts b/src/collections/Users.ts index c59e838..773550d 100644 --- a/src/collections/Users.ts +++ b/src/collections/Users.ts @@ -5,6 +5,7 @@ export const Users: CollectionConfig = { admin: { useAsTitle: "email", hideAPIURL: true, + group: "Users", }, auth: true, fields: [ diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index 10bbab6..a518cbd 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -1,13 +1,10 @@ -"use client"; -import React from "react"; import Image from "next/image"; -import { FaPhone, FaFax, FaFacebook, FaMapMarkerAlt, FaClock } from "react-icons/fa"; +import { FaClock, FaFacebook, FaFax, FaMapMarkerAlt, FaPhone } from "react-icons/fa"; +import ScrollToTop from "./ScrollToTop"; +import { fetchContact } from "@/services/payload/contact"; -export default function Footer() { - const scrollToTop = (event: React.MouseEvent) => { - event.preventDefault(); - window.scrollTo({ top: 0, behavior: "smooth" }); - }; +export default async function Footer() { + const contact = await fetchContact(); return (
- 5151 E HIGHWAY 90 + {contact?.location?.street ?? ""}
- Sierra Vista, Arizona 85635 + {contact?.fullLocation ?? ""}
  • - (520) 803-6644 + {contact?.phone ?? ""}
  • - Fax: (520) 459-3193 + Fax: {contact?.fax ?? ""}
  • Business Hours

    -
    - - Monday - Friday: 8am - 5pm -
    + {contact?.hours?.map?.((hour) => ( +
    + + {hour?.hour ?? ""} +
    + )) ?? []}
    -
    - Back to Top ↑ + +
    +
    + ); +} + +export function FooterSkeleton() { + return ( +
    +
    +
    + Cochise Oncology Logo +

    © {new Date().getFullYear()} All Rights Reserved

    +
    + +
    +

    Contact Us

    +
      +
    • + +
      +
      +
    • +
    • + +
      +
      +
    • +
    • + +
      +
      +
    • +
    • + +
      +
      +
    • +
    +
    + +
    +

    Business Hours

    +
    + +
    +
    +
    diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 01e4cd2..f555d49 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,3 +1,5 @@ +"use client"; + import HeaderNav from "@/components/HeaderNav"; import { navMenuData } from "@/data/menu"; import { toggleMobileMenu } from "@/utils/toggleMobileMenu"; diff --git a/src/components/InitialScript.tsx b/src/components/InitialScript.tsx new file mode 100644 index 0000000..ee97d94 --- /dev/null +++ b/src/components/InitialScript.tsx @@ -0,0 +1,40 @@ +"use client"; + +import { headerChangeOnScroll } from "@/utils/changeHeaderOnScroll"; +import { init_wow } from "@/utils/initWow"; +import { parallaxMouseMovement, parallaxScroll } from "@/utils/parallax"; +import { usePathname } from "next/navigation"; +import { useEffect } from "react"; + +export default function InitialScript() { + const path = usePathname(); + + useEffect(() => { + init_wow(); + parallaxMouseMovement(); + const mainNav = document.querySelector(".main-nav"); + if (mainNav?.classList.contains("transparent")) { + mainNav.classList.add("js-transparent"); + } else if (!mainNav?.classList?.contains("dark")) { + mainNav?.classList.add("js-no-transparent-white"); + } + + window.addEventListener("scroll", headerChangeOnScroll); + parallaxScroll(); + return () => { + window.removeEventListener("scroll", headerChangeOnScroll); + }; + }, [path]); + + useEffect(() => { + if (typeof window !== "undefined") { + // Import the script only on the client side + // @ts-ignore + import("bootstrap/dist/js/bootstrap.esm").then(() => { + // Module is imported, you can access any exported functionality if + }); + } + }, []); + + return <>; +} diff --git a/src/components/ScrollToTop.tsx b/src/components/ScrollToTop.tsx new file mode 100644 index 0000000..2d6565a --- /dev/null +++ b/src/components/ScrollToTop.tsx @@ -0,0 +1,14 @@ +"use client"; + +export default function ScrollToTop() { + const scrollToTop = (event: React.MouseEvent) => { + event.preventDefault(); + window.scrollTo({ top: 0, behavior: "smooth" }); + }; + + return ( +
    + Back to Top ↑ +
    + ); +} diff --git a/src/globals/Contacts.ts b/src/globals/Contacts.ts new file mode 100644 index 0000000..e103044 --- /dev/null +++ b/src/globals/Contacts.ts @@ -0,0 +1,74 @@ +import type { GlobalConfig } from "payload"; + +export const Contacts: GlobalConfig = { + slug: "contacts", + fields: [ + { + name: "location", + type: "group", + fields: [ + { + name: "street", + label: "Street", + type: "text", + }, + { + name: "city", + label: "City", + type: "text", + }, + { + name: "state", + label: "State", + type: "text", + }, + { + name: "postcode", + label: "Postcode", + type: "text", + }, + { + name: "href", + label: "Google Map Link", + type: "text", + }, + { + name: "iframeSrc", + label: "Google Map Iframe Source", + type: "text", + }, + ], + }, + { + name: "phone", + label: "Phone Number", + type: "text", + }, + { + name: "fax", + label: "Fax", + type: "text", + }, + { + name: "facebook", + label: "Facebook Link", + type: "text", + }, + { + name: "hours", + label: "Business Hours", + type: "array", + minRows: 1, + fields: [ + { + name: "hour", + type: "text", + }, + ], + }, + ], + admin: { + hideAPIURL: true, + group: "General", + }, +}; diff --git a/src/globals/GoogleReviews.ts b/src/globals/GoogleReviews.ts index f8d19c9..6122b10 100644 --- a/src/globals/GoogleReviews.ts +++ b/src/globals/GoogleReviews.ts @@ -38,5 +38,6 @@ export const GoogleReviews: GlobalConfig = { ], admin: { hideAPIURL: true, + group: "General", }, }; diff --git a/src/payload-types.ts b/src/payload-types.ts index 3c9ca6a..78e2779 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -44,9 +44,11 @@ export interface Config { }; globals: { 'google-reviews': GoogleReview; + contacts: Contact; }; globalsSelect: { 'google-reviews': GoogleReviewsSelect | GoogleReviewsSelect; + contacts: ContactsSelect | ContactsSelect; }; locale: null; user: User & { @@ -926,6 +928,32 @@ export interface GoogleReview { updatedAt?: string | null; createdAt?: string | null; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "contacts". + */ +export interface Contact { + id: number; + location?: { + street?: string | null; + city?: string | null; + state?: string | null; + postcode?: string | null; + href?: string | null; + iframeSrc?: string | null; + }; + phone?: string | null; + fax?: string | null; + facebook?: string | null; + hours?: + | { + hour?: string | null; + 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". @@ -944,6 +972,34 @@ export interface GoogleReviewsSelect { createdAt?: T; globalType?: T; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "contacts_select". + */ +export interface ContactsSelect { + location?: + | T + | { + street?: T; + city?: T; + state?: T; + postcode?: T; + href?: T; + iframeSrc?: T; + }; + phone?: T; + fax?: T; + facebook?: T; + hours?: + | T + | { + hour?: T; + id?: T; + }; + updatedAt?: T; + createdAt?: T; + globalType?: T; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "auth". diff --git a/src/payload.config.ts b/src/payload.config.ts index d4c41bf..b400fda 100644 --- a/src/payload.config.ts +++ b/src/payload.config.ts @@ -25,6 +25,7 @@ import { LinkFeature, } from "@payloadcms/richtext-lexical"; import { GoogleReviews } from "@/globals/GoogleReviews"; +import { Contacts } from "./globals/Contacts"; const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); @@ -51,7 +52,7 @@ export default buildConfig({ theme: "dark", }, collections: [Users, Media, Blogs, Pages, Teams, BlogCategories, BlogTags], - globals: [GoogleReviews], + globals: [GoogleReviews, Contacts], secret: process.env.PAYLOAD_SECRET || "", typescript: { outputFile: path.resolve(dirname, "payload-types.ts"), @@ -111,6 +112,14 @@ export default buildConfig({ }, formOverrides: { fields: undefined, + admin: { + group: "General", + }, + }, + formSubmissionOverrides: { + admin: { + group: "General", + }, }, }), ], diff --git a/src/services/payload/contact.ts b/src/services/payload/contact.ts new file mode 100644 index 0000000..eff96ca --- /dev/null +++ b/src/services/payload/contact.ts @@ -0,0 +1,25 @@ +import payloadConfig from "@/payload.config"; +import { getPayload } from "payload"; + +export async function fetchContact() { + const payload = await getPayload({ config: payloadConfig }); + const dataQuery = await payload.findGlobal({ slug: "contacts" }); + + let fullLocationArr: string[] = []; + if (!!dataQuery?.location?.city) { + fullLocationArr.push(dataQuery.location.city); + } + if (!!dataQuery?.location?.state) { + fullLocationArr.push(dataQuery.location.state); + } + if (!!dataQuery?.location?.postcode) { + fullLocationArr.push(dataQuery.location.postcode); + } + + return !!dataQuery + ? { + ...dataQuery, + fullLocation: fullLocationArr.join(","), + } + : null; +}