diff --git a/components/dashboard/src/components/ContextMenu.tsx b/components/dashboard/src/components/ContextMenu.tsx
index 7b4db1518ef80e..206e8368052713 100644
--- a/components/dashboard/src/components/ContextMenu.tsx
+++ b/components/dashboard/src/components/ContextMenu.tsx
@@ -35,7 +35,7 @@ function ContextMenu(props: ContextMenuProps) {
setExpanded(!expanded);
};
- const handler = (evt: KeyboardEvent) => {
+ const keydownHandler = (evt: KeyboardEvent) => {
if (evt.key === "Escape") {
setExpanded(false);
}
@@ -55,11 +55,11 @@ function ContextMenu(props: ContextMenuProps) {
};
useEffect(() => {
- window.addEventListener("keydown", handler);
+ window.addEventListener("keydown", keydownHandler);
window.addEventListener("click", clickHandler);
// Remove event listeners on cleanup
return () => {
- window.removeEventListener("keydown", handler);
+ window.removeEventListener("keydown", keydownHandler);
window.removeEventListener("click", clickHandler);
};
}, []); // Empty array ensures that effect is only run on mount and unmount
diff --git a/components/dashboard/src/components/Modal.tsx b/components/dashboard/src/components/Modal.tsx
index df1a4e65d06072..c65e7bcbd039fe 100644
--- a/components/dashboard/src/components/Modal.tsx
+++ b/components/dashboard/src/components/Modal.tsx
@@ -79,7 +79,6 @@ export default function Modal(props: {
"relative bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-xl p-6 max-w-lg mx-auto text-left " +
(props.className || "")
}
- onClick={(e) => e.stopPropagation()}
>
{props.closeable !== false && (
(undefined);
const [stripeSubscriptionId, setStripeSubscriptionId] = useState();
const [isLoading, setIsLoading] = useState(true);
@@ -87,7 +87,7 @@ export default function TeamUsageBasedBilling() {
JSON.stringify(pendingSubscription),
);
try {
- await getGitpodService().server.subscribeTeamToStripe(team.id, setupIntentId, currency);
+ await getGitpodService().server.subscribeTeamToStripe(team.id, setupIntentId);
} catch (error) {
console.error("Could not subscribe team to Stripe", error);
window.localStorage.removeItem(`pendingStripeSubscriptionForTeam${team.id}`);
@@ -214,7 +214,9 @@ export default function TeamUsageBasedBilling() {
)}
- {showBillingSetupModal && setShowBillingSetupModal(false)} />}
+ {showBillingSetupModal && (
+ setShowBillingSetupModal(false)} />
+ )}
{showUpdateLimitModal && (
void }) {
+function BillingSetupModal(props: { teamId: string; onClose: () => void }) {
const { isDark } = useContext(ThemeContext);
const [stripePromise, setStripePromise] = useState | undefined>();
const [stripeSetupIntentClientSecret, setStripeSetupIntentClientSecret] = useState();
@@ -297,7 +299,12 @@ function BillingSetupModal(props: { onClose: () => void }) {
return (
Upgrade Billing
-
+
+ {(!stripePromise || !stripeSetupIntentClientSecret) && (
+
+
+
+ )}
{!!stripePromise && !!stripeSetupIntentClientSecret && (
void }) {
clientSecret: stripeSetupIntentClientSecret,
}}
>
-
+
)}
@@ -314,9 +321,10 @@ function BillingSetupModal(props: { onClose: () => void }) {
);
}
-function CreditCardInputForm() {
+function CreditCardInputForm(props: { teamId: string }) {
const stripe = useStripe();
const elements = useElements();
+ const { currency, setCurrency } = useContext(PaymentContext);
const [isLoading, setIsLoading] = useState
(false);
const handleSubmit = async (event: React.FormEvent) => {
@@ -326,6 +334,8 @@ function CreditCardInputForm() {
}
setIsLoading(true);
try {
+ // Create Stripe customer for team & currency (or update currency)
+ await getGitpodService().server.createOrUpdateStripeCustomerForTeam(props.teamId, currency);
const result = await stripe.confirmSetup({
elements,
confirmParams: {
@@ -351,7 +361,25 @@ function CreditCardInputForm() {
return (