瀏覽代碼

usage tab fe

jusrhee 2 年之前
父節點
當前提交
62cb029895

+ 7 - 5
dashboard/src/components/TabRegion.tsx

@@ -1,13 +1,13 @@
 import React, { Component } from "react";
 import styled from "styled-components";
 
-import TabSelector from "./TabSelector";
 import Loading from "./Loading";
+import TabSelector from "./TabSelector";
 
-export interface TabOption {
+export type TabOption = {
   label: string;
   value: string;
-}
+};
 
 type PropsType = {
   options: TabOption[];
@@ -31,7 +31,7 @@ export default class TabRegion extends Component<PropsType, StateType> {
       : "";
 
   componentDidUpdate(prevProps: PropsType) {
-    let { options, currentTab } = this.props;
+    const { options, currentTab } = this.props;
     if (prevProps.options !== options) {
       if (options.filter((x) => x.value === currentTab).length === 0) {
         this.props.setCurrentTab(this.defaultTab());
@@ -50,7 +50,9 @@ export default class TabRegion extends Component<PropsType, StateType> {
               options={this.props.options}
               color={this.props.color}
               currentTab={this.props.currentTab}
-              setCurrentTab={(x: string) => this.props.setCurrentTab(x)}
+              setCurrentTab={(x: string) => {
+                this.props.setCurrentTab(x);
+              }}
               addendum={this.props.addendum}
             />
             <Gap />

+ 42 - 149
dashboard/src/main/home/project-settings/BillingPage.tsx

@@ -19,7 +19,6 @@ import {
   checkIfProjectHasPayment,
   useCustomerInvoices,
   useCustomerPlan,
-  useCustomerUsage,
   usePaymentMethods,
   usePorterCredits,
   useReferralDetails,
@@ -32,7 +31,6 @@ import gift from "assets/gift.svg";
 import trashIcon from "assets/trash.png";
 
 import BillingModal from "../modals/BillingModal";
-import Bars from "./Bars";
 
 dayjs.extend(relativeTime);
 
@@ -57,45 +55,10 @@ function BillingPage(): JSX.Element {
 
   const { refetchPaymentEnabled } = checkIfProjectHasPayment();
 
-  const { usage } = useCustomerUsage("day", true);
-
-  const processedData = useMemo(() => {
-    const before = usage;
-    const resultMap = new Map();
-
-    before?.forEach(
-      (metric: {
-        metric_name: string;
-        usage_metrics: Array<{ starting_on: string; value: number }>;
-      }) => {
-        const metricName = metric.metric_name.toLowerCase().replace(" ", "_");
-        metric.usage_metrics.forEach(({ starting_on, value }) => {
-          if (resultMap.has(starting_on)) {
-            resultMap.get(starting_on)[metricName] = value;
-          } else {
-            resultMap.set(starting_on, {
-              starting_on: new Date(starting_on).toLocaleDateString("en-US", {
-                month: "short",
-                day: "numeric",
-              }),
-              [metricName]: value,
-            });
-          }
-        });
-      }
-    );
-
-    // Convert the map to an array of values
-    const x = Array.from(resultMap.values());
-    return x;
-  }, [usage]);
-
   const formatCredits = (credits: number): string => {
     return (credits / 100).toFixed(2);
   };
 
-  const readableDate = (s: string): string => new Date(s).toLocaleDateString();
-
   const onCreate = async (): Promise<void> => {
     await refetchPaymentMethods({ throwOnError: false, cancelRefetch: false });
     setShouldCreate(false);
@@ -250,107 +213,48 @@ function BillingPage(): JSX.Element {
       </Button>
       <Spacer y={2} />
 
-      {currentProject?.metronome_enabled && (
-        <div>
-          {currentProject?.sandbox_enabled && (
-            <div>
-              <Text size={16}>Porter credit grants</Text>
-              <Spacer y={1} />
-              <Text color="helper">
-                No usage data available for this billing period.
-              </Text>
-              <Spacer y={1} />
-
-              <Container>
-                <Image src={gift} style={{ marginTop: "-2px" }} />
-                <Spacer inline x={1} />
-                <Text size={20}>
-                  {creditGrants && creditGrants.remaining_credits > 0
-                    ? `$${formatCredits(
-                        creditGrants.remaining_credits
-                      )}/$${formatCredits(creditGrants.granted_credits)}`
-                    : "$ 0.00"}
-                </Text>
-              </Container>
-              <Spacer y={2} />
-            </div>
-          )}
-
-          <div>
-            <Text size={16}>Plan Details</Text>
-            <Spacer y={1} />
-            <Text color="helper">
-              View the details of the current billing plan of this project.
-            </Text>
-            <Spacer y={1} />
-
-            {plan && plan.plan_name !== "" ? (
-              <div>
-                <Text>Active Plan</Text>
-                <Spacer y={0.5} />
-                <Fieldset row>
-                  <Container row spaced>
-                    <Container row>
-                      <Text color="helper">{plan.plan_name}</Text>
-                    </Container>
-                    <Container row>
-                      {plan.trial_info !== undefined &&
-                      plan.trial_info.ending_before !== "" ? (
-                        <Text>
-                          Free trial ends{" "}
-                          {dayjs().to(dayjs(plan.trial_info.ending_before))}
-                        </Text>
-                      ) : (
-                        <Text>Started on {readableDate(plan.starting_on)}</Text>
-                      )}
-                    </Container>
-                  </Container>
-                </Fieldset>
-                <Spacer y={2} />
-                <Text size={16}>Current Usage</Text>
-                <Spacer y={1} />
-                <Text color="helper">
-                  View the current usage of this billing period.
-                </Text>
-                <Spacer y={1} />
-                {usage?.length &&
-                usage.length > 0 &&
-                usage[0].usage_metrics.length > 0 ? (
-                  <Flex>
-                    <BarWrapper>
-                      <Bars
-                        title="GiB Hours"
-                        fill="#8784D2"
-                        yKey="gib_hours"
-                        xKey="starting_on"
-                        data={processedData}
-                      />
-                    </BarWrapper>
-                    <Spacer x={1} inline />
-                    <BarWrapper>
-                      <Bars
-                        title="CPU Hours"
-                        fill="#5886E0"
-                        yKey="cpu_hours"
-                        xKey="starting_on"
-                        data={processedData}
-                      />
-                    </BarWrapper>
-                  </Flex>
-                ) : (
-                  <Fieldset>
-                    <Text color="helper">
-                      No usage data available for this billing period.
-                    </Text>
-                  </Fieldset>
-                )}
-                <Spacer y={2} />
-              </div>
-            ) : (
-              <Text>This project does not have an active billing plan.</Text>
-            )}
-          </div>
-        </div>
+      {showReferralModal && (
+        <Modal
+          closeModal={() => {
+            setShowReferralModal(false);
+          }}
+        >
+          <Text size={16}>Refer users to Porter</Text>
+          <Spacer y={1} />
+          <Text color="helper">
+            Earn $10 in free credits for each user you refer to Porter. Referred
+            users need to connect a payment method for credits to be added to
+            your account.
+          </Text>
+          <Spacer y={1} />
+          <Container row>
+            <ReferralCode>
+              Referral code:{" "}
+              {currentProject?.referral_code ? (
+                <Code>{currentProject.referral_code}</Code>
+              ) : (
+                "n/a"
+              )}
+            </ReferralCode>
+            <Spacer inline x={1} />
+            <CopyToClipboard
+              text={
+                window.location.origin +
+                "/register?referral=" +
+                currentProject?.referral_code
+              }
+              tooltip="Copied to clipboard"
+            >
+              <CopyButton>Copy referral link</CopyButton>
+            </CopyToClipboard>
+          </Container>
+          <Spacer y={1} />
+          <Text color="helper">
+            You have referred{" "}
+            {referralDetails ? referralDetails.referral_count : "?"}/
+            {referralDetails?.max_allowed_referrals} users.
+          </Text>
+        </Modal>
       )}
     </>
   );
@@ -377,17 +281,6 @@ const ReferralCode = styled.div`
   width: fit-content;
 `;
 
-const Flex = styled.div`
-  display: flex;
-  flex-wrap: wrap;
-`;
-
-const BarWrapper = styled.div`
-  flex: 1;
-  height: 300px;
-  min-width: 450px;
-`;
-
 const I = styled.i`
   font-size: 16px;
   margin-right: 8px;

+ 14 - 1
dashboard/src/main/home/project-settings/ProjectSettings.tsx

@@ -29,6 +29,7 @@ import BillingPage from "./BillingPage";
 import InvitePage from "./InviteList";
 import Metadata from "./Metadata";
 import ProjectDeleteConsent from "./ProjectDeleteConsent";
+import UsagePage from "./UsagePage";
 
 type PropsType = RouteComponentProps & WithAuthProps & {};
 type ValidationError = {
@@ -95,6 +96,16 @@ function ProjectSettings(props: any) {
         });
       }
 
+      if (
+        currentProject?.billing_enabled &&
+        currentProject?.metronome_enabled
+      ) {
+        tabOpts.push({
+          value: "usage",
+          label: "Usage",
+        });
+      }
+
       tabOpts.push({
         value: "additional-settings",
         label: "Additional settings",
@@ -171,7 +182,9 @@ function ProjectSettings(props: any) {
     } else if (currentTab === "api-tokens") {
       return <APITokensSection />;
     } else if (currentTab === "billing") {
-      return <BillingPage></BillingPage>;
+      return <BillingPage />;
+    } else if (currentTab === "usage") {
+      return <UsagePage />;
     } else {
       return (
         <>

+ 138 - 0
dashboard/src/main/home/project-settings/UsagePage.tsx

@@ -0,0 +1,138 @@
+import React, { useContext, useMemo, useState } from "react";
+import styled from "styled-components";
+
+import Fieldset from "components/porter/Fieldset";
+import Select from "components/porter/Select";
+import Spacer from "components/porter/Spacer";
+import Text from "components/porter/Text";
+import { useCustomerUsage } from "lib/hooks/useStripe";
+
+import Bars from "./Bars";
+
+function UsagePage(): JSX.Element {
+  const [currentPeriod, setCurrentPeriod] = useState("4-17-24");
+
+  const { usage } = useCustomerUsage("day", true);
+
+  const processedData = useMemo(() => {
+    const before = usage;
+    const resultMap = new Map();
+
+    before?.forEach(
+      (metric: {
+        metric_name: string;
+        usage_metrics: Array<{ starting_on: string; value: number }>;
+      }) => {
+        const metricName = metric.metric_name.toLowerCase().replace(" ", "_");
+        metric.usage_metrics.forEach(({ starting_on, value }) => {
+          if (resultMap.has(starting_on)) {
+            resultMap.get(starting_on)[metricName] = value;
+          } else {
+            resultMap.set(starting_on, {
+              starting_on: new Date(starting_on).toLocaleDateString("en-US", {
+                month: "short",
+                day: "numeric",
+              }),
+              [metricName]: value,
+            });
+          }
+        });
+      }
+    );
+
+    // Convert the map to an array of values
+    const x = Array.from(resultMap.values());
+    return x;
+  }, [usage]);
+
+  return (
+    <>
+      <Select
+        options={[
+          { value: "4-17-24", label: "4/17/24 - 5/17/24" },
+          { value: "3-17-24", label: "3/17/24 - 4/17/24" },
+          { value: "2-17-24", label: "2/17/24 - 3/17/24" },
+          { value: "1-17-24", label: "1/17/24 - 2/17/24" },
+          { value: "12-17-23", label: "12/17/23 - 1/17/24" },
+        ]}
+        value={currentPeriod}
+        setValue={(value) => {
+          setCurrentPeriod(value);
+        }}
+        width="fit-content"
+        prefix={<>Billing period</>}
+      />
+      <Spacer y={1} />
+      {/* usage?.length &&
+      usage.length > 0 &&
+      usage[0].usage_metrics.length > 0 ? ( */}
+      {true ? (
+        <>
+          <BarWrapper>
+            <Total>Total cost: $457.58</Total>
+            <Bars
+              fill="#8784D2"
+              yKey="cost"
+              xKey="starting_on"
+              data={processedData}
+            />
+          </BarWrapper>
+          <Spacer y={0.5} />
+          <Flex>
+            <BarWrapper>
+              <Bars
+                title="GiB Hours"
+                fill="#8784D2"
+                yKey="gib_hours"
+                xKey="starting_on"
+                data={processedData}
+              />
+            </BarWrapper>
+            <Spacer x={1} inline />
+            <BarWrapper>
+              <Bars
+                title="CPU Hours"
+                fill="#5886E0"
+                yKey="cpu_hours"
+                xKey="starting_on"
+                data={processedData}
+              />
+            </BarWrapper>
+          </Flex>
+        </>
+      ) : (
+        <Fieldset>
+          <Text color="helper">
+            No usage data available for this billing period.
+          </Text>
+        </Fieldset>
+      )}
+    </>
+  );
+}
+
+export default UsagePage;
+
+const Total = styled.div`
+  position: absolute;
+  top: 20px;
+  left: 15px;
+  font-size: 13px;
+  background: #42444933;
+  backdrop-filter: saturate(150%) blur(8px);
+  padding: 7px 10px;
+  border-radius: 5px;
+  border: 1px solid #494b4f;
+`;
+
+const Flex = styled.div`
+  display: flex;
+  flex-wrap: wrap;
+`;
+
+const BarWrapper = styled.div`
+  flex: 1;
+  height: 300px;
+  min-width: 450px;
+  position: relative;
+`;