feat(bugtracker, tokens): Add project search, push_id, target_agent, tags, edit modal, extended severities (idea, wishlist), token actions (copy/toggle/delete), and agent integration guide
This commit is contained in:
@@ -14,7 +14,7 @@ class BugRepo
|
||||
}
|
||||
|
||||
/**
|
||||
* Ingest a Bug or Feature Request. Automates error-hash deduplication for bugs.
|
||||
* Ingest a Bug or Feature Request / Idea. Automates error-hash deduplication for bugs.
|
||||
*/
|
||||
public function reportItem(array $data): array
|
||||
{
|
||||
@@ -28,9 +28,12 @@ class BugRepo
|
||||
$environment = in_array($data['environment'] ?? '', ['production', 'development', 'staging', 'testing'])
|
||||
? $data['environment']
|
||||
: 'production';
|
||||
$severity = in_array($data['severity'] ?? '', ['low', 'medium', 'high', 'critical'])
|
||||
$severity = in_array($data['severity'] ?? '', ['idea', 'wishlist', 'low', 'medium', 'high', 'critical'])
|
||||
? $data['severity']
|
||||
: 'medium';
|
||||
: ($type === 'feature_request' ? 'medium' : 'medium');
|
||||
$pushId = !empty($data['push_id']) ? trim($data['push_id']) : null;
|
||||
$targetAgent = !empty($data['target_agent']) ? trim($data['target_agent']) : null;
|
||||
$tags = !empty($data['tags']) ? trim($data['tags']) : null;
|
||||
$createdBy = !empty($data['created_by']) ? trim($data['created_by']) : 'agent';
|
||||
|
||||
// Find associated project_id from dc_projects
|
||||
@@ -59,10 +62,16 @@ class BugRepo
|
||||
$newCount = (int)$existing['occurrence_count'] + 1;
|
||||
$upd = $this->db->prepare('
|
||||
UPDATE bugtracker_items
|
||||
SET occurrence_count = :count, last_seen_at = NOW()
|
||||
SET occurrence_count = :count,
|
||||
last_seen_at = NOW(),
|
||||
push_id = COALESCE(:push_id, push_id)
|
||||
WHERE id = :id
|
||||
');
|
||||
$upd->execute([':count' => $newCount, ':id' => $existing['id']]);
|
||||
$upd->execute([
|
||||
':count' => $newCount,
|
||||
':push_id' => $pushId,
|
||||
':id' => $existing['id'],
|
||||
]);
|
||||
|
||||
return [
|
||||
'id' => (int)$existing['id'],
|
||||
@@ -71,6 +80,7 @@ class BugRepo
|
||||
'error_hash' => $errorHash,
|
||||
'type' => $type,
|
||||
'environment' => $environment,
|
||||
'push_id' => $pushId,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -80,39 +90,43 @@ class BugRepo
|
||||
project_id, project_slug, type, title, description,
|
||||
error_message, stack_trace, error_hash, build_version,
|
||||
environment, severity, status, occurrence_count,
|
||||
push_id, target_agent, tags,
|
||||
first_seen_at, last_seen_at, created_by, created_at
|
||||
) VALUES (
|
||||
:pid, :slug, :type, :title, :desc,
|
||||
:err, :trace, :hash, :build,
|
||||
:env, :sev, "open", 1,
|
||||
:push_id, :target_agent, :tags,
|
||||
NOW(), NOW(), :created_by, NOW()
|
||||
)
|
||||
');
|
||||
|
||||
$ins->execute([
|
||||
':pid' => $projectId,
|
||||
':slug' => $projectSlug,
|
||||
':type' => $type,
|
||||
':title' => $title,
|
||||
':desc' => $description,
|
||||
':err' => $errorMessage,
|
||||
':trace' => $stackTrace,
|
||||
':hash' => $errorHash,
|
||||
':build' => $buildVersion,
|
||||
':env' => $environment,
|
||||
':sev' => $severity,
|
||||
':created_by' => $createdBy,
|
||||
':pid' => $projectId,
|
||||
':slug' => $projectSlug,
|
||||
':type' => $type,
|
||||
':title' => $title,
|
||||
':desc' => $description,
|
||||
':err' => $errorMessage,
|
||||
':trace' => $stackTrace,
|
||||
':hash' => $errorHash,
|
||||
':build' => $buildVersion,
|
||||
':env' => $environment,
|
||||
':sev' => $severity,
|
||||
':push_id' => $pushId,
|
||||
':target_agent' => $targetAgent,
|
||||
':tags' => $tags,
|
||||
':created_by' => $createdBy,
|
||||
]);
|
||||
|
||||
$newItemId = (int)$this->db->lastInsertId();
|
||||
|
||||
// Initial comment log
|
||||
$this->addComment(
|
||||
$newItemId,
|
||||
$createdBy,
|
||||
$type === 'bug' ? 'Bug in System erfasst.' : 'Feature-Request eingereicht.',
|
||||
'reported'
|
||||
);
|
||||
$initialNote = ($type === 'bug')
|
||||
? 'Bug in System erfasst.'
|
||||
: ($severity === 'idea' ? '💡 Neue Idee / Gedanke hinterlegt.' : 'Feature-Request eingereicht.');
|
||||
|
||||
$this->addComment($newItemId, $createdBy, $initialNote, 'reported');
|
||||
|
||||
return [
|
||||
'id' => $newItemId,
|
||||
@@ -121,11 +135,12 @@ class BugRepo
|
||||
'error_hash' => $errorHash,
|
||||
'type' => $type,
|
||||
'environment' => $environment,
|
||||
'push_id' => $pushId,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filtered list of Bugs & Feature Requests.
|
||||
* Get filtered list of Bugs, Features, & Ideas.
|
||||
*/
|
||||
public function getItems(array $filters = []): array
|
||||
{
|
||||
@@ -157,8 +172,18 @@ class BugRepo
|
||||
$params[':severity'] = $filters['severity'];
|
||||
}
|
||||
|
||||
if (!empty($filters['push_id'])) {
|
||||
$where[] = 'push_id = :push_id';
|
||||
$params[':push_id'] = trim($filters['push_id']);
|
||||
}
|
||||
|
||||
if (!empty($filters['target_agent'])) {
|
||||
$where[] = 'target_agent = :agent';
|
||||
$params[':agent'] = trim($filters['target_agent']);
|
||||
}
|
||||
|
||||
if (!empty($filters['search'])) {
|
||||
$where[] = '(title LIKE :q OR description LIKE :q OR error_message LIKE :q)';
|
||||
$where[] = '(title LIKE :q OR description LIKE :q OR error_message LIKE :q OR push_id LIKE :q OR target_agent LIKE :q OR tags LIKE :q)';
|
||||
$params[':q'] = '%' . trim($filters['search']) . '%';
|
||||
}
|
||||
|
||||
@@ -248,6 +273,83 @@ class BugRepo
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comprehensive Item Edit (Status, Severity, Push ID, Target Agent, Tags, Notes).
|
||||
*/
|
||||
public function updateItemDetails(int $itemId, array $updates, string $author = 'agent'): bool
|
||||
{
|
||||
$existing = $this->getItemDetails($itemId);
|
||||
if (!$existing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$fields = [];
|
||||
$params = [':id' => $itemId];
|
||||
$changes = [];
|
||||
|
||||
if (isset($updates['status']) && in_array($updates['status'], ['open', 'planned', 'in_progress', 'resolved', 'closed', 'rejected'])) {
|
||||
$fields[] = 'status = :status';
|
||||
$params[':status'] = $updates['status'];
|
||||
if ($existing['status'] !== $updates['status']) {
|
||||
$changes[] = "Status: {$existing['status']} ➔ {$updates['status']}";
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($updates['severity']) && in_array($updates['severity'], ['idea', 'wishlist', 'low', 'medium', 'high', 'critical'])) {
|
||||
$fields[] = 'severity = :severity';
|
||||
$params[':severity'] = $updates['severity'];
|
||||
if ($existing['severity'] !== $updates['severity']) {
|
||||
$changes[] = "Schweregrad: {$existing['severity']} ➔ {$updates['severity']}";
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists('push_id', $updates)) {
|
||||
$fields[] = 'push_id = :push_id';
|
||||
$params[':push_id'] = !empty($updates['push_id']) ? trim($updates['push_id']) : null;
|
||||
if ($existing['push_id'] !== $updates['push_id']) {
|
||||
$changes[] = "Push-ID hinterlegt: {$updates['push_id']}";
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists('target_agent', $updates)) {
|
||||
$fields[] = 'target_agent = :target_agent';
|
||||
$params[':target_agent'] = !empty($updates['target_agent']) ? trim($updates['target_agent']) : null;
|
||||
if ($existing['target_agent'] !== $updates['target_agent']) {
|
||||
$changes[] = "Ziel-Agent: {$updates['target_agent']}";
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists('tags', $updates)) {
|
||||
$fields[] = 'tags = :tags';
|
||||
$params[':tags'] = !empty($updates['tags']) ? trim($updates['tags']) : null;
|
||||
}
|
||||
|
||||
if (array_key_exists('resolved_in_build', $updates)) {
|
||||
$fields[] = 'resolved_in_build = :build';
|
||||
$params[':build'] = !empty($updates['resolved_in_build']) ? trim($updates['resolved_in_build']) : null;
|
||||
}
|
||||
|
||||
if (array_key_exists('resolution_notes', $updates)) {
|
||||
$fields[] = 'resolution_notes = :notes';
|
||||
$params[':notes'] = !empty($updates['resolution_notes']) ? trim($updates['resolution_notes']) : null;
|
||||
}
|
||||
|
||||
if (empty($fields)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$sql = 'UPDATE bugtracker_items SET ' . implode(', ', $fields) . ' WHERE id = :id';
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$result = $stmt->execute($params);
|
||||
|
||||
if ($result && !empty($changes)) {
|
||||
$changeMsg = 'Item aktualisiert: ' . implode(' | ', $changes);
|
||||
$this->addComment($itemId, $author, $changeMsg, 'item_updated');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a Bug or complete a Feature Request with build details.
|
||||
*/
|
||||
@@ -288,6 +390,7 @@ class BugRepo
|
||||
'open_bugs_prod' => 0,
|
||||
'open_bugs_dev' => 0,
|
||||
'open_features' => 0,
|
||||
'ideas_count' => 0,
|
||||
'resolved_total' => 0,
|
||||
'critical_bugs' => 0,
|
||||
];
|
||||
@@ -296,7 +399,8 @@ class BugRepo
|
||||
SELECT
|
||||
SUM(CASE WHEN type = "bug" AND environment = "production" AND status IN ("open", "in_progress") THEN 1 ELSE 0 END) as open_bugs_prod,
|
||||
SUM(CASE WHEN type = "bug" AND environment = "development" AND status IN ("open", "in_progress") THEN 1 ELSE 0 END) as open_bugs_dev,
|
||||
SUM(CASE WHEN type = "feature_request" AND status IN ("open", "planned", "in_progress") THEN 1 ELSE 0 END) as open_features,
|
||||
SUM(CASE WHEN type = "feature_request" AND severity NOT IN ("idea") AND status IN ("open", "planned", "in_progress") THEN 1 ELSE 0 END) as open_features,
|
||||
SUM(CASE WHEN severity = "idea" AND status IN ("open", "planned") THEN 1 ELSE 0 END) as ideas_count,
|
||||
SUM(CASE WHEN status = "resolved" THEN 1 ELSE 0 END) as resolved_total,
|
||||
SUM(CASE WHEN type = "bug" AND severity = "critical" AND status IN ("open", "in_progress") THEN 1 ELSE 0 END) as critical_bugs
|
||||
FROM bugtracker_items
|
||||
@@ -306,6 +410,7 @@ class BugRepo
|
||||
$stats['open_bugs_prod'] = (int)($res['open_bugs_prod'] ?? 0);
|
||||
$stats['open_bugs_dev'] = (int)($res['open_bugs_dev'] ?? 0);
|
||||
$stats['open_features'] = (int)($res['open_features'] ?? 0);
|
||||
$stats['ideas_count'] = (int)($res['ideas_count'] ?? 0);
|
||||
$stats['resolved_total'] = (int)($res['resolved_total'] ?? 0);
|
||||
$stats['critical_bugs'] = (int)($res['critical_bugs'] ?? 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user