@burdenoff/microfe-vibecontrols 2026.608.3 → 2026.608.4

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.
@@ -6,16 +6,16 @@ import { ErrorState as ie } from "../components/shared/ErrorState.js";
6
6
  import { EmptyState as ae } from "../components/shared/EmptyState.js";
7
7
  import { GetHelpButton as oe } from "../components/shared/GetHelpButton.js";
8
8
  import "../components/shared/index.js";
9
- import { isNative as se, nativeNotify as a } from "../utils/nativeBridge.js";
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";
9
+ import { isNative as a, nativeNotify as o } from "../utils/nativeBridge.js";
10
+ import { VIBE_PERMISSIONS as s } from "../constants/permissions.js";
11
+ import { AuditLogTable as se } from "../components/audit/AuditLogTable.js";
12
+ import { AuditLogFilters as ce } from "../components/audit/AuditLogFilters.js";
13
+ import { AuditLogDetail as le } from "../components/audit/AuditLogDetail.js";
14
+ import { AuditLogStats as ue } from "../components/audit/AuditLogStats.js";
15
+ import { AuditDeleteDialog as de } from "../components/audit/AuditDeleteDialog.js";
16
+ import { AuditExportButton as fe } from "../components/audit/AuditExportButton.js";
17
17
  import "../components/audit/index.js";
18
- import { useCallback as s, useEffect as c, useRef as me, useState as l } from "react";
18
+ import { useCallback as c, useEffect as pe, useRef as me, useState as l } from "react";
19
19
  import { useI18n as he } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
20
20
  import { Shield as u } from "lucide-react";
21
21
  import { usePermissions as ge } from "@burdenoff/fe-libs/shared/providers/shell";
@@ -25,129 +25,150 @@ import { AccessDenied as _e } from "@burdenoff/fe-libs/shared/components";
25
25
  var p = 50;
26
26
  function m() {
27
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) => {
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, we] = l(() => typeof window > "u" ? null : new URLSearchParams(window.location.search).get("sessionId")), [j, M] = l(null), [N, P] = l(/* @__PURE__ */ new Set()), [F, I] = l([]), [L, R] = l(null), [z, B] = l(null), V = me(null), H = c((e, t) => {
29
29
  let n = m(e);
30
30
  return n === e ? t : n;
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({
31
+ }, [m]), U = h(s.AUDIT_LIST), W = h(s.AUDIT_DELETE), Te = h(s.AUDIT_EXPORT), G = g || !U, K = {};
32
+ y && (K.search = y), b && b !== "all" && (K.action = b), x && x !== "all" && (K.resourceType = x), S && S !== "all" && (K.status = S), D && (K.hasCommand = !0), C && C !== "all" ? (K.resourceId = C, K.resourceType ||= "AGENT") : T && T !== "all" && (K.resourceId = T, K.resourceType ||= "VIBE"), A && (K.sessionId = A);
33
+ let { data: q, loading: Ee, error: De, refetch: Oe } = te({
34
34
  variables: {
35
- filter: U,
35
+ filter: K,
36
36
  pagination: { limit: Math.min(Math.max((O + 1) * p, p), 100) }
37
37
  },
38
- skip: H,
38
+ skip: G,
39
39
  fetchPolicy: "cache-and-network",
40
40
  notifyOnNetworkStatusChange: !0
41
- }), { data: De } = r({
41
+ }), { data: ke } = r({
42
42
  variables: { pagination: { limit: 200 } },
43
43
  fetchPolicy: "cache-first",
44
- skip: H
45
- }), Oe = (De?.vibecontrolsAgents ?? []).map((e) => ({
44
+ skip: G
45
+ }), Ae = (ke?.vibecontrolsAgents ?? []).map((e) => ({
46
46
  id: e.id,
47
47
  name: e.name
48
- })), { data: ke } = ee({
48
+ })), { data: je } = ee({
49
49
  variables: { pagination: { limit: 200 } },
50
50
  fetchPolicy: "cache-first",
51
- skip: H
52
- }), Ae = (ke?.vibes?.items ?? []).map((e) => ({
51
+ skip: G
52
+ }), Me = (je?.vibes?.items ?? []).map((e) => ({
53
53
  id: e.id,
54
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(() => {
55
+ })), Ne = q?.vibeAuditLogStats?.retentionPolicy?.retentionDays ?? 90, Pe = q?.vibeAuditLogStats?.retentionPolicy?.nextCleanup ?? null, { data: J } = n({
56
+ variables: { id: j },
57
+ skip: !j
58
+ }), [Fe, { loading: Ie }] = t(), [Le] = e();
59
+ pe(() => {
60
60
  let e = window.setTimeout(() => {
61
61
  ye(v), k(0);
62
62
  }, 300);
63
63
  return () => window.clearTimeout(e);
64
- }, [v]), c(() => () => {
65
- R.current && clearTimeout(R.current);
64
+ }, [v]), pe(() => () => {
65
+ V.current && clearTimeout(V.current);
66
66
  }, []);
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();
67
+ let Y = c(() => {
68
+ R(null), V.current &&= (clearTimeout(V.current), null);
69
+ }, []), X = c(() => {
70
+ B(null);
71
+ }, []), Z = (q?.vibeAuditLogs.items ?? []).slice(O * p, (O + 1) * p), Re = q?.vibeAuditLogs.total ?? 0, Q = Z.map((e) => e.id), $ = Q.length > 0 && Q.every((e) => N.has(e)), ze = Q.some((e) => N.has(e)) && !$, Be = async (e) => {
72
+ if (F.length !== 0) {
73
+ Y(), X();
74
74
  try {
75
- if (P.length === 1) await Me({ variables: {
76
- id: P[0],
75
+ if (F.length === 1) await Fe({ variables: {
76
+ id: F[0],
77
77
  input: { reason: e }
78
- } }), L("Deleted 1 audit log");
78
+ } }), B("Deleted 1 audit log");
79
79
  else {
80
- let t = await Pe({ variables: {
81
- ids: P,
80
+ let t = await Le({ variables: {
81
+ ids: F,
82
82
  input: { reason: e }
83
83
  } }), n = t.data?.bulkDeleteVibeAuditLogs.deletedCount ?? 0, r = t.data?.bulkDeleteVibeAuditLogs.failedCount ?? 0;
84
- L(`Deleted ${n} audit logs${r > 0 ? ` • ${r} failed` : ""}`);
84
+ B(`Deleted ${n} audit logs${r > 0 ? ` • ${r} failed` : ""}`);
85
85
  }
86
- F([]), N((e) => {
86
+ I([]), P((e) => {
87
87
  let t = new Set(e);
88
- return P.forEach((e) => t.delete(e)), t;
89
- }), A && P.includes(A) && j(null), await q(), se() && a("success");
88
+ return F.forEach((e) => t.delete(e)), t;
89
+ }), j && F.includes(j) && M(null), await Oe(), a() && o("success");
90
90
  } catch (e) {
91
- console.error("Failed to delete audit log:", e), I(z("audit.deleteFailed", "Failed to delete audit log. Please try again.")), se() && a("error"), R.current = setTimeout(() => {
92
- I(null), R.current = null;
91
+ console.error("Failed to delete audit log:", e), R(H("audit.deleteFailed", "Failed to delete audit log. Please try again.")), a() && o("error"), V.current = setTimeout(() => {
92
+ R(null), V.current = null;
93
93
  }, 3e4);
94
94
  }
95
95
  }
96
- }, ze = s((e) => {
97
- V && (X(), Z(), F([e]));
96
+ }, Ve = c((e) => {
97
+ W && (Y(), X(), I([e]));
98
98
  }, [
99
- V,
100
- X,
101
- Z
102
- ]), Be = s((e, t) => {
103
- N((n) => {
99
+ W,
100
+ Y,
101
+ X
102
+ ]), He = c((e, t) => {
103
+ P((n) => {
104
104
  let r = new Set(n);
105
105
  return t ? r.add(e) : r.delete(e), r;
106
106
  });
107
- }, []), Ve = s((e) => {
108
- N((t) => {
107
+ }, []), Ue = c((e) => {
108
+ P((t) => {
109
109
  let n = new Set(t);
110
- return $.forEach((t) => {
110
+ return Q.forEach((t) => {
111
111
  e ? n.add(t) : n.delete(t);
112
112
  }), n;
113
113
  });
114
- }, [$]), He = s(() => {
115
- !V || M.size === 0 || (X(), Z(), F(Array.from(M)));
114
+ }, [Q]), We = c(() => {
115
+ !W || N.size === 0 || (Y(), X(), I(Array.from(N)));
116
116
  }, [
117
- V,
117
+ W,
118
+ Y,
118
119
  X,
119
- Z,
120
- M
120
+ N
121
121
  ]);
122
122
  return g ? /* @__PURE__ */ d("div", {
123
123
  className: "mx-auto max-w-[1400px] p-4 sm:p-6",
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.")
124
+ children: /* @__PURE__ */ d(i, { message: H("audit.loadingPermissions", "Loading permissions...") })
125
+ }) : _ && _.permissionStrings?.length > 0 && !U ? /* @__PURE__ */ d(_e, {
126
+ title: H("common.accessDeniedTitle", "Access Denied"),
127
+ description: H("common.accessDeniedDescription", "You don't have permission to access this resource.")
128
128
  }) : /* @__PURE__ */ f("div", {
129
129
  className: "mx-auto max-w-[1400px] p-4 sm:p-6",
130
130
  children: [
131
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
132
+ title: H("nav.vibeAudit", "Audit"),
133
+ description: H("audit.description", "Comprehensive audit trail of all VibeControls operations"),
134
+ actions: Te ? /* @__PURE__ */ d(fe, { filter: K }) : void 0
135
135
  }),
136
136
  /* @__PURE__ */ f("div", {
137
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
138
  children: [/* @__PURE__ */ d(u, {
139
139
  size: 14,
140
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()}.`)}` : ""] })]
141
+ }), /* @__PURE__ */ f("span", { children: [H("audit.retentionBanner", `Audit logs are kept for ${Ne} days. Older entries are automatically purged.`), Pe ? ` ${H("audit.retentionNextCleanup", `Next cleanup: ${new Date(Pe).toLocaleString()}.`)}` : ""] })]
142
142
  }),
143
+ A ? /* @__PURE__ */ f("div", {
144
+ className: "mb-4 flex items-center justify-between gap-2 rounded-md border border-action-primary-border bg-action-primary-bg/10 px-3 py-2 text-xs text-text-primary",
145
+ children: [/* @__PURE__ */ f("span", {
146
+ className: "truncate",
147
+ children: [
148
+ H("audit.sessionScopeBanner", "Showing activity for session"),
149
+ " ",
150
+ /* @__PURE__ */ d("span", {
151
+ className: "font-mono",
152
+ children: A
153
+ })
154
+ ]
155
+ }), /* @__PURE__ */ d("button", {
156
+ type: "button",
157
+ onClick: () => {
158
+ we(null), k(0);
159
+ },
160
+ className: "flex-shrink-0 rounded bg-bg-sunken px-2 py-1 font-medium text-text-secondary hover:text-text-primary",
161
+ children: H("audit.clearSessionScope", "Clear")
162
+ })]
163
+ }) : null,
143
164
  /* @__PURE__ */ d("div", {
144
165
  "data-tour": "audit-log-stats",
145
- children: /* @__PURE__ */ d(de, {
146
- stats: W?.vibeAuditLogStats,
147
- loading: G && !W
166
+ children: /* @__PURE__ */ d(ue, {
167
+ stats: q?.vibeAuditLogStats,
168
+ loading: Ee && !q
148
169
  })
149
170
  }),
150
- /* @__PURE__ */ d(le, {
171
+ /* @__PURE__ */ d(ce, {
151
172
  searchQuery: v,
152
173
  onSearchChange: ve,
153
174
  actionFilter: b,
@@ -170,14 +191,14 @@ function m() {
170
191
  onAgentChange: (e) => {
171
192
  w(e), e !== "all" && E("all"), k(0);
172
193
  },
173
- agents: Oe,
194
+ agents: Ae,
174
195
  vibeFilter: T,
175
196
  onVibeChange: (e) => {
176
197
  E(e), e !== "all" && w("all"), k(0);
177
198
  },
178
- vibes: Ae
199
+ vibes: Me
179
200
  }),
180
- Q.length > 0 ? /* @__PURE__ */ d("div", {
201
+ Z.length > 0 ? /* @__PURE__ */ d("div", {
181
202
  className: "rounded-lg border border-border-primary bg-bg-surface p-4",
182
203
  children: /* @__PURE__ */ f("div", {
183
204
  className: "flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between",
@@ -185,10 +206,10 @@ function m() {
185
206
  className: "flex flex-wrap items-center gap-3",
186
207
  children: [/* @__PURE__ */ f("span", {
187
208
  className: "text-sm font-medium text-text-primary",
188
- children: [M.size, " selected"]
189
- }), M.size > 0 ? /* @__PURE__ */ d("button", {
209
+ children: [N.size, " selected"]
210
+ }), N.size > 0 ? /* @__PURE__ */ d("button", {
190
211
  type: "button",
191
- onClick: () => N(/* @__PURE__ */ new Set()),
212
+ onClick: () => P(/* @__PURE__ */ new Set()),
192
213
  className: "text-sm text-text-secondary transition-colors hover:text-text-primary",
193
214
  children: "Clear selection"
194
215
  }) : null]
@@ -196,76 +217,84 @@ function m() {
196
217
  className: "flex flex-wrap items-center gap-2",
197
218
  children: /* @__PURE__ */ f("button", {
198
219
  type: "button",
199
- onClick: He,
200
- disabled: !V || M.size === 0,
220
+ onClick: We,
221
+ disabled: !W || N.size === 0,
201
222
  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",
202
- children: ["Delete Selected", M.size > 0 ? ` (${M.size})` : ""]
223
+ children: ["Delete Selected", N.size > 0 ? ` (${N.size})` : ""]
203
224
  })
204
225
  })]
205
226
  })
206
227
  }) : null,
207
- G && !W ? /* @__PURE__ */ d(i, { message: z("audit.loadingLogs", "Loading audit logs...") }) : K ? /* @__PURE__ */ d(ie, {
208
- message: K.message,
209
- onRetry: () => q()
210
- }) : Fe === 0 ? /* @__PURE__ */ d(ae, {
228
+ Ee && !q ? /* @__PURE__ */ d(i, { message: H("audit.loadingLogs", "Loading audit logs...") }) : De ? /* @__PURE__ */ d(ie, {
229
+ message: De.message,
230
+ onRetry: () => Oe()
231
+ }) : Re === 0 ? /* @__PURE__ */ d(ae, {
211
232
  icon: /* @__PURE__ */ d(u, {
212
233
  size: 48,
213
234
  className: "text-text-secondary"
214
235
  }),
215
- title: z("audit.noLogs", "No audit logs"),
216
- description: z("audit.emptyDescription", "Audit logs will appear here as operations are performed in VibeControls."),
236
+ title: H("audit.noLogs", "No audit logs"),
237
+ description: H("audit.emptyDescription", "Audit logs will appear here as operations are performed in VibeControls."),
217
238
  action: /* @__PURE__ */ d(oe, { prompt: "What is the Audit Log in VibeControls and how do I use it?" })
218
239
  }) : /* @__PURE__ */ d("div", {
219
240
  "data-tour": "audit-log-table",
220
- children: /* @__PURE__ */ d(ce, {
221
- items: Q,
222
- total: Fe,
223
- hasMore: W?.vibeAuditLogs.hasMore ?? !1,
241
+ children: /* @__PURE__ */ d(se, {
242
+ items: Z,
243
+ total: Re,
244
+ hasMore: q?.vibeAuditLogs.hasMore ?? !1,
224
245
  page: O,
225
246
  pageSize: p,
226
247
  onPageChange: k,
227
- onSelectLog: j,
228
- onDeleteLog: ze,
229
- selectedLogId: A,
230
- selectedLogIds: M,
231
- allVisibleSelected: Ie,
232
- partiallySelected: Le,
233
- onToggleLogSelection: Be,
234
- onToggleAllVisible: Ve
248
+ onSelectLog: M,
249
+ onDeleteLog: Ve,
250
+ selectedLogId: j,
251
+ selectedLogIds: N,
252
+ allVisibleSelected: $,
253
+ partiallySelected: ze,
254
+ onToggleLogSelection: He,
255
+ onToggleAllVisible: Ue
235
256
  })
236
257
  }),
237
- A && Y?.vibeAuditLog ? /* @__PURE__ */ d(ue, {
238
- log: Y.vibeAuditLog,
239
- onClose: () => j(null)
258
+ j && J?.vibeAuditLog ? /* @__PURE__ */ d(le, {
259
+ log: J.vibeAuditLog,
260
+ onClose: () => M(null),
261
+ footer: J.vibeAuditLog.resourceType === "SESSION" && J.vibeAuditLog.resourceId && J.vibeAuditLog.resourceId !== A ? /* @__PURE__ */ d("button", {
262
+ type: "button",
263
+ onClick: () => {
264
+ we(J.vibeAuditLog?.resourceId ?? null), M(null), k(0);
265
+ },
266
+ className: "w-full rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg-hover",
267
+ children: H("audit.showOnlyThisSession", "Show only this session's activity")
268
+ }) : null
240
269
  }) : null,
241
- we ? /* @__PURE__ */ f("div", {
270
+ L ? /* @__PURE__ */ f("div", {
242
271
  role: "alert",
243
272
  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",
244
- children: [we, /* @__PURE__ */ d("button", {
273
+ children: [L, /* @__PURE__ */ d("button", {
245
274
  type: "button",
246
- onClick: X,
275
+ onClick: Y,
247
276
  className: "ml-3 text-xs underline opacity-80 hover:opacity-100",
248
- children: z("common.dismiss", "Dismiss")
277
+ children: H("common.dismiss", "Dismiss")
249
278
  })]
250
279
  }) : null,
251
- Te ? /* @__PURE__ */ f("div", {
280
+ z ? /* @__PURE__ */ f("div", {
252
281
  role: "status",
253
282
  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",
254
- children: [Te, /* @__PURE__ */ d("button", {
283
+ children: [z, /* @__PURE__ */ d("button", {
255
284
  type: "button",
256
- onClick: Z,
285
+ onClick: X,
257
286
  className: "ml-3 text-xs underline opacity-80 hover:opacity-100",
258
- children: z("common.dismiss", "Dismiss")
287
+ children: H("common.dismiss", "Dismiss")
259
288
  })]
260
289
  }) : null,
261
- /* @__PURE__ */ d(fe, {
262
- isOpen: P.length > 0,
263
- onClose: () => F([]),
264
- onConfirm: Re,
265
- loading: Ne,
266
- title: P.length > 1 ? "Delete Audit Logs" : "Delete Audit Log",
267
- 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.",
268
- confirmLabel: P.length > 1 ? "Delete Logs" : "Delete"
290
+ /* @__PURE__ */ d(de, {
291
+ isOpen: F.length > 0,
292
+ onClose: () => I([]),
293
+ onConfirm: Be,
294
+ loading: Ie,
295
+ title: F.length > 1 ? "Delete Audit Logs" : "Delete Audit Log",
296
+ description: F.length > 1 ? `This will permanently delete ${F.length} audit logs and record the reason in the audit trail.` : "This action will be recorded in the audit trail.",
297
+ confirmLabel: F.length > 1 ? "Delete Logs" : "Delete"
269
298
  })
270
299
  ]
271
300
  });
@@ -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 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 is a custom component that doesn't forward rest props\n onto a DOM node, so the tour anchor lives on a wrapper <div>. */}\n <div data-tour=\"audit-log-stats\">\n <AuditLogStats stats={data?.vibeAuditLogStats} loading={loading && !data} />\n </div>\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 <div data-tour=\"audit-log-table\">\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 </div>\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":";;;;;;;;;;;;;;;;;;;;;;;;AAsCA,IAAM,IAAY;AAElB,SAAgB,IAAgB;AAC9B,KAAiB;CAEjB,IAAM,EAAE,SAAM,IAAS,EACjB,EAAE,kBAAe,WAAW,GAAoB,mBAAgB,IAAgB,EAEhF,CAAC,GAAa,MAAkB,EAAS,GAAG,EAC5C,CAAC,GAAsB,MAA2B,EAAS,GAAG,EAC9D,CAAC,GAAc,MAAmB,EAAwB,MAAM,EAChE,CAAC,GAAoB,MAAyB,EAAwB,MAAM,EAC5E,CAAC,GAAc,MAAmB,EAAwB,MAAM,EAChE,CAAC,GAAa,KAAkB,EAAwB,MAAM,EAC9D,CAAC,GAAY,KAAiB,EAAwB,MAAM,EAC5D,CAAC,GAAkB,MAAuB,EAAS,GAAM,EACzD,CAAC,GAAM,KAAW,EAAS,EAAE,EAC7B,CAAC,GAAe,KAAoB,EAAwB,KAAK,EACjE,CAAC,GAAgB,KAAqB,kBAAsB,IAAI,KAAK,CAAC,EACtE,CAAC,GAAiB,KAAsB,EAAmB,EAAE,CAAC,EAC9D,CAAC,IAAa,KAAkB,EAAwB,KAAK,EAC7D,CAAC,IAAe,KAAoB,EAAwB,KAAK,EAEjE,IAAmB,GAA6C,KAAK,EAErE,IAAK,GACR,GAAa,MAA6B;EACzC,IAAM,IAAQ,EAAE,EAAI;AACpB,SAAO,MAAU,IAAM,IAAW;IAEpC,CAAC,EAAE,CACJ,EAEK,IAAe,EAAc,EAAiB,WAAW,EACzD,IAAiB,EAAc,EAAiB,aAAa,EAC7D,KAAiB,EAAc,EAAiB,aAAa,EAC7D,IAAoB,KAAsB,CAAC,GAE3C,IAAkC,EAAE;AAa1C,CAZI,MAAsB,EAAO,SAAS,IACtC,KAAgB,MAAiB,UAAO,EAAO,SAAS,IACxD,KAAsB,MAAuB,UAC/C,EAAO,eAAe,IACpB,KAAgB,MAAiB,UAAO,EAAO,SAAS,IACxD,MAAkB,EAAO,aAAa,KAOtC,KAAe,MAAgB,SACjC,EAAO,aAAa,GACpB,AAA0B,EAAO,iBAAe,WACvC,KAAc,MAAe,UACtC,EAAO,aAAa,GACpB,AAA0B,EAAO,iBAAe;CAGlD,IAAM,EAAE,SAAM,YAAS,UAAO,eAAY,GAA2B;EACnE,WAAW;GACT;GACA,YAAY,EAAE,OAAO,KAAK,IAAI,KAAK,KAAK,IAAO,KAAK,GAAW,EAAU,EAAE,IAAI,EAAE;GAClF;EACD,MAAM;EACN,aAAa;EACb,6BAA6B;EAC9B,CAAC,EAEI,EAAE,MAAM,OAAe,EAAmB;EAC9C,WAAW,EAAE,YAAY,EAAE,OAAO,KAAK,EAAE;EACzC,aAAa;EACb,MAAM;EACP,CAAC,EACI,MAAc,IAAY,sBAAsB,EAAE,EAAE,KACvD,OAAqC;EAAE,IAAI,EAAE;EAAI,MAAM,EAAE;EAAM,EACjE,EAEK,EAAE,MAAM,OAAc,GAAkB;EAC5C,WAAW,EAAE,YAAY,EAAE,OAAO,KAAK,EAAE;EACzC,aAAa;EACb,MAAM;EACP,CAAC,EACI,MAAa,IAAW,OAAO,SAAS,EAAE,EAAE,KAAK,OAAqC;EAC1F,IAAI,EAAE;EACN,MAAM,EAAE;EACT,EAAE,EAEG,KAAgB,GAAM,mBAAmB,iBAAiB,iBAAiB,IAC3E,IAAgB,GAAM,mBAAmB,iBAAiB,eAAe,MAEzE,EAAE,MAAM,MAAe,EAAwB;EACnD,WAAW,EAAE,IAAI,GAAgB;EACjC,MAAM,CAAC;EACR,CAAC,EAEI,CAAC,IAAgB,EAAE,SAAS,QAAmB,GAA+B,EAC9E,CAAC,MAAuB,GAAoC;AAWlE,CATA,QAAgB;EACd,IAAM,IAAY,OAAO,iBAAiB;AAExC,GADA,GAAwB,EAAY,EACpC,EAAQ,EAAE;KACT,IAAI;AAEP,eAAa,OAAO,aAAa,EAAU;IAC1C,CAAC,EAAY,CAAC,EAEjB,cACe;AACX,EAAI,EAAiB,WACnB,aAAa,EAAiB,QAAQ;IAGzC,EAAE,CAAC;CAEN,IAAM,IAAmB,QAAkB;AAEzC,EADA,EAAe,KAAK,EACpB,AAEE,EAAiB,aADjB,aAAa,EAAiB,QAAQ,EACX;IAE5B,EAAE,CAAC,EAEA,IAAqB,QAAkB;AAC3C,IAAiB,KAAK;IACrB,EAAE,CAAC,EAGA,KADW,GAAM,cAAc,SAAS,EAAE,EACzB,MAAM,IAAO,IAAY,IAAO,KAAK,EAAU,EAChE,KAAkB,GAAM,cAAc,SAAS,GAC/C,IAAgB,EAAM,KAAK,MAAiC,EAAK,GAAG,EACpE,KACJ,EAAc,SAAS,KAAK,EAAc,OAAO,MAAe,EAAe,IAAI,EAAG,CAAC,EACnF,KACJ,EAAc,MAAM,MAAe,EAAe,IAAI,EAAG,CAAC,IAAI,CAAC,IAE3D,KAAe,OAAO,MAAmB;AACzC,QAAgB,WAAW,GAG/B;GADA,GAAkB,EAClB,GAAoB;AAEpB,OAAI;AACF,QAAI,EAAgB,WAAW,EAE7B,CADA,MAAM,GAAe,EAAE,WAAW;KAAE,IAAI,EAAgB;KAAI,OAAO,EAAE,WAAQ;KAAE,EAAE,CAAC,EAClF,EAAiB,sBAAsB;SAClC;KACL,IAAM,IAAS,MAAM,GAAoB,EACvC,WAAW;MAAE,KAAK;MAAiB,OAAO,EAAE,WAAQ;MAAE,EACvD,CAAC,EACI,IAAe,EAAO,MAAM,wBAAwB,gBAAgB,GACpE,IAAc,EAAO,MAAM,wBAAwB,eAAe;AACxE,OACE,WAAW,EAAa,aAAa,IAAc,IAAI,MAAM,EAAY,WAAW,KACrF;;AAaH,IAVA,EAAmB,EAAE,CAAC,EACtB,GAAmB,MAAa;KAC9B,IAAM,IAAO,IAAI,IAAI,EAAS;AAE9B,YADA,EAAgB,SAAS,MAAO,EAAK,OAAO,EAAG,CAAC,EACzC;MACP,EACE,KAAiB,EAAgB,SAAS,EAAc,IAC1D,EAAiB,KAAK,EAExB,MAAM,GAAS,EACX,IAAU,IAAO,EAAa,UAAU;YACrC,GAAkB;AAIzB,IAHA,QAAQ,MAAM,+BAA+B,EAAiB,EAC9D,EAAe,EAAG,sBAAsB,gDAAgD,CAAC,EACrF,IAAU,IAAO,EAAa,QAAQ,EAC1C,EAAiB,UAAU,iBAAiB;AAE1C,KADA,EAAe,KAAK,EACpB,EAAiB,UAAU;OAC1B,IAAM;;;IAIP,KAAmB,GACtB,MAAe;AACT,QACL,GAAkB,EAClB,GAAoB,EACpB,EAAmB,CAAC,EAAG,CAAC;IAE1B;EAAC;EAAgB;EAAkB;EAAmB,CACvD,EAEK,KAA2B,GAAa,GAAY,MAAqB;AAC7E,KAAmB,MAAa;GAC9B,IAAM,IAAO,IAAI,IAAI,EAAS;AAM9B,UALI,IACF,EAAK,IAAI,EAAG,GAEZ,EAAK,OAAO,EAAG,EAEV;IACP;IACD,EAAE,CAAC,EAEA,KAAyB,GAC5B,MAAqB;AACpB,KAAmB,MAAa;GAC9B,IAAM,IAAO,IAAI,IAAI,EAAS;AAQ9B,UAPA,EAAc,SAAS,MAAe;AACpC,IAAI,IACF,EAAK,IAAI,EAAG,GAEZ,EAAK,OAAO,EAAG;KAEjB,EACK;IACP;IAEJ,CAAC,EAAc,CAChB,EAEK,KAAuB,QAAkB;AACzC,GAAC,KAAkB,EAAe,SAAS,MAC/C,GAAkB,EAClB,GAAoB,EACpB,EAAmB,MAAM,KAAK,EAAe,CAAC;IAC7C;EAAC;EAAgB;EAAkB;EAAoB;EAAe,CAAC;AAsB1E,QApBI,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAc,SAAS,EAAG,4BAA4B,yBAAyB,EAAI,CAAA;EAC/E,CAAA,GAIN,KAAe,EAAY,mBAAmB,SAAS,KAAK,CAAC,IAE7D,kBAAC,IAAD;EACE,OAAO,EAAG,4BAA4B,gBAAgB;EACtD,aAAa,EACX,kCACA,qDACD;EACD,CAAA,GAKJ,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,IAAD;IACE,OAAO,EAAG,iBAAiB,QAAQ;IACnC,aAAa,EACX,qBACA,2DACD;IACD,SAAS,KAAiB,kBAAC,IAAD,EAA2B,WAAU,CAAA,GAAG,KAAA;IAClE,CAAA;GAIF,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAQ,MAAM;KAAI,WAAU;KAAyB,CAAA,EACrD,kBAAC,QAAD,EAAA,UAAA,CACG,EACC,yBACA,2BAA2B,GAAc,gDAC1C,EACA,IACG,IAAI,EAAG,8BAA8B,iBAAiB,IAAI,KAAK,EAAc,CAAC,gBAAgB,CAAC,GAAG,KAClG,GACC,EAAA,CAAA,CACH;;GAIN,kBAAC,OAAD;IAAK,aAAU;cACb,kBAAC,IAAD;KAAe,OAAO,GAAM;KAAmB,SAAS,KAAW,CAAC;KAAQ,CAAA;IACxE,CAAA;GAEN,kBAAC,IAAD;IACe;IACb,gBAAgB;IACF;IACd,iBAAiB,MAAU;AAEzB,KADA,GAAgB,EAAM,EACtB,EAAQ,EAAE;;IAEQ;IACpB,uBAAuB,MAAU;AAE/B,KADA,GAAsB,EAAM,EAC5B,EAAQ,EAAE;;IAEE;IACd,iBAAiB,MAAU;AAEzB,KADA,GAAgB,EAAM,EACtB,EAAQ,EAAE;;IAEM;IAClB,qBAAqB,MAAU;AAE7B,KADA,GAAoB,EAAM,EAC1B,EAAQ,EAAE;;IAEC;IACb,gBAAgB,MAAU;AAKxB,KAJA,EAAe,EAAM,EAGjB,MAAU,SAAO,EAAc,MAAM,EACzC,EAAQ,EAAE;;IAEZ,QAAQ;IACI;IACZ,eAAe,MAAU;AAGvB,KAFA,EAAc,EAAM,EAChB,MAAU,SAAO,EAAe,MAAM,EAC1C,EAAQ,EAAE;;IAEZ,OAAO;IACP,CAAA;GAED,EAAM,SAAS,IACd,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,QAAD;OAAM,WAAU;iBAAhB,CACG,EAAe,MAAK,YAChB;UACN,EAAe,OAAO,IACrB,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,kBAAkB,IAAI,KAAK,CAAC;OAC3C,WAAU;iBACX;OAEQ,CAAA,GACP,KACA;SACN,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,UAAU,CAAC,KAAkB,EAAe,SAAS;OACrD,WAAU;iBAJZ,CAKC,mBACiB,EAAe,OAAO,IAAI,KAAK,EAAe,KAAK,KAAK,GACjE;;MACL,CAAA,CACF;;IACF,CAAA,GACJ;GAEH,KAAW,CAAC,IACX,kBAAC,GAAD,EAAc,SAAS,EAAG,qBAAqB,wBAAwB,EAAI,CAAA,GACzE,IACF,kBAAC,IAAD;IAAY,SAAS,EAAM;IAAS,eAAe,GAAS;IAAI,CAAA,GAC9D,OAAoB,IACtB,kBAAC,IAAD;IACE,MAAM,kBAAC,GAAD;KAAQ,MAAM;KAAI,WAAU;KAAwB,CAAA;IAC1D,OAAO,EAAG,gBAAgB,gBAAgB;IAC1C,aAAa,EACX,0BACA,2EACD;IACD,QACE,kBAAC,IAAD,EAAe,QAAO,8DAA+D,CAAA;IAEvF,CAAA,GAEF,kBAAC,OAAD;IAAK,aAAU;cACb,kBAAC,IAAD;KACS;KACP,OAAO;KACP,SAAS,GAAM,cAAc,WAAW;KAClC;KACN,UAAU;KACV,cAAc;KACd,aAAa;KACb,aAAa;KACE;KACC;KACI;KACD;KACnB,sBAAsB;KACtB,oBAAoB;KACpB,CAAA;IACE,CAAA;GAGP,KAAiB,GAAY,eAC5B,kBAAC,IAAD;IAAgB,KAAK,EAAW;IAAc,eAAe,EAAiB,KAAK;IAAI,CAAA,GACrF;GAEH,KACC,kBAAC,OAAD;IACE,MAAK;IACL,WAAU;cAFZ,CAIG,IACD,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAET,EAAG,kBAAkB,UAAU;KACzB,CAAA,CACL;QACJ;GAEH,KACC,kBAAC,OAAD;IACE,MAAK;IACL,WAAU;cAFZ,CAIG,IACD,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAET,EAAG,kBAAkB,UAAU;KACzB,CAAA,CACL;QACJ;GAEJ,kBAAC,IAAD;IACE,QAAQ,EAAgB,SAAS;IACjC,eAAe,EAAmB,EAAE,CAAC;IACrC,WAAW;IACX,SAAS;IACT,OAAO,EAAgB,SAAS,IAAI,sBAAsB;IAC1D,aACE,EAAgB,SAAS,IACrB,gCAAgC,EAAgB,OAAO,yDACvD;IAEN,cAAc,EAAgB,SAAS,IAAI,gBAAgB;IAC3D,CAAA;GACE"}
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 // F2 — session-history drill-down. Initialised from a ?sessionId= URL param\n // (reciprocal link from the in-session History tab) and also settable by\n // clicking \"Show only this session\" on a SESSION-typed audit entry.\n const [sessionDrillId, setSessionDrillId] = useState<string | null>(() => {\n if (typeof window === 'undefined') return null;\n return new URLSearchParams(window.location.search).get('sessionId');\n });\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 // Session drill-down — the server matches resourceType=SESSION AND\n // resourceId=sessionId. Works for killed (soft-deleted) sessions too.\n if (sessionDrillId) filter.sessionId = sessionDrillId;\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 {/* Session drill-down banner — shown when scoped to one session's activity. */}\n {sessionDrillId ? (\n <div className=\"mb-4 flex items-center justify-between gap-2 rounded-md border border-action-primary-border bg-action-primary-bg/10 px-3 py-2 text-xs text-text-primary\">\n <span className=\"truncate\">\n {tr('audit.sessionScopeBanner', 'Showing activity for session')}{' '}\n <span className=\"font-mono\">{sessionDrillId}</span>\n </span>\n <button\n type=\"button\"\n onClick={() => {\n setSessionDrillId(null);\n setPage(0);\n }}\n className=\"flex-shrink-0 rounded bg-bg-sunken px-2 py-1 font-medium text-text-secondary hover:text-text-primary\"\n >\n {tr('audit.clearSessionScope', 'Clear')}\n </button>\n </div>\n ) : null}\n\n {/* AuditLogStats is a custom component that doesn't forward rest props\n onto a DOM node, so the tour anchor lives on a wrapper <div>. */}\n <div data-tour=\"audit-log-stats\">\n <AuditLogStats stats={data?.vibeAuditLogStats} loading={loading && !data} />\n </div>\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 <div data-tour=\"audit-log-table\">\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 </div>\n )}\n\n {selectedLogId && detailData?.vibeAuditLog ? (\n <AuditLogDetail\n log={detailData.vibeAuditLog}\n onClose={() => setSelectedLogId(null)}\n footer={\n detailData.vibeAuditLog.resourceType === 'SESSION' &&\n detailData.vibeAuditLog.resourceId &&\n detailData.vibeAuditLog.resourceId !== sessionDrillId ? (\n <button\n type=\"button\"\n onClick={() => {\n setSessionDrillId(detailData.vibeAuditLog?.resourceId ?? null);\n setSelectedLogId(null);\n setPage(0);\n }}\n className=\"w-full rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg-hover\"\n >\n {tr('audit.showOnlyThisSession', \"Show only this session's activity\")}\n </button>\n ) : null\n }\n />\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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-vibecontrols",
3
- "version": "2026.608.3",
3
+ "version": "2026.608.4",
4
4
  "description": "VibeControls microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [