From d3ef8e1a98a4dc6332af0138ddb60a044b585d61 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Sun, 21 Jun 2026 11:08:11 +0330 Subject: [PATCH] load pattern --- .../checkout/[id]/components/TableSection.tsx | 2 +- src/app/[name]/(Main)/[id]/page.tsx | 2 +- src/app/[name]/(Main)/about/AboutPage.tsx | 2 +- .../[name]/(Main)/components/MenuIndex.tsx | 4 +- .../new/components/AddressDetailsModal.tsx | 2 +- .../[name]/(Profile)/profile/address/page.tsx | 6 +- src/app/[name]/RestaurantLayoutClient.tsx | 6 +- src/app/[name]/api/proxy-svg/route.ts | 2 +- src/app/globals.css | 18 +++ src/components/background/AppBackground.tsx | 107 ++++++++++++++++++ src/components/listview/MenuItem.tsx | 2 +- src/components/overlays/SplashScreen.tsx | 1 + src/lib/helpers/backgroundUtils.ts | 80 +++++++++++++ 13 files changed, 222 insertions(+), 12 deletions(-) create mode 100644 src/components/background/AppBackground.tsx create mode 100644 src/lib/helpers/backgroundUtils.ts diff --git a/src/app/[name]/(Dialogs)/order/checkout/[id]/components/TableSection.tsx b/src/app/[name]/(Dialogs)/order/checkout/[id]/components/TableSection.tsx index 93364d9..1221b0c 100644 --- a/src/app/[name]/(Dialogs)/order/checkout/[id]/components/TableSection.tsx +++ b/src/app/[name]/(Dialogs)/order/checkout/[id]/components/TableSection.tsx @@ -22,7 +22,7 @@ export const TableSection = ({ tableNumber, onIncrement, onDecrement }: TableSec
diff --git a/src/app/[name]/(Main)/components/MenuIndex.tsx b/src/app/[name]/(Main)/components/MenuIndex.tsx index 013ec15..b226c44 100644 --- a/src/app/[name]/(Main)/components/MenuIndex.tsx +++ b/src/app/[name]/(Main)/components/MenuIndex.tsx @@ -175,11 +175,11 @@ const MenuIndex = () => {
{selectedCategory === "0" ? "" : categories.find((c) => c.id === selectedCategory)?.title || ""}
- - diff --git a/src/app/[name]/(Profile)/profile/address/new/components/AddressDetailsModal.tsx b/src/app/[name]/(Profile)/profile/address/new/components/AddressDetailsModal.tsx index b91199f..5f3a8c5 100644 --- a/src/app/[name]/(Profile)/profile/address/new/components/AddressDetailsModal.tsx +++ b/src/app/[name]/(Profile)/profile/address/new/components/AddressDetailsModal.tsx @@ -58,7 +58,7 @@ export const AddressDetailsModal = ({ e.preventDefault(); onChangePosition(); }} - className='bg-background! w-fit! text-foreground! flex items-center gap-2 text-xs' + className='bg-background! pattern-secondary-bg w-fit! text-foreground! flex items-center gap-2 text-xs' disabled={isPending} > diff --git a/src/app/[name]/(Profile)/profile/address/page.tsx b/src/app/[name]/(Profile)/profile/address/page.tsx index 939a200..89ee865 100644 --- a/src/app/[name]/(Profile)/profile/address/page.tsx +++ b/src/app/[name]/(Profile)/profile/address/page.tsx @@ -132,7 +132,7 @@ function UserAddressesPage({ }: Props) { onClick={() => handleSetDefault(address.id)} disabled={isSettingDefault} pending={isSettingDefault} - className='bg-background! text-foreground! flex items-center gap-2 text-xs h-8 flex-1' + className='bg-background! pattern-secondary-bg text-foreground! flex items-center gap-2 text-xs h-8 flex-1' > تنظیم به عنوان فعال @@ -140,14 +140,14 @@ function UserAddressesPage({ }: Props) { )}
- diff --git a/src/app/[name]/RestaurantLayoutClient.tsx b/src/app/[name]/RestaurantLayoutClient.tsx index a0bfa7a..ce64dbc 100644 --- a/src/app/[name]/RestaurantLayoutClient.tsx +++ b/src/app/[name]/RestaurantLayoutClient.tsx @@ -1,6 +1,7 @@ "use client"; import ActiveChecker from "@/components/ActiveChecker"; +import AppBackground from "@/components/background/AppBackground"; import CartChecker from "@/components/CartChecker"; import RestaurantHeadManager from "@/components/RestaurantHeadManager"; import RestaurantNotFoundGuard from "@/components/RestaurantNotFoundGuard"; @@ -15,7 +16,10 @@ export default function RestaurantLayoutClient({ children }: Props) { <> - {children} + + + {children} + diff --git a/src/app/[name]/api/proxy-svg/route.ts b/src/app/[name]/api/proxy-svg/route.ts index 91e709d..3046cd6 100644 --- a/src/app/[name]/api/proxy-svg/route.ts +++ b/src/app/[name]/api/proxy-svg/route.ts @@ -17,7 +17,7 @@ export async function GET(request: NextRequest) { return NextResponse.json({ error: "Invalid URL" }, { status: 400 }); } const res = await fetchWithTimeout(url, { - timeoutMs: 5000, + timeoutMs: 15_000, headers: { Accept: "image/svg+xml, text/xml, text/plain" }, }); if (!res.ok) { diff --git a/src/app/globals.css b/src/app/globals.css index 12781e7..fa3270b 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -395,4 +395,22 @@ html[data-theme="dark"] { background-color: var(--primary); -webkit-mask: url("/assets/images/fast-delivery.svg") no-repeat center / contain; mask: url("/assets/images/fast-delivery.svg") no-repeat center / contain; +} + +html[data-pattern-bg="true"] #root { + position: relative; + z-index: 1; + isolation: isolate; +} + +html[data-pattern-bg="true"] [data-slot="dialog-content"] { + background-color: var(--container) !important; +} + +html[data-pattern-bg="true"] [data-slot="splash-screen"] { + background-color: var(--container) !important; +} + +html[data-pattern-bg="true"] .pattern-secondary-bg { + background-color: var(--primary-light) !important; } \ No newline at end of file diff --git a/src/components/background/AppBackground.tsx b/src/components/background/AppBackground.tsx new file mode 100644 index 0000000..1778b55 --- /dev/null +++ b/src/components/background/AppBackground.tsx @@ -0,0 +1,107 @@ +"use client"; + +import { useGetAbout } from "@/app/[name]/(Main)/about/hooks/useAboutData"; +import { + applyPatternCssVariables, + clearPatternCssVariables, + colorizeSvg, + hexToRgba, + isPatternBackground, + PATTERN_BACKGROUND_OPACITY, +} from "@/lib/helpers/backgroundUtils"; +import { useParams } from "next/navigation"; +import { useEffect, useRef, useState } from "react"; +import { createPortal } from "react-dom"; + +const DEFAULT_MENU_COLOR = "#1E3A8A"; + +export default function AppBackground() { + const { name: restaurantSlug } = useParams<{ name: string }>(); + const { data: aboutData } = useGetAbout(); + const restaurant = aboutData?.data; + const [mounted, setMounted] = useState(false); + const [patternImageUrl, setPatternImageUrl] = useState(null); + const blobUrlRef = useRef(null); + + const isPattern = restaurant ? isPatternBackground(restaurant) : false; + const menuColor = restaurant?.menuColor || DEFAULT_MENU_COLOR; + const baseTint = hexToRgba(menuColor, PATTERN_BACKGROUND_OPACITY); + + useEffect(() => { + setMounted(true); + }, []); + + useEffect(() => { + const revokeBlobUrl = () => { + if (blobUrlRef.current) { + URL.revokeObjectURL(blobUrlRef.current); + blobUrlRef.current = null; + } + setPatternImageUrl(null); + }; + + if (!isPattern || !restaurant?.bgUrl || !restaurantSlug) { + clearPatternCssVariables(); + revokeBlobUrl(); + return; + } + + applyPatternCssVariables(menuColor); + + let cancelled = false; + const proxyUrl = `/${restaurantSlug}/api/proxy-svg?url=${encodeURIComponent(restaurant.bgUrl)}`; + + fetch(proxyUrl) + .then((response) => { + if (!response.ok) { + throw new Error("Failed to fetch background pattern"); + } + return response.text(); + }) + .then((svg) => { + if (cancelled) return; + + revokeBlobUrl(); + const blob = new Blob([colorizeSvg(svg, menuColor)], { + type: "image/svg+xml;charset=utf-8", + }); + const blobUrl = URL.createObjectURL(blob); + blobUrlRef.current = blobUrl; + setPatternImageUrl(blobUrl); + }) + .catch(() => { + if (!cancelled) { + revokeBlobUrl(); + } + }); + + return () => { + cancelled = true; + clearPatternCssVariables(); + revokeBlobUrl(); + }; + }, [isPattern, menuColor, restaurant?.bgUrl, restaurantSlug]); + + if (!mounted || !isPattern) { + return null; + } + + return createPortal( +
, + document.body, + ); +} diff --git a/src/components/listview/MenuItem.tsx b/src/components/listview/MenuItem.tsx index d03da94..5e150d7 100644 --- a/src/components/listview/MenuItem.tsx +++ b/src/components/listview/MenuItem.tsx @@ -150,7 +150,7 @@ const MenuItem = ({ food }: MenuItemProps) => {
{quantity <= 0 ? (