UsagePage.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import React, { useEffect, useMemo, useState } from "react";
  2. import dayjs from "dayjs";
  3. import utc from "dayjs/plugin/utc";
  4. import Container from "components/porter/Container";
  5. import Fieldset from "components/porter/Fieldset";
  6. import Select from "components/porter/Select";
  7. import Spacer from "components/porter/Spacer";
  8. import Text from "components/porter/Text";
  9. import { useCustomerPlan, useCustomerUsage } from "lib/hooks/useLago";
  10. dayjs.extend(utc);
  11. function UsagePage(): JSX.Element {
  12. const { plan } = useCustomerPlan();
  13. const planStartDate = dayjs.utc(plan?.starting_on).startOf("month");
  14. const [currentPeriod, setCurrentPeriod] = useState(
  15. dayjs().utc().startOf("month")
  16. );
  17. const [options, setOptions] = useState<
  18. Array<{ value: string; label: string }>
  19. >([]);
  20. const [previousPeriodCount, setPreviousPeriodCount] = useState(0);
  21. const [showCurrentPeriod, setShowCurrentPeriod] = useState(true);
  22. const { usageList } = useCustomerUsage(
  23. previousPeriodCount,
  24. showCurrentPeriod
  25. );
  26. useEffect(() => {
  27. const newOptions = generateOptions();
  28. setOptions(newOptions);
  29. }, [previousPeriodCount, showCurrentPeriod]);
  30. const generateOptions = (): Array<{ value: string; label: string }> => {
  31. const options = [];
  32. const monthsElapsed = dayjs
  33. .utc()
  34. .startOf("month")
  35. .diff(planStartDate, "month");
  36. if (monthsElapsed <= 0) {
  37. options.push({
  38. value: currentPeriod.month().toString(),
  39. label: dayjs().utc().format("MMMM YYYY"),
  40. });
  41. setShowCurrentPeriod(true);
  42. return options;
  43. }
  44. setPreviousPeriodCount(monthsElapsed);
  45. for (let i = 0; i <= monthsElapsed; i++) {
  46. const optionDate = planStartDate.add(i, "month");
  47. options.push({
  48. value: optionDate.month().toString(),
  49. label: optionDate.format("MMMM YYYY"),
  50. });
  51. }
  52. return options;
  53. };
  54. const processedUsage = useMemo(() => {
  55. if (!usageList?.length) {
  56. return null;
  57. }
  58. const periodUsage = usageList.find(
  59. (usage) =>
  60. dayjs(usage.from_datetime).utc().month() === currentPeriod.month()
  61. );
  62. if (!periodUsage) {
  63. return null;
  64. }
  65. const totalCost = periodUsage?.total_amount_cents
  66. ? (periodUsage.total_amount_cents / 100).toFixed(4)
  67. : "0";
  68. const totalCpuHours =
  69. periodUsage?.charges_usage.find((x) =>
  70. x.billable_metric.name.includes("CPU")
  71. )?.units ?? "";
  72. const totalGibHours =
  73. periodUsage?.charges_usage.find((x) =>
  74. x.billable_metric.name.includes("GiB")
  75. )?.units ?? "";
  76. const currency = periodUsage?.charges_usage[0].amount_currency ?? "";
  77. if (totalCpuHours === "" || totalGibHours === "") {
  78. return null;
  79. }
  80. return {
  81. total_cost: totalCost,
  82. total_cpu_hours: totalCpuHours,
  83. total_gib_hours: totalGibHours,
  84. currency,
  85. };
  86. }, [usageList]);
  87. return (
  88. <>
  89. <Select
  90. options={options}
  91. value={currentPeriod.month().toString()}
  92. setValue={(value) => {
  93. setCurrentPeriod(dayjs.utc(value));
  94. if (dayjs(value).isSame(dayjs(), "month")) {
  95. setShowCurrentPeriod(true);
  96. } else {
  97. setShowCurrentPeriod(false);
  98. }
  99. }}
  100. width="fit-content"
  101. prefix={<>Billing period</>}
  102. />
  103. <Spacer y={1} />
  104. {processedUsage ? (
  105. <>
  106. <Text color="helper">Total usage (selected period):</Text>
  107. <Spacer y={0.5} />
  108. <Container row>
  109. <Fieldset>
  110. <Text size={16}>
  111. $ {processedUsage.total_cost} {processedUsage.currency}
  112. </Text>
  113. </Fieldset>
  114. <Spacer inline x={1} />
  115. <Fieldset>
  116. <Text size={16}>{processedUsage.total_gib_hours} GiB hours</Text>
  117. </Fieldset>
  118. <Spacer inline x={1} />
  119. <Fieldset>
  120. <Text size={16}>{processedUsage.total_cpu_hours} CPU hours</Text>
  121. </Fieldset>
  122. </Container>
  123. </>
  124. ) : (
  125. <Fieldset>
  126. <Text color="helper">
  127. No usage data available for this billing period.
  128. </Text>
  129. </Fieldset>
  130. )}
  131. </>
  132. );
  133. }
  134. export default UsagePage;