浏览代码

feat: update folder structure

Ahmad Kholid 2 年之前
父节点
当前提交
bb45bc6b80

+ 2 - 3
.gitignore

@@ -27,7 +27,6 @@ get-pass-key.js
 getPassKey.js
 
 /business/prod
+/business/test
 
-.idea
-
-business/prod
+.idea

+ 1 - 0
backgroundHandler/index.js

@@ -0,0 +1 @@
+export default {};

+ 0 - 0
business/dev/blocks/backgroundHandler/index.js


+ 0 - 0
business/dev/blocks/contentHandler/index.js


+ 1 - 0
business/dev/blocks/editComponents/index.js

@@ -0,0 +1 @@
+export default {};

+ 1 - 0
business/dev/blocks/index.js

@@ -0,0 +1 @@
+export default {};

+ 1 - 0
business/dev/parameters/index.js

@@ -0,0 +1 @@
+export default {};

+ 2 - 0
package.json

@@ -55,8 +55,10 @@
     "dayjs": "^1.11.5",
     "defu": "^6.0.0",
     "dexie": "^3.2.2",
+    "html2canvas": "^1.4.1",
     "idb": "^7.0.2",
     "jsonpath": "^1.1.1",
+    "jspdf": "^2.5.1",
     "lodash.clonedeep": "^4.5.0",
     "lodash.merge": "^4.6.2",
     "mitt": "^3.0.0",

+ 5 - 1
src/background/workflowEngine/blocksHandler.js

@@ -1,3 +1,4 @@
+import customHandlers from '@business/blocks/backgroundHandler';
 import { toCamelCase } from '@/utils/helper';
 
 const blocksHandler = require.context('./blocksHandler', false, /\.js$/);
@@ -9,4 +10,7 @@ const handlers = blocksHandler.keys().reduce((acc, key) => {
   return acc;
 }, {});
 
-export default handlers;
+export default {
+  ...handlers,
+  ...customHandlers,
+};

+ 1 - 0
src/background/workflowEngine/worker.js

@@ -144,6 +144,7 @@ class Worker {
         : blockHandler;
 
     if (!handler) {
+      console.error(`${block.label} doesn't have handler`);
       this.engine.destroy('stopped');
       return;
     }

+ 19 - 3
src/components/block/BlockBasic.vue

@@ -19,14 +19,17 @@
         :class="data.disableBlock ? 'bg-box-transparent' : block.category.color"
         class="inline-block p-2 mr-2 rounded-lg dark:text-black"
       >
-        <v-remixicon :name="block.details.icon || 'riGlobalLine'" />
+        <v-remixicon
+          :path="getIconPath(block.details.icon)"
+          :name="block.details.icon || 'riGlobalLine'"
+        />
       </span>
       <div class="overflow-hidden flex-1">
         <p
           v-if="block.details.id"
           class="font-semibold leading-tight text-overflow whitespace-nowrap"
         >
-          {{ t(`workflow.blocks.${block.details.id}.name`) }}
+          {{ getBlockName() }}
         </p>
         <p
           :class="{ 'mb-1': data.description && data.loopId }"
@@ -117,7 +120,7 @@ const props = defineProps({
 });
 defineEmits(['delete', 'edit', 'update']);
 
-const { t } = useI18n();
+const { t, te } = useI18n();
 const block = useEditorBlock(props.label);
 const componentId = useComponentId('block-base');
 
@@ -134,4 +137,17 @@ function handleStartDrag(event) {
 function copyLoopId() {
   navigator.clipboard.writeText(props.data.loopId);
 }
+function getBlockName() {
+  const key = `workflow.blocks.${block.details.id}.name`;
+
+  return te(key) ? t(key) : block.details.name;
+}
+function getIconPath(path) {
+  if (path && path.startsWith('path')) {
+    const { 1: iconPath } = path.split(':');
+    return iconPath;
+  }
+
+  return '';
+}
 </script>

+ 10 - 2
src/components/block/BlockGroup.vue

@@ -52,7 +52,12 @@
           />
           <div class="leading-tight flex-1 overflow-hidden">
             <p class="text-overflow">
-              {{ t(`workflow.blocks.${element.id}.name`) }}
+              {{
+                getTranslation(
+                  `workflow.blocks.${element.id}.name`,
+                  tasks[element.id].name
+                )
+              }}
             </p>
             <p
               :title="element.data.description"
@@ -134,7 +139,7 @@ const excludeBlocks = [
   'element-exists',
 ];
 
-const { t } = useI18n();
+const { t, te } = useI18n();
 const toast = useToast();
 const componentId = useComponentId('blocks-group');
 const block = useEditorBlock(props.label);
@@ -184,6 +189,9 @@ function deleteItem(index, itemId) {
   copyBlocks.splice(index, 1);
   emit('update', { blocks: copyBlocks });
 }
+function getTranslation(key, defText = '') {
+  return te(key) ? t(key) : defText;
+}
 function handleDrop(event) {
   event.preventDefault();
   event.stopPropagation();

+ 7 - 1
src/components/newtab/shared/SharedLogsTable.vue

@@ -38,7 +38,10 @@
               <ui-spinner color="text-accent" size="20" />
               <span class="align-middle inline-block ml-3 text-overflow">
                 {{
-                  t(`workflow.blocks.${item.state.currentBlock[0].name}.name`)
+                  getTranslation(
+                    `workflow.blocks.${item.state.currentBlock[0].name}.name`,
+                    item.state.currentBlock[0].name
+                  )
                 }}
               </span>
             </td>
@@ -133,6 +136,9 @@ const state = reactive({
   selected: [],
 });
 
+function getTranslation(key, defText = '') {
+  return te(key) ? t(key) : defText;
+}
 function stopWorkflow(stateId) {
   sendMessage('workflow:stop', stateId, 'background');
 }

+ 18 - 6
src/components/newtab/workflow/WorkflowBlockList.vue

@@ -43,7 +43,12 @@
             />
           </span>
         </div>
-        <v-remixicon :name="block.icon" size="24" class="mb-2" />
+        <v-remixicon
+          :path="getIconPath(block.icon)"
+          :name="block.icon"
+          size="24"
+          class="mb-2"
+        />
         <p class="leading-tight text-overflow capitalize">
           {{ block.name }}
         </p>
@@ -70,13 +75,12 @@ defineProps({
 });
 defineEmits(['pin']);
 
-const { t } = useI18n();
+const { t, te } = useI18n();
 
-function getBlockTitle({ description, id }) {
+function getBlockTitle({ description, id, name }) {
   const blockPath = `workflow.blocks.${id}`;
-  let blockDescription = t(
-    `${blockPath}.${description ? 'description' : 'name'}`
-  );
+  const descPath = `${blockPath}.${description ? 'description' : 'name'}`;
+  let blockDescription = te(descPath) ? t(descPath) : name;
 
   if (description) {
     blockDescription = `[${t(`${blockPath}.name`)}]\n${blockDescription}`;
@@ -84,4 +88,12 @@ function getBlockTitle({ description, id }) {
 
   return blockDescription;
 }
+function getIconPath(path) {
+  if (path && path.startsWith('path')) {
+    const { 1: iconPath } = path.split(':');
+    return iconPath;
+  }
+
+  return '';
+}
 </script>

+ 13 - 6
src/components/newtab/workflow/WorkflowDetailsCard.vue

@@ -84,6 +84,7 @@ import { useI18n } from 'vue-i18n';
 import browser from 'webextension-polyfill';
 import { useShortcut } from '@/composable/shortcut';
 import { tasks, categories } from '@/utils/shared';
+import customBlocks from '@business/blocks';
 import WorkflowBlockList from './WorkflowBlockList.vue';
 
 defineProps({
@@ -98,7 +99,7 @@ defineProps({
 });
 const emit = defineEmits(['update']);
 
-const { t } = useI18n();
+const { t, te } = useI18n();
 const shortcut = useShortcut('action:search', () => {
   const searchInput = document.querySelector('#search-input input');
 
@@ -124,11 +125,17 @@ const icons = [
   'riCommandLine',
 ];
 
-const blocksArr = Object.entries(tasks).map(([key, block]) => ({
-  ...block,
-  id: key,
-  name: t(`workflow.blocks.${key}.name`),
-}));
+const blocksArr = Object.entries({ ...tasks, ...customBlocks }).map(
+  ([key, block]) => {
+    const localeKey = `workflow.blocks.${key}.name`;
+
+    return {
+      ...block,
+      id: key,
+      name: te(localeKey) ? t(localeKey) : block.name,
+    };
+  }
+);
 
 const descriptionCollapsed = ref(true);
 

+ 18 - 4
src/components/newtab/workflow/WorkflowEditBlock.vue

@@ -7,7 +7,7 @@
         <v-remixicon name="riArrowLeftLine" />
       </button>
       <p class="font-semibold inline-block capitalize">
-        {{ t(`workflow.blocks.${data.id}.name`) }}
+        {{ getBlockName() }}
       </p>
       <a
         :title="t('common.docs')"
@@ -33,7 +33,7 @@
       />
     </div>
     <component
-      :is="components[data.editComponent]"
+      :is="getEditComponent()"
       v-if="blockData"
       :key="data.itemId || data.blockId"
       v-model:data="blockData"
@@ -56,6 +56,7 @@
 import { computed } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { excludeOnError } from '@/utils/shared';
+import customEditComponents from '@business/blocks/editComponents';
 import EditBlockSettings from './edit/EditBlockSettings.vue';
 
 const editComponents = require.context(
@@ -63,7 +64,6 @@ const editComponents = require.context(
   false,
   /^(?:.*\/)?Edit[^/]*\.vue$/
 );
-
 /* eslint-disable-next-line */
 const components = editComponents.keys().reduce((acc, key) => {
   const name = key.replace(/(.\/)|\.vue$/g, '');
@@ -74,6 +74,8 @@ const components = editComponents.keys().reduce((acc, key) => {
   return acc;
 }, {});
 
+Object.assign(components, customEditComponents);
+
 const props = defineProps({
   data: {
     type: Object,
@@ -95,7 +97,7 @@ const props = defineProps({
 });
 const emit = defineEmits(['close', 'update', 'update:autocomplete']);
 
-const { t } = useI18n();
+const { t, te } = useI18n();
 
 const blockData = computed({
   get() {
@@ -105,6 +107,18 @@ const blockData = computed({
     emit('update', data);
   },
 });
+
+function getEditComponent() {
+  const editComp = props.data.editComponent;
+  if (typeof editComp === 'object') return editComp;
+
+  return components[editComp];
+}
+function getBlockName() {
+  const key = `workflow.blocks.${props.data.id}.name`;
+
+  return te(key) ? t(key) : props.data.name;
+}
 </script>
 <style>
 #workflow-edit-block hr {

+ 3 - 1
src/components/newtab/workflow/WorkflowEditor.vue

@@ -67,6 +67,7 @@ import {
 import cloneDeep from 'lodash.clonedeep';
 import { useStore } from '@/stores/main';
 import { tasks, categories } from '@/utils/shared';
+import customBlocks from '@business/blocks';
 import EditorSearchBlocks from './editor/EditorSearchBlocks.vue';
 
 const props = defineProps({
@@ -151,11 +152,12 @@ editor.onEdgeUpdate(({ edge, connection }) => {
   Object.assign(edge, connection);
 });
 
+const blocks = { ...tasks, ...customBlocks };
 const settings = store.settings.editor;
 const isDisabled = computed(() => props.options.disabled ?? props.disabled);
 
 function minimapNodeClassName({ label }) {
-  const { category } = tasks[label];
+  const { category } = blocks[label];
   const { color } = categories[category];
 
   return color;

+ 2 - 0
src/components/newtab/workflow/edit/EditInteractionBase.vue

@@ -3,6 +3,7 @@
     <slot name="prepend" />
     <template v-if="!hide">
       <ui-textarea
+        v-if="!hideDescription"
         :model-value="data.description"
         :placeholder="t('common.description')"
         class="w-full mb-2"
@@ -110,6 +111,7 @@ const props = defineProps({
     type: Boolean,
     default: false,
   },
+  hideDescription: Boolean,
 });
 const emit = defineEmits(['update:data', 'change']);
 

+ 2 - 1
src/components/newtab/workflow/edit/EditWorkflowParameters.vue

@@ -52,6 +52,7 @@
                 v-if="paramTypes[param.type].valueComp"
                 v-model="param.defaultValue"
                 :param-data="param"
+                :editor="true"
                 max-width="250px"
               />
               <ui-input
@@ -101,7 +102,7 @@
 <script setup>
 import { reactive, watch } from 'vue';
 import cloneDeep from 'lodash.clonedeep';
-import { workflowParameters } from '@business';
+import * as workflowParameters from '@business/parameters';
 
 const props = defineProps({
   data: {

+ 28 - 22
src/components/newtab/workflow/edit/InsertWorkflowData.vue

@@ -17,29 +17,31 @@
       @change="updateData({ variableName: $event })"
     />
   </template>
-  <ui-checkbox
-    :model-value="data.saveData"
-    block
-    class="mt-4"
-    @change="updateData({ saveData: $event })"
-  >
-    {{ t('workflow.blocks.base.table.checkbox') }}
-  </ui-checkbox>
-  <ui-select
-    v-if="data.saveData"
-    :model-value="data.dataColumn"
-    :placeholder="t('workflow.blocks.base.table.select')"
-    class="w-full mt-2"
-    @change="updateData({ dataColumn: $event })"
-  >
-    <option
-      v-for="column in [...columns, ...workflow.columns.value]"
-      :key="column.id"
-      :value="column.id"
+  <template v-if="table">
+    <ui-checkbox
+      :model-value="data.saveData"
+      block
+      class="mt-4"
+      @change="updateData({ saveData: $event })"
+    >
+      {{ t('workflow.blocks.base.table.checkbox') }}
+    </ui-checkbox>
+    <ui-select
+      v-if="data.saveData"
+      :model-value="data.dataColumn"
+      :placeholder="t('workflow.blocks.base.table.select')"
+      class="w-full mt-2"
+      @change="updateData({ dataColumn: $event })"
     >
-      {{ column.name }}
-    </option>
-  </ui-select>
+      <option
+        v-for="column in [...columns, ...workflow.columns.value]"
+        :key="column.id"
+        :value="column.id"
+      >
+        {{ column.name }}
+      </option>
+    </ui-select>
+  </template>
   <template v-if="extraRow">
     <ui-checkbox
       :model-value="data.addExtraRow"
@@ -83,6 +85,10 @@ defineProps({
     type: Object,
     default: () => ({}),
   },
+  table: {
+    type: Boolean,
+    default: true,
+  },
   extraRow: Boolean,
   variables: Boolean,
   columns: {

+ 5 - 0
src/components/ui/UiPopover.vue

@@ -148,3 +148,8 @@ export default {
   },
 };
 </script>
+<style>
+.ui-popover.content-full .ui-popover__trigger {
+  width: 100%;
+}
+</style>

+ 7 - 1
src/composable/editorBlock.js

@@ -1,6 +1,12 @@
 import { reactive, onMounted } from 'vue';
+import customBlocks from '@business/blocks';
 import { tasks, categories } from '@/utils/shared';
 
+const blocks = {
+  ...tasks,
+  ...customBlocks,
+};
+
 export function useEditorBlock(label) {
   const block = reactive({
     details: {},
@@ -10,7 +16,7 @@ export function useEditorBlock(label) {
   onMounted(() => {
     if (!label) return;
 
-    const details = tasks[label];
+    const details = blocks[label];
 
     block.details = { id: label, ...details };
     block.category = categories[details.category];

+ 5 - 1
src/content/blocksHandler.js

@@ -1,3 +1,4 @@
+import customHandlers from '@business/blocks/contentHandler';
 import { toCamelCase } from '@/utils/helper';
 
 const blocksHandler = require.context('./blocksHandler', false, /\.js$/);
@@ -9,4 +10,7 @@ const handlers = blocksHandler.keys().reduce((acc, key) => {
   return acc;
 }, {});
 
-export default handlers;
+export default {
+  ...customHandlers,
+  ...handlers,
+};

+ 7 - 1
src/content/handleSelector.js

@@ -54,7 +54,7 @@ export function queryElements(data, documentCtx = document) {
 
 export default async function (
   { data, id, frameSelector, debugMode },
-  { onSelected, onError, onSuccess } = {}
+  { onSelected, onError, onSuccess, withDocument } = {}
 ) {
   if (!data || !data.selector) {
     if (onError) onError(new Error('selector-empty'));
@@ -95,6 +95,12 @@ export default async function (
     );
 
     if (onSuccess) onSuccess();
+    if (withDocument) {
+      return {
+        elements,
+        document: documentCtx,
+      };
+    }
 
     return elements;
   } catch (error) {

+ 2 - 2
src/content/index.js

@@ -2,6 +2,7 @@ import browser from 'webextension-polyfill';
 import findSelector from '@/lib/findSelector';
 import { toCamelCase } from '@/utils/helper';
 import { nanoid } from 'nanoid';
+import handleSelector from './handleSelector';
 import blocksHandler from './blocksHandler';
 import showExecutedBlock from './showExecutedBlock';
 import shortcutListener from './services/shortcutListener';
@@ -67,9 +68,8 @@ async function executeBlock(data) {
     }
   }
   const handler = blocksHandler[toCamelCase(data.name || data.label)];
-
   if (handler) {
-    const result = await handler(data);
+    const result = await handler(data, { handleSelector });
     removeExecutedBlock();
 
     return result;

+ 75 - 2
src/lib/vRemixicon.js

@@ -1,4 +1,4 @@
-import vRemixicon from 'v-remixicon';
+import { h, inject, computed } from 'vue';
 import {
   riH1,
   riH2,
@@ -270,4 +270,77 @@ export const icons = {
     'M11.5,11L17.88,16.37L17,16.55L16.36,16.67C15.73,16.8 15.37,17.5 15.65,18.07L15.92,18.65L17.28,21.59L15.86,22.25L14.5,19.32L14.24,18.74C13.97,18.15 13.22,17.97 12.72,18.38L12.21,18.78L11.5,19.35V11M10.76,8.69A0.76,0.76 0 0,0 10,9.45V20.9C10,21.32 10.34,21.66 10.76,21.66C10.95,21.66 11.11,21.6 11.24,21.5L13.15,19.95L14.81,23.57C14.94,23.84 15.21,24 15.5,24C15.61,24 15.72,24 15.83,23.92L18.59,22.64C18.97,22.46 19.15,22 18.95,21.63L17.28,18L19.69,17.55C19.85,17.5 20,17.43 20.12,17.29C20.39,16.97 20.35,16.5 20,16.21L11.26,8.86L11.25,8.87C11.12,8.76 10.95,8.69 10.76,8.69M15,10V8H20V10H15M13.83,4.76L16.66,1.93L18.07,3.34L15.24,6.17L13.83,4.76M10,0H12V5H10V0M3.93,14.66L6.76,11.83L8.17,13.24L5.34,16.07L3.93,14.66M3.93,3.34L5.34,1.93L8.17,4.76L6.76,6.17L3.93,3.34M7,10H2V8H7V10',
 };
 
-export default vRemixicon;
+const component = {
+  name: 'v-remixicon',
+  props: {
+    name: String,
+    title: String,
+    viewBox: {
+      type: String,
+      default: '0 0 24 24',
+    },
+    size: {
+      type: [String, Number],
+      default: 24,
+    },
+    fill: {
+      type: String,
+      default: 'currentColor',
+    },
+    rotate: {
+      type: [Number, String],
+      default: 0,
+    },
+    path: {
+      type: String,
+      default: '',
+    },
+  },
+  setup(props) {
+    const injectIcons = inject('remixicons');
+    const icon = computed(() => {
+      if (props.path) return props.path;
+
+      const iconStr = injectIcons[props.name];
+
+      if (typeof iconStr === 'undefined') {
+        console.error(
+          `[v-remixicon] ${props.name} name of the icon is incorrect`
+        );
+        return null;
+      }
+
+      return iconStr;
+    });
+
+    return () =>
+      h(
+        'svg',
+        {
+          viewBox: props.viewBox,
+          fill: props.fill,
+          height: props.size,
+          width: props.size,
+          class: 'v-remixicon',
+          xmlns: 'http://www.w3.org/2000/svg',
+          style: {
+            transform: props.rotate ? `rotate(${props.rotate}deg)` : null,
+          },
+        },
+        [
+          props.title ? h('title', {}, props.title) : null,
+          h('g', {}, [
+            h('path', { fill: 'none', d: 'M0 0h24v24H0z' }),
+            h('path', { 'fill-rule': 'nonzero', d: icon.value }),
+          ]),
+        ]
+      );
+  },
+};
+
+export default {
+  install(app) {
+    app.provide('remixicons', icons);
+    app.component('VRemixicon', component);
+  },
+};

+ 22 - 11
src/newtab/pages/workflows/[id].vue

@@ -210,6 +210,7 @@
 import {
   watch,
   provide,
+  markRaw,
   reactive,
   computed,
   onMounted,
@@ -239,6 +240,7 @@ import { useGroupTooltip } from '@/composable/groupTooltip';
 import { useCommandManager } from '@/composable/commandManager';
 import { debounce, parseJSON, throttle } from '@/utils/helper';
 import { registerWorkflowTrigger } from '@/utils/workflowTrigger';
+import customBlocks from '@business/blocks';
 import browser from 'webextension-polyfill';
 import dbStorage from '@/db/storage';
 import DroppedNode from '@/utils/editor/DroppedNode';
@@ -258,6 +260,8 @@ import EditorLocalCtxMenu from '@/components/newtab/workflow/editor/EditorLocalC
 import EditorLocalActions from '@/components/newtab/workflow/editor/EditorLocalActions.vue';
 import EditorUsedCredentials from '@/components/newtab/workflow/editor/EditorUsedCredentials.vue';
 
+const blocks = { ...tasks, ...customBlocks };
+
 let editorCommands = null;
 const executeCommandTimeout = null;
 const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 7);
@@ -568,7 +572,7 @@ const onEdgesChange = debounce((changes) => {
 function extractAutocopmleteData(label, { data, id }) {
   const autocompleteData = { [id]: {} };
   const getData = (blockName, blockData) => {
-    const keys = tasks[blockName]?.autocomplete;
+    const keys = blocks[blockName]?.autocomplete;
     const dataList = {};
     if (!keys) return dataList;
 
@@ -735,24 +739,31 @@ function toggleSidebar() {
   localStorage.setItem('workflow:sidebar', state.showSidebar);
 }
 function initEditBlock(data) {
-  const { editComponent, data: blockDefData } = tasks[data.id];
+  const { editComponent, data: blockDefData, name } = blocks[data.id];
   const blockData = defu(data.data, blockDefData);
-
-  editState.blockData = { ...data, editComponent, data: blockData };
+  const blockEditComponent =
+    typeof editComponent === 'string' ? editComponent : markRaw(editComponent);
+
+  editState.blockData = {
+    ...data,
+    editComponent: blockEditComponent,
+    name,
+    data: blockData,
+  };
 
   if (data.id === 'wait-connections') {
     const connections = editor.value.getEdges.value.reduce(
       (acc, { target, sourceNode, source }) => {
         if (target !== data.blockId) return acc;
 
-        let name = t(`workflow.blocks.${sourceNode.label}.name`);
+        let blockName = t(`workflow.blocks.${sourceNode.label}.name`);
 
         const { description } = sourceNode.data;
-        if (description) name += ` (${description})`;
+        if (description) blockName += ` (${description})`;
 
         acc.push({
-          name,
           id: source,
+          name: blockName,
         });
 
         return acc;
@@ -1059,12 +1070,12 @@ async function pasteCopiedElements(position) {
 
   try {
     const copiedText = await navigator.clipboard.readText();
-    const blocks = parseJSON(copiedText);
+    const workflowBlocks = parseJSON(copiedText);
 
-    if (blocks && blocks.name === 'automa-blocks') {
+    if (workflowBlocks && workflowBlocks.name === 'automa-blocks') {
       const { nodes, edges } = copyElements(
-        blocks.data.nodes,
-        blocks.data.edges,
+        workflowBlocks.data.nodes,
+        workflowBlocks.data.edges,
         position
       );
       editor.value.addNodes(nodes);

+ 1 - 1
src/params/App.vue

@@ -81,7 +81,7 @@
 <script setup>
 import { onMounted, ref, computed } from 'vue';
 import browser from 'webextension-polyfill';
-import { workflowParameters } from '@business';
+import * as workflowParameters from '@business/parameters';
 import dayjs from '@/lib/dayjs';
 import { useTheme } from '@/composable/theme';
 

+ 3 - 0
src/utils/shared.js

@@ -1,4 +1,7 @@
+import customBlocks from '@business/blocks';
+
 export const tasks = {
+  ...customBlocks,
   trigger: {
     name: 'Trigger',
     description: 'Block where workflow will start executing',

+ 1 - 1
webpack.config.js

@@ -14,7 +14,7 @@ const ASSET_PATH = process.env.ASSET_PATH || '/';
 const alias = {
   '@': path.resolve(__dirname, 'src/'),
   secrets: path.join(__dirname, 'secrets.blank.js'),
-  '@business': path.resolve(__dirname, 'business/prod'),
+  '@business': path.resolve(__dirname, 'business/dev'),
 };
 
 // load the secrets

+ 118 - 3
yarn.lock

@@ -870,7 +870,7 @@
     "@babel/types" "^7.4.4"
     esutils "^2.0.2"
 
-"@babel/runtime@^7.8.4":
+"@babel/runtime@^7.12.5", "@babel/runtime@^7.14.0", "@babel/runtime@^7.8.4":
   version "7.18.9"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a"
   integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==
@@ -1618,6 +1618,11 @@
   resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
   integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
 
+"@types/raf@^3.4.0":
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/@types/raf/-/raf-3.4.0.tgz#2b72cbd55405e071f1c4d29992638e022b20acc2"
+  integrity sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw==
+
 "@types/range-parser@*":
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
@@ -2204,6 +2209,11 @@ async@^3.2.3:
   resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
   integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
 
+atob@^2.1.2:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
+  integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
+
 autoprefixer@^10.4.7:
   version "10.4.8"
   resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.8.tgz#92c7a0199e1cfb2ad5d9427bd585a3d75895b9e5"
@@ -2262,6 +2272,11 @@ balanced-match@^1.0.0:
   resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
   integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
 
+base64-arraybuffer@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc"
+  integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==
+
 base64-js@^1.3.1:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
@@ -2356,6 +2371,11 @@ browserslist@^4.14.5, browserslist@^4.20.2, browserslist@^4.21.3:
     node-releases "^2.0.6"
     update-browserslist-db "^1.0.5"
 
+btoa@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73"
+  integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==
+
 buffer-crc32@^0.2.1, buffer-crc32@^0.2.13:
   version "0.2.13"
   resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
@@ -2415,6 +2435,20 @@ caniuse-lite@^1.0.30001370, caniuse-lite@^1.0.30001373:
   resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz#3dab138e3f5485ba2e74bd13eca7fe1037ce6f57"
   integrity sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw==
 
+canvg@^3.0.6:
+  version "3.0.10"
+  resolved "https://registry.yarnpkg.com/canvg/-/canvg-3.0.10.tgz#8e52a2d088b6ffa23ac78970b2a9eebfae0ef4b3"
+  integrity sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==
+  dependencies:
+    "@babel/runtime" "^7.12.5"
+    "@types/raf" "^3.4.0"
+    core-js "^3.8.3"
+    raf "^3.4.1"
+    regenerator-runtime "^0.13.7"
+    rgbcolor "^1.0.1"
+    stackblur-canvas "^2.0.0"
+    svg-pathdata "^6.0.3"
+
 cartesian@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/cartesian/-/cartesian-1.0.1.tgz#ae3fc8a63e2ba7e2c4989ce696207457bcae65af"
@@ -2681,7 +2715,7 @@ core-js-compat@^3.21.0, core-js-compat@^3.22.1:
     browserslist "^4.21.3"
     semver "7.0.0"
 
-core-js@^3.23.3:
+core-js@^3.23.3, core-js@^3.6.0, core-js@^3.8.3:
   version "3.24.1"
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.24.1.tgz#cf7724d41724154010a6576b7b57d94c5d66e64f"
   integrity sha512-0QTBSYSUZ6Gq21utGzkfITDylE8jWC9Ne1D2MrhvlsZBI1x39OdDIVbzSqtgMndIy6BlHxBXpMGqzZmnztg2rg==
@@ -2741,6 +2775,13 @@ crypto-js@^4.1.1:
   resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf"
   integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==
 
+css-line-break@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0"
+  integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==
+  dependencies:
+    utrie "^1.0.2"
+
 css-loader@^6.7.1:
   version "6.7.1"
   resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.1.tgz#e98106f154f6e1baf3fc3bc455cb9981c1d5fd2e"
@@ -3038,6 +3079,11 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1:
   dependencies:
     domelementtype "^2.2.0"
 
+dompurify@^2.2.0:
+  version "2.3.10"
+  resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.10.tgz#901f7390ffe16a91a5a556b94043314cd4850385"
+  integrity sha512-o7Fg/AgC7p/XpKjf/+RC3Ok6k4St5F7Q6q6+Nnm3p2zGWioAY6dh0CbbuwOhH2UcSzKsdniE/YnE2/92JcsA+g==
+
 domutils@^2.5.2, domutils@^2.8.0:
   version "2.8.0"
   resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
@@ -3606,6 +3652,11 @@ faye-websocket@^0.11.3:
   dependencies:
     websocket-driver ">=0.5.1"
 
+fflate@^0.4.8:
+  version "0.4.8"
+  resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.4.8.tgz#f90b82aefbd8ac174213abb338bd7ef848f0f5ae"
+  integrity sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==
+
 file-entry-cache@^6.0.1:
   version "6.0.1"
   resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
@@ -3989,6 +4040,14 @@ html-webpack-plugin@^5.5.0:
     pretty-error "^4.0.0"
     tapable "^2.0.0"
 
+html2canvas@^1.0.0-rc.5, html2canvas@^1.4.1:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543"
+  integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==
+  dependencies:
+    css-line-break "^2.1.0"
+    text-segmentation "^1.0.3"
+
 htmlparser2@^6.1.0:
   version "6.1.0"
   resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7"
@@ -4453,6 +4512,21 @@ jsonpath@^1.1.1:
     static-eval "2.0.2"
     underscore "1.12.1"
 
+jspdf@^2.5.1:
+  version "2.5.1"
+  resolved "https://registry.yarnpkg.com/jspdf/-/jspdf-2.5.1.tgz#00c85250abf5447a05f3b32ab9935ab4a56592cc"
+  integrity sha512-hXObxz7ZqoyhxET78+XR34Xu2qFGrJJ2I2bE5w4SM8eFaFEkW2xcGRVUss360fYelwRSid/jT078kbNvmoW0QA==
+  dependencies:
+    "@babel/runtime" "^7.14.0"
+    atob "^2.1.2"
+    btoa "^1.2.1"
+    fflate "^0.4.8"
+  optionalDependencies:
+    canvg "^3.0.6"
+    core-js "^3.6.0"
+    dompurify "^2.2.0"
+    html2canvas "^1.0.0-rc.5"
+
 kind-of@^6.0.2:
   version "6.0.3"
   resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
@@ -5174,6 +5248,11 @@ path-type@^4.0.0:
   resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
   integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
 
+performance-now@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
+  integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==
+
 picocolors@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
@@ -5484,6 +5563,13 @@ quick-lru@^5.1.1:
   resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
   integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
 
+raf@^3.4.1:
+  version "3.4.1"
+  resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
+  integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==
+  dependencies:
+    performance-now "^2.1.0"
+
 randombytes@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
@@ -5568,7 +5654,7 @@ regenerate@^1.4.2:
   resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a"
   integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==
 
-regenerator-runtime@^0.13.4:
+regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7:
   version "0.13.9"
   resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
   integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
@@ -5693,6 +5779,11 @@ rfdc@^1.3.0:
   resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b"
   integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==
 
+rgbcolor@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/rgbcolor/-/rgbcolor-1.0.1.tgz#d6505ecdb304a6595da26fa4b43307306775945d"
+  integrity sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==
+
 rimraf@^2.6.3:
   version "2.7.1"
   resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
@@ -6005,6 +6096,11 @@ spdy@^4.0.2:
     select-hose "^2.0.0"
     spdy-transport "^3.0.0"
 
+stackblur-canvas@^2.0.0:
+  version "2.5.0"
+  resolved "https://registry.yarnpkg.com/stackblur-canvas/-/stackblur-canvas-2.5.0.tgz#aa87bbed1560fdcd3138fff344fc6a1c413ebac4"
+  integrity sha512-EeNzTVfj+1In7aSLPKDD03F/ly4RxEuF/EX0YcOG0cKoPXs+SLZxDawQbexQDBzwROs4VKLWTOaZQlZkGBFEIQ==
+
 static-eval@2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.2.tgz#2d1759306b1befa688938454c546b7871f806a42"
@@ -6149,6 +6245,11 @@ supports-preserve-symlinks-flag@^1.0.0:
   resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
   integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
 
+svg-pathdata@^6.0.3:
+  version "6.0.3"
+  resolved "https://registry.yarnpkg.com/svg-pathdata/-/svg-pathdata-6.0.3.tgz#80b0e0283b652ccbafb69ad4f8f73e8d3fbf2cac"
+  integrity sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==
+
 tailwindcss@^3.1.6:
   version "3.1.8"
   resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.1.8.tgz#4f8520550d67a835d32f2f4021580f9fddb7b741"
@@ -6230,6 +6331,13 @@ terser@^5.10.0, terser@^5.14.1, terser@^5.7.2:
     commander "^2.20.0"
     source-map-support "~0.5.20"
 
+text-segmentation@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943"
+  integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==
+  dependencies:
+    utrie "^1.0.2"
+
 text-table@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
@@ -6394,6 +6502,13 @@ utils-merge@1.0.1:
   resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
   integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
 
+utrie@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/utrie/-/utrie-1.0.2.tgz#d42fe44de9bc0119c25de7f564a6ed1b2c87a645"
+  integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==
+  dependencies:
+    base64-arraybuffer "^1.0.2"
+
 uuid@^8.3.2:
   version "8.3.2"
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"