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

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