|
4 | 4 | * See License.AGPL.txt in the project root for license information.
|
5 | 5 | */
|
6 | 6 |
|
7 |
| -import { useState, useContext, useEffect } from "react"; |
| 7 | +import { useState, useContext, useEffect, useCallback, useMemo } from "react"; |
8 | 8 | import { useLocation } from "react-router";
|
9 | 9 | import { Link } from "react-router-dom";
|
10 | 10 | import { Appearance, loadStripe, Stripe } from "@stripe/stripe-js";
|
@@ -47,43 +47,46 @@ export default function UsageBasedBillingConfig({ attributionId }: Props) {
|
47 | 47 | const [errorMessage, setErrorMessage] = useState<string | undefined>();
|
48 | 48 |
|
49 | 49 | const localStorageKey = `pendingStripeSubscriptionFor${attributionId}`;
|
50 |
| - const now = dayjs().utc(true); |
| 50 | + const now = useMemo(() => dayjs().utc(true), []); |
51 | 51 | const [billingCycleFrom, setBillingCycleFrom] = useState<dayjs.Dayjs>(now.startOf("month"));
|
52 | 52 | const [billingCycleTo, setBillingCycleTo] = useState<dayjs.Dayjs>(now.endOf("month"));
|
53 | 53 |
|
54 |
| - const refreshSubscriptionDetails = async (attributionId: string) => { |
55 |
| - setStripeSubscriptionId(undefined); |
56 |
| - setIsLoadingStripeSubscription(true); |
57 |
| - try { |
58 |
| - getGitpodService().server.findStripeSubscriptionId(attributionId).then(setStripeSubscriptionId); |
59 |
| - const costCenter = await getGitpodService().server.getCostCenter(attributionId); |
60 |
| - setUsageLimit(costCenter?.spendingLimit || 0); |
61 |
| - setBillingCycleFrom(dayjs(costCenter?.billingCycleStart || now.startOf("month")).utc(true)); |
62 |
| - setBillingCycleTo(dayjs(costCenter?.nextBillingTime || now.endOf("month")).utc(true)); |
63 |
| - } catch (error) { |
64 |
| - console.error("Could not get Stripe subscription details.", error); |
65 |
| - setErrorMessage(`Could not get Stripe subscription details. ${error?.message || String(error)}`); |
66 |
| - } finally { |
67 |
| - setIsLoadingStripeSubscription(false); |
68 |
| - } |
69 |
| - }; |
| 54 | + const refreshSubscriptionDetails = useCallback( |
| 55 | + async (attributionId: string) => { |
| 56 | + setStripeSubscriptionId(undefined); |
| 57 | + setIsLoadingStripeSubscription(true); |
| 58 | + try { |
| 59 | + getGitpodService().server.findStripeSubscriptionId(attributionId).then(setStripeSubscriptionId); |
| 60 | + const costCenter = await getGitpodService().server.getCostCenter(attributionId); |
| 61 | + setUsageLimit(costCenter?.spendingLimit || 0); |
| 62 | + setBillingCycleFrom(dayjs(costCenter?.billingCycleStart || now.startOf("month")).utc(true)); |
| 63 | + setBillingCycleTo(dayjs(costCenter?.nextBillingTime || now.endOf("month")).utc(true)); |
| 64 | + } catch (error) { |
| 65 | + console.error("Could not get Stripe subscription details.", error); |
| 66 | + setErrorMessage(`Could not get Stripe subscription details. ${error?.message || String(error)}`); |
| 67 | + } finally { |
| 68 | + setIsLoadingStripeSubscription(false); |
| 69 | + } |
| 70 | + }, |
| 71 | + [now], |
| 72 | + ); |
70 | 73 |
|
71 | 74 | useEffect(() => {
|
72 | 75 | if (!attributionId) {
|
73 | 76 | return;
|
74 | 77 | }
|
75 | 78 | refreshSubscriptionDetails(attributionId);
|
76 |
| - }, [attributionId]); |
| 79 | + }, [attributionId, refreshSubscriptionDetails]); |
77 | 80 |
|
78 | 81 | useEffect(() => {
|
79 |
| - if (!attributionId || !stripeSubscriptionId) { |
| 82 | + if (!attributionId) { |
80 | 83 | return;
|
81 | 84 | }
|
82 | 85 | (async () => {
|
83 | 86 | const portalUrl = await getGitpodService().server.getStripePortalUrl(attributionId);
|
84 | 87 | setStripePortalUrl(portalUrl);
|
85 | 88 | })();
|
86 |
| - }, [attributionId, stripeSubscriptionId]); |
| 89 | + }, [attributionId]); |
87 | 90 |
|
88 | 91 | useEffect(() => {
|
89 | 92 | if (!attributionId) {
|
@@ -124,7 +127,14 @@ export default function UsageBasedBillingConfig({ attributionId }: Props) {
|
124 | 127 | setErrorMessage(`Could not subscribe to Stripe. ${error?.message || String(error)}`);
|
125 | 128 | }
|
126 | 129 | })();
|
127 |
| - }, [attributionId, location.search]); |
| 130 | + }, [ |
| 131 | + attributionId, |
| 132 | + localStorageKey, |
| 133 | + location.pathname, |
| 134 | + location.search, |
| 135 | + pollStripeSubscriptionTimeout, |
| 136 | + usePublicApiTeamsService, |
| 137 | + ]); |
128 | 138 |
|
129 | 139 | useEffect(() => {
|
130 | 140 | setPendingStripeSubscription(undefined);
|
@@ -175,6 +185,7 @@ export default function UsageBasedBillingConfig({ attributionId }: Props) {
|
175 | 185 | stripeSubscriptionId,
|
176 | 186 | attributionId,
|
177 | 187 | localStorageKey,
|
| 188 | + refreshSubscriptionDetails, |
178 | 189 | ]);
|
179 | 190 |
|
180 | 191 | useEffect(() => {
|
@@ -299,9 +310,16 @@ export default function UsageBasedBillingConfig({ attributionId }: Props) {
|
299 | 310 | </span>
|
300 | 311 | </div>
|
301 | 312 | </div>
|
302 |
| - <button className="mt-5 self-end" onClick={() => setShowBillingSetupModal(true)}> |
303 |
| - Upgrade Plan |
304 |
| - </button> |
| 313 | + <div className="flex justify-end mt-6 space-x-2"> |
| 314 | + {stripePortalUrl && ( |
| 315 | + <a href={stripePortalUrl}> |
| 316 | + <button className="secondary" disabled={!stripePortalUrl}> |
| 317 | + View Past Invoices ↗ |
| 318 | + </button> |
| 319 | + </a> |
| 320 | + )} |
| 321 | + <button onClick={() => setShowBillingSetupModal(true)}>Upgrade Plan</button> |
| 322 | + </div> |
305 | 323 | </div>
|
306 | 324 | )}
|
307 | 325 | {showUpgradeUser && (
|
@@ -343,10 +361,15 @@ export default function UsageBasedBillingConfig({ attributionId }: Props) {
|
343 | 361 | </span>
|
344 | 362 | </div>
|
345 | 363 | </div>
|
346 |
| - <div className="mt-5 flex flex-col"> |
347 |
| - <button className="self-end" onClick={() => setShowBillingSetupModal(true)}> |
348 |
| - Upgrade Plan |
349 |
| - </button> |
| 364 | + <div className="flex justify-end mt-6 space-x-2"> |
| 365 | + {stripePortalUrl && ( |
| 366 | + <a href={stripePortalUrl}> |
| 367 | + <button className="secondary" disabled={!stripePortalUrl}> |
| 368 | + View Past Invoices ↗ |
| 369 | + </button> |
| 370 | + </a> |
| 371 | + )} |
| 372 | + <button onClick={() => setShowBillingSetupModal(true)}>Upgrade Plan</button> |
350 | 373 | </div>
|
351 | 374 | </div>
|
352 | 375 | </div>
|
|
0 commit comments