@burdenoff/microfe-assethandler 2026.826.4 → 2026.826.5

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.
@@ -1 +1 @@
1
- {"version":3,"file":"KnowledgeBasePage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/KnowledgeBasePage.tsx"],"names":[],"mappings":"AA6iBA,wBAAgB,iBAAiB,gCAoUhC"}
1
+ {"version":3,"file":"KnowledgeBasePage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/KnowledgeBasePage.tsx"],"names":[],"mappings":"AA0jBA,wBAAgB,iBAAiB,gCA0VhC"}
@@ -9,15 +9,15 @@ import { Pagination as s, usePagination as c } from "../components/Pagination.js
9
9
  import { ChartCard as l } from "../components/ChartCard.js";
10
10
  import { SectionHeader as u } from "../components/SectionHeader.js";
11
11
  import "../components/index.js";
12
- import { formatAssetOption as d, formatIncidentOption as f, parseTagsInput as ee } from "../utils/knowledgeBase.js";
13
- import { useMemo as p, useState as m } from "react";
14
- import { BookOpen as h, ExternalLink as g, Eye as _, Link2 as v, Pencil as y, Plus as b, Search as x, User as S, X as C } from "lucide-react";
15
- import { gql as w } from "@apollo/client";
16
- import { useMutation as T, useQuery as E } from "@apollo/client/react";
17
- import { Fragment as D, jsx as O, jsxs as k } from "react/jsx-runtime";
18
- import { Button as A, GlassCard as j, IllustratedEmptyState as M, Input as N, Label as P, PagePurpose as F, ResponsiveDialog as I, ResponsiveDialogContent as L, ResponsiveDialogDescription as R, ResponsiveDialogFooter as te, ResponsiveDialogHeader as z, ResponsiveDialogTitle as B, Select as V, SelectContent as H, SelectItem as U, SelectTrigger as W, SelectValue as G, Textarea as K } from "@burdenoff/fe-libs/ui";
12
+ import { formatAssetOption as d, formatIncidentOption as f, parseTagsInput as p } from "../utils/knowledgeBase.js";
13
+ import { useMemo as m, useState as h } from "react";
14
+ import { BookOpen as g, ExternalLink as _, Eye as v, Link2 as y, Pencil as b, Plus as x, Search as S, User as C, X as w } from "lucide-react";
15
+ import { gql as T } from "@apollo/client";
16
+ import { useMutation as E, useQuery as D } from "@apollo/client/react";
17
+ import { Fragment as O, jsx as k, jsxs as A } from "react/jsx-runtime";
18
+ import { Button as j, GlassCard as ee, IllustratedEmptyState as M, Input as N, Label as P, PagePurpose as F, ResponsiveDialog as I, ResponsiveDialogContent as L, ResponsiveDialogDescription as R, ResponsiveDialogFooter as z, ResponsiveDialogHeader as B, ResponsiveDialogTitle as V, Select as H, SelectContent as U, SelectItem as W, SelectTrigger as G, SelectValue as K, Textarea as te } from "@burdenoff/fe-libs/ui";
19
19
  //#region src/assethandler/pages/KnowledgeBasePage.tsx
20
- var ne = w`
20
+ var ne = T`
21
21
  query AssetHandlerKnowledgeArticles {
22
22
  knowledgeArticles {
23
23
  id
@@ -37,25 +37,32 @@ var ne = w`
37
37
  relatedIncidentId
38
38
  }
39
39
  }
40
- `, q = w`
40
+ `, q = T`
41
41
  mutation AssetHandlerCreateKnowledgeArticle($input: CreateKnowledgeArticleInput!) {
42
42
  createKnowledgeArticle(input: $input) {
43
43
  id
44
44
  title
45
45
  }
46
46
  }
47
- `, J = w`
47
+ `, re = T`
48
48
  mutation AssetHandlerUpdateKnowledgeArticle($id: ID!, $input: UpdateKnowledgeArticleInput!) {
49
49
  updateKnowledgeArticle(id: $id, input: $input) {
50
50
  id
51
51
  title
52
52
  }
53
53
  }
54
- `, re = [
54
+ `, ie = T`
55
+ mutation AssetHandlerRecordKnowledgeArticleView($id: ID!) {
56
+ recordKnowledgeArticleView(id: $id) {
57
+ id
58
+ views
59
+ }
60
+ }
61
+ `, ae = [
55
62
  "DRAFT",
56
63
  "PUBLISHED",
57
64
  "ARCHIVED"
58
- ], Y = {
65
+ ], J = {
59
66
  PUBLISHED: {
60
67
  label: "Published",
61
68
  className: "bg-status-success-bg-subtle text-status-success-text"
@@ -68,36 +75,36 @@ var ne = w`
68
75
  label: "Archived",
69
76
  className: "bg-status-warning-bg-subtle text-status-warning-text"
70
77
  }
71
- }, X = (e) => e.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""), Z = (e) => e ? new Date(e).toLocaleDateString(void 0, {
78
+ }, Y = (e) => e.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""), X = (e) => e ? new Date(e).toLocaleDateString(void 0, {
72
79
  year: "numeric",
73
80
  month: "short",
74
81
  day: "numeric"
75
82
  }) : "—";
76
- function ie(e) {
83
+ function Z(e) {
77
84
  if (e.summary && e.summary.trim()) return e.summary.trim();
78
85
  let t = e.content.replace(/\s+/g, " ").trim();
79
86
  return t.length > 160 ? `${t.slice(0, 160)}…` : t;
80
87
  }
81
- function Q(e) {
88
+ function oe(e) {
82
89
  return (e.replace(/[^a-zA-Z0-9]/g, "").slice(0, 2) || "?").toUpperCase();
83
90
  }
84
- function $({ status: e }) {
85
- let t = Y[e] ?? {
91
+ function Q({ status: e }) {
92
+ let t = J[e] ?? {
86
93
  label: e,
87
94
  className: "bg-bg-sunken text-text-secondary"
88
95
  };
89
- return /* @__PURE__ */ O("span", {
96
+ return /* @__PURE__ */ k("span", {
90
97
  className: r("inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium", t.className),
91
98
  children: t.label
92
99
  });
93
100
  }
94
- function ae({ open: e, onOpenChange: t, article: n, onSaved: r }) {
95
- return /* @__PURE__ */ O(I, {
101
+ function se({ open: e, onOpenChange: t, article: n, onSaved: r }) {
102
+ return /* @__PURE__ */ k(I, {
96
103
  open: e,
97
104
  onOpenChange: t,
98
- children: /* @__PURE__ */ O(L, {
105
+ children: /* @__PURE__ */ k(L, {
99
106
  className: "max-h-[85vh] overflow-y-auto sm:max-w-2xl",
100
- children: e && /* @__PURE__ */ O(oe, {
107
+ children: e && /* @__PURE__ */ k(ce, {
101
108
  article: n,
102
109
  onOpenChange: t,
103
110
  onSaved: r
@@ -105,141 +112,141 @@ function ae({ open: e, onOpenChange: t, article: n, onSaved: r }) {
105
112
  })
106
113
  });
107
114
  }
108
- function oe({ article: t, onOpenChange: n, onSaved: r }) {
109
- let a = !!t, { assets: o, incidents: s } = e(), [c, l] = m(t?.title ?? ""), [u, h] = m(t?.slug ?? ""), [g, _] = m(a), [v, y] = m(t?.summary ?? ""), [b, x] = m(t?.content ?? ""), [S, w] = m(t ? t.tags.join(", ") : ""), [E, j] = m(t?.status ?? "PUBLISHED"), [M, F] = m(t?.relatedAssetId ?? ""), [I, L] = m(t?.relatedIncidentId ?? ""), ne = () => {
110
- l(""), h(""), _(!1), y(""), x(""), w(""), j("PUBLISHED"), F(""), L("");
111
- }, [Y, { loading: Z }] = T(q, {
115
+ function ce({ article: t, onOpenChange: n, onSaved: r }) {
116
+ let a = !!t, { assets: o, incidents: s } = e(), [c, l] = h(t?.title ?? ""), [u, g] = h(t?.slug ?? ""), [_, v] = h(a), [y, b] = h(t?.summary ?? ""), [x, S] = h(t?.content ?? ""), [C, T] = h(t ? t.tags.join(", ") : ""), [D, ee] = h(t?.status ?? "PUBLISHED"), [M, F] = h(t?.relatedAssetId ?? ""), [I, L] = h(t?.relatedIncidentId ?? ""), ne = () => {
117
+ l(""), g(""), v(!1), b(""), S(""), T(""), ee("PUBLISHED"), F(""), L("");
118
+ }, [ie, { loading: J }] = E(q, {
112
119
  onCompleted: (e) => {
113
120
  i.success("Article published", `${e.createKnowledgeArticle.title} is now available.`), ne(), n(!1), r();
114
121
  },
115
122
  onError: (e) => i.error("Could not publish article", e.message)
116
- }), [ie, { loading: Q }] = T(J, {
123
+ }), [X, { loading: Z }] = E(re, {
117
124
  refetchQueries: ["AssetHandlerKnowledgeArticles"],
118
125
  onCompleted: (e) => {
119
126
  i.success("Article updated", `${e.updateKnowledgeArticle.title} has been saved.`), n(!1), r();
120
127
  },
121
128
  onError: (e) => i.error("Could not update article", e.message)
122
- }), $ = p(() => o.find((e) => e.id === M), [o, M]), ae = p(() => s.find((e) => e.id === I), [s, I]), oe = (e) => {
123
- l(e), g || h(X(e));
129
+ }), oe = m(() => o.find((e) => e.id === M), [o, M]), Q = m(() => s.find((e) => e.id === I), [s, I]), se = (e) => {
130
+ l(e), _ || g(Y(e));
124
131
  };
125
- return /* @__PURE__ */ k(D, { children: [
126
- /* @__PURE__ */ k(z, { children: [/* @__PURE__ */ O(B, { children: a ? "Edit article" : "New Article" }), /* @__PURE__ */ O(R, { children: a ? "Update the runbook or fix so the team has the latest guidance." : "Capture a runbook or fix so your team can find it later." })] }),
127
- /* @__PURE__ */ k("div", {
132
+ return /* @__PURE__ */ A(O, { children: [
133
+ /* @__PURE__ */ A(B, { children: [/* @__PURE__ */ k(V, { children: a ? "Edit article" : "New Article" }), /* @__PURE__ */ k(R, { children: a ? "Update the runbook or fix so the team has the latest guidance." : "Capture a runbook or fix so your team can find it later." })] }),
134
+ /* @__PURE__ */ A("div", {
128
135
  className: "space-y-4",
129
136
  children: [
130
- /* @__PURE__ */ k("div", {
137
+ /* @__PURE__ */ A("div", {
131
138
  className: "grid gap-4 md:grid-cols-2",
132
- children: [/* @__PURE__ */ k("div", {
139
+ children: [/* @__PURE__ */ A("div", {
133
140
  className: "space-y-2",
134
- children: [/* @__PURE__ */ O(P, { children: "Title *" }), /* @__PURE__ */ O(N, {
141
+ children: [/* @__PURE__ */ k(P, { children: "Title *" }), /* @__PURE__ */ k(N, {
135
142
  value: c,
136
- onChange: (e) => oe(e.target.value)
143
+ onChange: (e) => se(e.target.value)
137
144
  })]
138
- }), /* @__PURE__ */ k("div", {
145
+ }), /* @__PURE__ */ A("div", {
139
146
  className: "space-y-2",
140
- children: [/* @__PURE__ */ O(P, { children: "Slug *" }), /* @__PURE__ */ O(N, {
147
+ children: [/* @__PURE__ */ k(P, { children: "Slug *" }), /* @__PURE__ */ k(N, {
141
148
  value: u,
142
149
  disabled: a,
143
150
  onChange: (e) => {
144
- _(!0), h(e.target.value);
151
+ v(!0), g(e.target.value);
145
152
  }
146
153
  })]
147
154
  })]
148
155
  }),
149
- /* @__PURE__ */ k("div", {
156
+ /* @__PURE__ */ A("div", {
150
157
  className: "space-y-2",
151
- children: [/* @__PURE__ */ O(P, { children: "Summary" }), /* @__PURE__ */ O(N, {
158
+ children: [/* @__PURE__ */ k(P, { children: "Summary" }), /* @__PURE__ */ k(N, {
152
159
  placeholder: "One-line description",
153
- value: v,
154
- onChange: (e) => y(e.target.value)
160
+ value: y,
161
+ onChange: (e) => b(e.target.value)
155
162
  })]
156
163
  }),
157
- /* @__PURE__ */ k("div", {
164
+ /* @__PURE__ */ A("div", {
158
165
  className: "space-y-2",
159
- children: [/* @__PURE__ */ O(P, { children: "Body *" }), /* @__PURE__ */ O(K, {
166
+ children: [/* @__PURE__ */ k(P, { children: "Body *" }), /* @__PURE__ */ k(te, {
160
167
  placeholder: "Write the full article body — steps, context, links…",
161
- value: b,
162
- onChange: (e) => x(e.target.value),
168
+ value: x,
169
+ onChange: (e) => S(e.target.value),
163
170
  rows: 8
164
171
  })]
165
172
  }),
166
- /* @__PURE__ */ k("div", {
173
+ /* @__PURE__ */ A("div", {
167
174
  className: "grid gap-4 md:grid-cols-2",
168
- children: [/* @__PURE__ */ k("div", {
175
+ children: [/* @__PURE__ */ A("div", {
169
176
  className: "space-y-2",
170
- children: [/* @__PURE__ */ O(P, { children: "Category tags" }), /* @__PURE__ */ O(N, {
177
+ children: [/* @__PURE__ */ k(P, { children: "Category tags" }), /* @__PURE__ */ k(N, {
171
178
  placeholder: "Runbooks, Fixes, Procedures…",
172
- value: S,
173
- onChange: (e) => w(e.target.value)
179
+ value: C,
180
+ onChange: (e) => T(e.target.value)
174
181
  })]
175
- }), /* @__PURE__ */ k("div", {
182
+ }), /* @__PURE__ */ A("div", {
176
183
  className: "space-y-2",
177
- children: [/* @__PURE__ */ O(P, { children: "Status" }), /* @__PURE__ */ k(V, {
178
- value: E,
179
- onValueChange: j,
180
- children: [/* @__PURE__ */ O(W, { children: /* @__PURE__ */ O(G, {}) }), /* @__PURE__ */ O(H, { children: re.map((e) => /* @__PURE__ */ O(U, {
184
+ children: [/* @__PURE__ */ k(P, { children: "Status" }), /* @__PURE__ */ A(H, {
185
+ value: D,
186
+ onValueChange: ee,
187
+ children: [/* @__PURE__ */ k(G, { children: /* @__PURE__ */ k(K, {}) }), /* @__PURE__ */ k(U, { children: ae.map((e) => /* @__PURE__ */ k(W, {
181
188
  value: e,
182
189
  children: e
183
190
  }, e)) })]
184
191
  })]
185
192
  })]
186
193
  }),
187
- /* @__PURE__ */ k("div", {
194
+ /* @__PURE__ */ A("div", {
188
195
  className: "grid gap-4 md:grid-cols-2",
189
- children: [/* @__PURE__ */ k("div", {
196
+ children: [/* @__PURE__ */ A("div", {
190
197
  className: "space-y-2",
191
198
  children: [
192
- /* @__PURE__ */ O(P, { children: "Related asset" }),
193
- /* @__PURE__ */ k(V, {
199
+ /* @__PURE__ */ k(P, { children: "Related asset" }),
200
+ /* @__PURE__ */ A(H, {
194
201
  value: M,
195
202
  onValueChange: F,
196
- children: [/* @__PURE__ */ O(W, { children: /* @__PURE__ */ O(G, { placeholder: "Select an asset" }) }), /* @__PURE__ */ k(H, { children: [/* @__PURE__ */ O(U, {
203
+ children: [/* @__PURE__ */ k(G, { children: /* @__PURE__ */ k(K, { placeholder: "Select an asset" }) }), /* @__PURE__ */ A(U, { children: [/* @__PURE__ */ k(W, {
197
204
  value: "",
198
205
  children: "None"
199
- }), o.map((e) => /* @__PURE__ */ O(U, {
206
+ }), o.map((e) => /* @__PURE__ */ k(W, {
200
207
  value: e.id,
201
208
  children: d(e)
202
209
  }, e.id))] })]
203
210
  }),
204
- $ ? /* @__PURE__ */ O("div", {
211
+ oe ? /* @__PURE__ */ k("div", {
205
212
  className: "flex flex-wrap gap-2",
206
- children: /* @__PURE__ */ k("span", {
213
+ children: /* @__PURE__ */ A("span", {
207
214
  className: "inline-flex items-center gap-1 rounded-full bg-action-primary-bg/10 px-2 py-0.5 text-xs font-medium text-primary",
208
- children: [d($), /* @__PURE__ */ O("button", {
215
+ children: [d(oe), /* @__PURE__ */ k("button", {
209
216
  type: "button",
210
217
  onClick: () => F(""),
211
218
  className: "rounded-full p-0.5 hover:bg-action-primary-bg/20",
212
219
  "aria-label": "Clear related asset",
213
- children: /* @__PURE__ */ O(C, { className: "h-3 w-3" })
220
+ children: /* @__PURE__ */ k(w, { className: "h-3 w-3" })
214
221
  })]
215
222
  })
216
223
  }) : null
217
224
  ]
218
- }), /* @__PURE__ */ k("div", {
225
+ }), /* @__PURE__ */ A("div", {
219
226
  className: "space-y-2",
220
227
  children: [
221
- /* @__PURE__ */ O(P, { children: "Related incident" }),
222
- /* @__PURE__ */ k(V, {
228
+ /* @__PURE__ */ k(P, { children: "Related incident" }),
229
+ /* @__PURE__ */ A(H, {
223
230
  value: I,
224
231
  onValueChange: L,
225
- children: [/* @__PURE__ */ O(W, { children: /* @__PURE__ */ O(G, { placeholder: "Select an incident" }) }), /* @__PURE__ */ k(H, { children: [/* @__PURE__ */ O(U, {
232
+ children: [/* @__PURE__ */ k(G, { children: /* @__PURE__ */ k(K, { placeholder: "Select an incident" }) }), /* @__PURE__ */ A(U, { children: [/* @__PURE__ */ k(W, {
226
233
  value: "",
227
234
  children: "None"
228
- }), s.map((e) => /* @__PURE__ */ O(U, {
235
+ }), s.map((e) => /* @__PURE__ */ k(W, {
229
236
  value: e.id,
230
237
  children: f(e)
231
238
  }, e.id))] })]
232
239
  }),
233
- ae ? /* @__PURE__ */ O("div", {
240
+ Q ? /* @__PURE__ */ k("div", {
234
241
  className: "flex flex-wrap gap-2",
235
- children: /* @__PURE__ */ k("span", {
242
+ children: /* @__PURE__ */ A("span", {
236
243
  className: "inline-flex items-center gap-1 rounded-full bg-action-primary-bg/10 px-2 py-0.5 text-xs font-medium text-primary",
237
- children: [f(ae), /* @__PURE__ */ O("button", {
244
+ children: [f(Q), /* @__PURE__ */ k("button", {
238
245
  type: "button",
239
246
  onClick: () => L(""),
240
247
  className: "rounded-full p-0.5 hover:bg-action-primary-bg/20",
241
248
  "aria-label": "Clear related incident",
242
- children: /* @__PURE__ */ O(C, { className: "h-3 w-3" })
249
+ children: /* @__PURE__ */ k(w, { className: "h-3 w-3" })
243
250
  })]
244
251
  })
245
252
  }) : null
@@ -248,105 +255,105 @@ function oe({ article: t, onOpenChange: n, onSaved: r }) {
248
255
  })
249
256
  ]
250
257
  }),
251
- /* @__PURE__ */ k(te, { children: [/* @__PURE__ */ O(A, {
258
+ /* @__PURE__ */ A(z, { children: [/* @__PURE__ */ k(j, {
252
259
  variant: "outline",
253
260
  onClick: () => n(!1),
254
- disabled: Z || Q,
261
+ disabled: J || Z,
255
262
  children: "Cancel"
256
- }), /* @__PURE__ */ O(A, {
263
+ }), /* @__PURE__ */ k(j, {
257
264
  onClick: () => {
258
265
  if (!c.trim() || !u.trim()) {
259
266
  i.warning("Missing details", "Title and slug are required.");
260
267
  return;
261
268
  }
262
- if (!b.trim()) {
269
+ if (!x.trim()) {
263
270
  i.warning("Body required", "Write the article body before publishing.");
264
271
  return;
265
272
  }
266
- let e = ee(S), n = {
273
+ let e = p(C), n = {
267
274
  title: c.trim(),
268
- summary: v.trim() || null,
269
- content: b.trim(),
275
+ summary: y.trim() || null,
276
+ content: x.trim(),
270
277
  tags: e,
271
- status: E,
278
+ status: D,
272
279
  relatedAssetId: M || null,
273
280
  relatedIncidentId: I || null
274
281
  };
275
- a && t ? ie({ variables: {
282
+ a && t ? X({ variables: {
276
283
  id: t.id,
277
284
  input: n
278
- } }) : Y({ variables: { input: {
285
+ } }) : ie({ variables: { input: {
279
286
  ...n,
280
287
  slug: u.trim()
281
288
  } } });
282
289
  },
283
- disabled: Z || Q,
284
- children: a ? Q ? "Saving…" : "Save changes" : Z ? "Publishing…" : "Publish article"
290
+ disabled: J || Z,
291
+ children: a ? Z ? "Saving…" : "Save changes" : J ? "Publishing…" : "Publish article"
285
292
  })] })
286
293
  ] });
287
294
  }
288
- function se({ article: e, onOpenChange: t, onEdit: r }) {
295
+ function le({ article: e, onOpenChange: t, onEdit: r }) {
289
296
  let i = n();
290
- return /* @__PURE__ */ O(I, {
297
+ return /* @__PURE__ */ k(I, {
291
298
  open: !!e,
292
299
  onOpenChange: t,
293
- children: /* @__PURE__ */ O(L, {
300
+ children: /* @__PURE__ */ k(L, {
294
301
  className: "max-h-[85vh] overflow-y-auto sm:max-w-2xl",
295
- children: e ? /* @__PURE__ */ k(D, { children: [
296
- /* @__PURE__ */ k(z, { children: [/* @__PURE__ */ k("div", {
302
+ children: e ? /* @__PURE__ */ A(O, { children: [
303
+ /* @__PURE__ */ A(B, { children: [/* @__PURE__ */ A("div", {
297
304
  className: "flex flex-wrap items-center gap-2",
298
- children: [/* @__PURE__ */ O($, { status: e.status }), e.tags.map((e) => /* @__PURE__ */ O("span", {
305
+ children: [/* @__PURE__ */ k(Q, { status: e.status }), e.tags.map((e) => /* @__PURE__ */ k("span", {
299
306
  className: "rounded-full bg-action-primary-bg/10 px-2 py-0.5 text-xs font-medium text-primary",
300
307
  children: e
301
308
  }, e))]
302
- }), /* @__PURE__ */ k("div", {
309
+ }), /* @__PURE__ */ A("div", {
303
310
  className: "flex items-start justify-between gap-4",
304
- children: [/* @__PURE__ */ k("div", {
311
+ children: [/* @__PURE__ */ A("div", {
305
312
  className: "min-w-0 flex-1",
306
- children: [/* @__PURE__ */ O(B, {
313
+ children: [/* @__PURE__ */ k(V, {
307
314
  className: "mt-1",
308
315
  children: e.title
309
- }), /* @__PURE__ */ k(R, { children: [
316
+ }), /* @__PURE__ */ A(R, { children: [
310
317
  "Updated ",
311
- Z(e.updatedAt),
318
+ X(e.updatedAt),
312
319
  " · ",
313
320
  e.views.toLocaleString(),
314
321
  " views"
315
322
  ] })]
316
- }), /* @__PURE__ */ k(A, {
323
+ }), /* @__PURE__ */ A(j, {
317
324
  variant: "outline",
318
325
  size: "sm",
319
326
  onClick: () => {
320
327
  t(!1), r(e);
321
328
  },
322
- children: [/* @__PURE__ */ O(y, { className: "mr-1.5 h-3.5 w-3.5" }), "Edit"]
329
+ children: [/* @__PURE__ */ k(b, { className: "mr-1.5 h-3.5 w-3.5" }), "Edit"]
323
330
  })]
324
331
  })] }),
325
- /* @__PURE__ */ O("div", {
332
+ /* @__PURE__ */ k("div", {
326
333
  className: "whitespace-pre-wrap text-sm leading-relaxed text-text-primary",
327
334
  children: e.content
328
335
  }),
329
- e.relatedAssetId || e.relatedIncidentId ? /* @__PURE__ */ k("div", {
336
+ e.relatedAssetId || e.relatedIncidentId ? /* @__PURE__ */ A("div", {
330
337
  className: "mt-4 space-y-2 border-t border-border-seam pt-4",
331
- children: [/* @__PURE__ */ k("h4", {
338
+ children: [/* @__PURE__ */ A("h4", {
332
339
  className: "flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-text-secondary",
333
- children: [/* @__PURE__ */ O(v, { className: "h-3.5 w-3.5" }), "Linked records"]
334
- }), /* @__PURE__ */ k("div", {
340
+ children: [/* @__PURE__ */ k(y, { className: "h-3.5 w-3.5" }), "Linked records"]
341
+ }), /* @__PURE__ */ A("div", {
335
342
  className: "flex flex-wrap gap-2",
336
- children: [e.relatedAssetId ? /* @__PURE__ */ k(A, {
343
+ children: [e.relatedAssetId ? /* @__PURE__ */ A(j, {
337
344
  variant: "outline",
338
345
  size: "sm",
339
346
  onClick: () => {
340
347
  t(!1), i(`/assets/${e.relatedAssetId}`);
341
348
  },
342
- children: ["View linked asset", /* @__PURE__ */ O(g, { className: "ml-1.5 h-3.5 w-3.5" })]
343
- }) : null, e.relatedIncidentId ? /* @__PURE__ */ k(A, {
349
+ children: ["View linked asset", /* @__PURE__ */ k(_, { className: "ml-1.5 h-3.5 w-3.5" })]
350
+ }) : null, e.relatedIncidentId ? /* @__PURE__ */ A(j, {
344
351
  variant: "outline",
345
352
  size: "sm",
346
353
  onClick: () => {
347
354
  t(!1), i(`/itil/incidents/${e.relatedIncidentId}`);
348
355
  },
349
- children: ["View linked incident", /* @__PURE__ */ O(g, { className: "ml-1.5 h-3.5 w-3.5" })]
356
+ children: ["View linked incident", /* @__PURE__ */ k(_, { className: "ml-1.5 h-3.5 w-3.5" })]
350
357
  }) : null]
351
358
  })]
352
359
  }) : null
@@ -354,96 +361,106 @@ function se({ article: e, onOpenChange: t, onEdit: r }) {
354
361
  })
355
362
  });
356
363
  }
357
- function ce() {
358
- let [e, n] = m(!1), [i, d] = m(null), [f, ee] = m(""), [g, v] = m("ALL"), [y, C] = m("ALL"), [w, T] = m("recent"), [D, I] = m(null), { data: L, loading: R, refetch: te } = E(ne, { fetchPolicy: "cache-and-network" }), z = p(() => L?.knowledgeArticles ?? [], [L?.knowledgeArticles]), B = p(() => {
364
+ function ue() {
365
+ let [e, n] = h(!1), [d, f] = h(null), [p, _] = h(""), [y, b] = h("ALL"), [w, T] = h("ALL"), [O, I] = h("recent"), [L, R] = h(null), { data: z, loading: B, refetch: V } = D(ne, { fetchPolicy: "cache-and-network" }), [te] = E(ie), q = m(() => z?.knowledgeArticles ?? [], [z?.knowledgeArticles]), re = m(() => {
359
366
  let e = /* @__PURE__ */ new Map();
360
- for (let t of z) for (let n of t.tags) e.set(n, (e.get(n) ?? 0) + 1);
367
+ for (let t of q) for (let n of t.tags) e.set(n, (e.get(n) ?? 0) + 1);
361
368
  return [...e.entries()].sort((e, t) => t[1] - e[1]);
362
- }, [z]), K = p(() => {
363
- let e = f.trim().toLowerCase();
364
- return [...z.filter((t) => g !== "ALL" && !t.tags.includes(g) || y !== "ALL" && t.status !== y ? !1 : e ? [
369
+ }, [q]), Y = m(() => {
370
+ let e = p.trim().toLowerCase();
371
+ return [...q.filter((t) => y !== "ALL" && !t.tags.includes(y) || w !== "ALL" && t.status !== w ? !1 : e ? [
365
372
  t.title,
366
373
  t.summary ?? "",
367
374
  t.content,
368
375
  t.tags.join(" ")
369
- ].join(" ").toLowerCase().includes(e) : !0)].sort((e, t) => w === "views" ? t.views - e.views : new Date(t.updatedAt).getTime() - new Date(e.updatedAt).getTime());
376
+ ].join(" ").toLowerCase().includes(e) : !0)].sort((e, t) => O === "views" ? t.views - e.views : new Date(t.updatedAt).getTime() - new Date(e.updatedAt).getTime());
370
377
  }, [
371
- z,
372
- f,
373
- g,
378
+ q,
379
+ p,
374
380
  y,
375
- w
376
- ]), q = p(() => {
377
- let e = z.reduce((e, t) => (e[t.status] = (e[t.status] ?? 0) + 1, e), {});
381
+ w,
382
+ O
383
+ ]), ce = m(() => {
384
+ let e = q.reduce((e, t) => (e[t.status] = (e[t.status] ?? 0) + 1, e), {});
378
385
  return Object.entries(e).map(([e, n], r) => ({
379
- name: Y[e]?.label ?? e,
386
+ name: J[e]?.label ?? e,
380
387
  value: n,
381
388
  color: t(r)
382
389
  }));
383
- }, [z]), J = p(() => [...z].filter((e) => e.views > 0).sort((e, t) => t.views - e.views).slice(0, 6).map((e) => ({
390
+ }, [q]), ue = m(() => [...q].filter((e) => e.views > 0).sort((e, t) => t.views - e.views).slice(0, 6).map((e) => ({
384
391
  name: e.title,
385
392
  views: e.views
386
- })), [z]), X = c(K, {
393
+ })), [q]), $ = c(Y, {
387
394
  pageSize: 9,
388
- resetKey: `${f}|${g}|${y}|${w}`
389
- });
390
- return /* @__PURE__ */ k("div", {
395
+ resetKey: `${p}|${y}|${w}|${O}`
396
+ }), de = () => {
397
+ f(null), n(!0);
398
+ }, fe = (e) => {
399
+ R(null), f(e), n(!0);
400
+ }, pe = (e) => {
401
+ R(e), te({ variables: { id: e.id } }).then((e) => {
402
+ let t = e.data?.recordKnowledgeArticleView;
403
+ t && (R((e) => e?.id === t.id ? {
404
+ ...e,
405
+ views: t.views
406
+ } : e), V());
407
+ }).catch((e) => {
408
+ i.error("Could not record article view", e instanceof Error ? e.message : "The article is still available to read.");
409
+ });
410
+ };
411
+ return /* @__PURE__ */ A("div", {
391
412
  className: "space-y-5 p-4 sm:p-6",
392
413
  children: [
393
- /* @__PURE__ */ O(u, {
414
+ /* @__PURE__ */ k(u, {
394
415
  hero: !0,
395
- icon: h,
416
+ icon: g,
396
417
  title: "Knowledge Base",
397
418
  description: "Turn one-off fixes and runbooks into repeatable, searchable operational knowledge.",
398
- actions: /* @__PURE__ */ k(A, {
419
+ actions: /* @__PURE__ */ A(j, {
399
420
  className: "w-full sm:w-auto",
400
- onClick: () => {
401
- d(null), n(!0);
402
- },
403
- children: [/* @__PURE__ */ O(b, { className: "mr-2 h-4 w-4" }), "New Article"]
421
+ onClick: de,
422
+ children: [/* @__PURE__ */ k(x, { className: "mr-2 h-4 w-4" }), "New Article"]
404
423
  })
405
424
  }),
406
- /* @__PURE__ */ O(F, { children: "Capture the fixes and runbooks your team relies on so the next person doesn't start from scratch. Link articles to incidents and assets, then search them when troubleshooting." }),
407
- /* @__PURE__ */ O(ae, {
425
+ /* @__PURE__ */ k(F, { children: "Capture the fixes and runbooks your team relies on so the next person doesn't start from scratch. Link articles to incidents and assets, then search them when troubleshooting." }),
426
+ /* @__PURE__ */ k(se, {
408
427
  open: e,
409
428
  onOpenChange: n,
410
- article: i,
411
- onSaved: () => void te()
429
+ article: d,
430
+ onSaved: () => void V()
412
431
  }),
413
- /* @__PURE__ */ O(se, {
414
- article: D,
415
- onOpenChange: (e) => !e && I(null),
416
- onEdit: (e) => {
417
- I(null), d(e), n(!0);
418
- }
432
+ /* @__PURE__ */ k(le, {
433
+ article: L,
434
+ onOpenChange: (e) => !e && R(null),
435
+ onEdit: fe
419
436
  }),
420
- /* @__PURE__ */ k("div", {
437
+ /* @__PURE__ */ A("div", {
421
438
  className: "relative",
422
- children: [/* @__PURE__ */ O(x, { className: "absolute left-3.5 top-1/2 h-4.5 w-4.5 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ O(N, {
439
+ children: [/* @__PURE__ */ k(S, { className: "absolute left-3.5 top-1/2 h-4.5 w-4.5 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ k(N, {
423
440
  className: "h-12 pl-11 text-base",
424
441
  placeholder: "Search runbooks, fixes and procedures…",
425
- value: f,
426
- onChange: (e) => ee(e.target.value),
442
+ value: p,
443
+ onChange: (e) => _(e.target.value),
427
444
  "aria-label": "Search knowledge articles"
428
445
  })]
429
446
  }),
430
- z.length > 0 && (q.length > 0 || J.length > 0) ? /* @__PURE__ */ k("div", {
447
+ q.length > 0 && (ce.length > 0 || ue.length > 0) ? /* @__PURE__ */ A("div", {
431
448
  className: "grid gap-4 lg:grid-cols-2",
432
- children: [/* @__PURE__ */ O(l, {
449
+ children: [/* @__PURE__ */ k(l, {
433
450
  title: "Publishing status",
434
451
  subtitle: "Articles by status",
435
- children: /* @__PURE__ */ O(o, {
436
- data: q,
452
+ children: /* @__PURE__ */ k(o, {
453
+ data: ce,
437
454
  height: 200,
438
455
  centerLabel: "Articles",
439
- centerValue: z.length,
456
+ centerValue: q.length,
440
457
  ariaLabel: "Articles by status"
441
458
  })
442
- }), /* @__PURE__ */ O(l, {
459
+ }), /* @__PURE__ */ k(l, {
443
460
  title: "Most consulted",
444
461
  subtitle: "Top articles by views",
445
- children: /* @__PURE__ */ O(a, {
446
- data: J,
462
+ children: /* @__PURE__ */ k(a, {
463
+ data: ue,
447
464
  xKey: "name",
448
465
  series: [{
449
466
  key: "views",
@@ -457,89 +474,89 @@ function ce() {
457
474
  })
458
475
  })]
459
476
  }) : null,
460
- /* @__PURE__ */ k("div", {
477
+ /* @__PURE__ */ A("div", {
461
478
  className: "grid gap-5 lg:grid-cols-[200px_1fr]",
462
- children: [/* @__PURE__ */ k("aside", {
479
+ children: [/* @__PURE__ */ A("aside", {
463
480
  className: "space-y-3",
464
481
  children: [
465
- /* @__PURE__ */ k("div", {
482
+ /* @__PURE__ */ A("div", {
466
483
  className: "flex items-center justify-between gap-2",
467
- children: [/* @__PURE__ */ O("h3", {
484
+ children: [/* @__PURE__ */ k("h3", {
468
485
  className: "text-sm font-semibold text-text-primary",
469
486
  children: "Category"
470
- }), /* @__PURE__ */ k(V, {
471
- value: w,
472
- onValueChange: (e) => T(e),
473
- children: [/* @__PURE__ */ O(W, {
487
+ }), /* @__PURE__ */ A(H, {
488
+ value: O,
489
+ onValueChange: (e) => I(e),
490
+ children: [/* @__PURE__ */ k(G, {
474
491
  className: "h-8 w-[130px] text-xs lg:hidden",
475
- children: /* @__PURE__ */ O(G, {})
476
- }), /* @__PURE__ */ k(H, { children: [/* @__PURE__ */ O(U, {
492
+ children: /* @__PURE__ */ k(K, {})
493
+ }), /* @__PURE__ */ A(U, { children: [/* @__PURE__ */ k(W, {
477
494
  value: "recent",
478
495
  children: "Recently updated"
479
- }), /* @__PURE__ */ O(U, {
496
+ }), /* @__PURE__ */ k(W, {
480
497
  value: "views",
481
498
  children: "Most viewed"
482
499
  })] })]
483
500
  })]
484
501
  }),
485
- /* @__PURE__ */ k("div", {
502
+ /* @__PURE__ */ A("div", {
486
503
  className: "flex gap-2 overflow-x-auto pb-1 lg:flex-col lg:overflow-visible lg:pb-0",
487
- children: [/* @__PURE__ */ k("button", {
504
+ children: [/* @__PURE__ */ A("button", {
488
505
  type: "button",
489
- onClick: () => v("ALL"),
490
- className: r("shrink-0 rounded-md px-3 py-1.5 text-left text-sm font-medium transition-colors lg:w-full", g === "ALL" ? "bg-action-primary-bg/10 text-primary" : "text-text-secondary hover:text-text-primary"),
491
- children: ["All articles", /* @__PURE__ */ O("span", {
506
+ onClick: () => b("ALL"),
507
+ className: r("shrink-0 rounded-md px-3 py-1.5 text-left text-sm font-medium transition-colors lg:w-full", y === "ALL" ? "bg-action-primary-bg/10 text-primary" : "text-text-secondary hover:text-text-primary"),
508
+ children: ["All articles", /* @__PURE__ */ k("span", {
492
509
  className: "ml-1.5 text-xs text-text-muted tabular-nums",
493
- children: z.length
510
+ children: q.length
494
511
  })]
495
- }), B.map(([e, t]) => /* @__PURE__ */ k("button", {
512
+ }), re.map(([e, t]) => /* @__PURE__ */ A("button", {
496
513
  type: "button",
497
- onClick: () => v(e),
498
- className: r("flex shrink-0 items-center justify-between gap-2 rounded-md px-3 py-1.5 text-left text-sm font-medium transition-colors lg:w-full", g === e ? "bg-action-primary-bg/10 text-primary" : "text-text-secondary hover:text-text-primary"),
499
- children: [/* @__PURE__ */ O("span", {
514
+ onClick: () => b(e),
515
+ className: r("flex shrink-0 items-center justify-between gap-2 rounded-md px-3 py-1.5 text-left text-sm font-medium transition-colors lg:w-full", y === e ? "bg-action-primary-bg/10 text-primary" : "text-text-secondary hover:text-text-primary"),
516
+ children: [/* @__PURE__ */ k("span", {
500
517
  className: "truncate",
501
518
  children: e
502
- }), /* @__PURE__ */ O("span", {
519
+ }), /* @__PURE__ */ k("span", {
503
520
  className: "text-xs text-text-muted tabular-nums",
504
521
  children: t
505
522
  })]
506
523
  }, e))]
507
524
  }),
508
- /* @__PURE__ */ k("div", {
525
+ /* @__PURE__ */ A("div", {
509
526
  className: "hidden space-y-1.5 border-t border-border-seam pt-3 lg:block",
510
527
  children: [
511
- /* @__PURE__ */ O(P, {
528
+ /* @__PURE__ */ k(P, {
512
529
  className: "text-xs",
513
530
  children: "Status"
514
531
  }),
515
- /* @__PURE__ */ k(V, {
516
- value: y,
517
- onValueChange: C,
518
- children: [/* @__PURE__ */ O(W, {
532
+ /* @__PURE__ */ A(H, {
533
+ value: w,
534
+ onValueChange: T,
535
+ children: [/* @__PURE__ */ k(G, {
519
536
  className: "h-9",
520
- children: /* @__PURE__ */ O(G, {})
521
- }), /* @__PURE__ */ k(H, { children: [/* @__PURE__ */ O(U, {
537
+ children: /* @__PURE__ */ k(K, {})
538
+ }), /* @__PURE__ */ A(U, { children: [/* @__PURE__ */ k(W, {
522
539
  value: "ALL",
523
540
  children: "All statuses"
524
- }), re.map((e) => /* @__PURE__ */ O(U, {
541
+ }), ae.map((e) => /* @__PURE__ */ k(W, {
525
542
  value: e,
526
543
  children: e
527
544
  }, e))] })]
528
545
  }),
529
- /* @__PURE__ */ O(P, {
546
+ /* @__PURE__ */ k(P, {
530
547
  className: "text-xs",
531
548
  children: "Sort"
532
549
  }),
533
- /* @__PURE__ */ k(V, {
534
- value: w,
535
- onValueChange: (e) => T(e),
536
- children: [/* @__PURE__ */ O(W, {
550
+ /* @__PURE__ */ A(H, {
551
+ value: O,
552
+ onValueChange: (e) => I(e),
553
+ children: [/* @__PURE__ */ k(G, {
537
554
  className: "h-9",
538
- children: /* @__PURE__ */ O(G, {})
539
- }), /* @__PURE__ */ k(H, { children: [/* @__PURE__ */ O(U, {
555
+ children: /* @__PURE__ */ k(K, {})
556
+ }), /* @__PURE__ */ A(U, { children: [/* @__PURE__ */ k(W, {
540
557
  value: "recent",
541
558
  children: "Recently updated"
542
- }), /* @__PURE__ */ O(U, {
559
+ }), /* @__PURE__ */ k(W, {
543
560
  value: "views",
544
561
  children: "Most viewed"
545
562
  })] })]
@@ -547,73 +564,73 @@ function ce() {
547
564
  ]
548
565
  })
549
566
  ]
550
- }), /* @__PURE__ */ k("div", { children: [
551
- R && z.length === 0 ? /* @__PURE__ */ O("div", {
567
+ }), /* @__PURE__ */ A("div", { children: [
568
+ B && q.length === 0 ? /* @__PURE__ */ k("div", {
552
569
  className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-3",
553
- children: Array.from({ length: 6 }).map((e, t) => /* @__PURE__ */ O("div", { className: "h-40 rounded-xl bg-bg-sunken motion-safe:animate-vc-shimmer" }, t))
570
+ children: Array.from({ length: 6 }).map((e, t) => /* @__PURE__ */ k("div", { className: "h-40 rounded-xl bg-bg-sunken motion-safe:animate-vc-shimmer" }, t))
554
571
  }) : null,
555
- !R && K.length === 0 ? /* @__PURE__ */ O(M, {
556
- illustration: f || g !== "ALL" || y !== "ALL" ? "empty-search" : "empty-data",
557
- title: f || g !== "ALL" || y !== "ALL" ? "No articles match your filters" : "No knowledge articles found",
558
- description: f || g !== "ALL" || y !== "ALL" ? "Try a different term, category or status." : "Publish an article to start building your knowledge base."
572
+ !B && Y.length === 0 ? /* @__PURE__ */ k(M, {
573
+ illustration: p || y !== "ALL" || w !== "ALL" ? "empty-search" : "empty-data",
574
+ title: p || y !== "ALL" || w !== "ALL" ? "No articles match your filters" : "No knowledge articles found",
575
+ description: p || y !== "ALL" || w !== "ALL" ? "Try a different term, category or status." : "Publish an article to start building your knowledge base."
559
576
  }) : null,
560
- /* @__PURE__ */ O("div", {
577
+ /* @__PURE__ */ k("div", {
561
578
  className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-3",
562
- children: X.pageItems.map((e) => /* @__PURE__ */ O(j, {
579
+ children: $.pageItems.map((e) => /* @__PURE__ */ k(ee, {
563
580
  treatment: "glass",
564
581
  className: "border-border-seam",
565
- children: /* @__PURE__ */ k("button", {
582
+ children: /* @__PURE__ */ A("button", {
566
583
  type: "button",
567
- onClick: () => I(e),
584
+ onClick: () => pe(e),
568
585
  className: "flex h-full w-full flex-col gap-3 p-4 text-left",
569
586
  children: [
570
- /* @__PURE__ */ k("div", {
587
+ /* @__PURE__ */ A("div", {
571
588
  className: "flex flex-wrap items-center gap-1.5",
572
- children: [/* @__PURE__ */ O($, { status: e.status }), e.tags.slice(0, 1).map((e) => /* @__PURE__ */ O("span", {
589
+ children: [/* @__PURE__ */ k(Q, { status: e.status }), e.tags.slice(0, 1).map((e) => /* @__PURE__ */ k("span", {
573
590
  className: "rounded-full bg-action-primary-bg/10 px-2 py-0.5 text-xs font-medium text-primary",
574
591
  children: e
575
592
  }, e))]
576
593
  }),
577
- /* @__PURE__ */ O("h2", {
594
+ /* @__PURE__ */ k("h2", {
578
595
  className: "line-clamp-2 text-base font-semibold text-text-primary",
579
596
  children: e.title
580
597
  }),
581
- /* @__PURE__ */ O("p", {
598
+ /* @__PURE__ */ k("p", {
582
599
  className: "line-clamp-2 flex-1 text-sm text-text-secondary",
583
- children: ie(e)
600
+ children: Z(e)
584
601
  }),
585
- /* @__PURE__ */ k("div", {
602
+ /* @__PURE__ */ A("div", {
586
603
  className: "flex items-center justify-between gap-2 border-t border-border-seam/60 pt-3",
587
- children: [/* @__PURE__ */ k("div", {
604
+ children: [/* @__PURE__ */ A("div", {
588
605
  className: "flex min-w-0 items-center gap-2",
589
- children: [/* @__PURE__ */ O("span", {
606
+ children: [/* @__PURE__ */ k("span", {
590
607
  className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-[10px] font-semibold text-text-secondary",
591
- children: e.updatedBy ? Q(e.updatedBy) : /* @__PURE__ */ O(S, { className: "h-3 w-3" })
592
- }), /* @__PURE__ */ k("span", {
608
+ children: e.updatedBy ? oe(e.updatedBy) : /* @__PURE__ */ k(C, { className: "h-3 w-3" })
609
+ }), /* @__PURE__ */ A("span", {
593
610
  className: "truncate text-xs text-text-secondary",
594
- children: ["Updated ", Z(e.updatedAt)]
611
+ children: ["Updated ", X(e.updatedAt)]
595
612
  })]
596
- }), /* @__PURE__ */ k("span", {
613
+ }), /* @__PURE__ */ A("span", {
597
614
  className: "flex shrink-0 items-center gap-1 text-xs text-text-muted tabular-nums",
598
- children: [/* @__PURE__ */ O(_, { className: "h-3.5 w-3.5" }), e.views.toLocaleString()]
615
+ children: [/* @__PURE__ */ k(v, { className: "h-3.5 w-3.5" }), e.views.toLocaleString()]
599
616
  })]
600
617
  })
601
618
  ]
602
619
  })
603
620
  }, e.id))
604
621
  }),
605
- /* @__PURE__ */ O("div", {
622
+ /* @__PURE__ */ k("div", {
606
623
  className: "mt-4",
607
- children: /* @__PURE__ */ O(s, {
608
- page: X.page,
609
- pageCount: X.pageCount,
610
- rangeStart: X.rangeStart,
611
- rangeEnd: X.rangeEnd,
612
- totalItems: X.totalItems,
613
- canPrev: X.canPrev,
614
- canNext: X.canNext,
615
- onPrev: X.prevPage,
616
- onNext: X.nextPage,
624
+ children: /* @__PURE__ */ k(s, {
625
+ page: $.page,
626
+ pageCount: $.pageCount,
627
+ rangeStart: $.rangeStart,
628
+ rangeEnd: $.rangeEnd,
629
+ totalItems: $.totalItems,
630
+ canPrev: $.canPrev,
631
+ canNext: $.canNext,
632
+ onPrev: $.prevPage,
633
+ onNext: $.nextPage,
617
634
  itemLabel: "articles"
618
635
  })
619
636
  })
@@ -623,6 +640,6 @@ function ce() {
623
640
  });
624
641
  }
625
642
  //#endregion
626
- export { ce as KnowledgeBasePage };
643
+ export { ue as KnowledgeBasePage };
627
644
 
628
645
  //# sourceMappingURL=KnowledgeBasePage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"KnowledgeBasePage.js","names":[],"sources":["../../../src/assethandler/pages/KnowledgeBasePage.tsx"],"sourcesContent":["import { BookOpen, Plus, Search, Eye, User, Link2, ExternalLink, Pencil, X } from 'lucide-react';\nimport {\n Button,\n IllustratedEmptyState,\n GlassCard,\n PagePurpose,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { useMemo, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport {\n Pagination,\n usePagination,\n SectionHeader,\n ChartCard,\n DonutChart,\n BarsChart,\n seriesColor,\n type DonutSlice,\n} from '../components';\nimport { useAssetHandlerNavigate } from '../hooks/useAssetHandlerNavigate';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport { cn } from '../utils/cn';\nimport { notify } from '../utils/toast';\nimport { formatAssetOption, formatIncidentOption, parseTagsInput } from '../utils/knowledgeBase';\n\nconst KNOWLEDGE_ARTICLES_QUERY = gql`\n query AssetHandlerKnowledgeArticles {\n knowledgeArticles {\n id\n title\n slug\n summary\n content\n status\n tags\n views\n createdBy\n updatedBy\n publishedAt\n createdAt\n updatedAt\n relatedAssetId\n relatedIncidentId\n }\n }\n`;\n\nconst CREATE_KNOWLEDGE_ARTICLE_MUTATION = gql`\n mutation AssetHandlerCreateKnowledgeArticle($input: CreateKnowledgeArticleInput!) {\n createKnowledgeArticle(input: $input) {\n id\n title\n }\n }\n`;\n\nconst UPDATE_KNOWLEDGE_ARTICLE_MUTATION = gql`\n mutation AssetHandlerUpdateKnowledgeArticle($id: ID!, $input: UpdateKnowledgeArticleInput!) {\n updateKnowledgeArticle(id: $id, input: $input) {\n id\n title\n }\n }\n`;\n\ninterface KnowledgeArticle {\n id: string;\n title: string;\n slug: string;\n summary: string | null;\n content: string;\n status: string;\n tags: string[];\n views: number;\n createdBy: string;\n updatedBy: string;\n publishedAt: string | null;\n createdAt: string;\n updatedAt: string;\n relatedAssetId: string | null;\n relatedIncidentId: string | null;\n}\n\ninterface CreateKnowledgeArticleResult {\n createKnowledgeArticle: { id: string; title: string };\n}\n\ninterface UpdateKnowledgeArticleResult {\n updateKnowledgeArticle: { id: string; title: string };\n}\n\nconst ARTICLE_STATUSES = ['DRAFT', 'PUBLISHED', 'ARCHIVED'] as const;\n\nconst STATUS_META: Record<string, { label: string; className: string }> = {\n PUBLISHED: {\n label: 'Published',\n className: 'bg-status-success-bg-subtle text-status-success-text',\n },\n DRAFT: { label: 'Draft', className: 'bg-bg-sunken text-text-secondary' },\n ARCHIVED: {\n label: 'Archived',\n className: 'bg-status-warning-bg-subtle text-status-warning-text',\n },\n};\n\nconst slugify = (value: string): string =>\n value\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n\nconst formatDate = (value: string | null): string =>\n value\n ? new Date(value).toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })\n : '—';\n\n/** A 2-line excerpt: the summary if present, otherwise the opening of the body. */\nfunction excerptOf(article: KnowledgeArticle): string {\n if (article.summary && article.summary.trim()) return article.summary.trim();\n const body = article.content.replace(/\\s+/g, ' ').trim();\n return body.length > 160 ? `${body.slice(0, 160)}…` : body;\n}\n\n/** Short avatar initials from an actor id (best-effort, no fabricated names). */\nfunction initialsOf(actor: string): string {\n const clean = actor.replace(/[^a-zA-Z0-9]/g, '');\n return (clean.slice(0, 2) || '?').toUpperCase();\n}\n\nfunction StatusPill({ status }: { status: string }) {\n const meta = STATUS_META[status] ?? {\n label: status,\n className: 'bg-bg-sunken text-text-secondary',\n };\n return (\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium',\n meta.className\n )}\n >\n {meta.label}\n </span>\n );\n}\n\nfunction ArticleDialog({\n open,\n onOpenChange,\n article,\n onSaved,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n article: KnowledgeArticle | null;\n onSaved: () => void;\n}) {\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-2xl\">\n {open && (\n <ArticleForm\n key={article?.id ?? 'new'}\n article={article}\n onOpenChange={onOpenChange}\n onSaved={onSaved}\n />\n )}\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\nfunction ArticleForm({\n article,\n onOpenChange,\n onSaved,\n}: {\n article: KnowledgeArticle | null;\n onOpenChange: (open: boolean) => void;\n onSaved: () => void;\n}) {\n const isEdit = Boolean(article);\n const { assets, incidents } = useAssetHandlerStore();\n\n const [title, setTitle] = useState(article?.title ?? '');\n const [slug, setSlug] = useState(article?.slug ?? '');\n const [slugTouched, setSlugTouched] = useState(isEdit);\n const [summary, setSummary] = useState(article?.summary ?? '');\n const [content, setContent] = useState(article?.content ?? '');\n const [tags, setTags] = useState(article ? article.tags.join(', ') : '');\n const [status, setStatus] = useState<string>(article?.status ?? 'PUBLISHED');\n const [relatedAssetId, setRelatedAssetId] = useState(article?.relatedAssetId ?? '');\n const [relatedIncidentId, setRelatedIncidentId] = useState(article?.relatedIncidentId ?? '');\n\n const reset = () => {\n setTitle('');\n setSlug('');\n setSlugTouched(false);\n setSummary('');\n setContent('');\n setTags('');\n setStatus('PUBLISHED');\n setRelatedAssetId('');\n setRelatedIncidentId('');\n };\n\n const [createArticle, { loading: creating }] = useMutation<CreateKnowledgeArticleResult>(\n CREATE_KNOWLEDGE_ARTICLE_MUTATION,\n {\n onCompleted: (data) => {\n notify.success(\n 'Article published',\n `${data.createKnowledgeArticle.title} is now available.`\n );\n reset();\n onOpenChange(false);\n onSaved();\n },\n onError: (error) => notify.error('Could not publish article', error.message),\n }\n );\n\n const [updateArticle, { loading: updating }] = useMutation<UpdateKnowledgeArticleResult>(\n UPDATE_KNOWLEDGE_ARTICLE_MUTATION,\n {\n refetchQueries: ['AssetHandlerKnowledgeArticles'],\n onCompleted: (data) => {\n notify.success('Article updated', `${data.updateKnowledgeArticle.title} has been saved.`);\n onOpenChange(false);\n onSaved();\n },\n onError: (error) => notify.error('Could not update article', error.message),\n }\n );\n\n const selectedAsset = useMemo(\n () => assets.find((a) => a.id === relatedAssetId),\n [assets, relatedAssetId]\n );\n const selectedIncident = useMemo(\n () => incidents.find((i) => i.id === relatedIncidentId),\n [incidents, relatedIncidentId]\n );\n\n const handleTitleChange = (next: string) => {\n setTitle(next);\n if (!slugTouched) setSlug(slugify(next));\n };\n\n const handleSubmit = () => {\n if (!title.trim() || !slug.trim()) {\n notify.warning('Missing details', 'Title and slug are required.');\n return;\n }\n if (!content.trim()) {\n notify.warning('Body required', 'Write the article body before publishing.');\n return;\n }\n\n const parsedTags = parseTagsInput(tags);\n const baseInput = {\n title: title.trim(),\n summary: summary.trim() || null,\n content: content.trim(),\n tags: parsedTags,\n status,\n relatedAssetId: relatedAssetId || null,\n relatedIncidentId: relatedIncidentId || null,\n };\n\n if (isEdit && article) {\n void updateArticle({\n variables: { id: article.id, input: baseInput },\n });\n } else {\n void createArticle({\n variables: {\n input: { ...baseInput, slug: slug.trim() },\n },\n });\n }\n };\n\n return (\n <>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>{isEdit ? 'Edit article' : 'New Article'}</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {isEdit\n ? 'Update the runbook or fix so the team has the latest guidance.'\n : 'Capture a runbook or fix so your team can find it later.'}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-4\">\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Title *</Label>\n <Input value={title} onChange={(e) => handleTitleChange(e.target.value)} />\n </div>\n <div className=\"space-y-2\">\n <Label>Slug *</Label>\n <Input\n value={slug}\n disabled={isEdit}\n onChange={(e) => {\n setSlugTouched(true);\n setSlug(e.target.value);\n }}\n />\n </div>\n </div>\n <div className=\"space-y-2\">\n <Label>Summary</Label>\n <Input\n placeholder=\"One-line description\"\n value={summary}\n onChange={(e) => setSummary(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Body *</Label>\n <Textarea\n placeholder=\"Write the full article body — steps, context, links…\"\n value={content}\n onChange={(e) => setContent(e.target.value)}\n rows={8}\n />\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Category tags</Label>\n <Input\n placeholder=\"Runbooks, Fixes, Procedures…\"\n value={tags}\n onChange={(e) => setTags(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Status</Label>\n <Select value={status} onValueChange={setStatus}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {ARTICLE_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>\n {s}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Related asset</Label>\n <Select value={relatedAssetId} onValueChange={setRelatedAssetId}>\n <SelectTrigger>\n <SelectValue placeholder=\"Select an asset\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"\">None</SelectItem>\n {assets.map((asset) => (\n <SelectItem key={asset.id} value={asset.id}>\n {formatAssetOption(asset)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {selectedAsset ? (\n <div className=\"flex flex-wrap gap-2\">\n <span className=\"inline-flex items-center gap-1 rounded-full bg-action-primary-bg/10 px-2 py-0.5 text-xs font-medium text-primary\">\n {formatAssetOption(selectedAsset)}\n <button\n type=\"button\"\n onClick={() => setRelatedAssetId('')}\n className=\"rounded-full p-0.5 hover:bg-action-primary-bg/20\"\n aria-label=\"Clear related asset\"\n >\n <X className=\"h-3 w-3\" />\n </button>\n </span>\n </div>\n ) : null}\n </div>\n <div className=\"space-y-2\">\n <Label>Related incident</Label>\n <Select value={relatedIncidentId} onValueChange={setRelatedIncidentId}>\n <SelectTrigger>\n <SelectValue placeholder=\"Select an incident\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"\">None</SelectItem>\n {incidents.map((incident) => (\n <SelectItem key={incident.id} value={incident.id}>\n {formatIncidentOption(incident)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {selectedIncident ? (\n <div className=\"flex flex-wrap gap-2\">\n <span className=\"inline-flex items-center gap-1 rounded-full bg-action-primary-bg/10 px-2 py-0.5 text-xs font-medium text-primary\">\n {formatIncidentOption(selectedIncident)}\n <button\n type=\"button\"\n onClick={() => setRelatedIncidentId('')}\n className=\"rounded-full p-0.5 hover:bg-action-primary-bg/20\"\n aria-label=\"Clear related incident\"\n >\n <X className=\"h-3 w-3\" />\n </button>\n </span>\n </div>\n ) : null}\n </div>\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n disabled={creating || updating}\n >\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={creating || updating}>\n {isEdit\n ? updating\n ? 'Saving…'\n : 'Save changes'\n : creating\n ? 'Publishing…'\n : 'Publish article'}\n </Button>\n </ResponsiveDialogFooter>\n </>\n );\n}\n\nfunction ArticleReader({\n article,\n onOpenChange,\n onEdit,\n}: {\n article: KnowledgeArticle | null;\n onOpenChange: (open: boolean) => void;\n onEdit: (article: KnowledgeArticle) => void;\n}) {\n const navigate = useAssetHandlerNavigate();\n return (\n <ResponsiveDialog open={Boolean(article)} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-2xl\">\n {article ? (\n <>\n <ResponsiveDialogHeader>\n <div className=\"flex flex-wrap items-center gap-2\">\n <StatusPill status={article.status} />\n {article.tags.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-action-primary-bg/10 px-2 py-0.5 text-xs font-medium text-primary\"\n >\n {tag}\n </span>\n ))}\n </div>\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"min-w-0 flex-1\">\n <ResponsiveDialogTitle className=\"mt-1\">{article.title}</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Updated {formatDate(article.updatedAt)} · {article.views.toLocaleString()} views\n </ResponsiveDialogDescription>\n </div>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n onOpenChange(false);\n onEdit(article);\n }}\n >\n <Pencil className=\"mr-1.5 h-3.5 w-3.5\" />\n Edit\n </Button>\n </div>\n </ResponsiveDialogHeader>\n <div className=\"whitespace-pre-wrap text-sm leading-relaxed text-text-primary\">\n {article.content}\n </div>\n {article.relatedAssetId || article.relatedIncidentId ? (\n <div className=\"mt-4 space-y-2 border-t border-border-seam pt-4\">\n <h4 className=\"flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-text-secondary\">\n <Link2 className=\"h-3.5 w-3.5\" />\n Linked records\n </h4>\n <div className=\"flex flex-wrap gap-2\">\n {article.relatedAssetId ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n onOpenChange(false);\n navigate(`/assets/${article.relatedAssetId}`);\n }}\n >\n View linked asset\n <ExternalLink className=\"ml-1.5 h-3.5 w-3.5\" />\n </Button>\n ) : null}\n {article.relatedIncidentId ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n onOpenChange(false);\n navigate(`/itil/incidents/${article.relatedIncidentId}`);\n }}\n >\n View linked incident\n <ExternalLink className=\"ml-1.5 h-3.5 w-3.5\" />\n </Button>\n ) : null}\n </div>\n </div>\n ) : null}\n </>\n ) : null}\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\ntype SortKey = 'recent' | 'views';\n\nexport function KnowledgeBasePage() {\n const [isDialogOpen, setIsDialogOpen] = useState(false);\n const [dialogArticle, setDialogArticle] = useState<KnowledgeArticle | null>(null);\n const [search, setSearch] = useState('');\n const [category, setCategory] = useState<string>('ALL');\n const [statusFilter, setStatusFilter] = useState<string>('ALL');\n const [sort, setSort] = useState<SortKey>('recent');\n const [reading, setReading] = useState<KnowledgeArticle | null>(null);\n\n const { data, loading, refetch } = useQuery<{ knowledgeArticles: KnowledgeArticle[] }>(\n KNOWLEDGE_ARTICLES_QUERY,\n { fetchPolicy: 'cache-and-network' }\n );\n\n const articles = useMemo(() => data?.knowledgeArticles ?? [], [data?.knowledgeArticles]);\n\n const categories = useMemo(() => {\n const counts = new Map<string, number>();\n for (const a of articles) {\n for (const tag of a.tags) counts.set(tag, (counts.get(tag) ?? 0) + 1);\n }\n return [...counts.entries()].sort((a, b) => b[1] - a[1]);\n }, [articles]);\n\n const filtered = useMemo(() => {\n const term = search.trim().toLowerCase();\n const list = articles.filter((article) => {\n if (category !== 'ALL' && !article.tags.includes(category)) return false;\n if (statusFilter !== 'ALL' && article.status !== statusFilter) return false;\n if (!term) return true;\n const haystack = [\n article.title,\n article.summary ?? '',\n article.content,\n article.tags.join(' '),\n ]\n .join(' ')\n .toLowerCase();\n return haystack.includes(term);\n });\n return [...list].sort((a, b) =>\n sort === 'views'\n ? b.views - a.views\n : new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()\n );\n }, [articles, search, category, statusFilter, sort]);\n\n const statusMix = useMemo<DonutSlice[]>(() => {\n const counts = articles.reduce<Record<string, number>>((acc, a) => {\n acc[a.status] = (acc[a.status] ?? 0) + 1;\n return acc;\n }, {});\n return Object.entries(counts).map(([status, value], i) => ({\n name: STATUS_META[status]?.label ?? status,\n value,\n color: seriesColor(i),\n }));\n }, [articles]);\n\n const topByViews = useMemo(\n () =>\n [...articles]\n .filter((a) => a.views > 0)\n .sort((a, b) => b.views - a.views)\n .slice(0, 6)\n .map((a) => ({ name: a.title, views: a.views })),\n [articles]\n );\n\n const pagination = usePagination(filtered, {\n pageSize: 9,\n resetKey: `${search}|${category}|${statusFilter}|${sort}`,\n });\n\n const handleNewArticle = () => {\n setDialogArticle(null);\n setIsDialogOpen(true);\n };\n\n const handleEditArticle = (article: KnowledgeArticle) => {\n setReading(null);\n setDialogArticle(article);\n setIsDialogOpen(true);\n };\n\n return (\n <div className=\"space-y-5 p-4 sm:p-6\">\n <SectionHeader\n hero\n icon={BookOpen}\n title=\"Knowledge Base\"\n description=\"Turn one-off fixes and runbooks into repeatable, searchable operational knowledge.\"\n actions={\n <Button className=\"w-full sm:w-auto\" onClick={handleNewArticle}>\n <Plus className=\"mr-2 h-4 w-4\" />\n New Article\n </Button>\n }\n />\n <PagePurpose>\n Capture the fixes and runbooks your team relies on so the next person doesn&apos;t start\n from scratch. Link articles to incidents and assets, then search them when troubleshooting.\n </PagePurpose>\n\n <ArticleDialog\n open={isDialogOpen}\n onOpenChange={setIsDialogOpen}\n article={dialogArticle}\n onSaved={() => void refetch()}\n />\n <ArticleReader\n article={reading}\n onOpenChange={(open) => !open && setReading(null)}\n onEdit={handleEditArticle}\n />\n\n <div className=\"relative\">\n <Search className=\"absolute left-3.5 top-1/2 h-4.5 w-4.5 -translate-y-1/2 text-text-secondary\" />\n <Input\n className=\"h-12 pl-11 text-base\"\n placeholder=\"Search runbooks, fixes and procedures…\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n aria-label=\"Search knowledge articles\"\n />\n </div>\n\n {articles.length > 0 && (statusMix.length > 0 || topByViews.length > 0) ? (\n <div className=\"grid gap-4 lg:grid-cols-2\">\n <ChartCard title=\"Publishing status\" subtitle=\"Articles by status\">\n <DonutChart\n data={statusMix}\n height={200}\n centerLabel=\"Articles\"\n centerValue={articles.length}\n ariaLabel=\"Articles by status\"\n />\n </ChartCard>\n <ChartCard title=\"Most consulted\" subtitle=\"Top articles by views\">\n <BarsChart\n data={topByViews}\n xKey=\"name\"\n series={[{ key: 'views', label: 'Views' }]}\n orientation=\"horizontal\"\n height={200}\n categoryWidth={150}\n emptyLabel=\"No views recorded yet\"\n ariaLabel=\"Top articles by views\"\n />\n </ChartCard>\n </div>\n ) : null}\n\n <div className=\"grid gap-5 lg:grid-cols-[200px_1fr]\">\n {/* Category facet rail */}\n <aside className=\"space-y-3\">\n <div className=\"flex items-center justify-between gap-2\">\n <h3 className=\"text-sm font-semibold text-text-primary\">Category</h3>\n <Select value={sort} onValueChange={(v) => setSort(v as SortKey)}>\n <SelectTrigger className=\"h-8 w-[130px] text-xs lg:hidden\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"recent\">Recently updated</SelectItem>\n <SelectItem value=\"views\">Most viewed</SelectItem>\n </SelectContent>\n </Select>\n </div>\n {/* Mobile: horizontal chips; desktop: vertical list */}\n <div className=\"flex gap-2 overflow-x-auto pb-1 lg:flex-col lg:overflow-visible lg:pb-0\">\n <button\n type=\"button\"\n onClick={() => setCategory('ALL')}\n className={cn(\n 'shrink-0 rounded-md px-3 py-1.5 text-left text-sm font-medium transition-colors lg:w-full',\n category === 'ALL'\n ? 'bg-action-primary-bg/10 text-primary'\n : 'text-text-secondary hover:text-text-primary'\n )}\n >\n All articles\n <span className=\"ml-1.5 text-xs text-text-muted tabular-nums\">{articles.length}</span>\n </button>\n {categories.map(([tag, count]) => (\n <button\n key={tag}\n type=\"button\"\n onClick={() => setCategory(tag)}\n className={cn(\n 'flex shrink-0 items-center justify-between gap-2 rounded-md px-3 py-1.5 text-left text-sm font-medium transition-colors lg:w-full',\n category === tag\n ? 'bg-action-primary-bg/10 text-primary'\n : 'text-text-secondary hover:text-text-primary'\n )}\n >\n <span className=\"truncate\">{tag}</span>\n <span className=\"text-xs text-text-muted tabular-nums\">{count}</span>\n </button>\n ))}\n </div>\n <div className=\"hidden space-y-1.5 border-t border-border-seam pt-3 lg:block\">\n <Label className=\"text-xs\">Status</Label>\n <Select value={statusFilter} onValueChange={setStatusFilter}>\n <SelectTrigger className=\"h-9\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All statuses</SelectItem>\n {ARTICLE_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>\n {s}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Label className=\"text-xs\">Sort</Label>\n <Select value={sort} onValueChange={(v) => setSort(v as SortKey)}>\n <SelectTrigger className=\"h-9\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"recent\">Recently updated</SelectItem>\n <SelectItem value=\"views\">Most viewed</SelectItem>\n </SelectContent>\n </Select>\n </div>\n </aside>\n\n {/* Article grid */}\n <div>\n {loading && articles.length === 0 ? (\n <div className=\"grid gap-4 sm:grid-cols-2 xl:grid-cols-3\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div\n key={i}\n className=\"h-40 rounded-xl bg-bg-sunken motion-safe:animate-vc-shimmer\"\n />\n ))}\n </div>\n ) : null}\n {!loading && filtered.length === 0 ? (\n <IllustratedEmptyState\n illustration={\n search || category !== 'ALL' || statusFilter !== 'ALL'\n ? 'empty-search'\n : 'empty-data'\n }\n title={\n search || category !== 'ALL' || statusFilter !== 'ALL'\n ? 'No articles match your filters'\n : 'No knowledge articles found'\n }\n description={\n search || category !== 'ALL' || statusFilter !== 'ALL'\n ? 'Try a different term, category or status.'\n : 'Publish an article to start building your knowledge base.'\n }\n />\n ) : null}\n <div className=\"grid gap-4 sm:grid-cols-2 xl:grid-cols-3\">\n {pagination.pageItems.map((article) => (\n <GlassCard key={article.id} treatment=\"glass\" className=\"border-border-seam\">\n <button\n type=\"button\"\n onClick={() => setReading(article)}\n className=\"flex h-full w-full flex-col gap-3 p-4 text-left\"\n >\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <StatusPill status={article.status} />\n {article.tags.slice(0, 1).map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-action-primary-bg/10 px-2 py-0.5 text-xs font-medium text-primary\"\n >\n {tag}\n </span>\n ))}\n </div>\n <h2 className=\"line-clamp-2 text-base font-semibold text-text-primary\">\n {article.title}\n </h2>\n <p className=\"line-clamp-2 flex-1 text-sm text-text-secondary\">\n {excerptOf(article)}\n </p>\n <div className=\"flex items-center justify-between gap-2 border-t border-border-seam/60 pt-3\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className=\"flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-[10px] font-semibold text-text-secondary\">\n {article.updatedBy ? (\n initialsOf(article.updatedBy)\n ) : (\n <User className=\"h-3 w-3\" />\n )}\n </span>\n <span className=\"truncate text-xs text-text-secondary\">\n Updated {formatDate(article.updatedAt)}\n </span>\n </div>\n <span className=\"flex shrink-0 items-center gap-1 text-xs text-text-muted tabular-nums\">\n <Eye className=\"h-3.5 w-3.5\" />\n {article.views.toLocaleString()}\n </span>\n </div>\n </button>\n </GlassCard>\n ))}\n </div>\n <div className=\"mt-4\">\n <Pagination\n page={pagination.page}\n pageCount={pagination.pageCount}\n rangeStart={pagination.rangeStart}\n rangeEnd={pagination.rangeEnd}\n totalItems={pagination.totalItems}\n canPrev={pagination.canPrev}\n canNext={pagination.canNext}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"articles\"\n />\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"KnowledgeBasePage.js","names":[],"sources":["../../../src/assethandler/pages/KnowledgeBasePage.tsx"],"sourcesContent":["import { BookOpen, Plus, Search, Eye, User, Link2, ExternalLink, Pencil, X } from 'lucide-react';\nimport {\n Button,\n IllustratedEmptyState,\n GlassCard,\n PagePurpose,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { useMemo, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport {\n Pagination,\n usePagination,\n SectionHeader,\n ChartCard,\n DonutChart,\n BarsChart,\n seriesColor,\n type DonutSlice,\n} from '../components';\nimport { useAssetHandlerNavigate } from '../hooks/useAssetHandlerNavigate';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport { cn } from '../utils/cn';\nimport { notify } from '../utils/toast';\nimport { formatAssetOption, formatIncidentOption, parseTagsInput } from '../utils/knowledgeBase';\n\nconst KNOWLEDGE_ARTICLES_QUERY = gql`\n query AssetHandlerKnowledgeArticles {\n knowledgeArticles {\n id\n title\n slug\n summary\n content\n status\n tags\n views\n createdBy\n updatedBy\n publishedAt\n createdAt\n updatedAt\n relatedAssetId\n relatedIncidentId\n }\n }\n`;\n\nconst CREATE_KNOWLEDGE_ARTICLE_MUTATION = gql`\n mutation AssetHandlerCreateKnowledgeArticle($input: CreateKnowledgeArticleInput!) {\n createKnowledgeArticle(input: $input) {\n id\n title\n }\n }\n`;\n\nconst UPDATE_KNOWLEDGE_ARTICLE_MUTATION = gql`\n mutation AssetHandlerUpdateKnowledgeArticle($id: ID!, $input: UpdateKnowledgeArticleInput!) {\n updateKnowledgeArticle(id: $id, input: $input) {\n id\n title\n }\n }\n`;\n\nconst RECORD_KNOWLEDGE_ARTICLE_VIEW_MUTATION = gql`\n mutation AssetHandlerRecordKnowledgeArticleView($id: ID!) {\n recordKnowledgeArticleView(id: $id) {\n id\n views\n }\n }\n`;\n\ninterface KnowledgeArticle {\n id: string;\n title: string;\n slug: string;\n summary: string | null;\n content: string;\n status: string;\n tags: string[];\n views: number;\n createdBy: string;\n updatedBy: string;\n publishedAt: string | null;\n createdAt: string;\n updatedAt: string;\n relatedAssetId: string | null;\n relatedIncidentId: string | null;\n}\n\ninterface CreateKnowledgeArticleResult {\n createKnowledgeArticle: { id: string; title: string };\n}\n\ninterface UpdateKnowledgeArticleResult {\n updateKnowledgeArticle: { id: string; title: string };\n}\n\ninterface RecordKnowledgeArticleViewResult {\n recordKnowledgeArticleView: { id: string; views: number };\n}\n\nconst ARTICLE_STATUSES = ['DRAFT', 'PUBLISHED', 'ARCHIVED'] as const;\n\nconst STATUS_META: Record<string, { label: string; className: string }> = {\n PUBLISHED: {\n label: 'Published',\n className: 'bg-status-success-bg-subtle text-status-success-text',\n },\n DRAFT: { label: 'Draft', className: 'bg-bg-sunken text-text-secondary' },\n ARCHIVED: {\n label: 'Archived',\n className: 'bg-status-warning-bg-subtle text-status-warning-text',\n },\n};\n\nconst slugify = (value: string): string =>\n value\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n\nconst formatDate = (value: string | null): string =>\n value\n ? new Date(value).toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })\n : '—';\n\n/** A 2-line excerpt: the summary if present, otherwise the opening of the body. */\nfunction excerptOf(article: KnowledgeArticle): string {\n if (article.summary && article.summary.trim()) return article.summary.trim();\n const body = article.content.replace(/\\s+/g, ' ').trim();\n return body.length > 160 ? `${body.slice(0, 160)}…` : body;\n}\n\n/** Short avatar initials from an actor id (best-effort, no fabricated names). */\nfunction initialsOf(actor: string): string {\n const clean = actor.replace(/[^a-zA-Z0-9]/g, '');\n return (clean.slice(0, 2) || '?').toUpperCase();\n}\n\nfunction StatusPill({ status }: { status: string }) {\n const meta = STATUS_META[status] ?? {\n label: status,\n className: 'bg-bg-sunken text-text-secondary',\n };\n return (\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium',\n meta.className\n )}\n >\n {meta.label}\n </span>\n );\n}\n\nfunction ArticleDialog({\n open,\n onOpenChange,\n article,\n onSaved,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n article: KnowledgeArticle | null;\n onSaved: () => void;\n}) {\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-2xl\">\n {open && (\n <ArticleForm\n key={article?.id ?? 'new'}\n article={article}\n onOpenChange={onOpenChange}\n onSaved={onSaved}\n />\n )}\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\nfunction ArticleForm({\n article,\n onOpenChange,\n onSaved,\n}: {\n article: KnowledgeArticle | null;\n onOpenChange: (open: boolean) => void;\n onSaved: () => void;\n}) {\n const isEdit = Boolean(article);\n const { assets, incidents } = useAssetHandlerStore();\n\n const [title, setTitle] = useState(article?.title ?? '');\n const [slug, setSlug] = useState(article?.slug ?? '');\n const [slugTouched, setSlugTouched] = useState(isEdit);\n const [summary, setSummary] = useState(article?.summary ?? '');\n const [content, setContent] = useState(article?.content ?? '');\n const [tags, setTags] = useState(article ? article.tags.join(', ') : '');\n const [status, setStatus] = useState<string>(article?.status ?? 'PUBLISHED');\n const [relatedAssetId, setRelatedAssetId] = useState(article?.relatedAssetId ?? '');\n const [relatedIncidentId, setRelatedIncidentId] = useState(article?.relatedIncidentId ?? '');\n\n const reset = () => {\n setTitle('');\n setSlug('');\n setSlugTouched(false);\n setSummary('');\n setContent('');\n setTags('');\n setStatus('PUBLISHED');\n setRelatedAssetId('');\n setRelatedIncidentId('');\n };\n\n const [createArticle, { loading: creating }] = useMutation<CreateKnowledgeArticleResult>(\n CREATE_KNOWLEDGE_ARTICLE_MUTATION,\n {\n onCompleted: (data) => {\n notify.success(\n 'Article published',\n `${data.createKnowledgeArticle.title} is now available.`\n );\n reset();\n onOpenChange(false);\n onSaved();\n },\n onError: (error) => notify.error('Could not publish article', error.message),\n }\n );\n\n const [updateArticle, { loading: updating }] = useMutation<UpdateKnowledgeArticleResult>(\n UPDATE_KNOWLEDGE_ARTICLE_MUTATION,\n {\n refetchQueries: ['AssetHandlerKnowledgeArticles'],\n onCompleted: (data) => {\n notify.success('Article updated', `${data.updateKnowledgeArticle.title} has been saved.`);\n onOpenChange(false);\n onSaved();\n },\n onError: (error) => notify.error('Could not update article', error.message),\n }\n );\n\n const selectedAsset = useMemo(\n () => assets.find((a) => a.id === relatedAssetId),\n [assets, relatedAssetId]\n );\n const selectedIncident = useMemo(\n () => incidents.find((i) => i.id === relatedIncidentId),\n [incidents, relatedIncidentId]\n );\n\n const handleTitleChange = (next: string) => {\n setTitle(next);\n if (!slugTouched) setSlug(slugify(next));\n };\n\n const handleSubmit = () => {\n if (!title.trim() || !slug.trim()) {\n notify.warning('Missing details', 'Title and slug are required.');\n return;\n }\n if (!content.trim()) {\n notify.warning('Body required', 'Write the article body before publishing.');\n return;\n }\n\n const parsedTags = parseTagsInput(tags);\n const baseInput = {\n title: title.trim(),\n summary: summary.trim() || null,\n content: content.trim(),\n tags: parsedTags,\n status,\n relatedAssetId: relatedAssetId || null,\n relatedIncidentId: relatedIncidentId || null,\n };\n\n if (isEdit && article) {\n void updateArticle({\n variables: { id: article.id, input: baseInput },\n });\n } else {\n void createArticle({\n variables: {\n input: { ...baseInput, slug: slug.trim() },\n },\n });\n }\n };\n\n return (\n <>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>{isEdit ? 'Edit article' : 'New Article'}</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {isEdit\n ? 'Update the runbook or fix so the team has the latest guidance.'\n : 'Capture a runbook or fix so your team can find it later.'}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-4\">\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Title *</Label>\n <Input value={title} onChange={(e) => handleTitleChange(e.target.value)} />\n </div>\n <div className=\"space-y-2\">\n <Label>Slug *</Label>\n <Input\n value={slug}\n disabled={isEdit}\n onChange={(e) => {\n setSlugTouched(true);\n setSlug(e.target.value);\n }}\n />\n </div>\n </div>\n <div className=\"space-y-2\">\n <Label>Summary</Label>\n <Input\n placeholder=\"One-line description\"\n value={summary}\n onChange={(e) => setSummary(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Body *</Label>\n <Textarea\n placeholder=\"Write the full article body — steps, context, links…\"\n value={content}\n onChange={(e) => setContent(e.target.value)}\n rows={8}\n />\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Category tags</Label>\n <Input\n placeholder=\"Runbooks, Fixes, Procedures…\"\n value={tags}\n onChange={(e) => setTags(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Status</Label>\n <Select value={status} onValueChange={setStatus}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {ARTICLE_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>\n {s}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Related asset</Label>\n <Select value={relatedAssetId} onValueChange={setRelatedAssetId}>\n <SelectTrigger>\n <SelectValue placeholder=\"Select an asset\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"\">None</SelectItem>\n {assets.map((asset) => (\n <SelectItem key={asset.id} value={asset.id}>\n {formatAssetOption(asset)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {selectedAsset ? (\n <div className=\"flex flex-wrap gap-2\">\n <span className=\"inline-flex items-center gap-1 rounded-full bg-action-primary-bg/10 px-2 py-0.5 text-xs font-medium text-primary\">\n {formatAssetOption(selectedAsset)}\n <button\n type=\"button\"\n onClick={() => setRelatedAssetId('')}\n className=\"rounded-full p-0.5 hover:bg-action-primary-bg/20\"\n aria-label=\"Clear related asset\"\n >\n <X className=\"h-3 w-3\" />\n </button>\n </span>\n </div>\n ) : null}\n </div>\n <div className=\"space-y-2\">\n <Label>Related incident</Label>\n <Select value={relatedIncidentId} onValueChange={setRelatedIncidentId}>\n <SelectTrigger>\n <SelectValue placeholder=\"Select an incident\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"\">None</SelectItem>\n {incidents.map((incident) => (\n <SelectItem key={incident.id} value={incident.id}>\n {formatIncidentOption(incident)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {selectedIncident ? (\n <div className=\"flex flex-wrap gap-2\">\n <span className=\"inline-flex items-center gap-1 rounded-full bg-action-primary-bg/10 px-2 py-0.5 text-xs font-medium text-primary\">\n {formatIncidentOption(selectedIncident)}\n <button\n type=\"button\"\n onClick={() => setRelatedIncidentId('')}\n className=\"rounded-full p-0.5 hover:bg-action-primary-bg/20\"\n aria-label=\"Clear related incident\"\n >\n <X className=\"h-3 w-3\" />\n </button>\n </span>\n </div>\n ) : null}\n </div>\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n disabled={creating || updating}\n >\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={creating || updating}>\n {isEdit\n ? updating\n ? 'Saving…'\n : 'Save changes'\n : creating\n ? 'Publishing…'\n : 'Publish article'}\n </Button>\n </ResponsiveDialogFooter>\n </>\n );\n}\n\nfunction ArticleReader({\n article,\n onOpenChange,\n onEdit,\n}: {\n article: KnowledgeArticle | null;\n onOpenChange: (open: boolean) => void;\n onEdit: (article: KnowledgeArticle) => void;\n}) {\n const navigate = useAssetHandlerNavigate();\n return (\n <ResponsiveDialog open={Boolean(article)} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-2xl\">\n {article ? (\n <>\n <ResponsiveDialogHeader>\n <div className=\"flex flex-wrap items-center gap-2\">\n <StatusPill status={article.status} />\n {article.tags.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-action-primary-bg/10 px-2 py-0.5 text-xs font-medium text-primary\"\n >\n {tag}\n </span>\n ))}\n </div>\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"min-w-0 flex-1\">\n <ResponsiveDialogTitle className=\"mt-1\">{article.title}</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Updated {formatDate(article.updatedAt)} · {article.views.toLocaleString()} views\n </ResponsiveDialogDescription>\n </div>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n onOpenChange(false);\n onEdit(article);\n }}\n >\n <Pencil className=\"mr-1.5 h-3.5 w-3.5\" />\n Edit\n </Button>\n </div>\n </ResponsiveDialogHeader>\n <div className=\"whitespace-pre-wrap text-sm leading-relaxed text-text-primary\">\n {article.content}\n </div>\n {article.relatedAssetId || article.relatedIncidentId ? (\n <div className=\"mt-4 space-y-2 border-t border-border-seam pt-4\">\n <h4 className=\"flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-text-secondary\">\n <Link2 className=\"h-3.5 w-3.5\" />\n Linked records\n </h4>\n <div className=\"flex flex-wrap gap-2\">\n {article.relatedAssetId ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n onOpenChange(false);\n navigate(`/assets/${article.relatedAssetId}`);\n }}\n >\n View linked asset\n <ExternalLink className=\"ml-1.5 h-3.5 w-3.5\" />\n </Button>\n ) : null}\n {article.relatedIncidentId ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n onOpenChange(false);\n navigate(`/itil/incidents/${article.relatedIncidentId}`);\n }}\n >\n View linked incident\n <ExternalLink className=\"ml-1.5 h-3.5 w-3.5\" />\n </Button>\n ) : null}\n </div>\n </div>\n ) : null}\n </>\n ) : null}\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\ntype SortKey = 'recent' | 'views';\n\nexport function KnowledgeBasePage() {\n const [isDialogOpen, setIsDialogOpen] = useState(false);\n const [dialogArticle, setDialogArticle] = useState<KnowledgeArticle | null>(null);\n const [search, setSearch] = useState('');\n const [category, setCategory] = useState<string>('ALL');\n const [statusFilter, setStatusFilter] = useState<string>('ALL');\n const [sort, setSort] = useState<SortKey>('recent');\n const [reading, setReading] = useState<KnowledgeArticle | null>(null);\n\n const { data, loading, refetch } = useQuery<{ knowledgeArticles: KnowledgeArticle[] }>(\n KNOWLEDGE_ARTICLES_QUERY,\n { fetchPolicy: 'cache-and-network' }\n );\n const [recordKnowledgeArticleView] = useMutation<RecordKnowledgeArticleViewResult>(\n RECORD_KNOWLEDGE_ARTICLE_VIEW_MUTATION\n );\n\n const articles = useMemo(() => data?.knowledgeArticles ?? [], [data?.knowledgeArticles]);\n\n const categories = useMemo(() => {\n const counts = new Map<string, number>();\n for (const a of articles) {\n for (const tag of a.tags) counts.set(tag, (counts.get(tag) ?? 0) + 1);\n }\n return [...counts.entries()].sort((a, b) => b[1] - a[1]);\n }, [articles]);\n\n const filtered = useMemo(() => {\n const term = search.trim().toLowerCase();\n const list = articles.filter((article) => {\n if (category !== 'ALL' && !article.tags.includes(category)) return false;\n if (statusFilter !== 'ALL' && article.status !== statusFilter) return false;\n if (!term) return true;\n const haystack = [\n article.title,\n article.summary ?? '',\n article.content,\n article.tags.join(' '),\n ]\n .join(' ')\n .toLowerCase();\n return haystack.includes(term);\n });\n return [...list].sort((a, b) =>\n sort === 'views'\n ? b.views - a.views\n : new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()\n );\n }, [articles, search, category, statusFilter, sort]);\n\n const statusMix = useMemo<DonutSlice[]>(() => {\n const counts = articles.reduce<Record<string, number>>((acc, a) => {\n acc[a.status] = (acc[a.status] ?? 0) + 1;\n return acc;\n }, {});\n return Object.entries(counts).map(([status, value], i) => ({\n name: STATUS_META[status]?.label ?? status,\n value,\n color: seriesColor(i),\n }));\n }, [articles]);\n\n const topByViews = useMemo(\n () =>\n [...articles]\n .filter((a) => a.views > 0)\n .sort((a, b) => b.views - a.views)\n .slice(0, 6)\n .map((a) => ({ name: a.title, views: a.views })),\n [articles]\n );\n\n const pagination = usePagination(filtered, {\n pageSize: 9,\n resetKey: `${search}|${category}|${statusFilter}|${sort}`,\n });\n\n const handleNewArticle = () => {\n setDialogArticle(null);\n setIsDialogOpen(true);\n };\n\n const handleEditArticle = (article: KnowledgeArticle) => {\n setReading(null);\n setDialogArticle(article);\n setIsDialogOpen(true);\n };\n\n const handleReadArticle = (article: KnowledgeArticle) => {\n setReading(article);\n void recordKnowledgeArticleView({ variables: { id: article.id } })\n .then((result) => {\n const recorded = result.data?.recordKnowledgeArticleView;\n if (!recorded) return;\n setReading((current) =>\n current?.id === recorded.id ? { ...current, views: recorded.views } : current\n );\n void refetch();\n })\n .catch((error: unknown) => {\n notify.error(\n 'Could not record article view',\n error instanceof Error ? error.message : 'The article is still available to read.'\n );\n });\n };\n\n return (\n <div className=\"space-y-5 p-4 sm:p-6\">\n <SectionHeader\n hero\n icon={BookOpen}\n title=\"Knowledge Base\"\n description=\"Turn one-off fixes and runbooks into repeatable, searchable operational knowledge.\"\n actions={\n <Button className=\"w-full sm:w-auto\" onClick={handleNewArticle}>\n <Plus className=\"mr-2 h-4 w-4\" />\n New Article\n </Button>\n }\n />\n <PagePurpose>\n Capture the fixes and runbooks your team relies on so the next person doesn&apos;t start\n from scratch. Link articles to incidents and assets, then search them when troubleshooting.\n </PagePurpose>\n\n <ArticleDialog\n open={isDialogOpen}\n onOpenChange={setIsDialogOpen}\n article={dialogArticle}\n onSaved={() => void refetch()}\n />\n <ArticleReader\n article={reading}\n onOpenChange={(open) => !open && setReading(null)}\n onEdit={handleEditArticle}\n />\n\n <div className=\"relative\">\n <Search className=\"absolute left-3.5 top-1/2 h-4.5 w-4.5 -translate-y-1/2 text-text-secondary\" />\n <Input\n className=\"h-12 pl-11 text-base\"\n placeholder=\"Search runbooks, fixes and procedures…\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n aria-label=\"Search knowledge articles\"\n />\n </div>\n\n {articles.length > 0 && (statusMix.length > 0 || topByViews.length > 0) ? (\n <div className=\"grid gap-4 lg:grid-cols-2\">\n <ChartCard title=\"Publishing status\" subtitle=\"Articles by status\">\n <DonutChart\n data={statusMix}\n height={200}\n centerLabel=\"Articles\"\n centerValue={articles.length}\n ariaLabel=\"Articles by status\"\n />\n </ChartCard>\n <ChartCard title=\"Most consulted\" subtitle=\"Top articles by views\">\n <BarsChart\n data={topByViews}\n xKey=\"name\"\n series={[{ key: 'views', label: 'Views' }]}\n orientation=\"horizontal\"\n height={200}\n categoryWidth={150}\n emptyLabel=\"No views recorded yet\"\n ariaLabel=\"Top articles by views\"\n />\n </ChartCard>\n </div>\n ) : null}\n\n <div className=\"grid gap-5 lg:grid-cols-[200px_1fr]\">\n {/* Category facet rail */}\n <aside className=\"space-y-3\">\n <div className=\"flex items-center justify-between gap-2\">\n <h3 className=\"text-sm font-semibold text-text-primary\">Category</h3>\n <Select value={sort} onValueChange={(v) => setSort(v as SortKey)}>\n <SelectTrigger className=\"h-8 w-[130px] text-xs lg:hidden\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"recent\">Recently updated</SelectItem>\n <SelectItem value=\"views\">Most viewed</SelectItem>\n </SelectContent>\n </Select>\n </div>\n {/* Mobile: horizontal chips; desktop: vertical list */}\n <div className=\"flex gap-2 overflow-x-auto pb-1 lg:flex-col lg:overflow-visible lg:pb-0\">\n <button\n type=\"button\"\n onClick={() => setCategory('ALL')}\n className={cn(\n 'shrink-0 rounded-md px-3 py-1.5 text-left text-sm font-medium transition-colors lg:w-full',\n category === 'ALL'\n ? 'bg-action-primary-bg/10 text-primary'\n : 'text-text-secondary hover:text-text-primary'\n )}\n >\n All articles\n <span className=\"ml-1.5 text-xs text-text-muted tabular-nums\">{articles.length}</span>\n </button>\n {categories.map(([tag, count]) => (\n <button\n key={tag}\n type=\"button\"\n onClick={() => setCategory(tag)}\n className={cn(\n 'flex shrink-0 items-center justify-between gap-2 rounded-md px-3 py-1.5 text-left text-sm font-medium transition-colors lg:w-full',\n category === tag\n ? 'bg-action-primary-bg/10 text-primary'\n : 'text-text-secondary hover:text-text-primary'\n )}\n >\n <span className=\"truncate\">{tag}</span>\n <span className=\"text-xs text-text-muted tabular-nums\">{count}</span>\n </button>\n ))}\n </div>\n <div className=\"hidden space-y-1.5 border-t border-border-seam pt-3 lg:block\">\n <Label className=\"text-xs\">Status</Label>\n <Select value={statusFilter} onValueChange={setStatusFilter}>\n <SelectTrigger className=\"h-9\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All statuses</SelectItem>\n {ARTICLE_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>\n {s}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Label className=\"text-xs\">Sort</Label>\n <Select value={sort} onValueChange={(v) => setSort(v as SortKey)}>\n <SelectTrigger className=\"h-9\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"recent\">Recently updated</SelectItem>\n <SelectItem value=\"views\">Most viewed</SelectItem>\n </SelectContent>\n </Select>\n </div>\n </aside>\n\n {/* Article grid */}\n <div>\n {loading && articles.length === 0 ? (\n <div className=\"grid gap-4 sm:grid-cols-2 xl:grid-cols-3\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div\n key={i}\n className=\"h-40 rounded-xl bg-bg-sunken motion-safe:animate-vc-shimmer\"\n />\n ))}\n </div>\n ) : null}\n {!loading && filtered.length === 0 ? (\n <IllustratedEmptyState\n illustration={\n search || category !== 'ALL' || statusFilter !== 'ALL'\n ? 'empty-search'\n : 'empty-data'\n }\n title={\n search || category !== 'ALL' || statusFilter !== 'ALL'\n ? 'No articles match your filters'\n : 'No knowledge articles found'\n }\n description={\n search || category !== 'ALL' || statusFilter !== 'ALL'\n ? 'Try a different term, category or status.'\n : 'Publish an article to start building your knowledge base.'\n }\n />\n ) : null}\n <div className=\"grid gap-4 sm:grid-cols-2 xl:grid-cols-3\">\n {pagination.pageItems.map((article) => (\n <GlassCard key={article.id} treatment=\"glass\" className=\"border-border-seam\">\n <button\n type=\"button\"\n onClick={() => handleReadArticle(article)}\n className=\"flex h-full w-full flex-col gap-3 p-4 text-left\"\n >\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <StatusPill status={article.status} />\n {article.tags.slice(0, 1).map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-action-primary-bg/10 px-2 py-0.5 text-xs font-medium text-primary\"\n >\n {tag}\n </span>\n ))}\n </div>\n <h2 className=\"line-clamp-2 text-base font-semibold text-text-primary\">\n {article.title}\n </h2>\n <p className=\"line-clamp-2 flex-1 text-sm text-text-secondary\">\n {excerptOf(article)}\n </p>\n <div className=\"flex items-center justify-between gap-2 border-t border-border-seam/60 pt-3\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className=\"flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-[10px] font-semibold text-text-secondary\">\n {article.updatedBy ? (\n initialsOf(article.updatedBy)\n ) : (\n <User className=\"h-3 w-3\" />\n )}\n </span>\n <span className=\"truncate text-xs text-text-secondary\">\n Updated {formatDate(article.updatedAt)}\n </span>\n </div>\n <span className=\"flex shrink-0 items-center gap-1 text-xs text-text-muted tabular-nums\">\n <Eye className=\"h-3.5 w-3.5\" />\n {article.views.toLocaleString()}\n </span>\n </div>\n </button>\n </GlassCard>\n ))}\n </div>\n <div className=\"mt-4\">\n <Pagination\n page={pagination.page}\n pageCount={pagination.pageCount}\n rangeStart={pagination.rangeStart}\n rangeEnd={pagination.rangeEnd}\n totalItems={pagination.totalItems}\n canPrev={pagination.canPrev}\n canNext={pagination.canNext}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"articles\"\n />\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAwCA,IAAM,KAA2B,CAAG;;;;;;;;;;;;;;;;;;;;GAsB9B,IAAoC,CAAG;;;;;;;GASvC,KAAoC,CAAG;;;;;;;GASvC,KAAyC,CAAG;;;;;;;GAuC5C,KAAmB;CAAC;CAAS;CAAa;AAAU,GAEpD,IAAoE;CACxE,WAAW;EACT,OAAO;EACP,WAAW;CACb;CACA,OAAO;EAAE,OAAO;EAAS,WAAW;CAAmC;CACvE,UAAU;EACR,OAAO;EACP,WAAW;CACb;AACF,GAEM,KAAW,MACf,EACG,YAAY,EACZ,KAAK,EACL,QAAQ,eAAe,GAAG,EAC1B,QAAQ,YAAY,EAAE,GAErB,KAAc,MAClB,IACI,IAAI,KAAK,CAAK,EAAE,mBAAmB,KAAA,GAAW;CAC5C,MAAM;CACN,OAAO;CACP,KAAK;AACP,CAAC,IACD;AAGN,SAAS,EAAU,GAAmC;CACpD,IAAI,EAAQ,WAAW,EAAQ,QAAQ,KAAK,GAAG,OAAO,EAAQ,QAAQ,KAAK;CAC3E,IAAM,IAAO,EAAQ,QAAQ,QAAQ,QAAQ,GAAG,EAAE,KAAK;CACvD,OAAO,EAAK,SAAS,MAAM,GAAG,EAAK,MAAM,GAAG,GAAG,EAAE,KAAK;AACxD;AAGA,SAAS,GAAW,GAAuB;CAEzC,QADc,EAAM,QAAQ,iBAAiB,EACrC,EAAM,MAAM,GAAG,CAAC,KAAK,KAAK,YAAY;AAChD;AAEA,SAAS,EAAW,EAAE,aAA8B;CAClD,IAAM,IAAO,EAAY,MAAW;EAClC,OAAO;EACP,WAAW;CACb;CACA,OACE,kBAAC,QAAD;EACE,WAAW,EACT,yEACA,EAAK,SACP;YAEC,EAAK;CACF,CAAA;AAEV;AAEA,SAAS,GAAc,EACrB,SACA,iBACA,YACA,cAMC;CACD,OACE,kBAAC,GAAD;EAAwB;EAAoB;YAC1C,kBAAC,GAAD;GAAyB,WAAU;aAChC,KACC,kBAAC,IAAD;IAEW;IACK;IACL;GACV,GAJM,GAAS,MAAM,KAIrB;EAEoB,CAAA;CACT,CAAA;AAEtB;AAEA,SAAS,GAAY,EACnB,YACA,iBACA,cAKC;CACD,IAAM,IAAS,EAAQ,GACjB,EAAE,WAAQ,iBAAc,EAAqB,GAE7C,CAAC,GAAO,KAAY,EAAS,GAAS,SAAS,EAAE,GACjD,CAAC,GAAM,KAAW,EAAS,GAAS,QAAQ,EAAE,GAC9C,CAAC,GAAa,KAAkB,EAAS,CAAM,GAC/C,CAAC,GAAS,KAAc,EAAS,GAAS,WAAW,EAAE,GACvD,CAAC,GAAS,KAAc,EAAS,GAAS,WAAW,EAAE,GACvD,CAAC,GAAM,KAAW,EAAS,IAAU,EAAQ,KAAK,KAAK,IAAI,IAAI,EAAE,GACjE,CAAC,GAAQ,MAAa,EAAiB,GAAS,UAAU,WAAW,GACrE,CAAC,GAAgB,KAAqB,EAAS,GAAS,kBAAkB,EAAE,GAC5E,CAAC,GAAmB,KAAwB,EAAS,GAAS,qBAAqB,EAAE,GAErF,WAAc;EASlB,AARA,EAAS,EAAE,GACX,EAAQ,EAAE,GACV,EAAe,EAAK,GACpB,EAAW,EAAE,GACb,EAAW,EAAE,GACb,EAAQ,EAAE,GACV,GAAU,WAAW,GACrB,EAAkB,EAAE,GACpB,EAAqB,EAAE;CACzB,GAEM,CAAC,IAAe,EAAE,SAAS,OAAc,EAC7C,GACA;EACE,cAAc,MAAS;GAOrB,AANA,EAAO,QACL,qBACA,GAAG,EAAK,uBAAuB,MAAM,mBACvC,GACA,GAAM,GACN,EAAa,EAAK,GAClB,EAAQ;EACV;EACA,UAAU,MAAU,EAAO,MAAM,6BAA6B,EAAM,OAAO;CAC7E,CACF,GAEM,CAAC,GAAe,EAAE,SAAS,OAAc,EAC7C,IACA;EACE,gBAAgB,CAAC,+BAA+B;EAChD,cAAc,MAAS;GAGrB,AAFA,EAAO,QAAQ,mBAAmB,GAAG,EAAK,uBAAuB,MAAM,iBAAiB,GACxF,EAAa,EAAK,GAClB,EAAQ;EACV;EACA,UAAU,MAAU,EAAO,MAAM,4BAA4B,EAAM,OAAO;CAC5E,CACF,GAEM,KAAgB,QACd,EAAO,MAAM,MAAM,EAAE,OAAO,CAAc,GAChD,CAAC,GAAQ,CAAc,CACzB,GACM,IAAmB,QACjB,EAAU,MAAM,MAAM,EAAE,OAAO,CAAiB,GACtD,CAAC,GAAW,CAAiB,CAC/B,GAEM,MAAqB,MAAiB;EAE1C,AADA,EAAS,CAAI,GACR,KAAa,EAAQ,EAAQ,CAAI,CAAC;CACzC;CAoCA,OACE,kBAAA,GAAA,EAAA,UAAA;EACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAwB,IAAS,iBAAiB,cAAqC,CAAA,GACvF,kBAAC,GAAD,EAAA,UACG,IACG,mEACA,2DACuB,CAAA,CACP,EAAA,CAAA;EACxB,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAA,UAAO,UAAc,CAAA,GACrB,kBAAC,GAAD;OAAO,OAAO;OAAO,WAAW,MAAM,GAAkB,EAAE,OAAO,KAAK;MAAI,CAAA,CACvE;SACL,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAA,UAAO,SAAa,CAAA,GACpB,kBAAC,GAAD;OACE,OAAO;OACP,UAAU;OACV,WAAW,MAAM;QAEf,AADA,EAAe,EAAI,GACnB,EAAQ,EAAE,OAAO,KAAK;OACxB;MACD,CAAA,CACE;OACF;;IACL,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAA,UAAO,UAAc,CAAA,GACrB,kBAAC,GAAD;MACE,aAAY;MACZ,OAAO;MACP,WAAW,MAAM,EAAW,EAAE,OAAO,KAAK;KAC3C,CAAA,CACE;;IACL,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAA,UAAO,SAAa,CAAA,GACpB,kBAAC,IAAD;MACE,aAAY;MACZ,OAAO;MACP,WAAW,MAAM,EAAW,EAAE,OAAO,KAAK;MAC1C,MAAM;KACP,CAAA,CACE;;IACL,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAA,UAAO,gBAAoB,CAAA,GAC3B,kBAAC,GAAD;OACE,aAAY;OACZ,OAAO;OACP,WAAW,MAAM,EAAQ,EAAE,OAAO,KAAK;MACxC,CAAA,CACE;SACL,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAA,UAAO,SAAa,CAAA,GACpB,kBAAC,GAAD;OAAQ,OAAO;OAAQ,eAAe;iBAAtC,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,CAAc,CAAA,EACD,CAAA,GACf,kBAAC,GAAD,EAAA,UACG,GAAiB,KAAK,MACrB,kBAAC,GAAD;QAAoB,OAAO;kBACxB;OACS,GAFK,CAEL,CACb,EACY,CAAA,CACT;QACL;OACF;;IAEL,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,GAAD,EAAA,UAAO,gBAAoB,CAAA;OAC3B,kBAAC,GAAD;QAAQ,OAAO;QAAgB,eAAe;kBAA9C,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAa,aAAY,kBAAmB,CAAA,EAC/B,CAAA,GACf,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;SAAY,OAAM;mBAAG;QAAgB,CAAA,GACpC,EAAO,KAAK,MACX,kBAAC,GAAD;SAA2B,OAAO,EAAM;mBACrC,EAAkB,CAAK;QACd,GAFK,EAAM,EAEX,CACb,CACY,EAAA,CAAA,CACT;;OACP,KACC,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CACG,EAAkB,EAAa,GAChC,kBAAC,UAAD;UACE,MAAK;UACL,eAAe,EAAkB,EAAE;UACnC,WAAU;UACV,cAAW;oBAEX,kBAAC,GAAD,EAAG,WAAU,UAAW,CAAA;SAClB,CAAA,CACJ;;OACH,CAAA,IACH;MACD;SACL,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,GAAD,EAAA,UAAO,mBAAuB,CAAA;OAC9B,kBAAC,GAAD;QAAQ,OAAO;QAAmB,eAAe;kBAAjD,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAa,aAAY,qBAAsB,CAAA,EAClC,CAAA,GACf,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;SAAY,OAAM;mBAAG;QAAgB,CAAA,GACpC,EAAU,KAAK,MACd,kBAAC,GAAD;SAA8B,OAAO,EAAS;mBAC3C,EAAqB,CAAQ;QACpB,GAFK,EAAS,EAEd,CACb,CACY,EAAA,CAAA,CACT;;OACP,IACC,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CACG,EAAqB,CAAgB,GACtC,kBAAC,UAAD;UACE,MAAK;UACL,eAAe,EAAqB,EAAE;UACtC,WAAU;UACV,cAAW;oBAEX,kBAAC,GAAD,EAAG,WAAU,UAAW,CAAA;SAClB,CAAA,CACJ;;OACH,CAAA,IACH;MACD;OACF;;GACF;;EACL,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;GACE,SAAQ;GACR,eAAe,EAAa,EAAK;GACjC,UAAU,KAAY;aACvB;EAEO,CAAA,GACR,kBAAC,GAAD;GAAQ,eAlLa;IACzB,IAAI,CAAC,EAAM,KAAK,KAAK,CAAC,EAAK,KAAK,GAAG;KACjC,EAAO,QAAQ,mBAAmB,8BAA8B;KAChE;IACF;IACA,IAAI,CAAC,EAAQ,KAAK,GAAG;KACnB,EAAO,QAAQ,iBAAiB,2CAA2C;KAC3E;IACF;IAEA,IAAM,IAAa,EAAe,CAAI,GAChC,IAAY;KAChB,OAAO,EAAM,KAAK;KAClB,SAAS,EAAQ,KAAK,KAAK;KAC3B,SAAS,EAAQ,KAAK;KACtB,MAAM;KACN;KACA,gBAAgB,KAAkB;KAClC,mBAAmB,KAAqB;IAC1C;IAEA,AAAI,KAAU,IACZ,EAAmB,EACjB,WAAW;KAAE,IAAI,EAAQ;KAAI,OAAO;IAAU,EAChD,CAAC,IAED,GAAmB,EACjB,WAAW,EACT,OAAO;KAAE,GAAG;KAAW,MAAM,EAAK,KAAK;IAAE,EAC3C,EACF,CAAC;GAEL;GAkJqC,UAAU,KAAY;aAClD,IACG,IACE,YACA,iBACF,IACE,gBACA;EACA,CAAA,CACc,EAAA,CAAA;CACxB,EAAA,CAAA;AAEN;AAEA,SAAS,GAAc,EACrB,YACA,iBACA,aAKC;CACD,IAAM,IAAW,EAAwB;CACzC,OACE,kBAAC,GAAD;EAAkB,MAAM,EAAQ;EAAwB;YACtD,kBAAC,GAAD;GAAyB,WAAU;aAChC,IACC,kBAAA,GAAA,EAAA,UAAA;IACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAY,QAAQ,EAAQ,OAAS,CAAA,GACpC,EAAQ,KAAK,KAAK,MACjB,kBAAC,QAAD;MAEE,WAAU;gBAET;KACG,GAJC,CAID,CACP,CACE;QACL,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OAAuB,WAAU;iBAAQ,EAAQ;MAA6B,CAAA,GAC9E,kBAAC,GAAD,EAAA,UAAA;OAA6B;OAClB,EAAW,EAAQ,SAAS;OAAE;OAAI,EAAQ,MAAM,eAAe;OAAE;MAC/C,EAAA,CAAA,CAC1B;SACL,kBAAC,GAAD;MACE,SAAQ;MACR,MAAK;MACL,eAAe;OAEb,AADA,EAAa,EAAK,GAClB,EAAO,CAAO;MAChB;gBANF,CAQE,kBAAC,GAAD,EAAQ,WAAU,qBAAsB,CAAA,GAAC,MAEnC;OACL;MACiB,EAAA,CAAA;IACxB,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAQ;IACN,CAAA;IACJ,EAAQ,kBAAkB,EAAQ,oBACjC,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAAd,CACE,kBAAC,GAAD,EAAO,WAAU,cAAe,CAAA,GAAC,gBAE/B;SACJ,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACG,EAAQ,iBACP,kBAAC,GAAD;OACE,SAAQ;OACR,MAAK;OACL,eAAe;QAEb,AADA,EAAa,EAAK,GAClB,EAAS,WAAW,EAAQ,gBAAgB;OAC9C;iBANF,CAOC,qBAEC,kBAAC,GAAD,EAAc,WAAU,qBAAsB,CAAA,CACxC;WACN,MACH,EAAQ,oBACP,kBAAC,GAAD;OACE,SAAQ;OACR,MAAK;OACL,eAAe;QAEb,AADA,EAAa,EAAK,GAClB,EAAS,mBAAmB,EAAQ,mBAAmB;OACzD;iBANF,CAOC,wBAEC,kBAAC,GAAD,EAAc,WAAU,qBAAsB,CAAA,CACxC;WACN,IACD;OACF;SACH;GACJ,EAAA,CAAA,IACA;EACmB,CAAA;CACT,CAAA;AAEtB;AAIA,SAAgB,KAAoB;CAClC,IAAM,CAAC,GAAc,KAAmB,EAAS,EAAK,GAChD,CAAC,GAAe,KAAoB,EAAkC,IAAI,GAC1E,CAAC,GAAQ,KAAa,EAAS,EAAE,GACjC,CAAC,GAAU,KAAe,EAAiB,KAAK,GAChD,CAAC,GAAc,KAAmB,EAAiB,KAAK,GACxD,CAAC,GAAM,KAAW,EAAkB,QAAQ,GAC5C,CAAC,GAAS,KAAc,EAAkC,IAAI,GAE9D,EAAE,SAAM,YAAS,eAAY,EACjC,IACA,EAAE,aAAa,oBAAoB,CACrC,GACM,CAAC,MAA8B,EACnC,EACF,GAEM,IAAW,QAAc,GAAM,qBAAqB,CAAC,GAAG,CAAC,GAAM,iBAAiB,CAAC,GAEjF,KAAa,QAAc;EAC/B,IAAM,oBAAS,IAAI,IAAoB;EACvC,KAAK,IAAM,KAAK,GACd,KAAK,IAAM,KAAO,EAAE,MAAM,EAAO,IAAI,IAAM,EAAO,IAAI,CAAG,KAAK,KAAK,CAAC;EAEtE,OAAO,CAAC,GAAG,EAAO,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,EAAE;CACzD,GAAG,CAAC,CAAQ,CAAC,GAEP,IAAW,QAAc;EAC7B,IAAM,IAAO,EAAO,KAAK,EAAE,YAAY;EAevC,OAAO,CAAC,GAdK,EAAS,QAAQ,MACxB,MAAa,SAAS,CAAC,EAAQ,KAAK,SAAS,CAAQ,KACrD,MAAiB,SAAS,EAAQ,WAAW,IAAqB,KACjE,IACY;GACf,EAAQ;GACR,EAAQ,WAAW;GACnB,EAAQ;GACR,EAAQ,KAAK,KAAK,GAAG;EACvB,EACG,KAAK,GAAG,EACR,YACI,EAAS,SAAS,CAAI,IATX,EAWT,CAAI,EAAE,MAAM,GAAG,MACxB,MAAS,UACL,EAAE,QAAQ,EAAE,QACZ,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,IAAI,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,CACtE;CACF,GAAG;EAAC;EAAU;EAAQ;EAAU;EAAc;CAAI,CAAC,GAE7C,KAAY,QAA4B;EAC5C,IAAM,IAAS,EAAS,QAAgC,GAAK,OAC3D,EAAI,EAAE,WAAW,EAAI,EAAE,WAAW,KAAK,GAChC,IACN,CAAC,CAAC;EACL,OAAO,OAAO,QAAQ,CAAM,EAAE,KAAK,CAAC,GAAQ,IAAQ,OAAO;GACzD,MAAM,EAAY,IAAS,SAAS;GACpC;GACA,OAAO,EAAY,CAAC;EACtB,EAAE;CACJ,GAAG,CAAC,CAAQ,CAAC,GAEP,KAAa,QAEf,CAAC,GAAG,CAAQ,EACT,QAAQ,MAAM,EAAE,QAAQ,CAAC,EACzB,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,EAChC,MAAM,GAAG,CAAC,EACV,KAAK,OAAO;EAAE,MAAM,EAAE;EAAO,OAAO,EAAE;CAAM,EAAE,GACnD,CAAC,CAAQ,CACX,GAEM,IAAa,EAAc,GAAU;EACzC,UAAU;EACV,UAAU,GAAG,EAAO,GAAG,EAAS,GAAG,EAAa,GAAG;CACrD,CAAC,GAEK,WAAyB;EAE7B,AADA,EAAiB,IAAI,GACrB,EAAgB,EAAI;CACtB,GAEM,MAAqB,MAA8B;EAGvD,AAFA,EAAW,IAAI,GACf,EAAiB,CAAO,GACxB,EAAgB,EAAI;CACtB,GAEM,MAAqB,MAA8B;EAEvD,AADA,EAAW,CAAO,GAClB,GAAgC,EAAE,WAAW,EAAE,IAAI,EAAQ,GAAG,EAAE,CAAC,EAC9D,MAAM,MAAW;GAChB,IAAM,IAAW,EAAO,MAAM;GACzB,MACL,GAAY,MACV,GAAS,OAAO,EAAS,KAAK;IAAE,GAAG;IAAS,OAAO,EAAS;GAAM,IAAI,CACxE,GACA,EAAa;EACf,CAAC,EACA,OAAO,MAAmB;GACzB,EAAO,MACL,iCACA,aAAiB,QAAQ,EAAM,UAAU,yCAC3C;EACF,CAAC;CACL;CAEA,OACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD;IACE,MAAA;IACA,MAAM;IACN,OAAM;IACN,aAAY;IACZ,SACE,kBAAC,GAAD;KAAQ,WAAU;KAAmB,SAAS;eAA9C,CACE,kBAAC,GAAD,EAAM,WAAU,eAAgB,CAAA,GAAC,aAE3B;;GAEX,CAAA;GACD,kBAAC,GAAD,EAAA,UAAa,kLAGA,CAAA;GAEb,kBAAC,IAAD;IACE,MAAM;IACN,cAAc;IACd,SAAS;IACT,eAAe,KAAK,EAAQ;GAC7B,CAAA;GACD,kBAAC,IAAD;IACE,SAAS;IACT,eAAe,MAAS,CAAC,KAAQ,EAAW,IAAI;IAChD,QAAQ;GACT,CAAA;GAED,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,6EAA8E,CAAA,GAChG,kBAAC,GAAD;KACE,WAAU;KACV,aAAY;KACZ,OAAO;KACP,WAAW,MAAM,EAAU,EAAE,OAAO,KAAK;KACzC,cAAW;IACZ,CAAA,CACE;;GAEJ,EAAS,SAAS,MAAM,GAAU,SAAS,KAAK,GAAW,SAAS,KACnE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAW,OAAM;KAAoB,UAAS;eAC5C,kBAAC,GAAD;MACE,MAAM;MACN,QAAQ;MACR,aAAY;MACZ,aAAa,EAAS;MACtB,WAAU;KACX,CAAA;IACQ,CAAA,GACX,kBAAC,GAAD;KAAW,OAAM;KAAiB,UAAS;eACzC,kBAAC,GAAD;MACE,MAAM;MACN,MAAK;MACL,QAAQ,CAAC;OAAE,KAAK;OAAS,OAAO;MAAQ,CAAC;MACzC,aAAY;MACZ,QAAQ;MACR,eAAe;MACf,YAAW;MACX,WAAU;KACX,CAAA;IACQ,CAAA,CACR;QACH;GAEJ,kBAAC,OAAD;IAAK,WAAU;cAAf,CAEE,kBAAC,SAAD;KAAO,WAAU;eAAjB;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,MAAD;QAAI,WAAU;kBAA0C;OAAY,CAAA,GACpE,kBAAC,GAAD;QAAQ,OAAO;QAAM,gBAAgB,MAAM,EAAQ,CAAY;kBAA/D,CACE,kBAAC,GAAD;SAAe,WAAU;mBACvB,kBAAC,GAAD,CAAc,CAAA;QACD,CAAA,GACf,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;SAAY,OAAM;mBAAS;QAA4B,CAAA,GACvD,kBAAC,GAAD;SAAY,OAAM;mBAAQ;QAAuB,CAAA,CACpC,EAAA,CAAA,CACT;SACL;;MAEL,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAY,KAAK;QAChC,WAAW,EACT,6FACA,MAAa,QACT,yCACA,6CACN;kBARF,CASC,gBAEC,kBAAC,QAAD;SAAM,WAAU;mBAA+C,EAAS;QAAa,CAAA,CAC/E;WACP,GAAW,KAAK,CAAC,GAAK,OACrB,kBAAC,UAAD;QAEE,MAAK;QACL,eAAe,EAAY,CAAG;QAC9B,WAAW,EACT,qIACA,MAAa,IACT,yCACA,6CACN;kBATF,CAWE,kBAAC,QAAD;SAAM,WAAU;mBAAY;QAAU,CAAA,GACtC,kBAAC,QAAD;SAAM,WAAU;mBAAwC;QAAY,CAAA,CAC9D;UAZD,CAYC,CACT,CACE;;MACL,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD;SAAO,WAAU;mBAAU;QAAa,CAAA;QACxC,kBAAC,GAAD;SAAQ,OAAO;SAAc,eAAe;mBAA5C,CACE,kBAAC,GAAD;UAAe,WAAU;oBACvB,kBAAC,GAAD,CAAc,CAAA;SACD,CAAA,GACf,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;UAAY,OAAM;oBAAM;SAAwB,CAAA,GAC/C,GAAiB,KAAK,MACrB,kBAAC,GAAD;UAAoB,OAAO;oBACxB;SACS,GAFK,CAEL,CACb,CACY,EAAA,CAAA,CACT;;QACR,kBAAC,GAAD;SAAO,WAAU;mBAAU;QAAW,CAAA;QACtC,kBAAC,GAAD;SAAQ,OAAO;SAAM,gBAAgB,MAAM,EAAQ,CAAY;mBAA/D,CACE,kBAAC,GAAD;UAAe,WAAU;oBACvB,kBAAC,GAAD,CAAc,CAAA;SACD,CAAA,GACf,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;UAAY,OAAM;oBAAS;SAA4B,CAAA,GACvD,kBAAC,GAAD;UAAY,OAAM;oBAAQ;SAAuB,CAAA,CACpC,EAAA,CAAA,CACT;;OACL;;KACA;QAGP,kBAAC,OAAD,EAAA,UAAA;KACG,KAAW,EAAS,WAAW,IAC9B,kBAAC,OAAD;MAAK,WAAU;gBACZ,MAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,GAAG,MACjC,kBAAC,OAAD,EAEE,WAAU,8DACX,GAFM,CAEN,CACF;KACE,CAAA,IACH;KACH,CAAC,KAAW,EAAS,WAAW,IAC/B,kBAAC,GAAD;MACE,cACE,KAAU,MAAa,SAAS,MAAiB,QAC7C,iBACA;MAEN,OACE,KAAU,MAAa,SAAS,MAAiB,QAC7C,mCACA;MAEN,aACE,KAAU,MAAa,SAAS,MAAiB,QAC7C,8CACA;KAEP,CAAA,IACC;KACJ,kBAAC,OAAD;MAAK,WAAU;gBACZ,EAAW,UAAU,KAAK,MACzB,kBAAC,IAAD;OAA4B,WAAU;OAAQ,WAAU;iBACtD,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,GAAkB,CAAO;QACxC,WAAU;kBAHZ;SAKE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,GAAD,EAAY,QAAQ,EAAQ,OAAS,CAAA,GACpC,EAAQ,KAAK,MAAM,GAAG,CAAC,EAAE,KAAK,MAC7B,kBAAC,QAAD;WAEE,WAAU;qBAET;UACG,GAJC,CAID,CACP,CACE;;SACL,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAQ;SACP,CAAA;SACJ,kBAAC,KAAD;UAAG,WAAU;oBACV,EAAU,CAAO;SACjB,CAAA;SACH,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,QAAD;YAAM,WAAU;sBACb,EAAQ,YACP,GAAW,EAAQ,SAAS,IAE5B,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA;WAEzB,CAAA,GACN,kBAAC,QAAD;YAAM,WAAU;sBAAhB,CAAuD,YAC5C,EAAW,EAAQ,SAAS,CACjC;aACH;cACL,kBAAC,QAAD;WAAM,WAAU;qBAAhB,CACE,kBAAC,GAAD,EAAK,WAAU,cAAe,CAAA,GAC7B,EAAQ,MAAM,eAAe,CAC1B;YACH;;QACC;;MACC,GA1CK,EAAQ,EA0Cb,CACZ;KACE,CAAA;KACL,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD;OACE,MAAM,EAAW;OACjB,WAAW,EAAW;OACtB,YAAY,EAAW;OACvB,UAAU,EAAW;OACrB,YAAY,EAAW;OACvB,SAAS,EAAW;OACpB,SAAS,EAAW;OACpB,QAAQ,EAAW;OACnB,QAAQ,EAAW;OACnB,WAAU;MACX,CAAA;KACE,CAAA;IACF,EAAA,CAAA,CACF;;EACF;;AAET"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-assethandler",
3
- "version": "2026.826.4",
3
+ "version": "2026.826.5",
4
4
  "description": "Burdenoff AssetHandler Microfrontend",
5
5
  "type": "module",
6
6
  "files": [