sanity-plugin-workflow 1.0.6 → 2.0.1

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.
Files changed (52) hide show
  1. package/README.md +2 -17
  2. package/dist/index.d.ts +17 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +1647 -0
  5. package/dist/index.js.map +1 -0
  6. package/package.json +32 -71
  7. package/lib/index.d.ts +0 -20
  8. package/lib/index.esm.js +0 -2135
  9. package/lib/index.esm.js.map +0 -1
  10. package/lib/index.js +0 -2147
  11. package/lib/index.js.map +0 -1
  12. package/sanity.json +0 -8
  13. package/src/actions/AssignWorkflow.tsx +0 -47
  14. package/src/actions/BeginWorkflow.tsx +0 -63
  15. package/src/actions/CompleteWorkflow.tsx +0 -64
  16. package/src/actions/UpdateWorkflow.tsx +0 -126
  17. package/src/badges/AssigneesBadge.tsx +0 -53
  18. package/src/badges/StateBadge.tsx +0 -28
  19. package/src/components/DocumentCard/AvatarGroup.tsx +0 -43
  20. package/src/components/DocumentCard/CompleteButton.tsx +0 -56
  21. package/src/components/DocumentCard/EditButton.tsx +0 -28
  22. package/src/components/DocumentCard/Field.tsx +0 -38
  23. package/src/components/DocumentCard/Validate.tsx +0 -21
  24. package/src/components/DocumentCard/ValidationStatus.tsx +0 -37
  25. package/src/components/DocumentCard/core/DraftStatus.tsx +0 -32
  26. package/src/components/DocumentCard/core/PublishedStatus.tsx +0 -39
  27. package/src/components/DocumentCard/core/TimeAgo.tsx +0 -11
  28. package/src/components/DocumentCard/index.tsx +0 -200
  29. package/src/components/DocumentList.tsx +0 -169
  30. package/src/components/Filters.tsx +0 -174
  31. package/src/components/FloatingCard.tsx +0 -36
  32. package/src/components/StateTitle/Status.tsx +0 -27
  33. package/src/components/StateTitle/index.tsx +0 -78
  34. package/src/components/UserAssignment.tsx +0 -121
  35. package/src/components/UserAssignmentInput.tsx +0 -27
  36. package/src/components/UserDisplay.tsx +0 -57
  37. package/src/components/Verify.tsx +0 -297
  38. package/src/components/WorkflowContext.tsx +0 -71
  39. package/src/components/WorkflowSignal.tsx +0 -30
  40. package/src/components/WorkflowTool.tsx +0 -437
  41. package/src/constants/index.ts +0 -31
  42. package/src/helpers/arraysContainMatchingString.ts +0 -6
  43. package/src/helpers/filterItemsAndSort.ts +0 -41
  44. package/src/helpers/generateMultipleOrderRanks.ts +0 -80
  45. package/src/helpers/initialRank.ts +0 -13
  46. package/src/hooks/useWorkflowDocuments.tsx +0 -167
  47. package/src/hooks/useWorkflowMetadata.tsx +0 -49
  48. package/src/index.ts +0 -97
  49. package/src/schema/workflow/workflow.metadata.ts +0 -68
  50. package/src/tools/index.ts +0 -15
  51. package/src/types/index.ts +0 -71
  52. package/v2-incompatible.js +0 -11
package/lib/index.js DELETED
@@ -1,2147 +0,0 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', {
4
- value: true
5
- });
6
- var sanity = require('sanity');
7
- var jsxRuntime = require('react/jsx-runtime');
8
- var icons = require('@sanity/icons');
9
- var React = require('react');
10
- var sanityPluginUtils = require('sanity-plugin-utils');
11
- var ui = require('@sanity/ui');
12
- var lexorank = require('lexorank');
13
- var router = require('sanity/router');
14
- var dnd = require('@hello-pangea/dnd');
15
- var groq = require('groq');
16
- var reactVirtual = require('@tanstack/react-virtual');
17
- var styledComponents = require('styled-components');
18
- var framerMotion = require('framer-motion');
19
- function _interopDefaultCompat(e) {
20
- return e && typeof e === 'object' && 'default' in e ? e : {
21
- default: e
22
- };
23
- }
24
- var React__default = /*#__PURE__*/_interopDefaultCompat(React);
25
- var groq__default = /*#__PURE__*/_interopDefaultCompat(groq);
26
- function defineStates(states) {
27
- return states;
28
- }
29
- const API_VERSION = "2023-01-01";
30
- const DEFAULT_CONFIG = {
31
- schemaTypes: [],
32
- states: defineStates([{
33
- id: "inReview",
34
- title: "In review",
35
- color: "primary",
36
- roles: ["editor", "administrator"],
37
- transitions: ["changesRequested", "approved"]
38
- }, {
39
- id: "changesRequested",
40
- title: "Changes requested",
41
- color: "warning",
42
- roles: ["editor", "administrator"],
43
- transitions: ["approved"]
44
- }, {
45
- id: "approved",
46
- title: "Approved",
47
- color: "success",
48
- roles: ["administrator"],
49
- transitions: ["changesRequested"],
50
- requireAssignment: true
51
- }])
52
- };
53
- function UserAssignment(props) {
54
- const {
55
- assignees,
56
- userList,
57
- documentId
58
- } = props;
59
- const client = sanity.useClient({
60
- apiVersion: API_VERSION
61
- });
62
- const toast = ui.useToast();
63
- const addAssignee = React__default.default.useCallback(userId => {
64
- const user = userList.find(u => u.id === userId);
65
- if (!userId || !user) {
66
- return toast.push({
67
- status: "error",
68
- title: "Could not find User"
69
- });
70
- }
71
- return client.patch("workflow-metadata.".concat(documentId)).setIfMissing({
72
- assignees: []
73
- }).insert("after", "assignees[-1]", [userId]).commit().then(() => {
74
- return toast.push({
75
- title: "Added ".concat(user.displayName, " to assignees"),
76
- status: "success"
77
- });
78
- }).catch(err => {
79
- console.error(err);
80
- return toast.push({
81
- title: "Failed to add assignee",
82
- description: userId,
83
- status: "error"
84
- });
85
- });
86
- }, [documentId, client, toast, userList]);
87
- const removeAssignee = React__default.default.useCallback(userId => {
88
- const user = userList.find(u => u.id === userId);
89
- if (!userId || !user) {
90
- return toast.push({
91
- status: "error",
92
- title: "Could not find User"
93
- });
94
- }
95
- return client.patch("workflow-metadata.".concat(documentId)).unset(['assignees[@ == "'.concat(userId, '"]')]).commit().then(() => {
96
- return toast.push({
97
- title: "Removed ".concat(user.displayName, " from assignees"),
98
- status: "success"
99
- });
100
- }).catch(err => {
101
- console.error(err);
102
- return toast.push({
103
- title: "Failed to remove assignee",
104
- description: documentId,
105
- status: "error"
106
- });
107
- });
108
- }, [client, toast, documentId, userList]);
109
- const clearAssignees = React__default.default.useCallback(() => {
110
- return client.patch("workflow-metadata.".concat(documentId)).unset(["assignees"]).commit().then(() => {
111
- return toast.push({
112
- title: "Cleared assignees",
113
- status: "success"
114
- });
115
- }).catch(err => {
116
- console.error(err);
117
- return toast.push({
118
- title: "Failed to clear assignees",
119
- description: documentId,
120
- status: "error"
121
- });
122
- });
123
- }, [client, toast, documentId]);
124
- return /* @__PURE__ */jsxRuntime.jsx(sanityPluginUtils.UserSelectMenu, {
125
- style: {
126
- maxHeight: 300
127
- },
128
- value: assignees || [],
129
- userList,
130
- onAdd: addAssignee,
131
- onClear: clearAssignees,
132
- onRemove: removeAssignee
133
- });
134
- }
135
- function useWorkflowMetadata(ids) {
136
- const {
137
- data: rawData,
138
- loading,
139
- error
140
- } = sanityPluginUtils.useListeningQuery('*[_type == "workflow.metadata" && documentId in $ids]{\n _id,\n _type,\n _rev,\n assignees,\n documentId,\n state,\n orderRank\n }', {
141
- params: {
142
- ids
143
- },
144
- options: {
145
- apiVersion: API_VERSION
146
- }
147
- });
148
- const keyedMetadata = React.useMemo(() => {
149
- if (!rawData || rawData.length === 0) return {};
150
- return rawData.reduce((acc, cur) => {
151
- return {
152
- ...acc,
153
- [cur.documentId]: cur
154
- };
155
- }, {});
156
- }, [rawData]);
157
- return {
158
- data: keyedMetadata,
159
- loading,
160
- error
161
- };
162
- }
163
- const WorkflowContext = React.createContext({
164
- data: {},
165
- loading: false,
166
- error: false,
167
- ids: [],
168
- addId: () => null,
169
- removeId: () => null,
170
- ...DEFAULT_CONFIG
171
- });
172
- function useWorkflowContext(id) {
173
- const current = React.useContext(WorkflowContext);
174
- return {
175
- ...current,
176
- metadata: id ? current.data[id] : null
177
- };
178
- }
179
- function WorkflowProvider(props) {
180
- const [ids, setIds] = React.useState([]);
181
- const addId = React.useCallback(id => setIds(current => current.includes(id) ? current : [...current, id]), []);
182
- const removeId = React.useCallback(id => setIds(current => current.filter(i => i !== id)), []);
183
- const {
184
- data,
185
- loading,
186
- error
187
- } = useWorkflowMetadata(ids);
188
- return /* @__PURE__ */jsxRuntime.jsx(WorkflowContext.Provider, {
189
- value: {
190
- data,
191
- loading,
192
- error,
193
- ids,
194
- addId,
195
- removeId,
196
- states: props.workflow.states,
197
- schemaTypes: props.workflow.schemaTypes
198
- },
199
- children: props.renderDefault(props)
200
- });
201
- }
202
- function AssignWorkflow(props) {
203
- var _a;
204
- const {
205
- id
206
- } = props;
207
- const {
208
- metadata,
209
- loading,
210
- error
211
- } = useWorkflowContext(id);
212
- const [isDialogOpen, setDialogOpen] = React.useState(false);
213
- const userList = sanityPluginUtils.useProjectUsers({
214
- apiVersion: API_VERSION
215
- });
216
- if (error) {
217
- console.error(error);
218
- }
219
- if (!metadata) {
220
- return null;
221
- }
222
- return {
223
- icon: icons.UsersIcon,
224
- type: "dialog",
225
- disabled: !metadata || loading || error,
226
- label: "Assign",
227
- title: metadata ? null : "Document is not in Workflow",
228
- dialog: isDialogOpen && {
229
- type: "popover",
230
- onClose: () => {
231
- setDialogOpen(false);
232
- },
233
- content: /* @__PURE__ */jsxRuntime.jsx(UserAssignment, {
234
- userList,
235
- assignees: ((_a = metadata == null ? void 0 : metadata.assignees) == null ? void 0 : _a.length) > 0 ? metadata.assignees : [],
236
- documentId: id
237
- })
238
- },
239
- onHandle: () => {
240
- setDialogOpen(true);
241
- }
242
- };
243
- }
244
- function BeginWorkflow(props) {
245
- const {
246
- id,
247
- draft
248
- } = props;
249
- const {
250
- metadata,
251
- loading,
252
- error,
253
- states
254
- } = useWorkflowContext(id);
255
- const client = sanity.useClient({
256
- apiVersion: API_VERSION
257
- });
258
- const toast = ui.useToast();
259
- const [beginning, setBeginning] = React.useState(false);
260
- const [complete, setComplete] = React.useState(false);
261
- if (error) {
262
- console.error(error);
263
- }
264
- const handle = React.useCallback(async () => {
265
- setBeginning(true);
266
- const lowestOrderFirstState = await client.fetch('*[_type == "workflow.metadata" && state == $state]|order(orderRank)[0].orderRank', {
267
- state: states[0].id
268
- });
269
- client.createIfNotExists({
270
- _id: "workflow-metadata.".concat(id),
271
- _type: "workflow.metadata",
272
- documentId: id,
273
- state: states[0].id,
274
- orderRank: lowestOrderFirstState ? lexorank.LexoRank.parse(lowestOrderFirstState).genNext().toString() : lexorank.LexoRank.min().toString()
275
- }).then(() => {
276
- toast.push({
277
- status: "success",
278
- title: "Workflow started",
279
- description: 'Document is now "'.concat(states[0].title, '"')
280
- });
281
- setBeginning(false);
282
- setComplete(true);
283
- });
284
- }, [id, states, client, toast]);
285
- if (!draft || complete || metadata) {
286
- return null;
287
- }
288
- return {
289
- icon: icons.SplitVerticalIcon,
290
- type: "dialog",
291
- disabled: metadata || loading || error || beginning || complete,
292
- label: beginning ? "Beginning..." : "Begin Workflow",
293
- onHandle: () => {
294
- handle();
295
- }
296
- };
297
- }
298
- const handleDeleteMetadata = async (client, toast, id) => {
299
- try {
300
- await client.delete("workflow-metadata.".concat(id));
301
- toast.push({
302
- status: "success",
303
- title: "Workflow completed"
304
- });
305
- } catch (error) {
306
- console.error(error);
307
- toast.push({
308
- status: "error",
309
- title: "Could not complete Workflow"
310
- });
311
- }
312
- };
313
- function CompleteWorkflow(props) {
314
- const {
315
- id
316
- } = props;
317
- const {
318
- metadata,
319
- loading,
320
- error,
321
- states
322
- } = useWorkflowContext(id);
323
- const client = sanity.useClient({
324
- apiVersion: API_VERSION
325
- });
326
- const toast = ui.useToast();
327
- if (error) {
328
- console.error(error);
329
- }
330
- const handle = React.useCallback(async () => {
331
- await handleDeleteMetadata(client, toast, id);
332
- }, [client, toast, id]);
333
- if (!metadata) {
334
- return null;
335
- }
336
- const state = states.find(s => s.id === metadata.state);
337
- const isLastState = (state == null ? void 0 : state.id) === states[states.length - 1].id;
338
- return {
339
- icon: icons.CheckmarkIcon,
340
- type: "dialog",
341
- disabled: loading || error || !isLastState,
342
- label: "Complete Workflow",
343
- title: isLastState ? "Removes the document from the Workflow process" : "Cannot remove from workflow until in the last state",
344
- onHandle: async () => {
345
- await handle();
346
- props.onComplete();
347
- },
348
- color: "positive"
349
- };
350
- }
351
- function arraysContainMatchingString(one, two) {
352
- return one.some(item => two.includes(item));
353
- }
354
- function UpdateWorkflow(props, actionState) {
355
- var _a, _b, _c, _d;
356
- const {
357
- id,
358
- type
359
- } = props;
360
- const user = sanity.useCurrentUser();
361
- const client = sanity.useClient({
362
- apiVersion: API_VERSION
363
- });
364
- const toast = ui.useToast();
365
- const currentUser = sanity.useCurrentUser();
366
- const {
367
- metadata,
368
- loading,
369
- error,
370
- states
371
- } = useWorkflowContext(id);
372
- const currentState = states.find(s => s.id === (metadata == null ? void 0 : metadata.state));
373
- const {
374
- assignees = []
375
- } = metadata != null ? metadata : {};
376
- const {
377
- validation,
378
- isValidating
379
- } = sanity.useValidationStatus(id, type);
380
- const hasValidationErrors = (currentState == null ? void 0 : currentState.requireValidation) && !isValidating && (validation == null ? void 0 : validation.length) > 0 && validation.find(v => v.level === "error");
381
- if (error) {
382
- console.error(error);
383
- }
384
- const onHandle = (documentId, newState) => {
385
- client.patch("workflow-metadata.".concat(documentId)).set({
386
- state: newState.id
387
- }).commit().then(() => {
388
- props.onComplete();
389
- toast.push({
390
- status: "success",
391
- title: 'Document state now "'.concat(newState.title, '"')
392
- });
393
- }).catch(err => {
394
- props.onComplete();
395
- console.error(err);
396
- toast.push({
397
- status: "error",
398
- title: "Document state update failed"
399
- });
400
- });
401
- };
402
- if (!metadata || currentState && currentState.id === actionState.id) {
403
- return null;
404
- }
405
- const currentStateIndex = states.findIndex(s => s.id === (currentState == null ? void 0 : currentState.id));
406
- const actionStateIndex = states.findIndex(s => s.id === actionState.id);
407
- const direction = actionStateIndex > currentStateIndex ? "promote" : "demote";
408
- const DirectionIcon = direction === "promote" ? icons.ArrowRightIcon : icons.ArrowLeftIcon;
409
- const directionLabel = direction === "promote" ? "Promote" : "Demote";
410
- const userRoleCanUpdateState = ((_a = user == null ? void 0 : user.roles) == null ? void 0 : _a.length) && ((_b = actionState == null ? void 0 : actionState.roles) == null ? void 0 : _b.length) ?
411
- // If the Action state is limited to specific roles
412
- // check that the current user has one of those roles
413
- arraysContainMatchingString(user.roles.map(r => r.name), actionState.roles) :
414
- // No roles specified on the next state, so anyone can update
415
- ((_c = actionState == null ? void 0 : actionState.roles) == null ? void 0 : _c.length) !== 0;
416
- const actionStateIsAValidTransition = (currentState == null ? void 0 : currentState.id) && ((_d = currentState == null ? void 0 : currentState.transitions) == null ? void 0 : _d.length) ?
417
- // If the Current State limits transitions to specific States
418
- // Check that the Action State is in Current State's transitions array
419
- currentState.transitions.includes(actionState.id) :
420
- // Otherwise this isn't a problem
421
- true;
422
- const userAssignmentCanUpdateState = actionState.requireAssignment ?
423
- // If the Action State requires assigned users
424
- // Check the current user ID is in the assignees array
425
- currentUser && (assignees == null ? void 0 : assignees.length) && assignees.includes(currentUser.id) :
426
- // Otherwise this isn't a problem
427
- true;
428
- let title = "".concat(directionLabel, ' State to "').concat(actionState.title, '"');
429
- if (!userRoleCanUpdateState) {
430
- title = "Your User role cannot ".concat(directionLabel, ' State to "').concat(actionState.title, '"');
431
- } else if (!actionStateIsAValidTransition) {
432
- title = "You cannot ".concat(directionLabel, ' State to "').concat(actionState.title, '" from "').concat(currentState == null ? void 0 : currentState.title, '"');
433
- } else if (!userAssignmentCanUpdateState) {
434
- title = "You must be assigned to the document to ".concat(directionLabel, ' State to "').concat(actionState.title, '"');
435
- } else if ((currentState == null ? void 0 : currentState.requireValidation) && isValidating) {
436
- title = "Document is validating, cannot ".concat(directionLabel, ' State to "').concat(actionState.title, '"');
437
- } else if (hasValidationErrors) {
438
- title = "Document has validation errors, cannot ".concat(directionLabel, ' State to "').concat(actionState.title, '"');
439
- }
440
- return {
441
- icon: DirectionIcon,
442
- disabled: loading || error || (currentState == null ? void 0 : currentState.requireValidation) && isValidating || hasValidationErrors || !currentState || !userRoleCanUpdateState || !actionStateIsAValidTransition || !userAssignmentCanUpdateState,
443
- title,
444
- label: actionState.title,
445
- onHandle: () => onHandle(id, actionState)
446
- };
447
- }
448
- function AssigneesBadge(documentId, currentUser) {
449
- var _a;
450
- const {
451
- metadata,
452
- loading,
453
- error
454
- } = useWorkflowContext(documentId);
455
- const userList = sanityPluginUtils.useProjectUsers({
456
- apiVersion: API_VERSION
457
- });
458
- if (loading || error || !metadata) {
459
- if (error) {
460
- console.error(error);
461
- }
462
- return null;
463
- }
464
- if (!((_a = metadata == null ? void 0 : metadata.assignees) == null ? void 0 : _a.length)) {
465
- return {
466
- label: "Unassigned"
467
- };
468
- }
469
- const {
470
- assignees
471
- } = metadata != null ? metadata : [];
472
- const hasMe = currentUser ? assignees.some(assignee => assignee === currentUser.id) : false;
473
- const assigneesCount = hasMe ? assignees.length - 1 : assignees.length;
474
- const assigneeUsers = userList.filter(user => assignees.includes(user.id));
475
- const title = assigneeUsers.map(user => user.displayName).join(", ");
476
- let label;
477
- if (hasMe && assigneesCount === 0) {
478
- label = "Assigned to Me";
479
- } else if (hasMe && assigneesCount > 0) {
480
- label = "Me and ".concat(assigneesCount, " ").concat(assigneesCount === 1 ? "other" : "others");
481
- } else {
482
- label = "".concat(assigneesCount, " assigned");
483
- }
484
- return {
485
- label,
486
- title,
487
- color: "primary"
488
- };
489
- }
490
- function StateBadge(documentId) {
491
- const {
492
- metadata,
493
- loading,
494
- error,
495
- states
496
- } = useWorkflowContext(documentId);
497
- const state = states.find(s => s.id === (metadata == null ? void 0 : metadata.state));
498
- if (loading || error) {
499
- if (error) {
500
- console.error(error);
501
- }
502
- return null;
503
- }
504
- if (!state) {
505
- return null;
506
- }
507
- return {
508
- label: state.title,
509
- // title: state.title,
510
- color: state == null ? void 0 : state.color
511
- };
512
- }
513
- function WorkflowSignal(props) {
514
- var _a;
515
- const documentId = ((_a = props == null ? void 0 : props.value) == null ? void 0 : _a._id) ? props.value._id.replace("drafts.", "") : null;
516
- const {
517
- addId,
518
- removeId
519
- } = useWorkflowContext();
520
- React.useEffect(() => {
521
- if (documentId) {
522
- addId(documentId);
523
- }
524
- return () => {
525
- if (documentId) {
526
- removeId(documentId);
527
- }
528
- };
529
- }, [documentId, addId, removeId]);
530
- return props.renderDefault(props);
531
- }
532
- function EditButton(props) {
533
- const {
534
- id,
535
- type,
536
- disabled = false
537
- } = props;
538
- const {
539
- navigateIntent
540
- } = router.useRouter();
541
- return /* @__PURE__ */jsxRuntime.jsx(ui.Button, {
542
- onClick: () => navigateIntent("edit", {
543
- id,
544
- type
545
- }),
546
- mode: "ghost",
547
- fontSize: 1,
548
- padding: 2,
549
- tabIndex: -1,
550
- icon: icons.EditIcon,
551
- text: "Edit",
552
- disabled
553
- });
554
- }
555
- function Field(props) {
556
- var _a;
557
- const schema = sanity.useSchema();
558
- const {
559
- data,
560
- loading,
561
- error
562
- } = sanityPluginUtils.useListeningQuery("*[_id in [$id, $draftId]]|order(_updatedAt)[0]", {
563
- params: {
564
- id: String(props.value),
565
- draftId: "drafts.".concat(String(props.value))
566
- }
567
- });
568
- if (loading) {
569
- return /* @__PURE__ */jsxRuntime.jsx(ui.Spinner, {});
570
- }
571
- const schemaType = schema.get((_a = data == null ? void 0 : data._type) != null ? _a : "");
572
- if (error || !(data == null ? void 0 : data._type) || !schemaType) {
573
- return /* @__PURE__ */jsxRuntime.jsx(sanityPluginUtils.Feedback, {
574
- tone: "critical",
575
- title: "Error with query"
576
- });
577
- }
578
- return /* @__PURE__ */jsxRuntime.jsx(ui.Card, {
579
- border: true,
580
- padding: 2,
581
- children: /* @__PURE__ */jsxRuntime.jsxs(ui.Flex, {
582
- align: "center",
583
- justify: "space-between",
584
- gap: 2,
585
- children: [/* @__PURE__ */jsxRuntime.jsx(sanity.Preview, {
586
- layout: "default",
587
- value: data,
588
- schemaType
589
- }), /* @__PURE__ */jsxRuntime.jsx(EditButton, {
590
- id: data._id,
591
- type: data._type
592
- })]
593
- })
594
- });
595
- }
596
- const UserAssignmentInput = props => {
597
- var _a;
598
- const documentId = sanity.useFormValue(["documentId"]);
599
- const userList = sanityPluginUtils.useProjectUsers({
600
- apiVersion: API_VERSION
601
- });
602
- 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)) : [];
603
- return /* @__PURE__ */jsxRuntime.jsx(ui.Card, {
604
- border: true,
605
- padding: 1,
606
- children: /* @__PURE__ */jsxRuntime.jsx(UserAssignment, {
607
- userList,
608
- assignees: stringValue,
609
- documentId: String(documentId)
610
- })
611
- });
612
- };
613
- function initialRank() {
614
- let lastRankValue = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "";
615
- const lastRank = lastRankValue && typeof lastRankValue === "string" ? lexorank.LexoRank.parse(lastRankValue) : lexorank.LexoRank.min();
616
- const nextRank = lastRank.genNext().genNext();
617
- return nextRank.value;
618
- }
619
- var metadata = states => sanity.defineType({
620
- type: "document",
621
- name: "workflow.metadata",
622
- title: "Workflow metadata",
623
- liveEdit: true,
624
- fields: [sanity.defineField({
625
- name: "state",
626
- 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.',
627
- readOnly: true,
628
- type: "string",
629
- options: {
630
- list: states.length ? states.map(state => ({
631
- value: state.id,
632
- title: state.title
633
- })) : [],
634
- layout: "radio"
635
- }
636
- }), sanity.defineField({
637
- name: "documentId",
638
- title: "Document ID",
639
- description: "Used to help identify the target document that this metadata is tracking state for.",
640
- type: "string",
641
- readOnly: true,
642
- components: {
643
- input: Field
644
- }
645
- }), sanity.defineField({
646
- name: "orderRank",
647
- description: "Used to maintain order position of cards in the Tool.",
648
- type: "string",
649
- readOnly: true,
650
- initialValue: async (p, _ref) => {
651
- let {
652
- getClient
653
- } = _ref;
654
- const lastDocOrderRank = await getClient({
655
- apiVersion: API_VERSION
656
- }).fetch("*[_type == $type]|order(@[$order] desc)[0][$order]", {
657
- order: "orderRank",
658
- type: "workflow.metadata"
659
- });
660
- return initialRank(lastDocOrderRank);
661
- }
662
- }), sanity.defineField({
663
- type: "array",
664
- name: "assignees",
665
- of: [{
666
- type: "string"
667
- }],
668
- components: {
669
- input: UserAssignmentInput
670
- }
671
- })]
672
- });
673
- function filterItemsAndSort(items, stateId) {
674
- let selectedUsers = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
675
- let selectedSchemaTypes = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [];
676
- return items.filter(item => item == null ? void 0 : item._id).filter(item => {
677
- var _a;
678
- return ((_a = item == null ? void 0 : item._metadata) == null ? void 0 : _a.state) === stateId;
679
- }).filter(item => {
680
- var _a, _b, _c;
681
- 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;
682
- }).filter(item => {
683
- if (!selectedSchemaTypes) {
684
- return true;
685
- }
686
- return selectedSchemaTypes.length ? selectedSchemaTypes.includes(item._type) : false;
687
- }).sort((a, b) => {
688
- var _a, _b;
689
- const aOrderRank = ((_a = a._metadata) == null ? void 0 : _a.orderRank) || "0";
690
- const bOrderRank = ((_b = b._metadata) == null ? void 0 : _b.orderRank) || "0";
691
- return aOrderRank.localeCompare(bOrderRank);
692
- });
693
- }
694
- var __freeze$2 = Object.freeze;
695
- var __defProp$2 = Object.defineProperty;
696
- var __template$2 = (cooked, raw) => __freeze$2(__defProp$2(cooked, "raw", {
697
- value: __freeze$2(raw || cooked.slice())
698
- }));
699
- var _a$2;
700
- const QUERY = groq__default.default(_a$2 || (_a$2 = __template$2(['*[_type == "workflow.metadata"]|order(orderRank){\n "_metadata": {\n _rev,\n assignees,\n documentId,\n state,\n orderRank,\n "draftDocumentId": "drafts." + documentId,\n }\n}{\n ...,\n ...(\n *[_id == ^._metadata.documentId || _id == ^._metadata.draftDocumentId]|order(_updatedAt)[0]{ \n _id, \n _type, \n _rev, \n _updatedAt \n }\n )\n}'])));
701
- function useWorkflowDocuments(schemaTypes) {
702
- const toast = ui.useToast();
703
- const client = sanity.useClient({
704
- apiVersion: API_VERSION
705
- });
706
- const {
707
- data,
708
- loading,
709
- error
710
- } = sanityPluginUtils.useListeningQuery(QUERY, {
711
- params: {
712
- schemaTypes
713
- },
714
- initialValue: []
715
- });
716
- const [localDocuments, setLocalDocuments] = React__default.default.useState([]);
717
- React__default.default.useEffect(() => {
718
- if (data) {
719
- setLocalDocuments(data);
720
- }
721
- }, [data]);
722
- const move = React__default.default.useCallback(async (draggedId, destination, states, newOrder) => {
723
- const currentLocalData = localDocuments;
724
- const newLocalDocuments = localDocuments.map(item => {
725
- var _a2;
726
- if (((_a2 = item == null ? void 0 : item._metadata) == null ? void 0 : _a2.documentId) === draggedId) {
727
- return {
728
- ...item,
729
- _metadata: {
730
- ...item._metadata,
731
- state: destination.droppableId,
732
- orderRank: newOrder,
733
- // This value won't be written to the document
734
- // It's done so that un/publish operations don't happen twice
735
- // Because a moved document's card will update once optimistically
736
- // and then again when the document is updated
737
- optimistic: true
738
- }
739
- };
740
- }
741
- return item;
742
- });
743
- setLocalDocuments(newLocalDocuments);
744
- const newStateId = destination.droppableId;
745
- const newState = states.find(s => s.id === newStateId);
746
- const document = localDocuments.find(d => {
747
- var _a2;
748
- return ((_a2 = d == null ? void 0 : d._metadata) == null ? void 0 : _a2.documentId) === draggedId;
749
- });
750
- if (!(newState == null ? void 0 : newState.id)) {
751
- toast.push({
752
- title: "Could not find target state ".concat(newStateId),
753
- status: "error"
754
- });
755
- return null;
756
- }
757
- if (!document) {
758
- toast.push({
759
- title: "Could not find dragged document in data",
760
- status: "error"
761
- });
762
- return null;
763
- }
764
- const {
765
- _id,
766
- _type
767
- } = document;
768
- const {
769
- documentId,
770
- _rev
771
- } = document._metadata || {};
772
- await client.patch("workflow-metadata.".concat(documentId)).ifRevisionId(_rev).set({
773
- state: newStateId,
774
- orderRank: newOrder
775
- }).commit().then(res => {
776
- var _a2, _b;
777
- toast.push({
778
- title: newState.id === document._metadata.state ? 'Reordered in "'.concat((_a2 = newState == null ? void 0 : newState.title) != null ? _a2 : newStateId, '"') : 'Moved to "'.concat((_b = newState == null ? void 0 : newState.title) != null ? _b : newStateId, '"'),
779
- status: "success"
780
- });
781
- return res;
782
- }).catch(err => {
783
- var _a2;
784
- setLocalDocuments(currentLocalData);
785
- toast.push({
786
- title: 'Failed to move to "'.concat((_a2 = newState == null ? void 0 : newState.title) != null ? _a2 : newStateId, '"'),
787
- description: err.message,
788
- status: "error"
789
- });
790
- return null;
791
- });
792
- return {
793
- _id,
794
- _type,
795
- documentId,
796
- state: newState
797
- };
798
- }, [client, toast, localDocuments]);
799
- return {
800
- workflowData: {
801
- data: localDocuments,
802
- loading,
803
- error
804
- },
805
- operations: {
806
- move
807
- }
808
- };
809
- }
810
- function AvatarGroup(props) {
811
- const currentUser = sanity.useCurrentUser();
812
- const {
813
- users,
814
- max = 4
815
- } = props;
816
- const len = users == null ? void 0 : users.length;
817
- const {
818
- me,
819
- visibleUsers
820
- } = React__default.default.useMemo(() => {
821
- return {
822
- me: (currentUser == null ? void 0 : currentUser.id) ? users.find(u => u.id === currentUser.id) : void 0,
823
- visibleUsers: users.filter(u => u.id !== (currentUser == null ? void 0 : currentUser.id)).slice(0, max - 1)
824
- };
825
- }, [users, max, currentUser]);
826
- if (!(users == null ? void 0 : users.length)) {
827
- return null;
828
- }
829
- return /* @__PURE__ */jsxRuntime.jsxs(ui.Flex, {
830
- align: "center",
831
- gap: 1,
832
- children: [me ? /* @__PURE__ */jsxRuntime.jsx(sanity.UserAvatar, {
833
- user: me
834
- }) : null, visibleUsers.map(user => /* @__PURE__ */jsxRuntime.jsx(ui.Box, {
835
- style: {
836
- marginRight: -8
837
- },
838
- children: /* @__PURE__ */jsxRuntime.jsx(sanity.UserAvatar, {
839
- user
840
- })
841
- }, user.id)), len > max && /* @__PURE__ */jsxRuntime.jsx(ui.Box, {
842
- paddingLeft: 2,
843
- children: /* @__PURE__ */jsxRuntime.jsxs(ui.Text, {
844
- size: 1,
845
- children: ["+", len - max]
846
- })
847
- })]
848
- });
849
- }
850
- function UserDisplay(props) {
851
- const {
852
- assignees,
853
- userList,
854
- documentId,
855
- disabled = false
856
- } = props;
857
- const [button] = React__default.default.useState(null);
858
- const [popover, setPopover] = React__default.default.useState(null);
859
- const [isOpen, setIsOpen] = React__default.default.useState(false);
860
- const close = React__default.default.useCallback(() => setIsOpen(false), []);
861
- const open = React__default.default.useCallback(() => setIsOpen(true), []);
862
- ui.useClickOutside(close, [button, popover]);
863
- return /* @__PURE__ */jsxRuntime.jsx(ui.Popover, {
864
- ref: setPopover,
865
- content: /* @__PURE__ */jsxRuntime.jsx(UserAssignment, {
866
- userList,
867
- assignees,
868
- documentId
869
- }),
870
- portal: true,
871
- open: isOpen,
872
- children: !assignees || assignees.length === 0 ? /* @__PURE__ */jsxRuntime.jsx(ui.Button, {
873
- onClick: open,
874
- fontSize: 1,
875
- padding: 2,
876
- tabIndex: -1,
877
- icon: icons.AddIcon,
878
- text: "Assign",
879
- tone: "positive",
880
- mode: "ghost",
881
- disabled
882
- }) : /* @__PURE__ */jsxRuntime.jsx(ui.Grid, {
883
- children: /* @__PURE__ */jsxRuntime.jsx(ui.Button, {
884
- onClick: open,
885
- padding: 0,
886
- mode: "bleed",
887
- disabled,
888
- children: /* @__PURE__ */jsxRuntime.jsx(AvatarGroup, {
889
- users: userList.filter(u => assignees.includes(u.id))
890
- })
891
- })
892
- })
893
- });
894
- }
895
- function CompleteButton(props) {
896
- const {
897
- documentId,
898
- disabled = false
899
- } = props;
900
- const client = sanity.useClient({
901
- apiVersion: API_VERSION
902
- });
903
- const toast = ui.useToast();
904
- const handleComplete = React__default.default.useCallback(event => {
905
- const id = event.currentTarget.value;
906
- if (!id) {
907
- return;
908
- }
909
- handleDeleteMetadata(client, toast, id);
910
- }, [client, toast]);
911
- return /* @__PURE__ */jsxRuntime.jsx(ui.Tooltip, {
912
- portal: true,
913
- content: /* @__PURE__ */jsxRuntime.jsx(ui.Box, {
914
- padding: 2,
915
- children: /* @__PURE__ */jsxRuntime.jsx(ui.Text, {
916
- size: 1,
917
- children: "Remove this document from Workflow"
918
- })
919
- }),
920
- children: /* @__PURE__ */jsxRuntime.jsx(ui.Button, {
921
- value: documentId,
922
- onClick: handleComplete,
923
- text: "Complete",
924
- icon: icons.CheckmarkIcon,
925
- tone: "positive",
926
- mode: "ghost",
927
- fontSize: 1,
928
- padding: 2,
929
- tabIndex: -1,
930
- disabled
931
- })
932
- });
933
- }
934
- function TimeAgo(_ref2) {
935
- let {
936
- time
937
- } = _ref2;
938
- const timeAgo = sanity.useTimeAgo(time);
939
- return /* @__PURE__ */jsxRuntime.jsxs("span", {
940
- title: timeAgo,
941
- children: [timeAgo, " ago"]
942
- });
943
- }
944
- function DraftStatus(props) {
945
- const {
946
- document
947
- } = props;
948
- const updatedAt = document && "_updatedAt" in document && document._updatedAt;
949
- return /* @__PURE__ */jsxRuntime.jsx(ui.Tooltip, {
950
- portal: true,
951
- content: /* @__PURE__ */jsxRuntime.jsx(ui.Box, {
952
- padding: 2,
953
- children: /* @__PURE__ */jsxRuntime.jsx(ui.Text, {
954
- size: 1,
955
- children: document ? /* @__PURE__ */jsxRuntime.jsxs(jsxRuntime.Fragment, {
956
- children: ["Edited ", updatedAt && /* @__PURE__ */jsxRuntime.jsx(TimeAgo, {
957
- time: updatedAt
958
- })]
959
- }) : /* @__PURE__ */jsxRuntime.jsx(jsxRuntime.Fragment, {
960
- children: "No unpublished edits"
961
- })
962
- })
963
- }),
964
- children: /* @__PURE__ */jsxRuntime.jsx(sanity.TextWithTone, {
965
- tone: "caution",
966
- dimmed: !document,
967
- muted: !document,
968
- size: 1,
969
- children: /* @__PURE__ */jsxRuntime.jsx(icons.EditIcon, {})
970
- })
971
- });
972
- }
973
- function PublishedStatus(props) {
974
- const {
975
- document
976
- } = props;
977
- const updatedAt = document && "_updatedAt" in document && document._updatedAt;
978
- return /* @__PURE__ */jsxRuntime.jsx(ui.Tooltip, {
979
- portal: true,
980
- content: /* @__PURE__ */jsxRuntime.jsx(ui.Box, {
981
- padding: 2,
982
- children: /* @__PURE__ */jsxRuntime.jsx(ui.Text, {
983
- size: 1,
984
- children: document ? /* @__PURE__ */jsxRuntime.jsxs(jsxRuntime.Fragment, {
985
- children: ["Published ", updatedAt && /* @__PURE__ */jsxRuntime.jsx(TimeAgo, {
986
- time: updatedAt
987
- })]
988
- }) : /* @__PURE__ */jsxRuntime.jsx(jsxRuntime.Fragment, {
989
- children: "Not published"
990
- })
991
- })
992
- }),
993
- children: /* @__PURE__ */jsxRuntime.jsx(sanity.TextWithTone, {
994
- tone: "positive",
995
- dimmed: !document,
996
- muted: !document,
997
- size: 1,
998
- children: /* @__PURE__ */jsxRuntime.jsx(icons.PublishIcon, {})
999
- })
1000
- });
1001
- }
1002
- function Validate(props) {
1003
- const {
1004
- documentId,
1005
- type,
1006
- onChange
1007
- } = props;
1008
- const {
1009
- isValidating,
1010
- validation = []
1011
- } = sanity.useValidationStatus(documentId, type);
1012
- React.useEffect(() => {
1013
- onChange({
1014
- isValidating,
1015
- validation
1016
- });
1017
- }, [onChange, isValidating, validation]);
1018
- return null;
1019
- }
1020
- function ValidationStatus(props) {
1021
- const {
1022
- validation = []
1023
- } = props;
1024
- if (!validation.length) {
1025
- return null;
1026
- }
1027
- const hasError = validation.some(item => item.level === "error");
1028
- return /* @__PURE__ */jsxRuntime.jsx(ui.Tooltip, {
1029
- portal: true,
1030
- content: /* @__PURE__ */jsxRuntime.jsx(ui.Box, {
1031
- padding: 2,
1032
- children: /* @__PURE__ */jsxRuntime.jsx(ui.Text, {
1033
- size: 1,
1034
- children: validation.length === 1 ? "1 validation issue" : "".concat(validation.length, " validation issues")
1035
- })
1036
- }),
1037
- children: /* @__PURE__ */jsxRuntime.jsx(sanity.TextWithTone, {
1038
- tone: hasError ? "critical" : "caution",
1039
- size: 1,
1040
- children: hasError ? /* @__PURE__ */jsxRuntime.jsx(icons.ErrorOutlineIcon, {}) : /* @__PURE__ */jsxRuntime.jsx(icons.WarningOutlineIcon, {})
1041
- })
1042
- });
1043
- }
1044
- function DocumentCard(props) {
1045
- var _a, _b;
1046
- const {
1047
- isDragDisabled,
1048
- isPatching,
1049
- userRoleCanDrop,
1050
- isDragging,
1051
- item,
1052
- states,
1053
- toggleInvalidDocumentId,
1054
- userList
1055
- } = props;
1056
- const {
1057
- assignees = [],
1058
- documentId
1059
- } = (_a = item._metadata) != null ? _a : {};
1060
- const schema = sanity.useSchema();
1061
- const state = states.find(s => {
1062
- var _a2;
1063
- return s.id === ((_a2 = item._metadata) == null ? void 0 : _a2.state);
1064
- });
1065
- const isDarkMode = ui.useTheme().sanity.color.dark;
1066
- const defaultCardTone = isDarkMode ? "transparent" : "default";
1067
- const [optimisticValidation, setOptimisticValidation] = React.useState({
1068
- isValidating: (_b = state == null ? void 0 : state.requireValidation) != null ? _b : false,
1069
- validation: []
1070
- });
1071
- const {
1072
- isValidating,
1073
- validation
1074
- } = optimisticValidation;
1075
- const handleValidation = React.useCallback(updates => {
1076
- setOptimisticValidation(updates);
1077
- }, []);
1078
- const cardTone = React.useMemo(() => {
1079
- let tone = defaultCardTone;
1080
- if (!userRoleCanDrop) return isDarkMode ? "default" : "transparent";
1081
- if (!documentId) return tone;
1082
- if (isPatching) tone = isDarkMode ? "default" : "transparent";
1083
- if (isDragging) tone = "positive";
1084
- if ((state == null ? void 0 : state.requireValidation) && !isValidating && validation.length > 0) {
1085
- if (validation.some(v => v.level === "error")) {
1086
- tone = "critical";
1087
- } else {
1088
- tone = "caution";
1089
- }
1090
- }
1091
- return tone;
1092
- }, [defaultCardTone, userRoleCanDrop, isPatching, isDarkMode, documentId, isDragging, isValidating, validation, state == null ? void 0 : state.requireValidation]);
1093
- React.useEffect(() => {
1094
- if (!isValidating && validation.length > 0) {
1095
- if (validation.some(v => v.level === "error")) {
1096
- toggleInvalidDocumentId(documentId, "ADD");
1097
- } else {
1098
- toggleInvalidDocumentId(documentId, "REMOVE");
1099
- }
1100
- } else {
1101
- toggleInvalidDocumentId(documentId, "REMOVE");
1102
- }
1103
- }, [documentId, isValidating, toggleInvalidDocumentId, validation]);
1104
- const hasError = React.useMemo(() => isValidating ? false : validation.some(v => v.level === "error"), [isValidating, validation]);
1105
- const isLastState = React.useMemo(() => {
1106
- var _a2;
1107
- return states[states.length - 1].id === ((_a2 = item._metadata) == null ? void 0 : _a2.state);
1108
- }, [states, item._metadata.state]);
1109
- return /* @__PURE__ */jsxRuntime.jsxs(jsxRuntime.Fragment, {
1110
- children: [(state == null ? void 0 : state.requireValidation) ? /* @__PURE__ */jsxRuntime.jsx(Validate, {
1111
- documentId,
1112
- type: item._type,
1113
- onChange: handleValidation
1114
- }) : null, /* @__PURE__ */jsxRuntime.jsx(ui.Box, {
1115
- paddingBottom: 3,
1116
- paddingX: 3,
1117
- children: /* @__PURE__ */jsxRuntime.jsx(ui.Card, {
1118
- radius: 2,
1119
- shadow: isDragging ? 3 : 1,
1120
- tone: cardTone,
1121
- children: /* @__PURE__ */jsxRuntime.jsxs(ui.Stack, {
1122
- children: [/* @__PURE__ */jsxRuntime.jsx(ui.Card, {
1123
- borderBottom: true,
1124
- radius: 2,
1125
- paddingRight: 2,
1126
- tone: cardTone,
1127
- style: {
1128
- pointerEvents: "none"
1129
- },
1130
- children: /* @__PURE__ */jsxRuntime.jsxs(ui.Flex, {
1131
- align: "center",
1132
- justify: "space-between",
1133
- gap: 1,
1134
- children: [/* @__PURE__ */jsxRuntime.jsx(ui.Box, {
1135
- flex: 1,
1136
- children: /* @__PURE__ */jsxRuntime.jsx(sanity.Preview, {
1137
- layout: "default",
1138
- skipVisibilityCheck: true,
1139
- value: item,
1140
- schemaType: schema.get(item._type)
1141
- })
1142
- }), /* @__PURE__ */jsxRuntime.jsx(ui.Box, {
1143
- style: {
1144
- flexShrink: 0
1145
- },
1146
- children: hasError || isDragDisabled || isPatching ? null : /* @__PURE__ */jsxRuntime.jsx(icons.DragHandleIcon, {})
1147
- })]
1148
- })
1149
- }), /* @__PURE__ */jsxRuntime.jsxs(ui.Card, {
1150
- padding: 2,
1151
- radius: 2,
1152
- tone: "inherit",
1153
- children: [/* @__PURE__ */jsxRuntime.jsxs(ui.Flex, {
1154
- align: "center",
1155
- justify: "space-between",
1156
- gap: 3,
1157
- children: [/* @__PURE__ */jsxRuntime.jsx(ui.Box, {
1158
- flex: 1,
1159
- children: documentId && /* @__PURE__ */jsxRuntime.jsx(UserDisplay, {
1160
- userList,
1161
- assignees,
1162
- documentId,
1163
- disabled: !userRoleCanDrop
1164
- })
1165
- }), validation.length > 0 ? /* @__PURE__ */jsxRuntime.jsx(ValidationStatus, {
1166
- validation
1167
- }) : null, /* @__PURE__ */jsxRuntime.jsx(DraftStatus, {
1168
- document: item
1169
- }), /* @__PURE__ */jsxRuntime.jsx(PublishedStatus, {
1170
- document: item
1171
- }), /* @__PURE__ */jsxRuntime.jsx(EditButton, {
1172
- id: item._id,
1173
- type: item._type,
1174
- disabled: !userRoleCanDrop
1175
- }), isLastState && states.length <= 3 ? /* @__PURE__ */jsxRuntime.jsx(CompleteButton, {
1176
- documentId,
1177
- disabled: !userRoleCanDrop
1178
- }) : null]
1179
- }), isLastState && states.length > 3 ? /* @__PURE__ */jsxRuntime.jsx(ui.Stack, {
1180
- paddingTop: 2,
1181
- children: /* @__PURE__ */jsxRuntime.jsx(CompleteButton, {
1182
- documentId,
1183
- disabled: !userRoleCanDrop
1184
- })
1185
- }) : null]
1186
- })]
1187
- })
1188
- })
1189
- })]
1190
- });
1191
- }
1192
- function getStyle(draggableStyle, virtualItem) {
1193
- let transform = "translateY(".concat(virtualItem.start, "px)");
1194
- if (draggableStyle && draggableStyle.transform) {
1195
- const draggableTransformY = parseInt(draggableStyle.transform.split(",")[1].split("px")[0], 10);
1196
- transform = "translateY(".concat(virtualItem.start + draggableTransformY, "px)");
1197
- }
1198
- return {
1199
- position: "absolute",
1200
- top: 0,
1201
- left: 0,
1202
- width: "100%",
1203
- height: "".concat(virtualItem.size, "px"),
1204
- transform
1205
- };
1206
- }
1207
- function DocumentList(props) {
1208
- const {
1209
- data = [],
1210
- invalidDocumentIds,
1211
- patchingIds,
1212
- selectedSchemaTypes,
1213
- selectedUserIds,
1214
- state,
1215
- states,
1216
- toggleInvalidDocumentId,
1217
- user,
1218
- userList,
1219
- userRoleCanDrop
1220
- } = props;
1221
- const dataFiltered = React.useMemo(() => {
1222
- return data.length ? filterItemsAndSort(data, state.id, selectedUserIds, selectedSchemaTypes) : [];
1223
- }, [data, selectedSchemaTypes, selectedUserIds, state.id]);
1224
- const parentRef = React.useRef(null);
1225
- const virtualizer = reactVirtual.useVirtualizer({
1226
- count: dataFiltered.length,
1227
- getScrollElement: () => parentRef.current,
1228
- getItemKey: index => {
1229
- var _a, _b, _c;
1230
- return (_c = (_b = (_a = dataFiltered[index]) == null ? void 0 : _a._metadata) == null ? void 0 : _b.documentId) != null ? _c : index;
1231
- },
1232
- estimateSize: () => 115,
1233
- overscan: 7,
1234
- measureElement: element => {
1235
- return element.getBoundingClientRect().height || 115;
1236
- }
1237
- });
1238
- if (!data.length || !dataFiltered.length) {
1239
- return null;
1240
- }
1241
- return /* @__PURE__ */jsxRuntime.jsx("div", {
1242
- ref: parentRef,
1243
- style: {
1244
- height: "100%",
1245
- overflow: "auto",
1246
- // Smooths scrollbar behaviour
1247
- overflowAnchor: "none",
1248
- scrollBehavior: "auto",
1249
- paddingTop: 1
1250
- },
1251
- children: /* @__PURE__ */jsxRuntime.jsx("div", {
1252
- style: {
1253
- height: "".concat(virtualizer.getTotalSize(), "px"),
1254
- width: "100%",
1255
- position: "relative"
1256
- },
1257
- children: virtualizer.getVirtualItems().map(virtualItem => {
1258
- var _a;
1259
- const item = dataFiltered[virtualItem.index];
1260
- const {
1261
- documentId,
1262
- assignees
1263
- } = (_a = item == null ? void 0 : item._metadata) != null ? _a : {};
1264
- const isInvalid = invalidDocumentIds.includes(documentId);
1265
- const meInAssignees = (user == null ? void 0 : user.id) ? assignees == null ? void 0 : assignees.includes(user.id) : false;
1266
- const isDragDisabled = patchingIds.includes(documentId) || !userRoleCanDrop || isInvalid || !(state.requireAssignment ? state.requireAssignment && meInAssignees : true);
1267
- return /* @__PURE__ */jsxRuntime.jsx(dnd.Draggable, {
1268
- draggableId: documentId,
1269
- index: virtualItem.index,
1270
- isDragDisabled,
1271
- children: (draggableProvided, draggableSnapshot) => /* @__PURE__ */jsxRuntime.jsx("div", {
1272
- ref: draggableProvided.innerRef,
1273
- ...draggableProvided.draggableProps,
1274
- ...draggableProvided.dragHandleProps,
1275
- style: getStyle(draggableProvided.draggableProps.style, virtualItem),
1276
- children: /* @__PURE__ */jsxRuntime.jsx("div", {
1277
- ref: virtualizer.measureElement,
1278
- "data-index": virtualItem.index,
1279
- children: /* @__PURE__ */jsxRuntime.jsx(DocumentCard, {
1280
- userRoleCanDrop,
1281
- isDragDisabled,
1282
- isPatching: patchingIds.includes(documentId),
1283
- isDragging: draggableSnapshot.isDragging,
1284
- item,
1285
- toggleInvalidDocumentId,
1286
- userList,
1287
- states
1288
- })
1289
- })
1290
- })
1291
- }, virtualItem.key);
1292
- })
1293
- })
1294
- });
1295
- }
1296
- function Filters(props) {
1297
- const {
1298
- uniqueAssignedUsers = [],
1299
- selectedUserIds,
1300
- schemaTypes,
1301
- selectedSchemaTypes,
1302
- toggleSelectedUser,
1303
- resetSelectedUsers,
1304
- toggleSelectedSchemaType
1305
- } = props;
1306
- const currentUser = sanity.useCurrentUser();
1307
- const schema = sanity.useSchema();
1308
- const onAdd = React.useCallback(id => {
1309
- if (!selectedUserIds.includes(id)) {
1310
- toggleSelectedUser(id);
1311
- }
1312
- }, [selectedUserIds, toggleSelectedUser]);
1313
- const onRemove = React.useCallback(id => {
1314
- if (selectedUserIds.includes(id)) {
1315
- toggleSelectedUser(id);
1316
- }
1317
- }, [selectedUserIds, toggleSelectedUser]);
1318
- const onClear = React.useCallback(() => {
1319
- resetSelectedUsers();
1320
- }, [resetSelectedUsers]);
1321
- if (uniqueAssignedUsers.length === 0 && schemaTypes.length < 2) {
1322
- return null;
1323
- }
1324
- const meInUniqueAssignees = (currentUser == null ? void 0 : currentUser.id) && uniqueAssignedUsers.find(u => u.id === currentUser.id);
1325
- const uniqueAssigneesNotMe = uniqueAssignedUsers.filter(u => u.id !== (currentUser == null ? void 0 : currentUser.id));
1326
- return /* @__PURE__ */jsxRuntime.jsx(ui.Card, {
1327
- tone: "primary",
1328
- padding: 2,
1329
- borderBottom: true,
1330
- style: {
1331
- overflowX: "hidden"
1332
- },
1333
- children: /* @__PURE__ */jsxRuntime.jsxs(ui.Flex, {
1334
- align: "center",
1335
- children: [/* @__PURE__ */jsxRuntime.jsx(ui.Flex, {
1336
- align: "center",
1337
- gap: 1,
1338
- flex: 1,
1339
- children: uniqueAssignedUsers.length > 5 ? /* @__PURE__ */jsxRuntime.jsx(ui.Card, {
1340
- tone: "default",
1341
- children: /* @__PURE__ */jsxRuntime.jsx(ui.MenuButton, {
1342
- button: /* @__PURE__ */jsxRuntime.jsx(ui.Button, {
1343
- padding: 3,
1344
- fontSize: 1,
1345
- text: "Filter Assignees",
1346
- tone: "primary",
1347
- icon: icons.UserIcon
1348
- }),
1349
- id: "user-filters",
1350
- menu: /* @__PURE__ */jsxRuntime.jsx(ui.Menu, {
1351
- children: /* @__PURE__ */jsxRuntime.jsx(sanityPluginUtils.UserSelectMenu, {
1352
- value: selectedUserIds,
1353
- userList: uniqueAssignedUsers,
1354
- onAdd,
1355
- onRemove,
1356
- onClear,
1357
- labels: {
1358
- addMe: "Filter mine",
1359
- removeMe: "Clear mine",
1360
- clear: "Clear filters"
1361
- }
1362
- })
1363
- }),
1364
- popover: {
1365
- portal: true
1366
- }
1367
- })
1368
- }) : /* @__PURE__ */jsxRuntime.jsxs(jsxRuntime.Fragment, {
1369
- children: [meInUniqueAssignees ? /* @__PURE__ */jsxRuntime.jsxs(jsxRuntime.Fragment, {
1370
- children: [/* @__PURE__ */jsxRuntime.jsx(ui.Button, {
1371
- padding: 0,
1372
- mode: selectedUserIds.includes(currentUser.id) ? "default" : "bleed",
1373
- onClick: () => toggleSelectedUser(currentUser.id),
1374
- children: /* @__PURE__ */jsxRuntime.jsx(ui.Flex, {
1375
- padding: 1,
1376
- align: "center",
1377
- justify: "center",
1378
- children: /* @__PURE__ */jsxRuntime.jsx(sanity.UserAvatar, {
1379
- user: currentUser.id,
1380
- size: 1,
1381
- withTooltip: true
1382
- })
1383
- })
1384
- }), /* @__PURE__ */jsxRuntime.jsx(ui.Card, {
1385
- borderRight: true,
1386
- style: {
1387
- height: 30
1388
- },
1389
- tone: "inherit"
1390
- })]
1391
- }) : null, uniqueAssigneesNotMe.map(user => /* @__PURE__ */jsxRuntime.jsx(ui.Button, {
1392
- padding: 0,
1393
- mode: selectedUserIds.includes(user.id) ? "default" : "bleed",
1394
- onClick: () => toggleSelectedUser(user.id),
1395
- children: /* @__PURE__ */jsxRuntime.jsx(ui.Flex, {
1396
- padding: 1,
1397
- align: "center",
1398
- justify: "center",
1399
- children: /* @__PURE__ */jsxRuntime.jsx(sanity.UserAvatar, {
1400
- user,
1401
- size: 1,
1402
- withTooltip: true
1403
- })
1404
- })
1405
- }, user.id)), selectedUserIds.length > 0 ? /* @__PURE__ */jsxRuntime.jsx(ui.Button, {
1406
- padding: 3,
1407
- fontSize: 1,
1408
- text: "Clear",
1409
- onClick: resetSelectedUsers,
1410
- mode: "ghost",
1411
- icon: icons.ResetIcon
1412
- }) : null]
1413
- })
1414
- }), schemaTypes.length > 1 ? /* @__PURE__ */jsxRuntime.jsx(ui.Flex, {
1415
- align: "center",
1416
- gap: 1,
1417
- children: schemaTypes.map(typeName => {
1418
- var _a, _b;
1419
- const schemaType = schema.get(typeName);
1420
- if (!schemaType) {
1421
- return null;
1422
- }
1423
- return /* @__PURE__ */jsxRuntime.jsx(ui.Button, {
1424
- padding: 3,
1425
- fontSize: 1,
1426
- text: (_a = schemaType == null ? void 0 : schemaType.title) != null ? _a : typeName,
1427
- icon: (_b = schemaType == null ? void 0 : schemaType.icon) != null ? _b : void 0,
1428
- mode: selectedSchemaTypes.includes(typeName) ? "default" : "ghost",
1429
- onClick: () => toggleSelectedSchemaType(typeName)
1430
- }, typeName);
1431
- })
1432
- }) : null]
1433
- })
1434
- });
1435
- }
1436
- function Status(props) {
1437
- const {
1438
- text,
1439
- icon
1440
- } = props;
1441
- const Icon = icon;
1442
- return /* @__PURE__ */jsxRuntime.jsx(ui.Tooltip, {
1443
- portal: true,
1444
- content: /* @__PURE__ */jsxRuntime.jsx(ui.Box, {
1445
- padding: 2,
1446
- children: /* @__PURE__ */jsxRuntime.jsx(ui.Text, {
1447
- size: 1,
1448
- children: text
1449
- })
1450
- }),
1451
- children: /* @__PURE__ */jsxRuntime.jsx(ui.Text, {
1452
- size: 1,
1453
- children: /* @__PURE__ */jsxRuntime.jsx(Icon, {})
1454
- })
1455
- });
1456
- }
1457
- var __freeze$1 = Object.freeze;
1458
- var __defProp$1 = Object.defineProperty;
1459
- var __template$1 = (cooked, raw) => __freeze$1(__defProp$1(cooked, "raw", {
1460
- value: __freeze$1(raw || cooked.slice())
1461
- }));
1462
- var _a$1;
1463
- const StyledStickyCard = styledComponents.styled(ui.Card)(() => styledComponents.css(_a$1 || (_a$1 = __template$1(["\n position: sticky;\n top: 0;\n z-index: 1;\n "]))));
1464
- function StateTitle(props) {
1465
- const {
1466
- state,
1467
- requireAssignment,
1468
- userRoleCanDrop,
1469
- isDropDisabled,
1470
- draggingFrom,
1471
- documentCount
1472
- } = props;
1473
- let tone = "default";
1474
- const isSource = draggingFrom === state.id;
1475
- if (draggingFrom) {
1476
- tone = isDropDisabled || isSource ? "default" : "positive";
1477
- }
1478
- return /* @__PURE__ */jsxRuntime.jsx(StyledStickyCard, {
1479
- paddingY: 4,
1480
- padding: 3,
1481
- tone: "inherit",
1482
- children: /* @__PURE__ */jsxRuntime.jsxs(ui.Flex, {
1483
- gap: 3,
1484
- align: "center",
1485
- children: [/* @__PURE__ */jsxRuntime.jsx(ui.Badge, {
1486
- mode: draggingFrom && !isDropDisabled || isSource ? "default" : "outline",
1487
- tone,
1488
- muted: !userRoleCanDrop || isDropDisabled,
1489
- children: state.title
1490
- }), userRoleCanDrop ? null : /* @__PURE__ */jsxRuntime.jsx(Status, {
1491
- text: "You do not have permissions to move documents to this State",
1492
- icon: icons.InfoOutlineIcon
1493
- }), requireAssignment ? /* @__PURE__ */jsxRuntime.jsx(Status, {
1494
- text: "You must be assigned to the document to move documents to this State",
1495
- icon: icons.UserIcon
1496
- }) : null, /* @__PURE__ */jsxRuntime.jsx(ui.Box, {
1497
- flex: 1,
1498
- children: documentCount > 0 ? /* @__PURE__ */jsxRuntime.jsx(ui.Text, {
1499
- weight: "semibold",
1500
- align: "right",
1501
- size: 1,
1502
- children: documentCount
1503
- }) : null
1504
- })]
1505
- })
1506
- });
1507
- }
1508
- function generateMiddleValue(ranks) {
1509
- if (!ranks.some(rank => !rank)) {
1510
- return ranks;
1511
- }
1512
- const firstUndefined = ranks.findIndex(rank => !rank);
1513
- const firstDefinedAfter = ranks.findIndex((rank, index) => rank && index > firstUndefined);
1514
- const firstDefinedBefore = ranks.findLastIndex((rank, index) => rank && index < firstUndefined);
1515
- if (firstDefinedAfter === -1 || firstDefinedBefore === -1) {
1516
- throw new Error("Unable to generate middle value between indexes ".concat(firstDefinedBefore, " and ").concat(firstDefinedAfter));
1517
- }
1518
- const beforeRank = ranks[firstDefinedBefore];
1519
- const afterRank = ranks[firstDefinedAfter];
1520
- if (!beforeRank || typeof beforeRank === "undefined" || !afterRank || typeof afterRank === "undefined") {
1521
- throw new Error("Unable to generate middle value between indexes ".concat(firstDefinedBefore, " and ").concat(firstDefinedAfter));
1522
- }
1523
- const between = beforeRank.between(afterRank);
1524
- const middle = Math.floor((firstDefinedAfter + firstDefinedBefore) / 2);
1525
- if (ranks[middle]) {
1526
- throw new Error("Should not have overwritten value at index ".concat(middle));
1527
- }
1528
- ranks[middle] = between;
1529
- return ranks;
1530
- }
1531
- function generateMultipleOrderRanks(count, start, end) {
1532
- let ranks = [...Array(count)];
1533
- const rankStart = start != null ? start : lexorank.LexoRank.min().genNext().genNext();
1534
- const rankEnd = end != null ? end : lexorank.LexoRank.max().genPrev().genPrev();
1535
- ranks[0] = rankStart;
1536
- ranks[count - 1] = rankEnd;
1537
- for (let i = 0; i < count; i++) {
1538
- ranks = generateMiddleValue(ranks);
1539
- }
1540
- return ranks.sort((a, b) => a.toString().localeCompare(b.toString()));
1541
- }
1542
- var __freeze = Object.freeze;
1543
- var __defProp = Object.defineProperty;
1544
- var __template = (cooked, raw) => __freeze(__defProp(cooked, "raw", {
1545
- value: __freeze(raw || cooked.slice())
1546
- }));
1547
- var _a;
1548
- const StyledFloatingCard = styledComponents.styled(ui.Card)(() => styledComponents.css(_a || (_a = __template(["\n position: fixed;\n bottom: 0;\n left: 0;\n z-index: 1000;\n "]))));
1549
- function FloatingCard(_ref3) {
1550
- let {
1551
- children
1552
- } = _ref3;
1553
- const childrenHaveValues = Array.isArray(children) ? children.some(Boolean) : Boolean(children);
1554
- return /* @__PURE__ */jsxRuntime.jsx(framerMotion.AnimatePresence, {
1555
- children: childrenHaveValues ? /* @__PURE__ */jsxRuntime.jsx(framerMotion.motion.div, {
1556
- initial: {
1557
- opacity: 0
1558
- },
1559
- animate: {
1560
- opacity: 1
1561
- },
1562
- exit: {
1563
- opacity: 0
1564
- },
1565
- children: /* @__PURE__ */jsxRuntime.jsx(StyledFloatingCard, {
1566
- shadow: 3,
1567
- padding: 3,
1568
- margin: 3,
1569
- radius: 3,
1570
- children: /* @__PURE__ */jsxRuntime.jsx(ui.Grid, {
1571
- gap: 2,
1572
- children
1573
- })
1574
- })
1575
- }, "floater") : null
1576
- });
1577
- }
1578
- function Verify(props) {
1579
- const {
1580
- data,
1581
- userList,
1582
- states
1583
- } = props;
1584
- const client = sanity.useClient({
1585
- apiVersion: API_VERSION
1586
- });
1587
- const toast = ui.useToast();
1588
- const documentsWithoutValidMetadataIds = (data == null ? void 0 : data.length) ? data.reduce((acc, cur) => {
1589
- var _a;
1590
- const {
1591
- documentId,
1592
- state
1593
- } = (_a = cur._metadata) != null ? _a : {};
1594
- const stateExists = states.find(s => s.id === state);
1595
- return !stateExists && documentId ? [...acc, documentId] : acc;
1596
- }, []) : [];
1597
- const documentsWithInvalidUserIds = (data == null ? void 0 : data.length) && (userList == null ? void 0 : userList.length) ? data.reduce((acc, cur) => {
1598
- var _a;
1599
- const {
1600
- documentId,
1601
- assignees
1602
- } = (_a = cur._metadata) != null ? _a : {};
1603
- const allAssigneesExist = (assignees == null ? void 0 : assignees.length) ? assignees == null ? void 0 : assignees.every(a => userList.find(u => u.id === a)) : true;
1604
- return !allAssigneesExist && documentId ? [...acc, documentId] : acc;
1605
- }, []) : [];
1606
- const documentsWithoutOrderIds = (data == null ? void 0 : data.length) ? data.reduce((acc, cur) => {
1607
- var _a;
1608
- const {
1609
- documentId,
1610
- orderRank
1611
- } = (_a = cur._metadata) != null ? _a : {};
1612
- return !orderRank && documentId ? [...acc, documentId] : acc;
1613
- }, []) : [];
1614
- const documentsWithDuplicatedOrderIds = (data == null ? void 0 : data.length) ? data.reduce((acc, cur) => {
1615
- var _a;
1616
- const {
1617
- documentId,
1618
- orderRank
1619
- } = (_a = cur._metadata) != null ? _a : {};
1620
- return orderRank && data.filter(d => {
1621
- var _a2;
1622
- return ((_a2 = d._metadata) == null ? void 0 : _a2.orderRank) === orderRank;
1623
- }).length > 1 && documentId ? [...acc, documentId] : acc;
1624
- }, []) : [];
1625
- const correctDocuments = React__default.default.useCallback(async ids => {
1626
- toast.push({
1627
- title: "Correcting...",
1628
- status: "info"
1629
- });
1630
- const tx = ids.reduce((item, documentId) => {
1631
- return item.patch("workflow-metadata.".concat(documentId), {
1632
- set: {
1633
- state: states[0].id
1634
- }
1635
- });
1636
- }, client.transaction());
1637
- await tx.commit();
1638
- toast.push({
1639
- title: "Corrected ".concat(ids.length === 1 ? "1 Document" : "".concat(ids.length, " Documents")),
1640
- status: "success"
1641
- });
1642
- }, [client, states, toast]);
1643
- const removeUsersFromDocuments = React__default.default.useCallback(async ids => {
1644
- toast.push({
1645
- title: "Removing users...",
1646
- status: "info"
1647
- });
1648
- const tx = ids.reduce((item, documentId) => {
1649
- var _a, _b;
1650
- const {
1651
- assignees
1652
- } = (_b = (_a = data.find(d => d._id === documentId)) == null ? void 0 : _a._metadata) != null ? _b : {};
1653
- const validAssignees = (assignees == null ? void 0 : assignees.length) ?
1654
- // eslint-disable-next-line max-nested-callbacks
1655
- assignees.filter(a => {
1656
- var _a2;
1657
- return (_a2 = userList.find(u => u.id === a)) == null ? void 0 : _a2.id;
1658
- }) : [];
1659
- return item.patch("workflow-metadata.".concat(documentId), {
1660
- set: {
1661
- assignees: validAssignees
1662
- }
1663
- });
1664
- }, client.transaction());
1665
- await tx.commit();
1666
- toast.push({
1667
- title: "Corrected ".concat(ids.length === 1 ? "1 Document" : "".concat(ids.length, " Documents")),
1668
- status: "success"
1669
- });
1670
- }, [client, data, toast, userList]);
1671
- const addOrderToDocuments = React__default.default.useCallback(async ids => {
1672
- toast.push({
1673
- title: "Adding ordering...",
1674
- status: "info"
1675
- });
1676
- const [firstOrder, secondOrder] = [...data].slice(0, 2).map(d => {
1677
- var _a;
1678
- return (_a = d._metadata) == null ? void 0 : _a.orderRank;
1679
- });
1680
- const minLexo = firstOrder ? lexorank.LexoRank.parse(firstOrder) : void 0;
1681
- const maxLexo = secondOrder ? lexorank.LexoRank.parse(secondOrder) : void 0;
1682
- const ranks = generateMultipleOrderRanks(ids.length, minLexo, maxLexo);
1683
- const tx = client.transaction();
1684
- for (let index = 0; index < ids.length; index += 1) {
1685
- tx.patch("workflow-metadata.".concat(ids[index]), {
1686
- set: {
1687
- orderRank: ranks[index].toString()
1688
- }
1689
- });
1690
- }
1691
- await tx.commit();
1692
- toast.push({
1693
- title: "Added order to ".concat(ids.length === 1 ? "1 Document" : "".concat(ids.length, " Documents")),
1694
- status: "success"
1695
- });
1696
- }, [data, client, toast]);
1697
- const resetOrderOfAllDocuments = React__default.default.useCallback(async ids => {
1698
- toast.push({
1699
- title: "Adding ordering...",
1700
- status: "info"
1701
- });
1702
- const ranks = generateMultipleOrderRanks(ids.length);
1703
- const tx = client.transaction();
1704
- for (let index = 0; index < ids.length; index += 1) {
1705
- tx.patch("workflow-metadata.".concat(ids[index]), {
1706
- set: {
1707
- orderRank: ranks[index].toString()
1708
- }
1709
- });
1710
- }
1711
- await tx.commit();
1712
- toast.push({
1713
- title: "Added order to ".concat(ids.length === 1 ? "1 Document" : "".concat(ids.length, " Documents")),
1714
- status: "success"
1715
- });
1716
- }, [data, client, toast]);
1717
- const orphanedMetadataDocumentIds = React__default.default.useMemo(() => {
1718
- return data.length ? data.filter(doc => !(doc == null ? void 0 : doc._id)).map(doc => doc._metadata.documentId) : [];
1719
- }, [data]);
1720
- const handleOrphans = React__default.default.useCallback(() => {
1721
- toast.push({
1722
- title: "Removing orphaned metadata...",
1723
- status: "info"
1724
- });
1725
- const tx = client.transaction();
1726
- orphanedMetadataDocumentIds.forEach(id => {
1727
- tx.delete("workflow-metadata.".concat(id));
1728
- });
1729
- tx.commit();
1730
- toast.push({
1731
- title: "Removed ".concat(orphanedMetadataDocumentIds.length, " orphaned metadata documents"),
1732
- status: "success"
1733
- });
1734
- }, [client, orphanedMetadataDocumentIds, toast]);
1735
- return /* @__PURE__ */jsxRuntime.jsxs(FloatingCard, {
1736
- children: [documentsWithoutValidMetadataIds.length > 0 ? /* @__PURE__ */jsxRuntime.jsx(ui.Button, {
1737
- tone: "caution",
1738
- mode: "ghost",
1739
- onClick: () => correctDocuments(documentsWithoutValidMetadataIds),
1740
- text: documentsWithoutValidMetadataIds.length === 1 ? "Correct 1 Document State" : "Correct ".concat(documentsWithoutValidMetadataIds.length, " Document States")
1741
- }) : null, documentsWithInvalidUserIds.length > 0 ? /* @__PURE__ */jsxRuntime.jsx(ui.Button, {
1742
- tone: "caution",
1743
- mode: "ghost",
1744
- onClick: () => removeUsersFromDocuments(documentsWithInvalidUserIds),
1745
- text: documentsWithInvalidUserIds.length === 1 ? "Remove Invalid Users from 1 Document" : "Remove Invalid Users from ".concat(documentsWithInvalidUserIds.length, " Documents")
1746
- }) : null, documentsWithoutOrderIds.length > 0 ? /* @__PURE__ */jsxRuntime.jsx(ui.Button, {
1747
- tone: "caution",
1748
- mode: "ghost",
1749
- onClick: () => addOrderToDocuments(documentsWithoutOrderIds),
1750
- text: documentsWithoutOrderIds.length === 1 ? "Set Order for 1 Document" : "Set Order for ".concat(documentsWithoutOrderIds.length, " Documents")
1751
- }) : null, documentsWithDuplicatedOrderIds.length > 0 ? /* @__PURE__ */jsxRuntime.jsxs(jsxRuntime.Fragment, {
1752
- children: [/* @__PURE__ */jsxRuntime.jsx(ui.Button, {
1753
- tone: "caution",
1754
- mode: "ghost",
1755
- onClick: () => addOrderToDocuments(documentsWithDuplicatedOrderIds),
1756
- text: documentsWithDuplicatedOrderIds.length === 1 ? "Set Unique Order for 1 Document" : "Set Unique Order for ".concat(documentsWithDuplicatedOrderIds.length, " Documents")
1757
- }), /* @__PURE__ */jsxRuntime.jsx(ui.Button, {
1758
- tone: "caution",
1759
- mode: "ghost",
1760
- onClick: () => resetOrderOfAllDocuments(data.map(doc => {
1761
- var _a;
1762
- return String((_a = doc._metadata) == null ? void 0 : _a.documentId);
1763
- })),
1764
- text: data.length === 1 ? "Reset Order for 1 Document" : "Reset Order for all ".concat(data.length, " Documents")
1765
- })]
1766
- }) : null, orphanedMetadataDocumentIds.length > 0 ? /* @__PURE__ */jsxRuntime.jsx(ui.Button, {
1767
- text: "Cleanup orphaned metadata",
1768
- onClick: handleOrphans,
1769
- tone: "caution",
1770
- mode: "ghost"
1771
- }) : null]
1772
- });
1773
- }
1774
- function WorkflowTool(props) {
1775
- var _a, _b, _c;
1776
- const {
1777
- schemaTypes = [],
1778
- states = []
1779
- } = (_b = (_a = props == null ? void 0 : props.tool) == null ? void 0 : _a.options) != null ? _b : {};
1780
- const isDarkMode = ui.useTheme().sanity.color.dark;
1781
- const defaultCardTone = isDarkMode ? "default" : "transparent";
1782
- const toast = ui.useToast();
1783
- const userList = sanityPluginUtils.useProjectUsers({
1784
- apiVersion: API_VERSION
1785
- });
1786
- const user = sanity.useCurrentUser();
1787
- const userRoleNames = ((_c = user == null ? void 0 : user.roles) == null ? void 0 : _c.length) ? user == null ? void 0 : user.roles.map(r => r.name) : [];
1788
- const {
1789
- workflowData,
1790
- operations
1791
- } = useWorkflowDocuments(schemaTypes);
1792
- const [patchingIds, setPatchingIds] = React__default.default.useState([]);
1793
- const {
1794
- data,
1795
- loading,
1796
- error
1797
- } = workflowData;
1798
- const {
1799
- move
1800
- } = operations;
1801
- const [undroppableStates, setUndroppableStates] = React__default.default.useState([]);
1802
- const [draggingFrom, setDraggingFrom] = React__default.default.useState("");
1803
- const handleDragStart = React__default.default.useCallback(start => {
1804
- var _a2, _b2;
1805
- const {
1806
- draggableId,
1807
- source
1808
- } = start;
1809
- const {
1810
- droppableId: currentStateId
1811
- } = source;
1812
- setDraggingFrom(currentStateId);
1813
- const document = data.find(item => {
1814
- var _a3;
1815
- return ((_a3 = item._metadata) == null ? void 0 : _a3.documentId) === draggableId;
1816
- });
1817
- const state = states.find(s => s.id === currentStateId);
1818
- if (!document || !state) return;
1819
- const undroppableStateIds = [];
1820
- const statesThatRequireAssignmentIds = states.filter(s => s.requireAssignment).map(s => s.id);
1821
- if (statesThatRequireAssignmentIds.length) {
1822
- const documentAssignees = (_b2 = (_a2 = document._metadata) == null ? void 0 : _a2.assignees) != null ? _b2 : [];
1823
- const userIsAssignedToDocument = (user == null ? void 0 : user.id) ? documentAssignees.includes(user.id) : false;
1824
- if (!userIsAssignedToDocument) {
1825
- undroppableStateIds.push(...statesThatRequireAssignmentIds);
1826
- }
1827
- }
1828
- const statesThatCannotBeTransitionedToIds = state.transitions && state.transitions.length ? states.filter(s => {
1829
- var _a3;
1830
- return !((_a3 = state.transitions) == null ? void 0 : _a3.includes(s.id));
1831
- }).map(s => s.id) : [];
1832
- if (statesThatCannotBeTransitionedToIds.length) {
1833
- undroppableStateIds.push(...statesThatCannotBeTransitionedToIds);
1834
- }
1835
- const undroppableExceptSelf = undroppableStateIds.filter(id => id !== currentStateId);
1836
- if (undroppableExceptSelf.length) {
1837
- setUndroppableStates(undroppableExceptSelf);
1838
- }
1839
- }, [data, states, user]);
1840
- const handleDragEnd = React__default.default.useCallback(async result => {
1841
- var _a2, _b2, _c2, _d, _e, _f;
1842
- setUndroppableStates([]);
1843
- setDraggingFrom("");
1844
- const {
1845
- draggableId,
1846
- source,
1847
- destination
1848
- } = result;
1849
- if (
1850
- // No destination?
1851
- !destination ||
1852
- // No change in position?
1853
- destination.droppableId === source.droppableId && destination.index === source.index) {
1854
- return;
1855
- }
1856
- const destinationStateItems = [...filterItemsAndSort(data, destination.droppableId, [], null)];
1857
- const destinationStateIndex = states.findIndex(s => s.id === destination.droppableId);
1858
- const globalStateMinimumRank = data[0]._metadata.orderRank;
1859
- const globalStateMaximumRank = data[data.length - 1]._metadata.orderRank;
1860
- let newOrder;
1861
- if (!destinationStateItems.length) {
1862
- if (destinationStateIndex === 0) {
1863
- newOrder = lexorank.LexoRank.min().toString();
1864
- } else {
1865
- newOrder = lexorank.LexoRank.min().genNext().toString();
1866
- }
1867
- } else if (destination.index === 0) {
1868
- const firstItemOrderRank = (_b2 = (_a2 = [...destinationStateItems].shift()) == null ? void 0 : _a2._metadata) == null ? void 0 : _b2.orderRank;
1869
- if (firstItemOrderRank && typeof firstItemOrderRank === "string") {
1870
- newOrder = lexorank.LexoRank.parse(firstItemOrderRank).genPrev().toString();
1871
- } else if (destinationStateIndex === 0) {
1872
- newOrder = lexorank.LexoRank.min().toString();
1873
- } else {
1874
- newOrder = lexorank.LexoRank.parse(globalStateMinimumRank).between(lexorank.LexoRank.min()).toString();
1875
- }
1876
- } else if (destination.index + 1 === destinationStateItems.length) {
1877
- const lastItemOrderRank = (_d = (_c2 = [...destinationStateItems].pop()) == null ? void 0 : _c2._metadata) == null ? void 0 : _d.orderRank;
1878
- if (lastItemOrderRank && typeof lastItemOrderRank === "string") {
1879
- newOrder = lexorank.LexoRank.parse(lastItemOrderRank).genNext().toString();
1880
- } else if (destinationStateIndex === states.length - 1) {
1881
- newOrder = lexorank.LexoRank.max().toString();
1882
- } else {
1883
- newOrder = lexorank.LexoRank.parse(globalStateMaximumRank).between(lexorank.LexoRank.min()).toString();
1884
- }
1885
- } else {
1886
- const itemBefore = destinationStateItems[destination.index - 1];
1887
- const itemBeforeRank = (_e = itemBefore == null ? void 0 : itemBefore._metadata) == null ? void 0 : _e.orderRank;
1888
- let itemBeforeRankParsed;
1889
- if (itemBeforeRank) {
1890
- itemBeforeRankParsed = lexorank.LexoRank.parse(itemBeforeRank);
1891
- } else if (destinationStateIndex === 0) {
1892
- itemBeforeRankParsed = lexorank.LexoRank.min();
1893
- } else {
1894
- itemBeforeRankParsed = lexorank.LexoRank.parse(globalStateMinimumRank);
1895
- }
1896
- const itemAfter = destinationStateItems[destination.index];
1897
- const itemAfterRank = (_f = itemAfter == null ? void 0 : itemAfter._metadata) == null ? void 0 : _f.orderRank;
1898
- let itemAfterRankParsed;
1899
- if (itemAfterRank) {
1900
- itemAfterRankParsed = lexorank.LexoRank.parse(itemAfterRank);
1901
- } else if (destinationStateIndex === states.length - 1) {
1902
- itemAfterRankParsed = lexorank.LexoRank.max();
1903
- } else {
1904
- itemAfterRankParsed = lexorank.LexoRank.parse(globalStateMaximumRank);
1905
- }
1906
- newOrder = itemBeforeRankParsed.between(itemAfterRankParsed).toString();
1907
- }
1908
- setPatchingIds([...patchingIds, draggableId]);
1909
- toast.push({
1910
- status: "info",
1911
- title: "Updating document state..."
1912
- });
1913
- await move(draggableId, destination, states, newOrder);
1914
- setPatchingIds(ids => ids.filter(id => id !== draggableId));
1915
- }, [data, patchingIds, toast, move, states]);
1916
- const uniqueAssignedUsers = React__default.default.useMemo(() => {
1917
- const uniqueUserIds = data.reduce((acc, item) => {
1918
- var _a2;
1919
- const {
1920
- assignees = []
1921
- } = (_a2 = item._metadata) != null ? _a2 : {};
1922
- const newAssignees = (assignees == null ? void 0 : assignees.length) ? assignees.filter(a => !acc.includes(a)) : [];
1923
- return newAssignees.length ? [...acc, ...newAssignees] : acc;
1924
- }, []);
1925
- return userList.filter(u => uniqueUserIds.includes(u.id));
1926
- }, [data, userList]);
1927
- const [selectedUserIds, setSelectedUserIds] = React__default.default.useState(uniqueAssignedUsers.map(u => u.id));
1928
- const toggleSelectedUser = React__default.default.useCallback(userId => {
1929
- setSelectedUserIds(prev => prev.includes(userId) ? prev.filter(u => u !== userId) : [...prev, userId]);
1930
- }, []);
1931
- const resetSelectedUsers = React__default.default.useCallback(() => {
1932
- setSelectedUserIds([]);
1933
- }, []);
1934
- const [selectedSchemaTypes, setSelectedSchemaTypes] = React__default.default.useState(schemaTypes);
1935
- const toggleSelectedSchemaType = React__default.default.useCallback(schemaType => {
1936
- setSelectedSchemaTypes(prev => prev.includes(schemaType) ? prev.filter(u => u !== schemaType) : [...prev, schemaType]);
1937
- }, []);
1938
- const [invalidDocumentIds, setInvalidDocumentIds] = React__default.default.useState([]);
1939
- const toggleInvalidDocumentId = React__default.default.useCallback((docId, action) => {
1940
- setInvalidDocumentIds(prev => action === "ADD" ? [...prev, docId] : prev.filter(id => id !== docId));
1941
- }, []);
1942
- const Clone = React__default.default.useCallback((provided, snapshot, rubric) => {
1943
- const item = data.find(doc => {
1944
- var _a2;
1945
- return ((_a2 = doc == null ? void 0 : doc._metadata) == null ? void 0 : _a2.documentId) === rubric.draggableId;
1946
- });
1947
- return /* @__PURE__ */jsxRuntime.jsx("div", {
1948
- ...provided.draggableProps,
1949
- ...provided.dragHandleProps,
1950
- ref: provided.innerRef,
1951
- children: item ? /* @__PURE__ */jsxRuntime.jsx(DocumentCard, {
1952
- isDragDisabled: false,
1953
- isPatching: false,
1954
- userRoleCanDrop: true,
1955
- isDragging: snapshot.isDragging,
1956
- item,
1957
- states,
1958
- toggleInvalidDocumentId,
1959
- userList
1960
- }) : /* @__PURE__ */jsxRuntime.jsx(sanityPluginUtils.Feedback, {
1961
- title: "Item not found",
1962
- tone: "caution"
1963
- })
1964
- });
1965
- }, [data, states, toggleInvalidDocumentId, userList]);
1966
- if (!(states == null ? void 0 : states.length)) {
1967
- return /* @__PURE__ */jsxRuntime.jsx(ui.Container, {
1968
- width: 1,
1969
- padding: 5,
1970
- children: /* @__PURE__ */jsxRuntime.jsx(sanityPluginUtils.Feedback, {
1971
- tone: "caution",
1972
- title: "Plugin options error",
1973
- description: "No States defined in plugin config"
1974
- })
1975
- });
1976
- }
1977
- if (error && !data.length) {
1978
- return /* @__PURE__ */jsxRuntime.jsx(ui.Container, {
1979
- width: 1,
1980
- padding: 5,
1981
- children: /* @__PURE__ */jsxRuntime.jsx(sanityPluginUtils.Feedback, {
1982
- tone: "critical",
1983
- title: "Error querying for Workflow documents"
1984
- })
1985
- });
1986
- }
1987
- return /* @__PURE__ */jsxRuntime.jsxs(ui.Flex, {
1988
- direction: "column",
1989
- height: "fill",
1990
- overflow: "hidden",
1991
- children: [/* @__PURE__ */jsxRuntime.jsx(Verify, {
1992
- data,
1993
- userList,
1994
- states
1995
- }), /* @__PURE__ */jsxRuntime.jsx(Filters, {
1996
- uniqueAssignedUsers,
1997
- selectedUserIds,
1998
- toggleSelectedUser,
1999
- resetSelectedUsers,
2000
- schemaTypes,
2001
- selectedSchemaTypes,
2002
- toggleSelectedSchemaType
2003
- }), /* @__PURE__ */jsxRuntime.jsx(dnd.DragDropContext, {
2004
- onDragStart: handleDragStart,
2005
- onDragEnd: handleDragEnd,
2006
- children: /* @__PURE__ */jsxRuntime.jsx(ui.Grid, {
2007
- columns: states.length,
2008
- height: "fill",
2009
- children: states.map((state, stateIndex) => {
2010
- var _a2, _b2;
2011
- const userRoleCanDrop = ((_a2 = state == null ? void 0 : state.roles) == null ? void 0 : _a2.length) ? arraysContainMatchingString(state.roles, userRoleNames) : true;
2012
- const isDropDisabled = !userRoleCanDrop || undroppableStates.includes(state.id);
2013
- return /* @__PURE__ */jsxRuntime.jsx(ui.Card, {
2014
- borderLeft: stateIndex > 0,
2015
- tone: defaultCardTone,
2016
- children: /* @__PURE__ */jsxRuntime.jsxs(ui.Flex, {
2017
- direction: "column",
2018
- height: "fill",
2019
- children: [/* @__PURE__ */jsxRuntime.jsx(StateTitle, {
2020
- state,
2021
- requireAssignment: (_b2 = state.requireAssignment) != null ? _b2 : false,
2022
- userRoleCanDrop,
2023
- isDropDisabled,
2024
- draggingFrom,
2025
- documentCount: filterItemsAndSort(data, state.id, selectedUserIds, selectedSchemaTypes).length
2026
- }), /* @__PURE__ */jsxRuntime.jsx(ui.Box, {
2027
- flex: 1,
2028
- children: /* @__PURE__ */jsxRuntime.jsx(dnd.Droppable, {
2029
- droppableId: state.id,
2030
- isDropDisabled,
2031
- mode: "virtual",
2032
- renderClone: Clone,
2033
- children: (provided, snapshot) => /* @__PURE__ */jsxRuntime.jsxs(ui.Card, {
2034
- ref: provided.innerRef,
2035
- tone: snapshot.isDraggingOver ? "primary" : defaultCardTone,
2036
- height: "fill",
2037
- children: [loading ? /* @__PURE__ */jsxRuntime.jsx(ui.Flex, {
2038
- padding: 5,
2039
- align: "center",
2040
- justify: "center",
2041
- children: /* @__PURE__ */jsxRuntime.jsx(ui.Spinner, {
2042
- muted: true
2043
- })
2044
- }) : null, /* @__PURE__ */jsxRuntime.jsx(DocumentList, {
2045
- data,
2046
- invalidDocumentIds,
2047
- patchingIds,
2048
- selectedSchemaTypes,
2049
- selectedUserIds,
2050
- state,
2051
- states,
2052
- toggleInvalidDocumentId,
2053
- user,
2054
- userList,
2055
- userRoleCanDrop
2056
- })]
2057
- })
2058
- })
2059
- })]
2060
- })
2061
- }, state.id);
2062
- })
2063
- })
2064
- })]
2065
- });
2066
- }
2067
- const workflowTool = options => ({
2068
- name: "workflow",
2069
- title: "Workflow",
2070
- component: WorkflowTool,
2071
- icon: icons.SplitVerticalIcon,
2072
- options
2073
- });
2074
- const workflow = sanity.definePlugin(function () {
2075
- let config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_CONFIG;
2076
- const {
2077
- schemaTypes,
2078
- states
2079
- } = {
2080
- ...DEFAULT_CONFIG,
2081
- ...config
2082
- };
2083
- if (!(states == null ? void 0 : states.length)) {
2084
- throw new Error('Workflow plugin: Missing "states" in config');
2085
- }
2086
- if (!(schemaTypes == null ? void 0 : schemaTypes.length)) {
2087
- throw new Error('Workflow plugin: Missing "schemaTypes" in config');
2088
- }
2089
- return {
2090
- name: "sanity-plugin-workflow",
2091
- schema: {
2092
- types: [metadata(states)]
2093
- },
2094
- // TODO: Remove 'workflow.metadata' from list of new document types
2095
- // ...
2096
- studio: {
2097
- components: {
2098
- layout: props => WorkflowProvider({
2099
- ...props,
2100
- workflow: {
2101
- schemaTypes,
2102
- states
2103
- }
2104
- })
2105
- }
2106
- },
2107
- form: {
2108
- components: {
2109
- input: props => {
2110
- if (props.id === "root" && sanity.isObjectInputProps(props) && schemaTypes.includes(props.schemaType.name)) {
2111
- return WorkflowSignal(props);
2112
- }
2113
- return props.renderDefault(props);
2114
- }
2115
- }
2116
- },
2117
- document: {
2118
- actions: (prev, context) => {
2119
- if (!schemaTypes.includes(context.schemaType)) {
2120
- return prev;
2121
- }
2122
- return [props => BeginWorkflow(props), props => AssignWorkflow(props), ...states.map(state => props => UpdateWorkflow(props, state)), props => CompleteWorkflow(props), ...prev];
2123
- },
2124
- badges: (prev, context) => {
2125
- if (!schemaTypes.includes(context.schemaType)) {
2126
- return prev;
2127
- }
2128
- const {
2129
- documentId,
2130
- currentUser
2131
- } = context;
2132
- if (!documentId) {
2133
- return prev;
2134
- }
2135
- return [() => StateBadge(documentId), () => AssigneesBadge(documentId, currentUser), ...prev];
2136
- }
2137
- },
2138
- tools: [
2139
- // TODO: These configs could be read from Context
2140
- workflowTool({
2141
- schemaTypes,
2142
- states
2143
- })]
2144
- };
2145
- });
2146
- exports.workflow = workflow;
2147
- //# sourceMappingURL=index.js.map