2
0
jusrhee 2 жил өмнө
parent
commit
ddf0413463

+ 9 - 0
dashboard/src/assets/gift.svg

@@ -0,0 +1,9 @@
+<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M18.2909 34V10.8566M16.5826 9.46825C16.8707 9.53255 17.1743 9.45374 17.3817 9.24746C17.5892 9.04118 17.6683 8.73915 17.6038 8.45275C17.3452 7.40232 16.3926 3.88491 15.4145 2.91224C14.1959 1.70041 12.2126 1.69536 11 2.90119C9.78751 4.10694 9.79242 6.07925 11.0111 7.29116C12.0052 8.27975 15.5263 9.21113 16.5826 9.46825ZM18.5402 8.45267C18.4756 8.73918 18.5548 9.0411 18.7622 9.24738C18.9697 9.45366 19.2734 9.53235 19.5614 9.46817C20.6177 9.21102 24.1548 8.26375 25.1329 7.29108C26.3515 6.07925 26.3566 4.10694 25.144 2.90111C23.9315 1.69536 21.9482 1.70024 20.7295 2.91215C19.7354 3.90074 18.7988 7.40227 18.5402 8.45267ZM3.16364 18.9568H32.8364C33.479 18.9568 34 18.4387 34 17.7996V12.0138C34 11.3747 33.479 10.8566 32.8364 10.8566H3.16364C2.52098 10.8566 2 11.3747 2 12.0138V17.7996C2 18.4387 2.52098 18.9568 3.16364 18.9568ZM31.0909 18.9568V32.8428C31.0909 33.4819 30.5699 34 29.9273 34H6.07273C5.43007 34 4.90909 33.4819 4.90909 32.8428V18.9568H31.0909Z" stroke="url(#paint0_linear_1699_18)" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/>
+<defs>
+<linearGradient id="paint0_linear_1699_18" x1="14.5" y1="5" x2="33.4613" y2="50.1509" gradientUnits="userSpaceOnUse">
+<stop stop-color="#F8F8F8"/>
+<stop offset="1" stop-color="#666666" stop-opacity="0"/>
+</linearGradient>
+</defs>
+</svg>

+ 186 - 167
dashboard/src/main/home/app-dashboard/AppDashboard.tsx

@@ -1,37 +1,37 @@
-import React, { useEffect, useState, useContext, useMemo } from "react";
-import styled from "styled-components";
+import React, { useContext, useEffect, useMemo, useState } from "react";
 import _ from "lodash";
 import _ from "lodash";
 import { Link, LinkProps } from "react-router-dom";
 import { Link, LinkProps } from "react-router-dom";
+import styled from "styled-components";
+
+import ClusterProvisioningPlaceholder from "components/ClusterProvisioningPlaceholder";
+import Loading from "components/Loading";
+import Button from "components/porter/Button";
+import Container from "components/porter/Container";
+import DashboardPlaceholder from "components/porter/DashboardPlaceholder";
+import Fieldset from "components/porter/Fieldset";
+import Icon from "components/porter/Icon";
+import PorterLink from "components/porter/Link";
+import SearchBar from "components/porter/SearchBar";
+import Spacer from "components/porter/Spacer";
+import Text from "components/porter/Text";
+import Toggle from "components/porter/Toggle";
 
 
+import api from "shared/api";
+import { Context } from "shared/Context";
+import { search } from "shared/search";
+import { readableDate } from "shared/string_utils";
 import applications from "assets/applications.svg";
 import applications from "assets/applications.svg";
 import box from "assets/box.png";
 import box from "assets/box.png";
+import calendar from "assets/calendar-number.svg";
 import github from "assets/github.png";
 import github from "assets/github.png";
-import time from "assets/time.png";
-import healthy from "assets/status-healthy.png";
 import grid from "assets/grid.png";
 import grid from "assets/grid.png";
 import list from "assets/list.png";
 import list from "assets/list.png";
-import letter from "assets/vector.svg";
-import calendar from "assets/calendar-number.svg";
 import notFound from "assets/not-found.png";
 import notFound from "assets/not-found.png";
-
-import { Context } from "shared/Context";
-import { search } from "shared/search";
-import api from "shared/api";
-import { readableDate } from "shared/string_utils";
+import healthy from "assets/status-healthy.png";
+import time from "assets/time.png";
+import letter from "assets/vector.svg";
 
 
 import DashboardHeader from "../cluster-dashboard/DashboardHeader";
 import DashboardHeader from "../cluster-dashboard/DashboardHeader";
-import Container from "components/porter/Container";
-import Button from "components/porter/Button";
-import Spacer from "components/porter/Spacer";
-import Text from "components/porter/Text";
-import SearchBar from "components/porter/SearchBar";
-import Toggle from "components/porter/Toggle";
-import PorterLink from "components/porter/Link";
-import Loading from "components/Loading";
-import Fieldset from "components/porter/Fieldset";
-import ClusterProvisioningPlaceholder from "components/ClusterProvisioningPlaceholder";
-import Icon from "components/porter/Icon";
-import DashboardPlaceholder from "components/porter/DashboardPlaceholder";
 
 
 type Props = {};
 type Props = {};
 
 
@@ -53,8 +53,9 @@ const namespaceBlacklist = [
   "monitoring",
   "monitoring",
 ];
 ];
 
 
-const AppDashboard: React.FC<Props> = ({ }) => {
-  const { currentProject, currentCluster, setFeaturePreview } = useContext(Context);
+const AppDashboard: React.FC<Props> = ({}) => {
+  const { currentProject, currentCluster, setFeaturePreview } =
+    useContext(Context);
   const [apps, setApps] = useState([]);
   const [apps, setApps] = useState([]);
   const [charts, setCharts] = useState([]);
   const [charts, setCharts] = useState([]);
   const [error, setError] = useState(null);
   const [error, setError] = useState(null);
@@ -93,8 +94,8 @@ const AppDashboard: React.FC<Props> = ({ }) => {
       );
       );
       const apps = res.data;
       const apps = res.data;
       const timeRes = await Promise.all(
       const timeRes = await Promise.all(
-        apps.map((app: any) => {
-          return api.getCharts(
+        apps.map(async (app: any) => {
+          return await api.getCharts(
             "<token>",
             "<token>",
             {
             {
               limit: 1,
               limit: 1,
@@ -120,7 +121,7 @@ const AppDashboard: React.FC<Props> = ({ }) => {
       );
       );
       apps.forEach((app: any, i: number) => {
       apps.forEach((app: any, i: number) => {
         if (timeRes?.[i]?.data?.[0]?.info?.last_deployed != null) {
         if (timeRes?.[i]?.data?.[0]?.info?.last_deployed != null) {
-          app["last_deployed"] = readableDate(
+          app.last_deployed = readableDate(
             timeRes[i].data[0].info.last_deployed
             timeRes[i].data[0].info.last_deployed
           );
           );
         }
         }
@@ -145,7 +146,9 @@ const AppDashboard: React.FC<Props> = ({ }) => {
         {app.repo_name ? (
         {app.repo_name ? (
           <Container row>
           <Container row>
             <SmallIcon opacity="0.6" src={github} />
             <SmallIcon opacity="0.6" src={github} />
-            <Text size={13} color="#ffffff44">{app.repo_name}</Text>
+            <Text size={13} color="#ffffff44">
+              {app.repo_name}
+            </Text>
           </Container>
           </Container>
         ) : (
         ) : (
           <Container row>
           <Container row>
@@ -154,7 +157,9 @@ const AppDashboard: React.FC<Props> = ({ }) => {
               height="18px"
               height="18px"
               src="https://cdn4.iconfinder.com/data/icons/logos-and-brands/512/97_Docker_logo_logos-512.png"
               src="https://cdn4.iconfinder.com/data/icons/logos-and-brands/512/97_Docker_logo_logos-512.png"
             />
             />
-            <Text truncate={true} size={13} color="#ffffff44">{app.image_repo_uri}</Text>
+            <Text truncate={true} size={13} color="#ffffff44">
+              {app.image_repo_uri}
+            </Text>
           </Container>
           </Container>
         )}
         )}
       </>
       </>
@@ -166,7 +171,7 @@ const AppDashboard: React.FC<Props> = ({ }) => {
       await api.updateStackStep(
       await api.updateStackStep(
         "<token>",
         "<token>",
         {
         {
-          step: 'stack-launch-start'
+          step: "stack-launch-start",
         },
         },
         {
         {
           cluster_id: currentCluster.id,
           cluster_id: currentCluster.id,
@@ -176,11 +181,10 @@ const AppDashboard: React.FC<Props> = ({ }) => {
     } catch (err) {
     } catch (err) {
       // TODO: handle error
       // TODO: handle error
     }
     }
-  }
-
+  };
 
 
   const renderIcon = (b: string, size?: string) => {
   const renderIcon = (b: string, size?: string) => {
-    var src = box;
+    let src = box;
     if (b) {
     if (b) {
       const bp = b.split(",")[0]?.split("/")[1];
       const bp = b.split(",")[0]?.split("/")[1];
       switch (bp) {
       switch (bp) {
@@ -201,7 +205,13 @@ const AppDashboard: React.FC<Props> = ({ }) => {
       }
       }
     }
     }
     return (
     return (
-      <>{size === "larger" ? <Icon height="16px" src={src} /> : <Icon height="18px" src={src} />}</>
+      <>
+        {size === "larger" ? (
+          <Icon height="16px" src={src} />
+        ) : (
+          <Icon height="18px" src={src} />
+        )}
+      </>
     );
     );
   };
   };
 
 
@@ -215,140 +225,149 @@ const AppDashboard: React.FC<Props> = ({ }) => {
       />
       />
       {currentCluster?.status === "UPDATING_UNAVAILABLE" ? (
       {currentCluster?.status === "UPDATING_UNAVAILABLE" ? (
         <ClusterProvisioningPlaceholder />
         <ClusterProvisioningPlaceholder />
-      ) : (
-        apps.length === 0 ? (
-          isLoading ?
-            (<Loading offset="-150px" />) : (
-              <DashboardPlaceholder>
-                <Text size={16}>
-                  No apps have been deployed yet
-                </Text>
-                <Spacer y={0.5} />
-                <Text color={"helper"}>
-                  Get started by deploying your app.
-                </Text>
-                <Spacer y={1} />
-                <PorterLink to="/apps/new/app">
-                  <Button alt onClick={async () => updateStackStartedStep()} height="35px">
-                    Deploy app <Spacer inline x={1} /> <i className="material-icons" style={{ fontSize: '18px' }}>east</i>
-                  </Button>
-                </PorterLink>
-              </DashboardPlaceholder>
-            )
+      ) : apps.length === 0 ? (
+        isLoading ? (
+          <Loading offset="-150px" />
         ) : (
         ) : (
-          <>
-            <Container row spaced>
-              <SearchBar
-                value={searchValue}
-                setValue={(x) => {
-                  if (x === "open_sesame") {
-                    setFeaturePreview(true);
-                  }
-                  setSearchValue(x);
-                }}
-                placeholder="Search applications . . ."
-                width="100%"
-              />
-              <Spacer inline x={2} />
-              <Toggle
-                items={[
-                  { label: <ToggleIcon src={calendar} />, value: "calendar" },
-                  { label: <ToggleIcon src={letter} />, value: "letter" },
-                ]}
-                active={sort}
-                setActive={setSort}
-              />
-              <Spacer inline x={1} />
-
-              <Toggle
-                items={[
-                  { label: <ToggleIcon src={grid} />, value: "grid" },
-                  { label: <ToggleIcon src={list} />, value: "list" },
-                ]}
-                active={view}
-                setActive={setView}
-              />
-
-              <Spacer inline x={2} />
-              <PorterLink to="/apps/new/app">
-                <Button onClick={async () => updateStackStartedStep()} height="30px" width="160px">
-                  <I className="material-icons">add</I> New application
-                </Button>
-              </PorterLink>
-            </Container>
+          <DashboardPlaceholder>
+            <Text size={16}>No apps have been deployed yet</Text>
+            <Spacer y={0.5} />
+            <Text color={"helper"}>Get started by deploying your app.</Text>
             <Spacer y={1} />
             <Spacer y={1} />
+            <PorterLink to="/apps/new/app">
+              <Button
+                alt
+                onClick={async () => {
+                  await updateStackStartedStep();
+                }}
+                height="35px"
+              >
+                Deploy app <Spacer inline x={1} />{" "}
+                <i className="material-icons" style={{ fontSize: "18px" }}>
+                  east
+                </i>
+              </Button>
+            </PorterLink>
+          </DashboardPlaceholder>
+        )
+      ) : (
+        <>
+          <Container row spaced>
+            <SearchBar
+              value={searchValue}
+              setValue={(x) => {
+                if (x === "open_sesame") {
+                  setFeaturePreview(true);
+                }
+                setSearchValue(x);
+              }}
+              placeholder="Search applications . . ."
+              width="100%"
+            />
+            <Spacer inline x={2} />
+            <Toggle
+              items={[
+                { label: <ToggleIcon src={calendar} />, value: "calendar" },
+                { label: <ToggleIcon src={letter} />, value: "letter" },
+              ]}
+              active={sort}
+              setActive={setSort}
+            />
+            <Spacer inline x={1} />
+
+            <Toggle
+              items={[
+                { label: <ToggleIcon src={grid} />, value: "grid" },
+                { label: <ToggleIcon src={list} />, value: "list" },
+              ]}
+              active={view}
+              setActive={setView}
+            />
 
 
-            {filteredApps.length === 0 ? (
-              <Fieldset>
-                <Container row>
-                  <PlaceholderIcon src={notFound} />
-                  <Text color="helper">No matching apps were found.</Text>
-                </Container>
-              </Fieldset>
-            ) : (isLoading ? (
-              <Loading offset="-150px" />
-            ) : view === "grid" ? (
-              <GridList>
-                {(filteredApps ?? []).map((app: any, i: number) => {
-                  if (!namespaceBlacklist.includes(app.name)) {
-                    return (
-                      <Link to={`/apps/${app.name}`} key={i}>
-                        <Block>
-                          <Container row>
-                            {renderIcon(app["buildpacks"])}
-                            <Spacer inline width="12px" />
-                            <Text size={14}>{app.name}</Text>
-                            <Spacer inline x={2} />
-                          </Container>
-                          <StatusIcon src={healthy} />
+            <Spacer inline x={2} />
+            <PorterLink to="/apps/new/app">
+              <Button
+                onClick={async () => {
+                  await updateStackStartedStep();
+                }}
+                height="30px"
+                width="160px"
+              >
+                <I className="material-icons">add</I> New application
+              </Button>
+            </PorterLink>
+          </Container>
+          <Spacer y={1} />
+
+          {filteredApps.length === 0 ? (
+            <Fieldset>
+              <Container row>
+                <PlaceholderIcon src={notFound} />
+                <Text color="helper">No matching apps were found.</Text>
+              </Container>
+            </Fieldset>
+          ) : isLoading ? (
+            <Loading offset="-150px" />
+          ) : view === "grid" ? (
+            <GridList>
+              {(filteredApps ?? []).map((app: any, i: number) => {
+                if (!namespaceBlacklist.includes(app.name)) {
+                  return (
+                    <Link to={`/apps/${app.name}`} key={i}>
+                      <Block>
+                        <Container row>
+                          {renderIcon(app.buildpacks)}
+                          <Spacer inline width="12px" />
+                          <Text size={14}>{app.name}</Text>
+                          <Spacer inline x={2} />
+                        </Container>
+                        <StatusIcon src={healthy} />
+                        {renderSource(app)}
+                        <Container row>
+                          <SmallIcon opacity="0.4" src={time} />
+                          <Text size={13} color="#ffffff44">
+                            {app.last_deployed}
+                          </Text>
+                        </Container>
+                      </Block>
+                    </Link>
+                  );
+                }
+              })}
+            </GridList>
+          ) : (
+            <List>
+              {(filteredApps ?? []).map((app: any, i: number) => {
+                if (!namespaceBlacklist.includes(app.name)) {
+                  return (
+                    <Link to={`/apps/${app.name}`} key={i}>
+                      <Row>
+                        <Container row>
+                          <Spacer inline width="1px" />
+                          {renderIcon(app.buildpacks, "larger")}
+                          <Spacer inline width="12px" />
+                          <Text size={14}>{app.name}</Text>
+                          <Spacer inline x={1} />
+                          <Icon height="16px" src={healthy} />
+                        </Container>
+                        <Spacer height="15px" />
+                        <Container row>
                           {renderSource(app)}
                           {renderSource(app)}
-                          <Container row>
-                            <SmallIcon opacity="0.4" src={time} />
-                            <Text size={13} color="#ffffff44">{app.last_deployed}</Text>
-                          </Container>
-                        </Block>
-                      </Link>
-                    );
-                  }
-                })}
-              </GridList>
-            ) : (
-              <List>
-                {(filteredApps ?? []).map((app: any, i: number) => {
-                  if (!namespaceBlacklist.includes(app.name)) {
-                    return (
-                      <Link to={`/apps/${app.name}`} key={i}>
-                        <Row>
-                          <Container row>
-                            <Spacer inline width="1px" />
-                            {renderIcon(app["buildpacks"], "larger")}
-                            <Spacer inline width="12px" />
-                            <Text size={14}>
-                              {app.name}
-                            </Text>
-                            <Spacer inline x={1} />
-                            <Icon height="16px" src={healthy} />
-                          </Container>
-                          <Spacer height="15px" />
-                          <Container row>
-                            {renderSource(app)}
-                            <Spacer inline x={1} />
-                            <SmallIcon opacity="0.4" src={time} />
-                            <Text size={13} color="#ffffff44">
-                              {app.last_deployed}
-                            </Text>
-                          </Container>
-                        </Row>
-                      </Link>
-                    );
-                  }
-                })}
-              </List>
-            ))}
-          </>
-        )
-      )
-      }
+                          <Spacer inline x={1} />
+                          <SmallIcon opacity="0.4" src={time} />
+                          <Text size={13} color="#ffffff44">
+                            {app.last_deployed}
+                          </Text>
+                        </Container>
+                      </Row>
+                    </Link>
+                  );
+                }
+              })}
+            </List>
+          )}
+        </>
+      )}
       <Spacer y={5} />
       <Spacer y={5} />
     </StyledAppDashboard>
     </StyledAppDashboard>
   );
   );
@@ -451,5 +470,5 @@ const CentralContainer = styled.div`
   display: flex;
   display: flex;
   flex-direction: column;
   flex-direction: column;
   justify-content: left;
   justify-content: left;
-  align-items: left;   
-`;
+  align-items: left;
+`;

+ 53 - 34
dashboard/src/main/home/app-dashboard/apps/Apps.tsx

@@ -7,10 +7,13 @@ import { z } from "zod";
 
 
 import ClusterProvisioningPlaceholder from "components/ClusterProvisioningPlaceholder";
 import ClusterProvisioningPlaceholder from "components/ClusterProvisioningPlaceholder";
 import Loading from "components/Loading";
 import Loading from "components/Loading";
+import Banner from "components/porter/Banner";
 import Button from "components/porter/Button";
 import Button from "components/porter/Button";
 import Container from "components/porter/Container";
 import Container from "components/porter/Container";
 import DashboardPlaceholder from "components/porter/DashboardPlaceholder";
 import DashboardPlaceholder from "components/porter/DashboardPlaceholder";
+import Image from "components/porter/Image";
 import PorterLink from "components/porter/Link";
 import PorterLink from "components/porter/Link";
+import Link from "components/porter/Link";
 import Modal from "components/porter/Modal";
 import Modal from "components/porter/Modal";
 import SearchBar from "components/porter/SearchBar";
 import SearchBar from "components/porter/SearchBar";
 import Spacer from "components/porter/Spacer";
 import Spacer from "components/porter/Spacer";
@@ -28,6 +31,7 @@ import { Context } from "shared/Context";
 import { useDeploymentTarget } from "shared/DeploymentTargetContext";
 import { useDeploymentTarget } from "shared/DeploymentTargetContext";
 import applicationGrad from "assets/application-grad.svg";
 import applicationGrad from "assets/application-grad.svg";
 import calendar from "assets/calendar-number.svg";
 import calendar from "assets/calendar-number.svg";
+import gift from "assets/gift.svg";
 import grid from "assets/grid.png";
 import grid from "assets/grid.png";
 import list from "assets/list.png";
 import list from "assets/list.png";
 import pull_request from "assets/pull_request_icon.svg";
 import pull_request from "assets/pull_request_icon.svg";
@@ -212,31 +216,28 @@ const Apps: React.FC = () => {
       }
       }
 
 
       return (
       return (
-        <DashboardPlaceholder>
-          <Text size={16}>No applications have been created yet</Text>
-          <Spacer y={0.5} />
-          <Text color={"helper"}>Get started by creating an application.</Text>
-          <Spacer y={1} />
-          {currentProject?.billing_enabled && !hasPaymentEnabled ? (
-            <Button
-              alt
-              onClick={() => {
-                setShowBillingModal(true);
-              }}
-              height="35px"
-            >
-              Create a new application
-              <Spacer inline x={1} />{" "}
-              <i className="material-icons" style={{ fontSize: "18px" }}>
-                east
-              </i>
-            </Button>
-          ) : (
-            <PorterLink to="/apps/new/app">
+        <>
+          {currentProject?.sandbox_enabled && (
+            <>
+              <Banner icon={<Image src={gift} />}>
+                $5 of Porter credits have automatically been credited to your
+                account.
+              </Banner>
+              <Spacer y={1} />
+            </>
+          )}
+          <DashboardPlaceholder>
+            <Text size={16}>No applications have been created yet</Text>
+            <Spacer y={0.5} />
+            <Text color={"helper"}>
+              Get started by creating an application.
+            </Text>
+            <Spacer y={1} />
+            {currentProject?.billing_enabled && !hasPaymentEnabled ? (
               <Button
               <Button
                 alt
                 alt
-                onClick={async () => {
-                  await updateAppStep({ step: "stack-launch-start" });
+                onClick={() => {
+                  setShowBillingModal(true);
                 }}
                 }}
                 height="35px"
                 height="35px"
               >
               >
@@ -246,17 +247,35 @@ const Apps: React.FC = () => {
                   east
                   east
                 </i>
                 </i>
               </Button>
               </Button>
-            </PorterLink>
-          )}
-          {showBillingModal && (
-            <BillingModal
-              back={() => setShowBillingModal(false)}
-              onCreate={() => {
-                history.push("/apps/new/app");
-              }}
-            />
-          )}
-        </DashboardPlaceholder>
+            ) : (
+              <PorterLink to="/apps/new/app">
+                <Button
+                  alt
+                  onClick={async () => {
+                    await updateAppStep({ step: "stack-launch-start" });
+                  }}
+                  height="35px"
+                >
+                  Create a new application
+                  <Spacer inline x={1} />{" "}
+                  <i className="material-icons" style={{ fontSize: "18px" }}>
+                    east
+                  </i>
+                </Button>
+              </PorterLink>
+            )}
+            {showBillingModal && (
+              <BillingModal
+                back={() => {
+                  setShowBillingModal(false);
+                }}
+                onCreate={() => {
+                  history.push("/apps/new/app");
+                }}
+              />
+            )}
+          </DashboardPlaceholder>
+        </>
       );
       );
     }
     }
 
 

+ 21 - 1
dashboard/src/main/home/project-settings/BillingPage.tsx

@@ -6,6 +6,7 @@ import Button from "components/porter/Button";
 import Container from "components/porter/Container";
 import Container from "components/porter/Container";
 import Fieldset from "components/porter/Fieldset";
 import Fieldset from "components/porter/Fieldset";
 import Icon from "components/porter/Icon";
 import Icon from "components/porter/Icon";
+import Image from "components/porter/Image";
 import Spacer from "components/porter/Spacer";
 import Spacer from "components/porter/Spacer";
 import Text from "components/porter/Text";
 import Text from "components/porter/Text";
 import {
 import {
@@ -17,6 +18,7 @@ import {
 
 
 import { Context } from "shared/Context";
 import { Context } from "shared/Context";
 import cardIcon from "assets/credit-card.svg";
 import cardIcon from "assets/credit-card.svg";
+import gift from "assets/gift.svg";
 import trashIcon from "assets/trash.png";
 import trashIcon from "assets/trash.png";
 
 
 import BillingModal from "../modals/BillingModal";
 import BillingModal from "../modals/BillingModal";
@@ -44,12 +46,30 @@ function BillingPage(): JSX.Element {
 
 
   if (shouldCreate) {
   if (shouldCreate) {
     return (
     return (
-      <BillingModal onCreate={onCreate} back={() => setShouldCreate(false)} />
+      <BillingModal
+        onCreate={onCreate}
+        back={() => {
+          setShouldCreate(false);
+        }}
+      />
     );
     );
   }
   }
 
 
   return (
   return (
     <>
     <>
+      <Text size={16}>Porter credit balance</Text>
+      <Spacer y={1} />
+      <Text color="helper">
+        View the amount of Porter credits you have available to spend on
+        resources within this project.
+      </Text>
+      <Spacer y={1} />
+      <Container row>
+        <Image src={gift} style={{ marginTop: "-2px" }} />
+        <Spacer inline x={1} />
+        <Text size={20}>$ 5.00</Text>
+      </Container>
+      <Spacer y={2} />
       <Text size={16}>Payment methods</Text>
       <Text size={16}>Payment methods</Text>
       <Spacer y={1} />
       <Spacer y={1} />
       <Text color="helper">
       <Text color="helper">