kirin-ri / memo

0 stars 0 forks source link

mock2 #29

Open kirin-ri opened 3 months ago

kirin-ri commented 3 months ago

収入 プラス(収入を増やす要因) 製品販売の増加: 販売数量や価格の上昇により収入が増えます。 新規受注の獲得: 新たな注文が入ることで将来の収入が増加します。 市場需要の拡大: 市場の成長やニーズの増加により売上が増えます。 為替レートの有利な変動(輸出の場合): 為替レートが有利に変動した場合、輸出による収入が増加します。 補助金・助成金の獲得: 政府や機関からの補助金や助成金が収入に加わります。 マイナス(収入を減らす要因) 製品販売の減少: 販売数量や価格の低下により収入が減少します。 受注減少: 新規受注が減少し、将来の収入が見込めなくなります。 市場需要の縮小: 市場全体の需要が減少し、売上が減ります。 為替レートの不利な変動(輸出の場合): 為替レートが不利に動いた場合、輸出による収入が減少します。 支払い遅延: 顧客からの支払いが遅れたり、回収不能になることで収入が減少します。 支出 プラス(支出を増やす要因) 原材料費の上昇: 原材料の価格が上昇することで支出が増えます。 労務費の増加: 従業員の給与や福利厚生費の増加が支出を増加させます。 設備投資の増加: 新規設備の導入や既存設備のメンテナンス費用が増えることで支出が増加します。 外注費の増加: 外部業者に依頼する費用が増えることで支出が増加します。 過剰在庫の発生: 過剰在庫の維持コストが支出を増加させます。 利息支払いの増加: 借入金の利息が増加することで定期的な支出が増えます。 税金・保険料の増加: 税率の引き上げや保険料の増加が支出を増やします。 マイナス(支出を減らす要因) 原材料費の減少: 原材料の価格が下がることで支出が減少します。 労務費の削減: 従業員数の削減や給与カットなどにより支出が減少します。 設備投資の抑制: 設備投資を延期または削減することで支出が減少します。 外注費の削減: 外部業者への依頼を減らすことで支出が減少します。 在庫管理の最適化: 適切な在庫管理により、過剰在庫の削減が支出を抑えます。 利息支払いの減少: 借入金の返済や利率の低下により、利息支払いが減少します。 税金・保険料の減少: 税制優遇措置や保険料の見直しにより支出が減少します。

kirin-ri commented 3 months ago
                        <div style={{ textAlign: 'left', marginBottom: '10px', fontWeight: 'bold', fontSize: '14px', color: '#666666' }}>
                            {yearText}
                        </div>
kirin-ri commented 3 months ago
import { BarController, BarElement, CategoryScale, Chart as ChartJS, ChartTypeRegistry, Legend, LegendItem, LinearScale, LineController, LineElement, PointElement, Title, Tooltip } from 'chart.js';
import { useEffect, useRef, useState } from "react";
import { Chart } from 'react-chartjs-2';

ChartJS.register(BarController, LineController, CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend);

type DataSet = {
  income: number[];
  expense: number[];
  balance?: number[];
  incomeExpenseDiff?: number[];
  action?: {
    cashFlow: string;
    countermeasure: string;
  };
};

const CollapsiblePanel = ({ title, money, details }: { title: string; money: string; details: { month: string, amount: string, alert?: boolean }[] }) => {
  const [isOpen, setIsOpen] = useState(false);

  const togglePanel = () => {
    setIsOpen(!isOpen);
  };

  return (
    <div className="collapsible-panel">
      <div className="panel-header" onClick={togglePanel}>
        <div className="panel-title">{title}</div>
        <div className="panel-money">{money}</div>
      </div>
      {isOpen && (
        <div className="panel-content">
          <div className="details-container">
            {details.map((detail, index) => (
              <div key={index} className="detail-item" style={{ color: 'black' }}>
                <span>{detail.month}</span>
                <span style={{ color: detail.alert ? 'red' : 'black', marginLeft: '5px' }}>
                  {detail.amount}
                </span>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
};

const AlertBox = ({ message, onClose }: { message: string; onClose: () => void }) => {
  return (
    <div className="alert-box">
      <div className="alert-content">
        <i className="fa fa-exclamation-circle alert-icon" aria-hidden="true"></i>
        <span className="alert-message">{message}</span>
      </div>
      <button className="close-btn" onClick={onClose}>非表示</button>
    </div>
  );
};

const EmptyPage = () => {
  const pageName = '資金繰り表';
  const [showAlert, setShowAlert] = useState(true);
  const [activeComparison, setActiveComparison] = useState('今年度比較');
  const [incomeLevel, setIncomeLevel] = useState('中立');
  const [expenseLevel, setExpenseLevel] = useState('中立');
  const [cashFlow, setCashFlow] = useState('');
  const [countermeasure, setCountermeasure] = useState('');
  const chartRef = useRef<ChartJS | null>(null);

  const options = {
    responsive: true,
    layout: {
      padding: {
        top: 30,
        bottom: 0,
        left: 0,
        right: 0,
      },
    },
    plugins: {
      legend: {
        display: false,
        labels: {
          color: 'white',
          boxWidth: 0,
          boxHeight: 0,
        },
      },
    },
    scales: {
      y: {
        beginAtZero: true,
        title: {
          display: true,
          text: '[百万円]', // Y-axis label
        },
      },
    },
  };

  const updateChartAndAction = () => {
    let selectedData: DataSet | undefined;

    if (activeComparison === '前年度比較') {
        selectedData = dummyPreviousYearData;
    } else {
        const key = `${incomeLevel}-${expenseLevel}` as keyof typeof dataSets;
        selectedData = dataSets[key];
    }

    if (!selectedData) {
        console.error(`No data found for key: ${incomeLevel}-${expenseLevel}`);
        return;
    }

    const isPreviousYear = activeComparison === '前年度比較';
    const incomeExpenseDiff = selectedData.incomeExpenseDiff || selectedData.income.map((income, i) => income + selectedData!.expense[i]);

    if (chartRef.current) {
        chartRef.current.data = {
            labels: ['4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月', '1月', '2月', '3月'],
            datasets: [
                {
                    type: 'bar',
                    label: '収入',
                    data: selectedData.income,
                    backgroundColor: function (context) {
                        const index = context.dataIndex;
                        return isPreviousYear ? 'rgba(153, 102, 255, 0.5)' : (index < 3 ? 'rgba(153, 102, 255, 0.5)' : 'rgba(153, 102, 255, 0.2)');
                    },
                },
                {
                    type: 'bar',
                    label: '支出',
                    data: selectedData.expense,
                    backgroundColor: function (context) {
                        const index = context.dataIndex;
                        return isPreviousYear ? 'rgba(54, 162, 235, 0.5)' : (index < 3 ? 'rgba(54, 162, 235, 0.5)' : 'rgba(54, 162, 235, 0.2)');
                    },
                },
                {
                    type: 'line',
                    label: '収支差',
                    data: incomeExpenseDiff,
                    borderColor: 'blue',
                    backgroundColor: 'blue',
                    fill: false,
                    tension: 0.1,
                    borderWidth: 2,
                    pointStyle: 'circle',
                    pointRadius: 4,
                    pointHoverRadius: 6,
                    segment: {
                        borderDash: (ctx) => {
                            return isPreviousYear ? [] : (ctx.p0DataIndex < 3 ? [] : [5, 5]); // 今年度は7月以降破線、前年度はすべて実線
                        },
                    },
                    pointBackgroundColor: function (context) {
                        const index = context.dataIndex;
                        const value = context.dataset.data[index] ?? 0;
                        return value < 0 ? 'red' : 'blue';
                    }
                },
                {
                    type: 'line',
                    label: '期末残高',
                    data: selectedData.balance || selectedData.income.reduce((acc: number[], income, i) => {
                        let newBalance = (acc.length > 0 ? acc[acc.length - 1] : initialBalance) + income + selectedData!.expense[i];
                        acc.push(newBalance);
                        return acc;
                    }, [] as number[]),
                    borderColor: 'black',
                    backgroundColor: 'black',
                    fill: false,
                    tension: 0.1,
                    borderWidth: 2,
                    pointStyle: 'rectRot',
                    pointRadius: 6,
                    pointHoverRadius: 8,
                    segment: {
                        borderDash: (ctx) => {
                            return isPreviousYear ? [] : (ctx.p0DataIndex < 3 ? [] : [5, 5]); // 今年度は7月以降破線、前年度はすべて実線
                        },
                    },
                    pointBackgroundColor: function (context) {
                        const index = context.dataIndex;
                        const value = context.dataset.data[index] ?? 0;
                        return value < 0 ? 'red' : 'black';
                    }
                },
            ],
        };

        chartRef.current.update();
    }

    if (activeComparison !== '前年度比較' && selectedData.action) {
        setCashFlow(selectedData.action.cashFlow);
        setCountermeasure(selectedData.action.countermeasure);
    }

    const actionBox = document.querySelector('.actionbox-message');
    if (actionBox) {
      actionBox.scrollTop = 0;
    }
};

  useEffect(() => {
    updateChartAndAction();
  }, [incomeLevel, expenseLevel, activeComparison]);

  const handleComparisonClick = (type: string) => {
    setActiveComparison(type);
  };

  const handleIncomeChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    setIncomeLevel(e.target.value);
    setActiveComparison('今年度比較'); // 切换到“今年度比較”
  };

  const handleExpenseChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    setExpenseLevel(e.target.value);
    setActiveComparison('今年度比較'); // 切换到“今年度比較”
  };

  useEffect(() => {
    ChartJS.register(customLegendPlugin);
    return () => {
      ChartJS.unregister(customLegendPlugin);
    };
  }, []);

  const customLegendPlugin = {
    id: 'customLegend',
    afterDraw: function (chart: ChartJS<keyof ChartTypeRegistry, unknown[], unknown>) {
      const legend = chart?.legend;
      if (!legend || !legend.legendItems) return;

      const ctx = chart.ctx;
      const itemWidth = 100;
      const startX = (chart.width - legend.legendItems.length * itemWidth) / 2;
      let currentX = startX;

      const y = 10;

      legend.legendItems.forEach((legendItem: LegendItem, i: number) => {
        if (legendItem.text === '期末残高' || legendItem.text === '収支差') {
          ctx.save();
          ctx.strokeStyle = legendItem.text === '期末残高' ? 'black' : 'blue';
          ctx.lineWidth = 2;
          ctx.beginPath();
          ctx.moveTo(currentX, y);
          ctx.lineTo(currentX + 40, y);
          ctx.stroke();
          ctx.restore();
        } else {
          ctx.save();
          ctx.fillStyle = legendItem.fillStyle as string;
          ctx.fillRect(currentX, y - 5, 40, 10);
          ctx.restore();
        }

        ctx.textBaseline = 'middle';
        ctx.fillStyle = 'black';
        ctx.fillText(legendItem.text, currentX + 50, y);

        currentX += itemWidth;
      });
    },
  };

  const defaultData = {
    labels: [],
    datasets: []
  };

  return (
    <div className="content-wrapper metrics-details">
      <section className="page-cover">
        <div className="page-cover-title-frame">
          <h1>{pageName}</h1>
        </div>
      </section>
      {showAlert && (
        <div className="alert-container">
          <AlertBox
            message="期中に資金がマイナスとなる期間があります"
            onClose={() => setShowAlert(false)}
          />
        </div>
      )}
      <div className="main-content">
        <div className="left-container">
          <div className="graph-container">
            <Chart ref={chartRef} type="bar" data={chartRef.current?.data || defaultData} options={options} />
          </div>
          <div className="additional-section">
            <div className="data-filter">
              <h2>データ予測</h2>
              <div className="filter-group">
                <div className="filter-btn">収入</div>
                <select className="filter-select" onChange={handleIncomeChange} value={incomeLevel}>
                  <option>楽観</option>
                  <option>中立</option>
                  <option>悲観</option>
                </select>
              </div>
              <div className="filter-group">
                <div className="filter-btn">支出</div>
                <select className="filter-select" onChange={handleExpenseChange} value={expenseLevel}>
                  <option>楽観</option>
                  <option>中立</option>
                  <option>悲観</option>
                </select>
              </div>
            </div>
            <div className="data-comparison">
              <h2>データ比較</h2>
              <button
                className={`comparison-btn ${activeComparison === '今年度比較' ? 'active' : ''}`}
                onClick={() => handleComparisonClick('今年度比較')}
              >
                今年度比較
              </button>
              <button
                className={`comparison-btn ${activeComparison === '前年度比較' ? 'active' : ''}`}
                onClick={() => handleComparisonClick('前年度比較')}
              >
                前年度比較
              </button>
            </div>
          </div>
        </div>
        <div className="right-container">
          <div className="actionbox-title">
            <div>推奨アクション</div>
          </div>
          <div className="actionbox-message">
            <div className="action-section">
              <h3>現金の流れ</h3>
              <p>{cashFlow}</p>
            </div>
            <div className="action-section">
              <h3>対策</h3>
              <p>{countermeasure}</p>
            </div>
          </div>
          <div className="collapsible-panels">
            <CollapsiblePanel title="営業キャッシュフロー" money="32,990,433円" details={operatingCashFlow} />
            <CollapsiblePanel title="投資キャッシュフロー" money="▲25,947,004円" details={investingCashFlow} />
            <CollapsiblePanel title="財務キャッシュフロー" money="▲6,415,126円" details={financingCashFlow} />
          </div>
        </div>
      </div>
    </div>
  );
};

const initialBalance = 10.0;
const dummyPreviousYearData = {
  income: [36.0, 35.0, 34.5, 30.0, 36.5, 32.0, 36.0, 35.5, 36.0, 31.0, 36.5, 30.0],
  expense: [-34.0, -32.0, -35.0, -36.0, -33.0, -36.5, -32.0, -36.0, -34.5, -36.5, -31.0, -35.0],
  balance: [10.0, 12.0, 11.5, 5.5, 9.0, 4.5, 8.5, 8.0, 9.5, 4.0, 9.5, 10.0],
  incomeExpenseDiff: [2.0, 3.0, -0.5, -6.0, 3.5, -4.5, 4.0, -0.5, 1.5, -5.5, 5.5, -5.0],
};
const dataSets = {
  "楽観-楽観": {
    "income": [34.0, 35.5, 34.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5, 40.0, 40.5, 41.0],
    "expense": [-33.0, -34.0, -35.0, -33.5, -33.0, -32.5, -32.0, -31.5, -31.0, -30.5, -30.0, -29.5],
    "action": {
      "cashFlow": "昨年の現金の流れは比較的安定していましたが、4月と12月に特に注意が必要でした。今年の4月から6月のデータでは、収入が前年同期よりも若干低下しているものの、支出の管理がより効率的であったため、全体としての現金の流れは安定しています。7月以降も、収入の増加が期待されますが、支出の抑制が鍵となります。",
      "countermeasure": "銀行としては、企業の資金繰りを支援するために、柔軟な融資条件の提供を検討することが重要です。特に、短期的な運転資金の貸付を拡大し、企業が収益機会を最大限に活かせるようにサポートすることが求められます。また、為替リスクに対応した金融商品の提供や、資産運用のアドバイスを通じて、企業の財務安定を支援することが効果的です。"
    }
  },
  "楽観-中立": {
    "income": [34.0, 35.5, 34.5, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5, 40.0],
    "expense": [-33.0, -34.0, -35.0, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5],
    "action": {
      "cashFlow": "昨年は現金の流れが年間を通して安定していましたが、特に後半での支出管理が課題となりました。今年の前半では、収入は前年並みで推移している一方で、支出がやや増加しています。このため、後半にかけては支出管理がさらに重要になります。",
      "countermeasure": "銀行としては、企業が支出管理を改善できるように、コスト削減に関連するコンサルティングサービスの提供を検討すべきです。また、キャッシュフローの安定化を図るための短期融資や、支出が増加する期間に備えた資金計画の策定をサポートすることが求められます。"
    }
  },
  "楽観-悲観": {
    "income": [34.0, 35.5, 34.5, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5, 40.0],
    "expense": [-33.0, -34.0, -35.0, -36.0, -37.0, -38.0, -39.0, -40.0, -41.0, -42.0, -43.0, -44.0],
    "action": {
      "cashFlow": "昨年は収入が安定していたものの、設備投資や労務費の増加により、現金の流れが後半にかけて悪化しました。今年の前半でも同様の傾向が見られ、特に支出が収入を上回るリスクが高まっています。",
      "countermeasure": "銀行としては、企業が直面するリスクに対処するための資金支援を強化する必要があります。具体的には、リスクを分散するための投資アドバイスや、支出が増加する際の柔軟な融資枠の提供が考えられます。また、企業がコスト管理を改善するためのツールやリソースを提供することも重要です。"
    }
  },
  "中立-楽観": {
    "income": [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    "expense": [-33.0, -34.0, -35.0, -33.0, -32.0, -31.0, -30.0, -29.0, -28.0, -27.0, -26.0, -25.0],
    "action": {
      "cashFlow": "昨年は全体的に安定した現金の流れが見られましたが、今年の前半では収入が予想を下回る一方で、支出管理が良好であったため、現金の流れは比較的安定しています。今後の成長のためには、収入増加のための戦略が重要です。",
      "countermeasure": "銀行としては、企業の成長を支援するために、成長資金の提供や、新市場開拓に向けた資金計画の策定支援を行うことが重要です。また、企業の収益性向上をサポートするために、金融商品の多様化や、リスク管理に関するアドバイスを提供することが効果的です。"
    }
  },
  "中立-中立": {
    "income": [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    "expense": [-33.0, -34.0, -35.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0],
    "action": {
      "cashFlow": "昨年の現金の流れは非常に安定していました。今年の前半でも、収入と支出のバランスが取れており、全体として安定した現金の流れを維持しています。今後も同様のパターンが続くと予想されます。",
      "countermeasure": "銀行としては、企業の財務安定を維持するために、リスク管理強化のサポートを行うことが求められます。安定した収益基盤をさらに強化するために、長期的な投資計画の立案や、安定的な収益を得るためのポートフォリオの構築支援を提供することが有効です。"
    }
  },
  "中立-悲観": {
    "income": [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    "expense": [-33.0, -34.0, -35.0, -36.0, -37.0, -38.0, -39.0, -40.0, -41.0, -42.0, -43.0, -44.0],
    "action": {
      "cashFlow": "昨年は支出が増加し、現金の流れが後半にかけて悪化しました。今年の前半でも同様の傾向が見られ、収入は安定していますが、支出の増加により現金の流れが厳しくなっています。",
      "countermeasure": "銀行としては、企業の支出削減を支援するためのコンサルティングサービスを提供することが重要です。また、短期的な資金ニーズに対応するための融資プログラムの提供や、支出増加に備えたリスクヘッジ手段の提案も有効です。企業が支出管理を強化することで、現金の流れを改善するための具体的なアドバイスを提供することが求められます。"
    }
  },
  "悲観-楽観": {
    "income": [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    "expense": [-33.0, -34.0, -35.0, -33.0, -32.0, -31.0, -30.0, -29.0, -28.0, -27.0, -26.0, -25.0],
    "action": {
      "cashFlow": "昨年は収入の減少と支出の抑制により、現金の流れが後半にかけて改善されましたが、今年の前半では収入の減少が続き、支出の抑制も限界に達しています。",
      "countermeasure": "銀行としては、企業の収益性向上を支援するために、ビジネスモデルの転換をサポートする資金提供を検討すべきです。また、既存事業の効率化を促進するための設備投資支援や、コスト管理の改善に役立つツールの提供が重要です。さらに、企業が収益性の高い市場での競争力を維持できるよう、戦略的なアドバイスを行うことが求められます。"
    }
  },
  "悲観-中立": {
    "income": [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    "expense": [-33.0, -34.0, -35.0, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5],
    "action": {
      "cashFlow": "昨年の現金の流れは、支出の抑制により後半にかけて若干の改善が見られましたが、今年の前半では収入の減少が続き、支出の増加が懸念されています。現金の流れが厳しい状況です。",
      "countermeasure": "銀行としては、企業の現金流の改善を支援するために、短期融資や支出削減をサポートするコンサルティングサービスを提供することが重要です。また、財務リスクを最小限に抑えるための戦略的アドバイスや、資金運用の効率化を促進するための金融商品の提供も検討すべきです。"
    }
  },
  "悲観-悲観": {
    "income": [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    "expense": [-33.0, -34.0, -35.0, -36.0, -37.0, -38.0, -39.0, -40.0, -41.0, -42.0, -43.0, -44.0],
    "action": {
      "cashFlow": "昨年は支出の増加により現金の流れが悪化し、今年の前半でも同様の傾向が見られます。収入の減少とコストの増加が重なり、現金の流れが非常に厳しい状況です。",
      "countermeasure": "銀行としては、企業が速やかに財務の再構築を行えるように、リストラ資金の提供や、不要な投資の見直しを支援するプログラムを提供することが求められます。また、戦略的な投資をサポートするための資金提供や、企業が新たな収益源を開拓できるような金融サービスを提供することも重要です。"
    }
  }
};

const operatingCashFlow = [
  { month: '4月', amount: '34,035,567円' },
  { month: '5月', amount: '30,407,343円' },
  { month: '6月', amount: '34,528,390円' },
  { month: '7月', amount: '-' },
  { month: '8月', amount: '-' },
  { month: '9月', amount: '-' },
  { month: '10月', amount: '-' },
  { month: '11月', amount: '-' },
  { month: '12月', amount: '-' },
  { month: '1月', amount: '-' },
  { month: '2月', amount: '-' },
  { month: '3月', amount: '-' },
];
const investingCashFlow = [
  { month: '4月', amount: '▲21,502,456円', alert: true },
  { month: '5月', amount: '▲34,023,289円', alert: true },
  { month: '6月', amount: '▲22,315,267円', alert: true },
  { month: '7月', amount: '-' },
  { month: '8月', amount: '-' },
  { month: '9月', amount: '-' },
  { month: '10月', amount: '-' },
  { month: '11月', amount: '-' },
  { month: '12月', amount: '-' },
  { month: '1月', amount: '-' },
  { month: '2月', amount: '-' },
  { month: '3月', amount: '-' },
];

const financingCashFlow = [
  { month: '4月', amount: '▲11,504,456円', alert: true },
  { month: '5月', amount: '5,005,275円' },
  { month: '6月', amount: '▲12,746,198円', alert: true },
  { month: '7月', amount: '-' },
  { month: '8月', amount: '-' },
  { month: '9月', amount: '-' },
  { month: '10月', amount: '-' },
  { month: '11月', amount: '-' },
  { month: '12月', amount: '-' },
  { month: '1月', amount: '-' },
  { month: '2月', amount: '-' },
  { month: '3月', amount: '-' },
];

export default EmptyPage;
kirin-ri commented 3 months ago
import { BarController, BarElement, CategoryScale, Chart as ChartJS, ChartTypeRegistry, Legend, LegendItem, LinearScale, LineController, LineElement, PointElement, Title, Tooltip } from 'chart.js';
import { useEffect, useRef, useState } from "react";
import { Chart } from 'react-chartjs-2';

ChartJS.register(BarController, LineController, CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend);

type DataSet = {
  income: number[];
  expense: number[];
  balance?: number[];
  incomeExpenseDiff?: number[];
  action?: {
    cashFlow: string;
    countermeasure: string;
  };
};

const CollapsiblePanel = ({ title, money, details }: { title: string; money: string; details: { month: string, amount: string, alert?: boolean }[] }) => {
  const [isOpen, setIsOpen] = useState(false);

  const togglePanel = () => {
    setIsOpen(!isOpen);
  };

  return (
    <div className="collapsible-panel">
      <div className="panel-header" onClick={togglePanel}>
        <div className="panel-title">{title}</div>
        <div className="panel-money">{money}</div>
      </div>
      {isOpen && (
        <div className="panel-content">
          <div className="details-container">
            {details.map((detail, index) => (
              <div key={index} className="detail-item" style={{ color: 'black' }}>
                <span>{detail.month}</span>
                <span style={{ color: detail.alert ? 'red' : 'black', marginLeft: '5px' }}>
                  {detail.amount}
                </span>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
};

const AlertBox = ({ message, onClose }: { message: string; onClose: () => void }) => {
  return (
    <div className="alert-box">
      <div className="alert-content">
        <i className="fa fa-exclamation-circle alert-icon" aria-hidden="true"></i>
        <span className="alert-message">{message}</span>
      </div>
      <button className="close-btn" onClick={onClose}>非表示</button>
    </div>
  );
};

const EmptyPage = () => {
  const pageName = '資金繰り表';
  const [showAlert, setShowAlert] = useState(true);
  const [incomeLevel, setIncomeLevel] = useState('中立');
  const [expenseLevel, setExpenseLevel] = useState('中立');
  const [cashFlow, setCashFlow] = useState('');
  const [countermeasure, setCountermeasure] = useState('');
  const chartRef = useRef<ChartJS | null>(null);

  const options = {
    responsive: true,
    layout: {
      padding: {
        top: 30,
        bottom: 0,
        left: 0,
        right: 0,
      },
    },
    plugins: {
      legend: {
        display: false,
        labels: {
          color: 'white',
          boxWidth: 0,
          boxHeight: 0,
        },
      },
    },
    scales: {
      y: {
        beginAtZero: true,
        title: {
          display: true,
          text: '[百万円]', // Y-axis label
        },
      },
    },
  };

  const updateChartAndAction = () => {
    const key = `${incomeLevel}-${expenseLevel}` as keyof typeof dataSets;
    const selectedData = dataSets[key];

    if (!selectedData) {
      console.error(`No data found for key: ${incomeLevel}-${expenseLevel}`);
      return;
    }

    const incomeExpenseDiff = selectedData.incomeExpenseDiff || selectedData.income.map((income, i) => income + selectedData!.expense[i]);

    if (chartRef.current) {
      chartRef.current.data = {
        labels: ['4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月', '1月', '2月', '3月'],
        datasets: [
          {
            type: 'bar',
            label: '収入',
            data: selectedData.income,
            backgroundColor: function (context) {
              const index = context.dataIndex;
              return index < 3 ? 'rgba(153, 102, 255, 0.5)' : 'rgba(153, 102, 255, 0.2)';
            },
          },
          {
            type: 'bar',
            label: '支出',
            data: selectedData.expense,
            backgroundColor: function (context) {
              const index = context.dataIndex;
              return index < 3 ? 'rgba(54, 162, 235, 0.5)' : 'rgba(54, 162, 235, 0.2)';
            },
          },
          {
            type: 'line',
            label: '収支差',
            data: incomeExpenseDiff,
            borderColor: 'blue',
            backgroundColor: 'blue',
            fill: false,
            tension: 0.1,
            borderWidth: 2,
            pointStyle: 'circle',
            pointRadius: 4,
            pointHoverRadius: 6,
            segment: {
              borderDash: (ctx) => {
                return ctx.p0DataIndex < 3 ? [] : [5, 5]; // 4月から6月は実線、7月から3月は破線
              },
            },
            pointBackgroundColor: function (context) {
              const index = context.dataIndex;
              const value = context.dataset.data[index] ?? 0;
              return value < 0 ? 'red' : 'blue';
            }
          },
          {
            type: 'line',
            label: '期末残高',
            data: selectedData.balance || selectedData.income.reduce((acc: number[], income, i) => {
              let newBalance = (acc.length > 0 ? acc[acc.length - 1] : initialBalance) + income + selectedData!.expense[i];
              acc.push(newBalance);
              return acc;
            }, [] as number[]),
            borderColor: 'black',
            backgroundColor: 'black',
            fill: false,
            tension: 0.1,
            borderWidth: 2,
            pointStyle: 'rectRot',
            pointRadius: 6,
            pointHoverRadius: 8,
            segment: {
              borderDash: (ctx) => {
                return ctx.p0DataIndex < 3 ? [] : [5, 5]; // 4月から6月は実線、7月から3月は破線
              },
            },
            pointBackgroundColor: function (context) {
              const index = context.dataIndex;
              const value = context.dataset.data[index] ?? 0;
              return value < 0 ? 'red' : 'black';
            }
          },
        ],
      };

      chartRef.current.update();
    }

    if (selectedData.action) {
      setCashFlow(selectedData.action.cashFlow);
      setCountermeasure(selectedData.action.countermeasure);
    }

    const actionBox = document.querySelector('.actionbox-message');
    if (actionBox) {
      actionBox.scrollTop = 0;
    }
  };

  useEffect(() => {
    updateChartAndAction();
  }, [incomeLevel, expenseLevel]);

  const handleIncomeChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    setIncomeLevel(e.target.value);
  };

  const handleExpenseChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    setExpenseLevel(e.target.value);
  };

  useEffect(() => {
    ChartJS.register(customLegendPlugin);
    return () => {
      ChartJS.unregister(customLegendPlugin);
    };
  }, []);

  const customLegendPlugin = {
    id: 'customLegend',
    afterDraw: function (chart: ChartJS<keyof ChartTypeRegistry, unknown[], unknown>) {
      const legend = chart?.legend;
      if (!legend || !legend.legendItems) return;

      const ctx = chart.ctx;
      const itemWidth = 100;
      const startX = (chart.width - legend.legendItems.length * itemWidth) / 2;
      let currentX = startX;

      const y = 10;

      legend.legendItems.forEach((legendItem: LegendItem, i: number) => {
        if (legendItem.text === '期末残高' || legendItem.text === '収支差') {
          ctx.save();
          ctx.strokeStyle = legendItem.text === '期末残高' ? 'black' : 'blue';
          ctx.lineWidth = 2;
          ctx.beginPath();
          ctx.moveTo(currentX, y);
          ctx.lineTo(currentX + 40, y);
          ctx.stroke();
          ctx.restore();
        } else {
          ctx.save();
          ctx.fillStyle = legendItem.fillStyle as string;
          ctx.fillRect(currentX, y - 5, 40, 10);
          ctx.restore();
        }

        ctx.textBaseline = 'middle';
        ctx.fillStyle = 'black';
        ctx.fillText(legendItem.text, currentX + 50, y);

        currentX += itemWidth;
      });
    },
  };

  const defaultData = {
    labels: [],
    datasets: []
  };

  return (
    <div className="content-wrapper metrics-details">
      <section className="page-cover">
        <div className="page-cover-title-frame">
          <h1>{pageName}</h1>
        </div>
      </section>
      {showAlert && (
        <div className="alert-container">
          <AlertBox
            message="期中に資金がマイナスとなる期間があります"
            onClose={() => setShowAlert(false)}
          />
        </div>
      )}
      <div className="main-content">
        <div className="left-container">
          <div className="graph-container">
            <div style={{ textAlign: 'left', marginBottom: '10px', fontWeight: 'bold', fontSize: '14px', color: '#666666' }}>
              2024年
            </div>
            <Chart ref={chartRef} type="bar" data={chartRef.current?.data || defaultData} options={options} />
          </div>
          <div className="additional-section">
            <div className="data-filter">
              <h2>データ予測</h2>
              <div className="filter-group">
                <div className="filter-btn">収入</div>
                <select className="filter-select" onChange={handleIncomeChange} value={incomeLevel}>
                  <option>楽観</option>
                  <option>中立</option>
                  <option>悲観</option>
                </select>
              </div>
              <div className="filter-group">
                <div className="filter-btn">支出</div>
                <select className="filter-select" onChange={handleExpenseChange} value={expenseLevel}>
                  <option>楽観</option>
                  <option>中立</option>
                  <option>悲観</option>
                </select>
              </div>
            </div>
            <div className="data-comparison">
              <h2>データ比較</h2>
              <button
                className={`comparison-btn active`}
              >
                今年度比較
              </button>
            </div>
          </div>
        </div>
        <div className="right-container">
          <div className="actionbox-title">
            <div>推奨アクション</div>
          </div>
          <div className="actionbox-message">
            <div className="action-section">
              <h3>現金の流れ</h3>
              <p>{cashFlow}</p>
            </div>
            <div className="action-section">
              <h3>対策</h3>
              <p>{countermeasure}</p>
            </div>
          </div>
          <div className="collapsible-panels">
            <CollapsiblePanel title="営業キャッシュフロー" money="32,990,433円" details={operatingCashFlow} />
            <CollapsiblePanel title="投資キャッシュフロー" money="▲25,947,004円" details={investingCashFlow} />
            <CollapsiblePanel title="財務キャッシュフロー" money="▲6,415,126円" details={financingCashFlow} />
          </div>
        </div>
      </div>
    </div>
  );
};
kirin-ri commented 3 months ago
ERROR in src/components/pages/financingPage.tsx:110:44
TS2339: Property 'incomeExpenseDiff' does not exist on type '{ income: number[]; expense: number[]; action: { cashFlow: string; countermeasure: string; }; } | { income: number[]; expense: number[]; action: { cashFlow: string; countermeasure: string; }; } | { income: number[]; expense: number[]; action: { ...; }; } | ... 5 more ... | { ...; }'.
  Property 'incomeExpenseDiff' does not exist on type '{ income: number[]; expense: number[]; action: { cashFlow: string; countermeasure: string; }; }'.
    108 |     }
    109 |
  > 110 |     const incomeExpenseDiff = selectedData.incomeExpenseDiff || selectedData.income.map((income, i) => income + selectedData!.expense[i]);
        |                                            ^^^^^^^^^^^^^^^^^
    111 |
    112 |     if (chartRef.current) {
    113 |       chartRef.current.data = {

ERROR in src/components/pages/financingPage.tsx:160:32
TS2339: Property 'balance' does not exist on type '{ income: number[]; expense: number[]; action: { cashFlow: string; countermeasure: string; }; } | { income: number[]; expense: number[]; action: { cashFlow: string; countermeasure: string; }; } | { income: number[]; expense: number[]; action: { ...; }; } | ... 5 more ... | { ...; }'.
  Property 'balance' does not exist on type '{ income: number[]; expense: number[]; action: { cashFlow: string; countermeasure: string; }; }'.
    158 |             type: 'line',
    159 |             label: '期末残高',
  > 160 |             data: selectedData.balance || selectedData.income.reduce((acc: number[], income, i) => {
        |                                ^^^^^^^
    161 |               let newBalance = (acc.length > 0 ? acc[acc.length - 1] : initialBalance) + income + selectedData!.expense[i];
    162 |               acc.push(newBalance);
    163 |               return acc;
kirin-ri commented 3 months ago
const updateChartAndAction = () => {
  const key = `${incomeLevel}-${expenseLevel}` as keyof typeof dataSets;
  const selectedData = dataSets[key];

  if (!selectedData) {
    console.error(`No data found for key: ${incomeLevel}-${expenseLevel}`);
    return;
  }

  const incomeExpenseDiff = selectedData.incomeExpenseDiff || selectedData.income.map((income, i) => income + selectedData!.expense[i]);
  const balance = selectedData.balance || selectedData.income.reduce((acc: number[], income, i) => {
    let newBalance = (acc.length > 0 ? acc[acc.length - 1] : initialBalance) + income + selectedData!.expense[i];
    acc.push(newBalance);
    return acc;
  }, []);

  if (chartRef.current) {
    chartRef.current.data = {
      labels: ['4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月', '1月', '2月', '3月'],
      datasets: [
        {
          type: 'bar',
          label: '収入',
          data: selectedData.income,
          backgroundColor: function (context) {
            const index = context.dataIndex;
            return index < 3 ? 'rgba(153, 102, 255, 0.5)' : 'rgba(153, 102, 255, 0.2)';
          },
        },
        {
          type: 'bar',
          label: '支出',
          data: selectedData.expense,
          backgroundColor: function (context) {
            const index = context.dataIndex;
            return index < 3 ? 'rgba(54, 162, 235, 0.5)' : 'rgba(54, 162, 235, 0.2)';
          },
        },
        {
          type: 'line',
          label: '収支差',
          data: incomeExpenseDiff,
          borderColor: 'blue',
          backgroundColor: 'blue',
          fill: false,
          tension: 0.1,
          borderWidth: 2,
          pointStyle: 'circle',
          pointRadius: 4,
          pointHoverRadius: 6,
          segment: {
            borderDash: (ctx) => {
              return ctx.p0DataIndex < 3 ? [] : [5, 5]; // 4月から6月は実線、7月から3月は破線
            },
          },
          pointBackgroundColor: function (context) {
            const index = context.dataIndex;
            const value = context.dataset.data[index] ?? 0;
            return value < 0 ? 'red' : 'blue';
          }
        },
        {
          type: 'line',
          label: '期末残高',
          data: balance,
          borderColor: 'black',
          backgroundColor: 'black',
          fill: false,
          tension: 0.1,
          borderWidth: 2,
          pointStyle: 'rectRot',
          pointRadius: 6,
          pointHoverRadius: 8,
          segment: {
            borderDash: (ctx) => {
              return ctx.p0DataIndex < 3 ? [] : [5, 5]; // 4月から6月は実線、7月から3月は破線
            },
          },
          pointBackgroundColor: function (context) {
            const index = context.dataIndex;
            const value = context.dataset.data[index] ?? 0;
            return value < 0 ? 'red' : 'black';
          }
        },
      ],
    };

    chartRef.current.update();
  }

  if (selectedData.action) {
    setCashFlow(selectedData.action.cashFlow);
    setCountermeasure(selectedData.action.countermeasure);
  }

  const actionBox = document.querySelector('.actionbox-message');
  if (actionBox) {
    actionBox.scrollTop = 0;
  }
};
kirin-ri commented 3 months ago
type DataSet = {
  income: number[];
  expense: number[];
  balance: number[];  // 将 balance 设为必需
  incomeExpenseDiff: number[];  // 将 incomeExpenseDiff 设为必需
  action?: {
    cashFlow: string;
    countermeasure: string;
  };
};
kirin-ri commented 3 months ago
import { BarController, BarElement, CategoryScale, Chart as ChartJS, ChartTypeRegistry, Legend, LegendItem, LinearScale, LineController, LineElement, PointElement, Title, Tooltip } from 'chart.js';
import { useEffect, useRef, useState } from "react";
import { Chart } from 'react-chartjs-2';

ChartJS.register(BarController, LineController, CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend);

type DataSet = {
  income: number[];
  expense: number[];
  balance?: number[];
  incomeExpenseDiff?: number[];
  action?: {
    cashFlow: string;
    countermeasure: string;
  };
};

const CollapsiblePanel = ({ title, money, details }: { title: string; money: string; details: { month: string, amount: string, alert?: boolean }[] }) => {
  const [isOpen, setIsOpen] = useState(false);

  const togglePanel = () => {
    setIsOpen(!isOpen);
  };

  return (
    <div className="collapsible-panel">
      <div className="panel-header" onClick={togglePanel}>
        <div className="panel-title">{title}</div>
        <div className="panel-money">{money}</div>
      </div>
      {isOpen && (
        <div className="panel-content">
          <div className="details-container">
            {details.map((detail, index) => (
              <div key={index} className="detail-item" style={{ color: 'black' }}>
                <span>{detail.month}</span>
                <span style={{ color: detail.alert ? 'red' : 'black', marginLeft: '5px' }}>
                  {detail.amount}
                </span>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
};

const AlertBox = ({ message, onClose }: { message: string; onClose: () => void }) => {
  return (
    <div className="alert-box">
      <div className="alert-content">
        <i className="fa fa-exclamation-circle alert-icon" aria-hidden="true"></i>
        <span className="alert-message">{message}</span>
      </div>
      <button className="close-btn" onClick={onClose}>非表示</button>
    </div>
  );
};

const EmptyPage = () => {
  const pageName = '資金繰り表';
  const [showAlert, setShowAlert] = useState(true);
  const [incomeLevel, setIncomeLevel] = useState('中立');
  const [expenseLevel, setExpenseLevel] = useState('中立');
  const [cashFlow, setCashFlow] = useState('');
  const [countermeasure, setCountermeasure] = useState('');
  const chartRef = useRef<ChartJS | null>(null);

  const options = {
    responsive: true,
    layout: {
      padding: {
        top: 30,
        bottom: 0,
        left: 0,
        right: 0,
      },
    },
    plugins: {
      legend: {
        display: false,
        labels: {
          color: 'white',
          boxWidth: 0,
          boxHeight: 0,
        },
      },
    },
    scales: {
      y: {
        beginAtZero: true,
        title: {
          display: true,
          text: '[百万円]', // Y-axis label
        },
      },
    },
  };

  const updateChartAndAction = () => {
    const key = `${incomeLevel}-${expenseLevel}` as keyof typeof dataSets;
    const selectedData = dataSets[key];

    if (!selectedData) {
      console.error(`No data found for key: ${incomeLevel}-${expenseLevel}`);
      return;
    }

    const incomeExpenseDiff = selectedData.incomeExpenseDiff || selectedData.income.map((income, i) => income + selectedData.expense[i]);
    const balance = selectedData.balance || selectedData.income.reduce((acc: number[], income, i) => {
      let newBalance = (acc.length > 0 ? acc[acc.length - 1] : initialBalance) + income + selectedData.expense[i];
      acc.push(newBalance);
      return acc;
    }, []);

    if (chartRef.current) {
      chartRef.current.data = {
        labels: ['4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月', '1月', '2月', '3月'],
        datasets: [
          {
            type: 'bar',
            label: '収入',
            data: selectedData.income,
            backgroundColor: function (context) {
              const index = context.dataIndex;
              return index < 3 ? 'rgba(153, 102, 255, 0.5)' : 'rgba(153, 102, 255, 0.2)';
            },
          },
          {
            type: 'bar',
            label: '支出',
            data: selectedData.expense,
            backgroundColor: function (context) {
              const index = context.dataIndex;
              return index < 3 ? 'rgba(54, 162, 235, 0.5)' : 'rgba(54, 162, 235, 0.2)';
            },
          },
          {
            type: 'line',
            label: '収支差',
            data: incomeExpenseDiff,
            borderColor: 'blue',
            backgroundColor: 'blue',
            fill: false,
            tension: 0.1,
            borderWidth: 2,
            pointStyle: 'circle',
            pointRadius: 4,
            pointHoverRadius: 6,
            segment: {
              borderDash: (ctx) => {
                return ctx.p0DataIndex < 3 ? [] : [5, 5]; // 4月から6月は実線、7月から3月は破線
              },
            },
            pointBackgroundColor: function (context) {
              const index = context.dataIndex;
              const value = context.dataset.data[index] ?? 0;
              return value < 0 ? 'red' : 'blue';
            }
          },
          {
            type: 'line',
            label: '期末残高',
            data: balance,
            borderColor: 'black',
            backgroundColor: 'black',
            fill: false,
            tension: 0.1,
            borderWidth: 2,
            pointStyle: 'rectRot',
            pointRadius: 6,
            pointHoverRadius: 8,
            segment: {
              borderDash: (ctx) => {
                return ctx.p0DataIndex < 3 ? [] : [5, 5]; // 4月から6月は実線、7月から3月は破線
              },
            },
            pointBackgroundColor: function (context) {
              const index = context.dataIndex;
              const value = context.dataset.data[index] ?? 0;
              return value < 0 ? 'red' : 'black';
            }
          },
        ],
      };

      chartRef.current.update();
    }

    if (selectedData.action) {
      setCashFlow(selectedData.action.cashFlow);
      setCountermeasure(selectedData.action.countermeasure);
    }

    const actionBox = document.querySelector('.actionbox-message');
    if (actionBox) {
      actionBox.scrollTop = 0;
    }
  };

  useEffect(() => {
    updateChartAndAction();
  }, [incomeLevel, expenseLevel]);

  const handleIncomeChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    setIncomeLevel(e.target.value);
  };

  const handleExpenseChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    setExpenseLevel(e.target.value);
  };

  useEffect(() => {
    ChartJS.register(customLegendPlugin);
    return () => {
      ChartJS.unregister(customLegendPlugin);
    };
  }, []);

  const customLegendPlugin = {
    id: 'customLegend',
    afterDraw: function (chart: ChartJS<keyof ChartTypeRegistry, unknown[], unknown>) {
      const legend = chart?.legend;
      if (!legend || !legend.legendItems) return;

      const ctx = chart.ctx;
      const itemWidth = 100;
      const startX = (chart.width - legend.legendItems.length * itemWidth) / 2;
      let currentX = startX;

      const y = 10;

      legend.legendItems.forEach((legendItem: LegendItem, i: number) => {
        if (legendItem.text === '期末残高' || legendItem.text === '収支差') {
          ctx.save();
          ctx.strokeStyle = legendItem.text === '期末残高' ? 'black' : 'blue';
          ctx.lineWidth = 2;
          ctx.beginPath();
          ctx.moveTo(currentX, y);
          ctx.lineTo(currentX + 40, y);
          ctx.stroke();
          ctx.restore();
        } else {
          ctx.save();
          ctx.fillStyle = legendItem.fillStyle as string;
          ctx.fillRect(currentX, y - 5, 40, 10);
          ctx.restore();
        }

        ctx.textBaseline = 'middle';
        ctx.fillStyle = 'black';
        ctx.fillText(legendItem.text, currentX + 50, y);

        currentX += itemWidth;
      });
    },
  };

  const defaultData = {
    labels: [],
    datasets: []
  };

  return (
    <div className="content-wrapper metrics-details">
      <section className="page-cover">
        <div className="page-cover-title-frame">
          <h1>{pageName}</h1>
        </div>
      </section>
      {showAlert && (
        <div className="alert-container">
          <AlertBox
            message="期中に資金がマイナスとなる期間があります"
            onClose={() => setShowAlert(false)}
          />
        </div>
      )}
      <div className="main-content">
        <div className="left-container">
          <div className="graph-container">
            <Chart ref={chartRef} type="bar" data={chartRef.current?.data || defaultData} options={options} />
          </div>
          <div className="additional-section">
            <div className="data-filter">
              <h2>データ予測</h2>
              <div className="filter-group">
                <div className="filter-btn">収入</div>
                <select className="filter-select" onChange={handleIncomeChange} value={incomeLevel}>
                  <option>楽観</option>
                  <option>中立</option>
                  <option>悲観</option>
                </select>
              </div>
              <div className="filter-group">
                <div className="filter-btn">支出</div>
                <select className="filter-select" onChange={handleExpenseChange} value={expenseLevel}>
                  <option>楽観</option>
                  <option>中立</option>
                  <option>悲観</option>
                </select>
              </div>
            </div>
            <div className="data-comparison">
              <h2>データ比較</h2>
              <button
                className={`comparison-btn active`}
                onClick={() => {}}
              >
                今年度比較
              </button>
            </div>
          </div>
        </div>
        <div className="right-container">
          <div className="actionbox-title">
            <div>推奨アクション</div>
          </div>
          <div className="actionbox-message">
            <div className="action-section">
              <h3>現金の流れ</h3>
              <p>{cashFlow}</p>
            </div>
            <div className="action-section">
              <h3>対策</h3>
              <p>{countermeasure}</p>
            </div>
          </div>
          <div className="collapsible-panels">
            <CollapsiblePanel title="営業キャッシュフロー" money="32,990,433円" details={operatingCashFlow} />
            <CollapsiblePanel title="投資キャッシュフロー" money="▲25,947,004円" details={investingCashFlow} />
            <CollapsiblePanel title="財務キャッシュフロー" money="▲6,415,126円" details={financingCashFlow} />
          </div>
        </div>
      </div>
    </div>
  );
};
kirin-ri commented 3 months ago
ERROR in src/components/pages/financingPage.tsx:110:44
TS2339: Property 'incomeExpenseDiff' does not exist on type '{ income: number[]; expense: number[]; action: { cashFlow: string; countermeasure: string; }; } | { income: number[]; expense: number[]; action: { cashFlow: string; countermeasure: string; }; } | { income: number[]; expense: number[]; action: { ...; }; } | ... 5 more ... | { ...; }'.
  Property 'incomeExpenseDiff' does not exist on type '{ income: number[]; expense: number[]; action: { cashFlow: string; countermeasure: string; }; }'.
    108 |     }
    109 |
  > 110 |     const incomeExpenseDiff = selectedData.incomeExpenseDiff || selectedData.income.map((income, i) => income + selectedData.expense[i]);
        |                                            ^^^^^^^^^^^^^^^^^
    111 |     const balance = selectedData.balance || selectedData.income.reduce((acc: number[], income, i) => {
    112 |       let newBalance = (acc.length > 0 ? acc[acc.length - 1] : initialBalance) + income + selectedData.expense[i];
    113 |       acc.push(newBalance);

ERROR in src/components/pages/financingPage.tsx:111:34
TS2339: Property 'balance' does not exist on type '{ income: number[]; expense: number[]; action: { cashFlow: string; countermeasure: string; }; } | { income: number[]; expense: number[]; action: { cashFlow: string; countermeasure: string; }; } | { income: number[]; expense: number[]; action: { ...; }; } | ... 5 more ... | { ...; }'.
  Property 'balance' does not exist on type '{ income: number[]; expense: number[]; action: { cashFlow: string; countermeasure: string; }; }'.
    109 |
    110 |     const incomeExpenseDiff = selectedData.incomeExpenseDiff || selectedData.income.map((income, i) => income + selectedData.expense[i]);
  > 111 |     const balance = selectedData.balance || selectedData.income.reduce((acc: number[], income, i) => {
        |                                  ^^^^^^^
    112 |       let newBalance = (acc.length > 0 ? acc[acc.length - 1] : initialBalance) + income + selectedData.expense[i];
    113 |       acc.push(newBalance);
    114 |       return acc;
kirin-ri commented 3 months ago
import { BarController, BarElement, CategoryScale, Chart as ChartJS, ChartTypeRegistry, Legend, LegendItem, LinearScale, LineController, LineElement, PointElement, Title, Tooltip } from 'chart.js';
import { useEffect, useRef, useState } from "react";
import { Chart } from 'react-chartjs-2';

ChartJS.register(BarController, LineController, CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend);

type DataSet = {
  income: number[];
  expense: number[];
  balance?: number[];
  incomeExpenseDiff?: number[];
  action?: {
    cashFlow: string;
    countermeasure: string;
  };
};

const CollapsiblePanel = ({ title, money, details }: { title: string; money: string; details: { month: string, amount: string, alert?: boolean }[] }) => {
  const [isOpen, setIsOpen] = useState(false);

  const togglePanel = () => {
    setIsOpen(!isOpen);
  };

  return (
    <div className="collapsible-panel">
      <div className="panel-header" onClick={togglePanel}>
        <div className="panel-title">{title}</div>
        <div className="panel-money">{money}</div>
      </div>
      {isOpen && (
        <div className="panel-content">
          <div className="details-container">
            {details.map((detail, index) => (
              <div key={index} className="detail-item" style={{ color: 'black' }}>
                <span>{detail.month}</span>
                <span style={{ color: detail.alert ? 'red' : 'black', marginLeft: '5px' }}>
                  {detail.amount}
                </span>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
};

const AlertBox = ({ message, onClose }: { message: string; onClose: () => void }) => {
  return (
    <div className="alert-box">
      <div className="alert-content">
        <i className="fa fa-exclamation-circle alert-icon" aria-hidden="true"></i>
        <span className="alert-message">{message}</span>
      </div>
      <button className="close-btn" onClick={onClose}>非表示</button>
    </div>
  );
};

const EmptyPage = () => {
  const pageName = '資金繰り表';
  const [showAlert, setShowAlert] = useState(true);
  const [incomeLevel, setIncomeLevel] = useState('中立');
  const [expenseLevel, setExpenseLevel] = useState('中立');
  const [cashFlow, setCashFlow] = useState('');
  const [countermeasure, setCountermeasure] = useState('');
  const chartRef = useRef<ChartJS | null>(null);

  const options = {
    responsive: true,
    layout: {
      padding: {
        top: 30,
        bottom: 0,
        left: 0,
        right: 0,
      },
    },
    plugins: {
      legend: {
        display: false,
        labels: {
          color: 'white',
          boxWidth: 0,
          boxHeight: 0,
        },
      },
    },
    scales: {
      y: {
        beginAtZero: true,
        title: {
          display: true,
          text: '[百万円]', // Y-axis label
        },
      },
    },
  };

  const updateChartAndAction = () => {
    const key = `${incomeLevel}-${expenseLevel}` as keyof typeof dataSets;
    const selectedData = dataSets[key];

    if (!selectedData) {
      console.error(`No data found for key: ${incomeLevel}-${expenseLevel}`);
      return;
    }

    // 如果没有定义incomeExpenseDiff或balance,为它们设置默认值
    const incomeExpenseDiff = selectedData.incomeExpenseDiff || selectedData.income.map((income, i) => income + selectedData.expense[i]);
    const balance = selectedData.balance || selectedData.income.reduce((acc: number[], income, i) => {
      let newBalance = (acc.length > 0 ? acc[acc.length - 1] : initialBalance) + income + selectedData.expense[i];
      acc.push(newBalance);
      return acc;
    }, []);

    if (chartRef.current) {
      chartRef.current.data = {
        labels: ['4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月', '1月', '2月', '3月'],
        datasets: [
          {
            type: 'bar',
            label: '収入',
            data: selectedData.income,
            backgroundColor: function (context) {
              const index = context.dataIndex;
              return index < 3 ? 'rgba(153, 102, 255, 0.5)' : 'rgba(153, 102, 255, 0.2)';
            },
          },
          {
            type: 'bar',
            label: '支出',
            data: selectedData.expense,
            backgroundColor: function (context) {
              const index = context.dataIndex;
              return index < 3 ? 'rgba(54, 162, 235, 0.5)' : 'rgba(54, 162, 235, 0.2)';
            },
          },
          {
            type: 'line',
            label: '収支差',
            data: incomeExpenseDiff,
            borderColor: 'blue',
            backgroundColor: 'blue',
            fill: false,
            tension: 0.1,
            borderWidth: 2,
            pointStyle: 'circle',
            pointRadius: 4,
            pointHoverRadius: 6,
            segment: {
              borderDash: (ctx) => {
                return ctx.p0DataIndex < 3 ? [] : [5, 5]; // 4月から6月は実線、7月から3月は破線
              },
            },
            pointBackgroundColor: function (context) {
              const index = context.dataIndex;
              const value = context.dataset.data[index] ?? 0;
              return value < 0 ? 'red' : 'blue';
            }
          },
          {
            type: 'line',
            label: '期末残高',
            data: balance,
            borderColor: 'black',
            backgroundColor: 'black',
            fill: false,
            tension: 0.1,
            borderWidth: 2,
            pointStyle: 'rectRot',
            pointRadius: 6,
            pointHoverRadius: 8,
            segment: {
              borderDash: (ctx) => {
                return ctx.p0DataIndex < 3 ? [] : [5, 5]; // 4月から6月は実線、7月から3月は破線
              },
            },
            pointBackgroundColor: function (context) {
              const index = context.dataIndex;
              const value = context.dataset.data[index] ?? 0;
              return value < 0 ? 'red' : 'black';
            }
          },
        ],
      };

      chartRef.current.update();
    }

    if (selectedData.action) {
      setCashFlow(selectedData.action.cashFlow);
      setCountermeasure(selectedData.action.countermeasure);
    }

    const actionBox = document.querySelector('.actionbox-message');
    if (actionBox) {
      actionBox.scrollTop = 0;
    }
  };

  useEffect(() => {
    updateChartAndAction();
  }, [incomeLevel, expenseLevel]);

  const handleIncomeChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    setIncomeLevel(e.target.value);
  };

  const handleExpenseChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    setExpenseLevel(e.target.value);
  };

  useEffect(() => {
    ChartJS.register(customLegendPlugin);
    return () => {
      ChartJS.unregister(customLegendPlugin);
    };
  }, []);

  const customLegendPlugin = {
    id: 'customLegend',
    afterDraw: function (chart: ChartJS<keyof ChartTypeRegistry, unknown[], unknown>) {
      const legend = chart?.legend;
      if (!legend || !legend.legendItems) return;

      const ctx = chart.ctx;
      const itemWidth = 100;
      const startX = (chart.width - legend.legendItems.length * itemWidth) / 2;
      let currentX = startX;

      const y = 10;

      legend.legendItems.forEach((legendItem: LegendItem, i: number) => {
        if (legendItem.text === '期末残高' || legendItem.text === '収支差') {
          ctx.save();
          ctx.strokeStyle = legendItem.text === '期末残高' ? 'black' : 'blue';
          ctx.lineWidth = 2;
          ctx.beginPath();
          ctx.moveTo(currentX, y);
          ctx.lineTo(currentX + 40, y);
          ctx.stroke();
          ctx.restore();
        } else {
          ctx.save();
          ctx.fillStyle = legendItem.fillStyle as string;
          ctx.fillRect(currentX, y - 5, 40, 10);
          ctx.restore();
        }

        ctx.textBaseline = 'middle';
        ctx.fillStyle = 'black';
        ctx.fillText(legendItem.text, currentX + 50, y);

        currentX += itemWidth;
      });
    },
  };

  const defaultData = {
    labels: [],
    datasets: []
  };

  return (
    <div className="content-wrapper metrics-details">
      <section className="page-cover">
        <div className="page-cover-title-frame">
          <h1>{pageName}</h1>
        </div>
      </section>
      {showAlert && (
        <div className="alert-container">
          <AlertBox
            message="期中に資金がマイナスとなる期間があります"
            onClose={() => setShowAlert(false)}
          />
        </div>
      )}
      <div className="main-content">
        <div className="left-container">
          <div className="graph-container">
            <Chart ref={chartRef} type="bar" data={chartRef.current?.data || defaultData} options={options} />
          </div>
          <div className="additional-section">
            <div className="data-filter">
              <h2>データ予測</h2>
              <div className="filter-group">
                <div className="filter-btn">収入</div>
                <select className="filter-select" onChange={handleIncomeChange} value={incomeLevel}>
                  <option>楽観</option>
                  <option>中立</option>
                  <option>悲観</option>
                </select>
              </div>
              <div className="filter-group">
                <div className="filter-btn">支出</div>
                <select className="filter-select" onChange={handleExpenseChange} value={expenseLevel}>
                  <option>楽観</option>
                  <option>中立</option>
                  <option>悲観</option>
                </select>
              </div>
            </div>
            <div className="data-comparison">
              <h2>データ比較</h2>
              <button className={`comparison-btn active`} onClick={() => {}}>
                今年度比較
              </button>
            </div>
          </div>
        </div>
        <div className="right-container">
          <div className="actionbox-title">
            <div>推奨アクション</div>
          </div>
          <div className="actionbox-message">
            <div className="action-section">
              <h3>現金の流れ</h3>
              <p>{cashFlow}</p>
            </div>
            <div className="action-section">
              <h3>対策</h3>
              <p>{countermeasure}</p>
            </div>
          </div>
          <div className="collapsible-panels">
            <CollapsiblePanel title="営業キャッシュフロー" money="32,990,433円" details={operatingCashFlow} />
            <CollapsiblePanel title="投資キャッシュフロー" money="▲25,947,004円" details={investingCashFlow} />
            <CollapsiblePanel title="財務キャッシュフロー" money="▲6,415,126円" details={financingCashFlow} />
          </div>
        </div>
      </div>
    </div>
  );
};
kirin-ri commented 3 months ago
import { BarController, BarElement, CategoryScale, Chart as ChartJS, ChartTypeRegistry, Legend, LegendItem, LinearScale, LineController, LineElement, PointElement, Title, Tooltip } from 'chart.js';
import { useEffect, useRef, useState } from "react";
import { Chart } from 'react-chartjs-2';

ChartJS.register(BarController, LineController, CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend);

type DataSet = {
  income: number[];
  expense: number[];
  balance: number[];
  incomeExpenseDiff: number[];
  action: {
    cashFlow: string;
    countermeasure: string;
  };
};

const CollapsiblePanel = ({ title, money, details }: { title: string; money: string; details: { month: string, amount: string, alert?: boolean }[] }) => {
  const [isOpen, setIsOpen] = useState(false);

  const togglePanel = () => {
    setIsOpen(!isOpen);
  };

  return (
    <div className="collapsible-panel">
      <div className="panel-header" onClick={togglePanel}>
        <div className="panel-title">{title}</div>
        <div className="panel-money">{money}</div>
      </div>
      {isOpen && (
        <div className="panel-content">
          <div className="details-container">
            {details.map((detail, index) => (
              <div key={index} className="detail-item" style={{ color: 'black' }}>
                <span>{detail.month}</span>
                <span style={{ color: detail.alert ? 'red' : 'black', marginLeft: '5px' }}>
                  {detail.amount}
                </span>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
};

const AlertBox = ({ message, onClose }: { message: string; onClose: () => void }) => {
  return (
    <div className="alert-box">
      <div className="alert-content">
        <i className="fa fa-exclamation-circle alert-icon" aria-hidden="true"></i>
        <span className="alert-message">{message}</span>
      </div>
      <button className="close-btn" onClick={onClose}>非表示</button>
    </div>
  );
};

const EmptyPage = () => {
  const pageName = '資金繰り表';
  const [showAlert, setShowAlert] = useState(true);
  const [incomeLevel, setIncomeLevel] = useState('中立');
  const [expenseLevel, setExpenseLevel] = useState('中立');
  const [cashFlow, setCashFlow] = useState('');
  const [countermeasure, setCountermeasure] = useState('');
  const chartRef = useRef<ChartJS | null>(null);

  const options = {
    responsive: true,
    layout: {
      padding: {
        top: 30,
        bottom: 0,
        left: 0,
        right: 0,
      },
    },
    plugins: {
      legend: {
        display: false,
        labels: {
          color: 'white',
          boxWidth: 0,
          boxHeight: 0,
        },
      },
    },
    scales: {
      y: {
        beginAtZero: true,
        title: {
          display: true,
          text: '[百万円]', // Y-axis label
        },
      },
    },
  };

  const updateChartAndAction = () => {
    const key = `${incomeLevel}-${expenseLevel}` as keyof typeof dataSets;
    const selectedData = dataSets[key];

    if (!selectedData) {
      console.error(`No data found for key: ${incomeLevel}-${expenseLevel}`);
      return;
    }

    const incomeExpenseDiff = selectedData.incomeExpenseDiff;
    const balance = selectedData.balance;

    if (chartRef.current) {
      chartRef.current.data = {
        labels: ['4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月', '1月', '2月', '3月'],
        datasets: [
          {
            type: 'bar',
            label: '収入',
            data: selectedData.income,
            backgroundColor: function (context) {
              const index = context.dataIndex;
              return index < 3 ? 'rgba(153, 102, 255, 0.5)' : 'rgba(153, 102, 255, 0.2)';
            },
          },
          {
            type: 'bar',
            label: '支出',
            data: selectedData.expense,
            backgroundColor: function (context) {
              const index = context.dataIndex;
              return index < 3 ? 'rgba(54, 162, 235, 0.5)' : 'rgba(54, 162, 235, 0.2)';
            },
          },
          {
            type: 'line',
            label: '収支差',
            data: incomeExpenseDiff,
            borderColor: 'blue',
            backgroundColor: 'blue',
            fill: false,
            tension: 0.1,
            borderWidth: 2,
            pointStyle: 'circle',
            pointRadius: 4,
            pointHoverRadius: 6,
            segment: {
              borderDash: (ctx) => {
                return ctx.p0DataIndex < 3 ? [] : [5, 5]; // 4月から6月は実線、7月から3月は破線
              },
            },
            pointBackgroundColor: function (context) {
              const index = context.dataIndex;
              const value = context.dataset.data[index] ?? 0;
              return value < 0 ? 'red' : 'blue';
            }
          },
          {
            type: 'line',
            label: '期末残高',
            data: balance,
            borderColor: 'black',
            backgroundColor: 'black',
            fill: false,
            tension: 0.1,
            borderWidth: 2,
            pointStyle: 'rectRot',
            pointRadius: 6,
            pointHoverRadius: 8,
            segment: {
              borderDash: (ctx) => {
                return ctx.p0DataIndex < 3 ? [] : [5, 5]; // 4月から6月は実線、7月から3月は破線
              },
            },
            pointBackgroundColor: function (context) {
              const index = context.dataIndex;
              const value = context.dataset.data[index] ?? 0;
              return value < 0 ? 'red' : 'black';
            }
          },
        ],
      };

      chartRef.current.update();
    }

    if (selectedData.action) {
      setCashFlow(selectedData.action.cashFlow);
      setCountermeasure(selectedData.action.countermeasure);
    }

    const actionBox = document.querySelector('.actionbox-message');
    if (actionBox) {
      actionBox.scrollTop = 0;
    }
  };

  useEffect(() => {
    updateChartAndAction();
  }, [incomeLevel, expenseLevel]);

  const handleIncomeChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    setIncomeLevel(e.target.value);
  };

  const handleExpenseChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    setExpenseLevel(e.target.value);
  };

  useEffect(() => {
    ChartJS.register(customLegendPlugin);
    return () => {
      ChartJS.unregister(customLegendPlugin);
    };
  }, []);

  const customLegendPlugin = {
    id: 'customLegend',
    afterDraw: function (chart: ChartJS<keyof ChartTypeRegistry, unknown[], unknown>) {
      const legend = chart?.legend;
      if (!legend || !legend.legendItems) return;

      const ctx = chart.ctx;
      const itemWidth = 100;
      const startX = (chart.width - legend.legendItems.length * itemWidth) / 2;
      let currentX = startX;

      const y = 10;

      legend.legendItems.forEach((legendItem: LegendItem, i: number) => {
        if (legendItem.text === '期末残高' || legendItem.text === '収支差') {
          ctx.save();
          ctx.strokeStyle = legendItem.text === '期末残高' ? 'black' : 'blue';
          ctx.lineWidth = 2;
          ctx.beginPath();
          ctx.moveTo(currentX, y);
          ctx.lineTo(currentX + 40, y);
          ctx.stroke();
          ctx.restore();
        } else {
          ctx.save();
          ctx.fillStyle = legendItem.fillStyle as string;
          ctx.fillRect(currentX, y - 5, 40, 10);
          ctx.restore();
        }

        ctx.textBaseline = 'middle';
        ctx.fillStyle = 'black';
        ctx.fillText(legendItem.text, currentX + 50, y);

        currentX += itemWidth;
      });
    },
  };

  const defaultData = {
    labels: [],
    datasets: []
  };

  return (
    <div className="content-wrapper metrics-details">
      <section className="page-cover">
        <div className="page-cover-title-frame">
          <h1>{pageName}</h1>
        </div>
      </section>
      {showAlert && (
        <div className="alert-container">
          <AlertBox
            message="期中に資金がマイナスとなる期間があります"
            onClose={() => setShowAlert(false)}
          />
        </div>
      )}
      <div className="main-content">
        <div className="left-container">
          <div className="graph-container">
            <Chart ref={chartRef} type="bar" data={chartRef.current?.data || defaultData} options={options} />
          </div>
          <div className="additional-section">
            <div className="data-filter">
              <h2>データ予測</h2>
              <div className="filter-group">
                <div className="filter-btn">収入</div>
                <select className="filter-select" onChange={handleIncomeChange} value={incomeLevel}>
                  <option>楽観</option>
                  <option>中立</option>
                  <option>悲観</option>
                </select>
              </div>
              <div className="filter-group">
                <div className="filter-btn">支出</div>
                <select className="filter-select" onChange={handleExpenseChange} value={expenseLevel}>
                  <option>楽観</option>
                  <option>中立</option>
                  <option>悲観</option>
                </select>
              </div>
            </div>
            <div className="data-comparison">
              <h2>データ比較</h2>
              <button className={`comparison-btn active`} onClick={() => {}}>
                今年度比較
              </button>
            </div>
          </div>
        </div>
        <div className="right-container">
          <div className="actionbox-title">
            <div>推奨アクション</div>
          </div>
          <div className="actionbox-message">
            <div className="action-section">
              <h3>現金の流れ</h3>
              <p>{cashFlow}</p>
            </div>
            <div className="action-section">
              <h3>対策</h3>
              <p>{countermeasure}</p>
            </div>
          </div>
          <div className="collapsible-panels">
            <CollapsiblePanel title="営業キャッシュフロー" money="32,990,433円" details={operatingCashFlow} />
            <CollapsiblePanel title="投資キャッシュフロー" money="▲25,947,004円" details={investingCashFlow} />
            <CollapsiblePanel title="財務キャッシュフロー" money="▲6,415,126円" details={financingCashFlow} />
          </div>
        </div>
      </div>
    </div>
  );
};
kirin-ri commented 3 months ago
const dataSets: { [key: string]: DataSet } = {
  "楽観-楽観": {
    income: [34.0, 35.5, 34.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5, 40.0, 40.5, 41.0],
    expense: [-33.0, -34.0, -35.0, -33.5, -33.0, -32.5, -32.0, -31.5, -31.0, -30.5, -30.0, -29.5],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5, 11.5],
    balance: [10.0, 11.0, 10.5, 14.0, 18.5, 24.0, 30.5, 38.0, 46.5, 56.0, 66.5, 78.0],
    action: {
      cashFlow: "昨年の現金の流れは比較的安定していましたが、4月と12月に特に注意が必要でした。今年の4月から6月のデータでは、収入が前年同期よりも若干低下しているものの、支出の管理がより効率的であったため、全体としての現金の流れは安定しています。7月以降も、収入の増加が期待されますが、支出の抑制が鍵となります。",
      countermeasure: "銀行としては、企業の資金繰りを支援するために、柔軟な融資条件の提供を検討することが重要です。特に、短期的な運転資金の貸付を拡大し、企業が収益機会を最大限に活かせるようにサポートすることが求められます。また、為替リスクに対応した金融商品の提供や、資産運用のアドバイスを通じて、企業の財務安定を支援することが効果的です。"
    }
  },
  "楽観-中立": {
    income: [34.0, 35.5, 34.5, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5, 40.0],
    expense: [-33.0, -34.0, -35.0, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5],
    balance: [10.0, 11.5, 11.0, 12.5, 14.5, 17.0, 20.0, 23.5, 27.5, 32.0, 37.0, 42.5],
    action: {
      cashFlow: "昨年は現金の流れが年間を通して安定していましたが、特に後半での支出管理が課題となりました。今年の前半では、収入は前年並みで推移している一方で、支出がやや増加しています。このため、後半にかけては支出管理がさらに重要になります。",
      countermeasure: "銀行としては、企業が支出管理を改善できるように、コスト削減に関連するコンサルティングサービスの提供を検討すべきです。また、キャッシュフローの安定化を図るための短期融資や、支出が増加する期間に備えた資金計画の策定をサポートすることが求められます。"
    }
  },
  "楽観-悲観": {
    income: [34.0, 35.5, 34.5, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5, 40.0],
    expense: [-33.0, -34.0, -35.0, -36.0, -37.0, -38.0, -39.0, -40.0, -41.0, -42.0, -43.0, -44.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.0, -0.5, -1.0, -1.5, -2.0, -2.5, -3.0, -3.5, -4.0],
    balance: [10.0, 11.5, 11.0, 11.0, 10.5, 9.5, 8.0, 6.0, 3.5, 0.5, -3.0, -7.0],
    action: {
      cashFlow: "昨年は収入が安定していたものの、設備投資や労務費の増加により、現金の流れが後半にかけて悪化しました。今年の前半でも同様の傾向が見られ、特に支出が収入を上回るリスクが高まっています。",
      countermeasure: "銀行としては、企業が直面するリスクに対処するための資金支援を強化する必要があります。具体的には、リスクを分散するための投資アドバイスや、支出が増加する際の柔軟な融資枠の提供が考えられます。また、企業がコスト管理を改善するためのツールやリソースを提供することも重要です。"
    }
  },
  "中立-楽観": {
    income: [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    expense: [-33.0, -34.0, -35.0, -33.0, -32.0, -31.0, -30.0, -29.0, -28.0, -27.0, -26.0, -25.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5],
    balance: [10.0, 11.5, 11.0, 12.5, 15.0, 18.5, 23.0, 28.5, 35.0, 42.5, 51.0, 60.5],
    action: {
      cashFlow: "昨年は全体的に安定した現金の流れが見られましたが、今年の前半では収入が予想を下回る一方で、支出管理が良好であったため、現金の流れは比較的安定しています。今後の成長のためには、収入増加のための戦略が重要です。",
      countermeasure: "銀行としては、企業の成長を支援するために、成長資金の提供や、新市場開拓に向けた資金計画の策定支援を行うことが重要です。また、企業の収益性向上をサポートするために、金融商品の多様化や、リスク管理に関するアドバイスを提供することが効果的です。"
    }
  },
  "中立-中立": {
    income: [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    expense: [-33.0, -34.0, -35.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
    balance: [10.0, 11.5, 11.0, 11.5, 12.0, 12.5, 13.0, 13.5, 14.0, 14.5, 15.0, 15.5],
    action: {
      cashFlow: "昨年の現金の流れは非常に安定していました。今年の前半でも、収入と支出のバランスが取れており、全体として安定した現金の流れを維持しています。今後も同様のパターンが続くと予想されます。",
      countermeasure: "銀行としては、企業の財務安定を維持するために、リスク管理強化のサポートを行うことが求められます。安定した収益基盤をさらに強化するために、長期的な投資計画の立案や、安定的な収益を得るためのポートフォリオの構築支援を提供することが有効です。"
    }
  },
  "中立-悲観": {
    income: [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    expense: [-33.0, -34.0, -35.0, -36.0, -37.0, -38.0, -39.0, -40.0, -41.0, -42.0, -43.0, -44.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -1.5, -2.5, -3.5, -4.5, -5.5, -6.5, -7.5, -8.5, -9.5],
    balance: [10.0, 11.5, 11.0, 9.5, 7.0, 3.5, -1.0, -6.5, -13.0, -20.5, -29.0, -38.5],
    action: {
      cashFlow: "昨年は支出が増加し、現金の流れが後半にかけて悪化しました。今年の前半でも同様の傾向が見られ、収入は安定していますが、支出の増加により現金の流れが厳しくなっています。",
      countermeasure: "銀行としては、企業の支出削減を支援するためのコンサルティングサービスを提供することが重要です。また、短期的な資金ニーズに対応するための融資プログラムの提供や、支出増加に備えたリスクヘッジ手段の提案も有効です。企業が支出管理を強化することで、現金の流れを改善するための具体的なアドバイスを提供することが求められます。"
    }
  },
  "悲観-楽観": {
    income: [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    expense: [-33.0, -34.0, -35.0, -33.0, -32.0, -31.0, -30.0, -29.0, -28.0, -27.0, -26.0, -25.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0],
    balance: [10.0, 11.5, 11.0, 11.0, 11.5, 12.5, 14.0, 16.0, 18.5, 21.5, 25.0, 29.0],
    action: {
      cashFlow: "昨年は収入の減少と支出の抑制により、現金の流れが後半にかけて改善されましたが、今年の前半では収入の減少が続き、支出の抑制も限界に達しています。",
      countermeasure: "銀行としては、企業の収益性向上を支援するために、ビジネスモデルの転換をサポートする資金提供を検討すべきです。また、既存事業の効率化を促進するための設備投資支援や、コスト管理の改善に役立つツールの提供が重要です。さらに、企業が収益性の高い市場での競争力を維持できるよう、戦略的なアドバイスを行うことが求められます。"
    }
  },
  "悲観-中立": {
    income: [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    expense: [-33.0, -34.0, -35.0, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -1.5, -2.0, -2.5, -3.0, -3.5, -4.0, -4.5, -5.0, -5.5],
    balance: [10.0, 11.5, 11.0, 9.5, 7.5, 5.0, 2.0, -1.5, -5.5, -10.0, -15.0, -20.5],
    action: {
      cashFlow: "昨年の現金の流れは、支出の抑制により後半にかけて若干の改善が見られましたが、今年の前半では収入の減少が続き、支出の増加が懸念されています。現金の流れが厳しい状況です。",
      countermeasure: "銀行としては、企業の現金流の改善を支援するために、短期融資や支出削減をサポートするコンサルティングサービスを提供することが重要です。また、財務リスクを最小限に抑えるための戦略的アドバイスや、資金運用の効率化を促進するための金融商品の提供も検討すべきです。"
    }
  },
  "悲観-悲観": {
    income: [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    expense: [-33.0, -34.0, -35.0, -36.0, -37.0, -38.0, -39.0, -40.0, -41.0, -42.0, -43.0, -44.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -3.0, -4.5, -6.0, -7.5, -9.0, -10.5, -12.0, -13.5, -15.0],
    balance: [10.0, 11.5, 11.0, 8.0, 3.5, -2.5, -10.0, -19.0, -29.5, -41.5, -55.0, -70.0],
    action: {
      cashFlow: "昨年は支出の増加により現金の流れが悪化し、今年の前半でも同様の傾向が見られます。収入の減少とコストの増加が重なり、現金の流れが非常に厳しい状況です。",
      countermeasure: "銀行としては、企業が速やかに財務の再構築を行えるように、リストラ資金の提供や、不要な投資の見直しを支援するプログラムを提供することが求められます。また、戦略的な投資をサポートするための資金提供や、企業が新たな収益源を開拓できるような金融サービスを提供することも重要です。"
    }
  }
};
kirin-ri commented 3 months ago
      {
        type: 'line',
        label: '新しい実線',
        data: [10.0, 12.0, 11.5, 5.5, 9.0, 4.5, 8.5, 8.0, 9.5, 4.0, 9.5, 10.0],
        borderColor: 'gray',
        backgroundColor: 'gray',
        fill: false,
        borderWidth: 2,
        pointStyle: 'rect',
        pointRadius: 4,
        pointHoverRadius: 6,
      },
kirin-ri commented 3 months ago
if (chartRef.current) {
  const incomeExpenseDiff = selectedData.incomeExpenseDiff || selectedData.income.map((income, i) => income + selectedData.expense[i]);
  const balance = selectedData.balance || selectedData.income.reduce((acc: number[], income, i) => {
    let newBalance = (acc.length > 0 ? acc[acc.length - 1] : initialBalance) + income + selectedData.expense[i];
    acc.push(newBalance);
    return acc;
  }, [] as number[]);

  chartRef.current.data = {
    labels: ['4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月', '1月', '2月', '3月'],
    datasets: [
      {
        type: 'bar',
        label: '収入',
        data: selectedData.income,
        backgroundColor: function (context) {
          const index = context.dataIndex;
          return index < 3 ? 'rgba(153, 102, 255, 0.5)' : 'rgba(153, 102, 255, 0.2)';
        },
      },
      {
        type: 'bar',
        label: '支出',
        data: selectedData.expense,
        backgroundColor: function (context) {
          const index = context.dataIndex;
          return index < 3 ? 'rgba(54, 162, 235, 0.5)' : 'rgba(54, 162, 235, 0.2)';
        },
      },
      {
        type: 'line',
        label: '収支差',
        data: incomeExpenseDiff,
        borderColor: 'blue',
        backgroundColor: 'blue',
        fill: false,
        tension: 0.1,
        borderWidth: 2,
        pointStyle: 'circle',
        pointRadius: 4,
        pointHoverRadius: 6,
        segment: {
          borderDash: (ctx) => {
            return ctx.p0DataIndex < 3 ? [] : [5, 5];
          },
        },
        pointBackgroundColor: function (context) {
          const index = context.dataIndex;
          const value = context.dataset.data[index] ?? 0;
          return value < 0 ? 'red' : 'blue';
        }
      },
      {
        type: 'line',
        label: '期末残高',
        data: balance,
        borderColor: 'black',
        backgroundColor: 'black',
        fill: false,
        tension: 0.1,
        borderWidth: 2,
        pointStyle: 'rectRot',
        pointRadius: 6,
        pointHoverRadius: 8,
        segment: {
          borderDash: (ctx) => {
            return ctx.p0DataIndex < 3 ? [] : [5, 5];
          },
        },
        pointBackgroundColor: function (context) {
          const index = context.dataIndex;
          const value = context.dataset.data[index] ?? 0;
          return value < 0 ? 'red' : 'black';
        }
      },
      {
        type: 'line',
        label: '新しい実線',
        data: [10.0, 12.0, 11.5, 5.5, 9.0, 4.5, 8.5, 8.0, 9.5, 4.0, 9.5, 10.0],
        borderColor: 'gray',
        backgroundColor: 'gray',
        fill: false,
        borderWidth: 2,
        pointStyle: 'circle',
        pointRadius: 4,
        pointHoverRadius: 6,
      },
    ],
  };

  chartRef.current.update();
}
kirin-ri commented 3 months ago
useEffect(() => {
  const customLegendPlugin = {
    id: 'customLegend',
    afterDraw: function (chart) {
      const legend = chart?.legend;
      if (!legend || !legend.legendItems) return;

      const ctx = chart.ctx;
      const itemWidth = chart.width / 4; // 调整每个图例项的宽度以适应两行
      let currentX = (chart.width - itemWidth * 3) / 2; // 调整起始X位置以居中
      let currentY = 20; // 第一行Y坐标

      legend.legendItems.forEach((legendItem, i) => {
        if (i === 3) { // 换行
          currentX = (chart.width - itemWidth * 2) / 2;
          currentY += 30; // 第二行Y坐标,增加行距
        }

        if (legendItem.text === '収支差' || legendItem.text === '新しい実線' || legendItem.text === '期末残高') {
          ctx.save();
          ctx.strokeStyle = legendItem.text === '収支差' ? 'blue' : legendItem.text === '新しい実線' ? 'gray' : 'black';
          ctx.lineWidth = 2;
          ctx.beginPath();
          ctx.moveTo(currentX, currentY);
          ctx.lineTo(currentX + 40, currentY);
          ctx.stroke();
          ctx.restore();
        } else {
          ctx.save();
          ctx.fillStyle = legendItem.fillStyle as string;
          ctx.fillRect(currentX, currentY - 5, 40, 10);
          ctx.restore();
        }

        ctx.textBaseline = 'middle';
        ctx.fillStyle = 'black';
        ctx.fillText(legendItem.text, currentX + 50, currentY);

        currentX += itemWidth; // 移动到下一个图例项的位置
      });
    },
  };

  ChartJS.register(customLegendPlugin);
  return () => {
    ChartJS.unregister(customLegendPlugin);
  };
}, []);
kirin-ri commented 3 months ago
import { BarController, BarElement, CategoryScale, Chart as ChartJS, ChartTypeRegistry, Legend, LegendItem, LinearScale, LineController, LineElement, PointElement, Title, Tooltip } from 'chart.js';
import { useEffect, useRef, useState } from "react";
import { Chart } from 'react-chartjs-2';

ChartJS.register(BarController, LineController, CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend);

type DataSet = {
  income: number[];
  expense: number[];
  incomeExpenseDiff?: number[];
  balance?: number[];
  action?: {
    cashFlow: string;
    countermeasure: string;
  };
};

const CollapsiblePanel = ({ title, money, details }: { title: string; money: string; details: { month: string, amount: string, alert?: boolean }[] }) => {
  const [isOpen, setIsOpen] = useState(false);

  const togglePanel = () => {
    setIsOpen(!isOpen);
  };

  return (
    <div className="collapsible-panel">
      <div className="panel-header" onClick={togglePanel}>
        <div className="panel-title">{title}</div>
        <div className="panel-money">{money}</div>
      </div>
      {isOpen && (
        <div className="panel-content">
          <div className="details-container">
            {details.map((detail, index) => (
              <div key={index} className="detail-item" style={{ color: 'black' }}>
                <span>{detail.month}</span>
                <span style={{ color: detail.alert ? 'red' : 'black', marginLeft: '5px' }}>
                  {detail.amount}
                </span>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
};

const AlertBox = ({ message, onClose }: { message: string; onClose: () => void }) => {
  return (
    <div className="alert-box">
      <div className="alert-content">
        <i className="fa fa-exclamation-circle alert-icon" aria-hidden="true"></i>
        <span className="alert-message">{message}</span>
      </div>
      <button className="close-btn" onClick={onClose}>非表示</button>
    </div>
  );
};

const EmptyPage = () => {
  const pageName = '資金繰り表';
  const [showAlert, setShowAlert] = useState(true);
  const [incomeLevel, setIncomeLevel] = useState('中立');
  const [expenseLevel, setExpenseLevel] = useState('中立');
  const [cashFlow, setCashFlow] = useState('');
  const [countermeasure, setCountermeasure] = useState('');
  const chartRef = useRef<ChartJS | null>(null);

  const options = {
    responsive: true,
    layout: {
      padding: {
        top: 30,
        bottom: 0,
        left: 0,
        right: 0,
      },
    },
    plugins: {
      legend: {
        display: false,
        labels: {
          color: 'white',
          boxWidth: 0,
          boxHeight: 0,
        },
      },
    },
    scales: {
      y: {
        beginAtZero: true,
        title: {
          display: true,
          text: '[百万円]', // Y-axis label
        },
      },
    },
  };

  const updateChartAndAction = () => {
    const key = `${incomeLevel}-${expenseLevel}` as keyof typeof dataSets;
    const selectedData = dataSets[key];

    if (!selectedData) {
      console.error(`No data found for key: ${incomeLevel}-${expenseLevel}`);
      return;
    }

    const incomeExpenseDiff = selectedData.incomeExpenseDiff || selectedData.income.map((income, i) => income + selectedData.expense[i]);
    const balance = selectedData.balance || selectedData.income.reduce((acc: number[], income, i) => {
      let newBalance = (acc.length > 0 ? acc[acc.length - 1] : initialBalance) + income + selectedData.expense[i];
      acc.push(newBalance);
      return acc;
    }, [] as number[]);

    if (chartRef.current) {
      chartRef.current.data = {
        labels: ['4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月', '1月', '2月', '3月'],
        datasets: [
          {
            type: 'bar',
            label: '収入',
            data: selectedData.income,
            backgroundColor: function (context) {
              const index = context.dataIndex;
              return index < 3 ? 'rgba(153, 102, 255, 0.5)' : 'rgba(153, 102, 255, 0.2)';
            },
          },
          {
            type: 'bar',
            label: '支出',
            data: selectedData.expense,
            backgroundColor: function (context) {
              const index = context.dataIndex;
              return index < 3 ? 'rgba(54, 162, 235, 0.5)' : 'rgba(54, 162, 235, 0.2)';
            },
          },
          {
            type: 'line',
            label: '収支差',
            data: incomeExpenseDiff,
            borderColor: 'blue',
            backgroundColor: 'blue',
            fill: false,
            tension: 0.1,
            borderWidth: 2,
            pointStyle: 'circle',
            pointRadius: 4,
            pointHoverRadius: 6,
            segment: {
              borderDash: (ctx) => {
                return ctx.p0DataIndex < 3 ? [] : [5, 5];
              },
            },
            pointBackgroundColor: function (context) {
              const index = context.dataIndex;
              const value = context.dataset.data[index] ?? 0;
              return value < 0 ? 'red' : 'blue';
            }
          },
          {
            type: 'line',
            label: '期末残高',
            data: balance,
            borderColor: 'black',
            backgroundColor: 'black',
            fill: false,
            tension: 0.1,
            borderWidth: 2,
            pointStyle: 'rectRot',
            pointRadius: 6,
            pointHoverRadius: 8,
            segment: {
              borderDash: (ctx) => {
                return ctx.p0DataIndex < 3 ? [] : [5, 5];
              },
            },
            pointBackgroundColor: function (context) {
              const index = context.dataIndex;
              const value = context.dataset.data[index] ?? 0;
              return value < 0 ? 'red' : 'black';
            }
          },
          {
            type: 'line',
            label: '新しい実線',
            data: [10.0, 12.0, 11.5, 5.5, 9.0, 4.5, 8.5, 8.0, 9.5, 4.0, 9.5, 10.0],
            borderColor: 'gray',
            backgroundColor: 'gray',
            fill: false,
            borderWidth: 2,
            pointStyle: 'circle',
            pointRadius: 4,
            pointHoverRadius: 6,
          },
        ],
      };

      chartRef.current.update();
    }

    if (selectedData.action) {
      setCashFlow(selectedData.action.cashFlow);
      setCountermeasure(selectedData.action.countermeasure);
    }

    const actionBox = document.querySelector('.actionbox-message');
    if (actionBox) {
      actionBox.scrollTop = 0;
    }
  };

  useEffect(() => {
    updateChartAndAction();
  }, [incomeLevel, expenseLevel]);

  const handleIncomeChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    setIncomeLevel(e.target.value);
  };

  const handleExpenseChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    setExpenseLevel(e.target.value);
  };

  useEffect(() => {
    const customLegendPlugin = {
      id: 'customLegend',
      afterDraw: function (chart: ChartJS<keyof ChartTypeRegistry, unknown[], unknown>) {
        const legend = chart?.legend;
        if (!legend || !legend.legendItems) return;

        const ctx = chart.ctx;
        const itemWidth = chart.width / 4; // 调整每个图例项的宽度以适应两行
        let currentX = (chart.width - itemWidth * 3) / 2; // 调整起始X位置以居中
        let currentY = 20; // 第一行Y坐标

        legend.legendItems.forEach((legendItem, i) => {
          if (i === 3) { // 换行
            currentX = (chart.width - itemWidth * 2) / 2;
            currentY += 30; // 第二行Y坐标,增加行距
          }

          if (legendItem.text === '収支差' || legendItem.text === '新しい実線' || legendItem.text === '期末残高') {
            ctx.save();
            ctx.strokeStyle = legendItem.text === '収支差' ? 'blue' : legendItem.text === '新しい実線' ? 'gray' : 'black';
            ctx.lineWidth = 2;
            ctx.beginPath();
            ctx.moveTo(currentX, currentY);
            ctx.lineTo(currentX + 40, currentY);
            ctx.stroke();
            ctx.restore();
          } else {
            ctx.save();
            ctx.fillStyle = legendItem.fillStyle as string;
            ctx.fillRect(currentX, currentY - 5, 40, 10);
            ctx.restore();
          }

          ctx.textBaseline = 'middle';
          ctx.fillStyle = 'black';
          ctx.fillText(legendItem.text, currentX + 50, currentY);

          currentX += itemWidth; // 移动到下一个图例项的位置
        });
      },
    };

    ChartJS.register(customLegendPlugin);
    return () => {
      ChartJS.unregister(customLegendPlugin);
    };
  }, []);

  const defaultData = {
    labels: [],
    datasets: []
  };

  return (
    <div className="content-wrapper metrics-details">
      <section className="page-cover">
        <div className="page-cover-title-frame">
          <h1>{pageName}</h1>
        </div>
      </section>
      {showAlert && (
        <div className="alert-container">
          <AlertBox
            message="期中に資金がマイナスとなる期間があります"
            onClose={() => setShowAlert(false)}
          />
        </div>
      )}
      <div className="main-content">
        <div className="left-container">
          <div className="graph-container">
            <Chart ref={chartRef} type="bar" data={chartRef.current?.data || defaultData} options={options} />
          </div>
          <div className="additional-section">
            <div className="data-filter">
              <h2>データ予測</h2>
              <div className="filter-group">
                <div className="filter-btn">収入</div>
                <select className="filter-select" onChange={handleIncomeChange} value={incomeLevel}>
                  <option>楽観</option>
                  <option>中立</option>
                  <option>悲観</option>
                </select>
              </div>
              <div className="filter-group">
                <div className="filter-btn">支出</div>
                <select className="filter-select" onChange={handleExpenseChange} value={expenseLevel}>
                  <option>楽観</option>
                  <option>中立</option>
                  <option>悲観</option>
                </select>
              </div>
            </div>
          </div>
        </div>
        <div className="right-container">
          <div className="actionbox-title">
            <div>推奨アクション</div>
          </div>
          <div className="actionbox-message">
            <div className="action-section">
              <h3>現金の流れ</h3>
              <p>{cashFlow}</p>
            </div>
            <div className="action-section">
              <h3>対策</h3>
              <p>{countermeasure}</p>
            </div>
          </div>
          <div className="collapsible-panels">
            <CollapsiblePanel title="営業キャッシュフロー" money="32,990,433円" details={operatingCashFlow} />
            <CollapsiblePanel title="投資キャッシュフロー" money="▲25,947,004円" details={investingCashFlow} />
            <CollapsiblePanel title="財務キャッシュフロー" money="▲6,415,126円" details={financingCashFlow} />
          </div>
        </div>
      </div>
    </div>
  );
};

const initialBalance = 10.0;

const dataSets: { [key: string]: DataSet } = {
  "楽観-楽観": {
    income: [34.0, 35.5, 34.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5, 40.0, 40.5, 41.0],
    expense: [-33.0, -34.0, -35.0, -33.5, -33.0, -32.5, -32.0, -31.5, -31.0, -30.5, -30.0, -29.5],
    incomeExpenseDiff: [],
    balance: [],
    action: {
      cashFlow: "昨年の現金の流れは比較的安定していましたが、4月と12月に特に注意が必要でした。今年の4月から6月のデータでは、収入が前年同期よりも若干低下しているものの、支出の管理がより効率的であったため、全体としての現金の流れは安定しています。7月以降も、収入の増加が期待されますが、支出の抑制が鍵となります。",
      countermeasure: "銀行としては、企業の資金繰りを支援するために、柔軟な融資条件の提供を検討することが重要です。特に、短期的な運転資金の貸付を拡大し、企業が収益機会を最大限に活かせるようにサポートすることが求められます。また、為替リスクに対応した金融商品の提供や、資産運用のアドバイスを通じて、企業の財務安定を支援することが効果的です。"
    }
  },
  // 其他可能的数据集配置可以按照相同的方式进行添加
};

const operatingCashFlow = [
  { month: '4月', amount: '34,035,567円' },
  { month: '5月', amount: '30,407,343円' },
  { month: '6月', amount: '34,528,390円' },
  { month: '7月', amount: '-' },
  { month: '8月', amount: '-' },
  { month: '9月', amount: '-' },
  { month: '10月', amount: '-' },
  { month: '11月', amount: '-' },
  { month: '12月', amount: '-' },
  { month: '1月', amount: '-' },
  { month: '2月', amount: '-' },
  { month: '3月', amount: '-' },
];

const investingCashFlow = [
  { month: '4月', amount: '▲21,502,456円', alert: true },
  { month: '5月', amount: '▲34,023,289円', alert: true },
  { month: '6月', amount: '▲22,315,267円', alert: true },
  { month: '7月', amount: '-' },
  { month: '8月', amount: '-' },
  { month: '9月', amount: '-' },
  { month: '10月', amount: '-' },
  { month: '11月', amount: '-' },
  { month: '12月', amount: '-' },
  { month: '1月', amount: '-' },
  { month: '2月', amount: '-' },
  { month: '3月', amount: '-' },
];

const financingCashFlow = [
  { month: '4月', amount: '▲11,504,456円', alert: true },
  { month: '5月', amount: '5,005,275円' },
  { month: '6月', amount: '▲12,746,198円', alert: true },
  { month: '7月', amount: '-' },
  { month: '8月', amount: '-' },
  { month: '9月', amount: '-' },
  { month: '10月', amount: '-' },
  { month: '11月', amount: '-' },
  { month: '12月', amount: '-' },
  { month: '1月', amount: '-' },
  { month: '2月', amount: '-' },
  { month: '3月', amount: '-' },
];

export default EmptyPage;
kirin-ri commented 3 months ago
ERROR in src/components/pages/financingPage.tsx:112:64
TS2304: Cannot find name 'initialBalance'.
    110 |     const incomeExpenseDiff = selectedData.incomeExpenseDiff || selectedData.income.map((income, i) => income + selectedData.expense[i]);
    111 |     const balance = selectedData.balance || selectedData.income.reduce((acc: number[], income, i) => {
  > 112 |       let newBalance = (acc.length > 0 ? acc[acc.length - 1] : initialBalance) + income + selectedData.expense[i];
        |                                                                ^^^^^^^^^^^^^^
    113 |       acc.push(newBalance);
    114 |       return acc;
    115 |     }, [] as number[]);
kirin-ri commented 3 months ago
import { BarController, BarElement, CategoryScale, Chart as ChartJS, ChartTypeRegistry, Legend, LinearScale, LineController, LineElement, PointElement, Title, Tooltip } from 'chart.js';
import { useEffect, useRef, useState } from "react";
import { Chart } from 'react-chartjs-2';

ChartJS.register(BarController, LineController, CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend);

type DataSet = {
  income: number[];
  expense: number[];
  incomeExpenseDiff?: number[];
  balance?: number[];
  action?: {
    cashFlow: string;
    countermeasure: string;
  };
};

const CollapsiblePanel = ({ title, money, details }: { title: string; money: string; details: { month: string, amount: string, alert?: boolean }[] }) => {
  const [isOpen, setIsOpen] = useState(false);

  const togglePanel = () => {
    setIsOpen(!isOpen);
  };

  return (
    <div className="collapsible-panel">
      <div className="panel-header" onClick={togglePanel}>
        <div className="panel-title">{title}</div>
        <div className="panel-money">{money}</div>
      </div>
      {isOpen && (
        <div className="panel-content">
          <div className="details-container">
            {details.map((detail, index) => (
              <div key={index} className="detail-item" style={{ color: 'black' }}>
                <span>{detail.month}</span>
                <span style={{ color: detail.alert ? 'red' : 'black', marginLeft: '5px' }}>
                  {detail.amount}
                </span>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
};

const AlertBox = ({ message, onClose }: { message: string; onClose: () => void }) => {
  return (
    <div className="alert-box">
      <div className="alert-content">
        <i className="fa fa-exclamation-circle alert-icon" aria-hidden="true"></i>
        <span className="alert-message">{message}</span>
      </div>
      <button className="close-btn" onClick={onClose}>非表示</button>
    </div>
  );
};

const EmptyPage = () => {
  const pageName = '資金繰り表';
  const [showAlert, setShowAlert] = useState(true);
  const [activeComparison, setActiveComparison] = useState('時系列比較');
  const [incomeLevel, setIncomeLevel] = useState('中立');
  const [expenseLevel, setExpenseLevel] = useState('中立');
  const [cashFlow, setCashFlow] = useState('');
  const [countermeasure, setCountermeasure] = useState('');
  const chartRef = useRef<ChartJS | null>(null);

  const options = {
    responsive: true,
    layout: {
      padding: {
        top: 50,
        bottom: 0,
        left: 0,
        right: 0,
      },
    },
    plugins: {
      legend: {
        display: false,
        labels: {
          color: 'white',
          boxWidth: 0,
          boxHeight: 0,
        },
      },
    },
    scales: {
      y: {
        beginAtZero: true,
        title: {
          display: true,
          text: '[百万円]', // Y-axis label
        },
      },
    },
  };

  const updateChartAndAction = () => {
    const key = `${incomeLevel}-${expenseLevel}` as keyof typeof dataSets;
    const selectedData = dataSets[key];

    if (!selectedData) {
      console.error(`No data found for key: ${incomeLevel}-${expenseLevel}`);
      return;
    }

    const incomeExpenseDiff = selectedData.incomeExpenseDiff || selectedData.income.map((income, i) => income + selectedData.expense[i]);
    const balance = selectedData.balance || selectedData.income.reduce((acc: number[], income, i) => {
      let newBalance = (acc.length > 0 ? acc[acc.length - 1] : initialBalance) + income + selectedData.expense[i];
      acc.push(newBalance);
      return acc;
    }, [] as number[]);

    if (chartRef.current) {
      chartRef.current.data = {
        labels: ['4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月', '1月', '2月', '3月'],
        datasets: [
          {
            type: 'bar',
            label: '収入',
            data: selectedData.income,
            backgroundColor: function (context) {
              const index = context.dataIndex;
              return index < 3 ? 'rgba(153, 102, 255, 0.5)' : 'rgba(153, 102, 255, 0.2)';
            },
          },
          {
            type: 'bar',
            label: '支出',
            data: selectedData.expense,
            backgroundColor: function (context) {
              const index = context.dataIndex;
              return index < 3 ? 'rgba(54, 162, 235, 0.5)' : 'rgba(54, 162, 235, 0.2)';
            },
          },
          {
            type: 'line',
            label: '収支差',
            data: incomeExpenseDiff,
            borderColor: 'blue',
            backgroundColor: 'blue',
            fill: false,
            tension: 0.1,
            borderWidth: 2,
            pointStyle: 'circle',
            pointRadius: 4,
            pointHoverRadius: 6,
            segment: {
              borderDash: (ctx) => {
                return ctx.p0DataIndex < 3 ? [] : [5, 5];
              },
            },
            pointBackgroundColor: function (context) {
              const index = context.dataIndex;
              const value = context.dataset.data[index] ?? 0;
              return value < 0 ? 'red' : 'blue';
            }
          },
          {
            type: 'line',
            label: '期末残高',
            data: balance,
            borderColor: 'black',
            backgroundColor: 'black',
            fill: false,
            tension: 0.1,
            borderWidth: 2,
            pointStyle: 'rectRot',
            pointRadius: 6,
            pointHoverRadius: 8,
            segment: {
              borderDash: (ctx) => {
                return ctx.p0DataIndex < 3 ? [] : [5, 5];
              },
            },
            pointBackgroundColor: function (context) {
              const index = context.dataIndex;
              const value = context.dataset.data[index] ?? 0;
              return value < 0 ? 'red' : 'black';
            }
          },
          {
            type: 'line',
            label: '新しい実線',
            data: [10.0, 12.0, 11.5, 5.5, 9.0, 4.5, 8.5, 8.0, 9.5, 4.0, 9.5, 10.0],
            borderColor: 'gray',
            backgroundColor: 'gray',
            fill: false,
            borderWidth: 2,
            pointStyle: 'circle',
            pointRadius: 4,
            pointHoverRadius: 6,
          },
        ],
      };

      chartRef.current.update();
    }

    if (selectedData.action) {
      setCashFlow(selectedData.action.cashFlow);
      setCountermeasure(selectedData.action.countermeasure);
    }

    const actionBox = document.querySelector('.actionbox-message');
    if (actionBox) {
      actionBox.scrollTop = 0;
    }
  };

  useEffect(() => {
    updateChartAndAction();
  }, [incomeLevel, expenseLevel]);

  const handleIncomeChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    setIncomeLevel(e.target.value);
  };

  const handleExpenseChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    setExpenseLevel(e.target.value);
  };

  useEffect(() => {
    const customLegendPlugin = {
      id: 'customLegend',
      afterDraw: function (chart: ChartJS<keyof ChartTypeRegistry, unknown[], unknown>) {
        const legend = chart?.legend;
        if (!legend || !legend.legendItems) return;

        const ctx = chart.ctx;
        const itemWidth = chart.width / 4; // 调整每个图例项的宽度以适应两行
        let currentX = (chart.width - itemWidth * 3) / 2; // 调整起始X位置以居中
        let currentY = 20; // 第一行Y坐标

        legend.legendItems.forEach((legendItem, i) => {
          if (i === 3) { // 换行
            currentX = (chart.width - itemWidth * 2) / 2;
            currentY += 30; // 第二行Y坐标,增加行距
          }

          if (legendItem.text === '収支差' || legendItem.text === '新しい実線' || legendItem.text === '期末残高') {
            ctx.save();
            ctx.strokeStyle = legendItem.text === '収支差' ? 'blue' : legendItem.text === '新しい実線' ? 'gray' : 'black';
            ctx.lineWidth = 2;
            ctx.beginPath();
            ctx.moveTo(currentX, currentY);
            ctx.lineTo(currentX + 40, currentY);
            ctx.stroke();
            ctx.restore();
          } else {
            ctx.save();
            ctx.fillStyle = legendItem.fillStyle as string;
            ctx.fillRect(currentX, currentY - 5, 40, 10);
            ctx.restore();
          }

          ctx.textBaseline = 'middle';
          ctx.fillStyle = 'black';
          ctx.fillText(legendItem.text, currentX + 50, currentY);

          currentX += itemWidth; // 移动到下一个图例项的位置
        });
      },
    };

    ChartJS.register(customLegendPlugin);
    return () => {
      ChartJS.unregister(customLegendPlugin);
    };
  }, []);

  const defaultData = {
    labels: [],
    datasets: []
  };

  return (
    <div className="content-wrapper metrics-details">
      <section className="page-cover">
        <div className="page-cover-title-frame">
          <h1>{pageName}</h1>
        </div>
      </section>
      {showAlert && (
        <div className="alert-container">
          <AlertBox
            message="期中に資金がマイナスとなる期間があります"
            onClose={() => setShowAlert(false)}
          />
        </div>
      )}
      <div className="main-content">
        <div className="left-container">
          <div className="graph-container">
            <Chart ref={chartRef} type="bar" data={chartRef.current?.data || defaultData} options={options} />
          </div>
          <div className="additional-section">
            <div className="data-filter">
              <h2>データ予測</h2>
              <div className="filter-group">
                <div className="filter-btn">収入</div>
                <select className="filter-select" onChange={handleIncomeChange} value={incomeLevel}>
                  <option>楽観</option>
                  <option>中立</option>
                  <option>悲観</option>
                </select>
              </div>
              <div className="filter-group">
                <div className="filter-btn">支出</div>
                <select className="filter-select" onChange={handleExpenseChange} value={expenseLevel}>
                  <option>楽観</option>
                  <option>中立</option>
                  <option>悲観</option>
                </select>
              </div>
            </div>
            <div className="data-comparison">
              <h2>データ比較</h2>
              <button
                className={`comparison-btn ${activeComparison === '時系列比較' ? 'active' : ''}`}
              >
                時系列比較
              </button>
            </div>
          </div>
        </div>
        <div className="right-container">
          <div className="actionbox-title">
            <div>推奨アクション</div>
          </div>
          <div className="actionbox-message">
            <div className="action-section">
              <h3>現金の流れ</h3>
              <p>{cashFlow}</p>
            </div>
            <div className="action-section">
              <h3>対策</h3>
              <p>{countermeasure}</p>
            </div>
          </div>
          <div className="collapsible-panels">
            <CollapsiblePanel title="営業キャッシュフロー" money="32,990,433円" details={operatingCashFlow} />
            <CollapsiblePanel title="投資キャッシュフロー" money="▲25,947,004円" details={investingCashFlow} />
            <CollapsiblePanel title="財務キャッシュフロー" money="▲6,415,126円" details={financingCashFlow} />
          </div>
        </div>
      </div>
    </div>
  );
};

const initialBalance =10.0
const dataSets: { [key: string]: DataSet } = {
  "楽観-楽観": {
    income: [34.0, 35.5, 34.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5, 40.0, 40.5, 41.0],
    expense: [-33.0, -34.0, -35.0, -33.5, -33.0, -32.5, -32.0, -31.5, -31.0, -30.5, -30.0, -29.5],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5, 11.5],
    balance: [10.0, 11.0, 10.5, 14.0, 18.5, 24.0, 30.5, 38.0, 46.5, 56.0, 66.5, 78.0],
    action: {
      cashFlow: "昨年の現金の流れは比較的安定していましたが、4月と12月に特に注意が必要でした。今年の4月から6月のデータでは、収入が前年同期よりも若干低下しているものの、支出の管理がより効率的であったため、全体としての現金の流れは安定しています。7月以降も、収入の増加が期待されますが、支出の抑制が鍵となります。",
      countermeasure: "銀行としては、企業の資金繰りを支援するために、柔軟な融資条件の提供を検討することが重要です。特に、短期的な運転資金の貸付を拡大し、企業が収益機会を最大限に活かせるようにサポートすることが求められます。また、為替リスクに対応した金融商品の提供や、資産運用のアドバイスを通じて、企業の財務安定を支援することが効果的です。"
    }
  },
  "楽観-中立": {
    income: [34.0, 35.5, 34.5, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5, 40.0],
    expense: [-33.0, -34.0, -35.0, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5],
    balance: [10.0, 11.5, 11.0, 12.5, 14.5, 17.0, 20.0, 23.5, 27.5, 32.0, 37.0, 42.5],
    action: {
      cashFlow: "昨年は現金の流れが年間を通して安定していましたが、特に後半での支出管理が課題となりました。今年の前半では、収入は前年並みで推移している一方で、支出がやや増加しています。このため、後半にかけては支出管理がさらに重要になります。",
      countermeasure: "銀行としては、企業が支出管理を改善できるように、コスト削減に関連するコンサルティングサービスの提供を検討すべきです。また、キャッシュフローの安定化を図るための短期融資や、支出が増加する期間に備えた資金計画の策定をサポートすることが求められます。"
    }
  },
  "楽観-悲観": {
    income: [34.0, 35.5, 34.5, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5, 40.0],
    expense: [-33.0, -34.0, -35.0, -36.0, -37.0, -38.0, -39.0, -40.0, -41.0, -42.0, -43.0, -44.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.0, -0.5, -1.0, -1.5, -2.0, -2.5, -3.0, -3.5, -4.0],
    balance: [10.0, 11.5, 11.0, 11.0, 10.5, 9.5, 8.0, 6.0, 3.5, 0.5, -3.0, -7.0],
    action: {
      cashFlow: "昨年は収入が安定していたものの、設備投資や労務費の増加により、現金の流れが後半にかけて悪化しました。今年の前半でも同様の傾向が見られ、特に支出が収入を上回るリスクが高まっています。",
      countermeasure: "銀行としては、企業が直面するリスクに対処するための資金支援を強化する必要があります。具体的には、リスクを分散するための投資アドバイスや、支出が増加する際の柔軟な融資枠の提供が考えられます。また、企業がコスト管理を改善するためのツールやリソースを提供することも重要です。"
    }
  },
  "中立-楽観": {
    income: [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    expense: [-33.0, -34.0, -35.0, -33.0, -32.0, -31.0, -30.0, -29.0, -28.0, -27.0, -26.0, -25.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5],
    balance: [10.0, 11.5, 11.0, 12.5, 15.0, 18.5, 23.0, 28.5, 35.0, 42.5, 51.0, 60.5],
    action: {
      cashFlow: "昨年は全体的に安定した現金の流れが見られましたが、今年の前半では収入が予想を下回る一方で、支出管理が良好であったため、現金の流れは比較的安定しています。今後の成長のためには、収入増加のための戦略が重要です。",
      countermeasure: "銀行としては、企業の成長を支援するために、成長資金の提供や、新市場開拓に向けた資金計画の策定支援を行うことが重要です。また、企業の収益性向上をサポートするために、金融商品の多様化や、リスク管理に関するアドバイスを提供することが効果的です。"
    }
  },
  "中立-中立": {
    income: [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    expense: [-33.0, -34.0, -35.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
    balance: [10.0, 11.5, 11.0, 11.5, 12.0, 12.5, 13.0, 13.5, 14.0, 14.5, 15.0, 15.5],
    action: {
      cashFlow: "昨年の現金の流れは非常に安定していました。今年の前半でも、収入と支出のバランスが取れており、全体として安定した現金の流れを維持しています。今後も同様のパターンが続くと予想されます。",
      countermeasure: "銀行としては、企業の財務安定を維持するために、リスク管理強化のサポートを行うことが求められます。安定した収益基盤をさらに強化するために、長期的な投資計画の立案や、安定的な収益を得るためのポートフォリオの構築支援を提供することが有効です。"
    }
  },
  "中立-悲観": {
    income: [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    expense: [-33.0, -34.0, -35.0, -36.0, -37.0, -38.0, -39.0, -40.0, -41.0, -42.0, -43.0, -44.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -1.5, -2.5, -3.5, -4.5, -5.5, -6.5, -7.5, -8.5, -9.5],
    balance: [10.0, 11.5, 11.0, 9.5, 7.0, 3.5, -1.0, -6.5, -13.0, -20.5, -29.0, -38.5],
    action: {
      cashFlow: "昨年は支出が増加し、現金の流れが後半にかけて悪化しました。今年の前半でも同様の傾向が見られ、収入は安定していますが、支出の増加により現金の流れが厳しくなっています。",
      countermeasure: "銀行としては、企業の支出削減を支援するためのコンサルティングサービスを提供することが重要です。また、短期的な資金ニーズに対応するための融資プログラムの提供や、支出増加に備えたリスクヘッジ手段の提案も有効です。企業が支出管理を強化することで、現金の流れを改善するための具体的なアドバイスを提供することが求められます。"
    }
  },
  "悲観-楽観": {
    income: [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    expense: [-33.0, -34.0, -35.0, -33.0, -32.0, -31.0, -30.0, -29.0, -28.0, -27.0, -26.0, -25.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0],
    balance: [10.0, 11.5, 11.0, 11.0, 11.5, 12.5, 14.0, 16.0, 18.5, 21.5, 25.0, 29.0],
    action: {
      cashFlow: "昨年は収入の減少と支出の抑制により、現金の流れが後半にかけて改善されましたが、今年の前半では収入の減少が続き、支出の抑制も限界に達しています。",
      countermeasure: "銀行としては、企業の収益性向上を支援するために、ビジネスモデルの転換をサポートする資金提供を検討すべきです。また、既存事業の効率化を促進するための設備投資支援や、コスト管理の改善に役立つツールの提供が重要です。さらに、企業が収益性の高い市場での競争力を維持できるよう、戦略的なアドバイスを行うことが求められます。"
    }
  },
  "悲観-中立": {
    income: [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    expense: [-33.0, -34.0, -35.0, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -1.5, -2.0, -2.5, -3.0, -3.5, -4.0, -4.5, -5.0, -5.5],
    balance: [10.0, 11.5, 11.0, 9.5, 7.5, 5.0, 2.0, -1.5, -5.5, -10.0, -15.0, -20.5],
    action: {
      cashFlow: "昨年の現金の流れは、支出の抑制により後半にかけて若干の改善が見られましたが、今年の前半では収入の減少が続き、支出の増加が懸念されています。現金の流れが厳しい状況です。",
      countermeasure: "銀行としては、企業の現金流の改善を支援するために、短期融資や支出削減をサポートするコンサルティングサービスを提供することが重要です。また、財務リスクを最小限に抑えるための戦略的アドバイスや、資金運用の効率化を促進するための金融商品の提供も検討すべきです。"
    }
  },
  "悲観-悲観": {
    income: [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    expense: [-33.0, -34.0, -35.0, -36.0, -37.0, -38.0, -39.0, -40.0, -41.0, -42.0, -43.0, -44.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -3.0, -4.5, -6.0, -7.5, -9.0, -10.5, -12.0, -13.5, -15.0],
    balance: [10.0, 11.5, 11.0, 8.0, 3.5, -2.5, -10.0, -19.0, -29.5, -41.5, -55.0, -70.0],
    action: {
      cashFlow: "昨年は支出の増加により現金の流れが悪化し、今年の前半でも同様の傾向が見られます。収入の減少とコストの増加が重なり、現金の流れが非常に厳しい状況です。",
      countermeasure: "銀行としては、企業が速やかに財務の再構築を行えるように、リストラ資金の提供や、不要な投資の見直しを支援するプログラムを提供することが求められます。また、戦略的な投資をサポートするための資金提供や、企業が新たな収益源を開拓できるような金融サービスを提供することも重要です。"
    }
  }
};

const operatingCashFlow = [
  { month: '4月', amount: '34,035,567円' },
  { month: '5月', amount: '30,407,343円' },
  { month: '6月', amount: '34,528,390円' },
  { month: '7月', amount: '-' },
  { month: '8月', amount: '-' },
  { month: '9月', amount: '-' },
  { month: '10月', amount: '-' },
  { month: '11月', amount: '-' },
  { month: '12月', amount: '-' },
  { month: '1月', amount: '-' },
  { month: '2月', amount: '-' },
  { month: '3月', amount: '-' },
];
const investingCashFlow = [
  { month: '4月', amount: '▲21,502,456円', alert: true },
  { month: '5月', amount: '▲34,023,289円', alert: true },
  { month: '6月', amount: '▲22,315,267円', alert: true },
  { month: '7月', amount: '-' },
  { month: '8月', amount: '-' },
  { month: '9月', amount: '-' },
  { month: '10月', amount: '-' },
  { month: '11月', amount: '-' },
  { month: '12月', amount: '-' },
  { month: '1月', amount: '-' },
  { month: '2月', amount: '-' },
  { month: '3月', amount: '-' },
];

const financingCashFlow = [
  { month: '4月', amount: '▲11,504,456円', alert: true },
  { month: '5月', amount: '5,005,275円' },
  { month: '6月', amount: '▲12,746,198円', alert: true },
  { month: '7月', amount: '-' },
  { month: '8月', amount: '-' },
  { month: '9月', amount: '-' },
  { month: '10月', amount: '-' },
  { month: '11月', amount: '-' },
  { month: '12月', amount: '-' },
  { month: '1月', amount: '-' },
  { month: '2月', amount: '-' },
  { month: '3月', amount: '-' },
];

export default EmptyPage;
kirin-ri commented 2 months ago
const initialBalance =10.0
const dataSets: { [key: string]: DataSet } = {
  "楽観-楽観": {
    income: [34.0, 35.5, 34.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5, 40.0, 40.5, 41.0],
    expense: [-33.0, -34.0, -35.0, -33.5, -33.0, -32.5, -32.0, -31.5, -31.0, -30.5, -30.0, -29.5],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5, 11.5],
    balance: [10.0, 11.0, 10.5, 14.0, 18.5, 24.0, 30.5, 38.0, 46.5, 56.0, 66.5, 78.0],
    action: {
      cashFlow: "昨年の現金の流れは比較的安定していましたが、4月と12月に特に注意が必要でした。今年の4月から6月のデータでは、収入が前年同期よりも若干低下しているものの、支出の管理がより効率的であったため、全体としての現金の流れは安定しています。7月以降も、収入の増加が期待されますが、支出の抑制が鍵となります。",
      countermeasure: "銀行としては、企業の資金繰りを支援するために、柔軟な融資条件の提供を検討することが重要です。特に、短期的な運転資金の貸付を拡大し、企業が収益機会を最大限に活かせるようにサポートすることが求められます。また、為替リスクに対応した金融商品の提供や、資産運用のアドバイスを通じて、企業の財務安定を支援することが効果的です。"
    }
  },
  "楽観-中立": {
    income: [34.0, 35.5, 34.5, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5, 40.0],
    expense: [-33.0, -34.0, -35.0, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5],
    balance: [10.0, 11.5, 11.0, 12.5, 14.5, 17.0, 20.0, 23.5, 27.5, 32.0, 37.0, 42.5],
    action: {
      cashFlow: "昨年は現金の流れが年間を通して安定していましたが、特に後半での支出管理が課題となりました。今年の前半では、収入は前年並みで推移している一方で、支出がやや増加しています。このため、後半にかけては支出管理がさらに重要になります。",
      countermeasure: "銀行としては、企業が支出管理を改善できるように、コスト削減に関連するコンサルティングサービスの提供を検討すべきです。また、キャッシュフローの安定化を図るための短期融資や、支出が増加する期間に備えた資金計画の策定をサポートすることが求められます。"
    }
  },
  "楽観-悲観": {
    income: [34.0, 35.5, 34.5, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5, 40.0],
    expense: [-33.0, -34.0, -35.0, -36.0, -37.0, -38.0, -39.0, -40.0, -41.0, -42.0, -43.0, -44.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.0, -0.5, -1.0, -1.5, -2.0, -2.5, -3.0, -3.5, -4.0],
    balance: [10.0, 11.5, 11.0, 11.0, 10.5, 9.5, 8.0, 6.0, 3.5, 0.5, -3.0, -7.0],
    action: {
      cashFlow: "昨年は収入が安定していたものの、設備投資や労務費の増加により、現金の流れが後半にかけて悪化しました。今年の前半でも同様の傾向が見られ、特に支出が収入を上回るリスクが高まっています。",
      countermeasure: "銀行としては、企業が直面するリスクに対処するための資金支援を強化する必要があります。具体的には、リスクを分散するための投資アドバイスや、支出が増加する際の柔軟な融資枠の提供が考えられます。また、企業がコスト管理を改善するためのツールやリソースを提供することも重要です。"
    }
  },
  "中立-楽観": {
    income: [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    expense: [-33.0, -34.0, -35.0, -33.0, -32.0, -31.0, -30.0, -29.0, -28.0, -27.0, -26.0, -25.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5],
    balance: [10.0, 11.5, 11.0, 12.5, 15.0, 18.5, 23.0, 28.5, 35.0, 42.5, 51.0, 60.5],
    action: {
      cashFlow: "昨年は全体的に安定した現金の流れが見られましたが、今年の前半では収入が予想を下回る一方で、支出管理が良好であったため、現金の流れは比較的安定しています。今後の成長のためには、収入増加のための戦略が重要です。",
      countermeasure: "銀行としては、企業の成長を支援するために、成長資金の提供や、新市場開拓に向けた資金計画の策定支援を行うことが重要です。また、企業の収益性向上をサポートするために、金融商品の多様化や、リスク管理に関するアドバイスを提供することが効果的です。"
    }
  },
  "中立-中立": {
    income: [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    expense: [-33.0, -34.0, -35.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
    balance: [10.0, 11.5, 11.0, 11.5, 12.0, 12.5, 13.0, 13.5, 14.0, 14.5, 15.0, 15.5],
    action: {
      cashFlow: "昨年の現金の流れは非常に安定していました。今年の前半でも、収入と支出のバランスが取れており、全体として安定した現金の流れを維持しています。今後も同様のパターンが続くと予想されます。",
      countermeasure: "銀行としては、企業の財務安定を維持するために、リスク管理強化のサポートを行うことが求められます。安定した収益基盤をさらに強化するために、長期的な投資計画の立案や、安定的な収益を得るためのポートフォリオの構築支援を提供することが有効です。"
    }
  },
  "中立-悲観": {
    income: [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    expense: [-33.0, -34.0, -35.0, -36.0, -37.0, -38.0, -39.0, -40.0, -41.0, -42.0, -43.0, -44.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -1.5, -2.5, -3.5, -4.5, -5.5, -6.5, -7.5, -8.5, -9.5],
    balance: [10.0, 11.5, 11.0, 9.5, 7.0, 3.5, -1.0, -6.5, -13.0, -20.5, -29.0, -38.5],
    action: {
      cashFlow: "昨年は支出が増加し、現金の流れが後半にかけて悪化しました。今年の前半でも同様の傾向が見られ、収入は安定していますが、支出の増加により現金の流れが厳しくなっています。",
      countermeasure: "銀行としては、企業の支出削減を支援するためのコンサルティングサービスを提供することが重要です。また、短期的な資金ニーズに対応するための融資プログラムの提供や、支出増加に備えたリスクヘッジ手段の提案も有効です。企業が支出管理を強化することで、現金の流れを改善するための具体的なアドバイスを提供することが求められます。"
    }
  },
  "悲観-楽観": {
    income: [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    expense: [-33.0, -34.0, -35.0, -33.0, -32.0, -31.0, -30.0, -29.0, -28.0, -27.0, -26.0, -25.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0],
    balance: [10.0, 11.5, 11.0, 11.0, 11.5, 12.5, 14.0, 16.0, 18.5, 21.5, 25.0, 29.0],
    action: {
      cashFlow: "昨年は収入の減少と支出の抑制により、現金の流れが後半にかけて改善されましたが、今年の前半では収入の減少が続き、支出の抑制も限界に達しています。",
      countermeasure: "銀行としては、企業の収益性向上を支援するために、ビジネスモデルの転換をサポートする資金提供を検討すべきです。また、既存事業の効率化を促進するための設備投資支援や、コスト管理の改善に役立つツールの提供が重要です。さらに、企業が収益性の高い市場での競争力を維持できるよう、戦略的なアドバイスを行うことが求められます。"
    }
  },
  "悲観-中立": {
    income: [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    expense: [-33.0, -34.0, -35.0, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -1.5, -2.0, -2.5, -3.0, -3.5, -4.0, -4.5, -5.0, -5.5],
    balance: [10.0, 11.5, 11.0, 9.5, 7.5, 5.0, 2.0, -1.5, -5.5, -10.0, -15.0, -20.5],
    action: {
      cashFlow: "昨年の現金の流れは、支出の抑制により後半にかけて若干の改善が見られましたが、今年の前半では収入の減少が続き、支出の増加が懸念されています。現金の流れが厳しい状況です。",
      countermeasure: "銀行としては、企業の現金流の改善を支援するために、短期融資や支出削減をサポートするコンサルティングサービスを提供することが重要です。また、財務リスクを最小限に抑えるための戦略的アドバイスや、資金運用の効率化を促進するための金融商品の提供も検討すべきです。"
    }
  },
  "悲観-悲観": {
    income: [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    expense: [-33.0, -34.0, -35.0, -36.0, -37.0, -38.0, -39.0, -40.0, -41.0, -42.0, -43.0, -44.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -3.0, -4.5, -6.0, -7.5, -9.0, -10.5, -12.0, -13.5, -15.0],
    balance: [10.0, 11.5, 11.0, 8.0, 3.5, -2.5, -10.0, -19.0, -29.5, -41.5, -55.0, -70.0],
    action: {
      cashFlow: "昨年は支出の増加により現金の流れが悪化し、今年の前半でも同様の傾向が見られます。収入の減少とコストの増加が重なり、現金の流れが非常に厳しい状況です。",
      countermeasure: "銀行としては、企業が速やかに財務の再構築を行えるように、リストラ資金の提供や、不要な投資の見直しを支援するプログラムを提供することが求められます。また、戦略的な投資をサポートするための資金提供や、企業が新たな収益源を開拓できるような金融サービスを提供することも重要です。"
    }
  }
};
kirin-ri commented 2 months ago

支出悲観が悲観すぎるので、もう少しマイナスのブレを抑える。「悲観-悲観」が「悲観-中立」になるくらいの数字に。

kirin-ri commented 2 months ago
"悲観-悲観": {
  income: [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
  expense: [-33.0, -34.0, -35.0, -35.0, -36.0, -36.5, -37.0, -37.5, -38.0, -38.5, -39.0, -39.5],  // Adjusted values
  incomeExpenseDiff: [1.0, 1.5, -0.5, -2.0, -3.5, -4.5, -5.5, -6.5, -7.5, -8.5, -9.5, -10.5], // Adjusted values
  balance: [10.0, 11.5, 11.0, 9.0, 5.5, 1.0, -4.5, -11.0, -18.5, -27.0, -36.5, -47.0], // Adjusted values
  action: {
    cashFlow: "昨年は支出の増加により現金の流れが悪化し、今年の前半でも同様の傾向が見られます。収入の減少とコストの増加が重なり、現金の流れが厳しい状況ですが、ある程度の調整が見込まれます。",
    countermeasure: "銀行としては、企業が支出をより効率的に管理できるように支援することが重要です。リスクを最小限に抑えるための短期融資や支出削減のためのアドバイスを提供し、財務安定を図るための戦略的支援を行うことが求められます。"
  }
}
kirin-ri commented 2 months ago
好調パターン:「楽観- 楽観」、「楽観-中立」、「中立-楽観」
現金の流れ
事実だけを記載する形に変更。
例えば、「楽観-楽観」の「7月以降も〜」、「楽観-中立」の「このため〜」、「中立-楽観」の「今後の成長のためには〜」の文章は不要
対策
あえて記載なしにしましょう。「-」だけ。銀行とのディスカッションで追加すべきものがあれば反映。
kirin-ri commented 2 months ago

昨年の現金の流れは比較的安定していました。今年の4月から6月のデータでは、収入が前年同期よりも若干低下しているものの、支出の管理がより効率的であったため、全体としての現金の流れは安定しています。

kirin-ri commented 2 months ago

昨年は現金の流れが年間を通して安定していました。今年の前半では、収入は前年並みで推移している一方で、支出がやや増加しています。

kirin-ri commented 2 months ago

昨年は全体的に安定した現金の流れが見られました。今年の前半では収入が予想を下回る一方で、支出管理が良好であったため、現金の流れは比較的安定しています。

kirin-ri commented 2 months ago
堅調パターン:「悲観-楽観」、「中立-中立」
現金の流れ
修正不要
対策
打ち合わせでコメントした通り、銀行目線での記載ではなく、ユーザ企業目線での記載に変更。
例えば、「維持するためのリスク管理を強化すべく、定期的なレビューによる見直し、調整を推奨」、「現金回収を早めるべく、請求の迅速化を検討」、「計画を見直し、過剰在庫の削減することで資金の効率運用」、「状況に応じて短期、中長期の追加融資の検討」、「投資家からの資金調達など、手段の多様化を検討」、「売掛金の管理を徹底し、未回収の債権に対して迅速に対応」などを追加。
kirin-ri commented 2 months ago
  "悲観-楽観": {
    income: [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    expense: [-33.0, -34.0, -35.0, -33.0, -32.0, -31.0, -30.0, -29.0, -28.0, -27.0, -26.0, -25.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0],
    balance: [10.0, 11.5, 11.0, 11.0, 11.5, 12.5, 14.0, 16.0, 18.5, 21.5, 25.0, 29.0],
    action: {
      cashFlow: "昨年は収入の減少と支出の抑制により、現金の流れが後半にかけて改善されましたが、今年の前半では収入の減少が続き、支出の抑制も限界に達しています。",
      countermeasure: "現金回収を早めるべく、請求の迅速化を検討し、売掛金の管理を徹底することで、未回収の債権に対して迅速に対応する。計画を見直し、過剰在庫の削減に取り組むことで、資金の効率運用を図る。また、状況に応じて短期および中長期の追加融資の検討や、投資家からの資金調達など、手段の多様化を検討することが求められる。"
    }
  },
kirin-ri commented 2 months ago
  "悲観-楽観": {
    income: [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    expense: [-33.0, -34.0, -35.0, -33.0, -32.0, -31.0, -30.0, -29.0, -28.0, -27.0, -26.0, -25.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0],
    balance: [10.0, 11.5, 11.0, 11.0, 11.5, 12.5, 14.0, 16.0, 18.5, 21.5, 25.0, 29.0],
    action: {
      cashFlow: "昨年は収入の減少と支出の抑制により、現金の流れが後半にかけて改善されましたが、今年の前半では収入の減少が続き、支出の抑制も限界に達しています。",
      countermeasure: "現金回収を早めるために、請求の迅速化を検討し、売掛金の管理を徹底します。計画を見直し、過剰在庫を削減することで資金を効率的に運用します。また、状況に応じて短期および中長期の追加融資を検討し、投資家からの資金調達など、手段の多様化を検討します。"
    }
  },
kirin-ri commented 2 months ago
  "中立-中立": {
    income: [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    expense: [-33.0, -34.0, -35.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
    balance: [10.0, 11.5, 11.0, 11.5, 12.0, 12.5, 13.0, 13.5, 14.0, 14.5, 15.0, 15.5],
    action: {
      cashFlow: "昨年の現金の流れは非常に安定していました。今年の前半でも、収入と支出のバランスが取れており、全体として安定した現金の流れを維持しています。今後も同様のパターンが続くと予想されます。",
      countermeasure: "リスク管理を強化するために、定期的なレビューによる見直しと調整を行います。売掛金の管理を徹底し、現金回収の迅速化を図ることで、現金の流れをさらに安定させます。また、計画的な支出管理を徹底し、予測可能なキャッシュフローを維持します。必要に応じて、短期および中長期の資金計画の策定を検討します。"
    }
  }
kirin-ri commented 2 months ago
不調パターン:「楽観-悲観」、「中立-悲観」、「悲観-中立」、「悲観-悲観」
現金の流れ
修正不要
対策
堅調パターンと同様。ただし、早急な改善が必要であるため、短期的な改善が見込める対策に限定。
例えば、「維持するためのリスク管理を強化すべく、定期的なレビューによる見直し、調整を推奨」、「現金回収を早めるべく、請求の迅速化を検討」、「短期追加融資の検討」、「売掛金の管理を徹底し、未回収の債権に対して迅速に対応」などを追加。
kirin-ri commented 2 months ago
  "楽観-悲観": {
    income: [34.0, 35.5, 34.5, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5, 40.0],
    expense: [-33.0, -34.0, -35.0, -36.0, -37.0, -38.0, -39.0, -40.0, -41.0, -42.0, -43.0, -44.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.0, -0.5, -1.0, -1.5, -2.0, -2.5, -3.0, -3.5, -4.0],
    balance: [10.0, 11.5, 11.0, 11.0, 10.5, 9.5, 8.0, 6.0, 3.5, 0.5, -3.0, -7.0],
    action: {
      cashFlow: "昨年は収入が安定していたものの、設備投資や労務費の増加により、現金の流れが後半にかけて悪化しました。今年の前半でも同様の傾向が見られ、特に支出が収入を上回るリスクが高まっています。",
      countermeasure: "現金回収を早めるために、請求の迅速化を検討し、売掛金の管理を徹底します。短期追加融資を検討し、資金不足に対する迅速な対応を行います。リスク管理を強化するために、定期的なレビューによる見直しと調整を行い、未回収の債権に対して迅速に対応します。"
    }
  },
  "中立-悲観": {
    income: [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    expense: [-33.0, -34.0, -35.0, -36.0, -37.0, -38.0, -39.0, -40.0, -41.0, -42.0, -43.0, -44.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -1.5, -2.5, -3.5, -4.5, -5.5, -6.5, -7.5, -8.5, -9.5],
    balance: [10.0, 11.5, 11.0, 9.5, 7.0, 3.5, -1.0, -6.5, -13.0, -20.5, -29.0, -38.5],
    action: {
      cashFlow: "昨年は支出が増加し、現金の流れが後半にかけて悪化しました。今年の前半でも同様の傾向が見られ、収入は安定していますが、支出の増加により現金の流れが厳しくなっています。",
      countermeasure: "現金回収を早めるために、請求の迅速化を検討します。売掛金の管理を徹底し、未回収の債権に対して迅速に対応します。短期追加融資を検討し、資金不足に対する迅速な対応を行います。また、リスク管理を強化するため、定期的なレビューによる見直しと調整を行います。"
    }
  },
  "悲観-中立": {
    income: [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    expense: [-33.0, -34.0, -35.0, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -1.5, -2.0, -2.5, -3.0, -3.5, -4.0, -4.5, -5.0, -5.5],
    balance: [10.0, 11.5, 11.0, 9.5, 7.5, 5.0, 2.0, -1.5, -5.5, -10.0, -15.0, -20.5],
    action: {
      cashFlow: "昨年の現金の流れは、支出の抑制により後半にかけて若干の改善が見られましたが、今年の前半では収入の減少が続き、支出の増加が懸念されています。現金の流れが厳しい状況です。",
      countermeasure: "売掛金の管理を徹底し、未回収の債権に対して迅速に対応します。現金回収を早めるために、請求の迅速化を検討します。短期追加融資を検討し、資金不足に対する迅速な対応を行います。リスク管理を強化し、定期的なレビューによる見直しと調整を行います。"
    }
  },
  "悲観-悲観": {
    income: [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    expense: [-33.0, -34.0, -35.0, -36.0, -37.0, -38.0, -39.0, -40.0, -41.0, -42.0, -43.0, -44.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -3.0, -4.5, -6.0, -7.5, -9.0, -10.5, -12.0, -13.5, -15.0],
    balance: [10.0, 11.5, 11.0, 8.0, 3.5, -2.5, -10.0, -19.0, -29.5, -41.5, -55.0, -70.0],
    action: {
      cashFlow: "昨年は支出の増加により現金の流れが悪化し、今年の前半でも同様の傾向が見られます。収入の減少とコストの増加が重なり、現金の流れが非常に厳しい状況です。",
      countermeasure: "売掛金の管理を徹底し、未回収の債権に対して迅速に対応します。現金回収を早めるために、請求の迅速化を検討します。短期追加融資を検討し、資金不足に対する迅速な対応を行います。また、リスク管理を強化するために、定期的なレビューによる見直しと調整を行います。"
    }
  }
kirin-ri commented 2 months ago
  "悲観-悲観": {
    income: [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    expense: [-33.0, -34.0, -35.0, -34.0, -34.5, -35.0, -35.5, -36.0, -36.5, -37.0, -37.5, -38.0], // Adjusted values
    incomeExpenseDiff: [1.0, 1.5, -0.5, -1.0, -2.0, -3.0, -4.0, -5.0, -6.0, -7.0, -8.0, -9.0], // Adjusted values
    balance: [10.0, 11.5, 11.0, 10.0, 8.0, 5.0, 1.0, -4.0, -10.0, -17.0, -25.0, -34.0], // Adjusted values
    action: {
      cashFlow: "昨年は支出の増加により現金の流れが悪化し、今年の前半でも同様の傾向が見られます。収入の減少とコストの増加が重なり、現金の流れが非常に厳しい状況です。",
      countermeasure: "売掛金の管理を徹底し、未回収の債権に対して迅速に対応します。現金回収を早めるために、請求の迅速化を検討します。短期追加融資を検討し、資金不足に対する迅速な対応を行います。また、リスク管理を強化するために、定期的なレビューによる見直しと調整を行います。"
    }
  }
kirin-ri commented 2 months ago
  "楽観-楽観": {
    income: [34.0, 35.0, 34.5, 35.5, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5], // Adjusted values
    expense: [-33.0, -33.5, -34.0, -33.5, -33.0, -32.5, -32.0, -31.5, -31.0, -30.5, -30.0, -29.5], // Adjusted values
    incomeExpenseDiff: [1.0, 1.5, 0.5, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0], // Adjusted values
    balance: [10.0, 11.0, 11.5, 13.5, 16.5, 20.5, 25.5, 31.5, 38.5, 46.5, 55.5, 65.5], // Adjusted values
    action: {
      cashFlow: "昨年の現金の流れは比較的安定していました。今年の4月から6月のデータでは、収入が前年同期よりも若干低下しているものの、支出の管理がより効率的であったため、全体としての現金の流れは安定しています。",
      countermeasure: "-"
    }
  }
};
kirin-ri commented 2 months ago
const dataSets: { [key: string]: DataSet } = {
  "楽観-楽観": {
    income: [34.0, 35.0, 34.5, 35.5, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5], // Adjusted values
    expense: [-33.0, -33.5, -34.0, -33.5, -33.0, -32.5, -32.0, -31.5, -31.0, -30.5, -30.0, -29.5], // Adjusted values
    incomeExpenseDiff: [1.0, 1.5, 0.5, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0], // Adjusted values
    balance: [10.0, 11.0, 11.5, 13.5, 16.5, 20.5, 25.5, 31.5, 38.5, 46.5, 55.5, 65.5], // Adjusted values
    action: {
      cashFlow: "昨年の現金の流れは比較的安定していました。今年の4月から6月のデータでは、収入が前年同期よりも若干低下しているものの、支出の管理がより効率的であったため、全体としての現金の流れは安定しています。",
      countermeasure: "-"
    }
  },
  "楽観-中立": {
    income: [34.0, 35.5, 34.5, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5, 40.0],
    expense: [-33.0, -34.0, -35.0, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5],
    balance: [10.0, 11.5, 11.0, 12.5, 14.5, 17.0, 20.0, 23.5, 27.5, 32.0, 37.0, 42.5],
    action: {
      cashFlow: "昨年は現金の流れが年間を通して安定していました。今年の前半では、収入は前年並みで推移している一方で、支出がやや増加しています。",
      countermeasure: "-"
    }
  },
  "楽観-悲観": {
    income: [34.0, 35.5, 34.5, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5, 40.0],
    expense: [-33.0, -34.0, -35.0, -36.0, -37.0, -38.0, -39.0, -40.0, -41.0, -42.0, -43.0, -44.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.0, -0.5, -1.0, -1.5, -2.0, -2.5, -3.0, -3.5, -4.0],
    balance: [10.0, 11.5, 11.0, 11.0, 10.5, 9.5, 8.0, 6.0, 3.5, 0.5, -3.0, -7.0],
    action: {
      cashFlow: "昨年は収入が安定していたものの、設備投資や労務費の増加により、現金の流れが後半にかけて悪化しました。今年の前半でも同様の傾向が見られ、特に支出が収入を上回るリスクが高まっています。",
      countermeasure: "現金回収を早めるために、請求の迅速化を検討し、売掛金の管理を徹底します。短期追加融資を検討し、資金不足に対する迅速な対応を行います。リスク管理を強化するために、定期的なレビューによる見直しと調整を行い、未回収の債権に対して迅速に対応します。"
    }
  },
  "中立-楽観": {
    income: [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    expense: [-33.0, -34.0, -35.0, -33.0, -32.0, -31.0, -30.0, -29.0, -28.0, -27.0, -26.0, -25.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5],
    balance: [10.0, 11.5, 11.0, 12.5, 15.0, 18.5, 23.0, 28.5, 35.0, 42.5, 51.0, 60.5],
    action: {
      cashFlow: "昨年は全体的に安定した現金の流れが見られました。今年の前半では収入が予想を下回る一方で、支出管理が良好であったため、現金の流れは比較的安定しています。",
      countermeasure: "-"
    }
  },
  "中立-中立": {
    income: [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    expense: [-33.0, -34.0, -35.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
    balance: [10.0, 11.5, 11.0, 11.5, 12.0, 12.5, 13.0, 13.5, 14.0, 14.5, 15.0, 15.5],
    action: {
      cashFlow: "昨年の現金の流れは非常に安定していました。今年の前半でも、収入と支出のバランスが取れており、全体として安定した現金の流れを維持しています。今後も同様のパターンが続くと予想されます。",
      countermeasure: "リスク管理を強化するために、定期的なレビューによる見直しと調整を行います。売掛金の管理を徹底し、現金回収の迅速化を図ることで、現金の流れをさらに安定させます。また、計画的な支出管理を徹底し、予測可能なキャッシュフローを維持します。必要に応じて、短期および中長期の資金計画の策定を検討します。"
    }
  },
  "中立-悲観": {
    income: [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    expense: [-33.0, -34.0, -35.0, -36.0, -37.0, -38.0, -39.0, -40.0, -41.0, -42.0, -43.0, -44.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -1.5, -2.5, -3.5, -4.5, -5.5, -6.5, -7.5, -8.5, -9.5],
    balance: [10.0, 11.5, 11.0, 9.5, 7.0, 3.5, -1.0, -6.5, -13.0, -20.5, -29.0, -38.5],
    action: {
      cashFlow: "昨年は支出が増加し、現金の流れが後半にかけて悪化しました。今年の前半でも同様の傾向が見られ、収入は安定していますが、支出の増加により現金の流れが厳しくなっています。",
      countermeasure: "現金回収を早めるために、請求の迅速化を検討します。売掛金の管理を徹底し、未回収の債権に対して迅速に対応します。短期追加融資を検討し、資金不足に対する迅速な対応を行います。また、リスク管理を強化するため、定期的なレビューによる見直しと調整を行います。"
    }
  },
  "悲観-楽観": {
    income: [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    expense: [-33.0, -34.0, -35.0, -33.0, -32.0, -31.0, -30.0, -29.0, -28.0, -27.0, -26.0, -25.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0],
    balance: [10.0, 11.5, 11.0, 11.0, 11.5, 12.5, 14.0, 16.0, 18.5, 21.5, 25.0, 29.0],
    action: {
      cashFlow: "昨年は収入の減少と支出の抑制により、現金の流れが後半にかけて改善されましたが、今年の前半では収入の減少が続き、支出の抑制も限界に達しています。",
      countermeasure: "現金回収を早めるために、請求の迅速化を検討し、売掛金の管理を徹底します。計画を見直し、過剰在庫を削減することで資金を効率的に運用します。また、状況に応じて短期および中長期の追加融資を検討し、投資家からの資金調達など、手段の多様化を検討します。"
    }
  },
  "悲観-中立": {
    income: [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    expense: [-33.0, -34.0, -35.0, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -1.5, -2.0, -2.5, -3.0, -3.5, -4.0, -4.5, -5.0, -5.5],
    balance: [10.0, 11.5, 11.0, 9.5, 7.5, 5.0, 2.0, -1.5, -5.5, -10.0, -15.0, -20.5],
    action: {
      cashFlow: "昨年の現金の流れは、支出の抑制により後半にかけて若干の改善が見られましたが、今年の前半では収入の減少が続き、支出の増加が懸念されています。現金の流れが厳しい状況です。",
      countermeasure: "売掛金の管理を徹底し、未回収の債権に対して迅速に対応します。現金回収を早めるために、請求の迅速化を検討します。短期追加融資を検討し、資金不足に対する迅速な対応を行います。リスク管理を強化し、定期的なレビューによる見直しと調整を行います。"
    }
  },
  "悲観-悲観": {
    income: [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    expense: [-33.0, -34.0, -35.0, -34.0, -34.5, -35.0, -35.5, -36.0, -36.5, -37.0, -37.5, -38.0], // Adjusted values
    incomeExpenseDiff: [1.0, 1.5, -0.5, -1.0, -2.0, -3.0, -4.0, -5.0, -6.0, -7.0, -8.0, -9.0], // Adjusted values
    balance: [10.0, 11.5, 11.0, 10.0, 8.0, 5.0, 1.0, -4.0, -10.0, -17.0, -25.0, -34.0], // Adjusted values
    action: {
      cashFlow: "昨年は支出の増加により現金の流れが悪化し、今年の前半でも同様の傾向が見られます。収入の減少とコストの増加が重なり、現金の流れが非常に厳しい状況です。",
      countermeasure: "売掛金の管理を徹底し、未回収の債権に対して迅速に対応します。現金回収を早めるために、請求の迅速化を検討します。短期追加融資を検討し、資金不足に対する迅速な対応を行います。また、リスク管理を強化するために、定期的なレビューによる見直しと調整を行います。"
    }
  }
};
kirin-ri commented 2 months ago

収入34.0,35.5,34.5 支出-33.0,-34.0,-35.0

kirin-ri commented 2 months ago
const dataSets: { [key: string]: DataSet } = {
  "楽観-楽観": {
    income: [34.0, 35.5, 34.5, 35.0, 35.5, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0],
    expense: [-33.0, -34.0, -35.0, -34.5, -34.0, -33.5, -33.0, -32.5, -32.0, -31.5, -31.0, -30.5],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5],
    balance: [10.0, 11.0, 12.5, 12.0, 12.5, 14.0, 16.5, 20.0, 24.5, 30.0, 36.5, 44.0],  // Corrected balance values
    action: {
      cashFlow: "昨年の現金の流れは比較的安定していました。今年の4月から6月のデータでは、収入が前年同期よりも若干低下しているものの、支出の管理がより効率的であったため、全体としての現金の流れは安定しています。",
      countermeasure: "-"
    }
  },
  "楽観-中立": {
    income: [34.0, 35.5, 34.5, 35.0, 35.5, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0],
    expense: [-33.0, -34.0, -35.0, -35.0, -35.0, -35.0, -35.0, -35.0, -35.0, -35.0, -35.0, -35.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0],
    balance: [10.0, 11.0, 12.5, 12.5, 13.0, 14.0, 15.5, 17.5, 20.0, 23.0, 26.5, 30.5],  // Corrected balance values
    action: {
      cashFlow: "昨年は現金の流れが年間を通して安定していました。今年の前半では、収入は前年並みで推移している一方で、支出がやや増加しています。",
      countermeasure: "-"
    }
  },
  "楽観-悲観": {
    income: [34.0, 35.5, 34.5, 35.0, 35.5, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0],
    expense: [-33.0, -34.0, -35.0, -35.5, -36.0, -36.5, -37.0, -37.5, -38.0, -38.5, -39.0, -39.5],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5],
    balance: [10.0, 11.0, 12.5, 12.0, 11.5, 11.0, 10.5, 10.0, 9.5, 9.0, 8.5, 8.0],  // Corrected balance values
    action: {
      cashFlow: "昨年は収入が安定していたものの、設備投資や労務費の増加により、現金の流れが後半にかけて悪化しました。今年の前半でも同様の傾向が見られ、特に支出が収入を上回るリスクが高まっています。",
      countermeasure: "現金回収を早めるために、請求の迅速化を検討し、売掛金の管理を徹底します。短期追加融資を検討し、資金不足に対する迅速な対応を行います。リスク管理を強化するために、定期的なレビューによる見直しと調整を行い、未回収の債権に対して迅速に対応します。"
    }
  },
  "中立-楽観": {
    income: [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    expense: [-33.0, -34.0, -35.0, -33.0, -32.5, -32.0, -31.5, -31.0, -30.5, -30.0, -29.5, -29.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5],
    balance: [10.0, 11.0, 12.5, 14.0, 16.0, 18.5, 21.5, 25.0, 29.0, 33.5, 38.5, 44.0],  // Corrected balance values
    action: {
      cashFlow: "昨年は全体的に安定した現金の流れが見られました。今年の前半では収入が予想を下回る一方で、支出管理が良好であったため、現金の流れは比較的安定しています。",
      countermeasure: "-"
    }
  },
  "中立-中立": {
    income: [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    expense: [-33.0, -34.0, -35.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
    balance: [10.0, 11.0, 12.5, 13.0, 13.5, 14.0, 14.5, 15.0, 15.5, 16.0, 16.5, 17.0],  // Corrected balance values
    action: {
      cashFlow: "昨年の現金の流れは非常に安定していました。今年の前半でも、収入と支出のバランスが取れており、全体として安定した現金の流れを維持しています。今後も同様のパターンが続くと予想されます。",
      countermeasure: "リスク管理を強化するために、定期的なレビューによる見直しと調整を行います。売掛金の管理を徹底し、現金回収の迅速化を図ることで、現金の流れをさらに安定させます。また、計画的な支出管理を徹底し、予測可能なキャッシュフローを維持します。必要に応じて、短期および中長期の資金計画の策定を検討します。"
    }
  },
  "中立-悲観": {
    income: [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    expense: [-33.0, -34.0, -35.0, -35.5, -36.0, -36.5, -37.0, -37.5, -38.0, -38.5, -39.0, -39.5],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -1.0, -1.5, -2.0, -2.5, -3.0, -3.5, -4.0, -4.5, -5.0],
    balance: [10.0, 11.0, 12.5, 11.5, 10.0, 8.0, 5.5, 2.5, -1.0, -5.0, -9.5, -14.5],  // Corrected balance values
    action: {
      cashFlow: "昨年は支出が増加し、現金の流れが後半にかけて悪化しました。今年の前半でも同様の傾向が見られ、収入は安定していますが、支出の増加により現金の流れが厳しくなっています。",
      countermeasure: "現金回収を早めるために、請求の迅速化を検討します。売掛金の管理を徹底し、未回収の債権に対して迅速に対応します。短期追加融資を検討し、資金不足に対する迅速な対応を行います。また、リスク管理を強化するため、定期的なレビューによる見直しと調整を行います。"
    }
  },
  "悲観-楽観": {
    income: [34.0, 35.5, 34.5, 33.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5],
    expense: [-33.0, -34.0, -35.0, -34.0, -33.5, -33.0, -32.5, -32.0, -31.5, -31.0, -30.5, -30.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5],
    balance: [10.0, 11.0, 12.5, 12.0, 11.5, 11.0, 10.5, 10.0, 9.5, 9.0, 8.5, 8.0],  // Corrected balance values
    action: {
      cashFlow: "昨年は収入の減少と支出の抑制により、現金の流れが後半にかけて改善されましたが、今年の前半では収入の減少が続き、支出の抑制も限界に達しています。",
      countermeasure: "現金回収を早めるために、請求の迅速化を検討し、売掛金の管理を徹底します。計画を見直し、過剰在庫を削減することで資金を効率的に運用します。また、状況に応じて短期および中長期の追加融資を検討し、投資家からの資金調達など、手段の多様化を検討します。"
    }
  },
  "悲観-中立": {
    income: [34.0, 35.5, 34.5, 33.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5],
    expense: [-33.0, -34.0, -35.0, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -1.0, -1.5, -2.0, -2.5, -3.0, -3.5, -4.0, -4.5, -5.0],
    balance: [10.0, 11.0, 12.5, 11.5, 10.0, 8.0, 5.5, 2.5, -1.0, -5.0, -9.5, -14.5],  // Corrected balance values
    action: {
      cashFlow: "昨年の現金の流れは、支出の抑制により後半にかけて若干の改善が見られましたが、今年の前半では収入の減少が続き、支出の増加が懸念されています。現金の流れが厳しい状況です。",
      countermeasure: "売掛金の管理を徹底し、未回収の債権に対して迅速に対応します。現金回収を早めるために、請求の迅速化を検討します。短期追加融資を検討し、資金不足に対する迅速な対応を行います。リスク管理を強化し、定期的なレビューによる見直しと調整を行います。"
    }
  },
  "悲観-悲観": {
    income: [34.0, 35.5, 34.5, 33.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5],
    expense: [-33.0, -34.0, -35.0, -34.5, -35.0, -35.5, -36.0, -36.5, -37.0, -37.5, -38.0, -38.5],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -1.0, -2.0, -3.0, -4.0, -5.0, -6.0, -7.0, -8.0, -9.0],
    balance: [10.0, 11.0, 12.5, 11.5, 9.5, 6.5, 2.5, -2.5, -8.5, -15.5, -23.5, -32.5],  // Corrected balance values
    action: {
      cashFlow: "昨年は支出の増加により現金の流れが悪化し、今年の前半でも同様の傾向が見られます。収入の減少とコストの増加が重なり、現金の流れが非常に厳しい状況です。",
      countermeasure: "売掛金の管理を徹底し、未回収の債権に対して迅速に対応します。現金回収を早めるために、請求の迅速化を検討します。短期追加融資を検討し、資金不足に対する迅速な対応を行います。また、リスク管理を強化するために、定期的なレビューによる見直しと調整を行います。"
    }
  }
};
kirin-ri commented 2 months ago
  "楽観-楽観": {
    income: [34.0, 35.5, 34.5, 35.0, 35.5, 36.0, 36.0, 36.0, 36.0, 36.0, 36.0, 36.0], // Adjusted values
    expense: [-33.0, -34.0, -35.0, -34.5, -34.0, -33.5, -33.5, -33.5, -33.5, -33.5, -33.5, -33.5], // Adjusted values
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.5, 1.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5], // Adjusted values
    balance: [10.0, 11.0, 12.5, 12.0, 13.5, 16.0, 18.5, 21.0, 23.5, 26.0, 28.5, 31.0, 33.5, 36.0, 38.5, 41.0, 43.5, 46.0], // Adjusted to end at 46
    action: {
      cashFlow: "昨年の現金の流れは比較的安定していました。今年の4月から6月のデータでは、収入が前年同期よりも若干低下しているものの、支出の管理がより効率的であったため、全体としての現金の流れは安定しています。",
      countermeasure: "-"
    }
  }
kirin-ri commented 2 months ago
  "楽観-楽観": {
    income: [34.0, 35.5, 34.5, 35.0, 35.5, 36.0, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5], // Adjusted values
    expense: [-33.0, -34.0, -35.0, -34.5, -34.5, -34.0, -33.5, -33.5, -33.0, -32.5, -32.0, -31.5], // Adjusted values
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.5, 1.0, 2.0, 3.5, 4.0, 5.0, 6.0, 7.0, 8.0], // Adjusted values
    balance: [10.0, 11.0, 12.5, 12.0, 12.5, 13.5, 15.5, 19.0, 23.0, 28.0, 34.0, 41.0, 46.0], // Adjusted to end at 46
    action: {
      cashFlow: "昨年の現金の流れは比較的安定していました。今年の4月から6月のデータでは、収入が前年同期よりも若干低下しているものの、支出の管理がより効率的であったため、全体としての現金の流れは安定しています。",
      countermeasure: "-"
    }
  },
kirin-ri commented 2 months ago
  "楽観-楽観": {
    income: [34.0, 35.5, 34.5, 35.0, 35.5, 36.0, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5],
    expense: [-33.0, -34.0, -35.0, -34.5, -34.5, -34.0, -33.5, -33.5, -33.0, -32.5, -32.0, -31.5],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.5, 1.0, 2.0, 3.5, 4.0, 5.0, 6.0, 7.0, 8.0],
    balance: [10.0, 11.0, 12.5, 12.0, 13.0, 15.0, 18.5, 22.5, 27.5, 33.5, 40.5, 46.0], // Corrected to end at 46
    action: {
      cashFlow: "昨年の現金の流れは比較的安定していました。今年の4月から6月のデータでは、収入が前年同期よりも若干低下しているものの、支出の管理がより効率的であったため、全体としての現金の流れは安定しています。",
      countermeasure: "-"
    }
  }
kirin-ri commented 2 months ago
  "楽観-楽観": {
    income: [34.0, 35.5, 34.5, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5, 40.0], // Adjusted values
    expense: [-33.0, -34.0, -35.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0], // Adjusted values
    incomeExpenseDiff: [1.0, 1.5, -0.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0], // Adjusted values
    balance: [10.0, 11.0, 12.5, 12.0, 14.0, 16.5, 19.5, 23.0, 27.0, 31.5, 36.5, 42.0, 46.0], // Corrected balance to end at 46
    action: {
      cashFlow: "昨年の現金の流れは比較的安定していました。今年の4月から6月のデータでは、収入が前年同期よりも若干低下しているものの、支出の管理がより効率的であったため、全体としての現金の流れは安定しています。",
      countermeasure: "-"
    }
  }
kirin-ri commented 2 months ago
const dataSets: { [key: string]: DataSet } = {
  "楽観-楽観": {
    income: [34.0, 35.5, 34.5, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5, 40.0],
    expense: [-33.0, -34.0, -35.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0],
    balance: [11.0, 12.5, 12.0, 14.0, 16.5, 19.5, 23.0, 27.0, 31.5, 36.5, 42.0, 48.0], // Adjusted to fixed initial values
    action: {
      cashFlow: "昨年の現金の流れは比較的安定していました。今年の4月から6月のデータでは、収入が前年同期よりも若干低下しているものの、支出の管理がより効率的であったため、全体としての現金の流れは安定しています。",
      countermeasure: "-"
    }
  },
  "楽観-中立": {
    income: [34.0, 35.5, 34.5, 35.5, 36.0, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0],
    expense: [-33.0, -34.0, -35.0, -35.0, -35.0, -35.0, -35.0, -35.0, -35.0, -35.0, -35.0, -35.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.5, 1.0, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0],
    balance: [11.0, 12.5, 12.0, 12.5, 13.5, 14.5, 16.0, 18.0, 20.5, 23.5, 27.0, 31.0], // Adjusted to fixed initial values
    action: {
      cashFlow: "昨年は現金の流れが年間を通して安定していました。今年の前半では、収入は前年並みで推移している一方で、支出がやや増加しています。",
      countermeasure: "-"
    }
  },
  "楽観-悲観": {
    income: [34.0, 35.5, 34.5, 35.0, 35.5, 36.0, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5],
    expense: [-33.0, -34.0, -35.0, -36.0, -36.5, -37.0, -37.5, -38.0, -38.5, -39.0, -39.5, -40.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -1.0, -1.0, -1.0, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5],
    balance: [11.0, 12.5, 12.0, 11.0, 10.0, 9.0, 7.5, 6.0, 4.5, 3.0, 1.5, 0.0], // Adjusted to fixed initial values
    action: {
      cashFlow: "昨年は収入が安定していたものの、設備投資や労務費の増加により、現金の流れが後半にかけて悪化しました。今年の前半でも同様の傾向が見られ、特に支出が収入を上回るリスクが高まっています。",
      countermeasure: "現金回収を早めるために、請求の迅速化を検討し、売掛金の管理を徹底します。短期追加融資を検討し、資金不足に対する迅速な対応を行います。リスク管理を強化するために、定期的なレビューによる見直しと調整を行い、未回収の債権に対して迅速に対応します。"
    }
  },
  "中立-楽観": {
    income: [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    expense: [-33.0, -34.0, -35.0, -33.0, -32.5, -32.0, -31.5, -31.0, -30.5, -30.0, -29.5, -29.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5],
    balance: [11.0, 12.5, 12.0, 13.5, 15.5, 18.0, 21.0, 24.5, 28.5, 33.0, 38.0, 43.5], // Adjusted to fixed initial values
    action: {
      cashFlow: "昨年は全体的に安定した現金の流れが見られました。今年の前半では収入が予想を下回る一方で、支出管理が良好であったため、現金の流れは比較的安定しています。",
      countermeasure: "-"
    }
  },
  "中立-中立": {
    income: [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    expense: [-33.0, -34.0, -35.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0, -34.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
    balance: [11.0, 12.5, 12.0, 12.5, 13.0, 13.5, 14.0, 14.5, 15.0, 15.5, 16.0, 16.5], // Adjusted to fixed initial values
    action: {
      cashFlow: "昨年の現金の流れは非常に安定していました。今年の前半でも、収入と支出のバランスが取れており、全体として安定した現金の流れを維持しています。今後も同様のパターンが続くと予想されます。",
      countermeasure: "リスク管理を強化するために、定期的なレビューによる見直しと調整を行います。売掛金の管理を徹底し、現金回収の迅速化を図ることで、現金の流れをさらに安定させます。また、計画的な支出管理を徹底し、予測可能なキャッシュフローを維持します。必要に応じて、短期および中長期の資金計画の策定を検討します。"
    }
  },
  "中立-悲観": {
    income: [34.0, 35.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5, 34.5],
    expense: [-33.0, -34.0, -35.0, -36.0, -37.0, -38.0, -39.0, -40.0, -41.0, -42.0, -43.0, -44.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -1.5, -2.5, -3.5, -4.5, -5.5, -6.5, -7.5, -8.5, -9.5],
    balance: [11.0, 12.5, 12.0, 10.5, 8.0, 4.5, 0.0, -5.5, -12.0, -19.5, -28.0, -37.5], // Adjusted to fixed initial values
    action: {
      cashFlow: "昨年は支出が増加し、現金の流れが後半にかけて悪化しました。今年の前半でも同様の傾向が見られ、収入は安定していますが、支出の増加により現金の流れが厳しくなっています。",
      countermeasure: "現金回収を早めるために、請求の迅速化を検討します。売掛金の管理を徹底し、未回収の債権に対して迅速に対応します。短期追加融資を検討し、資金不足に対する迅速な対応を行います。また、リスク管理を強化するため、定期的なレビューによる見直しと調整を行います。"
    }
  },
  "悲観-楽観": {
    income: [34.0, 35.5, 34.5, 33.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5],
    expense: [-33.0, -34.0, -35.0, -33.0, -32.0, -31.0, -30.0, -29.0, -28.0, -27.0, -26.0, -25.0],
    incomeExpenseDiff: [1.0, 1.5, -0.5, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5],
    balance: [11.0, 12.5, 12.0, 12.5, 13.5, 15.0, 17.0, 19.5, 22.5, 26.0, 30.0, 34.5], // Adjusted to fixed initial values
    action: {
      cashFlow: "昨年は収入の減少と支出の抑制により、現金の流れが後半にかけて改善されましたが、今年の前半では収入の減少が続き、支出の抑制も限界に達しています。",
      countermeasure: "現金回収を早めるために、請求の迅速化を検討し、売掛金の管理を徹底します。計画を見直し、過剰在庫を削減することで資金を効率的に運用します。また、状況に応じて短期および中長期の追加融資を検討し、投資家からの資金調達など、手段の多様化を検討します。"
    }
  },
  "悲観-中立": {
    income: [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    expense: [-33.0, -34.0, -35.0, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5, -34.5],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -1.5, -2.0, -2.5, -3.0, -3.5, -4.0, -4.5, -5.0, -5.5],
    balance: [11.0, 12.5, 12.0, 10.5, 8.5, 6.0, 3.0, -0.5, -4.5, -9.0, -14.0, -19.5], // Adjusted to fixed initial values
    action: {
      cashFlow: "昨年の現金の流れは、支出の抑制により後半にかけて若干の改善が見られましたが、今年の前半では収入の減少が続き、支出の増加が懸念されています。現金の流れが厳しい状況です。",
      countermeasure: "売掛金の管理を徹底し、未回収の債権に対して迅速に対応します。現金回収を早めるために、請求の迅速化を検討します。短期追加融資を検討し、資金不足に対する迅速な対応を行います。リスク管理を強化し、定期的なレビューによる見直しと調整を行います。"
    }
  },
  "悲観-悲観": {
    income: [34.0, 35.5, 34.5, 33.0, 32.5, 32.0, 31.5, 31.0, 30.5, 30.0, 29.5, 29.0],
    expense: [-33.0, -34.0, -35.0, -34.5, -35.0, -35.5, -36.0, -36.5, -37.0, -37.5, -38.0, -38.5],
    incomeExpenseDiff: [1.0, 1.5, -0.5, -1.5, -2.5, -3.5, -4.5, -5.5, -6.5, -7.5, -8.5, -9.5],
    balance: [11.0, 12.5, 12.0, 10.5, 8.0, 4.5, 0.0, -5.5, -12.0, -19.5, -28.0, -37.5], // Adjusted to fixed initial values
    action: {
      cashFlow: "昨年は支出の増加により現金の流れが悪化し、今年の前半でも同様の傾向が見られます。収入の減少とコストの増加が重なり、現金の流れが非常に厳しい状況です。",
      countermeasure: "売掛金の管理を徹底し、未回収の債権に対して迅速に対応します。現金回収を早めるために、請求の迅速化を検討します。短期追加融資を検討し、資金不足に対する迅速な対応を行います。また、リスク管理を強化するために、定期的なレビューによる見直しと調整を行います。"
    }
  }
};