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
+17 -22
View File
@@ -4,7 +4,6 @@ import {
Users, Timer,
Tag, LockIcon, Check,
} from "lucide-react";
import { ThemeSwitcher } from "../components/ThemeSwitcher";
import AppBreadcrumb from "@/components/generic/Breadcrumb/AppBreadcrumb";
import { Badge } from "@/components/ui/badge";
import { Skeleton } from "@/components/ui/skeleton";
@@ -131,13 +130,15 @@ const GroupsTable = ({ groups, onView }) => (
<TableHead className="w-10 text-center px-4">#</TableHead>
<TableHead className="px-4">Group Name</TableHead>
<TableHead className="px-4">Code</TableHead>
<TableHead className="px-4 w-full">Description</TableHead>
<TableHead className="px-4">Description</TableHead>
<TableHead className="px-4">Task Lists</TableHead>
<TableHead className="px-4" />
</TableRow>
</TableHeader>
<TableBody>
{groups.map((g, i) => {
const isDefault = g.group_code === 'NOGRP';
const taskListCount = Number(g.task_list_count ?? g.taskLists?.length ?? 0);
return (
<TableRow key={g.group_id}>
<TableCell className="text-center px-4 text-muted-foreground tabular-nums">
@@ -147,12 +148,15 @@ const GroupsTable = ({ groups, onView }) => (
<TableCell className="px-4">
<Badge variant="outline" className="font-mono text-xs">{g.group_code}</Badge>
</TableCell>
<TableCell className="px-4 text-muted-foreground">
<TableCell className="px-4 text-muted-foreground ">
{isDefault
? <span className="text-xs italic">Awaiting assignment by admin</span>
: (g.description ?? <span className="text-xs text-muted-foreground/50">—</span>)
}
</TableCell>
<TableCell className="px-4">
{taskListCount}
</TableCell>
<TableCell className="px-4 text-right">
<Button size="sm" variant="outline" onClick={() => onView(g)}>
View
@@ -175,6 +179,7 @@ const GroupsTableSkeleton = () => (
<TableHead className="px-4">Group Name</TableHead>
<TableHead className="px-4">Code</TableHead>
<TableHead className="px-4 w-full">Description</TableHead>
<TableHead className="px-4 text-center whitespace-nowrap">Task Lists</TableHead>
<TableHead className="px-4" />
</TableRow>
</TableHeader>
@@ -185,6 +190,7 @@ const GroupsTableSkeleton = () => (
<TableCell className="px-4"><Skeleton className="h-4 w-32" /></TableCell>
<TableCell className="px-4"><Skeleton className="h-5 w-16 rounded-full" /></TableCell>
<TableCell className="px-4"><Skeleton className="h-4 w-48" /></TableCell>
<TableCell className="px-4"><Skeleton className="h-4 w-8 mx-auto" /></TableCell>
<TableCell className="px-4 text-right"><Skeleton className="h-8 w-14 ml-auto" /></TableCell>
</TableRow>
))}
@@ -201,7 +207,7 @@ const Client = () => {
const { courses, coursesLoading, getCourses } = useClientCourses();
const { myTier, getMyTier, tierMap } = useClientTiers();
const { groups, fetchGroups, loading: groupLoading } = useGroup();
const { advertisements, loading: adLoading, getActiveAdvertisement, trackClick } = useClientAdvertisements();
const { advertisements, loading: adLoading, getActiveAdvertisements, handleAdCtaClick, dismissPopupForever } = useClientAdvertisements();
const [modalOpen, setModalOpen] = useState(false);
const [selectedCourse, setSelectedCourse] = useState(null);
@@ -210,8 +216,8 @@ const Client = () => {
const userTier = myTier?.tier ?? "free";
const heroAd = advertisements.hero ?? null;
const popupAd = advertisements.popup ?? null;
const heroAd = advertisements["dashboard.hero"] ?? null;
const popupAd = advertisements["dashboard.popup"] ?? null;
// Show welcome toast on first registration
useEffect(() => {
@@ -234,24 +240,12 @@ const Client = () => {
// ── Resolve active hero + popup ads once on mount ────────────────────────
useEffect(() => {
getActiveAdvertisement("hero");
getActiveAdvertisement("popup").then((ad) => {
if (ad) setPopupOpen(true);
getActiveAdvertisements(["dashboard.hero", "dashboard.popup"]).then((result) => {
if (result["dashboard.popup"]) setPopupOpen(true);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// ── CTA click — track then navigate ───────────────────────────────────────
const handleAdCtaClick = (ad, cta) => {
trackClick(ad.advertisement_id);
if (!cta?.link) return;
if (/^https?:\/\//.test(cta.link)) {
window.open(cta.link, "_blank", "noopener,noreferrer");
} else {
navigate(cta.link);
}
};
// Show only first 3
const featuredCourses = courses.slice(0, 3);
@@ -277,7 +271,7 @@ const Client = () => {
<div className="flex flex-col gap-8 justify-between lg:container lg:mx-auto pt-8 px-16">
{/* ── Hero Advertisement ── */}
{adLoading.hero ? (
{adLoading["dashboard.hero"] ? (
<HeroSkeleton />
) : (
<Hero ad={heroAd} onCtaClick={handleAdCtaClick} />
@@ -342,6 +336,7 @@ const Client = () => {
open={popupOpen}
onOpenChange={setPopupOpen}
onCtaClick={handleAdCtaClick}
onDismissForever={dismissPopupForever}
/>
{/* ── Upsell Modal — only for locked courses ── */}
@@ -387,4 +382,4 @@ const Client = () => {
);
};
export default Client;
export default Client;