## Документация для фронтенда: как отображать графики из маркетингового анализа Сервис возвращает данные для графиков в двух местах: - **`report.chartsData`** (объект-словарь ключ → данные графика/таблицы) - **`report.fullAnalysis`** (markdown-текст), где данные вставлены **инлайн** как fenced-блоки: - формат: ```json: … ``` Фронтенд может рендерить графики либо **по `chartsData` (проще)**, либо **инлайн** — парся `fullAnalysis` и заменяя ` ```json:` на React-компоненты. --- ### Ключи `json:`, которые нужно поддержать #### Chart.js config (рендерить через Chart.js / react-chartjs-2) - **`seasonality`**: сезонность (Chart.js config) - **`audienceAge`**: возрастное распределение (Chart.js config) - **`audienceGender`**: гендерное распределение (Chart.js config) - **`marketShareChart`**: доли рынка (Chart.js config) - **`channelsPotential`**: потенциал каналов (Chart.js config) - **`funnel`**: воронка (Chart.js config) Примечание: в `chartsData` ключ может быть `conversionFunnel`, но в тексте `fullAnalysis` блок идёт как `json:funnel`. #### Таблица (рендерить табличным компонентом) - **`comparisonTable`**: сравнительная таблица конкурентов (обычно `Array`) --- ### Формат данных для графиков (Chart.js config) Ваш пример — это **валидный Chart.js config** (минимально нужные поля: `labels`, `datasets[]`): ```json { "datasets": [ { "data": [25, 30, 20, 25], "label": "Возрастные группы" } ], "labels": ["18-24", "25-34", "35-44", "45+"] } ``` Рендеринг (пример для Bar): ```ts ``` --- ### Инлайн-рендеринг из `fullAnalysis` (React + react-markdown) Идея: перехватить `code`-блоки, найти `json:`, распарсить JSON и заменить на компонент. ```tsx import React from "react"; import ReactMarkdown from "react-markdown"; function parseInfoString(className?: string) { // react-markdown обычно кладёт info string как className вида: // "language-json:audienceAge" или "language-json:marketShareChart" const m = /language-([^ ]+)/.exec(className || ""); if (!m) return null; const raw = m[1]; // "json:audienceAge" const idx = raw.indexOf(":"); if (idx === -1) return { lang: raw, key: null }; return { lang: raw.slice(0, idx), key: raw.slice(idx + 1) }; } export function ReportMarkdown({ markdown }: { markdown: string }) { return ( {children}; const info = parseInfoString(className); if (!info || info.lang !== "json" || !info.key) { return
{children}
; } const raw = String(children).replace(/\n$/, ""); let data: any; try { data = JSON.parse(raw); } catch { return
{children}
; } switch (info.key) { case "seasonality": return ; case "audienceAge": return ; case "audienceGender": return ; case "marketShareChart": return ; case "channelsPotential": return ; case "funnel": return ; case "comparisonTable": return ; default: return
{children}
; } }, }} > {markdown}
); } ``` --- ### Рекомендации по устойчивости - **JSON.parse**: всегда `trim`/убирайте trailing newline (`replace(/\n$/, "")`). - **Fallback**: для неизвестных ключей оставляйте `
` (чтобы ничего не “ломалось”).
- **Таблицы**: `comparisonTable` лучше рендерить как таблицу, а не Chart.js.