From 23186e38b8b8ecd99b65a64f65491cf4f904a969 Mon Sep 17 00:00:00 2001 From: Tomas Dvorak Date: Wed, 16 Sep 2026 19:18:15 +0200 Subject: [PATCH] feat: integration/webhook counts in project overview ProjectOverview gains integration_count + webhook_count (enabled only); dashboard Resources list gets an Integrations row linking to the new view. Covers the "dashboard widget if cheap" Phase 4 item. Authored By: TDvorak --- apps/backend/db/queries/projects.sql | 13 ++++++++++++- .../internal/database/db/projects.sql.go | 17 ++++++++++++++++- .../internal/services/platform_service.go | 4 ++++ apps/backend/openapi/openapi.yaml | 10 ++++++++++ .../src/components/ProjectDashboard.tsx | 7 +++++++ apps/frontend/src/lib/demo-mode.ts | 2 ++ .../src/generated/models/ProjectOverview.ts | 8 ++++++++ .../src/generated/schemas/$ProjectOverview.ts | 12 ++++++++++++ 8 files changed, 71 insertions(+), 2 deletions(-) diff --git a/apps/backend/db/queries/projects.sql b/apps/backend/db/queries/projects.sql index 6895597..837394b 100644 --- a/apps/backend/db/queries/projects.sql +++ b/apps/backend/db/queries/projects.sql @@ -136,7 +136,18 @@ SELECT SELECT MAX(al.created_at)::TIMESTAMPTZ FROM core.audit_logs al WHERE al.project_id = p.id - ) AS last_audit_at + ) AS last_audit_at, + ( + SELECT COUNT(*)::BIGINT + FROM core.integrations i + WHERE i.project_id = p.id + ) AS integration_count, + ( + SELECT COUNT(*)::BIGINT + FROM core.webhooks w + WHERE w.project_id = p.id + AND w.enabled + ) AS webhook_count FROM core.projects p WHERE p.id = $1; diff --git a/apps/backend/internal/database/db/projects.sql.go b/apps/backend/internal/database/db/projects.sql.go index 3e6f739..b943524 100644 --- a/apps/backend/internal/database/db/projects.sql.go +++ b/apps/backend/internal/database/db/projects.sql.go @@ -217,7 +217,18 @@ SELECT SELECT MAX(al.created_at)::TIMESTAMPTZ FROM core.audit_logs al WHERE al.project_id = p.id - ) AS last_audit_at + ) AS last_audit_at, + ( + SELECT COUNT(*)::BIGINT + FROM core.integrations i + WHERE i.project_id = p.id + ) AS integration_count, + ( + SELECT COUNT(*)::BIGINT + FROM core.webhooks w + WHERE w.project_id = p.id + AND w.enabled + ) AS webhook_count FROM core.projects p WHERE p.id = $1 ` @@ -235,6 +246,8 @@ type GetProjectOverviewRow struct { PendingInvitationCount int64 `json:"pending_invitation_count"` AuditEvents24h int64 `json:"audit_events_24h"` LastAuditAt pgtype.Timestamptz `json:"last_audit_at"` + IntegrationCount int64 `json:"integration_count"` + WebhookCount int64 `json:"webhook_count"` } func (q *Queries) GetProjectOverview(ctx context.Context, id uuid.UUID) (GetProjectOverviewRow, error) { @@ -253,6 +266,8 @@ func (q *Queries) GetProjectOverview(ctx context.Context, id uuid.UUID) (GetProj &i.PendingInvitationCount, &i.AuditEvents24h, &i.LastAuditAt, + &i.IntegrationCount, + &i.WebhookCount, ) return i, err } diff --git a/apps/backend/internal/services/platform_service.go b/apps/backend/internal/services/platform_service.go index 6453667..8a1c5ca 100644 --- a/apps/backend/internal/services/platform_service.go +++ b/apps/backend/internal/services/platform_service.go @@ -179,6 +179,8 @@ type ProjectOverview struct { PendingInvitationCount int64 `json:"pending_invitation_count"` AuditEvents24h int64 `json:"audit_events_24h"` LastAuditAt *time.Time `json:"last_audit_at,omitempty"` + IntegrationCount int64 `json:"integration_count"` + WebhookCount int64 `json:"webhook_count"` } type OrganizationMemberSummary struct { @@ -586,6 +588,8 @@ func (s *PlatformService) GetProjectOverview(ctx context.Context, actor *models. PendingInvitationCount: row.PendingInvitationCount, AuditEvents24h: row.AuditEvents24h, LastAuditAt: projectOverviewLastAuditAt(row.LastAuditAt), + IntegrationCount: row.IntegrationCount, + WebhookCount: row.WebhookCount, }, nil } diff --git a/apps/backend/openapi/openapi.yaml b/apps/backend/openapi/openapi.yaml index d1fee40..ff8f901 100644 --- a/apps/backend/openapi/openapi.yaml +++ b/apps/backend/openapi/openapi.yaml @@ -1979,6 +1979,14 @@ components: type: string format: date-time nullable: true + integration_count: + type: integer + format: int64 + description: Connectors attached to the project. + webhook_count: + type: integer + format: int64 + description: Enabled outbound webhooks. required: - project_id - organization_id @@ -1991,6 +1999,8 @@ components: - object_bytes_total - pending_invitation_count - audit_events_24h + - integration_count + - webhook_count ProjectMember: type: object properties: diff --git a/apps/frontend/src/components/ProjectDashboard.tsx b/apps/frontend/src/components/ProjectDashboard.tsx index c40bef6..bc46dc9 100644 --- a/apps/frontend/src/components/ProjectDashboard.tsx +++ b/apps/frontend/src/components/ProjectDashboard.tsx @@ -6,6 +6,7 @@ import { IconMembers, IconAudit, IconCollections, + IconIntegrations, IconChevronRight, } from "./Icons"; @@ -110,6 +111,12 @@ export function ProjectDashboard(props: ProjectDashboardProps) { icon: IconMembers, view: "members", }, + { + title: "Integrations", + sub: `${overview()?.integration_count ?? 0} connectors ยท ${overview()?.webhook_count ?? 0} webhooks`, + icon: IconIntegrations, + view: "integrations", + }, ]; const audit = () => (props.recentAudit ?? []).slice(0, 6); diff --git a/apps/frontend/src/lib/demo-mode.ts b/apps/frontend/src/lib/demo-mode.ts index f59f5c9..456e646 100644 --- a/apps/frontend/src/lib/demo-mode.ts +++ b/apps/frontend/src/lib/demo-mode.ts @@ -511,6 +511,8 @@ class DemoService { pending_invitation_count: invitations.filter((i) => i.status === OrganizationInvitation.status.PENDING).length, audit_events_24h: auditLogs.length, last_audit_at: auditLogs[0]?.created_at ?? null, + integration_count: demoIntegrations.length, + webhook_count: demoWebhooks.filter((w) => w.enabled).length, }; } diff --git a/packages/api-client/src/generated/models/ProjectOverview.ts b/packages/api-client/src/generated/models/ProjectOverview.ts index 874a985..9dd3071 100644 --- a/packages/api-client/src/generated/models/ProjectOverview.ts +++ b/packages/api-client/src/generated/models/ProjectOverview.ts @@ -15,5 +15,13 @@ export type ProjectOverview = { pending_invitation_count: number; audit_events_24h: number; last_audit_at?: string | null; + /** + * Connectors attached to the project. + */ + integration_count: number; + /** + * Enabled outbound webhooks. + */ + webhook_count: number; }; diff --git a/packages/api-client/src/generated/schemas/$ProjectOverview.ts b/packages/api-client/src/generated/schemas/$ProjectOverview.ts index 334347a..c59b40f 100644 --- a/packages/api-client/src/generated/schemas/$ProjectOverview.ts +++ b/packages/api-client/src/generated/schemas/$ProjectOverview.ts @@ -62,5 +62,17 @@ export const $ProjectOverview = { isNullable: true, format: 'date-time', }, + integration_count: { + type: 'number', + description: `Connectors attached to the project.`, + isRequired: true, + format: 'int64', + }, + webhook_count: { + type: 'number', + description: `Enabled outbound webhooks.`, + isRequired: true, + format: 'int64', + }, }, } as const;