From f39a0c83dd7beb52a9d342b77f5da068c87d2850 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 10 Jun 2026 05:27:39 +0700 Subject: [PATCH] x --- apps/admin/.env.production | 1 + apps/admin/src/pages/PageBuilderPage.tsx | 64 ++++++++++++++++++- apps/backend/src/routes/pages.ts | 5 +- .../spa/src/components/DynamicHeroArticle.tsx | 2 - .../src/components/DynamicProductCarousel.tsx | 1 - .../src/components/DynamicQuoteCallout.tsx | 2 - .../components/DynamicScentNotesBreakdown.tsx | 2 - apps/spa/src/components/Navbar.tsx | 5 +- apps/spa/src/pages/CustomPage.tsx | 21 +++++- 9 files changed, 86 insertions(+), 17 deletions(-) diff --git a/apps/admin/.env.production b/apps/admin/.env.production index b0269fe..3da6d3e 100644 --- a/apps/admin/.env.production +++ b/apps/admin/.env.production @@ -1 +1,2 @@ VITE_API_URL="https://api.homaroma.shop/api" +VITE_FRONTEND_URL="https://homaroma.shop" diff --git a/apps/admin/src/pages/PageBuilderPage.tsx b/apps/admin/src/pages/PageBuilderPage.tsx index 3387d86..97b1fd8 100644 --- a/apps/admin/src/pages/PageBuilderPage.tsx +++ b/apps/admin/src/pages/PageBuilderPage.tsx @@ -14,7 +14,8 @@ import { Loader2, X, Settings2, - CheckCircle2 + CheckCircle2, + ExternalLink } from 'lucide-react'; import { API_BASE, type Page, type PageSection, type Category } from '@homaroma/types'; @@ -24,6 +25,7 @@ interface Scent { } export default function PageBuilderPage() { + const frontendBase = (import.meta.env.VITE_FRONTEND_URL || window.location.origin).replace(/\/$/, ''); const [page, setPage] = useState(null); const [sections, setSections] = useState([]); const [categories, setCategories] = useState([]); @@ -40,6 +42,7 @@ export default function PageBuilderPage() { const [isPageModalOpen, setIsPageModalOpen] = useState(false); const [newPageData, setNewPageData] = useState({ title: '', slug: '' }); const [isSavingPage, setIsSavingPage] = useState(false); + const getPageUrl = (slug: string) => `${frontendBase}/pages/${slug}`; const fetchPagesList = async () => { try { @@ -67,7 +70,7 @@ export default function PageBuilderPage() { setLoading(true); try { - const res = await fetch(`${API_BASE}/pages/${pageObj.slug}`); + const res = await fetch(`${API_BASE}/pages/${pageObj.slug}?includeInactive=true`); const data = await res.json(); if (data && !data.error) { setPage(data); @@ -466,6 +469,19 @@ export default function PageBuilderPage() { ))} + {page && ( + + + Open Live + + )} + + / {p.slug} + + + + + ))} + + + )} + {saveStatus === 'saved' && (
diff --git a/apps/backend/src/routes/pages.ts b/apps/backend/src/routes/pages.ts index 97b8a41..014bf56 100644 --- a/apps/backend/src/routes/pages.ts +++ b/apps/backend/src/routes/pages.ts @@ -18,12 +18,13 @@ export const pagesRoute = new Elysia({ prefix: "/pages" }) } }); }) - .get("/:slug", async ({ params: { slug }, set }) => { + .get("/:slug", async ({ params: { slug }, query, set }) => { + const includeInactive = String((query as any)?.includeInactive || "").toLowerCase() === "true"; const page = await prisma.page.findUnique({ where: { slug }, include: { sections: { - where: { isActive: true }, + where: includeInactive ? undefined : { isActive: true }, orderBy: { order: "asc" } } } diff --git a/apps/spa/src/components/DynamicHeroArticle.tsx b/apps/spa/src/components/DynamicHeroArticle.tsx index 5aab534..1c1f7e6 100644 --- a/apps/spa/src/components/DynamicHeroArticle.tsx +++ b/apps/spa/src/components/DynamicHeroArticle.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - interface DynamicHeroArticleProps { payload: any; } diff --git a/apps/spa/src/components/DynamicProductCarousel.tsx b/apps/spa/src/components/DynamicProductCarousel.tsx index ba8bba3..884f382 100644 --- a/apps/spa/src/components/DynamicProductCarousel.tsx +++ b/apps/spa/src/components/DynamicProductCarousel.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { Swiper, SwiperSlide } from 'swiper/react'; import { Autoplay, Pagination, Navigation } from 'swiper/modules'; import ProductCard from './ProductCard'; diff --git a/apps/spa/src/components/DynamicQuoteCallout.tsx b/apps/spa/src/components/DynamicQuoteCallout.tsx index b6db8e2..930dd05 100644 --- a/apps/spa/src/components/DynamicQuoteCallout.tsx +++ b/apps/spa/src/components/DynamicQuoteCallout.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - interface DynamicQuoteCalloutProps { payload: any; } diff --git a/apps/spa/src/components/DynamicScentNotesBreakdown.tsx b/apps/spa/src/components/DynamicScentNotesBreakdown.tsx index 0cb6f5e..98fbd58 100644 --- a/apps/spa/src/components/DynamicScentNotesBreakdown.tsx +++ b/apps/spa/src/components/DynamicScentNotesBreakdown.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - interface DynamicScentNotesBreakdownProps { payload: any; } diff --git a/apps/spa/src/components/Navbar.tsx b/apps/spa/src/components/Navbar.tsx index c8d8397..568a33a 100644 --- a/apps/spa/src/components/Navbar.tsx +++ b/apps/spa/src/components/Navbar.tsx @@ -10,9 +10,10 @@ import { useTranslation } from 'react-i18next' interface NavbarProps { categories?: Category[] + topOffsetClassName?: string } -export default function Navbar({ categories = [] }: NavbarProps) { +export default function Navbar({ categories = [], topOffsetClassName = 'top-0' }: NavbarProps) { const [searchOpen, setSearchOpen] = useState(false) const [mobileOpen, setMobileOpen] = useState(false) const [searchQuery, setSearchQuery] = useState('') @@ -71,7 +72,7 @@ export default function Navbar({ categories = [] }: NavbarProps) { <> {/* ── NAVBAR BAR ── */}