unify report email and pdf download and add inesco logo
This commit is contained in:
parent
35938e9597
commit
33fc7fff01
|
|
@ -1471,6 +1471,91 @@ public class Controller : ControllerBase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Report HTML (for PDF download) ─────────────────────────────
|
||||||
|
|
||||||
|
[HttpGet(nameof(GetWeeklyReportHtml))]
|
||||||
|
public async Task<ActionResult> GetWeeklyReportHtml(Int64 installationId, Token authToken, String? language = null)
|
||||||
|
{
|
||||||
|
var user = Db.GetSession(authToken)?.User;
|
||||||
|
if (user == null) return Unauthorized();
|
||||||
|
|
||||||
|
var installation = Db.GetInstallationById(installationId);
|
||||||
|
if (installation is null || !user.HasAccessTo(installation)) return Unauthorized();
|
||||||
|
|
||||||
|
var lang = language ?? user.Language ?? "en";
|
||||||
|
var report = await WeeklyReportService.GenerateReportAsync(installationId, installation.Name, lang);
|
||||||
|
var html = ReportEmailService.BuildHtmlEmail(report, lang);
|
||||||
|
return Content(html, "text/html");
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet(nameof(GetMonthlyReportHtml))]
|
||||||
|
public async Task<ActionResult> GetMonthlyReportHtml(Int64 installationId, Int32 year, Int32 month, Token authToken, String? language = null)
|
||||||
|
{
|
||||||
|
var user = Db.GetSession(authToken)?.User;
|
||||||
|
if (user == null) return Unauthorized();
|
||||||
|
|
||||||
|
var installation = Db.GetInstallationById(installationId);
|
||||||
|
if (installation is null || !user.HasAccessTo(installation)) return Unauthorized();
|
||||||
|
|
||||||
|
var lang = language ?? user.Language ?? "en";
|
||||||
|
var report = Db.GetMonthlyReports(installationId).FirstOrDefault(r => r.Year == year && r.Month == month);
|
||||||
|
if (report == null) return BadRequest($"No monthly report found for {year}-{month:D2}.");
|
||||||
|
|
||||||
|
report.AiInsight = await ReportAggregationService.GetOrGenerateMonthlyInsightAsync(report, lang);
|
||||||
|
var s = ReportEmailService.GetAggregatedStrings(lang, "monthly");
|
||||||
|
var html = ReportEmailService.BuildAggregatedHtmlEmail(
|
||||||
|
report.PeriodStart, report.PeriodEnd, installation.Name,
|
||||||
|
report.TotalPvProduction, report.TotalConsumption, report.TotalGridImport, report.TotalGridExport,
|
||||||
|
report.TotalBatteryCharged, report.TotalBatteryDischarged, report.TotalEnergySaved, report.TotalSavingsCHF,
|
||||||
|
report.SelfSufficiencyPercent, report.BatteryEfficiencyPercent, report.AiInsight,
|
||||||
|
$"{report.WeekCount} {s.CountLabel}", s);
|
||||||
|
return Content(html, "text/html");
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet(nameof(GetYearlyReportHtml))]
|
||||||
|
public async Task<ActionResult> GetYearlyReportHtml(Int64 installationId, Int32 year, Token authToken, String? language = null)
|
||||||
|
{
|
||||||
|
var user = Db.GetSession(authToken)?.User;
|
||||||
|
if (user == null) return Unauthorized();
|
||||||
|
|
||||||
|
var installation = Db.GetInstallationById(installationId);
|
||||||
|
if (installation is null || !user.HasAccessTo(installation)) return Unauthorized();
|
||||||
|
|
||||||
|
var lang = language ?? user.Language ?? "en";
|
||||||
|
var report = Db.GetYearlyReports(installationId).FirstOrDefault(r => r.Year == year);
|
||||||
|
if (report == null) return BadRequest($"No yearly report found for {year}.");
|
||||||
|
|
||||||
|
report.AiInsight = await ReportAggregationService.GetOrGenerateYearlyInsightAsync(report, lang);
|
||||||
|
var s = ReportEmailService.GetAggregatedStrings(lang, "yearly");
|
||||||
|
var html = ReportEmailService.BuildAggregatedHtmlEmail(
|
||||||
|
report.PeriodStart, report.PeriodEnd, installation.Name,
|
||||||
|
report.TotalPvProduction, report.TotalConsumption, report.TotalGridImport, report.TotalGridExport,
|
||||||
|
report.TotalBatteryCharged, report.TotalBatteryDischarged, report.TotalEnergySaved, report.TotalSavingsCHF,
|
||||||
|
report.SelfSufficiencyPercent, report.BatteryEfficiencyPercent, report.AiInsight,
|
||||||
|
$"{report.MonthCount} {s.CountLabel}", s);
|
||||||
|
return Content(html, "text/html");
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet(nameof(GetDailyReportHtml))]
|
||||||
|
public ActionResult GetDailyReportHtml(Int64 installationId, String date, Token authToken, String? language = null)
|
||||||
|
{
|
||||||
|
var user = Db.GetSession(authToken)?.User;
|
||||||
|
if (user == null) return Unauthorized();
|
||||||
|
|
||||||
|
var installation = Db.GetInstallationById(installationId);
|
||||||
|
if (installation is null || !user.HasAccessTo(installation)) return Unauthorized();
|
||||||
|
|
||||||
|
if (!DateOnly.TryParseExact(date, "yyyy-MM-dd", out var parsedDate))
|
||||||
|
return BadRequest("date must be in yyyy-MM-dd format.");
|
||||||
|
|
||||||
|
var records = Db.GetDailyRecords(installationId, parsedDate, parsedDate);
|
||||||
|
if (records.Count == 0) return BadRequest($"No daily record found for {date}.");
|
||||||
|
|
||||||
|
var lang = language ?? user.Language ?? "en";
|
||||||
|
var html = ReportEmailService.BuildDailyHtmlEmail(records[0], installation.Name, lang);
|
||||||
|
return Content(html, "text/html");
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet(nameof(GetWeeklyReportSummaries))]
|
[HttpGet(nameof(GetWeeklyReportSummaries))]
|
||||||
public async Task<ActionResult<List<WeeklyReportSummary>>> GetWeeklyReportSummaries(
|
public async Task<ActionResult<List<WeeklyReportSummary>>> GetWeeklyReportSummaries(
|
||||||
Int64 installationId, Int32 year, Int32 month, Token authToken, String? language = null)
|
Int64 installationId, Int32 year, Int32 month, Token authToken, String? language = null)
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 6.2 KiB |
|
|
@ -97,10 +97,12 @@ function getCurrentWeekDays(currentMonday: Date): Date[] {
|
||||||
|
|
||||||
export default function DailySection({
|
export default function DailySection({
|
||||||
installationId,
|
installationId,
|
||||||
onHasData
|
onHasData,
|
||||||
|
onPeriodChange
|
||||||
}: {
|
}: {
|
||||||
installationId: number;
|
installationId: number;
|
||||||
onHasData?: (hasData: boolean) => void;
|
onHasData?: (hasData: boolean) => void;
|
||||||
|
onPeriodChange?: (date: string) => void;
|
||||||
}) {
|
}) {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const currentMonday = useMemo(() => getCurrentMonday(), []);
|
const currentMonday = useMemo(() => getCurrentMonday(), []);
|
||||||
|
|
@ -113,7 +115,11 @@ export default function DailySection({
|
||||||
|
|
||||||
const [allRecords, setAllRecords] = useState<DailyEnergyData[]>([]);
|
const [allRecords, setAllRecords] = useState<DailyEnergyData[]>([]);
|
||||||
const [allHourlyRecords, setAllHourlyRecords] = useState<HourlyEnergyRecord[]>([]);
|
const [allHourlyRecords, setAllHourlyRecords] = useState<HourlyEnergyRecord[]>([]);
|
||||||
const [selectedDate, setSelectedDate] = useState(formatDateISO(yesterday));
|
const [selectedDate, setSelectedDate] = useState(() => {
|
||||||
|
const date = formatDateISO(yesterday);
|
||||||
|
onPeriodChange?.(date);
|
||||||
|
return date;
|
||||||
|
});
|
||||||
const [selectedDayRecord, setSelectedDayRecord] = useState<DailyEnergyData | null>(null);
|
const [selectedDayRecord, setSelectedDayRecord] = useState<DailyEnergyData | null>(null);
|
||||||
const [hourlyRecords, setHourlyRecords] = useState<HourlyEnergyRecord[]>([]);
|
const [hourlyRecords, setHourlyRecords] = useState<HourlyEnergyRecord[]>([]);
|
||||||
const [loadingWeek, setLoadingWeek] = useState(false);
|
const [loadingWeek, setLoadingWeek] = useState(false);
|
||||||
|
|
@ -174,6 +180,7 @@ export default function DailySection({
|
||||||
const handleStripSelect = (date: string) => {
|
const handleStripSelect = (date: string) => {
|
||||||
setSelectedDate(date);
|
setSelectedDate(date);
|
||||||
setNoData(false);
|
setNoData(false);
|
||||||
|
onPeriodChange?.(date);
|
||||||
};
|
};
|
||||||
|
|
||||||
const dt = new Date(selectedDate + 'T00:00:00');
|
const dt = new Date(selectedDate + 'T00:00:00');
|
||||||
|
|
|
||||||
|
|
@ -236,6 +236,8 @@ function WeeklyReport({ installationId }: WeeklyReportProps) {
|
||||||
const [regenerating, setRegenerating] = useState(false);
|
const [regenerating, setRegenerating] = useState(false);
|
||||||
const [dailyHasData, setDailyHasData] = useState(false);
|
const [dailyHasData, setDailyHasData] = useState(false);
|
||||||
const [weeklyHasData, setWeeklyHasData] = useState(false);
|
const [weeklyHasData, setWeeklyHasData] = useState(false);
|
||||||
|
const [downloadingPdf, setDownloadingPdf] = useState(false);
|
||||||
|
const [reportPeriod, setReportPeriod] = useState<{ start: string; end: string; year?: number; month?: number } | null>(null);
|
||||||
const weeklyRef = useRef<WeeklySectionHandle>(null);
|
const weeklyRef = useRef<WeeklySectionHandle>(null);
|
||||||
|
|
||||||
const fetchReportData = () => {
|
const fetchReportData = () => {
|
||||||
|
|
@ -302,16 +304,56 @@ function WeeklyReport({ installationId }: WeeklyReportProps) {
|
||||||
return false;
|
return false;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
const handleDownloadPdf = async () => {
|
||||||
|
const reportType = tabs[safeTab]?.key ?? 'report';
|
||||||
|
let endpoint = '';
|
||||||
|
const params: Record<string, any> = { installationId, language: intl.locale };
|
||||||
|
|
||||||
|
switch (reportType) {
|
||||||
|
case 'daily':
|
||||||
|
endpoint = '/GetDailyReportHtml';
|
||||||
|
if (reportPeriod?.start) params.date = reportPeriod.start;
|
||||||
|
break;
|
||||||
|
case 'weekly':
|
||||||
|
endpoint = '/GetWeeklyReportHtml';
|
||||||
|
break;
|
||||||
|
case 'monthly':
|
||||||
|
endpoint = '/GetMonthlyReportHtml';
|
||||||
|
if (reportPeriod?.year) params.year = reportPeriod.year;
|
||||||
|
if (reportPeriod?.month) params.month = reportPeriod.month;
|
||||||
|
break;
|
||||||
|
case 'yearly':
|
||||||
|
endpoint = '/GetYearlyReportHtml';
|
||||||
|
if (reportPeriod?.year) params.year = reportPeriod.year;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!endpoint) return;
|
||||||
|
|
||||||
|
setDownloadingPdf(true);
|
||||||
|
try {
|
||||||
|
const res = await axiosConfig.get(endpoint, { params, responseType: 'text' });
|
||||||
|
const printWindow = window.open('', '_blank');
|
||||||
|
if (!printWindow) return;
|
||||||
|
|
||||||
|
const dateRange = reportPeriod
|
||||||
|
? `${reportPeriod.start.replace(/-/g, '')}-${reportPeriod.end.replace(/-/g, '')}`
|
||||||
|
: new Date().toISOString().split('T')[0].replace(/-/g, '');
|
||||||
|
|
||||||
|
printWindow.document.write(res.data);
|
||||||
|
printWindow.document.close();
|
||||||
|
printWindow.document.title = `inesco-energy-${installationId}-${reportType}-${dateRange}`;
|
||||||
|
printWindow.onafterprint = () => printWindow.close();
|
||||||
|
setTimeout(() => printWindow.print(), 500);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('PDF download failed', err);
|
||||||
|
} finally {
|
||||||
|
setDownloadingPdf(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ p: 2, width: '100%', maxWidth: 900, mx: 'auto' }} className="report-container">
|
<Box sx={{ p: 2, width: '100%', maxWidth: 900, mx: 'auto' }} className="report-container">
|
||||||
<style>{`
|
|
||||||
@media print {
|
|
||||||
body * { visibility: hidden; }
|
|
||||||
.report-container, .report-container * { visibility: visible; }
|
|
||||||
.report-container { position: absolute; left: 0; top: 0; width: 100%; padding: 20px; }
|
|
||||||
.no-print { display: none !important; }
|
|
||||||
}
|
|
||||||
`}</style>
|
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }} className="no-print">
|
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }} className="no-print">
|
||||||
<Tabs
|
<Tabs
|
||||||
value={safeTab}
|
value={safeTab}
|
||||||
|
|
@ -323,8 +365,9 @@ function WeeklyReport({ installationId }: WeeklyReportProps) {
|
||||||
{activeTabHasData && (
|
{activeTabHasData && (
|
||||||
<Button
|
<Button
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
startIcon={<DownloadIcon />}
|
startIcon={downloadingPdf ? <CircularProgress size={16} /> : <DownloadIcon />}
|
||||||
onClick={() => window.print()}
|
onClick={handleDownloadPdf}
|
||||||
|
disabled={downloadingPdf}
|
||||||
sx={{ ml: 2, whiteSpace: 'nowrap' }}
|
sx={{ ml: 2, whiteSpace: 'nowrap' }}
|
||||||
>
|
>
|
||||||
<FormattedMessage id="downloadPdf" defaultMessage="Download PDF" />
|
<FormattedMessage id="downloadPdf" defaultMessage="Download PDF" />
|
||||||
|
|
@ -361,7 +404,7 @@ function WeeklyReport({ installationId }: WeeklyReportProps) {
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ display: tabs[safeTab]?.key === 'daily' ? 'block' : 'none', minHeight: '50vh' }}>
|
<Box sx={{ display: tabs[safeTab]?.key === 'daily' ? 'block' : 'none', minHeight: '50vh' }}>
|
||||||
<DailySection installationId={installationId} onHasData={setDailyHasData} />
|
<DailySection installationId={installationId} onHasData={setDailyHasData} onPeriodChange={(date: string) => setReportPeriod({ start: date, end: date })} />
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ display: tabs[safeTab]?.key === 'weekly' ? 'block' : 'none', minHeight: '50vh' }}>
|
<Box sx={{ display: tabs[safeTab]?.key === 'weekly' ? 'block' : 'none', minHeight: '50vh' }}>
|
||||||
<WeeklySection
|
<WeeklySection
|
||||||
|
|
@ -373,6 +416,7 @@ function WeeklyReport({ installationId }: WeeklyReportProps) {
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
onHasData={setWeeklyHasData}
|
onHasData={setWeeklyHasData}
|
||||||
|
onPeriodChange={(start, end) => setReportPeriod({ start, end })}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ display: tabs[safeTab]?.key === 'monthly' ? 'block' : 'none', minHeight: '50vh' }}>
|
<Box sx={{ display: tabs[safeTab]?.key === 'monthly' ? 'block' : 'none', minHeight: '50vh' }}>
|
||||||
|
|
@ -384,6 +428,7 @@ function WeeklyReport({ installationId }: WeeklyReportProps) {
|
||||||
onGenerate={handleGenerateMonthly}
|
onGenerate={handleGenerateMonthly}
|
||||||
selectedIdx={selectedMonthlyIdx}
|
selectedIdx={selectedMonthlyIdx}
|
||||||
onSelectedIdxChange={setSelectedMonthlyIdx}
|
onSelectedIdxChange={setSelectedMonthlyIdx}
|
||||||
|
onPeriodChange={(r: MonthlyReport) => setReportPeriod({ start: r.periodStart, end: r.periodEnd, year: r.year, month: r.month })}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ display: tabs[safeTab]?.key === 'yearly' ? 'block' : 'none', minHeight: '50vh' }}>
|
<Box sx={{ display: tabs[safeTab]?.key === 'yearly' ? 'block' : 'none', minHeight: '50vh' }}>
|
||||||
|
|
@ -395,6 +440,7 @@ function WeeklyReport({ installationId }: WeeklyReportProps) {
|
||||||
onGenerate={handleGenerateYearly}
|
onGenerate={handleGenerateYearly}
|
||||||
selectedIdx={selectedYearlyIdx}
|
selectedIdx={selectedYearlyIdx}
|
||||||
onSelectedIdxChange={setSelectedYearlyIdx}
|
onSelectedIdxChange={setSelectedYearlyIdx}
|
||||||
|
onPeriodChange={(r: YearlyReport) => setReportPeriod({ start: r.periodStart, end: r.periodEnd, year: r.year })}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
@ -407,8 +453,8 @@ interface WeeklySectionHandle {
|
||||||
regenerate: () => void;
|
regenerate: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const WeeklySection = forwardRef<WeeklySectionHandle, { installationId: number; latestMonthlyPeriodEnd: string | null; onHasData?: (hasData: boolean) => void }>(
|
const WeeklySection = forwardRef<WeeklySectionHandle, { installationId: number; latestMonthlyPeriodEnd: string | null; onHasData?: (hasData: boolean) => void; onPeriodChange?: (start: string, end: string) => void }>(
|
||||||
({ installationId, latestMonthlyPeriodEnd, onHasData }, ref) => {
|
({ installationId, latestMonthlyPeriodEnd, onHasData, onPeriodChange }, ref) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [report, setReport] = useState<WeeklyReportResponse | null>(null);
|
const [report, setReport] = useState<WeeklyReportResponse | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
@ -427,6 +473,7 @@ const WeeklySection = forwardRef<WeeklySectionHandle, { installationId: number;
|
||||||
});
|
});
|
||||||
setReport(res.data);
|
setReport(res.data);
|
||||||
onHasData?.(true);
|
onHasData?.(true);
|
||||||
|
onPeriodChange?.(res.data.periodStart, res.data.periodEnd);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
const msg =
|
const msg =
|
||||||
err.response?.data ||
|
err.response?.data ||
|
||||||
|
|
@ -811,7 +858,8 @@ function MonthlySection({
|
||||||
generating,
|
generating,
|
||||||
onGenerate,
|
onGenerate,
|
||||||
selectedIdx,
|
selectedIdx,
|
||||||
onSelectedIdxChange
|
onSelectedIdxChange,
|
||||||
|
onPeriodChange
|
||||||
}: {
|
}: {
|
||||||
installationId: number;
|
installationId: number;
|
||||||
reports: MonthlyReport[];
|
reports: MonthlyReport[];
|
||||||
|
|
@ -820,6 +868,7 @@ function MonthlySection({
|
||||||
onGenerate: (year: number, month: number) => void;
|
onGenerate: (year: number, month: number) => void;
|
||||||
selectedIdx: number;
|
selectedIdx: number;
|
||||||
onSelectedIdxChange: (idx: number) => void;
|
onSelectedIdxChange: (idx: number) => void;
|
||||||
|
onPeriodChange?: (report: MonthlyReport) => void;
|
||||||
}) {
|
}) {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
|
@ -871,6 +920,7 @@ function MonthlySection({
|
||||||
sendParamsFn={(r: MonthlyReport) => ({ installationId, year: r.year, month: r.month })}
|
sendParamsFn={(r: MonthlyReport) => ({ installationId, year: r.year, month: r.month })}
|
||||||
controlledIdx={selectedIdx}
|
controlledIdx={selectedIdx}
|
||||||
onIdxChange={onSelectedIdxChange}
|
onIdxChange={onSelectedIdxChange}
|
||||||
|
onPeriodChange={onPeriodChange}
|
||||||
/>
|
/>
|
||||||
) : pendingMonths.length === 0 ? (
|
) : pendingMonths.length === 0 ? (
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', py: 6 }}>
|
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', py: 6 }}>
|
||||||
|
|
@ -892,7 +942,8 @@ function YearlySection({
|
||||||
generating,
|
generating,
|
||||||
onGenerate,
|
onGenerate,
|
||||||
selectedIdx,
|
selectedIdx,
|
||||||
onSelectedIdxChange
|
onSelectedIdxChange,
|
||||||
|
onPeriodChange
|
||||||
}: {
|
}: {
|
||||||
installationId: number;
|
installationId: number;
|
||||||
reports: YearlyReport[];
|
reports: YearlyReport[];
|
||||||
|
|
@ -901,6 +952,7 @@ function YearlySection({
|
||||||
onGenerate: (year: number) => void;
|
onGenerate: (year: number) => void;
|
||||||
selectedIdx: number;
|
selectedIdx: number;
|
||||||
onSelectedIdxChange: (idx: number) => void;
|
onSelectedIdxChange: (idx: number) => void;
|
||||||
|
onPeriodChange?: (report: YearlyReport) => void;
|
||||||
}) {
|
}) {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
|
@ -952,6 +1004,7 @@ function YearlySection({
|
||||||
sendParamsFn={(r: YearlyReport) => ({ installationId, year: r.year })}
|
sendParamsFn={(r: YearlyReport) => ({ installationId, year: r.year })}
|
||||||
controlledIdx={selectedIdx}
|
controlledIdx={selectedIdx}
|
||||||
onIdxChange={onSelectedIdxChange}
|
onIdxChange={onSelectedIdxChange}
|
||||||
|
onPeriodChange={onPeriodChange}
|
||||||
/>
|
/>
|
||||||
) : pendingYears.length === 0 ? (
|
) : pendingYears.length === 0 ? (
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', py: 6 }}>
|
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', py: 6 }}>
|
||||||
|
|
@ -975,7 +1028,8 @@ function AggregatedSection<T extends ReportSummary>({
|
||||||
sendEndpoint,
|
sendEndpoint,
|
||||||
sendParamsFn,
|
sendParamsFn,
|
||||||
controlledIdx,
|
controlledIdx,
|
||||||
onIdxChange
|
onIdxChange,
|
||||||
|
onPeriodChange
|
||||||
}: {
|
}: {
|
||||||
reports: T[];
|
reports: T[];
|
||||||
type: 'monthly' | 'yearly';
|
type: 'monthly' | 'yearly';
|
||||||
|
|
@ -986,6 +1040,7 @@ function AggregatedSection<T extends ReportSummary>({
|
||||||
sendParamsFn: (r: T) => object;
|
sendParamsFn: (r: T) => object;
|
||||||
controlledIdx?: number;
|
controlledIdx?: number;
|
||||||
onIdxChange?: (idx: number) => void;
|
onIdxChange?: (idx: number) => void;
|
||||||
|
onPeriodChange?: (report: T) => void;
|
||||||
}) {
|
}) {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [internalIdx, setInternalIdx] = useState(0);
|
const [internalIdx, setInternalIdx] = useState(0);
|
||||||
|
|
@ -993,8 +1048,16 @@ function AggregatedSection<T extends ReportSummary>({
|
||||||
const handleIdxChange = (idx: number) => {
|
const handleIdxChange = (idx: number) => {
|
||||||
setInternalIdx(idx);
|
setInternalIdx(idx);
|
||||||
onIdxChange?.(idx);
|
onIdxChange?.(idx);
|
||||||
|
if (reports[idx]) onPeriodChange?.(reports[idx]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Report initial period on mount
|
||||||
|
useEffect(() => {
|
||||||
|
if (reports.length > 0 && reports[selectedIdx]) {
|
||||||
|
onPeriodChange?.(reports[selectedIdx]);
|
||||||
|
}
|
||||||
|
}, [reports.length]);
|
||||||
|
|
||||||
if (reports.length === 0) {
|
if (reports.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', py: 6 }}>
|
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', py: 6 }}>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue