Skip to content

Commit a89180a

Browse files
saulmoralespaCopilot
andcommitted
feat: Add premium survey feature for user feedback
- Introduced a premium survey button in the admin settings to gather user feedback on the premium version. - Implemented AJAX handlers for sending and dismissing the survey notice. - Added validation for the survey payload to ensure data integrity. - Created a new test suite for the premium survey functionality, covering visibility, dismissal, payload validation, and email sending. - Updated plugin version to 0.1.1 and modified the readme file to reflect changes. Co-authored-by: Copilot <copilot@github.com>
1 parent d0ed935 commit a89180a

8 files changed

Lines changed: 1121 additions & 6 deletions

Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ GREEN := \033[0;32m
88
YELLOW := \033[1;33m
99
NC := \033[0m # No Color
1010

11-
.PHONY: test test-calculate-dv test-invoice test-client test-all help
11+
.PHONY: test test-calculate-dv test-invoice test-client test-survey test-all help
1212

1313
# Ayuda
1414
help:
@@ -17,6 +17,7 @@ help:
1717
@echo " $(GREEN)make test-calculate-dv$(NC) - Tests de calculate_dv"
1818
@echo " $(GREEN)make test-invoice$(NC) - Tests de Invoice Generation"
1919
@echo " $(GREEN)make test-client$(NC) - Tests de Client Management"
20+
@echo " $(GREEN)make test-survey$(NC) - Tests de Premium Survey"
2021
@echo " $(GREEN)make test-all$(NC) - Todos los tests con detalles"
2122

2223
# Ejecutar todos los tests
@@ -39,6 +40,11 @@ test-client:
3940
@echo "$(YELLOW)Ejecutando tests de Client Management...$(NC)"
4041
WP_TEST__DIR=${WP_TEST__DIR} ${TEST_UNIT} --filter Test_Client_Management --testdox --colors=always
4142

43+
# Tests de Premium Survey
44+
test-survey:
45+
@echo "$(YELLOW)Ejecutando tests de Premium Survey...$(NC)"
46+
WP_TEST__DIR=${WP_TEST__DIR} ${TEST_UNIT} --filter Test_Premium_Survey --testdox --colors=always
47+
4248
# Todos los tests con detalles
4349
test-all:
4450
@echo "$(YELLOW)Ejecutando todos los tests con detalles...$(NC)"

assets/js/integration-alegra.js

Lines changed: 328 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,331 @@
4040
}
4141
});
4242
});
43-
})(jQuery);
43+
})(jQuery);
44+
45+
// =============================================================================
46+
// Premium Survey Modal — Integration Alegra WooCommerce
47+
// =============================================================================
48+
(function ($) {
49+
50+
// -------------------------------------------------------------------------
51+
// Constants
52+
// -------------------------------------------------------------------------
53+
const premiumSurveyButton = 'button.alegra-send-premium-survey';
54+
const actionSendPremiumSurvey = 'integration_alegra_send_premium_survey';
55+
const actionDismissPremiumSurveyNotice = 'integration_alegra_dismiss_premium_survey_notice';
56+
57+
// -------------------------------------------------------------------------
58+
// Question options
59+
// -------------------------------------------------------------------------
60+
const premiumSurveyOptions = {
61+
q2: [
62+
{ value: '', label: '— Selecciona —' },
63+
{ value: 'facturacion', label: 'Problemas con la facturación automática' },
64+
{ value: 'sincronizacion', label: 'Sincronización lenta o incompleta de productos/pedidos' },
65+
{ value: 'configuracion', label: 'Configuración compleja o poco intuitiva' },
66+
{ value: 'soporte', label: 'Falta de soporte técnico oportuno' },
67+
{ value: 'funcionalidades', label: 'Faltan funcionalidades que necesito' },
68+
{ value: 'otro', label: 'Otro' },
69+
],
70+
q3: [
71+
{ value: '', label: '— Selecciona —' },
72+
{ value: 'menos_1h', label: 'Menos de 1 hora' },
73+
{ value: '1_3h', label: 'Entre 1 y 3 horas' },
74+
{ value: '3_5h', label: 'Entre 3 y 5 horas' },
75+
{ value: 'mas_5h', label: 'Más de 5 horas' },
76+
],
77+
q4: [
78+
{ value: 'webhooks', label: 'Webhooks: sincronización automática entre Alegra y WooCommerce' },
79+
{ value: 'notas_credito', label: 'Notas de crédito automáticas' },
80+
{ value: 'cotizaciones', label: 'Cotizaciones desde WooCommerce' },
81+
{ value: 'reportes', label: 'Reportes y alertas inteligentes' },
82+
{ value: 'otras', label: 'Otras funcionalidades (especificar abajo)' },
83+
],
84+
q6: [
85+
{ value: '', label: '— Selecciona —' },
86+
{ value: 'mensual', label: 'Pago mensual recurrente' },
87+
{ value: 'anual', label: 'Pago anual (descuento)' },
88+
{ value: 'pago_unico', label: 'Pago único (licencia permanente)' },
89+
],
90+
q7: [
91+
{ value: '', label: '— Selecciona —' },
92+
{ value: '30000_49000', label: '$30.000 – $49.000 COP/mes' },
93+
{ value: '50000_99000', label: '$50.000 – $99.000 COP/mes' },
94+
{ value: '100000_199000', label: '$100.000 – $199.000 COP/mes' },
95+
{ value: '200000_mas', label: '$200.000 o más COP/mes' },
96+
],
97+
};
98+
99+
// -------------------------------------------------------------------------
100+
// Build HTML
101+
// -------------------------------------------------------------------------
102+
function buildPremiumSurveyHtml() {
103+
const select = (id, opts, required = false) =>
104+
`<select id="${id}" style="width:100%;margin-top:4px" ${required ? 'required' : ''}>
105+
${opts.map(o => `<option value="${o.value}">${o.label}</option>`).join('')}
106+
</select>`;
107+
108+
const checkboxes = (opts) =>
109+
opts.map(o =>
110+
`<label style="display:block;margin:4px 0">
111+
<input type="checkbox" class="swal2-checkbox" name="q4_top_features[]" value="${o.value}"> ${o.label}
112+
</label>`
113+
).join('');
114+
115+
return `
116+
<div style="text-align:left;font-size:14px">
117+
118+
<p style="margin-bottom:16px">Tus respuestas nos ayudan a construir la versión premium que realmente necesitas. 🙏</p>
119+
120+
<!-- Q1: Satisfacción -->
121+
<div class="swal2-input-wrapper" style="margin-bottom:12px">
122+
<label for="q1_score"><strong>1. ¿Qué tan satisfecho estás con el plugin actual? (1 = muy insatisfecho, 10 = muy satisfecho) *</strong></label>
123+
${select('q1_score', Array.from({length:10}, (_,i) => ({value: String(i+1), label: String(i+1)})))}
124+
</div>
125+
126+
<!-- Q1-motivo: solo visible si score < 8 -->
127+
<div id="q1_motivo_wrapper" style="margin-bottom:12px;display:none">
128+
<label for="q1_motivo"><strong>¿Por qué diste esa puntuación? (requerido) *</strong></label>
129+
<textarea id="q1_motivo" style="width:100%;margin-top:4px" rows="3" maxlength="500" placeholder="Cuéntanos qué podemos mejorar..."></textarea>
130+
</div>
131+
132+
<!-- Q2: Pain point -->
133+
<div class="swal2-input-wrapper" style="margin-bottom:12px">
134+
<label for="q2_pain_point"><strong>2. ¿Cuál es tu principal dolor con la integración actual?</strong></label>
135+
${select('q2_pain_point', premiumSurveyOptions.q2)}
136+
</div>
137+
138+
<!-- Q3: Time loss -->
139+
<div class="swal2-input-wrapper" style="margin-bottom:12px">
140+
<label for="q3_time_loss"><strong>3. ¿Cuánto tiempo semanal pierdes en tareas manuales de facturación/contabilidad?</strong></label>
141+
${select('q3_time_loss', premiumSurveyOptions.q3)}
142+
</div>
143+
144+
<!-- Q4: Top features (checkboxes, max 3) -->
145+
<div style="margin-bottom:12px">
146+
<label><strong>4. ¿Cuáles de estas funcionalidades valorarías más en la versión premium? (máximo 3) *</strong></label>
147+
<div id="q4_top_features" style="margin-top:6px">
148+
${checkboxes(premiumSurveyOptions.q4)}
149+
</div>
150+
</div>
151+
152+
<!-- Q5: Other feature -->
153+
<div class="swal2-input-wrapper" style="margin-bottom:12px">
154+
<label for="q5_other_feature"><strong>5. Si seleccionaste "Otras", ¿cuál funcionalidad necesitas?</strong></label>
155+
<input id="q5_other_feature" type="text" style="width:100%;margin-top:4px" maxlength="200" placeholder="Describe la funcionalidad...">
156+
</div>
157+
158+
<!-- Q6: Billing model -->
159+
<div class="swal2-input-wrapper" style="margin-bottom:12px">
160+
<label for="q6_billing_model"><strong>6. ¿Qué modelo de cobro prefieres para la versión premium?</strong></label>
161+
${select('q6_billing_model', premiumSurveyOptions.q6)}
162+
</div>
163+
164+
<!-- Q7: Price range -->
165+
<div class="swal2-input-wrapper" style="margin-bottom:12px">
166+
<label for="q7_price_range"><strong>7. ¿Cuánto estarías dispuesto a pagar mensualmente? *</strong></label>
167+
${select('q7_price_range', premiumSurveyOptions.q7, true)}
168+
</div>
169+
170+
<!-- Q8: Open feedback -->
171+
<div class="swal2-input-wrapper" style="margin-bottom:12px">
172+
<label for="q8_open_feedback"><strong>8. ¿Algo más que quieras compartirnos?</strong></label>
173+
<textarea id="q8_open_feedback" style="width:100%;margin-top:4px" rows="3" maxlength="1000" placeholder="Comentarios adicionales..."></textarea>
174+
</div>
175+
176+
<!-- Consent -->
177+
<div style="margin-bottom:12px">
178+
<label>
179+
<input type="checkbox" id="consent_yes_no" class="swal2-checkbox">
180+
Autorizo que el equipo de Alegra WooCommerce me contacte para profundizar en mis respuestas.
181+
</label>
182+
</div>
183+
184+
<p style="font-size:12px;color:#666">* Campos obligatorios</p>
185+
</div>`;
186+
}
187+
188+
// -------------------------------------------------------------------------
189+
// Collect and validate
190+
// -------------------------------------------------------------------------
191+
function collectPremiumSurveyData() {
192+
const q1Score = parseInt($('#q1_score').val(), 10);
193+
194+
if (isNaN(q1Score) || q1Score < 1 || q1Score > 10) {
195+
Swal.showValidationMessage('La satisfacción debe ser un valor entre 1 y 10.');
196+
return false;
197+
}
198+
199+
if (q1Score < 8) {
200+
const motivo = $('#q1_motivo').val().trim();
201+
if (!motivo) {
202+
Swal.showValidationMessage('Por favor explica el motivo de tu baja satisfacción.');
203+
return false;
204+
}
205+
}
206+
207+
const selectedFeatures = [];
208+
$('#q4_top_features input[type="checkbox"]:checked').each(function () {
209+
selectedFeatures.push($(this).val());
210+
});
211+
212+
if (selectedFeatures.length === 0) {
213+
Swal.showValidationMessage('Selecciona al menos una funcionalidad premium.');
214+
return false;
215+
}
216+
217+
if (selectedFeatures.length > 3) {
218+
Swal.showValidationMessage('Selecciona máximo 3 funcionalidades.');
219+
return false;
220+
}
221+
222+
const q7PriceRange = $('#q7_price_range').val();
223+
if (!q7PriceRange) {
224+
Swal.showValidationMessage('Selecciona un rango de precio.');
225+
return false;
226+
}
227+
228+
return {
229+
q1_score: q1Score,
230+
q1_motivo: $('#q1_motivo').val().trim(),
231+
q2_pain_point: $('#q2_pain_point').val(),
232+
q3_time_loss: $('#q3_time_loss').val(),
233+
q4_top_features: JSON.stringify(selectedFeatures),
234+
q5_other_feature: $('#q5_other_feature').val().trim(),
235+
q6_billing_model: $('#q6_billing_model').val(),
236+
q7_price_range: q7PriceRange,
237+
q8_open_feedback: $('#q8_open_feedback').val().trim(),
238+
consent_yes_no: $('#consent_yes_no').is(':checked') ? 'yes' : 'no',
239+
};
240+
}
241+
242+
// -------------------------------------------------------------------------
243+
// Send survey via AJAX
244+
// -------------------------------------------------------------------------
245+
function sendPremiumSurveyResponse(nonce, surveyData) {
246+
return $.ajax({
247+
url: ajaxurl,
248+
type: 'POST',
249+
dataType: 'json',
250+
data: Object.assign({}, surveyData, {
251+
action: actionSendPremiumSurvey,
252+
nonce,
253+
}),
254+
});
255+
}
256+
257+
// -------------------------------------------------------------------------
258+
// Open modal
259+
// -------------------------------------------------------------------------
260+
function openPremiumSurveyModal(nonce) {
261+
Swal.fire({
262+
title: 'Encuesta Premium — Integration Alegra',
263+
html: buildPremiumSurveyHtml(),
264+
width: '680px',
265+
showCancelButton: true,
266+
confirmButtonText: 'Enviar respuesta',
267+
cancelButtonText: 'Cancelar',
268+
allowOutsideClick: false,
269+
showLoaderOnConfirm: true,
270+
didOpen: () => {
271+
// Show/hide q1_motivo based on q1_score
272+
$('#q1_score').on('change', function () {
273+
const score = parseInt($(this).val(), 10);
274+
if (score < 8) {
275+
$('#q1_motivo_wrapper').show();
276+
} else {
277+
$('#q1_motivo_wrapper').hide();
278+
$('#q1_motivo').val('');
279+
}
280+
});
281+
282+
// Enforce max 3 checkboxes
283+
$(document).on('change', '#q4_top_features input[type="checkbox"]', function () {
284+
const checked = $('#q4_top_features input[type="checkbox"]:checked');
285+
if (checked.length > 3) {
286+
$(this).prop('checked', false);
287+
Swal.showValidationMessage('Solo puedes seleccionar un máximo de 3 funcionalidades.');
288+
}
289+
});
290+
},
291+
preConfirm: () => {
292+
return collectPremiumSurveyData();
293+
},
294+
}).then((result) => {
295+
if (!result.isConfirmed || !result.value) return;
296+
297+
Swal.fire({
298+
title: 'Enviando respuesta…',
299+
didOpen: () => Swal.showLoading(),
300+
allowOutsideClick: false,
301+
});
302+
303+
sendPremiumSurveyResponse(nonce, result.value)
304+
.done((r) => {
305+
if (r.status) {
306+
Swal.fire({
307+
icon: 'success',
308+
title: '¡Gracias!',
309+
text: r.message || 'Tu respuesta fue enviada correctamente.',
310+
});
311+
} else {
312+
Swal.fire({
313+
icon: 'error',
314+
title: 'Error',
315+
text: r.message || 'No fue posible enviar la respuesta.',
316+
});
317+
}
318+
})
319+
.fail(() => {
320+
Swal.fire({
321+
icon: 'error',
322+
title: 'Error de conexión',
323+
text: 'No fue posible conectar con el servidor. Intenta nuevamente.',
324+
});
325+
});
326+
});
327+
}
328+
329+
// -------------------------------------------------------------------------
330+
// Auto-open from URL (?open_premium_survey=1)
331+
// -------------------------------------------------------------------------
332+
function maybeOpenPremiumSurveyFromUrl() {
333+
const params = new URLSearchParams(window.location.search);
334+
if (params.get('open_premium_survey') !== '1') return;
335+
336+
const $btn = $(premiumSurveyButton).first();
337+
if (!$btn.length) return;
338+
339+
openPremiumSurveyModal($btn.data('nonce'));
340+
}
341+
342+
// -------------------------------------------------------------------------
343+
// Dismiss notice via AJAX on WP dismiss button click
344+
// -------------------------------------------------------------------------
345+
function persistPremiumSurveyNoticeDismiss() {
346+
$(document).on('click', '.alegra-premium-survey-notice .notice-dismiss', function () {
347+
const nonce = $(this).closest('.alegra-premium-survey-notice').data('dismiss-nonce');
348+
if (!nonce) return;
349+
350+
$.post(ajaxurl, {
351+
action: actionDismissPremiumSurveyNotice,
352+
nonce,
353+
});
354+
});
355+
}
356+
357+
// -------------------------------------------------------------------------
358+
// Event listeners
359+
// -------------------------------------------------------------------------
360+
$(document).on('click', premiumSurveyButton, function (e) {
361+
e.preventDefault();
362+
openPremiumSurveyModal($(this).data('nonce'));
363+
});
364+
365+
$(document).ready(function () {
366+
maybeOpenPremiumSurveyFromUrl();
367+
});
368+
persistPremiumSurveyNoticeDismiss();
369+
370+
})(jQuery);

includes/admin/other_settings.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@
3434
$all_gateways = Integration_Alegra_WC::get_wc_payment_gateways();
3535

3636
return [
37+
'premium_survey' => array(
38+
'title' => __( 'Encuesta de versión premium' ),
39+
'type' => 'button',
40+
'class' => 'button-primary alegra-send-premium-survey',
41+
'description' => __( 'Ayúdanos a priorizar la versión premium. Toma menos de 3 minutos.' ),
42+
'text' => 'Responder encuesta',
43+
'custom_attributes' => array(
44+
'data-nonce' => wp_create_nonce('integration_alegra_send_premium_survey'),
45+
),
46+
),
3747
'invoice' => array(
3848
'title' => __( 'Facturas de ventas' ),
3949
'type' => 'title'

0 commit comments

Comments
 (0)