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