@elevasis/ui 2.69.0 → 2.70.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/index.js +7 -7
- package/dist/auth/index.css +149 -149
- package/dist/auth/index.js +3 -3
- package/dist/charts/index.css +208 -208
- package/dist/charts/index.js +3 -3
- package/dist/{chunk-ZD6LM53O.js → chunk-5C62IRKS.js} +2 -2
- package/dist/{chunk-WXHAHKKI.js → chunk-6TT3FQEZ.js} +16204 -16039
- package/dist/{chunk-MFLM7BVC.js → chunk-AGX2CY2O.js} +1 -1
- package/dist/{chunk-IRV6KJPV.js → chunk-CCZJ4OWY.js} +1 -1
- package/dist/{chunk-XAZR3T6Y.js → chunk-DUEGHF7A.js} +3 -3
- package/dist/{chunk-NU3NBXHP.js → chunk-GJQF3S2Q.js} +1 -1
- package/dist/{chunk-YNRYIXUT.js → chunk-GZADST64.js} +6 -6
- package/dist/{chunk-YCRINA2D.js → chunk-JA2XDMAE.js} +1 -1
- package/dist/{chunk-6PZFOHJK.js → chunk-LWYLZUUV.js} +2 -2
- package/dist/{chunk-7WJEPTE7.js → chunk-LY3IKYTZ.js} +1 -1
- package/dist/{chunk-RW64UJWT.js → chunk-MFCV4JMQ.js} +2 -2
- package/dist/{chunk-IT7GTJCB.js → chunk-QDMIFD6F.js} +1 -1
- package/dist/{chunk-FLECOYUS.js → chunk-R6NMRXNO.js} +1 -1
- package/dist/{chunk-XQ3ILKYN.js → chunk-SMH6SQQE.js} +1 -1
- package/dist/{chunk-MRVGQLWN.js → chunk-TJMZUL6E.js} +1 -1
- package/dist/{chunk-YGCMMOY4.js → chunk-XKUEO4HG.js} +695 -73
- package/dist/{chunk-4JHLS234.js → chunk-ZFISGKYW.js} +1 -1
- package/dist/components/index.css +208 -208
- package/dist/components/index.js +24 -25
- package/dist/components/navigation/index.css +149 -149
- package/dist/components/navigation/index.js +4 -4
- package/dist/features/auth/index.css +149 -149
- package/dist/features/auth/index.js +5 -5
- package/dist/features/clients/index.css +208 -208
- package/dist/features/clients/index.js +4 -4
- package/dist/features/content/index.d.ts +238 -48
- package/dist/features/content/index.js +542 -410
- package/dist/features/crm/index.js +5 -5
- package/dist/features/dashboard/index.js +6 -7
- package/dist/features/delivery/index.css +149 -149
- package/dist/features/delivery/index.js +4 -4
- package/dist/features/knowledge/index.css +149 -149
- package/dist/features/knowledge/index.js +3 -3
- package/dist/features/lead-gen/index.js +5 -5
- package/dist/features/monitoring/index.css +149 -149
- package/dist/features/monitoring/index.js +6 -6
- package/dist/features/monitoring/requests/index.js +4 -4
- package/dist/features/operations/index.js +9 -10
- package/dist/features/settings/index.js +6 -6
- package/dist/hooks/access/index.css +149 -149
- package/dist/hooks/access/index.js +3 -3
- package/dist/hooks/index.css +149 -149
- package/dist/hooks/index.d.ts +2 -60
- package/dist/hooks/index.js +3 -3
- package/dist/hooks/published.css +149 -149
- package/dist/hooks/published.d.ts +2 -60
- package/dist/hooks/published.js +3 -3
- package/dist/knowledge/index.css +149 -149
- package/dist/knowledge/index.js +7 -7
- package/dist/layout/index.css +149 -149
- package/dist/layout/index.js +6 -6
- package/dist/provider/index.js +3 -3
- package/dist/provider/published.css +149 -149
- package/dist/provider/published.js +3 -3
- package/dist/theme/index.js +3 -3
- package/dist/theme/presets/index.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-ZPTEGGKU.js +0 -631
- /package/dist/{chunk-XQBURFKM.js → chunk-RBGVNPA6.js} +0 -0
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import { RunResourceButton } from '../../chunk-YGCMMOY4.js';
|
|
2
|
-
import '../../chunk-3MEXPLWT.js';
|
|
3
|
-
import '../../chunk-HENXLGVD.js';
|
|
4
1
|
import { PageContainer } from '../../chunk-BZZCNLT6.js';
|
|
5
2
|
import '../../chunk-TUMSNGTX.js';
|
|
6
3
|
import { FilterBar } from '../../chunk-PDHTXPSF.js';
|
|
7
|
-
import { CustomModal,
|
|
8
|
-
export { CONTENT_ITEMS, ContentSidebar, ContentSidebarMiddle,
|
|
9
|
-
import '../../chunk-
|
|
10
|
-
import '../../chunk-
|
|
4
|
+
import { CustomModal, useContentItems, useContentDistributions, useContentConfig, SubshellContentContainer, PageTitleCaption, CenteredErrorState, EmptyState, StatCard, getContentItemIdentity, usePaginationState, useContentItem, useContentItemAttempts, useUpdateContentItem, useReviewContentItem, useContentDistribution, useUpdateContentDistribution, useContentPipelineSummary, deriveContentBoard, useExecutions } from '../../chunk-6TT3FQEZ.js';
|
|
5
|
+
export { CONTENT_ITEMS, ContentSidebar, ContentSidebarMiddle, DEFAULT_CONTENT_VOCABULARY, MyReviewQueuePanel, contentManifest, deriveContentBoard, getContentItemIdentity, resolveContentStepResource, resolveContentVocabulary, useContentConfig } from '../../chunk-6TT3FQEZ.js';
|
|
6
|
+
import '../../chunk-R6NMRXNO.js';
|
|
7
|
+
import '../../chunk-RBGVNPA6.js';
|
|
11
8
|
import '../../chunk-MKH2KOAO.js';
|
|
12
9
|
import '../../chunk-VDX6DL7Z.js';
|
|
13
10
|
import '../../chunk-L7BZZ4SI.js';
|
|
@@ -23,7 +20,7 @@ import '../../chunk-UD657ICS.js';
|
|
|
23
20
|
import '../../chunk-DD3CCMCZ.js';
|
|
24
21
|
import '../../chunk-RNP5R5I3.js';
|
|
25
22
|
import '../../chunk-RT4KRGZT.js';
|
|
26
|
-
import {
|
|
23
|
+
import { formatTimeAgo, formatRelativeTime } from '../../chunk-NM4A63KJ.js';
|
|
27
24
|
import '../../chunk-Y6I3IC45.js';
|
|
28
25
|
import '../../chunk-6SSQGWK7.js';
|
|
29
26
|
import '../../chunk-BI5VDO2K.js';
|
|
@@ -32,10 +29,10 @@ import '../../chunk-TVTSASST.js';
|
|
|
32
29
|
import '../../chunk-BRJ3QZ4E.js';
|
|
33
30
|
import '../../chunk-Q7DJKLEN.js';
|
|
34
31
|
import '../../chunk-I2KLQ2HA.js';
|
|
35
|
-
import { Stack, Group, Text, Badge, Button, Textarea, Box, Loader, Accordion, Card, Alert, List, Paper, Center, SimpleGrid, Table, TextInput, Select, Pagination, Title, Tabs, Anchor, Divider, Progress, Checkbox, TagsInput, Image, Tooltip, ActionIcon, UnstyledButton } from '@mantine/core';
|
|
36
|
-
import { IconAlertTriangle, IconShieldCheck, IconShieldX,
|
|
32
|
+
import { Stack, Group, Text, Badge, Button, Textarea, Box, Loader, Accordion, Card, Alert, List, Paper, Center, SimpleGrid, Table, TextInput, Select, Pagination, Title, Tabs, Anchor, Divider, Progress, Checkbox, TagsInput, Image, Tooltip, ActionIcon, UnstyledButton, ScrollArea } from '@mantine/core';
|
|
33
|
+
import { IconAlertTriangle, IconShieldCheck, IconShieldX, IconArticle, IconArrowRight, IconClock, IconSend, IconSearch, IconFileText, IconListDetails, IconAlertCircle, IconLayoutGrid, IconShare, IconPlayerPlay, IconExternalLink, IconMaximize, IconCopy, IconDownload, IconTrash, IconArrowLeft, IconArrowsExchange, IconCheck, IconX, IconChecklist, IconRoute, IconInfoCircle, IconClockHour4 } from '@tabler/icons-react';
|
|
37
34
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
38
|
-
import { useState, useMemo,
|
|
35
|
+
import { useState, useMemo, Fragment as Fragment$1, useCallback, useEffect } from 'react';
|
|
39
36
|
import { useNavigate, useSearch } from '@tanstack/react-router';
|
|
40
37
|
import { useDisclosure } from '@mantine/hooks';
|
|
41
38
|
|
|
@@ -75,7 +72,7 @@ function PayloadBody({
|
|
|
75
72
|
value: body,
|
|
76
73
|
onChange: (event) => onBodyChange(event.currentTarget.value),
|
|
77
74
|
autosize: true,
|
|
78
|
-
minRows:
|
|
75
|
+
minRows: 3,
|
|
79
76
|
disabled,
|
|
80
77
|
styles: {
|
|
81
78
|
input: {
|
|
@@ -303,6 +300,17 @@ function AlternatesPanel({
|
|
|
303
300
|
] }) })
|
|
304
301
|
] }) });
|
|
305
302
|
}
|
|
303
|
+
var IRREVERSIBLE_WARNING = "This action is irreversible and visible outside the platform.";
|
|
304
|
+
function isIrreversible(action) {
|
|
305
|
+
return action?.consequence === "irreversible";
|
|
306
|
+
}
|
|
307
|
+
function resolveActionColor(action) {
|
|
308
|
+
if (!action) return void 0;
|
|
309
|
+
if (action.color) return action.color;
|
|
310
|
+
if (action.tone === "danger") return "var(--color-error)";
|
|
311
|
+
if (isIrreversible(action)) return "var(--color-warning)";
|
|
312
|
+
return void 0;
|
|
313
|
+
}
|
|
306
314
|
function ReviewActionBar({ actions, onAction, pending = false }) {
|
|
307
315
|
const [confirmAction, setConfirmAction] = useState(null);
|
|
308
316
|
const [feedbackAction, setFeedbackAction] = useState(null);
|
|
@@ -311,7 +319,7 @@ function ReviewActionBar({ actions, onAction, pending = false }) {
|
|
|
311
319
|
if (action.requiresFeedback) {
|
|
312
320
|
setFeedback("");
|
|
313
321
|
setFeedbackAction(action);
|
|
314
|
-
} else if (action.requiresConfirmation) {
|
|
322
|
+
} else if (action.requiresConfirmation || isIrreversible(action)) {
|
|
315
323
|
setConfirmAction(action);
|
|
316
324
|
} else {
|
|
317
325
|
onAction(action.id);
|
|
@@ -326,11 +334,13 @@ function ReviewActionBar({ actions, onAction, pending = false }) {
|
|
|
326
334
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
327
335
|
/* @__PURE__ */ jsx(Group, { gap: "xs", children: actions.map((action) => {
|
|
328
336
|
const Icon = action.icon;
|
|
337
|
+
const irreversible = isIrreversible(action);
|
|
329
338
|
return /* @__PURE__ */ jsx(
|
|
330
339
|
Button,
|
|
331
340
|
{
|
|
332
|
-
variant: "light",
|
|
333
|
-
|
|
341
|
+
variant: irreversible ? "filled" : "light",
|
|
342
|
+
fw: irreversible ? 700 : void 0,
|
|
343
|
+
color: resolveActionColor(action),
|
|
334
344
|
leftSection: Icon ? /* @__PURE__ */ jsx(Icon, { size: 16 }) : void 0,
|
|
335
345
|
loading: pending,
|
|
336
346
|
disabled: pending,
|
|
@@ -345,13 +355,15 @@ function ReviewActionBar({ actions, onAction, pending = false }) {
|
|
|
345
355
|
}) }),
|
|
346
356
|
/* @__PURE__ */ jsx(CustomModal, { opened: !!confirmAction, onClose: () => setConfirmAction(null), size: "sm", children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
347
357
|
/* @__PURE__ */ jsx(Text, { fw: 600, children: confirmAction?.label }),
|
|
358
|
+
isIrreversible(confirmAction) ? /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, c: "var(--color-warning)", children: IRREVERSIBLE_WARNING }) : null,
|
|
348
359
|
/* @__PURE__ */ jsx(Text, { size: "sm", children: confirmAction?.confirmationMessage || `Are you sure you want to ${confirmAction?.label.toLowerCase()}?` }),
|
|
349
360
|
/* @__PURE__ */ jsxs(Group, { justify: "flex-end", children: [
|
|
350
361
|
/* @__PURE__ */ jsx(Button, { variant: "default", onClick: () => setConfirmAction(null), children: "Cancel" }),
|
|
351
362
|
/* @__PURE__ */ jsx(
|
|
352
363
|
Button,
|
|
353
364
|
{
|
|
354
|
-
|
|
365
|
+
variant: isIrreversible(confirmAction) ? "filled" : void 0,
|
|
366
|
+
color: resolveActionColor(confirmAction),
|
|
355
367
|
onClick: () => {
|
|
356
368
|
if (confirmAction) onAction(confirmAction.id);
|
|
357
369
|
setConfirmAction(null);
|
|
@@ -363,6 +375,7 @@ function ReviewActionBar({ actions, onAction, pending = false }) {
|
|
|
363
375
|
] }) }),
|
|
364
376
|
/* @__PURE__ */ jsx(CustomModal, { opened: !!feedbackAction, onClose: () => setFeedbackAction(null), size: "sm", children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
365
377
|
/* @__PURE__ */ jsx(Text, { fw: 600, children: feedbackAction?.label }),
|
|
378
|
+
isIrreversible(feedbackAction) ? /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, c: "var(--color-warning)", children: IRREVERSIBLE_WARNING }) : null,
|
|
366
379
|
feedbackAction?.confirmationMessage ? /* @__PURE__ */ jsx(Text, { size: "sm", children: feedbackAction.confirmationMessage }) : null,
|
|
367
380
|
/* @__PURE__ */ jsx(
|
|
368
381
|
Textarea,
|
|
@@ -381,7 +394,8 @@ function ReviewActionBar({ actions, onAction, pending = false }) {
|
|
|
381
394
|
/* @__PURE__ */ jsx(
|
|
382
395
|
Button,
|
|
383
396
|
{
|
|
384
|
-
|
|
397
|
+
variant: isIrreversible(feedbackAction) ? "filled" : void 0,
|
|
398
|
+
color: resolveActionColor(feedbackAction),
|
|
385
399
|
disabled: Boolean(feedbackAction?.feedbackRequired) && feedback.trim().length === 0,
|
|
386
400
|
onClick: submitFeedback,
|
|
387
401
|
"data-testid": "content-review-feedback-submit",
|
|
@@ -414,10 +428,10 @@ function ContentReviewCard({
|
|
|
414
428
|
actionsPending = false
|
|
415
429
|
}) {
|
|
416
430
|
return /* @__PURE__ */ jsx(Card, { withBorder: true, radius: "md", padding: "xl", "data-testid": "content-review-card", children: /* @__PURE__ */ jsxs(Stack, { gap: "lg", children: [
|
|
417
|
-
/* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", wrap: "nowrap", children: [
|
|
418
|
-
/* @__PURE__ */ jsx(Text, { fw: 700, size: "lg", children: title }),
|
|
431
|
+
title || processingSteps.length > 0 ? /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", wrap: "nowrap", children: [
|
|
432
|
+
title ? /* @__PURE__ */ jsx(Text, { fw: 700, size: "lg", children: title }) : null,
|
|
419
433
|
/* @__PURE__ */ jsx(ProcessingStateStrip, { steps: processingSteps })
|
|
420
|
-
] }),
|
|
434
|
+
] }) : null,
|
|
421
435
|
fidelity && !fidelity.matches && /* @__PURE__ */ jsx(
|
|
422
436
|
Alert,
|
|
423
437
|
{
|
|
@@ -521,52 +535,6 @@ function ContentReviewCard({
|
|
|
521
535
|
/* @__PURE__ */ jsx(ReviewActionBar, { actions, onAction, pending: actionsPending })
|
|
522
536
|
] }) });
|
|
523
537
|
}
|
|
524
|
-
var CONTENT_SYSTEM_PATH = "content";
|
|
525
|
-
function resolveContentStepResource(model, step) {
|
|
526
|
-
const actionId = step.actionId;
|
|
527
|
-
if (!actionId) return void 0;
|
|
528
|
-
const resource = Object.values(model.resources ?? {}).find(
|
|
529
|
-
(candidate) => candidate.systemPath === CONTENT_SYSTEM_PATH && (candidate.ontology?.primaryAction === actionId || candidate.ontology?.actions?.includes(actionId) === true)
|
|
530
|
-
);
|
|
531
|
-
return resource ? { resourceId: resource.id, resourceKind: resource.kind } : void 0;
|
|
532
|
-
}
|
|
533
|
-
var EMPTY_CONFIG = {
|
|
534
|
-
pipelines: [],
|
|
535
|
-
stageCatalog: {},
|
|
536
|
-
stagesByCatalog: {},
|
|
537
|
-
defaultPipelineId: void 0,
|
|
538
|
-
resolveStepResource: () => void 0
|
|
539
|
-
};
|
|
540
|
-
function groupStagesByCatalog(stageCatalog) {
|
|
541
|
-
const groups = {};
|
|
542
|
-
for (const entry of Object.values(stageCatalog)) {
|
|
543
|
-
(groups[entry.catalogKey] ??= []).push(entry);
|
|
544
|
-
}
|
|
545
|
-
return groups;
|
|
546
|
-
}
|
|
547
|
-
function getDefaultPipelineId(model, pipelines) {
|
|
548
|
-
const configuredId = getSystem(model, CONTENT_SYSTEM_PATH)?.config?.defaultPipelineId;
|
|
549
|
-
if (typeof configuredId === "string" && pipelines.some((pipeline) => pipeline.id === configuredId)) {
|
|
550
|
-
return configuredId;
|
|
551
|
-
}
|
|
552
|
-
return pipelines[0]?.id;
|
|
553
|
-
}
|
|
554
|
-
function useContentConfig() {
|
|
555
|
-
const systems = useOptionalElevasisSystems();
|
|
556
|
-
const organizationModel = systems?.organizationModel;
|
|
557
|
-
return useMemo(() => {
|
|
558
|
-
if (!organizationModel) return EMPTY_CONFIG;
|
|
559
|
-
const pipelines = getContentBuildTemplates(organizationModel);
|
|
560
|
-
const stageCatalog = getContentStageCatalog(organizationModel);
|
|
561
|
-
return {
|
|
562
|
-
pipelines,
|
|
563
|
-
stageCatalog,
|
|
564
|
-
stagesByCatalog: groupStagesByCatalog(stageCatalog),
|
|
565
|
-
defaultPipelineId: getDefaultPipelineId(organizationModel, pipelines),
|
|
566
|
-
resolveStepResource: (step) => resolveContentStepResource(organizationModel, step)
|
|
567
|
-
};
|
|
568
|
-
}, [organizationModel]);
|
|
569
|
-
}
|
|
570
538
|
|
|
571
539
|
// src/lib/content/stage-colors.ts
|
|
572
540
|
var FALLBACK_CONTENT_STAGE_COLORS = {
|
|
@@ -609,43 +577,100 @@ function ContentOverviewPage({ headerActions, footer } = {}) {
|
|
|
609
577
|
);
|
|
610
578
|
const isLoading = itemsQuery.isLoading;
|
|
611
579
|
const error = itemsQuery.error;
|
|
580
|
+
const { vocabulary } = contentConfig;
|
|
612
581
|
return /* @__PURE__ */ jsxs(SubshellContentContainer, { children: [
|
|
613
582
|
/* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
614
|
-
/* @__PURE__ */ jsx(
|
|
583
|
+
/* @__PURE__ */ jsx(
|
|
584
|
+
PageTitleCaption,
|
|
585
|
+
{
|
|
586
|
+
title: `${vocabulary.item.plural} Overview`,
|
|
587
|
+
caption: `${vocabulary.pipeline.singular} status, review load, and recent activity`
|
|
588
|
+
}
|
|
589
|
+
),
|
|
615
590
|
headerActions ? /* @__PURE__ */ jsx(Group, { gap: "xs", children: headerActions }) : null,
|
|
616
|
-
isLoading ? /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) }) : error ? /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(CenteredErrorState, { error, title:
|
|
591
|
+
isLoading ? /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) }) : error ? /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(CenteredErrorState, { error, title: `Failed to load ${vocabulary.item.plural.toLowerCase()}` }) }) : items.length === 0 ? (
|
|
592
|
+
// Finding 11: zero items is the state every tenant starts in, and the only one
|
|
593
|
+
// with no path forward on screen. The one available action is to go produce
|
|
594
|
+
// something -- routed through the pipelines page, which itself lands a
|
|
595
|
+
// single-pipeline tenant straight inside its workspace (Proposal, "Design the
|
|
596
|
+
// first run").
|
|
597
|
+
/* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(
|
|
598
|
+
EmptyState,
|
|
599
|
+
{
|
|
600
|
+
icon: IconArticle,
|
|
601
|
+
title: `No ${vocabulary.item.plural.toLowerCase()} yet`,
|
|
602
|
+
description: `${vocabulary.pipeline.plural} produce ${vocabulary.item.plural.toLowerCase()} as they move from draft through review to publish. Pick a pipeline to produce the first one.`,
|
|
603
|
+
action: {
|
|
604
|
+
label: `Go to ${vocabulary.pipeline.plural.toLowerCase()}`,
|
|
605
|
+
onClick: () => navigate({ to: "/content/pipelines" }),
|
|
606
|
+
icon: /* @__PURE__ */ jsx(IconArrowRight, { size: 14 })
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
) })
|
|
610
|
+
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
617
611
|
/* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 3 }, children: [
|
|
618
|
-
/* @__PURE__ */ jsx(
|
|
612
|
+
/* @__PURE__ */ jsx(
|
|
613
|
+
StatCard,
|
|
614
|
+
{
|
|
615
|
+
variant: "hero",
|
|
616
|
+
icon: IconArticle,
|
|
617
|
+
value: totalItems,
|
|
618
|
+
label: `Total ${vocabulary.item.plural}`
|
|
619
|
+
}
|
|
620
|
+
),
|
|
619
621
|
/* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconClock, value: pendingReviewCount, label: "Pending Review" }),
|
|
620
|
-
/* @__PURE__ */ jsx(
|
|
622
|
+
/* @__PURE__ */ jsx(
|
|
623
|
+
StatCard,
|
|
624
|
+
{
|
|
625
|
+
variant: "hero",
|
|
626
|
+
icon: IconSend,
|
|
627
|
+
value: totalDistributions,
|
|
628
|
+
label: vocabulary.distribution.plural
|
|
629
|
+
}
|
|
630
|
+
)
|
|
621
631
|
] }),
|
|
622
632
|
statusEntries.length > 0 && /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
623
|
-
/* @__PURE__ */ jsx(
|
|
624
|
-
|
|
633
|
+
/* @__PURE__ */ jsx(
|
|
634
|
+
CardHeader,
|
|
635
|
+
{
|
|
636
|
+
title: "By Status",
|
|
637
|
+
subtitle: `Where each ${vocabulary.item.singular.toLowerCase()} currently stands`
|
|
638
|
+
}
|
|
639
|
+
),
|
|
640
|
+
/* @__PURE__ */ jsx(Group, { gap: 4, wrap: "wrap", children: statusEntries.map((entry, index) => {
|
|
625
641
|
const colors = getContentStageColor(entry);
|
|
626
|
-
return /* @__PURE__ */ jsxs(
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
642
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
643
|
+
index > 0 && /* @__PURE__ */ jsx(IconArrowRight, { size: 14, color: "var(--color-text-dimmed)" }),
|
|
644
|
+
/* @__PURE__ */ jsxs(
|
|
645
|
+
Badge,
|
|
646
|
+
{
|
|
647
|
+
size: "lg",
|
|
648
|
+
variant: "light",
|
|
649
|
+
styles: { root: { color: colors.accent, backgroundColor: colors.background } },
|
|
650
|
+
children: [
|
|
651
|
+
entry.label,
|
|
652
|
+
": ",
|
|
653
|
+
statusCounts.get(entry.key) ?? 0
|
|
654
|
+
]
|
|
655
|
+
}
|
|
656
|
+
)
|
|
657
|
+
] }, entry.key);
|
|
640
658
|
}) })
|
|
641
659
|
] }) }),
|
|
642
660
|
/* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
643
|
-
/* @__PURE__ */ jsx(
|
|
661
|
+
/* @__PURE__ */ jsx(
|
|
662
|
+
CardHeader,
|
|
663
|
+
{
|
|
664
|
+
title: "Recently Updated",
|
|
665
|
+
subtitle: `Jump into a ${vocabulary.item.singular.toLowerCase()}'s review page`
|
|
666
|
+
}
|
|
667
|
+
),
|
|
644
668
|
/* @__PURE__ */ jsxs(Table, { children: [
|
|
645
669
|
/* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
646
|
-
/* @__PURE__ */ jsx(Table.Th, { children:
|
|
670
|
+
/* @__PURE__ */ jsx(Table.Th, { children: vocabulary.item.singular }),
|
|
647
671
|
/* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
|
|
648
672
|
/* @__PURE__ */ jsx(Table.Th, { children: "Pillar" }),
|
|
673
|
+
/* @__PURE__ */ jsx(Table.Th, { children: "Age" }),
|
|
649
674
|
/* @__PURE__ */ jsx(Table.Th, { children: "Reviewed" })
|
|
650
675
|
] }) }),
|
|
651
676
|
/* @__PURE__ */ jsx(Table.Tbody, { children: recentItems.map((item) => {
|
|
@@ -656,9 +681,10 @@ function ContentOverviewPage({ headerActions, footer } = {}) {
|
|
|
656
681
|
style: { cursor: "pointer" },
|
|
657
682
|
onClick: () => navigate({ to: "/content/items/$itemId", params: { itemId: item.id } }),
|
|
658
683
|
children: [
|
|
659
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: item
|
|
684
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: getContentItemIdentity(item) }) }),
|
|
660
685
|
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", children: statusEntry?.label ?? item.status }) }),
|
|
661
686
|
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: item.pillar ?? "\u2014" }) }),
|
|
687
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatTimeAgo(item.updatedAt) }) }),
|
|
662
688
|
/* @__PURE__ */ jsx(Table.Td, { children: item.reviewedAt ? /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: "var(--color-success)", children: "Reviewed" }) : /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: "var(--color-warning)", children: "Pending" }) })
|
|
663
689
|
]
|
|
664
690
|
},
|
|
@@ -676,10 +702,12 @@ var PAGE_SIZE = 20;
|
|
|
676
702
|
function ContentItemsPage({ headerActions } = {}) {
|
|
677
703
|
const navigate = useNavigate();
|
|
678
704
|
const contentConfig = useContentConfig();
|
|
705
|
+
const rawSearch = useSearch({ strict: false });
|
|
706
|
+
const reviewFilterActive = rawSearch.review === "pending";
|
|
679
707
|
const [searchQuery, setSearchQuery] = useState("");
|
|
680
708
|
const [statusFilter, setStatusFilter] = useState("");
|
|
681
709
|
const [pillarFilter, setPillarFilter] = useState("");
|
|
682
|
-
const pagination = usePaginationState(PAGE_SIZE, [searchQuery, statusFilter, pillarFilter]);
|
|
710
|
+
const pagination = usePaginationState(PAGE_SIZE, [searchQuery, statusFilter, pillarFilter, reviewFilterActive]);
|
|
683
711
|
const itemsQuery = useContentItems({
|
|
684
712
|
search: searchQuery.trim() || void 0,
|
|
685
713
|
status: statusFilter || void 0,
|
|
@@ -687,7 +715,8 @@ function ContentItemsPage({ headerActions } = {}) {
|
|
|
687
715
|
limit: PAGE_SIZE,
|
|
688
716
|
offset: pagination.offset
|
|
689
717
|
});
|
|
690
|
-
const
|
|
718
|
+
const fetchedItems = itemsQuery.data?.data ?? [];
|
|
719
|
+
const items = reviewFilterActive ? fetchedItems.filter((item) => item.reviewedAt === null) : fetchedItems;
|
|
691
720
|
const total = itemsQuery.data?.total ?? 0;
|
|
692
721
|
const statusOptions = [
|
|
693
722
|
{ value: "", label: "All statuses" },
|
|
@@ -698,7 +727,13 @@ function ContentItemsPage({ headerActions } = {}) {
|
|
|
698
727
|
...(contentConfig.stagesByCatalog.pillar ?? []).map((entry) => ({ value: entry.key, label: entry.label }))
|
|
699
728
|
];
|
|
700
729
|
return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
701
|
-
/* @__PURE__ */ jsx(
|
|
730
|
+
/* @__PURE__ */ jsx(
|
|
731
|
+
PageTitleCaption,
|
|
732
|
+
{
|
|
733
|
+
title: contentConfig.vocabulary.item.plural,
|
|
734
|
+
caption: reviewFilterActive ? `${contentConfig.vocabulary.item.plural} awaiting review, across every pipeline` : `Every ${contentConfig.vocabulary.item.singular.toLowerCase()} across the content pipeline`
|
|
735
|
+
}
|
|
736
|
+
),
|
|
702
737
|
headerActions ? /* @__PURE__ */ jsx(Group, { gap: "xs", children: headerActions }) : null,
|
|
703
738
|
/* @__PURE__ */ jsx(Paper, { children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
704
739
|
/* @__PURE__ */ jsxs(FilterBar, { children: [
|
|
@@ -740,11 +775,11 @@ function ContentItemsPage({ headerActions } = {}) {
|
|
|
740
775
|
EmptyState,
|
|
741
776
|
{
|
|
742
777
|
icon: IconFileText,
|
|
743
|
-
title: searchQuery.trim() || statusFilter || pillarFilter ? "No items match your filters" : "No
|
|
778
|
+
title: searchQuery.trim() || statusFilter || pillarFilter ? "No items match your filters" : reviewFilterActive ? "Nothing awaiting review" : `No ${contentConfig.vocabulary.item.plural.toLowerCase()} yet`
|
|
744
779
|
}
|
|
745
780
|
) : /* @__PURE__ */ jsxs(Table, { children: [
|
|
746
781
|
/* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
747
|
-
/* @__PURE__ */ jsx(Table.Th, { children:
|
|
782
|
+
/* @__PURE__ */ jsx(Table.Th, { children: contentConfig.vocabulary.item.singular }),
|
|
748
783
|
/* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
|
|
749
784
|
/* @__PURE__ */ jsx(Table.Th, { children: "Pillar" }),
|
|
750
785
|
/* @__PURE__ */ jsx(Table.Th, { children: "Reviewed" }),
|
|
@@ -760,7 +795,7 @@ function ContentItemsPage({ headerActions } = {}) {
|
|
|
760
795
|
style: { cursor: "pointer" },
|
|
761
796
|
onClick: () => navigate({ to: "/content/items/$itemId", params: { itemId: item.id } }),
|
|
762
797
|
children: [
|
|
763
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: item
|
|
798
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: getContentItemIdentity(item) }) }),
|
|
764
799
|
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(
|
|
765
800
|
Badge,
|
|
766
801
|
{
|
|
@@ -774,7 +809,7 @@ function ContentItemsPage({ headerActions } = {}) {
|
|
|
774
809
|
) }),
|
|
775
810
|
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: pillarEntry?.label ?? item.pillar ?? "\u2014" }) }),
|
|
776
811
|
/* @__PURE__ */ jsx(Table.Td, { children: item.reviewedAt ? /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: "var(--color-success)", children: "Reviewed" }) : /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: "var(--color-warning)", children: "Pending" }) }),
|
|
777
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children:
|
|
812
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatRelativeTime(item.updatedAt) }) })
|
|
778
813
|
]
|
|
779
814
|
},
|
|
780
815
|
item.id
|
|
@@ -837,38 +872,42 @@ function toPayloadDisplayFields(fields, payload) {
|
|
|
837
872
|
const payloadRecord = payload;
|
|
838
873
|
return fields.filter((field) => field.key in payloadRecord).map((field) => ({ key: field.key, label: field.label, kind: toDisplayKind(field.type) }));
|
|
839
874
|
}
|
|
840
|
-
var
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
875
|
+
var REVIEW_QUEUE_FETCH_LIMIT = 100;
|
|
876
|
+
function buildReviewActions(isTerminalPipeline) {
|
|
877
|
+
return [
|
|
878
|
+
{
|
|
879
|
+
id: "approved",
|
|
880
|
+
label: "Approve",
|
|
881
|
+
icon: IconCheck,
|
|
882
|
+
// Decision 7, resolved 2026-08-12: approve collects an OPTIONAL note, which is what
|
|
883
|
+
// finally gives `content_items.reviewer_feedback` a writer -- the page has always
|
|
884
|
+
// rendered it and the API has always accepted it, but nothing in the UI could set it.
|
|
885
|
+
// `feedbackRequired` is deliberately omitted: a reviewer with nothing to add still
|
|
886
|
+
// approves in two clicks, and a blank note leaves the column NULL rather than ''.
|
|
887
|
+
requiresFeedback: true,
|
|
888
|
+
feedbackLabel: "Note (optional)",
|
|
889
|
+
feedbackPlaceholder: "Anything the next step or a later reader should know?",
|
|
890
|
+
confirmationMessage: isTerminalPipeline ? "Approve this item? There is no further pipeline step -- this is the last internal gate before it is published." : "Approve this item? It moves on to the next pipeline step.",
|
|
891
|
+
consequence: isTerminalPipeline ? "irreversible" : "standard"
|
|
892
|
+
},
|
|
893
|
+
{
|
|
894
|
+
id: "rejected",
|
|
895
|
+
label: "Reject",
|
|
896
|
+
icon: IconX,
|
|
897
|
+
tone: "danger",
|
|
898
|
+
requiresFeedback: true,
|
|
899
|
+
feedbackLabel: "Reason for rejection",
|
|
900
|
+
feedbackPlaceholder: "What needs to change before this can go out?",
|
|
901
|
+
feedbackRequired: true
|
|
902
|
+
}
|
|
903
|
+
];
|
|
904
|
+
}
|
|
866
905
|
function DistributionMetricsCell({ metrics }) {
|
|
867
|
-
|
|
906
|
+
const entries = metrics && typeof metrics === "object" ? Object.entries(metrics) : [];
|
|
907
|
+
if (entries.length === 0) {
|
|
868
908
|
return /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "\u2014" });
|
|
869
909
|
}
|
|
870
|
-
const
|
|
871
|
-
const isFlat = entries.length > 0 && entries.every(([, value]) => value === null || typeof value !== "object");
|
|
910
|
+
const isFlat = entries.every(([, value]) => value === null || typeof value !== "object");
|
|
872
911
|
if (!isFlat) {
|
|
873
912
|
return /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", style: { overflowWrap: "anywhere" }, children: JSON.stringify(metrics) });
|
|
874
913
|
}
|
|
@@ -890,9 +929,12 @@ function ContentItemReviewPage({ itemId, headerActions }) {
|
|
|
890
929
|
const item = itemQuery.data;
|
|
891
930
|
const pipeline = item ? contentConfig.pipelines.find((candidate) => candidate.id === item.pipelineId) : void 0;
|
|
892
931
|
const pipelineSteps = useMemo(() => pipeline?.steps ?? [], [pipeline]);
|
|
932
|
+
const isTerminalPipeline = pipelineSteps.length <= 1;
|
|
933
|
+
const reviewActions = useMemo(() => buildReviewActions(isTerminalPipeline), [isTerminalPipeline]);
|
|
893
934
|
const pipelinePayloadFields = useMemo(() => getPipelinePayloadFields(pipelineSteps), [pipelineSteps]);
|
|
894
935
|
const primaryTextField = useMemo(() => getPrimaryPayloadTextField(pipelinePayloadFields), [pipelinePayloadFields]);
|
|
895
936
|
const originalBody = item ? getPayloadText(item.payload, primaryTextField?.key) : "";
|
|
937
|
+
const savedBody = item?.body ?? originalBody;
|
|
896
938
|
const displayPayloadFields = useMemo(
|
|
897
939
|
() => toPayloadDisplayFields(pipelinePayloadFields, item?.payload),
|
|
898
940
|
[pipelinePayloadFields, item?.payload]
|
|
@@ -901,9 +943,51 @@ function ContentItemReviewPage({ itemId, headerActions }) {
|
|
|
901
943
|
() => getContentProcessingSteps(item?.processingState, pipelineSteps),
|
|
902
944
|
[item?.processingState, pipelineSteps]
|
|
903
945
|
);
|
|
946
|
+
const visibleProcessingSteps = useMemo(
|
|
947
|
+
() => processingSteps.some((step) => step.status !== "pending") ? processingSteps : [],
|
|
948
|
+
[processingSteps]
|
|
949
|
+
);
|
|
950
|
+
const pipelineItemsQuery = useContentItems({
|
|
951
|
+
pipelineId: item?.pipelineId ?? void 0,
|
|
952
|
+
limit: REVIEW_QUEUE_FETCH_LIMIT
|
|
953
|
+
});
|
|
954
|
+
const reviewQueue = useMemo(() => {
|
|
955
|
+
if (!item?.pipelineId) return [];
|
|
956
|
+
return (pipelineItemsQuery.data?.data ?? []).filter((candidate) => candidate.reviewedAt === null).sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
|
|
957
|
+
}, [pipelineItemsQuery.data, item?.pipelineId]);
|
|
958
|
+
const queueIndex = item ? reviewQueue.findIndex((candidate) => candidate.id === item.id) : -1;
|
|
959
|
+
const queuePosition = queueIndex === -1 ? void 0 : queueIndex + 1;
|
|
960
|
+
const nextQueueItemId = queueIndex === -1 ? void 0 : reviewQueue[queueIndex + 1]?.id;
|
|
961
|
+
const goToNextInQueue = useCallback(() => {
|
|
962
|
+
if (nextQueueItemId) {
|
|
963
|
+
navigate({ to: "/content/items/$itemId", params: { itemId: nextQueueItemId } });
|
|
964
|
+
} else {
|
|
965
|
+
navigate({ to: "/content/items" });
|
|
966
|
+
}
|
|
967
|
+
}, [navigate, nextQueueItemId]);
|
|
904
968
|
useEffect(() => {
|
|
905
969
|
if (item) setBody(item.body ?? originalBody);
|
|
906
970
|
}, [item?.id]);
|
|
971
|
+
useEffect(() => {
|
|
972
|
+
function handleKeyDown(event) {
|
|
973
|
+
const target = event.target;
|
|
974
|
+
if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLElement && target.isContentEditable) {
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
if (event.key === "a" || event.key === "A") {
|
|
978
|
+
event.preventDefault();
|
|
979
|
+
document.querySelector('[data-testid="content-review-action-approved"]')?.click();
|
|
980
|
+
} else if (event.key === "r" || event.key === "R") {
|
|
981
|
+
event.preventDefault();
|
|
982
|
+
document.querySelector('[data-testid="content-review-action-rejected"]')?.click();
|
|
983
|
+
} else if (event.key === "n" || event.key === "N" || event.key === "ArrowRight") {
|
|
984
|
+
event.preventDefault();
|
|
985
|
+
goToNextInQueue();
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
989
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
990
|
+
}, [goToNextInQueue]);
|
|
907
991
|
const backButton = /* @__PURE__ */ jsx(
|
|
908
992
|
Button,
|
|
909
993
|
{
|
|
@@ -945,6 +1029,7 @@ function ContentItemReviewPage({ itemId, headerActions }) {
|
|
|
945
1029
|
const distributions = distributionsQuery.data?.data ?? [];
|
|
946
1030
|
const isSaving = updateItem.isPending || reviewItem.isPending;
|
|
947
1031
|
const viewingAttempt = attempts.find((attempt) => attempt.id === viewingAttemptId);
|
|
1032
|
+
const identity = getContentItemIdentity(item, pipelinePayloadFields);
|
|
948
1033
|
const persistBodyIfDirty = async () => {
|
|
949
1034
|
if (!isDirty) return;
|
|
950
1035
|
await updateItem.mutateAsync({ itemId: item.id, updates: { body } });
|
|
@@ -966,52 +1051,49 @@ function ContentItemReviewPage({ itemId, headerActions }) {
|
|
|
966
1051
|
await reviewItem.mutateAsync({ itemId: item.id, review: { decision: "rejected", rejectReason: feedback ?? "" } });
|
|
967
1052
|
showSuccessNotification("Item rejected");
|
|
968
1053
|
}
|
|
1054
|
+
goToNextInQueue();
|
|
969
1055
|
};
|
|
970
1056
|
return /* @__PURE__ */ jsxs(SubshellContentContainer, { children: [
|
|
971
1057
|
/* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
972
|
-
/* @__PURE__ */ jsx(
|
|
973
|
-
PageTitleCaption,
|
|
974
|
-
{
|
|
975
|
-
title: item.title,
|
|
976
|
-
caption: `Pipeline: ${pipeline?.label ?? item.pipelineId ?? "Unassigned"}`,
|
|
977
|
-
rightSection: backButton
|
|
978
|
-
}
|
|
979
|
-
),
|
|
1058
|
+
/* @__PURE__ */ jsx(PageTitleCaption, { title: identity }),
|
|
980
1059
|
headerActions ? /* @__PURE__ */ jsx(Group, { gap: "xs", children: headerActions }) : null,
|
|
981
|
-
/* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", children: [
|
|
982
|
-
/* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
1060
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", align: "center", children: [
|
|
1061
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "wrap", children: [
|
|
1062
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: pipeline?.label ?? item.pipelineId ?? "Unassigned" }),
|
|
983
1063
|
/* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", children: contentConfig.stageCatalog[item.status]?.label ?? item.status }),
|
|
984
1064
|
item.pillar && /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", children: contentConfig.stageCatalog[item.pillar]?.label ?? item.pillar }),
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
] })
|
|
1065
|
+
/* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
1066
|
+
item.reviewedAt ? `Reviewed ${new Date(item.reviewedAt).toLocaleDateString()}` : "Not yet reviewed",
|
|
1067
|
+
queuePosition ? ` \xB7 ${queuePosition} of ${reviewQueue.length}` : ""
|
|
1068
|
+
] })
|
|
989
1069
|
] }),
|
|
990
|
-
/* @__PURE__ */
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1070
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
|
|
1071
|
+
/* @__PURE__ */ jsx(
|
|
1072
|
+
Button,
|
|
1073
|
+
{
|
|
1074
|
+
size: "xs",
|
|
1075
|
+
variant: "light",
|
|
1076
|
+
onClick: handleSave,
|
|
1077
|
+
disabled: !isDirty || isSaving,
|
|
1078
|
+
loading: updateItem.isPending,
|
|
1079
|
+
children: "Save"
|
|
1080
|
+
}
|
|
1081
|
+
),
|
|
1082
|
+
backButton
|
|
1083
|
+
] })
|
|
1001
1084
|
] }),
|
|
1002
1085
|
/* @__PURE__ */ jsx(
|
|
1003
1086
|
ContentReviewCard,
|
|
1004
1087
|
{
|
|
1005
|
-
title: item.title,
|
|
1006
1088
|
reviewMode: "live",
|
|
1007
1089
|
body,
|
|
1008
1090
|
onBodyChange: setBody,
|
|
1009
|
-
originalBody,
|
|
1010
|
-
onReset: () => setBody(
|
|
1091
|
+
originalBody: savedBody,
|
|
1092
|
+
onReset: () => setBody(savedBody),
|
|
1011
1093
|
payload: item.payload,
|
|
1012
1094
|
payloadFields: displayPayloadFields,
|
|
1013
|
-
processingSteps,
|
|
1014
|
-
actions:
|
|
1095
|
+
processingSteps: visibleProcessingSteps,
|
|
1096
|
+
actions: reviewActions,
|
|
1015
1097
|
onAction: handleAction,
|
|
1016
1098
|
actionsPending: isSaving
|
|
1017
1099
|
}
|
|
@@ -1105,8 +1187,6 @@ function PipelineRow({
|
|
|
1105
1187
|
[pipeline.steps, contentConfig]
|
|
1106
1188
|
);
|
|
1107
1189
|
const summary = useContentPipelineSummary(pipeline.id, statusKeys, stepResourceIds);
|
|
1108
|
-
const artifactsQuery = useContentArtifacts({ pipelineId: pipeline.id });
|
|
1109
|
-
const activeArtifacts = artifactsQuery.artifacts.filter((artifact) => artifact.isActive);
|
|
1110
1190
|
return /* @__PURE__ */ jsxs(
|
|
1111
1191
|
Table.Tr,
|
|
1112
1192
|
{
|
|
@@ -1117,28 +1197,6 @@ function PipelineRow({
|
|
|
1117
1197
|
/* @__PURE__ */ jsx(Text, { fw: 500, children: pipeline.label }),
|
|
1118
1198
|
pipeline.description ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: pipeline.description }) : null
|
|
1119
1199
|
] }),
|
|
1120
|
-
/* @__PURE__ */ jsx(Table.Td, { children: summary.isLoading ? /* @__PURE__ */ jsx(Loader, { size: "xs" }) : /* @__PURE__ */ jsx(Group, { gap: 4, wrap: "wrap", children: statusEntries.map((entry) => {
|
|
1121
|
-
const colors = getContentStageColor(entry);
|
|
1122
|
-
return /* @__PURE__ */ jsxs(
|
|
1123
|
-
Badge,
|
|
1124
|
-
{
|
|
1125
|
-
size: "sm",
|
|
1126
|
-
variant: "light",
|
|
1127
|
-
styles: { root: { color: colors.accent, backgroundColor: colors.background } },
|
|
1128
|
-
children: [
|
|
1129
|
-
entry.label,
|
|
1130
|
-
": ",
|
|
1131
|
-
summary.countsByStatus[entry.key] ?? 0
|
|
1132
|
-
]
|
|
1133
|
-
},
|
|
1134
|
-
entry.key
|
|
1135
|
-
);
|
|
1136
|
-
}) }) }),
|
|
1137
|
-
/* @__PURE__ */ jsx(Table.Td, { children: activeArtifacts.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No active artifacts" }) : /* @__PURE__ */ jsx(Group, { gap: 4, wrap: "wrap", children: activeArtifacts.map((artifact) => /* @__PURE__ */ jsxs(Badge, { size: "sm", variant: "outline", children: [
|
|
1138
|
-
artifact.kind.replace("content:", ""),
|
|
1139
|
-
" v",
|
|
1140
|
-
artifact.version
|
|
1141
|
-
] }, artifact.id)) }) }),
|
|
1142
1200
|
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: summary.isLoading ? /* @__PURE__ */ jsx(Loader, { size: "xs" }) : formatLastRun(summary.lastRunAt) }) })
|
|
1143
1201
|
]
|
|
1144
1202
|
}
|
|
@@ -1146,158 +1204,323 @@ function PipelineRow({
|
|
|
1146
1204
|
}
|
|
1147
1205
|
function ContentPipelinesPage({ headerActions } = {}) {
|
|
1148
1206
|
const contentConfig = useContentConfig();
|
|
1207
|
+
const { vocabulary } = contentConfig;
|
|
1149
1208
|
const statusEntries = contentConfig.stagesByCatalog.status ?? [];
|
|
1150
1209
|
return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
1151
|
-
/* @__PURE__ */ jsx(
|
|
1210
|
+
/* @__PURE__ */ jsx(
|
|
1211
|
+
PageTitleCaption,
|
|
1212
|
+
{
|
|
1213
|
+
title: vocabulary.pipeline.plural,
|
|
1214
|
+
caption: `Every declared ${vocabulary.pipeline.singular.toLowerCase()} and its production state`
|
|
1215
|
+
}
|
|
1216
|
+
),
|
|
1152
1217
|
headerActions ? /* @__PURE__ */ jsx(Group, { gap: "xs", children: headerActions }) : null,
|
|
1153
|
-
/* @__PURE__ */ jsx(Paper, { children:
|
|
1218
|
+
/* @__PURE__ */ jsx(Paper, { children: contentConfig.pipelines.length === 0 ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(
|
|
1154
1219
|
EmptyState,
|
|
1155
1220
|
{
|
|
1156
1221
|
icon: IconListDetails,
|
|
1157
|
-
title:
|
|
1158
|
-
description:
|
|
1222
|
+
title: `No ${vocabulary.pipeline.plural.toLowerCase()} declared`,
|
|
1223
|
+
description: `The content System's Organization Model declares no ${vocabulary.pipeline.singular.toLowerCase()} catalog entries yet.`
|
|
1159
1224
|
}
|
|
1160
1225
|
) }) : /* @__PURE__ */ jsxs(Table, { children: [
|
|
1161
1226
|
/* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
1162
|
-
/* @__PURE__ */ jsx(Table.Th, { children:
|
|
1163
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Items by status" }),
|
|
1164
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Active rules artifacts" }),
|
|
1227
|
+
/* @__PURE__ */ jsx(Table.Th, { children: vocabulary.pipeline.singular }),
|
|
1165
1228
|
/* @__PURE__ */ jsx(Table.Th, { children: "Last run" })
|
|
1166
1229
|
] }) }),
|
|
1167
1230
|
/* @__PURE__ */ jsx(Table.Tbody, { children: contentConfig.pipelines.map((pipeline) => /* @__PURE__ */ jsx(PipelineRow, { pipeline, statusEntries }, pipeline.id)) })
|
|
1168
|
-
] }) })
|
|
1231
|
+
] }) })
|
|
1169
1232
|
] }) }) });
|
|
1170
1233
|
}
|
|
1171
|
-
|
|
1234
|
+
var ACTOR_LEAD = {
|
|
1235
|
+
vendor: "An outside service",
|
|
1236
|
+
workflow: "An automated workflow",
|
|
1237
|
+
agent: "An AI agent"
|
|
1238
|
+
};
|
|
1239
|
+
var REVIEW_MODE_TAIL = {
|
|
1240
|
+
live: "and you review it live, in the moment",
|
|
1241
|
+
queued: "then it waits for your review",
|
|
1242
|
+
none: "and moves on without review"
|
|
1243
|
+
};
|
|
1244
|
+
function describeStep(step) {
|
|
1245
|
+
return `${ACTOR_LEAD[step.actor]} handles "${step.label}," ${REVIEW_MODE_TAIL[step.reviewMode]}.`;
|
|
1246
|
+
}
|
|
1247
|
+
function StepRow({ step, resourceId }) {
|
|
1248
|
+
return /* @__PURE__ */ jsxs(Group, { gap: "sm", align: "flex-start", wrap: "nowrap", children: [
|
|
1249
|
+
/* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", style: { flexShrink: 0 }, children: step.order }),
|
|
1250
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
|
|
1251
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", children: describeStep(step) }),
|
|
1252
|
+
step.description ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: step.description }) : null,
|
|
1253
|
+
resourceId ? /* @__PURE__ */ jsx(Text, { size: "xs", ff: "monospace", c: "dimmed", children: resourceId }) : null
|
|
1254
|
+
] })
|
|
1255
|
+
] });
|
|
1256
|
+
}
|
|
1257
|
+
function HowItWorks({ pipeline }) {
|
|
1172
1258
|
const contentConfig = useContentConfig();
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
const summary = useContentPipelineSummary(pipeline.id, statusKeys, stepResourceIds);
|
|
1179
|
-
return /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
1180
|
-
/* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
1181
|
-
/* @__PURE__ */ jsx(CardHeader, { title: "Funnel", subtitle: "Item counts across content:catalog/status for this pipeline" }),
|
|
1182
|
-
summary.isLoading ? /* @__PURE__ */ jsx(Center, { p: "md", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) }) : statusEntries.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No content-stage catalog declared for status yet." }) : /* @__PURE__ */ jsx(Group, { gap: "xs", wrap: "wrap", children: statusEntries.map((entry) => {
|
|
1183
|
-
const colors = getContentStageColor(entry);
|
|
1184
|
-
return /* @__PURE__ */ jsxs(
|
|
1185
|
-
Badge,
|
|
1186
|
-
{
|
|
1187
|
-
size: "lg",
|
|
1188
|
-
variant: "light",
|
|
1189
|
-
styles: { root: { color: colors.accent, backgroundColor: colors.background } },
|
|
1190
|
-
children: [
|
|
1191
|
-
entry.label,
|
|
1192
|
-
": ",
|
|
1193
|
-
summary.countsByStatus[entry.key] ?? 0
|
|
1194
|
-
]
|
|
1195
|
-
},
|
|
1196
|
-
entry.key
|
|
1197
|
-
);
|
|
1198
|
-
}) })
|
|
1199
|
-
] }) }),
|
|
1200
|
-
/* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
1201
|
-
/* @__PURE__ */ jsx(CardHeader, { title: "Declared config", subtitle: `${pipeline.steps.length} step(s), in catalog order` }),
|
|
1202
|
-
/* @__PURE__ */ jsxs(Table, { children: [
|
|
1203
|
-
/* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
1204
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Order" }),
|
|
1205
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Step" }),
|
|
1206
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Actor" }),
|
|
1207
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Review mode" }),
|
|
1208
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Advances to" }),
|
|
1209
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Resource" })
|
|
1210
|
-
] }) }),
|
|
1211
|
-
/* @__PURE__ */ jsx(Table.Tbody, { children: pipeline.steps.map((step) => {
|
|
1212
|
-
const binding = contentConfig.resolveStepResource(step);
|
|
1213
|
-
return /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
1214
|
-
/* @__PURE__ */ jsx(Table.Td, { children: step.order }),
|
|
1215
|
-
/* @__PURE__ */ jsxs(Table.Td, { children: [
|
|
1216
|
-
/* @__PURE__ */ jsx(Text, { fw: 500, children: step.label }),
|
|
1217
|
-
step.description ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: step.description }) : null
|
|
1218
|
-
] }),
|
|
1219
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", children: step.actor }) }),
|
|
1220
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: step.reviewMode }) }),
|
|
1221
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: step.advancesTo ?? "\u2014" }) }),
|
|
1222
|
-
/* @__PURE__ */ jsx(Table.Td, { children: binding ? /* @__PURE__ */ jsx(Text, { size: "xs", ff: "monospace", c: "dimmed", children: binding.resourceId }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No resource configured" }) })
|
|
1223
|
-
] }, step.key);
|
|
1224
|
-
}) })
|
|
1225
|
-
] })
|
|
1259
|
+
return /* @__PURE__ */ jsx(Accordion, { variant: "separated", children: /* @__PURE__ */ jsxs(Accordion.Item, { value: "how-it-works", children: [
|
|
1260
|
+
/* @__PURE__ */ jsx(Accordion.Control, { icon: /* @__PURE__ */ jsx(IconInfoCircle, { size: 16 }), children: /* @__PURE__ */ jsxs(Text, { size: "sm", fw: 500, children: [
|
|
1261
|
+
"How this ",
|
|
1262
|
+
contentConfig.vocabulary.pipeline.singular.toLowerCase(),
|
|
1263
|
+
" works"
|
|
1226
1264
|
] }) }),
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1265
|
+
/* @__PURE__ */ jsx(Accordion.Panel, { children: /* @__PURE__ */ jsx(Stack, { gap: "sm", children: pipeline.steps.map((step) => /* @__PURE__ */ jsx(StepRow, { step, resourceId: contentConfig.resolveStepResource(step)?.resourceId }, step.key)) }) })
|
|
1266
|
+
] }) });
|
|
1267
|
+
}
|
|
1268
|
+
var BOARD_FETCH_LIMIT = 100;
|
|
1269
|
+
function formatAge(ms) {
|
|
1270
|
+
const seconds = Math.floor(ms / 1e3);
|
|
1271
|
+
if (seconds < 60) return `${seconds}s`;
|
|
1272
|
+
const minutes = Math.floor(seconds / 60);
|
|
1273
|
+
if (minutes < 60) return `${minutes}m`;
|
|
1274
|
+
const hours = Math.floor(minutes / 60);
|
|
1275
|
+
if (hours < 24) return `${hours}h`;
|
|
1276
|
+
return `${Math.floor(hours / 24)}d`;
|
|
1277
|
+
}
|
|
1278
|
+
function BoardItemCard({ card, onOpen }) {
|
|
1279
|
+
return /* @__PURE__ */ jsx(Card, { withBorder: true, padding: "xs", onClick: () => onOpen(card.item.id), style: { cursor: "pointer" }, children: /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
1280
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, lineClamp: 2, children: getContentItemIdentity(card.item) }),
|
|
1281
|
+
/* @__PURE__ */ jsxs(Group, { gap: 6, justify: "space-between", wrap: "nowrap", children: [
|
|
1282
|
+
/* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", children: [
|
|
1283
|
+
/* @__PURE__ */ jsx(IconClockHour4, { size: 12, style: { color: "var(--color-text-dimmed)" } }),
|
|
1284
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: formatAge(card.ageMs) })
|
|
1285
|
+
] }),
|
|
1286
|
+
card.awaitingReview ? /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", children: "Awaiting review" }) : null
|
|
1287
|
+
] })
|
|
1288
|
+
] }) });
|
|
1289
|
+
}
|
|
1290
|
+
function BoardColumn({ column, onOpen }) {
|
|
1291
|
+
return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "sm", style: { minWidth: 260, maxWidth: 260, flexShrink: 0 }, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
|
|
1292
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", wrap: "nowrap", children: [
|
|
1293
|
+
/* @__PURE__ */ jsx(Text, { fw: 600, size: "sm", truncate: true, children: column.step.label }),
|
|
1294
|
+
/* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", children: column.cards.length })
|
|
1295
|
+
] }),
|
|
1296
|
+
column.cards.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Nothing here" }) : /* @__PURE__ */ jsx(Stack, { gap: "xs", children: column.cards.map((card) => /* @__PURE__ */ jsx(BoardItemCard, { card, onOpen }, card.item.id)) })
|
|
1297
|
+
] }) });
|
|
1298
|
+
}
|
|
1299
|
+
function ColumnBoard({ board, onOpen }) {
|
|
1300
|
+
return /* @__PURE__ */ jsx(ScrollArea, { type: "auto", offsetScrollbars: true, children: /* @__PURE__ */ jsx(Group, { gap: "xs", align: "flex-start", wrap: "nowrap", children: board.columns.map((column) => /* @__PURE__ */ jsx(BoardColumn, { column, onOpen }, column.step.key)) }) });
|
|
1301
|
+
}
|
|
1302
|
+
function CollapsedBoard({
|
|
1303
|
+
board,
|
|
1304
|
+
itemNounPlural,
|
|
1305
|
+
onOpen
|
|
1306
|
+
}) {
|
|
1307
|
+
const column = board.columns[0];
|
|
1308
|
+
if (!column) {
|
|
1309
|
+
return /* @__PURE__ */ jsx(
|
|
1310
|
+
EmptyState,
|
|
1311
|
+
{
|
|
1312
|
+
icon: IconRoute,
|
|
1313
|
+
title: "No steps declared",
|
|
1314
|
+
description: "This pipeline has no steps yet, so nothing has anywhere to sit."
|
|
1315
|
+
}
|
|
1316
|
+
);
|
|
1317
|
+
}
|
|
1318
|
+
const waiting = column.cards.filter((card) => card.awaitingReview);
|
|
1319
|
+
const settled = column.cards.filter((card) => !card.awaitingReview);
|
|
1320
|
+
const hasGate = !!column.gate;
|
|
1321
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
1322
|
+
/* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
|
|
1323
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: hasGate ? "Waiting on review" : column.step.label }),
|
|
1324
|
+
waiting.length === 0 ? column.cards.length === 0 ? /* @__PURE__ */ jsx(
|
|
1325
|
+
EmptyState,
|
|
1326
|
+
{
|
|
1327
|
+
icon: IconChecklist,
|
|
1328
|
+
title: `No ${itemNounPlural.toLowerCase()} yet`,
|
|
1329
|
+
description: `Produce one and it lands at "${column.step.label}."`
|
|
1330
|
+
}
|
|
1331
|
+
) : /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "Nothing is waiting on review right now." }) : /* @__PURE__ */ jsx(Stack, { gap: "xs", children: waiting.map((card) => /* @__PURE__ */ jsx(BoardItemCard, { card, onOpen }, card.item.id)) })
|
|
1332
|
+
] }),
|
|
1333
|
+
settled.length > 0 ? /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
|
|
1334
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", tt: "uppercase", fw: 600, children: hasGate ? "Already reviewed" : "In progress" }),
|
|
1335
|
+
/* @__PURE__ */ jsx(Stack, { gap: "xs", children: settled.map((card) => /* @__PURE__ */ jsx(BoardItemCard, { card, onOpen }, card.item.id)) })
|
|
1230
1336
|
] }) : null
|
|
1231
1337
|
] });
|
|
1232
1338
|
}
|
|
1233
|
-
function
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1339
|
+
function BoardOverflowSection({
|
|
1340
|
+
title,
|
|
1341
|
+
description,
|
|
1342
|
+
cards,
|
|
1343
|
+
onOpen
|
|
1344
|
+
}) {
|
|
1345
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
|
|
1346
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
|
|
1347
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", tt: "uppercase", fw: 600, children: title }),
|
|
1348
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: description })
|
|
1349
|
+
] }),
|
|
1350
|
+
/* @__PURE__ */ jsx(Group, { gap: "xs", align: "flex-start", wrap: "wrap", children: cards.map((card) => /* @__PURE__ */ jsx(Box, { style: { width: 260 }, children: /* @__PURE__ */ jsx(BoardItemCard, { card, onOpen }) }, card.item.id)) })
|
|
1351
|
+
] });
|
|
1242
1352
|
}
|
|
1243
|
-
function
|
|
1353
|
+
function BoardTab({ pipeline }) {
|
|
1354
|
+
const navigate = useNavigate();
|
|
1244
1355
|
const contentConfig = useContentConfig();
|
|
1245
|
-
const
|
|
1246
|
-
const
|
|
1247
|
-
const
|
|
1248
|
-
const
|
|
1356
|
+
const itemsQuery = useContentItems({ pipelineId: pipeline.id, limit: BOARD_FETCH_LIMIT });
|
|
1357
|
+
const items = useMemo(() => itemsQuery.data?.data ?? [], [itemsQuery.data]);
|
|
1358
|
+
const board = useMemo(() => deriveContentBoard(items, pipeline, Date.now()), [items, pipeline]);
|
|
1359
|
+
const openItem = (itemId) => navigate({ to: "/content/items/$itemId", params: { itemId } });
|
|
1360
|
+
if (itemsQuery.isLoading) {
|
|
1361
|
+
return /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) });
|
|
1362
|
+
}
|
|
1363
|
+
if (itemsQuery.isError) {
|
|
1364
|
+
return /* @__PURE__ */ jsx(
|
|
1365
|
+
CenteredErrorState,
|
|
1366
|
+
{
|
|
1367
|
+
error: itemsQuery.error,
|
|
1368
|
+
title: `Failed to load ${contentConfig.vocabulary.item.plural.toLowerCase()}`
|
|
1369
|
+
}
|
|
1370
|
+
);
|
|
1371
|
+
}
|
|
1372
|
+
if (items.length === 0) {
|
|
1373
|
+
return /* @__PURE__ */ jsx(
|
|
1374
|
+
EmptyState,
|
|
1375
|
+
{
|
|
1376
|
+
icon: IconChecklist,
|
|
1377
|
+
title: `No ${contentConfig.vocabulary.item.plural.toLowerCase()} in this pipeline yet`,
|
|
1378
|
+
description: "Produce something from the Produce tab and it will show up here."
|
|
1379
|
+
}
|
|
1380
|
+
);
|
|
1381
|
+
}
|
|
1249
1382
|
return /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
1250
|
-
/* @__PURE__ */ jsx(
|
|
1251
|
-
|
|
1383
|
+
board.isCollapsed ? /* @__PURE__ */ jsx(CollapsedBoard, { board, itemNounPlural: contentConfig.vocabulary.item.plural, onOpen: openItem }) : /* @__PURE__ */ jsx(ColumnBoard, { board, onOpen: openItem }),
|
|
1384
|
+
board.done.length > 0 ? /* @__PURE__ */ jsx(
|
|
1385
|
+
BoardOverflowSection,
|
|
1252
1386
|
{
|
|
1253
|
-
|
|
1254
|
-
description: "
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
value: contentItemId,
|
|
1258
|
-
onChange: setContentItemId,
|
|
1259
|
-
disabled: itemsQuery.isLoading || items.length === 0,
|
|
1260
|
-
searchable: true,
|
|
1261
|
-
clearable: true
|
|
1387
|
+
title: "Done",
|
|
1388
|
+
description: "Every declared step has succeeded for these.",
|
|
1389
|
+
cards: board.done,
|
|
1390
|
+
onOpen: openItem
|
|
1262
1391
|
}
|
|
1263
|
-
)
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
step.reviewMode,
|
|
1275
|
-
" review"
|
|
1276
|
-
] })
|
|
1277
|
-
] })
|
|
1278
|
-
] }),
|
|
1279
|
-
isRunnable && canManageContent.allowed ? /* @__PURE__ */ jsx(
|
|
1280
|
-
RunResourceButton,
|
|
1281
|
-
{
|
|
1282
|
-
resourceId: binding.resourceId,
|
|
1283
|
-
resourceType: binding.resourceKind,
|
|
1284
|
-
icon: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 }),
|
|
1285
|
-
label: "Run",
|
|
1286
|
-
title: step.label,
|
|
1287
|
-
disabled: contentItemId === null,
|
|
1288
|
-
baseInput: contentItemId !== null ? { contentItemId } : void 0,
|
|
1289
|
-
getInput: () => step.payloadFields.length > 0 ? { schema: compileContentPayloadValidator(step.payloadFields) } : {}
|
|
1290
|
-
}
|
|
1291
|
-
) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", maw: 220, ta: "right", children: !canManageContent.allowed ? "You do not have permission to run content pipeline steps." : binding ? `Resource kind "${binding.resourceKind}" isn't directly runnable from here.` : "No resource configured for this step yet." })
|
|
1292
|
-
] }) }, step.key);
|
|
1293
|
-
})
|
|
1392
|
+
) : null,
|
|
1393
|
+
board.unplaced.length > 0 ? /* @__PURE__ */ jsx(
|
|
1394
|
+
BoardOverflowSection,
|
|
1395
|
+
{
|
|
1396
|
+
title: "Not on this pipeline's current steps",
|
|
1397
|
+
description: "Their recorded steps aren't in the step catalog anymore -- shown honestly rather than dropped into the first column.",
|
|
1398
|
+
cards: board.unplaced,
|
|
1399
|
+
onOpen: openItem
|
|
1400
|
+
}
|
|
1401
|
+
) : null,
|
|
1402
|
+
/* @__PURE__ */ jsx(HowItWorks, { pipeline })
|
|
1294
1403
|
] });
|
|
1295
1404
|
}
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1405
|
+
var PAGE_SIZE2 = 20;
|
|
1406
|
+
var FETCH_LIMIT = 100;
|
|
1407
|
+
function summarizeMetrics(metrics) {
|
|
1408
|
+
if (!metrics || typeof metrics !== "object" || Array.isArray(metrics)) return "\u2014";
|
|
1409
|
+
const entries = Object.entries(metrics);
|
|
1410
|
+
if (entries.length === 0) return "\u2014";
|
|
1411
|
+
const shown = entries.slice(0, 2).map(([key, value]) => `${key} ${String(value)}`).join(" \xB7 ");
|
|
1412
|
+
return entries.length > 2 ? `${shown} +${entries.length - 2}` : shown;
|
|
1299
1413
|
}
|
|
1300
1414
|
function formatDateTime(value) {
|
|
1415
|
+
return value ? new Date(value).toLocaleDateString() : "\u2014";
|
|
1416
|
+
}
|
|
1417
|
+
function DistributionsTab({ pipeline }) {
|
|
1418
|
+
const navigate = useNavigate();
|
|
1419
|
+
const contentConfig = useContentConfig();
|
|
1420
|
+
const distributionLabel = contentConfig.vocabulary.distribution.plural.toLowerCase();
|
|
1421
|
+
const itemsQuery = useContentItems({ pipelineId: pipeline.id, limit: FETCH_LIMIT });
|
|
1422
|
+
const distributionsQuery = useContentDistributions({ limit: FETCH_LIMIT });
|
|
1423
|
+
const itemById = useMemo(() => {
|
|
1424
|
+
const map = /* @__PURE__ */ new Map();
|
|
1425
|
+
for (const item of itemsQuery.data?.data ?? []) map.set(item.id, item);
|
|
1426
|
+
return map;
|
|
1427
|
+
}, [itemsQuery.data]);
|
|
1428
|
+
const distributions = useMemo(
|
|
1429
|
+
() => (distributionsQuery.data?.data ?? []).filter((distribution) => itemById.has(distribution.contentItemId)),
|
|
1430
|
+
[distributionsQuery.data, itemById]
|
|
1431
|
+
);
|
|
1432
|
+
const pagination = usePaginationState(PAGE_SIZE2, [pipeline.id], distributions.length);
|
|
1433
|
+
const paginatedDistributions = distributions.slice(pagination.offset, pagination.offset + PAGE_SIZE2);
|
|
1434
|
+
const isLoading = itemsQuery.isLoading || distributionsQuery.isLoading;
|
|
1435
|
+
const error = itemsQuery.error ?? distributionsQuery.error;
|
|
1436
|
+
const resolveIdentity = (distribution) => {
|
|
1437
|
+
const item = itemById.get(distribution.contentItemId);
|
|
1438
|
+
return item ? getContentItemIdentity(item) : distribution.contentItemId;
|
|
1439
|
+
};
|
|
1440
|
+
if (isLoading) {
|
|
1441
|
+
return /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) });
|
|
1442
|
+
}
|
|
1443
|
+
if (itemsQuery.isError || distributionsQuery.isError) {
|
|
1444
|
+
return /* @__PURE__ */ jsx(CenteredErrorState, { error, title: `Failed to load ${distributionLabel}` });
|
|
1445
|
+
}
|
|
1446
|
+
if (distributions.length === 0) {
|
|
1447
|
+
return /* @__PURE__ */ jsx(
|
|
1448
|
+
EmptyState,
|
|
1449
|
+
{
|
|
1450
|
+
icon: IconShare,
|
|
1451
|
+
title: `No ${distributionLabel} yet`,
|
|
1452
|
+
description: `Nothing from this pipeline has been distributed anywhere yet.`
|
|
1453
|
+
}
|
|
1454
|
+
);
|
|
1455
|
+
}
|
|
1456
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
1457
|
+
/* @__PURE__ */ jsxs(Table, { children: [
|
|
1458
|
+
/* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
1459
|
+
/* @__PURE__ */ jsx(Table.Th, { children: contentConfig.vocabulary.item.singular }),
|
|
1460
|
+
/* @__PURE__ */ jsx(Table.Th, { children: "Platform" }),
|
|
1461
|
+
/* @__PURE__ */ jsx(Table.Th, { style: { whiteSpace: "nowrap" }, children: "Publish method" }),
|
|
1462
|
+
/* @__PURE__ */ jsx(Table.Th, { style: { whiteSpace: "nowrap" }, children: "Platform post ID" }),
|
|
1463
|
+
/* @__PURE__ */ jsx(Table.Th, { children: "Metrics" }),
|
|
1464
|
+
/* @__PURE__ */ jsx(Table.Th, { style: { whiteSpace: "nowrap" }, children: "Metrics updated" }),
|
|
1465
|
+
/* @__PURE__ */ jsx(Table.Th, { children: "Published" }),
|
|
1466
|
+
/* @__PURE__ */ jsx(Table.Th, {})
|
|
1467
|
+
] }) }),
|
|
1468
|
+
/* @__PURE__ */ jsx(Table.Tbody, { children: paginatedDistributions.map((distribution) => {
|
|
1469
|
+
const platformEntry = contentConfig.stageCatalog[distribution.platform];
|
|
1470
|
+
const platformColors = getContentStageColor(platformEntry);
|
|
1471
|
+
return /* @__PURE__ */ jsxs(
|
|
1472
|
+
Table.Tr,
|
|
1473
|
+
{
|
|
1474
|
+
style: { cursor: "pointer" },
|
|
1475
|
+
onClick: () => navigate({
|
|
1476
|
+
to: "/content/distributions/$distributionId",
|
|
1477
|
+
params: { distributionId: distribution.id }
|
|
1478
|
+
}),
|
|
1479
|
+
children: [
|
|
1480
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, lineClamp: 1, children: resolveIdentity(distribution) }) }),
|
|
1481
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(
|
|
1482
|
+
Badge,
|
|
1483
|
+
{
|
|
1484
|
+
size: "sm",
|
|
1485
|
+
variant: "light",
|
|
1486
|
+
styles: { root: { color: platformColors.accent, backgroundColor: platformColors.background } },
|
|
1487
|
+
children: platformEntry?.label ?? distribution.platform
|
|
1488
|
+
}
|
|
1489
|
+
) }),
|
|
1490
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: distribution.publishMethod ?? "\u2014" }) }),
|
|
1491
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "xs", ff: "monospace", c: "dimmed", children: distribution.platformPostId ?? "\u2014" }) }),
|
|
1492
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: summarizeMetrics(distribution.metrics) }) }),
|
|
1493
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDateTime(distribution.metricsUpdatedAt) }) }),
|
|
1494
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDateTime(distribution.publishedAt) }) }),
|
|
1495
|
+
/* @__PURE__ */ jsx(Table.Td, { children: distribution.platformUrl ? /* @__PURE__ */ jsx(
|
|
1496
|
+
Anchor,
|
|
1497
|
+
{
|
|
1498
|
+
href: distribution.platformUrl,
|
|
1499
|
+
target: "_blank",
|
|
1500
|
+
rel: "noopener noreferrer",
|
|
1501
|
+
style: { display: "inline-flex" },
|
|
1502
|
+
onClick: (event) => event.stopPropagation(),
|
|
1503
|
+
children: /* @__PURE__ */ jsx(IconExternalLink, { size: 16 })
|
|
1504
|
+
}
|
|
1505
|
+
) : null })
|
|
1506
|
+
]
|
|
1507
|
+
},
|
|
1508
|
+
distribution.id
|
|
1509
|
+
);
|
|
1510
|
+
}) })
|
|
1511
|
+
] }),
|
|
1512
|
+
distributions.length > PAGE_SIZE2 ? /* @__PURE__ */ jsx(Group, { justify: "center", children: /* @__PURE__ */ jsx(
|
|
1513
|
+
Pagination,
|
|
1514
|
+
{
|
|
1515
|
+
value: pagination.page,
|
|
1516
|
+
onChange: pagination.setPage,
|
|
1517
|
+
total: pagination.totalPages(distributions.length),
|
|
1518
|
+
size: "sm"
|
|
1519
|
+
}
|
|
1520
|
+
) }) : null
|
|
1521
|
+
] });
|
|
1522
|
+
}
|
|
1523
|
+
function formatDateTime2(value) {
|
|
1301
1524
|
return value ? new Date(value).toLocaleString() : "\u2014";
|
|
1302
1525
|
}
|
|
1303
1526
|
function formatDuration(startTime, endTime) {
|
|
@@ -1338,7 +1561,7 @@ function StepRunsSection({ step, resourceId }) {
|
|
|
1338
1561
|
/* @__PURE__ */ jsx(Table.Tbody, { children: executions.map((execution) => /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
1339
1562
|
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "xs", ff: "monospace", c: "dimmed", children: execution.id }) }),
|
|
1340
1563
|
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", styles: { root: { color: getStatusColor(execution.status) } }, children: execution.status }) }),
|
|
1341
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children:
|
|
1564
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDateTime2(execution.startTime) }) }),
|
|
1342
1565
|
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDuration(execution.startTime, execution.endTime) }) })
|
|
1343
1566
|
] }, execution.id)) })
|
|
1344
1567
|
] })
|
|
@@ -1348,19 +1571,18 @@ function RunsTab({ pipeline }) {
|
|
|
1348
1571
|
const contentConfig = useContentConfig();
|
|
1349
1572
|
return /* @__PURE__ */ jsx(Stack, { gap: "md", children: pipeline.steps.map((step) => /* @__PURE__ */ jsx(StepRunsSection, { step, resourceId: contentConfig.resolveStepResource(step)?.resourceId }, step.key)) });
|
|
1350
1573
|
}
|
|
1351
|
-
var PIPELINE_WORKSPACE_TABS = ["
|
|
1574
|
+
var PIPELINE_WORKSPACE_TABS = ["board", "distributions", "runs"];
|
|
1352
1575
|
function isPipelineWorkspaceTab(value) {
|
|
1353
1576
|
return typeof value === "string" && PIPELINE_WORKSPACE_TABS.includes(value);
|
|
1354
1577
|
}
|
|
1355
1578
|
function ContentPipelineWorkspacePage({
|
|
1356
1579
|
pipelineId,
|
|
1357
|
-
headerActions
|
|
1358
|
-
renderProduce
|
|
1580
|
+
headerActions
|
|
1359
1581
|
}) {
|
|
1360
1582
|
const navigate = useNavigate();
|
|
1361
1583
|
const contentConfig = useContentConfig();
|
|
1362
1584
|
const rawSearch = useSearch({ strict: false });
|
|
1363
|
-
const activeTab = isPipelineWorkspaceTab(rawSearch.tab) ? rawSearch.tab : "
|
|
1585
|
+
const activeTab = isPipelineWorkspaceTab(rawSearch.tab) ? rawSearch.tab : "board";
|
|
1364
1586
|
const handleTabChange = useCallback(
|
|
1365
1587
|
(value) => {
|
|
1366
1588
|
if (!isPipelineWorkspaceTab(value) || value === activeTab) return;
|
|
@@ -1373,7 +1595,7 @@ function ContentPipelineWorkspacePage({
|
|
|
1373
1595
|
[activeTab, navigate]
|
|
1374
1596
|
);
|
|
1375
1597
|
const pipeline = contentConfig.pipelines.find((candidate) => candidate.id === pipelineId);
|
|
1376
|
-
const
|
|
1598
|
+
const vocabulary = contentConfig.vocabulary;
|
|
1377
1599
|
const backButton = /* @__PURE__ */ jsx(
|
|
1378
1600
|
Button,
|
|
1379
1601
|
{
|
|
@@ -1411,109 +1633,19 @@ function ContentPipelineWorkspacePage({
|
|
|
1411
1633
|
}
|
|
1412
1634
|
),
|
|
1413
1635
|
headerActions ? /* @__PURE__ */ jsx(Group, { gap: "xs", children: headerActions }) : null,
|
|
1414
|
-
/* @__PURE__ */ jsx(
|
|
1415
|
-
pipeline.steps.length,
|
|
1416
|
-
" step(s)"
|
|
1417
|
-
] }) }),
|
|
1418
|
-
/* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Tabs, { value: activeTab, onChange: handleTabChange, keepMounted: false, children: [
|
|
1636
|
+
/* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Tabs, { value: activeTab, onChange: handleTabChange, keepMounted: false, children: [
|
|
1419
1637
|
/* @__PURE__ */ jsxs(Tabs.List, { children: [
|
|
1420
|
-
/* @__PURE__ */ jsx(Tabs.Tab, { value: "
|
|
1421
|
-
/* @__PURE__ */ jsx(Tabs.Tab, { value: "
|
|
1422
|
-
/* @__PURE__ */ jsx(Tabs.Tab, { value: "produce", leftSection: /* @__PURE__ */ jsx(IconBolt, { size: 14 }), children: "Produce" }),
|
|
1638
|
+
/* @__PURE__ */ jsx(Tabs.Tab, { value: "board", leftSection: /* @__PURE__ */ jsx(IconLayoutGrid, { size: 14 }), children: vocabulary.board }),
|
|
1639
|
+
/* @__PURE__ */ jsx(Tabs.Tab, { value: "distributions", leftSection: /* @__PURE__ */ jsx(IconShare, { size: 14 }), children: vocabulary.distribution.plural }),
|
|
1423
1640
|
/* @__PURE__ */ jsx(Tabs.Tab, { value: "runs", leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 }), children: "Runs" })
|
|
1424
1641
|
] }),
|
|
1425
|
-
/* @__PURE__ */ jsx(Tabs.Panel, { value: "
|
|
1426
|
-
/* @__PURE__ */ jsx(Tabs.Panel, { value: "
|
|
1427
|
-
/* @__PURE__ */ jsx(Tabs.Panel, { value: "
|
|
1428
|
-
/* @__PURE__ */ jsx(Tabs.Panel, { value: "runs", pt: "sm", children: /* @__PURE__ */ jsx(RunsTab, { pipeline }) })
|
|
1642
|
+
/* @__PURE__ */ jsx(Tabs.Panel, { value: "board", pt: "md", children: /* @__PURE__ */ jsx(BoardTab, { pipeline }) }),
|
|
1643
|
+
/* @__PURE__ */ jsx(Tabs.Panel, { value: "distributions", pt: "md", children: /* @__PURE__ */ jsx(DistributionsTab, { pipeline }) }),
|
|
1644
|
+
/* @__PURE__ */ jsx(Tabs.Panel, { value: "runs", pt: "md", children: /* @__PURE__ */ jsx(RunsTab, { pipeline }) })
|
|
1429
1645
|
] }) })
|
|
1430
1646
|
] }) }) });
|
|
1431
1647
|
}
|
|
1432
|
-
|
|
1433
|
-
return new Date(value).toLocaleString();
|
|
1434
|
-
}
|
|
1435
|
-
function ContentSettingsPage({ headerActions } = {}) {
|
|
1436
|
-
const contentConfig = useContentConfig();
|
|
1437
|
-
const [selectedPipelineId, setSelectedPipelineId] = useState(void 0);
|
|
1438
|
-
const pipelineId = selectedPipelineId ?? contentConfig.defaultPipelineId;
|
|
1439
|
-
const pipelineOptions = useMemo(
|
|
1440
|
-
() => contentConfig.pipelines.map((pipeline) => ({ value: pipeline.id, label: pipeline.label })),
|
|
1441
|
-
[contentConfig.pipelines]
|
|
1442
|
-
);
|
|
1443
|
-
const artifactsQuery = useContentArtifacts({ pipelineId });
|
|
1444
|
-
const artifactsByKind = useMemo(() => {
|
|
1445
|
-
const groups = /* @__PURE__ */ new Map();
|
|
1446
|
-
for (const artifact of artifactsQuery.artifacts) {
|
|
1447
|
-
const existing = groups.get(artifact.kind) ?? [];
|
|
1448
|
-
existing.push(artifact);
|
|
1449
|
-
groups.set(artifact.kind, existing);
|
|
1450
|
-
}
|
|
1451
|
-
for (const [, entries] of groups) {
|
|
1452
|
-
entries.sort((a, b) => b.version - a.version);
|
|
1453
|
-
}
|
|
1454
|
-
return groups;
|
|
1455
|
-
}, [artifactsQuery.artifacts]);
|
|
1456
|
-
return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
1457
|
-
/* @__PURE__ */ jsx(
|
|
1458
|
-
PageTitleCaption,
|
|
1459
|
-
{
|
|
1460
|
-
title: "Rules and Artifacts",
|
|
1461
|
-
caption: "Browse content: rules documents and their version history"
|
|
1462
|
-
}
|
|
1463
|
-
),
|
|
1464
|
-
headerActions ? /* @__PURE__ */ jsx(Group, { gap: "xs", children: headerActions }) : null,
|
|
1465
|
-
pipelineOptions.length === 0 ? /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "yellow", variant: "light", children: "No content pipelines are declared yet, so there is nothing to scope artifacts to." }) }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1466
|
-
/* @__PURE__ */ jsx(Group, { gap: "xs", align: "flex-end", children: /* @__PURE__ */ jsx(
|
|
1467
|
-
Select,
|
|
1468
|
-
{
|
|
1469
|
-
label: "Pipeline",
|
|
1470
|
-
data: pipelineOptions,
|
|
1471
|
-
value: pipelineId ?? null,
|
|
1472
|
-
onChange: (value) => setSelectedPipelineId(value ?? void 0),
|
|
1473
|
-
allowDeselect: false,
|
|
1474
|
-
w: 280
|
|
1475
|
-
}
|
|
1476
|
-
) }),
|
|
1477
|
-
/* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsx(Stack, { gap: "md", children: artifactsQuery.isLoading ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) : artifactsQuery.isError ? /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "red", variant: "light", children: "Failed to load artifacts." }) : artifactsByKind.size === 0 ? /* @__PURE__ */ jsx(
|
|
1478
|
-
EmptyState,
|
|
1479
|
-
{
|
|
1480
|
-
icon: IconFileText,
|
|
1481
|
-
title: "No content: artifacts for this pipeline",
|
|
1482
|
-
description: "Rules documents (idea banks, ICP docs, style guides) created through the artifacts tool will appear here, grouped by kind."
|
|
1483
|
-
}
|
|
1484
|
-
) : [...artifactsByKind.entries()].map(([kind, versions]) => /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
|
|
1485
|
-
/* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
1486
|
-
/* @__PURE__ */ jsx(Text, { fw: 600, children: kind }),
|
|
1487
|
-
/* @__PURE__ */ jsxs(Badge, { size: "sm", variant: "light", children: [
|
|
1488
|
-
versions.length,
|
|
1489
|
-
" version",
|
|
1490
|
-
versions.length === 1 ? "" : "s"
|
|
1491
|
-
] })
|
|
1492
|
-
] }),
|
|
1493
|
-
/* @__PURE__ */ jsxs(Table, { children: [
|
|
1494
|
-
/* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
1495
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Version" }),
|
|
1496
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Active" }),
|
|
1497
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Created" }),
|
|
1498
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Updated" }),
|
|
1499
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Source execution" })
|
|
1500
|
-
] }) }),
|
|
1501
|
-
/* @__PURE__ */ jsx(Table.Tbody, { children: versions.map((artifact) => /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
1502
|
-
/* @__PURE__ */ jsxs(Table.Td, { children: [
|
|
1503
|
-
"v",
|
|
1504
|
-
artifact.version
|
|
1505
|
-
] }),
|
|
1506
|
-
/* @__PURE__ */ jsx(Table.Td, { children: artifact.isActive ? /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: "var(--color-success)", children: "Active" }) : /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: "var(--color-text-dimmed)", children: "Inactive" }) }),
|
|
1507
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDateTime2(artifact.createdAt) }) }),
|
|
1508
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDateTime2(artifact.updatedAt) }) }),
|
|
1509
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", children: artifact.sourceExecutionId ?? "\u2014" }) })
|
|
1510
|
-
] }, artifact.id)) })
|
|
1511
|
-
] })
|
|
1512
|
-
] }, kind)) }) })
|
|
1513
|
-
] })
|
|
1514
|
-
] }) }) });
|
|
1515
|
-
}
|
|
1516
|
-
var PAGE_SIZE2 = 20;
|
|
1648
|
+
var PAGE_SIZE3 = 20;
|
|
1517
1649
|
var QUERY_LIMIT = 100;
|
|
1518
1650
|
function formatDate(value) {
|
|
1519
1651
|
return value ? new Date(value).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" }) : "\u2014";
|
|
@@ -1553,11 +1685,11 @@ function ContentDistributionsPage({ headerActions } = {}) {
|
|
|
1553
1685
|
...(contentConfig.stagesByCatalog.platform ?? []).map((entry) => ({ value: entry.key, label: entry.label }))
|
|
1554
1686
|
];
|
|
1555
1687
|
const pagination = usePaginationState(
|
|
1556
|
-
|
|
1688
|
+
PAGE_SIZE3,
|
|
1557
1689
|
[platformFilter, statusFilter, formatFilter],
|
|
1558
1690
|
filteredDistributions.length
|
|
1559
1691
|
);
|
|
1560
|
-
const paginatedDistributions = filteredDistributions.slice(pagination.offset, pagination.offset +
|
|
1692
|
+
const paginatedDistributions = filteredDistributions.slice(pagination.offset, pagination.offset + PAGE_SIZE3);
|
|
1561
1693
|
const resolveTitle = (distribution) => itemTitleById.get(distribution.contentItemId) ?? distribution.contentItemId;
|
|
1562
1694
|
return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
1563
1695
|
/* @__PURE__ */ jsx(
|
|
@@ -1662,7 +1794,7 @@ function ContentDistributionsPage({ headerActions } = {}) {
|
|
|
1662
1794
|
);
|
|
1663
1795
|
}) })
|
|
1664
1796
|
] }),
|
|
1665
|
-
filteredDistributions.length >
|
|
1797
|
+
filteredDistributions.length > PAGE_SIZE3 && /* @__PURE__ */ jsx(Group, { justify: "center", children: /* @__PURE__ */ jsx(
|
|
1666
1798
|
Pagination,
|
|
1667
1799
|
{
|
|
1668
1800
|
value: pagination.page,
|
|
@@ -2177,4 +2309,4 @@ function ContentDistributionDetailPage({
|
|
|
2177
2309
|
] });
|
|
2178
2310
|
}
|
|
2179
2311
|
|
|
2180
|
-
export { AlternatesPanel, ContentDistributionDetailPage, ContentDistributionsPage, ContentItemReviewPage, ContentItemsPage, ContentOverviewPage, ContentPipelineWorkspacePage, ContentPipelinesPage, ContentReviewCard,
|
|
2312
|
+
export { AlternatesPanel, ContentDistributionDetailPage, ContentDistributionsPage, ContentItemReviewPage, ContentItemsPage, ContentOverviewPage, ContentPipelineWorkspacePage, ContentPipelinesPage, ContentReviewCard, PayloadBody, ProcessingStateStrip, ReviewActionBar, firstLine, formatElapsed, lastLine, swapFirstLine, swapLastLine };
|