|null $channels */ public function logEntry( int $alertId, float $metricValue, string $status, ?array $channels = null, ?string $errorMessage = null ): void { $this->insert([ 'alert_id' => $alertId, 'metric_value' => $metricValue, 'channels_notified' => $channels !== null ? json_encode($channels) : null, 'status' => $status, 'error_message' => $errorMessage, ]); } /** * Recent history rows for one alert (newest first). * * @return list> */ public function forAlert(int $alertId, int $limit = 50, int $offset = 0): array { return $this->where('alert_id', $alertId) ->orderBy('id', 'DESC') ->findAll($limit, $offset); } public function countForAlert(int $alertId): int { return (int) $this->where('alert_id', $alertId)->countAllResults(); } /** * Badge: distinct alerts with a successful trigger in the last N seconds (workspace). */ public function countRecentTriggeredAlerts(int $workspaceId, int $secondsAgo = 3600): int { $since = date('Y-m-d H:i:s', time() - $secondsAgo); $row = $this->builder() ->select('COUNT(DISTINCT alerts.id) AS c', false) ->join('alerts', 'alerts.id = alert_history.alert_id') ->where('alerts.workspace_id', $workspaceId) ->where('alert_history.status', 'sent') ->where('alert_history.triggered_at >=', $since) ->get() ->getRowArray(); return (int) ($row['c'] ?? 0); } /** * Recent triggers for workspace (for sidebar panel). * * @return list> */ public function recentForWorkspace(int $workspaceId, int $limit = 15): array { return $this->builder() ->select('alert_history.*, alerts.name AS alert_name, charts.name AS chart_name') ->join('alerts', 'alerts.id = alert_history.alert_id') ->join('charts', 'charts.id = alerts.chart_id', 'left') ->where('alerts.workspace_id', $workspaceId) ->where('alert_history.status', 'sent') ->orderBy('alert_history.id', 'DESC') ->limit($limit) ->get() ->getResultArray(); } }