|
1 | 1 | (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 | + |
2 | 17 | function setText(el, lang) { |
3 | 18 | const value = el.getAttribute(lang === "en" ? "data-en" : "data-ru"); |
4 | 19 | if (value) el.textContent = value; |
|
14 | 29 | }); |
15 | 30 | } |
16 | 31 |
|
| 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 | + |
17 | 44 | document.addEventListener("DOMContentLoaded", function () { |
18 | 45 | const widget = document.querySelector(".cs-feedback"); |
19 | 46 | if (!widget) return; |
20 | 47 |
|
21 | 48 | const lang = widget.getAttribute("data-lang") || "ru"; |
| 49 | + const strings = STRINGS[lang] || STRINGS.ru; |
22 | 50 | const endpoint = widget.getAttribute("data-endpoint") || ""; |
23 | 51 | const pageTitle = widget.getAttribute("data-page-title") || ""; |
24 | 52 | const pageUrl = widget.getAttribute("data-page-url") || ""; |
|
27 | 55 | widget.querySelectorAll(".cs-feedback__btn").forEach((btn) => setText(btn, lang)); |
28 | 56 | widget.querySelectorAll(".cs-feedback__submit").forEach((btn) => setText(btn, lang)); |
29 | 57 |
|
30 | | - const status = widget.querySelector(".cs-feedback__status"); |
31 | 58 | const statusText = widget.querySelector(".cs-feedback__status-text"); |
32 | 59 | const details = widget.querySelector(".cs-feedback__details"); |
33 | 60 | const comment = widget.querySelector(".cs-feedback__comment"); |
34 | 61 | const submit = widget.querySelector(".cs-feedback__submit"); |
35 | 62 | const trap = widget.querySelector(".cs-feedback__trap"); |
36 | | - const storageKey = `cs-feedback:${pageUrl || location.pathname}`; |
37 | | - const cooldownMs = 2 * 60 * 1000; |
38 | 63 |
|
39 | 64 | function setStatus(text) { |
40 | 65 | if (statusText) statusText.textContent = text || ""; |
41 | 66 | } |
42 | 67 |
|
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 | | - |
59 | 68 | function markDone() { |
60 | 69 | widget.classList.add("cs-feedback--done"); |
61 | 70 | } |
|
64 | 73 | widget.classList.toggle("cs-feedback--loading", state); |
65 | 74 | } |
66 | 75 |
|
67 | | - function saveSent() { |
| 76 | + async function sendFeedback(rating, value) { |
68 | 77 | 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(); |
74 | 82 |
|
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); |
81 | 91 | } |
82 | 92 | } |
83 | 93 |
|
|
87 | 97 | setStatus(""); |
88 | 98 |
|
89 | 99 | if (trap && trap.value) { |
90 | | - setStatus(doneMessage()); |
91 | | - markDone(); |
92 | | - return; |
93 | | - } |
94 | | - |
95 | | - if (isRateLimited()) { |
96 | | - setStatus(alreadySentMessage()); |
| 100 | + setStatus(strings.done); |
97 | 101 | markDone(); |
98 | 102 | return; |
99 | 103 | } |
100 | 104 |
|
101 | 105 | if (!endpoint) { |
102 | | - setStatus(requireEndpoint()); |
| 106 | + setStatus(strings.missingEndpoint); |
103 | 107 | return; |
104 | 108 | } |
105 | 109 |
|
|
109 | 113 | return; |
110 | 114 | } |
111 | 115 |
|
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", ""); |
131 | 117 | }); |
132 | 118 | }); |
133 | 119 |
|
134 | 120 | submit.addEventListener("click", async function () { |
135 | 121 | if (trap && trap.value) { |
136 | | - setStatus(doneMessage()); |
137 | | - markDone(); |
138 | | - return; |
139 | | - } |
140 | | - |
141 | | - if (isRateLimited()) { |
142 | | - setStatus(alreadySentMessage()); |
| 122 | + setStatus(strings.done); |
143 | 123 | markDone(); |
144 | 124 | return; |
145 | 125 | } |
146 | 126 |
|
147 | 127 | if (!endpoint) { |
148 | | - setStatus(requireEndpoint()); |
| 128 | + setStatus(strings.missingEndpoint); |
149 | 129 | return; |
150 | 130 | } |
151 | 131 |
|
152 | 132 | const value = comment.value.trim(); |
153 | 133 | if (!value) { |
154 | | - setStatus(lang === "en" ? "Please add a comment." : "Добавьте комментарий."); |
| 134 | + setStatus(strings.emptyComment); |
155 | 135 | return; |
156 | 136 | } |
157 | 137 |
|
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); |
179 | 139 | }); |
180 | 140 | }); |
181 | 141 | })(); |
0 commit comments