add: more commits

Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
2026-07-03 16:21:27 +08:00
parent 17326b2c2e
commit 7e964f2432
112 changed files with 9160 additions and 3461 deletions
@@ -2,12 +2,16 @@
import { Megaphone } from "lucide-react";
import { Skeleton } from "@/components/ui/skeleton";
import { resolveAssetSrc } from "@/utils/media.util";
// Height per size — controls the strip's visual weight, not its width (always full-width).
// sm/md are fixed; lg/xl use clamp() so they scale with viewport width between
// a min and max instead of jumping at breakpoints.
const SIZE_HEIGHT = {
sm: "h-20",
md: "h-32",
lg: "h-48",
sm: "h-[80px]",
md: "h-[140px]",
lg: "h-[clamp(220px,26vw,320px)]",
xl: "h-[clamp(320px,34vw,460px)]",
};
// ── Banner ───────────────────────────────────────────────────────────────────
@@ -19,13 +23,13 @@ const SIZE_HEIGHT = {
*
* Props:
* ad — advertisement object { headline, ctas, image, image_url, advertisement_id }
* size — "sm" | "md" | "lg" (default "md")
* size — "sm" | "md" | "lg" | "xl" (default "md")
* onCtaClick — (ad, cta) => void, called on click. cta may be undefined if the ad has none.
*/
export function Banner({ ad, size, onCtaClick }) {
if (!ad) return null;
const imageSrc = ad.image?.file_url || ad.image_url || null;
const imageSrc = resolveAssetSrc(ad.image) || ad.image_url || null;
const ctas = Array.isArray(ad.ctas) ? ad.ctas : [];
const resolvedSize = ad.size || size || "md";
const heightClass = SIZE_HEIGHT[resolvedSize] ?? SIZE_HEIGHT.md;
@@ -39,13 +43,13 @@ export function Banner({ ad, size, onCtaClick }) {
className={`relative w-full rounded-lg bg-muted overflow-hidden flex items-center justify-center text-left ${heightClass}`}
>
{imageSrc ? (
<img src={imageSrc} alt={ad.headline || "Advertisement"} className="w-full h-full object-cover" />
<img src={imageSrc} alt={ad.headline || "Advertisement"} className="w-full h-full object-cover pointer-events-none select-none" />
) : (
<Megaphone className="size-6 text-muted-foreground" />
<Megaphone className="size-6 text-muted-foreground pointer-events-none select-none" />
)}
{ad.headline && (
<div className="absolute inset-0 bg-gradient-to-t from-black/60 via-black/0 to-black/0 flex items-end p-4">
<div className="absolute inset-0 bg-gradient-to-t from-black/60 via-black/0 to-black/0 flex items-end p-4 pointer-events-none select-none">
<p className="text-white font-medium text-sm sm:text-base">{ad.headline}</p>
</div>
)}