directus-extension-flow-manager 1.1.1 → 1.2.0
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/README.md +6 -1
- package/dist/index.js +1 -1
- package/package.json +6 -4
- package/src/module.vue +46 -13
package/README.md
CHANGED
|
@@ -6,9 +6,11 @@ You can install it via ``npm install directus-extension-flow-manager``
|
|
|
6
6
|
- [x] Duplicate flow
|
|
7
7
|
- [x] Export and import flow
|
|
8
8
|
- [x] Add flow validation when Restore
|
|
9
|
+
- [x] Feature for keeping original flow id when restore
|
|
10
|
+
- [ ] Add flow grouping
|
|
9
11
|
|
|
10
12
|
Screenshoots
|
|
11
|
-

|
|
13
|
+

|
|
12
14
|
|
|
13
15
|
Changelogs:
|
|
14
16
|
- 1.0.0: (13 July 2023)
|
|
@@ -18,5 +20,8 @@ Changelogs:
|
|
|
18
20
|
* Allow user to click the flow row and bring to flow detail page
|
|
19
21
|
- 1.1.1: (02 August 2023)
|
|
20
22
|
* Add flow validation on restore
|
|
23
|
+
- 1.2.1: (14 September 2023)
|
|
24
|
+
* Add feature for keeping original flow id when restore
|
|
25
|
+
* Add New flow name textfield on restore confirmation dialog
|
|
21
26
|
|
|
22
27
|
If you want to contribute kindly to create a PR and if you want to request a feature or report of a bug kindly create the Issue
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{useStores as e,useApi as t,defineModule as o}from"@directus/extensions-sdk";import{defineComponent as n,ref as i,unref as
|
|
1
|
+
import{useStores as e,useApi as t,defineModule as o}from"@directus/extensions-sdk";import{defineComponent as n,ref as i,unref as l,resolveComponent as a,resolveDirective as r,openBlock as s,createBlock as d,withCtx as u,withDirectives as c,createVNode as p,createCommentVNode as v,normalizeClass as m,createTextVNode as f,toDisplayString as g,createElementVNode as y,createElementBlock as w,Fragment as h,renderList as b,pushScopeId as _,popScopeId as k}from"vue";import{useRouter as C}from"vue-router";var x=n({setup(){const{useFlowsStore:o,useNotificationsStore:n,useCollectionsStore:a}=e(),r=o(),s=n(),d=a(),u=t(),c=C(),p=i(r.flows),{allCollections:v}=d,m=v.reduce(((e,t)=>(e[t.collection]=!0,e)),{}),f=i(null),g=i({}),y=i(!1),w=i([]),h=i([{text:"",value:"icon",width:50,sortable:!1},{text:"Status",value:"status"},{text:"Name",value:"name",width:400}]),b=i(""),_=i(!1);return{headers:h,flows:p,restoredFile:f,restoreConfirmationDialog:y,errors:w,duplicate:k,backup:async function(e){const t={id:e.id,name:e.name,icon:e.icon,color:e.color,description:e.description,trigger:e.trigger,options:e.options,operation:e.operation,operations:e.operations.map((e=>({id:e.id,name:e.name,key:e.key,type:e.type,position_x:e.position_x,position_y:e.position_y,options:e.options,resolve:e.resolve,reject:e.reject})))},o=new Blob([JSON.stringify(t,null,2)],{type:"application/json"});var n=window.URL.createObjectURL(o),i=document.createElement("a");i.href=n,i.setAttribute("download",`${function(){const e=new Date,t=e.getFullYear(),o=(e.getMonth()+1).toString().padStart(2,"0"),n=e.getDate().toString().padStart(2,"0"),i=e.getHours().toString().padStart(2,"0"),l=e.getMinutes().toString().padStart(2,"0"),a=e.getSeconds().toString().padStart(2,"0");return`${t}${o}${n}${i}${l}${a}`}()}-${e.name}.json`),document.body.appendChild(i),i.click()},onRestoredFileChanged:function(e){var t;const o=null==(t=null==e?void 0:e.target)?void 0:t.files[0];if(!o)return;const n=new FileReader;n.onload=async e=>{var t;try{const o=null==(t=e.target)?void 0:t.result,n=JSON.parse(o);if(w.value=[],(null==n?void 0:n.trigger)||w.value.push("Trigger is required"),(null==n?void 0:n.options)||w.value.push("Flow Options are required"),null==n?void 0:n.operations)for(const e of n.operations)["item-read","item-create","item-update","item-delete"].includes(e.type)&&(m[e.options.collection]||w.value.push(`Collection "${e.options.collection}"" does not exist on ${e.name} operation`));y.value=!0,g.value=n,b.value=`${n.name} - Copy`,n.id?_.value=!0:_.value=!1}catch(e){console.log(e)}},n.readAsText(o)},onRestoreButtonClicked:function(){var e;null==(e=f.value)||e.click()},goToFlow:function({item:e}){c.push(`/settings/flows/${e.id}`)},onConfirmRestore:function(){k(g.value,!1),y.value=!1},flowDuplicatedName:b,isPreviousIdPersisted:_,restoredFileObj:g};async function k(e,t=!0){try{let o=function(t){const o={};function i(e){return t.find((t=>t.id===e))}function l(e){if(!e)return null;return{name:e.name,position_x:e.position_x,position_y:e.position_y,key:e.key,type:e.type,options:e.options,flow:n.data.data.id,resolve:l(i(e.resolve)),reject:l(i(e.reject))}}const a=i(e.operation);return o.name=null==a?void 0:a.name,o.position_x=null==a?void 0:a.position_x,o.position_y=null==a?void 0:a.position_y,o.key=null==a?void 0:a.key,o.type=null==a?void 0:a.type,o.options=null==a?void 0:a.options,o.flow=n.data.data.id,o.resolve=l(i((null==a?void 0:a.resolve)||null)),o.reject=l(i((null==a?void 0:a.reject)||null)),o};const n=await u.post("/flows",{id:!t&&_.value?e.id:void 0,name:t?e.name:b.value,status:"inactive",icon:e.icon,accountability:e.accountability,description:e.description,trigger:e.trigger,options:e.options}),i=o(e.operations);await u.patch(`/flows/${n.data.data.id}`,{operation:e.operation?i:null}),await r.hydrate(),p.value=l(r.flows),_.value=!1,s.add({type:"success",title:t?"Flow Duplicated successfully":`Flow "${e.name}" restored successfully`,closeable:!0,persist:!0})}catch(o){s.add({type:"error",title:t?"Flow Duplication failed":`Failed to restore Flow "${e.name}"`,closeable:!0,persist:!0})}finally{f.value&&(f.value.value=null)}}}});const F=e=>(_("data-v-b52a34f6"),e=e(),k(),e),S={key:0},$=F((()=>y("div",{style:{"margin-top":"15px"}},"There are some errors in the file you are trying to restore. Do you want to continue?",-1))),D={key:1},j=[F((()=>y("div",{style:{"margin-top":"15px"}},"Do you want to continue?",-1)))];var N=[],R=[];!function(e,t){if(e&&"undefined"!=typeof document){var o,n=!0===t.prepend?"prepend":"append",i=!0===t.singleTag,l="string"==typeof t.container?document.querySelector(t.container):document.getElementsByTagName("head")[0];if(i){var a=N.indexOf(l);-1===a&&(a=N.push(l)-1,R[a]={}),o=R[a]&&R[a][n]?R[a][n]:R[a][n]=r()}else o=r();65279===e.charCodeAt(0)&&(e=e.substring(1)),o.styleSheet?o.styleSheet.cssText+=e:o.appendChild(document.createTextNode(e))}function r(){var e=document.createElement("style");if(e.setAttribute("type","text/css"),t.attributes)for(var o=Object.keys(t.attributes),i=0;i<o.length;i++)e.setAttribute(o[i],t.attributes[o[i]]);var a="prepend"===n?"afterbegin":"beforeend";return l.insertAdjacentElement(a,e),e}}(".active-chip[data-v-b52a34f6] {\n background-color: var(--primary);\n}\n.inactive-chip[data-v-b52a34f6] {\n background-color: var(--foreground-subdued);\n}",{}),x.render=function(e,t,o,n,i,l){const _=a("v-icon"),k=a("v-chip"),C=a("v-list-item-icon"),x=a("v-list-item-content"),F=a("v-list-item"),N=a("v-list"),R=a("v-menu"),T=a("v-table"),O=a("v-button"),A=a("v-card-title"),P=a("v-input"),U=a("v-checkbox"),B=a("v-error"),E=a("v-card-text"),I=a("v-card-actions"),V=a("v-card"),M=a("v-dialog"),q=a("private-view"),J=r("tooltip");return s(),d(q,{title:"Flow Manager"},{actions:u((()=>[c((s(),d(O,{rounded:"",icon:"",onClick:e.onRestoreButtonClicked},{default:u((()=>[p(_,{name:"file_upload"})])),_:1},8,["onClick"])),[[J,"Restore",void 0,{bottom:!0}]])])),default:u((()=>[p(T,{items:e.flows,headers:e.headers,"onUpdate:headers":t[0]||(t[0]=t=>e.headers=t),"onClick:row":e.goToFlow},{"item.icon":u((({item:e})=>[e.icon?(s(),d(_,{key:0,name:e.icon},null,8,["name"])):v("v-if",!0)])),"item.status":u((({item:e})=>[p(k,{rounded:"",class:m("active"===e.status?"active-chip":"inactive-chip")},{default:u((()=>[f(g(e.status),1)])),_:2},1032,["class"])])),"item-append":u((({item:t})=>[p(R,{placement:"bottom-end","show-arrow":"","close-on-content-click":!0},{activator:u((({toggle:e})=>[p(_,{name:"more_vert",class:"ctx-toggle",onClick:e},null,8,["onClick"])])),default:u((()=>[p(N,null,{default:u((()=>[p(F,{clickable:"",onClick:o=>e.duplicate(t)},{default:u((()=>[p(C,null,{default:u((()=>[p(_,{name:"content_copy"})])),_:1}),p(x,null,{default:u((()=>[f(" Duplicate ")])),_:1})])),_:2},1032,["onClick"]),p(F,{clickable:"",onClick:o=>e.backup(t)},{default:u((()=>[p(C,null,{default:u((()=>[p(_,{name:"file_download"})])),_:1}),p(x,null,{default:u((()=>[f(" Backup ")])),_:1})])),_:2},1032,["onClick"])])),_:2},1024)])),_:2},1024)])),_:2},1032,["items","headers","onClick:row"]),y("input",{ref:"restoredFile",type:"file",accept:"application/json",onChange:t[1]||(t[1]=(...t)=>e.onRestoredFileChanged&&e.onRestoredFileChanged(...t)),style:{display:"none"}},null,544),p(M,{"model-value":e.restoreConfirmationDialog,persistent:!0,"onUpdate:modelValue":t[5]||(t[5]=t=>e.restoreConfirmationDialog=!1)},{default:u((()=>[p(V,null,{default:u((()=>[p(A,null,{default:u((()=>[f("Confirmation Dialog")])),_:1}),p(E,null,{default:u((()=>[c(p(P,{placeholder:"Flow Name",modelValue:e.flowDuplicatedName,"onUpdate:modelValue":t[2]||(t[2]=t=>e.flowDuplicatedName=t)},null,8,["modelValue"]),[[J,"Flow Name",void 0,{bottom:!0}]]),p(U,{style:{"margin-top":"4px"},label:"Keep the same flow id as the original flow","model-value":e.isPreviousIdPersisted,disabled:!e.restoredFileObj.id,"onUpdate:modelValue":t[3]||(t[3]=t=>e.isPreviousIdPersisted=t)},null,8,["model-value","disabled"]),e.errors.length?(s(),w("div",S,[(s(!0),w(h,null,b(e.errors,((e,t)=>(s(),d(B,{key:`errorIndex-${t}`,error:{extensions:{code:"Error"},message:e}},null,8,["error"])))),128)),$])):(s(),w("div",D,j))])),_:1}),p(I,null,{default:u((()=>[p(O,{secondary:"",onClick:t[4]||(t[4]=t=>e.restoreConfirmationDialog=!1)},{default:u((()=>[f(" Cancel ")])),_:1}),p(O,{onClick:e.onConfirmRestore},{default:u((()=>[f(" Continue ")])),_:1},8,["onClick"])])),_:1})])),_:1})])),_:1},8,["model-value"])])),_:1})},x.__scopeId="data-v-b52a34f6",x.__file="src/module.vue";var T=o({id:"flow-manager",name:"Flow Manager",icon:"bolt",routes:[{path:"",component:x}]});export{T as default};
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "directus-extension-flow-manager",
|
|
3
3
|
"description": "This is a custom module for managing Flow",
|
|
4
4
|
"icon": "extension",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.2.0",
|
|
6
6
|
"author": "Bagus Andreanto<andreanto.bagus@gmail.com>",
|
|
7
7
|
"homepage": "https://github.com/baguse/directus-extension-flow-manager",
|
|
8
8
|
"license": "MIT",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
],
|
|
14
14
|
"directus:extension": {
|
|
15
15
|
"type": "module",
|
|
16
|
-
"path": "
|
|
16
|
+
"path": "/myproject/project/freelance/directus/latest/extensions/modules/flow-manager/index.js",
|
|
17
17
|
"source": "src/index.ts",
|
|
18
18
|
"host": "^9.26.0"
|
|
19
19
|
},
|
|
@@ -24,9 +24,11 @@
|
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@directus/extensions-sdk": "9.26.0",
|
|
27
|
+
"@vue-flow/core": "^1.22.2",
|
|
28
|
+
"sass": "^1.63.6",
|
|
27
29
|
"typescript": "^5.1.6",
|
|
28
30
|
"vue": "^3.3.4",
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
"vue-router": "^4.2.4",
|
|
32
|
+
"vue3-html2pdf": "^1.1.2"
|
|
31
33
|
}
|
|
32
34
|
}
|
package/src/module.vue
CHANGED
|
@@ -43,9 +43,24 @@
|
|
|
43
43
|
<v-card>
|
|
44
44
|
<v-card-title>Confirmation Dialog</v-card-title>
|
|
45
45
|
<v-card-text>
|
|
46
|
-
<v-
|
|
47
|
-
<
|
|
48
|
-
|
|
46
|
+
<v-input placeholder="Flow Name" v-model="flowDuplicatedName" v-tooltip.bottom="'Flow Name'" />
|
|
47
|
+
<v-checkbox
|
|
48
|
+
style="margin-top: 4px"
|
|
49
|
+
label="Keep the same flow id as the original flow"
|
|
50
|
+
:model-value="isPreviousIdPersisted"
|
|
51
|
+
:disabled="restoredFileObj.id ? false : true"
|
|
52
|
+
@update:model-value="isPreviousIdPersisted = $event"
|
|
53
|
+
/>
|
|
54
|
+
<div v-if="errors.length">
|
|
55
|
+
<v-error
|
|
56
|
+
v-for="(error, indexError) in errors"
|
|
57
|
+
:key="`errorIndex-${indexError}`"
|
|
58
|
+
:error="{ extensions: { code: 'Error' }, message: error }"
|
|
59
|
+
></v-error>
|
|
60
|
+
<div style="margin-top: 15px">There are some errors in the file you are trying to restore. Do you want to continue?</div>
|
|
61
|
+
</div>
|
|
62
|
+
<div v-else>
|
|
63
|
+
<div style="margin-top: 15px">Do you want to continue?</div>
|
|
49
64
|
</div>
|
|
50
65
|
</v-card-text>
|
|
51
66
|
<v-card-actions>
|
|
@@ -145,6 +160,9 @@ export default defineComponent({
|
|
|
145
160
|
},
|
|
146
161
|
]);
|
|
147
162
|
|
|
163
|
+
const flowDuplicatedName = ref("");
|
|
164
|
+
const isPreviousIdPersisted = ref(false);
|
|
165
|
+
|
|
148
166
|
return {
|
|
149
167
|
headers,
|
|
150
168
|
flows,
|
|
@@ -156,13 +174,17 @@ export default defineComponent({
|
|
|
156
174
|
onRestoredFileChanged,
|
|
157
175
|
onRestoreButtonClicked,
|
|
158
176
|
goToFlow,
|
|
159
|
-
onConfirmRestore
|
|
177
|
+
onConfirmRestore,
|
|
178
|
+
flowDuplicatedName,
|
|
179
|
+
isPreviousIdPersisted,
|
|
180
|
+
restoredFileObj,
|
|
160
181
|
};
|
|
161
182
|
|
|
162
183
|
async function duplicate(item: IFlow, isDuplicate = true) {
|
|
163
184
|
try {
|
|
164
185
|
const response = await api.post("/flows", {
|
|
165
|
-
|
|
186
|
+
id: !isDuplicate && isPreviousIdPersisted.value ? item.id : undefined,
|
|
187
|
+
name: isDuplicate ? item.name : flowDuplicatedName.value,
|
|
166
188
|
status: "inactive",
|
|
167
189
|
icon: item.icon,
|
|
168
190
|
accountability: item.accountability,
|
|
@@ -220,15 +242,24 @@ export default defineComponent({
|
|
|
220
242
|
|
|
221
243
|
await flowsStore.hydrate();
|
|
222
244
|
flows.value = unref(flowsStore.flows);
|
|
245
|
+
isPreviousIdPersisted.value = false;
|
|
223
246
|
|
|
224
247
|
notificationsStore.add({
|
|
225
248
|
type: "success",
|
|
226
|
-
title: isDuplicate ? "Flow Duplicated successfully" : `Flow ${item.name} restored successfully`,
|
|
249
|
+
title: isDuplicate ? "Flow Duplicated successfully" : `Flow "${item.name}" restored successfully`,
|
|
227
250
|
closeable: true,
|
|
228
251
|
persist: true,
|
|
229
252
|
});
|
|
230
253
|
} catch (error) {
|
|
231
|
-
|
|
254
|
+
notificationsStore.add({
|
|
255
|
+
type: "error",
|
|
256
|
+
title: isDuplicate ? "Flow Duplication failed" : `Failed to restore Flow "${item.name}"`,
|
|
257
|
+
closeable: true,
|
|
258
|
+
persist: true,
|
|
259
|
+
});
|
|
260
|
+
} finally {
|
|
261
|
+
if (restoredFile.value)
|
|
262
|
+
restoredFile.value.value = null;
|
|
232
263
|
}
|
|
233
264
|
}
|
|
234
265
|
|
|
@@ -249,6 +280,7 @@ export default defineComponent({
|
|
|
249
280
|
operations: Partial<IOperation>[];
|
|
250
281
|
}
|
|
251
282
|
const sanitizedFlow: ISanitizedFlow = {
|
|
283
|
+
id: item.id,
|
|
252
284
|
name: item.name,
|
|
253
285
|
icon: item.icon,
|
|
254
286
|
color: item.color,
|
|
@@ -307,11 +339,14 @@ export default defineComponent({
|
|
|
307
339
|
}
|
|
308
340
|
}
|
|
309
341
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
342
|
+
restoreConfirmationDialog.value = true;
|
|
343
|
+
restoredFileObj.value = parsedResult;
|
|
344
|
+
|
|
345
|
+
flowDuplicatedName.value = `${parsedResult.name} - Copy`;
|
|
346
|
+
if (parsedResult.id) {
|
|
347
|
+
isPreviousIdPersisted.value = true;
|
|
313
348
|
} else {
|
|
314
|
-
|
|
349
|
+
isPreviousIdPersisted.value = false;
|
|
315
350
|
}
|
|
316
351
|
} catch (error) {
|
|
317
352
|
console.log(error);
|
|
@@ -321,12 +356,10 @@ export default defineComponent({
|
|
|
321
356
|
}
|
|
322
357
|
|
|
323
358
|
function onRestoreButtonClicked() {
|
|
324
|
-
console.log("restore button clicked");
|
|
325
359
|
restoredFile.value?.click();
|
|
326
360
|
}
|
|
327
361
|
|
|
328
362
|
function goToFlow({ item }: { item: IFlow }) {
|
|
329
|
-
console.log("go to flow", item);
|
|
330
363
|
router.push(`/settings/flows/${item.id}`);
|
|
331
364
|
}
|
|
332
365
|
|