From 5b06154ea37b5731dcb1894650f4402b541b64ee Mon Sep 17 00:00:00 2001 From: lucumango Date: Wed, 4 Mar 2026 01:42:29 +0100 Subject: [PATCH 1/2] feat(ux): add character counter and helper text to textareas to emphasize qualitative feedback value --- src/app/plugins/firebase/index.js | 8 +++++++- src/app/plugins/locales/en.json | 4 +++- src/app/plugins/locales/es.json | 4 +++- src/shared/components/TextareaForm.vue | 24 ++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/app/plugins/firebase/index.js b/src/app/plugins/firebase/index.js index afc5bd7c8..b968a0c17 100644 --- a/src/app/plugins/firebase/index.js +++ b/src/app/plugins/firebase/index.js @@ -19,7 +19,13 @@ const firebaseConfig = { const firebaseApp = initializeApp(firebaseConfig) const auth = getAuth(firebaseApp) const db = getFirestore(firebaseApp) -const analytics = getAnalytics(firebaseApp) + +// Analytics doesn't work well with emulators and fake API keys +let analytics = null +if (process.env.VUE_APP_USE_EMULATORS !== 'true' && process.env.VUE_APP_FIREBASE_API_KEY && !process.env.VUE_APP_FIREBASE_API_KEY.includes('dummy')) { + analytics = getAnalytics(firebaseApp) +} + const fbFunctions = getFunctions(firebaseApp) const storage = getStorage(firebaseApp, `gs://${firebaseConfig.storageBucket}`) const database = getDatabase(firebaseApp, firebaseConfig.databaseURL) diff --git a/src/app/plugins/locales/en.json b/src/app/plugins/locales/en.json index 9588ef59b..f14665af4 100644 --- a/src/app/plugins/locales/en.json +++ b/src/app/plugins/locales/en.json @@ -47,6 +47,8 @@ }, "common": { "enterTextHere": "Enter text here...", + "valuableFeedback": "Your qualitative feedback is highly valuable", + "characters": "characters", "units": { "mb": "MB", "gb": "GB" @@ -2313,4 +2315,4 @@ "rawWcagDataAvailable": "Raw WCAG Data Available:", "filteredWcagDataAvailable": "Filtered WCAG Data Available:" } -} +} \ No newline at end of file diff --git a/src/app/plugins/locales/es.json b/src/app/plugins/locales/es.json index ee9a68c34..bab0adbe1 100644 --- a/src/app/plugins/locales/es.json +++ b/src/app/plugins/locales/es.json @@ -47,6 +47,8 @@ }, "common": { "enterTextHere": "Ingrese texto aquí...", + "valuableFeedback": "Tus comentarios cualitativos son muy valiosos", + "characters": "caracteres", "units": { "mb": "MB", "gb": "GB" @@ -2299,4 +2301,4 @@ }, "title": "Política de Privacidad" } -} +} \ No newline at end of file diff --git a/src/shared/components/TextareaForm.vue b/src/shared/components/TextareaForm.vue index 6237834f6..332f22f81 100644 --- a/src/shared/components/TextareaForm.vue +++ b/src/shared/components/TextareaForm.vue @@ -25,6 +25,20 @@ :class="['editor-container', { 'editor-readonly': props.readonly }]" @ready="applyReadonlyAttributes" /> + + + + + mdi-lightbulb-outline + {{ props.helperText || t('common.valuableFeedback') }} + + + + + {{ characterCount }} {{ t('common.characters') }} + + + @@ -54,12 +68,22 @@ const props = defineProps({ type: String, default: '', }, + helperText: { + type: String, + default: '', + }, readonly: { type: Boolean, default: false, }, }) +const characterCount = computed(() => { + if (!value.value) return 0 + // Strip HTML tags for an accurate character count, as Quill outputs HTML + return value.value.replace(/<[^>]*>?/gm, '').trim().length +}) + const editorOptions = computed(() => ({ theme: 'snow', readOnly: props.readonly, From 6d9a02188af05458306e7dac7374c9efe4bcd9ce Mon Sep 17 00:00:00 2001 From: lucumango Date: Thu, 5 Mar 2026 03:35:30 +0100 Subject: [PATCH 2/2] fix(pr): address review comments --- src/app/plugins/firebase/index.js | 6 +----- src/app/plugins/locales/ar.json | 4 +++- src/app/plugins/locales/de.json | 4 +++- src/app/plugins/locales/fr.json | 4 +++- src/app/plugins/locales/hi.json | 4 +++- src/app/plugins/locales/ja.json | 4 +++- src/app/plugins/locales/pt_br.json | 4 +++- src/app/plugins/locales/ru.json | 4 +++- src/app/plugins/locales/zh.json | 4 +++- src/shared/components/TextareaForm.vue | 8 ++++++-- 10 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/app/plugins/firebase/index.js b/src/app/plugins/firebase/index.js index b968a0c17..96b6edaf2 100644 --- a/src/app/plugins/firebase/index.js +++ b/src/app/plugins/firebase/index.js @@ -20,11 +20,7 @@ const firebaseApp = initializeApp(firebaseConfig) const auth = getAuth(firebaseApp) const db = getFirestore(firebaseApp) -// Analytics doesn't work well with emulators and fake API keys -let analytics = null -if (process.env.VUE_APP_USE_EMULATORS !== 'true' && process.env.VUE_APP_FIREBASE_API_KEY && !process.env.VUE_APP_FIREBASE_API_KEY.includes('dummy')) { - analytics = getAnalytics(firebaseApp) -} +const analytics = getAnalytics(firebaseApp) const fbFunctions = getFunctions(firebaseApp) const storage = getStorage(firebaseApp, `gs://${firebaseConfig.storageBucket}`) diff --git a/src/app/plugins/locales/ar.json b/src/app/plugins/locales/ar.json index 15ad53358..c5a00718f 100644 --- a/src/app/plugins/locales/ar.json +++ b/src/app/plugins/locales/ar.json @@ -112,7 +112,9 @@ "user": "مستخدم", "id": "ID", "itemsPerPage": "عدد العناصر في الصفحة:", - "visible": "مرئي" + "visible": "مرئي", + "valuableFeedback": "Your qualitative feedback is highly valuable", + "characters": "characters" }, "storage": { "pageTitle": "التخزين", diff --git a/src/app/plugins/locales/de.json b/src/app/plugins/locales/de.json index 7998a1aeb..957ab0df5 100644 --- a/src/app/plugins/locales/de.json +++ b/src/app/plugins/locales/de.json @@ -112,7 +112,9 @@ "user": "Benutzer", "id": "ID", "itemsPerPage": "Elemente pro Seite:", - "visible": "Sichtbar" + "visible": "Sichtbar", + "valuableFeedback": "Your qualitative feedback is highly valuable", + "characters": "characters" }, "storage": { "pageTitle": "Speicher", diff --git a/src/app/plugins/locales/fr.json b/src/app/plugins/locales/fr.json index 0b26d531f..ff9d3f56e 100644 --- a/src/app/plugins/locales/fr.json +++ b/src/app/plugins/locales/fr.json @@ -112,7 +112,9 @@ "user": "Utilisateur", "id": "ID", "itemsPerPage": "Éléments par page :", - "visible": "Visible" + "visible": "Visible", + "valuableFeedback": "Your qualitative feedback is highly valuable", + "characters": "characters" }, "storage": { "pageTitle": "Stockage", diff --git a/src/app/plugins/locales/hi.json b/src/app/plugins/locales/hi.json index ebbdecd72..57471305c 100644 --- a/src/app/plugins/locales/hi.json +++ b/src/app/plugins/locales/hi.json @@ -112,7 +112,9 @@ "user": "उपयोगकर्ता", "id": "आईडी", "itemsPerPage": "प्रति पृष्ठ आइटम:", - "visible": "दृश्य" + "visible": "दृश्य", + "valuableFeedback": "Your qualitative feedback is highly valuable", + "characters": "characters" }, "storage": { "pageTitle": "संग्रहण", diff --git a/src/app/plugins/locales/ja.json b/src/app/plugins/locales/ja.json index b134de620..619463dba 100644 --- a/src/app/plugins/locales/ja.json +++ b/src/app/plugins/locales/ja.json @@ -112,7 +112,9 @@ "user": "ユーザー", "id": "ID", "itemsPerPage": "ページごとの項目数:", - "visible": "表示" + "visible": "表示", + "valuableFeedback": "Your qualitative feedback is highly valuable", + "characters": "characters" }, "storage": { "pageTitle": "ストレージ", diff --git a/src/app/plugins/locales/pt_br.json b/src/app/plugins/locales/pt_br.json index d1e001eeb..9108557ba 100644 --- a/src/app/plugins/locales/pt_br.json +++ b/src/app/plugins/locales/pt_br.json @@ -112,7 +112,9 @@ "user": "Usuário", "id": "ID", "itemsPerPage": "Itens por página:", - "visible": "Visível" + "visible": "Visível", + "valuableFeedback": "Your qualitative feedback is highly valuable", + "characters": "characters" }, "storage": { "pageTitle": "Armazenamento", diff --git a/src/app/plugins/locales/ru.json b/src/app/plugins/locales/ru.json index 95ec376ce..a7ec7bfe4 100644 --- a/src/app/plugins/locales/ru.json +++ b/src/app/plugins/locales/ru.json @@ -112,7 +112,9 @@ "user": "Пользователь", "id": "ID", "itemsPerPage": "Элементов на странице:", - "visible": "Видимый" + "visible": "Видимый", + "valuableFeedback": "Your qualitative feedback is highly valuable", + "characters": "characters" }, "storage": { "pageTitle": "Хранилище", diff --git a/src/app/plugins/locales/zh.json b/src/app/plugins/locales/zh.json index 9992f52b5..689427ebd 100644 --- a/src/app/plugins/locales/zh.json +++ b/src/app/plugins/locales/zh.json @@ -112,7 +112,9 @@ "user": "用户", "id": "ID", "itemsPerPage": "每页项目数:", - "visible": "可见" + "visible": "可见", + "valuableFeedback": "Your qualitative feedback is highly valuable", + "characters": "characters" }, "storage": { "pageTitle": "存储", diff --git a/src/shared/components/TextareaForm.vue b/src/shared/components/TextareaForm.vue index 332f22f81..6f2475568 100644 --- a/src/shared/components/TextareaForm.vue +++ b/src/shared/components/TextareaForm.vue @@ -80,8 +80,12 @@ const props = defineProps({ const characterCount = computed(() => { if (!value.value) return 0 - // Strip HTML tags for an accurate character count, as Quill outputs HTML - return value.value.replace(/<[^>]*>?/gm, '').trim().length + // Use DOMParser to derive plain text from HTML, decoding entities for an accurate count + const parser = new DOMParser() + const doc = parser.parseFromString(value.value, 'text/html') + const text = doc.body?.textContent || '' + + return text.trim().length }) const editorOptions = computed(() => ({