Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 86f52029f5 |
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "Planification",
|
"name": "Planification",
|
||||||
"version": "5.0.3",
|
"version": "5.0.4",
|
||||||
"description": "Vue claire et rapide du planning des techniciens EasyVista. Regroupe interventions et réservations par tech, affiche horaires, contact, lieu, catégorie et statut en un coup d'œil.",
|
"description": "Vue claire et rapide du planning des techniciens EasyVista. Regroupe interventions et réservations par tech, affiche horaires, contact, lieu, catégorie et statut en un coup d'œil.",
|
||||||
"permissions": ["activeTab", "scripting", "storage", "tabs", "alarms"],
|
"permissions": ["activeTab", "scripting", "storage", "tabs", "alarms"],
|
||||||
"host_permissions": [
|
"host_permissions": [
|
||||||
|
|||||||
+13
@@ -2204,3 +2204,16 @@ header.topbar::before {
|
|||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* v5.0.4 : boutons preset matin / après-midi / journée dans modal absence */
|
||||||
|
.modal-preset-row {
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.modal-preset-btn {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 100px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1424,6 +1424,38 @@ function showAbsenceModal() {
|
|||||||
endGroup.appendChild(endRow);
|
endGroup.appendChild(endRow);
|
||||||
card.appendChild(endGroup);
|
card.appendChild(endGroup);
|
||||||
|
|
||||||
|
// v5.0.4 : presets rapides pour les horaires (matin / après-midi / journée)
|
||||||
|
const presetGroup = document.createElement("div");
|
||||||
|
presetGroup.className = "modal-form-group";
|
||||||
|
const presetLabel = document.createElement("label");
|
||||||
|
presetLabel.className = "modal-form-label";
|
||||||
|
presetLabel.textContent = "Presets rapides";
|
||||||
|
presetGroup.appendChild(presetLabel);
|
||||||
|
const presetRow = document.createElement("div");
|
||||||
|
presetRow.className = "modal-form-row modal-preset-row";
|
||||||
|
const presets = [
|
||||||
|
{ label: "Matin", start: "08:00", end: "12:00" },
|
||||||
|
{ label: "Après-midi", start: "13:00", end: "18:00" },
|
||||||
|
{ label: "Toute la journée", start: "08:00", end: "18:00" }
|
||||||
|
];
|
||||||
|
for (const p of presets) {
|
||||||
|
const btn = document.createElement("button");
|
||||||
|
btn.type = "button";
|
||||||
|
btn.className = "btn btn-secondary modal-preset-btn";
|
||||||
|
btn.textContent = p.label;
|
||||||
|
btn.addEventListener("click", () => {
|
||||||
|
startTime.value = p.start;
|
||||||
|
endTime.value = p.end;
|
||||||
|
// Synchroniser visuellement la mise à jour et déclencher
|
||||||
|
// endDateTouched si besoin (la date reste inchangée)
|
||||||
|
startTime.dispatchEvent(new Event("input", { bubbles: true }));
|
||||||
|
endTime.dispatchEvent(new Event("input", { bubbles: true }));
|
||||||
|
});
|
||||||
|
presetRow.appendChild(btn);
|
||||||
|
}
|
||||||
|
presetGroup.appendChild(presetRow);
|
||||||
|
card.appendChild(presetGroup);
|
||||||
|
|
||||||
// v5.0.0 : la date de fin suit la date de début tant que l'user ne l'a
|
// v5.0.0 : la date de fin suit la date de début tant que l'user ne l'a
|
||||||
// pas explicitement modifiée. 95% des absences sont d'un seul jour, donc
|
// pas explicitement modifiée. 95% des absences sont d'un seul jour, donc
|
||||||
// changer juste le start doit mettre à jour le end aussi.
|
// changer juste le start doit mettre à jour le end aussi.
|
||||||
@@ -2139,24 +2171,18 @@ function actionNodeToIntervention(node) {
|
|||||||
if (refFromLabel) ref = refFromLabel[1];
|
if (refFromLabel) ref = refFromLabel[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Détection du type "Réservation" : un coordinateur a bloqué un créneau.
|
// Détection du type "Réservation" vs "Absence".
|
||||||
// Dans le XML, action_type = "AL-Absence" pour ce genre de créneau, mais
|
|
||||||
// action_label contient le vrai pattern :
|
|
||||||
// action_label = "Xxxxx / Créé par : Nom, Prénom"
|
|
||||||
// Ex: "Ecrans / Créé par : Nom20, Prénom20" → Réservation (matériel)
|
|
||||||
// "Rollout / Créé par : Nom24, Prénom24" → Réservation
|
|
||||||
// "Congés / Créé par : ..." → Absence
|
|
||||||
// "Maladie / Créé par : ..." → Absence
|
|
||||||
// "Pompier / Créé par : ..." → Absence
|
|
||||||
// "Evènements spéciaux / Créé par : ..." → Absence
|
|
||||||
// "Réunion / Créé par : ..." → Absence
|
|
||||||
// "Déménagement / Créé par : ..." → Absence
|
|
||||||
//
|
//
|
||||||
// v5.0.2 : pour éviter les faux positifs, on considère qu'une entrée est
|
// v5.0.3 (simplifiée) : le label suit le pattern "Nom / Créé par : X Y".
|
||||||
// une Réservation UNIQUEMENT si son label correspond à une ressource
|
//
|
||||||
// matérielle (Ecrans, PC, MAC, Téléphones, UTP, Rollout). Tout le reste
|
// - Congés / Maladie / Pompier → AL-Absence (tech réellement absent)
|
||||||
// est une absence. Ça couvre les types de HOLIDAY_TYPES non-matériels.
|
// - TOUT LE RESTE (Ecrans, PC, MAC, Rollout, Téléphones, UTP, Réunion,
|
||||||
const RESERVATION_LABELS = /^(ecran(s)?|pc|mac|t[ée]l[ée]phones?|utp|rollout)$/i;
|
// Déménagement, Evènements spéciaux, Formation, ...)
|
||||||
|
// → AL-Reservation (créneau bloqué, tech pas absent)
|
||||||
|
//
|
||||||
|
// Cette règle simple évite les cas "absence toute la journée" déclenchés
|
||||||
|
// par erreur pour des réservations de type événement / réunion.
|
||||||
|
const ABSENCE_LABELS = /^(cong[ée]s|maladie|pompier)$/i;
|
||||||
let effectiveType = actionType;
|
let effectiveType = actionType;
|
||||||
let reservationLabel = null;
|
let reservationLabel = null;
|
||||||
let reservationCreator = null;
|
let reservationCreator = null;
|
||||||
@@ -2164,15 +2190,14 @@ function actionNodeToIntervention(node) {
|
|||||||
if (reservationMatch) {
|
if (reservationMatch) {
|
||||||
const label1 = reservationMatch[1].trim();
|
const label1 = reservationMatch[1].trim();
|
||||||
const creator = reservationMatch[2].trim();
|
const creator = reservationMatch[2].trim();
|
||||||
if (RESERVATION_LABELS.test(label1)) {
|
if (ABSENCE_LABELS.test(label1)) {
|
||||||
// Ressource matérielle → Réservation
|
// Vraie absence du tech
|
||||||
|
effectiveType = "AL-Absence";
|
||||||
|
} else {
|
||||||
|
// Réservation : créneau bloqué (matériel ou activité), tech pas absent
|
||||||
effectiveType = "AL-Reservation";
|
effectiveType = "AL-Reservation";
|
||||||
reservationLabel = label1;
|
reservationLabel = label1;
|
||||||
reservationCreator = creator;
|
reservationCreator = creator;
|
||||||
} else {
|
|
||||||
// Tout autre (Congés, Maladie, Pompier, Evènements spéciaux, Réunion,
|
|
||||||
// Déménagement, Formation, etc.) → Absence
|
|
||||||
effectiveType = "AL-Absence";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3912,24 +3937,23 @@ function buildCard(tech, isoDate) {
|
|||||||
note.textContent = "Absent toute la journée";
|
note.textContent = "Absent toute la journée";
|
||||||
}
|
}
|
||||||
body.appendChild(note);
|
body.appendChild(note);
|
||||||
// v5.0.1 : bouton 🗑 pour supprimer l'absence (seulement si actionId réel,
|
|
||||||
// pas les absences récurrentes type Pillonel vendredi qui n'existent pas
|
// v5.0.4 : tooltip au hover sur toute la carte absent (pas juste un
|
||||||
// dans EasyVista).
|
// bouton visible). Contient : détail période + bouton supprimer si
|
||||||
|
// c'est une absence supprimable (actionId réel, pas pompier récurrent).
|
||||||
if (ab.actionId && !ab.isPompier && !ab._recurring) {
|
if (ab.actionId && !ab.isPompier && !ab._recurring) {
|
||||||
const delWrap = document.createElement("div");
|
// On attache le tooltip sur la CARD ENTIÈRE (cardEl) — comme ça
|
||||||
delWrap.className = "absence-delete-wrap";
|
// survoler n'importe où sur la zone grisée "absent" le déclenche.
|
||||||
const delBtn = document.createElement("button");
|
const ivCopy = {
|
||||||
delBtn.type = "button";
|
...ab,
|
||||||
delBtn.className = "tooltip-delete-btn";
|
type: "AL-Absence" // force pour buildTooltipHTML
|
||||||
delBtn.textContent = "🗑 Supprimer l'absence";
|
};
|
||||||
delBtn.dataset.actionId = ab.actionId;
|
cardEl.addEventListener("mouseenter", (e) => {
|
||||||
delBtn.dataset.kind = "absence";
|
showTooltip(e, ivCopy, cardEl);
|
||||||
delBtn.addEventListener("click", (e) => {
|
});
|
||||||
e.stopPropagation();
|
cardEl.addEventListener("mouseleave", () => {
|
||||||
_triggerDeleteItem(ab.actionId, "absence");
|
hideTooltip();
|
||||||
});
|
});
|
||||||
delWrap.appendChild(delBtn);
|
|
||||||
body.appendChild(delWrap);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user