/* ============================================================
KeS Metas — Painel (Dashboard) do usuário / equipe
============================================================ */
const { useState, useEffect } = React;
const KESM_ = window.KESM;
function fonteLabel(fonte) {
if (fonte === "usuario") return { txt: "Preenchido por você", icon: "user" };
if (fonte === "admin") return { txt: "Preenchido pelo admin", icon: "lock" };
return { txt: "Automático · Google Agenda", icon: "calendarCheck" };
}
/* ---- Cartão de META (topo) ---- */
function GoalCard({ rotulo, periodMeta, base }) {
return (
);
}
/* ---- Cartão de INDICADOR (realizado x meta) ---- */
function MetricCard({ ind, data, canEdit, onEdit }) {
const K = window.KES;
const pct = Math.round(data.pct);
const tone = window.tonePct(data.pct);
const f = fonteLabel(ind.fonte);
const valTxt = ind.tipo === "moeda" ? K.fmtMoeda(data.realizado) : K.fmtNum(data.realizado);
const metaTxt = ind.tipo === "moeda" ? K.fmtMoeda(data.meta) : K.fmtNum(data.meta);
return (
{ind.fonte === "auto" ? (
) : canEdit ? (
) : (
)}
{valTxt}
/ {metaTxt}
{pct}%
{f.txt}
{tone === "ok" ? "Meta batida" : tone === "warn" ? "Quase lá" : "Abaixo"}
);
}
/* ---- Modal: registrar atividade do dia ---- */
function RegisterModal({ subjectUser, editableKeys, state, dispatch, onClose }) {
const K = window.KES;
const todayISO = K.iso(K.SIM_TODAY);
const [date, setDate] = useState(todayISO);
const existing = state.activities.find((a) => a.userId === subjectUser.id && a.date === date) || {};
const [vals, setVals] = useState({});
// recarrega valores quando muda a data
useEffect(() => {
const ex = state.activities.find((a) => a.userId === subjectUser.id && a.date === date) || {};
const v = {};
editableKeys.forEach((k) => { v[k] = ex[k] != null ? ex[k] : ""; });
setVals(v);
}, [date]);
const labels = {
ligacoes: "Ligações realizadas", conexoes: "Conexões feitas",
reunioesRealizadas: "Reuniões realizadas", reunioesQualificadas: "Reuniões qualificadas",
vendas: "Vendas realizadas", valorVendas: "Valor das vendas (R$)",
};
function save() {
const patch = {};
editableKeys.forEach((k) => { patch[k] = Math.max(0, parseInt(vals[k], 10) || 0); });
// // AQUI: integrar com backend/banco de dados (persistir atividade)
dispatch({ type: "UPSERT_ACTIVITY", userId: subjectUser.id, date, patch });
onClose(`Atividade de ${K.fmtBR(date)} registrada.`);
}
return (
onClose()}
footer={<> onClose()}>CancelarSalvar>}>
{subjectUser.nome}
Lançamento diário de atividade
setDate(e.target.value)} />
{editableKeys.map((k) => (
setVals((s) => ({ ...s, [k]: e.target.value }))} />
))}
"Reuniões Agendadas" é calculado automaticamente pelos agendamentos.
);
}
/* ---- Ranking / Competição da equipe (leaderboard por pessoa) ---- */
function RankingChart({ state, userIds, metricKey, period, hideNumbers, highlightId }) {
const K = window.KES, KM = window.KESM;
const usersById = {}; state.users.forEach((u) => { usersById[u.id] = u; });
const ind = KM.INDICADORES.find((i) => i.key === metricKey);
const fmt = ind && ind.tipo === "moeda" ? K.fmtMoeda : K.fmtNum;
const rows = userIds.map((uid) => {
const m = KM.userMetrics(state, uid, period)[metricKey];
return { user: usersById[uid], val: m.realizado, pct: m.pct };
}).filter((r) => r.user).sort((a, b) => b.val - a.val);
const max = Math.max(1, ...rows.map((r) => r.val));
return (
{rows.map((r, i) => {
const tone = window.tonePct(r.pct);
const me = r.user.id === highlightId;
return (
{i === 0 ? : (i + 1) + "º"}
{r.user.nome}
{me && Você}
{!hideNumbers && {Math.round(r.pct)}%}
{!hideNumbers &&
{fmt(r.val)}
}
);
})}
);
}
/* ---- Tela principal ---- */
function DashboardScreen({ state, dispatch, viewer, subject, extraActions }) {
const K = window.KES, KM = window.KESM;
const [chartKey, setChartKey] = useState("ligacoes");
const [chartMode, setChartMode] = useState("rank");
const [reg, setReg] = useState(false);
const metrics = subject.isTeam
? KM.teamMetrics(state, subject.userIds, state.period)
: KM.userMetrics(state, subject.user.id, state.period);
// chaves que o viewer pode editar para este subject
let editableKeys = [];
if (!subject.isTeam) {
if (viewer.papel === "admin") editableKeys = ["ligacoes", "conexoes", "reunioesRealizadas", "reunioesQualificadas", "vendas", "valorVendas"];
else if (subject.user.id === viewer.id) editableKeys = ["ligacoes", "conexoes"];
}
const goalDefs = [
{ key: "ligacoes", rotulo: "Meta de Ligações" },
{ key: "reunioesRealizadas", rotulo: "Meta de Reuniões Realizadas" },
{ key: "reunioesQualificadas", rotulo: "Meta de Reuniões Qualificadas" },
{ key: "vendas", rotulo: "Meta de Vendas" },
];
const baseTxt = (key) => {
const m = state.metas[key];
return m.unidade === "dia" ? `${K.fmtNum(m.alvo)} / dia útil` : `${K.fmtNum(m.alvo)} / mês`;
};
const chartTabs = [
["ligacoes", "Ligações"], ["conexoes", "Conexões"], ["reunioesAgendadas", "Reuniões agend."],
["reunioesRealizadas", "Reuniões realiz."], ["reunioesQualificadas", "Reuniões qualif."], ["vendas", "Vendas"],
];
const chartLbl = (chartTabs.find(([k]) => k === chartKey) || ["", ""])[1];
const canRank = subject.isTeam || viewer.papel === "comum";
const effMode = canRank ? chartMode : "evo";
const rankIds = subject.isTeam ? subject.userIds : state.users.map((u) => u.id);
const rankHideNumbers = !subject.isTeam; // usuário comum vê só posições
const series = KM.dailySeries(state, subject.userIds, chartKey, state.period);
const targetLine = KM.dailyTargetFor(state, chartKey, subject.userIds.length);
const overall = Math.round(KM.overallPct(metrics));
const greeting = subject.isTeam ? "Visão da Equipe" : (subject.user.id === viewer.id ? `Olá, ${viewer.nome.split(" ")[0]}` : subject.user.nome);
return (
{greeting}
{subject.isTeam ? `${subject.userIds.length} pessoas · ` : ""}Atingimento médio do período: {overall}%
{extraActions}
dispatch({ type: "SET_PERIOD", period: p })} />
{!subject.isTeam && editableKeys.length > 0 && (
setReg(true)}>Registrar dia
)}
{/* Metas */}
{goalDefs.map((g) => (
))}
{/* Indicadores */}
Indicadores de realização
{KM.INDICADORES.map((ind) => (
setReg(ind.key)} />
))}
{/* Gráfico */}
{effMode === "rank" ? "Competição da equipe" : "Evolução no período"}
{canRank ? (
) : (
Por dia útil
)}
{chartLbl}
{chartTabs.map(([k, lbl]) => (
))}
{effMode === "rank"
?
:
}
{effMode === "rank" && rankHideNumbers && (
Você vê apenas as posições da competição. Os números da equipe ficam visíveis só para administradores.
)}
{reg && !subject.isTeam && (
{ setReg(false); if (msg) dispatch({ type: "TOAST", msg }); }} />
)}
);
}
window.DashboardScreen = DashboardScreen;