@burdenoff/microfe-vibecontrols 2026.528.7 → 2026.528.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/VibeControlsRoot.js +1 -0
- package/dist/VibeControlsRoot.js.map +1 -1
- package/dist/components/audit/AuditLogFilters.js +71 -47
- package/dist/components/audit/AuditLogFilters.js.map +1 -1
- package/dist/components/shared/AgentSetupBanner.js +50 -45
- package/dist/components/shared/AgentSetupBanner.js.map +1 -1
- package/dist/components/shared/LinkToVibePanel.js +97 -0
- package/dist/components/shared/LinkToVibePanel.js.map +1 -0
- package/dist/components/shared/index.js +1 -0
- package/dist/components/vibe/VibeLinkedEntitiesPanel.js +60 -0
- package/dist/components/vibe/VibeLinkedEntitiesPanel.js.map +1 -0
- package/dist/components/vibedeck/VibeDeckButtonTile.js +25 -23
- package/dist/components/vibedeck/VibeDeckButtonTile.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckGrid.js +2 -2
- package/dist/components/vibedeck/VibeDeckGrid.js.map +1 -1
- package/dist/microfe-vibecontrols.css +1 -1
- package/dist/pages/AgentDetailsPage.js +2 -2
- package/dist/pages/AgentDetailsPage.js.map +1 -1
- package/dist/pages/AnalyticsPage.js +255 -204
- package/dist/pages/AnalyticsPage.js.map +1 -1
- package/dist/pages/OverviewPage.js +1 -11
- package/dist/pages/OverviewPage.js.map +1 -1
- package/dist/pages/SessionsPage.js +825 -629
- package/dist/pages/SessionsPage.js.map +1 -1
- package/dist/pages/VibeAuditPage.js +184 -153
- package/dist/pages/VibeAuditPage.js.map +1 -1
- package/dist/pages/VibeDeckPage.js +416 -379
- package/dist/pages/VibeDeckPage.js.map +1 -1
- package/dist/pages/VibeDetailsPage.js +621 -557
- package/dist/pages/VibeDetailsPage.js.map +1 -1
- package/dist/pages/tunnels/TunnelsTab.js +115 -90
- package/dist/pages/tunnels/TunnelsTab.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,239 +1,270 @@
|
|
|
1
|
-
import { useBulkDeleteVibeAuditLogsMutation as e, useDeleteVibeAuditLogMutation as t, useGetVibeAuditLogQuery as n,
|
|
2
|
-
import { useVibeControls as
|
|
3
|
-
import { PageHeader as
|
|
1
|
+
import { useBulkDeleteVibeAuditLogsMutation as e, useDeleteVibeAuditLogMutation as t, useGetVibeAuditLogQuery as n, useListAgentsQuery as r, useListVibesQuery as ee, useVibeAuditDashboardQuery as te } from "../generated/wspace-operations.js";
|
|
2
|
+
import { useVibeControls as ne } from "../providers/VibeControlsProvider.js";
|
|
3
|
+
import { PageHeader as re } from "../components/shared/PageHeader.js";
|
|
4
4
|
import { LoadingState as i } from "../components/shared/LoadingState.js";
|
|
5
|
-
import { ErrorState as
|
|
6
|
-
import { isNative as
|
|
7
|
-
import { EmptyState as
|
|
8
|
-
import { GetHelpButton as
|
|
5
|
+
import { ErrorState as ie } from "../components/shared/ErrorState.js";
|
|
6
|
+
import { isNative as ae, nativeNotify as a } from "../utils/nativeBridge.js";
|
|
7
|
+
import { EmptyState as oe } from "../components/shared/EmptyState.js";
|
|
8
|
+
import { GetHelpButton as se } from "../components/shared/GetHelpButton.js";
|
|
9
9
|
import "../components/shared/index.js";
|
|
10
|
-
import { VIBE_PERMISSIONS as
|
|
11
|
-
import { AuditLogTable as
|
|
12
|
-
import { AuditLogFilters as
|
|
13
|
-
import { AuditLogDetail as
|
|
14
|
-
import { AuditLogStats as
|
|
15
|
-
import { AuditDeleteDialog as
|
|
16
|
-
import { AuditExportButton as
|
|
10
|
+
import { VIBE_PERMISSIONS as o } from "../constants/permissions.js";
|
|
11
|
+
import { AuditLogTable as ce } from "../components/audit/AuditLogTable.js";
|
|
12
|
+
import { AuditLogFilters as le } from "../components/audit/AuditLogFilters.js";
|
|
13
|
+
import { AuditLogDetail as ue } from "../components/audit/AuditLogDetail.js";
|
|
14
|
+
import { AuditLogStats as de } from "../components/audit/AuditLogStats.js";
|
|
15
|
+
import { AuditDeleteDialog as fe } from "../components/audit/AuditDeleteDialog.js";
|
|
16
|
+
import { AuditExportButton as pe } from "../components/audit/AuditExportButton.js";
|
|
17
17
|
import "../components/audit/index.js";
|
|
18
|
-
import { useCallback as
|
|
19
|
-
import { useI18n as
|
|
20
|
-
import { Shield as
|
|
21
|
-
import { usePermissions as
|
|
22
|
-
import { jsx as
|
|
23
|
-
import { AccessDenied as
|
|
18
|
+
import { useCallback as s, useEffect as c, useRef as me, useState as l } from "react";
|
|
19
|
+
import { useI18n as he } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
20
|
+
import { Shield as u } from "lucide-react";
|
|
21
|
+
import { usePermissions as ge } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
22
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
23
|
+
import { AccessDenied as _e } from "@burdenoff/fe-libs/shared/components";
|
|
24
24
|
//#region src/pages/VibeAuditPage.tsx
|
|
25
|
-
var
|
|
26
|
-
function
|
|
27
|
-
|
|
28
|
-
let { t:
|
|
29
|
-
let n =
|
|
25
|
+
var p = 50;
|
|
26
|
+
function m() {
|
|
27
|
+
ne();
|
|
28
|
+
let { t: m } = he(), { hasPermission: h, isLoading: g, permissions: _ } = ge(), [v, ve] = l(""), [y, ye] = l(""), [b, be] = l("all"), [x, xe] = l("all"), [S, Se] = l("all"), [C, w] = l("all"), [T, E] = l("all"), [D, Ce] = l(!1), [O, k] = l(0), [A, j] = l(null), [M, N] = l(/* @__PURE__ */ new Set()), [P, F] = l([]), [we, I] = l(null), [Te, L] = l(null), R = me(null), z = s((e, t) => {
|
|
29
|
+
let n = m(e);
|
|
30
30
|
return n === e ? t : n;
|
|
31
|
-
}, [
|
|
32
|
-
|
|
33
|
-
let { data:
|
|
31
|
+
}, [m]), B = h(o.AUDIT_LIST), V = h(o.AUDIT_DELETE), Ee = h(o.AUDIT_EXPORT), H = g || !B, U = {};
|
|
32
|
+
y && (U.search = y), b && b !== "all" && (U.action = b), x && x !== "all" && (U.resourceType = x), S && S !== "all" && (U.status = S), D && (U.hasCommand = !0), C && C !== "all" ? (U.resourceId = C, U.resourceType ||= "AGENT") : T && T !== "all" && (U.resourceId = T, U.resourceType ||= "VIBE");
|
|
33
|
+
let { data: W, loading: G, error: K, refetch: q } = te({
|
|
34
34
|
variables: {
|
|
35
|
-
filter:
|
|
36
|
-
pagination: { limit: Math.min(Math.max((
|
|
35
|
+
filter: U,
|
|
36
|
+
pagination: { limit: Math.min(Math.max((O + 1) * p, p), 100) }
|
|
37
37
|
},
|
|
38
|
-
skip:
|
|
38
|
+
skip: H,
|
|
39
39
|
fetchPolicy: "cache-and-network",
|
|
40
40
|
notifyOnNetworkStatusChange: !0
|
|
41
|
-
}), { data:
|
|
42
|
-
variables: {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
}), { data: De } = r({
|
|
42
|
+
variables: { pagination: { limit: 200 } },
|
|
43
|
+
fetchPolicy: "cache-first",
|
|
44
|
+
skip: H
|
|
45
|
+
}), Oe = (De?.vibecontrolsAgents ?? []).map((e) => ({
|
|
46
|
+
id: e.id,
|
|
47
|
+
name: e.name
|
|
48
|
+
})), { data: ke } = ee({
|
|
49
|
+
variables: { pagination: { limit: 200 } },
|
|
50
|
+
fetchPolicy: "cache-first",
|
|
51
|
+
skip: H
|
|
52
|
+
}), Ae = (ke?.vibes?.items ?? []).map((e) => ({
|
|
53
|
+
id: e.id,
|
|
54
|
+
name: e.name
|
|
55
|
+
})), je = W?.vibeAuditLogStats?.retentionPolicy?.retentionDays ?? 90, J = W?.vibeAuditLogStats?.retentionPolicy?.nextCleanup ?? null, { data: Y } = n({
|
|
56
|
+
variables: { id: A },
|
|
57
|
+
skip: !A
|
|
58
|
+
}), [Me, { loading: Ne }] = t(), [Pe] = e();
|
|
59
|
+
c(() => {
|
|
46
60
|
let e = window.setTimeout(() => {
|
|
47
|
-
|
|
61
|
+
ye(v), k(0);
|
|
48
62
|
}, 300);
|
|
49
63
|
return () => window.clearTimeout(e);
|
|
50
|
-
}, [
|
|
51
|
-
|
|
64
|
+
}, [v]), c(() => () => {
|
|
65
|
+
R.current && clearTimeout(R.current);
|
|
52
66
|
}, []);
|
|
53
|
-
let
|
|
54
|
-
|
|
55
|
-
}, []),
|
|
56
|
-
|
|
57
|
-
}, []),
|
|
58
|
-
if (
|
|
59
|
-
|
|
67
|
+
let X = s(() => {
|
|
68
|
+
I(null), R.current &&= (clearTimeout(R.current), null);
|
|
69
|
+
}, []), Z = s(() => {
|
|
70
|
+
L(null);
|
|
71
|
+
}, []), Q = (W?.vibeAuditLogs.items ?? []).slice(O * p, (O + 1) * p), Fe = W?.vibeAuditLogs.total ?? 0, $ = Q.map((e) => e.id), Ie = $.length > 0 && $.every((e) => M.has(e)), Le = $.some((e) => M.has(e)) && !Ie, Re = async (e) => {
|
|
72
|
+
if (P.length !== 0) {
|
|
73
|
+
X(), Z();
|
|
60
74
|
try {
|
|
61
|
-
if (
|
|
62
|
-
id:
|
|
75
|
+
if (P.length === 1) await Me({ variables: {
|
|
76
|
+
id: P[0],
|
|
63
77
|
input: { reason: e }
|
|
64
|
-
} }),
|
|
78
|
+
} }), L("Deleted 1 audit log");
|
|
65
79
|
else {
|
|
66
|
-
let t = await
|
|
67
|
-
ids:
|
|
80
|
+
let t = await Pe({ variables: {
|
|
81
|
+
ids: P,
|
|
68
82
|
input: { reason: e }
|
|
69
83
|
} }), n = t.data?.bulkDeleteVibeAuditLogs.deletedCount ?? 0, r = t.data?.bulkDeleteVibeAuditLogs.failedCount ?? 0;
|
|
70
|
-
|
|
84
|
+
L(`Deleted ${n} audit logs${r > 0 ? ` • ${r} failed` : ""}`);
|
|
71
85
|
}
|
|
72
|
-
|
|
86
|
+
F([]), N((e) => {
|
|
73
87
|
let t = new Set(e);
|
|
74
|
-
return
|
|
75
|
-
}),
|
|
88
|
+
return P.forEach((e) => t.delete(e)), t;
|
|
89
|
+
}), A && P.includes(A) && j(null), await q(), ae() && a("success");
|
|
76
90
|
} catch (e) {
|
|
77
|
-
console.error("Failed to delete audit log:", e),
|
|
78
|
-
|
|
91
|
+
console.error("Failed to delete audit log:", e), I(z("audit.deleteFailed", "Failed to delete audit log. Please try again.")), ae() && a("error"), R.current = setTimeout(() => {
|
|
92
|
+
I(null), R.current = null;
|
|
79
93
|
}, 3e4);
|
|
80
94
|
}
|
|
81
95
|
}
|
|
82
|
-
},
|
|
83
|
-
|
|
96
|
+
}, ze = s((e) => {
|
|
97
|
+
V && (X(), Z(), F([e]));
|
|
84
98
|
}, [
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
]),
|
|
89
|
-
|
|
99
|
+
V,
|
|
100
|
+
X,
|
|
101
|
+
Z
|
|
102
|
+
]), Be = s((e, t) => {
|
|
103
|
+
N((n) => {
|
|
90
104
|
let r = new Set(n);
|
|
91
105
|
return t ? r.add(e) : r.delete(e), r;
|
|
92
106
|
});
|
|
93
|
-
}, []),
|
|
94
|
-
|
|
107
|
+
}, []), Ve = s((e) => {
|
|
108
|
+
N((t) => {
|
|
95
109
|
let n = new Set(t);
|
|
96
|
-
return
|
|
110
|
+
return $.forEach((t) => {
|
|
97
111
|
e ? n.add(t) : n.delete(t);
|
|
98
112
|
}), n;
|
|
99
113
|
});
|
|
100
|
-
}, [
|
|
101
|
-
!
|
|
114
|
+
}, [$]), He = s(() => {
|
|
115
|
+
!V || M.size === 0 || (X(), Z(), F(Array.from(M)));
|
|
102
116
|
}, [
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
117
|
+
V,
|
|
118
|
+
X,
|
|
119
|
+
Z,
|
|
120
|
+
M
|
|
107
121
|
]);
|
|
108
|
-
return
|
|
122
|
+
return g ? /* @__PURE__ */ d("div", {
|
|
109
123
|
className: "mx-auto max-w-[1400px] p-4 sm:p-6",
|
|
110
|
-
children: /* @__PURE__ */
|
|
111
|
-
}) :
|
|
112
|
-
title:
|
|
113
|
-
description:
|
|
114
|
-
}) : /* @__PURE__ */
|
|
124
|
+
children: /* @__PURE__ */ d(i, { message: z("audit.loadingPermissions", "Loading permissions...") })
|
|
125
|
+
}) : _ && _.permissionStrings?.length > 0 && !B ? /* @__PURE__ */ d(_e, {
|
|
126
|
+
title: z("common.accessDeniedTitle", "Access Denied"),
|
|
127
|
+
description: z("common.accessDeniedDescription", "You don't have permission to access this resource.")
|
|
128
|
+
}) : /* @__PURE__ */ f("div", {
|
|
115
129
|
className: "mx-auto max-w-[1400px] p-4 sm:p-6",
|
|
116
130
|
children: [
|
|
117
|
-
/* @__PURE__ */
|
|
118
|
-
title:
|
|
119
|
-
description:
|
|
120
|
-
actions:
|
|
131
|
+
/* @__PURE__ */ d(re, {
|
|
132
|
+
title: z("nav.vibeAudit", "Audit"),
|
|
133
|
+
description: z("audit.description", "Comprehensive audit trail of all VibeControls operations"),
|
|
134
|
+
actions: Ee ? /* @__PURE__ */ d(pe, { filter: U }) : void 0
|
|
121
135
|
}),
|
|
122
|
-
/* @__PURE__ */ f(
|
|
123
|
-
|
|
124
|
-
|
|
136
|
+
/* @__PURE__ */ f("div", {
|
|
137
|
+
className: "mb-4 flex items-start gap-2 rounded-md border border-status-info-border bg-status-info-bg/10 px-3 py-2 text-xs text-status-info-text",
|
|
138
|
+
children: [/* @__PURE__ */ d(u, {
|
|
139
|
+
size: 14,
|
|
140
|
+
className: "mt-0.5 flex-shrink-0"
|
|
141
|
+
}), /* @__PURE__ */ f("span", { children: [z("audit.retentionBanner", `Audit logs are kept for ${je} days. Older entries are automatically purged.`), J ? ` ${z("audit.retentionNextCleanup", `Next cleanup: ${new Date(J).toLocaleString()}.`)}` : ""] })]
|
|
125
142
|
}),
|
|
126
|
-
/* @__PURE__ */
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
143
|
+
/* @__PURE__ */ d(de, {
|
|
144
|
+
stats: W?.vibeAuditLogStats,
|
|
145
|
+
loading: G && !W
|
|
146
|
+
}),
|
|
147
|
+
/* @__PURE__ */ d(le, {
|
|
148
|
+
searchQuery: v,
|
|
149
|
+
onSearchChange: ve,
|
|
150
|
+
actionFilter: b,
|
|
130
151
|
onActionChange: (e) => {
|
|
131
|
-
|
|
152
|
+
be(e), k(0);
|
|
132
153
|
},
|
|
133
|
-
resourceTypeFilter:
|
|
154
|
+
resourceTypeFilter: x,
|
|
134
155
|
onResourceTypeChange: (e) => {
|
|
135
|
-
|
|
156
|
+
xe(e), k(0);
|
|
136
157
|
},
|
|
137
|
-
statusFilter:
|
|
158
|
+
statusFilter: S,
|
|
138
159
|
onStatusChange: (e) => {
|
|
139
|
-
|
|
160
|
+
Se(e), k(0);
|
|
140
161
|
},
|
|
141
|
-
hasCommandFilter:
|
|
162
|
+
hasCommandFilter: D,
|
|
142
163
|
onHasCommandChange: (e) => {
|
|
143
|
-
|
|
144
|
-
}
|
|
164
|
+
Ce(e), k(0);
|
|
165
|
+
},
|
|
166
|
+
agentFilter: C,
|
|
167
|
+
onAgentChange: (e) => {
|
|
168
|
+
w(e), e !== "all" && E("all"), k(0);
|
|
169
|
+
},
|
|
170
|
+
agents: Oe,
|
|
171
|
+
vibeFilter: T,
|
|
172
|
+
onVibeChange: (e) => {
|
|
173
|
+
E(e), e !== "all" && w("all"), k(0);
|
|
174
|
+
},
|
|
175
|
+
vibes: Ae
|
|
145
176
|
}),
|
|
146
|
-
|
|
177
|
+
Q.length > 0 ? /* @__PURE__ */ d("div", {
|
|
147
178
|
className: "rounded-lg border border-border-primary bg-bg-surface p-4",
|
|
148
|
-
children: /* @__PURE__ */
|
|
179
|
+
children: /* @__PURE__ */ f("div", {
|
|
149
180
|
className: "flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between",
|
|
150
|
-
children: [/* @__PURE__ */
|
|
181
|
+
children: [/* @__PURE__ */ f("div", {
|
|
151
182
|
className: "flex flex-wrap items-center gap-3",
|
|
152
|
-
children: [/* @__PURE__ */
|
|
183
|
+
children: [/* @__PURE__ */ f("span", {
|
|
153
184
|
className: "text-sm font-medium text-text-primary",
|
|
154
|
-
children: [
|
|
155
|
-
}),
|
|
185
|
+
children: [M.size, " selected"]
|
|
186
|
+
}), M.size > 0 ? /* @__PURE__ */ d("button", {
|
|
156
187
|
type: "button",
|
|
157
|
-
onClick: () =>
|
|
188
|
+
onClick: () => N(/* @__PURE__ */ new Set()),
|
|
158
189
|
className: "text-sm text-text-secondary transition-colors hover:text-text-primary",
|
|
159
190
|
children: "Clear selection"
|
|
160
191
|
}) : null]
|
|
161
|
-
}), /* @__PURE__ */
|
|
192
|
+
}), /* @__PURE__ */ d("div", {
|
|
162
193
|
className: "flex flex-wrap items-center gap-2",
|
|
163
|
-
children: /* @__PURE__ */
|
|
194
|
+
children: /* @__PURE__ */ f("button", {
|
|
164
195
|
type: "button",
|
|
165
|
-
onClick:
|
|
166
|
-
disabled: !
|
|
196
|
+
onClick: He,
|
|
197
|
+
disabled: !V || M.size === 0,
|
|
167
198
|
className: "rounded-md border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg disabled:cursor-not-allowed disabled:opacity-50",
|
|
168
|
-
children: ["Delete Selected",
|
|
199
|
+
children: ["Delete Selected", M.size > 0 ? ` (${M.size})` : ""]
|
|
169
200
|
})
|
|
170
201
|
})]
|
|
171
202
|
})
|
|
172
203
|
}) : null,
|
|
173
|
-
|
|
174
|
-
message:
|
|
175
|
-
onRetry: () =>
|
|
176
|
-
}) :
|
|
177
|
-
icon: /* @__PURE__ */
|
|
204
|
+
G && !W ? /* @__PURE__ */ d(i, { message: z("audit.loadingLogs", "Loading audit logs...") }) : K ? /* @__PURE__ */ d(ie, {
|
|
205
|
+
message: K.message,
|
|
206
|
+
onRetry: () => q()
|
|
207
|
+
}) : Fe === 0 ? /* @__PURE__ */ d(oe, {
|
|
208
|
+
icon: /* @__PURE__ */ d(u, {
|
|
178
209
|
size: 48,
|
|
179
210
|
className: "text-text-secondary"
|
|
180
211
|
}),
|
|
181
|
-
title:
|
|
182
|
-
description:
|
|
183
|
-
action: /* @__PURE__ */
|
|
184
|
-
}) : /* @__PURE__ */
|
|
185
|
-
items:
|
|
186
|
-
total:
|
|
187
|
-
hasMore:
|
|
188
|
-
page:
|
|
189
|
-
pageSize:
|
|
190
|
-
onPageChange:
|
|
191
|
-
onSelectLog:
|
|
192
|
-
onDeleteLog:
|
|
193
|
-
selectedLogId:
|
|
194
|
-
selectedLogIds:
|
|
195
|
-
allVisibleSelected:
|
|
196
|
-
partiallySelected:
|
|
197
|
-
onToggleLogSelection:
|
|
198
|
-
onToggleAllVisible:
|
|
212
|
+
title: z("audit.noLogs", "No audit logs"),
|
|
213
|
+
description: z("audit.emptyDescription", "Audit logs will appear here as operations are performed in VibeControls."),
|
|
214
|
+
action: /* @__PURE__ */ d(se, { prompt: "What is the Audit Log in VibeControls and how do I use it?" })
|
|
215
|
+
}) : /* @__PURE__ */ d(ce, {
|
|
216
|
+
items: Q,
|
|
217
|
+
total: Fe,
|
|
218
|
+
hasMore: W?.vibeAuditLogs.hasMore ?? !1,
|
|
219
|
+
page: O,
|
|
220
|
+
pageSize: p,
|
|
221
|
+
onPageChange: k,
|
|
222
|
+
onSelectLog: j,
|
|
223
|
+
onDeleteLog: ze,
|
|
224
|
+
selectedLogId: A,
|
|
225
|
+
selectedLogIds: M,
|
|
226
|
+
allVisibleSelected: Ie,
|
|
227
|
+
partiallySelected: Le,
|
|
228
|
+
onToggleLogSelection: Be,
|
|
229
|
+
onToggleAllVisible: Ve
|
|
199
230
|
}),
|
|
200
|
-
|
|
201
|
-
log:
|
|
202
|
-
onClose: () =>
|
|
231
|
+
A && Y?.vibeAuditLog ? /* @__PURE__ */ d(ue, {
|
|
232
|
+
log: Y.vibeAuditLog,
|
|
233
|
+
onClose: () => j(null)
|
|
203
234
|
}) : null,
|
|
204
|
-
|
|
235
|
+
we ? /* @__PURE__ */ f("div", {
|
|
205
236
|
role: "alert",
|
|
206
237
|
className: "fixed bottom-4 right-4 z-50 rounded-md bg-status-error-bg px-4 py-3 text-sm text-status-error-text shadow-lg",
|
|
207
|
-
children: [
|
|
238
|
+
children: [we, /* @__PURE__ */ d("button", {
|
|
208
239
|
type: "button",
|
|
209
|
-
onClick:
|
|
240
|
+
onClick: X,
|
|
210
241
|
className: "ml-3 text-xs underline opacity-80 hover:opacity-100",
|
|
211
|
-
children:
|
|
242
|
+
children: z("common.dismiss", "Dismiss")
|
|
212
243
|
})]
|
|
213
244
|
}) : null,
|
|
214
|
-
|
|
245
|
+
Te ? /* @__PURE__ */ f("div", {
|
|
215
246
|
role: "status",
|
|
216
247
|
className: "fixed bottom-4 right-4 z-50 rounded-md bg-status-success-bg px-4 py-3 text-sm text-status-success-text shadow-lg",
|
|
217
|
-
children: [
|
|
248
|
+
children: [Te, /* @__PURE__ */ d("button", {
|
|
218
249
|
type: "button",
|
|
219
|
-
onClick:
|
|
250
|
+
onClick: Z,
|
|
220
251
|
className: "ml-3 text-xs underline opacity-80 hover:opacity-100",
|
|
221
|
-
children:
|
|
252
|
+
children: z("common.dismiss", "Dismiss")
|
|
222
253
|
})]
|
|
223
254
|
}) : null,
|
|
224
|
-
/* @__PURE__ */
|
|
225
|
-
isOpen:
|
|
226
|
-
onClose: () =>
|
|
227
|
-
onConfirm:
|
|
228
|
-
loading:
|
|
229
|
-
title:
|
|
230
|
-
description:
|
|
231
|
-
confirmLabel:
|
|
255
|
+
/* @__PURE__ */ d(fe, {
|
|
256
|
+
isOpen: P.length > 0,
|
|
257
|
+
onClose: () => F([]),
|
|
258
|
+
onConfirm: Re,
|
|
259
|
+
loading: Ne,
|
|
260
|
+
title: P.length > 1 ? "Delete Audit Logs" : "Delete Audit Log",
|
|
261
|
+
description: P.length > 1 ? `This will permanently delete ${P.length} audit logs and record the reason in the audit trail.` : "This action will be recorded in the audit trail.",
|
|
262
|
+
confirmLabel: P.length > 1 ? "Delete Logs" : "Delete"
|
|
232
263
|
})
|
|
233
264
|
]
|
|
234
265
|
});
|
|
235
266
|
}
|
|
236
267
|
//#endregion
|
|
237
|
-
export {
|
|
268
|
+
export { m as VibeAuditPage };
|
|
238
269
|
|
|
239
270
|
//# sourceMappingURL=VibeAuditPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VibeAuditPage.js","names":[],"sources":["../../src/pages/VibeAuditPage.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\nimport { Shield } from 'lucide-react';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { isNative, nativeNotify } from '../utils/nativeBridge';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n AuditDeleteDialog,\n AuditExportButton,\n AuditLogDetail,\n AuditLogFilters,\n AuditLogStats,\n AuditLogTable,\n} from '@/components/audit';\nimport {\n EmptyState,\n ErrorState,\n GetHelpButton,\n LoadingState,\n PageHeader,\n} from '@/components/shared';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport {\n useBulkDeleteVibeAuditLogsMutation,\n useDeleteVibeAuditLogMutation,\n useGetVibeAuditLogQuery,\n useVibeAuditDashboardQuery,\n} from '@/generated/wspace-operations';\nimport type {\n VibeAuditAction,\n VibeAuditLogFilterInput,\n VibeAuditResourceType,\n VibeAuditStatus,\n} from '@/generated/wspace-types';\nimport { useVibeControls } from '@/providers/VibeControlsProvider';\n\nconst PAGE_SIZE = 50;\n\nexport function VibeAuditPage() {\n useVibeControls();\n\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n\n const [searchQuery, setSearchQuery] = useState('');\n const [debouncedSearchQuery, setDebouncedSearchQuery] = useState('');\n const [actionFilter, setActionFilter] = useState<string | null>('all');\n const [resourceTypeFilter, setResourceTypeFilter] = useState<string | null>('all');\n const [statusFilter, setStatusFilter] = useState<string | null>('all');\n const [hasCommandFilter, setHasCommandFilter] = useState(false);\n const [page, setPage] = useState(0);\n const [selectedLogId, setSelectedLogId] = useState<string | null>(null);\n const [selectedLogIds, setSelectedLogIds] = useState<Set<string>>(new Set());\n const [deleteTargetIds, setDeleteTargetIds] = useState<string[]>([]);\n const [deleteError, setDeleteError] = useState<string | null>(null);\n const [deleteSuccess, setDeleteSuccess] = useState<string | null>(null);\n\n const deleteErrorTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n const canListAudit = hasPermission(VIBE_PERMISSIONS.AUDIT_LIST);\n const canDeleteAudit = hasPermission(VIBE_PERMISSIONS.AUDIT_DELETE);\n const canExportAudit = hasPermission(VIBE_PERMISSIONS.AUDIT_EXPORT);\n const shouldSkipQueries = permissionsLoading || !canListAudit;\n\n const filter: VibeAuditLogFilterInput = {};\n if (debouncedSearchQuery) filter.search = debouncedSearchQuery;\n if (actionFilter && actionFilter !== 'all') filter.action = actionFilter as VibeAuditAction;\n if (resourceTypeFilter && resourceTypeFilter !== 'all')\n filter.resourceType = resourceTypeFilter as VibeAuditResourceType;\n if (statusFilter && statusFilter !== 'all') filter.status = statusFilter as VibeAuditStatus;\n if (hasCommandFilter) filter.hasCommand = true;\n\n const { data, loading, error, refetch } = useVibeAuditDashboardQuery({\n variables: {\n filter,\n pagination: { limit: Math.min(Math.max((page + 1) * PAGE_SIZE, PAGE_SIZE), 100) },\n },\n skip: shouldSkipQueries,\n fetchPolicy: 'cache-and-network',\n notifyOnNetworkStatusChange: true,\n });\n\n const { data: detailData } = useGetVibeAuditLogQuery({\n variables: { id: selectedLogId! },\n skip: !selectedLogId,\n });\n\n const [deleteAuditLog, { loading: deleteLoading }] = useDeleteVibeAuditLogMutation();\n const [bulkDeleteAuditLogs] = useBulkDeleteVibeAuditLogsMutation();\n\n useEffect(() => {\n const timeoutId = window.setTimeout(() => {\n setDebouncedSearchQuery(searchQuery);\n setPage(0);\n }, 300);\n\n return () => window.clearTimeout(timeoutId);\n }, [searchQuery]);\n\n useEffect(() => {\n return () => {\n if (deleteErrorTimer.current) {\n clearTimeout(deleteErrorTimer.current);\n }\n };\n }, []);\n\n const clearDeleteError = useCallback(() => {\n setDeleteError(null);\n if (deleteErrorTimer.current) {\n clearTimeout(deleteErrorTimer.current);\n deleteErrorTimer.current = null;\n }\n }, []);\n\n const clearDeleteSuccess = useCallback(() => {\n setDeleteSuccess(null);\n }, []);\n\n const allItems = data?.vibeAuditLogs.items ?? [];\n const items = allItems.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE);\n const totalAuditItems = data?.vibeAuditLogs.total ?? 0;\n const visibleLogIds = items.map((item: (typeof items)[number]) => item.id);\n const allVisibleSelected =\n visibleLogIds.length > 0 && visibleLogIds.every((id: string) => selectedLogIds.has(id));\n const partiallySelected =\n visibleLogIds.some((id: string) => selectedLogIds.has(id)) && !allVisibleSelected;\n\n const handleDelete = async (reason: string) => {\n if (deleteTargetIds.length === 0) return;\n\n clearDeleteError();\n clearDeleteSuccess();\n\n try {\n if (deleteTargetIds.length === 1) {\n await deleteAuditLog({ variables: { id: deleteTargetIds[0], input: { reason } } });\n setDeleteSuccess('Deleted 1 audit log');\n } else {\n const result = await bulkDeleteAuditLogs({\n variables: { ids: deleteTargetIds, input: { reason } },\n });\n const deletedCount = result.data?.bulkDeleteVibeAuditLogs.deletedCount ?? 0;\n const failedCount = result.data?.bulkDeleteVibeAuditLogs.failedCount ?? 0;\n setDeleteSuccess(\n `Deleted ${deletedCount} audit logs${failedCount > 0 ? ` • ${failedCount} failed` : ''}`\n );\n }\n\n setDeleteTargetIds([]);\n setSelectedLogIds((previous) => {\n const next = new Set(previous);\n deleteTargetIds.forEach((id) => next.delete(id));\n return next;\n });\n if (selectedLogId && deleteTargetIds.includes(selectedLogId)) {\n setSelectedLogId(null);\n }\n await refetch();\n if (isNative()) void nativeNotify('success');\n } catch (deleteAuditError) {\n console.error('Failed to delete audit log:', deleteAuditError);\n setDeleteError(tr('audit.deleteFailed', 'Failed to delete audit log. Please try again.'));\n if (isNative()) void nativeNotify('error');\n deleteErrorTimer.current = setTimeout(() => {\n setDeleteError(null);\n deleteErrorTimer.current = null;\n }, 30000);\n }\n };\n\n const handleOpenDelete = useCallback(\n (id: string) => {\n if (!canDeleteAudit) return;\n clearDeleteError();\n clearDeleteSuccess();\n setDeleteTargetIds([id]);\n },\n [canDeleteAudit, clearDeleteError, clearDeleteSuccess]\n );\n\n const handleToggleLogSelection = useCallback((id: string, checked: boolean) => {\n setSelectedLogIds((previous) => {\n const next = new Set(previous);\n if (checked) {\n next.add(id);\n } else {\n next.delete(id);\n }\n return next;\n });\n }, []);\n\n const handleToggleAllVisible = useCallback(\n (checked: boolean) => {\n setSelectedLogIds((previous) => {\n const next = new Set(previous);\n visibleLogIds.forEach((id: string) => {\n if (checked) {\n next.add(id);\n } else {\n next.delete(id);\n }\n });\n return next;\n });\n },\n [visibleLogIds]\n );\n\n const handleOpenBulkDelete = useCallback(() => {\n if (!canDeleteAudit || selectedLogIds.size === 0) return;\n clearDeleteError();\n clearDeleteSuccess();\n setDeleteTargetIds(Array.from(selectedLogIds));\n }, [canDeleteAudit, clearDeleteError, clearDeleteSuccess, selectedLogIds]);\n\n if (permissionsLoading) {\n return (\n <div className=\"mx-auto max-w-[1400px] p-4 sm:p-6\">\n <LoadingState message={tr('audit.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (permissions && permissions.permissionStrings?.length > 0 && !canListAudit) {\n return (\n <AccessDenied\n title={tr('common.accessDeniedTitle', 'Access Denied')}\n description={tr(\n 'common.accessDeniedDescription',\n \"You don't have permission to access this resource.\"\n )}\n />\n );\n }\n\n return (\n <div className=\"mx-auto max-w-[1400px] p-4 sm:p-6\">\n <PageHeader\n title={tr('nav.vibeAudit', 'Audit')}\n description={tr(\n 'audit.description',\n 'Comprehensive audit trail of all VibeControls operations'\n )}\n actions={canExportAudit ? <AuditExportButton filter={filter} /> : undefined}\n />\n\n <AuditLogStats stats={data?.vibeAuditLogStats} loading={loading && !data} />\n\n <AuditLogFilters\n searchQuery={searchQuery}\n onSearchChange={setSearchQuery}\n actionFilter={actionFilter}\n onActionChange={(value) => {\n setActionFilter(value);\n setPage(0);\n }}\n resourceTypeFilter={resourceTypeFilter}\n onResourceTypeChange={(value) => {\n setResourceTypeFilter(value);\n setPage(0);\n }}\n statusFilter={statusFilter}\n onStatusChange={(value) => {\n setStatusFilter(value);\n setPage(0);\n }}\n hasCommandFilter={hasCommandFilter}\n onHasCommandChange={(value) => {\n setHasCommandFilter(value);\n setPage(0);\n }}\n />\n\n {items.length > 0 ? (\n <div className=\"rounded-lg border border-border-primary bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <span className=\"text-sm font-medium text-text-primary\">\n {selectedLogIds.size} selected\n </span>\n {selectedLogIds.size > 0 ? (\n <button\n type=\"button\"\n onClick={() => setSelectedLogIds(new Set())}\n className=\"text-sm text-text-secondary transition-colors hover:text-text-primary\"\n >\n Clear selection\n </button>\n ) : null}\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={handleOpenBulkDelete}\n disabled={!canDeleteAudit || selectedLogIds.size === 0}\n className=\"rounded-md border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n Delete Selected{selectedLogIds.size > 0 ? ` (${selectedLogIds.size})` : ''}\n </button>\n </div>\n </div>\n </div>\n ) : null}\n\n {loading && !data ? (\n <LoadingState message={tr('audit.loadingLogs', 'Loading audit logs...')} />\n ) : error ? (\n <ErrorState message={error.message} onRetry={() => refetch()} />\n ) : totalAuditItems === 0 ? (\n <EmptyState\n icon={<Shield size={48} className=\"text-text-secondary\" />}\n title={tr('audit.noLogs', 'No audit logs')}\n description={tr(\n 'audit.emptyDescription',\n 'Audit logs will appear here as operations are performed in VibeControls.'\n )}\n action={\n <GetHelpButton prompt=\"What is the Audit Log in VibeControls and how do I use it?\" />\n }\n />\n ) : (\n <AuditLogTable\n items={items}\n total={totalAuditItems}\n hasMore={data?.vibeAuditLogs.hasMore ?? false}\n page={page}\n pageSize={PAGE_SIZE}\n onPageChange={setPage}\n onSelectLog={setSelectedLogId}\n onDeleteLog={handleOpenDelete}\n selectedLogId={selectedLogId}\n selectedLogIds={selectedLogIds}\n allVisibleSelected={allVisibleSelected}\n partiallySelected={partiallySelected}\n onToggleLogSelection={handleToggleLogSelection}\n onToggleAllVisible={handleToggleAllVisible}\n />\n )}\n\n {selectedLogId && detailData?.vibeAuditLog ? (\n <AuditLogDetail log={detailData.vibeAuditLog} onClose={() => setSelectedLogId(null)} />\n ) : null}\n\n {deleteError ? (\n <div\n role=\"alert\"\n className=\"fixed bottom-4 right-4 z-50 rounded-md bg-status-error-bg px-4 py-3 text-sm text-status-error-text shadow-lg\"\n >\n {deleteError}\n <button\n type=\"button\"\n onClick={clearDeleteError}\n className=\"ml-3 text-xs underline opacity-80 hover:opacity-100\"\n >\n {tr('common.dismiss', 'Dismiss')}\n </button>\n </div>\n ) : null}\n\n {deleteSuccess ? (\n <div\n role=\"status\"\n className=\"fixed bottom-4 right-4 z-50 rounded-md bg-status-success-bg px-4 py-3 text-sm text-status-success-text shadow-lg\"\n >\n {deleteSuccess}\n <button\n type=\"button\"\n onClick={clearDeleteSuccess}\n className=\"ml-3 text-xs underline opacity-80 hover:opacity-100\"\n >\n {tr('common.dismiss', 'Dismiss')}\n </button>\n </div>\n ) : null}\n\n <AuditDeleteDialog\n isOpen={deleteTargetIds.length > 0}\n onClose={() => setDeleteTargetIds([])}\n onConfirm={handleDelete}\n loading={deleteLoading}\n title={deleteTargetIds.length > 1 ? 'Delete Audit Logs' : 'Delete Audit Log'}\n description={\n deleteTargetIds.length > 1\n ? `This will permanently delete ${deleteTargetIds.length} audit logs and record the reason in the audit trail.`\n : 'This action will be recorded in the audit trail.'\n }\n confirmLabel={deleteTargetIds.length > 1 ? 'Delete Logs' : 'Delete'}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VibeAuditPage.js","names":[],"sources":["../../src/pages/VibeAuditPage.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\nimport { Shield } from 'lucide-react';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { isNative, nativeNotify } from '../utils/nativeBridge';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n AuditDeleteDialog,\n AuditExportButton,\n AuditLogDetail,\n AuditLogFilters,\n AuditLogStats,\n AuditLogTable,\n} from '@/components/audit';\nimport {\n EmptyState,\n ErrorState,\n GetHelpButton,\n LoadingState,\n PageHeader,\n} from '@/components/shared';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport {\n useBulkDeleteVibeAuditLogsMutation,\n useDeleteVibeAuditLogMutation,\n useGetVibeAuditLogQuery,\n useListAgentsQuery,\n useListVibesQuery,\n useVibeAuditDashboardQuery,\n} from '@/generated/wspace-operations';\nimport type {\n VibeAuditAction,\n VibeAuditLogFilterInput,\n VibeAuditResourceType,\n VibeAuditStatus,\n} from '@/generated/wspace-types';\nimport { useVibeControls } from '@/providers/VibeControlsProvider';\n\nconst PAGE_SIZE = 50;\n\nexport function VibeAuditPage() {\n useVibeControls();\n\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n\n const [searchQuery, setSearchQuery] = useState('');\n const [debouncedSearchQuery, setDebouncedSearchQuery] = useState('');\n const [actionFilter, setActionFilter] = useState<string | null>('all');\n const [resourceTypeFilter, setResourceTypeFilter] = useState<string | null>('all');\n const [statusFilter, setStatusFilter] = useState<string | null>('all');\n const [agentFilter, setAgentFilter] = useState<string | null>('all');\n const [vibeFilter, setVibeFilter] = useState<string | null>('all');\n const [hasCommandFilter, setHasCommandFilter] = useState(false);\n const [page, setPage] = useState(0);\n const [selectedLogId, setSelectedLogId] = useState<string | null>(null);\n const [selectedLogIds, setSelectedLogIds] = useState<Set<string>>(new Set());\n const [deleteTargetIds, setDeleteTargetIds] = useState<string[]>([]);\n const [deleteError, setDeleteError] = useState<string | null>(null);\n const [deleteSuccess, setDeleteSuccess] = useState<string | null>(null);\n\n const deleteErrorTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n const canListAudit = hasPermission(VIBE_PERMISSIONS.AUDIT_LIST);\n const canDeleteAudit = hasPermission(VIBE_PERMISSIONS.AUDIT_DELETE);\n const canExportAudit = hasPermission(VIBE_PERMISSIONS.AUDIT_EXPORT);\n const shouldSkipQueries = permissionsLoading || !canListAudit;\n\n const filter: VibeAuditLogFilterInput = {};\n if (debouncedSearchQuery) filter.search = debouncedSearchQuery;\n if (actionFilter && actionFilter !== 'all') filter.action = actionFilter as VibeAuditAction;\n if (resourceTypeFilter && resourceTypeFilter !== 'all')\n filter.resourceType = resourceTypeFilter as VibeAuditResourceType;\n if (statusFilter && statusFilter !== 'all') filter.status = statusFilter as VibeAuditStatus;\n if (hasCommandFilter) filter.hasCommand = true;\n // Agent / Vibe filters: backend exposes `resourceId` + `resourceType`.\n // We narrow `resourceType` accordingly so the server returns only audit\n // rows whose target is the chosen agent / vibe. If the user already\n // chose a resourceType that conflicts (e.g. SESSION), the resourceId\n // filter would over-narrow — but that combination is documented as\n // intentional power-user behaviour.\n if (agentFilter && agentFilter !== 'all') {\n filter.resourceId = agentFilter;\n if (!filter.resourceType) filter.resourceType = 'AGENT' as VibeAuditResourceType;\n } else if (vibeFilter && vibeFilter !== 'all') {\n filter.resourceId = vibeFilter;\n if (!filter.resourceType) filter.resourceType = 'VIBE' as VibeAuditResourceType;\n }\n\n const { data, loading, error, refetch } = useVibeAuditDashboardQuery({\n variables: {\n filter,\n pagination: { limit: Math.min(Math.max((page + 1) * PAGE_SIZE, PAGE_SIZE), 100) },\n },\n skip: shouldSkipQueries,\n fetchPolicy: 'cache-and-network',\n notifyOnNetworkStatusChange: true,\n });\n\n const { data: agentsData } = useListAgentsQuery({\n variables: { pagination: { limit: 200 } },\n fetchPolicy: 'cache-first',\n skip: shouldSkipQueries,\n });\n const agentsList = (agentsData?.vibecontrolsAgents ?? []).map(\n (a: { id: string; name: string }) => ({ id: a.id, name: a.name })\n );\n\n const { data: vibesData } = useListVibesQuery({\n variables: { pagination: { limit: 200 } },\n fetchPolicy: 'cache-first',\n skip: shouldSkipQueries,\n });\n const vibesList = (vibesData?.vibes?.items ?? []).map((v: { id: string; name: string }) => ({\n id: v.id,\n name: v.name,\n }));\n\n const retentionDays = data?.vibeAuditLogStats?.retentionPolicy?.retentionDays ?? 90;\n const nextCleanupAt = data?.vibeAuditLogStats?.retentionPolicy?.nextCleanup ?? null;\n\n const { data: detailData } = useGetVibeAuditLogQuery({\n variables: { id: selectedLogId! },\n skip: !selectedLogId,\n });\n\n const [deleteAuditLog, { loading: deleteLoading }] = useDeleteVibeAuditLogMutation();\n const [bulkDeleteAuditLogs] = useBulkDeleteVibeAuditLogsMutation();\n\n useEffect(() => {\n const timeoutId = window.setTimeout(() => {\n setDebouncedSearchQuery(searchQuery);\n setPage(0);\n }, 300);\n\n return () => window.clearTimeout(timeoutId);\n }, [searchQuery]);\n\n useEffect(() => {\n return () => {\n if (deleteErrorTimer.current) {\n clearTimeout(deleteErrorTimer.current);\n }\n };\n }, []);\n\n const clearDeleteError = useCallback(() => {\n setDeleteError(null);\n if (deleteErrorTimer.current) {\n clearTimeout(deleteErrorTimer.current);\n deleteErrorTimer.current = null;\n }\n }, []);\n\n const clearDeleteSuccess = useCallback(() => {\n setDeleteSuccess(null);\n }, []);\n\n const allItems = data?.vibeAuditLogs.items ?? [];\n const items = allItems.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE);\n const totalAuditItems = data?.vibeAuditLogs.total ?? 0;\n const visibleLogIds = items.map((item: (typeof items)[number]) => item.id);\n const allVisibleSelected =\n visibleLogIds.length > 0 && visibleLogIds.every((id: string) => selectedLogIds.has(id));\n const partiallySelected =\n visibleLogIds.some((id: string) => selectedLogIds.has(id)) && !allVisibleSelected;\n\n const handleDelete = async (reason: string) => {\n if (deleteTargetIds.length === 0) return;\n\n clearDeleteError();\n clearDeleteSuccess();\n\n try {\n if (deleteTargetIds.length === 1) {\n await deleteAuditLog({ variables: { id: deleteTargetIds[0], input: { reason } } });\n setDeleteSuccess('Deleted 1 audit log');\n } else {\n const result = await bulkDeleteAuditLogs({\n variables: { ids: deleteTargetIds, input: { reason } },\n });\n const deletedCount = result.data?.bulkDeleteVibeAuditLogs.deletedCount ?? 0;\n const failedCount = result.data?.bulkDeleteVibeAuditLogs.failedCount ?? 0;\n setDeleteSuccess(\n `Deleted ${deletedCount} audit logs${failedCount > 0 ? ` • ${failedCount} failed` : ''}`\n );\n }\n\n setDeleteTargetIds([]);\n setSelectedLogIds((previous) => {\n const next = new Set(previous);\n deleteTargetIds.forEach((id) => next.delete(id));\n return next;\n });\n if (selectedLogId && deleteTargetIds.includes(selectedLogId)) {\n setSelectedLogId(null);\n }\n await refetch();\n if (isNative()) void nativeNotify('success');\n } catch (deleteAuditError) {\n console.error('Failed to delete audit log:', deleteAuditError);\n setDeleteError(tr('audit.deleteFailed', 'Failed to delete audit log. Please try again.'));\n if (isNative()) void nativeNotify('error');\n deleteErrorTimer.current = setTimeout(() => {\n setDeleteError(null);\n deleteErrorTimer.current = null;\n }, 30000);\n }\n };\n\n const handleOpenDelete = useCallback(\n (id: string) => {\n if (!canDeleteAudit) return;\n clearDeleteError();\n clearDeleteSuccess();\n setDeleteTargetIds([id]);\n },\n [canDeleteAudit, clearDeleteError, clearDeleteSuccess]\n );\n\n const handleToggleLogSelection = useCallback((id: string, checked: boolean) => {\n setSelectedLogIds((previous) => {\n const next = new Set(previous);\n if (checked) {\n next.add(id);\n } else {\n next.delete(id);\n }\n return next;\n });\n }, []);\n\n const handleToggleAllVisible = useCallback(\n (checked: boolean) => {\n setSelectedLogIds((previous) => {\n const next = new Set(previous);\n visibleLogIds.forEach((id: string) => {\n if (checked) {\n next.add(id);\n } else {\n next.delete(id);\n }\n });\n return next;\n });\n },\n [visibleLogIds]\n );\n\n const handleOpenBulkDelete = useCallback(() => {\n if (!canDeleteAudit || selectedLogIds.size === 0) return;\n clearDeleteError();\n clearDeleteSuccess();\n setDeleteTargetIds(Array.from(selectedLogIds));\n }, [canDeleteAudit, clearDeleteError, clearDeleteSuccess, selectedLogIds]);\n\n if (permissionsLoading) {\n return (\n <div className=\"mx-auto max-w-[1400px] p-4 sm:p-6\">\n <LoadingState message={tr('audit.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (permissions && permissions.permissionStrings?.length > 0 && !canListAudit) {\n return (\n <AccessDenied\n title={tr('common.accessDeniedTitle', 'Access Denied')}\n description={tr(\n 'common.accessDeniedDescription',\n \"You don't have permission to access this resource.\"\n )}\n />\n );\n }\n\n return (\n <div className=\"mx-auto max-w-[1400px] p-4 sm:p-6\">\n <PageHeader\n title={tr('nav.vibeAudit', 'Audit')}\n description={tr(\n 'audit.description',\n 'Comprehensive audit trail of all VibeControls operations'\n )}\n actions={canExportAudit ? <AuditExportButton filter={filter} /> : undefined}\n />\n\n {/* Retention banner — sourced from the backend `retentionPolicy`\n so the displayed window updates if the tier changes. */}\n <div className=\"mb-4 flex items-start gap-2 rounded-md border border-status-info-border bg-status-info-bg/10 px-3 py-2 text-xs text-status-info-text\">\n <Shield size={14} className=\"mt-0.5 flex-shrink-0\" />\n <span>\n {tr(\n 'audit.retentionBanner',\n `Audit logs are kept for ${retentionDays} days. Older entries are automatically purged.`\n )}\n {nextCleanupAt\n ? ` ${tr('audit.retentionNextCleanup', `Next cleanup: ${new Date(nextCleanupAt).toLocaleString()}.`)}`\n : ''}\n </span>\n </div>\n\n <AuditLogStats stats={data?.vibeAuditLogStats} loading={loading && !data} />\n\n <AuditLogFilters\n searchQuery={searchQuery}\n onSearchChange={setSearchQuery}\n actionFilter={actionFilter}\n onActionChange={(value) => {\n setActionFilter(value);\n setPage(0);\n }}\n resourceTypeFilter={resourceTypeFilter}\n onResourceTypeChange={(value) => {\n setResourceTypeFilter(value);\n setPage(0);\n }}\n statusFilter={statusFilter}\n onStatusChange={(value) => {\n setStatusFilter(value);\n setPage(0);\n }}\n hasCommandFilter={hasCommandFilter}\n onHasCommandChange={(value) => {\n setHasCommandFilter(value);\n setPage(0);\n }}\n agentFilter={agentFilter}\n onAgentChange={(value) => {\n setAgentFilter(value);\n // Agent + Vibe are mutually exclusive via resourceId — clearing one when\n // the other is set keeps the resolved filter input coherent.\n if (value !== 'all') setVibeFilter('all');\n setPage(0);\n }}\n agents={agentsList}\n vibeFilter={vibeFilter}\n onVibeChange={(value) => {\n setVibeFilter(value);\n if (value !== 'all') setAgentFilter('all');\n setPage(0);\n }}\n vibes={vibesList}\n />\n\n {items.length > 0 ? (\n <div className=\"rounded-lg border border-border-primary bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <span className=\"text-sm font-medium text-text-primary\">\n {selectedLogIds.size} selected\n </span>\n {selectedLogIds.size > 0 ? (\n <button\n type=\"button\"\n onClick={() => setSelectedLogIds(new Set())}\n className=\"text-sm text-text-secondary transition-colors hover:text-text-primary\"\n >\n Clear selection\n </button>\n ) : null}\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={handleOpenBulkDelete}\n disabled={!canDeleteAudit || selectedLogIds.size === 0}\n className=\"rounded-md border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n Delete Selected{selectedLogIds.size > 0 ? ` (${selectedLogIds.size})` : ''}\n </button>\n </div>\n </div>\n </div>\n ) : null}\n\n {loading && !data ? (\n <LoadingState message={tr('audit.loadingLogs', 'Loading audit logs...')} />\n ) : error ? (\n <ErrorState message={error.message} onRetry={() => refetch()} />\n ) : totalAuditItems === 0 ? (\n <EmptyState\n icon={<Shield size={48} className=\"text-text-secondary\" />}\n title={tr('audit.noLogs', 'No audit logs')}\n description={tr(\n 'audit.emptyDescription',\n 'Audit logs will appear here as operations are performed in VibeControls.'\n )}\n action={\n <GetHelpButton prompt=\"What is the Audit Log in VibeControls and how do I use it?\" />\n }\n />\n ) : (\n <AuditLogTable\n items={items}\n total={totalAuditItems}\n hasMore={data?.vibeAuditLogs.hasMore ?? false}\n page={page}\n pageSize={PAGE_SIZE}\n onPageChange={setPage}\n onSelectLog={setSelectedLogId}\n onDeleteLog={handleOpenDelete}\n selectedLogId={selectedLogId}\n selectedLogIds={selectedLogIds}\n allVisibleSelected={allVisibleSelected}\n partiallySelected={partiallySelected}\n onToggleLogSelection={handleToggleLogSelection}\n onToggleAllVisible={handleToggleAllVisible}\n />\n )}\n\n {selectedLogId && detailData?.vibeAuditLog ? (\n <AuditLogDetail log={detailData.vibeAuditLog} onClose={() => setSelectedLogId(null)} />\n ) : null}\n\n {deleteError ? (\n <div\n role=\"alert\"\n className=\"fixed bottom-4 right-4 z-50 rounded-md bg-status-error-bg px-4 py-3 text-sm text-status-error-text shadow-lg\"\n >\n {deleteError}\n <button\n type=\"button\"\n onClick={clearDeleteError}\n className=\"ml-3 text-xs underline opacity-80 hover:opacity-100\"\n >\n {tr('common.dismiss', 'Dismiss')}\n </button>\n </div>\n ) : null}\n\n {deleteSuccess ? (\n <div\n role=\"status\"\n className=\"fixed bottom-4 right-4 z-50 rounded-md bg-status-success-bg px-4 py-3 text-sm text-status-success-text shadow-lg\"\n >\n {deleteSuccess}\n <button\n type=\"button\"\n onClick={clearDeleteSuccess}\n className=\"ml-3 text-xs underline opacity-80 hover:opacity-100\"\n >\n {tr('common.dismiss', 'Dismiss')}\n </button>\n </div>\n ) : null}\n\n <AuditDeleteDialog\n isOpen={deleteTargetIds.length > 0}\n onClose={() => setDeleteTargetIds([])}\n onConfirm={handleDelete}\n loading={deleteLoading}\n title={deleteTargetIds.length > 1 ? 'Delete Audit Logs' : 'Delete Audit Log'}\n description={\n deleteTargetIds.length > 1\n ? `This will permanently delete ${deleteTargetIds.length} audit logs and record the reason in the audit trail.`\n : 'This action will be recorded in the audit trail.'\n }\n confirmLabel={deleteTargetIds.length > 1 ? 'Delete Logs' : 'Delete'}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|