Czym jest widget głosowania?
Widget głosowania pozwala zbierać opinie użytkowników bezpośrednio na stronach marketingowych lub w aplikacjach zewnętrznych. Użytkownicy mogą głosować czy dana funkcja jest dla nich przydatna, a ich głosy trafiają do Forum Sugestii.
Jak to wygląda?
Przed głosowaniem
Po głosowaniu
Funkcje widgetu
- Głosowanie - przyciski “Tak” / “Nie”
- Licznik głosów - po zagłosowaniu pokazuje ile osób głosowało
- Komentarze - możliwość dodania opinii tekstowej
- Link do dyskusji - przekierowanie do pełnego wątku na forum
Prompt dla LLM (Claude, ChatGPT, itp.)
Jeśli używasz LLM do kodowania strony, możesz użyć tego promptu:
Dodaj widget głosowania do mojej strony. Widget powinien:
1. Wyświetlać pytanie "Przydatna funkcja?" z przyciskami "Tak" / "Nie"
2. Po głosowaniu pokazać liczbę głosów i link do dyskusji
3. Wysyłać głos na endpoint: POST {FORUM_URL}/rate?helpful=yes|no
4. Pobierać dane z: GET {FORUM_URL}.json (zwraca rating, comments_count)
Gdzie {FORUM_URL} to ścieżka do wpisu w Forum Sugestii, np.:
/forum/sugestie/nazwa-funkcji-ABC123
Widget powinien być:
- Kompaktowy (max 280px szerokości)
- W ciemnym motywie (slate/purple) lub dopasowany do strony
- Responsywny (fixed na mobile, float-right na desktop)
Mapowanie stron na wpisy forum:
- /funkcje/moja-funkcja → /forum/sugestie/moja-funkcja-TOKEN
Gdzie to działa?
| Strony w CMS Sugester/Intum | ✅ Tak | Pełna integracja |
| Aplikacje NOE | ✅ Tak | Pełna integracja |
| Zewnętrzne strony (własny hosting) | ✅ Tak | Wymaga CORS lub proxy |
| Inne CMS-y (WordPress, itp.) | ⚠️ Częściowo | Backend musi być Sugester |
Wymagania backendu:
- Forum Sugestii w Sugester/Intum
- Endpoint
/ratez wyłączonym CSRF (skip_forgery_protection) - Endpoint
.jsonzwracającyratingicomments_count
Kod do osadzenia
Aby osadzić widget na swojej stronie, dodaj poniższy kod HTML i JavaScript:
1. HTML widgetu
<!-- Widget głosowania -->
<div id="voting-widget" class="hidden">
<div class="voting-box">
<p class="voting-question">Przydatna funkcja?</p>
<div id="vote-buttons" class="voting-buttons">
<button onclick="vote('yes')" class="voting-btn btn-yes">👍 Tak</button>
<button onclick="vote('no')" class="voting-btn btn-no">👎 Nie</button>
</div>
<p id="vote-result" class="hidden"></p>
<div id="forum-link" class="hidden">
<a id="forum-link-href" href="#" target="_blank">
💬 Dyskusja <span id="comment-count"></span>
</a>
</div>
</div>
</div>
2. JavaScript
(function() {
// Konfiguracja - zmień na swoje wartości
const FORUM_BASE = 'https://twoja-domena.pl/forum/sugestie';
const entryMap = {
'/funkcje/moja-funkcja': {
path: FORUM_BASE + '/moja-funkcja-ABC123',
id: 123
},
// dodaj więcej mapowań...
};
const path = window.location.pathname.replace(/\/$/, '');
const entry = entryMap[path];
if (entry) {
document.getElementById('voting-widget').classList.remove('hidden');
window.currentEntry = entry;
// Link do forum
document.getElementById('forum-link-href').href = entry.path;
document.getElementById('forum-link').classList.remove('hidden');
// Pobierz liczbę komentarzy
fetch(entry.path + '.json')
.then(r => r.json())
.then(data => {
if (data.comments_count > 0) {
document.getElementById('comment-count').textContent =
'(' + data.comments_count + ')';
}
})
.catch(() => {});
}
window.vote = async function(helpful) {
const btns = document.querySelectorAll('.voting-btn');
btns.forEach(b => b.disabled = true);
try {
await fetch(window.currentEntry.path + '/rate?helpful=' + helpful,
{ method: 'POST' });
// Pobierz aktualny rating
const data = await fetch(window.currentEntry.path + '.json')
.then(r => r.json());
const rating = data?.rating || 0;
let msg = '✓ Dziękujemy!';
if (rating > 0) {
msg = helpful === 'yes'
? '✓ ' + rating + ' osób chce tę funkcję'
: '✓ Dziękujemy! (głosów: ' + rating + ')';
}
document.getElementById('vote-result').textContent = msg;
document.getElementById('vote-result').classList.remove('hidden');
document.getElementById('vote-buttons').style.opacity = '0.5';
} catch(e) {
document.getElementById('vote-result').textContent = 'Błąd - spróbuj ponownie';
document.getElementById('vote-result').classList.remove('hidden');
btns.forEach(b => b.disabled = false);
}
};
})();
3. Style CSS
.voting-box {
padding: 1rem;
background: rgba(30, 41, 59, 0.9);
border-radius: 0.75rem;
border: 1px solid rgba(168, 85, 247, 0.3);
max-width: 280px;
}
.voting-question {
color: #cbd5e1;
font-size: 0.875rem;
margin-bottom: 0.75rem;
}
.voting-buttons {
display: flex;
gap: 0.5rem;
}
.voting-btn {
flex: 1;
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
font-weight: 500;
border-radius: 0.5rem;
border: none;
cursor: pointer;
white-space: nowrap;
}
.btn-yes { background: #059669; color: white; }
.btn-yes:hover { background: #10b981; }
.btn-no { background: #475569; color: white; }
.btn-no:hover { background: #64748b; }
#vote-result {
color: #10b981;
font-size: 0.875rem;
margin-top: 0.75rem;
}
#forum-link a {
color: #a78bfa;
font-size: 0.75rem;
}
.hidden { display: none; }
Wymagania techniczne
- Wpis w Forum Sugestii z włączonym publicznym dostępem
- Endpoint
/ratez wyłączoną ochroną CSRF (skip_forgery_protection) - Endpoint
.jsonzwracającycomments_countirating - CORS skonfigurowany jeśli widget na innej domenie