SCALE — Build Lab
開発パターン · JAVASCRIPT PATTERN

SalesNow CSV 自動取込

CATEGORY開発パターン TYPEJavaScript Pattern EFFORT240〜600分 DIFFICULTY
PRIMARY CODE
js
// SalesNow CSV 自動判定 + 取込(簡略版)
function parseSalesNowCSV(csvText) {
  const lines = csvText.split(/\r?\n/);
  // 1. SalesNow 判定: 先頭数行に「SalesNow」or 特定メタが含まれる
  const isSalesNow = lines.slice(0, 5).some(l => /SalesNow|検索条件|抽出日時/.test(l));

  // 2. メタ行スキップ(先頭の '#' or 空行 or 検索条件などを除外)
  let headerIdx = lines.findIndex(l => /会社名|社名|URL|電話/.test(l));
  if (headerIdx < 0) throw new Error('ヘッダ行が見つかりません');

  const headers = lines[headerIdx].split(',').map(h => h.trim());
  const rows = lines.slice(headerIdx + 1).filter(l => l.trim());

  // 3. ヘッダ自動マッピング
  const FIELD_MAP = {
    '会社名': 'company', '社名': 'company',
    'ホームページURL': 'url', 'URL': 'url',
    '代表電話番号': 'tel', '電話': 'tel',
    '住所': 'address', '業種': 'industry',
    'salesNowリンク': 'salesNowLink',
  };

  const fieldIndex = {};
  headers.forEach((h, i) => { if (FIELD_MAP[h]) fieldIndex[FIELD_MAP[h]] = i; });

  return rows.map(row => {
    const cols = row.split(',');
    const out = {};
    Object.entries(fieldIndex).forEach(([k, i]) => { out[k] = (cols[i] || '').trim(); });
    return out;
  });
}

// 重複検知3軸
function isDuplicate(newItem, existingList) {
  return existingList.some(it =>
    (newItem.company && it.company === newItem.company) ||
    (newItem.url && it.url === newItem.url) ||
    (newItem.tel && it.tel === newItem.tel)
  );
}
前提条件
Tailwind CSS v4TypeScript 5
USE CASES
  • SalesNow / Sansan / 営業リストツール

SalesNow CSV 自動取込

:LiTarget: 用途

SalesNow CSV を判定してメタ行スキップ・ヘッダ自動マッピング・重複検知3軸(社名/URL/電話)でリッチ取込。

:LiSparkle: 特徴

  • SalesNow CSV 自動判定
  • メタ行(先頭数行)スキップ
  • ヘッダ自動マッピング
  • 重複検知3軸(社名/URL/電話)
  • リッチデータ生成
  • Slack 通知

:LiCode: コード(コピペ用)

// SalesNow CSV 自動判定 + 取込(簡略版)
function parseSalesNowCSV(csvText) {
  const lines = csvText.split(/\r?\n/);
  // 1. SalesNow 判定: 先頭数行に「SalesNow」or 特定メタが含まれる
  const isSalesNow = lines.slice(0, 5).some(l => /SalesNow|検索条件|抽出日時/.test(l));

  // 2. メタ行スキップ(先頭の '#' or 空行 or 検索条件などを除外)
  let headerIdx = lines.findIndex(l => /会社名|社名|URL|電話/.test(l));
  if (headerIdx < 0) throw new Error('ヘッダ行が見つかりません');

  const headers = lines[headerIdx].split(',').map(h => h.trim());
  const rows = lines.slice(headerIdx + 1).filter(l => l.trim());

  // 3. ヘッダ自動マッピング
  const FIELD_MAP = {
    '会社名': 'company', '社名': 'company',
    'ホームページURL': 'url', 'URL': 'url',
    '代表電話番号': 'tel', '電話': 'tel',
    '住所': 'address', '業種': 'industry',
    'salesNowリンク': 'salesNowLink',
  };

  const fieldIndex = {};
  headers.forEach((h, i) => { if (FIELD_MAP[h]) fieldIndex[FIELD_MAP[h]] = i; });

  return rows.map(row => {
    const cols = row.split(',');
    const out = {};
    Object.entries(fieldIndex).forEach(([k, i]) => { out[k] = (cols[i] || '').trim(); });
    return out;
  });
}

// 重複検知3軸
function isDuplicate(newItem, existingList) {
  return existingList.some(it =>
    (newItem.company && it.company === newItem.company) ||
    (newItem.url && it.url === newItem.url) ||
    (newItem.tel && it.tel === newItem.tel)
  );
}

:LiHandPointer: 使い方

対象プロジェクトに該当ファイルをコピーして、props を流し込むだけ。

:LiAlertCircle: 注意事項

  • 依存パッケージを忘れず追加