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
@@ -1,7 +1,7 @@
import { useEffect, useState, useCallback } from "react";
import { useNavigate } from "react-router-dom";
import { useUsers } from "@/contexts/AdminUserContext";
import { House, RefreshCw, ChevronLeft, ChevronRight, ExternalLink, CalendarIcon } from "lucide-react";
import { House, RefreshCw, ChevronLeft, ChevronRight, ExternalLink } from "lucide-react";
import AppBreadcrumb from "@/components/generic/Breadcrumb/AppBreadcrumb";
import { Button } from "@/components/ui/button";
@@ -9,9 +9,8 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
import { Skeleton } from "@/components/ui/skeleton";
import { Badge } from "@/components/ui/badge";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Calendar } from "@/components/ui/calendar";
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
import { DatePickerButton } from "@/components/generic/DatePickerButton";
import { ACTION_CONFIG, getActionBadge } from "@/data/activity.data";
import { timeAgo } from "@/utils/timestamp.util";
@@ -195,57 +194,6 @@ export default function ActivityFeed() {
);
}
// ─── DatePickerButton ─────────────────────────────────────────────────────────
function DatePickerButton({ value, onChange, placeholder, disabled }) {
const [open, setOpen] = useState(false);
const { fmtDate } = useDateFormat();
const label = value ? fmtDate(value) : placeholder;
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
className={`h-8 text-sm w-[150px] justify-start font-normal gap-2 ${!value ? "text-muted-foreground" : ""}`}
>
<CalendarIcon className="size-3.5 shrink-0" />
<span className="truncate">{label}</span>
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={value}
onSelect={(d) => { onChange(d ?? null); setOpen(false); }}
disabled={disabled}
initialFocus
/>
<div className="border-t px-3 py-2 flex gap-2">
<Button
variant="outline"
size="sm"
className="flex-1 h-7 text-xs"
onClick={() => { onChange(new Date()); setOpen(false); }}
>
Today
</Button>
{value && (
<Button
variant="ghost"
size="sm"
className="flex-1 h-7 text-xs text-muted-foreground"
onClick={() => { onChange(null); setOpen(false); }}
>
Clear
</Button>
)}
</div>
</PopoverContent>
</Popover>
);
}
// ─── Row ──────────────────────────────────────────────────────────────────────
function initials(name, email) {
if (name) return name.split(" ").map((n) => n[0]).slice(0, 2).join("").toUpperCase();
@@ -293,10 +241,10 @@ function ActivityRow({ row, onViewUser }) {
{ts ? (
<Tooltip>
<TooltipTrigger asChild>
<span className="text-muted-foreground cursor-default">{timeAgo(ts)}</span>
<span className="text-muted-foreground cursor-default">{fmtDateTime(ts)}</span>
</TooltipTrigger>
<TooltipContent side="left">
{fmtDateTime(ts)}
{timeAgo(ts)}
</TooltipContent>
</Tooltip>
) : (
@@ -8,13 +8,20 @@ import { Button } from "@/components/ui/button";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Skeleton } from "@/components/ui/skeleton";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { DatePickerButton } from "@/components/generic/DatePickerButton";
import { ACTION_CONFIG, getActionBadge } from "@/data/activity.data";
import { timeAgo } from "@/utils/timestamp.util";
import { fmtISO } from "@/utils/datetime.util";
import { useDateFormat } from "@/hooks/useDateFormat";
const LIMIT = 20;
function toDateStr(d) {
if (!d) return undefined;
return fmtISO(d);
}
export default function UserActivityPage() {
const { userId } = useParams();
const navigate = useNavigate();
@@ -22,6 +29,8 @@ export default function UserActivityPage() {
const [page, setPage] = useState(1);
const [action, setAction] = useState("all");
const [from, setFrom] = useState(null);
const [to, setTo] = useState(null);
const load = useCallback(
(p = 1) => {
@@ -29,14 +38,16 @@ export default function UserActivityPage() {
page: p,
limit: LIMIT,
action: action === "all" ? undefined : action,
from: toDateStr(from),
to: toDateStr(to),
});
setPage(p);
},
[fetchUserActivity, userId, action]
[fetchUserActivity, userId, action, from, to]
);
useEffect(() => { fetchUser(userId); }, [userId]);
useEffect(() => { load(1); }, [action, userId]);
useEffect(() => { load(1); }, [action, from, to, userId]);
const displayName = user?.personal_info?.name?.full_name ?? user?.email ?? `User #${userId}`;
@@ -76,7 +87,7 @@ export default function UserActivityPage() {
</div>
{/* ─── Filter ──────────────────────────────────────────────── */}
<div className="flex items-center gap-3 bg-card border rounded-lg p-4">
<div className="flex flex-wrap items-end gap-3 bg-card border rounded-lg p-4">
<div className="flex flex-col gap-1 min-w-[180px]">
<span className="text-xs text-muted-foreground">Filter by action</span>
<Select value={action} onValueChange={setAction}>
@@ -91,10 +102,26 @@ export default function UserActivityPage() {
</SelectContent>
</Select>
</div>
{action !== "all" && (
<div className="flex items-end pt-5">
<Button variant="ghost" size="sm" className="h-8 text-xs" onClick={() => setAction("all")}>
Clear
<div className="flex flex-col gap-1">
<span className="text-xs text-muted-foreground">From</span>
<DatePickerButton value={from} onChange={setFrom} placeholder="Start date" />
</div>
<div className="flex flex-col gap-1">
<span className="text-xs text-muted-foreground">To</span>
<DatePickerButton value={to} onChange={setTo} placeholder="End date" disabled={from ? { before: from } : undefined} />
</div>
{(action !== "all" || from || to) && (
<div className="flex items-end">
<Button
variant="ghost"
size="sm"
className="h-8 text-xs"
onClick={() => { setAction("all"); setFrom(null); setTo(null); }}
>
Clear filters
</Button>
</div>
)}
@@ -187,10 +214,10 @@ function ActivityItem({ row }) {
{ts ? (
<Tooltip>
<TooltipTrigger asChild>
<span className="text-muted-foreground cursor-default">{timeAgo(ts)}</span>
<span className="text-muted-foreground cursor-default">{fmtDateTime(ts)}</span>
</TooltipTrigger>
<TooltipContent side="left">
{fmtDateTime(ts)}
{timeAgo(ts)}
</TooltipContent>
</Tooltip>
) : (