Case completion and tasks completion gadgets added.

This commit is contained in:
2026-07-12 02:08:54 +03:30
parent d79aab907c
commit 0f1004a34a
10 changed files with 616 additions and 87 deletions

View File

@@ -235,8 +235,16 @@
"chartCaseCompletionSubtitle": "All active cases",
"chartCaseCompletionPercent": "{percent}%",
"chartCaseCompletionTasks": "Tasks completed",
"chartTreatmentPlanCompletionTitle": "Treatment Plan Completion",
"chartTreatmentPlanCompletionSubtitle": "All appointments",
"chartTreatmentPlanCompletionRatio": "With treatment plan",
"chartTasksByProsthesisTitle": "In-Progress Tasks by Prosthesis",
"chartTasksByProsthesisSubtitle": "Current workload mix",
"chartCasePartnersClinicTitle": "Cases by Lab",
"chartCasePartnersLabTitle": "Cases by Clinic",
"chartCasePartnersSubtitle": "Last 30 days",
"chartCasePartnersSentLegend": "Sent",
"chartCasePartnersOpenLegend": "In progress",
"chartEfficiencyReportTitle": "Efficiency Report",
"chartEfficiencyReportSubtitleClinic": "Treatments created by staff — last 30 days",
"chartEfficiencyReportSubtitleLab": "Tasks completed by staff — last 30 days",

View File

@@ -235,8 +235,16 @@
"chartCaseCompletionSubtitle": "همه پرونده‌های فعال",
"chartCaseCompletionPercent": "{percent}٪",
"chartCaseCompletionTasks": "وظایف تکمیل‌شده",
"chartTreatmentPlanCompletionTitle": "تکمیل طرح درمان",
"chartTreatmentPlanCompletionSubtitle": "همه نوبت‌ها",
"chartTreatmentPlanCompletionRatio": "دارای طرح درمان",
"chartTasksByProsthesisTitle": "وظایف در حال انجام بر اساس پروتز",
"chartTasksByProsthesisSubtitle": "ترکیب بار کاری فعلی",
"chartCasePartnersClinicTitle": "کیس‌ها بر اساس لابراتوار",
"chartCasePartnersLabTitle": "کیس‌ها بر اساس کلینیک",
"chartCasePartnersSubtitle": "۳۰ روز گذشته",
"chartCasePartnersSentLegend": "ارسال‌شده",
"chartCasePartnersOpenLegend": "در حال انجام",
"chartEfficiencyReportTitle": "گزارش کارایی",
"chartEfficiencyReportSubtitleClinic": "درمان‌های ثبت‌شده توسط کارکنان — ۳۰ روز گذشته",
"chartEfficiencyReportSubtitleLab": "وظایف تکمیل‌شده توسط کارکنان — ۳۰ روز گذشته",

View File

@@ -235,8 +235,16 @@
"chartCaseCompletionSubtitle": "Alle actieve cases",
"chartCaseCompletionPercent": "{percent}%",
"chartCaseCompletionTasks": "Taken voltooid",
"chartTreatmentPlanCompletionTitle": "Behandelplanvoltooiing",
"chartTreatmentPlanCompletionSubtitle": "Alle afspraken",
"chartTreatmentPlanCompletionRatio": "Met behandelplan",
"chartTasksByProsthesisTitle": "Lopende taken per prothese",
"chartTasksByProsthesisSubtitle": "Huidige werklastmix",
"chartCasePartnersClinicTitle": "Cases per lab",
"chartCasePartnersLabTitle": "Cases per kliniek",
"chartCasePartnersSubtitle": "Afgelopen 30 dagen",
"chartCasePartnersSentLegend": "Verzonden",
"chartCasePartnersOpenLegend": "In uitvoering",
"chartEfficiencyReportTitle": "Efficiëntierapport",
"chartEfficiencyReportSubtitleClinic": "Behandelingen aangemaakt door medewerkers — afgelopen 30 dagen",
"chartEfficiencyReportSubtitleLab": "Taken voltooid door medewerkers — afgelopen 30 dagen",

View File

@@ -1,42 +1,44 @@
'use client';
import { useTranslations } from 'next-intl';
import { Package } from 'lucide-react';
import type { LucideIcon } from 'lucide-react';
import { Link } from '@/i18n/navigation';
import { Card } from '@/components/ui/shared/Card';
import { TODAY_CHART_COMPLETED_COLOR } from '@/components/today/chart-theme';
import { TodayRadialGaugeChart } from '@/components/today/TodayRadialGaugeChart';
import type { TodayCompletionGauge } from '@/types/today';
interface TodayCaseCompletionKpiCardProps {
completed: number;
total: number;
percent: number;
export interface TodayCompletionGaugeKpiCardProps extends TodayCompletionGauge {
title: string;
subtitle: string;
percentLabel: string;
ratioLabel: string;
href: string;
icon: LucideIcon;
}
export function TodayCaseCompletionKpiCard({
export function TodayCompletionGaugeKpiCard({
completed,
total,
percent,
}: TodayCaseCompletionKpiCardProps) {
const t = useTranslations('today');
const percentLabel =
total > 0 ? t('chartCaseCompletionPercent', { percent }) : '—';
title,
subtitle,
percentLabel,
ratioLabel,
href,
icon: Icon,
}: TodayCompletionGaugeKpiCardProps) {
return (
<Link
href="/cases"
href={href}
className="block h-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60 rounded-[var(--radius-lg)]"
>
<Card className="flex h-full min-h-0 flex-col transition-opacity hover:opacity-90">
<div className="flex items-start justify-between gap-3">
<div className="min-w-0">
<p className="text-sm font-medium">{t('chartCaseCompletionTitle')}</p>
<p className="mt-0.5 truncate text-xs text-text-muted">
{t('chartCaseCompletionSubtitle')}
</p>
<p className="text-sm font-medium">{title}</p>
<p className="mt-0.5 truncate text-xs text-text-muted">{subtitle}</p>
</div>
<Package className="h-4 w-4 shrink-0 !text-current" aria-hidden />
<Icon className="h-4 w-4 shrink-0 !text-current" aria-hidden />
</div>
<div className="mt-2 flex min-h-0 flex-1 items-center justify-center">
@@ -49,8 +51,8 @@ export function TodayCaseCompletionKpiCard({
percent={total > 0 ? percent : 0}
completed={completed}
total={total}
percentLabel={percentLabel}
tasksLabel={t('chartCaseCompletionTasks')}
percentLabel={total > 0 ? percentLabel : '—'}
tasksLabel={ratioLabel}
fillColor={TODAY_CHART_COMPLETED_COLOR}
showRatio={total > 0}
/>

View File

@@ -4,6 +4,8 @@ import { useMemo } from 'react';
import { useTranslations } from 'next-intl';
import { useAuth } from '@/lib/hooks/useAuth';
import {
canEditCases,
canEditTreatment,
canViewAppointmentsTab,
canViewCases,
canViewLabCasesOrTasks,
@@ -22,7 +24,9 @@ import {
import { TodayDashboardGrid } from '@/components/today/TodayDashboardGrid';
import { TodayDonutChart, TodayDonutChartLegend } from '@/components/today/TodayDonutChart';
import { TodayHorizontalBarChart } from '@/components/today/TodayHorizontalBarChart';
import { TodayCaseCompletionKpiCard } from '@/components/today/TodayCaseCompletionKpiCard';
import { TodayPartnerCasesStackedBarChart } from '@/components/today/TodayPartnerCasesStackedBarChart';
import { Package, Stethoscope, type LucideIcon } from 'lucide-react';
import { TodayCompletionGaugeKpiCard } from '@/components/today/TodayCompletionGaugeKpiCard';
import {
mapLabTaskActivityChartData,
TodayLabTaskActivityChart,
@@ -42,6 +46,7 @@ import { getEligibleTodayKpis, getVisibleTodayKpis } from '@/components/today/wi
import { prosthesisTypeColor } from '@/components/ui/treatment/prosthesisTypeDisplay';
import { treatmentTypeColor } from '@/components/ui/treatment/treatmentTypeDisplay';
import type {
TodayCompletionGauge,
TodaySubscriptionSnapshot,
TodaySummaryActions,
TodaySummaryCharts,
@@ -78,6 +83,12 @@ export function TodayDashboard({
currentOrganization &&
canViewMyAppointmentsWeekChart(currentOrganization);
const showCasePartnersChart = Boolean(
currentOrganization &&
((orgType === 'CLINIC' && canEditTreatment(currentOrganization)) ||
(orgType === 'LAB' && canEditCases(currentOrganization))),
);
const showCharts = useMemo(() => {
if (!orgType || !currentOrganization) return false;
if (orgType === 'CLINIC') {
@@ -104,12 +115,18 @@ export function TodayDashboard({
Boolean(currentOrganization && canViewCases(currentOrganization)) &&
(isInitialLoad || charts.caseCompletion !== undefined);
const showTreatmentPlanCompletionCard =
orgType === 'CLINIC' &&
Boolean(currentOrganization && canEditTreatment(currentOrganization)) &&
(isInitialLoad || charts.treatmentPlanCompletion !== undefined);
const cells = useMemo(() => {
if (isInitialLoad) {
return buildSkeletonCells({
kpiDefinitions,
showSubscriptionCard,
showCaseCompletionCard,
showTreatmentPlanCompletionCard,
showUpcoming: Boolean(showUpcoming),
showCharts,
orgType,
@@ -118,6 +135,7 @@ export function TodayDashboard({
currentOrganization &&
canViewMyAppointmentsWeekChart(currentOrganization),
),
showCasePartnersChart,
charts,
});
}
@@ -133,6 +151,9 @@ export function TodayDashboard({
showSubscriptionCard: showSubscriptionCard && Boolean(subscription),
showCaseCompletionCard:
showCaseCompletionCard && charts.caseCompletion !== undefined,
showTreatmentPlanCompletionCard:
showTreatmentPlanCompletionCard &&
charts.treatmentPlanCompletion !== undefined,
showUpcoming: Boolean(showUpcoming),
showCharts,
orgType,
@@ -144,6 +165,7 @@ export function TodayDashboard({
kpiDefinitions,
showSubscriptionCard,
showCaseCompletionCard,
showTreatmentPlanCompletionCard,
showUpcoming,
showCharts,
orgType,
@@ -176,11 +198,13 @@ function buildSkeletonCells(options: {
kpiDefinitions: ReturnType<typeof getEligibleTodayKpis>;
showSubscriptionCard: boolean;
showCaseCompletionCard: boolean;
showTreatmentPlanCompletionCard: boolean;
showUpcoming: boolean;
showCharts: boolean;
orgType?: 'CLINIC' | 'LAB';
isOwner: boolean;
showMyAppointmentsWeekChart: boolean;
showCasePartnersChart: boolean;
charts: TodaySummaryCharts;
}): TodayDashboardCell[] {
const cells: TodayDashboardCell[] = [];
@@ -191,6 +215,7 @@ function buildSkeletonCells(options: {
options.orgType,
options.isOwner,
options.showMyAppointmentsWeekChart,
options.showCasePartnersChart,
);
for (let index = 0; index < Math.min(chartCount, 4); index += 1) {
cells.push({
@@ -237,6 +262,14 @@ function buildSkeletonCells(options: {
});
}
if (options.showTreatmentPlanCompletionCard) {
cells.push({
id: 'treatment-plan-completion-skeleton',
layout: TODAY_DASHBOARD_LAYOUT.subscription,
content: <KpiCardSkeleton tall />,
});
}
for (const definition of options.kpiDefinitions) {
cells.push({
id: `kpi-skeleton-${definition.key}`,
@@ -258,6 +291,7 @@ function buildDashboardCells(options: {
kpiDefinitions: ReturnType<typeof getVisibleTodayKpis>;
showSubscriptionCard: boolean;
showCaseCompletionCard: boolean;
showTreatmentPlanCompletionCard: boolean;
showUpcoming: boolean;
showCharts: boolean;
orgType?: 'CLINIC' | 'LAB';
@@ -277,6 +311,12 @@ function buildDashboardCells(options: {
options.currentOrganization &&
canViewMyAppointmentsWeekChart(options.currentOrganization),
),
showCasePartnersChart:
Boolean(options.currentOrganization) &&
((options.orgType === 'CLINIC' &&
canEditTreatment(options.currentOrganization)) ||
(options.orgType === 'LAB' &&
canEditCases(options.currentOrganization))),
dayLabelFormatter: options.dayLabelFormatter,
}),
);
@@ -301,17 +341,35 @@ function buildDashboardCells(options: {
}
if (options.showCaseCompletionCard && options.charts.caseCompletion !== undefined) {
const caseCompletion = options.charts.caseCompletion;
cells.push({
pushCompletionGaugeCell(cells, {
id: 'case-completion',
layout: TODAY_DASHBOARD_LAYOUT.subscription,
content: (
<TodayCaseCompletionKpiCard
completed={caseCompletion.completed}
total={caseCompletion.total}
percent={caseCompletion.percent}
/>
),
gauge: options.charts.caseCompletion,
title: options.t('chartCaseCompletionTitle'),
subtitle: options.t('chartCaseCompletionSubtitle'),
percentLabel: options.t('chartCaseCompletionPercent', {
percent: options.charts.caseCompletion.percent,
}),
ratioLabel: options.t('chartCaseCompletionTasks'),
href: '/cases',
icon: Package,
});
}
if (
options.showTreatmentPlanCompletionCard &&
options.charts.treatmentPlanCompletion !== undefined
) {
pushCompletionGaugeCell(cells, {
id: 'treatment-plan-completion',
gauge: options.charts.treatmentPlanCompletion,
title: options.t('chartTreatmentPlanCompletionTitle'),
subtitle: options.t('chartTreatmentPlanCompletionSubtitle'),
percentLabel: options.t('chartCaseCompletionPercent', {
percent: options.charts.treatmentPlanCompletion.percent,
}),
ratioLabel: options.t('chartTreatmentPlanCompletionRatio'),
href: '/appointments',
icon: Stethoscope,
});
}
@@ -345,6 +403,7 @@ function buildChartCells(options: {
orgType?: 'CLINIC' | 'LAB';
isOwner: boolean;
showMyAppointmentsWeekChart: boolean;
showCasePartnersChart: boolean;
dayLabelFormatter: ReturnType<typeof useTodayDayLabelFormatter>;
}): TodayDashboardCell[] {
const { t, charts, orgType, isOwner, showMyAppointmentsWeekChart } = options;
@@ -528,6 +587,38 @@ function buildChartCells(options: {
});
}
const casePartnersData = charts.casePartnersMonth ?? [];
if (options.showCasePartnersChart && charts.casePartnersMonth !== undefined) {
cells.push({
id: 'chart-case-partners-month',
layout: barChart,
content: (
<ChartCard
title={
orgType === 'CLINIC'
? t('chartCasePartnersClinicTitle')
: t('chartCasePartnersLabTitle')
}
subtitle={t('chartCasePartnersSubtitle')}
isEmpty={casePartnersData.every(
(row) => row.completed === 0 && row.pending === 0,
)}
emptyMessage={t('chartEmpty')}
>
<TodayPartnerCasesStackedBarChart
data={casePartnersData}
completedLabel={t('chartLabTaskCompletedLegend')}
pendingLabel={
orgType === 'CLINIC'
? t('chartCasePartnersSentLegend')
: t('chartCasePartnersOpenLegend')
}
/>
</ChartCard>
),
});
}
return cells;
}
@@ -536,6 +627,7 @@ function countVisibleCharts(
orgType?: 'CLINIC' | 'LAB',
isOwner = false,
showMyAppointmentsWeekChart = false,
showCasePartnersChart = false,
): number {
let count = 0;
if (orgType === 'CLINIC') {
@@ -544,10 +636,12 @@ function countVisibleCharts(
showMyAppointmentsWeekChart && charts.appointmentsWeekMine !== undefined ? 1 : 0;
count += charts.appointmentsByProvider !== undefined ? 1 : 0;
count += charts.treatmentMixWeek !== undefined ? 1 : 0;
count += showCasePartnersChart && charts.casePartnersMonth !== undefined ? 1 : 0;
}
if (orgType === 'LAB') {
count += charts.labTaskActivityWeek !== undefined ? 1 : 0;
count += charts.tasksByProsthesis !== undefined ? 1 : 0;
count += showCasePartnersChart && charts.casePartnersMonth !== undefined ? 1 : 0;
}
if (
isOwner &&
@@ -558,3 +652,35 @@ function countVisibleCharts(
}
return count;
}
function pushCompletionGaugeCell(
cells: TodayDashboardCell[],
options: {
id: string;
gauge: TodayCompletionGauge;
title: string;
subtitle: string;
percentLabel: string;
ratioLabel: string;
href: string;
icon: LucideIcon;
},
) {
cells.push({
id: options.id,
layout: TODAY_DASHBOARD_LAYOUT.subscription,
content: (
<TodayCompletionGaugeKpiCard
completed={options.gauge.completed}
total={options.gauge.total}
percent={options.gauge.percent}
title={options.title}
subtitle={options.subtitle}
percentLabel={options.percentLabel}
ratioLabel={options.ratioLabel}
href={options.href}
icon={options.icon}
/>
),
});
}

View File

@@ -0,0 +1,113 @@
'use client';
import {
Bar,
BarChart,
CartesianGrid,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from 'recharts';
import { TodayChartFrame } from '@/components/today/TodayChartFrame';
import {
TODAY_CHART_AXIS_COLOR,
TODAY_CHART_COMPLETED_COLOR,
TODAY_CHART_GRID_COLOR,
TODAY_CHART_RECEIVED_COLOR,
TODAY_CHART_TOOLTIP_STYLE,
} from '@/components/today/chart-theme';
import type { TodayPartnerCasesBucket } from '@/types/today';
interface TodayPartnerCasesStackedBarChartProps {
data: TodayPartnerCasesBucket[];
completedLabel: string;
pendingLabel: string;
}
export function TodayPartnerCasesStackedBarChart({
data,
completedLabel,
pendingLabel,
}: TodayPartnerCasesStackedBarChartProps) {
const chartData = data.map((item) => ({
...item,
shortLabel: truncateLabel(item.label),
}));
return (
<TodayChartFrame>
<div className="flex h-full min-h-0 flex-col">
<div className="min-h-0 flex-1">
<ResponsiveContainer width="100%" height="100%">
<BarChart data={chartData} margin={{ top: 8, right: 8, left: -12, bottom: 0 }}>
<CartesianGrid stroke={TODAY_CHART_GRID_COLOR} vertical={false} />
<XAxis
dataKey="shortLabel"
tick={{ fill: TODAY_CHART_AXIS_COLOR, fontSize: 11 }}
axisLine={{ stroke: TODAY_CHART_GRID_COLOR }}
tickLine={false}
interval={0}
/>
<YAxis
allowDecimals={false}
tick={{ fill: TODAY_CHART_AXIS_COLOR, fontSize: 11 }}
axisLine={false}
tickLine={false}
width={32}
/>
<Tooltip
cursor={{ fill: 'rgba(0, 188, 255, 0.08)' }}
contentStyle={TODAY_CHART_TOOLTIP_STYLE}
labelFormatter={(_, payload) => {
const row = payload?.[0]?.payload as TodayPartnerCasesBucket | undefined;
return row?.label ?? '';
}}
/>
<Bar
dataKey="completed"
name={completedLabel}
stackId="cases"
fill={TODAY_CHART_COMPLETED_COLOR}
radius={[0, 0, 0, 0]}
maxBarSize={48}
/>
<Bar
dataKey="pending"
name={pendingLabel}
stackId="cases"
fill={TODAY_CHART_RECEIVED_COLOR}
radius={[4, 4, 0, 0]}
maxBarSize={48}
/>
</BarChart>
</ResponsiveContainer>
</div>
<div className="mt-0.5 flex shrink-0 flex-wrap items-center justify-center gap-x-4 gap-y-0.5 pb-0 text-[11px] text-text-muted">
<span className="inline-flex items-center gap-1.5">
<span
className="inline-block h-2.5 w-2.5 shrink-0 rounded-sm"
style={{ backgroundColor: TODAY_CHART_COMPLETED_COLOR }}
aria-hidden
/>
{completedLabel}
</span>
<span className="inline-flex items-center gap-1.5">
<span
className="inline-block h-2.5 w-2.5 shrink-0 rounded-sm"
style={{ backgroundColor: TODAY_CHART_RECEIVED_COLOR }}
aria-hidden
/>
{pendingLabel}
</span>
</div>
</div>
</TodayChartFrame>
);
}
function truncateLabel(label: string, max = 12): string {
if (label.length <= max) return label;
return `${label.slice(0, max - 1)}`;
}

View File

@@ -1,4 +1,5 @@
import type { ReactNode } from 'react';
import { getTodayGadgetFeatureOrder } from '@/components/today/today-gadget-order';
/** Dashboard grid is always 4 columns (at lg+). Widgets use fixed width/height units. */
export type TodayDashboardWidth = 1 | 2;
@@ -49,6 +50,10 @@ export function sortDashboardCells<T extends { layout: TodayDashboardLayout; id:
return [...cells].sort((a, b) => {
const byLayout = compareDashboardLayout(a.layout, b.layout);
if (byLayout !== 0) return byLayout;
const byFeature = getTodayGadgetFeatureOrder(a.id) - getTodayGadgetFeatureOrder(b.id);
if (byFeature !== 0) return byFeature;
return a.id.localeCompare(b.id);
});
}

View File

@@ -0,0 +1,78 @@
import type { TodayWidgetKey } from '@/types/today';
/**
* Feature domains for Today dashboard gadgets, ordered like app permissions:
* owner-only → staff → organizations → patients → appointments → treatment → cases → tasks
*/
export type TodayGadgetFeature =
| 'owner'
| 'staff'
| 'organizations'
| 'patients'
| 'appointments'
| 'treatment'
| 'cases'
| 'tasks';
export const TODAY_GADGET_FEATURE_SORT_ORDER: Record<TodayGadgetFeature, number> = {
owner: 0,
staff: 10,
organizations: 20,
patients: 30,
appointments: 40,
treatment: 50,
cases: 60,
tasks: 70,
};
/** KPI widgets — keyed by TodayWidgetKey. */
export const TODAY_KPI_GADGET_FEATURE: Record<TodayWidgetKey, TodayGadgetFeature> = {
appointmentsToday: 'appointments',
patientsToday: 'patients',
treatmentsToday: 'treatment',
labCasesPendingSend: 'treatment',
providersWithoutWorkingHours: 'staff',
casesReceivedToday: 'cases',
casesInProgress: 'cases',
tasksInProgress: 'tasks',
importantTasks: 'tasks',
pendingConnections: 'organizations',
pendingStaffInvites: 'staff',
};
/** Charts and composite gadgets — keyed by stable cell id. */
export const TODAY_GADGET_ID_FEATURE: Record<string, TodayGadgetFeature> = {
subscription: 'owner',
'case-completion': 'cases',
'treatment-plan-completion': 'treatment',
'upcoming-appointments': 'treatment',
'chart-efficiency-report': 'owner',
'chart-appointments-week-all': 'appointments',
'chart-appointments-week-mine': 'treatment',
'chart-appointments-by-provider': 'appointments',
'chart-treatment-mix': 'treatment',
'chart-lab-task-activity': 'cases',
'chart-tasks-by-prosthesis': 'tasks',
'chart-case-partners-month': 'treatment',
};
export function todayGadgetFeatureSortRank(feature: TodayGadgetFeature): number {
return TODAY_GADGET_FEATURE_SORT_ORDER[feature];
}
export function getTodayGadgetFeatureOrder(gadgetId: string): number {
const direct = TODAY_GADGET_ID_FEATURE[gadgetId];
if (direct) {
return todayGadgetFeatureSortRank(direct);
}
if (gadgetId.startsWith('kpi-')) {
const key = gadgetId.slice(4) as TodayWidgetKey;
const feature = TODAY_KPI_GADGET_FEATURE[key];
if (feature) {
return todayGadgetFeatureSortRank(feature);
}
}
return Number.MAX_SAFE_INTEGER;
}

View File

@@ -23,18 +23,29 @@ export type TodayStackedDayBucket = {
received: number;
};
export type TodayPartnerCasesBucket = {
code: string;
label: string;
completed: number;
pending: number;
};
export type TodayCompletionGauge = {
completed: number;
total: number;
percent: number;
};
export type TodaySummaryCharts = {
treatmentMixWeek?: TodayChartBucket[];
tasksByProsthesis?: TodayChartBucket[];
appointmentsByProvider?: TodayChartBucket[];
caseCompletion?: {
completed: number;
total: number;
percent: number;
};
caseCompletion?: TodayCompletionGauge;
treatmentPlanCompletion?: TodayCompletionGauge;
appointmentsWeekAll?: TodayChartBucket[];
appointmentsWeekMine?: TodayChartBucket[];
labTaskActivityWeek?: TodayStackedDayBucket[];
casePartnersMonth?: TodayPartnerCasesBucket[];
efficiencyReport?: TodayChartBucket[];
};