Skip to content

Commit 8f717ac

Browse files
committed
removed rate limit, fixed translations, hidden message on changelog pages
1 parent 10d73ae commit 8f717ac

3 files changed

Lines changed: 63 additions & 93 deletions

File tree

docs/js/feedback.js

Lines changed: 48 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,19 @@
11
(function () {
2+
const STRINGS = {
3+
ru: {
4+
done: "Спасибо!",
5+
error: "Не удалось отправить. Попробуйте позже.",
6+
missingEndpoint: "Не задан endpoint для фидбэка.",
7+
emptyComment: "Добавьте комментарий."
8+
},
9+
en: {
10+
done: "Thanks!",
11+
error: "Failed to send. Try later.",
12+
missingEndpoint: "Feedback endpoint is not configured.",
13+
emptyComment: "Please add a comment."
14+
}
15+
};
16+
217
function setText(el, lang) {
318
const value = el.getAttribute(lang === "en" ? "data-en" : "data-ru");
419
if (value) el.textContent = value;
@@ -14,11 +29,24 @@
1429
});
1530
}
1631

32+
function buildPayload(pageTitle, pageUrl, lang, rating, comment) {
33+
return {
34+
rating: rating,
35+
comment: comment,
36+
page_title: pageTitle,
37+
page_url: pageUrl,
38+
lang: lang,
39+
user_agent: navigator.userAgent,
40+
created_at: new Date().toISOString()
41+
};
42+
}
43+
1744
document.addEventListener("DOMContentLoaded", function () {
1845
const widget = document.querySelector(".cs-feedback");
1946
if (!widget) return;
2047

2148
const lang = widget.getAttribute("data-lang") || "ru";
49+
const strings = STRINGS[lang] || STRINGS.ru;
2250
const endpoint = widget.getAttribute("data-endpoint") || "";
2351
const pageTitle = widget.getAttribute("data-page-title") || "";
2452
const pageUrl = widget.getAttribute("data-page-url") || "";
@@ -27,35 +55,16 @@
2755
widget.querySelectorAll(".cs-feedback__btn").forEach((btn) => setText(btn, lang));
2856
widget.querySelectorAll(".cs-feedback__submit").forEach((btn) => setText(btn, lang));
2957

30-
const status = widget.querySelector(".cs-feedback__status");
3158
const statusText = widget.querySelector(".cs-feedback__status-text");
3259
const details = widget.querySelector(".cs-feedback__details");
3360
const comment = widget.querySelector(".cs-feedback__comment");
3461
const submit = widget.querySelector(".cs-feedback__submit");
3562
const trap = widget.querySelector(".cs-feedback__trap");
36-
const storageKey = `cs-feedback:${pageUrl || location.pathname}`;
37-
const cooldownMs = 2 * 60 * 1000;
3863

3964
function setStatus(text) {
4065
if (statusText) statusText.textContent = text || "";
4166
}
4267

43-
function doneMessage() {
44-
return lang === "en" ? "Thanks!" : "Спасибо!";
45-
}
46-
47-
function errorMessage() {
48-
return lang === "en" ? "Failed to send. Try later." : "Не удалось отправить. Попробуйте позже.";
49-
}
50-
51-
function requireEndpoint() {
52-
return lang === "en" ? "Feedback endpoint is not configured." : "Не задан endpoint для фидбэка.";
53-
}
54-
55-
function alreadySentMessage() {
56-
return lang === "en" ? "We already got your feedback. Спасибо!" : "Мы уже получили ваш отзыв. Спасибо!";
57-
}
58-
5968
function markDone() {
6069
widget.classList.add("cs-feedback--done");
6170
}
@@ -64,20 +73,21 @@
6473
widget.classList.toggle("cs-feedback--loading", state);
6574
}
6675

67-
function saveSent() {
76+
async function sendFeedback(rating, value) {
6877
try {
69-
localStorage.setItem(storageKey, String(Date.now()));
70-
} catch (e) {
71-
// ignore storage errors
72-
}
73-
}
78+
setLoading(true);
79+
await postFeedback(endpoint, buildPayload(pageTitle, pageUrl, lang, rating, value));
80+
setStatus(strings.done);
81+
markDone();
7482

75-
function isRateLimited() {
76-
try {
77-
const last = Number(localStorage.getItem(storageKey) || 0);
78-
return last && Date.now() - last < cooldownMs;
79-
} catch (e) {
80-
return false;
83+
if (rating === "no") {
84+
details.hidden = true;
85+
comment.value = "";
86+
}
87+
} catch (err) {
88+
setStatus(strings.error);
89+
} finally {
90+
setLoading(false);
8191
}
8292
}
8393

@@ -87,19 +97,13 @@
8797
setStatus("");
8898

8999
if (trap && trap.value) {
90-
setStatus(doneMessage());
91-
markDone();
92-
return;
93-
}
94-
95-
if (isRateLimited()) {
96-
setStatus(alreadySentMessage());
100+
setStatus(strings.done);
97101
markDone();
98102
return;
99103
}
100104

101105
if (!endpoint) {
102-
setStatus(requireEndpoint());
106+
setStatus(strings.missingEndpoint);
103107
return;
104108
}
105109

@@ -109,73 +113,29 @@
109113
return;
110114
}
111115

112-
try {
113-
setLoading(true);
114-
await postFeedback(endpoint, {
115-
rating: "yes",
116-
comment: "",
117-
page_title: pageTitle,
118-
page_url: pageUrl,
119-
lang,
120-
user_agent: navigator.userAgent,
121-
created_at: new Date().toISOString()
122-
});
123-
saveSent();
124-
setStatus(doneMessage());
125-
markDone();
126-
} catch (err) {
127-
setStatus(errorMessage());
128-
} finally {
129-
setLoading(false);
130-
}
116+
await sendFeedback("yes", "");
131117
});
132118
});
133119

134120
submit.addEventListener("click", async function () {
135121
if (trap && trap.value) {
136-
setStatus(doneMessage());
137-
markDone();
138-
return;
139-
}
140-
141-
if (isRateLimited()) {
142-
setStatus(alreadySentMessage());
122+
setStatus(strings.done);
143123
markDone();
144124
return;
145125
}
146126

147127
if (!endpoint) {
148-
setStatus(requireEndpoint());
128+
setStatus(strings.missingEndpoint);
149129
return;
150130
}
151131

152132
const value = comment.value.trim();
153133
if (!value) {
154-
setStatus(lang === "en" ? "Please add a comment." : "Добавьте комментарий.");
134+
setStatus(strings.emptyComment);
155135
return;
156136
}
157137

158-
try {
159-
setLoading(true);
160-
await postFeedback(endpoint, {
161-
rating: "no",
162-
comment: value,
163-
page_title: pageTitle,
164-
page_url: pageUrl,
165-
lang,
166-
user_agent: navigator.userAgent,
167-
created_at: new Date().toISOString()
168-
});
169-
saveSent();
170-
setStatus(doneMessage());
171-
markDone();
172-
details.hidden = true;
173-
comment.value = "";
174-
} catch (err) {
175-
setStatus(errorMessage());
176-
} finally {
177-
setLoading(false);
178-
}
138+
await sendFeedback("no", value);
179139
});
180140
});
181141
})();

docs/stylesheets/extra.css

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -461,20 +461,21 @@
461461
}
462462

463463
.cs-feedback__loader {
464+
display: inline-block;
464465
width: 14px;
465466
height: 14px;
466467
border-radius: 50%;
467468
border: 2px solid rgba(255, 61, 108, 0.35);
468469
border-top-color: #ff3d6c;
469470
animation: csFeedbackSpin 0.8s linear infinite;
471+
animation-play-state: paused;
470472
opacity: 0;
471-
transform: scale(0.9);
472-
transition: opacity 0.2s ease, transform 0.2s ease;
473+
transition: opacity 0.2s ease;
473474
}
474475

475476
.cs-feedback--loading .cs-feedback__loader {
476477
opacity: 1;
477-
transform: scale(1);
478+
animation-play-state: running;
478479
}
479480

480481
@keyframes csFeedbackSpin {

overrides/main.html

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,17 @@
77
{% set file_name = page.file.name | default('') | lower %}
88
{% set page_url = page.url | default('') %}
99
{% set is_index = file_name.startswith('index.') %}
10-
{% set is_guide = page_url in ['user-guide/', 'user-guide.en/', 'admin-guide/', 'admin-guide.en/'] %}
11-
{% if not page.is_homepage and not is_index and not is_guide %}
10+
{% set excluded_feedback_urls = [
11+
'user-guide/',
12+
'user-guide.en/',
13+
'admin-guide/',
14+
'admin-guide.en/',
15+
'on-premise/how-to/general/',
16+
'on-premise/how-to/general.en/'
17+
] %}
18+
{% set is_guide = page_url in excluded_feedback_urls %}
19+
{% set is_changelog = page_url.startswith('changelog/') %}
20+
{% if not page.is_homepage and not is_index and not is_guide and not is_changelog %}
1221
<div
1322
class="md-feedback cs-feedback"
1423
data-lang="{% if '.en/' in page.url or page.url.endswith('.en/') %}en{% else %}ru{% endif %}"

0 commit comments

Comments
 (0)