Skip to content

Commit ea5d20f

Browse files
luannmoreiragustavosbarreto
authored andcommitted
feat(ui): add paywall check for chat support access
Introduced PaywallChat component in AppBar.vue to handle restricted chat support access. Updated openShellhubHelp logic to route users based on subscription and environment. Modified Vuex support module to track chat support status. Refactored openChatwoot function for improved organization. Adjusted tests to validate new paywall behavior and chatSupportPaywall state.
1 parent 27ec987 commit ea5d20f

File tree

10 files changed

+311
-41
lines changed

10 files changed

+311
-41
lines changed

ui/src/components/AppBar/AppBar.vue

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<template>
2+
<PaywallChat v-model="chatSupportPaywall" />
23
<v-app-bar
34
flat
45
floating
@@ -98,13 +99,14 @@ import {
9899
ref,
99100
} from "vue";
100101
import { useRouter, useRoute, RouteLocationRaw, RouteLocation } from "vue-router";
101-
import { useEventListener } from "@vueuse/core";
102102
import { useChatWoot } from "@productdevbook/chatwoot/vue";
103+
import { useEventListener } from "@vueuse/core";
103104
import { useStore } from "../../store";
104105
import { createNewClient } from "../../api/http";
105106
import handleError from "../../utils/handleError";
106107
import UserIcon from "../User/UserIcon.vue";
107108
import Notification from "./Notifications/Notification.vue";
109+
import PaywallChat from "../User/PaywallChat.vue";
108110
import { envVariables } from "@/envVariables";
109111
110112
type MenuItem = {
@@ -119,9 +121,6 @@ type BreadcrumbItem = {
119121
title: string;
120122
href: string;
121123
};
122-
123-
const { setUser, setConversationCustomAttributes } = useChatWoot();
124-
125124
const store = useStore();
126125
const router = useRouter();
127126
const route = useRoute();
@@ -131,11 +130,11 @@ const getStatusDarkMode = computed(
131130
const tenant = computed(() => store.getters["auth/tenant"]);
132131
const userEmail = computed(() => store.getters["auth/email"]);
133132
const userId = computed(() => store.getters["auth/id"]);
134-
const identifier = computed(() => store.getters["support/getIdentifier"]);
135133
const currentUser = computed(() => store.getters["auth/currentUser"]);
136-
const isDarkMode = ref(getStatusDarkMode.value === "dark");
137134
const billingActive = computed(() => store.getters["billing/active"]);
138-
135+
const identifier = computed(() => store.getters["support/getIdentifier"]);
136+
const isDarkMode = ref(getStatusDarkMode.value === "dark");
137+
const chatSupportPaywall = ref(false);
139138
const showNavigationDrawer = defineModel<boolean>();
140139
141140
const triggerClick = (item: MenuItem): void => {
@@ -168,27 +167,54 @@ const toggleDarkMode = () => {
168167
store.dispatch("layout/setStatusDarkMode", isDarkMode.value);
169168
};
170169
171-
const openShellhubHelp = async () => {
172-
if ((envVariables.isCloud || envVariables.isEnterprise) && billingActive) {
173-
await store.dispatch("support/get", tenant.value);
174-
}
175-
if (identifier.value.length === 0) {
176-
window.open("https://github.com/shellhub-io/shellhub/issues/new/choose", "_blank");
177-
return;
178-
}
170+
const openChatwoot = async (): Promise<void> => {
171+
const { setUser, setConversationCustomAttributes, toggle } = useChatWoot();
172+
173+
await store.dispatch("support/get", tenant.value);
174+
179175
setUser(userId.value, {
180176
name: currentUser.value,
181177
email: userEmail.value,
182178
identifier_hash: identifier.value,
183179
});
180+
184181
useEventListener(window, "chatwoot:on-message", () => {
185182
setConversationCustomAttributes({
186183
namespace: store.getters["namespaces/get"].name,
187184
tenant: tenant.value,
188185
domain: window.location.hostname,
189186
});
190187
});
191-
await store.dispatch("support/toggle");
188+
189+
store.commit("support/setCreatedStatus", true);
190+
toggle("open");
191+
};
192+
193+
const openPaywall = (): void => {
194+
chatSupportPaywall.value = true;
195+
};
196+
197+
const redirectToGitHub = (): void => {
198+
window.open("https://github.com/shellhub-io/shellhub/issues/new/choose", "_blank");
199+
};
200+
201+
const openShellhubHelp = async (): Promise<void> => {
202+
switch (true) {
203+
case envVariables.isCloud && billingActive.value:
204+
await openChatwoot();
205+
break;
206+
207+
case envVariables.isCommunity || (envVariables.isCloud && !billingActive.value):
208+
openPaywall();
209+
break;
210+
211+
case envVariables.isEnterprise:
212+
redirectToGitHub();
213+
break;
214+
215+
default:
216+
throw new Error("Unsupported environment configuration.");
217+
}
192218
};
193219
194220
const menu = [
@@ -225,5 +251,5 @@ const generateBreadcrumbs = (route: RouteLocation): BreadcrumbItem[] => {
225251
226252
const breadcrumbItems = computed(() => generateBreadcrumbs(route));
227253
228-
defineExpose({ openShellhubHelp, logout, isDarkMode, breadcrumbItems, currentUser, identifier });
254+
defineExpose({ openShellhubHelp, chatSupportPaywall, logout, isDarkMode, breadcrumbItems, currentUser, identifier });
229255
</script>
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<template>
2+
<v-dialog
3+
v-model="dialog"
4+
transition="dialog-bottom-transition"
5+
max-width="600"
6+
@click:outside="close"
7+
@keydown.esc="close"
8+
>
9+
<v-card color="background" data-test="card-dialog">
10+
<v-container>
11+
<v-row data-test="icon-chat">
12+
<v-col class="d-flex justify-center align-center">
13+
<div class="circle-one shadow d-flex justify-center align-center">
14+
<div class="circle-two shadow d-flex justify-center align-center">
15+
<v-icon color="success" class="green-inner-shadow" size="108">
16+
mdi-chat-question
17+
</v-icon>
18+
</div>
19+
</div>
20+
</v-col>
21+
</v-row>
22+
<v-row>
23+
<v-col class="pb-0">
24+
<h1 class="d-flex justify-center align-center text-center" data-test="upgrade-heading">
25+
Upgrade to have access to chat support!
26+
</h1>
27+
</v-col>
28+
</v-row>
29+
<v-row class="px-6">
30+
<v-col class="pt-1 pb-6">
31+
<p class="d-flex justify-center align-center text-grey text-center" data-test="upgrade-description">
32+
Get real-time assistance from our team with priority responses.
33+
Skip the hassle of documentation — upgrade now and unlock direct chat support.
34+
</p>
35+
<p class="align-center text-grey text-center" data-test="upgrade-description2">
36+
However, you can still use
37+
<a href="https://docs.shellhub.io/" target="_blank" rel="noopener noreferrer" data-test="link-anchor">our Documentation</a>
38+
to find answers to your questions and troubleshoot issues on your own.
39+
</p>
40+
</v-col>
41+
</v-row>
42+
<v-card-actions data-test="card-actions">
43+
<v-btn @click="close()" class="mt-4" variant="text" data-test="close-btn">
44+
Close
45+
</v-btn>
46+
<v-spacer />
47+
<v-btn
48+
color="primary"
49+
variant="flat"
50+
href="www.shellhub.io/pricing"
51+
target="_blank"
52+
rel="noreferrer noopener"
53+
data-test="upgrade-btn">
54+
Upgrade
55+
</v-btn>
56+
</v-card-actions>
57+
</v-container>
58+
</v-card>
59+
</v-dialog>
60+
</template>
61+
62+
<script setup lang="ts">
63+
const dialog = defineModel({ default: false });
64+
65+
const close = () => {
66+
dialog.value = false;
67+
};
68+
69+
defineExpose({ dialog });
70+
</script>
71+
72+
<style scoped>
73+
.green-inner-shadow {
74+
filter: drop-shadow(0px 0px 20px rgba(43, 255, 10, 0.45));
75+
}
76+
77+
.shadow {
78+
box-shadow: rgba(0, 0, 0, 0.35) 0px 15px 10px 0px;
79+
border-radius: 50%;
80+
}
81+
82+
.circle-one {
83+
height: 15.625rem;
84+
width: 15.625rem;
85+
background: linear-gradient(180deg, rgba(0,0,0,0) 40%, rgba(76,175,80,0.1) 60%, rgba(76,175,80,0.75) 120%);
86+
}
87+
88+
.circle-two {
89+
height: 12.5rem;
90+
width: 12.5rem;
91+
background: linear-gradient(180deg, rgba(0,0,0,0) 60%, rgba(76,175,80,0.1) 75%, rgba(76,175,80,0.75) 120%);
92+
}
93+
</style>

ui/src/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ app.use(VueGtag, {
4040
config: { id: envVariables.googleAnalyticsID || "" },
4141
});
4242

43-
if ((envVariables.isCloud || envVariables.isEnterprise) && (envVariables.chatWootWebsiteToken && envVariables.chatWootBaseURL)) {
43+
if ((envVariables.isCloud) && (envVariables.chatWootWebsiteToken && envVariables.chatWootBaseURL)) {
4444
app.use(
4545
createChatWoot({
4646
init: {

ui/src/store/modules/auth.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,6 @@ export const auth: Module<AuthState, State> = {
129129
state.email = "";
130130
state.role = "";
131131
state.mfa = false;
132-
toggle("close");
133-
reset();
134132
},
135133

136134
changeData(state, data) {
@@ -363,6 +361,11 @@ export const auth: Module<AuthState, State> = {
363361
localStorage.removeItem("role");
364362
localStorage.removeItem("mfa");
365363
localStorage.removeItem("recovery_email");
364+
const chatIsCreated = context.rootGetters["support/getCreatedStatus"];
365+
if (chatIsCreated) {
366+
toggle("close");
367+
reset();
368+
}
366369
},
367370

368371
changeUserData(context, data) {

ui/src/store/modules/support.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,32 @@
11
import { Module } from "vuex";
2-
import { useChatWoot } from "@productdevbook/chatwoot/vue";
32
import * as apiSupport from "../api/namespaces";
43
import { State } from "..";
54

6-
const { toggle } = useChatWoot();
7-
85
export interface SupportState {
96
identifier: string;
7+
chatCreated: boolean;
108
}
119

1210
export const support: Module<SupportState, State> = {
1311
namespaced: true,
1412
state: {
1513
identifier: "",
14+
chatCreated: false,
1615
},
1716

1817
getters: {
1918
getIdentifier: (state) => state.identifier,
19+
getCreatedStatus: (state) => state.chatCreated,
2020
},
2121

2222
mutations: {
2323
setIdentifier: (state, identifier) => {
2424
state.identifier = identifier;
2525
},
26+
27+
setCreatedStatus: (state, status) => {
28+
state.chatCreated = status;
29+
},
2630
},
2731

2832
actions: {
@@ -35,6 +39,5 @@ export const support: Module<SupportState, State> = {
3539
throw error;
3640
}
3741
},
38-
toggle: () => { toggle("open"); },
3942
},
4043
};

0 commit comments

Comments
 (0)