From 882dfbe67a72b7637180bfe165cb151933f5a802 Mon Sep 17 00:00:00 2001 From: rgrgogu Date: Wed, 5 Aug 2026 04:32:29 +0800 Subject: [PATCH] client revised --- .../advertisements/AdvertisementPreview.jsx | 97 +++++ .../generic/Blocks/Admin/AudioBlock.jsx | 6 +- .../generic/Blocks/Admin/VideoBlock.jsx | 81 +++- .../Blocks/Client/Advertisements/Banner.jsx | 66 +-- .../Blocks/Client/Advertisements/Hero.jsx | 24 +- .../generic/Blocks/Client/AudioBlock.jsx | 12 +- .../generic/Blocks/Client/VideoBlock.jsx | 79 +++- src/contexts/AdminTiersContext.jsx | 13 +- src/data/advertisement.data.js | 8 - .../components/tiers/AccessRuleItemPicker.jsx | 153 ------- .../tiers/ArchivedTierPlansTable.jsx | 4 +- .../admin/components/tiers/BundlesCell.jsx | 159 +------ .../admin/components/tiers/CoursePicker.jsx | 169 +------- .../admin/components/tiers/LessonPicker.jsx | 157 +------ .../admin/components/tiers/TierPlansTable.jsx | 4 +- .../admin/components/tiers/UnitPicker.jsx | 157 +------ .../admin/config/users/rowActions.config.jsx | 4 +- .../pages/advertisements/AddAdvertisement.jsx | 356 ++++------------ .../advertisements/EditAdvertisement.jsx | 291 ++++--------- .../advertisements/ViewAdvertisement.jsx | 18 +- .../admin/pages/courses/lessons/AddLesson.jsx | 42 +- .../pages/courses/lessons/EditLesson.jsx | 45 +- .../admin/pages/courses/units/AddUnit.jsx | 45 +- .../admin/pages/courses/units/EditUnit.jsx | 46 +- .../library/lessons/AddLibraryLesson.jsx | 12 +- .../library/lessons/EditLibraryLesson.jsx | 12 +- .../pages/library/units/AddLibraryUnit.jsx | 12 +- .../pages/library/units/EditLibraryUnit.jsx | 12 +- .../admin/pages/task_list/CreateTaskList.jsx | 58 ++- src/modules/admin/pages/tiers/AddPlan.jsx | 368 +++++++++++----- src/modules/admin/pages/tiers/EditPlan.jsx | 171 +++++--- .../admin/pages/tiers/EditTierCategory.jsx | 11 - src/modules/admin/pages/tiers/ViewPlan.jsx | 394 ++---------------- src/modules/auth/pages/Intro.jsx | 10 +- src/modules/client/components/LessonCard.jsx | 4 +- .../client/components/LessonUpsellModal.jsx | 83 ++-- .../client/components/PlanComparisonTable.jsx | 39 +- src/modules/client/components/UnitCard.jsx | 5 + .../client/components/UnitUpsellModal.jsx | 82 ++-- src/modules/client/pages/Checkout.jsx | 2 +- src/modules/client/pages/CourseDetails.jsx | 8 +- src/modules/client/pages/Dashboard.jsx | 12 +- src/modules/client/pages/LessonDetails.jsx | 71 +++- src/modules/client/pages/PlanList.jsx | 252 +++++------ src/modules/client/pages/UnitList.jsx | 230 +++++++--- src/modules/client/pages/UnitReader.jsx | 113 ++++- src/modules/client/pages/ViewPlan.jsx | 82 ++-- src/modules/public/pages/LandingFooter.jsx | 7 +- src/modules/public/pages/PrivacyPolicy.jsx | 114 +++++ src/routes/router.jsx | 4 + src/utils/planBundle.util.js | 40 ++ src/utils/table.util.jsx | 4 + 52 files changed, 1946 insertions(+), 2302 deletions(-) create mode 100644 src/components/admin/advertisements/AdvertisementPreview.jsx delete mode 100644 src/modules/admin/components/tiers/AccessRuleItemPicker.jsx create mode 100644 src/modules/public/pages/PrivacyPolicy.jsx create mode 100644 src/utils/planBundle.util.js diff --git a/src/components/admin/advertisements/AdvertisementPreview.jsx b/src/components/admin/advertisements/AdvertisementPreview.jsx new file mode 100644 index 0000000..22a0314 --- /dev/null +++ b/src/components/admin/advertisements/AdvertisementPreview.jsx @@ -0,0 +1,97 @@ +// components/admin/advertisements/AdvertisementPreview.jsx + +import { ChevronLeft, ChevronRight, Megaphone } from "lucide-react"; +import { Badge } from "@/components/ui/badge"; +import { Card, CardContent } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Progress } from "@/components/ui/progress"; + +// ── AdvertisementPreview ───────────────────────────────────────────────────── +/** + * Live, presentational preview of a single ad slide as it will actually + * render inside the real Hero/Banner carousel (see components/generic/Blocks/ + * Client/Advertisements/{Hero,Banner}.jsx) — same image + gradient overlay + + * bottom-left badge/headline/description layout, just scaled down for the + * admin form. The prev/next/progress chrome below the slide is static (no + * real carousel behind it, since there's only ever one slide to preview) — + * it's there purely so the admin recognizes this as "inside a carousel." + * + * Props: + * format — "hero" | "banner", determines slide height/typography + * badgeLabels — string[] + * headline — string + * description — string + * imageSrc — resolved image URL, or null/undefined + */ +export function AdvertisementPreview({ format = "hero", badgeLabels, headline, description, imageSrc }) { + const isHero = format !== "banner"; + const labels = Array.isArray(badgeLabels) ? badgeLabels.filter(Boolean) : []; + + return ( +
+

+ Preview — this is how it will appear in the carousel. +

+ + + {imageSrc ? ( + {headline + ) : ( + + )} + +
+ +
+ {labels.length > 0 && ( +
+ {labels.map((label, i) => ( + + {label} + + ))} +
+ )} + {headline && ( +
+ {headline} +
+ )} + {description && ( +

+ {description} +

+ )} +
+ + + + {/* Static carousel chrome — communicates "this sits inside a carousel," not functional */} +
+
+ + +
+
+ +
+
+
+ ); +} + +function cnRounded(isHero) { + return "border overflow-hidden pl-0 py-0 " + (isHero ? "rounded-2xl" : "rounded-xl"); +} + +function cnHeight(isHero) { + return isHero ? "h-56" : "h-48"; +} diff --git a/src/components/generic/Blocks/Admin/AudioBlock.jsx b/src/components/generic/Blocks/Admin/AudioBlock.jsx index 15a51f3..aa02ba7 100644 --- a/src/components/generic/Blocks/Admin/AudioBlock.jsx +++ b/src/components/generic/Blocks/Admin/AudioBlock.jsx @@ -211,7 +211,7 @@ export function AudioBlock({ content, onUpdate, readOnly = false }) { }} /> ) : ( -
+
)}
@@ -291,7 +291,9 @@ export function AudioBlock({ content, onUpdate, readOnly = false }) { value={muted ? 0 : volume} onChange={handleVolume} aria-label="Volume" - className="w-16 h-1.5" + className="w-16 h-1.5 accent-primary cursor-pointer + [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:size-3 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-primary [&::-webkit-slider-thumb]:cursor-pointer + [&::-moz-range-thumb]:appearance-none [&::-moz-range-thumb]:size-3 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:bg-primary [&::-moz-range-thumb]:border-0 [&::-moz-range-thumb]:cursor-pointer" />
diff --git a/src/components/generic/Blocks/Admin/VideoBlock.jsx b/src/components/generic/Blocks/Admin/VideoBlock.jsx index 8c711ca..02cbd08 100644 --- a/src/components/generic/Blocks/Admin/VideoBlock.jsx +++ b/src/components/generic/Blocks/Admin/VideoBlock.jsx @@ -31,6 +31,7 @@ export function VideoBlock({ content, onUpdate, readOnly = false }) { const poster = thumbnailUrl ?? content?.thumbnail_url ?? undefined; const vidRef = useRef(null); + const wrapRef = useRef(null); const [playing, setPlaying] = useState(false); const [progress, setProgress] = useState(0); const [currentTime, setCurrentTime] = useState(0); @@ -38,7 +39,13 @@ export function VideoBlock({ content, onUpdate, readOnly = false }) { const [muted, setMuted] = useState(false); const [volume, setVolume] = useState(1); const [overlayVisible,setOverlayVisible]= useState(true); - const [isFullscreen, setIsFullscreen] = useState(false); + // Native Fullscreen API works on desktop (all browsers) and Android Chrome. + // iOS Safari doesn't support Fullscreen API on arbitrary elements at all, so + // pseudoFullscreen is a CSS-only (fixed inset-0) fallback that keeps our + // custom controls instead of falling back to native