From 8d280b868c7aebffcf157cc0a53c6359eeb3af36 Mon Sep 17 00:00:00 2001
From: Nikita <39565703+nicitaacom@users.noreply.github.com>
Date: Tue, 7 Nov 2023 13:47:50 +0100
Subject: [PATCH 1/7] ModalContainer renamed to ModalQueryContainer
---
.../ui/{ModalContainer.tsx => ModalQueryContainer.tsx} | 4 ++--
app/components/ui/Modals/AdminPanel/AdminPanelModal.tsx | 6 +++---
app/components/ui/Modals/AuthModal/AuthModal.tsx | 6 +++---
app/components/ui/Modals/CartModal/CartModal.tsx | 6 +++---
app/components/ui/Modals/ChangeLanguageModal.tsx | 6 +++---
app/components/ui/index.ts | 4 ++--
6 files changed, 16 insertions(+), 16 deletions(-)
rename app/components/ui/{ModalContainer.tsx => ModalQueryContainer.tsx} (95%)
diff --git a/app/components/ui/ModalContainer.tsx b/app/components/ui/ModalQueryContainer.tsx
similarity index 95%
rename from app/components/ui/ModalContainer.tsx
rename to app/components/ui/ModalQueryContainer.tsx
index ac810852..8166326d 100644
--- a/app/components/ui/ModalContainer.tsx
+++ b/app/components/ui/ModalQueryContainer.tsx
@@ -7,14 +7,14 @@ import { useSwipeable } from "react-swipeable"
import { twMerge } from "tailwind-merge"
import { AnimatePresence, motion } from "framer-motion"
-interface ModalContainerProps {
+interface ModalQueryContainerProps {
children: React.ReactNode
modalQuery: string
className?: string
isLoading?: boolean
}
-export function ModalContainer({ children, modalQuery, className, isLoading }: ModalContainerProps) {
+export function ModalQueryContainer({ children, modalQuery, className, isLoading }: ModalQueryContainerProps) {
const pathname = usePathname()
const router = useRouter()
const queryParams = useSearchParams()
diff --git a/app/components/ui/Modals/AdminPanel/AdminPanelModal.tsx b/app/components/ui/Modals/AdminPanel/AdminPanelModal.tsx
index 1ef257e5..66026177 100644
--- a/app/components/ui/Modals/AdminPanel/AdminPanelModal.tsx
+++ b/app/components/ui/Modals/AdminPanel/AdminPanelModal.tsx
@@ -10,7 +10,7 @@ import { RadioButton } from "@/components/ui"
import useUserStore from "@/store/user/userStore"
import { IDBProduct } from "@/interfaces/IDBProduct"
-import { ModalContainer } from "../../ModalContainer"
+import { ModalQueryContainer } from "../../ModalQueryContainer"
import { EditProductForm } from "./components/EditProductForm"
import { AddProductForm } from "./components/AddProductForm"
import { DeleteProductForm } from "./components/DeleteProductForm"
@@ -35,7 +35,7 @@ export function AdminPanelModal({ label, ownerProducts }: AdminPanelModalProps)
}, [])
return (
- }
-
+
)
}
diff --git a/app/components/ui/Modals/AuthModal/AuthModal.tsx b/app/components/ui/Modals/AuthModal/AuthModal.tsx
index 79303073..88d33b81 100644
--- a/app/components/ui/Modals/AuthModal/AuthModal.tsx
+++ b/app/components/ui/Modals/AuthModal/AuthModal.tsx
@@ -11,7 +11,7 @@ import { AuthError } from "@supabase/supabase-js"
import { FormInput } from "../../Inputs/Validation/FormInput"
import ContinueWithButton from "@/(auth)/components/ContinueWithButton"
-import { Button, Checkbox, ModalContainer } from "../.."
+import { Button, Checkbox, ModalQueryContainer } from "../.."
import { Timer } from "@/(auth)/components"
import useDarkMode from "@/store/ui/darkModeStore"
import useUserStore from "@/store/user/userStore"
@@ -280,7 +280,7 @@ export function AuthModal({ label }: AdminModalProps) {
}
return (
- Now change query params back to &variant=login :)
)}
-
+
)
}
diff --git a/app/components/ui/Modals/CartModal/CartModal.tsx b/app/components/ui/Modals/CartModal/CartModal.tsx
index 0b4da52f..a9dde1f2 100644
--- a/app/components/ui/Modals/CartModal/CartModal.tsx
+++ b/app/components/ui/Modals/CartModal/CartModal.tsx
@@ -15,11 +15,11 @@ import useToast from "@/store/ui/useToast"
import useCartStore from "@/store/user/cartStore"
import EmptyCart from "./EmptyCart"
import { formatCurrency } from "@/utils/currencyFormatter"
-import { ModalContainer } from "@/components/ui/ModalContainer"
//Are you sure in what - please use clear naming
import { Button, Slider } from "../.."
import { useAreYouSureClearCartModal } from "@/store/ui/areYouSureClearCartModal"
+import { ModalQueryContainer } from "../../ModalQueryContainer"
interface CartModalProps {
label: string
@@ -241,7 +241,7 @@ export function CartModal({ label }: CartModalProps) {
}
return (
-
@@ -399,6 +399,6 @@ export function CartModal({ label }: CartModalProps) {
)}
-
+
)
}
diff --git a/app/components/ui/Modals/ChangeLanguageModal.tsx b/app/components/ui/Modals/ChangeLanguageModal.tsx
index 66663fdf..d3dbf60c 100644
--- a/app/components/ui/Modals/ChangeLanguageModal.tsx
+++ b/app/components/ui/Modals/ChangeLanguageModal.tsx
@@ -1,4 +1,4 @@
-import { ModalContainer } from "../ModalContainer"
+import { ModalQueryContainer } from "../ModalQueryContainer"
import { Button } from ".."
import { languages } from "@/constant/languages"
import Image from "next/image"
@@ -9,7 +9,7 @@ interface NameModalProps {
export function ChangeLanguageModal({ label }: NameModalProps) {
return (
-
+
{/* ANY CONTENT (to keep consistent keep {label})*/}
{label}
@@ -22,6 +22,6 @@ export function ChangeLanguageModal({ label }: NameModalProps) {
))}
-
+
)
}
diff --git a/app/components/ui/index.ts b/app/components/ui/index.ts
index 029746d6..285ceb29 100644
--- a/app/components/ui/index.ts
+++ b/app/components/ui/index.ts
@@ -1,9 +1,9 @@
import { Button } from "./Button"
import { DropdownContainer } from "./DropdownContainer"
import { DropdownItem } from "./DropdownItem"
-import { ModalContainer } from "./ModalContainer"
+import { ModalQueryContainer } from "./ModalQueryContainer"
import { Checkbox } from "./Checkbox"
import { Slider } from "./Slider"
import { RadioButton } from "./RadioButton"
-export { Button, DropdownContainer, DropdownItem, ModalContainer, Checkbox, Slider, RadioButton }
+export { Button, DropdownContainer, DropdownItem, ModalQueryContainer, Checkbox, Slider, RadioButton }
From 2a0085c6efe839a65ba920c9e2262775e2055863 Mon Sep 17 00:00:00 2001
From: Nikita <39565703+nicitaacom@users.noreply.github.com>
Date: Tue, 7 Nov 2023 15:25:50 +0100
Subject: [PATCH 2/7] overflow-hidden when modal isOpen improved
I know that I renamed and moved some file for some reasons and it makes hard for me to review PR - in future I will consider that and use 'stash changes' - then rename file - then 'restore'
---
app/(site)/page.tsx | 4 +-
app/(site)/search/page.tsx | 8 +-
app/components/Layout.tsx | 5 +-
app/components/Navbar/Navbar.tsx | 5 +-
.../Navbar/components/CtrlKBadge.tsx | 22 ++++
.../Navbar/components/NavbarSearch.tsx | 3 +-
.../Navbar/components/NavbarWrapper.tsx | 5 +-
app/components/ui/Inputs/SearchInput.tsx | 3 +
.../ui/Modals/AdminPanel/AdminPanelModal.tsx | 2 +-
.../ui/Modals/AreYouSureClearCartModal.tsx | 6 +-
.../Modals/AreYouSureDeleteProductModal.tsx | 16 ++-
.../ui/Modals/CartModal/CartModal.tsx | 4 +-
.../ui/Modals/ChangeLanguageModal.tsx | 6 +-
app/components/ui/Modals/CtrlKModal.tsx | 24 +++++
...ainer.tsx => AdminPanelModalContainer.tsx} | 0
.../AreYouSureModalContainer.tsx} | 19 ++--
.../ModalContainers/CartModalContainer.tsx | 6 +-
.../ChangeLanguageModalContainer.tsx | 3 +-
.../Modals/ModalContainers/ModalContainer.tsx | 96 +++++++++++++++++
.../ModalContainers/ModalQueryContainer.tsx | 101 ++++++++++++++++++
.../ui/Modals/ModalContainers/index.ts | 7 +-
app/components/ui/index.ts | 2 +-
app/globals.css | 7 +-
app/layout.tsx | 10 +-
app/providers/ModalsProvider.tsx | 5 +-
app/store/ui/ctrlKModal.ts | 15 +++
26 files changed, 342 insertions(+), 42 deletions(-)
create mode 100644 app/components/Navbar/components/CtrlKBadge.tsx
create mode 100644 app/components/ui/Modals/CtrlKModal.tsx
rename app/components/ui/Modals/ModalContainers/{AdminPanelContainer.tsx => AdminPanelModalContainer.tsx} (100%)
rename app/components/ui/{AreYouSureModal.tsx => Modals/ModalContainers/AreYouSureModalContainer.tsx} (91%)
create mode 100644 app/components/ui/Modals/ModalContainers/ModalContainer.tsx
create mode 100644 app/components/ui/Modals/ModalContainers/ModalQueryContainer.tsx
create mode 100644 app/store/ui/ctrlKModal.ts
diff --git a/app/(site)/page.tsx b/app/(site)/page.tsx
index 1248158e..0d0d594f 100644
--- a/app/(site)/page.tsx
+++ b/app/(site)/page.tsx
@@ -26,7 +26,9 @@ export default async function Home({ searchParams }: SearchProps) {
const entries = products.slice(start, end)
return (
-
+
diff --git a/app/(site)/search/page.tsx b/app/(site)/search/page.tsx
index 5f8544c0..57c004e5 100644
--- a/app/(site)/search/page.tsx
+++ b/app/(site)/search/page.tsx
@@ -9,8 +9,14 @@ interface SearchPageProps {
}
export function generateMetadata({ searchParams: { query } }: SearchPageProps): Metadata {
+ if (query === undefined) {
+ return {
+ title: `Search - 23_store`,
+ }
+ }
+
return {
- title: `Search ${query} - Flowmazon`,
+ title: `Search ${query} - 23_store`,
}
}
diff --git a/app/components/Layout.tsx b/app/components/Layout.tsx
index 240cab8b..b1fd9edf 100644
--- a/app/components/Layout.tsx
+++ b/app/components/Layout.tsx
@@ -32,8 +32,9 @@ export default function Layout({ children }: { children: React.ReactNode }) {
return (
+ className="flex flex-col w-full overflow-hidden min-h-screen
+ bg-background text-title
+ transition-colors duration-300">
{children}
{toast.isOpen && }
diff --git a/app/components/Navbar/Navbar.tsx b/app/components/Navbar/Navbar.tsx
index d8b579a6..bb793389 100644
--- a/app/components/Navbar/Navbar.tsx
+++ b/app/components/Navbar/Navbar.tsx
@@ -16,6 +16,7 @@ import {
OpenCartModalButton,
OpenUserMenuButton,
} from "./components"
+import { CtrlKBadge } from "./components/CtrlKBadge"
export default async function Navbar() {
const {
@@ -31,7 +32,9 @@ export default async function Navbar() {
{/* SEARCH + LANGUAGE */}
-
+
+
+
diff --git a/app/components/Navbar/components/CtrlKBadge.tsx b/app/components/Navbar/components/CtrlKBadge.tsx
new file mode 100644
index 00000000..0ab3a768
--- /dev/null
+++ b/app/components/Navbar/components/CtrlKBadge.tsx
@@ -0,0 +1,22 @@
+"use client"
+
+import { useCtrlKModal } from "@/store/ui/ctrlKModal"
+import { useEffect } from "react"
+
+export function CtrlKBadge() {
+ const ctrlKModal = useCtrlKModal()
+
+ useEffect(() => {
+ const handleKeydown = (e: KeyboardEvent) => {
+ if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
+ //to prevent focus state on browser searchbar
+ e.preventDefault()
+ ctrlKModal.toggle()
+ }
+ }
+ document.addEventListener("keydown", handleKeydown)
+ return () => document.removeEventListener("keydown", handleKeydown)
+ }, [ctrlKModal, ctrlKModal.toggle])
+
+ return
⌘+K
+}
diff --git a/app/components/Navbar/components/NavbarSearch.tsx b/app/components/Navbar/components/NavbarSearch.tsx
index f24a4631..944744cb 100644
--- a/app/components/Navbar/components/NavbarSearch.tsx
+++ b/app/components/Navbar/components/NavbarSearch.tsx
@@ -17,12 +17,13 @@ async function searchProducts(formData: FormData) {
}
}
-export function NavbarSearch() {
+export function NavbarSearch({ children: Children }: { children: React.ReactNode }) {
return (
)
}
diff --git a/app/components/ui/Modals/AdminPanel/AdminPanelModal.tsx b/app/components/ui/Modals/AdminPanel/AdminPanelModal.tsx
index 66026177..fecaabf4 100644
--- a/app/components/ui/Modals/AdminPanel/AdminPanelModal.tsx
+++ b/app/components/ui/Modals/AdminPanel/AdminPanelModal.tsx
@@ -10,7 +10,7 @@ import { RadioButton } from "@/components/ui"
import useUserStore from "@/store/user/userStore"
import { IDBProduct } from "@/interfaces/IDBProduct"
-import { ModalQueryContainer } from "../../ModalQueryContainer"
+import { ModalQueryContainer } from "../ModalContainers/ModalQueryContainer"
import { EditProductForm } from "./components/EditProductForm"
import { AddProductForm } from "./components/AddProductForm"
import { DeleteProductForm } from "./components/DeleteProductForm"
diff --git a/app/components/ui/Modals/AreYouSureClearCartModal.tsx b/app/components/ui/Modals/AreYouSureClearCartModal.tsx
index 4df801b2..f2cc987e 100644
--- a/app/components/ui/Modals/AreYouSureClearCartModal.tsx
+++ b/app/components/ui/Modals/AreYouSureClearCartModal.tsx
@@ -1,7 +1,7 @@
"use client"
import { useAreYouSureClearCartModal } from "@/store/ui/areYouSureClearCartModal"
-import { AreYouSureModal } from "../AreYouSureModal"
+import { AreYouSureModalContainer } from "./ModalContainers/AreYouSureModalContainer"
import useCartStore from "@/store/user/cartStore"
export function AreYouSureClearCartButton() {
@@ -10,13 +10,13 @@ export function AreYouSureClearCartButton() {
const cartStore = useCartStore()
return (
-
+ secondaryButtonLabel="Back">
)
}
diff --git a/app/components/ui/Modals/AreYouSureDeleteProductModal.tsx b/app/components/ui/Modals/AreYouSureDeleteProductModal.tsx
index c6c77667..4c769e4d 100644
--- a/app/components/ui/Modals/AreYouSureDeleteProductModal.tsx
+++ b/app/components/ui/Modals/AreYouSureDeleteProductModal.tsx
@@ -1,12 +1,12 @@
"use client"
-import { useState } from "react"
+import { useEffect, useState } from "react"
import { useRouter } from "next/navigation"
import { BiTrash } from "react-icons/bi"
import axios from "axios"
import { useAreYouSureDeleteProductModal } from "@/store/ui/areYouSureDeleteProductModal"
-import { AreYouSureModal } from "../AreYouSureModal"
+import { AreYouSureModalContainer } from "./ModalContainers/AreYouSureModalContainer"
import useCartStore from "@/store/user/cartStore"
export function AreYouSureDeleteProductModal() {
@@ -15,6 +15,16 @@ export function AreYouSureDeleteProductModal() {
const cartStore = useCartStore()
const { title, id } = useAreYouSureDeleteProductModal()
const [isLoading, setIsLoading] = useState(false)
+ const [isMounted, setIsMounted] = useState(false)
+
+ useEffect(() => {
+ setIsLoading(true)
+ }, [])
+ // to prevent SSR because if I render this modal I trigger useEffect
+ // that trigger document.addEventListener on esc (I need close ctrlK modal on esc press)
+ if (!isMounted) {
+ return null
+ }
async function deleteProduct() {
setIsLoading(true)
@@ -29,7 +39,7 @@ export function AreYouSureDeleteProductModal() {
}
return (
- {
router.push(`${location.origin}/payment?status=success`)
- console.log("txHash - ", txHash)
+ console.log(169, "You may use txHash as check QR code or payment identifier - ", txHash)
})
.catch((error: Error) => {
error.message.includes("MetaMask Tx Signature: User denied transaction signature.")
@@ -175,7 +175,7 @@ export function CartModal({ label }: CartModalProps) {
})
.finally(() => setIsConnecting(false))
} catch (error) {
- console.log("Error -", error)
+ console.log(178, "Payment with metamask error -", error)
}
}
diff --git a/app/components/ui/Modals/ChangeLanguageModal.tsx b/app/components/ui/Modals/ChangeLanguageModal.tsx
index d3dbf60c..196bd048 100644
--- a/app/components/ui/Modals/ChangeLanguageModal.tsx
+++ b/app/components/ui/Modals/ChangeLanguageModal.tsx
@@ -1,13 +1,13 @@
-import { ModalQueryContainer } from "../ModalQueryContainer"
+import { ModalQueryContainer } from "./ModalContainers/ModalQueryContainer"
import { Button } from ".."
import { languages } from "@/constant/languages"
import Image from "next/image"
-interface NameModalProps {
+interface ChangeLanguageModalProps {
label: string
}
-export function ChangeLanguageModal({ label }: NameModalProps) {
+export function ChangeLanguageModal({ label }: ChangeLanguageModalProps) {
return (
{/* ANY CONTENT (to keep consistent keep {label})*/}
diff --git a/app/components/ui/Modals/CtrlKModal.tsx b/app/components/ui/Modals/CtrlKModal.tsx
new file mode 100644
index 00000000..2774e194
--- /dev/null
+++ b/app/components/ui/Modals/CtrlKModal.tsx
@@ -0,0 +1,24 @@
+"use client"
+
+import { BiSearchAlt } from "react-icons/bi"
+import { SearchInput } from "../Inputs/SearchInput"
+import { ModalContainer } from "./ModalContainers/"
+import { useCtrlKModal } from "@/store/ui/ctrlKModal"
+
+export function CtrlKModal() {
+ const ctrlKModal = useCtrlKModal()
+
+ return (
+
+
+
Search for products
+ }
+ name="searchQuery"
+ placeholder="Search..."
+ />
+
+
+ )
+}
diff --git a/app/components/ui/Modals/ModalContainers/AdminPanelContainer.tsx b/app/components/ui/Modals/ModalContainers/AdminPanelModalContainer.tsx
similarity index 100%
rename from app/components/ui/Modals/ModalContainers/AdminPanelContainer.tsx
rename to app/components/ui/Modals/ModalContainers/AdminPanelModalContainer.tsx
diff --git a/app/components/ui/AreYouSureModal.tsx b/app/components/ui/Modals/ModalContainers/AreYouSureModalContainer.tsx
similarity index 91%
rename from app/components/ui/AreYouSureModal.tsx
rename to app/components/ui/Modals/ModalContainers/AreYouSureModalContainer.tsx
index 5fc0713c..3423d299 100644
--- a/app/components/ui/AreYouSureModal.tsx
+++ b/app/components/ui/Modals/ModalContainers/AreYouSureModalContainer.tsx
@@ -1,3 +1,5 @@
+"use client"
+
import { useEffect, useState } from "react"
import { IoMdClose } from "react-icons/io"
import { IconType } from "react-icons"
@@ -5,9 +7,9 @@ import { useSwipeable } from "react-swipeable"
import { AnimatePresence, motion } from "framer-motion"
import { twMerge } from "tailwind-merge"
-import { Button } from "."
+import { Button } from "../.."
-interface ModalContainerProps {
+interface AreYouSureModalContainerProps {
isOpen: boolean
isLoading?: boolean
label: string | React.ReactNode
@@ -52,9 +54,9 @@ interface ModalContainerProps {
className?: string
}
-export function AreYouSureModal({
- isOpen,
- isLoading,
+export function AreYouSureModalContainer({
+ isOpen = false,
+ isLoading = false,
label,
primaryButtonVariant,
primaryButtonIcon: PrimaryButtonIcon,
@@ -65,15 +67,17 @@ export function AreYouSureModal({
secondaryButtonAction,
secondaryButtonLabel,
className,
-}: ModalContainerProps) {
+}: AreYouSureModalContainerProps) {
const [showModal, setShowModal] = useState(isOpen)
+ console.log(72, "showModal - ", showModal)
/* onOpen - show modal - disable scroll and scrollbar */
useEffect(() => {
setShowModal(isOpen)
if (isOpen) {
document.body.style.overflow = "hidden"
- document.body.style.width = "calc(100% - 17px)"
+ document.body.style.width = "calc(100% - 16px)"
+ document.getElementById("nav")!.style.width = "calc(100% - 16px)"
}
}, [isOpen])
@@ -88,6 +92,7 @@ export function AreYouSureModal({
function closeModal() {
secondaryButtonAction()
document.body.removeAttribute("style")
+ document.getElementById("nav")!.removeAttribute("style")
}
//Close modal on esc
diff --git a/app/components/ui/Modals/ModalContainers/CartModalContainer.tsx b/app/components/ui/Modals/ModalContainers/CartModalContainer.tsx
index 473b1d1a..4576f1fa 100644
--- a/app/components/ui/Modals/ModalContainers/CartModalContainer.tsx
+++ b/app/components/ui/Modals/ModalContainers/CartModalContainer.tsx
@@ -1,12 +1,10 @@
"use client"
-import { useSearchParams } from "next/navigation"
+
import React from "react"
-import { CartModal } from ".."
-import useCartStore from "@/store/user/cartStore"
+import { useSearchParams } from "next/navigation"
export function CartModalContainer({ children }: { children: React.ReactNode }) {
const searchParams = useSearchParams()
- const cartStore = useCartStore()
return <>{searchParams.getAll("modal").includes("CartModal") && children}>
}
diff --git a/app/components/ui/Modals/ModalContainers/ChangeLanguageModalContainer.tsx b/app/components/ui/Modals/ModalContainers/ChangeLanguageModalContainer.tsx
index 0352ffe3..7af14eba 100644
--- a/app/components/ui/Modals/ModalContainers/ChangeLanguageModalContainer.tsx
+++ b/app/components/ui/Modals/ModalContainers/ChangeLanguageModalContainer.tsx
@@ -1,6 +1,7 @@
"use client"
-import { useSearchParams } from "next/navigation"
+
import React from "react"
+import { useSearchParams } from "next/navigation"
export function ChangeLanguageModalContainer({ children }: { children: React.ReactNode }) {
const searchParams = useSearchParams()
diff --git a/app/components/ui/Modals/ModalContainers/ModalContainer.tsx b/app/components/ui/Modals/ModalContainers/ModalContainer.tsx
new file mode 100644
index 00000000..0465609b
--- /dev/null
+++ b/app/components/ui/Modals/ModalContainers/ModalContainer.tsx
@@ -0,0 +1,96 @@
+"use client"
+
+import { useEffect, useState } from "react"
+import { IoMdClose } from "react-icons/io"
+import { useSwipeable } from "react-swipeable"
+import { AnimatePresence, motion } from "framer-motion"
+import { twMerge } from "tailwind-merge"
+
+interface ModalContainerProps {
+ isOpen: boolean
+ isLoading?: boolean
+ onClose: () => void
+ className?: string
+ label?: string | React.ReactNode
+ children: React.ReactNode
+}
+
+export function ModalContainer({ isOpen, isLoading, onClose, className, label, children }: ModalContainerProps) {
+ const [showModal, setShowModal] = useState(isOpen)
+
+ /* onOpen - show modal - disable scroll and scrollbar */
+ useEffect(() => {
+ setShowModal(isOpen)
+ }, [isOpen])
+
+ //correct way to add event listener to listen keydown
+ useEffect(() => {
+ document.addEventListener("keydown", handleKeyDown)
+ return () => document.removeEventListener("keydown", handleKeyDown)
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isLoading])
+
+ /* onClose - close modal - show scrollbar */
+ function closeModal() {
+ onClose()
+ }
+
+ //Close modal on esc
+ const handleKeyDown = (event: KeyboardEvent) => {
+ //TODO - block esc key if isLoading (in ModalContainer.tsx)
+ if (event.key === "Escape" && !isLoading) {
+ closeModal()
+ }
+ }
+
+ /* for e.stopPropagation when mousedown on modal and mouseup on modalBg */
+ const modalBgHandler = useSwipeable({
+ onTouchStartOrOnMouseDown: () => {
+ closeModal()
+ },
+ trackMouse: true,
+ })
+
+ const modalHandler = useSwipeable({
+ onTouchStartOrOnMouseDown: e => {
+ e.event.stopPropagation()
+ },
+ trackMouse: true,
+ })
+
+ return (
+
+ {showModal && (
+
+
+
+
+
+
+ )}
+
+ )
+}
diff --git a/app/components/ui/Modals/ModalContainers/ModalQueryContainer.tsx b/app/components/ui/Modals/ModalContainers/ModalQueryContainer.tsx
new file mode 100644
index 00000000..defbd044
--- /dev/null
+++ b/app/components/ui/Modals/ModalContainers/ModalQueryContainer.tsx
@@ -0,0 +1,101 @@
+"use client"
+
+import { useCallback, useEffect, useState } from "react"
+import { usePathname, useRouter, useSearchParams } from "next/navigation"
+import { IoMdClose } from "react-icons/io"
+import { useSwipeable } from "react-swipeable"
+import { twMerge } from "tailwind-merge"
+import { AnimatePresence, motion } from "framer-motion"
+
+interface ModalQueryContainerProps {
+ children: React.ReactNode
+ modalQuery: string
+ className?: string
+ isLoading?: boolean
+}
+
+export function ModalQueryContainer({ children, modalQuery, className, isLoading = false }: ModalQueryContainerProps) {
+ const pathname = usePathname()
+ const router = useRouter()
+ const queryParams = useSearchParams()
+
+ const showModal = queryParams.getAll("modal").includes(modalQuery)
+ const [shouldClose, setShouldClose] = useState(false)
+
+ //correct way to add event listener to listen keydown
+ useEffect(() => {
+ document.addEventListener("keydown", handleKeyDown)
+ return () => document.removeEventListener("keydown", handleKeyDown)
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isLoading])
+
+ // Close modal and redirect on close
+ const closeModal = useCallback(() => {
+ document.body.removeAttribute("style")
+ document.getElementById("nav")!.removeAttribute("style")
+ setShouldClose(true)
+ setTimeout(() => {
+ router.push(pathname)
+ }, 500)
+ }, [router, pathname])
+
+ //Close modal on esc
+ const handleKeyDown = (event: KeyboardEvent) => {
+ if (event.key === "Escape" && !isLoading) {
+ closeModal()
+ }
+ }
+
+ /* for e.stopPropagation when mousedown on modal and mouseup on modalBg */
+ const modalBgHandler = useSwipeable({
+ onTouchStartOrOnMouseDown: () => {
+ closeModal()
+ },
+ trackMouse: true,
+ })
+
+ const modalHandler = useSwipeable({
+ onTouchStartOrOnMouseDown: e => {
+ e.event.stopPropagation()
+ },
+ trackMouse: true,
+ })
+
+ if (!showModal) {
+ return null
+ }
+
+ return (
+
+ {shouldClose ||
+ (showModal && (
+
+
+
+ {children}
+
+
+ ))}
+
+ )
+}
diff --git a/app/components/ui/Modals/ModalContainers/index.ts b/app/components/ui/Modals/ModalContainers/index.ts
index 476e697e..852c5e4d 100644
--- a/app/components/ui/Modals/ModalContainers/index.ts
+++ b/app/components/ui/Modals/ModalContainers/index.ts
@@ -1,4 +1,7 @@
-export { CartModalContainer } from "./CartModalContainer"
+export { AdminPanelModalContainer } from "./AdminPanelModalContainer"
+export { AreYouSureModalContainer } from "./AreYouSureModalContainer"
export { AuthModalContainer } from "./AuthModalContainer"
+export { CartModalContainer } from "./CartModalContainer"
export { ChangeLanguageModalContainer } from "./ChangeLanguageModalContainer"
-export { AdminPanelModalContainer } from "./AdminPanelContainer"
+export { ModalContainer } from "./ModalContainer"
+export { ModalQueryContainer } from "./ModalQueryContainer"
diff --git a/app/components/ui/index.ts b/app/components/ui/index.ts
index 285ceb29..ae748824 100644
--- a/app/components/ui/index.ts
+++ b/app/components/ui/index.ts
@@ -1,7 +1,7 @@
import { Button } from "./Button"
import { DropdownContainer } from "./DropdownContainer"
import { DropdownItem } from "./DropdownItem"
-import { ModalQueryContainer } from "./ModalQueryContainer"
+import { ModalQueryContainer } from "./Modals/ModalContainers/ModalQueryContainer"
import { Checkbox } from "./Checkbox"
import { Slider } from "./Slider"
import { RadioButton } from "./RadioButton"
diff --git a/app/globals.css b/app/globals.css
index 556bc51f..d69d5875 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -51,7 +51,12 @@ html,
body,
:root {
height: 100%;
- background-color: #202020;
+ width: 100%;
+ overflow: hidden;
+ /* This is strongly recommended when modal isOpen */
+ /* overflow-y: auto; */
+ /* Red color because user should not see this */
+ background-color: red;
}
* {
diff --git a/app/layout.tsx b/app/layout.tsx
index f52d2e6f..706d0049 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -3,7 +3,6 @@ import "./globals.css"
import type { Metadata } from "next"
import { Layout } from "./components"
-import Navbar from "./components/Navbar/Navbar"
import {
AdminPanelModalContainer,
AuthModalContainer,
@@ -14,8 +13,7 @@ import { AdminPanelModal, AuthModal, CartModal, ChangeLanguageModal } from "./co
import ClientOnly from "./components/ClientOnly"
import getOwnerProducts from "./actions/getOwnerProducts"
import { ModalsProvider } from "./providers/ModalsProvider"
-import { InitialPageLoadingSkeleton } from "./components/Skeletons/InitialPageLoadingSkeleton"
-import { Dispatch, SetStateAction } from "react"
+import Navbar from "./components/Navbar/Navbar"
export const metadata: Metadata = {
title: "23_store",
@@ -42,8 +40,10 @@ export default async function RootLayout({ children }: { children: React.ReactNo
-
- {children}
+
+
+ {children}
+
diff --git a/app/providers/ModalsProvider.tsx b/app/providers/ModalsProvider.tsx
index 5d15e6a3..274af8fd 100644
--- a/app/providers/ModalsProvider.tsx
+++ b/app/providers/ModalsProvider.tsx
@@ -1,8 +1,10 @@
"use client"
-import { AreYouSureDeleteProductModal } from "@/components/ui/Modals/AreYouSureDeleteProductModal"
import { useEffect, useState } from "react"
+import { AreYouSureDeleteProductModal } from "@/components/ui/Modals/AreYouSureDeleteProductModal"
+import { CtrlKModal } from "@/components/ui/Modals/CtrlKModal"
+
export function ModalsProvider() {
const [isMounted, setIsMounted] = useState(false)
@@ -18,6 +20,7 @@ export function ModalsProvider() {
<>
{/* */}
+
>
)
}
diff --git a/app/store/ui/ctrlKModal.ts b/app/store/ui/ctrlKModal.ts
new file mode 100644
index 00000000..654e9d35
--- /dev/null
+++ b/app/store/ui/ctrlKModal.ts
@@ -0,0 +1,15 @@
+import { create } from "zustand"
+
+type CtrlKModalStore = {
+ isOpen: boolean
+ openModal: () => void
+ closeModal: () => void
+ toggle: () => void
+}
+
+export const useCtrlKModal = create((set, get) => ({
+ isOpen: false,
+ openModal: () => set({ isOpen: true }),
+ closeModal: () => set({ isOpen: false }),
+ toggle: () => set({ isOpen: !get().isOpen }),
+}))
From 2223c508d1ba07400d61d438f859bed5f7331da2 Mon Sep 17 00:00:00 2001
From: Nikita <39565703+nicitaacom@users.noreply.github.com>
Date: Tue, 7 Nov 2023 15:37:33 +0100
Subject: [PATCH 3/7] SupportButton incapsulated
---
app/components/Navbar/Navbar.tsx | 23 ++--------------
.../Navbar/components/SupportButton.tsx | 27 +++++++++++++++++++
2 files changed, 29 insertions(+), 21 deletions(-)
create mode 100644 app/components/Navbar/components/SupportButton.tsx
diff --git a/app/components/Navbar/Navbar.tsx b/app/components/Navbar/Navbar.tsx
index bb793389..8dbd3076 100644
--- a/app/components/Navbar/Navbar.tsx
+++ b/app/components/Navbar/Navbar.tsx
@@ -1,11 +1,7 @@
-import Link from "next/link"
-import { FiPhoneCall } from "react-icons/fi"
import { BiSearchAlt } from "react-icons/bi"
import supabaseServer from "@/libs/supabaseServer"
-import { contact } from "@/constant/contacts"
import { Language } from "../Language"
-import { DropdownContainer } from "../ui/DropdownContainer"
import { SwitchDarkMode } from ".."
import { NavbarWrapper } from "./components/NavbarWrapper"
import {
@@ -17,6 +13,7 @@ import {
OpenUserMenuButton,
} from "./components"
import { CtrlKBadge } from "./components/CtrlKBadge"
+import { SupportButton } from "./components/SupportButton"
export default async function Navbar() {
const {
@@ -43,23 +40,7 @@ export default async function Navbar() {
- }>
-
-
-
- Telegram
-
-
(response 8s)
-
-
-
+
{user ? : }
diff --git a/app/components/Navbar/components/SupportButton.tsx b/app/components/Navbar/components/SupportButton.tsx
new file mode 100644
index 00000000..ad150208
--- /dev/null
+++ b/app/components/Navbar/components/SupportButton.tsx
@@ -0,0 +1,27 @@
+import Link from "next/link"
+import { FiPhoneCall } from "react-icons/fi"
+
+import { contact } from "@/constant/contacts"
+import { DropdownContainer } from "@/components/ui"
+
+export function SupportButton() {
+ return (
+ }>
+
+
+
+ Telegram
+
+
(response 8s)
+
+
+
+ )
+}
From 6b6a0095f99766a84f2721741f00e636db0d5db9 Mon Sep 17 00:00:00 2001
From: Nikita <39565703+nicitaacom@users.noreply.github.com>
Date: Tue, 7 Nov 2023 15:39:00 +0100
Subject: [PATCH 4/7] store for avatarDropdown created
---
app/store/ui/avatarDropdown.ts | 13 +++++++++++++
1 file changed, 13 insertions(+)
create mode 100644 app/store/ui/avatarDropdown.ts
diff --git a/app/store/ui/avatarDropdown.ts b/app/store/ui/avatarDropdown.ts
new file mode 100644
index 00000000..a597aefd
--- /dev/null
+++ b/app/store/ui/avatarDropdown.ts
@@ -0,0 +1,13 @@
+import { create } from "zustand"
+
+type AvatarDropdownStore = {
+ isOpen: boolean
+ openModal: () => void
+ closeModal: () => void
+}
+
+export const useAvatarDropdown = create()(set => ({
+ isOpen: false,
+ openModal: () => set({ isOpen: true }),
+ closeModal: () => set({ isOpen: false }),
+}))
From b515098b05d910a46b4c8e2a89891b105a08836c Mon Sep 17 00:00:00 2001
From: Nikita <39565703+nicitaacom@users.noreply.github.com>
Date: Tue, 7 Nov 2023 15:44:08 +0100
Subject: [PATCH 5/7] profile_picture_url changed to avatar_url
to be consistent with AvatarDropdown
---
app/(auth)/auth/callback/route.ts | 2 +-
app/(auth)/auth/client/page.tsx | 4 ++--
.../Navbar/components/OpenUserMenuButton.tsx | 4 +++-
app/store/user/userStore.ts | 12 ++++++------
4 files changed, 12 insertions(+), 10 deletions(-)
diff --git a/app/(auth)/auth/callback/route.ts b/app/(auth)/auth/callback/route.ts
index 255f41b7..6d40ae12 100644
--- a/app/(auth)/auth/callback/route.ts
+++ b/app/(auth)/auth/callback/route.ts
@@ -26,6 +26,6 @@ export async function GET(request: Request) {
return NextResponse.redirect(
`${requestUrl.origin}/auth/client?userId=${user?.id}&username=${user?.user_metadata.name}
- &email=${user?.user_metadata.email}&profile_picture_url=${user?.user_metadata.picture}`,
+ &email=${user?.user_metadata.email}&avatar_url=${user?.user_metadata.picture}`,
)
}
diff --git a/app/(auth)/auth/client/page.tsx b/app/(auth)/auth/client/page.tsx
index 47dc259b..d70e544f 100644
--- a/app/(auth)/auth/client/page.tsx
+++ b/app/(auth)/auth/client/page.tsx
@@ -11,9 +11,9 @@ export default function Page() {
const userId = useSearchParams().get("userId")
const username = useSearchParams().get("username")
const email = useSearchParams().get("email")
- const profile_picture_url = useSearchParams().get("profile_picture_url")
+ const avatar_url = useSearchParams().get("avatar_url")
useEffect(() => {
- userStore.setUser(userId ?? "", username ?? "", email ?? "", profile_picture_url ?? "")
+ userStore.setUser(userId ?? "", username ?? "", email ?? "", avatar_url ?? "")
router.push("/")
//to prevent error about too many re-renders
// eslint-disable-next-line react-hooks/exhaustive-deps
diff --git a/app/components/Navbar/components/OpenUserMenuButton.tsx b/app/components/Navbar/components/OpenUserMenuButton.tsx
index 7dfd1829..31842c5f 100644
--- a/app/components/Navbar/components/OpenUserMenuButton.tsx
+++ b/app/components/Navbar/components/OpenUserMenuButton.tsx
@@ -11,8 +11,10 @@ import LogoutDropdownItem from "./LogoutDropdownItem"
import { SwitchDarkMode } from "@/components"
import { contact } from "@/constant/contacts"
import { DropdownContainer, DropdownItem } from "@/components/ui"
+import { useAvatarDropdown } from "@/store/ui/avatarDropdown"
export function OpenUserMenuButton() {
+ const avatarDropdown = useAvatarDropdown()
const userStore = useUserStore()
const mode = useDarkMode()
@@ -25,7 +27,7 @@ export function OpenUserMenuButton() {
<>
void
+ avatarUrl: string
+ setUser: (userId: string, username: string, email: string, avatarUrl: string) => void
logoutUser: () => void
}
@@ -19,15 +19,15 @@ export const userStore = (set: SetState): UserStore => ({
isAuthenticated: false,
username: "",
email: "",
- profilePictureUrl: "",
- setUser(userId: string, username: string, email: string, profilePictureUrl: string) {
+ avatarUrl: "",
+ setUser(userId: string, username: string, email: string, avatarUrl: string) {
set((state: UserStore) => ({
...state,
userId: userId,
isAuthenticated: true,
username: username,
email: email,
- profilePictureUrl: profilePictureUrl,
+ avatarUrl: avatarUrl,
}))
},
logoutUser() {
@@ -37,7 +37,7 @@ export const userStore = (set: SetState): UserStore => ({
isAuthenticated: false,
username: "",
email: "",
- profilePictureUrl: "",
+ avatarUrl: "",
}))
},
})
From 17bc966fc6f1e98c59af954ba3b72751c46d0543 Mon Sep 17 00:00:00 2001
From: Nikita <39565703+nicitaacom@users.noreply.github.com>
Date: Tue, 7 Nov 2023 16:04:55 +0100
Subject: [PATCH 6/7] close avatarDropdown on DropdownItem click
Also I created store to get access to dropdown open / close functions
Also I improved avatarDropdown hook
---
.../Navbar/components/OpenUserMenuButton.tsx | 16 +++++++--
app/components/ui/DropdownContainer.tsx | 8 ++---
app/hooks/ui/useAvatarDropdownClose.ts | 34 +++++++++++++++++++
app/hooks/ui/useCloseOnClickOutside.ts | 32 -----------------
app/store/ui/avatarDropdown.ts | 16 +++++----
5 files changed, 61 insertions(+), 45 deletions(-)
create mode 100644 app/hooks/ui/useAvatarDropdownClose.ts
delete mode 100644 app/hooks/ui/useCloseOnClickOutside.ts
diff --git a/app/components/Navbar/components/OpenUserMenuButton.tsx b/app/components/Navbar/components/OpenUserMenuButton.tsx
index 31842c5f..14d61ac1 100644
--- a/app/components/Navbar/components/OpenUserMenuButton.tsx
+++ b/app/components/Navbar/components/OpenUserMenuButton.tsx
@@ -12,12 +12,24 @@ import { SwitchDarkMode } from "@/components"
import { contact } from "@/constant/contacts"
import { DropdownContainer, DropdownItem } from "@/components/ui"
import { useAvatarDropdown } from "@/store/ui/avatarDropdown"
+import { useRouter } from "next/navigation"
export function OpenUserMenuButton() {
+ const router = useRouter()
const avatarDropdown = useAvatarDropdown()
const userStore = useUserStore()
const mode = useDarkMode()
+ function openAdminPanel() {
+ router.push("?modal=AdminPanel")
+ avatarDropdown.closeDropdown()
+ }
+
+ function openChangeLanguageModal() {
+ router.push("?modal=ChangeLanguage")
+ avatarDropdown.closeDropdown()
+ }
+
return (
>
}>
-
+
-
+
- setIsDropdown(!isDropdown)}>
+
+
{icon}
diff --git a/app/hooks/ui/useAvatarDropdownClose.ts b/app/hooks/ui/useAvatarDropdownClose.ts
new file mode 100644
index 00000000..6bf7a3b6
--- /dev/null
+++ b/app/hooks/ui/useAvatarDropdownClose.ts
@@ -0,0 +1,34 @@
+import { useAvatarDropdown } from "@/store/ui/avatarDropdown"
+import { useEffect, useRef } from "react"
+
+const useAvatarDropdownClose = () => {
+ const avatarDropdownRef = useRef
(null)
+ const { isDropdown, openDropdown, closeDropdown, toggle } = useAvatarDropdown()
+
+ useEffect(() => {
+ const handleClickOutside = (event: MouseEvent) => {
+ if (avatarDropdownRef.current && !avatarDropdownRef.current.contains(event.target as Node)) {
+ closeDropdown()
+ }
+ }
+
+ const handleKeyPress = (event: KeyboardEvent) => {
+ if (event.key === "Escape") {
+ closeDropdown()
+ }
+ }
+
+ document.addEventListener("mousedown", handleClickOutside)
+ document.addEventListener("keydown", handleKeyPress)
+
+ return () => {
+ document.removeEventListener("mousedown", handleClickOutside)
+ document.removeEventListener("keydown", handleKeyPress)
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [])
+
+ return { isDropdown, openDropdown, closeDropdown, toggle, avatarDropdownRef }
+}
+
+export default useAvatarDropdownClose
diff --git a/app/hooks/ui/useCloseOnClickOutside.ts b/app/hooks/ui/useCloseOnClickOutside.ts
deleted file mode 100644
index fa15d658..00000000
--- a/app/hooks/ui/useCloseOnClickOutside.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { useEffect, useRef, useState } from "react"
-
-const useCloseOnClickOutside = () => {
- const [isDropdown, setIsDropdown] = useState(false)
- const dropdownContainerRef = useRef(null)
-
- useEffect(() => {
- const handleClickOutside = (event: MouseEvent) => {
- if (dropdownContainerRef.current && !dropdownContainerRef.current.contains(event.target as Node)) {
- setIsDropdown(false)
- }
- }
-
- const handleKeyPress = (event: KeyboardEvent) => {
- if (event.key === "Escape") {
- setIsDropdown(false)
- }
- }
-
- document.addEventListener("mousedown", handleClickOutside)
- document.addEventListener("keydown", handleKeyPress)
-
- return () => {
- document.removeEventListener("mousedown", handleClickOutside)
- document.removeEventListener("keydown", handleKeyPress)
- }
- }, [])
-
- return { isDropdown, dropdownContainerRef, setIsDropdown }
-}
-
-export default useCloseOnClickOutside
diff --git a/app/store/ui/avatarDropdown.ts b/app/store/ui/avatarDropdown.ts
index a597aefd..574d11ea 100644
--- a/app/store/ui/avatarDropdown.ts
+++ b/app/store/ui/avatarDropdown.ts
@@ -1,13 +1,15 @@
import { create } from "zustand"
type AvatarDropdownStore = {
- isOpen: boolean
- openModal: () => void
- closeModal: () => void
+ isDropdown: boolean
+ openDropdown: () => void
+ closeDropdown: () => void
+ toggle: () => void
}
-export const useAvatarDropdown = create()(set => ({
- isOpen: false,
- openModal: () => set({ isOpen: true }),
- closeModal: () => set({ isOpen: false }),
+export const useAvatarDropdown = create()((set, get) => ({
+ isDropdown: false,
+ openDropdown: () => set({ isDropdown: true }),
+ closeDropdown: () => set({ isDropdown: false }),
+ toggle: () => set({ isDropdown: !get().isDropdown }),
}))
From 4c05c443ff91025b89cc0c27855f5207c51289d8 Mon Sep 17 00:00:00 2001
From: Nikita <39565703+nicitaacom@users.noreply.github.com>
Date: Tue, 7 Nov 2023 19:38:08 +0100
Subject: [PATCH 7/7] areYouSureClearCartModal with lowercase
---
app/components/ui/Modals/CartModal/CartModal.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/components/ui/Modals/CartModal/CartModal.tsx b/app/components/ui/Modals/CartModal/CartModal.tsx
index dea91fe9..433f0a74 100644
--- a/app/components/ui/Modals/CartModal/CartModal.tsx
+++ b/app/components/ui/Modals/CartModal/CartModal.tsx
@@ -28,7 +28,7 @@ interface CartModalProps {
export function CartModal({ label }: CartModalProps) {
const router = useRouter()
const cartStore = useCartStore()
- const AreYouSureClearCartModal = useAreYouSureClearCartModal()
+ const areYouSureClearCartModal = useAreYouSureClearCartModal()
const toast = useToast()
const { isAuthenticated } = useUserStore()
@@ -360,7 +360,7 @@ export function CartModal({ label }: CartModalProps) {
Total:
{formatCurrency(cartStore.getProductsPrice())}
-