feat: reminder status counts in dashboard card
Напоминания card now shows: не спрош. (gold), придёт (green), не придёт (red) — same visual style as booking cards. Clickable to navigate to reminders tab. Skipped "нет ответа" per user request.
This commit is contained in:
@@ -582,6 +582,9 @@ interface DashboardCounts {
|
|||||||
od: TabCounts;
|
od: TabCounts;
|
||||||
remindersToday: number;
|
remindersToday: number;
|
||||||
remindersTomorrow: number;
|
remindersTomorrow: number;
|
||||||
|
remindersNotAsked: number;
|
||||||
|
remindersComing: number;
|
||||||
|
remindersCancelled: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function countByStatus(items: { status: string }[]): TabCounts {
|
function countByStatus(items: { status: string }[]): TabCounts {
|
||||||
@@ -621,13 +624,17 @@ function DashboardSummary({ statusFilter, onNavigate, onFilter }: {
|
|||||||
return adminFetch(`/api/admin/open-day/bookings?eventId=${active.id}`).then((r) => r.json());
|
return adminFetch(`/api/admin/open-day/bookings?eventId=${active.id}`).then((r) => r.json());
|
||||||
}),
|
}),
|
||||||
adminFetch("/api/admin/reminders").then((r) => r.json()).catch(() => []),
|
adminFetch("/api/admin/reminders").then((r) => r.json()).catch(() => []),
|
||||||
]).then(([gb, mc, od, rem]: [{ status: string }[], { status: string }[], { status: string }[], { eventDate: string }[]]) => {
|
]).then(([gb, mc, od, rem]: [{ status: string }[], { status: string }[], { status: string }[], { eventDate: string; reminderStatus?: string }[]]) => {
|
||||||
|
const upcoming = rem.filter((r) => r.eventDate === today || r.eventDate === tomorrow);
|
||||||
setCounts({
|
setCounts({
|
||||||
classes: countByStatus(gb),
|
classes: countByStatus(gb),
|
||||||
mc: countByStatus(mc),
|
mc: countByStatus(mc),
|
||||||
od: countByStatus(od),
|
od: countByStatus(od),
|
||||||
remindersToday: rem.filter((r) => r.eventDate === today).length,
|
remindersToday: rem.filter((r) => r.eventDate === today).length,
|
||||||
remindersTomorrow: rem.filter((r) => r.eventDate === tomorrow).length,
|
remindersTomorrow: rem.filter((r) => r.eventDate === tomorrow).length,
|
||||||
|
remindersNotAsked: upcoming.filter((r) => !r.reminderStatus).length,
|
||||||
|
remindersComing: upcoming.filter((r) => r.reminderStatus === "coming").length,
|
||||||
|
remindersCancelled: upcoming.filter((r) => r.reminderStatus === "cancelled").length,
|
||||||
});
|
});
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
}, []);
|
}, []);
|
||||||
@@ -667,6 +674,35 @@ function DashboardSummary({ statusFilter, onNavigate, onFilter }: {
|
|||||||
{counts.remindersToday > 0 && <><span className={`text-lg font-bold ${c.urgentColor}`}>{counts.remindersToday}</span><span className="text-[10px] text-neutral-500">сегодня</span></>}
|
{counts.remindersToday > 0 && <><span className={`text-lg font-bold ${c.urgentColor}`}>{counts.remindersToday}</span><span className="text-[10px] text-neutral-500">сегодня</span></>}
|
||||||
{counts.remindersTomorrow > 0 && <><span className="text-sm font-medium text-neutral-400">{counts.remindersTomorrow}</span><span className="text-[10px] text-neutral-500">завтра</span></>}
|
{counts.remindersTomorrow > 0 && <><span className="text-sm font-medium text-neutral-400">{counts.remindersTomorrow}</span><span className="text-[10px] text-neutral-500">завтра</span></>}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex items-baseline gap-2 mt-1">
|
||||||
|
{counts.remindersNotAsked > 0 && (
|
||||||
|
<span className="inline-flex items-baseline gap-1 cursor-pointer hover:underline decoration-neutral-500 underline-offset-2 transition-all"
|
||||||
|
onClick={(e) => { e.stopPropagation(); onNavigate(c.tab); }}>
|
||||||
|
<span className="text-sm font-medium text-gold">{counts.remindersNotAsked}</span>
|
||||||
|
<span className="text-[10px] text-neutral-500">не спрош.</span>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{counts.remindersComing > 0 && (
|
||||||
|
<>
|
||||||
|
{counts.remindersNotAsked > 0 && <span className="text-neutral-700">·</span>}
|
||||||
|
<span className="inline-flex items-baseline gap-1 cursor-pointer hover:underline decoration-neutral-500 underline-offset-2 transition-all"
|
||||||
|
onClick={(e) => { e.stopPropagation(); onNavigate(c.tab); }}>
|
||||||
|
<span className="text-sm font-medium text-emerald-400">{counts.remindersComing}</span>
|
||||||
|
<span className="text-[10px] text-neutral-500">придёт</span>
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{counts.remindersCancelled > 0 && (
|
||||||
|
<>
|
||||||
|
{(counts.remindersNotAsked > 0 || counts.remindersComing > 0) && <span className="text-neutral-700">·</span>}
|
||||||
|
<span className="inline-flex items-baseline gap-1 cursor-pointer hover:underline decoration-neutral-500 underline-offset-2 transition-all"
|
||||||
|
onClick={(e) => { e.stopPropagation(); onNavigate(c.tab); }}>
|
||||||
|
<span className="text-sm font-medium text-red-400">{counts.remindersCancelled}</span>
|
||||||
|
<span className="text-[10px] text-neutral-500">не придёт</span>
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user