mirror of
https://github.com/wasrusgen/zov-tech.git
synced 2026-06-03 15:44:47 +00:00
feat: add entrance+floor fields, fix geocoder false-positive on locality match
- clients.js: new client + edit client forms get подъезд and этаж fields (optional) - clients.js: address string includes подъезд/этаж when filled - clients.js: splitAddress parses подъезд/этаж from stored address string - clients.js: geocoder now checks result.kind — only shows ✓ for house/street precision; locality/province match shows warning "улица не найдена" without saving coords - podbor.css: addr-grid grows to 3 rows (город/улица, дом/кв, подъезд/этаж) - index.html: cache bump → v=20260514j Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
44799362c1
commit
bedef30465
@ -76,6 +76,14 @@ const Clients = (function () {
|
||||
<span class="field-sublabel">Кв./офис</span>
|
||||
<input type="text" id="ad_apt" placeholder="12" inputmode="numeric">
|
||||
</label>
|
||||
<label class="field addr-entrance">
|
||||
<span class="field-sublabel">Подъезд</span>
|
||||
<input type="text" id="ad_entrance" placeholder="1" inputmode="numeric">
|
||||
</label>
|
||||
<label class="field addr-floor">
|
||||
<span class="field-sublabel">Этаж</span>
|
||||
<input type="text" id="ad_floor" placeholder="3" inputmode="numeric">
|
||||
</label>
|
||||
</div>
|
||||
<span class="field-error" id="errAddr"></span>
|
||||
<div class="geo-status" id="geoStatus"></div>
|
||||
@ -133,17 +141,24 @@ const Clients = (function () {
|
||||
});
|
||||
const name = (form.querySelector("#fn").value || "").trim();
|
||||
const phoneRaw = (form.querySelector("#ph").value || "").trim();
|
||||
const adCity = (form.querySelector("#ad_city").value || "").trim();
|
||||
const adStreet = (form.querySelector("#ad_street").value|| "").trim();
|
||||
const adHouse = (form.querySelector("#ad_house").value || "").trim();
|
||||
const adApt = (form.querySelector("#ad_apt").value || "").trim();
|
||||
const adCity = (form.querySelector("#ad_city").value || "").trim();
|
||||
const adStreet = (form.querySelector("#ad_street").value || "").trim();
|
||||
const adHouse = (form.querySelector("#ad_house").value || "").trim();
|
||||
const adApt = (form.querySelector("#ad_apt").value || "").trim();
|
||||
const adEntrance = (form.querySelector("#ad_entrance").value|| "").trim();
|
||||
const adFloor = (form.querySelector("#ad_floor").value || "").trim();
|
||||
const note = (form.querySelector("#nt").value || "").trim();
|
||||
const contract_no = (form.querySelector("#cn").value || "").trim();
|
||||
const contract_date = (form.querySelector("#cd").value || "").trim();
|
||||
|
||||
// Собираем адрес из полей
|
||||
const address = [adCity, adStreet, adHouse ? "д. " + adHouse : "", adApt ? "кв. " + adApt : ""]
|
||||
.filter(Boolean).join(", ");
|
||||
const address = [
|
||||
adCity, adStreet,
|
||||
adHouse ? "д. " + adHouse : "",
|
||||
adApt ? "кв. " + adApt : "",
|
||||
adEntrance ? "подъезд " + adEntrance : "",
|
||||
adFloor ? "этаж " + adFloor : "",
|
||||
].filter(Boolean).join(", ");
|
||||
|
||||
// Валидация
|
||||
if (!name || name.length < 2) {
|
||||
@ -179,9 +194,15 @@ const Clients = (function () {
|
||||
});
|
||||
const geoData = await geoRes.json();
|
||||
if (geoData.ok && geoData.result) {
|
||||
gps_lat = geoData.result.lat;
|
||||
gps_lng = geoData.result.lng;
|
||||
geoEl.innerHTML = `<span class="geo-ok">✓ ${escHtml(geoData.result.formatted || address)}</span>`;
|
||||
const kind = (geoData.result.kind || "").toLowerCase();
|
||||
const precise = ["house", "street", "entrance", "building"].includes(kind);
|
||||
if (precise) {
|
||||
gps_lat = geoData.result.lat;
|
||||
gps_lng = geoData.result.lng;
|
||||
geoEl.innerHTML = `<span class="geo-ok">✓ ${escHtml(geoData.result.formatted || address)}</span>`;
|
||||
} else {
|
||||
geoEl.innerHTML = `<span class="geo-warn">⚠ Улица не найдена — геокодер вернул «${escHtml(geoData.result.formatted || "")}». Проверьте написание улицы. Сохраняем без координат.</span>`;
|
||||
}
|
||||
} else {
|
||||
geoEl.innerHTML = `<span class="geo-warn">⚠ Адрес не найден в геокодере — проверьте написание. Сохраняем без координат.</span>`;
|
||||
}
|
||||
@ -240,23 +261,22 @@ const Clients = (function () {
|
||||
});
|
||||
}
|
||||
|
||||
// Разбирает сохранённый адрес «Город, Улица, д. NN, кв. MM» обратно в поля.
|
||||
// Разбирает сохранённый адрес «Город, Улица, д. NN, кв. MM, подъезд P, этаж F» обратно в поля.
|
||||
function splitAddress(combined) {
|
||||
if (!combined) return { city: "Санкт-Петербург", street: "", house: "", apt: "" };
|
||||
if (!combined) return { city: "Санкт-Петербург", street: "", house: "", apt: "", entrance: "", floor: "" };
|
||||
let s = combined.trim();
|
||||
let apt = "";
|
||||
const aptMatch = s.match(/,\s*кв\.?\s*([^\s,]+)/i);
|
||||
if (aptMatch) { apt = aptMatch[1]; s = s.replace(aptMatch[0], ""); }
|
||||
let house = "";
|
||||
const houseMatch = s.match(/,\s*д\.?\s*([^\s,]+)/i);
|
||||
if (houseMatch) { house = houseMatch[1]; s = s.replace(houseMatch[0], ""); }
|
||||
const grab = (re) => { const m = s.match(re); if (m) { s = s.replace(m[0], ""); return m[1]; } return ""; };
|
||||
const floor = grab(/,\s*этаж\s+([^\s,]+)/i);
|
||||
const entrance = grab(/,\s*подъезд\s+([^\s,]+)/i);
|
||||
const apt = grab(/,\s*кв\.?\s*([^\s,]+)/i);
|
||||
const house = grab(/,\s*д\.?\s*([^\s,]+)/i);
|
||||
s = s.replace(/,$/, "").trim();
|
||||
const parts = s.split(",").map(p => p.trim()).filter(Boolean);
|
||||
let city = "", street = "";
|
||||
if (parts.length >= 2) { city = parts[0]; street = parts.slice(1).join(", "); }
|
||||
else if (parts.length === 1) { city = parts[0]; }
|
||||
if (!city) city = "Санкт-Петербург";
|
||||
return { city, street, house, apt };
|
||||
return { city, street, house, apt, entrance, floor };
|
||||
}
|
||||
|
||||
function normalizePhone(raw) {
|
||||
@ -751,6 +771,14 @@ const Clients = (function () {
|
||||
<span class="field-sublabel">Кв./офис</span>
|
||||
<input type="text" id="ed_apt" value="${escAttr(addrParts.apt)}" placeholder="12" inputmode="numeric">
|
||||
</label>
|
||||
<label class="field addr-entrance">
|
||||
<span class="field-sublabel">Подъезд</span>
|
||||
<input type="text" id="ed_entrance" value="${escAttr(addrParts.entrance)}" placeholder="1" inputmode="numeric">
|
||||
</label>
|
||||
<label class="field addr-floor">
|
||||
<span class="field-sublabel">Этаж</span>
|
||||
<input type="text" id="ed_floor" value="${escAttr(addrParts.floor)}" placeholder="3" inputmode="numeric">
|
||||
</label>
|
||||
</div>
|
||||
<span class="field-error" id="ed_errAddr"></span>
|
||||
<div class="geo-status" id="ed_geoStatus"></div>
|
||||
@ -781,12 +809,14 @@ const Clients = (function () {
|
||||
});
|
||||
|
||||
form.querySelector("#ed_save").addEventListener("click", async () => {
|
||||
const fn = form.querySelector("#ed_fn").value.trim();
|
||||
const ph = form.querySelector("#ed_ph").value.trim();
|
||||
const edCity = (form.querySelector("#ed_city").value || "").trim();
|
||||
const edStreet = (form.querySelector("#ed_street").value || "").trim();
|
||||
const edHouse = (form.querySelector("#ed_house").value || "").trim();
|
||||
const edApt = (form.querySelector("#ed_apt").value || "").trim();
|
||||
const fn = form.querySelector("#ed_fn").value.trim();
|
||||
const ph = form.querySelector("#ed_ph").value.trim();
|
||||
const edCity = (form.querySelector("#ed_city").value || "").trim();
|
||||
const edStreet = (form.querySelector("#ed_street").value || "").trim();
|
||||
const edHouse = (form.querySelector("#ed_house").value || "").trim();
|
||||
const edApt = (form.querySelector("#ed_apt").value || "").trim();
|
||||
const edEntrance = (form.querySelector("#ed_entrance").value || "").trim();
|
||||
const edFloor = (form.querySelector("#ed_floor").value || "").trim();
|
||||
const cno = form.querySelector("#ed_cno").value.trim();
|
||||
const cdate = form.querySelector("#ed_cdate").value.trim();
|
||||
const errName = form.querySelector("#ed_errName");
|
||||
@ -809,8 +839,13 @@ const Clients = (function () {
|
||||
return;
|
||||
}
|
||||
|
||||
const address = [edCity, edStreet, edHouse ? "д. " + edHouse : "", edApt ? "кв. " + edApt : ""]
|
||||
.filter(Boolean).join(", ");
|
||||
const address = [
|
||||
edCity, edStreet,
|
||||
edHouse ? "д. " + edHouse : "",
|
||||
edApt ? "кв. " + edApt : "",
|
||||
edEntrance ? "подъезд " + edEntrance : "",
|
||||
edFloor ? "этаж " + edFloor : "",
|
||||
].filter(Boolean).join(", ");
|
||||
|
||||
const btn = form.querySelector("#ed_save");
|
||||
btn.disabled = true; btn.textContent = "Проверяем адрес…";
|
||||
@ -830,9 +865,15 @@ const Clients = (function () {
|
||||
});
|
||||
const geoData = await geoRes.json();
|
||||
if (geoData.ok && geoData.result) {
|
||||
gps_lat = geoData.result.lat;
|
||||
gps_lng = geoData.result.lng;
|
||||
geoEl.innerHTML = `<span class="geo-ok">✓ ${escHtml(geoData.result.formatted || address)}</span>`;
|
||||
const kind = (geoData.result.kind || "").toLowerCase();
|
||||
const precise = ["house", "street", "entrance", "building"].includes(kind);
|
||||
if (precise) {
|
||||
gps_lat = geoData.result.lat;
|
||||
gps_lng = geoData.result.lng;
|
||||
geoEl.innerHTML = `<span class="geo-ok">✓ ${escHtml(geoData.result.formatted || address)}</span>`;
|
||||
} else {
|
||||
geoEl.innerHTML = `<span class="geo-warn">⚠ Улица не найдена — геокодер вернул «${escHtml(geoData.result.formatted || "")}». Проверьте написание улицы. Сохраняем без координат.</span>`;
|
||||
}
|
||||
} else {
|
||||
geoEl.innerHTML = `<span class="geo-warn">⚠ Адрес не найден — сохраняем без координат.</span>`;
|
||||
}
|
||||
|
||||
@ -3328,8 +3328,10 @@
|
||||
margin-top: 6px;
|
||||
}
|
||||
.addr-grid .field { margin: 0; }
|
||||
.addr-house { grid-column: 1; }
|
||||
.addr-apt { grid-column: 2; }
|
||||
.addr-house { grid-column: 1; }
|
||||
.addr-apt { grid-column: 2; }
|
||||
.addr-entrance { grid-column: 1; }
|
||||
.addr-floor { grid-column: 2; }
|
||||
.field-sublabel {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
|
||||
@ -12,14 +12,14 @@
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Geist:wght@400;500;600&family=Newsreader:ital,wght@0,400..600;1,400..600&family=Instrument+Serif:ital@0;1&family=JetBrains+Mono:wght@400;500&family=Cormorant+Garamond:ital,wght@1,400;1,500;1,600&family=Caveat:wght@500;700&display=swap">
|
||||
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
||||
<link rel="stylesheet" href="assets/styles.css?v=20260514i">
|
||||
<link rel="stylesheet" href="assets/podbor.css?v=20260514i">
|
||||
<link rel="stylesheet" href="assets/styles.css?v=20260514j">
|
||||
<link rel="stylesheet" href="assets/podbor.css?v=20260514j">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Splash — лого @wasrusgen1 + опилки (16) + вращающийся диск -->
|
||||
<div class="loader splash" id="splash">
|
||||
<div class="brand-logo-wrap">
|
||||
<img class="brand-logo" src="assets/wasrusgen-logo.svg?v=20260514i" alt="@wasrusgen1">
|
||||
<img class="brand-logo" src="assets/wasrusgen-logo.svg?v=20260514j" alt="@wasrusgen1">
|
||||
<div class="splash-dust" aria-hidden="true">
|
||||
<span class="dust d1"></span> <span class="dust d2"></span>
|
||||
<span class="dust d3"></span> <span class="dust d4"></span>
|
||||
@ -35,15 +35,15 @@
|
||||
<div class="brand-tagline-gold">CRM</div>
|
||||
</div>
|
||||
<main id="app"></main>
|
||||
<script src="assets/icons.js?v=20260514i"></script>
|
||||
<script src="assets/podbor.config.js?v=20260514i"></script>
|
||||
<script src="assets/podbor.picts.js?v=20260514i"></script>
|
||||
<script src="assets/podbor.js?v=20260514i"></script>
|
||||
<script src="assets/clients.js?v=20260514i"></script>
|
||||
<script src="assets/zamer-picts.js?v=20260514i"></script>
|
||||
<script src="assets/measurements.js?v=20260514i"></script>
|
||||
<script src="assets/request.js?v=20260514i"></script>
|
||||
<script src="assets/assembly.js?v=20260514i"></script>
|
||||
<script src="assets/app.js?v=20260514i"></script>
|
||||
<script src="assets/icons.js?v=20260514j"></script>
|
||||
<script src="assets/podbor.config.js?v=20260514j"></script>
|
||||
<script src="assets/podbor.picts.js?v=20260514j"></script>
|
||||
<script src="assets/podbor.js?v=20260514j"></script>
|
||||
<script src="assets/clients.js?v=20260514j"></script>
|
||||
<script src="assets/zamer-picts.js?v=20260514j"></script>
|
||||
<script src="assets/measurements.js?v=20260514j"></script>
|
||||
<script src="assets/request.js?v=20260514j"></script>
|
||||
<script src="assets/assembly.js?v=20260514j"></script>
|
||||
<script src="assets/app.js?v=20260514j"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user