58 lines
5.0 KiB
JavaScript
58 lines
5.0 KiB
JavaScript
"use strict";
|
||
// ---------- Обратная связь по порталу ----------
|
||
// Неприметная ссылка внизу каждой страницы открывает короткую форму. Ответы видят только администраторы портала (#/feedback).
|
||
const FB_KIND = { idea: "💡 Идея", bug: "🐞 Ошибка", other: "💬 Другое" };
|
||
const FB_STATUS = { new: "Новое", taken: "В работе", done: "Сделано", rejected: "Не будем делать" };
|
||
|
||
function feedbackFooter() {
|
||
const foot = document.getElementById("appfoot");
|
||
if (!foot) return;
|
||
foot.replaceChildren(me ? h("a", { href: "javascript:void 0", class: "fblink", onclick: openFeedback }, "Предложить улучшение портала или сообщить об ошибке") : "");
|
||
}
|
||
|
||
function openFeedback() {
|
||
if (document.querySelector(".fbmodal")) return;
|
||
const st = { kind: "idea", text: "" };
|
||
const kinds = h("div", { class: "row" });
|
||
const drawKinds = () => kinds.replaceChildren(...Object.entries(FB_KIND).map(([k, t]) =>
|
||
h("button", { type: "button", class: "chipf" + (st.kind === k ? " on" : ""), onclick: () => { st.kind = k; drawKinds(); } }, t)));
|
||
drawKinds();
|
||
const text = h("textarea", { rows: 5, maxlength: 4000, placeholder: "Что улучшить или что не работает? Где именно, как повторить." });
|
||
const close = () => wrap.remove();
|
||
const wrap = h("div", { class: "fbmodal", onclick: e => { if (e.target === wrap) close(); } },
|
||
h("div", { class: "card" }, h("h2", { style: "margin-top:0" }, "Обратная связь по порталу"),
|
||
h("p", { class: "small muted" }, "Сообщение получат администраторы портала. Раздел, где вы сейчас, подставится сам."),
|
||
kinds, h("div", { style: "margin-top:8px" }, text),
|
||
h("div", { class: "row", style: "margin-top:10px" },
|
||
h("button", { onclick: guard(async () => {
|
||
if (text.value.trim().length < 3) throw new Error("Опишите идею или проблему");
|
||
await api("POST", "/feedback", { kind: st.kind, text: text.value, page: (location.hash || "#/").slice(0, 200), client: "web" });
|
||
close(); toast("Спасибо! Сообщение отправлено");
|
||
}) }, "Отправить"), h("button", { class: "sec", onclick: close }, "Отмена"))));
|
||
document.body.append(wrap);
|
||
text.focus();
|
||
}
|
||
|
||
async function feedbackPage(which) {
|
||
if (!me.portal_admin) throw new Error("Доступно только администраторам портала");
|
||
const status = which === "all" ? "" : which === "done" ? "done" : "open";
|
||
const list = await api("GET", "/feedback" + (status ? "?status=" + status : ""));
|
||
const card = f => {
|
||
const sel = h("select", {}, ...Object.entries(FB_STATUS).map(([k, t]) => h("option", { value: k, selected: f.status === k }, t)));
|
||
const ans = h("textarea", { rows: 2, placeholder: "Заметка: что решили, что сделано" }, f.answer);
|
||
return h("div", { class: "card fbcard fb-" + f.status },
|
||
h("div", { class: "row" }, h("b", { style: "flex:1" }, FB_KIND[f.kind] || f.kind), h("span", { class: "badge" }, FB_STATUS[f.status] || f.status)),
|
||
h("div", { class: "small muted" }, [fmt(f.created_at), f.user_name || "—", f.page ? "раздел " + f.page : "", { web: "сайт", m: "мобильная версия", android: "приложение" }[f.client] || f.client].filter(Boolean).join(" · ")),
|
||
h("div", { style: "margin-top:6px;white-space:pre-wrap" }, f.text),
|
||
f.answered_by_name ? h("div", { class: "small muted", style: "margin-top:4px" }, `Обновил ${f.answered_by_name}, ${fmt(f.answered_at)}`) : null,
|
||
h("div", { class: "grid", style: "margin-top:8px" }, field("Статус", sel), field("Заметка", ans)),
|
||
h("div", { class: "row", style: "margin-top:8px" },
|
||
h("button", { class: "sm", onclick: guard(async () => { await api("PATCH", `/feedback/${f.id}`, { status: sel.value, answer: ans.value }); toast("Сохранено"); feedbackPage(which); }) }, "Сохранить"),
|
||
h("button", { class: "sec sm", onclick: guard(async () => { if (confirm("Удалить сообщение?")) { await api("DELETE", `/feedback/${f.id}`); feedbackPage(which); } }) }, "Удалить")));
|
||
};
|
||
fill($app, h("h1", {}, "Обратная связь по порталу"),
|
||
tabs([["#/feedback", "Открытые"], ["#/feedback/done", "Сделано"], ["#/feedback/all", "Все"]], which === "all" ? "#/feedback/all" : which === "done" ? "#/feedback/done" : "#/feedback"),
|
||
h("p", { class: "small muted" }, "Идеи и замечания сотрудников. Видят только администраторы портала. Ссылка на форму — внизу каждой страницы и в меню приложения."),
|
||
...(list.length ? list.map(card) : [h("div", { class: "card muted" }, "Сообщений нет")]));
|
||
}
|