File size: 4,660 Bytes
964fb13 99a2718 cfbfed3 964fb13 50f9fbd cfbfed3 3845801 cfbfed3 964fb13 cfbfed3 50f9fbd cfbfed3 3845801 cfbfed3 964fb13 50f9fbd cfbfed3 50f9fbd cfbfed3 964fb13 50f9fbd cfbfed3 50f9fbd cfbfed3 964fb13 50f9fbd cfbfed3 3845801 50f9fbd 727eb79 cfbfed3 50f9fbd 964fb13 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | // frontend/src/components/admin/AIInsightBox.jsx
import React from "react";
import {
Sparkles,
TrendingUp,
AlertTriangle,
Users as UsersIcon,
Lightbulb,
} from "lucide-react";
import { differenceInDays } from "date-fns";
export default function AIInsightBox({
memberships = [],
onRenewingSoonClick,
onNeedAttentionClick,
onGoodStandingClick,
onSuggestionClick,
}) {
const now = new Date();
const totalMembers = memberships.length;
const renewingSoon = memberships.filter((m) => {
if (m.status !== "active" || !m.renewal_date) return false;
const days = differenceInDays(new Date(m.renewal_date), now);
return days >= 0 && days <= 14;
}).length;
const needAttention = memberships.filter((m) => {
if (!m.renewal_date) return false;
const days = differenceInDays(new Date(m.renewal_date), now);
return m.status === "expired" || days < 0;
}).length;
const goodStanding = memberships.filter((m) => m.status === "active").length;
const suggestionText =
totalMembers === 0
? "Once members join, I’ll highlight who needs your attention first."
: `Focus on the ${needAttention} at-risk member${
needAttention === 1 ? "" : "s"
} this week to improve retention.`;
return (
<div className="mb-8 rounded-3xl border border-purple-200 bg-gradient-to-r from-purple-50 to-purple-100/70 px-4 py-5 sm:px-6 sm:py-6">
<h2 className="text-sm font-semibold text-stone-900 mb-4 flex items-center gap-2">
<span className="inline-flex items-center justify-center h-6 w-6 rounded-full bg-purple-100 text-purple-700">
<Sparkles className="w-3 h-3" />
</span>
AI Assistant Insights
</h2>
<div className="space-y-3">
{/* Row 1: renewing soon */}
<button
type="button"
onClick={onRenewingSoonClick}
className="w-full flex items-center justify-between rounded-2xl bg-white/90 px-4 py-3 shadow-sm hover:shadow-md transition-shadow cursor-pointer text-left"
>
<div className="flex items-center gap-3">
<div className="h-8 w-8 rounded-full bg-emerald-50 flex items-center justify-center">
<TrendingUp className="w-4 h-4 text-emerald-600" />
</div>
<p className="text-sm text-stone-700">
{renewingSoon} member{renewingSoon === 1 ? "" : "s"} are due for
renewal in the next 2 weeks
</p>
</div>
</button>
{/* Row 2: need attention */}
<button
type="button"
onClick={onNeedAttentionClick}
className="w-full flex items-center justify-between rounded-2xl bg-white/90 px-4 py-3 shadow-sm hover:shadow-md transition-shadow cursor-pointer text-left"
>
<div className="flex items-center gap-3">
<div className="h-8 w-8 rounded-full bg-amber-50 flex items-center justify-center">
<AlertTriangle className="w-4 h-4 text-amber-600" />
</div>
<p className="text-sm text-stone-700">
{needAttention} member{needAttention === 1 ? "" : "s"} need
immediate attention for renewal
</p>
</div>
</button>
{/* Row 3: good standing */}
<button
type="button"
onClick={onGoodStandingClick}
className="w-full flex items-center justify-between rounded-2xl bg-white/90 px-4 py-3 shadow-sm hover:shadow-md transition-shadow cursor-pointer text-left"
>
<div className="flex items-center gap-3">
<div className="h-8 w-8 rounded-full bg-blue-50 flex items-center justify-center">
<UsersIcon className="w-4 h-4 text-blue-600" />
</div>
<p className="text-sm text-stone-700">
{goodStanding} active member{goodStanding === 1 ? "" : "s"}{" "}
maintaining good standing
</p>
</div>
</button>
{/* Row 4: suggestion */}
<button
type="button"
onClick={onSuggestionClick}
className="mt-3 w-full rounded-2xl bg-purple-100 px-4 py-3 text-left hover:bg-purple-200/80 transition-colors cursor-pointer flex items-center gap-2"
>
<span className="inline-flex h-7 w-7 items-center justify-center rounded-full bg-yellow-100">
<Lightbulb className="w-4 h-4 text-yellow-500" />
</span>
<p className="text-sm text-purple-900">
<span className="font-semibold">Suggestion:</span> {suggestionText}
</p>
</button>
</div>
</div>
);
}
|