feat(feedback): implement ChipDip-style floating dev feedback & bug report widget with markdown sync

This commit is contained in:
2026-10-08 11:53:18 +03:00
parent e1cacde543
commit 8cffae46fa
11 changed files with 1104 additions and 3 deletions
+42
View File
@@ -514,6 +514,48 @@ export const api = {
if (resp.ok) return await resp.json();
throw new Error(`Failed to get lesson history: ${resp.status}`);
}
},
// In-app Feedback & Bug Tracker
feedback: {
async getAll(params = {}) {
let url = '/api/feedback';
const qs = new URLSearchParams();
if (params.category) qs.set('category', params.category);
if (params.status) qs.set('status', params.status);
if (qs.toString()) url += `?${qs.toString()}`;
const resp = await fetch(url);
if (resp.ok) return await resp.json();
return [];
},
async create(category, message, contextUrl = '', contextData = {}) {
const resp = await fetch('/api/feedback', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
category,
message,
context_url: contextUrl,
context_data: contextData
})
});
if (resp.ok) return await resp.json();
const err = await resp.json().catch(() => ({}));
throw new Error(err.detail || 'Не удалось отправить репорт');
},
async updateStatus(id, status) {
const resp = await fetch(`/api/feedback/${id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ status })
});
if (resp.ok) return await resp.json();
throw new Error('Не удалось обновить статус');
},
async delete(id) {
const resp = await fetch(`/api/feedback/${id}`, { method: 'DELETE' });
if (resp.ok) return await resp.json();
throw new Error('Не удалось удалить');
}
}
};