Spaces:
Sleeping
Sleeping
| 'use client'; | |
| import { LoadingPulse } from '@/components/parts/loading-pulse'; | |
| import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; | |
| import type { SwotData } from '@/schema/pox'; | |
| import type { ProposalTabData } from '@/schema/proposal'; | |
| import { useGlobalStore } from '@/store/global'; | |
| import { useResultStore } from '@/store/result'; | |
| import { useEffect, useState } from 'react'; | |
| interface AnalysisTableProps { | |
| onLoadingChange?: (isLoading: boolean) => void; | |
| proposalTabsData?: Record<string, ProposalTabData>; // 各タブのデータ(prediction値順) | |
| } | |
| export default function AnalysisTable({ onLoadingChange, proposalTabsData }: AnalysisTableProps) { | |
| // ストアからSWOTデータを取得(既にrefresh-momentsページで取得済み) | |
| const { swotData75, swotData78 } = useResultStore(); | |
| const { dummyMode } = useGlobalStore(); | |
| // ダミーモード用のデータ取得 | |
| const [dummySwotData, setDummySwotData] = useState<SwotData | null>(null); | |
| useEffect(() => { | |
| if (dummyMode) { | |
| // ダミーモードの場合はPOX JSONファイルから取得 | |
| fetch('/dummy/get_pox.json') | |
| .then((res) => res.json()) | |
| .then((data) => { | |
| if (Array.isArray(data) && data[0]) { | |
| setDummySwotData(data[0]); | |
| } | |
| }) | |
| .catch((err) => console.error('Failed to load dummy POX data:', err)); | |
| } | |
| }, [dummyMode]); | |
| const swotData = dummyMode ? dummySwotData : swotData75 || swotData78; | |
| // 戦略別にタブをグループ化する関数 | |
| const getProposalsByStrategy = (strategy: string): string[] => { | |
| if (!proposalTabsData) { | |
| return []; | |
| } | |
| const proposals: string[] = []; | |
| Object.entries(proposalTabsData).forEach(([key, data]) => { | |
| // 戦略名から「(強み×機会)」などの括弧部分を削除して比較 | |
| const normalizedStrategy = data?.intent?.strategy?.split('(')[0]; | |
| if (normalizedStrategy === strategy) { | |
| proposals.push(key); | |
| } | |
| }); | |
| // 最大3つまで返す | |
| return proposals.slice(0, 3); | |
| }; | |
| // 初期ローディング状態を管理 | |
| const [isInitialLoading, setIsInitialLoading] = useState(!swotData); | |
| // SWOTデータが取得されたらローディングを終了 | |
| useEffect(() => { | |
| if (swotData) { | |
| setIsInitialLoading(false); | |
| } | |
| }, [swotData]); | |
| const isLoading = isInitialLoading; | |
| const error = null; // ストアから取得するためエラーはない | |
| // ローディング状態を親コンポーネントに通知 | |
| useEffect(() => { | |
| if (onLoadingChange) { | |
| onLoadingChange(isLoading); | |
| } | |
| }, [isLoading, onLoadingChange]); | |
| // ローディング中の表示 | |
| if (isLoading) { | |
| return ( | |
| <div className="flex flex-col space-y-6 rounded-xl bg-[#f3fafd] px-12 py-8"> | |
| <h1 className="mb-3 text-[20px] leading-[135%] font-bold text-[#212121]">分析に基づく改善アプローチ</h1> | |
| <div className="flex items-center justify-center py-8"> | |
| <LoadingPulse /> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| // エラー時またはデータがない場合は簡単なメッセージを表示 | |
| if (error || !swotData) { | |
| return ( | |
| <div className="flex flex-col space-y-6 rounded-xl bg-[#f3fafd] px-12 py-8"> | |
| <h1 className="mb-3 text-[20px] leading-[135%] font-bold text-[#212121]">分析に基づく改善アプローチ</h1> | |
| <div className="flex items-center justify-center py-8"> | |
| <p className="text-gray-600">{error ? 'データの読み込み中にエラーが発生しました' : 'データを準備中です...'}</p> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <div className="flex flex-col space-y-6 rounded-xl bg-[#f3fafd] px-12 py-8"> | |
| <> | |
| <h1 className="mb-3 text-[20px] leading-[135%] font-bold text-[#212121]">分析に基づく改善アプローチ</h1> | |
| <p className="text-[14px] leading-[135%] font-bold text-[#212121]"> | |
| 競合分析で抽出したLP情報を活用してクロスSWOT分析を行い、戦略的な改善アプローチを導き出しました。 | |
| </p> | |
| </> | |
| <Table className="table-fixed border border-[#ccc]" data-testid="analysis-table"> | |
| <TableHeader> | |
| <TableRow> | |
| <TableHead | |
| className="w-[22%] border-r border-b border-[#ccc] bg-[#D6EBF7] text-start text-base leading-[135%] font-bold text-[#212121]" | |
| rowSpan={2} | |
| colSpan={3} | |
| data-testid="swot-analysis-section" | |
| > | |
| クロスSWOT分析 | |
| </TableHead> | |
| <TableHead className="text-foreground border-b border-[#ccc] bg-[#CDE7F5] text-center text-[14px] font-bold" colSpan={6}> | |
| 内部要因 | |
| </TableHead> | |
| </TableRow> | |
| <TableRow> | |
| <TableHead | |
| colSpan={3} | |
| rowSpan={1} | |
| className="text-foreground h-auto border-r border-b border-[#ccc] bg-[#E2F0F9] px-4! py-3! text-center font-bold" | |
| > | |
| <h1 className="text-lg leading-[135%] font-bold text-[#212121]">Strength 強み</h1> | |
| <span className="text-[11px] font-medium">{swotData.強み?.一番の強み?.言い換え}</span> | |
| </TableHead> | |
| <TableHead colSpan={3} className="text-foreground h-auto border-r border-b border-[#ccc] bg-[#E2F0F9] px-4! py-3! text-center font-bold"> | |
| <h1 className="text-lg leading-[135%] font-bold text-[#212121]">Weakness 弱み</h1> | |
| <span className="text-[11px] font-medium">{swotData.弱み?.一番の弱み?.言い換え}</span> | |
| </TableHead> | |
| </TableRow> | |
| </TableHeader> | |
| <TableBody> | |
| <TableRow className="w-full"> | |
| <TableCell | |
| className="text-foreground border-r border-[#ccc] bg-[#CDE7F5] px-[16px] py-[8px] align-middle text-[14px] font-bold" | |
| rowSpan={2} | |
| colSpan={1} | |
| > | |
| 外部要因 | |
| </TableCell> | |
| <TableCell | |
| className="space-y-2 border-r border-b border-[#ccc] bg-[#E2F0F9] px-[16px] py-[8px] text-center align-middle font-bold" | |
| colSpan={2} | |
| > | |
| <p className="text-lg leading-[135%] font-bold text-[#212121]">Opportunity</p> | |
| <p className="text-lg leading-[135%] font-bold text-[#212121]">機会</p> | |
| <ul className="flex flex-col items-center space-y-1 text-[11px] font-medium"> | |
| {(swotData.機会 || []).slice(0, 2).map((item: string, index: number) => ( | |
| <li key={index} className="text-[11px] leading-[20px] font-medium"> | |
| ・{item} | |
| </li> | |
| ))} | |
| </ul> | |
| </TableCell> | |
| <TableCell className="space-y-2 border-r border-b border-[#ccc] bg-[#F8FBFE] px-[16px] py-[8px] text-center font-bold" colSpan={3}> | |
| <p className="text-lg font-semibold text-[#0186C9]">積極化戦略</p> | |
| <p className="text-[13px] font-semibold">{swotData.積極化戦略?.テーマ || ''}</p> | |
| <p className="text-start text-[11px] font-medium">{swotData.積極化戦略?.戦略の内容 || ''}</p> | |
| <div className="flex w-full justify-center space-x-4"> | |
| {getProposalsByStrategy('積極化戦略').map((proposalKey) => ( | |
| <div | |
| key={proposalKey} | |
| className="flex h-[24px] w-[65px] items-center justify-center rounded-xs border border-[#ccc] bg-[#FFFFFF] px-[8px] py-[8px] text-sm" | |
| > | |
| {proposalKey} | |
| </div> | |
| ))} | |
| </div> | |
| </TableCell> | |
| <TableCell className="space-y-2 border-r border-b border-[#ccc] bg-[#F8FBFE] px-[16px] py-[8px] text-center font-bold" colSpan={3}> | |
| <p className="text-lg font-semibold text-[#0186C9]">改善戦略</p> | |
| <p className="text-[13px] font-semibold">{swotData.改善戦略?.テーマ || ''}</p> | |
| <p className="text-start text-[11px] font-medium">{swotData.改善戦略?.戦略の内容 || ''}</p> | |
| <div className="flex w-full justify-center space-x-4"> | |
| {getProposalsByStrategy('改善戦略').map((proposalKey) => ( | |
| <div | |
| key={proposalKey} | |
| className="flex h-[24px] w-[65px] items-center justify-center rounded-xs border border-[#ccc] bg-[#FFFFFF] px-[8px] py-[8px] text-sm text-[12px]" | |
| > | |
| {proposalKey} | |
| </div> | |
| ))} | |
| </div> | |
| </TableCell> | |
| </TableRow> | |
| <TableRow className="h-full min-h-[200px] w-full"> | |
| <TableCell | |
| className="space-y-2 border-r border-b border-[#ccc] bg-[#E2F0F9] px-[16px] py-[8px] text-center align-middle font-bold" | |
| colSpan={2} | |
| > | |
| <p className="text-lg leading-[135%] font-bold text-[#212121]">Threat</p> | |
| <p className="text-lg leading-[135%] font-bold text-[#212121]">脅威</p> | |
| <ul className="flex flex-col items-center space-y-1 text-[11px] font-medium"> | |
| {(swotData.脅威 || []).slice(0, 2).map((item: string, index: number) => ( | |
| <li key={index} className="text-[11px] leading-[20px] font-medium"> | |
| ・{item} | |
| </li> | |
| ))} | |
| </ul> | |
| </TableCell> | |
| <TableCell className="space-y-2 border-r border-b border-[#ccc] bg-[#F8FBFE] px-[16px] py-[8px] text-center font-bold" colSpan={3}> | |
| <p className="text-lg font-semibold text-[#0186C9]">差別化戦略</p> | |
| <p className="text-[13px] font-semibold">{swotData.差別化戦略?.テーマ || ''}</p> | |
| <p className="text-start text-[11px] font-medium">{swotData.差別化戦略?.戦略の内容 || ''}</p> | |
| <div className="flex w-full justify-center space-x-4"> | |
| {getProposalsByStrategy('差別化戦略').map((proposalKey) => ( | |
| <div | |
| key={proposalKey} | |
| className="flex h-[24px] w-[65px] items-center justify-center rounded-xs border border-[#ccc] bg-[#FFFFFF] px-[8px] py-[8px] text-sm" | |
| > | |
| {proposalKey} | |
| </div> | |
| ))} | |
| </div> | |
| </TableCell> | |
| <TableCell className="border-r border-b border-[#ccc] bg-[#EFEFEF]/50 px-[16px] py-[8px] text-center align-middle font-bold" colSpan={3}> | |
| <p className="text-lg font-semibold text-[#212121]">防衛戦略</p> | |
| </TableCell> | |
| </TableRow> | |
| </TableBody> | |
| </Table> | |
| </div> | |
| ); | |
| } | |