Browse Source

Merge pull request #192 from Kholid060/dev

v0.9.6
Ahmad Kholid 3 năm trước cách đây
mục cha
commit
533a9eb06a

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "automa",
-  "version": "0.9.3",
+  "version": "0.9.6",
   "description": "An extension for automating your browser by connecting blocks",
   "license": "MIT",
   "repository": {

+ 1 - 1
src/components/newtab/shared/SharedWorkflowState.vue

@@ -39,7 +39,7 @@
       >
         <v-remixicon :name="block.icon" />
         <p class="flex-1 ml-2 mr-4 text-overflow">{{ block.name }}</p>
-        <ui-spinner color="text-accnet" size="20" />
+        <ui-spinner color="text-accent" size="20" />
       </div>
     </div>
     <div

+ 10 - 2
src/components/newtab/workflow/WorkflowDetailsCard.vue

@@ -38,7 +38,7 @@
           type="url"
           placeholder="http://example.com/img.png"
           label="Icon URL"
-          @change="$emit('update', { icon: $event })"
+          @change="updateWorkflowIcon"
         />
       </div>
     </ui-popover>
@@ -126,7 +126,7 @@ defineProps({
     default: false,
   },
 });
-defineEmits(['update']);
+const emit = defineEmits(['update']);
 
 const { t } = useI18n();
 
@@ -163,4 +163,12 @@ const blocks = computed(() =>
     return arr;
   }, {})
 );
+
+function updateWorkflowIcon(value) {
+  if (!value.startsWith('http')) return;
+
+  const iconUrl = value.slice(0, 1024);
+
+  emit('update', { icon: iconUrl });
+}
 </script>

+ 1 - 1
src/components/newtab/workflow/WorkflowRunning.vue

@@ -32,7 +32,7 @@
         <template v-if="item.state.currentBlock">
           <v-remixicon :name="getBlock(item).icon" />
           <p class="flex-1 ml-2 mr-4">{{ getBlock(item).name }}</p>
-          <ui-spinner color="text-accnet" size="20" />
+          <ui-spinner color="text-accent" size="20" />
         </template>
         <p v-else>{{ t('message.noBlock') }}</p>
       </div>

+ 12 - 7
src/components/newtab/workflow/edit/EditConditions.vue

@@ -1,7 +1,12 @@
 <template>
   <div>
-    <ui-button variant="accent" class="mb-4" @click="addCondition">
-      Add condition
+    <ui-button
+      :disabled="conditions.length >= 10"
+      variant="accent"
+      class="mb-4"
+      @click="addCondition"
+    >
+      {{ t('workflow.blocks.conditions.add') }}
     </ui-button>
     <ul class="space-y-2">
       <li
@@ -109,22 +114,22 @@ function getTitle(index) {
 function addCondition() {
   if (conditions.value.length >= 10) return;
 
+  emitter.emit('conditions-block:add', {
+    id: props.blockId,
+  });
+
   conditions.value.unshift({
     compareValue: '',
     value: '',
     type: '==',
   });
-
-  emitter.emit('conditions-block:add', {
-    id: props.blockId,
-  });
 }
 function deleteCondition(index) {
   conditions.value.splice(index, 1);
 
   emitter.emit('conditions-block:delete', {
     index,
-    id: prps.blockId,
+    id: props.blockId,
   });
 }
 // function updateData(value) {

+ 1 - 1
src/components/newtab/workflow/edit/EditForms.vue

@@ -7,7 +7,7 @@
     >
       {{ t('workflow.blocks.forms.getValue') }}
     </ui-checkbox>
-    <template v-if="data.getValue">
+    <template v-if="data.getValue && !hideBase">
       <ui-checkbox
         :model-value="data.saveData"
         class="mb-2 ml-2"

+ 1 - 0
src/lib/dayjs.js

@@ -3,6 +3,7 @@ import relativeTime from 'dayjs/plugin/relativeTime';
 import 'dayjs/locale/zh';
 import 'dayjs/locale/zh-tw';
 import 'dayjs/locale/vi';
+import 'dayjs/locale/fr';
 
 dayjs.extend(relativeTime);
 

+ 1 - 0
src/locales/en/blocks.json

@@ -223,6 +223,7 @@
       },
       "conditions": {
         "name": "Conditions",
+        "add": "Add condition",
         "description": "Conditional block",
         "fallbackTitle": "Execute when all comparisons don't meet the requirement",
         "equals": "Equals",

+ 324 - 0
src/locales/fr/blocks.json

@@ -0,0 +1,324 @@
+{
+  "collection": {
+    "blocks": {
+      "export-result": {
+        "name": "Exporter le résultat",
+        "description": "Exporte le résultat de la collection au format JSON"
+      }
+    }
+  },
+  "workflow": {
+    "blocks": {
+      "base": {
+        "selector": "Sélecteur d'éléments",
+        "findElement": {
+          "placeholder": "Rechercher un élément par",
+          "options": {
+            "cssSelector": "Sélecteur CSS",
+            "xpath": "XPath"
+          }
+        },
+        "markElement": {
+          "title": "Un élément ne sera pas sélectionné s'il a déjà été sélectionné avant",
+          "text": "Marquer l'élément"
+        },
+        "multiple": {
+          "title": "Sélectionner plusieurs éléments",
+          "text": "Multiple"
+        }
+      },
+      "trigger": {
+        "name": "Déclencheur",
+        "description": "Bloc où le workflow commencera à s'exécuter",
+        "days": [
+          "Dimanche",
+          "Lundi",
+          "Mardi",
+          "Mercredi",
+          "Jeudi",
+          "Vendredi",
+          "Samedi"
+        ],
+        "useRegex": "Utiliser une Regex",
+        "shortcut": {
+          "tootlip": "Enregistrer un raccourci",
+          "checkboxTitle": "Exécuter le raccourci même lorsque vous êtes dans un élément de saisie",
+          "checkbox": "Actif dans un élément de saisie",
+          "note": "Note: le raccourci clavier ne fonctionne que lorsque vous êtes sur une page web"
+        },
+        "forms": {
+          "triggerWorkflow": "Déclencher le workflow",
+          "interval": "Intervalle (minutes)",
+          "delay": "Délai (minutes)",
+          "date": "Date",
+          "time": "Heure",
+          "url": "URL ou Regex",
+          "shortcut": "Rccourci"
+        },
+        "items": {
+          "manual": "Manuellement",
+          "interval": "Intervalle",
+          "date": "À une date précise",
+          "specific-day": "À un jour précis",
+          "visit-web": "Lorsque vous visitez un site Web",
+          "keyboard-shortcut": "Raccourci clavier"
+        }
+      },
+      "execute-workflow": {
+        "name": "Executer le workflow",
+        "overwriteNote": "Cela écrasera les données globales du workflow sélectionné",
+        "select": "Selectionner le workflow",
+        "description": ""
+      },
+      "active-tab": {
+        "name": "Onglet actif",
+        "description": "Définit l'onglet actuel dans lequel vous vous trouvez en tant qu'onglet actif"
+      },
+      "proxy": {
+        "name": "Proxy",
+        "description": "Définit le proxy du navigateur",
+        "clear": "Effacer tous les proxys",
+        "bypass": {
+          "label": "Liste des contournements",
+          "note": "Utilisez des virgules (,) pour séparer les URLs"
+        }
+      },
+      "new-window": {
+        "name": "Nouvelle fenêtre",
+        "description": "Créer une nouvelle fenêtre",
+        "windowState": {
+          "placeholder": "État de la fenêtre",
+          "options": {
+            "normal": "Normale",
+            "minimized": "Minimisée",
+            "maximized": "Maximisée",
+            "fullscreen": "Plein écran"
+          }
+        },
+        "incognito": {
+          "text": "Définir comme fenêtre de navigation privée",
+          "note": "Vous devez activer « Autoriser en navigation privée » pour que cette extension puisse utiliser l'option"
+        }
+      },
+      "go-back": {
+        "name": "Revenir en arrière",
+        "description": "Retourne à la page précédente"
+      },
+      "forward-page": {
+        "name": "Avancer",
+        "description": "Aller à la page suivante"
+      },
+      "close-tab": {
+        "name": "Fermer l'onglet",
+        "description": "",
+        "activeTab": "Ferme l'onglet actif",
+        "url": "URL ou modèle de correspondance"
+      },
+      "event-click": {
+        "name": "Cliquer sur l'élément",
+        "description": ""
+      },
+      "delay": {
+        "name": "Délai",
+        "description": "Ajoute un délai avant d'exécuter le bloc suivant",
+        "input": {
+          "title": "Délai en milliseconde",
+          "placeholder": "(milliseconde)"
+        }
+      },
+      "get-text": {
+        "name": "Obtenir le texte",
+        "description": "Obtenir le texte d'un élément",
+        "prefixText": {
+          "placeholder": "Préfixe du texte",
+          "title": "Ajouter un préfixe au texte"
+        },
+        "suffixText": {
+          "placeholder": "Suffixe du texte",
+          "title": "Ajouter un suffixe au texte"
+        }
+      },
+      "export-data": {
+        "name": "Exporter les données",
+        "description": "Exporte les colonnes de données du workflow"
+      },
+      "element-scroll": {
+        "name": "Défiler l'élément",
+        "description": "",
+        "scrollY": "Défilement vertical",
+        "scrollX": "Défilement horizontal",
+        "intoView": "Défiler dans la vue",
+        "smooth": "Défilement fluide",
+        "incScrollX": "Incrémenter le défilement horizontal",
+        "incScrollY": "Incrémenter le défilement vertical"
+      },
+      "new-tab": {
+        "name": "Nouvel onglet",
+        "description": "",
+        "activeTab": "Définir comme onglet actif",
+        "tabToGroup": "Ajouter l'onglet au groupe",
+        "updatePrevTab": {
+          "title": "Utilise le nouvel onglet précédemment ouvert au lieu d'en créer un nouveau",
+          "text": "Mettre à jour l'onglet précédemment ouvert"
+        }
+      },
+      "link": {
+        "name": "Lien",
+        "description": "Ouvre le lien d'un élément"
+      },
+      "attribute-value": {
+        "name": "Valeur de l'attribut",
+        "description": "Obtenir la valeur de l'attribut d'un élément",
+        "forms": {
+          "name": "Nom de l'attribut",
+          "checkbox": "Enregistrer des données",
+          "column": "Sélectionnez la colonne"
+        }
+      },
+      "forms": {
+        "name": "Formulaires",
+        "description": "",
+        "selected": "Selectionné",
+        "type": "Type de formulaire",
+        "getValue": "Obtenir la valeur du formulaire",
+        "text-field": {
+          "name": "Champ de texte",
+          "value": "Valeur",
+          "clearValue": "Effacer la valeur du formulaire",
+          "delay": {
+            "placeholder": "Délai",
+            "label": "Délai de frappe (milliseconde) (0 pour désactiver)"
+          }
+        },
+        "select": {
+          "name": "Sélection"
+        },
+        "radio": {
+          "name": "Radio"
+        },
+        "checkbox": {
+          "name": "Case à cocher"
+        }
+      },
+      "repeat-task": {
+        "name": "Répéter la tâche",
+        "description": "",
+        "times": "fois",
+        "repeatFrom": "Répéter depuis"
+      },
+      "javascript-code": {
+        "name": "Code JavaScript",
+        "description": "Exécutez votre code javascript dans la page web",
+        "availabeFuncs": "Méthodes disponibles:",
+        "removeAfterExec": "Supprimer après l'exécution du bloc",
+        "modal": {
+          "tabs": {
+            "code": "Code JavaScript",
+            "preloadScript": "Script de préchargement"
+          }
+        },
+        "timeout": {
+          "placeholder": "Délai d'attente",
+          "title": "Délai d'exécution du code Javascript"
+        }
+      },
+      "trigger-event": {
+        "name": "Événement déclencheur",
+        "description": "",
+        "selectEvent": "Sélectionnez un événement"
+      },
+      "conditions": {
+        "name": "Conditions",
+        "description": "Bloc conditionnel",
+        "fallbackTitle": "Exécuté lorsque toutes les comparaisons ne répondent pas aux exigences",
+        "equals": "Égale à",
+        "gt": "Plus grand que",
+        "gte": "Plus grand que ou égal",
+        "lt": "Moins que",
+        "lte": "Moins que ou égal",
+        "ne": "Différent",
+        "contains": "Contient"
+      },
+      "element-exists": {
+        "name": "L'élément existe",
+        "description": "Vérifie si un élément existe",
+        "selector": "Sélecteur d'éléments",
+        "fallbackTitle": "Exécuté lorsque l'élément n'existe pas",
+        "tryFor": {
+          "title": "Essaye de vérifier si l'élément existe",
+          "label": "Nombre d'essai"
+        },
+        "timeout": {
+          "label": "Délai d'attente (millisecondes)",
+          "title": "Délai d'attente pour chaque essai"
+        }
+      },
+      "webhook": {
+        "name": "Webhook",
+        "description": "Un Webhook permet à un service externe d'être notifié",
+        "url": "L'URL de réception de la requête",
+        "contentType": "Sélectionnez un type de contenu",
+        "buttons": {
+          "header": "Ajouter un en-tête"
+        },
+        "timeout": {
+          "placeholder": "Délai d'attente",
+          "title": "Délai d'exécution de la requête HTTP (ms)"
+        },
+        "tabs": {
+          "headers": "En-têtes",
+          "body": "Corps du contenu"
+        }
+      },
+      "loop-data": {
+        "name": "Boucle de données",
+        "description": "Itérer depuis les colonnes de données ou vos données personnalisées",
+        "loopId": "ID de la boucle",
+        "modal": {
+          "fileTooLarge": "Fichier trop volumineux pour être modifié",
+          "maxFile": "La taille maximale du fichier est de 1 Mo",
+          "options": {
+            "firstRow": "Utiliser la première ligne comme clé"
+          }
+        },
+        "buttons": {
+          "clear": "Effacer les données",
+          "insert": "Insérer des données",
+          "import": "Importer un fichier"
+        },
+        "maxLoop": {
+          "title": "Nombre maximum d'itérations de la boucle",
+          "label": "Nombre maximum d'itérations (0 pour désactiver)"
+        },
+        "loopThrough": {
+          "placeholder": "Boucler depuis",
+          "fromNumber": "Depuis le nombre",
+          "toNumber": "Vers le nombre",
+          "options": {
+            "numbers": "Nombres",
+            "data-columns": "Colonnes de données",
+            "custom-data": "Données personnalisées"
+          }
+        }
+      },
+      "loop-breakpoint": {
+        "name": "Point d'arrêt de la boucle",
+        "description": "Pour dire où la boucle doit s'arrêter"
+      },
+      "take-screenshot": {
+        "name": "Prendre une capture d'écran",
+        "description": "Prend une capture d'écran de l'onglet actif",
+        "imageQuality": "Qualité de l'image"
+      },
+      "switch-to": {
+        "name": "Basculer de cadre",
+        "description": "Basculer entre la fenêtre principale et l'iframe",
+        "iframeSelector": "Sélecteur de l'élément iframe",
+        "windowTypes": {
+          "main": "Fenêtre principale",
+          "iframe": "Iframe"
+        }
+      }
+    }
+  }
+}

+ 56 - 0
src/locales/fr/common.json

@@ -0,0 +1,56 @@
+{
+  "common": {
+    "dashboard": "Tableau de bord",
+    "workflow": "Workflow | Workflows",
+    "collection": "Collection | Collections",
+    "log": "Log | Logs",
+    "block": "Bloc | Blocs",
+    "docs": "Documentation",
+    "search": "Rechercher",
+    "import": "Importer",
+    "export": "Exporter",
+    "rename": "Renomer",
+    "execute": "Executer",
+    "delete": "Supprimer",
+    "cancel": "Annuler",
+    "settings": "Réglages",
+    "options": "Options",
+    "confirm": "Confirmer",
+    "name": "Nom",
+    "all": "Tous",
+    "add": "Ajouter",
+    "save": "Enregistrer",
+    "data": "Données",
+    "stop": "Arrêter",
+    "editor": "Éditeur",
+    "running": "En cours",
+    "globalData": "Données générales",
+    "fileName": "Nom du fichier",
+    "description": "Description",
+    "disable": "Désactiver",
+    "disabled": "Désactivé",
+    "enable": "Activer",
+    "fallback": "Fallback",
+    "update": "Mettre à jour"
+  },
+  "message": {
+    "noBlock": "Pas de bloc",
+    "noData": "Aucune donnée à afficher",
+    "noTriggerBlock": "Impossible de trouver un bloc déclencheur",
+    "useDynamicData": "Apprenez à ajouter des données dynamiques",
+    "delete": "Voulez-vous vraiment supprimer \"{name}\"?",
+    "empty": "Oupss... Il semble que vous n'ayez aucun élément",
+    "notSaved": "Voulez-vous vraiment partir ? Vous avez des changements non enregistrés !",
+    "maxSizeExceeded": "La taille maximum autorisé du fichier est dépassé"
+  },
+  "sort": {
+    "sortBy": "Trier par",
+    "name": "Nom",
+    "createdAt": "Date de création"
+  },
+  "logStatus": {
+    "stopped": "arrêté",
+    "error": "erreur",
+    "success": "succès"
+  }
+}

+ 143 - 0
src/locales/fr/newtab.json

@@ -0,0 +1,143 @@
+{
+  "home": {
+    "viewAll": "Voir tout"
+  },
+  "settings": {
+    "language": {
+      "label": "Langue",
+      "helpTranslate": "Vous ne trouvez pas votre langue ? Aider à traduire.",
+      "reloadPage": "Recharger la page pour appliquer"
+    }
+  },
+  "workflow": {
+    "import": "Importer un workflow",
+    "new": "Nouveau workflow",
+    "delete": "Supprimer le workflow",
+    "name": "Nom du workflow",
+    "rename": "Renommer le workflow",
+    "add": "Ajouter un workflow",
+    "clickToEnable": "Cliquer pour activer",
+    "state": {
+      "executeBy": "Exécuté par: \"{name}\""
+    },
+    "dataColumns": {
+      "title": "Colonnes de données",
+      "placeholder": "Rechercher ou ajouter une colonne",
+      "column": {
+        "name": "Nom de la colonne",
+        "type": "Type de donnée"
+      }
+    },
+    "sidebar": {
+      "workflowIcon": "Icône du workflow"
+    },
+    "editor": {
+      "zoomIn": "Agrandir",
+      "zoomOut": "Réduire",
+      "resetZoom": "Réinitialiser le zoom",
+      "duplicate": "Dupliquer"
+    },
+    "settings": {
+      "onError": {
+        "title": "Lors d'une erreur du workflow",
+        "items": {
+          "keepRunning": "Continuer",
+          "stopWorkflow": "Arrêter le workflow"
+        }
+      },
+      "timeout": {
+        "title": "Délai d'expiration du workflow (millisecondes)"
+      },
+      "blockDelay": {
+        "title": "Délai du bloc (millisecondes)",
+        "description": "Ajouter un délai avant d'exécuter chacun des blocs"
+      }
+    }
+  },
+  "collection": {
+    "description": "Exécutez vos workflows en séquence",
+    "new": "Nouvelle collection",
+    "delete": "Supprimer la collection",
+    "add": "Ajouter une collection",
+    "rename": "Renommer la collection",
+    "flow": "Flow",
+    "dragDropText": "Déposez un workflow ou un bloc ici",
+    "options": {
+      "atOnce": {
+        "title": "Exécuter tous les workflows de la collection en même temps",
+        "description": "Le bloc ne sera pas exécuté lors de l'utilisation de cette option"
+      }
+    },
+    "globalData": {
+      "note": "Cela écrasera les données globales du workflow"
+    }
+  },
+  "log": {
+    "goBack": "Revenir aux logs de \"{name}\"",
+    "startedDate": "Date de début",
+    "duration": "Durée",
+    "selectAll": "Tout sélectionner",
+    "deselectAll": "Tout déselectionner",
+    "deleteSelected": "Supprimer les logs sélectionnés",
+    "types": {
+      "stop": "Le workflow est arrêté",
+      "finish": "Finir"
+    },
+    "messages": {
+      "conditions-empty": "Les conditions sont vides",
+      "invalid-proxy-host": "Hôte proxy non valide",
+      "workflow-disabled": "Le workflow est désactivé",
+      "selector-empty": "Le sélecteur d'élément est vide",
+      "empty-workflow": "Vous devez d'abord sélectionner workflow",
+      "active-tab-removed": "L'onglet actif du workflow est supprimé",
+      "stop-timeout": "Le workflow est arrêté en raison du délai d'attente",
+      "no-workflow": "Impossible de trouver le workflow avec l'ID \"{workflowId}\"",
+      "element-not-found": "Impossible de trouver un élément avec le sélecteur \"{selector}\".",
+      "workflow-infinite-loop": "Impossible d'exécuter le workflow pour éviter une boucle infinie",
+      "no-iframe-id": "Impossible de trouver l'ID de Frame pour l'élément iframe avec le sélecteur \"{selector}\"",
+      "no-tab": "Impossible de se connecter à un onglet, utilisez le bloc \"Nouvel onglet\" ou \"Onglet actif\" avant d'utiliser le bloc \"{name}\"."
+    },
+    "description": {
+      "text": "{status} le {date} en {duration}",
+      "status": {
+        "success": "Réussi",
+        "error": "Échoué",
+        "stopped": "Arrêté"
+      }
+    },
+    "delete": {
+      "title": "Supprimer le log",
+      "description": "Voulez-vous vraiment supprimer tous les logs sélectionnés ?"
+    },
+    "exportData": {
+      "title": "Exporter les données",
+      "types": {
+        "json": "JSON",
+        "csv": "CSV",
+        "plain-text": "Texte brut"
+      }
+    },
+    "filter": {
+      "title": "Filtrer",
+      "byStatus": "Par statut",
+      "byDate": {
+        "title": "Par date",
+        "items": {
+          "lastDay": "Hier",
+          "last7Days": "La semaine dernière",
+          "last30Days": "Le mois dernier"
+        }
+      }
+    }
+  },
+  "components": {
+    "pagination": {
+      "text1": "Afficher",
+      "text2": "Éléments sur {count}",
+      "nextPage": "Page suivante",
+      "currentPage": "Page actuelle",
+      "prevPage": "Page précédente",
+      "of": "sur {page}"
+    }
+  }
+}

+ 13 - 0
src/locales/fr/popup.json

@@ -0,0 +1,13 @@
+{
+  "home": {
+    "elementSelector": {
+      "name": "Sélecteur d'éléments",
+      "noAccess": "L'extension n'a pas accès à ce site"
+    },
+    "workflow": {
+      "new": "Nouveau workflow",
+      "rename": "Renommer le workflow",
+      "delete": "Supprimer le workflow"
+    }
+  }
+}

+ 3 - 5
src/utils/reference-data.js

@@ -17,14 +17,12 @@ export function parseKey(key) {
   if (dataKey !== 'dataColumns') return { dataKey, path: path || '' };
 
   const pathArr = path?.split('.') ?? [];
-  let dataPath = '';
+  let dataPath = path;
 
   if (pathArr.length === 1) {
     dataPath = `0.${pathArr[0]}`;
-  } else if (typeof +pathArr[0] !== 'number') {
-    const firstPath = pathArr.shift();
-
-    dataPath = `0.${firstPath}.${pathArr.join('.')}`;
+  } else if (typeof +pathArr[0] !== 'number' || Number.isNaN(+pathArr[0])) {
+    dataPath = `0.${pathArr.join('.')}`;
   }
 
   if (dataPath.endsWith('.')) dataPath = dataPath.slice(0, -1);

+ 1 - 0
src/utils/shared.js

@@ -588,4 +588,5 @@ export const supportLocales = [
   { id: 'zh', name: '简体中文' },
   { id: 'zh-tw', name: '繁體中文' },
   { id: 'vi', name: 'Tiếng Việt' },
+  { id: 'fr', name: 'Français' },
 ];