feat(tools): owner feedback review surface + tool_feedback migration
Adds the missing migration for the tool_feedback table (dobetternorge_maindb) that the in-result feedback widget writes to, repoints api/feedback.php to dbnmDb() for consistency with the engine-config table, and adds an owner-only dashboard (page + read API + nav) summarising ratings and notes by tool/engine. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
/**
|
||||
* /api/dashboard/feedback.php — owner-only tool-feedback review surface (read).
|
||||
*
|
||||
* GET → { ok, totals, by_tool, by_engine, recent }
|
||||
*
|
||||
* Reads dobetternorge_maindb.tool_feedback (written by api/feedback.php).
|
||||
* Owner-gated via dbnToolsIsOwner(). Comments are voluntary, case-content-free.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once dirname(__DIR__, 2) . '/includes/bootstrap.php';
|
||||
|
||||
dbnToolsRequireAuth();
|
||||
|
||||
if (!dbnToolsIsOwner()) {
|
||||
dbnToolsError('Owner access required.', 403, 'forbidden');
|
||||
}
|
||||
|
||||
try {
|
||||
$db = dbnmDb();
|
||||
|
||||
$totalsRow = $db->query(
|
||||
"SELECT
|
||||
COUNT(*) AS total,
|
||||
SUM(rating = 'positive') AS positive,
|
||||
SUM(rating = 'negative') AS negative
|
||||
FROM tool_feedback"
|
||||
)->fetch();
|
||||
|
||||
$byTool = $db->query(
|
||||
"SELECT
|
||||
tool,
|
||||
COUNT(*) AS total,
|
||||
SUM(rating = 'positive') AS positive,
|
||||
SUM(rating = 'negative') AS negative,
|
||||
MAX(created_at) AS last_at
|
||||
FROM tool_feedback
|
||||
GROUP BY tool
|
||||
ORDER BY total DESC, tool ASC"
|
||||
)->fetchAll();
|
||||
|
||||
$byEngine = $db->query(
|
||||
"SELECT
|
||||
COALESCE(NULLIF(engine, ''), '(unknown)') AS engine,
|
||||
COUNT(*) AS total,
|
||||
SUM(rating = 'positive') AS positive,
|
||||
SUM(rating = 'negative') AS negative
|
||||
FROM tool_feedback
|
||||
GROUP BY engine
|
||||
ORDER BY total DESC, engine ASC"
|
||||
)->fetchAll();
|
||||
|
||||
$recent = $db->query(
|
||||
"SELECT tool, rating, engine, missed_or_wrong, created_at
|
||||
FROM tool_feedback
|
||||
WHERE missed_or_wrong IS NOT NULL AND missed_or_wrong <> ''
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 50"
|
||||
)->fetchAll();
|
||||
|
||||
$toInt = static fn($v): int => (int) ($v ?? 0);
|
||||
|
||||
dbnToolsRespond([
|
||||
'ok' => true,
|
||||
'totals' => [
|
||||
'total' => $toInt($totalsRow['total'] ?? 0),
|
||||
'positive' => $toInt($totalsRow['positive'] ?? 0),
|
||||
'negative' => $toInt($totalsRow['negative'] ?? 0),
|
||||
],
|
||||
'by_tool' => array_map(static fn(array $r): array => [
|
||||
'tool' => $r['tool'],
|
||||
'total' => $toInt($r['total']),
|
||||
'positive' => $toInt($r['positive']),
|
||||
'negative' => $toInt($r['negative']),
|
||||
'last_at' => $r['last_at'],
|
||||
], $byTool),
|
||||
'by_engine' => array_map(static fn(array $r): array => [
|
||||
'engine' => $r['engine'],
|
||||
'total' => $toInt($r['total']),
|
||||
'positive' => $toInt($r['positive']),
|
||||
'negative' => $toInt($r['negative']),
|
||||
], $byEngine),
|
||||
'recent' => array_map(static fn(array $r): array => [
|
||||
'tool' => $r['tool'],
|
||||
'rating' => $r['rating'],
|
||||
'engine' => $r['engine'],
|
||||
'missed_or_wrong' => $r['missed_or_wrong'],
|
||||
'created_at' => $r['created_at'],
|
||||
], $recent),
|
||||
]);
|
||||
} catch (DbnToolsHttpException $e) {
|
||||
dbnToolsError($e->getMessage(), $e->status, $e->errorCode, $e->extra ?? []);
|
||||
} catch (Throwable $e) {
|
||||
error_log('[dbn-feedback] ' . $e->getMessage());
|
||||
dbnToolsError('Feedback review failed.', 500, 'op_failed');
|
||||
}
|
||||
Reference in New Issue
Block a user