//==UserScript==
// @name ReviewRemember
// @namespace http://tampermonkey.net/
// @version 1.8.7
// @description Outils pour les avis Amazon
// @author Créateur/Codeur principal : MegaMan / Codeur secondaire : Sulff
// @match https://www.amazon.fr/review/create-review*
// @match https://www.amazon.fr/reviews/edit-review*
// @match https://www.amazon.fr/vine/vine-reviews*
// @match https://www.amazon.fr/vine/account
// @match https://www.amazon.fr/gp/profile/*
// @match https://www.amazon.fr/vine/orders*
// @match https://www.amazon.fr/gp/profile/*
// @match https://www.amazon.fr/vine/resources
// @icon https://pickme.alwaysdata.net/img/RR-ICO-2.png
// @updateURL https://raw.githubusercontent.com/teitong/reviewremember/main/ReviewRemember.user.js
// @downloadURL https://raw.githubusercontent.com/teitong/reviewremember/main/ReviewRemember.user.js
// @grant GM_registerMenuCommand
// @run-at document-end
//==/UserScript==
(function() {
'use strict';
//Test si on utilise RR en standalone
if (Math.random() < 0.1) {
localStorage.setItem('useRR', '1');
}
var version = GM_info.script.version;
const selectorTitle = 'reviewTitle';
const selectorReview = 'reviewText';
const selectorButtons = '.in-context-ryp__form_fields_container-desktop';
const selectorTitleOld = 'scarface-review-title-label';
const selectorReviewOld = 'scarface-review-text-card-title';
const selectorButtonsOld = '.ryp__submit-button-card__card-frame';
//Correction du mot sur la page
var element = document.querySelector('#vvp-reviews-button--completed a.a-button-text');
//Vérifie si l'élément existe et si son texte est "Vérifiées"
if (element && element.textContent.trim() === "Vérifiées") {
//Modifie le texte en "Vérifiés"
element.textContent = "Vérifiés";
}
//Sélectionne tous les liens qui ont des IDs correspondant au pattern "a-autoid-*-announce" pour modifier le texte
var links = document.querySelectorAll('.vvp-reviews-table--action-btn .a-button-text');
//Boucle à travers chaque lien pour changer le texte
links.forEach(function(link) {
if (link.textContent.trim() === "Donner un avis sur l'article") {
link.textContent = "Donner un avis";
} else if (link.textContent.trim() === "Modifier le commentaire") {
link.textContent = "Modifier l'avis";
}
});
links = document.querySelectorAll('.vvp-orders-table--action-btn .a-button-text');
//Boucle à travers chaque lien pour changer le texte
links.forEach(function(link) {
if (link.textContent.trim() === "Détails de la commande") {
link.textContent = "Détails";
}
});
//On initialise les infos pour la version mobile (ou non)
var pageX = "Page X";
// Fonction pour détecter si l'utilisateur est sur mobile (à ne pas confondre avec le mode mobile activable manuellement
// dans les paramètres utilisateur)
// Note : si le mode PC est forcé sur mobile, cette fonction renverra toujours false, ce qui est le comportement attendu,
// car les traitements spécifiques au PC s'exécuteront, et la structure HTML liée sera présente
// => Cette fonction ne devrait pas poser de problème de fonctionnement si le mode PC est forcé sur mobile
function isMobile() {
return document.documentElement.classList.contains('a-mobile');
}
//On remplace l'image et son lien par notre menu
function replaceImageUrl() {
//Sélectionner le lien contenant l'image avec l'attribut alt "vine_logo_title"
var link = document.querySelector('a > img[alt="vine_logo_title"]') ? document.querySelector('a > img[alt="vine_logo_title"]').parentNode : null;
//Vérifier si le lien existe
if (link) {
//Sélectionner directement l'image à l'intérieur du lien
var img = link.querySelector('img');
//Remplacer l'URL de l'image
img.src = 'https://pickme.alwaysdata.net/img/RR.png';
if (localStorage.getItem('mobileEnabled') == 'true') {
img.style.maxHeight = '50px';
img.style.maxWidth = '100%';
img.style.height = 'auto';
img.style.width = 'auto';
}
//Modifier le comportement du lien pour empêcher le chargement de la page
link.onclick = function(event) {
//Empêcher l'action par défaut du lien
event.preventDefault();
//Appeler la fonction createConfigPopup
createConfigPopup();
};
}
}
//Export des avis
function exportReviewsToCSV() {
let csvContent = "\uFEFF"; // BOM pour UTF-8
//Ajouter l'en-tête du CSV
csvContent += "Type;Nom;ASIN;Titre de l'avis;Contenu de l'avis\n";
//Exporter les modèles
let savedTemplates = JSON.parse(localStorage.getItem('review_templates')) || [];
savedTemplates.forEach(template => {
const { name, title, review } = template;
//Ajoute une ligne détaillée pour chaque modèle avec une colonne vide pour ASIN
csvContent += `Modèle;${name};;${title.replace(/;/g, ',')};${review.replace(/\n/g, '\\n')}\n`;
});
//Itérer sur les éléments de localStorage
Object.keys(localStorage).forEach(function(key) {
if (key.startsWith('review_') && key !== 'review_templates') {
const reviewData = JSON.parse(localStorage.getItem(key));
const asin = key.replace('review_', ''); // Extraire l'ASIN
const title = reviewData.title.replace(/;/g, ','); // Remplacer les ";" par des ","
const review = reviewData.review.replace(/\n/g, '\\n');
//Ajouter la ligne pour les avis
csvContent += `Avis;;${asin};${title};${review}\n`;
}
});
//Créer un objet Blob avec le contenu CSV en spécifiant le type MIME
var blob = new Blob([csvContent], {type: "text/csv;charset=utf-8;"});
var url = URL.createObjectURL(blob);
//Créer un lien pour télécharger le fichier
var link = document.createElement("a");
link.setAttribute("href", url);
link.setAttribute("download", "RR_backup.csv");
document.body.appendChild(link); // Nécessaire pour certains navigateurs
//Simuler un clic sur le lien pour déclencher le téléchargement
link.click();
//Nettoyer en supprimant le lien et en libérant l'objet URL
document.body.removeChild(link);
URL.revokeObjectURL(url);
}
//Import d'un fichier CSV
function readAndImportCSV(file) {
const reader = new FileReader();
reader.onload = function(event) {
const csv = event.target.result;
const lines = csv.split('\n');
for (let i = 1; i < lines.length; i++) {
if (lines[i]) {
const columns = lines[i].split(';');
if (columns.length >= 4) { // On s'assure qu'il y a assez de colonnes
const type = columns[0].trim();
const name = columns[1].trim();
const asin = columns[2].trim();
const title = columns[3].trim();
const review = columns[4].trim().replace(/\\n/g, '\n'); // Remplacer \\n par de vrais retours à la ligne
if (type === "Avis") {
//Sauvegarder l'avis
localStorage.setItem(`review_${asin}`, JSON.stringify({ title, review }));
} else if (type === "Modèle") {
//Ajouter ou remplacer le modèle dans le tableau
let savedTemplates = JSON.parse(localStorage.getItem('review_templates')) || [];
//Vérifier si un modèle avec le même nom existe déjà
const existingIndex = savedTemplates.findIndex(template => template.name === name);
if (existingIndex !== -1) {
//Remplacer le modèle existant
savedTemplates[existingIndex] = { name, title, review };
} else {
//Ajouter un nouveau modèle
savedTemplates.push({ name, title, review });
}
localStorage.setItem('review_templates', JSON.stringify(savedTemplates));
}
}
}
}
alert('Importation terminée.');
};
reader.readAsText(file, 'UTF-8');
}
//Trie des avis sur profil
//Marquer une carte comme traitée
function marquerCarteCommeTraitee(carte) {
carte.dataset.traitee = 'true';
}
//Fonction pour classer les cartes traitées par ordre décroissant de leur valeur
function classerCartesTraitees() {
//Sélectionne uniquement les cartes marquées comme traitées
const cartesTraitees = Array.from(document.querySelectorAll('.review-card-container[data-traitee="true"]'));
//Trie les cartes en fonction de leur valeur numérique de manière croissante
cartesTraitees.sort((a, b) => extraireValeur(a) - extraireValeur(b));
//Réorganise les cartes dans leur conteneur parent selon le nouvel ordre croissant
const conteneur = document.querySelector('#reviewTabContentContainer');
cartesTraitees.forEach(carte => conteneur.prepend(carte));
}
//Extraire la valeur numérique d'un "like", retourne 0 si non applicable
function extraireValeur(carte) {
const valeurElement = carte.querySelector('.review-reaction-count');
return valeurElement ? parseInt(valeurElement.innerText.trim(), 10) : 0;
}
//Fonction principale de réorganisation des cartes
function reorganiserCartes() {
//Sélectionne uniquement les cartes pas encore traitées
const cartes = Array.from(document.querySelectorAll('.review-card-container:not([data-traitee="true"])'));
//Filtre les cartes avec une valeur numérique strictement supérieure à 0
const cartesAvecValeur = cartes.filter(carte => extraireValeur(carte) > 0);
if (cartesAvecValeur.length > 0) {
//Trie les cartes en fonction de leur valeur numérique de manière décroissante
cartesAvecValeur.sort((a, b) => extraireValeur(b) - extraireValeur(a));
//Préfixe les cartes triées au début de leur conteneur parent
const conteneur = document.querySelector('#reviewTabContentContainer');
cartesAvecValeur.forEach(carte => {
marquerCarteCommeTraitee(carte);
carte.style.setProperty('border', `3px solid ${reviewColor}`, 'important');
conteneur.prepend(carte);
});
//Réorganiser les cartes traitées par ordre croissant
classerCartesTraitees();
}
}
//Détecter les changements dans le DOM et appliquer le tri
function changeProfil() {
if (window.location.href.startsWith('https://www.amazon.fr/gp/profile')) {
//Configuration de l'observer pour réagir aux modifications du DOM
const observer = new MutationObserver((mutations) => {
let mutationsAvecAjouts = mutations.some(mutation => mutation.addedNodes.length > 0);
if (mutationsAvecAjouts) {
reorganiserCartes();
}
});
//Observer les changements dans le DOM
observer.observe(document.querySelector('#reviewTabContentContainer'), { childList: true, subtree: true });
//Exécution initiale au cas où des cartes seraient déjà présentes
reorganiserCartes();
}
}
function setHighlightColor() {
//Extraire les composantes r, g, b de la couleur actuelle
const rgbaMatch = reviewColor.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+),\s*(\d*\.?\d+)\)$/);
let hexColor = "#FFFF00"; //Fallback couleur jaune si la conversion échoue
if (rgbaMatch) {
const r = parseInt(rgbaMatch[1]).toString(16).padStart(2, '0');
const g = parseInt(rgbaMatch[2]).toString(16).padStart(2, '0');
const b = parseInt(rgbaMatch[3]).toString(16).padStart(2, '0');
hexColor = `#${r}${g}${b}`;
}
//Vérifie si une popup existe déjà et la supprime si c'est le cas
const existingPopup = document.getElementById('colorPickerPopup');
if (existingPopup) {
existingPopup.remove();
}
//Crée la fenêtre popup
const popup = document.createElement('div');
popup.id = "colorPickerPopup";
/*popup.style.cssText = `
position: fixed;
z-index: 10001;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding: 20px;
background-color: white;
border: 1px solid #ccc;
box-shadow: 0px 0px 10px #ccc;
`;*/
popup.innerHTML = `
Couleur de la bordure des avis utiles×
`;
document.body.appendChild(popup);
//Ajoute des écouteurs d'événement pour les boutons
document.getElementById('saveColor').addEventListener('click', function() {
const selectedColor = document.getElementById('colorPicker').value;
//Convertir la couleur hexadécimale en RGBA pour la transparence
const r = parseInt(selectedColor.substr(1, 2), 16);
const g = parseInt(selectedColor.substr(3, 2), 16);
const b = parseInt(selectedColor.substr(5, 2), 16);
const rgbaColor = `rgba(${r}, ${g}, ${b}, 0.5)`;
//Stocker la couleur sélectionnée
localStorage.setItem('reviewColor', rgbaColor);
reviewColor = rgbaColor;
popup.remove();
});
document.getElementById('closeColor').addEventListener('click', function() {
popup.remove();
});
document.getElementById('closeColorPicker').addEventListener('click', function() {
popup.remove();
});
}
const asin = new URLSearchParams(window.location.search).get('asin');
//Définition des styles pour les boutons
const styles = `
.custom-button {
padding: 0 10px 0 11px;
font-size: 13px;
line-height: 29px;
vertical-align: middle;
cursor: pointer;
}
.custom-button-container {
margin-right: 10px; /* Ajoute un espace après les boutons et avant le bouton 'Envoyer' */
}
.template-button {
background-color: #FFA500; /* Couleur orange pour les boutons liés au modèle */
border-color: #FFA500;
}
.template-button:hover {
background-color: #cc8400;
}
`;
//Crée une balise de style et ajoute les styles définis ci-dessus
const styleSheet = document.createElement("style");
styleSheet.type = "text/css";
styleSheet.innerText = styles;
document.head.appendChild(styleSheet);
//Fonction pour obtenir l'ASIN du produit à partir de l'URL
function getASIN() {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get('asin');
}
//Fonction pour recharger les boutons
function reloadButtons() {
//Supprime les boutons existants
document.querySelectorAll('.custom-button-container').forEach(container => container.remove());
//Ajoute les boutons à nouveau
const submitButtonArea =
document.querySelector(selectorButtons) ||
document.querySelector(selectorButtonsOld);
if (submitButtonArea) {
addButtons(submitButtonArea);
}
}
//Ajout des différents boutons
function addButtons(targetElement) {
const buttonsContainer = document.createElement('div');
buttonsContainer.style.display = 'flex';
buttonsContainer.style.flexDirection = 'column'; //Les éléments seront empilés en colonne
buttonsContainer.style.alignItems = 'flex-start'; //Alignement des éléments à gauche
buttonsContainer.className = 'custom-button-container';
//Créer un conteneur pour la première ligne (menu déroulant)
const firstLineContainer = document.createElement('div');
firstLineContainer.className = 'first-line-container';
firstLineContainer.style.marginBottom = '15px'; //Ajout d'espace entre la première et la deuxième ligne
//Vérifie si review_template existe (ancienne version du modèle)
if (localStorage.getItem('review_template')) {
const savedTemplate = JSON.parse(localStorage.getItem('review_template'));
const { title, review } = savedTemplate;
//Utilise le titre de review_template comme nom du modèle ou "Ancien modèle" si vide
const name = title.trim() === "" ? "Ancien modèle" : title;
//Récupère les modèles existants
let savedTemplates = JSON.parse(localStorage.getItem('review_templates')) || [];
//Ajoute le nouveau modèle
savedTemplates.push({ name, title, review });
//Sauvegarde les modèles dans localStorage
localStorage.setItem('review_templates', JSON.stringify(savedTemplates));
//Supprime review_template
localStorage.removeItem('review_template');
}
//Ajout d'un champ de sélection pour les modèles
const selectTemplate = document.createElement('select');
selectTemplate.className = 'template-select';
selectTemplate.innerHTML = ``;
const savedTemplates = JSON.parse(localStorage.getItem('review_templates')) || [];
savedTemplates.forEach((template, index) => {
const option = document.createElement('option');
option.value = index;
option.textContent = template.name;
selectTemplate.appendChild(option);
});
firstLineContainer.appendChild(selectTemplate);
buttonsContainer.appendChild(firstLineContainer); //Ajouter la première ligne au conteneur principal
//Créer un conteneur pour la deuxième ligne (boutons liés aux modèles)
const secondLineContainer = document.createElement('div');
secondLineContainer.style.display = 'flex'; //Les boutons seront alignés horizontalement
secondLineContainer.style.gap = '10px'; //Espace entre les boutons
secondLineContainer.style.marginBottom = '15px'; //Ajout d'espace entre la deuxième et la troisième ligne
secondLineContainer.className = 'second-line-container';
//Bouton pour sauvegarder un modèle
addButton('Sauvegarder un nouveau modèle', saveTemplate, secondLineContainer, 'template-button');
//Bouton pour utiliser un modèle
const useTemplateButton = addButton('Utiliser modèle', () => useTemplate(selectTemplate.value), secondLineContainer, 'template-button');
useTemplateButton.style.display = 'none';
//Bouton pour supprimer un modèle
const deleteTemplateButton = addButton('Supprimer le modèle', () => deleteTemplate(selectTemplate.value), secondLineContainer, 'template-button');
deleteTemplateButton.style.display = 'none';
buttonsContainer.appendChild(secondLineContainer); //Ajouter la deuxième ligne au conteneur principal
//Créer un conteneur pour la troisième ligne (boutons d'avis)
const thirdLineContainer = document.createElement('div');
thirdLineContainer.style.display = 'flex'; //Les boutons seront alignés horizontalement
thirdLineContainer.style.gap = '10px'; //Espace entre les boutons
thirdLineContainer.className = 'third-line-container';
//Bouton pour sauvegarder l'avis
addButton('Sauvegarder l\'avis', saveReview, thirdLineContainer);
//Vérifie si un avis a été sauvegardé pour cet ASIN avant d'ajouter le bouton de restauration
const asin = getASIN();
if (localStorage.getItem(`review_${asin}`)) {
addButton('Restaurer l\'avis', restoreReview, thirdLineContainer);
}
buttonsContainer.appendChild(thirdLineContainer); //Ajouter la troisième ligne au conteneur principal
//Afficher/cacher les boutons "Utiliser modèle" et "Supprimer modèle" lorsque l'utilisateur sélectionne un modèle
selectTemplate.addEventListener('change', function () {
const selectedValue = selectTemplate.value;
if (selectedValue === "") {
useTemplateButton.style.display = 'none';
deleteTemplateButton.style.display = 'none';
} else {
useTemplateButton.style.removeProperty('display');
deleteTemplateButton.style.removeProperty('display');
}
});
//submitButtonArea.prepend(buttonsContainer);
// Ajouter les boutons à l'élément cible
targetElement.appendChild(buttonsContainer);
document.querySelectorAll('.custom-button').forEach(button => {
button.addEventListener('click', function(event) {
event.preventDefault(); // Empêche le comportement par défaut (comme un "submit")
event.stopPropagation(); // Empêche la propagation de l'événement
});
});
}
//Ajoute un seul bouton au conteneur spécifié avec une classe optionnelle pour le style
function addButton(text, onClickFunction, container, className = '') {
const button = document.createElement('button');
button.textContent = text;
button.className = 'a-button a-button-normal a-button-primary custom-button ' + className;
button.addEventListener('click', function() {
onClickFunction.call(this);
});
container.appendChild(button);
return button;
}
//Fonction pour utiliser un modèle spécifique
function useTemplate(index) {
const savedTemplates = JSON.parse(localStorage.getItem('review_templates')) || [];
const template = savedTemplates[index];
if (template) {
//Si null ou undefined, on utilise selectorTitleOld
const titleElement = document.getElementById(selectorTitle)
|| document.getElementById(selectorTitleOld);
const reviewElement = document.getElementById(selectorReview)
|| document.getElementById(selectorReviewOld);
//On vérifie l'existence de titleElement avant de l'utiliser
if (titleElement) {
titleElement.value = template.title;
}
if (reviewElement) {
reviewElement.value = template.review;
}
forceChangeReview();
} else {
alert('Aucun modèle sélectionné.');
}
}
//Fonction pour sauvegarder un nouveau modèle ou écraser un existant
function saveTemplate() {
const name = prompt("Entrez un nom pour ce modèle :");
if (!name) {
return alert('Le nom du modèle ne peut pas être vide.');
}
//Si null ou undefined, on utilise selectorTitleOld
const titleElement = document.getElementById(selectorTitle)
|| document.getElementById(selectorTitleOld);
const reviewElement = document.getElementById(selectorReview)
|| document.getElementById(selectorReviewOld);
//On vérifie l'existence de titleElement avant de l'utiliser
if (titleElement) {
var title = titleElement.value;
}
if (reviewElement) {
var review = reviewElement.value;
}
let savedTemplates = JSON.parse(localStorage.getItem('review_templates')) || [];
const existingIndex = savedTemplates.findIndex(template => template.name === name);
if (existingIndex !== -1) {
//Confirmer l'écrasement si le nom du modèle existe déjà
if (confirm(`Le modèle "${name}" existe déjà. Voulez-vous le remplacer ?`)) {
savedTemplates[existingIndex] = { name, title, review };
}
} else {
//Ajouter un nouveau modèle
savedTemplates.push({ name, title, review });
}
localStorage.setItem('review_templates', JSON.stringify(savedTemplates));
alert(`Le modèle "${name}" a été sauvegardé.`);
reloadButtons();
}
//Fonction pour supprimer un modèle
function deleteTemplate(index) {
let savedTemplates = JSON.parse(localStorage.getItem('review_templates')) || [];
if (savedTemplates[index]) {
if (confirm(`Voulez-vous vraiment supprimer le modèle "${savedTemplates[index].name}" ?`)) {
savedTemplates.splice(index, 1);
localStorage.setItem('review_templates', JSON.stringify(savedTemplates));
reloadButtons(); //Actualise les boutons et la liste de sélection
}
}
}
function deleteAllTemplates() {
localStorage.removeItem('review_templates');
alert('Tous les modèles ont été supprimés.');
}
//Fonction pour restaurer un avis
function restoreReview() {
const asin = getASIN();
const savedReview = JSON.parse(localStorage.getItem(`review_${asin}`));
if (savedReview) {
//Si null ou undefined, on utilise selectorTitleOld
const titleElement = document.getElementById(selectorTitle)
|| document.getElementById(selectorTitleOld);
const reviewElement = document.getElementById(selectorReview)
|| document.getElementById(selectorReviewOld);
//On vérifie l'existence de titleElement avant de l'utiliser
if (titleElement) {
titleElement.value = savedReview.title;
}
if (reviewElement) {
reviewElement.value = savedReview.review;
}
forceChangeReview();
} else {
alert('Aucun avis sauvegardé pour ce produit.');
}
}
//Fonction pour sauvegarder l'avis
function saveReview(autoSave = false) {
//Si null ou undefined, on utilise selectorTitleOld
const titleElement = document.getElementById(selectorTitle)
|| document.getElementById(selectorTitleOld);
const reviewElement = document.getElementById(selectorReview)
|| document.getElementById(selectorReviewOld);
//On vérifie l'existence de titleElement avant de l'utiliser
if (titleElement) {
var title = titleElement.value;
}
if (reviewElement) {
var review = reviewElement.value;
}
const asin = getASIN();
localStorage.setItem(`review_${asin}`, JSON.stringify({ title, review }));
if (!autoSave) {
const saveButton = this;
const originalText = saveButton.textContent;
saveButton.textContent = 'Enregistré !';
setTimeout(() => {
saveButton.textContent = originalText;
saveButton.disabled = false;
saveButton.style.backgroundColor = '';
reloadButtons();
}, 2000);
}
}
//Supprimer les avis
function deleteAllReviews() {
Object.keys(localStorage).forEach(key => {
if (key.startsWith('review_')) {
localStorage.removeItem(key);
}
});
}
//Fonctions pour les couleurs des avis
//Fonction pour changer la couleur de la barre en fonction du pourcentage
function changeColor() {
if (document.URL === "https://www.amazon.fr/vine/account") {
var progressBar = document.querySelector('#vvp-perc-reviewed-metric-display .animated-progress span');
var progressValue = parseFloat(progressBar.getAttribute('data-progress'));
var color = '';
var width = progressBar.style.width;
if (progressValue < 60) {
color = 'red';
} else if (progressValue >= 60 && progressValue < 90) {
color = 'orange';
} else {
color = '#32cd32';
}
progressBar.style.backgroundColor = color;
progressBar.style.width = width;
}
}
//Griser le bouton Envoyer après avoir chargé un avis
//Fonction de nettoyage qui supprime l'intervalle, les écouteurs, le message, etc...
function cleanupPreviousRun() {
const data = window._fcrData;
if (!data) return;
//Supprimer l'intervalle s'il existe
if (data.hideInterval) {
clearInterval(data.hideInterval);
data.hideInterval = null;
}
//Supprimer les écouteurs sur les champs
if (data.reviewTextarea && data.onChangeReview) {
data.reviewTextarea.removeEventListener('input', data.onChangeReview);
}
if (data.reviewTitle && data.onChangeTitle) {
data.reviewTitle.removeEventListener('input', data.onChangeTitle);
}
//Supprimer le message rouge (s'il existe encore)
if (data.message && data.message.parentNode) {
data.message.parentNode.removeChild(data.message);
}
//Rétablir l'affichage par défaut du conteneur
if (data.boutonContainer) {
data.boutonContainer.style.removeProperty('display');
}
window._fcrData = null;
}
function forceChangeReview() {
//Si on a déjà lancé la fonction auparavant, on nettoie d'abord
if (window._fcrData) {
cleanupPreviousRun();
}
const reviewTextarea = document.getElementById(selectorReview);
const reviewTitle = document.getElementById(selectorTitle);
const boutonContainer = document.querySelector('.in-context-ryp__submit-button-frame-desktop');
if (!reviewTextarea || !reviewTitle || !boutonContainer) {
console.log("Impossible de trouver reviewTextarea, reviewTitle ou boutonContainer.");
return;
}
//On crée un objet où on stocke nos références
window._fcrData = {
reviewTextarea: reviewTextarea,
reviewTitle: reviewTitle,
boutonContainer: boutonContainer,
hideInterval: null,
message: null,
onChangeReview: null,
onChangeTitle: null,
hasRun: true
};
//Valeurs initiales
const initialReview = reviewTextarea.value;
const initialTitle = reviewTitle.value;
//Création du message
const message = document.createElement('p');
message.style.color = 'red';
message.style.fontWeight = 'bold';
message.style.marginTop = '8px';
message.style.marginBottom = '8px';
//On l'insère après le bouton
boutonContainer.insertAdjacentElement('afterend', message);
window._fcrData.message = message;
//On cache immédiatement le conteneur
boutonContainer.style.setProperty('display', 'none', 'important');
//Timer car Amazon garde pas la propriété none sinon
let hideInterval = setInterval(() => {
boutonContainer.style.setProperty('display', 'none', 'important');
}, 500);
window._fcrData.hideInterval = hideInterval;
let changedReview = false;
let changedTitle = false;
function checkIfBothChanged() {
//Si les deux champs ont été modifiés
if (changedReview && changedTitle) {
//On arrête le masquage
if (window._fcrData.hideInterval) {
clearInterval(window._fcrData.hideInterval);
window._fcrData.hideInterval = null;
}
boutonContainer.style.removeProperty('display');
message.textContent = "";
//On supprime les écouteurs
reviewTextarea.removeEventListener('input', onChangeReview);
reviewTitle.removeEventListener('input', onChangeTitle);
} else {
//On indique ce qu'il manque à modifier
const missing = [];
if (!changedReview) missing.push("votre avis");
if (!changedTitle) missing.push("le titre de l'avis");
message.textContent = "Pour envoyer l'avis, veuillez modifier : " + missing.join(" et ");
}
}
function onChangeReview() {
//S'il n'est pas encore modifié, on compare à la valeur initiale
if (!changedReview && reviewTextarea.value !== initialReview) {
changedReview = true;
}
checkIfBothChanged();
}
function onChangeTitle() {
if (!changedTitle && reviewTitle.value !== initialTitle) {
changedTitle = true;
}
checkIfBothChanged();
}
//On garde la référence pour pouvoir les enlever plus tard
window._fcrData.onChangeReview = onChangeReview;
window._fcrData.onChangeTitle = onChangeTitle;
reviewTextarea.addEventListener('input', onChangeReview);
reviewTitle.addEventListener('input', onChangeTitle);
//Vérification initiale
checkIfBothChanged();
}
//Affiche la dernière mise a jour du profil
function lastUpdate() {
if (document.URL === "https://www.amazon.fr/vine/account") {
//Récupérer le pourcentage et la date précédents depuis le stockage local
const previousPercentage = parseFloat(localStorage.getItem('vineProgressPercentage')) || null;
const previousDate = localStorage.getItem('vineProgressDate') || null;
//console.log("Pourcentage précédent :", previousPercentage);
//console.log("Date précédente :", previousDate);
const progressText = document.querySelector('#vvp-perc-reviewed-metric-display p strong');
const progressContainer = document.querySelector('#vvp-perc-reviewed-metric-display .animated-progress');
const metricsBox = document.querySelector('#vvp-vine-activity-metrics-box .a-box-inner');
//Augmenter dynamiquement la hauteur du bloc des métriques
metricsBox.style.paddingTop = '10px'; //Ajouter du padding en haut
metricsBox.style.paddingBottom = '10px'; //Ajouter du padding en bas
if (progressText) {
const currentPercentageText = progressText.textContent.trim();
const currentPercentage = parseFloat(currentPercentageText.replace('%', '').replace(',', '.'));
//console.log("Pourcentage actuel :", currentPercentage);
if (previousPercentage === null || previousPercentage !== currentPercentage) {
const dateTimeNow = new Date().toLocaleString();
const difference = previousPercentage !== null ? currentPercentage - previousPercentage : 0;
const differenceText = previousPercentage !== null ? (difference > 0 ? `+${difference.toFixed(1)} %` : `${difference.toFixed(1)} %`) : '';
const differenceColor = difference > 0 ? 'green' : 'red';
//console.log("Différence :", differenceText);
//Stocker le nouveau pourcentage et la date dans le stockage local
localStorage.setItem('vineProgressPercentage', currentPercentage);
localStorage.setItem('vineProgressDate', dateTimeNow);
//console.log("Nouveau pourcentage stocké :", currentPercentage);
//console.log("Nouvelle date stockée :", dateTimeNow);
//Mettre à jour le texte de progression avec la date et l'heure de la dernière modification
updateDateTimeElement(progressContainer, dateTimeNow, differenceText, differenceColor);
} else if (previousDate) {
//Si aucune modification détectée, afficher la date et l'heure de la dernière modification
updateDateTimeElement(progressContainer, previousDate);
}
}
function updateDateTimeElement(containerElement, dateTime, differenceText = '', differenceColor = '') {
//Supprimer l'élément de date précédent s'il existe
let previousDateTimeElement = document.querySelector('.last-modification');
if (previousDateTimeElement) {
previousDateTimeElement.remove();
}
//Créer un nouvel élément de date
const dateTimeElement = document.createElement('span');
dateTimeElement.className = 'last-modification';
//dateTimeElement.style.marginLeft = '10px';
dateTimeElement.innerHTML = `Dernière modification constatée le ${dateTime}`;
if (differenceText) {
const differenceElement = document.createElement('span');
differenceElement.style.color = differenceColor;
differenceElement.textContent = ` (${differenceText})`;
dateTimeElement.appendChild(differenceElement);
}
//Insérer le nouvel élément après le conteneur de progression
containerElement.parentNode.insertBefore(dateTimeElement, containerElement.nextSibling);
}
}
}
function targetPercentage() {
if (document.URL === "https://www.amazon.fr/vine/account") {
const { percentage, evaluatedArticles } = extractData();
const storedValue = parseFloat(localStorage.getItem('gestavisTargetPercentage'));
const missingArticles = calculateMissingReviews(percentage, evaluatedArticles, storedValue);
const doFireWorks = localStorage.getItem('doFireWorks');
if (storedValue <= percentage && doFireWorks === 'true') {
fireWorks();
localStorage.setItem('doFireWorks', 'false');
} else if (storedValue > percentage) {
localStorage.setItem('doFireWorks', 'true');
}
insertResult(missingArticles, percentage, evaluatedArticles, storedValue);
centerContentVertically();
removeGreyText();
//Extraction des données de la page
function extractData() {
const percentageText = document.querySelector('#vvp-perc-reviewed-metric-display p strong').innerText;
const articlesText = document.querySelector('#vvp-num-reviewed-metric-display p strong').innerText;
const percentage = parseFloat(percentageText.replace(',', '.').replace('%', '').trim());
const evaluatedArticles = parseInt(articlesText, 10);
return { percentage, evaluatedArticles };
}
//Calcul du nombre d'avis manquants
function calculateMissingReviews(percentage, evaluatedArticles, targetPercentage) {
if (percentage === 0) return 0;
const totalArticles = evaluatedArticles / (percentage / targetPercentage);
const missingArticles = Math.ceil(totalArticles - evaluatedArticles);
return missingArticles;
}
//Injection des résultats
function insertResult(missingArticles, currentPercentage, evaluatedArticles, targetPercentage) {
const targetDiv = document.querySelector('#vvp-num-reviewed-metric-display');
const progressBar = targetDiv.querySelector('.animated-progress.progress-green');
const resultSpan = document.createElement('span');
resultSpan.className = 'review-todo';
const missingArticlesNumber = parseInt(missingArticles, 10);
if (!isNaN(missingArticlesNumber) && missingArticlesNumber > 0) {
resultSpan.innerHTML = `Nombre d'avis à soumettre : ${missingArticlesNumber} (avant d'atteindre ${targetPercentage} %).`;
} else {
const buffer = Math.floor((evaluatedArticles * (currentPercentage - targetPercentage)) / currentPercentage);
if (buffer > 0) {
resultSpan.innerHTML = `
Nombre d'avis à soumettre : Objectif atteint (${targetPercentage}% ou plus).
Nombre de produits à commander avant de retomber sous les ${targetPercentage}% : ${buffer}.
`;
} else {
resultSpan.innerHTML = `Nombre d'avis à soumettre : Objectif atteint (${targetPercentage}% ou plus).`;
}
}
resultSpan.style.display = 'block';
resultSpan.style.marginTop = '10px';
const hrElement = document.createElement('hr');
progressBar.insertAdjacentElement('afterend', resultSpan);
resultSpan.insertAdjacentElement('afterend', hrElement);
}
function centerContentVertically() {
const metricsBox = document.querySelector('#vvp-vine-activity-metrics-box .a-box-inner');
metricsBox.style.display = 'flex';
metricsBox.style.flexDirection = 'column';
metricsBox.style.justifyContent = 'center';
metricsBox.style.height = '100%';
}
function removeGreyText() {
const greyTextElement = document.querySelector('p.grey-text');
if (greyTextElement) {
greyTextElement.remove();
}
}
}
}
//Fonction pour formater une date en format 'DD/MM/YYYY'
function formatDate(date) {
var day = date.getDate().toString().padStart(2, '0');
var month = (1 + date.getMonth()).toString().padStart(2, '0');
var year = date.getFullYear();
return day + '/' + month + '/' + year;
}
//Fonction pour calculer la différence en jours entre deux dates
function dateDiffInDays(date1, date2) {
const diffInTime = date2.getTime() - date1.getTime();
return Math.floor(diffInTime / (1000 * 3600 * 24));
}
//Style pour "Pas encore examiné"
var styleReview = document.createElement('style');
styleReview.textContent = `
.pending-review-blue {
font-weight: bold;
color: #007FFF !important;
}
.pending-review-green {
font-weight: bold;
color: #008000 !important;
}
.pending-review-orange {
font-weight: bold;
color: #FFA500 !important;
}
.pending-review-red {
font-weight: bold;
color: #FF0000 !important;
}
`;
document.head.appendChild(styleReview);
//Fonction pour mettre en surbrillance les dates en fonction de leur âge
function highlightDates() {
if (window.location.href.includes('review-type=completed') || window.location.href.includes('orders')) {
return; //Ne rien faire si l'URL contient "review-type=completed" ou "orders"
}
var tdElements = document.querySelectorAll('.vvp-reviews-table--text-col');
var currentDate = new Date();
tdElements.forEach(function(td, index, array) {
var timestamp = parseInt(td.getAttribute('data-order-timestamp'));
if (td.hasAttribute('data-order-timestamp')) {
var nextTd = array[index + 1];
//Vérifier si le timestamp est en millisecondes et le convertir en secondes si nécessaire
if (timestamp > 1000000000000) {
timestamp /= 1000; //Conversion en secondes
}
var date = new Date(timestamp * 1000); //Convertir le timestamp en millisecondes avant de créer l'objet Date
var daysDifference = dateDiffInDays(date, currentDate);
var formattedDate = formatDate(date);
//var style = '';
//var color = '';
if (daysDifference < 7) {
//color = '#0000FF'; //bleu
nextTd.classList.add('pending-review-blue');
} else if (daysDifference >= 7 && daysDifference < 14) {
//color = '#008000'; //vert
nextTd.classList.add('pending-review-green');
} else if (daysDifference >= 14 && daysDifference < 30) {
//color = '#FFA500'; //orange
nextTd.classList.add('pending-review-orange');
} else {
//color = '#FF0000'; //rouge
nextTd.classList.add('pending-review-red');
}
//Ajouter la couleur et le style gras au texte de la date
//style = 'font-weight: bold; color: ' + color + ';';
//td.innerHTML = '' + formattedDate + '';
}
});
}
//Fonction pour mettre en surbrillance le statut de la revue
function highlightReviewStatus() {
var enableReviewStatusFunction = localStorage.getItem('enableReviewStatusFunction');
if (enableReviewStatusFunction === 'true') {
var tdElements = document.querySelectorAll('td.vvp-reviews-table--text-col');
tdElements.forEach(function(td) {
var reviewStatus = td.innerText.trim();
var style = '';
switch (reviewStatus) {
case 'En attente d\'approbation':
style += 'font-weight: bold; color: #FFA500;'; //orange
break;
case 'Approuvé':
style += 'font-weight: bold; color: #008000;'; //vert
break;
case 'Non approuvé':
style += 'font-weight: bold; color: #FF0000;'; //rouge
break;
case 'Vous avez commenté cet article':
style += 'font-weight: bold; color: #0000FF;'; //bleu
break;
default:
style += 'color: inherit;'; //utiliser la couleur par défaut
}
//Appliquer le style au texte de la revue
td.style = style;
});
}
}
//Fonction pour mettre en surbrillance le statut "Cet article n'est plus disponible"
function highlightUnavailableStatus() {
var divElements = document.querySelectorAll('div.vvp-subtitle-color');
divElements.forEach(function(div) {
var subtitle = div.innerText.trim();
if (subtitle === "Cet article n'est plus disponible") {
div.style.fontWeight = 'bold';
div.style.color = '#FF0000'; //rouge
}
});
}
//Fonction pour masquer les lignes de tableau contenant le mot-clé "Approuvé" et afficher les autres lignes
function masquerLignesApprouve() {
var lignes = document.querySelectorAll('.vvp-reviews-table--row');
lignes.forEach(function(ligne) {
var cellulesStatut = ligne.querySelectorAll('.vvp-reviews-table--text-col');
var contientApprouve = false;
cellulesStatut.forEach(function(celluleStatut) {
var texteStatut = celluleStatut.innerText.trim().toLowerCase();
if (texteStatut.includes('approuvé') && texteStatut !== 'non approuvé') {
contientApprouve = true;
}
});
if (contientApprouve) {
ligne.style.display = 'none';
} else {
ligne.style.display = ''; //Afficher la ligne si elle ne contient pas "Approuvé"
}
});
}
//Suppression header
function hideHeader() {
var styleHeader = document.createElement('style');
styleHeader.textContent = `
body {
padding-right: 0px !important;
}
#navbar-main, #nav-main, #skiplink {
display: none;
}
.amzn-ss-wrap {
display: none !important;
}
`
document.head.appendChild(styleHeader);
}
//Ajoute les pages en partie haute
function addPage() {
//Sélection du contenu HTML du div source
const sourceElement = document.querySelector('.a-text-center');
//Vérifier si l'élément source existe
if (sourceElement) {
//Maintenant que l'élément source a été mis à jour, copier son contenu HTML
const sourceContent = sourceElement.outerHTML;
const currentUrl = window.location.href;
//Création d'un nouveau div pour le contenu copié
const newDiv = document.createElement('div');
newDiv.innerHTML = sourceContent;
newDiv.style.textAlign = 'center'; //Centrer le contenu
//Sélection du div cible où le contenu sera affiché
//const targetDiv = document.querySelector('.vvp-tab-content .vvp-tab-content');
var targetDiv = false;
if (currentUrl.includes("vine-reviews")) {
targetDiv = document.querySelector('.vvp-reviews-table--heading-top');
if (targetDiv && targetDiv.parentNode) {
targetDiv.parentNode.insertBefore(newDiv, targetDiv);
}
} else if (currentUrl.includes("orders")) {
targetDiv = document.querySelector('.vvp-tab-content .vvp-orders-table--heading-top') ||
document.querySelector('.vvp-orders-table');
if (targetDiv && targetDiv.parentNode) {
targetDiv.parentNode.insertBefore(newDiv, targetDiv);
}
}
//Trouver ou créer le conteneur de pagination si nécessaire
let paginationContainer = sourceElement.querySelector('.a-pagination');
if (!paginationContainer) {
paginationContainer = document.createElement('ul');
paginationContainer.className = 'a-pagination';
sourceElement.appendChild(paginationContainer);
}
//Ajout du bouton "Aller à" en haut et en bas
if (currentUrl.includes("orders") || currentUrl.includes("vine-reviews")) {
//Création du bouton "Aller à la page X"
const gotoButtonUp = document.createElement('li');
gotoButtonUp.className = 'a-last'; //Utiliser la même classe que le bouton "Suivant" pour le style
gotoButtonUp.innerHTML = `${pageX}`;
//Ajouter un événement click au bouton "Aller à"
gotoButtonUp.querySelector('a').addEventListener('click', function() {
askPage();
});
//Création du bouton "Aller à la page X"
const gotoButton = document.createElement('li');
gotoButton.className = 'a-last'; //Utiliser la même classe que le bouton "Suivant" pour le style
gotoButton.innerHTML = `${pageX}`;
//Ajouter un événement click au bouton "Aller à"
gotoButton.querySelector('a').addEventListener('click', function() {
askPage();
});
//On insère Page X en début de page
newDiv.querySelector('.a-pagination').insertBefore(gotoButtonUp, newDiv.querySelector('.a-last'));
//On insère en bas de page
paginationContainer.insertBefore(gotoButton, paginationContainer.querySelector('.a-last'));
}
}
}
function askPage() {
const userInput = prompt("Saisir la page où se rendre");
const pageNumber = parseInt(userInput, 10); //Convertit en nombre en base 10
if (!isNaN(pageNumber)) { //Vérifie si le résultat est un nombre
//Obtient l'URL actuelle
const currentUrl = window.location.href;
//Crée un objet URL pour faciliter l'analyse des paramètres de l'URL
const urlObj = new URL(currentUrl);
var newUrl = "";
if (window.location.href.includes("vine-reviews")) {
const reviewType = urlObj.searchParams.get('review-type') || '';
//Construit la nouvelle URL avec le numéro de page
newUrl = `https://www.amazon.fr/vine/vine-reviews?page=${pageNumber}&review-type=${reviewType}`;
//Redirige vers la nouvelle URL
} else if (window.location.href.includes("orders")) {
//Construit la nouvelle URL avec le numéro de page et la valeur de 'pn' existante
newUrl = `https://www.amazon.fr/vine/orders?page=${pageNumber}`;
}
window.location.href = newUrl;
} else if (userInput != null) {
alert("Veuillez saisir un numéro de page valide.");
}
}
//Fonction pour extraire le numéro de commande de l'URL
function extractOrderId(url) {
const match = url.match(/orderID=([0-9-]+)/);
return match ? match[1] : null;
}
function extractASIN(input) {
//Expression régulière pour identifier un ASIN dans une URL ou directement
const regex = /\/dp\/([A-Z0-9]{10})|([A-Z0-9]{10})/;
const match = input.match(regex);
if (match) {
return match[1] || match[2];
}
return null;
}
//Pour sauvegarder le contenu des commandes, si on est sur la page des commandes
function saveOrders() {
if (window.location.href.includes('orders')) {
//Extraction des données de chaque ligne de produit
document.querySelectorAll('.vvp-orders-table--row').forEach(row => {
let productUrl = row.querySelector('.vvp-orders-table--text-col a');
let asin;
if (productUrl) {
productUrl = productUrl.href;
asin = extractASIN(productUrl);
} else {
return;
}
const key_asin = "order_" + asin;
if (!localStorage.getItem(key_asin)) {
const imageUrl = row.querySelector('.vvp-orders-table--image-col img').src;
let productName = row.querySelector('.vvp-orders-table--text-col a .a-truncate-full')
if (productName) {
productName = productName.textContent.trim();
} else {
productName = "Indispo";
}
const timestampElement = row.querySelector('[data-order-timestamp]');
const timestamp = timestampElement.getAttribute('data-order-timestamp');
const orderDate = timestampElement ? new Date(parseInt(timestampElement.getAttribute('data-order-timestamp'))).toLocaleDateString("fr-FR") : null;
const etv = row.querySelector('.vvp-orders-table--text-col.vvp-text-align-right').textContent.trim();
const orderDetailsUrl = row.querySelector('.vvp-orders-table--action-btn a').href;
const orderId = extractOrderId(orderDetailsUrl);
//Préparation de l'objet à stocker
const productData = {
productName,
imageUrl,
orderDate,
timestamp,
etv,
orderId
};
//console.log(productData);
//Stockage dans localStorage avec l'ASIN comme clé
if (localStorage.getItem(key_asin) === null || !isMobile()) {
localStorage.setItem(key_asin, JSON.stringify(productData));
}
}
});
}
}
function fireWorks() {
//Ajout de styles pour le feu d'artifice
let style = document.createElement('style');
style.innerHTML = `
.firework {
position: absolute;
width: 4px;
height: 4px;
background: red;
border-radius: 50%;
pointer-events: none;
animation: explode 1s ease-out forwards;
}
@keyframes explode {
0% { transform: translate(0, 0) scale(1); opacity: 1; }
100% { transform: translate(var(--x, 0), var(--y, 0)) scale(0.5); opacity: 0; }
}
`;
document.head.appendChild(style);
//Fonction pour créer une particule de feu d'artifice
function createParticle(x, y, color, angle, speed) {
let particle = document.createElement('div');
particle.className = 'firework';
particle.style.background = color;
particle.style.left = `${x}px`;
particle.style.top = `${y}px`;
//Calcul de la trajectoire
let radians = angle * (Math.PI / 180);
let dx = Math.cos(radians) * speed;
let dy = Math.sin(radians) * speed;
particle.style.setProperty('--x', `${dx}px`);
particle.style.setProperty('--y', `${dy}px`);
document.body.appendChild(particle);
//Retirer la particule après l'animation
setTimeout(() => {
particle.remove();
}, 1000);
}
//Fonction pour lancer le feu d'artifice
function lancerFeuArtifice() {
let numberOfBursts = 10;
let particlesPerBurst = 50;
let burstInterval = 500; //Intervalle entre chaque explosion
let duration = 5000; //Durée du feu d'artifice
let colors = ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff'];
let interval = setInterval(() => {
for (let i = 0; i < numberOfBursts; i++) {
let x = Math.random() * (window.innerWidth - 50) + 25;
let y = Math.random() * (window.innerHeight - 50) + 25;
let color = colors[Math.floor(Math.random() * colors.length)];
for (let j = 0; j < particlesPerBurst; j++) {
let angle = Math.random() * 360;
let speed = Math.random() * 100 + 50;
createParticle(x, y, color, angle, speed);
}
}
}, burstInterval);
setTimeout(() => {
clearInterval(interval);
}, duration);
}
//Ajouter la fonction au contexte global pour pouvoir l'appeler facilement
window.lancerFeuArtifice = lancerFeuArtifice;
//Appeler la fonction pour démarrer automatiquement les feux d'artifice
lancerFeuArtifice();
}
function addMail() {
if (!window.location.href.includes('review-type=completed')) {
const rows = document.querySelectorAll('.vvp-reviews-table--row');
rows.forEach(row => {
//const productUrl = row.querySelector('.vvp-reviews-table--text-col a').href;
const productCell = row.querySelector('.vvp-reviews-table--text-col');
let asin;
if (productCell.querySelector('a')) {
//L'URL existe dans un lien, on extrait depuis l'href
const productUrl = productCell.querySelector('a').href;
asin = extractASIN(productUrl);
} else {
//Directement disponible comme texte dans la cellule
asin = extractASIN(productCell.textContent);
}
//const asin = extractASIN(productUrl);
const key_asin = "email_" + asin;
//Clé pour le numéro de commande
const orderKey_asin = "order_" + asin;
//Créer la checkbox
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = 'check_' + asin;
checkbox.style.margin = '7px';
//Définir la taille de la checkbox
checkbox.style.width = '15px';
checkbox.style.height = '15px';
//Créer la liste déroulante
const select = document.createElement('select');
select.id = 'reason_' + asin;
const defaultEmailTemplates = [
{ title: 'Produit non reçu', text: 'Bonjour,\n\nJe n\'ai jamais reçu le produit suivant, pouvez-vous le retirer de ma liste ?\n\nCommande : $order\nASIN : $asin\n\nCordialement.' },
{ title: 'Produit supprimé', text: 'Bonjour,\n\nLe produit suivant a été supprimé, pouvez-vous le retirer de ma liste ?\n\nCommande : $order\nASIN : $asin\n\nCordialement.' },
{ title: 'Avis en doublon', text: 'Bonjour,\n\nJe ne peux pas déposer d\'avis sur le produit suivant, pouvez-vous le retirer de ma liste ?\n\nCommande : $order\nASIN : $asin\n\nCordialement.' }
];
//Récupérer les modèles depuis localStorage
const emailTemplates = JSON.parse(localStorage.getItem('emailTemplates')) || defaultEmailTemplates;
if (!localStorage.getItem('emailTemplates')) {
localStorage.setItem('emailTemplates', JSON.stringify(defaultEmailTemplates));
}
emailTemplates.forEach(template => {
const option = document.createElement('option');
option.value = template.title;
option.textContent = template.title; //ou template.text selon ce que vous voulez afficher
select.appendChild(option);
});
//Gérer l'état initial à partir de localStorage
const storedData = JSON.parse(localStorage.getItem(key_asin));
if (storedData) {
checkbox.checked = true;
select.value = storedData.reason;
}
//Gérer l'activation de la liste déroulante
const orderDataExists = localStorage.getItem(orderKey_asin);
if (!orderDataExists) {
select.disabled = true; //Désactive la liste déroulante
select.innerHTML = '';
checkbox.disabled = true; //Désactive la checkbox
} else {
const orderData = JSON.parse(orderDataExists);
//Active ou désactive la checkbox en fonction de son état actuel
checkbox.disabled = false; //Assure-toi que la checkbox est activée
select.disabled = !checkbox.checked; //Active ou désactive la liste déroulante basée sur l'état de la checkbox
var originalButton = row.querySelector('.vvp-reviews-table--actions-col');
if (originalButton) {
//Créez un nouveau bouton
var newButton = document.createElement('span');
newButton.className = 'a-button a-button-primary vvp-reviews-table--action-btn';
newButton.style.display = 'block'; //Assurez le retour à la ligne
newButton.style.marginTop = '5px'; //Espacement en haut
//Créez l'intérieur du bouton
var buttonInner = document.createElement('span');
buttonInner.className = 'a-button-inner';
newButton.appendChild(buttonInner);
//Créez le lien et ajustez l'URL
var link = document.createElement('a');
link.className = 'a-button-text';
link.id = 'order-details-link';
link.textContent = 'Voir la commande';
//Assurez-vous que l'orderId est correctement défini ici
link.href = "https://www.amazon.fr/gp/your-account/order-details?ie=UTF8&orderID=" + orderData.orderId;
link.target = '_blank';
buttonInner.appendChild(link);
//Insérez le nouveau bouton après le bouton existant
originalButton.appendChild(newButton);
}
}
//Écouter les changements de checkbox
checkbox.addEventListener('change', () => {
if (checkbox.checked) {
//Activer la liste déroulante
select.disabled = false;
const reason = select.value;
localStorage.setItem(key_asin, JSON.stringify({ asin, reason }));
} else {
//Désactiver la liste déroulante
select.disabled = true;
localStorage.removeItem(key_asin);
}
});
//Sauvegarder les modifications de la liste déroulante
select.addEventListener('change', () => {
if (checkbox.checked) {
const reason = select.value;
localStorage.setItem(key_asin, JSON.stringify({ asin, reason }));
}
});
//Ajouter les éléments à la ligne
const actionCol = row.querySelector('.vvp-reviews-table--actions-col');
const inlineContainer = document.createElement('div');
inlineContainer.style.display = 'flex';
inlineContainer.style.flexFlow = 'row nowrap'; //Force les éléments à s'aligner horizontalement
inlineContainer.style.alignItems = 'center'; //Aligner les éléments verticalement
//Ajoute la checkbox et la liste déroulante au nouveau div
inlineContainer.appendChild(checkbox);
inlineContainer.appendChild(select);
//Ajouter le nouveau div au conteneur d'actions existant
actionCol.appendChild(inlineContainer);
});
addEmailButton();
}
}
function addEmailButton() {
let header = document.querySelector('.vvp-reviews-table--heading-top');
if (isMobile()) {
//On rend visible le header qui est caché par défaut
const header = document.querySelector('.vvp-reviews-table--heading-top');
if (header) header.style.display = 'block';
}
//Créer un conteneur pour le bouton et l'email qui seront alignés à droite
const actionsContainer = document.createElement('div');
if (mobileEnabled == 'true') {
actionsContainer.style.cssText = 'right: 0; top: 0;';
} else {
actionsContainer.style.cssText = 'text-align: right; position: absolute; right: 0; top: 0;';
}
//Bouton 'Générer email'
const button = document.createElement('span');
button.className = 'a-button a-button-primary vvp-reviews-table--action-btn';
button.style.marginRight = '10px'; //Marge à droite du bouton
button.style.marginTop = '10px'; //Marge en haut du bouton
button.style.marginBottom = '5px'; //Marge en haut du bouton
button.style.paddingLeft = '12px';
button.style.paddingRight = '12px';
const buttonInner = document.createElement('span');
buttonInner.className = 'a-button-inner';
const buttonText = document.createElement('a');
buttonText.className = 'a-button-text';
buttonText.textContent = 'Générer email';
buttonText.href = 'javascript:void(0)';
buttonText.addEventListener('click', function() {
const emailText = generateEmail();
navigator.clipboard.writeText(emailText).then(() => {
if (emailText != null) {
alert("Le texte suivant vient d'être copié dans le presse-papiers afin que tu puisses l'envoyer par mail au support :\n\n" + emailText);
window.location.reload();
}
}).catch(err => {
console.error('Erreur lors de la copie :', err);
});
});
//Réduction du padding sur `buttonText`
buttonText.style.paddingLeft = '2px'; //Ajustez selon vos besoins
buttonText.style.paddingRight = '2px'; //Ajustez selon vos besoins
buttonInner.style.paddingLeft = '0px'; //Enlève le padding à gauche
buttonInner.style.paddingRight = '0px'; //Enlève le padding à droite
buttonInner.appendChild(buttonText);
button.appendChild(buttonInner);
//Conteneur et style pour l'email
const emailSpan = document.createElement('div');
emailSpan.innerHTML = 'Support : vine-support@amazon.fr';
emailSpan.style.marginRight = '5px';
//Gestionnaire d'événements pour copier l'email
const emailLink = emailSpan.querySelector('a');
emailLink.addEventListener('click', function() {
navigator.clipboard.writeText('vine-support@amazon.fr').then(() => {
alert('Email copié dans le presse-papiers');
}).catch(err => {
console.error('Erreur lors de la copie :', err);
});
});
//Ajouter le bouton et l'email au conteneur d'actions
actionsContainer.appendChild(button);
actionsContainer.appendChild(emailSpan);
console.log("action", actionsContainer);
//Ajouter le conteneur d'actions à l'en-tête
if (header) {
header.style.position = 'relative'; //S'assure que le positionnement absolu de actionsContainer fonctionne correctement
header.appendChild(actionsContainer);
}
}
function generateEmail() {
//Trouver tous les ASINs cochés dans localStorage
const keys = Object.keys(localStorage);
const checkedAsins = keys.filter(key => key.startsWith("email_") && localStorage.getItem(key));
const emailData = checkedAsins.map(key => {
const asin = key.split("_")[1];
const data = JSON.parse(localStorage.getItem(key));
const orderData = JSON.parse(localStorage.getItem("order_" + asin));
const selectedTemplate = JSON.parse(localStorage.getItem('emailTemplates')).find(t => t.title === data.reason);
return { asin, reason: data.reason, orderData, selectedTemplate, key };
});
if (emailData.length === 0) {
alert("Aucun produit n'est sélectionné pour l'envoi d'email.");
return null;
}
if (emailData.length === 1) {
//Utiliser le modèle spécifique pour un seul produit
const { asin, reason, orderData, selectedTemplate, key } = emailData[0];
if (selectedTemplate && orderData) {
let emailText = selectedTemplate.text.replace(/\$asin/g, asin)
.replace(/\$(commande|order|cmd)/gi, orderData.orderId)
.replace(/\$(nom|name|titre|title)/gi, orderData.productName)
.replace(/\$(date)/gi, orderData.orderDate)
.replace(/\$(reason|raison)/gi, reason);
//navigator.clipboard.writeText(emailText);
//alert(emailText);
localStorage.removeItem(key);
//window.location.reload();
return emailText;
} else {
alert("Il manque des données pour générer l'email.");
}
} else {
//Utiliser le modèle multiproduits
var multiProductTemplate = JSON.parse(localStorage.getItem('multiProductEmailTemplate'));
if (!multiProductTemplate) {
initmultiProductTemplate();
multiProductTemplate = JSON.parse(localStorage.getItem('multiProductEmailTemplate'));
}
let emailText = multiProductTemplate.text;
const productDetailsSegmentMatch = emailText.match(/\$debut(.*?)\$fin/s);
if (!productDetailsSegmentMatch) {
alert("Le modèle d'email multiproduits est mal formé ou les balises $debut/$fin sont absentes.");
return;
}
const productDetailsSegment = productDetailsSegmentMatch[1];
const productDetails = emailData.map(({ asin, orderData, reason }) => {
if (!orderData) return "Données manquantes pour un ou plusieurs produits.";
return productDetailsSegment
.replace(/\$asin/g, asin)
.replace(/\$(commande|order|cmd)/gi, orderData.orderId)
.replace(/\$(nom|name|titre|title)/gi, orderData.productName)
.replace(/\$(date)/gi, orderData.orderDate)
.replace(/\$(reason|raison)/gi, reason);
}).join("");
emailText = emailText.replace(/\$debut.*?\$fin/s, productDetails);
//navigator.clipboard.writeText(emailText);
//alert(emailText);
//Supprimer les données des checkbox après la génération de l'email pour tous les ASINs concernés
emailData.forEach(({ key }) => {
localStorage.removeItem(key);
});
//window.location.reload();
return emailText;
}
}
function autoSaveReview() {
window.addEventListener('load', function() {
// Sélectionner le bouton à l'aide du nouveau sélecteur
var button = document.querySelector('div.a-section.in-context-ryp__submit-button-frame-desktop input.a-button-input');
// Vérifier si le bouton existe avant d'ajouter l'écouteur d'événements
if (button) {
button.addEventListener('click', function() {
saveReview(true);
});
}
});
}
//localStorage.removeItem('enableDateFunction');
var enableDateFunction = localStorage.getItem('enableDateFunction');
var enableReviewStatusFunction = localStorage.getItem('enableReviewStatusFunction');
var enableColorFunction = localStorage.getItem('enableColorFunction');
var reviewColor = localStorage.getItem('reviewColor');
var filterEnabled = localStorage.getItem('filterEnabled');
var profilEnabled = localStorage.getItem('profilEnabled');
//var footerEnabled = localStorage.getItem('footerEnabled');
var footerEnabled = 'false';
var headerEnabled = localStorage.getItem('headerEnabled');
var pageEnabled = localStorage.getItem('pageEnabled');
var mobileEnabled = localStorage.getItem('mobileEnabled');
var emailEnabled = localStorage.getItem('emailEnabled');
var lastUpdateEnabled = localStorage.getItem('lastUpdateEnabled');
var targetPercentageEnabled = localStorage.getItem('targetPercentageEnabled');
var autoSaveEnabled = localStorage.getItem('autoSaveEnabled');
//Initialiser à true si la clé n'existe pas dans le stockage local
if (enableDateFunction === null) {
enableDateFunction = 'true';
localStorage.setItem('enableDateFunction', enableDateFunction);
}
if (enableReviewStatusFunction === null) {
enableReviewStatusFunction = 'true';
localStorage.setItem('enableReviewStatusFunction', enableReviewStatusFunction);
}
if (enableColorFunction === null) {
enableColorFunction = 'true';
localStorage.setItem('enableColorFunction', enableColorFunction);
}
if (reviewColor === null) {
reviewColor = '#0000FF';
localStorage.setItem('reviewColor', reviewColor);
}
if (filterEnabled === null) {
filterEnabled = 'true';
localStorage.setItem('filterEnabled', filterEnabled);
}
if (profilEnabled === null) {
profilEnabled = 'true';
localStorage.setItem('profilEnabled', profilEnabled);
}
if (footerEnabled === null) {
footerEnabled = 'false';
localStorage.setItem('footerEnabled', footerEnabled);
}
if (headerEnabled === null) {
headerEnabled = 'false';
localStorage.setItem('headerEnabled', headerEnabled);
}
if (pageEnabled === null) {
pageEnabled = 'true';
localStorage.setItem('pageEnabled', pageEnabled);
}
if (mobileEnabled === null) {
mobileEnabled = 'false';
localStorage.setItem('mobileEnabled', mobileEnabled);
}
if (emailEnabled === null) {
emailEnabled = 'true';
localStorage.setItem('emailEnabled', emailEnabled);
}
if (lastUpdateEnabled === null) {
lastUpdateEnabled = 'true';
localStorage.setItem('lastUpdateEnabled', lastUpdateEnabled);
}
if (targetPercentageEnabled === null) {
targetPercentageEnabled = 'true';
localStorage.setItem('targetPercentageEnabled', targetPercentageEnabled);
localStorage.setItem('gestavisTargetPercentage', '90');
localStorage.setItem('doFireWorks', 'true');
}
if (autoSaveEnabled === null) {
autoSaveEnabled = 'true';
localStorage.setItem('autoSaveEnabled', autoSaveEnabled);
}
if (mobileEnabled === 'true') {
pageX = "X";
mobileDesign();
}
replaceImageUrl();
//Ajout pour avoir le bon logo/menu sur iPhone principalement
setTimeout(function() {
replaceImageUrl();
}, 50);
if (enableDateFunction === 'true') {
highlightDates();
}
if (enableReviewStatusFunction === 'true') {
highlightReviewStatus();
}
if (enableColorFunction === 'true') {
changeColor();
}
if (filterEnabled === 'true') {
masquerLignesApprouve();
}
if (headerEnabled === 'true') {
hideHeader();
}
if (pageEnabled === 'true') {
addPage();
}
if (enableReviewStatusFunction === 'true' || enableDateFunction === 'true') {
highlightUnavailableStatus();
}
if (profilEnabled === 'true') {
changeProfil();
}
if (emailEnabled === 'true') {
//Les ASIN ne sont pas disponibles sur la page des commandes sur Mobile, donc on ne peut pas sauvegarder les commandes
if (!isMobile()) {
saveOrders();
}
addMail();
}
if (lastUpdateEnabled === 'true') {
lastUpdate();
}
if (targetPercentageEnabled === 'true') {
targetPercentage();
}
if (autoSaveEnabled === 'true') {
autoSaveReview();
}
//End
//Ajout du menu
//Création de la popup pour les raisons de refus
function createEmailPopup() {
if (document.getElementById('emailTemplates')) {
return; //Termine la fonction pour éviter de créer une nouvelle popup
}
//Création de la popup
const popup = document.createElement('div');
popup.id = "emailPopup";
/* popup.style.cssText = `
position: fixed;
z-index: 10001;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding: 20px;
background-color: white;
border: 1px solid #ccc;
box-shadow: 0px 0px 10px #ccc;
`;*/
popup.innerHTML = `
Configuration des Emails
×
Modèles existants
Ajouter un nouveau modèle
?
`;
document.body.appendChild(popup);
document.getElementById('helpIcon').addEventListener('click', function() {
alert('Informations sur la rédaction des modèles.\n\n' +
'Liste des variables qui seront remplacées lors de la génération du mail :\n' +
'- $asin : ASIN du produit\n' +
'- $order : numéro de commande\n' +
'- $reason : raison de la suppression\n' +
'- $nom : nom du produit\n' +
'- $date : date de la commande\n\n' +
'Sur le mail multiproduits, les balises $debut et $fin délimitent la zone de texte qui sera générée pour chaque produit.\n\n' +
'Le titre du modèle servira aussi de raison de suppression lors de la génération multiproduits ($reason).');
});
//Boutons et leurs événements
document.getElementById('closeEmailPopup').addEventListener('click', () => popup.remove());
document.getElementById('closeEmailConfig').addEventListener('click', () => popup.remove());
document.getElementById('saveTemplateButton').addEventListener('click', saveEmailTemplate);
document.getElementById('loadTemplateButton').addEventListener('click', loadSelectedTemplate);
document.getElementById('deleteTemplateButton').addEventListener('click', deleteSelectedTemplate);
document.getElementById('loadMultiProductTemplateButton').addEventListener('click', loadMultiProductTemplate);
//Charger les modèles existants dans la liste déroulante
loadEmailTemplatesDropdown();
}
function loadMultiProductTemplate() {
const multiProductTemplateKey = 'multiProductEmailTemplate';
//Charger le modèle multiproduits ou initialiser avec le modèle par défaut
let multiProductTemplate = JSON.parse(localStorage.getItem(multiProductTemplateKey));
if (!multiProductTemplate) {
initmultiProductTemplate();
}
//Remplissez les champs avec les données du modèle multiproduits
document.getElementById('templateTitle').value = multiProductTemplate.title;
document.getElementById('templateText').value = multiProductTemplate.text;
//Changez l'interface pour refléter que l'utilisateur modifie le modèle multiproduits
document.getElementById('templateActionTitle').innerText = 'Modifier le modèle multiproduits';
document.getElementById('saveTemplateButton').innerText = 'Enregistrer';
document.getElementById('deleteTemplateButton').style.display = 'none'; //Cache le bouton supprimer car ce modèle ne peut pas être supprimé
//Stockez l'index ou la clé du modèle multiproduits
selectedTemplateIndex = multiProductTemplateKey; //Utilisez une clé spéciale ou un index pour identifier le modèle multiproduits
}
function initmultiProductTemplate() {
const multiProductTemplateKey = 'multiProductEmailTemplate';
const defaultMultiProductTemplate = {
title: 'Mail multiproduits',
text: 'Bonjour,\n\nVoici une liste de commande à supprimer de mes avis :\n$debut\nASIN : $asin\nCommande : $order\nRaison : $raison\n$fin\nCordialement.'
};
const multiProductTemplate = defaultMultiProductTemplate;
localStorage.setItem(multiProductTemplateKey, JSON.stringify(multiProductTemplate));
}
function loadEmailTemplatesDropdown() {
//Charger la liste des modèles existants dans la liste déroulante
const templates = JSON.parse(localStorage.getItem('emailTemplates') || '[]');
const templatesDropdown = document.getElementById('existingTemplates');
templatesDropdown.innerHTML = templates.map((template, index) =>
``
).join('');
templatesDropdown.selectedIndex = -1; //Aucune sélection par défaut
}
function addEmailTemplate() {
const title = document.getElementById('newTemplateTitle').value;
const text = document.getElementById('newTemplateText').value;
if (title && text) {
const templates = JSON.parse(localStorage.getItem('emailTemplates') || '[]');
templates.push({ title, text });
localStorage.setItem('emailTemplates', JSON.stringify(templates));
loadEmailTemplates(); //Recharger la liste des modèles
} else {
alert('Veuillez remplir le titre et le texte du modèle.');
}
}
function loadSelectedTemplate() {
const selectedIndex = document.getElementById('existingTemplates').value;
if (selectedIndex !== null) {
const templates = JSON.parse(localStorage.getItem('emailTemplates') || '[]');
const selectedTemplate = templates[selectedIndex];
document.getElementById('templateTitle').value = selectedTemplate.title;
document.getElementById('templateText').value = selectedTemplate.text;
selectedTemplateIndex = selectedIndex; //Mettre à jour l'index sélectionné
//Mettre à jour les textes des boutons et afficher le bouton Supprimer
document.getElementById('templateActionTitle').innerText = 'Modifier le modèle';
document.getElementById('saveTemplateButton').innerText = 'Enregistrer';
document.getElementById('deleteTemplateButton').style.display = 'inline';
}
}
function loadEmailTemplates() {
const templates = JSON.parse(localStorage.getItem('emailTemplates') || '[]');
const templatesContainer = document.getElementById('existingTemplates');
templatesContainer.innerHTML = '';
templates.forEach((template, index) => {
const templateDiv = document.createElement('div');
templateDiv.className = 'template-entry';
templateDiv.dataset.index = index;
templateDiv.innerHTML = `
${template.title}
${template.text}
`;
templateDiv.onclick = function() {
selectTemplate(this);
}
templatesContainer.appendChild(templateDiv);
});
}
function selectTemplate(element) {
//Désélectionner le précédent élément sélectionné
document.querySelectorAll('.template-entry.selected').forEach(e => e.classList.remove('selected'));
//Sélectionner le nouvel élément
element.classList.add('selected');
selectedTemplateIndex = parseInt(element.dataset.index);
//Remplir les champs de modification avec les données du modèle sélectionné
const templates = JSON.parse(localStorage.getItem('emailTemplates') || '[]');
if (templates[selectedTemplateIndex]) {
document.getElementById('editTemplateTitle').value = templates[selectedTemplateIndex].title;
document.getElementById('editTemplateText').value = templates[selectedTemplateIndex].text;
}
}
function saveEmailTemplate() {
const title = document.getElementById('templateTitle').value;
const text = document.getElementById('templateText').value;
const templates = JSON.parse(localStorage.getItem('emailTemplates') || '[]');
if (title.trim() === '' || text.trim() === '') {
alert('Le titre et le texte du modèle ne peuvent pas être vides.');
return;
}
if (selectedTemplateIndex === 'multiProductEmailTemplate') { //Si le modèle multiproduits est en cours de modification
const title = document.getElementById('templateTitle').value;
const text = document.getElementById('templateText').value;
const multiProductTemplate = { title, text };
localStorage.setItem('multiProductEmailTemplate', JSON.stringify(multiProductTemplate));
} else if (selectedTemplateIndex !== null) { //Si un modèle est sélectionné, le mettre à jour
templates[selectedTemplateIndex] = { title, text };
selectedTemplateIndex = null; //Réinitialiser l'index sélectionné après la sauvegarde
} else { //Sinon, ajouter un nouveau modèle
templates.push({ title, text });
}
localStorage.setItem('emailTemplates', JSON.stringify(templates));
loadEmailTemplatesDropdown(); //Recharger la liste déroulante
clearTemplateFields(); //Fonction pour vider les champs
}
function clearTemplateFields() {
//Vider les champs de saisie et réinitialiser les libellés des boutons
document.getElementById('templateTitle').value = '';
document.getElementById('templateText').value = '';
document.getElementById('templateActionTitle').innerText = 'Ajouter un nouveau modèle';
document.getElementById('saveTemplateButton').innerText = 'Ajouter';
document.getElementById('deleteTemplateButton').style.display = 'none';
//Réinitialiser l'index sélectionné
selectedTemplateIndex = null;
}
function deleteSelectedTemplate() {
if (selectedTemplateIndex !== null && confirm('Êtes-vous sûr de vouloir supprimer ce modèle ?')) {
const templates = JSON.parse(localStorage.getItem('emailTemplates') || '[]');
templates.splice(selectedTemplateIndex, 1);
localStorage.setItem('emailTemplates', JSON.stringify(templates));
loadEmailTemplatesDropdown(); //Recharger la liste déroulante
clearTemplateFields(); //Fonction pour vider les champs
}
}
let selectedTemplateIndex = null; //Index du modèle sélectionné
const styleMenu = document.createElement('style');
styleMenu.type = 'text/css';
styleMenu.innerHTML = `
#configPopup, #colorPickerPopup, #emailConfigPopup {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 10000;
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
width: 500px; /* Ajusté pour mieux s'adapter aux deux colonnes de checkbox */
display: flex;
flex-direction: column;
align-items: stretch;
cursor: auto;
border: 2px solid #ccc; /* Ajout d'un contour */
overflow: auto; /* Ajout de défilement si nécessaire */
resize: both; /* Permet le redimensionnement horizontal et vertical */
}
#configPopup h2, #configPopup label {
color: #333;
margin-bottom: 20px;
}
#configPopup h2, #colorPickerPopup h2 {
cursor: grab;
font-size: 1.5em;
text-align: center;
}
#configPopup label {
display: flex;
align-items: center;
}
#configPopup label input[type="checkbox"] {
margin-right: 10px;
}
#configPopup .button-container,
#emailConfigPopup .button-container,
#configPopup .checkbox-container {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
#configPopup .button-container button,
#emailConfigPopup .button-container,
#configPopup .checkbox-container label {
margin-bottom: 10px;
flex-basis: 48%; /* Ajusté pour uniformiser l'apparence des boutons et des labels */
}
#configPopup button,
#emailConfigPopup button {
padding: 5px 10px;
background-color: #f3f3f3;
border: 1px solid #ddd;
border-radius: 4px;
cursor: pointer;
text-align: center;
}
#configPopup button:not(.full-width), #colorPickerPopup button:not(.full-width), #emailConfigPopup button:not(.full-width) {
margin-right: 1%;
margin-left: 1%;
}
#configPopup button.full-width, #colorPickerPopup button.full-width, #emailConfigPopup button.full-width {
flex-basis: 48%;
margin-right: 1%;
margin-left: 1%;
}
#configPopup button:hover,
#emailConfigPopup button:hover {
background-color: #e8e8e8;
}
#configPopup button:active,
#emailConfigPopup button:active {
background-color: #ddd;
}
#configPopup label.disabled {
color: #ccc;
}
#configPopup label.disabled input[type="checkbox"] {
cursor: not-allowed;
}
#saveConfig, #closeConfig, #saveColor, #closeColor, #saveTemplateButton, #closeEmailConfig, #deleteTemplateButton {
padding: 8px 15px !important; /* Plus de padding pour un meilleur visuel */
margin-top !important: 5px;
border-radius: 5px !important; /* Bordures légèrement arrondies */
font-weight: bold !important; /* Texte en gras */
border: none !important; /* Supprime la bordure par défaut */
color: white !important; /* Texte en blanc */
cursor: pointer !important;
transition: background-color 0.3s ease !important; /* Transition pour l'effet au survol */
}
#saveConfig, #saveColor, #saveTemplateButton {
background-color: #4CAF50 !important; /* Vert pour le bouton "Enregistrer" */
}
#closeConfig, #closeColor, #closeEmailConfig, #deleteTemplateButton {
background-color: #f44336 !important; /* Rouge pour le bouton "Fermer" */
}
#saveConfig:hover, #saveColor:hover, #saveTemplateButton:hover {
background-color: #45a049 !important; /* Assombrit le vert au survol */
}
#closeConfig:hover, #closeColor:hover, #closeEmailConfig:hover, #deleteTemplateButton:hover {
background-color: #e53935 !important; /* Assombrit le rouge au survol */
}
#saveColor, #closeColor, #closeEmailConfig, #saveTemplateButton, #deleteTemplateButton {
margin-top: 10px; /* Ajoute un espace de 10px au-dessus du second bouton */
width: 100%; /* Utilise width: 100% pour assurer que le bouton prend toute la largeur */
}
#existingTemplates {
border: 1px solid #ccc;
padding: 4px;
margin-top: 10px;
margin-bottom: 10px;
background-color: white;
width: auto; /* ou une largeur spécifique selon votre design */
}
/* Quand un bouton est seul sur une ligne */
/*
#reviewColor {
flex-basis: 100% !important; /* Prend la pleine largeur pour forcer à aller sur une nouvelle ligne */
margin-right: 1% !important; /* Annuler la marge droite si elle est définie ailleurs */
margin-left: 1% !important; /* Annuler la marge droite si elle est définie ailleurs */
}*/
`;
document.head.appendChild(styleMenu);
//Fonction pour afficher une boîte de dialogue pour définir le pourcentage cible
function promptForTargetPercentage() {
const storedValue = localStorage.getItem('gestavisTargetPercentage');
const targetPercentage = prompt('Entrez le pourcentage cible à atteindre (entre 60 et 100):', storedValue);
if (targetPercentage !== null) {
const parsedValue = parseFloat(targetPercentage);
if (!isNaN(parsedValue) && parsedValue >= 60 && parsedValue <= 100) {
localStorage.setItem('gestavisTargetPercentage', parsedValue);
} else {
alert('Pourcentage invalide. Veuillez entrer un nombre entre 60 et 100.');
}
}
}
//Fonction pour rendre la fenêtre déplaçable
function dragElement(elmnt) {
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
if (document.getElementById(elmnt.id + "Header")) {
//si présent, le header est l'endroit où vous pouvez déplacer la DIV:
document.getElementById(elmnt.id + "Header").onmousedown = dragMouseDown;
} else {
//sinon, déplace la DIV de n'importe quel endroit à l'intérieur de la DIV:
elmnt.onmousedown = dragMouseDown;
}
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
//position de la souris au démarrage:
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
//appelle la fonction chaque fois que le curseur bouge:
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
//calcule la nouvelle position de la souris:
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
//définit la nouvelle position de l'élément:
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
}
function closeDragElement() {
//arrête le mouvement quand le bouton de la souris est relâché:
document.onmouseup = null;
document.onmousemove = null;
}
}
//Crée la fenêtre popup de configuration avec la fonction de déplacement
async function createConfigPopup() {
if (document.getElementById('configPopup')) {
return; //Termine la fonction pour éviter de créer une nouvelle popup
}
const popup = document.createElement('div');
popup.id = "configPopup";
popup.innerHTML = `
${createCheckbox('autoSaveEnabled', 'Sauvegarde automatique des avis', 'Les avis sont sauvegardés dès que vous cliquez sur "Envoyer" sans avoir besoin de l\'enregistrer avant')}
${createCheckbox('enableDateFunction', 'Surlignage du statut des avis', 'Change la couleur du "Statut du commentaire" dans vos avis "En attente de vérification" en fonction de leur date d\'ancienneté. Entre 0 et 6 jours -> Bleu, 7 à 13 jours -> Vert, 14 à 29 jours -> Orange, plus de 30 jours -> Rouge')}
${createCheckbox('enableReviewStatusFunction', 'Surlignage des avis vérifiés', 'Change la couleur du "Statut du commentaire" dans vos avis "Vérifiées" en fonction de leur statut actuel (Approuvé, Non approuvé, etc...)')}
${createCheckbox('enableColorFunction', 'Changer la couleur de la barre de progression des avis', 'Change la couleur de la barre de progression des avis sur la page "Compte". Entre 0 et 59% -> Rouge, 60 à 89% -> Orange et supérieur à 90% -> Vert')}
${createCheckbox('filterEnabled', 'Cacher les avis approuvés', 'Dans l\'onglet "Vérifiées" de vos avis, si l\'avis est Approuvé, alors il est caché')}
${createCheckbox('lastUpdateEnabled', 'Afficher la date de la dernière modification du % d\'avis', 'Indique la date de la dernière modification du % des avis sur le compte')}
${createCheckbox('targetPercentageEnabled', 'Afficher le nombre d\'avis nécessaires pour atteindre un % cible', 'Affiche le nombre d\'avis qu\'il va être nécessaire de faire pour atteindre le % défini')}
${createCheckbox('headerEnabled', 'Cacher totalement l\'entête de la page', 'Cache le haut de la page Amazon, celle avec la zone de recherche et les menus')}
${createCheckbox('mobileEnabled', 'Utiliser l\'affichage mobile', 'Optimise l\affichage sur mobile, pour éviter de mettre la "Version PC". Il est conseillé de cacher également l\'entête avec cette option.')}
${createCheckbox('pageEnabled', 'Affichage des pages en partie haute', 'En plus des pages de navigation en partie basse, ajoute également la navigation des pages en haut')}
${createCheckbox('emailEnabled', 'Génération automatique des emails', 'Permet de générer automatiquement des mails à destination du support vine pour faire retirer un produit de votre liste d\'avis. Attention, on ne peut générer un mail que si le produit a été vu au moins une fois dans la liste de l\'onglet "Commandes"')}
${createCheckbox('profilEnabled', 'Mise en avant des avis avec des votes utiles sur les profils Amazon','Surligne de la couleur définie les avis ayant un vote utile ou plus. Il est également mis en début de page. Le surlignage ne fonctionne pas si l\'avis possède des photos')}
${false ? createCheckbox('footerEnabled', 'Supprimer le footer sur les profils Amazon (à décocher si les avis ne se chargent pas)', 'Supprime le bas de page sur les pages de profil Amazon, cela permet de charger plus facilement les avis sans descendre tout en bas de la page. Cela ne fonctionne que sur PC, donc à désactiver si vous avez le moindre problème sur cette page') : ''}
${addActionButtons()}
`;
document.body.appendChild(popup);
document.getElementById('closePopup').addEventListener('click', () => {
document.getElementById('configPopup').remove();
});
//Ajoute des écouteurs pour les nouveaux boutons
document.getElementById('emailPopup').addEventListener('click', createEmailPopup);
document.getElementById('reviewColor').addEventListener('click', setHighlightColor);
document.getElementById('exportCSV').addEventListener('click', exportReviewsToCSV);
document.getElementById('targetPercentageEnabled').addEventListener('click', function() {
if (this.checked) {
promptForTargetPercentage();
}
});
document.getElementById('purgeTemplate').addEventListener('click', () => {
if (confirm("Êtes-vous sûr de vouloir supprimer tous les modèles d'avis ?")) {
deleteAllTemplates();
reloadButtons();
}
});
document.getElementById('purgeReview').addEventListener('click', () => {
if (confirm("Êtes-vous sûr de vouloir supprimer tous les avis ?")) {
deleteAllReviews();
reloadButtons();
}
});
//Import
document.getElementById('importCSV').addEventListener('click', function() {
document.getElementById('fileInput').click();
});
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.id = 'fileInput';
fileInput.style.display = 'none'; //Le rend invisible
fileInput.accept = '.csv'; //Accepte uniquement les fichiers .csv
//Ajoute l'élément input au body du document
document.body.appendChild(fileInput);
document.getElementById('fileInput').addEventListener('change', function(event) {
const file = event.target.files[0]; //Obtient le fichier sélectionné
if (file) {
readAndImportCSV(file); //Envoie le fichier à la fonction
}
});
dragElement(popup);
document.getElementById('saveConfig').addEventListener('click', saveConfig);
document.getElementById('closeConfig').addEventListener('click', () => popup.remove());
}
function createCheckbox(name, label, explanation = null, disabled = false) {
const isChecked = localStorage.getItem(name) === 'true' ? 'checked' : '';
const isDisabled = disabled ? 'disabled' : '';
//Choisis la couleur ici. Options: 'black', 'white', 'gray'
const color = 'gray'; //Exemple: change cette valeur pour 'black', 'white', ou une autre couleur CSS valide
//Génération de l'ID unique pour le span d'aide
const helpSpanId = `help-span-${name}`;
//Icône d'aide avec gestionnaire d'événements attaché via addEventListener
const helpIcon = explanation ? `?` : '';
const checkboxHtml = ``;
//Attacher le gestionnaire d'événements après le rendu de l'HTML
setTimeout(() => {
const helpSpan = document.getElementById(helpSpanId);
if (helpSpan) {
helpSpan.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
alert(explanation); //Ou toute autre logique d'affichage d'explication
});
}
}, 0);
return checkboxHtml;
}
//Sauvegarde la configuration
async function saveConfig() {
document.querySelectorAll('#configPopup input[type="checkbox"]').forEach(input => {
//Stocke la valeur (true ou false) dans localStorage en tant que chaîne de caractères
localStorage.setItem(input.name, input.checked.toString());
});
//alert('Configuration sauvegardée.');
window.location.reload();
document.getElementById('configPopup').remove();
}
//Ajoute les boutons pour les actions spécifiques qui ne sont pas juste des toggles on/off
function addActionButtons() {
return `
`;
}
//Ajouter la commande de menu "Paramètres"
GM_registerMenuCommand("Paramètres", createConfigPopup, "p");
//End
let buttonsAdded = false; //Suivre si les boutons ont été ajoutés
function tryToAddButtons() {
if (buttonsAdded) return; //Arrêtez si les boutons ont déjà été ajoutés
const submitButtonArea =
document.querySelector(selectorButtons) ||
document.querySelector(selectorButtonsOld);
if (submitButtonArea) {
addButtons(submitButtonArea);
buttonsAdded = true; //Marquer que les boutons ont été ajoutés
//Agrandir la zone pour le texte de l'avis
const textarea = document.getElementById('reviewText');
if (textarea) {
textarea.style.height = '300px'; //Définit la hauteur à 300px
textarea.style.resize = 'both';
}
//HS pour l'instant, permettre l'ajout de plusieurs médias d'un coup
/*var inputElement = document.getElementById('ryp__media-upload-banner-input');
if (inputElement) {
inputElement.setAttribute('multiple', '');
}*/
} else {
setTimeout(tryToAddButtons, 100); //Réessayer après un demi-seconde
}
}
tryToAddButtons();
//Suppression du footer uniquement sur les PC (1000 étant la valeur pour "Version pour ordinateur" sur Kiwi à priori)
if (window.innerWidth > 768 && window.innerWidth != 1000 && window.innerWidth != 1100 && window.location.href.startsWith("https://www.amazon.fr/gp/profile/") && footerEnabled === 'true') {
//Votre code de suppression du footer ici
var styleFooter = document.createElement('style');
styleFooter.textContent = `
#rhf, #rhf-shoveler, .rhf-frame, #navFooter {
display: none !important;
}
footer.nav-mobile.nav-ftr-batmobile {
display: none !important;
}
`;
document.head.appendChild(styleFooter);
}
//Suppression footer partout sauf sur le profil car configurable
if (!window.location.href.startsWith("https://www.amazon.fr/gp/profile/")) {
var supFooter = document.createElement('style');
supFooter.textContent = `
#rhf, #rhf-shoveler, .rhf-frame, #navFooter {
display: none !important;
}
footer.nav-mobile.nav-ftr-batmobile {
display: none !important;
}
`
document.head.appendChild(supFooter);
}
function mobileDesign() {
var mobileCss = document.createElement('style');
mobileCss.textContent = `
#configPopup, #emailConfigPopup {
width: 350px !important;
height: 600px;
}
#colorPickerPopup {
width: 350px !important;
}
/* Taille dynamique pour mobile */
@media (max-width: 600px) {
#configPopup {
width: 90%; /* Prendre 90% de la largeur de l'écran */
height: 90%;
margin: 10px auto; /* Ajout d'un peu de marge autour des popups */
}
}
@media (max-width: 600px) {
#colorPickerPopup, #emailConfigPopup {
width: 90%; /* Prendre 90% de la largeur de l'écran */
margin: 10px auto; /* Ajout d'un peu de marge autour des popups */
}
}
/* Fix CSS du 12/05/25 */
.vvp-centered-logo {
width: auto !important;
}
.vvp-items-button-and-search-container {
margin-top: 0 !important;
margin-left: 0 !important;
}
#vvp-header .vvp-header-links-container {
display: block !important;
}
/* Taille de police différente
.a-ember body {
font-size : 12px !important;
}*/
/* Taille de police pour le texte gris de la page du compte */
.grey-text {
font-size: 12px;
}
/* Taille des fonds gris sur le compte */
#vvp-current-status-box {
height: 200px !important;
}
.vvp-body {
padding: 0px !important;
}
#vvp-vine-activity-metrics-box {
height: 320px !important;
}
.a-button-text {
/* Si nécessaire, ajustez aussi le padding pour .a-button-text */
padding: 2px; /* Ajustement du padding pour le texte du bouton */
}
/* Modification du bouton du rapport */
.a-button-dropdown {
width: auto;
max-width: 300px;
}
.a-button-inner {
padding: 5px 10px;
}
.a-dropdown-prompt {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* On retire le texte de l'écran compte */
#vvp-gold-status-perks-display * {
visibility: hidden;
}
.a-column.a-span6.a-span-last #vvp-you-are-awesome-display {
visibility: hidden;
}
body {
padding-right: 0px !important;
}
.a-section.vvp-items-button-and-search-container {
flex-direction: column !important;
}
.vvp-container-right-align {
margin-top: 10px !important;
width: 100% !important;
flex-grow: 1 !important;
}
.a-icon-search {
display: none;
}
.a-search {
flex-grow: 1;
}
#vvp-search-text-input {
width: 100% !important;
}
.a-tabs {
margin: 0px !important;
}
.a-tabs li a {
padding: 1rem !important;
}
.nav-mobile.nav-ftr-batmobile {
display: none;
}
.vvp-tab-set-container
[data-a-name="vine-items"]
.a-box-inner
.vvp-tab-content
.vvp-items-button-and-search-container {
margin: 0px !important;
}
#a-page
> div.a-container.vvp-body
> div.a-tab-container.vvp-tab-set-container
> ul {
margin-bottom: 0px !important;
}
.a-button-primary {
transition: 0.2s !important;
}
.a-button-primary .a-button-inner {
background-color: transparent !important;
}
.a-button-primary:hover {
opacity: 0.85 !important;
}
/* Pagination styles */
.a-pagination {
display: flex !important;
justify-content: center;
}
.a-pagination li:first-child,
.a-pagination li:last-child {
color: transparent !important;
position: relative;
}
.a-pagination li.a-disabled {
display: none !important;
}
.a-pagination li:first-child a,
.a-pagination li:last-child a {
display: flex;
align-content: center;
position: relative;
justify-content: center;
}
.a-pagination li:first-child a:before,
.a-pagination li:last-child a:before {
position: absolute !important;
color: white !important;
font-size: 2rem !important;
line-height: 4rem;
height: 100%;
width: 100%;
}
ul.a-pagination li:first-child a, /* Cible le premier li de la liste, supposant que c'est Précédent */
li:last-child.a-last a { /* Cible les li avec classe 'a-last', supposant que c'est Suivant */
font-size: 0;
}
li:first-child a span.larr, /* Cible le span larr dans le premier li */
li.a-last a span.larr { /* Cible le span larr dans les li a-last */
font-size: 16px;
visibility: visible;
}
.a-pagination li {
width: 40px !important;
height: 40px !important;
}
.a-pagination li a {
padding: 0px !important;
margin: 0px !important;
height: 100%;
line-height: 40px !important;
}
.vvp-details-btn {
padding: 0.25rem 0 !important;
margin: 0.25rem 0rem !important;
}
.vvp-details-btn .a-button-text {
padding: 0.5px 0.25px !important;
}
/* PRODUCT AND REVIEW PAGES */
#vvp-product-details-img-container,
#vvp-product-details-img-container img {
height: 75px;
}
#vvp-browse-nodes-container,
#vvp-browse-nodes-container .parent-node,
#vvp-browse-nodes-container .child-node {
width: unset !important;
}
.vvp-reviews-table .vvp-reviews-table--row,
.vvp-orders-table .vvp-orders-table--row {
display: flex;
flex-wrap: wrap;
}
.vvp-reviews-table tbody,
.vvp-orders-table tbody {
display: flex !important;
flex-wrap: wrap;
}
.vvp-reviews-table--heading-row,
.vvp-orders-table--heading-row {
display: none !important;
}
.vvp-reviews-table td,
.vvp-orders-table td {
padding-top: 0px !important;
padding-bottom: 0px !important;
}
.vvp-reviews-table td.vvp-reviews-table--image-col,
.vvp-orders-table td.vvp-orders-table--image-col {
padding-top: 10px !important;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
}
.vvp-reviews-table td.vvp-reviews-table--image-col img,
.vvp-orders-table td.vvp-orders-table--image-col img {
height: 75px;
}
.vvp-reviews-table--actions-col,
.vvp-orders-table--actions-col {
width: 100% !important;
display: flex !important;
align-items: center !important;
}
#vvp-items-grid, #tab-unavailable, #tab-hidden, #tab-favourite {
grid-template-columns: repeat(
auto-fill,
minmax(var(--grid-column-width), auto)
) !important;
}
#vvp-header .vvp-header-links-container {
display: block !important;
}
`;
document.head.appendChild(mobileCss);
}
window.addEventListener('load', function () {
//Active le bouton de téléchargement du rapport
var element = document.querySelector('.vvp-tax-report-file-type-select-container.download-disabled');
if (element) {
element.classList.remove('download-disabled');
}
//Ajoute l'heure de l'évaluation
const timeStampElementEnd = document.getElementById('vvp-eval-end-stamp');
const timeStampElementJoin = document.getElementById('vvp-join-vine-stamp');
//const timeStampElementEnd = document.getElementById('vvp-eval-end-stamp');
const timeStampEnd = timeStampElementEnd ? timeStampElementEnd.textContent : null;
const timeStampJoin = timeStampElementJoin ? timeStampElementJoin.textContent : null;
if (timeStampEnd) {
const date = new Date(parseInt(timeStampEnd));
const optionsDate = { day: '2-digit', month: '2-digit', year: 'numeric' };
const optionsTime = { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false };
const formattedDate = date.toLocaleDateString('fr-FR', optionsDate) + ' à ' + date.toLocaleTimeString('fr-FR', optionsTime);
const dateStringElement = document.getElementById('vvp-evaluation-date-string');
if (dateStringElement) {
dateStringElement.innerHTML = `Réévaluation : ${formattedDate}`;
}
}
if (timeStampJoin) {
const date = new Date(parseInt(timeStampJoin));
const optionsDate = { day: '2-digit', month: '2-digit', year: 'numeric' };
const optionsTime = { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false };
const formattedDate = date.toLocaleDateString('fr-FR', optionsDate) + ' à ' + date.toLocaleTimeString('fr-FR', optionsTime);
const dateStringElement = document.getElementById('vvp-member-since-display');
if (dateStringElement) {
dateStringElement.innerHTML = `Membre depuis : ${formattedDate}`;
}
}
//Suppression du bouton pour se désincrire
var elem = document.getElementById('vvp-opt-out-of-vine-button');
if (elem) {
elem.style.display = 'none';
}
});
})();