Files
marketing-parser/frontend-chart-rendering.md
2025-12-20 20:44:40 +05:00

4.9 KiB

Документация для фронтенда: как отображать графики из маркетингового анализа

Сервис возвращает данные для графиков в двух местах:

  • report.chartsData (объект-словарь ключ → данные графика/таблицы)
  • report.fullAnalysis (markdown-текст), где данные вставлены инлайн как fenced-блоки:
    • формат: json:<key> …

Фронтенд может рендерить графики либо по chartsData (проще), либо инлайн — парся fullAnalysis и заменяя ```json:<key> на React-компоненты.


Ключи json:<key>, которые нужно поддержать

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<Object>)

Формат данных для графиков (Chart.js config)

Ваш пример — это валидный Chart.js config (минимально нужные поля: labels, datasets[]):

{
  "datasets": [
    {
      "data": [25, 30, 20, 25],
      "label": "Возрастные группы"
    }
  ],
  "labels": ["18-24", "25-34", "35-44", "45+"]
}

Рендеринг (пример для Bar):

<Bar data={chartJson} />

Инлайн-рендеринг из fullAnalysis (React + react-markdown)

Идея: перехватить code-блоки, найти json:<key>, распарсить JSON и заменить на компонент.

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 (
    <ReactMarkdown
      components={{
        code({ inline, className, children, ...props }) {
          if (inline) return <code className={className} {...props}>{children}</code>;

          const info = parseInfoString(className);
          if (!info || info.lang !== "json" || !info.key) {
            return <pre><code className={className} {...props}>{children}</code></pre>;
          }

          const raw = String(children).replace(/\n$/, "");
          let data: any;
          try {
            data = JSON.parse(raw);
          } catch {
            return <pre><code className={className} {...props}>{children}</code></pre>;
          }

          switch (info.key) {
            case "seasonality":
              return <SeasonalityChart data={data} />;
            case "audienceAge":
              return <AudienceAgeChart data={data} />;
            case "audienceGender":
              return <AudienceGenderChart data={data} />;
            case "marketShareChart":
              return <MarketShareChart data={data} />;
            case "channelsPotential":
              return <ChannelsPotentialChart data={data} />;
            case "funnel":
              return <FunnelChart data={data} />;
            case "comparisonTable":
              return <ComparisonTable data={data} />;
            default:
              return <pre><code className={className} {...props}>{children}</code></pre>;
          }
        },
      }}
    >
      {markdown}
    </ReactMarkdown>
  );
}

Рекомендации по устойчивости

  • JSON.parse: всегда trim/убирайте trailing newline (replace(/\n$/, "")).
  • Fallback: для неизвестных ключей оставляйте <pre> (чтобы ничего не “ломалось”).
  • Таблицы: comparisonTable лучше рендерить как таблицу, а не Chart.js.