sanity-plugin-workflow 1.0.0-beta.3 → 1.0.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.esm.js CHANGED
@@ -1,15 +1,16 @@
1
1
  var _templateObject, _templateObject2, _templateObject3;
2
2
  function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
3
- import { useCurrentUser, UserAvatar, useClient, useTimeAgo, TextWithTone, useSchema, useValidationStatus, Preview, useFormValue, defineType, defineField, definePlugin } from 'sanity';
4
- import { EditIcon, AddIcon, PublishIcon, ErrorOutlineIcon, WarningOutlineIcon, CheckmarkIcon, DragHandleIcon, UserIcon, ResetIcon, InfoOutlineIcon, SplitVerticalIcon, UsersIcon, ArrowRightIcon, ArrowLeftIcon } from '@sanity/icons';
3
+ import { useClient, useValidationStatus, useCurrentUser, useSchema, Preview, useFormValue, defineType, defineField, UserAvatar, useTimeAgo, TextWithTone, definePlugin } from 'sanity';
5
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
- import React, { useMemo, useEffect, useCallback, useState } from 'react';
7
- import { Button, Flex, Box, Text, useToast, useClickOutside, Popover, Grid, Tooltip, useTheme, Card, Stack, MenuButton, Menu, Badge, Container, Spinner } from '@sanity/ui';
5
+ import { UsersIcon, SplitVerticalIcon, CheckmarkIcon, ArrowRightIcon, ArrowLeftIcon, EditIcon, AddIcon, PublishIcon, ErrorOutlineIcon, WarningOutlineIcon, DragHandleIcon, UserIcon, ResetIcon, InfoOutlineIcon } from '@sanity/icons';
6
+ import React, { useState, useCallback, useMemo, useEffect, useRef } from 'react';
8
7
  import { UserSelectMenu, useListeningQuery, useProjectUsers, Feedback } from 'sanity-plugin-utils';
9
- import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
8
+ import { useToast, Button, Spinner, Card, Flex, Box, Text, useClickOutside, Popover, Grid, Tooltip, useTheme, Stack, MenuButton, Menu, Badge, Container } from '@sanity/ui';
9
+ import { LexoRank } from 'lexorank';
10
10
  import { useRouter } from 'sanity/router';
11
+ import { Draggable, DragDropContext, Droppable } from '@hello-pangea/dnd';
11
12
  import groq from 'groq';
12
- import { LexoRank } from 'lexorank';
13
+ import { useVirtualizer } from '@tanstack/react-virtual';
13
14
  import styled, { css } from 'styled-components';
14
15
  import { AnimatePresence, motion } from 'framer-motion';
15
16
  function defineStates(states) {
@@ -39,69 +40,6 @@ const DEFAULT_CONFIG = {
39
40
  transitions: ["changesRequested"]
40
41
  }])
41
42
  };
42
- function EditButton(props) {
43
- const {
44
- id,
45
- type,
46
- disabled = false
47
- } = props;
48
- const {
49
- navigateIntent
50
- } = useRouter();
51
- return /* @__PURE__ */jsx(Button, {
52
- onClick: () => navigateIntent("edit", {
53
- id,
54
- type
55
- }),
56
- mode: "ghost",
57
- fontSize: 1,
58
- padding: 2,
59
- tabIndex: -1,
60
- icon: EditIcon,
61
- text: "Edit",
62
- disabled
63
- });
64
- }
65
- function AvatarGroup(props) {
66
- const currentUser = useCurrentUser();
67
- const {
68
- users,
69
- max = 4
70
- } = props;
71
- const len = users == null ? void 0 : users.length;
72
- const {
73
- me,
74
- visibleUsers
75
- } = React.useMemo(() => {
76
- return {
77
- me: (currentUser == null ? void 0 : currentUser.id) ? users.find(u => u.id === currentUser.id) : void 0,
78
- visibleUsers: users.filter(u => u.id !== (currentUser == null ? void 0 : currentUser.id)).slice(0, max - 1)
79
- };
80
- }, [users, max, currentUser]);
81
- if (!(users == null ? void 0 : users.length)) {
82
- return null;
83
- }
84
- return /* @__PURE__ */jsxs(Flex, {
85
- align: "center",
86
- gap: 1,
87
- children: [me ? /* @__PURE__ */jsx(UserAvatar, {
88
- user: me
89
- }) : null, visibleUsers.map(user => /* @__PURE__ */jsx(Box, {
90
- style: {
91
- marginRight: -8
92
- },
93
- children: /* @__PURE__ */jsx(UserAvatar, {
94
- user
95
- })
96
- }, user.id)), len > max && /* @__PURE__ */jsx(Box, {
97
- paddingLeft: 2,
98
- children: /* @__PURE__ */jsxs(Text, {
99
- size: 1,
100
- children: ["+", len - max]
101
- })
102
- })]
103
- });
104
- }
105
43
  function UserAssignment(props) {
106
44
  const {
107
45
  assignees,
@@ -184,698 +122,479 @@ function UserAssignment(props) {
184
122
  onRemove: removeAssignee
185
123
  });
186
124
  }
187
- function UserDisplay(props) {
188
- const {
189
- assignees,
190
- userList,
191
- documentId,
192
- disabled = false
193
- } = props;
194
- const [button] = React.useState(null);
195
- const [popover, setPopover] = React.useState(null);
196
- const [isOpen, setIsOpen] = React.useState(false);
197
- const close = React.useCallback(() => setIsOpen(false), []);
198
- const open = React.useCallback(() => setIsOpen(true), []);
199
- useClickOutside(close, [button, popover]);
200
- return /* @__PURE__ */jsx(Popover, {
201
- ref: setPopover,
202
- content: /* @__PURE__ */jsx(UserAssignment, {
203
- userList,
204
- assignees,
205
- documentId
206
- }),
207
- portal: true,
208
- open: isOpen,
209
- children: !assignees || assignees.length === 0 ? /* @__PURE__ */jsx(Button, {
210
- onClick: open,
211
- fontSize: 1,
212
- padding: 2,
213
- tabIndex: -1,
214
- icon: AddIcon,
215
- text: "Assign",
216
- tone: "positive",
217
- mode: "ghost",
218
- disabled
219
- }) : /* @__PURE__ */jsx(Grid, {
220
- children: /* @__PURE__ */jsx(Button, {
221
- onClick: open,
222
- padding: 0,
223
- mode: "bleed",
224
- disabled,
225
- children: /* @__PURE__ */jsx(AvatarGroup, {
226
- users: userList.filter(u => assignees.includes(u.id))
227
- })
228
- })
229
- })
230
- });
231
- }
232
- function TimeAgo(_ref) {
233
- let {
234
- time
235
- } = _ref;
236
- const timeAgo = useTimeAgo(time);
237
- return /* @__PURE__ */jsxs("span", {
238
- title: timeAgo,
239
- children: [timeAgo, " ago"]
240
- });
241
- }
242
- function DraftStatus(props) {
125
+ function useWorkflowMetadata(id, states) {
243
126
  const {
244
- document
245
- } = props;
246
- const updatedAt = document && "_updatedAt" in document && document._updatedAt;
247
- return /* @__PURE__ */jsx(Tooltip, {
248
- portal: true,
249
- content: /* @__PURE__ */jsx(Box, {
250
- padding: 2,
251
- children: /* @__PURE__ */jsx(Text, {
252
- size: 1,
253
- children: document ? /* @__PURE__ */jsxs(Fragment, {
254
- children: ["Edited ", updatedAt && /* @__PURE__ */jsx(TimeAgo, {
255
- time: updatedAt
256
- })]
257
- }) : /* @__PURE__ */jsx(Fragment, {
258
- children: "No unpublished edits"
259
- })
260
- })
261
- }),
262
- children: /* @__PURE__ */jsx(TextWithTone, {
263
- tone: "caution",
264
- dimmed: !document,
265
- muted: !document,
266
- size: 1,
267
- children: /* @__PURE__ */jsx(EditIcon, {})
268
- })
127
+ data: metadata,
128
+ loading,
129
+ error
130
+ } = useListeningQuery("*[_type == \"workflow.metadata\" && documentId == $id][0]", {
131
+ params: {
132
+ id
133
+ }
269
134
  });
135
+ if (metadata == null ? void 0 : metadata.state) {
136
+ return {
137
+ data: {
138
+ metadata,
139
+ state: states.find(s => s.id === metadata.state)
140
+ },
141
+ loading,
142
+ error
143
+ };
144
+ }
145
+ return {
146
+ data: {},
147
+ loading,
148
+ error
149
+ };
270
150
  }
271
- function PublishedStatus(props) {
151
+ function AssignWorkflow(props, states) {
152
+ var _a, _b;
272
153
  const {
273
- document
154
+ id
274
155
  } = props;
275
- const updatedAt = document && "_updatedAt" in document && document._updatedAt;
276
- return /* @__PURE__ */jsx(Tooltip, {
277
- portal: true,
278
- content: /* @__PURE__ */jsx(Box, {
279
- padding: 2,
280
- children: /* @__PURE__ */jsx(Text, {
281
- size: 1,
282
- children: document ? /* @__PURE__ */jsxs(Fragment, {
283
- children: ["Published ", updatedAt && /* @__PURE__ */jsx(TimeAgo, {
284
- time: updatedAt
285
- })]
286
- }) : /* @__PURE__ */jsx(Fragment, {
287
- children: "Not published"
288
- })
289
- })
290
- }),
291
- children: /* @__PURE__ */jsx(TextWithTone, {
292
- tone: "positive",
293
- dimmed: !document,
294
- muted: !document,
295
- size: 1,
296
- children: /* @__PURE__ */jsx(PublishIcon, {})
297
- })
156
+ const [isDialogOpen, setDialogOpen] = useState(false);
157
+ const userList = useProjectUsers({
158
+ apiVersion: API_VERSION
298
159
  });
299
- }
300
- function ValidationStatus(props) {
301
160
  const {
302
- validation = []
303
- } = props;
304
- if (!validation.length) {
161
+ data,
162
+ loading,
163
+ error
164
+ } = useWorkflowMetadata(id, states);
165
+ if (error) {
166
+ console.error(error);
167
+ }
168
+ if (!(data == null ? void 0 : data.metadata)) {
305
169
  return null;
306
170
  }
307
- const hasError = validation.some(item => item.level === "error");
308
- return /* @__PURE__ */jsx(Tooltip, {
309
- portal: true,
310
- content: /* @__PURE__ */jsx(Box, {
311
- padding: 2,
312
- children: /* @__PURE__ */jsx(Text, {
313
- size: 1,
314
- children: validation.length === 1 ? "1 validation issue" : "".concat(validation.length, " validation issues")
171
+ return {
172
+ icon: UsersIcon,
173
+ type: "dialog",
174
+ disabled: !data || loading || error,
175
+ label: "Assign",
176
+ title: data ? null : "Document is not in Workflow",
177
+ dialog: isDialogOpen && {
178
+ type: "popover",
179
+ onClose: () => {
180
+ setDialogOpen(false);
181
+ },
182
+ content: /* @__PURE__ */jsx(UserAssignment, {
183
+ userList,
184
+ assignees: (_b = (_a = data.metadata) == null ? void 0 : _a.assignees) != null ? _b : [],
185
+ documentId: id
315
186
  })
316
- }),
317
- children: /* @__PURE__ */jsx(TextWithTone, {
318
- tone: hasError ? "critical" : "caution",
319
- size: 1,
320
- children: hasError ? /* @__PURE__ */jsx(ErrorOutlineIcon, {}) : /* @__PURE__ */jsx(WarningOutlineIcon, {})
321
- })
322
- });
187
+ },
188
+ onHandle: () => {
189
+ setDialogOpen(true);
190
+ }
191
+ };
323
192
  }
324
- function CompleteButton(props) {
193
+ function BeginWorkflow(props, states) {
325
194
  const {
326
- documentId,
327
- disabled = false
195
+ id,
196
+ draft
328
197
  } = props;
329
- const client = useClient({
330
- apiVersion: API_VERSION
331
- });
332
- const toast = useToast();
333
- const handleComplete = React.useCallback(id => {
334
- client.delete("workflow-metadata.".concat(id)).then(() => {
198
+ const {
199
+ data,
200
+ loading,
201
+ error
202
+ } = useWorkflowMetadata(id, states);
203
+ const client = useClient({
204
+ apiVersion: API_VERSION
205
+ });
206
+ const toast = useToast();
207
+ const [beginning, setBeginning] = useState(false);
208
+ const [complete, setComplete] = useState(false);
209
+ if (error) {
210
+ console.error(error);
211
+ }
212
+ const handle = useCallback(async () => {
213
+ setBeginning(true);
214
+ const lowestOrderFirstState = await client.fetch("*[_type == \"workflow.metadata\" && state == $state]|order(orderRank)[0].orderRank", {
215
+ state: states[0].id
216
+ });
217
+ client.createIfNotExists({
218
+ _id: "workflow-metadata.".concat(id),
219
+ _type: "workflow.metadata",
220
+ documentId: id,
221
+ state: states[0].id,
222
+ orderRank: lowestOrderFirstState ? LexoRank.parse(lowestOrderFirstState).genNext().toString() : LexoRank.min().toString()
223
+ },
224
+ // Faster!
225
+ {
226
+ visibility: "async"
227
+ }).then(() => {
335
228
  toast.push({
336
229
  status: "success",
337
- title: "Workflow completed",
338
- description: id
339
- });
340
- }).catch(() => {
341
- toast.push({
342
- status: "error",
343
- title: "Could not complete Workflow",
344
- description: id
230
+ title: "Workflow started",
231
+ description: "Document is now \"".concat(states[0].title, "\"")
345
232
  });
233
+ setBeginning(false);
234
+ setComplete(true);
346
235
  });
347
- }, [client, toast]);
348
- return /* @__PURE__ */jsx(Button, {
349
- onClick: () => handleComplete(documentId),
350
- text: "Complete",
351
- icon: CheckmarkIcon,
352
- tone: "positive",
353
- mode: "ghost",
354
- fontSize: 1,
355
- padding: 2,
356
- tabIndex: -1,
357
- disabled
358
- });
236
+ }, [id, states, client, toast]);
237
+ if (!draft || complete || data.metadata) {
238
+ return null;
239
+ }
240
+ return {
241
+ icon: SplitVerticalIcon,
242
+ type: "dialog",
243
+ disabled: (data == null ? void 0 : data.metadata) || loading || error || beginning || complete,
244
+ label: beginning ? "Beginning..." : "Begin Workflow",
245
+ onHandle: () => {
246
+ handle();
247
+ }
248
+ };
359
249
  }
360
- function DocumentCard(props) {
250
+ function CompleteWorkflow(props, states) {
361
251
  var _a;
362
252
  const {
363
- isDragDisabled,
364
- userRoleCanDrop,
365
- isDragging,
366
- item,
367
- states,
368
- toggleInvalidDocumentId,
369
- userList
253
+ id
370
254
  } = props;
371
255
  const {
372
- assignees = [],
373
- documentId
374
- } = (_a = item._metadata) != null ? _a : {};
375
- const schema = useSchema();
376
- const isDarkMode = useTheme().sanity.color.dark;
377
- const defaultCardTone = isDarkMode ? "transparent" : "default";
378
- const {
379
- validation = [],
380
- isValidating
381
- } = useValidationStatus(documentId != null ? documentId : "", item._type);
382
- const cardTone = useMemo(() => {
383
- let tone = defaultCardTone;
384
- if (!userRoleCanDrop) return isDarkMode ? "default" : "transparent";
385
- if (!documentId) return tone;
386
- if (isDragging) tone = "positive";
387
- if (!isValidating && validation.length > 0) {
388
- if (validation.some(v => v.level === "error")) {
389
- tone = "critical";
390
- } else {
391
- tone = "caution";
392
- }
393
- }
394
- return tone;
395
- }, [isDarkMode, userRoleCanDrop, defaultCardTone, documentId, isDragging, validation, isValidating]);
396
- useEffect(() => {
397
- if (!isValidating && validation.length > 0) {
398
- if (validation.some(v => v.level === "error")) {
399
- toggleInvalidDocumentId(documentId, "ADD");
400
- } else {
401
- toggleInvalidDocumentId(documentId, "REMOVE");
402
- }
403
- } else {
404
- toggleInvalidDocumentId(documentId, "REMOVE");
405
- }
406
- }, [documentId, isValidating, toggleInvalidDocumentId, validation]);
407
- const hasError = useMemo(() => isValidating ? false : validation.some(v => v.level === "error"), [isValidating, validation]);
408
- const isLastState = useMemo(() => {
409
- var _a2;
410
- return states[states.length - 1].id === ((_a2 = item._metadata) == null ? void 0 : _a2.state);
411
- }, [states, item._metadata.state]);
412
- return /* @__PURE__ */jsx(Box, {
413
- paddingBottom: 3,
414
- paddingX: 3,
415
- children: /* @__PURE__ */jsx(Card, {
416
- radius: 2,
417
- shadow: isDragging ? 3 : 1,
418
- tone: cardTone,
419
- children: /* @__PURE__ */jsxs(Stack, {
420
- children: [/* @__PURE__ */jsx(Card, {
421
- borderBottom: true,
422
- radius: 2,
423
- padding: 3,
424
- paddingLeft: 2,
425
- tone: cardTone,
426
- style: {
427
- pointerEvents: "none"
428
- },
429
- children: /* @__PURE__ */jsxs(Flex, {
430
- align: "center",
431
- justify: "space-between",
432
- gap: 1,
433
- children: [/* @__PURE__ */jsx(Box, {
434
- flex: 1,
435
- children: /* @__PURE__ */jsx(Preview, {
436
- layout: "default",
437
- value: item,
438
- schemaType: schema.get(item._type)
439
- })
440
- }), /* @__PURE__ */jsx(Box, {
441
- style: {
442
- flexShrink: 0
443
- },
444
- children: hasError || isDragDisabled ? null : /* @__PURE__ */jsx(DragHandleIcon, {})
445
- })]
446
- })
447
- }), /* @__PURE__ */jsx(Card, {
448
- padding: 2,
449
- radius: 2,
450
- tone: "inherit",
451
- children: /* @__PURE__ */jsxs(Flex, {
452
- align: "center",
453
- justify: "space-between",
454
- gap: 3,
455
- children: [/* @__PURE__ */jsx(Box, {
456
- flex: 1,
457
- children: documentId && /* @__PURE__ */jsx(UserDisplay, {
458
- userList,
459
- assignees,
460
- documentId,
461
- disabled: !userRoleCanDrop
462
- })
463
- }), validation.length > 0 ? /* @__PURE__ */jsx(ValidationStatus, {
464
- validation
465
- }) : null, /* @__PURE__ */jsx(DraftStatus, {
466
- document: item
467
- }), /* @__PURE__ */jsx(PublishedStatus, {
468
- document: item
469
- }), /* @__PURE__ */jsx(EditButton, {
470
- id: item._id,
471
- type: item._type,
472
- disabled: !userRoleCanDrop
473
- }), isLastState ? /* @__PURE__ */jsx(CompleteButton, {
474
- documentId,
475
- disabled: !userRoleCanDrop
476
- }) : null]
477
- })
478
- })]
479
- })
480
- })
256
+ data,
257
+ loading,
258
+ error
259
+ } = useWorkflowMetadata(id, states);
260
+ const client = useClient({
261
+ apiVersion: API_VERSION
481
262
  });
263
+ if (error) {
264
+ console.error(error);
265
+ }
266
+ const handle = useCallback(() => {
267
+ client.delete("workflow-metadata.".concat(id));
268
+ }, [id, client]);
269
+ const isLastState = ((_a = data == null ? void 0 : data.state) == null ? void 0 : _a.id) === states[states.length - 1].id;
270
+ if (!data.metadata) {
271
+ return null;
272
+ }
273
+ return {
274
+ icon: CheckmarkIcon,
275
+ type: "dialog",
276
+ disabled: loading || error || !isLastState,
277
+ label: "Complete Workflow",
278
+ title: isLastState ? "Removes the document from the Workflow process" : "Cannot remove from workflow until in the last state",
279
+ onHandle: () => {
280
+ handle();
281
+ },
282
+ color: "positive"
283
+ };
482
284
  }
483
- const QUERY = groq(_templateObject || (_templateObject = _taggedTemplateLiteral(["*[_type == \"workflow.metadata\"]|order(orderRank){\n \"_metadata\": {\n _rev,\n assignees,\n documentId,\n state,\n orderRank\n },\n ...(\n *[_id in [^.documentId, \"drafts.\" + ^.documentId]]|order(_updatedAt)[0]{ \n _id, \n _type, \n _rev, \n _updatedAt \n }\n )\n}[defined(_id)]"])));
484
- function useWorkflowDocuments(schemaTypes) {
485
- const toast = useToast();
285
+ function arraysContainMatchingString(one, two) {
286
+ return one.some(item => two.includes(item));
287
+ }
288
+ function UpdateWorkflow(props, allStates, actionState) {
289
+ var _a, _b, _c, _d;
290
+ const {
291
+ id,
292
+ type
293
+ } = props;
294
+ const {
295
+ validation,
296
+ isValidating
297
+ } = useValidationStatus(id, type);
298
+ const hasValidationErrors = !isValidating && (validation == null ? void 0 : validation.length) > 0 && validation.find(v => v.level === "error");
299
+ const user = useCurrentUser();
486
300
  const client = useClient({
487
301
  apiVersion: API_VERSION
488
302
  });
303
+ const toast = useToast();
304
+ const currentUser = useCurrentUser();
489
305
  const {
490
306
  data,
491
307
  loading,
492
308
  error
493
- } = useListeningQuery(QUERY, {
494
- params: {
495
- schemaTypes
496
- },
497
- initialValue: []
498
- });
499
- const [localDocuments, setLocalDocuments] = React.useState([]);
500
- React.useEffect(() => {
501
- if (data) {
502
- setLocalDocuments(data);
503
- }
504
- }, [data]);
505
- const move = React.useCallback((draggedId, destination, states, newOrder) => {
506
- const currentLocalData = localDocuments;
507
- const newLocalDocuments = localDocuments.map(item => {
508
- var _a;
509
- if (((_a = item == null ? void 0 : item._metadata) == null ? void 0 : _a.documentId) === draggedId) {
510
- return {
511
- ...item,
512
- _metadata: {
513
- ...item._metadata,
514
- state: destination.droppableId,
515
- orderRank: newOrder,
516
- // This value won't be written to the document
517
- // It's done so that un/publish operations don't happen twice
518
- // Because a moved document's card will update once optimistically
519
- // and then again when the document is updated
520
- optimistic: true
521
- }
522
- };
523
- }
524
- return item;
525
- });
526
- setLocalDocuments(newLocalDocuments);
527
- const newStateId = destination.droppableId;
528
- const newState = states.find(s => s.id === newStateId);
529
- const document = localDocuments.find(d => {
530
- var _a;
531
- return ((_a = d == null ? void 0 : d._metadata) == null ? void 0 : _a.documentId) === draggedId;
532
- });
533
- if (!(newState == null ? void 0 : newState.id)) {
309
+ } = useWorkflowMetadata(id, allStates);
310
+ const {
311
+ state: currentState
312
+ } = data;
313
+ const {
314
+ assignees = []
315
+ } = (_a = data == null ? void 0 : data.metadata) != null ? _a : {};
316
+ if (error) {
317
+ console.error(error);
318
+ }
319
+ const onHandle = (documentId, newState) => {
320
+ client.patch("workflow-metadata.".concat(documentId)).set({
321
+ state: newState.id
322
+ }).commit().then(() => {
323
+ props.onComplete();
534
324
  toast.push({
535
- title: "Could not find target state ".concat(newStateId),
536
- status: "error"
325
+ status: "success",
326
+ title: "Document state now \"".concat(newState.title, "\"")
537
327
  });
538
- return null;
539
- }
540
- if (!document) {
328
+ }).catch(err => {
329
+ props.onComplete();
330
+ console.error(err);
541
331
  toast.push({
542
- title: "Could not find dragged document in data",
543
- status: "error"
544
- });
545
- return null;
546
- }
547
- const {
548
- _id,
549
- _type
550
- } = document;
551
- const {
552
- _rev,
553
- documentId
554
- } = document._metadata || {};
555
- client.patch("workflow-metadata.".concat(documentId)).ifRevisionId(_rev).set({
556
- state: newStateId,
557
- orderRank: newOrder
558
- }).commit().then(() => {
559
- var _a;
560
- return toast.push({
561
- title: "Moved to \"".concat((_a = newState == null ? void 0 : newState.title) != null ? _a : newStateId, "\""),
562
- status: "success"
563
- });
564
- }).catch(() => {
565
- var _a;
566
- setLocalDocuments(currentLocalData);
567
- return toast.push({
568
- title: "Failed to move to \"".concat((_a = newState == null ? void 0 : newState.title) != null ? _a : newStateId, "\""),
569
- status: "error"
332
+ status: "error",
333
+ title: "Document state update failed"
570
334
  });
571
335
  });
336
+ };
337
+ if (!data.metadata || currentState && currentState.id === actionState.id) {
338
+ return null;
339
+ }
340
+ const currentStateIndex = allStates.findIndex(s => s.id === (currentState == null ? void 0 : currentState.id));
341
+ const actionStateIndex = allStates.findIndex(s => s.id === actionState.id);
342
+ const direction = actionStateIndex > currentStateIndex ? "promote" : "demote";
343
+ const DirectionIcon = direction === "promote" ? ArrowRightIcon : ArrowLeftIcon;
344
+ const directionLabel = direction === "promote" ? "Promote" : "Demote";
345
+ let title = "".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
346
+ const userRoleCanUpdateState = ((_b = user == null ? void 0 : user.roles) == null ? void 0 : _b.length) && ((_c = actionState == null ? void 0 : actionState.roles) == null ? void 0 : _c.length) ?
347
+ // If the Action state is limited to specific roles
348
+ // check that the current user has one of those roles
349
+ arraysContainMatchingString(user.roles.map(r => r.name), actionState.roles) :
350
+ // No roles specified on the next state, so anyone can update
351
+ ((_d = actionState == null ? void 0 : actionState.roles) == null ? void 0 : _d.length) !== 0;
352
+ if (!userRoleCanUpdateState) {
353
+ title = "Your User role cannot ".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
354
+ }
355
+ const actionStateIsAValidTransition = (currentState == null ? void 0 : currentState.id) && currentState.transitions.length ?
356
+ // If the Current State limits transitions to specific States
357
+ // Check that the Action State is in Current State's transitions array
358
+ currentState.transitions.includes(actionState.id) :
359
+ // Otherwise this isn't a problem
360
+ true;
361
+ if (!actionStateIsAValidTransition) {
362
+ title = "You cannot ".concat(directionLabel, " State to \"").concat(actionState.title, "\" from \"").concat(currentState == null ? void 0 : currentState.title, "\"");
363
+ }
364
+ const userAssignmentCanUpdateState = actionState.requireAssignment ?
365
+ // If the Action State requires assigned users
366
+ // Check the current user ID is in the assignees array
367
+ currentUser && assignees.length && assignees.includes(currentUser.id) :
368
+ // Otherwise this isn't a problem
369
+ true;
370
+ if (!userAssignmentCanUpdateState) {
371
+ title = "You must be assigned to the document to ".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
372
+ }
373
+ if (hasValidationErrors) {
374
+ title = "Document has validation errors, cannot ".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
375
+ }
376
+ return {
377
+ icon: DirectionIcon,
378
+ disabled: loading || error || isValidating || hasValidationErrors || !currentState || !userRoleCanUpdateState || !actionStateIsAValidTransition || !userAssignmentCanUpdateState,
379
+ title,
380
+ label: actionState.title,
381
+ onHandle: () => onHandle(id, actionState)
382
+ };
383
+ }
384
+ function AssigneesBadge(states, documentId, currentUser) {
385
+ var _a;
386
+ const {
387
+ data,
388
+ loading,
389
+ error
390
+ } = useWorkflowMetadata(documentId, states);
391
+ const {
392
+ metadata
393
+ } = data;
394
+ const userList = useProjectUsers({
395
+ apiVersion: API_VERSION
396
+ });
397
+ if (loading || error || !metadata) {
398
+ if (error) {
399
+ console.error(error);
400
+ }
401
+ return null;
402
+ }
403
+ if (!((_a = metadata == null ? void 0 : metadata.assignees) == null ? void 0 : _a.length)) {
572
404
  return {
573
- _id,
574
- _type,
575
- documentId,
576
- state: newState
405
+ label: "Unassigned"
577
406
  };
578
- }, [client, toast, localDocuments]);
407
+ }
408
+ const {
409
+ assignees
410
+ } = metadata != null ? metadata : [];
411
+ const hasMe = currentUser ? assignees.some(assignee => assignee === currentUser.id) : false;
412
+ const assigneesCount = hasMe ? assignees.length - 1 : assignees.length;
413
+ const assigneeUsers = userList.filter(user => assignees.includes(user.id));
414
+ const title = assigneeUsers.map(user => user.displayName).join(", ");
415
+ let label;
416
+ if (hasMe && assigneesCount === 0) {
417
+ label = "Assigned to Me";
418
+ } else if (hasMe && assigneesCount > 0) {
419
+ label = "Me and ".concat(assigneesCount, " ").concat(assigneesCount === 1 ? "other" : "others");
420
+ } else {
421
+ label = "".concat(assigneesCount, " assigned");
422
+ }
579
423
  return {
580
- workflowData: {
581
- data: localDocuments,
582
- loading,
583
- error
584
- },
585
- operations: {
586
- move
424
+ label,
425
+ title,
426
+ color: "primary"
427
+ };
428
+ }
429
+ function StateBadge(states, documentId) {
430
+ const {
431
+ data,
432
+ loading,
433
+ error
434
+ } = useWorkflowMetadata(documentId, states);
435
+ const {
436
+ state
437
+ } = data;
438
+ if (loading || error) {
439
+ if (error) {
440
+ console.error(error);
587
441
  }
442
+ return null;
443
+ }
444
+ if (!state) {
445
+ return null;
446
+ }
447
+ return {
448
+ label: state.title,
449
+ // title: state.title,
450
+ color: state == null ? void 0 : state.color
588
451
  };
589
452
  }
590
- const StyledFloatingCard = styled(Card)(() => css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n position: fixed;\n bottom: 0;\n left: 0;\n z-index: 1000;\n "]))));
591
- function FloatingCard(_ref2) {
592
- let {
593
- children
594
- } = _ref2;
595
- const childrenHaveValues = Array.isArray(children) ? children.some(Boolean) : Boolean(children);
596
- return /* @__PURE__ */jsx(AnimatePresence, {
597
- children: childrenHaveValues ? /* @__PURE__ */jsx(motion.div, {
598
- initial: {
599
- opacity: 0
600
- },
601
- animate: {
602
- opacity: 1
603
- },
604
- exit: {
605
- opacity: 0
606
- },
607
- children: /* @__PURE__ */jsx(StyledFloatingCard, {
608
- shadow: 3,
609
- padding: 3,
610
- margin: 3,
611
- radius: 3,
612
- children: /* @__PURE__ */jsx(Grid, {
613
- gap: 2,
614
- children
615
- })
616
- })
617
- }, "floater") : null
453
+ function EditButton(props) {
454
+ const {
455
+ id,
456
+ type,
457
+ disabled = false
458
+ } = props;
459
+ const {
460
+ navigateIntent
461
+ } = useRouter();
462
+ return /* @__PURE__ */jsx(Button, {
463
+ onClick: () => navigateIntent("edit", {
464
+ id,
465
+ type
466
+ }),
467
+ mode: "ghost",
468
+ fontSize: 1,
469
+ padding: 2,
470
+ tabIndex: -1,
471
+ icon: EditIcon,
472
+ text: "Edit",
473
+ disabled
618
474
  });
619
475
  }
620
- function Validators(_ref3) {
621
- let {
476
+ function Field(props) {
477
+ var _a;
478
+ const schema = useSchema();
479
+ const {
622
480
  data,
623
- userList,
624
- states
625
- } = _ref3;
626
- const client = useClient({
627
- apiVersion: API_VERSION
628
- });
629
- const toast = useToast();
630
- const documentsWithoutValidMetadataIds = (data == null ? void 0 : data.length) ? data.reduce((acc, cur) => {
631
- var _a;
632
- const {
633
- documentId,
634
- state
635
- } = (_a = cur._metadata) != null ? _a : {};
636
- const stateExists = states.find(s => s.id === state);
637
- return !stateExists && documentId ? [...acc, documentId] : acc;
638
- }, []) : [];
639
- const documentsWithInvalidUserIds = (data == null ? void 0 : data.length) ? data.reduce((acc, cur) => {
640
- var _a;
641
- const {
642
- documentId,
643
- assignees
644
- } = (_a = cur._metadata) != null ? _a : {};
645
- const allAssigneesExist = (assignees == null ? void 0 : assignees.length) ? assignees == null ? void 0 : assignees.every(a => userList.find(u => u.id === a)) : true;
646
- return !allAssigneesExist && documentId ? [...acc, documentId] : acc;
647
- }, []) : [];
648
- const documentsWithoutOrderIds = (data == null ? void 0 : data.length) ? data.reduce((acc, cur) => {
649
- var _a;
650
- const {
651
- documentId,
652
- orderRank
653
- } = (_a = cur._metadata) != null ? _a : {};
654
- return !orderRank && documentId ? [...acc, documentId] : acc;
655
- }, []) : [];
656
- const correctDocuments = React.useCallback(async ids => {
657
- toast.push({
658
- title: "Correcting...",
659
- status: "info"
660
- });
661
- const tx = ids.reduce((item, documentId) => {
662
- return item.patch("workflow-metadata.".concat(documentId), {
663
- set: {
664
- state: states[0].id
665
- }
666
- });
667
- }, client.transaction());
668
- await tx.commit();
669
- toast.push({
670
- title: "Corrected ".concat(ids.length === 1 ? "1 Document" : "".concat(ids.length, " Documents")),
671
- status: "success"
672
- });
673
- }, [client, states, toast]);
674
- const removeUsersFromDocuments = React.useCallback(async ids => {
675
- toast.push({
676
- title: "Removing users...",
677
- status: "info"
678
- });
679
- const tx = ids.reduce((item, documentId) => {
680
- var _a, _b;
681
- const {
682
- assignees
683
- } = (_b = (_a = data.find(d => d._id === documentId)) == null ? void 0 : _a._metadata) != null ? _b : {};
684
- const validAssignees = (assignees == null ? void 0 : assignees.length) ?
685
- // eslint-disable-next-line max-nested-callbacks
686
- assignees.filter(a => {
687
- var _a2;
688
- return (_a2 = userList.find(u => u.id === a)) == null ? void 0 : _a2.id;
689
- }) : [];
690
- return item.patch("workflow-metadata.".concat(documentId), {
691
- set: {
692
- assignees: validAssignees
693
- }
694
- });
695
- }, client.transaction());
696
- await tx.commit();
697
- toast.push({
698
- title: "Corrected ".concat(ids.length === 1 ? "1 Document" : "".concat(ids.length, " Documents")),
699
- status: "success"
700
- });
701
- }, [client, data, toast, userList]);
702
- const addOrderToDocuments = React.useCallback(async ids => {
703
- var _a, _b;
704
- toast.push({
705
- title: "Adding ordering...",
706
- status: "info"
707
- });
708
- const firstOrder = (_b = (_a = data[0]) == null ? void 0 : _a._metadata) == null ? void 0 : _b.orderRank;
709
- let newLexo = firstOrder && data.length !== ids.length ? LexoRank.parse(firstOrder) : LexoRank.min();
710
- const tx = client.transaction();
711
- for (let index = 0; index < ids.length; index += 1) {
712
- newLexo = newLexo.genNext().genNext();
713
- tx.patch("workflow-metadata.".concat(ids[index]), {
714
- set: {
715
- orderRank: newLexo.toString()
716
- }
717
- });
481
+ loading,
482
+ error
483
+ } = useListeningQuery("*[_id in [$id, $draftId]]|order(_updatedAt)[0]", {
484
+ params: {
485
+ id: String(props.value),
486
+ draftId: "drafts.".concat(String(props.value))
718
487
  }
719
- await tx.commit();
720
- toast.push({
721
- title: "Added order to ".concat(ids.length === 1 ? "1 Document" : "".concat(ids.length, " Documents")),
722
- status: "success"
723
- });
724
- }, [data, client, toast]);
725
- return /* @__PURE__ */jsxs(FloatingCard, {
726
- children: [documentsWithoutValidMetadataIds.length > 0 ? /* @__PURE__ */jsx(Button, {
727
- tone: "caution",
728
- onClick: () => correctDocuments(documentsWithoutValidMetadataIds),
729
- text: documentsWithoutValidMetadataIds.length === 1 ? "Correct 1 Document State" : "Correct ".concat(documentsWithoutValidMetadataIds.length, " Document States")
730
- }) : null, documentsWithInvalidUserIds.length > 0 ? /* @__PURE__ */jsx(Button, {
731
- tone: "caution",
732
- onClick: () => removeUsersFromDocuments(documentsWithInvalidUserIds),
733
- text: documentsWithInvalidUserIds.length === 1 ? "Remove Invalid Users from 1 Document" : "Remove Invalid Users from ".concat(documentsWithInvalidUserIds.length, " Documents")
734
- }) : null, documentsWithoutOrderIds.length > 0 ? /* @__PURE__ */jsx(Button, {
735
- tone: "caution",
736
- onClick: () => addOrderToDocuments(documentsWithoutOrderIds),
737
- text: documentsWithoutOrderIds.length === 1 ? "Set Order for 1 Document" : "Set Order for ".concat(documentsWithoutOrderIds.length, " Documents")
738
- }) : null]
739
488
  });
740
- }
741
- function Filters(props) {
742
- const {
743
- uniqueAssignedUsers = [],
744
- selectedUserIds,
745
- schemaTypes,
746
- selectedSchemaTypes,
747
- toggleSelectedUser,
748
- resetSelectedUsers,
749
- toggleSelectedSchemaType
750
- } = props;
751
- const currentUser = useCurrentUser();
752
- const schema = useSchema();
753
- const onAdd = useCallback(id => {
754
- if (!selectedUserIds.includes(id)) {
755
- toggleSelectedUser(id);
756
- }
757
- }, [selectedUserIds, toggleSelectedUser]);
758
- const onRemove = useCallback(id => {
759
- if (selectedUserIds.includes(id)) {
760
- toggleSelectedUser(id);
761
- }
762
- }, [selectedUserIds, toggleSelectedUser]);
763
- const onClear = useCallback(() => {
764
- resetSelectedUsers();
765
- }, [resetSelectedUsers]);
766
- if (uniqueAssignedUsers.length === 0 && schemaTypes.length < 2) {
767
- return null;
489
+ if (loading) {
490
+ return /* @__PURE__ */jsx(Spinner, {});
491
+ }
492
+ const schemaType = schema.get((_a = data == null ? void 0 : data._type) != null ? _a : "");
493
+ if (error || !(data == null ? void 0 : data._type) || !schemaType) {
494
+ return /* @__PURE__ */jsx(Feedback, {
495
+ tone: "critical",
496
+ title: "Error with query"
497
+ });
768
498
  }
769
- const meInUniqueAssignees = (currentUser == null ? void 0 : currentUser.id) && uniqueAssignedUsers.find(u => u.id === currentUser.id);
770
- const uniqueAssigneesNotMe = uniqueAssignedUsers.filter(u => u.id !== (currentUser == null ? void 0 : currentUser.id));
771
499
  return /* @__PURE__ */jsx(Card, {
772
- tone: "primary",
500
+ border: true,
773
501
  padding: 2,
774
- borderBottom: true,
775
- style: {
776
- overflowX: "hidden"
777
- },
778
502
  children: /* @__PURE__ */jsxs(Flex, {
779
503
  align: "center",
780
- children: [/* @__PURE__ */jsx(Flex, {
781
- align: "center",
782
- gap: 1,
783
- flex: 1,
784
- children: uniqueAssignedUsers.length > 5 ? /* @__PURE__ */jsx(Card, {
785
- tone: "default",
786
- children: /* @__PURE__ */jsx(MenuButton, {
787
- button: /* @__PURE__ */jsx(Button, {
788
- text: "Filter Assignees",
789
- tone: "primary",
790
- icon: UserIcon
791
- }),
792
- id: "user-filters",
793
- menu: /* @__PURE__ */jsx(Menu, {
794
- children: /* @__PURE__ */jsx(UserSelectMenu, {
795
- value: selectedUserIds,
796
- userList: uniqueAssignedUsers,
797
- onAdd,
798
- onRemove,
799
- onClear,
800
- labels: {
801
- addMe: "Filter mine",
802
- removeMe: "Clear mine",
803
- clear: "Clear filters"
804
- }
805
- })
806
- }),
807
- popover: {
808
- portal: true
809
- }
810
- })
811
- }) : /* @__PURE__ */jsxs(Fragment, {
812
- children: [meInUniqueAssignees ? /* @__PURE__ */jsxs(Fragment, {
813
- children: [/* @__PURE__ */jsx(Button, {
814
- padding: 0,
815
- mode: selectedUserIds.includes(currentUser.id) ? "default" : "bleed",
816
- onClick: () => toggleSelectedUser(currentUser.id),
817
- children: /* @__PURE__ */jsx(Flex, {
818
- padding: 1,
819
- align: "center",
820
- justify: "center",
821
- children: /* @__PURE__ */jsx(UserAvatar, {
822
- user: currentUser.id,
823
- size: 1,
824
- withTooltip: true
825
- })
826
- })
827
- }), /* @__PURE__ */jsx(Card, {
828
- borderRight: true,
829
- style: {
830
- height: 30
831
- },
832
- tone: "inherit"
833
- })]
834
- }) : null, uniqueAssigneesNotMe.map(user => /* @__PURE__ */jsx(Button, {
835
- padding: 0,
836
- mode: selectedUserIds.includes(user.id) ? "default" : "bleed",
837
- onClick: () => toggleSelectedUser(user.id),
838
- children: /* @__PURE__ */jsx(Flex, {
839
- padding: 1,
840
- align: "center",
841
- justify: "center",
842
- children: /* @__PURE__ */jsx(UserAvatar, {
843
- user,
844
- size: 1,
845
- withTooltip: true
846
- })
847
- })
848
- }, user.id)), selectedUserIds.length > 0 ? /* @__PURE__ */jsx(Button, {
849
- text: "Clear",
850
- onClick: resetSelectedUsers,
851
- mode: "ghost",
852
- icon: ResetIcon
853
- }) : null]
854
- })
855
- }), schemaTypes.length > 0 ? /* @__PURE__ */jsx(Flex, {
856
- align: "center",
857
- gap: 1,
858
- children: schemaTypes.map(typeName => {
859
- var _a, _b;
860
- const schemaType = schema.get(typeName);
861
- if (!schemaType) {
862
- return null;
863
- }
864
- return /* @__PURE__ */jsx(Button, {
865
- text: (_a = schemaType == null ? void 0 : schemaType.title) != null ? _a : typeName,
866
- icon: (_b = schemaType == null ? void 0 : schemaType.icon) != null ? _b : void 0,
867
- mode: selectedSchemaTypes.includes(typeName) ? "default" : "ghost",
868
- onClick: () => toggleSelectedSchemaType(typeName)
869
- }, typeName);
870
- })
871
- }) : null]
872
- })
873
- });
874
- }
875
- function filterItemsAndSort(items, stateId) {
876
- let selectedUsers = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
504
+ justify: "space-between",
505
+ gap: 2,
506
+ children: [/* @__PURE__ */jsx(Preview, {
507
+ layout: "default",
508
+ value: data,
509
+ schemaType
510
+ }), /* @__PURE__ */jsx(EditButton, {
511
+ id: data._id,
512
+ type: data._type
513
+ })]
514
+ })
515
+ });
516
+ }
517
+ const UserAssignmentInput = props => {
518
+ var _a;
519
+ const documentId = useFormValue(["documentId"]);
520
+ const userList = useProjectUsers({
521
+ apiVersion: API_VERSION
522
+ });
523
+ const stringValue = Array.isArray(props == null ? void 0 : props.value) && ((_a = props == null ? void 0 : props.value) == null ? void 0 : _a.length) ? props.value.map(item => String(item)) : [];
524
+ return /* @__PURE__ */jsx(Card, {
525
+ border: true,
526
+ padding: 1,
527
+ children: /* @__PURE__ */jsx(UserAssignment, {
528
+ userList,
529
+ assignees: stringValue,
530
+ documentId: String(documentId)
531
+ })
532
+ });
533
+ };
534
+ function initialRank() {
535
+ let lastRankValue = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "";
536
+ const lastRank = lastRankValue && typeof lastRankValue === "string" ? LexoRank.parse(lastRankValue) : LexoRank.min();
537
+ const nextRank = lastRank.genNext().genNext();
538
+ return nextRank.value;
539
+ }
540
+ var metadata = states => defineType({
541
+ type: "document",
542
+ name: "workflow.metadata",
543
+ title: "Workflow metadata",
544
+ liveEdit: true,
545
+ fields: [defineField({
546
+ name: "state",
547
+ description: "The current \"State\" of the document. Field is read only as changing it would not fire the state's \"operation\" setting. These are fired in the Document Actions and in the custom Tool.",
548
+ readOnly: true,
549
+ type: "string",
550
+ options: {
551
+ list: states.length ? states.map(state => ({
552
+ value: state.id,
553
+ title: state.title
554
+ })) : [],
555
+ layout: "radio"
556
+ }
557
+ }), defineField({
558
+ name: "documentId",
559
+ title: "Document ID",
560
+ description: "Used to help identify the target document that this metadata is tracking state for.",
561
+ type: "string",
562
+ readOnly: true,
563
+ components: {
564
+ input: Field
565
+ }
566
+ }), defineField({
567
+ name: "orderRank",
568
+ description: "Used to maintain order position of cards in the Tool.",
569
+ type: "string",
570
+ readOnly: true,
571
+ initialValue: async (p, _ref) => {
572
+ let {
573
+ getClient
574
+ } = _ref;
575
+ const lastDocOrderRank = await getClient({
576
+ apiVersion: API_VERSION
577
+ }).fetch("*[_type == $type]|order(@[$order] desc)[0][$order]", {
578
+ order: "orderRank",
579
+ type: "workflow.metadata"
580
+ });
581
+ return initialRank(lastDocOrderRank);
582
+ }
583
+ }), defineField({
584
+ type: "array",
585
+ name: "assignees",
586
+ of: [{
587
+ type: "string"
588
+ }],
589
+ components: {
590
+ input: UserAssignmentInput
591
+ }
592
+ })]
593
+ });
594
+ function filterItemsAndSort(items, stateId) {
595
+ let selectedUsers = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
877
596
  let selectedSchemaTypes = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [];
878
- return items.filter(item => {
597
+ return items.filter(item => item == null ? void 0 : item._id).filter(item => {
879
598
  var _a;
880
599
  return ((_a = item == null ? void 0 : item._metadata) == null ? void 0 : _a.state) === stateId;
881
600
  }).filter(item => {
@@ -893,754 +612,1144 @@ function filterItemsAndSort(items, stateId) {
893
612
  return aOrderRank.localeCompare(bOrderRank);
894
613
  });
895
614
  }
896
- function arraysContainMatchingString(one, two) {
897
- return one.some(item => two.includes(item));
898
- }
899
- function Status(props) {
900
- const {
901
- text,
902
- icon
903
- } = props;
904
- const Icon = icon;
905
- return /* @__PURE__ */jsx(Tooltip, {
906
- portal: true,
907
- content: /* @__PURE__ */jsx(Box, {
908
- padding: 2,
909
- children: /* @__PURE__ */jsx(Text, {
910
- size: 1,
911
- children: text
912
- })
913
- }),
914
- children: /* @__PURE__ */jsx(Text, {
915
- size: 1,
916
- children: /* @__PURE__ */jsx(Icon, {})
917
- })
918
- });
919
- }
920
- const StyledStickyCard = styled(Card)(() => css(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["\n position: sticky;\n top: 0;\n z-index: 1;\n "]))));
921
- function StateTitle(props) {
922
- const {
923
- state,
924
- requireAssignment,
925
- userRoleCanDrop,
926
- isDropDisabled,
927
- draggingFrom
928
- } = props;
929
- let tone = "default";
930
- const isSource = draggingFrom === state.id;
931
- if (draggingFrom) {
932
- tone = isDropDisabled || isSource ? "default" : "positive";
933
- }
934
- return /* @__PURE__ */jsx(StyledStickyCard, {
935
- paddingY: 4,
936
- padding: 3,
937
- tone: "inherit",
938
- children: /* @__PURE__ */jsxs(Flex, {
939
- gap: 3,
940
- align: "center",
941
- children: [/* @__PURE__ */jsx(Badge, {
942
- mode: draggingFrom && !isDropDisabled || isSource ? "default" : "outline",
943
- tone,
944
- muted: !userRoleCanDrop || isDropDisabled,
945
- children: state.title
946
- }), userRoleCanDrop ? null : /* @__PURE__ */jsx(Status, {
947
- text: "You do not have permissions to move documents to this State",
948
- icon: InfoOutlineIcon
949
- }), requireAssignment ? /* @__PURE__ */jsx(Status, {
950
- text: "You must be assigned to the document to move documents to this State",
951
- icon: UserIcon
952
- }) : null]
953
- })
954
- });
955
- }
956
- function WorkflowTool(props) {
957
- var _a, _b, _c;
958
- const {
959
- schemaTypes = [],
960
- states = []
961
- } = (_b = (_a = props == null ? void 0 : props.tool) == null ? void 0 : _a.options) != null ? _b : {};
962
- const isDarkMode = useTheme().sanity.color.dark;
963
- const defaultCardTone = isDarkMode ? "default" : "transparent";
964
- const userList = useProjectUsers({
615
+ const QUERY = groq(_templateObject || (_templateObject = _taggedTemplateLiteral(["*[_type == \"workflow.metadata\"]|order(orderRank){\n \"_metadata\": {\n _rev,\n assignees,\n documentId,\n state,\n orderRank\n },\n ...(\n *[_id in [^.documentId, \"drafts.\" + ^.documentId]]|order(_updatedAt)[0]{ \n _id, \n _type, \n _rev, \n _updatedAt \n }\n )\n}"])));
616
+ function useWorkflowDocuments(schemaTypes) {
617
+ const toast = useToast();
618
+ const client = useClient({
965
619
  apiVersion: API_VERSION
966
620
  });
967
- const user = useCurrentUser();
968
- const userRoleNames = ((_c = user == null ? void 0 : user.roles) == null ? void 0 : _c.length) ? user == null ? void 0 : user.roles.map(r => r.name) : [];
969
- const {
970
- workflowData,
971
- operations
972
- } = useWorkflowDocuments(schemaTypes);
973
621
  const {
974
622
  data,
975
623
  loading,
976
624
  error
977
- } = workflowData;
978
- const {
979
- move
980
- } = operations;
981
- const [undroppableStates, setUndroppableStates] = React.useState([]);
982
- const [draggingFrom, setDraggingFrom] = React.useState("");
983
- const handleDragStart = React.useCallback(start => {
984
- var _a2, _b2;
985
- const {
986
- draggableId,
987
- source
988
- } = start;
989
- const {
990
- droppableId: currentStateId
991
- } = source;
992
- setDraggingFrom(currentStateId);
993
- const document = data.find(item => {
994
- var _a3;
995
- return ((_a3 = item._metadata) == null ? void 0 : _a3.documentId) === draggableId;
996
- });
997
- const state = states.find(s => s.id === currentStateId);
998
- if (!document || !state) return;
999
- const undroppableStateIds = [];
1000
- const statesThatRequireAssignmentIds = states.filter(s => s.requireAssignment).map(s => s.id);
1001
- if (statesThatRequireAssignmentIds.length) {
1002
- const documentAssignees = (_b2 = (_a2 = document._metadata) == null ? void 0 : _a2.assignees) != null ? _b2 : [];
1003
- const userIsAssignedToDocument = (user == null ? void 0 : user.id) ? documentAssignees.includes(user.id) : false;
1004
- if (!userIsAssignedToDocument) {
1005
- undroppableStateIds.push(...statesThatRequireAssignmentIds);
625
+ } = useListeningQuery(QUERY, {
626
+ params: {
627
+ schemaTypes
628
+ },
629
+ initialValue: []
630
+ });
631
+ const [localDocuments, setLocalDocuments] = React.useState([]);
632
+ React.useEffect(() => {
633
+ if (data) {
634
+ setLocalDocuments(data);
635
+ }
636
+ }, [data]);
637
+ const move = React.useCallback((draggedId, destination, states, newOrder) => {
638
+ const currentLocalData = localDocuments;
639
+ const newLocalDocuments = localDocuments.map(item => {
640
+ var _a;
641
+ if (((_a = item == null ? void 0 : item._metadata) == null ? void 0 : _a.documentId) === draggedId) {
642
+ return {
643
+ ...item,
644
+ _metadata: {
645
+ ...item._metadata,
646
+ state: destination.droppableId,
647
+ orderRank: newOrder,
648
+ // This value won't be written to the document
649
+ // It's done so that un/publish operations don't happen twice
650
+ // Because a moved document's card will update once optimistically
651
+ // and then again when the document is updated
652
+ optimistic: true
653
+ }
654
+ };
1006
655
  }
656
+ return item;
657
+ });
658
+ setLocalDocuments(newLocalDocuments);
659
+ const newStateId = destination.droppableId;
660
+ const newState = states.find(s => s.id === newStateId);
661
+ const document = localDocuments.find(d => {
662
+ var _a;
663
+ return ((_a = d == null ? void 0 : d._metadata) == null ? void 0 : _a.documentId) === draggedId;
664
+ });
665
+ if (!(newState == null ? void 0 : newState.id)) {
666
+ toast.push({
667
+ title: "Could not find target state ".concat(newStateId),
668
+ status: "error"
669
+ });
670
+ return null;
1007
671
  }
1008
- const statesThatCannotBeTransitionedToIds = state.transitions && state.transitions.length ? states.filter(s => {
1009
- var _a3;
1010
- return !((_a3 = state.transitions) == null ? void 0 : _a3.includes(s.id));
1011
- }).map(s => s.id) : [];
1012
- if (statesThatCannotBeTransitionedToIds.length) {
1013
- undroppableStateIds.push(...statesThatCannotBeTransitionedToIds);
672
+ if (!document) {
673
+ toast.push({
674
+ title: "Could not find dragged document in data",
675
+ status: "error"
676
+ });
677
+ return null;
1014
678
  }
1015
- const undroppableExceptSelf = undroppableStateIds.filter(id => id !== currentStateId);
1016
- if (undroppableExceptSelf.length) {
1017
- setUndroppableStates(undroppableExceptSelf);
1018
- }
1019
- }, [data, states, user]);
1020
- const handleDragEnd = React.useCallback(result => {
1021
- var _a2, _b2, _c2, _d, _e, _f;
1022
- setUndroppableStates([]);
1023
- setDraggingFrom("");
1024
679
  const {
1025
- draggableId,
1026
- source,
1027
- destination
1028
- } = result;
1029
- if (
1030
- // No destination?
1031
- !destination ||
1032
- // No change in position?
1033
- destination.droppableId === source.droppableId && destination.index === source.index) {
1034
- return;
1035
- }
1036
- const destinationStateItems = [...filterItemsAndSort(data, destination.droppableId, [], null)];
1037
- let newOrder;
1038
- if (!destinationStateItems.length) {
1039
- newOrder = LexoRank.min().toString();
1040
- } else if (destination.index === 0) {
1041
- const firstItemOrderRank = (_b2 = (_a2 = [...destinationStateItems].shift()) == null ? void 0 : _a2._metadata) == null ? void 0 : _b2.orderRank;
1042
- newOrder = firstItemOrderRank && typeof firstItemOrderRank === "string" ? LexoRank.parse(firstItemOrderRank).genPrev().toString() : LexoRank.min().toString();
1043
- } else if (destination.index + 1 === destinationStateItems.length) {
1044
- const lastItemOrderRank = (_d = (_c2 = [...destinationStateItems].pop()) == null ? void 0 : _c2._metadata) == null ? void 0 : _d.orderRank;
1045
- newOrder = lastItemOrderRank && typeof lastItemOrderRank === "string" ? LexoRank.parse(lastItemOrderRank).genNext().toString() : LexoRank.min().toString();
1046
- } else {
1047
- const itemBefore = destinationStateItems[destination.index];
1048
- const itemBeforeRank = (_e = itemBefore == null ? void 0 : itemBefore._metadata) == null ? void 0 : _e.orderRank;
1049
- const itemBeforeRankParsed = itemBefore._metadata.orderRank ? LexoRank.parse(itemBeforeRank) : LexoRank.min();
1050
- const itemAfter = destinationStateItems[destination.index + 1];
1051
- const itemAfterRank = (_f = itemAfter == null ? void 0 : itemAfter._metadata) == null ? void 0 : _f.orderRank;
1052
- const itemAfterRankParsed = itemAfter._metadata.orderRank ? LexoRank.parse(itemAfterRank) : LexoRank.max();
1053
- newOrder = itemBeforeRankParsed.between(itemAfterRankParsed).toString();
1054
- }
1055
- move(draggableId, destination, states, newOrder);
1056
- }, [data, move, states]);
1057
- const uniqueAssignedUsers = React.useMemo(() => {
1058
- const uniqueUserIds = data.reduce((acc, item) => {
1059
- var _a2;
1060
- const {
1061
- assignees = []
1062
- } = (_a2 = item._metadata) != null ? _a2 : {};
1063
- const newAssignees = (assignees == null ? void 0 : assignees.length) ? assignees.filter(a => !acc.includes(a)) : [];
1064
- return newAssignees.length ? [...acc, ...newAssignees] : acc;
1065
- }, []);
1066
- return userList.filter(u => uniqueUserIds.includes(u.id));
1067
- }, [data, userList]);
1068
- const [selectedUserIds, setSelectedUserIds] = React.useState(uniqueAssignedUsers.map(u => u.id));
1069
- const toggleSelectedUser = React.useCallback(userId => {
1070
- setSelectedUserIds(prev => prev.includes(userId) ? prev.filter(u => u !== userId) : [...prev, userId]);
1071
- }, []);
1072
- const resetSelectedUsers = React.useCallback(() => {
1073
- setSelectedUserIds([]);
1074
- }, []);
1075
- const [selectedSchemaTypes, setSelectedSchemaTypes] = React.useState(schemaTypes);
1076
- const toggleSelectedSchemaType = React.useCallback(schemaType => {
1077
- setSelectedSchemaTypes(prev => prev.includes(schemaType) ? prev.filter(u => u !== schemaType) : [...prev, schemaType]);
1078
- }, []);
1079
- const [invalidDocumentIds, setInvalidDocumentIds] = React.useState([]);
1080
- const toggleInvalidDocumentId = React.useCallback((docId, action) => {
1081
- setInvalidDocumentIds(prev => action === "ADD" ? [...prev, docId] : prev.filter(id => id !== docId));
1082
- }, []);
1083
- if (!(states == null ? void 0 : states.length)) {
1084
- return /* @__PURE__ */jsx(Container, {
1085
- width: 1,
1086
- padding: 5,
1087
- children: /* @__PURE__ */jsx(Feedback, {
1088
- tone: "caution",
1089
- title: "Plugin options error",
1090
- description: "No States defined in plugin config"
1091
- })
680
+ _id,
681
+ _type
682
+ } = document;
683
+ const {
684
+ _rev,
685
+ documentId
686
+ } = document._metadata || {};
687
+ client.patch("workflow-metadata.".concat(documentId)).ifRevisionId(_rev).set({
688
+ state: newStateId,
689
+ orderRank: newOrder
690
+ }).commit().then(() => {
691
+ var _a;
692
+ return toast.push({
693
+ title: "Moved to \"".concat((_a = newState == null ? void 0 : newState.title) != null ? _a : newStateId, "\""),
694
+ status: "success"
695
+ });
696
+ }).catch(() => {
697
+ var _a;
698
+ setLocalDocuments(currentLocalData);
699
+ return toast.push({
700
+ title: "Failed to move to \"".concat((_a = newState == null ? void 0 : newState.title) != null ? _a : newStateId, "\""),
701
+ status: "error"
702
+ });
1092
703
  });
704
+ return {
705
+ _id,
706
+ _type,
707
+ documentId,
708
+ state: newState
709
+ };
710
+ }, [client, toast, localDocuments]);
711
+ return {
712
+ workflowData: {
713
+ data: localDocuments,
714
+ loading,
715
+ error
716
+ },
717
+ operations: {
718
+ move
719
+ }
720
+ };
721
+ }
722
+ function AvatarGroup(props) {
723
+ const currentUser = useCurrentUser();
724
+ const {
725
+ users,
726
+ max = 4
727
+ } = props;
728
+ const len = users == null ? void 0 : users.length;
729
+ const {
730
+ me,
731
+ visibleUsers
732
+ } = React.useMemo(() => {
733
+ return {
734
+ me: (currentUser == null ? void 0 : currentUser.id) ? users.find(u => u.id === currentUser.id) : void 0,
735
+ visibleUsers: users.filter(u => u.id !== (currentUser == null ? void 0 : currentUser.id)).slice(0, max - 1)
736
+ };
737
+ }, [users, max, currentUser]);
738
+ if (!(users == null ? void 0 : users.length)) {
739
+ return null;
1093
740
  }
1094
- if (error && !data.length) {
1095
- return /* @__PURE__ */jsx(Container, {
1096
- width: 1,
1097
- padding: 5,
1098
- children: /* @__PURE__ */jsx(Feedback, {
1099
- tone: "critical",
1100
- title: "Error querying for Workflow documents"
741
+ return /* @__PURE__ */jsxs(Flex, {
742
+ align: "center",
743
+ gap: 1,
744
+ children: [me ? /* @__PURE__ */jsx(UserAvatar, {
745
+ user: me
746
+ }) : null, visibleUsers.map(user => /* @__PURE__ */jsx(Box, {
747
+ style: {
748
+ marginRight: -8
749
+ },
750
+ children: /* @__PURE__ */jsx(UserAvatar, {
751
+ user
1101
752
  })
1102
- });
1103
- }
1104
- return /* @__PURE__ */jsxs(Card, {
1105
- height: "fill",
1106
- overflow: "hidden",
1107
- children: [/* @__PURE__ */jsx(Validators, {
1108
- data,
1109
- userList,
1110
- states
1111
- }), /* @__PURE__ */jsx(Filters, {
1112
- uniqueAssignedUsers,
1113
- selectedUserIds,
1114
- toggleSelectedUser,
1115
- resetSelectedUsers,
1116
- schemaTypes,
1117
- selectedSchemaTypes,
1118
- toggleSelectedSchemaType
1119
- }), /* @__PURE__ */jsx(DragDropContext, {
1120
- onDragStart: handleDragStart,
1121
- onDragEnd: handleDragEnd,
1122
- children: /* @__PURE__ */jsx(Grid, {
1123
- columns: states.length,
1124
- height: "fill",
1125
- children: states.map((state, stateIndex) => {
1126
- var _a2, _b2;
1127
- const userRoleCanDrop = ((_a2 = state == null ? void 0 : state.roles) == null ? void 0 : _a2.length) ? arraysContainMatchingString(state.roles, userRoleNames) : true;
1128
- const isDropDisabled = !userRoleCanDrop || undroppableStates.includes(state.id);
1129
- return /* @__PURE__ */jsxs(Card, {
1130
- borderLeft: stateIndex > 0,
1131
- tone: defaultCardTone,
1132
- height: "fill",
1133
- overflow: "auto",
1134
- children: [/* @__PURE__ */jsx(StateTitle, {
1135
- state,
1136
- requireAssignment: (_b2 = state.requireAssignment) != null ? _b2 : false,
1137
- userRoleCanDrop,
1138
- isDropDisabled,
1139
- draggingFrom
1140
- }), /* @__PURE__ */jsx(Droppable, {
1141
- droppableId: state.id,
1142
- isDropDisabled,
1143
- children: (provided, snapshot) => /* @__PURE__ */jsxs(Card, {
1144
- ref: provided.innerRef,
1145
- tone: snapshot.isDraggingOver ? "primary" : defaultCardTone,
1146
- height: "fill",
1147
- paddingTop: 1,
1148
- children: [loading ? /* @__PURE__ */jsx(Flex, {
1149
- padding: 5,
1150
- align: "center",
1151
- justify: "center",
1152
- children: /* @__PURE__ */jsx(Spinner, {
1153
- muted: true
1154
- })
1155
- }) : null, data.length > 0 && filterItemsAndSort(data, state.id, selectedUserIds, selectedSchemaTypes).map((item, itemIndex) => {
1156
- var _a3, _b3, _c2, _d;
1157
- const isInvalid = invalidDocumentIds.includes(String((_a3 = item == null ? void 0 : item._metadata) == null ? void 0 : _a3.documentId));
1158
- const meInAssignees = (user == null ? void 0 : user.id) ? (_c2 = (_b3 = item == null ? void 0 : item._metadata) == null ? void 0 : _b3.assignees) == null ? void 0 : _c2.includes(user.id) : false;
1159
- const isDragDisabled = !userRoleCanDrop || isInvalid || !(state.requireAssignment ? state.requireAssignment && meInAssignees : true);
1160
- const {
1161
- documentId
1162
- } = (_d = item._metadata) != null ? _d : {};
1163
- if (!documentId) {
1164
- return null;
1165
- }
1166
- return /* @__PURE__ */jsx(Draggable, {
1167
- draggableId: documentId,
1168
- index: itemIndex,
1169
- isDragDisabled,
1170
- children: (draggableProvided, draggableSnapshot) => /* @__PURE__ */jsx("div", {
1171
- ref: draggableProvided.innerRef,
1172
- ...draggableProvided.draggableProps,
1173
- ...draggableProvided.dragHandleProps,
1174
- children: /* @__PURE__ */jsx(DocumentCard, {
1175
- userRoleCanDrop,
1176
- isDragDisabled,
1177
- isDragging: draggableSnapshot.isDragging,
1178
- item,
1179
- toggleInvalidDocumentId,
1180
- userList,
1181
- states
1182
- })
1183
- })
1184
- }, documentId);
1185
- }), provided.placeholder]
1186
- })
1187
- })]
1188
- }, state.id);
1189
- })
753
+ }, user.id)), len > max && /* @__PURE__ */jsx(Box, {
754
+ paddingLeft: 2,
755
+ children: /* @__PURE__ */jsxs(Text, {
756
+ size: 1,
757
+ children: ["+", len - max]
1190
758
  })
1191
759
  })]
1192
760
  });
1193
761
  }
1194
- const workflowTool = options => ({
1195
- name: "workflow",
1196
- title: "Workflow",
1197
- component: WorkflowTool,
1198
- icon: SplitVerticalIcon,
1199
- options
1200
- });
1201
- function Field(props) {
1202
- var _a;
1203
- const schema = useSchema();
762
+ function UserDisplay(props) {
1204
763
  const {
1205
- data,
1206
- loading,
1207
- error
1208
- } = useListeningQuery("*[_id in [$id, $draftId]]|order(_updatedAt)[0]", {
1209
- params: {
1210
- id: String(props.value),
1211
- draftId: "drafts.".concat(String(props.value))
1212
- }
764
+ assignees,
765
+ userList,
766
+ documentId,
767
+ disabled = false
768
+ } = props;
769
+ const [button] = React.useState(null);
770
+ const [popover, setPopover] = React.useState(null);
771
+ const [isOpen, setIsOpen] = React.useState(false);
772
+ const close = React.useCallback(() => setIsOpen(false), []);
773
+ const open = React.useCallback(() => setIsOpen(true), []);
774
+ useClickOutside(close, [button, popover]);
775
+ return /* @__PURE__ */jsx(Popover, {
776
+ ref: setPopover,
777
+ content: /* @__PURE__ */jsx(UserAssignment, {
778
+ userList,
779
+ assignees,
780
+ documentId
781
+ }),
782
+ portal: true,
783
+ open: isOpen,
784
+ children: !assignees || assignees.length === 0 ? /* @__PURE__ */jsx(Button, {
785
+ onClick: open,
786
+ fontSize: 1,
787
+ padding: 2,
788
+ tabIndex: -1,
789
+ icon: AddIcon,
790
+ text: "Assign",
791
+ tone: "positive",
792
+ mode: "ghost",
793
+ disabled
794
+ }) : /* @__PURE__ */jsx(Grid, {
795
+ children: /* @__PURE__ */jsx(Button, {
796
+ onClick: open,
797
+ padding: 0,
798
+ mode: "bleed",
799
+ disabled,
800
+ children: /* @__PURE__ */jsx(AvatarGroup, {
801
+ users: userList.filter(u => assignees.includes(u.id))
802
+ })
803
+ })
804
+ })
1213
805
  });
1214
- if (loading) {
1215
- return /* @__PURE__ */jsx(Spinner, {});
1216
- }
1217
- const schemaType = schema.get((_a = data == null ? void 0 : data._type) != null ? _a : "");
1218
- if (error || !(data == null ? void 0 : data._type) || !schemaType) {
1219
- return /* @__PURE__ */jsx(Feedback, {
1220
- tone: "critical",
1221
- title: "Error with query"
806
+ }
807
+ function CompleteButton(props) {
808
+ const {
809
+ documentId,
810
+ disabled = false
811
+ } = props;
812
+ const client = useClient({
813
+ apiVersion: API_VERSION
814
+ });
815
+ const toast = useToast();
816
+ const handleComplete = React.useCallback(id => {
817
+ client.delete("workflow-metadata.".concat(id)).then(() => {
818
+ toast.push({
819
+ status: "success",
820
+ title: "Workflow completed",
821
+ description: id
822
+ });
823
+ }).catch(() => {
824
+ toast.push({
825
+ status: "error",
826
+ title: "Could not complete Workflow",
827
+ description: id
828
+ });
1222
829
  });
1223
- }
1224
- return /* @__PURE__ */jsx(Card, {
1225
- border: true,
830
+ }, [client, toast]);
831
+ return /* @__PURE__ */jsx(Button, {
832
+ onClick: () => handleComplete(documentId),
833
+ text: "Complete",
834
+ icon: CheckmarkIcon,
835
+ tone: "positive",
836
+ mode: "ghost",
837
+ fontSize: 1,
1226
838
  padding: 2,
1227
- children: /* @__PURE__ */jsxs(Flex, {
1228
- align: "center",
1229
- justify: "space-between",
1230
- gap: 2,
1231
- children: [/* @__PURE__ */jsx(Preview, {
1232
- layout: "default",
1233
- value: data,
1234
- schemaType
1235
- }), /* @__PURE__ */jsx(EditButton, {
1236
- id: data._id,
1237
- type: data._type
1238
- })]
1239
- })
839
+ tabIndex: -1,
840
+ disabled
1240
841
  });
1241
842
  }
1242
- const UserAssignmentInput = props => {
1243
- var _a;
1244
- const documentId = useFormValue(["documentId"]);
1245
- const userList = useProjectUsers({
1246
- apiVersion: API_VERSION
1247
- });
1248
- const stringValue = Array.isArray(props == null ? void 0 : props.value) && ((_a = props == null ? void 0 : props.value) == null ? void 0 : _a.length) ? props.value.map(item => String(item)) : [];
1249
- return /* @__PURE__ */jsx(Card, {
1250
- border: true,
1251
- padding: 1,
1252
- children: /* @__PURE__ */jsx(UserAssignment, {
1253
- userList,
1254
- assignees: stringValue,
1255
- documentId: String(documentId)
1256
- })
843
+ function TimeAgo(_ref2) {
844
+ let {
845
+ time
846
+ } = _ref2;
847
+ const timeAgo = useTimeAgo(time);
848
+ return /* @__PURE__ */jsxs("span", {
849
+ title: timeAgo,
850
+ children: [timeAgo, " ago"]
1257
851
  });
1258
- };
1259
- function initialRank() {
1260
- let lastRankValue = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "";
1261
- const lastRank = lastRankValue && typeof lastRankValue === "string" ? LexoRank.parse(lastRankValue) : LexoRank.min();
1262
- const nextRank = lastRank.genNext().genNext();
1263
- return nextRank.value;
1264
852
  }
1265
- var metadata = states => defineType({
1266
- type: "document",
1267
- name: "workflow.metadata",
1268
- title: "Workflow metadata",
1269
- liveEdit: true,
1270
- fields: [defineField({
1271
- name: "state",
1272
- description: "The current \"State\" of the document. Field is read only as changing it would not fire the state's \"operation\" setting. These are fired in the Document Actions and in the custom Tool.",
1273
- readOnly: true,
1274
- type: "string",
1275
- options: {
1276
- list: states.length ? states.map(state => ({
1277
- value: state.id,
1278
- title: state.title
1279
- })) : [],
1280
- layout: "radio"
1281
- }
1282
- }), defineField({
1283
- name: "documentId",
1284
- title: "Document ID",
1285
- description: "Used to help identify the target document that this metadata is tracking state for.",
1286
- type: "string",
1287
- readOnly: true,
1288
- components: {
1289
- input: Field
1290
- }
1291
- }), defineField({
1292
- name: "orderRank",
1293
- description: "Used to maintain order position of cards in the Tool.",
1294
- type: "string",
1295
- readOnly: true,
1296
- initialValue: async (p, _ref4) => {
1297
- let {
1298
- getClient
1299
- } = _ref4;
1300
- const lastDocOrderRank = await getClient({
1301
- apiVersion: API_VERSION
1302
- }).fetch("*[_type == $type]|order(@[$order] desc)[0][$order]", {
1303
- order: "orderRank",
1304
- type: "workflow.metadata"
1305
- });
1306
- return initialRank(lastDocOrderRank);
1307
- }
1308
- }), defineField({
1309
- type: "array",
1310
- name: "assignees",
1311
- of: [{
1312
- type: "string"
1313
- }],
1314
- components: {
1315
- input: UserAssignmentInput
1316
- }
1317
- })]
1318
- });
1319
- function useWorkflowMetadata(id, states) {
853
+ function DraftStatus(props) {
1320
854
  const {
1321
- data: metadata,
1322
- loading,
1323
- error
1324
- } = useListeningQuery("*[_type == \"workflow.metadata\" && documentId == $id][0]", {
1325
- params: {
1326
- id
1327
- }
855
+ document
856
+ } = props;
857
+ const updatedAt = document && "_updatedAt" in document && document._updatedAt;
858
+ return /* @__PURE__ */jsx(Tooltip, {
859
+ portal: true,
860
+ content: /* @__PURE__ */jsx(Box, {
861
+ padding: 2,
862
+ children: /* @__PURE__ */jsx(Text, {
863
+ size: 1,
864
+ children: document ? /* @__PURE__ */jsxs(Fragment, {
865
+ children: ["Edited ", updatedAt && /* @__PURE__ */jsx(TimeAgo, {
866
+ time: updatedAt
867
+ })]
868
+ }) : /* @__PURE__ */jsx(Fragment, {
869
+ children: "No unpublished edits"
870
+ })
871
+ })
872
+ }),
873
+ children: /* @__PURE__ */jsx(TextWithTone, {
874
+ tone: "caution",
875
+ dimmed: !document,
876
+ muted: !document,
877
+ size: 1,
878
+ children: /* @__PURE__ */jsx(EditIcon, {})
879
+ })
1328
880
  });
1329
- if (metadata == null ? void 0 : metadata.state) {
1330
- return {
1331
- data: {
1332
- metadata,
1333
- state: states.find(s => s.id === metadata.state)
1334
- },
1335
- loading,
1336
- error
1337
- };
1338
- }
1339
- return {
1340
- data: {},
1341
- loading,
1342
- error
1343
- };
1344
881
  }
1345
- function AssignWorkflow(props, states) {
1346
- var _a, _b;
882
+ function PublishedStatus(props) {
1347
883
  const {
1348
- id
884
+ document
1349
885
  } = props;
1350
- const [isDialogOpen, setDialogOpen] = useState(false);
1351
- const userList = useProjectUsers({
1352
- apiVersion: API_VERSION
886
+ const updatedAt = document && "_updatedAt" in document && document._updatedAt;
887
+ return /* @__PURE__ */jsx(Tooltip, {
888
+ portal: true,
889
+ content: /* @__PURE__ */jsx(Box, {
890
+ padding: 2,
891
+ children: /* @__PURE__ */jsx(Text, {
892
+ size: 1,
893
+ children: document ? /* @__PURE__ */jsxs(Fragment, {
894
+ children: ["Published ", updatedAt && /* @__PURE__ */jsx(TimeAgo, {
895
+ time: updatedAt
896
+ })]
897
+ }) : /* @__PURE__ */jsx(Fragment, {
898
+ children: "Not published"
899
+ })
900
+ })
901
+ }),
902
+ children: /* @__PURE__ */jsx(TextWithTone, {
903
+ tone: "positive",
904
+ dimmed: !document,
905
+ muted: !document,
906
+ size: 1,
907
+ children: /* @__PURE__ */jsx(PublishIcon, {})
908
+ })
1353
909
  });
910
+ }
911
+ function ValidationStatus(props) {
1354
912
  const {
1355
- data,
1356
- loading,
1357
- error
1358
- } = useWorkflowMetadata(id, states);
1359
- if (error) {
1360
- console.error(error);
1361
- }
1362
- if (!(data == null ? void 0 : data.metadata)) {
913
+ validation = []
914
+ } = props;
915
+ if (!validation.length) {
1363
916
  return null;
1364
917
  }
1365
- return {
1366
- icon: UsersIcon,
1367
- type: "dialog",
1368
- disabled: !data || loading || error,
1369
- label: "Assign",
1370
- title: data ? null : "Document is not in Workflow",
1371
- dialog: isDialogOpen && {
1372
- type: "popover",
1373
- onClose: () => {
1374
- setDialogOpen(false);
1375
- },
1376
- content: /* @__PURE__ */jsx(UserAssignment, {
1377
- userList,
1378
- assignees: (_b = (_a = data.metadata) == null ? void 0 : _a.assignees) != null ? _b : [],
1379
- documentId: id
918
+ const hasError = validation.some(item => item.level === "error");
919
+ return /* @__PURE__ */jsx(Tooltip, {
920
+ portal: true,
921
+ content: /* @__PURE__ */jsx(Box, {
922
+ padding: 2,
923
+ children: /* @__PURE__ */jsx(Text, {
924
+ size: 1,
925
+ children: validation.length === 1 ? "1 validation issue" : "".concat(validation.length, " validation issues")
1380
926
  })
1381
- },
1382
- onHandle: () => {
1383
- setDialogOpen(true);
1384
- }
1385
- };
1386
- }
1387
- function BeginWorkflow(props, states) {
1388
- const {
1389
- id,
1390
- draft
1391
- } = props;
1392
- const {
1393
- data,
1394
- loading,
1395
- error
1396
- } = useWorkflowMetadata(id, states);
1397
- const client = useClient({
1398
- apiVersion: API_VERSION
927
+ }),
928
+ children: /* @__PURE__ */jsx(TextWithTone, {
929
+ tone: hasError ? "critical" : "caution",
930
+ size: 1,
931
+ children: hasError ? /* @__PURE__ */jsx(ErrorOutlineIcon, {}) : /* @__PURE__ */jsx(WarningOutlineIcon, {})
932
+ })
1399
933
  });
1400
- const toast = useToast();
1401
- const [beginning, setBeginning] = useState(false);
1402
- const [complete, setComplete] = useState(false);
1403
- if (error) {
1404
- console.error(error);
1405
- }
1406
- const handle = useCallback(async () => {
1407
- setBeginning(true);
1408
- const lowestOrderFirstState = await client.fetch("*[_type == \"workflow.metadata\" && state == $state]|order(orderRank)[0].orderRank", {
1409
- state: states[0].id
1410
- });
1411
- client.createIfNotExists({
1412
- _id: "workflow-metadata.".concat(id),
1413
- _type: "workflow.metadata",
1414
- documentId: id,
1415
- state: states[0].id,
1416
- orderRank: lowestOrderFirstState ? LexoRank.parse(lowestOrderFirstState).genNext().toString() : LexoRank.min().toString()
1417
- },
1418
- // Faster!
1419
- {
1420
- visibility: "async"
1421
- }).then(() => {
1422
- toast.push({
1423
- status: "success",
1424
- title: "Workflow started",
1425
- description: "Document is now \"".concat(states[0].title, "\"")
1426
- });
1427
- setBeginning(false);
1428
- setComplete(true);
1429
- });
1430
- }, [id, states, client, toast]);
1431
- if (!draft || complete || data.metadata) {
1432
- return null;
1433
- }
1434
- return {
1435
- icon: SplitVerticalIcon,
1436
- type: "dialog",
1437
- disabled: (data == null ? void 0 : data.metadata) || loading || error || beginning || complete,
1438
- label: beginning ? "Beginning..." : "Begin Workflow",
1439
- onHandle: () => {
1440
- handle();
1441
- }
1442
- };
1443
934
  }
1444
- function CompleteWorkflow(props, states) {
935
+ function DocumentCard(props) {
1445
936
  var _a;
1446
937
  const {
1447
- id
938
+ isDragDisabled,
939
+ userRoleCanDrop,
940
+ isDragging,
941
+ item,
942
+ states,
943
+ toggleInvalidDocumentId,
944
+ userList
1448
945
  } = props;
1449
946
  const {
1450
- data,
1451
- loading,
1452
- error
1453
- } = useWorkflowMetadata(id, states);
1454
- const client = useClient({
1455
- apiVersion: API_VERSION
947
+ assignees = [],
948
+ documentId
949
+ } = (_a = item._metadata) != null ? _a : {};
950
+ const schema = useSchema();
951
+ const isDarkMode = useTheme().sanity.color.dark;
952
+ const defaultCardTone = isDarkMode ? "transparent" : "default";
953
+ const {
954
+ validation = [],
955
+ isValidating
956
+ } = useValidationStatus(documentId != null ? documentId : "", item._type);
957
+ const cardTone = useMemo(() => {
958
+ let tone = defaultCardTone;
959
+ if (!userRoleCanDrop) return isDarkMode ? "default" : "transparent";
960
+ if (!documentId) return tone;
961
+ if (isDragging) tone = "positive";
962
+ if (!isValidating && validation.length > 0) {
963
+ if (validation.some(v => v.level === "error")) {
964
+ tone = "critical";
965
+ } else {
966
+ tone = "caution";
967
+ }
968
+ }
969
+ return tone;
970
+ }, [isDarkMode, userRoleCanDrop, defaultCardTone, documentId, isDragging, validation, isValidating]);
971
+ useEffect(() => {
972
+ if (!isValidating && validation.length > 0) {
973
+ if (validation.some(v => v.level === "error")) {
974
+ toggleInvalidDocumentId(documentId, "ADD");
975
+ } else {
976
+ toggleInvalidDocumentId(documentId, "REMOVE");
977
+ }
978
+ } else {
979
+ toggleInvalidDocumentId(documentId, "REMOVE");
980
+ }
981
+ }, [documentId, isValidating, toggleInvalidDocumentId, validation]);
982
+ const hasError = useMemo(() => isValidating ? false : validation.some(v => v.level === "error"), [isValidating, validation]);
983
+ const isLastState = useMemo(() => {
984
+ var _a2;
985
+ return states[states.length - 1].id === ((_a2 = item._metadata) == null ? void 0 : _a2.state);
986
+ }, [states, item._metadata.state]);
987
+ return /* @__PURE__ */jsx(Box, {
988
+ paddingBottom: 3,
989
+ paddingX: 3,
990
+ children: /* @__PURE__ */jsx(Card, {
991
+ radius: 2,
992
+ shadow: isDragging ? 3 : 1,
993
+ tone: cardTone,
994
+ children: /* @__PURE__ */jsxs(Stack, {
995
+ children: [/* @__PURE__ */jsx(Card, {
996
+ borderBottom: true,
997
+ radius: 2,
998
+ padding: 3,
999
+ paddingLeft: 2,
1000
+ tone: cardTone,
1001
+ style: {
1002
+ pointerEvents: "none"
1003
+ },
1004
+ children: /* @__PURE__ */jsxs(Flex, {
1005
+ align: "center",
1006
+ justify: "space-between",
1007
+ gap: 1,
1008
+ children: [/* @__PURE__ */jsx(Box, {
1009
+ flex: 1,
1010
+ children: /* @__PURE__ */jsx(Preview, {
1011
+ layout: "default",
1012
+ value: item,
1013
+ schemaType: schema.get(item._type)
1014
+ })
1015
+ }), /* @__PURE__ */jsx(Box, {
1016
+ style: {
1017
+ flexShrink: 0
1018
+ },
1019
+ children: hasError || isDragDisabled ? null : /* @__PURE__ */jsx(DragHandleIcon, {})
1020
+ })]
1021
+ })
1022
+ }), /* @__PURE__ */jsx(Card, {
1023
+ padding: 2,
1024
+ radius: 2,
1025
+ tone: "inherit",
1026
+ children: /* @__PURE__ */jsxs(Flex, {
1027
+ align: "center",
1028
+ justify: "space-between",
1029
+ gap: 3,
1030
+ children: [/* @__PURE__ */jsx(Box, {
1031
+ flex: 1,
1032
+ children: documentId && /* @__PURE__ */jsx(UserDisplay, {
1033
+ userList,
1034
+ assignees,
1035
+ documentId,
1036
+ disabled: !userRoleCanDrop
1037
+ })
1038
+ }), validation.length > 0 ? /* @__PURE__ */jsx(ValidationStatus, {
1039
+ validation
1040
+ }) : null, /* @__PURE__ */jsx(DraftStatus, {
1041
+ document: item
1042
+ }), /* @__PURE__ */jsx(PublishedStatus, {
1043
+ document: item
1044
+ }), /* @__PURE__ */jsx(EditButton, {
1045
+ id: item._id,
1046
+ type: item._type,
1047
+ disabled: !userRoleCanDrop
1048
+ }), isLastState ? /* @__PURE__ */jsx(CompleteButton, {
1049
+ documentId,
1050
+ disabled: !userRoleCanDrop
1051
+ }) : null]
1052
+ })
1053
+ })]
1054
+ })
1055
+ })
1456
1056
  });
1457
- if (error) {
1458
- console.error(error);
1459
- }
1460
- const handle = useCallback(() => {
1461
- client.delete("workflow-metadata.".concat(id));
1462
- }, [id, client]);
1463
- const isLastState = ((_a = data == null ? void 0 : data.state) == null ? void 0 : _a.id) === states[states.length - 1].id;
1464
- if (!data.metadata) {
1465
- return null;
1466
- }
1467
- return {
1468
- icon: CheckmarkIcon,
1469
- type: "dialog",
1470
- disabled: loading || error || !isLastState,
1471
- label: "Complete Workflow",
1472
- title: isLastState ? "Removes the document from the Workflow process" : "Cannot remove from workflow until in the last state",
1473
- onHandle: () => {
1474
- handle();
1475
- },
1476
- color: "positive"
1477
- };
1478
1057
  }
1479
- function AssigneesBadge(states, documentId, currentUser) {
1480
- var _a;
1481
- const {
1482
- data,
1483
- loading,
1484
- error
1485
- } = useWorkflowMetadata(documentId, states);
1058
+ function DocumentList(props) {
1486
1059
  const {
1487
- metadata
1488
- } = data;
1489
- const userList = useProjectUsers({
1490
- apiVersion: API_VERSION
1060
+ data = [],
1061
+ invalidDocumentIds,
1062
+ selectedSchemaTypes,
1063
+ selectedUserIds,
1064
+ state,
1065
+ states,
1066
+ toggleInvalidDocumentId,
1067
+ user,
1068
+ userList,
1069
+ userRoleCanDrop
1070
+ } = props;
1071
+ const dataFiltered = useMemo(() => {
1072
+ return data.length ? filterItemsAndSort(data, state.id, selectedUserIds, selectedSchemaTypes) : [];
1073
+ }, [data, selectedSchemaTypes, selectedUserIds, state.id]);
1074
+ const parentRef = useRef(null);
1075
+ const rowVirtualizer = useVirtualizer({
1076
+ count: data.length,
1077
+ getScrollElement: () => parentRef.current,
1078
+ getItemKey: index => {
1079
+ var _a, _b, _c;
1080
+ return (_c = (_b = (_a = dataFiltered[index]) == null ? void 0 : _a._metadata) == null ? void 0 : _b.documentId) != null ? _c : index;
1081
+ },
1082
+ estimateSize: () => 113,
1083
+ overscan: 5
1491
1084
  });
1492
- if (loading || error || !metadata) {
1493
- if (error) {
1494
- console.error(error);
1495
- }
1085
+ if (!data.length) {
1496
1086
  return null;
1497
1087
  }
1498
- if (!((_a = metadata == null ? void 0 : metadata.assignees) == null ? void 0 : _a.length)) {
1499
- return {
1500
- label: "Unassigned"
1501
- };
1502
- }
1088
+ return /* @__PURE__ */jsx("div", {
1089
+ ref: parentRef,
1090
+ style: {
1091
+ height: "100%",
1092
+ overflow: "auto",
1093
+ paddingTop: 1,
1094
+ // Smooths scrollbar behaviour
1095
+ overflowAnchor: "none",
1096
+ scrollBehavior: "auto"
1097
+ },
1098
+ children: rowVirtualizer.getVirtualItems().map(virtualItem => {
1099
+ var _a;
1100
+ const item = dataFiltered[virtualItem.index];
1101
+ const {
1102
+ documentId,
1103
+ assignees
1104
+ } = (_a = item == null ? void 0 : item._metadata) != null ? _a : {};
1105
+ if (!documentId) {
1106
+ return null;
1107
+ }
1108
+ const isInvalid = invalidDocumentIds.includes(documentId);
1109
+ const meInAssignees = (user == null ? void 0 : user.id) ? assignees == null ? void 0 : assignees.includes(user.id) : false;
1110
+ const isDragDisabled = !userRoleCanDrop || isInvalid || !(state.requireAssignment ? state.requireAssignment && meInAssignees : true);
1111
+ return /* @__PURE__ */jsx(Draggable, {
1112
+ draggableId: documentId,
1113
+ index: virtualItem.index,
1114
+ isDragDisabled,
1115
+ children: (draggableProvided, draggableSnapshot) => /* @__PURE__ */jsx("div", {
1116
+ ref: draggableProvided.innerRef,
1117
+ ...draggableProvided.draggableProps,
1118
+ ...draggableProvided.dragHandleProps,
1119
+ children: /* @__PURE__ */jsx(DocumentCard, {
1120
+ userRoleCanDrop,
1121
+ isDragDisabled,
1122
+ isDragging: draggableSnapshot.isDragging,
1123
+ item,
1124
+ toggleInvalidDocumentId,
1125
+ userList,
1126
+ states
1127
+ })
1128
+ })
1129
+ }, documentId);
1130
+ })
1131
+ });
1132
+ }
1133
+ function Filters(props) {
1503
1134
  const {
1504
- assignees
1505
- } = metadata != null ? metadata : [];
1506
- const hasMe = currentUser ? assignees.some(assignee => assignee === currentUser.id) : false;
1507
- const assigneesCount = hasMe ? assignees.length - 1 : assignees.length;
1508
- const assigneeUsers = userList.filter(user => assignees.includes(user.id));
1509
- const title = assigneeUsers.map(user => user.displayName).join(", ");
1510
- let label;
1511
- if (hasMe && assigneesCount === 0) {
1512
- label = "Assigned to Me";
1513
- } else if (hasMe && assigneesCount > 0) {
1514
- label = "Me and ".concat(assigneesCount, " ").concat(assigneesCount === 1 ? "other" : "others");
1515
- } else {
1516
- label = "".concat(assigneesCount, " assigned");
1517
- }
1518
- return {
1519
- label,
1520
- title,
1521
- color: "primary"
1522
- };
1523
- }
1524
- function StateBadge(states, documentId) {
1525
- const {
1526
- data,
1527
- loading,
1528
- error
1529
- } = useWorkflowMetadata(documentId, states);
1530
- const {
1531
- state
1532
- } = data;
1533
- if (loading || error) {
1534
- if (error) {
1535
- console.error(error);
1135
+ uniqueAssignedUsers = [],
1136
+ selectedUserIds,
1137
+ schemaTypes,
1138
+ selectedSchemaTypes,
1139
+ toggleSelectedUser,
1140
+ resetSelectedUsers,
1141
+ toggleSelectedSchemaType
1142
+ } = props;
1143
+ const currentUser = useCurrentUser();
1144
+ const schema = useSchema();
1145
+ const onAdd = useCallback(id => {
1146
+ if (!selectedUserIds.includes(id)) {
1147
+ toggleSelectedUser(id);
1536
1148
  }
1149
+ }, [selectedUserIds, toggleSelectedUser]);
1150
+ const onRemove = useCallback(id => {
1151
+ if (selectedUserIds.includes(id)) {
1152
+ toggleSelectedUser(id);
1153
+ }
1154
+ }, [selectedUserIds, toggleSelectedUser]);
1155
+ const onClear = useCallback(() => {
1156
+ resetSelectedUsers();
1157
+ }, [resetSelectedUsers]);
1158
+ if (uniqueAssignedUsers.length === 0 && schemaTypes.length < 2) {
1537
1159
  return null;
1538
1160
  }
1539
- if (!state) {
1540
- return null;
1541
- }
1542
- return {
1543
- label: state.title,
1544
- // title: state.title,
1545
- color: state == null ? void 0 : state.color
1546
- };
1161
+ const meInUniqueAssignees = (currentUser == null ? void 0 : currentUser.id) && uniqueAssignedUsers.find(u => u.id === currentUser.id);
1162
+ const uniqueAssigneesNotMe = uniqueAssignedUsers.filter(u => u.id !== (currentUser == null ? void 0 : currentUser.id));
1163
+ return /* @__PURE__ */jsx(Card, {
1164
+ tone: "primary",
1165
+ padding: 2,
1166
+ borderBottom: true,
1167
+ style: {
1168
+ overflowX: "hidden"
1169
+ },
1170
+ children: /* @__PURE__ */jsxs(Flex, {
1171
+ align: "center",
1172
+ children: [/* @__PURE__ */jsx(Flex, {
1173
+ align: "center",
1174
+ gap: 1,
1175
+ flex: 1,
1176
+ children: uniqueAssignedUsers.length > 5 ? /* @__PURE__ */jsx(Card, {
1177
+ tone: "default",
1178
+ children: /* @__PURE__ */jsx(MenuButton, {
1179
+ button: /* @__PURE__ */jsx(Button, {
1180
+ text: "Filter Assignees",
1181
+ tone: "primary",
1182
+ icon: UserIcon
1183
+ }),
1184
+ id: "user-filters",
1185
+ menu: /* @__PURE__ */jsx(Menu, {
1186
+ children: /* @__PURE__ */jsx(UserSelectMenu, {
1187
+ value: selectedUserIds,
1188
+ userList: uniqueAssignedUsers,
1189
+ onAdd,
1190
+ onRemove,
1191
+ onClear,
1192
+ labels: {
1193
+ addMe: "Filter mine",
1194
+ removeMe: "Clear mine",
1195
+ clear: "Clear filters"
1196
+ }
1197
+ })
1198
+ }),
1199
+ popover: {
1200
+ portal: true
1201
+ }
1202
+ })
1203
+ }) : /* @__PURE__ */jsxs(Fragment, {
1204
+ children: [meInUniqueAssignees ? /* @__PURE__ */jsxs(Fragment, {
1205
+ children: [/* @__PURE__ */jsx(Button, {
1206
+ padding: 0,
1207
+ mode: selectedUserIds.includes(currentUser.id) ? "default" : "bleed",
1208
+ onClick: () => toggleSelectedUser(currentUser.id),
1209
+ children: /* @__PURE__ */jsx(Flex, {
1210
+ padding: 1,
1211
+ align: "center",
1212
+ justify: "center",
1213
+ children: /* @__PURE__ */jsx(UserAvatar, {
1214
+ user: currentUser.id,
1215
+ size: 1,
1216
+ withTooltip: true
1217
+ })
1218
+ })
1219
+ }), /* @__PURE__ */jsx(Card, {
1220
+ borderRight: true,
1221
+ style: {
1222
+ height: 30
1223
+ },
1224
+ tone: "inherit"
1225
+ })]
1226
+ }) : null, uniqueAssigneesNotMe.map(user => /* @__PURE__ */jsx(Button, {
1227
+ padding: 0,
1228
+ mode: selectedUserIds.includes(user.id) ? "default" : "bleed",
1229
+ onClick: () => toggleSelectedUser(user.id),
1230
+ children: /* @__PURE__ */jsx(Flex, {
1231
+ padding: 1,
1232
+ align: "center",
1233
+ justify: "center",
1234
+ children: /* @__PURE__ */jsx(UserAvatar, {
1235
+ user,
1236
+ size: 1,
1237
+ withTooltip: true
1238
+ })
1239
+ })
1240
+ }, user.id)), selectedUserIds.length > 0 ? /* @__PURE__ */jsx(Button, {
1241
+ text: "Clear",
1242
+ onClick: resetSelectedUsers,
1243
+ mode: "ghost",
1244
+ icon: ResetIcon
1245
+ }) : null]
1246
+ })
1247
+ }), schemaTypes.length > 0 ? /* @__PURE__ */jsx(Flex, {
1248
+ align: "center",
1249
+ gap: 1,
1250
+ children: schemaTypes.map(typeName => {
1251
+ var _a, _b;
1252
+ const schemaType = schema.get(typeName);
1253
+ if (!schemaType) {
1254
+ return null;
1255
+ }
1256
+ return /* @__PURE__ */jsx(Button, {
1257
+ text: (_a = schemaType == null ? void 0 : schemaType.title) != null ? _a : typeName,
1258
+ icon: (_b = schemaType == null ? void 0 : schemaType.icon) != null ? _b : void 0,
1259
+ mode: selectedSchemaTypes.includes(typeName) ? "default" : "ghost",
1260
+ onClick: () => toggleSelectedSchemaType(typeName)
1261
+ }, typeName);
1262
+ })
1263
+ }) : null]
1264
+ })
1265
+ });
1547
1266
  }
1548
- function UpdateWorkflow(props, allStates, actionState) {
1549
- var _a, _b, _c, _d;
1267
+ function Status(props) {
1550
1268
  const {
1551
- id,
1552
- type
1269
+ text,
1270
+ icon
1553
1271
  } = props;
1272
+ const Icon = icon;
1273
+ return /* @__PURE__ */jsx(Tooltip, {
1274
+ portal: true,
1275
+ content: /* @__PURE__ */jsx(Box, {
1276
+ padding: 2,
1277
+ children: /* @__PURE__ */jsx(Text, {
1278
+ size: 1,
1279
+ children: text
1280
+ })
1281
+ }),
1282
+ children: /* @__PURE__ */jsx(Text, {
1283
+ size: 1,
1284
+ children: /* @__PURE__ */jsx(Icon, {})
1285
+ })
1286
+ });
1287
+ }
1288
+ const StyledStickyCard = styled(Card)(() => css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n position: sticky;\n top: 0;\n z-index: 1;\n "]))));
1289
+ function StateTitle(props) {
1554
1290
  const {
1555
- validation,
1556
- isValidating
1557
- } = useValidationStatus(id, type);
1558
- const hasValidationErrors = !isValidating && (validation == null ? void 0 : validation.length) > 0 && validation.find(v => v.level === "error");
1559
- const user = useCurrentUser();
1291
+ state,
1292
+ requireAssignment,
1293
+ userRoleCanDrop,
1294
+ isDropDisabled,
1295
+ draggingFrom
1296
+ } = props;
1297
+ let tone = "default";
1298
+ const isSource = draggingFrom === state.id;
1299
+ if (draggingFrom) {
1300
+ tone = isDropDisabled || isSource ? "default" : "positive";
1301
+ }
1302
+ return /* @__PURE__ */jsx(StyledStickyCard, {
1303
+ paddingY: 4,
1304
+ padding: 3,
1305
+ tone: "inherit",
1306
+ children: /* @__PURE__ */jsxs(Flex, {
1307
+ gap: 3,
1308
+ align: "center",
1309
+ children: [/* @__PURE__ */jsx(Badge, {
1310
+ mode: draggingFrom && !isDropDisabled || isSource ? "default" : "outline",
1311
+ tone,
1312
+ muted: !userRoleCanDrop || isDropDisabled,
1313
+ children: state.title
1314
+ }), userRoleCanDrop ? null : /* @__PURE__ */jsx(Status, {
1315
+ text: "You do not have permissions to move documents to this State",
1316
+ icon: InfoOutlineIcon
1317
+ }), requireAssignment ? /* @__PURE__ */jsx(Status, {
1318
+ text: "You must be assigned to the document to move documents to this State",
1319
+ icon: UserIcon
1320
+ }) : null]
1321
+ })
1322
+ });
1323
+ }
1324
+ const StyledFloatingCard = styled(Card)(() => css(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["\n position: fixed;\n bottom: 0;\n left: 0;\n z-index: 1000;\n "]))));
1325
+ function FloatingCard(_ref3) {
1326
+ let {
1327
+ children
1328
+ } = _ref3;
1329
+ const childrenHaveValues = Array.isArray(children) ? children.some(Boolean) : Boolean(children);
1330
+ return /* @__PURE__ */jsx(AnimatePresence, {
1331
+ children: childrenHaveValues ? /* @__PURE__ */jsx(motion.div, {
1332
+ initial: {
1333
+ opacity: 0
1334
+ },
1335
+ animate: {
1336
+ opacity: 1
1337
+ },
1338
+ exit: {
1339
+ opacity: 0
1340
+ },
1341
+ children: /* @__PURE__ */jsx(StyledFloatingCard, {
1342
+ shadow: 3,
1343
+ padding: 3,
1344
+ margin: 3,
1345
+ radius: 3,
1346
+ children: /* @__PURE__ */jsx(Grid, {
1347
+ gap: 2,
1348
+ children
1349
+ })
1350
+ })
1351
+ }, "floater") : null
1352
+ });
1353
+ }
1354
+ function Validators(_ref4) {
1355
+ let {
1356
+ data,
1357
+ userList,
1358
+ states
1359
+ } = _ref4;
1560
1360
  const client = useClient({
1561
1361
  apiVersion: API_VERSION
1562
1362
  });
1563
- const toast = useToast();
1564
- const currentUser = useCurrentUser();
1363
+ const toast = useToast();
1364
+ const documentsWithoutValidMetadataIds = (data == null ? void 0 : data.length) ? data.reduce((acc, cur) => {
1365
+ var _a;
1366
+ const {
1367
+ documentId,
1368
+ state
1369
+ } = (_a = cur._metadata) != null ? _a : {};
1370
+ const stateExists = states.find(s => s.id === state);
1371
+ return !stateExists && documentId ? [...acc, documentId] : acc;
1372
+ }, []) : [];
1373
+ const documentsWithInvalidUserIds = (data == null ? void 0 : data.length) ? data.reduce((acc, cur) => {
1374
+ var _a;
1375
+ const {
1376
+ documentId,
1377
+ assignees
1378
+ } = (_a = cur._metadata) != null ? _a : {};
1379
+ const allAssigneesExist = (assignees == null ? void 0 : assignees.length) ? assignees == null ? void 0 : assignees.every(a => userList.find(u => u.id === a)) : true;
1380
+ return !allAssigneesExist && documentId ? [...acc, documentId] : acc;
1381
+ }, []) : [];
1382
+ const documentsWithoutOrderIds = (data == null ? void 0 : data.length) ? data.reduce((acc, cur) => {
1383
+ var _a;
1384
+ const {
1385
+ documentId,
1386
+ orderRank
1387
+ } = (_a = cur._metadata) != null ? _a : {};
1388
+ return !orderRank && documentId ? [...acc, documentId] : acc;
1389
+ }, []) : [];
1390
+ const correctDocuments = React.useCallback(async ids => {
1391
+ toast.push({
1392
+ title: "Correcting...",
1393
+ status: "info"
1394
+ });
1395
+ const tx = ids.reduce((item, documentId) => {
1396
+ return item.patch("workflow-metadata.".concat(documentId), {
1397
+ set: {
1398
+ state: states[0].id
1399
+ }
1400
+ });
1401
+ }, client.transaction());
1402
+ await tx.commit();
1403
+ toast.push({
1404
+ title: "Corrected ".concat(ids.length === 1 ? "1 Document" : "".concat(ids.length, " Documents")),
1405
+ status: "success"
1406
+ });
1407
+ }, [client, states, toast]);
1408
+ const removeUsersFromDocuments = React.useCallback(async ids => {
1409
+ toast.push({
1410
+ title: "Removing users...",
1411
+ status: "info"
1412
+ });
1413
+ const tx = ids.reduce((item, documentId) => {
1414
+ var _a, _b;
1415
+ const {
1416
+ assignees
1417
+ } = (_b = (_a = data.find(d => d._id === documentId)) == null ? void 0 : _a._metadata) != null ? _b : {};
1418
+ const validAssignees = (assignees == null ? void 0 : assignees.length) ?
1419
+ // eslint-disable-next-line max-nested-callbacks
1420
+ assignees.filter(a => {
1421
+ var _a2;
1422
+ return (_a2 = userList.find(u => u.id === a)) == null ? void 0 : _a2.id;
1423
+ }) : [];
1424
+ return item.patch("workflow-metadata.".concat(documentId), {
1425
+ set: {
1426
+ assignees: validAssignees
1427
+ }
1428
+ });
1429
+ }, client.transaction());
1430
+ await tx.commit();
1431
+ toast.push({
1432
+ title: "Corrected ".concat(ids.length === 1 ? "1 Document" : "".concat(ids.length, " Documents")),
1433
+ status: "success"
1434
+ });
1435
+ }, [client, data, toast, userList]);
1436
+ const addOrderToDocuments = React.useCallback(async ids => {
1437
+ var _a, _b;
1438
+ toast.push({
1439
+ title: "Adding ordering...",
1440
+ status: "info"
1441
+ });
1442
+ const firstOrder = (_b = (_a = data[0]) == null ? void 0 : _a._metadata) == null ? void 0 : _b.orderRank;
1443
+ let newLexo = firstOrder && data.length !== ids.length ? LexoRank.parse(firstOrder) : LexoRank.min();
1444
+ const tx = client.transaction();
1445
+ for (let index = 0; index < ids.length; index += 1) {
1446
+ newLexo = newLexo.genNext().genNext();
1447
+ tx.patch("workflow-metadata.".concat(ids[index]), {
1448
+ set: {
1449
+ orderRank: newLexo.toString()
1450
+ }
1451
+ });
1452
+ }
1453
+ await tx.commit();
1454
+ toast.push({
1455
+ title: "Added order to ".concat(ids.length === 1 ? "1 Document" : "".concat(ids.length, " Documents")),
1456
+ status: "success"
1457
+ });
1458
+ }, [data, client, toast]);
1459
+ const orphanedMetadataDocumentIds = React.useMemo(() => {
1460
+ return data.length ? data.filter(doc => !(doc == null ? void 0 : doc._id)).map(doc => doc._metadata.documentId) : [];
1461
+ }, [data]);
1462
+ const handleOrphans = React.useCallback(() => {
1463
+ toast.push({
1464
+ title: "Removing orphaned metadata...",
1465
+ status: "info"
1466
+ });
1467
+ const tx = client.transaction();
1468
+ orphanedMetadataDocumentIds.forEach(id => {
1469
+ tx.delete("workflow-metadata.".concat(id));
1470
+ });
1471
+ tx.commit();
1472
+ toast.push({
1473
+ title: "Removed ".concat(orphanedMetadataDocumentIds.length, " orphaned metadata documents"),
1474
+ status: "success"
1475
+ });
1476
+ }, [client, orphanedMetadataDocumentIds, toast]);
1477
+ return /* @__PURE__ */jsxs(FloatingCard, {
1478
+ children: [documentsWithoutValidMetadataIds.length > 0 ? /* @__PURE__ */jsx(Button, {
1479
+ tone: "caution",
1480
+ onClick: () => correctDocuments(documentsWithoutValidMetadataIds),
1481
+ text: documentsWithoutValidMetadataIds.length === 1 ? "Correct 1 Document State" : "Correct ".concat(documentsWithoutValidMetadataIds.length, " Document States")
1482
+ }) : null, documentsWithInvalidUserIds.length > 0 ? /* @__PURE__ */jsx(Button, {
1483
+ tone: "caution",
1484
+ onClick: () => removeUsersFromDocuments(documentsWithInvalidUserIds),
1485
+ text: documentsWithInvalidUserIds.length === 1 ? "Remove Invalid Users from 1 Document" : "Remove Invalid Users from ".concat(documentsWithInvalidUserIds.length, " Documents")
1486
+ }) : null, documentsWithoutOrderIds.length > 0 ? /* @__PURE__ */jsx(Button, {
1487
+ tone: "caution",
1488
+ onClick: () => addOrderToDocuments(documentsWithoutOrderIds),
1489
+ text: documentsWithoutOrderIds.length === 1 ? "Set Order for 1 Document" : "Set Order for ".concat(documentsWithoutOrderIds.length, " Documents")
1490
+ }) : null, orphanedMetadataDocumentIds.length > 0 ? /* @__PURE__ */jsx(Button, {
1491
+ text: "Cleanup orphaned metadata",
1492
+ onClick: handleOrphans,
1493
+ tone: "caution"
1494
+ }) : null]
1495
+ });
1496
+ }
1497
+ function WorkflowTool(props) {
1498
+ var _a, _b, _c;
1499
+ const {
1500
+ schemaTypes = [],
1501
+ states = []
1502
+ } = (_b = (_a = props == null ? void 0 : props.tool) == null ? void 0 : _a.options) != null ? _b : {};
1503
+ const isDarkMode = useTheme().sanity.color.dark;
1504
+ const defaultCardTone = isDarkMode ? "default" : "transparent";
1505
+ const userList = useProjectUsers({
1506
+ apiVersion: API_VERSION
1507
+ });
1508
+ const user = useCurrentUser();
1509
+ const userRoleNames = ((_c = user == null ? void 0 : user.roles) == null ? void 0 : _c.length) ? user == null ? void 0 : user.roles.map(r => r.name) : [];
1510
+ const {
1511
+ workflowData,
1512
+ operations
1513
+ } = useWorkflowDocuments(schemaTypes);
1565
1514
  const {
1566
1515
  data,
1567
1516
  loading,
1568
1517
  error
1569
- } = useWorkflowMetadata(id, allStates);
1570
- const {
1571
- state: currentState
1572
- } = data;
1518
+ } = workflowData;
1573
1519
  const {
1574
- assignees = []
1575
- } = (_a = data == null ? void 0 : data.metadata) != null ? _a : {};
1576
- if (error) {
1577
- console.error(error);
1578
- }
1579
- const onHandle = (documentId, newState) => {
1580
- client.patch("workflow-metadata.".concat(documentId)).set({
1581
- state: newState.id
1582
- }).commit().then(() => {
1583
- props.onComplete();
1584
- toast.push({
1585
- status: "success",
1586
- title: "Document state now \"".concat(newState.title, "\"")
1587
- });
1588
- }).catch(err => {
1589
- props.onComplete();
1590
- console.error(err);
1591
- toast.push({
1592
- status: "error",
1593
- title: "Document state update failed"
1594
- });
1520
+ move
1521
+ } = operations;
1522
+ const [undroppableStates, setUndroppableStates] = React.useState([]);
1523
+ const [draggingFrom, setDraggingFrom] = React.useState("");
1524
+ const handleDragStart = React.useCallback(start => {
1525
+ var _a2, _b2;
1526
+ const {
1527
+ draggableId,
1528
+ source
1529
+ } = start;
1530
+ const {
1531
+ droppableId: currentStateId
1532
+ } = source;
1533
+ setDraggingFrom(currentStateId);
1534
+ const document = data.find(item => {
1535
+ var _a3;
1536
+ return ((_a3 = item._metadata) == null ? void 0 : _a3.documentId) === draggableId;
1537
+ });
1538
+ const state = states.find(s => s.id === currentStateId);
1539
+ if (!document || !state) return;
1540
+ const undroppableStateIds = [];
1541
+ const statesThatRequireAssignmentIds = states.filter(s => s.requireAssignment).map(s => s.id);
1542
+ if (statesThatRequireAssignmentIds.length) {
1543
+ const documentAssignees = (_b2 = (_a2 = document._metadata) == null ? void 0 : _a2.assignees) != null ? _b2 : [];
1544
+ const userIsAssignedToDocument = (user == null ? void 0 : user.id) ? documentAssignees.includes(user.id) : false;
1545
+ if (!userIsAssignedToDocument) {
1546
+ undroppableStateIds.push(...statesThatRequireAssignmentIds);
1547
+ }
1548
+ }
1549
+ const statesThatCannotBeTransitionedToIds = state.transitions && state.transitions.length ? states.filter(s => {
1550
+ var _a3;
1551
+ return !((_a3 = state.transitions) == null ? void 0 : _a3.includes(s.id));
1552
+ }).map(s => s.id) : [];
1553
+ if (statesThatCannotBeTransitionedToIds.length) {
1554
+ undroppableStateIds.push(...statesThatCannotBeTransitionedToIds);
1555
+ }
1556
+ const undroppableExceptSelf = undroppableStateIds.filter(id => id !== currentStateId);
1557
+ if (undroppableExceptSelf.length) {
1558
+ setUndroppableStates(undroppableExceptSelf);
1559
+ }
1560
+ }, [data, states, user]);
1561
+ const handleDragEnd = React.useCallback(result => {
1562
+ var _a2, _b2, _c2, _d, _e, _f;
1563
+ setUndroppableStates([]);
1564
+ setDraggingFrom("");
1565
+ const {
1566
+ draggableId,
1567
+ source,
1568
+ destination
1569
+ } = result;
1570
+ if (
1571
+ // No destination?
1572
+ !destination ||
1573
+ // No change in position?
1574
+ destination.droppableId === source.droppableId && destination.index === source.index) {
1575
+ return;
1576
+ }
1577
+ const destinationStateItems = [...filterItemsAndSort(data, destination.droppableId, [], null)];
1578
+ let newOrder;
1579
+ if (!destinationStateItems.length) {
1580
+ newOrder = LexoRank.min().toString();
1581
+ } else if (destination.index === 0) {
1582
+ const firstItemOrderRank = (_b2 = (_a2 = [...destinationStateItems].shift()) == null ? void 0 : _a2._metadata) == null ? void 0 : _b2.orderRank;
1583
+ newOrder = firstItemOrderRank && typeof firstItemOrderRank === "string" ? LexoRank.parse(firstItemOrderRank).genPrev().toString() : LexoRank.min().toString();
1584
+ } else if (destination.index + 1 === destinationStateItems.length) {
1585
+ const lastItemOrderRank = (_d = (_c2 = [...destinationStateItems].pop()) == null ? void 0 : _c2._metadata) == null ? void 0 : _d.orderRank;
1586
+ newOrder = lastItemOrderRank && typeof lastItemOrderRank === "string" ? LexoRank.parse(lastItemOrderRank).genNext().toString() : LexoRank.min().toString();
1587
+ } else {
1588
+ const itemBefore = destinationStateItems[destination.index - 1];
1589
+ const itemBeforeRank = (_e = itemBefore == null ? void 0 : itemBefore._metadata) == null ? void 0 : _e.orderRank;
1590
+ const itemBeforeRankParsed = itemBefore._metadata.orderRank ? LexoRank.parse(itemBeforeRank) : LexoRank.min();
1591
+ const itemAfter = destinationStateItems[destination.index];
1592
+ const itemAfterRank = (_f = itemAfter == null ? void 0 : itemAfter._metadata) == null ? void 0 : _f.orderRank;
1593
+ const itemAfterRankParsed = itemAfter._metadata.orderRank ? LexoRank.parse(itemAfterRank) : LexoRank.max();
1594
+ newOrder = itemBeforeRankParsed.between(itemAfterRankParsed).toString();
1595
+ }
1596
+ move(draggableId, destination, states, newOrder);
1597
+ }, [data, move, states]);
1598
+ const uniqueAssignedUsers = React.useMemo(() => {
1599
+ const uniqueUserIds = data.reduce((acc, item) => {
1600
+ var _a2;
1601
+ const {
1602
+ assignees = []
1603
+ } = (_a2 = item._metadata) != null ? _a2 : {};
1604
+ const newAssignees = (assignees == null ? void 0 : assignees.length) ? assignees.filter(a => !acc.includes(a)) : [];
1605
+ return newAssignees.length ? [...acc, ...newAssignees] : acc;
1606
+ }, []);
1607
+ return userList.filter(u => uniqueUserIds.includes(u.id));
1608
+ }, [data, userList]);
1609
+ const [selectedUserIds, setSelectedUserIds] = React.useState(uniqueAssignedUsers.map(u => u.id));
1610
+ const toggleSelectedUser = React.useCallback(userId => {
1611
+ setSelectedUserIds(prev => prev.includes(userId) ? prev.filter(u => u !== userId) : [...prev, userId]);
1612
+ }, []);
1613
+ const resetSelectedUsers = React.useCallback(() => {
1614
+ setSelectedUserIds([]);
1615
+ }, []);
1616
+ const [selectedSchemaTypes, setSelectedSchemaTypes] = React.useState(schemaTypes);
1617
+ const toggleSelectedSchemaType = React.useCallback(schemaType => {
1618
+ setSelectedSchemaTypes(prev => prev.includes(schemaType) ? prev.filter(u => u !== schemaType) : [...prev, schemaType]);
1619
+ }, []);
1620
+ const [invalidDocumentIds, setInvalidDocumentIds] = React.useState([]);
1621
+ const toggleInvalidDocumentId = React.useCallback((docId, action) => {
1622
+ setInvalidDocumentIds(prev => action === "ADD" ? [...prev, docId] : prev.filter(id => id !== docId));
1623
+ }, []);
1624
+ if (!(states == null ? void 0 : states.length)) {
1625
+ return /* @__PURE__ */jsx(Container, {
1626
+ width: 1,
1627
+ padding: 5,
1628
+ children: /* @__PURE__ */jsx(Feedback, {
1629
+ tone: "caution",
1630
+ title: "Plugin options error",
1631
+ description: "No States defined in plugin config"
1632
+ })
1595
1633
  });
1596
- };
1597
- if (!data.metadata || currentState && currentState.id === actionState.id) {
1598
- return null;
1599
- }
1600
- const currentStateIndex = allStates.findIndex(s => s.id === (currentState == null ? void 0 : currentState.id));
1601
- const actionStateIndex = allStates.findIndex(s => s.id === actionState.id);
1602
- const direction = actionStateIndex > currentStateIndex ? "promote" : "demote";
1603
- const DirectionIcon = direction === "promote" ? ArrowRightIcon : ArrowLeftIcon;
1604
- const directionLabel = direction === "promote" ? "Promote" : "Demote";
1605
- let title = "".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
1606
- const userRoleCanUpdateState = ((_b = user == null ? void 0 : user.roles) == null ? void 0 : _b.length) && ((_c = actionState == null ? void 0 : actionState.roles) == null ? void 0 : _c.length) ?
1607
- // If the Action state is limited to specific roles
1608
- // check that the current user has one of those roles
1609
- arraysContainMatchingString(user.roles.map(r => r.name), actionState.roles) :
1610
- // No roles specified on the next state, so anyone can update
1611
- ((_d = actionState == null ? void 0 : actionState.roles) == null ? void 0 : _d.length) !== 0;
1612
- if (!userRoleCanUpdateState) {
1613
- title = "Your User role cannot ".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
1614
- }
1615
- const actionStateIsAValidTransition = (currentState == null ? void 0 : currentState.id) && currentState.transitions.length ?
1616
- // If the Current State limits transitions to specific States
1617
- // Check that the Action State is in Current State's transitions array
1618
- currentState.transitions.includes(actionState.id) :
1619
- // Otherwise this isn't a problem
1620
- true;
1621
- if (!actionStateIsAValidTransition) {
1622
- title = "You cannot ".concat(directionLabel, " State to \"").concat(actionState.title, "\" from \"").concat(currentState == null ? void 0 : currentState.title, "\"");
1623
- }
1624
- const userAssignmentCanUpdateState = actionState.requireAssignment ?
1625
- // If the Action State requires assigned users
1626
- // Check the current user ID is in the assignees array
1627
- currentUser && assignees.length && assignees.includes(currentUser.id) :
1628
- // Otherwise this isn't a problem
1629
- true;
1630
- if (!userAssignmentCanUpdateState) {
1631
- title = "You must be assigned to the document to ".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
1632
1634
  }
1633
- if (hasValidationErrors) {
1634
- title = "Document has validation errors, cannot ".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
1635
+ if (error && !data.length) {
1636
+ return /* @__PURE__ */jsx(Container, {
1637
+ width: 1,
1638
+ padding: 5,
1639
+ children: /* @__PURE__ */jsx(Feedback, {
1640
+ tone: "critical",
1641
+ title: "Error querying for Workflow documents"
1642
+ })
1643
+ });
1635
1644
  }
1636
- return {
1637
- icon: DirectionIcon,
1638
- disabled: loading || error || isValidating || hasValidationErrors || !currentState || !userRoleCanUpdateState || !actionStateIsAValidTransition || !userAssignmentCanUpdateState,
1639
- title,
1640
- label: actionState.title,
1641
- onHandle: () => onHandle(id, actionState)
1642
- };
1645
+ return /* @__PURE__ */jsxs(Flex, {
1646
+ direction: "column",
1647
+ height: "fill",
1648
+ overflow: "hidden",
1649
+ children: [/* @__PURE__ */jsx(Validators, {
1650
+ data,
1651
+ userList,
1652
+ states
1653
+ }), /* @__PURE__ */jsx(Filters, {
1654
+ uniqueAssignedUsers,
1655
+ selectedUserIds,
1656
+ toggleSelectedUser,
1657
+ resetSelectedUsers,
1658
+ schemaTypes,
1659
+ selectedSchemaTypes,
1660
+ toggleSelectedSchemaType
1661
+ }), /* @__PURE__ */jsx(DragDropContext, {
1662
+ onDragStart: handleDragStart,
1663
+ onDragEnd: handleDragEnd,
1664
+ children: /* @__PURE__ */jsx(Grid, {
1665
+ columns: states.length,
1666
+ height: "fill",
1667
+ children: states.map((state, stateIndex) => {
1668
+ var _a2, _b2;
1669
+ const userRoleCanDrop = ((_a2 = state == null ? void 0 : state.roles) == null ? void 0 : _a2.length) ? arraysContainMatchingString(state.roles, userRoleNames) : true;
1670
+ const isDropDisabled = !userRoleCanDrop || undroppableStates.includes(state.id);
1671
+ return /* @__PURE__ */jsx(Card, {
1672
+ borderLeft: stateIndex > 0,
1673
+ tone: defaultCardTone,
1674
+ children: /* @__PURE__ */jsxs(Flex, {
1675
+ direction: "column",
1676
+ height: "fill",
1677
+ children: [/* @__PURE__ */jsx(StateTitle, {
1678
+ state,
1679
+ requireAssignment: (_b2 = state.requireAssignment) != null ? _b2 : false,
1680
+ userRoleCanDrop,
1681
+ isDropDisabled,
1682
+ draggingFrom
1683
+ }), /* @__PURE__ */jsx(Box, {
1684
+ flex: 1,
1685
+ children: /* @__PURE__ */jsx(Droppable, {
1686
+ droppableId: state.id,
1687
+ isDropDisabled,
1688
+ mode: "virtual",
1689
+ renderClone: (provided, snapshot, rubric) => {
1690
+ const item = data.find(doc => {
1691
+ var _a3;
1692
+ return ((_a3 = doc == null ? void 0 : doc._metadata) == null ? void 0 : _a3.documentId) === rubric.draggableId;
1693
+ });
1694
+ return /* @__PURE__ */jsx("div", {
1695
+ ...provided.draggableProps,
1696
+ ...provided.dragHandleProps,
1697
+ ref: provided.innerRef,
1698
+ children: item ? /* @__PURE__ */jsx(DocumentCard, {
1699
+ isDragDisabled: false,
1700
+ userRoleCanDrop,
1701
+ isDragging: snapshot.isDragging,
1702
+ item,
1703
+ states,
1704
+ toggleInvalidDocumentId,
1705
+ userList
1706
+ }) : /* @__PURE__ */jsx(Feedback, {
1707
+ title: "Item not found",
1708
+ tone: "caution"
1709
+ })
1710
+ });
1711
+ },
1712
+ children: (provided, snapshot) => /* @__PURE__ */jsxs(Card, {
1713
+ ref: provided.innerRef,
1714
+ tone: snapshot.isDraggingOver ? "primary" : defaultCardTone,
1715
+ height: "fill",
1716
+ paddingTop: 1,
1717
+ children: [loading ? /* @__PURE__ */jsx(Flex, {
1718
+ padding: 5,
1719
+ align: "center",
1720
+ justify: "center",
1721
+ children: /* @__PURE__ */jsx(Spinner, {
1722
+ muted: true
1723
+ })
1724
+ }) : null, /* @__PURE__ */jsx(DocumentList, {
1725
+ data,
1726
+ invalidDocumentIds,
1727
+ selectedSchemaTypes,
1728
+ selectedUserIds,
1729
+ state,
1730
+ states,
1731
+ toggleInvalidDocumentId,
1732
+ user,
1733
+ userList,
1734
+ userRoleCanDrop
1735
+ })]
1736
+ })
1737
+ })
1738
+ })]
1739
+ })
1740
+ }, state.id);
1741
+ })
1742
+ })
1743
+ })]
1744
+ });
1643
1745
  }
1746
+ const workflowTool = options => ({
1747
+ name: "workflow",
1748
+ title: "Workflow",
1749
+ component: WorkflowTool,
1750
+ icon: SplitVerticalIcon,
1751
+ options
1752
+ });
1644
1753
  const workflow = definePlugin(function () {
1645
1754
  let config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_CONFIG;
1646
1755
  const {