Files

58 lines
5.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"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" }, "Сообщений нет")]));
}