testing 101

Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
2026-06-30 19:32:00 +08:00
parent fe47bdea3c
commit 17326b2c2e
78 changed files with 4804 additions and 1054 deletions
+48 -18
View File
@@ -21,6 +21,7 @@ import { useClientTiers } from "@/contexts/ClientTiersProvider";
import { useDateFormat } from "@/hooks/useDateFormat";
import api from "@/utils/api.util";
import { toast } from "sonner";
import CourseBadge from "@/modules/admin/components/courses/CourseBadge";
// ─── Tier badge fallbacks (used when no policy is configured in DB) ───────────
@@ -113,22 +114,7 @@ const getFallbackIcon = (type) => type === "badge" ? BadgeCheck : Trophy;
// ─── Certificate landscape card (profile preview) ────────────────────────────
const CertBadgeIcon = ({ className }) => (
<svg className={className} width="120" height="120" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Prism logo">
<defs>
<linearGradient id="prism-p" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stopColor="#8EA2F6"/>
<stop offset="1" stopColor="#5061E6"/>
</linearGradient>
</defs>
<g transform="rotate(45 60 60)">
<rect x="24" y="24" width="72" height="72" rx="16" fill="url(#prism-p)"/>
<rect x="46" y="46" width="28" height="28" rx="6" fill="#FFFFFF"/>
</g>
</svg>
);
const LandscapeCertCard = ({ courseTitle, issuedAt, courseUuid }) => {
const LandscapeCertCard = ({ courseTitle, issuedAt, courseUuid, badgeColor, badgeImageUrl }) => {
const [downloading, setDownloading] = useState(false);
const { fmtDate } = useDateFormat();
@@ -156,7 +142,7 @@ const LandscapeCertCard = ({ courseTitle, issuedAt, courseUuid }) => {
return (
<div className="rounded-xl border bg-muted/40 p-3.5 flex items-center gap-3.5">
<CertBadgeIcon className="w-12 h-12 shrink-0" />
<CourseBadge mini color={badgeColor ?? "purple"} imageUrl={badgeImageUrl ?? null} />
<div className="flex-1 min-w-0">
<p className="text-[10px] uppercase tracking-widest text-muted-foreground font-semibold">Certificate of Completion</p>
<p className="text-sm font-semibold truncate mt-0.5">{courseTitle}</p>
@@ -195,6 +181,20 @@ const ProfilePage = () => {
const [badgeOpen, setBadgeOpen] = useState(false);
const [selectedBadge, setSelectedBadge] = useState(null);
const [badgeSrc, setBadgeSrc] = useState(null);
const [firstCertBadge, setFirstCertBadge] = useState({ color: "purple", imageUrl: null });
useEffect(() => {
const asset = myTier?.plan?.category?.badgeAsset ?? myTier?.category?.badgeAsset
if (!asset) { setBadgeSrc(null); return }
if (asset.storage_provider !== 's3') { setBadgeSrc(asset.file_url ?? null); return }
api.post('/client/media/token', { asset_id: asset.asset_id })
.then(({ data }) => {
const base = (import.meta.env.VITE_API_URL ?? '').replace(/\/$/, '')
setBadgeSrc(`${base}/client/media/stream/${data?.data?.token}`)
})
.catch(() => setBadgeSrc(null))
}, [myTier])
const [inProgressCourses, setInProgressCourses] = useState([]);
const [inProgressCoursesLoading, setInProgressCoursesLoading] = useState(false);
@@ -222,7 +222,7 @@ const ProfilePage = () => {
// ── Derived ────────────────────────────────────────────────────────────────
const tier = myTier?.tier ?? user?.tier ?? "free";
const tierBadge = resolveTierBadge(myTier);
const tierBadge = { ...resolveTierBadge(myTier), ...(badgeSrc ? { src: badgeSrc } : {}) };
const earlyAccessBadge = resolveEarlyAccessBadge(systemBadges);
const displayName = fullName || user?.personal_info?.name?.full_name || user?.email?.split("@")[0] || "—";
const initials = displayName.split(" ").map((w) => w[0]).join("").slice(0, 2).toUpperCase();
@@ -232,6 +232,34 @@ const ProfilePage = () => {
const certAchievements = achievements.filter((a) => a.key.startsWith("course_completed_"));
// Fetch badge color/image for the first certificate shown in the profile preview
useEffect(() => {
if (!certAchievements.length) return;
const first = certAchievements[0];
const uuid = first.metadata?.courseUuid ?? first.key.replace("course_completed_", "");
if (!uuid) return;
const STREAM_BASE = `${import.meta.env.VITE_API_URL}/client/media/stream`;
api.get(`/client/courses/uuid/${uuid}`)
.then(({ data }) => {
const c = data.data ?? data;
if (!c) return;
if (c.badge_asset_id) {
api.post("/client/media/token", { asset_id: c.badge_asset_id })
.then(({ data: td }) => {
const token = td.data?.token;
setFirstCertBadge({
color: c.badge_color ?? "purple",
imageUrl: token ? `${STREAM_BASE}/${token}` : null,
});
})
.catch(() => setFirstCertBadge({ color: c.badge_color ?? "purple", imageUrl: null }));
} else {
setFirstCertBadge({ color: c.badge_color ?? "purple", imageUrl: c.badge_image_url ?? null });
}
})
.catch(() => {});
}, [certAchievements.length]);
const hasEarlyAccess = achievements.some((a) => a.key === "early_access");
// Premium badge only shows when currently exclusive (went through premium to get here)
const hasPremiumBadge = achievements.some((a) => a.key === "premium_first_time") && userRank >= 2;
@@ -612,6 +640,8 @@ const ProfilePage = () => {
courseTitle={certAchievements[0].description}
issuedAt={certAchievements[0].granted_at}
courseUuid={certAchievements[0].metadata?.courseUuid ?? certAchievements[0].key.replace("course_completed_", "")}
badgeColor={firstCertBadge.color}
badgeImageUrl={firstCertBadge.imageUrl}
/>
</CardContent>
)}