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

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