519bdbb6e5
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>
99 lines
3.2 KiB
PHP
99 lines
3.2 KiB
PHP
<?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');
|
|
}
|