@elevasis/ui 2.69.0 → 2.71.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.d.ts +26 -0
- 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-6PZFOHJK.js → chunk-3BLYJT5L.js} +2 -2
- package/dist/{chunk-IT7GTJCB.js → chunk-AIOV5NM7.js} +1 -1
- package/dist/{chunk-MRVGQLWN.js → chunk-BM2WKRAQ.js} +1 -1
- package/dist/{chunk-XAZR3T6Y.js → chunk-CPBT6Z2G.js} +3 -3
- package/dist/{chunk-XQ3ILKYN.js → chunk-DCMFNSJ6.js} +1 -1
- package/dist/{chunk-NU3NBXHP.js → chunk-DH2MO5DX.js} +1 -1
- package/dist/{chunk-ZD6LM53O.js → chunk-H3SDGC5F.js} +2 -2
- package/dist/{chunk-YGCMMOY4.js → chunk-JTSNU6KA.js} +695 -73
- package/dist/{chunk-7WJEPTE7.js → chunk-LL5H7J4N.js} +1 -1
- package/dist/{chunk-FLECOYUS.js → chunk-R6NMRXNO.js} +1 -1
- package/dist/{chunk-MFLM7BVC.js → chunk-TB2PM2OU.js} +1 -1
- package/dist/{chunk-IRV6KJPV.js → chunk-TCQZ4AJI.js} +1 -1
- package/dist/{chunk-YNRYIXUT.js → chunk-TVFSVTRG.js} +6 -6
- package/dist/{chunk-WXHAHKKI.js → chunk-UZ7MSORW.js} +16291 -16029
- package/dist/{chunk-RW64UJWT.js → chunk-VHPHWTX7.js} +2 -2
- package/dist/{chunk-YCRINA2D.js → chunk-VRRIHA3S.js} +1 -1
- package/dist/{chunk-4JHLS234.js → chunk-ZFISGKYW.js} +1 -1
- package/dist/components/index.css +208 -208
- package/dist/components/index.d.ts +59 -3
- 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.d.ts +26 -0
- 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 +258 -51
- package/dist/features/content/index.js +595 -421
- package/dist/features/crm/index.d.ts +26 -0
- 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.d.ts +26 -0
- 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.d.ts +33 -3
- 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.d.ts +26 -0
- 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/delivery/index.d.ts +26 -0
- package/dist/hooks/index.css +149 -149
- package/dist/hooks/index.d.ts +296 -65
- package/dist/hooks/index.js +3 -3
- package/dist/hooks/published.css +149 -149
- package/dist/hooks/published.d.ts +296 -65
- package/dist/hooks/published.js +3 -3
- package/dist/initialization/index.d.ts +26 -0
- 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/profile/index.d.ts +26 -0
- package/dist/provider/index.d.ts +26 -0
- package/dist/provider/index.js +3 -3
- package/dist/provider/published.css +149 -149
- package/dist/provider/published.d.ts +26 -0
- package/dist/provider/published.js +3 -3
- package/dist/supabase/index.d.ts +50 -0
- package/dist/theme/index.js +3 -3
- package/dist/theme/presets/index.js +1 -1
- package/dist/types/index.d.ts +59 -3
- package/package.json +5 -5
- 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, getOpenContentReviewGates, useContentDistribution, useUpdateContentDistribution, useContentPipelineSummary, deriveContentBoard, useExecutions } from '../../chunk-UZ7MSORW.js';
|
|
5
|
+
export { CONTENT_ITEMS, ContentSidebar, ContentSidebarMiddle, DEFAULT_CONTENT_VOCABULARY, MyReviewQueuePanel, contentManifest, deriveContentBoard, getContentItemIdentity, resolveContentStepResource, resolveContentVocabulary, useContentConfig } from '../../chunk-UZ7MSORW.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,
|
|
36
|
-
import { IconAlertTriangle, IconShieldCheck, IconShieldX,
|
|
32
|
+
import { Stack, Group, Text, Badge, Button, Textarea, Box, Loader, Accordion, Tooltip, Card, Alert, List, Paper, Center, SimpleGrid, Table, TextInput, Select, Pagination, Title, Tabs, Anchor, Divider, Progress, Checkbox, TagsInput, Image, 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,32 +334,36 @@ 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;
|
|
329
|
-
|
|
337
|
+
const irreversible = isIrreversible(action);
|
|
338
|
+
const button = /* @__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
|
-
disabled: pending,
|
|
346
|
+
disabled: pending || action.disabled,
|
|
337
347
|
onClick: () => openAction(action),
|
|
338
348
|
size: "xs",
|
|
339
349
|
radius: "xs",
|
|
340
350
|
"data-testid": `content-review-action-${action.id}`,
|
|
341
351
|
children: action.label
|
|
342
|
-
}
|
|
343
|
-
action.id
|
|
352
|
+
}
|
|
344
353
|
);
|
|
354
|
+
return action.disabled && action.disabledReason ? /* @__PURE__ */ jsx(Tooltip, { label: action.disabledReason, withArrow: true, position: "top", children: /* @__PURE__ */ jsx("span", { children: button }) }, action.id) : /* @__PURE__ */ jsx("span", { children: button }, action.id);
|
|
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
|
|
@@ -797,15 +832,20 @@ function ContentItemsPage({ headerActions } = {}) {
|
|
|
797
832
|
// src/lib/content/processing-state.ts
|
|
798
833
|
function mapContentStepStatus(status) {
|
|
799
834
|
if (status === void 0) return "pending";
|
|
835
|
+
if (status === "running") return "in_progress";
|
|
800
836
|
if (status === "error") return "failed";
|
|
801
837
|
return "completed";
|
|
802
838
|
}
|
|
803
839
|
function getContentProcessingSteps(processingState, pipelineSteps) {
|
|
804
|
-
return pipelineSteps.map((step) =>
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
840
|
+
return pipelineSteps.map((step) => {
|
|
841
|
+
const entry = processingState?.[step.key];
|
|
842
|
+
return {
|
|
843
|
+
key: step.key,
|
|
844
|
+
label: step.label,
|
|
845
|
+
status: mapContentStepStatus(entry?.status),
|
|
846
|
+
executionId: entry ? entry.executionId : void 0
|
|
847
|
+
};
|
|
848
|
+
});
|
|
809
849
|
}
|
|
810
850
|
|
|
811
851
|
// src/lib/content/payload-text.ts
|
|
@@ -837,38 +877,49 @@ function toPayloadDisplayFields(fields, payload) {
|
|
|
837
877
|
const payloadRecord = payload;
|
|
838
878
|
return fields.filter((field) => field.key in payloadRecord).map((field) => ({ key: field.key, label: field.label, kind: toDisplayKind(field.type) }));
|
|
839
879
|
}
|
|
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
|
-
|
|
880
|
+
var REVIEW_QUEUE_FETCH_LIMIT = 100;
|
|
881
|
+
function buildReviewActions(isTerminalPipeline, gate, reason) {
|
|
882
|
+
const gateLabel = gate?.step.label;
|
|
883
|
+
const disabled = !gate;
|
|
884
|
+
return [
|
|
885
|
+
{
|
|
886
|
+
id: "approved",
|
|
887
|
+
label: "Approve",
|
|
888
|
+
icon: IconCheck,
|
|
889
|
+
// Decision 7, resolved 2026-08-12: approve collects an OPTIONAL note, which is what
|
|
890
|
+
// finally gives `content_items.reviewer_feedback` a writer -- the page has always
|
|
891
|
+
// rendered it and the API has always accepted it, but nothing in the UI could set it.
|
|
892
|
+
// `feedbackRequired` is deliberately omitted: a reviewer with nothing to add still
|
|
893
|
+
// approves in two clicks, and a blank note leaves the column NULL rather than ''.
|
|
894
|
+
requiresFeedback: true,
|
|
895
|
+
feedbackLabel: "Note (optional)",
|
|
896
|
+
feedbackPlaceholder: "Anything the next step or a later reader should know?",
|
|
897
|
+
confirmationMessage: gateLabel ? isTerminalPipeline ? `Approve "${gateLabel}"? There is no further pipeline step -- this is the last internal gate before it is published.` : `Approve "${gateLabel}"? It moves on to the next pipeline step.` : void 0,
|
|
898
|
+
consequence: isTerminalPipeline ? "irreversible" : "standard",
|
|
899
|
+
disabled,
|
|
900
|
+
disabledReason: reason
|
|
901
|
+
},
|
|
902
|
+
{
|
|
903
|
+
id: "rejected",
|
|
904
|
+
label: "Reject",
|
|
905
|
+
icon: IconX,
|
|
906
|
+
tone: "danger",
|
|
907
|
+
requiresFeedback: true,
|
|
908
|
+
feedbackLabel: "Reason for rejection",
|
|
909
|
+
feedbackPlaceholder: "What needs to change before this can go out?",
|
|
910
|
+
feedbackRequired: true,
|
|
911
|
+
confirmationMessage: gateLabel ? `Reject "${gateLabel}"? This sends it back for changes.` : void 0,
|
|
912
|
+
disabled,
|
|
913
|
+
disabledReason: reason
|
|
914
|
+
}
|
|
915
|
+
];
|
|
916
|
+
}
|
|
866
917
|
function DistributionMetricsCell({ metrics }) {
|
|
867
|
-
|
|
918
|
+
const entries = metrics && typeof metrics === "object" ? Object.entries(metrics) : [];
|
|
919
|
+
if (entries.length === 0) {
|
|
868
920
|
return /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "\u2014" });
|
|
869
921
|
}
|
|
870
|
-
const
|
|
871
|
-
const isFlat = entries.length > 0 && entries.every(([, value]) => value === null || typeof value !== "object");
|
|
922
|
+
const isFlat = entries.every(([, value]) => value === null || typeof value !== "object");
|
|
872
923
|
if (!isFlat) {
|
|
873
924
|
return /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", ff: "monospace", style: { overflowWrap: "anywhere" }, children: JSON.stringify(metrics) });
|
|
874
925
|
}
|
|
@@ -890,9 +941,26 @@ function ContentItemReviewPage({ itemId, headerActions }) {
|
|
|
890
941
|
const item = itemQuery.data;
|
|
891
942
|
const pipeline = item ? contentConfig.pipelines.find((candidate) => candidate.id === item.pipelineId) : void 0;
|
|
892
943
|
const pipelineSteps = useMemo(() => pipeline?.steps ?? [], [pipeline]);
|
|
944
|
+
const isTerminalPipeline = pipelineSteps.length <= 1;
|
|
945
|
+
const openGates = useMemo(
|
|
946
|
+
() => getOpenContentReviewGates(item?.processingState ?? {}, pipelineSteps),
|
|
947
|
+
[item?.processingState, pipelineSteps]
|
|
948
|
+
);
|
|
949
|
+
const earliestOpenGate = openGates[0];
|
|
950
|
+
const reviewDisabledReason = useMemo(() => {
|
|
951
|
+
if (earliestOpenGate) return void 0;
|
|
952
|
+
if (!item?.pipelineId) return "This item has no pipeline, so there is no review gate to clear.";
|
|
953
|
+
if (!pipeline) return `Unknown pipeline "${item.pipelineId}" -- there is no review gate to clear.`;
|
|
954
|
+
return "No open review gate on this item. Every queued step has already been reviewed, or the current step is still running.";
|
|
955
|
+
}, [earliestOpenGate, item?.pipelineId, pipeline]);
|
|
956
|
+
const reviewActions = useMemo(
|
|
957
|
+
() => buildReviewActions(isTerminalPipeline, earliestOpenGate, reviewDisabledReason),
|
|
958
|
+
[isTerminalPipeline, earliestOpenGate, reviewDisabledReason]
|
|
959
|
+
);
|
|
893
960
|
const pipelinePayloadFields = useMemo(() => getPipelinePayloadFields(pipelineSteps), [pipelineSteps]);
|
|
894
961
|
const primaryTextField = useMemo(() => getPrimaryPayloadTextField(pipelinePayloadFields), [pipelinePayloadFields]);
|
|
895
962
|
const originalBody = item ? getPayloadText(item.payload, primaryTextField?.key) : "";
|
|
963
|
+
const savedBody = item?.body ?? originalBody;
|
|
896
964
|
const displayPayloadFields = useMemo(
|
|
897
965
|
() => toPayloadDisplayFields(pipelinePayloadFields, item?.payload),
|
|
898
966
|
[pipelinePayloadFields, item?.payload]
|
|
@@ -901,9 +969,51 @@ function ContentItemReviewPage({ itemId, headerActions }) {
|
|
|
901
969
|
() => getContentProcessingSteps(item?.processingState, pipelineSteps),
|
|
902
970
|
[item?.processingState, pipelineSteps]
|
|
903
971
|
);
|
|
972
|
+
const visibleProcessingSteps = useMemo(
|
|
973
|
+
() => processingSteps.some((step) => step.status !== "pending") ? processingSteps : [],
|
|
974
|
+
[processingSteps]
|
|
975
|
+
);
|
|
976
|
+
const pipelineItemsQuery = useContentItems({
|
|
977
|
+
pipelineId: item?.pipelineId ?? void 0,
|
|
978
|
+
limit: REVIEW_QUEUE_FETCH_LIMIT
|
|
979
|
+
});
|
|
980
|
+
const reviewQueue = useMemo(() => {
|
|
981
|
+
if (!item?.pipelineId) return [];
|
|
982
|
+
return (pipelineItemsQuery.data?.data ?? []).filter((candidate) => candidate.reviewedAt === null).sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
|
|
983
|
+
}, [pipelineItemsQuery.data, item?.pipelineId]);
|
|
984
|
+
const queueIndex = item ? reviewQueue.findIndex((candidate) => candidate.id === item.id) : -1;
|
|
985
|
+
const queuePosition = queueIndex === -1 ? void 0 : queueIndex + 1;
|
|
986
|
+
const nextQueueItemId = queueIndex === -1 ? void 0 : reviewQueue[queueIndex + 1]?.id;
|
|
987
|
+
const goToNextInQueue = useCallback(() => {
|
|
988
|
+
if (nextQueueItemId) {
|
|
989
|
+
navigate({ to: "/content/items/$itemId", params: { itemId: nextQueueItemId } });
|
|
990
|
+
} else {
|
|
991
|
+
navigate({ to: "/content/items" });
|
|
992
|
+
}
|
|
993
|
+
}, [navigate, nextQueueItemId]);
|
|
904
994
|
useEffect(() => {
|
|
905
995
|
if (item) setBody(item.body ?? originalBody);
|
|
906
996
|
}, [item?.id]);
|
|
997
|
+
useEffect(() => {
|
|
998
|
+
function handleKeyDown(event) {
|
|
999
|
+
const target = event.target;
|
|
1000
|
+
if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLElement && target.isContentEditable) {
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
if (event.key === "a" || event.key === "A") {
|
|
1004
|
+
event.preventDefault();
|
|
1005
|
+
document.querySelector('[data-testid="content-review-action-approved"]')?.click();
|
|
1006
|
+
} else if (event.key === "r" || event.key === "R") {
|
|
1007
|
+
event.preventDefault();
|
|
1008
|
+
document.querySelector('[data-testid="content-review-action-rejected"]')?.click();
|
|
1009
|
+
} else if (event.key === "n" || event.key === "N" || event.key === "ArrowRight") {
|
|
1010
|
+
event.preventDefault();
|
|
1011
|
+
goToNextInQueue();
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
1015
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1016
|
+
}, [goToNextInQueue]);
|
|
907
1017
|
const backButton = /* @__PURE__ */ jsx(
|
|
908
1018
|
Button,
|
|
909
1019
|
{
|
|
@@ -945,6 +1055,7 @@ function ContentItemReviewPage({ itemId, headerActions }) {
|
|
|
945
1055
|
const distributions = distributionsQuery.data?.data ?? [];
|
|
946
1056
|
const isSaving = updateItem.isPending || reviewItem.isPending;
|
|
947
1057
|
const viewingAttempt = attempts.find((attempt) => attempt.id === viewingAttemptId);
|
|
1058
|
+
const identity = getContentItemIdentity(item, pipelinePayloadFields);
|
|
948
1059
|
const persistBodyIfDirty = async () => {
|
|
949
1060
|
if (!isDirty) return;
|
|
950
1061
|
await updateItem.mutateAsync({ itemId: item.id, updates: { body } });
|
|
@@ -954,64 +1065,77 @@ function ContentItemReviewPage({ itemId, headerActions }) {
|
|
|
954
1065
|
showSuccessNotification("Saved");
|
|
955
1066
|
};
|
|
956
1067
|
const handleAction = async (actionId, feedback) => {
|
|
1068
|
+
if (!earliestOpenGate) return;
|
|
1069
|
+
const stepKey = earliestOpenGate.stepKey;
|
|
957
1070
|
await persistBodyIfDirty();
|
|
958
1071
|
if (actionId === "approved") {
|
|
959
1072
|
const note = feedback?.trim();
|
|
960
1073
|
await reviewItem.mutateAsync({
|
|
961
1074
|
itemId: item.id,
|
|
962
|
-
review: { decision: "approved", reviewerFeedback: note ? note : void 0 }
|
|
1075
|
+
review: { decision: "approved", stepKey, reviewerFeedback: note ? note : void 0 }
|
|
963
1076
|
});
|
|
964
1077
|
showSuccessNotification("Item approved");
|
|
965
1078
|
} else if (actionId === "rejected") {
|
|
966
|
-
await reviewItem.mutateAsync({
|
|
1079
|
+
await reviewItem.mutateAsync({
|
|
1080
|
+
itemId: item.id,
|
|
1081
|
+
review: { decision: "rejected", stepKey, rejectReason: feedback ?? "" }
|
|
1082
|
+
});
|
|
967
1083
|
showSuccessNotification("Item rejected");
|
|
968
1084
|
}
|
|
1085
|
+
goToNextInQueue();
|
|
969
1086
|
};
|
|
970
1087
|
return /* @__PURE__ */ jsxs(SubshellContentContainer, { children: [
|
|
971
1088
|
/* @__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
|
-
),
|
|
1089
|
+
/* @__PURE__ */ jsx(PageTitleCaption, { title: identity }),
|
|
980
1090
|
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: [
|
|
1091
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "xs", wrap: "nowrap", align: "center", children: [
|
|
1092
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "wrap", children: [
|
|
1093
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: pipeline?.label ?? item.pipelineId ?? "Unassigned" }),
|
|
983
1094
|
/* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", children: contentConfig.stageCatalog[item.status]?.label ?? item.status }),
|
|
984
1095
|
item.pillar && /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", children: contentConfig.stageCatalog[item.pillar]?.label ?? item.pillar }),
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
] })
|
|
1096
|
+
/* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
1097
|
+
item.reviewedAt ? `Reviewed ${new Date(item.reviewedAt).toLocaleDateString()}` : "Not yet reviewed",
|
|
1098
|
+
queuePosition ? ` \xB7 ${queuePosition} of ${reviewQueue.length}` : ""
|
|
1099
|
+
] })
|
|
989
1100
|
] }),
|
|
990
|
-
/* @__PURE__ */
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1101
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
|
|
1102
|
+
/* @__PURE__ */ jsx(
|
|
1103
|
+
Button,
|
|
1104
|
+
{
|
|
1105
|
+
size: "xs",
|
|
1106
|
+
variant: "light",
|
|
1107
|
+
onClick: handleSave,
|
|
1108
|
+
disabled: !isDirty || isSaving,
|
|
1109
|
+
loading: updateItem.isPending,
|
|
1110
|
+
children: "Save"
|
|
1111
|
+
}
|
|
1112
|
+
),
|
|
1113
|
+
backButton
|
|
1114
|
+
] })
|
|
1001
1115
|
] }),
|
|
1116
|
+
reviewDisabledReason && /* @__PURE__ */ jsx(
|
|
1117
|
+
Alert,
|
|
1118
|
+
{
|
|
1119
|
+
icon: /* @__PURE__ */ jsx(IconAlertTriangle, { size: 16 }),
|
|
1120
|
+
color: "var(--color-warning)",
|
|
1121
|
+
variant: "light",
|
|
1122
|
+
title: "Nothing to review right now",
|
|
1123
|
+
"data-testid": "content-review-no-open-gate",
|
|
1124
|
+
children: /* @__PURE__ */ jsx(Text, { size: "sm", children: reviewDisabledReason })
|
|
1125
|
+
}
|
|
1126
|
+
),
|
|
1002
1127
|
/* @__PURE__ */ jsx(
|
|
1003
1128
|
ContentReviewCard,
|
|
1004
1129
|
{
|
|
1005
|
-
title: item.title,
|
|
1006
1130
|
reviewMode: "live",
|
|
1007
1131
|
body,
|
|
1008
1132
|
onBodyChange: setBody,
|
|
1009
|
-
originalBody,
|
|
1010
|
-
onReset: () => setBody(
|
|
1133
|
+
originalBody: savedBody,
|
|
1134
|
+
onReset: () => setBody(savedBody),
|
|
1011
1135
|
payload: item.payload,
|
|
1012
1136
|
payloadFields: displayPayloadFields,
|
|
1013
|
-
processingSteps,
|
|
1014
|
-
actions:
|
|
1137
|
+
processingSteps: visibleProcessingSteps,
|
|
1138
|
+
actions: reviewActions,
|
|
1015
1139
|
onAction: handleAction,
|
|
1016
1140
|
actionsPending: isSaving
|
|
1017
1141
|
}
|
|
@@ -1105,8 +1229,6 @@ function PipelineRow({
|
|
|
1105
1229
|
[pipeline.steps, contentConfig]
|
|
1106
1230
|
);
|
|
1107
1231
|
const summary = useContentPipelineSummary(pipeline.id, statusKeys, stepResourceIds);
|
|
1108
|
-
const artifactsQuery = useContentArtifacts({ pipelineId: pipeline.id });
|
|
1109
|
-
const activeArtifacts = artifactsQuery.artifacts.filter((artifact) => artifact.isActive);
|
|
1110
1232
|
return /* @__PURE__ */ jsxs(
|
|
1111
1233
|
Table.Tr,
|
|
1112
1234
|
{
|
|
@@ -1117,28 +1239,6 @@ function PipelineRow({
|
|
|
1117
1239
|
/* @__PURE__ */ jsx(Text, { fw: 500, children: pipeline.label }),
|
|
1118
1240
|
pipeline.description ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: pipeline.description }) : null
|
|
1119
1241
|
] }),
|
|
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
1242
|
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: summary.isLoading ? /* @__PURE__ */ jsx(Loader, { size: "xs" }) : formatLastRun(summary.lastRunAt) }) })
|
|
1143
1243
|
]
|
|
1144
1244
|
}
|
|
@@ -1146,158 +1246,323 @@ function PipelineRow({
|
|
|
1146
1246
|
}
|
|
1147
1247
|
function ContentPipelinesPage({ headerActions } = {}) {
|
|
1148
1248
|
const contentConfig = useContentConfig();
|
|
1249
|
+
const { vocabulary } = contentConfig;
|
|
1149
1250
|
const statusEntries = contentConfig.stagesByCatalog.status ?? [];
|
|
1150
1251
|
return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
1151
|
-
/* @__PURE__ */ jsx(
|
|
1252
|
+
/* @__PURE__ */ jsx(
|
|
1253
|
+
PageTitleCaption,
|
|
1254
|
+
{
|
|
1255
|
+
title: vocabulary.pipeline.plural,
|
|
1256
|
+
caption: `Every declared ${vocabulary.pipeline.singular.toLowerCase()} and its production state`
|
|
1257
|
+
}
|
|
1258
|
+
),
|
|
1152
1259
|
headerActions ? /* @__PURE__ */ jsx(Group, { gap: "xs", children: headerActions }) : null,
|
|
1153
|
-
/* @__PURE__ */ jsx(Paper, { children:
|
|
1260
|
+
/* @__PURE__ */ jsx(Paper, { children: contentConfig.pipelines.length === 0 ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(
|
|
1154
1261
|
EmptyState,
|
|
1155
1262
|
{
|
|
1156
1263
|
icon: IconListDetails,
|
|
1157
|
-
title:
|
|
1158
|
-
description:
|
|
1264
|
+
title: `No ${vocabulary.pipeline.plural.toLowerCase()} declared`,
|
|
1265
|
+
description: `The content System's Organization Model declares no ${vocabulary.pipeline.singular.toLowerCase()} catalog entries yet.`
|
|
1159
1266
|
}
|
|
1160
1267
|
) }) : /* @__PURE__ */ jsxs(Table, { children: [
|
|
1161
1268
|
/* @__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" }),
|
|
1269
|
+
/* @__PURE__ */ jsx(Table.Th, { children: vocabulary.pipeline.singular }),
|
|
1165
1270
|
/* @__PURE__ */ jsx(Table.Th, { children: "Last run" })
|
|
1166
1271
|
] }) }),
|
|
1167
1272
|
/* @__PURE__ */ jsx(Table.Tbody, { children: contentConfig.pipelines.map((pipeline) => /* @__PURE__ */ jsx(PipelineRow, { pipeline, statusEntries }, pipeline.id)) })
|
|
1168
|
-
] }) })
|
|
1273
|
+
] }) })
|
|
1169
1274
|
] }) }) });
|
|
1170
1275
|
}
|
|
1171
|
-
|
|
1276
|
+
var ACTOR_LEAD = {
|
|
1277
|
+
vendor: "An outside service",
|
|
1278
|
+
workflow: "An automated workflow",
|
|
1279
|
+
agent: "An AI agent"
|
|
1280
|
+
};
|
|
1281
|
+
var REVIEW_MODE_TAIL = {
|
|
1282
|
+
live: "and you review it live, in the moment",
|
|
1283
|
+
queued: "then it waits for your review",
|
|
1284
|
+
none: "and moves on without review"
|
|
1285
|
+
};
|
|
1286
|
+
function describeStep(step) {
|
|
1287
|
+
return `${ACTOR_LEAD[step.actor]} handles "${step.label}," ${REVIEW_MODE_TAIL[step.reviewMode]}.`;
|
|
1288
|
+
}
|
|
1289
|
+
function StepRow({ step, resourceId }) {
|
|
1290
|
+
return /* @__PURE__ */ jsxs(Group, { gap: "sm", align: "flex-start", wrap: "nowrap", children: [
|
|
1291
|
+
/* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", style: { flexShrink: 0 }, children: step.order }),
|
|
1292
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
|
|
1293
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", children: describeStep(step) }),
|
|
1294
|
+
step.description ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: step.description }) : null,
|
|
1295
|
+
resourceId ? /* @__PURE__ */ jsx(Text, { size: "xs", ff: "monospace", c: "dimmed", children: resourceId }) : null
|
|
1296
|
+
] })
|
|
1297
|
+
] });
|
|
1298
|
+
}
|
|
1299
|
+
function HowItWorks({ pipeline }) {
|
|
1172
1300
|
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
|
-
}) })
|
|
1301
|
+
return /* @__PURE__ */ jsx(Accordion, { variant: "separated", children: /* @__PURE__ */ jsxs(Accordion.Item, { value: "how-it-works", children: [
|
|
1302
|
+
/* @__PURE__ */ jsx(Accordion.Control, { icon: /* @__PURE__ */ jsx(IconInfoCircle, { size: 16 }), children: /* @__PURE__ */ jsxs(Text, { size: "sm", fw: 500, children: [
|
|
1303
|
+
"How this ",
|
|
1304
|
+
contentConfig.vocabulary.pipeline.singular.toLowerCase(),
|
|
1305
|
+
" works"
|
|
1199
1306
|
] }) }),
|
|
1200
|
-
/* @__PURE__ */ jsx(
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
/* @__PURE__ */ jsx(
|
|
1229
|
-
/* @__PURE__ */ jsx(Badge, { size: "sm", variant: "
|
|
1307
|
+
/* @__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)) }) })
|
|
1308
|
+
] }) });
|
|
1309
|
+
}
|
|
1310
|
+
var BOARD_FETCH_LIMIT = 100;
|
|
1311
|
+
function formatAge(ms) {
|
|
1312
|
+
const seconds = Math.floor(ms / 1e3);
|
|
1313
|
+
if (seconds < 60) return `${seconds}s`;
|
|
1314
|
+
const minutes = Math.floor(seconds / 60);
|
|
1315
|
+
if (minutes < 60) return `${minutes}m`;
|
|
1316
|
+
const hours = Math.floor(minutes / 60);
|
|
1317
|
+
if (hours < 24) return `${hours}h`;
|
|
1318
|
+
return `${Math.floor(hours / 24)}d`;
|
|
1319
|
+
}
|
|
1320
|
+
function BoardItemCard({ card, onOpen }) {
|
|
1321
|
+
return /* @__PURE__ */ jsx(Card, { withBorder: true, padding: "xs", onClick: () => onOpen(card.item.id), style: { cursor: "pointer" }, children: /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
1322
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, lineClamp: 2, children: getContentItemIdentity(card.item) }),
|
|
1323
|
+
/* @__PURE__ */ jsxs(Group, { gap: 6, justify: "space-between", wrap: "nowrap", children: [
|
|
1324
|
+
/* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", children: [
|
|
1325
|
+
/* @__PURE__ */ jsx(IconClockHour4, { size: 12, style: { color: "var(--color-text-dimmed)" } }),
|
|
1326
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: formatAge(card.ageMs) })
|
|
1327
|
+
] }),
|
|
1328
|
+
card.awaitingReview ? /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", children: "Awaiting review" }) : null
|
|
1329
|
+
] })
|
|
1330
|
+
] }) });
|
|
1331
|
+
}
|
|
1332
|
+
function BoardColumn({ column, onOpen }) {
|
|
1333
|
+
return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "sm", style: { minWidth: 260, maxWidth: 260, flexShrink: 0 }, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
|
|
1334
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", wrap: "nowrap", children: [
|
|
1335
|
+
/* @__PURE__ */ jsx(Text, { fw: 600, size: "sm", truncate: true, children: column.step.label }),
|
|
1336
|
+
/* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", children: column.cards.length })
|
|
1337
|
+
] }),
|
|
1338
|
+
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)) })
|
|
1339
|
+
] }) });
|
|
1340
|
+
}
|
|
1341
|
+
function ColumnBoard({ board, onOpen }) {
|
|
1342
|
+
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)) }) });
|
|
1343
|
+
}
|
|
1344
|
+
function CollapsedBoard({
|
|
1345
|
+
board,
|
|
1346
|
+
itemNounPlural,
|
|
1347
|
+
onOpen
|
|
1348
|
+
}) {
|
|
1349
|
+
const column = board.columns[0];
|
|
1350
|
+
if (!column) {
|
|
1351
|
+
return /* @__PURE__ */ jsx(
|
|
1352
|
+
EmptyState,
|
|
1353
|
+
{
|
|
1354
|
+
icon: IconRoute,
|
|
1355
|
+
title: "No steps declared",
|
|
1356
|
+
description: "This pipeline has no steps yet, so nothing has anywhere to sit."
|
|
1357
|
+
}
|
|
1358
|
+
);
|
|
1359
|
+
}
|
|
1360
|
+
const waiting = column.cards.filter((card) => card.awaitingReview);
|
|
1361
|
+
const settled = column.cards.filter((card) => !card.awaitingReview);
|
|
1362
|
+
const hasGate = !!column.gate;
|
|
1363
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
1364
|
+
/* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
|
|
1365
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: hasGate ? "Waiting on review" : column.step.label }),
|
|
1366
|
+
waiting.length === 0 ? column.cards.length === 0 ? /* @__PURE__ */ jsx(
|
|
1367
|
+
EmptyState,
|
|
1368
|
+
{
|
|
1369
|
+
icon: IconChecklist,
|
|
1370
|
+
title: `No ${itemNounPlural.toLowerCase()} yet`,
|
|
1371
|
+
description: `Produce one and it lands at "${column.step.label}."`
|
|
1372
|
+
}
|
|
1373
|
+
) : /* @__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)) })
|
|
1374
|
+
] }),
|
|
1375
|
+
settled.length > 0 ? /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
|
|
1376
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", tt: "uppercase", fw: 600, children: hasGate ? "Already reviewed" : "In progress" }),
|
|
1377
|
+
/* @__PURE__ */ jsx(Stack, { gap: "xs", children: settled.map((card) => /* @__PURE__ */ jsx(BoardItemCard, { card, onOpen }, card.item.id)) })
|
|
1230
1378
|
] }) : null
|
|
1231
1379
|
] });
|
|
1232
1380
|
}
|
|
1233
|
-
function
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1381
|
+
function BoardOverflowSection({
|
|
1382
|
+
title,
|
|
1383
|
+
description,
|
|
1384
|
+
cards,
|
|
1385
|
+
onOpen
|
|
1386
|
+
}) {
|
|
1387
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
|
|
1388
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
|
|
1389
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", tt: "uppercase", fw: 600, children: title }),
|
|
1390
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: description })
|
|
1391
|
+
] }),
|
|
1392
|
+
/* @__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)) })
|
|
1393
|
+
] });
|
|
1242
1394
|
}
|
|
1243
|
-
function
|
|
1395
|
+
function BoardTab({ pipeline }) {
|
|
1396
|
+
const navigate = useNavigate();
|
|
1244
1397
|
const contentConfig = useContentConfig();
|
|
1245
|
-
const
|
|
1246
|
-
const
|
|
1247
|
-
const
|
|
1248
|
-
const
|
|
1398
|
+
const itemsQuery = useContentItems({ pipelineId: pipeline.id, limit: BOARD_FETCH_LIMIT });
|
|
1399
|
+
const items = useMemo(() => itemsQuery.data?.data ?? [], [itemsQuery.data]);
|
|
1400
|
+
const board = useMemo(() => deriveContentBoard(items, pipeline, Date.now()), [items, pipeline]);
|
|
1401
|
+
const openItem = (itemId) => navigate({ to: "/content/items/$itemId", params: { itemId } });
|
|
1402
|
+
if (itemsQuery.isLoading) {
|
|
1403
|
+
return /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) });
|
|
1404
|
+
}
|
|
1405
|
+
if (itemsQuery.isError) {
|
|
1406
|
+
return /* @__PURE__ */ jsx(
|
|
1407
|
+
CenteredErrorState,
|
|
1408
|
+
{
|
|
1409
|
+
error: itemsQuery.error,
|
|
1410
|
+
title: `Failed to load ${contentConfig.vocabulary.item.plural.toLowerCase()}`
|
|
1411
|
+
}
|
|
1412
|
+
);
|
|
1413
|
+
}
|
|
1414
|
+
if (items.length === 0) {
|
|
1415
|
+
return /* @__PURE__ */ jsx(
|
|
1416
|
+
EmptyState,
|
|
1417
|
+
{
|
|
1418
|
+
icon: IconChecklist,
|
|
1419
|
+
title: `No ${contentConfig.vocabulary.item.plural.toLowerCase()} in this pipeline yet`,
|
|
1420
|
+
description: "Produce something from the Produce tab and it will show up here."
|
|
1421
|
+
}
|
|
1422
|
+
);
|
|
1423
|
+
}
|
|
1249
1424
|
return /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
1250
|
-
/* @__PURE__ */ jsx(
|
|
1251
|
-
|
|
1425
|
+
board.isCollapsed ? /* @__PURE__ */ jsx(CollapsedBoard, { board, itemNounPlural: contentConfig.vocabulary.item.plural, onOpen: openItem }) : /* @__PURE__ */ jsx(ColumnBoard, { board, onOpen: openItem }),
|
|
1426
|
+
board.done.length > 0 ? /* @__PURE__ */ jsx(
|
|
1427
|
+
BoardOverflowSection,
|
|
1252
1428
|
{
|
|
1253
|
-
|
|
1254
|
-
description: "
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
value: contentItemId,
|
|
1258
|
-
onChange: setContentItemId,
|
|
1259
|
-
disabled: itemsQuery.isLoading || items.length === 0,
|
|
1260
|
-
searchable: true,
|
|
1261
|
-
clearable: true
|
|
1429
|
+
title: "Done",
|
|
1430
|
+
description: "Every declared step has succeeded for these.",
|
|
1431
|
+
cards: board.done,
|
|
1432
|
+
onOpen: openItem
|
|
1262
1433
|
}
|
|
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
|
-
})
|
|
1434
|
+
) : null,
|
|
1435
|
+
board.unplaced.length > 0 ? /* @__PURE__ */ jsx(
|
|
1436
|
+
BoardOverflowSection,
|
|
1437
|
+
{
|
|
1438
|
+
title: "Not on this pipeline's current steps",
|
|
1439
|
+
description: "Their recorded steps aren't in the step catalog anymore -- shown honestly rather than dropped into the first column.",
|
|
1440
|
+
cards: board.unplaced,
|
|
1441
|
+
onOpen: openItem
|
|
1442
|
+
}
|
|
1443
|
+
) : null,
|
|
1444
|
+
/* @__PURE__ */ jsx(HowItWorks, { pipeline })
|
|
1294
1445
|
] });
|
|
1295
1446
|
}
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1447
|
+
var PAGE_SIZE2 = 20;
|
|
1448
|
+
var FETCH_LIMIT = 100;
|
|
1449
|
+
function summarizeMetrics(metrics) {
|
|
1450
|
+
if (!metrics || typeof metrics !== "object" || Array.isArray(metrics)) return "\u2014";
|
|
1451
|
+
const entries = Object.entries(metrics);
|
|
1452
|
+
if (entries.length === 0) return "\u2014";
|
|
1453
|
+
const shown = entries.slice(0, 2).map(([key, value]) => `${key} ${String(value)}`).join(" \xB7 ");
|
|
1454
|
+
return entries.length > 2 ? `${shown} +${entries.length - 2}` : shown;
|
|
1299
1455
|
}
|
|
1300
1456
|
function formatDateTime(value) {
|
|
1457
|
+
return value ? new Date(value).toLocaleDateString() : "\u2014";
|
|
1458
|
+
}
|
|
1459
|
+
function DistributionsTab({ pipeline }) {
|
|
1460
|
+
const navigate = useNavigate();
|
|
1461
|
+
const contentConfig = useContentConfig();
|
|
1462
|
+
const distributionLabel = contentConfig.vocabulary.distribution.plural.toLowerCase();
|
|
1463
|
+
const itemsQuery = useContentItems({ pipelineId: pipeline.id, limit: FETCH_LIMIT });
|
|
1464
|
+
const distributionsQuery = useContentDistributions({ limit: FETCH_LIMIT });
|
|
1465
|
+
const itemById = useMemo(() => {
|
|
1466
|
+
const map = /* @__PURE__ */ new Map();
|
|
1467
|
+
for (const item of itemsQuery.data?.data ?? []) map.set(item.id, item);
|
|
1468
|
+
return map;
|
|
1469
|
+
}, [itemsQuery.data]);
|
|
1470
|
+
const distributions = useMemo(
|
|
1471
|
+
() => (distributionsQuery.data?.data ?? []).filter((distribution) => itemById.has(distribution.contentItemId)),
|
|
1472
|
+
[distributionsQuery.data, itemById]
|
|
1473
|
+
);
|
|
1474
|
+
const pagination = usePaginationState(PAGE_SIZE2, [pipeline.id], distributions.length);
|
|
1475
|
+
const paginatedDistributions = distributions.slice(pagination.offset, pagination.offset + PAGE_SIZE2);
|
|
1476
|
+
const isLoading = itemsQuery.isLoading || distributionsQuery.isLoading;
|
|
1477
|
+
const error = itemsQuery.error ?? distributionsQuery.error;
|
|
1478
|
+
const resolveIdentity = (distribution) => {
|
|
1479
|
+
const item = itemById.get(distribution.contentItemId);
|
|
1480
|
+
return item ? getContentItemIdentity(item) : distribution.contentItemId;
|
|
1481
|
+
};
|
|
1482
|
+
if (isLoading) {
|
|
1483
|
+
return /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) });
|
|
1484
|
+
}
|
|
1485
|
+
if (itemsQuery.isError || distributionsQuery.isError) {
|
|
1486
|
+
return /* @__PURE__ */ jsx(CenteredErrorState, { error, title: `Failed to load ${distributionLabel}` });
|
|
1487
|
+
}
|
|
1488
|
+
if (distributions.length === 0) {
|
|
1489
|
+
return /* @__PURE__ */ jsx(
|
|
1490
|
+
EmptyState,
|
|
1491
|
+
{
|
|
1492
|
+
icon: IconShare,
|
|
1493
|
+
title: `No ${distributionLabel} yet`,
|
|
1494
|
+
description: `Nothing from this pipeline has been distributed anywhere yet.`
|
|
1495
|
+
}
|
|
1496
|
+
);
|
|
1497
|
+
}
|
|
1498
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
1499
|
+
/* @__PURE__ */ jsxs(Table, { children: [
|
|
1500
|
+
/* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
1501
|
+
/* @__PURE__ */ jsx(Table.Th, { children: contentConfig.vocabulary.item.singular }),
|
|
1502
|
+
/* @__PURE__ */ jsx(Table.Th, { children: "Platform" }),
|
|
1503
|
+
/* @__PURE__ */ jsx(Table.Th, { style: { whiteSpace: "nowrap" }, children: "Publish method" }),
|
|
1504
|
+
/* @__PURE__ */ jsx(Table.Th, { style: { whiteSpace: "nowrap" }, children: "Platform post ID" }),
|
|
1505
|
+
/* @__PURE__ */ jsx(Table.Th, { children: "Metrics" }),
|
|
1506
|
+
/* @__PURE__ */ jsx(Table.Th, { style: { whiteSpace: "nowrap" }, children: "Metrics updated" }),
|
|
1507
|
+
/* @__PURE__ */ jsx(Table.Th, { children: "Published" }),
|
|
1508
|
+
/* @__PURE__ */ jsx(Table.Th, {})
|
|
1509
|
+
] }) }),
|
|
1510
|
+
/* @__PURE__ */ jsx(Table.Tbody, { children: paginatedDistributions.map((distribution) => {
|
|
1511
|
+
const platformEntry = contentConfig.stageCatalog[distribution.platform];
|
|
1512
|
+
const platformColors = getContentStageColor(platformEntry);
|
|
1513
|
+
return /* @__PURE__ */ jsxs(
|
|
1514
|
+
Table.Tr,
|
|
1515
|
+
{
|
|
1516
|
+
style: { cursor: "pointer" },
|
|
1517
|
+
onClick: () => navigate({
|
|
1518
|
+
to: "/content/distributions/$distributionId",
|
|
1519
|
+
params: { distributionId: distribution.id }
|
|
1520
|
+
}),
|
|
1521
|
+
children: [
|
|
1522
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, lineClamp: 1, children: resolveIdentity(distribution) }) }),
|
|
1523
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(
|
|
1524
|
+
Badge,
|
|
1525
|
+
{
|
|
1526
|
+
size: "sm",
|
|
1527
|
+
variant: "light",
|
|
1528
|
+
styles: { root: { color: platformColors.accent, backgroundColor: platformColors.background } },
|
|
1529
|
+
children: platformEntry?.label ?? distribution.platform
|
|
1530
|
+
}
|
|
1531
|
+
) }),
|
|
1532
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: distribution.publishMethod ?? "\u2014" }) }),
|
|
1533
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "xs", ff: "monospace", c: "dimmed", children: distribution.platformPostId ?? "\u2014" }) }),
|
|
1534
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: summarizeMetrics(distribution.metrics) }) }),
|
|
1535
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDateTime(distribution.metricsUpdatedAt) }) }),
|
|
1536
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDateTime(distribution.publishedAt) }) }),
|
|
1537
|
+
/* @__PURE__ */ jsx(Table.Td, { children: distribution.platformUrl ? /* @__PURE__ */ jsx(
|
|
1538
|
+
Anchor,
|
|
1539
|
+
{
|
|
1540
|
+
href: distribution.platformUrl,
|
|
1541
|
+
target: "_blank",
|
|
1542
|
+
rel: "noopener noreferrer",
|
|
1543
|
+
style: { display: "inline-flex" },
|
|
1544
|
+
onClick: (event) => event.stopPropagation(),
|
|
1545
|
+
children: /* @__PURE__ */ jsx(IconExternalLink, { size: 16 })
|
|
1546
|
+
}
|
|
1547
|
+
) : null })
|
|
1548
|
+
]
|
|
1549
|
+
},
|
|
1550
|
+
distribution.id
|
|
1551
|
+
);
|
|
1552
|
+
}) })
|
|
1553
|
+
] }),
|
|
1554
|
+
distributions.length > PAGE_SIZE2 ? /* @__PURE__ */ jsx(Group, { justify: "center", children: /* @__PURE__ */ jsx(
|
|
1555
|
+
Pagination,
|
|
1556
|
+
{
|
|
1557
|
+
value: pagination.page,
|
|
1558
|
+
onChange: pagination.setPage,
|
|
1559
|
+
total: pagination.totalPages(distributions.length),
|
|
1560
|
+
size: "sm"
|
|
1561
|
+
}
|
|
1562
|
+
) }) : null
|
|
1563
|
+
] });
|
|
1564
|
+
}
|
|
1565
|
+
function formatDateTime2(value) {
|
|
1301
1566
|
return value ? new Date(value).toLocaleString() : "\u2014";
|
|
1302
1567
|
}
|
|
1303
1568
|
function formatDuration(startTime, endTime) {
|
|
@@ -1338,7 +1603,7 @@ function StepRunsSection({ step, resourceId }) {
|
|
|
1338
1603
|
/* @__PURE__ */ jsx(Table.Tbody, { children: executions.map((execution) => /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
1339
1604
|
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "xs", ff: "monospace", c: "dimmed", children: execution.id }) }),
|
|
1340
1605
|
/* @__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:
|
|
1606
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDateTime2(execution.startTime) }) }),
|
|
1342
1607
|
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDuration(execution.startTime, execution.endTime) }) })
|
|
1343
1608
|
] }, execution.id)) })
|
|
1344
1609
|
] })
|
|
@@ -1348,19 +1613,18 @@ function RunsTab({ pipeline }) {
|
|
|
1348
1613
|
const contentConfig = useContentConfig();
|
|
1349
1614
|
return /* @__PURE__ */ jsx(Stack, { gap: "md", children: pipeline.steps.map((step) => /* @__PURE__ */ jsx(StepRunsSection, { step, resourceId: contentConfig.resolveStepResource(step)?.resourceId }, step.key)) });
|
|
1350
1615
|
}
|
|
1351
|
-
var PIPELINE_WORKSPACE_TABS = ["
|
|
1616
|
+
var PIPELINE_WORKSPACE_TABS = ["board", "distributions", "runs"];
|
|
1352
1617
|
function isPipelineWorkspaceTab(value) {
|
|
1353
1618
|
return typeof value === "string" && PIPELINE_WORKSPACE_TABS.includes(value);
|
|
1354
1619
|
}
|
|
1355
1620
|
function ContentPipelineWorkspacePage({
|
|
1356
1621
|
pipelineId,
|
|
1357
|
-
headerActions
|
|
1358
|
-
renderProduce
|
|
1622
|
+
headerActions
|
|
1359
1623
|
}) {
|
|
1360
1624
|
const navigate = useNavigate();
|
|
1361
1625
|
const contentConfig = useContentConfig();
|
|
1362
1626
|
const rawSearch = useSearch({ strict: false });
|
|
1363
|
-
const activeTab = isPipelineWorkspaceTab(rawSearch.tab) ? rawSearch.tab : "
|
|
1627
|
+
const activeTab = isPipelineWorkspaceTab(rawSearch.tab) ? rawSearch.tab : "board";
|
|
1364
1628
|
const handleTabChange = useCallback(
|
|
1365
1629
|
(value) => {
|
|
1366
1630
|
if (!isPipelineWorkspaceTab(value) || value === activeTab) return;
|
|
@@ -1373,7 +1637,7 @@ function ContentPipelineWorkspacePage({
|
|
|
1373
1637
|
[activeTab, navigate]
|
|
1374
1638
|
);
|
|
1375
1639
|
const pipeline = contentConfig.pipelines.find((candidate) => candidate.id === pipelineId);
|
|
1376
|
-
const
|
|
1640
|
+
const vocabulary = contentConfig.vocabulary;
|
|
1377
1641
|
const backButton = /* @__PURE__ */ jsx(
|
|
1378
1642
|
Button,
|
|
1379
1643
|
{
|
|
@@ -1411,109 +1675,19 @@ function ContentPipelineWorkspacePage({
|
|
|
1411
1675
|
}
|
|
1412
1676
|
),
|
|
1413
1677
|
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: [
|
|
1678
|
+
/* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Tabs, { value: activeTab, onChange: handleTabChange, keepMounted: false, children: [
|
|
1419
1679
|
/* @__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" }),
|
|
1680
|
+
/* @__PURE__ */ jsx(Tabs.Tab, { value: "board", leftSection: /* @__PURE__ */ jsx(IconLayoutGrid, { size: 14 }), children: vocabulary.board }),
|
|
1681
|
+
/* @__PURE__ */ jsx(Tabs.Tab, { value: "distributions", leftSection: /* @__PURE__ */ jsx(IconShare, { size: 14 }), children: vocabulary.distribution.plural }),
|
|
1423
1682
|
/* @__PURE__ */ jsx(Tabs.Tab, { value: "runs", leftSection: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 14 }), children: "Runs" })
|
|
1424
1683
|
] }),
|
|
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 }) })
|
|
1684
|
+
/* @__PURE__ */ jsx(Tabs.Panel, { value: "board", pt: "md", children: /* @__PURE__ */ jsx(BoardTab, { pipeline }) }),
|
|
1685
|
+
/* @__PURE__ */ jsx(Tabs.Panel, { value: "distributions", pt: "md", children: /* @__PURE__ */ jsx(DistributionsTab, { pipeline }) }),
|
|
1686
|
+
/* @__PURE__ */ jsx(Tabs.Panel, { value: "runs", pt: "md", children: /* @__PURE__ */ jsx(RunsTab, { pipeline }) })
|
|
1429
1687
|
] }) })
|
|
1430
1688
|
] }) }) });
|
|
1431
1689
|
}
|
|
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;
|
|
1690
|
+
var PAGE_SIZE3 = 20;
|
|
1517
1691
|
var QUERY_LIMIT = 100;
|
|
1518
1692
|
function formatDate(value) {
|
|
1519
1693
|
return value ? new Date(value).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" }) : "\u2014";
|
|
@@ -1553,11 +1727,11 @@ function ContentDistributionsPage({ headerActions } = {}) {
|
|
|
1553
1727
|
...(contentConfig.stagesByCatalog.platform ?? []).map((entry) => ({ value: entry.key, label: entry.label }))
|
|
1554
1728
|
];
|
|
1555
1729
|
const pagination = usePaginationState(
|
|
1556
|
-
|
|
1730
|
+
PAGE_SIZE3,
|
|
1557
1731
|
[platformFilter, statusFilter, formatFilter],
|
|
1558
1732
|
filteredDistributions.length
|
|
1559
1733
|
);
|
|
1560
|
-
const paginatedDistributions = filteredDistributions.slice(pagination.offset, pagination.offset +
|
|
1734
|
+
const paginatedDistributions = filteredDistributions.slice(pagination.offset, pagination.offset + PAGE_SIZE3);
|
|
1561
1735
|
const resolveTitle = (distribution) => itemTitleById.get(distribution.contentItemId) ?? distribution.contentItemId;
|
|
1562
1736
|
return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
1563
1737
|
/* @__PURE__ */ jsx(
|
|
@@ -1662,7 +1836,7 @@ function ContentDistributionsPage({ headerActions } = {}) {
|
|
|
1662
1836
|
);
|
|
1663
1837
|
}) })
|
|
1664
1838
|
] }),
|
|
1665
|
-
filteredDistributions.length >
|
|
1839
|
+
filteredDistributions.length > PAGE_SIZE3 && /* @__PURE__ */ jsx(Group, { justify: "center", children: /* @__PURE__ */ jsx(
|
|
1666
1840
|
Pagination,
|
|
1667
1841
|
{
|
|
1668
1842
|
value: pagination.page,
|
|
@@ -2177,4 +2351,4 @@ function ContentDistributionDetailPage({
|
|
|
2177
2351
|
] });
|
|
2178
2352
|
}
|
|
2179
2353
|
|
|
2180
|
-
export { AlternatesPanel, ContentDistributionDetailPage, ContentDistributionsPage, ContentItemReviewPage, ContentItemsPage, ContentOverviewPage, ContentPipelineWorkspacePage, ContentPipelinesPage, ContentReviewCard,
|
|
2354
|
+
export { AlternatesPanel, ContentDistributionDetailPage, ContentDistributionsPage, ContentItemReviewPage, ContentItemsPage, ContentOverviewPage, ContentPipelineWorkspacePage, ContentPipelinesPage, ContentReviewCard, PayloadBody, ProcessingStateStrip, ReviewActionBar, firstLine, formatElapsed, lastLine, swapFirstLine, swapLastLine };
|