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

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