main
vue 145 lines 4.02 KB
Raw
1 <template>
2 <div>
3 <CardEntity
4 hoverable
5 :embedded
6 :disabled
7 :clickable="!selectable"
8 :loading="canceling"
9 @click="selectable ? () => {} : (showDetails = true)"
10 >
11 <template #headerMain>{{ subscription.name }}</template>
12 <template #headerExtra>
13 <div class="text-primary">
14 {{ price(subscription.price) }}
15 </div>
16 </template>
17 <template v-if="!hideDetails" #default>
18 <div class="flex flex-col gap-1">
19 {{ subscription.info }}
20 <p class="text-sm">
21 {{ subscription.short_description }}
22 </p>
23 </div>
24 </template>
25 <template v-if="showDeleteOnCard && licenseData" #footerMain>
26 <n-popconfirm @positive-click="cancelSubscription()">
27 <template #trigger>
28 <n-button text size="small">
29 <template #icon>
30 <Icon :name="DeleteIcon" :size="16" />
31 </template>
32 Unsubscribe
33 </n-button>
34 </template>
35 {{ deleteMessage }}
36 </n-popconfirm>
37 </template>
38 <template v-if="selectable" #footerExtra>
39 <n-button size="small" @click.stop="showDetails = true">
40 <template #icon>
41 <Icon :name="InfoIcon" />
42 </template>
43 Details
44 </n-button>
45 </template>
46 </CardEntity>
47
48 <n-modal
49 v-model:show="showDetails"
50 preset="card"
51 :style="{ maxWidth: 'min(600px, 90vw)', minHeight: 'min(300px, 90vh)', overflow: 'hidden' }"
52 :title="subscription.name"
53 :bordered="false"
54 content-class="flex flex-col"
55 segmented
56 >
57 <n-spin :show="canceling" content-class="flex grow flex-col gap-4" class="flex grow flex-col">
58 <div class="flex justify-between gap-4">
59 <div>{{ subscription.info }}</div>
60 <div class="text-primary font-mono whitespace-nowrap">
61 {{ price(subscription.price) }}
62 </div>
63 </div>
64 <div class="grow">
65 {{ subscription.full_description }}
66 </div>
67 <div v-if="showDeleteOnDialog && licenseData" class="flex items-center justify-end">
68 <n-popconfirm to="body" @positive-click="cancelSubscription()">
69 <template #trigger>
70 <n-button text size="small" class="opacity-50">
71 <template #icon>
72 <Icon :name="DeleteIcon" :size="16" />
73 </template>
74 Unsubscribe
75 </n-button>
76 </template>
77 {{ deleteMessage }}
78 </n-popconfirm>
79 </div>
80 </n-spin>
81 </n-modal>
82 </div>
83 </template>
84
85 <script setup lang="ts">
86 import type { CancelSubscriptionPayload } from "@/api/endpoints/license"
87 import type { License, SubscriptionFeature } from "@/types/license.d"
88 import { NButton, NModal, NPopconfirm, NSpin, useMessage } from "naive-ui"
89 import { ref, toRefs } from "vue"
90 import Api from "@/api"
91 import CardEntity from "@/components/common/cards/CardEntity.vue"
92 import Icon from "@/components/common/Icon.vue"
93 import { price } from "@/utils"
94
95 const props = defineProps<{
96 subscription: SubscriptionFeature
97 embedded?: boolean
98 selectable?: boolean
99 disabled?: boolean
100 hideDetails?: boolean
101 showDeleteOnCard?: boolean
102 showDeleteOnDialog?: boolean
103 licenseData?: License
104 }>()
105
106 const emit = defineEmits<{
107 (e: "deleted"): void
108 }>()
109
110 const { subscription, embedded, selectable, disabled, hideDetails, showDeleteOnCard, showDeleteOnDialog, licenseData } =
111 toRefs(props)
112
113 const DeleteIcon = "ph:minus-circle"
114 const InfoIcon = "carbon:information"
115 const message = useMessage()
116 const showDetails = ref(false)
117 const deleteMessage = "Are you sure you want to give up this feature?"
118 const canceling = ref(false)
119
120 function cancelSubscription() {
121 canceling.value = true
122
123 const payload: CancelSubscriptionPayload = {
124 customer_email: licenseData.value?.customer.email || "",
125 subscription_price_id: subscription.value.subscription_price_id,
126 feature_name: subscription.value.name
127 }
128
129 Api.license
130 .cancelSubscription(payload)
131 .then(res => {
132 if (res.data.success) {
133 emit("deleted")
134 } else {
135 message.warning(res.data?.message || "An error occurred. Please try again later.")
136 }
137 })
138 .catch(err => {
139 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
140 })
141 .finally(() => {
142 canceling.value = false
143 })
144 }
145 </script>