feat: delete client — removes project with all data (messages/models/artifacts/docs)

This commit is contained in:
wasrusgen 2026-05-30 14:38:35 +03:00
parent 9f63f3dd46
commit c37491cbc7
2 changed files with 27 additions and 1 deletions

View File

@ -616,6 +616,26 @@ def update_crm():
save_artifact(proj["id"], "crm", crm) save_artifact(proj["id"], "crm", crm)
return jsonify({"ok": True, "crm": crm}) return jsonify({"ok": True, "crm": crm})
@app.route("/api/project/delete", methods=["POST"])
def delete_project():
data = request.get_json(force=True) or {}
proj = get_project(data.get("token"))
if not proj:
return jsonify({"error": "project not found"}), 404
pid = proj["id"]
con = db()
con.execute("DELETE FROM messages WHERE project_id=?", (pid,))
con.execute("DELETE FROM models WHERE project_id=?", (pid,))
con.execute("DELETE FROM artifacts WHERE project_id=?", (pid,))
con.execute("DELETE FROM projects WHERE id=?", (pid,))
con.commit()
# удалить загруженные документы
import shutil
pdir = os.path.join(UPLOAD_DIR, proj["token"])
if os.path.isdir(pdir):
shutil.rmtree(pdir, ignore_errors=True)
return jsonify({"ok": True})
@app.route("/api/project/tasks", methods=["POST"]) @app.route("/api/project/tasks", methods=["POST"])
def update_tasks(): def update_tasks():
data = request.get_json(force=True) or {} data = request.get_json(force=True) or {}

View File

@ -211,7 +211,7 @@ function renderClient(){
<div class="cc-field"><div class="cc-fl">Оплачено</div><input class="cc-fi" id="cpPaid" type="number" value="${crm.paid_amount||''}" placeholder="0" onchange="saveCrm()"></div> <div class="cc-field"><div class="cc-fl">Оплачено</div><input class="cc-fi" id="cpPaid" type="number" value="${crm.paid_amount||''}" placeholder="0" onchange="saveCrm()"></div>
<div class="cc-field"><div class="cc-fl">Источник</div><input class="cc-fi" id="cpSrc" value="${esc(crm.source)}" placeholder="откуда пришёл" onchange="saveCrm()"></div> <div class="cc-field"><div class="cc-fl">Источник</div><input class="cc-fi" id="cpSrc" value="${esc(crm.source)}" placeholder="откуда пришёл" onchange="saveCrm()"></div>
</div> </div>
<div class="cc-actions"><button class="btn btn-p" onclick="inviteLink()">🔗 Ссылка клиенту</button><a class="btn btn-g" href="cabinet.html?t=${current}" target="_blank">👁 Открыть кабинет</a></div> <div class="cc-actions"><button class="btn btn-p" onclick="inviteLink()">🔗 Ссылка клиенту</button><a class="btn btn-g" href="cabinet.html?t=${current}" target="_blank">👁 Открыть кабинет</a><button class="btn btn-g" style="margin-left:auto;border-color:#FECACA;color:#DC2626" onclick="deleteClient()">🗑 Удалить клиента</button></div>
<div id="tasksBox"></div> <div id="tasksBox"></div>
<div class="tabs">${TABS.map(t=>`<div class="tab ${t.id===activeTab?'active':''} ${approved(t.id)?'done':''}" onclick="setTab('${t.id}')">${t.icon} ${t.name}</div>`).join("")}</div> <div class="tabs">${TABS.map(t=>`<div class="tab ${t.id===activeTab?'active':''} ${approved(t.id)?'done':''}" onclick="setTab('${t.id}')">${t.icon} ${t.name}</div>`).join("")}</div>
<div id="tabContent"></div>`; <div id="tabContent"></div>`;
@ -240,6 +240,12 @@ async function saveCrm(){
await loadProjects(); await loadProjects();
} }
function inviteLink(){const url=`${location.origin}${location.pathname.replace('crm.html','cabinet.html')}?t=${current}`;navigator.clipboard.writeText(url).then(()=>alert("Ссылка скопирована:\n\n"+url)).catch(()=>prompt("Ссылка:",url));} function inviteLink(){const url=`${location.origin}${location.pathname.replace('crm.html','cabinet.html')}?t=${current}`;navigator.clipboard.writeText(url).then(()=>alert("Ссылка скопирована:\n\n"+url)).catch(()=>prompt("Ссылка:",url));}
async function deleteClient(){
const nm=state.client_name||"клиента";
if(!confirm(`Удалить «${nm}» со всеми данными (интервью, анализ, ТЗ, документы)?\n\nЭто действие необратимо.`))return;
await fetch(`${API}/api/project/delete`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:current})});
current=null;await loadProjects();setView("dashboard");
}
function exportSpecPDF(){ function exportSpecPDF(){
const s=state.spec;if(!s){alert("ТЗ ещё не собрано");return} const s=state.spec;if(!s){alert("ТЗ ещё не собрано");return}