PluginProbe ʕ •ᴥ•ʔ
Hostinger Reach – AI-Powered Email Marketing for WordPress / 1.0.5
Hostinger Reach – AI-Powered Email Marketing for WordPress v1.0.5
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 / PluginEntry.vue
hostinger-reach / frontend / vue / components Last commit date
Modals 9 months ago skeletons 9 months ago ActionButton.vue 9 months ago ActionButtonsSection.vue 9 months ago Banner.vue 9 months ago FAQ.vue 9 months ago FormItem.vue 9 months ago FormsSection.vue 9 months ago Hero.vue 9 months ago PluginEntriesTable.vue 9 months ago PluginEntry.vue 9 months ago PluginExpansion.vue 9 months ago Toggle.vue 9 months ago UsageCard.vue 9 months ago UsageCardsRow.vue 9 months ago UsageCardsSection.vue 9 months ago
PluginEntry.vue
329 lines
1 <script setup lang="ts">
2 import { HIcon, HLabel, HPopover } from '@hostinger/hcomponents';
3 import { computed, ref } from 'vue';
4
5 import PluginExpansion from '@/components/PluginExpansion.vue';
6 import { INTEGRATION_TO_FORM_TYPE_MAP, PLUGIN_IDS, PLUGIN_STATUSES, type PluginStatus } from '@/data/pluginData';
7 import type { Form } from '@/types/models';
8 import { translate } from '@/utils/translate';
9
10 interface Props {
11 pluginId: string;
12 pluginName: string;
13 pluginIcon: string;
14 pluginStatus: PluginStatus;
15 totalEntries: number;
16 forms: Form[];
17 }
18
19 const props = defineProps<Props>();
20
21 const emit = defineEmits<{
22 toggleFormStatus: [form: Form, status: boolean];
23 goToPlugin: [id: string];
24 disconnectPlugin: [id: string];
25 viewForm: [form: Form];
26 editForm: [form: Form];
27 }>();
28
29 const isExpanded = ref(false);
30
31 const toggleExpansion = () => {
32 isExpanded.value = !isExpanded.value;
33 };
34
35 const getStatusLabel = (status: PluginStatus) =>
36 status === PLUGIN_STATUSES.ACTIVE
37 ? translate('hostinger_reach_plugin_entries_table_status_active')
38 : translate('hostinger_reach_plugin_entries_table_status_inactive');
39
40 const getStatusColor = (status: PluginStatus) => (status === PLUGIN_STATUSES.ACTIVE ? 'success' : 'gray');
41
42 const handleToggleFormStatus = (form: Form, status: boolean) => {
43 emit('toggleFormStatus', form, status);
44 };
45
46 const handleViewForm = (form: Form) => {
47 emit('viewForm', form);
48 };
49
50 const handleEditForm = (form: Form) => {
51 emit('editForm', form);
52 };
53
54 const expandButtonAriaLabel = computed(() => {
55 const translationKey = isExpanded.value
56 ? 'hostinger_reach_plugin_entries_table_collapse_aria'
57 : 'hostinger_reach_plugin_entries_table_expand_aria';
58
59 return translate(translationKey).replace('{pluginName}', props.pluginName);
60 });
61 </script>
62
63 <template>
64 <div class="plugin-entry-row">
65 <div class="plugin-entry-row__main">
66 <div class="plugin-entry-row__cell plugin-entry-row__cell--plugin">
67 <div class="plugin-entry-row__plugin-content" @click="toggleExpansion">
68 <button
69 class="plugin-entry-row__expand-button"
70 :aria-expanded="isExpanded"
71 :aria-controls="`plugin-expansion-${pluginId}`"
72 :aria-label="expandButtonAriaLabel"
73 @click.stop="toggleExpansion"
74 >
75 <HIcon :name="isExpanded ? 'ic-chevron-down-16' : 'ic-chevron-right-16'" dimensions="16px" />
76 </button>
77 <div class="plugin-entry-row__plugin-info">
78 <div class="plugin-entry-row__plugin-icon">
79 <img :src="pluginIcon" :alt="pluginName" />
80 </div>
81 <span class="plugin-entry-row__plugin-name">{{ pluginName }}</span>
82 </div>
83 </div>
84 </div>
85 <div class="plugin-entry-row__cell plugin-entry-row__cell--entries">
86 <span class="plugin-entry-row__mobile-label">
87 {{ translate('hostinger_reach_plugin_entries_table_entries_header') }}:
88 </span>
89 <span class="plugin-entry-row__entries-count">{{ totalEntries }}</span>
90 </div>
91 <div class="plugin-entry-row__cell plugin-entry-row__cell--status">
92 <span class="plugin-entry-row__mobile-label">
93 {{ translate('hostinger_reach_plugin_entries_table_status_header') }}:
94 </span>
95 <div class="plugin-entry-row__status-content">
96 <HLabel variant="outline" :color="getStatusColor(pluginStatus)" class="plugin-entry-row__status-label">
97 {{ getStatusLabel(pluginStatus) }}
98 </HLabel>
99 </div>
100 </div>
101 <div class="plugin-entry-row__cell plugin-entry-row__cell--actions">
102 <HPopover
103 v-if="INTEGRATION_TO_FORM_TYPE_MAP[pluginId] !== PLUGIN_IDS.HOSTINGER_REACH"
104 placement="bottom-end"
105 :show-arrow="false"
106 background-color="neutral--0"
107 border-radius="12px"
108 :outside-click-enabled="true"
109 >
110 <template #trigger>
111 <button class="plugin-entry-row__action-button">
112 <HIcon name="ic-dots-vertical-16" />
113 </button>
114 </template>
115 <div class="plugin-entry-row__popover-menu">
116 <div class="plugin-entry-row__menu-item" @click="emit('goToPlugin', props.pluginId)">
117 <HIcon name="ic-blocks-plus-16" />
118 <span>{{ translate('hostinger_reach_plugin_entries_table_go_to_plugin') }}</span>
119 <HIcon name="ic-arrow-up-right-square-16" />
120 </div>
121 <div class="plugin-entry-row__menu-item" @click="emit('disconnectPlugin', props.pluginId)">
122 <HIcon name="ic-cross-circle-16" />
123 <span>{{ translate('hostinger_reach_plugin_entries_table_disconnect_plugin') }}</span>
124 </div>
125 </div>
126 </HPopover>
127 </div>
128 </div>
129 <div v-if="isExpanded" class="plugin-entry-row__expansion">
130 <PluginExpansion
131 :plugin-id="pluginId"
132 :forms="forms"
133 @toggle-form-status="handleToggleFormStatus"
134 @view-form="handleViewForm"
135 @edit-form="handleEditForm"
136 />
137 </div>
138 </div>
139 </template>
140
141 <style scoped lang="scss">
142 .plugin-entry-row {
143 &__main {
144 display: flex;
145 padding: 16px 16px 16px 0;
146 }
147
148 &__cell {
149 display: flex;
150 align-items: center;
151
152 &--plugin {
153 width: 50%;
154 order: 1;
155 }
156
157 &--entries {
158 width: 20%;
159 order: 2;
160 }
161
162 &--status {
163 width: 20%;
164 order: 3;
165 }
166
167 &--actions {
168 width: 10%;
169 display: flex;
170 justify-content: flex-end;
171 order: 4;
172 }
173 }
174
175 &__action-button {
176 background: var(--neutral--0);
177 border: 1px solid var(--neutral--200);
178 border-radius: 8px;
179 padding: 0 8px;
180 height: 32px;
181 display: flex;
182 align-items: center;
183 justify-content: center;
184 cursor: pointer;
185 transition: all 0.2s ease;
186
187 &:hover {
188 border-color: var(--neutral--300);
189 }
190 }
191
192 &__plugin-content {
193 display: flex;
194 align-items: center;
195 gap: 8px;
196 cursor: pointer;
197 width: 100%;
198 }
199
200 &__expand-button {
201 background: none;
202 border: none;
203 cursor: pointer;
204 padding: 0;
205 display: flex;
206 align-items: center;
207 justify-content: center;
208 width: 16px;
209 height: 16px;
210 }
211
212 &__plugin-info {
213 display: flex;
214 align-items: center;
215 gap: 12px;
216 }
217
218 &__plugin-icon {
219 width: 28px;
220 height: 28px;
221 border-radius: 7px;
222 overflow: hidden;
223 display: flex;
224 align-items: center;
225 justify-content: center;
226 background: var(--neutral--100);
227
228 img {
229 width: 100%;
230 height: 100%;
231 object-fit: cover;
232 }
233 }
234
235 &__plugin-name {
236 font-weight: 700;
237 font-size: 14px;
238 color: var(--neutral--500);
239 }
240
241 &__entries-count {
242 font-weight: 400;
243 font-size: 14px;
244 color: var(--neutral--500);
245 }
246
247 &__status-content {
248 display: flex;
249 align-items: center;
250 }
251
252 &__status-label {
253 font-size: 12px;
254 }
255
256 &__mobile-label {
257 font-weight: 500;
258 font-size: 14px;
259 color: var(--neutral--600);
260 margin-right: 8px;
261 display: none;
262 }
263
264 &__expansion {
265 width: 100%;
266 height: 100%;
267 }
268
269 &__popover-menu {
270 padding: 4px;
271 min-width: 180px;
272 }
273
274 &__menu-item {
275 display: flex;
276 align-items: center;
277 gap: 8px;
278 padding: 12px;
279 cursor: pointer;
280 border-radius: 8px;
281 font-weight: 500;
282 font-size: 14px;
283 color: var(--neutral--600);
284 transition: background-color 0.2s ease;
285
286 &:hover {
287 background-color: var(--neutral--50);
288 }
289
290 span {
291 flex: 1;
292 }
293 }
294
295 @media (max-width: 1023px) {
296 &__main {
297 flex-direction: column;
298 gap: 12px;
299 padding: 16px;
300 border-radius: 12px;
301 background: var(--neutral--100);
302 margin-bottom: 12px;
303 }
304
305 &__cell--plugin,
306 &__cell--entries,
307 &__cell--status,
308 &__cell--actions {
309 width: 100%;
310 justify-content: flex-start;
311 }
312
313 &__cell--entries,
314 &__cell--status {
315 align-items: flex-start;
316 }
317
318 &__cell--actions {
319 padding-right: 0;
320 justify-content: flex-end;
321 }
322
323 &__mobile-label {
324 display: inline-block;
325 }
326 }
327 }
328 </style>
329