Mirror of https://github.com/roostorg/coop github.com/roostorg/coop
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 3a75984654db888a95d657c181e4e2a1c3a46b2d 181 lines 5.7 kB view raw
1import { 2 namedOperations, 3 useGQLBulkActionExecutionMutation, 4 useGQLBulkActionsFormDataQuery, 5} from '@/graphql/generated'; 6import { stripTypename } from '@/graphql/inputHelpers'; 7import { ItemIdentifier } from '@roostorg/types'; 8import { Select } from 'antd'; 9import orderBy from 'lodash/orderBy'; 10import { useCallback, useMemo, useState } from 'react'; 11 12import { selectFilterByLabelOption } from '@/webpages/dashboard/components/antDesignUtils'; 13import CoopButton from '@/webpages/dashboard/components/CoopButton'; 14import CoopModal from '@/webpages/dashboard/components/CoopModal'; 15import PolicyDropdown from '@/webpages/dashboard/components/PolicyDropdown'; 16 17const { Option } = Select; 18 19export default function ItemAction(props: { 20 itemIdentifier: ItemIdentifier; 21 title?: string; 22}) { 23 const { itemIdentifier, title = 'Take action on this item' } = props; 24 25 const { data: queryData } = useGQLBulkActionsFormDataQuery(); 26 const [bulkAction, { loading }] = useGQLBulkActionExecutionMutation({ 27 refetchQueries: [ 28 namedOperations.Query.ItemActionHistory, 29 namedOperations.Query.GetRecentDecisions, 30 ], 31 onCompleted: (data) => { 32 const results = data?.bulkExecuteActions?.results ?? []; 33 const anyFailed = results.some((r) => r.success === false); 34 if (anyFailed) { 35 setModalBody( 36 'One or more actions failed. The callback URL may have returned an error. If your org requires a policy for decisions, select a policy and try again.', 37 ); 38 } else { 39 setModalBody('Actions submitted successfully.'); 40 } 41 setShowModal(true); 42 }, 43 onError: () => { 44 setModalBody('Error submitting actions. Please try again.'); 45 setShowModal(true); 46 }, 47 }); 48 49 const [selectedPolicyIds, setSelectedPolicyIds] = useState<string[]>([]); 50 const [selectedActionIds, setSelectedActionIds] = useState<string[]>([]); 51 const [showModal, setShowModal] = useState(false); 52 const [modalBody, setModalBody] = useState<string>(''); 53 54 const eligibleActions = (queryData?.myOrg?.actions ?? []).filter((it) => 55 it.itemTypes.map((it) => it.id).includes(itemIdentifier.typeId), 56 ); 57 58 const selectOnChange = useCallback( 59 (actionIds: string[]) => setSelectedActionIds(actionIds), 60 [], 61 ); 62 63 const selectDropdownRender = useCallback( 64 (menu: React.ReactElement) => { 65 if (eligibleActions.length === 0) { 66 return ( 67 <div> 68 <div className="text-coop-alert-red">No actions available</div> 69 {menu} 70 </div> 71 ); 72 } 73 return menu; 74 }, 75 [eligibleActions.length], 76 ); 77 78 const policies = queryData?.myOrg?.policies; 79 const policiesMemo = useMemo( 80 () => (policies ? policies.map((p) => stripTypename(p)) : []), 81 [policies], 82 ); 83 84 const policiesDropdownOnChange = useCallback( 85 (policyIds: string | readonly string[]) => { 86 if (Array.isArray(policyIds)) { 87 setSelectedPolicyIds(policyIds.map((id) => id.toString())); 88 } else { 89 // NB: This cast is required because of a longstanding typescript 90 // issue. See https://github.com/microsoft/TypeScript/issues/17002 for 91 // more details. 92 const policyId = policyIds satisfies 93 | string 94 | readonly string[] as string; 95 setSelectedPolicyIds([policyId]); 96 } 97 }, 98 [], 99 ); 100 101 const buttonOnClick = useCallback( 102 async () => 103 bulkAction({ 104 variables: { 105 input: { 106 itemTypeId: itemIdentifier.typeId, 107 actionIds: selectedActionIds, 108 itemIds: [itemIdentifier.id], 109 policyIds: selectedPolicyIds, 110 }, 111 }, 112 }), 113 [ 114 bulkAction, 115 itemIdentifier.id, 116 itemIdentifier.typeId, 117 selectedActionIds, 118 selectedPolicyIds, 119 ], 120 ); 121 122 const modalOnClose = useCallback(() => setShowModal(false), []); 123 124 if (eligibleActions.length === 0) { 125 return null; 126 } 127 128 return ( 129 <div className="flex flex-col"> 130 <div className="flex flex-col items-start mb-2"> 131 <div className="text-base font-semibold">{title}</div> 132 </div> 133 <div className="flex flex-row gap-4"> 134 <div className="flex flex-col items-start"> 135 <div> 136 <Select 137 className="w-80" 138 mode="multiple" 139 maxTagCount={1} 140 placeholder="Select action" 141 dropdownMatchSelectWidth={false} 142 filterOption={selectFilterByLabelOption} 143 onChange={selectOnChange} 144 dropdownRender={selectDropdownRender} 145 > 146 {orderBy(eligibleActions, ['name']).map((action) => ( 147 <Option key={action.id} value={action.id} label={action.name}> 148 {action.name} 149 </Option> 150 ))} 151 </Select> 152 </div> 153 </div> 154 <div className="flex flex-col items-start"> 155 <div> 156 <PolicyDropdown 157 className="w-80" 158 policies={policiesMemo} 159 maxTagCount={1} 160 onChange={policiesDropdownOnChange} 161 selectedPolicyIds={selectedPolicyIds} 162 multiple={ 163 queryData?.myOrg?.allowMultiplePoliciesPerAction ?? false 164 } 165 /> 166 </div> 167 </div> 168 <CoopButton 169 title="Submit Actions" 170 size="small" 171 onClick={buttonOnClick} 172 loading={loading} 173 disabled={selectedActionIds.length === 0} 174 /> 175 </div> 176 <CoopModal visible={showModal} onClose={modalOnClose}> 177 {modalBody} 178 </CoopModal> 179 </div> 180 ); 181}