directus-extension-flow-manager 1.0.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/.nvmrc +1 -0
- package/README.md +1 -0
- package/package.json +31 -0
- package/src/index.ts +14 -0
- package/src/module.vue +191 -0
- package/src/shims.d.ts +5 -0
- package/tsconfig.json +28 -0
package/.nvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
v20
|
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# directus-extension-flow-manager
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "directus-extension-flow-manager",
|
|
3
|
+
"description": "This is a custom module for managing Flow",
|
|
4
|
+
"icon": "extension",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"author": "Bagus Andreanto<andreanto.bagus@gmail.com>",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"directus",
|
|
9
|
+
"directus-extension",
|
|
10
|
+
"directus-custom-module"
|
|
11
|
+
],
|
|
12
|
+
"directus:extension": {
|
|
13
|
+
"type": "module",
|
|
14
|
+
"path": "dist/index.js",
|
|
15
|
+
"source": "src/index.ts",
|
|
16
|
+
"host": "^9.26.0"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "directus-extension build",
|
|
20
|
+
"dev": "directus-extension build -w --no-minify",
|
|
21
|
+
"link": "directus-extension link"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@directus/extensions-sdk": "9.26.0",
|
|
25
|
+
"typescript": "^5.1.6",
|
|
26
|
+
"vue": "^3.3.4"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"sass": "^1.63.6"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineModule } from '@directus/extensions-sdk';
|
|
2
|
+
import ModuleComponent from './module.vue';
|
|
3
|
+
|
|
4
|
+
export default defineModule({
|
|
5
|
+
id: 'flow-manager',
|
|
6
|
+
name: 'Flow Manager',
|
|
7
|
+
icon: 'box',
|
|
8
|
+
routes: [
|
|
9
|
+
{
|
|
10
|
+
path: '',
|
|
11
|
+
component: ModuleComponent,
|
|
12
|
+
},
|
|
13
|
+
],
|
|
14
|
+
});
|
package/src/module.vue
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<private-view title="Flow Manager">
|
|
3
|
+
<v-table :items="flows" v-model:headers="headers">
|
|
4
|
+
<template #[`item.icon`]="{ item }">
|
|
5
|
+
<v-icon v-if="item.icon" :name="item.icon" />
|
|
6
|
+
</template>
|
|
7
|
+
<template #[`item.status`]="{ item }">
|
|
8
|
+
<v-chip rounded :class="item.status === 'active' ? 'active-chip' : 'inactive-chip'">{{ item.status }}</v-chip>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<template #item-append="{ item }">
|
|
12
|
+
<v-menu placement="bottom-end" show-arrow :close-on-content-click="true">
|
|
13
|
+
<template #activator="{ toggle }">
|
|
14
|
+
<v-icon name="more_vert" class="ctx-toggle" @click="toggle" />
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<v-list>
|
|
18
|
+
<v-list-item clickable @click="duplicate(item)">
|
|
19
|
+
<v-list-item-icon>
|
|
20
|
+
<v-icon name="content_copy" />
|
|
21
|
+
</v-list-item-icon>
|
|
22
|
+
<v-list-item-content> Duplicate </v-list-item-content>
|
|
23
|
+
</v-list-item>
|
|
24
|
+
</v-list>
|
|
25
|
+
</v-menu>
|
|
26
|
+
</template>
|
|
27
|
+
</v-table>
|
|
28
|
+
</private-view>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script lang="ts">
|
|
32
|
+
import { defineComponent, ref, unref } from "vue";
|
|
33
|
+
import { useStores, useApi } from "@directus/extensions-sdk";
|
|
34
|
+
|
|
35
|
+
interface IOperation {
|
|
36
|
+
id: string;
|
|
37
|
+
name: string;
|
|
38
|
+
key: string;
|
|
39
|
+
type: string;
|
|
40
|
+
position_x: number;
|
|
41
|
+
position_y: number;
|
|
42
|
+
options: any;
|
|
43
|
+
resolve: string | null;
|
|
44
|
+
reject: string | null;
|
|
45
|
+
flow: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface IPayload {
|
|
49
|
+
name: string;
|
|
50
|
+
key: string;
|
|
51
|
+
type: string;
|
|
52
|
+
position_x: number;
|
|
53
|
+
position_y: number;
|
|
54
|
+
options: any;
|
|
55
|
+
resolve: IPayload | null;
|
|
56
|
+
reject: IPayload | null;
|
|
57
|
+
flow: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface IFlow {
|
|
61
|
+
id: string;
|
|
62
|
+
name: string;
|
|
63
|
+
icon: string;
|
|
64
|
+
color: null;
|
|
65
|
+
description: null;
|
|
66
|
+
status: string;
|
|
67
|
+
trigger: string;
|
|
68
|
+
accountability: string;
|
|
69
|
+
options: { type: string; scope: string[]; collections: string[] };
|
|
70
|
+
operation: string;
|
|
71
|
+
date_created: "2023-06-26T17:09:53.274Z";
|
|
72
|
+
user_created: "8c4a648e-110d-493f-830a-48054a1d9109";
|
|
73
|
+
operations: IOperation[];
|
|
74
|
+
}
|
|
75
|
+
export default defineComponent({
|
|
76
|
+
setup() {
|
|
77
|
+
const { useFlowsStore, useNotificationsStore } = useStores();
|
|
78
|
+
const flowsStore = useFlowsStore();
|
|
79
|
+
const notificationsStore = useNotificationsStore();
|
|
80
|
+
const api = useApi();
|
|
81
|
+
|
|
82
|
+
const flows = ref(flowsStore.flows);
|
|
83
|
+
|
|
84
|
+
const headers = ref([
|
|
85
|
+
{
|
|
86
|
+
text: "",
|
|
87
|
+
value: "icon",
|
|
88
|
+
width: 50,
|
|
89
|
+
sortable: false,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
text: "Status",
|
|
93
|
+
value: "status",
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
text: "Name",
|
|
97
|
+
value: "name",
|
|
98
|
+
width: 400,
|
|
99
|
+
},
|
|
100
|
+
]);
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
headers,
|
|
104
|
+
flows,
|
|
105
|
+
duplicate,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
async function duplicate(item: IFlow) {
|
|
109
|
+
try {
|
|
110
|
+
const response = await api.post("/flows", {
|
|
111
|
+
name: `${item.name} - Copy`,
|
|
112
|
+
status: "inactive",
|
|
113
|
+
icon: item.icon,
|
|
114
|
+
accountability: item.accountability,
|
|
115
|
+
description: item.description,
|
|
116
|
+
trigger: item.trigger,
|
|
117
|
+
options: item.options,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
function transformData(list: IOperation[]) {
|
|
121
|
+
const result: Partial<IPayload> = {};
|
|
122
|
+
|
|
123
|
+
function findItemById(id: string | null) {
|
|
124
|
+
return list.find((item) => item.id === id);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function buildNestedObject(item?: IOperation) {
|
|
128
|
+
if (!item) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const nestedObject: IPayload = {
|
|
133
|
+
name: item.name,
|
|
134
|
+
position_x: item.position_x,
|
|
135
|
+
position_y: item.position_y,
|
|
136
|
+
key: item.key,
|
|
137
|
+
type: item.type,
|
|
138
|
+
options: item.options,
|
|
139
|
+
flow: response.data.data.id,
|
|
140
|
+
resolve: buildNestedObject(findItemById(item.resolve)),
|
|
141
|
+
reject: buildNestedObject(findItemById(item.reject)),
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
return nestedObject;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const firstItem = findItemById(item.operation);
|
|
148
|
+
result.name = firstItem?.name;
|
|
149
|
+
result.position_x = firstItem?.position_x;
|
|
150
|
+
result.position_y = firstItem?.position_y;
|
|
151
|
+
result.key = firstItem?.key;
|
|
152
|
+
result.type = firstItem?.type;
|
|
153
|
+
result.options = firstItem?.options;
|
|
154
|
+
result.flow = response.data.data.id;
|
|
155
|
+
result.resolve = buildNestedObject(findItemById(firstItem?.resolve || null));
|
|
156
|
+
result.reject = buildNestedObject(findItemById(firstItem?.reject || null));
|
|
157
|
+
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const payload = transformData(item.operations);
|
|
162
|
+
|
|
163
|
+
await api.patch(`/flows/${response.data.data.id}`, {
|
|
164
|
+
operation: item.operation ? payload : null,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
await flowsStore.hydrate();
|
|
168
|
+
flows.value = unref(flowsStore.flows);
|
|
169
|
+
|
|
170
|
+
notificationsStore.add({
|
|
171
|
+
type: "success",
|
|
172
|
+
text: "Flow duplicated successfully",
|
|
173
|
+
closeable: true,
|
|
174
|
+
});
|
|
175
|
+
} catch (error) {
|
|
176
|
+
console.log(error);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
</script>
|
|
182
|
+
|
|
183
|
+
<style lang="scss" scoped>
|
|
184
|
+
.active-chip {
|
|
185
|
+
background-color: var(--primary);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.inactive-chip {
|
|
189
|
+
background-color: var(--foreground-subdued);
|
|
190
|
+
}
|
|
191
|
+
</style>
|
package/src/shims.d.ts
ADDED
package/tsconfig.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2019",
|
|
4
|
+
"lib": ["ES2019", "DOM"],
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"noFallthroughCasesInSwitch": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"noImplicitAny": true,
|
|
10
|
+
"noImplicitThis": true,
|
|
11
|
+
"noImplicitReturns": true,
|
|
12
|
+
"noUnusedLocals": true,
|
|
13
|
+
"noUncheckedIndexedAccess": true,
|
|
14
|
+
"noUnusedParameters": true,
|
|
15
|
+
"alwaysStrict": true,
|
|
16
|
+
"strictNullChecks": true,
|
|
17
|
+
"strictFunctionTypes": true,
|
|
18
|
+
"strictBindCallApply": true,
|
|
19
|
+
"strictPropertyInitialization": true,
|
|
20
|
+
"resolveJsonModule": false,
|
|
21
|
+
"skipLibCheck": true,
|
|
22
|
+
"forceConsistentCasingInFileNames": true,
|
|
23
|
+
"allowSyntheticDefaultImports": true,
|
|
24
|
+
"isolatedModules": true,
|
|
25
|
+
"rootDir": "./src"
|
|
26
|
+
},
|
|
27
|
+
"include": ["./src/**/*.ts"]
|
|
28
|
+
}
|