Editor de Proposta
Valor Final de Venda
R$ 0,00
const API_URL = 'api.php';
let currentUser = null;
let proposals = [];
const stages = ['Novo lead', 'Visita técnica', 'Proposta enviada', 'Negociação', 'Fechado', 'Instalado', 'Perdido'];
document.getElementById('login-form').addEventListener('submit', async (e) => {
e.preventDefault();
try {
const res = await fetch(`${API_URL}?action=login`, {
method: 'POST',
body: JSON.stringify({
email: document.getElementById('email').value,
password: document.getElementById('password').value
})
});
const text = await res.text(); // Lê como texto primeiro para evitar erro de JSON
const data = JSON.parse(text);
if(data.success) {
currentUser = data.user;
document.getElementById('login-screen').classList.add('hidden');
document.getElementById('user-greeting').innerText = `Olá, ${currentUser.name}`;
if(currentUser.role === 'admin') {
document.getElementById('dashboard-area').classList.remove('hidden');
carregarDashboard();
} else {
document.getElementById('area-custos').style.display = 'none';
}
carregarPropostas();
} else {
alert(data.error || 'Credenciais inválidas.');
}
} catch (error) {
console.error(error);
alert('Erro ao conectar com o servidor. Verifique o api.php.');
}
});
async function logout() {
await fetch(`${API_URL}?action=logout`);
location.reload();
}
async function carregarPropostas() {
const res = await fetch(`${API_URL}?action=get_proposals`);
proposals = await res.json();
renderKanban();
}
async function carregarDashboard() {
const res = await fetch(`${API_URL}?action=dashboard_stats`);
const stats = await res.json();
document.getElementById('dash-total').innerText = stats.total;
document.getElementById('dash-fechados').innerText = stats.fechados;
document.getElementById('dash-conversao').innerText = stats.conversao + '%';
}
function renderKanban() {
const board = document.getElementById('kanban-board');
board.innerHTML = '';
stages.forEach(stage => {
const col = document.createElement('div');
col.className = 'kanban-col bg-gray-200 rounded-lg p-4 flex flex-col shadow-sm border border-gray-300 h-full overflow-y-auto';
col.innerHTML = `${stage} ${proposals.filter(p=>p.stage===stage).length}
`;
proposals.filter(p => p.stage === stage).forEach(p => {
const card = document.createElement('div');
card.className = 'bg-white p-4 rounded-lg shadow-sm mb-3 cursor-pointer hover:shadow-md border-l-4 border-headGreen transition';
card.innerHTML = `
${p.client_name}
${p.city} | ${p.consumption} kWh
R$ ${parseFloat(p.final_price).toLocaleString('pt-BR', {minimumFractionDigits:2})}
`;
card.onclick = () => abrirProposta(p);
col.appendChild(card);
});
board.appendChild(col);
});
}
function calcularCustosBase() {
const qty = parseFloat(document.getElementById('prop-qty').value) || 0;
const power = parseFloat(document.getElementById('prop-power').value) || 0;
const kwp = (qty * power) / 1000;
document.getElementById('display-kwp').innerText = kwp.toFixed(2);
if (qty > 0) document.getElementById('prop-instalacao').value = (qty * 120).toFixed(2);
if (kwp > 0) document.getElementById('prop-infra').value = (kwp * 200).toFixed(2);
calcFinal();
}
function calcFinal() {
const kit = parseFloat(document.getElementById('prop-kit').value) || 0;
const proj = parseFloat(document.getElementById('prop-projeto').value) || 0;
const inst = parseFloat(document.getElementById('prop-instalacao').value) || 0;
const infra = parseFloat(document.getElementById('prop-infra').value) || 0;
const margem = parseFloat(document.getElementById('prop-margem').value) || 25;
const custos = kit + proj + inst + infra;
const fator = (100 - margem) / 100;
const final = fator > 0 ? custos / fator : 0;
document.getElementById('display-final').innerText = final.toLocaleString('pt-BR', {minimumFractionDigits:2, maximumFractionDigits:2});
return final;
}
function novaProposta() {
document.getElementById('form-proposta').reset();
document.getElementById('prop-id').value = '';
document.getElementById('display-final').innerText = '0,00';
document.getElementById('display-kwp').innerText = '0.00';
document.getElementById('modal-proposta').classList.remove('hidden');
}
function fecharModal() {
document.getElementById('modal-proposta').classList.add('hidden');
}
function abrirProposta(p) {
document.getElementById('prop-id').value = p.id;
document.getElementById('prop-cliente').value = p.client_name;
document.getElementById('prop-doc').value = p.client_doc || '';
document.getElementById('prop-cidade').value = p.city;
document.getElementById('prop-consumo').value = p.consumption;
document.getElementById('prop-inversor').value = p.inverter_brand;
document.getElementById('prop-painel').value = p.panel_brand;
document.getElementById('prop-qty').value = p.panel_qty;
document.getElementById('prop-power').value = p.panel_power;
if(currentUser.role === 'admin') {
document.getElementById('prop-kit').value = p.kit_price;
document.getElementById('prop-projeto').value = p.project_price;
document.getElementById('prop-instalacao').value = p.install_price;
document.getElementById('prop-infra').value = p.infra_price;
document.getElementById('prop-margem').value = p.margin;
}
calcularCustosBase();
document.getElementById('display-final').innerText = parseFloat(p.final_price).toLocaleString('pt-BR', {minimumFractionDigits:2});
document.getElementById('modal-proposta').classList.remove('hidden');
}
async function salvarProposta() {
const finalP = calcFinal();
const data = {
id: document.getElementById('prop-id').value,
client_name: document.getElementById('prop-cliente').value,
client_doc: document.getElementById('prop-doc').value,
client_phone: '',
city: document.getElementById('prop-cidade').value,
consumption: document.getElementById('prop-consumo').value,
inverter_brand: document.getElementById('prop-inversor').value,
panel_brand: document.getElementById('prop-painel').value,
panel_qty: document.getElementById('prop-qty').value,
panel_power: document.getElementById('prop-power').value,
kit_price: document.getElementById('prop-kit').value || 0,
project_price: document.getElementById('prop-projeto').value || 0,
install_price: document.getElementById('prop-instalacao').value || 0,
infra_price: document.getElementById('prop-infra').value || 0,
margin: document.getElementById('prop-margem').value || 25
};
const res = await fetch(`${API_URL}?action=save_proposal`, { method: 'POST', body: JSON.stringify(data) });
if((await res.json()).success) { fecharModal(); carregarPropostas(); }
}
function formatMoney(value) {
return parseFloat(value).toLocaleString('pt-BR', {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
// O restante da função gerarPDFReal() mantém-se exatamente igual a partir daqui...
``` *(Certifique-se apenas de não apagar a sua função `gerarPDFReal()` que vinha logo a seguir no código anterior).*
### 2. Verifique o Ficheiro `api.php`
Se a correção acima lançar o alerta *"Erro ao conectar com o servidor"*, significa que a Hostinger bloqueou a sessão. Certifique-se de que a primeira linha do seu ficheiro `api.php` (antes de qualquer espaço em branco) é estritamente:
```php