try to deploy

Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
2026-07-13 21:27:56 +08:00
parent bfc4b9fc67
commit b1805cfe4d
87 changed files with 798 additions and 718 deletions
@@ -13,6 +13,7 @@ import {
import { useAdvertisements } from "@/contexts/AdminAdvertisementContext";
import { useAuth } from "@/contexts/AuthContext";
import { useUnsavedChangesGuard } from "@/hooks/useUnsavedChangesGuard";
import { useDateFormat } from "@/hooks/useDateFormat";
import { resolveAssetSrc } from "@/utils/media.util";
import { cn } from "@/lib/utils";
import AppBreadcrumb from "@/components/generic/Breadcrumb/AppBreadcrumb";
@@ -453,6 +454,7 @@ function SummaryRow({ label, value }) {
}
function StepReview({ data, selectedAsset, imageUrl }) {
const { fmtDateTime } = useDateFormat();
const placementMeta = PLACEMENT_MAP[data.placement];
const ctas = (data.ctas ?? []).filter((c) => c.label || c.link);
const hasLandingPage = !data.redirect_link && (data.landing_page?.title || data.landing_page?.body);
@@ -519,8 +521,8 @@ function StepReview({ data, selectedAsset, imageUrl }) {
<div className="border rounded-lg p-4 space-y-1">
<p className="text-sm font-medium mb-2">Scheduling & display</p>
<SummaryRow label="Start date" value={data.start_date} />
<SummaryRow label="End date" value={data.end_date} />
<SummaryRow label="Start date" value={data.start_date ? fmtDateTime(data.start_date) : null} />
<SummaryRow label="End date" value={data.end_date ? fmtDateTime(data.end_date) : null} />
<SummaryRow label="Order" value={data.order} />
<SummaryRow label="Status" value={data.is_active ? "Active" : "Draft"} />
</div>
@@ -693,7 +695,7 @@ export default function AddAdvertisement() {
<Button
type="button"
variant="outline"
onClick={stepIndex === 0 ? () => navigate(-1) : handleBack}
onClick={stepIndex === 0 ? () => navigate("/admin/advertisements") : handleBack}
disabled={loading}
>
<ChevronLeft className="size-4" />
@@ -17,11 +17,11 @@ import {
AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { ADVERTISEMENT_TYPES, ADVERTISEMENT_TYPE_MAP, ADVERTISEMENT_STATUSES, ADVERTISEMENT_STATUS_MAP } from "@/data/advertisement.data";
import { ADVERTISEMENT_TYPES, ADVERTISEMENT_TYPE_MAP, ADVERTISEMENT_FILTERABLE_STATUSES, ADVERTISEMENT_STATUS_MAP } from "@/data/advertisement.data";
import { PLACEMENT_MAP } from "@/data/placement.data";
import { TablePagination } from "@/components/generic/Table/TablePagination";
const PAGE_SIZE = 24;
const DEFAULT_PAGE_SIZE = 10;
export default function AdvertisementList() {
const navigate = useNavigate();
@@ -32,6 +32,7 @@ export default function AdvertisementList() {
const [searchInput, setSearchInput] = useState("");
const [search, setSearch] = useState("");
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(DEFAULT_PAGE_SIZE);
const buildFilters = () => {
const filters = [];
@@ -45,13 +46,14 @@ export default function AdvertisementList() {
// their state update with setPage(1) in the same handler so this only
// ever fires once per change (no separate "reset page" effect racing it).
useEffect(() => {
fetchAdvertisements({ page, limit: PAGE_SIZE, filters: buildFilters() });
fetchAdvertisements({ page, limit: pageSize, filters: buildFilters() });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [typeFilter, statusFilter, search, page]);
}, [typeFilter, statusFilter, search, page, pageSize]);
const handleTypeFilter = (v) => { setTypeFilter(v); setPage(1); };
const handleStatusFilter = (v) => { setStatusFilter(v); setPage(1); };
const runSearch = () => { setSearch(searchInput); setPage(1); };
const handlePageSizeChange = (size) => { setPageSize(size); setPage(1); };
const items = [
{ label: "Home", icon: <House className="size-4" />, to: "/admin" },
@@ -61,7 +63,6 @@ export default function AdvertisementList() {
const total = pagination?.totalRecords ?? advertisements.length;
const activeCount = advertisements.filter((a) => a.status === "active").length;
const scheduledCount = advertisements.filter((a) => a.status === "scheduled").length;
const expiredCount = advertisements.filter((a) => a.status === "expired").length;
async function handleArchive(advertisementId) {
await archiveAdvertisement(advertisementId);
@@ -95,11 +96,10 @@ export default function AdvertisementList() {
</div>
{/* ── Stat cards ─────────────────────────────────────────────── */}
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
<StatCard label="Total ads" value={total} />
<StatCard label="Active" value={activeCount} tone="success" />
<StatCard label="Scheduled" value={scheduledCount} tone="info" />
<StatCard label="Expired" value={expiredCount} tone="muted" />
</div>
{/* ── Filters ────────────────────────────────────────────────── */}
@@ -122,7 +122,7 @@ export default function AdvertisementList() {
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All statuses</SelectItem>
{ADVERTISEMENT_STATUSES.map((s) => (
{ADVERTISEMENT_FILTERABLE_STATUSES.map((s) => (
<SelectItem key={s.value} value={s.value}>{s.label}</SelectItem>
))}
</SelectContent>
@@ -170,6 +170,7 @@ export default function AdvertisementList() {
<TablePagination
pagination={pagination}
onPageChange={setPage}
onPageSizeChange={handlePageSizeChange}
rowCount={advertisements.length}
recordLabel="advertisement"
/>
@@ -203,7 +204,7 @@ function StatCard({ label, value, tone = "default" }) {
// ─── Advertisement card ─────────────────────────────────────────────────────
function AdvertisementCard({ ad, onView, onEdit, onArchive }) {
const { fmtDate } = useDateFormat();
const { fmtDateTime } = useDateFormat();
const typeMeta = ADVERTISEMENT_TYPE_MAP[ad.type] ?? {};
const statusMeta = ADVERTISEMENT_STATUS_MAP[ad.status] ?? {};
const placementMeta = PLACEMENT_MAP[ad.placement] ?? null;
@@ -212,7 +213,7 @@ function AdvertisementCard({ ad, onView, onEdit, onArchive }) {
const previewSrc = resolveAssetSrc(ad.image) || ad.image_url || null;
const isDimmed = ad.status === "expired" || ad.status === "archived";
const dateRange = formatDateRange(ad.start_date, ad.end_date, fmtDate);
const dateRange = formatDateRange(ad.start_date, ad.end_date, fmtDateTime);
return (
<div className={`bg-background rounded-lg border overflow-hidden flex flex-col ${isDimmed ? "opacity-70" : ""}`}>
@@ -213,7 +213,7 @@ export default function EditAdvertisement() {
}, [advertisementId]);
const onSubmit = async (values) => {
if (!isDirty) { bypassOnce(); return navigate(-1); }
if (!isDirty) { bypassOnce(); return navigate("/admin/advertisements"); }
const payload = {
...values,
@@ -506,7 +506,7 @@ export default function EditAdvertisement() {
</SectionCard>
<div className="flex justify-end gap-2 pt-2">
<Button type="button" variant="outline" onClick={() => navigate(-1)} disabled={loading}>
<Button type="button" variant="outline" onClick={() => navigate("/admin/advertisements")} disabled={loading}>
Cancel
</Button>
<Button type="submit" disabled={loading}>