|
@@ -6,67 +6,151 @@
|
|
|
class="w-full"
|
|
|
@change="updateData({ description: $event })"
|
|
|
/>
|
|
|
- <ul v-show="dataList.length > 0" class="mt-4 data-list">
|
|
|
- <li
|
|
|
- v-for="(item, index) in dataList"
|
|
|
- :key="index"
|
|
|
- class="mb-4 pb-4 border-b"
|
|
|
+ <ui-button
|
|
|
+ class="w-full mt-4 mb-2"
|
|
|
+ variant="accent"
|
|
|
+ @click="showModal = !showModal"
|
|
|
+ >
|
|
|
+ Insert data
|
|
|
+ </ui-button>
|
|
|
+ <ui-modal
|
|
|
+ v-model="showModal"
|
|
|
+ title="Insert data"
|
|
|
+ padding="p-0"
|
|
|
+ content-class="max-w-2xl insert-data-modal"
|
|
|
+ >
|
|
|
+ <ul
|
|
|
+ v-show="dataList.length > 0"
|
|
|
+ class="mt-4 data-list px-4 pb-4 overflow-auto scroll"
|
|
|
+ style="max-height: calc(100vh - 13rem)"
|
|
|
>
|
|
|
- <div class="flex mb-2">
|
|
|
- <ui-select
|
|
|
- :model-value="item.type"
|
|
|
- class="mr-2 flex-shrink-0"
|
|
|
- @change="changeItemType(index, $event)"
|
|
|
- >
|
|
|
- <option value="table">
|
|
|
- {{ t('workflow.table.title') }}
|
|
|
- </option>
|
|
|
- <option value="variable">
|
|
|
- {{ t('workflow.variables.title') }}
|
|
|
- </option>
|
|
|
- </ui-select>
|
|
|
- <ui-input
|
|
|
- v-if="item.type === 'variable'"
|
|
|
- v-model="item.name"
|
|
|
- :placeholder="t('workflow.variables.name')"
|
|
|
- :title="t('workflow.variables.name')"
|
|
|
- class="flex-1"
|
|
|
- />
|
|
|
- <ui-select
|
|
|
- v-else
|
|
|
- v-model="item.name"
|
|
|
- :placeholder="t('workflow.table.select')"
|
|
|
- >
|
|
|
- <option
|
|
|
- v-for="column in workflow.columns.value"
|
|
|
- :key="column.id"
|
|
|
- :value="column.id"
|
|
|
+ <li
|
|
|
+ v-for="(item, index) in dataList"
|
|
|
+ :key="index"
|
|
|
+ class="mb-4 rounded-lg border"
|
|
|
+ >
|
|
|
+ <div class="p-2 border-b flex items-center">
|
|
|
+ <ui-select
|
|
|
+ :model-value="item.type"
|
|
|
+ class="mr-2 flex-shrink-0"
|
|
|
+ @change="changeItemType(index, $event)"
|
|
|
>
|
|
|
- {{ column.name }}
|
|
|
- </option>
|
|
|
- </ui-select>
|
|
|
- </div>
|
|
|
- <div class="flex items-start">
|
|
|
- <ui-textarea
|
|
|
- v-model="item.value"
|
|
|
- placeholder="value"
|
|
|
- title="value"
|
|
|
- class="flex-1 mr-2"
|
|
|
- />
|
|
|
- <ui-button icon @click="dataList.splice(index, 1)">
|
|
|
- <v-remixicon name="riDeleteBin7Line" />
|
|
|
- </ui-button>
|
|
|
- </div>
|
|
|
- </li>
|
|
|
- </ul>
|
|
|
- <ui-button class="mt-4" variant="accent" @click="addItem">
|
|
|
- {{ t('common.add') }}
|
|
|
- </ui-button>
|
|
|
+ <option value="table">
|
|
|
+ {{ t('workflow.table.title') }}
|
|
|
+ </option>
|
|
|
+ <option value="variable">
|
|
|
+ {{ t('workflow.variables.title') }}
|
|
|
+ </option>
|
|
|
+ </ui-select>
|
|
|
+ <ui-input
|
|
|
+ v-if="item.type === 'variable'"
|
|
|
+ v-model="item.name"
|
|
|
+ :placeholder="t('workflow.variables.name')"
|
|
|
+ :title="t('workflow.variables.name')"
|
|
|
+ class="flex-1"
|
|
|
+ />
|
|
|
+ <ui-select
|
|
|
+ v-else
|
|
|
+ v-model="item.name"
|
|
|
+ :placeholder="t('workflow.table.select')"
|
|
|
+ >
|
|
|
+ <option
|
|
|
+ v-for="column in workflow.columns.value"
|
|
|
+ :key="column.id"
|
|
|
+ :value="column.id"
|
|
|
+ >
|
|
|
+ {{ column.name }}
|
|
|
+ </option>
|
|
|
+ </ui-select>
|
|
|
+ <div class="flex-grow" />
|
|
|
+ <v-remixicon
|
|
|
+ name="riDeleteBin7Line"
|
|
|
+ class="cursor-pointer"
|
|
|
+ @click="removeItem(index)"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div class="p-2">
|
|
|
+ <div v-if="hasFileAccess && item.isFile" class="flex items-start">
|
|
|
+ <ui-input
|
|
|
+ v-model="item.filePath"
|
|
|
+ placeholder="File absolute path"
|
|
|
+ class="flex-1"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <ui-textarea
|
|
|
+ v-else
|
|
|
+ v-model="item.value"
|
|
|
+ placeholder="value"
|
|
|
+ title="value"
|
|
|
+ class="w-full"
|
|
|
+ />
|
|
|
+ <div class="flex mt-2 items-center">
|
|
|
+ <ui-button
|
|
|
+ v-tooltip="
|
|
|
+ hasFileAccess
|
|
|
+ ? 'Import file'
|
|
|
+ : 'Don\'t have access, click to learn more'
|
|
|
+ "
|
|
|
+ :class="{ 'text-primary': item.isFile }"
|
|
|
+ icon
|
|
|
+ @click="setAsFile(item)"
|
|
|
+ >
|
|
|
+ <v-remixicon name="riFileLine" />
|
|
|
+ </ui-button>
|
|
|
+ <template v-if="hasFileAccess && item.isFile">
|
|
|
+ <ui-button class="ml-2" @click="previewData(index, item)">
|
|
|
+ Preview data
|
|
|
+ </ui-button>
|
|
|
+ <ui-button
|
|
|
+ v-if="previewState.itemId === index"
|
|
|
+ v-tooltip="'Clear preview'"
|
|
|
+ class="ml-2"
|
|
|
+ icon
|
|
|
+ @click="clearPreview"
|
|
|
+ >
|
|
|
+ <v-remixicon name="riBrush2Line" />
|
|
|
+ </ui-button>
|
|
|
+ <div class="flex-grow" />
|
|
|
+ <ui-select
|
|
|
+ v-if="item.filePath.endsWith('.csv')"
|
|
|
+ v-model="item.csvAction"
|
|
|
+ placeholder="CSV File Action"
|
|
|
+ >
|
|
|
+ <option value="text">Read as text</option>
|
|
|
+ <option value="json">Read as JSON</option>
|
|
|
+ <option value="json-header">Read as JSON with headers</option>
|
|
|
+ </ui-select>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ <shared-codemirror
|
|
|
+ v-if="previewState.itemId === index"
|
|
|
+ :model-value="previewState.data"
|
|
|
+ readonly
|
|
|
+ hide-lang
|
|
|
+ class="w-full mt-4"
|
|
|
+ lang="json"
|
|
|
+ style="max-height: 500px"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </li>
|
|
|
+ <ui-button class="mt-4 w-24" variant="accent" @click="addItem">
|
|
|
+ {{ t('common.add') }}
|
|
|
+ </ui-button>
|
|
|
+ </ul>
|
|
|
+ </ui-modal>
|
|
|
</div>
|
|
|
</template>
|
|
|
<script setup>
|
|
|
-import { ref, watch, inject } from 'vue';
|
|
|
+import { ref, watch, inject, shallowReactive, defineAsyncComponent } from 'vue';
|
|
|
import { useI18n } from 'vue-i18n';
|
|
|
+import { useToast } from 'vue-toastification';
|
|
|
+import Papa from 'papaparse';
|
|
|
+import browser from 'webextension-polyfill';
|
|
|
+import getFile from '@/utils/getFile';
|
|
|
+
|
|
|
+const SharedCodemirror = defineAsyncComponent(() =>
|
|
|
+ import('@/components/newtab/shared/SharedCodemirror.vue')
|
|
|
+);
|
|
|
|
|
|
const props = defineProps({
|
|
|
data: {
|
|
@@ -77,10 +161,26 @@ const props = defineProps({
|
|
|
const emit = defineEmits(['update:data']);
|
|
|
|
|
|
const { t } = useI18n();
|
|
|
+const toast = useToast();
|
|
|
|
|
|
const workflow = inject('workflow', {});
|
|
|
+const showModal = ref(true);
|
|
|
+const hasFileAccess = ref(false);
|
|
|
const dataList = ref(JSON.parse(JSON.stringify(props.data.dataList)));
|
|
|
|
|
|
+const previewState = shallowReactive({
|
|
|
+ data: '',
|
|
|
+ itemId: '',
|
|
|
+});
|
|
|
+
|
|
|
+function clearPreview() {
|
|
|
+ previewState.itemId = '';
|
|
|
+ previewState.data = '';
|
|
|
+}
|
|
|
+function removeItem(index) {
|
|
|
+ dataList.value.splice(index, 1);
|
|
|
+ clearPreview();
|
|
|
+}
|
|
|
function updateData(value) {
|
|
|
emit('update:data', { ...props.data, ...value });
|
|
|
}
|
|
@@ -89,6 +189,9 @@ function addItem() {
|
|
|
type: 'table',
|
|
|
name: '',
|
|
|
value: '',
|
|
|
+ filePath: '',
|
|
|
+ isFile: false,
|
|
|
+ csvAction: 'text',
|
|
|
});
|
|
|
}
|
|
|
function changeItemType(index, type) {
|
|
@@ -98,6 +201,47 @@ function changeItemType(index, type) {
|
|
|
name: '',
|
|
|
};
|
|
|
}
|
|
|
+function setAsFile(item) {
|
|
|
+ if (!hasFileAccess.value) {
|
|
|
+ window.open(
|
|
|
+ 'https://docs.automa.site/blocks/upload-file.html#requirements'
|
|
|
+ );
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ item.isFile = !item.isFile;
|
|
|
+}
|
|
|
+async function previewData(index, item) {
|
|
|
+ try {
|
|
|
+ const path = item.filePath || '';
|
|
|
+ const isJSON = path.endsWith('.json');
|
|
|
+ const isCSV = path.endsWith('.csv');
|
|
|
+
|
|
|
+ let stringify = isJSON;
|
|
|
+ let result = await getFile(path, {
|
|
|
+ returnValue: true,
|
|
|
+ responseType: isJSON ? 'json' : 'text',
|
|
|
+ });
|
|
|
+
|
|
|
+ if (result && isCSV && item.csvAction && item.csvAction.includes('json')) {
|
|
|
+ const parsedCSV = Papa.parse(result, {
|
|
|
+ header: item.csvAction.includes('header'),
|
|
|
+ });
|
|
|
+ result = parsedCSV.data || [];
|
|
|
+ stringify = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ previewState.itemId = index;
|
|
|
+ previewState.data = stringify ? JSON.stringify(result, null, 2) : result;
|
|
|
+ } catch (error) {
|
|
|
+ console.error(error);
|
|
|
+ toast.error(error.message);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+browser.extension.isAllowedFileSchemeAccess().then((value) => {
|
|
|
+ hasFileAccess.value = value;
|
|
|
+});
|
|
|
|
|
|
watch(
|
|
|
dataList,
|
|
@@ -107,10 +251,8 @@ watch(
|
|
|
{ deep: true }
|
|
|
);
|
|
|
</script>
|
|
|
-<style scoped>
|
|
|
-.data-list li:last-child {
|
|
|
- padding-bottom: 0;
|
|
|
- margin-bottom: 0;
|
|
|
- border-bottom: 0;
|
|
|
+<style>
|
|
|
+.insert-data-modal .modal-ui__content-header {
|
|
|
+ @apply p-4;
|
|
|
}
|
|
|
</style>
|