Skip to content

Commit 8e3268c

Browse files
committed
[dashboard] access invoices after cancellation
1 parent 35a30ea commit 8e3268c

File tree

1 file changed

+52
-29
lines changed

1 file changed

+52
-29
lines changed

components/dashboard/src/components/UsageBasedBillingConfig.tsx

Lines changed: 52 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* See License.AGPL.txt in the project root for license information.
55
*/
66

7-
import { useState, useContext, useEffect } from "react";
7+
import { useState, useContext, useEffect, useCallback, useMemo } from "react";
88
import { useLocation } from "react-router";
99
import { Link } from "react-router-dom";
1010
import { Appearance, loadStripe, Stripe } from "@stripe/stripe-js";
@@ -47,43 +47,46 @@ export default function UsageBasedBillingConfig({ attributionId }: Props) {
4747
const [errorMessage, setErrorMessage] = useState<string | undefined>();
4848

4949
const localStorageKey = `pendingStripeSubscriptionFor${attributionId}`;
50-
const now = dayjs().utc(true);
50+
const now = useMemo(() => dayjs().utc(true), []);
5151
const [billingCycleFrom, setBillingCycleFrom] = useState<dayjs.Dayjs>(now.startOf("month"));
5252
const [billingCycleTo, setBillingCycleTo] = useState<dayjs.Dayjs>(now.endOf("month"));
5353

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+
);
7073

7174
useEffect(() => {
7275
if (!attributionId) {
7376
return;
7477
}
7578
refreshSubscriptionDetails(attributionId);
76-
}, [attributionId]);
79+
}, [attributionId, refreshSubscriptionDetails]);
7780

7881
useEffect(() => {
79-
if (!attributionId || !stripeSubscriptionId) {
82+
if (!attributionId) {
8083
return;
8184
}
8285
(async () => {
8386
const portalUrl = await getGitpodService().server.getStripePortalUrl(attributionId);
8487
setStripePortalUrl(portalUrl);
8588
})();
86-
}, [attributionId, stripeSubscriptionId]);
89+
}, [attributionId]);
8790

8891
useEffect(() => {
8992
if (!attributionId) {
@@ -124,7 +127,14 @@ export default function UsageBasedBillingConfig({ attributionId }: Props) {
124127
setErrorMessage(`Could not subscribe to Stripe. ${error?.message || String(error)}`);
125128
}
126129
})();
127-
}, [attributionId, location.search]);
130+
}, [
131+
attributionId,
132+
localStorageKey,
133+
location.pathname,
134+
location.search,
135+
pollStripeSubscriptionTimeout,
136+
usePublicApiTeamsService,
137+
]);
128138

129139
useEffect(() => {
130140
setPendingStripeSubscription(undefined);
@@ -175,6 +185,7 @@ export default function UsageBasedBillingConfig({ attributionId }: Props) {
175185
stripeSubscriptionId,
176186
attributionId,
177187
localStorageKey,
188+
refreshSubscriptionDetails,
178189
]);
179190

180191
useEffect(() => {
@@ -299,9 +310,16 @@ export default function UsageBasedBillingConfig({ attributionId }: Props) {
299310
</span>
300311
</div>
301312
</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>
305323
</div>
306324
)}
307325
{showUpgradeUser && (
@@ -343,10 +361,15 @@ export default function UsageBasedBillingConfig({ attributionId }: Props) {
343361
</span>
344362
</div>
345363
</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>
350373
</div>
351374
</div>
352375
</div>

0 commit comments

Comments
 (0)