Skip to content

Commit 56ba342

Browse files
Initial servers upgrades frontend (#3219)
* Initial servers upgrades frontend * Fix error when purchasing non-custom servers * fix backend * Fix comment --------- Signed-off-by: Jai Agrawal <18202329+Geometrically@users.noreply.github.com> Co-authored-by: Jai A <jaiagr+gpg@pm.me> Co-authored-by: Jai Agrawal <18202329+Geometrically@users.noreply.github.com>
1 parent 6d810a4 commit 56ba342

File tree

5 files changed

+389
-171
lines changed

5 files changed

+389
-171
lines changed

apps/frontend/src/pages/settings/billing/index.vue

Lines changed: 165 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<section class="universal-card">
2+
<section class="universal-card experimental-styles-within">
33
<h2>{{ formatMessage(messages.subscriptionTitle) }}</h2>
44
<p>{{ formatMessage(messages.subscriptionDescription) }}</p>
55
<div class="universal-card recessed">
@@ -88,67 +88,69 @@
8888
v-if="midasCharge && midasCharge.status === 'failed'"
8989
class="ml-auto flex flex-row-reverse items-center gap-2"
9090
>
91+
<ButtonStyled v-if="midasCharge && midasCharge.status === 'failed'">
92+
<button
93+
@click="
94+
() => {
95+
$refs.midasPurchaseModal.show();
96+
}
97+
"
98+
>
99+
<UpdatedIcon />
100+
Update method
101+
</button>
102+
</ButtonStyled>
103+
<ButtonStyled type="transparent" circular>
104+
<OverflowMenu
105+
:dropdown-id="`${baseId}-cancel-midas`"
106+
:options="[
107+
{
108+
id: 'cancel',
109+
action: () => {
110+
cancelSubscriptionId = midasSubscription.id;
111+
$refs.modalCancel.show();
112+
},
113+
},
114+
]"
115+
>
116+
<MoreVerticalIcon />
117+
<template #cancel><XIcon /> Cancel</template>
118+
</OverflowMenu>
119+
</ButtonStyled>
120+
</div>
121+
<ButtonStyled v-else-if="midasCharge && midasCharge.status !== 'cancelled'">
91122
<button
92-
v-if="midasCharge && midasCharge.status === 'failed'"
93-
class="iconified-button raised-button"
123+
class="ml-auto"
94124
@click="
95125
() => {
96-
purchaseModalStep = 0;
97-
$refs.purchaseModal.show();
126+
cancelSubscriptionId = midasSubscription.id;
127+
$refs.modalCancel.show();
98128
}
99129
"
100130
>
101-
<UpdatedIcon />
102-
Update method
131+
<XIcon /> Cancel
103132
</button>
104-
<OverflowMenu
105-
class="btn icon-only transparent"
106-
:options="[
107-
{
108-
id: 'cancel',
109-
action: () => {
110-
cancelSubscriptionId = midasSubscription.id;
111-
$refs.modalCancel.show();
112-
},
113-
},
114-
]"
115-
>
116-
<MoreVerticalIcon />
117-
<template #cancel><XIcon /> Cancel</template>
118-
</OverflowMenu>
119-
</div>
120-
<button
121-
v-else-if="midasCharge && midasCharge.status !== 'cancelled'"
122-
class="iconified-button raised-button !ml-auto"
123-
@click="
124-
() => {
125-
cancelSubscriptionId = midasSubscription.id;
126-
$refs.modalCancel.show();
127-
}
128-
"
129-
>
130-
<XIcon /> Cancel
131-
</button>
132-
<button
133+
</ButtonStyled>
134+
<ButtonStyled
133135
v-else-if="midasCharge && midasCharge.status === 'cancelled'"
134-
class="btn btn-purple btn-large ml-auto"
135-
@click="cancelSubscription(midasSubscription.id, false)"
136+
color="purple"
136137
>
137-
<RightArrowIcon /> Resubscribe
138-
</button>
139-
<button
140-
v-else
141-
class="btn btn-purple btn-large ml-auto"
142-
@click="
143-
() => {
144-
purchaseModalStep = 0;
145-
$refs.purchaseModal.show();
146-
}
147-
"
148-
>
149-
<RightArrowIcon />
150-
Subscribe
151-
</button>
138+
<button class="ml-auto" @click="cancelSubscription(midasSubscription.id, false)">
139+
Resubscribe <RightArrowIcon />
140+
</button>
141+
</ButtonStyled>
142+
<ButtonStyled v-else color="purple" size="large">
143+
<button
144+
class="ml-auto"
145+
@click="
146+
() => {
147+
$refs.midasPurchaseModal.show();
148+
}
149+
"
150+
>
151+
Subscribe <RightArrowIcon />
152+
</button>
153+
</ButtonStyled>
152154
</div>
153155
</div>
154156
</div>
@@ -282,25 +284,37 @@
282284
getPyroCharge(subscription).status !== 'cancelled' &&
283285
getPyroCharge(subscription).status !== 'failed'
284286
"
285-
type="standard"
286-
@click="showPyroCancelModal(subscription.id)"
287287
>
288-
<button class="text-contrast">
288+
<button @click="showPyroCancelModal(subscription.id)">
289289
<XIcon />
290290
Cancel
291291
</button>
292292
</ButtonStyled>
293+
<ButtonStyled
294+
v-if="
295+
getPyroCharge(subscription) &&
296+
getPyroCharge(subscription).status !== 'cancelled' &&
297+
getPyroCharge(subscription).status !== 'failed'
298+
"
299+
color="green"
300+
color-fill="text"
301+
>
302+
<button @click="showPyroUpgradeModal(subscription)">
303+
<ArrowBigUpDashIcon />
304+
Upgrade
305+
</button>
306+
</ButtonStyled>
293307
<ButtonStyled
294308
v-else-if="
295309
getPyroCharge(subscription) &&
296310
(getPyroCharge(subscription).status === 'cancelled' ||
297311
getPyroCharge(subscription).status === 'failed')
298312
"
299-
type="standard"
300313
color="green"
301-
@click="resubscribePyro(subscription.id)"
302314
>
303-
<button class="text-contrast">Resubscribe</button>
315+
<button @click="resubscribePyro(subscription.id)">
316+
Resubscribe <RightArrowIcon />
317+
</button>
304318
</ButtonStyled>
305319
</div>
306320
</div>
@@ -312,7 +326,7 @@
312326
</div>
313327
</section>
314328

315-
<section class="universal-card">
329+
<section class="universal-card experimental-styles-within">
316330
<ConfirmModal
317331
ref="modal_confirm"
318332
:title="formatMessage(deleteModalMessages.title)"
@@ -321,7 +335,7 @@
321335
@proceed="removePaymentMethod(removePaymentMethodIndex)"
322336
/>
323337
<PurchaseModal
324-
ref="purchaseModal"
338+
ref="midasPurchaseModal"
325339
:product="midasProduct"
326340
:country="country"
327341
:publishable-key="config.public.stripePublishableKey"
@@ -342,6 +356,37 @@
342356
:payment-methods="paymentMethods"
343357
:return-url="`${config.public.siteUrl}/settings/billing`"
344358
/>
359+
<PurchaseModal
360+
ref="pyroPurchaseModal"
361+
:product="upgradeProducts"
362+
:country="country"
363+
custom-server
364+
:existing-subscription="currentSubscription"
365+
:existing-plan="currentProduct"
366+
:publishable-key="config.public.stripePublishableKey"
367+
:send-billing-request="
368+
async (body) =>
369+
await useBaseFetch(`billing/subscription/${currentSubscription.id}`, {
370+
internal: true,
371+
method: `PATCH`,
372+
body: body,
373+
})
374+
"
375+
:renewal-date="currentSubRenewalDate"
376+
:on-error="
377+
(err) =>
378+
data.$notify({
379+
group: 'main',
380+
title: 'An error occurred',
381+
type: 'error',
382+
text: err.message ?? (err.data ? err.data.description : err),
383+
})
384+
"
385+
:customer="customer"
386+
:payment-methods="paymentMethods"
387+
:return-url="`${config.public.siteUrl}/servers/manage`"
388+
:server-name="`${auth?.user?.username}'s server`"
389+
/>
345390
<NewModal ref="addPaymentMethodModal">
346391
<template #title>
347392
<span class="text-lg font-extrabold text-contrast">
@@ -359,15 +404,19 @@
359404
<div id="address-element"></div>
360405
<div id="payment-element" class="mt-4"></div>
361406
</div>
362-
<div v-show="loadingPaymentMethodModal === 2" class="input-group push-right mt-auto pt-4">
363-
<button class="btn" @click="$refs.addPaymentMethodModal.hide()">
364-
<XIcon />
365-
{{ formatMessage(commonMessages.cancelButton) }}
366-
</button>
367-
<button class="btn btn-primary" :disabled="loadingAddMethod" @click="submit">
368-
<PlusIcon />
369-
{{ formatMessage(messages.paymentMethodAdd) }}
370-
</button>
407+
<div v-show="loadingPaymentMethodModal === 2" class="input-group mt-auto pt-4">
408+
<ButtonStyled color="brand">
409+
<button :disabled="loadingAddMethod" @click="submit">
410+
<PlusIcon />
411+
{{ formatMessage(messages.paymentMethodAdd) }}
412+
</button>
413+
</ButtonStyled>
414+
<ButtonStyled>
415+
<button @click="$refs.addPaymentMethodModal.hide()">
416+
<XIcon />
417+
{{ formatMessage(commonMessages.cancelButton) }}
418+
</button>
419+
</ButtonStyled>
371420
</div>
372421
</div>
373422
</NewModal>
@@ -442,6 +491,7 @@
442491
</div>
443492
</div>
444493
<OverflowMenu
494+
:dropdown-id="`${baseId}-payment-method-overflow-${index}`"
445495
class="btn icon-only transparent"
446496
:options="
447497
[
@@ -493,6 +543,7 @@ import {
493543
} from "@modrinth/ui";
494544
import {
495545
PlusIcon,
546+
ArrowBigUpDashIcon,
496547
XIcon,
497548
CardIcon,
498549
MoreVerticalIcon,
@@ -515,6 +566,9 @@ definePageMeta({
515566
middleware: "auth",
516567
});
517568
569+
const auth = await useAuth();
570+
const baseId = useId();
571+
518572
useHead({
519573
script: [
520574
{
@@ -704,7 +758,7 @@ const pyroSubscriptions = computed(() => {
704758
});
705759
});
706760
707-
const purchaseModal = ref();
761+
const midasPurchaseModal = ref();
708762
const country = useUserCountry();
709763
const price = computed(() =>
710764
midasProduct.value?.prices?.find((x) => x.currency_code === getCurrency(country.value)),
@@ -896,6 +950,46 @@ const showPyroCancelModal = (subscriptionId) => {
896950
}
897951
};
898952
953+
const pyroPurchaseModal = ref();
954+
const currentSubscription = ref(null);
955+
const currentProduct = ref(null);
956+
const upgradeProducts = ref([]);
957+
upgradeProducts.value.metadata = { type: "pyro" };
958+
959+
const currentSubRenewalDate = ref();
960+
961+
const showPyroUpgradeModal = async (subscription) => {
962+
currentSubscription.value = subscription;
963+
currentSubRenewalDate.value = getPyroCharge(subscription).due;
964+
currentProduct.value = getPyroProduct(subscription);
965+
upgradeProducts.value = products.filter(
966+
(p) =>
967+
p.metadata.type === "pyro" &&
968+
(!currentProduct.value || p.metadata.ram > currentProduct.value.metadata.ram),
969+
);
970+
upgradeProducts.value.metadata = { type: "pyro" };
971+
972+
await nextTick();
973+
974+
if (!currentProduct.value) {
975+
console.error("Could not find product for current subscription");
976+
data.$notify({
977+
group: "main",
978+
title: "An error occurred",
979+
text: "Could not find product for current subscription",
980+
type: "error",
981+
});
982+
return;
983+
}
984+
985+
if (!pyroPurchaseModal.value) {
986+
console.error("pyroPurchaseModal ref is undefined");
987+
return;
988+
}
989+
990+
pyroPurchaseModal.value.show();
991+
};
992+
899993
const resubscribePyro = async (subscriptionId) => {
900994
try {
901995
await useBaseFetch(`billing/subscription/${subscriptionId}`, {

apps/labrinth/src/models/v3/billing.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ pub struct Charge {
176176
pub net: Option<i64>,
177177
}
178178

179-
#[derive(Serialize, Deserialize, Debug)]
179+
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
180180
#[serde(tag = "type", rename_all = "kebab-case")]
181181
pub enum ChargeType {
182182
OneTime,

0 commit comments

Comments
 (0)