// src/pages/process/ProcessList.ts import processHtml from "./process.html?raw"; import globalCss from "../../assets/styles/style.css?inline"; import Services from "../../services/service"; export class ProcessListPage extends HTMLElement { private services!: Services; // Éléments du DOM private inputInput!: HTMLInputElement; private autocompleteList!: HTMLUListElement; private tagsContainer!: HTMLElement; private detailsContainer!: HTMLElement; private okButton!: HTMLButtonElement; private wrapper!: HTMLElement; constructor() { super(); this.attachShadow({ mode: "open" }); } async connectedCallback() { this.services = await Services.getInstance(); this.render(); setTimeout(() => this.initLogic(), 0); } render() { if (this.shadowRoot) { // Le CSS et le HTML de base sont statiques, donc innerHTML est OK ici. this.shadowRoot.innerHTML = ` ${processHtml} `; } } async initLogic() { const root = this.shadowRoot; if (!root) return; this.wrapper = root.querySelector("#autocomplete-wrapper") as HTMLElement; this.inputInput = root.querySelector("#process-input") as HTMLInputElement; this.autocompleteList = root.querySelector( "#autocomplete-list" ) as HTMLUListElement; this.tagsContainer = root.querySelector( "#selected-tags-container" ) as HTMLElement; this.detailsContainer = root.querySelector( "#process-details" ) as HTMLElement; this.okButton = root.querySelector( "#go-to-process-btn" ) as HTMLButtonElement; this.inputInput.addEventListener("keyup", () => this.handleInput()); this.inputInput.addEventListener("click", () => this.openDropdown()); document.addEventListener("click", (e) => { const path = e.composedPath(); if (!path.includes(this.wrapper)) { this.closeDropdown(); } }); this.okButton.addEventListener("click", () => this.goToProcess()); document.addEventListener("processes-updated", async () => { await this.populateList(this.inputInput.value); }); await this.populateList(""); } // --- Logique Autocomplete Sécurisée --- async populateList(query: string) { this.autocompleteList.innerHTML = ""; // Vide la liste proprement const mineArray = (await this.services.getMyProcesses()) ?? []; const allProcesses = await this.services.getProcesses(); const otherProcesses = Object.keys(allProcesses).filter( (id) => !mineArray.includes(id) ); const listToShow = [...mineArray, ...otherProcesses]; let count = 0; for (const pid of listToShow) { const process = allProcesses[pid]; if (!process) continue; const name = this.services.getProcessName(process) || pid; if ( query && !name.toLowerCase().includes(query.toLowerCase()) && !pid.includes(query) ) { continue; } count++; const li = document.createElement("li"); const nameSpan = document.createElement("span"); nameSpan.textContent = name; li.appendChild(nameSpan); if (mineArray.includes(pid)) { li.classList.add("my-process"); const small = document.createElement("small"); small.style.opacity = "0.6"; small.style.marginLeft = "8px"; small.textContent = "(Mien)"; // Texte statique sûr li.appendChild(small); } li.addEventListener("click", () => { this.addTag(pid, name); this.inputInput.value = ""; this.showProcessDetails(pid); this.closeDropdown(); }); this.autocompleteList.appendChild(li); } if (count === 0) { const empty = document.createElement("li"); empty.textContent = "Aucun résultat"; empty.style.cursor = "default"; empty.style.opacity = "0.5"; this.autocompleteList.appendChild(empty); } } handleInput() { this.openDropdown(); this.populateList(this.inputInput.value); } openDropdown() { this.autocompleteList.style.display = "block"; } closeDropdown() { this.autocompleteList.style.display = "none"; } // --- Gestion des Tags Sécurisée --- addTag(pid: string, name: string) { this.tagsContainer.innerHTML = ""; const tag = document.createElement("div"); tag.className = "tag"; const spanName = document.createElement("span"); spanName.textContent = name; tag.appendChild(spanName); const closeBtn = document.createElement("span"); closeBtn.className = "tag-close"; closeBtn.innerHTML = "×"; closeBtn.addEventListener("click", (e) => { e.stopPropagation(); this.removeTag(); }); tag.appendChild(closeBtn); this.tagsContainer.appendChild(tag); } removeTag() { this.tagsContainer.innerHTML = ""; this.detailsContainer.innerHTML = ""; const emptyState = document.createElement("div"); emptyState.className = "empty-state"; const p = document.createElement("p"); p.textContent = "Aucun processus sélectionné."; emptyState.appendChild(p); this.detailsContainer.appendChild(emptyState); this.okButton.disabled = true; this.okButton.classList.add("disabled"); } // --- Détails du processus Sécurisés --- async showProcessDetails(pid: string) { this.detailsContainer.textContent = "Chargement..."; // Safe loader const process = await this.services.getProcess(pid); if (!process) return; this.detailsContainer.innerHTML = ""; const name = this.services.getProcessName(process) || "Sans nom"; // Description let description = "Pas de description"; const lastState = this.services.getLastCommitedState(process); if (lastState?.pcd_commitment["description"]) { const diff = await this.services.getDiffByValue( lastState.pcd_commitment["description"] ); if (diff) description = diff.value_commitment; } const containerDiv = document.createElement("div"); containerDiv.className = "process-item"; // Titre const titleDiv = document.createElement("div"); titleDiv.className = "process-title-display"; titleDiv.textContent = name; // Safe containerDiv.appendChild(titleDiv); // Description const descDiv = document.createElement("div"); descDiv.style.fontSize = "0.9rem"; descDiv.style.marginBottom = "10px"; descDiv.textContent = description; // Safe containerDiv.appendChild(descDiv); // ID const idDiv = document.createElement("div"); idDiv.style.fontSize = "0.8rem"; idDiv.style.opacity = "0.7"; idDiv.style.marginBottom = "10px"; idDiv.textContent = `ID: ${pid}`; // Safe containerDiv.appendChild(idDiv); // Label "États en attente" const labelDiv = document.createElement("div"); labelDiv.style.fontWeight = "bold"; labelDiv.style.marginTop = "15px"; labelDiv.textContent = "États en attente :"; containerDiv.appendChild(labelDiv); const uncommitted = this.services.getUncommitedStates(process); if (uncommitted.length > 0) { uncommitted.forEach((state) => { const el = document.createElement("div"); el.className = "state-element"; // textContent ici aussi, même si state_id est technique el.textContent = `État: ${state.state_id.substring(0, 16)}...`; el.addEventListener("click", () => { this.shadowRoot ?.querySelectorAll(".state-element") .forEach((x) => x.classList.remove("selected")); el.classList.add("selected"); this.okButton.disabled = false; this.okButton.dataset.target = `${pid}/${state.state_id}`; }); containerDiv.appendChild(el); }); } else { const empty = document.createElement("div"); empty.style.padding = "10px"; empty.style.opacity = "0.6"; empty.textContent = "Aucun état en attente de validation."; containerDiv.appendChild(empty); } this.detailsContainer.appendChild(containerDiv); } goToProcess() { const target = this.okButton.dataset.target; if (target) { console.log("Navigation vers", target); alert("Navigation vers : " + target); } } } customElements.define("process-list-page", ProcessListPage);