sanity-plugin-workflow 1.0.0-beta.1 → 1.0.0-beta.11

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