portal_publico/static/js/account.js

278 lines
11 KiB
JavaScript

document.addEventListener("DOMContentLoaded", async () => {
const chipBtn = document.getElementById("persona-chip");
const dropdown = document.getElementById("account-dropdown");
if (!chipBtn || !dropdown) return;
const me = await pidGetMe();
if (!me) return;
const chipLabel = document.getElementById("persona-chip-label");
const nameEl = document.getElementById("account-name");
const profileEl = document.getElementById("account-profile");
const manageLink = document.getElementById("account-manage-link");
const manageAccountBtn = document.getElementById("account-manage-account-btn");
const profiles = me.perfis || [];
if (chipLabel) chipLabel.textContent = me.nome;
if (nameEl) nameEl.textContent = me.nome;
if (profileEl) {
profileEl.textContent = profiles.length ? profiles.map((p) => p.nome).join(", ") : "Sem perfil vinculado";
}
if (manageLink) manageLink.hidden = !me.gerencia_permissoes;
const themeSwatchesEl = document.getElementById("theme-swatches");
if (themeSwatchesEl) {
const renderSwatches = () => {
const active = pidGetColorTheme();
themeSwatchesEl.innerHTML = PID_COLOR_THEMES.map(
(t) => `
<button type="button" class="theme-swatch ${t.key === active ? "is-active" : ""}" data-theme-choice="${t.key}" style="background:${t.swatch}" aria-label="Tema ${t.label}" title="${t.label}">
${
t.key === active
? '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><path d="M20 6L9 17l-5-5" stroke-linecap="round" stroke-linejoin="round"/></svg>'
: ""
}
</button>`
).join("");
};
renderSwatches();
themeSwatchesEl.addEventListener("click", (event) => {
const btn = event.target.closest("[data-theme-choice]");
if (!btn) return;
pidSetColorTheme(btn.getAttribute("data-theme-choice"));
renderSwatches();
});
}
/* Toggle "Temas sazonais" — liga/desliga temas sazonais (Halloween etc.),
ver seasonal-theme.js. Recarrega a página ao alternar: o tema sazonal já
foi aplicado (ou não) via troca de innerHTML/markup no carregamento
desta mesma página, então não dá pra simplesmente desfazer isso in-place
— o reload garante que tudo (ícone, teias, favicon) reflita a nova
preferência de forma correta e imediata (importante sobretudo pra quem
desligou por aracnofobia — a aranha precisa sumir na hora, não só na
próxima navegação). */
const seasonalToggleBtn = document.getElementById("seasonal-theme-toggle");
const seasonalToggleLabel = document.getElementById("seasonal-theme-toggle-label");
if (seasonalToggleBtn && seasonalToggleLabel && typeof pidSeasonalThemesOptedOut === "function") {
const syncSeasonalToggle = () => {
const optedOut = pidSeasonalThemesOptedOut();
seasonalToggleBtn.setAttribute("aria-pressed", String(!optedOut));
seasonalToggleLabel.textContent = optedOut ? "Inativo" : "Ativo";
};
syncSeasonalToggle();
seasonalToggleBtn.addEventListener("click", () => {
const optedOut = pidSeasonalThemesOptedOut();
pidSetSeasonalThemesOptedOut(!optedOut);
window.location.reload();
});
}
chipBtn.addEventListener("click", (event) => {
event.stopPropagation();
dropdown.hidden = !dropdown.hidden;
});
document.addEventListener("click", (event) => {
if (!dropdown.hidden && !dropdown.contains(event.target) && event.target !== chipBtn) {
dropdown.hidden = true;
}
});
const manageAccountModal = document.getElementById("manage-account-modal");
const manageAccountModalCard = document.getElementById("manage-account-modal-card");
const manageAccountChangePasswordBtn = document.getElementById("manage-account-change-password-btn");
const manageAccountCloseBtn = document.getElementById("manage-account-close-btn");
const manageAccountLideradosField = document.getElementById("manage-account-liderados-field");
const manageAccountLideradosError = document.getElementById("manage-account-liderados-error");
const manageAccountSaveBtn = document.getElementById("manage-account-save-btn");
// Redefinida abaixo quando o modal Gerenciar Usuário existe na página.
let lideradosAlterados = () => false;
if (manageAccountBtn && manageAccountModal) {
function departamentosTexto(usuario) {
return (usuario.departamentos || []).map((d) => d.nome).join(", ");
}
const seletorLiderados = pidCriarSeletorDuplo({
available: {
selectAll: document.getElementById("manage-account-liderados-available-select-all"),
sortHeader: document.getElementById("manage-account-liderados-available-sort-nome"),
searchPrincipal: document.getElementById("manage-account-liderados-available-search-nome"),
searchSecundaria: document.getElementById("manage-account-liderados-available-search-departamento"),
body: document.getElementById("manage-account-liderados-available-body"),
footer: document.getElementById("manage-account-liderados-available-footer"),
acaoBtn: document.getElementById("manage-account-liderados-available-link-btn"),
},
linked: {
selectAll: document.getElementById("manage-account-liderados-linked-select-all"),
sortHeader: document.getElementById("manage-account-liderados-linked-sort-nome"),
searchPrincipal: document.getElementById("manage-account-liderados-linked-search-nome"),
searchSecundaria: document.getElementById("manage-account-liderados-linked-search-departamento"),
body: document.getElementById("manage-account-liderados-linked-body"),
footer: document.getElementById("manage-account-liderados-linked-footer"),
acaoBtn: document.getElementById("manage-account-liderados-linked-unlink-btn"),
},
secundariaValor: departamentosTexto,
});
// Só há o que perder se o seletor de liderados foi carregado e a lista
// vinculada difere da que veio do servidor (me.liderados).
let lideradosCarregados = false;
function idsOrdenados(ids) {
return ids.map(Number).sort((a, b) => a - b).join(",");
}
lideradosAlterados = () => {
if (!lideradosCarregados) return false;
return idsOrdenados(seletorLiderados.getVinculadosIds()) !== idsOrdenados((me.liderados || []).map((u) => u.id));
};
manageAccountBtn.addEventListener("click", async () => {
dropdown.hidden = true;
manageAccountLideradosError.textContent = "";
lideradosCarregados = false;
if (me.lideranca) {
let usuarios;
try {
usuarios = (await pidApiRequest("/usuarios-resumo/")) || [];
} catch (e) {
await pidAlert(e.message, { titulo: "Não foi possível abrir Gerenciar Usuário" });
return;
}
manageAccountLideradosField.hidden = false;
manageAccountSaveBtn.hidden = false;
manageAccountModalCard.classList.add("modal-card--wide");
const candidatos = usuarios
.filter((u) => u.id !== me.id)
.slice()
.sort((a, b) => a.nome.localeCompare(b.nome));
seletorLiderados.setDados(candidatos, (me.liderados || []).map((u) => u.id));
lideradosCarregados = true;
} else {
manageAccountLideradosField.hidden = true;
manageAccountSaveBtn.hidden = true;
manageAccountModalCard.classList.remove("modal-card--wide");
}
manageAccountModal.hidden = false;
});
manageAccountCloseBtn.addEventListener("click", async () => {
if (lideradosAlterados()) {
const sair = await pidConfirm("Sair sem salvar as alterações?", {
titulo: "Sair sem salvar",
textoConfirmar: "Sair sem salvar",
perigoso: true,
});
if (!sair) return;
}
manageAccountModal.hidden = true;
});
manageAccountSaveBtn.addEventListener("click", async () => {
const liderados = seletorLiderados.getVinculadosIds();
const textoOriginal = manageAccountSaveBtn.textContent;
manageAccountSaveBtn.disabled = true;
manageAccountSaveBtn.textContent = "Salvando…";
manageAccountLideradosError.textContent = "";
try {
const atualizados = await pidApiRequest("/me/liderados/", { method: "PATCH", body: { liderados } });
me.liderados = atualizados;
manageAccountModal.hidden = true;
} catch (e) {
manageAccountLideradosError.textContent = e.message;
} finally {
manageAccountSaveBtn.disabled = false;
manageAccountSaveBtn.textContent = textoOriginal;
}
});
}
const passwordModal = document.getElementById("password-modal");
if (!passwordModal || !manageAccountChangePasswordBtn) return;
const currentInput = document.getElementById("pwd-current");
const newInput = document.getElementById("pwd-new");
const confirmInput = document.getElementById("pwd-confirm");
const pwdError = document.getElementById("pwd-error");
const pwdSaveBtn = document.getElementById("pwd-save-btn");
const pwdCancelBtn = document.getElementById("pwd-cancel-btn");
pidBindPasswordToggle(document.getElementById("pwd-current-toggle"), currentInput);
pidBindPasswordToggle(document.getElementById("pwd-new-toggle"), newInput);
pidBindPasswordToggle(document.getElementById("pwd-confirm-toggle"), confirmInput);
function closePasswordModal() {
passwordModal.hidden = true;
}
// Campo oculto de usuário (só existe em portal.html): dá ao gerenciador de
// senhas do navegador a qual conta a senha nova pertence.
const pwdUsernameInput = document.getElementById("pwd-username");
if (pwdUsernameInput) pwdUsernameInput.value = me.username || "";
manageAccountChangePasswordBtn.addEventListener("click", async () => {
// "Alterar senha" também fecha Gerenciar Usuário: mesmo aviso do Fechar.
if (lideradosAlterados()) {
const sair = await pidConfirm("Sair sem salvar as alterações?", {
titulo: "Sair sem salvar",
textoConfirmar: "Sair sem salvar",
perigoso: true,
});
if (!sair) return;
}
manageAccountModal.hidden = true;
currentInput.value = "";
newInput.value = "";
confirmInput.value = "";
currentInput.type = "password";
newInput.type = "password";
confirmInput.type = "password";
pwdError.textContent = "";
passwordModal.hidden = false;
currentInput.focus();
});
pwdCancelBtn.addEventListener("click", closePasswordModal);
pwdSaveBtn.addEventListener("click", async () => {
const current = currentInput.value;
const next = newInput.value;
const confirmValue = confirmInput.value;
if (!next) {
pwdError.textContent = "Informe a nova senha.";
return;
}
if (next !== confirmValue) {
pwdError.textContent = "A confirmação não corresponde à nova senha.";
return;
}
const textoOriginal = pwdSaveBtn.textContent;
pwdSaveBtn.disabled = true;
pwdSaveBtn.textContent = "Salvando…";
pwdError.textContent = "";
try {
await pidApiRequest("/me/senha/", {
method: "POST",
body: { senha_atual: current, nova_senha: next },
});
closePasswordModal();
await pidAlert("Senha alterada.", { titulo: "Senha alterada" });
} catch (e) {
pwdError.textContent = e.message;
} finally {
pwdSaveBtn.disabled = false;
pwdSaveBtn.textContent = textoOriginal;
}
});
});