diff --git a/components/dashboard/src/admin/TeamDetail.tsx b/components/dashboard/src/admin/TeamDetail.tsx index 5fefc823ac121d..a9849c26f4d42b 100644 --- a/components/dashboard/src/admin/TeamDetail.tsx +++ b/components/dashboard/src/admin/TeamDetail.tsx @@ -15,14 +15,22 @@ import Label from "./Label"; import Property from "./Property"; import { AttributionId } from "@gitpod/gitpod-protocol/lib/attribution"; import { BillingMode } from "@gitpod/gitpod-protocol/lib/billing-mode"; +import { CostCenterJSON, CostCenter_BillingStrategy } from "@gitpod/gitpod-protocol/lib/usage"; +import Modal from "../components/Modal"; export default function TeamDetail(props: { team: Team }) { const { team } = props; const [teamMembers, setTeamMembers] = useState(undefined); const [billingMode, setBillingMode] = useState(undefined); const [searchText, setSearchText] = useState(""); + const [costCenter, setCostCenter] = useState(); + const [usageBalance, setUsageBalance] = useState(0); + const [usageLimit, setUsageLimit] = useState(); + const [editSpendingLimit, setEditSpendingLimit] = useState(false); + const [creditNote, setCreditNote] = useState<{ credits: number; note?: string }>({ credits: 0 }); + const [editAddCreditNote, setEditAddCreditNote] = useState(false); - useEffect(() => { + const initialize = () => { (async () => { const members = await getGitpodService().server.adminGetTeamMembers(team.id); if (members.length > 0) { @@ -30,9 +38,22 @@ export default function TeamDetail(props: { team: Team }) { } })(); getGitpodService() - .server.adminGetBillingMode(AttributionId.render({ kind: "team", teamId: props.team.id })) + .server.adminGetBillingMode(AttributionId.render({ kind: "team", teamId: team.id })) .then((bm) => setBillingMode(bm)); - }, [team]); + const attributionId = AttributionId.render(AttributionId.create(team)); + getGitpodService().server.adminGetBillingMode(attributionId).then(setBillingMode); + getGitpodService().server.adminGetCostCenter(attributionId).then(setCostCenter); + getGitpodService().server.adminGetUsageBalance(attributionId).then(setUsageBalance); + }; + + useEffect(initialize, [team]); + + useEffect(() => { + if (!costCenter) { + return; + } + setUsageLimit(costCenter.spendingLimit); + }, [costCenter]); const filteredMembers = teamMembers?.filter((m) => { const memberSearchText = `${m.fullName || ""}${m.primaryEmail || ""}`.toLocaleLowerCase(); @@ -64,8 +85,53 @@ export default function TeamDetail(props: { team: Team }) {
- {!team.markedDeleted && teamMembers && {teamMembers.length}} - {!team.markedDeleted && {billingMode?.mode || "---"}} + {!team.markedDeleted && {teamMembers?.length || "?"}} + {!team.markedDeleted && {billingMode?.mode || "---"}} + {costCenter && ( + + + {costCenter?.billingStrategy === CostCenter_BillingStrategy.BILLING_STRATEGY_STRIPE + ? "Active" + : "Inactive"} + + + )} +
+
+ {costCenter && ( + + + {dayjs(costCenter?.billingCycleStart).format("MMM D")} -{" "} + {dayjs(costCenter?.nextBillingTime).format("MMM D")} + + + )} + {costCenter && ( + setEditAddCreditNote(true), + }, + ]} + > + {usageBalance * -1 + (costCenter?.spendingLimit || 0)} Credits + + )} + {costCenter && ( + setEditSpendingLimit(true), + }, + ]} + > + {costCenter?.spendingLimit} Credits + + )}
@@ -151,6 +217,91 @@ export default function TeamDetail(props: { team: Team }) { )) )} + setEditSpendingLimit(false)} + title="Change Usage Limit" + onEnter={() => false} + buttons={[ + , + ]} + > +

Change the usage limit in credits per month.

+ +
+ setUsageLimit(Number.parseInt(event.target.value))} + /> +
+
+ false} + visible={editAddCreditNote} + onClose={() => setEditAddCreditNote(false)} + title="Add Credits" + buttons={[ + , + ]} + > +

Adds or subtracts the amount of credits from this account.

+
+ + + setCreditNote({ credits: Number.parseInt(event.target.value), note: creditNote.note }) + } + /> + +