mirror of
http://100.103.83.12:3003/fegger/odoo-at-payroll.git
synced 2026-09-17 16:56:42 +00:00
feat(agent): log answer comments
This commit is contained in:
+57
-42
@@ -139,60 +139,75 @@ function followUpPanel(question) {
|
||||
|
||||
function ratingPanel(answerRequestId) {
|
||||
const panel = node("div", "rating-panel");
|
||||
panel.append(node("strong", "", "War diese Antwort hilfreich?"));
|
||||
panel.append(node("strong", "", "Antwort bewerten oder kommentieren"));
|
||||
|
||||
const actions = node("div", "rating-actions");
|
||||
const up = node("button", "secondary rating-choice", "👍 Ja");
|
||||
const down = node("button", "secondary rating-choice", "👎 Nein");
|
||||
const up = node("button", "secondary rating-choice", "👍 Hilfreich");
|
||||
const down = node("button", "secondary rating-choice", "👎 Nicht hilfreich");
|
||||
const ratingStatus = node("span", "rating-status");
|
||||
up.type = "button";
|
||||
down.type = "button";
|
||||
actions.append(up, down);
|
||||
actions.append(up, down, ratingStatus);
|
||||
panel.append(actions);
|
||||
|
||||
const form = node("div", "rating-form");
|
||||
form.hidden = true;
|
||||
const feedback = node("textarea", "rating-feedback");
|
||||
feedback.rows = 2;
|
||||
feedback.maxLength = 1000;
|
||||
feedback.placeholder = "Optional: Was war hilfreich oder was hat gefehlt?";
|
||||
const send = node("button", "primary", "Bewertung senden");
|
||||
send.type = "button";
|
||||
const status = node("span", "rating-status");
|
||||
form.append(feedback, send, status);
|
||||
panel.append(form);
|
||||
|
||||
let selection = null;
|
||||
const select = (value, selected, other) => {
|
||||
selection = value;
|
||||
selected.classList.add("is-selected");
|
||||
other.classList.remove("is-selected");
|
||||
form.hidden = false;
|
||||
feedback.focus();
|
||||
};
|
||||
up.addEventListener("click", () => select("up", up, down));
|
||||
down.addEventListener("click", () => select("down", down, up));
|
||||
send.addEventListener("click", async () => {
|
||||
if (!selection) return;
|
||||
send.disabled = true;
|
||||
status.textContent = "Wird gespeichert …";
|
||||
const sendRating = async (value, selected, other) => {
|
||||
up.disabled = true;
|
||||
down.disabled = true;
|
||||
ratingStatus.textContent = "Wird gespeichert …";
|
||||
try {
|
||||
const response = await fetch("/v1/ratings", {
|
||||
method: "POST",
|
||||
headers: apiHeaders(),
|
||||
body: JSON.stringify({
|
||||
request_id: answerRequestId,
|
||||
rating: selection,
|
||||
feedback: feedback.value.trim() || null,
|
||||
}),
|
||||
body: JSON.stringify({ request_id: answerRequestId, rating: value }),
|
||||
});
|
||||
if (!response.ok) throw new Error(await errorDetail(response));
|
||||
status.textContent = "Danke, Bewertung gespeichert.";
|
||||
up.disabled = true;
|
||||
down.disabled = true;
|
||||
feedback.disabled = true;
|
||||
send.hidden = true;
|
||||
selected.classList.add("is-selected");
|
||||
other.classList.remove("is-selected");
|
||||
ratingStatus.textContent = "Bewertung gespeichert.";
|
||||
} catch (error) {
|
||||
status.textContent = error.message || "Bewertung konnte nicht gespeichert werden.";
|
||||
send.disabled = false;
|
||||
ratingStatus.textContent = error.message || "Bewertung konnte nicht gespeichert werden.";
|
||||
} finally {
|
||||
up.disabled = false;
|
||||
down.disabled = false;
|
||||
}
|
||||
};
|
||||
up.addEventListener("click", () => sendRating("up", up, down));
|
||||
down.addEventListener("click", () => sendRating("down", down, up));
|
||||
|
||||
const commentForm = node("div", "comment-form");
|
||||
const comment = node("textarea", "comment-input");
|
||||
comment.rows = 3;
|
||||
comment.maxLength = 2000;
|
||||
comment.placeholder = "Kommentar zu dieser Antwort …";
|
||||
comment.setAttribute("aria-label", "Kommentar zu dieser Antwort");
|
||||
const sendComment = node("button", "primary", "Kommentar speichern");
|
||||
sendComment.type = "button";
|
||||
const commentStatus = node("span", "comment-status");
|
||||
commentForm.append(comment, sendComment, commentStatus);
|
||||
panel.append(commentForm);
|
||||
|
||||
sendComment.addEventListener("click", async () => {
|
||||
const text = comment.value.trim();
|
||||
if (!text) {
|
||||
commentStatus.textContent = "Bitte zuerst einen Kommentar eingeben.";
|
||||
comment.focus();
|
||||
return;
|
||||
}
|
||||
sendComment.disabled = true;
|
||||
commentStatus.textContent = "Wird gespeichert …";
|
||||
try {
|
||||
const response = await fetch("/v1/comments", {
|
||||
method: "POST",
|
||||
headers: apiHeaders(),
|
||||
body: JSON.stringify({ request_id: answerRequestId, comment: text }),
|
||||
});
|
||||
if (!response.ok) throw new Error(await errorDetail(response));
|
||||
comment.value = "";
|
||||
commentStatus.textContent = "Kommentar gespeichert. Weitere Kommentare sind möglich.";
|
||||
} catch (error) {
|
||||
commentStatus.textContent = error.message || "Kommentar konnte nicht gespeichert werden.";
|
||||
} finally {
|
||||
sendComment.disabled = false;
|
||||
}
|
||||
});
|
||||
return panel;
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
|
||||
<div class="privacy-note">
|
||||
<strong>Datenschutzgrenze</strong>
|
||||
<span>Keine Namen, Personalnummern oder Lohndaten eingeben. Fragen, Antworten und Bewertungen werden für die Qualitätsprüfung protokolliert.</span>
|
||||
<span>Keine Namen, Personalnummern oder Lohndaten eingeben. Fragen, Antworten, Bewertungen und Kommentare werden für die Qualitätsprüfung protokolliert.</span>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
@@ -124,11 +124,12 @@ details summary { color: var(--green-dark); font-size: 0.83rem; font-weight: 700
|
||||
.query-list { margin: 8px 0 0; padding-left: 20px; color: var(--muted); font-size: 0.76rem; line-height: 1.5; }
|
||||
.rating-panel { display: grid; gap: 9px; margin-top: 16px; padding: 13px; border: 1px solid var(--line); border-radius: 10px; background: #fafbf8; }
|
||||
.rating-panel > strong { font-size: 0.8rem; }
|
||||
.rating-actions { display: flex; gap: 8px; }
|
||||
.rating-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
|
||||
.rating-choice.is-selected { border-color: var(--green); color: var(--green-dark); background: var(--green-soft); }
|
||||
.rating-form { display: grid; grid-template-columns: 1fr auto; gap: 8px; align-items: end; }
|
||||
.rating-feedback { width: 100%; min-height: 62px; resize: vertical; padding: 9px; border: 1px solid var(--line); border-radius: 8px; color: var(--ink); background: white; }
|
||||
.rating-status { grid-column: 1 / -1; color: var(--muted); font-size: 0.72rem; }
|
||||
.rating-status, .comment-status { color: var(--muted); font-size: 0.72rem; }
|
||||
.comment-form { display: grid; grid-template-columns: 1fr auto; gap: 8px; align-items: end; padding-top: 10px; border-top: 1px solid var(--line); }
|
||||
.comment-input { width: 100%; min-height: 72px; resize: vertical; padding: 9px; border: 1px solid var(--line); border-radius: 8px; color: var(--ink); background: white; }
|
||||
.comment-status { grid-column: 1 / -1; }
|
||||
|
||||
.loading-row { display: flex; align-items: center; gap: 10px; color: var(--muted); }
|
||||
.loader { width: 18px; height: 18px; border: 2px solid #cbd3cd; border-top-color: var(--green); border-radius: 50%; animation: spin 0.8s linear infinite; }
|
||||
@@ -169,7 +170,7 @@ details summary { color: var(--green-dark); font-size: 0.83rem; font-weight: 700
|
||||
.messages { padding: 22px 14px 6px; }
|
||||
.message-user { width: 92%; }
|
||||
.composer { margin: 18px 10px 0; }
|
||||
.rating-form { grid-template-columns: 1fr; }
|
||||
.comment-form { grid-template-columns: 1fr; }
|
||||
.footnote { margin-inline: 14px; }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user