PluginProbe ʕ •ᴥ•ʔ
Hostinger Reach – AI-Powered Email Marketing for WordPress / 1.6.0
Hostinger Reach – AI-Powered Email Marketing for WordPress v1.6.0
1.6.0 1.5.9 1.5.8 1.5.7 1.5.6 1.5.5 1.5.4 1.5.3 1.5.2 1.5.1 1.5.0 1.4.12 1.4.11 1.4.10 1.4.9 1.4.8 1.4.7 trunk 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6
hostinger-reach / frontend / vue / components / Modals / AddFormModal.vue
hostinger-reach / frontend / vue / components / Modals Last commit date
Base 9 months ago Types 2 weeks ago AddFormModal.vue 9 months ago ConfirmDisconnectModal.vue 10 months ago Modals.vue 8 months ago SelectPageModal.vue 9 months ago
AddFormModal.vue
368 lines
1 <script lang="ts" setup>
2 import { HLabel } from '@hostinger/hcomponents';
3 import { computed, ref } from 'vue';
4
5 import BaseModal from '@/components/Modals/Base/BaseModal.vue';
6 import PluginIntegrationSkeleton from '@/components/skeletons/PluginIntegrationSkeleton.vue';
7 import PluginsSectionSkeleton from '@/components/skeletons/PluginsSectionSkeleton.vue';
8 import { useModal } from '@/composables/useModal';
9 import { getPluginInfo } from '@/data/pluginData';
10 import { useIntegrationsStore } from '@/stores/integrationsStore';
11 import { usePagesStore } from '@/stores/pagesStore';
12 import { ModalName } from '@/types/enums';
13 import { translate } from '@/utils/translate';
14
15 const { openModal } = useModal();
16
17 const integrationsStore = useIntegrationsStore();
18 const pagesStore = usePagesStore();
19 const showPlugins = ref(false);
20
21 const installedIntegrations = computed(() =>
22 integrationsStore.availableIntegrations.filter((integration) => integration.isActive)
23 );
24
25 const availableIntegrations = computed(() =>
26 integrationsStore.availableIntegrations.filter((integration) => !integration.isActive)
27 );
28
29 const handleCreateForm = () => {
30 openModal(
31 ModalName.SELECT_PAGE_MODAL,
32 {
33 title: translate('hostinger_reach_forms_modal_title'),
34 data: {
35 backButtonRedirectAction: () => {
36 openModal(ModalName.ADD_FORM_MODAL, {}, { hasCloseButton: true });
37 }
38 },
39 pages: pagesStore.pages
40 },
41 {
42 hasCloseButton: true
43 }
44 );
45 };
46
47 const handleConnect = async (integrationId: string) => {
48 await integrationsStore.toggleIntegrationStatus(integrationId, true);
49 };
50
51 const handleDisconnect = (id: string) => {
52 openModal(ModalName.CONFIRM_DISCONNECT_MODAL, {
53 data: {
54 integration: id,
55 backButtonRedirectAction: () => {
56 openModal(ModalName.ADD_FORM_MODAL, {}, { hasCloseButton: true });
57 }
58 }
59 });
60 };
61
62 const togglePluginsSection = () => {
63 showPlugins.value = !showPlugins.value;
64 };
65
66 pagesStore.loadData();
67 </script>
68
69 <template>
70 <BaseModal title-alignment="left" :title="translate('hostinger_reach_add_form_modal_title')">
71 <div class="add-form-modal">
72 <HSnackbar variant="info">
73 {{ translate('hostinger_reach_forms_consent_notice') }}
74 </HSnackbar>
75
76 <div class="add-form-modal__main-card">
77 <div class="add-form-modal__main-content">
78 <div class="add-form-modal__main-icon">
79 <HIcon name="ic-add-24" color="primary--500" />
80 </div>
81 <HText variant="body-1-bold" as="span">
82 {{ translate('hostinger_reach_forms_new_contact_form') }}
83 </HText>
84 </div>
85 <HButton
86 :is-loading="pagesStore.isPagesLoading"
87 variant="contain"
88 color="primary"
89 size="small"
90 @click="handleCreateForm"
91 >
92 {{ translate('hostinger_reach_forms_create_form_button') }}
93 </HButton>
94 </div>
95
96 <PluginsSectionSkeleton v-if="integrationsStore.isLoading" />
97
98 <template v-else>
99 <div v-if="installedIntegrations.length" class="add-form-modal__installed-section">
100 <HText variant="body-3" as="h3" color="neutral--300">
101 {{ translate('hostinger_reach_forms_installed_plugins') }}
102 </HText>
103 <div class="add-form-modal__plugins-list">
104 <div v-for="integration in installedIntegrations" :key="integration.id" class="add-form-modal__plugin-card">
105 <PluginIntegrationSkeleton v-if="integrationsStore.loadingIntegrations[integration.id]" />
106
107 <div v-else class="add-form-modal__plugin-content">
108 <div class="add-form-modal__plugin-info">
109 <div class="add-form-modal__plugin-icon">
110 <img
111 :src="getPluginInfo(integration).icon"
112 :alt="integration.title"
113 class="add-form-modal__plugin-image"
114 loading="lazy"
115 />
116 </div>
117 <div class="add-form-modal__plugin-details">
118 <HText variant="body-2-bold" as="span">
119 {{ integration.title }}
120 </HText>
121
122 <HLabel color="success" variant="outline">
123 {{ translate('hostinger_reach_forms_active') }}
124 </HLabel>
125 </div>
126 </div>
127 <div class="add-form-modal__plugin-actions">
128 <HButton
129 variant="outline"
130 color="danger"
131 size="small"
132 :is-disabled="integrationsStore.isIntegrationLoading(integration.id)"
133 @click="handleDisconnect(integration.id)"
134 >
135 {{ translate('hostinger_reach_forms_disconnect') }}
136 </HButton>
137 </div>
138 </div>
139 </div>
140 </div>
141 </div>
142
143 <div v-if="availableIntegrations.length" class="add-form-modal__plugins-section">
144 <HButton
145 variant="text"
146 color="primary"
147 size="small"
148 :icon-append="showPlugins ? 'ic-chevron-up-16' : 'ic-chevron-down-16'"
149 icon-color="primary--500"
150 class="add-form-modal__plugins-toggle"
151 :aria-expanded="showPlugins"
152 @click="togglePluginsSection"
153 >
154 {{ translate('hostinger_reach_forms_view_supported_plugins') }}
155 </HButton>
156
157 <div v-if="showPlugins" class="add-form-modal__plugins-list">
158 <div v-for="integration in availableIntegrations" :key="integration.id" class="add-form-modal__plugin-card">
159 <PluginIntegrationSkeleton v-if="integrationsStore.isIntegrationLoading(integration.id)" />
160
161 <div v-else class="add-form-modal__plugin-content">
162 <div class="add-form-modal__plugin-info">
163 <div class="add-form-modal__plugin-icon">
164 <img
165 :src="getPluginInfo(integration).icon"
166 :alt="integration.title"
167 class="add-form-modal__plugin-image"
168 loading="lazy"
169 />
170 </div>
171 <div class="add-form-modal__plugin-details">
172 <HText variant="body-2-bold" as="span">
173 {{ integration.title }}
174 </HText>
175 </div>
176 </div>
177 <div class="add-form-modal__plugin-actions">
178 <HButton
179 variant="outline"
180 color="neutral"
181 size="small"
182 :is-disabled="integrationsStore.isIntegrationLoading(integration.id)"
183 @click="handleConnect(integration.id)"
184 >
185 {{
186 integration.isPluginActive
187 ? translate('hostinger_reach_forms_connect')
188 : translate('hostinger_reach_forms_install_and_connect')
189 }}
190 </HButton>
191 </div>
192 </div>
193 </div>
194 </div>
195 </div>
196 </template>
197 </div>
198 </BaseModal>
199 </template>
200
201 <style lang="scss" scoped>
202 .add-form-modal {
203 display: flex;
204 flex-direction: column;
205 gap: 20px;
206 margin-top: 24px;
207
208 &__main-card {
209 display: flex;
210 align-items: center;
211 justify-content: space-between;
212 padding: 20px;
213 background: var(--neutral--0);
214 border: 1px solid var(--primary--500);
215 border-radius: 12px;
216 transition:
217 border-color 0.2s ease,
218 box-shadow 0.2s ease;
219
220 &:hover {
221 border-color: var(--primary--600);
222 box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.1);
223 }
224 }
225
226 &__main-content {
227 display: flex;
228 align-items: center;
229 gap: 12px;
230 }
231
232 &__main-icon {
233 width: 32px;
234 height: 32px;
235 flex-shrink: 0;
236 background: var(--primary--50);
237 border-radius: 8px;
238 display: flex;
239 align-items: center;
240 justify-content: center;
241 }
242
243 &__plugins-section {
244 display: flex;
245 flex-direction: column;
246 gap: 16px;
247 }
248
249 &__plugins-toggle {
250 align-self: center;
251 }
252
253 &__plugins-list {
254 display: flex;
255 flex-direction: column;
256 gap: 12px;
257 }
258
259 &__plugin-card {
260 display: flex;
261 flex-direction: column;
262 background: var(--neutral--0);
263 border: 1px solid var(--neutral--200);
264 border-radius: 12px;
265 transition: border-color 0.2s ease;
266 contain: layout style;
267
268 &:hover {
269 border-color: var(--neutral--300);
270 }
271 }
272
273 &__plugin-content {
274 display: flex;
275 align-items: center;
276 justify-content: space-between;
277 padding: 16px;
278 width: 100%;
279 }
280
281 &__plugin-info {
282 display: flex;
283 align-items: center;
284 gap: 12px;
285 }
286
287 &__plugin-icon {
288 width: 40px;
289 height: 40px;
290 border-radius: 8px;
291 display: flex;
292 align-items: center;
293 justify-content: center;
294 background: var(--neutral--50);
295 flex-shrink: 0;
296 }
297
298 &__plugin-image {
299 width: 32px;
300 height: 32px;
301 object-fit: contain;
302 }
303
304 &__plugin-details {
305 display: flex;
306 gap: 8px;
307 align-items: center;
308 }
309
310 &__plugin-actions {
311 display: flex;
312 align-items: center;
313 gap: 12px;
314 }
315
316 &__installed-section {
317 display: flex;
318 flex-direction: column;
319 gap: 8px;
320 }
321
322 @media (max-width: 640px) {
323 gap: 16px;
324
325 &__main-card {
326 padding: 16px;
327 flex-direction: column;
328 align-items: stretch;
329 gap: 16px;
330 }
331
332 &__main-content {
333 justify-content: center;
334 }
335
336 &__plugin-content {
337 padding: 14px;
338 }
339
340 &__plugin-icon {
341 width: 36px;
342 height: 36px;
343 }
344
345 &__plugin-image {
346 width: 28px;
347 height: 28px;
348 }
349 }
350
351 @media (max-width: 480px) {
352 gap: 12px;
353
354 &__main-card {
355 padding: 14px;
356 }
357
358 &__plugins-list {
359 gap: 8px;
360 }
361
362 &__plugin-content {
363 padding: 12px;
364 }
365 }
366 }
367 </style>
368