Skip to content
This repository was archived by the owner on Apr 19, 2023. It is now read-only.

Commit a06160c

Browse files
✨ Create subscriptions
1 parent b9b9efa commit a06160c

File tree

4 files changed

+229
-13
lines changed

4 files changed

+229
-13
lines changed

pages/manage/_team/subscriptions/index.vue

Lines changed: 144 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
cta-text="Setup billing"
88
cta-to="/manage/billing"
99
/>
10+
<Loading v-else-if="loading" :message="loading" />
1011
<div v-else>
11-
<h1>Subscription</h1>
12-
<Loading v-if="loading" :message="loading" />
12+
<h1>Subscriptions</h1>
1313
<LargeMessage
14-
v-else-if="
14+
v-if="
1515
!loading &&
1616
(!subscriptions ||
1717
!subscriptions.data ||
@@ -25,21 +25,105 @@
2525
subscriptions && subscriptions.data && subscriptions.data.length
2626
"
2727
>
28-
<table class="table table--type-cols">
28+
<table class="table">
29+
<thead>
30+
<tr>
31+
<th>Plan</th>
32+
<th>Price</th>
33+
<th>Billing period</th>
34+
<th></th>
35+
</tr>
36+
</thead>
2937
<tbody>
3038
<tr
3139
v-for="(subscription, index) in subscriptions.data"
3240
:key="`${subscription.id}_${index}`"
3341
>
3442
<td>
35-
{{ subscription }}
43+
<span>{{ subscription.plan.nickname }}</span>
44+
<span :class="`label label--color-${subscription.status}`">{{
45+
subscription.status
46+
}}</span>
47+
</td>
48+
<td>
49+
<span>{{ subscription.plan.currency.toUpperCase() }}</span>
50+
<span>{{ subscription.plan.amount }}</span>
51+
<span>{{
52+
subscription.plan.interval_count == 1
53+
? "per"
54+
: `per ${subscription.plan.interval_count}`
55+
}}</span>
56+
<span v-if="subscription.plan.interval_count != 1">{{
57+
subscription.plan.interval_count
58+
}}</span>
59+
<span>{{ subscription.plan.interval }}</span>
60+
</td>
61+
<td>
62+
Ends <TimeAgo :date="subscription.current_period_end * 1000" />
63+
</td>
64+
<td class="text text--align-right">
65+
<router-link
66+
:to="
67+
`/manage/${$route.params.team}/invoices/${subscription.latest_invoice}`
68+
"
69+
data-balloon="Last invoice"
70+
data-balloon-pos="up"
71+
class="button button--type-icon"
72+
>
73+
<font-awesome-icon
74+
title="Last invoice"
75+
class="icon"
76+
icon="file-invoice-dollar"
77+
fixed-width
78+
/>
79+
</router-link>
80+
<router-link
81+
:to="
82+
`/manage/${$route.params.team}/subscriptions/${subscription.id}`
83+
"
84+
data-balloon="Edit"
85+
data-balloon-pos="up"
86+
class="button button--type-icon"
87+
>
88+
<font-awesome-icon
89+
title="Edit"
90+
class="icon"
91+
icon="pencil-alt"
92+
fixed-width
93+
/>
94+
</router-link>
3695
</td>
3796
</tr>
3897
</tbody>
3998
</table>
99+
<div class="pagination text text--align-center">
100+
<button
101+
v-if="subscriptions && subscriptions.hasMore"
102+
class="button"
103+
:disabled="loadingMore"
104+
@click="loadMore"
105+
>
106+
<span>Load more subscriptions</span>
107+
<font-awesome-icon
108+
v-if="!loadingMore"
109+
class="icon"
110+
icon="arrow-down"
111+
/>
112+
<font-awesome-icon
113+
v-else
114+
title="Available"
115+
class="icon icon--ml-2 icon--color-light"
116+
icon="sync"
117+
spin
118+
/>
119+
</button>
120+
</div>
40121
</div>
41122
<h2>New subscription</h2>
42-
<p>Create a new subscription for your organization.</p>
123+
<p>
124+
Create a new subscription for your organization. Your default payment
125+
method will be charged automatically
126+
</p>
43127
<Loading v-if="loadingPricingPlans" :message="loadingPricingPlans" />
44128
<LargeMessage
45129
v-else-if="
@@ -49,13 +133,15 @@
49133
heading="No plans here"
50134
text="Unfortunately, we don't have any subscription plans available for you right now."
51135
/>
52-
<form v-else>
136+
<form v-else @submit.prevent="createSubscription">
53137
<div
54138
v-for="(plan, index) in pricingPlans.data"
55139
:key="`${plan.id}_${index}`"
140+
class="fake-radio-container"
56141
>
57142
<label>
58143
<input v-model="newPlan" type="radio" :value="plan.id" required />
144+
<span class="fake-radio" role="radio" tabindex="0" />
59145
<strong class="name">{{ plan.nickname }}</strong>
60146
<span class="amount">
61147
{{ (plan.currency || "eur").toUpperCase() }}
@@ -69,10 +155,6 @@
69155
</span>
70156
</label>
71157
</div>
72-
<p class="text text--size-small">
73-
On creating a new subscription, your default payment method will be
74-
billed.
75-
</p>
76158
<button class="button">Create subscription</button>
77159
</form>
78160
</div>
@@ -83,18 +165,31 @@
83165
import { Component, Vue, Watch } from "vue-property-decorator";
84166
import { mapGetters } from "vuex";
85167
import Loading from "@/components/Loading.vue";
168+
import TimeAgo from "@/components/TimeAgo.vue";
86169
import LargeMessage from "@/components/LargeMessage.vue";
87170
import Input from "@/components/form/Input.vue";
88-
import Select from "@/components/form/Select.vue";
89171
import Checkbox from "@/components/form/Checkbox.vue";
172+
import Select from "@/components/form/Select.vue";
173+
90174
import { getAllCountries } from "countries-and-timezones";
91175
import { User } from "@/types/auth";
176+
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
177+
import { library } from "@fortawesome/fontawesome-svg-core";
178+
import {
179+
faFileInvoiceDollar,
180+
faPencilAlt,
181+
faArrowDown,
182+
faSync
183+
} from "@fortawesome/free-solid-svg-icons";
92184
import { Subscriptions, emptyPagination } from "../../../../types/manage";
185+
library.add(faFileInvoiceDollar, faPencilAlt, faArrowDown, faSync);
93186
94187
@Component({
95188
components: {
96189
Loading,
190+
TimeAgo,
97191
Input,
192+
FontAwesomeIcon,
98193
Select,
99194
LargeMessage,
100195
Checkbox
@@ -107,6 +202,7 @@ import { Subscriptions, emptyPagination } from "../../../../types/manage";
107202
})
108203
export default class ManageSettings extends Vue {
109204
subscriptions: Subscriptions = emptyPagination;
205+
loadingMore = false;
110206
pricingPlans!: any;
111207
noBilling = false;
112208
user!: any;
@@ -135,10 +231,45 @@ export default class ManageSettings extends Vue {
135231
this.loadingPricingPlans = "Loading pricing plans";
136232
this.$store
137233
.dispatch("manage/getPricingPlans", this.$route.params.team)
138-
.then(() => {})
234+
.then(() => {
235+
this.newPlan = this.pricingPlans.data[0].id;
236+
})
139237
.catch(() => {})
140238
.finally(() => (this.loadingPricingPlans = ""));
141239
}
240+
241+
private loadMore() {
242+
this.loadingMore = true;
243+
this.$store
244+
.dispatch("manage/getSubscriptions", {
245+
team: this.$route.params.team,
246+
start: this.$store.state.manage.subscriptions[this.$route.params.team]
247+
.start
248+
})
249+
.then(subscriptions => {
250+
this.subscriptions = { ...subscriptions };
251+
})
252+
.catch(error => {
253+
if (error.response.data.error === "no-customer") this.noBilling = true;
254+
})
255+
.finally(() => (this.loadingMore = false));
256+
}
257+
258+
private createSubscription() {
259+
this.loading = "Creating your subscription";
260+
this.$store
261+
.dispatch("manage/createSubscription", {
262+
team: this.$route.params.team,
263+
plan: this.newPlan
264+
})
265+
.then(subscriptions => {
266+
this.subscriptions = { ...subscriptions };
267+
})
268+
.catch(error => {
269+
if (error.response.data.error === "no-customer") this.noBilling = true;
270+
})
271+
.finally(() => (this.loading = ""));
272+
}
142273
}
143274
</script>
144275

store/manage.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,12 @@ export const actions: ActionTree<RootState, RootState> = {
157157
commit("setSubscriptions", { team, subscriptions, start });
158158
return subscriptions;
159159
},
160+
async createSubscription({ dispatch }, { team, plan }) {
161+
const subscriptions: any = (await this.$axios.put(
162+
`/organizations/${team}/subscriptions`, { plan }
163+
)).data;
164+
return dispatch("getSubscriptions", { team });
165+
},
160166
async getPricingPlans({ commit }, context) {
161167
const subscriptions: any = (await this.$axios.get(
162168
`/organizations/${context}/pricing/${stripeProductId}`

styles/_variables.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ $button-primary-background-color: #492257;
1313
$button-primary-text-color: #fff;
1414
$button-info-background-color: #4285f4;
1515
$button-info-text-color: rgba(100, 100, 255, 0.01);
16+
$button-success-background-color: #009432;
17+
$button-success-text-color: rgba(100, 100, 255, 0.01);
18+
$button-warning-background-color: #f79f1f;
19+
$button-warning-text-color: rgba(100, 100, 255, 0.01);
1620
$button-danger-background-color: #af1101;
1721
$button-danger-text-color: rgba(255, 0, 0, 0.01);
1822

styles/ui.scss

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,3 +299,78 @@ code {
299299
.modal-front .button {
300300
margin-right: 0.5rem;
301301
}
302+
303+
.label {
304+
background-color: #eee;
305+
color: #000;
306+
text-transform: uppercase;
307+
font-size: 75%;
308+
font-weight: bold;
309+
margin-left: 0.5rem;
310+
vertical-align: middle;
311+
display: inline-block;
312+
line-height: 1;
313+
margin-top: -0.2rem;
314+
padding: 0.25rem 0.5rem;
315+
border-radius: 1rem;
316+
}
317+
.label--color-incomplete,
318+
.label--color-incomplete_expired,
319+
.label--color-trialing,
320+
.label--color-active,
321+
.label--color-past_due,
322+
.label--color-canceled,
323+
.label--color-unpaid {
324+
color: #fff !important;
325+
}
326+
.label--color-incomplete { background-color: $button-warning-background-color !important; }
327+
.label--color-incomplete_expired { background-color: $button-danger-background-color !important; }
328+
.label--color-trialing { background-color: $button-warning-background-color !important; }
329+
.label--color-active { background-color: $button-success-background-color !important; }
330+
.label--color-past_due { background-color: $button-danger-background-color !important; }
331+
.label--color-canceled { background-color: $button-danger-background-color !important; }
332+
.label--color-unpaid { background-color: $button-danger-background-color !important; }
333+
334+
input[type="radio"] {
335+
display: none;
336+
}
337+
input[type="radio"] + .fake-radio {
338+
background-color: #fff;
339+
border-radius: 100%;
340+
display: inline-block;
341+
vertical-align: middle;
342+
margin-top: -0.2rem;
343+
width: 1.25rem;
344+
position: relative;
345+
height: 1.25rem;
346+
margin-right: 0.5rem;
347+
outline: none;
348+
box-shadow: rgba(42, 47, 69, 0.16) 0px 0px 0px 1px,
349+
rgba(0, 0, 0, 0.12) 0px 1px 1px 0px, rgba(42, 47, 69, 0.12) 0px 2px 5px 0px;
350+
&:focus {
351+
box-shadow: rgba(73, 34, 87, 0.46) 0px 0px 0px 1px,
352+
rgba(0, 0, 0, 0.42) 0px 1px 1px 0px,
353+
rgba(73, 34, 87, 0.42) 0px 2px 5px 0px, 0 0 0 3px rgba(121, 82, 179, 0.25);
354+
}
355+
}
356+
.fake-radio-container {
357+
display: block;
358+
margin-bottom: 2rem;
359+
+ .fake-radio-container {
360+
margin-top: -1rem;
361+
}
362+
}
363+
input[type="radio"]:checked + .fake-radio {
364+
background-color: $link-color;
365+
&::after {
366+
content: "";
367+
width: 0.75rem;
368+
height: 0.75rem;
369+
background-color: #fff;
370+
position: absolute;
371+
transform: scale(0.5);
372+
left: 0.25rem;
373+
top: 0.25rem;
374+
border-radius: 100%;
375+
}
376+
}

0 commit comments

Comments
 (0)