Skip to content

Commit d82d622

Browse files
committed
feat(5957): redesign cost pages
1 parent b9787a4 commit d82d622

File tree

15 files changed

+693
-309
lines changed

15 files changed

+693
-309
lines changed
Lines changed: 33 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,53 @@
11
'use client';
22

3-
import { Button, Tooltip } from '@mantine/core';
4-
import { MonthPickerInput } from '@mantine/dates';
53
import { useQuery } from '@tanstack/react-query';
64
import { format } from 'date-fns';
75
import { Session } from 'next-auth';
8-
import { useState } from 'react';
9-
import DataTable from '@/components/generic/data-table/DataTable';
10-
import LoadingBox from '@/components/generic/LoadingBox';
11-
import MonthlyCostSummary from '@/components/private-cloud/monthly-cost/MonthlyCostSummary';
6+
import { useEffect } from 'react';
127
import MonthlyCostChart from '@/components/private-cloud/monthly-cost/MonthyCostChart';
13-
import { dailyCostColumns, periodicCostColumns } from '@/constants/private-cloud';
14-
import { downloadPrivateCloudMonthlyCosts, getMonthlyCosts } from '@/services/backend/private-cloud/products';
15-
import { DailyCostMetric, PeriodicCostMetric } from '@/types/private-cloud';
16-
import { getDateFromYyyyMmDd } from '@/utils/js';
17-
18-
export default function Monthly({ licencePlate, session }: { licencePlate: string; session: Session }) {
19-
const [selectedDate, setSelectedDate] = useState<Date>(new Date());
20-
const [downloading, setDownloading] = useState(false);
21-
22-
const { data, isLoading, isError } = useQuery({
8+
import { getMonthlyCosts } from '@/services/backend/private-cloud/products';
9+
import { MonthlyCost } from '@/types/private-cloud';
10+
11+
export default function Monthly({
12+
selectedDate,
13+
licencePlate,
14+
session,
15+
onDataLoaded,
16+
forecastEnabled,
17+
onLoadingDone,
18+
}: {
19+
selectedDate: Date;
20+
licencePlate: string;
21+
session: Session;
22+
onDataLoaded: (data: MonthlyCost) => void;
23+
forecastEnabled: boolean;
24+
onLoadingDone: (isLoading: boolean) => void;
25+
}) {
26+
const { data, isLoading } = useQuery({
2327
queryKey: ['costItems', licencePlate, selectedDate ? format(selectedDate, 'yyyy-MM') : null],
2428
queryFn: () => getMonthlyCosts(licencePlate, format(selectedDate!, 'yyyy-MM')),
2529
enabled: !!licencePlate && !!selectedDate,
2630
});
2731

32+
useEffect(() => {
33+
if (data) {
34+
onDataLoaded(data);
35+
onLoadingDone(isLoading);
36+
}
37+
}, [data, onDataLoaded, isLoading, onLoadingDone]);
38+
2839
if (!data || !session.previews.costRecovery) {
2940
return null;
3041
}
3142

32-
const handleChange = (date: string | null) => {
33-
setSelectedDate(date ? getDateFromYyyyMmDd(date) : new Date());
34-
};
35-
36-
const dailyCostData = data.days.map((day, idx) => {
37-
const { cpuToDate, storageToDate, cpuToProjected, storageToProjected } = data.dayDetails;
38-
const totalCost = cpuToDate[idx] + storageToDate[idx] + cpuToProjected[idx] + storageToProjected[idx];
39-
40-
return {
41-
day,
42-
dayDetails: {
43-
cpuToDate: cpuToDate[idx],
44-
storageToDate: storageToDate[idx],
45-
cpuToProjected: cpuToProjected[idx],
46-
storageToProjected: storageToProjected[idx],
47-
totalCost,
48-
},
49-
};
50-
});
51-
5243
return (
53-
<div>
54-
<div className="flex items-center gap-4 mb-6">
55-
<Tooltip label="Select a month">
56-
<MonthPickerInput
57-
placeholder="Select a month"
58-
value={selectedDate}
59-
onChange={handleChange}
60-
maw={200}
61-
clearable
62-
/>
63-
</Tooltip>
64-
65-
{data.items.length > 0 && (
66-
<div className="ml-auto">
67-
<Button
68-
loading={downloading}
69-
onClick={async () => {
70-
if (!data) return;
71-
setDownloading(true);
72-
await downloadPrivateCloudMonthlyCosts(licencePlate, format(selectedDate, 'yyyy-MM'));
73-
setDownloading(false);
74-
}}
75-
>
76-
Download PDF
77-
</Button>
78-
</div>
79-
)}
80-
</div>
81-
82-
<MonthlyCostSummary data={data} />
83-
44+
<>
8445
{data.items.length > 0 && (
85-
<div className="my-8">
86-
<MonthlyCostChart data={{ days: data.days, dayDetails: data.dayDetails }} />
87-
</div>
46+
<MonthlyCostChart
47+
data={{ days: data.days, dayDetails: data.dayDetails, billingPeriod: data.billingPeriod }}
48+
isForecastEnabled={forecastEnabled}
49+
/>
8850
)}
89-
90-
<LoadingBox isLoading={isLoading}>
91-
<DataTable<PeriodicCostMetric> data={data.items} columns={periodicCostColumns} defaultPageSize={5} />
92-
<DataTable<DailyCostMetric> data={dailyCostData} columns={dailyCostColumns} defaultPageSize={5} />
93-
</LoadingBox>
94-
</div>
51+
</>
9552
);
9653
}
Lines changed: 34 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,53 @@
11
'use client';
22

3-
import { Button, Tooltip } from '@mantine/core';
4-
import { MonthPickerInput } from '@mantine/dates';
53
import { useQuery } from '@tanstack/react-query';
64
import { Session } from 'next-auth';
7-
import { useState } from 'react';
8-
import DataTable from '@/components/generic/data-table/DataTable';
9-
import LoadingBox from '@/components/generic/LoadingBox';
5+
import { useEffect } from 'react';
106
import QuarterlyCostChart from '@/components/private-cloud/quarterly-cost/QuarterlyCostChart';
11-
import QuarterlyCostSummary from '@/components/private-cloud/quarterly-cost/QuarterlyCostSummary';
12-
import { monthlyCostColumns, periodicCostColumns } from '@/constants/private-cloud';
13-
import { downloadPrivateCloudQuarterlyCosts, getQuarterlyCosts } from '@/services/backend/private-cloud/products';
14-
import { MonthlyCostMetric, PeriodicCostMetric } from '@/types/private-cloud';
15-
import { formatAsYearQuarter, getDateFromYyyyMmDd } from '@/utils/js';
16-
17-
export default function Quarterly({ licencePlate, session }: { licencePlate: string; session: Session }) {
18-
const [selectedDate, setSelectedDate] = useState<Date>(new Date());
19-
const [downloading, setDownloading] = useState(false);
20-
21-
const { data, isLoading, isError } = useQuery({
7+
import { getQuarterlyCosts } from '@/services/backend/private-cloud/products';
8+
import { QuarterlyCost } from '@/types/private-cloud';
9+
import { formatAsYearQuarter } from '@/utils/js';
10+
11+
export default function Quarterly({
12+
selectedDate,
13+
licencePlate,
14+
session,
15+
onDataLoaded,
16+
forecastEnabled,
17+
onLoadingDone,
18+
}: {
19+
selectedDate: Date;
20+
licencePlate: string;
21+
session: Session;
22+
onDataLoaded: (data: QuarterlyCost) => void;
23+
forecastEnabled: boolean;
24+
onLoadingDone: (isLoading: boolean) => void;
25+
}) {
26+
const { data, isLoading } = useQuery({
2227
queryKey: ['costItems', licencePlate, selectedDate ? formatAsYearQuarter(selectedDate) : null],
2328
queryFn: () => getQuarterlyCosts(licencePlate, formatAsYearQuarter(selectedDate)),
2429
enabled: !!licencePlate && !!selectedDate,
2530
});
2631

32+
useEffect(() => {
33+
if (data) {
34+
onDataLoaded(data);
35+
onLoadingDone(isLoading);
36+
}
37+
}, [data, onDataLoaded, isLoading, onLoadingDone]);
38+
2739
if (!data || !session?.previews.costRecovery) {
2840
return null;
2941
}
3042

31-
const handleChange = (date: string | null) => {
32-
setSelectedDate(date ? getDateFromYyyyMmDd(date) : new Date());
33-
};
34-
35-
const monthlyCostData = data.months.map((month, idx) => {
36-
const { cpuToDate, storageToDate, cpuToProjected, storageToProjected } = data.monthDetails;
37-
const totalCost = cpuToDate[idx] + storageToDate[idx] + cpuToProjected[idx] + storageToProjected[idx];
38-
39-
return {
40-
month,
41-
monthDetails: {
42-
cpuToDate: cpuToDate[idx],
43-
storageToDate: storageToDate[idx],
44-
cpuToProjected: cpuToProjected[idx],
45-
storageToProjected: storageToProjected[idx],
46-
totalCost,
47-
},
48-
};
49-
});
50-
5143
return (
52-
<div>
53-
<div className="flex items-center gap-4 mb-6">
54-
<Tooltip label="Select a month within the quarter">
55-
<MonthPickerInput
56-
placeholder="Select a month"
57-
value={selectedDate}
58-
onChange={handleChange}
59-
maw={200}
60-
clearable
61-
/>
62-
</Tooltip>
63-
{data.items.length > 0 && (
64-
<div className="ml-auto">
65-
<Button
66-
loading={downloading}
67-
onClick={async () => {
68-
if (!data) return;
69-
setDownloading(true);
70-
await downloadPrivateCloudQuarterlyCosts(licencePlate, formatAsYearQuarter(selectedDate));
71-
setDownloading(false);
72-
}}
73-
>
74-
Download PDF
75-
</Button>
76-
</div>
77-
)}
78-
</div>
79-
80-
<QuarterlyCostSummary data={data} />
81-
44+
<>
8245
{data.items.length > 0 && (
83-
<div className="my-8">
84-
<QuarterlyCostChart data={{ months: data.months, monthDetails: data.monthDetails }} />
85-
</div>
46+
<QuarterlyCostChart
47+
data={{ months: data.months, monthDetails: data.monthDetails, billingPeriod: data.billingPeriod }}
48+
isForecastEnabled={forecastEnabled}
49+
/>
8650
)}
87-
88-
<LoadingBox isLoading={isLoading}>
89-
<DataTable<PeriodicCostMetric> data={data.items} columns={periodicCostColumns} defaultPageSize={5} />
90-
<DataTable<MonthlyCostMetric> data={monthlyCostData} columns={monthlyCostColumns} defaultPageSize={5} />
91-
</LoadingBox>
92-
</div>
51+
</>
9352
);
9453
}
Lines changed: 35 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,53 @@
11
'use client';
22

3-
import { Button, Tooltip } from '@mantine/core';
4-
import { YearPickerInput } from '@mantine/dates';
53
import { useQuery } from '@tanstack/react-query';
64
import { Session } from 'next-auth';
7-
import { useState } from 'react';
8-
import DataTable from '@/components/generic/data-table/DataTable';
9-
import LoadingBox from '@/components/generic/LoadingBox';
5+
import { useEffect } from 'react';
106
import YearlyCostChart from '@/components/private-cloud/yearly-cost/YearlyCostChart';
11-
import YearlyCostSummary from '@/components/private-cloud/yearly-cost/YearlyCostSummary';
12-
import { monthlyCostColumns, periodicCostColumns } from '@/constants/private-cloud';
13-
import { downloadPrivateCloudYearlyCosts, getYearlyCosts } from '@/services/backend/private-cloud/products';
14-
import { MonthlyCostMetric, PeriodicCostMetric } from '@/types/private-cloud';
15-
16-
export default function Yearly({ licencePlate, session }: { licencePlate: string; session: Session }) {
17-
const [selectedYear, setSelectedYear] = useState<Date>(new Date());
18-
const [downloading, setDownloading] = useState(false);
19-
20-
const year = selectedYear.getFullYear().toString();
21-
const { data, isLoading, isError } = useQuery({
7+
import { getYearlyCosts } from '@/services/backend/private-cloud/products';
8+
import { YearlyCost } from '@/types/private-cloud';
9+
10+
export default function Yearly({
11+
selectedDate,
12+
licencePlate,
13+
session,
14+
onDataLoaded,
15+
forecastEnabled,
16+
onLoadingDone,
17+
}: {
18+
selectedDate: Date;
19+
licencePlate: string;
20+
session: Session;
21+
onDataLoaded: (data: YearlyCost) => void;
22+
forecastEnabled: boolean;
23+
onLoadingDone: (isLoading: boolean) => void;
24+
}) {
25+
const year = selectedDate.getFullYear().toString();
26+
const { data, isLoading } = useQuery({
2227
queryKey: ['costItems', licencePlate, year],
2328
queryFn: () => getYearlyCosts(licencePlate, year),
24-
enabled: !!licencePlate && !!selectedYear,
29+
enabled: !!licencePlate && !!selectedDate,
2530
});
2631

32+
useEffect(() => {
33+
if (data) {
34+
onDataLoaded(data);
35+
onLoadingDone(isLoading);
36+
}
37+
}, [data, onDataLoaded, isLoading, onLoadingDone]);
38+
2739
if (!data || !session?.previews.costRecovery) {
2840
return null;
2941
}
3042

31-
const handleChange = (year: string | null) => {
32-
setSelectedYear(year ? new Date(parseInt(year, 10), 0, 1) : new Date());
33-
};
34-
35-
const monthlyCostData = data.months.map((month, idx) => {
36-
const { cpuToDate, storageToDate, cpuToProjected, storageToProjected } = data.monthDetails;
37-
const totalCost = cpuToDate[idx] + storageToDate[idx] + cpuToProjected[idx] + storageToProjected[idx];
38-
39-
return {
40-
month,
41-
monthDetails: {
42-
cpuToDate: cpuToDate[idx],
43-
storageToDate: storageToDate[idx],
44-
cpuToProjected: cpuToProjected[idx],
45-
storageToProjected: storageToProjected[idx],
46-
totalCost,
47-
},
48-
};
49-
});
50-
5143
return (
52-
<div>
53-
<div className="flex items-center justify-between w-full mb-6">
54-
<div className="flex items-center gap-4">
55-
<Tooltip label="Select a year">
56-
<YearPickerInput
57-
placeholder="Select a year"
58-
value={selectedYear}
59-
onChange={handleChange}
60-
maw={200}
61-
clearable
62-
/>
63-
</Tooltip>
64-
</div>
65-
{data.items.length > 0 && (
66-
<Button
67-
loading={downloading}
68-
onClick={async () => {
69-
if (!data) return;
70-
setDownloading(true);
71-
await downloadPrivateCloudYearlyCosts(licencePlate, year);
72-
setDownloading(false);
73-
}}
74-
>
75-
Download PDF
76-
</Button>
77-
)}
78-
</div>
79-
80-
<YearlyCostSummary data={data} />
81-
44+
<>
8245
{data.items.length > 0 && (
83-
<div className="my-8">
84-
<YearlyCostChart data={{ months: data.months, monthDetails: data.monthDetails }} />
85-
</div>
46+
<YearlyCostChart
47+
data={{ months: data.months, monthDetails: data.monthDetails, billingPeriod: data.billingPeriod }}
48+
isForecastEnabled={forecastEnabled}
49+
/>
8650
)}
87-
88-
<LoadingBox isLoading={isLoading}>
89-
<DataTable<PeriodicCostMetric> data={data.items} columns={periodicCostColumns} defaultPageSize={5} />
90-
<DataTable<MonthlyCostMetric> data={monthlyCostData} columns={monthlyCostColumns} defaultPageSize={5} />
91-
</LoadingBox>
92-
</div>
51+
</>
9352
);
9453
}

0 commit comments

Comments
 (0)