@adminforth/bulk-ai-flow 1.0.6 → 1.1.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/build.log CHANGED
@@ -10,5 +10,5 @@ custom/tsconfig.json
10
10
  custom/visionAction.vue
11
11
  custom/visionTable.vue
12
12
 
13
- sent 23,951 bytes received 115 bytes 48,132.00 bytes/sec
14
- total size is 23,526 speedup is 0.98
13
+ sent 24,032 bytes received 115 bytes 48,294.00 bytes/sec
14
+ total size is 23,613 speedup is 0.98
@@ -25,6 +25,7 @@
25
25
  :tableColumnsIndexes="tableColumnsIndexes"
26
26
  :selected="selected"
27
27
  :isAiResponseReceived="isAiResponseReceived"
28
+ :primaryKey="primaryKey"
28
29
  />
29
30
  <Button
30
31
  class="w-64"
@@ -67,6 +68,7 @@ const tableColumnsIndexes = ref([]);
67
68
  const customFieldNames = ref([]);
68
69
  const selected = ref<any[]>([]);
69
70
  const isAiResponseReceived = ref([]);
71
+ const primaryKey = props.meta.primaryKey;
70
72
 
71
73
  const openDialog = async () => {
72
74
  confirmDialog.value.open();
@@ -82,9 +84,9 @@ const openDialog = async () => {
82
84
  analyzeFields();
83
85
  }
84
86
 
85
- // watch(selected, (val) => {
86
- // console.log('Selected changed:', val);
87
- // }, { deep: true });
87
+ watch(selected, (val) => {
88
+ console.log('Selected changed:', val);
89
+ }, { deep: true });
88
90
 
89
91
  const closeDialog = () => {
90
92
  confirmDialog.value.close();
@@ -105,14 +107,11 @@ function generateTableHeaders(outputFields) {
105
107
  headers.push({ label: 'Field name', fieldName: 'label' });
106
108
  headers.push({ label: 'Source Images', fieldName: 'images' });
107
109
 
108
- if (outputFields.length > 0) {
109
- const sampleField = outputFields[0];
110
- for (const key in sampleField) {
111
- headers.push({
112
- label: formatLabel(key),
113
- fieldName: key,
114
- });
115
- }
110
+ for (const key in outputFields) {
111
+ headers.push({
112
+ label: formatLabel(key),
113
+ fieldName: key,
114
+ });
116
115
  }
117
116
  return headers;
118
117
  }
@@ -144,22 +143,19 @@ function generateTableColumns() {
144
143
 
145
144
  function setSelected() {
146
145
  selected.value = records.value.map(() => ({}));
147
-
148
146
  records.value.forEach((record, index) => {
149
- props.meta.outputFields.forEach((fieldObj, i) => {
150
- for (const key in fieldObj) {
151
- if(isInColumnEnum(key)){
152
- const colEnum = props.meta.columnEnums.find(c => c.name === key);
153
- const object = colEnum.enum.find(item => item.value === record[key]);
154
- selected.value[index][key] = object ? record[key] : null;
155
- } else {
156
- selected.value[index][key] = record[key];
157
- }
147
+ for (const key in props.meta.outputFields) {
148
+ if(isInColumnEnum(key)){
149
+ const colEnum = props.meta.columnEnums.find(c => c.name === key);
150
+ const object = colEnum.enum.find(item => item.value === record[key]);
151
+ selected.value[index][key] = object ? record[key] : null;
152
+ } else {
153
+ selected.value[index][key] = record[key];
158
154
  }
159
- selected.value[index].isChecked = true;
160
- selected.value[index].id = record.id;
161
- isAiResponseReceived.value[index] = true;
162
- });
155
+ }
156
+ selected.value[index].isChecked = true;
157
+ selected.value[index][primaryKey] = record[primaryKey];
158
+ isAiResponseReceived.value[index] = true;
163
159
  });
164
160
  }
165
161
 
@@ -44,32 +44,32 @@
44
44
  </template>
45
45
  <!-- CUSTOM FIELD TEMPLATES -->
46
46
  <template v-for="n in customFieldNames" :key="n" #[`cell:${n}`]="{ item, column }">
47
- <div v-if="isAiResponseReceived[tableColumnsIndexes.findIndex(el => el.id === item.id)]">
47
+ <div v-if="isAiResponseReceived[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])]">
48
48
  <div v-if="isInColumnEnum(n)">
49
49
  <Select
50
50
  :options="convertColumnEnumToSelectOptions(props.meta.columnEnums, n)"
51
- v-model="selected[tableColumnsIndexes.findIndex(el => el.id === item.id)][n]"
51
+ v-model="selected[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n]"
52
52
  >
53
53
  </Select>
54
54
  </div>
55
- <div v-else-if="typeof selected[tableColumnsIndexes.findIndex(el => el.id === item.id)][n] === 'string' || typeof selected[tableColumnsIndexes.findIndex(el => el.id === item.id)][n] === 'object'">
55
+ <div v-else-if="typeof selected[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n] === 'string' || typeof selected[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n] === 'object'">
56
56
  <Textarea
57
57
  class="w-full h-full"
58
58
  type="text"
59
- v-model="selected[tableColumnsIndexes.findIndex(el => el.id === item.id)][n]"
59
+ v-model="selected[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n]"
60
60
  >
61
61
  </Textarea>
62
62
  </div>
63
- <div v-else-if="typeof selected[tableColumnsIndexes.findIndex(el => el.id === item.id)][n] === 'boolean'">
63
+ <div v-else-if="typeof selected[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n] === 'boolean'">
64
64
  <Toggle
65
- v-model="selected[tableColumnsIndexes.findIndex(el => el.id === item.id)][n]"
65
+ v-model="selected[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n]"
66
66
  >
67
67
  </Toggle>
68
68
  </div>
69
69
  <div v-else>
70
70
  <Input
71
71
  type="number"
72
- v-model="selected[tableColumnsIndexes.findIndex(el => el.id === item.id)][n]"
72
+ v-model="selected[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n]"
73
73
  class="w-full "
74
74
  :fullWidth="true"
75
75
  />
@@ -95,7 +95,8 @@ const props = defineProps<{
95
95
  customFieldNames: any,
96
96
  tableColumnsIndexes: any,
97
97
  selected: any,
98
- isAiResponseReceived: boolean[]
98
+ isAiResponseReceived: boolean[],
99
+ primaryKey: any
99
100
  }>();
100
101
 
101
102
  const zoomedImage = ref(null)
@@ -25,6 +25,7 @@
25
25
  :tableColumnsIndexes="tableColumnsIndexes"
26
26
  :selected="selected"
27
27
  :isAiResponseReceived="isAiResponseReceived"
28
+ :primaryKey="primaryKey"
28
29
  />
29
30
  <Button
30
31
  class="w-64"
@@ -67,6 +68,7 @@ const tableColumnsIndexes = ref([]);
67
68
  const customFieldNames = ref([]);
68
69
  const selected = ref<any[]>([]);
69
70
  const isAiResponseReceived = ref([]);
71
+ const primaryKey = props.meta.primaryKey;
70
72
 
71
73
  const openDialog = async () => {
72
74
  confirmDialog.value.open();
@@ -82,9 +84,9 @@ const openDialog = async () => {
82
84
  analyzeFields();
83
85
  }
84
86
 
85
- // watch(selected, (val) => {
86
- // console.log('Selected changed:', val);
87
- // }, { deep: true });
87
+ watch(selected, (val) => {
88
+ console.log('Selected changed:', val);
89
+ }, { deep: true });
88
90
 
89
91
  const closeDialog = () => {
90
92
  confirmDialog.value.close();
@@ -105,14 +107,11 @@ function generateTableHeaders(outputFields) {
105
107
  headers.push({ label: 'Field name', fieldName: 'label' });
106
108
  headers.push({ label: 'Source Images', fieldName: 'images' });
107
109
 
108
- if (outputFields.length > 0) {
109
- const sampleField = outputFields[0];
110
- for (const key in sampleField) {
111
- headers.push({
112
- label: formatLabel(key),
113
- fieldName: key,
114
- });
115
- }
110
+ for (const key in outputFields) {
111
+ headers.push({
112
+ label: formatLabel(key),
113
+ fieldName: key,
114
+ });
116
115
  }
117
116
  return headers;
118
117
  }
@@ -144,22 +143,19 @@ function generateTableColumns() {
144
143
 
145
144
  function setSelected() {
146
145
  selected.value = records.value.map(() => ({}));
147
-
148
146
  records.value.forEach((record, index) => {
149
- props.meta.outputFields.forEach((fieldObj, i) => {
150
- for (const key in fieldObj) {
151
- if(isInColumnEnum(key)){
152
- const colEnum = props.meta.columnEnums.find(c => c.name === key);
153
- const object = colEnum.enum.find(item => item.value === record[key]);
154
- selected.value[index][key] = object ? record[key] : null;
155
- } else {
156
- selected.value[index][key] = record[key];
157
- }
147
+ for (const key in props.meta.outputFields) {
148
+ if(isInColumnEnum(key)){
149
+ const colEnum = props.meta.columnEnums.find(c => c.name === key);
150
+ const object = colEnum.enum.find(item => item.value === record[key]);
151
+ selected.value[index][key] = object ? record[key] : null;
152
+ } else {
153
+ selected.value[index][key] = record[key];
158
154
  }
159
- selected.value[index].isChecked = true;
160
- selected.value[index].id = record.id;
161
- isAiResponseReceived.value[index] = true;
162
- });
155
+ }
156
+ selected.value[index].isChecked = true;
157
+ selected.value[index][primaryKey] = record[primaryKey];
158
+ isAiResponseReceived.value[index] = true;
163
159
  });
164
160
  }
165
161
 
@@ -44,32 +44,32 @@
44
44
  </template>
45
45
  <!-- CUSTOM FIELD TEMPLATES -->
46
46
  <template v-for="n in customFieldNames" :key="n" #[`cell:${n}`]="{ item, column }">
47
- <div v-if="isAiResponseReceived[tableColumnsIndexes.findIndex(el => el.id === item.id)]">
47
+ <div v-if="isAiResponseReceived[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])]">
48
48
  <div v-if="isInColumnEnum(n)">
49
49
  <Select
50
50
  :options="convertColumnEnumToSelectOptions(props.meta.columnEnums, n)"
51
- v-model="selected[tableColumnsIndexes.findIndex(el => el.id === item.id)][n]"
51
+ v-model="selected[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n]"
52
52
  >
53
53
  </Select>
54
54
  </div>
55
- <div v-else-if="typeof selected[tableColumnsIndexes.findIndex(el => el.id === item.id)][n] === 'string' || typeof selected[tableColumnsIndexes.findIndex(el => el.id === item.id)][n] === 'object'">
55
+ <div v-else-if="typeof selected[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n] === 'string' || typeof selected[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n] === 'object'">
56
56
  <Textarea
57
57
  class="w-full h-full"
58
58
  type="text"
59
- v-model="selected[tableColumnsIndexes.findIndex(el => el.id === item.id)][n]"
59
+ v-model="selected[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n]"
60
60
  >
61
61
  </Textarea>
62
62
  </div>
63
- <div v-else-if="typeof selected[tableColumnsIndexes.findIndex(el => el.id === item.id)][n] === 'boolean'">
63
+ <div v-else-if="typeof selected[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n] === 'boolean'">
64
64
  <Toggle
65
- v-model="selected[tableColumnsIndexes.findIndex(el => el.id === item.id)][n]"
65
+ v-model="selected[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n]"
66
66
  >
67
67
  </Toggle>
68
68
  </div>
69
69
  <div v-else>
70
70
  <Input
71
71
  type="number"
72
- v-model="selected[tableColumnsIndexes.findIndex(el => el.id === item.id)][n]"
72
+ v-model="selected[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n]"
73
73
  class="w-full "
74
74
  :fullWidth="true"
75
75
  />
@@ -95,7 +95,8 @@ const props = defineProps<{
95
95
  customFieldNames: any,
96
96
  tableColumnsIndexes: any,
97
97
  selected: any,
98
- isAiResponseReceived: boolean[]
98
+ isAiResponseReceived: boolean[],
99
+ primaryKey: any
99
100
  }>();
100
101
 
101
102
  const zoomedImage = ref(null)
package/dist/index.js CHANGED
@@ -16,19 +16,17 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
16
16
  }
17
17
  // Compile Handlebars templates in outputFields using record fields as context
18
18
  compileOutputFieldsTemplates(record) {
19
- return this.options.outputFields.map((fieldObj) => {
20
- const compiled = {};
21
- for (const [key, templateStr] of Object.entries(fieldObj)) {
22
- try {
23
- const tpl = Handlebars.compile(String(templateStr));
24
- compiled[key] = tpl(record);
25
- }
26
- catch (_a) {
27
- compiled[key] = String(templateStr);
28
- }
19
+ const compiled = {};
20
+ for (const [key, templateStr] of Object.entries(this.options.fillFieldsFromImages)) {
21
+ try {
22
+ const tpl = Handlebars.compile(String(templateStr));
23
+ compiled[key] = tpl(record);
29
24
  }
30
- return compiled;
31
- });
25
+ catch (_a) {
26
+ compiled[key] = String(templateStr);
27
+ }
28
+ }
29
+ return compiled;
32
30
  }
33
31
  modifyResourceConfig(adminforth, resourceConfig) {
34
32
  const _super = Object.create(null, {
@@ -39,30 +37,31 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
39
37
  //check if options names are provided
40
38
  const columns = this.resourceConfig.columns;
41
39
  let columnEnums = [];
42
- for (const field of this.options.outputFields) {
43
- for (const [key, value] of Object.entries(field)) {
44
- const column = columns.find(c => c.name.toLowerCase() === key.toLowerCase());
45
- if (column) {
46
- if (column.enum) {
47
- field[key] = `${value} Select ${key} from the list (USE ONLY VALUE FIELD. USE ONLY VALUES FROM THIS LIST): ${JSON.stringify(column.enum)}`;
48
- columnEnums.push({
49
- name: key,
50
- enum: column.enum,
51
- });
52
- }
53
- }
54
- else {
55
- throw new Error(`⚠️ No column found for key "${key}"`);
40
+ for (const [key, value] of Object.entries(this.options.fillFieldsFromImages)) {
41
+ const column = columns.find(c => c.name.toLowerCase() === key.toLowerCase());
42
+ if (column) {
43
+ if (column.enum) {
44
+ this.options.fillFieldsFromImages[key] = `${value} Select ${key} from the list (USE ONLY VALUE FIELD. USE ONLY VALUES FROM THIS LIST): ${JSON.stringify(column.enum)}`;
45
+ columnEnums.push({
46
+ name: key,
47
+ enum: column.enum,
48
+ });
56
49
  }
57
50
  }
51
+ else {
52
+ throw new Error(`⚠️ No column found for key "${key}"`);
53
+ }
58
54
  }
55
+ const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
56
+ console.log('Primary Key Column:', primaryKeyColumn);
59
57
  const pageInjection = {
60
58
  file: this.componentPath('visionAction.vue'),
61
59
  meta: {
62
60
  pluginInstanceId: this.pluginInstanceId,
63
- outputFields: this.options.outputFields,
61
+ outputFields: this.options.fillFieldsFromImages,
64
62
  actionName: this.options.actionName,
65
63
  columnEnums: columnEnums,
64
+ primaryKey: primaryKeyColumn.name,
66
65
  }
67
66
  };
68
67
  if (!this.resourceConfig.options.pageInjections) {
@@ -101,7 +100,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
101
100
  Each object must contain the following fields: ${JSON.stringify(compiledOutputFields)} Use the exact field names. If it's number field - return only number.
102
101
  Image URLs:`;
103
102
  //send prompt to OpenAI and get response
104
- const chatResponse = yield this.options.adapter.generate({ prompt, inputFileUrls: attachmentFiles });
103
+ const chatResponse = yield this.options.visionAdapter.generate({ prompt, inputFileUrls: attachmentFiles });
105
104
  const resp = chatResponse.response;
106
105
  const topLevelError = chatResponse.error;
107
106
  if (topLevelError || (resp === null || resp === void 0 ? void 0 : resp.error)) {
package/index.ts CHANGED
@@ -14,19 +14,17 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
14
14
  }
15
15
 
16
16
  // Compile Handlebars templates in outputFields using record fields as context
17
- private compileOutputFieldsTemplates(record: any): Record<string, string>[] {
18
- return this.options.outputFields.map((fieldObj) => {
19
- const compiled: Record<string, string> = {};
20
- for (const [key, templateStr] of Object.entries(fieldObj)) {
21
- try {
22
- const tpl = Handlebars.compile(String(templateStr));
23
- compiled[key] = tpl(record);
24
- } catch {
25
- compiled[key] = String(templateStr);
26
- }
17
+ private compileOutputFieldsTemplates(record: any): Record<string, string> {
18
+ const compiled: Record<string, string> = {};
19
+ for (const [key, templateStr] of Object.entries(this.options.fillFieldsFromImages)) {
20
+ try {
21
+ const tpl = Handlebars.compile(String(templateStr));
22
+ compiled[key] = tpl(record);
23
+ } catch {
24
+ compiled[key] = String(templateStr);
27
25
  }
28
- return compiled;
29
- });
26
+ }
27
+ return compiled;
30
28
  }
31
29
 
32
30
  async modifyResourceConfig(adminforth: IAdminForth, resourceConfig: AdminForthResource) {
@@ -35,30 +33,32 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
35
33
  //check if options names are provided
36
34
  const columns = this.resourceConfig.columns;
37
35
  let columnEnums = [];
38
- for (const field of this.options.outputFields) {
39
- for (const [key, value] of Object.entries(field)) {
40
- const column = columns.find(c => c.name.toLowerCase() === key.toLowerCase());
41
- if (column) {
42
- if(column.enum){
43
- (field as any)[key] = `${value} Select ${key} from the list (USE ONLY VALUE FIELD. USE ONLY VALUES FROM THIS LIST): ${JSON.stringify(column.enum)}`;
44
- columnEnums.push({
45
- name: key,
46
- enum: column.enum,
47
- });
48
- }
49
- } else {
50
- throw new Error(`⚠️ No column found for key "${key}"`);
36
+ for (const [key, value] of Object.entries(this.options.fillFieldsFromImages)) {
37
+ const column = columns.find(c => c.name.toLowerCase() === key.toLowerCase());
38
+ if (column) {
39
+ if(column.enum){
40
+ (this.options.fillFieldsFromImages as any)[key] = `${value} Select ${key} from the list (USE ONLY VALUE FIELD. USE ONLY VALUES FROM THIS LIST): ${JSON.stringify(column.enum)}`;
41
+ columnEnums.push({
42
+ name: key,
43
+ enum: column.enum,
44
+ });
51
45
  }
46
+ } else {
47
+ throw new Error(`⚠️ No column found for key "${key}"`);
52
48
  }
53
49
  }
54
50
 
51
+ const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
52
+ console.log('Primary Key Column:', primaryKeyColumn);
53
+
55
54
  const pageInjection = {
56
55
  file: this.componentPath('visionAction.vue'),
57
56
  meta: {
58
57
  pluginInstanceId: this.pluginInstanceId,
59
- outputFields: this.options.outputFields,
58
+ outputFields: this.options.fillFieldsFromImages,
60
59
  actionName: this.options.actionName,
61
60
  columnEnums: columnEnums,
61
+ primaryKey: primaryKeyColumn.name,
62
62
  }
63
63
  }
64
64
 
@@ -102,7 +102,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
102
102
  Image URLs:`;
103
103
 
104
104
  //send prompt to OpenAI and get response
105
- const chatResponse = await this.options.adapter.generate({ prompt, inputFileUrls: attachmentFiles });
105
+ const chatResponse = await this.options.visionAdapter.generate({ prompt, inputFileUrls: attachmentFiles });
106
106
 
107
107
  const resp: any = (chatResponse as any).response;
108
108
  const topLevelError = (chatResponse as any).error;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/bulk-ai-flow",
3
- "version": "1.0.6",
3
+ "version": "1.1.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
package/types.ts CHANGED
@@ -3,8 +3,8 @@ import { ImageVisionAdapter, AdminUser, IAdminForth, StorageAdapter } from "admi
3
3
 
4
4
  export interface PluginOptions {
5
5
  actionName: string,
6
- adapter: ImageVisionAdapter,
7
- outputFields: Record<string, string>[],
6
+ visionAdapter: ImageVisionAdapter,
7
+ fillFieldsFromImages: Record<string, string>,
8
8
  attachFiles?: ({ record }: {
9
9
  record: any,
10
10
  }) => string[] | Promise<string[]>,