feat(docs): /docs/b2b-channels (rendered) + /docs/b2b-channels.md (raw) for sharing
This commit is contained in:
parent
59466de811
commit
13119f9cb5
4 changed files with 282 additions and 0 deletions
|
|
@ -33,6 +33,18 @@ const nextConfig: NextConfig = {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
// Markdown в public/docs/ — отдаём с text/markdown + charset, чтобы
|
||||||
|
// браузер показывал inline (не download).
|
||||||
|
async headers() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
source: "/docs/:path*.md",
|
||||||
|
headers: [
|
||||||
|
{ key: "Content-Type", value: "text/markdown; charset=utf-8" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
|
|
|
||||||
84
frontend/public/docs/b2b-channels.md
Normal file
84
frontend/public/docs/b2b-channels.md
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
# B2B-каналы для site-finder данных
|
||||||
|
|
||||||
|
**Эшелон:** 🤝 B2B / коммерческий канал — для Макса
|
||||||
|
|
||||||
|
## Контекст / проблема
|
||||||
|
|
||||||
|
Текущий scoring строится на open-data (OSM, ДОМ.РФ public API, ФГИС ТП HTML, ДДУ из открытых выгрузок). Это покрывает ~70% сигналов, но **30% решающих факторов для МКД требуют B2B-договорённостей** с разными контрагентами.
|
||||||
|
|
||||||
|
Этот документ — **реестр всех известных B2B-вопросов**, которые надо отдать Максу для проработки/договорённостей. По мере появления — комментарии.
|
||||||
|
|
||||||
|
## Реестр B2B-направлений
|
||||||
|
|
||||||
|
### 1. Инженерные сети (РСО) — высокий приоритет
|
||||||
|
|
||||||
|
- **МРСК Урала (электроэнергия):** доступная мощность по подстанции, прогноз очерёдности подключения, тариф за кВт мощности
|
||||||
|
- Вопрос: есть ли API/выгрузка по нагрузке per substation? Иначе нужен подписанный договор обмена данными.
|
||||||
|
- **МУП «Водоканал» Екатеринбурга:** наличие магистральных сетей в radius 200м, мощность, точки подключения
|
||||||
|
- Вопрос: предоставляют ли GIS-слой публичных сетей? Условия?
|
||||||
|
- **Газпром межрегионгаз / ГРО** — газоснабжение
|
||||||
|
- **СТК (Свердловская теплоснабжающая компания) / Т Плюс** — теплосеть, доступная нагрузка
|
||||||
|
|
||||||
|
**Альтернатива:** покупка от одного агрегатора (есть провайдеры типа ТехУслуги/Атон) — узнать у Макса контакты.
|
||||||
|
|
||||||
|
### 2. ЗОУИТ из ФГИС ТП — high priority для G3
|
||||||
|
|
||||||
|
- **Минэкономразвития РФ / Росреестр** — ФГИС ТП (fgistp.economy.gov.ru) даёт shape ЗОУИТ, но через личный кабинет / HTML-парсинг. Возможен ли B2B-доступ к структурированным выгрузкам?
|
||||||
|
- **Прокадастр / EGRN.reestr.net** — платные API ~10-30K ₽/мес выдают декодированные ЗОУИТ. Узнать у Макса какой провайдер удобнее.
|
||||||
|
|
||||||
|
### 3. ПЗЗ — medium priority для G1
|
||||||
|
|
||||||
|
- **Минстрой Свердл / Администрация ЕКБ** — официальный shape ПЗЗ с параметрами. data.midural.ru может не отдавать полноту.
|
||||||
|
- Узнать у Макса контакт в Минстрое.
|
||||||
|
|
||||||
|
### 4. Шахматки застройщиков — high priority для data moat
|
||||||
|
|
||||||
|
- **Топ-7 застройщиков Екб** (Атомстрой, Брусника, ТЭН, Стройтек, Прогресс, УГМК, КОРТРОС) — публичные шахматки на сайтах
|
||||||
|
- Web-scrape технически возможен, но **юр-партнёрство снижает риск исков**
|
||||||
|
- Из SalesTracking_Strategy_Apr25: «партнёрство возамен аналитики» с топ-3
|
||||||
|
- Узнать у Макса, кто открыт к диалогу
|
||||||
|
|
||||||
|
### 5. Аналитика продаж конкурентов — medium priority для D5
|
||||||
|
|
||||||
|
- **Объектив API** — уже используем (Антон), но ограниченный план
|
||||||
|
- Узнать про upgrade тариф и full Свердл coverage
|
||||||
|
- **Сбер ДДФ** — есть `sber_ddf_metrics` в БД, но не полно
|
||||||
|
- Узнать про подписку
|
||||||
|
|
||||||
|
### 6. Демография — medium priority для I1
|
||||||
|
|
||||||
|
- **Росстат** — публично, но per-район ЕКБ гранулярность OKATO/OKTMO
|
||||||
|
- Альтернатива: купить срез у Финама/Спарка/etc
|
||||||
|
|
||||||
|
### 7. CRM данных других девелоперов — long-term для D5
|
||||||
|
|
||||||
|
- Сейчас только PRINZIP (через Антона)
|
||||||
|
- B2B-предложение «вы даёте свой CRM, мы аналитику соседей» — нужны 2-3 договорённости для значимой агрегации
|
||||||
|
|
||||||
|
### 8. ОКН (объекты культурного наследия)
|
||||||
|
|
||||||
|
- Свердл реестр не структурирован, нужен HTML-парсинг
|
||||||
|
- Узнать про официальный shape от Управления ОКН Свердл
|
||||||
|
|
||||||
|
## Что от Макса
|
||||||
|
|
||||||
|
- ✅ Подтвердить приоритеты в реестре (что критично сейчас vs «можно подождать»)
|
||||||
|
- ✅ Контакты в РСО, Минстрое, Минэкономразвития
|
||||||
|
- ✅ Бюджет на платные API (Прокадастр ≈30K ₽/мес, Объектив upgrade — узнать прайс)
|
||||||
|
- ✅ Аналог LOI или MOU с топ-3 застройщиками Екб
|
||||||
|
- ✅ Кто готов давать данные за аналитику (partnership offer)
|
||||||
|
|
||||||
|
## Acceptance
|
||||||
|
|
||||||
|
- [ ] Этот документ — **постоянный реестр** (живёт до Раунда А)
|
||||||
|
- [ ] Каждый new B2B-вопрос → добавление в раздел с тегом контрагента
|
||||||
|
- [ ] Раз в 2 недели — sync с Максом, статусы переводятся (📞 contacted / ✅ agreed / ❌ declined)
|
||||||
|
|
||||||
|
## Owner
|
||||||
|
|
||||||
|
- **Coordinator:** main session ↔ Макс
|
||||||
|
- Реализация после получения каналов: соответствующий worker-агент (database-expert для shapefile import, backend-engineer для API integration)
|
||||||
|
|
||||||
|
## Effort
|
||||||
|
|
||||||
|
multi-week (не technical, organizational)
|
||||||
83
frontend/src/app/docs/b2b-channels/page.tsx
Normal file
83
frontend/src/app/docs/b2b-channels/page.tsx
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
import fs from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
import { renderMarkdown } from "./renderMarkdown";
|
||||||
|
|
||||||
|
export const metadata = {
|
||||||
|
title: "B2B-каналы для site-finder · GenDesign",
|
||||||
|
description: "Реестр B2B-вопросов для координации с Максом",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function B2BChannelsPage() {
|
||||||
|
const mdPath = path.join(process.cwd(), "public", "docs", "b2b-channels.md");
|
||||||
|
const raw = fs.readFileSync(mdPath, "utf-8");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main
|
||||||
|
style={{
|
||||||
|
maxWidth: 880,
|
||||||
|
margin: "0 auto",
|
||||||
|
padding: "32px 24px",
|
||||||
|
fontFamily:
|
||||||
|
'system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
|
||||||
|
color: "#1f2937",
|
||||||
|
lineHeight: 1.6,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginBottom: 24,
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
paddingBottom: 16,
|
||||||
|
borderBottom: "1px solid #e5e7eb",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
href="/"
|
||||||
|
style={{ fontSize: 13, color: "#6b7280", textDecoration: "none" }}
|
||||||
|
>
|
||||||
|
← Главная
|
||||||
|
</Link>
|
||||||
|
<a
|
||||||
|
href="/docs/b2b-channels.md"
|
||||||
|
download
|
||||||
|
style={{
|
||||||
|
fontSize: 13,
|
||||||
|
color: "#1d4ed8",
|
||||||
|
textDecoration: "none",
|
||||||
|
padding: "6px 12px",
|
||||||
|
border: "1px solid #1d4ed8",
|
||||||
|
borderRadius: 6,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
↓ Скачать .md
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
// eslint-disable-next-line react/no-danger
|
||||||
|
dangerouslySetInnerHTML={{ __html: renderMarkdown(raw) }}
|
||||||
|
className="markdown-body"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
.markdown-body h1 { font-size: 28px; font-weight: 700; margin: 24px 0 16px; line-height: 1.3; }
|
||||||
|
.markdown-body h2 { font-size: 22px; font-weight: 700; margin: 32px 0 12px; padding-bottom: 6px; border-bottom: 1px solid #e5e7eb; }
|
||||||
|
.markdown-body h3 { font-size: 17px; font-weight: 600; margin: 24px 0 8px; color: #1d4ed8; }
|
||||||
|
.markdown-body p { margin: 8px 0; }
|
||||||
|
.markdown-body ul { margin: 8px 0; padding-left: 24px; }
|
||||||
|
.markdown-body li { margin: 4px 0; }
|
||||||
|
.markdown-body code { background: #f3f4f6; padding: 2px 6px; border-radius: 4px; font-family: ui-monospace, monospace; font-size: 0.92em; color: #b91c1c; }
|
||||||
|
.markdown-body strong { font-weight: 600; }
|
||||||
|
.markdown-body a { color: #1d4ed8; text-decoration: underline; }
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
103
frontend/src/app/docs/b2b-channels/renderMarkdown.ts
Normal file
103
frontend/src/app/docs/b2b-channels/renderMarkdown.ts
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
/**
|
||||||
|
* Минимальный markdown→HTML renderer без внешних зависимостей.
|
||||||
|
* Поддерживает: # / ## / ### headings, **bold**, `code`, ссылки [text](url),
|
||||||
|
* списки -/* / 1., чекбоксы [ ]/[x], абзацы.
|
||||||
|
* Не поддерживает: tables, blockquotes, code fences (для нашего docs хватает).
|
||||||
|
*/
|
||||||
|
function escapeHtml(s: string): string {
|
||||||
|
return s
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
function inlineMd(line: string): string {
|
||||||
|
// escape first, then re-introduce safe markup
|
||||||
|
let html = escapeHtml(line);
|
||||||
|
// code spans `...`
|
||||||
|
html = html.replace(/`([^`]+)`/g, "<code>$1</code>");
|
||||||
|
// bold **...**
|
||||||
|
html = html.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
|
||||||
|
// links [text](url)
|
||||||
|
html = html.replace(
|
||||||
|
/\[([^\]]+)\]\(([^)]+)\)/g,
|
||||||
|
'<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>',
|
||||||
|
);
|
||||||
|
// checkboxes
|
||||||
|
html = html.replace(
|
||||||
|
/\[ \]/g,
|
||||||
|
'<input type="checkbox" disabled style="margin-right:6px;"/>',
|
||||||
|
);
|
||||||
|
html = html.replace(
|
||||||
|
/\[x\]/gi,
|
||||||
|
'<input type="checkbox" checked disabled style="margin-right:6px;"/>',
|
||||||
|
);
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function renderMarkdown(md: string): string {
|
||||||
|
const lines = md.split("\n");
|
||||||
|
const out: string[] = [];
|
||||||
|
let inList = false;
|
||||||
|
|
||||||
|
for (const rawLine of lines) {
|
||||||
|
const line = rawLine.replace(/\r$/, "");
|
||||||
|
|
||||||
|
if (line.trim() === "") {
|
||||||
|
if (inList) {
|
||||||
|
out.push("</ul>");
|
||||||
|
inList = false;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Headings
|
||||||
|
if (line.startsWith("### ")) {
|
||||||
|
if (inList) {
|
||||||
|
out.push("</ul>");
|
||||||
|
inList = false;
|
||||||
|
}
|
||||||
|
out.push(`<h3>${inlineMd(line.slice(4))}</h3>`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line.startsWith("## ")) {
|
||||||
|
if (inList) {
|
||||||
|
out.push("</ul>");
|
||||||
|
inList = false;
|
||||||
|
}
|
||||||
|
out.push(`<h2>${inlineMd(line.slice(3))}</h2>`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line.startsWith("# ")) {
|
||||||
|
if (inList) {
|
||||||
|
out.push("</ul>");
|
||||||
|
inList = false;
|
||||||
|
}
|
||||||
|
out.push(`<h1>${inlineMd(line.slice(2))}</h1>`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// List items (- or * at indent 0 or 2)
|
||||||
|
const listMatch = line.match(/^(\s*)[-*]\s+(.*)$/);
|
||||||
|
if (listMatch) {
|
||||||
|
if (!inList) {
|
||||||
|
out.push("<ul>");
|
||||||
|
inList = true;
|
||||||
|
}
|
||||||
|
out.push(`<li>${inlineMd(listMatch[2])}</li>`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plain paragraph
|
||||||
|
if (inList) {
|
||||||
|
out.push("</ul>");
|
||||||
|
inList = false;
|
||||||
|
}
|
||||||
|
out.push(`<p>${inlineMd(line)}</p>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inList) out.push("</ul>");
|
||||||
|
return out.join("\n");
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue