Идеи клиента — не игнорируем, а рассматриваем. Статус и решение видны клиенту: чувствует, что услышан.
`+sgs.map(s=>{
const st=CRM_SUG_ST[s.status]||CRM_SUG_ST.new;
return `
-
${s.source==='client'?'👤 клиент':'✍ оператор'}${s.topic?' · '+esc(s.topic):''}${(s.at||'').slice(0,10).split('-').reverse().join('.')}
+
${s.source==='client'?ic('user',12)+' клиент':ic('edit',12)+' оператор'}${s.topic?' · '+esc(s.topic):''}${(s.at||'').slice(0,10).split('-').reverse().join('.')}
${esc(s.text)}
${SUG_STATUSES.map(([k,n])=>``).join('')}
@@ -812,7 +824,7 @@ function syncPaymentReminders(){
const need=due[s.key]&&(sp[s.key]||0)>0&&!pays[s.key];
const idx=state.tasks.findIndex(t=>t.auto===marker);
if(need){
- const text=`💳 Получить оплату за «${s.name}» — ${money(sp[s.key])}`;
+ const text=`Получить оплату за «${s.name}» — ${money(sp[s.key])}`;
if(idx<0){state.tasks.push({text,due:due[s.key],done:false,auto:marker});changed=true;}
else if(state.tasks[idx].due!==due[s.key]||state.tasks[idx].text!==text||state.tasks[idx].done){
state.tasks[idx].due=due[s.key];state.tasks[idx].text=text;state.tasks[idx].done=false;changed=true;}
@@ -827,8 +839,8 @@ function alignDealToEstimate(){
saveEstimate();renderClient();
}
// ── Способы оплаты ──
-const PAY_METHODS=[["bank","🏦 Безнал"],["cash","💵 Наличные"],["sbp","📱 СБП"],["card","💳 Карта"]];
-const METHOD_ICON={bank:"🏦",cash:"💵",sbp:"📱",card:"💳"};
+const PAY_METHODS=[["bank","Безнал"],["cash","Наличные"],["sbp","СБП"],["card","Карта"]];
+const METHOD_ICON={bank:ic('card',13),cash:ic('cash',13),sbp:ic('phone',13),card:ic('card',13)};
const METHOD_NAME=Object.fromEntries(PAY_METHODS.map(m=>[m[0],m[1]]));
function methodSelect(id,cur){return `
`;}
async function saveEstimate(){
@@ -898,7 +910,7 @@ function showQRModal(d,name,amount){
ov.style.cssText='position:fixed;inset:0;background:rgba(0,0,0,.5);display:flex;align-items:center;justify-content:center;z-index:9999';
const qrBox=d.qr_image?`

`:`
QR придёт от Сбера
после настройки реквизитов
(демо-режим)
`;
ov.innerHTML=`
-
📱 СБП · ${esc(name)}
+
${ic('phone',16)} СБП · ${esc(name)}
${money(amount)}
${qrBox}
${d.demo?`
${esc(d.message||'Демо-режим')}
`:''}
@@ -965,7 +977,7 @@ function renderPaymentPlan(){
${CX_SHORT[cx]}
-
Получено ${money(paidTotal)} из ${money(target)} · ✎ цена · ↺ отмена оплаты · 📅 срок оплаты этапа
+
Получено ${money(paidTotal)} из ${money(target)} · ✎ цена · ↺ отмена оплаты · ${ic('calendar',12)} срок оплаты этапа
${CLIENT_STAGES.map(s=>{
const done=stageIsDone(s.key);
const paid=pays[s.key];
@@ -983,14 +995,14 @@ function renderPaymentPlan(){
} else if(done){
badge=`
Доступен к оплате`;
sub=`
${esc(s.desc)}
`;
- action=`
`;
+ action=`
`;
} else {
- badge=`
🔒 Ожидает`;
+ badge=`
${ic('lock',11)} Ожидает`;
sub=`
${esc(s.desc)}
`;
action=`
${money(price)}`;
}
const dd=due[s.key]||'';
- const dueChip=dd?`
📅 до ${fmtDate(dd)}`:'';
+ const dueChip=dd?`
${ic('calendar',11)} до ${fmtDate(dd)}`:'';
const dueInput=isFree?'':`
`;
return `
${s.icon}
@@ -1011,20 +1023,20 @@ function renderPaymentPlan(){
${money(target)}
${unalloc!==0?`
- ${unalloc>0?'⚠ Нераспределено':'⚠ Перебор сметы'}: ${money(Math.abs(unalloc))}
+ ${unalloc>0?ic('alert',12)+' Нераспределено':ic('alert',12)+' Перебор сметы'}: ${money(Math.abs(unalloc))}
`:''}
${isUnlocked
- ?`✅ Оплата получена. Клиент может скачать ТЗ и печатные документы.`
- :`🔒 Правило: ТЗ (печать + выгрузка) выдаётся клиенту только после полной оплаты — ${money(target)}.`}
+ ?`${ic('checkCircle',14)} Оплата получена. Клиент может скачать ТЗ и печатные документы.`
+ :`${ic('lock',13)} Правило: ТЗ (печать + выгрузка) выдаётся клиенту только после полной оплаты — ${money(target)}.`}
`;
}
function renderPricing(){
const box=document.getElementById("pricingBox");if(!box)return;
const billing=(state.crm||{}).billing_type||"paid";
- const freeNote=billing==="free"?`