"use client"; import { useEffect, useMemo, useState } from "react"; import Link from "next/link"; import { AlertTriangle, Users, ChevronRight } from "lucide-react"; import { PageHeader } from "@/components/common/page-header"; import { HostBadge } from "@/components/common/host-badge"; import { LearningApplicationBadge } from "@/components/common/learning-application-badge"; import { EmptyState } from "@/components/common/empty-state"; import { DeleteAllButton } from "@/components/common/delete-all-button"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { reflexio } from "@/lib/reflexio-client"; import { formatRelative, truncateId } from "@/lib/format"; import { useRequestHostAttribution } from "@/lib/host-attribution"; import type { PlaybookApplicationStat, UserProfile } from "@/lib/types"; type PreferenceSort = "newest" | "applied"; function profileStatKey(profile: UserProfile): string { return `profile:profile:${profile.profile_id}`; } export default function PreferencesPage() { const [profiles, setProfiles] = useState(null); const [appStats, setAppStats] = useState( null, ); const [error, setError] = useState(null); const [sortBy, setSortBy] = useState("newest"); const [filter, setFilter] = useState(""); const attribution = useRequestHostAttribution(); useEffect(() => { let cancelled = false; async function load() { try { const [profileRes, statsRes] = await Promise.all([ reflexio.getAllProfiles(), fetch("/api/rules/applied?daysBack=30&limit=200", { cache: "no-store", }) .then((r) => r.json()) .catch(() => ({ success: false, stats: [] as PlaybookApplicationStat[], })), ]); if (cancelled) return; setProfiles(profileRes.user_profiles ?? []); setAppStats(statsRes.stats ?? []); setError(null); } catch (e) { if (!cancelled) { setError(e instanceof Error ? e.message : String(e)); } } } load(); return () => { cancelled = true; }; }, []); const statsByProfile = useMemo(() => { const map = new Map(); for (const s of appStats ?? []) { map.set(`${s.kind}:${s.source_kind ?? "unknown"}:${s.real_id}`, s); } return map; }, [appStats]); const filtered = useMemo(() => { const query = filter.toLowerCase(); const matches = (profiles ?? []).filter( (p) => p.content.toLowerCase().includes(query) || p.user_id.toLowerCase().includes(query), ); return matches.sort((a, b) => { if (sortBy === "applied") { const aStat = statsByProfile.get(profileStatKey(a)); const bStat = statsByProfile.get(profileStatKey(b)); const appliedDelta = (bStat?.applied_count ?? 0) - (aStat?.applied_count ?? 0); if (appliedDelta !== 0) return appliedDelta; const recencyDelta = (bStat?.last_applied_at ?? 0) - (aStat?.last_applied_at ?? 0); if (recencyDelta !== 0) return recencyDelta; } return b.last_modified_timestamp - a.last_modified_timestamp; }); }, [filter, profiles, sortBy, statsByProfile]); return (
setFilter(e.target.value)} placeholder="Filter" className="h-9 w-56 text-xs bg-background/80" /> 0 ? ` (${profiles.length})` : ""}`} confirmMessage={`Delete ALL ${profiles?.length ?? 0} preferences? Preferences regenerate from fresh interactions, but this cannot be undone.`} disabled={!profiles || profiles.length === 0} onConfirm={async () => { await reflexio.deleteAllProfiles(); setProfiles([]); }} />
} />
{error && (
{error}. Is reflexio running on the configured backend URL?
)} {attribution?.unavailable && !error && (
Preference source details are temporarily unavailable. Preferences remain available.
)} {profiles === null && !error ? (
Loading…
) : filtered.length === 0 ? ( ) : (
{filtered.map((p) => { const stat = statsByProfile.get(profileStatKey(p)); return (
{truncateId(p.user_id, 32, 8)} {p.status && ( {p.status} )}
{formatRelative(p.last_modified_timestamp)}

{p.content}

{attribution && !attribution.unavailable && p.generated_from_request_id && ( )} {p.source && ( Integration: {p.source} )}
); })}
)}
); }