# React Components - Business Analyse Viewer

> **Usage:** React component that renders index.json directly
> **Loaded in:** /business-analyse-html
> **Data source (module):** `docs/{app}/{module}/business-analyse/v{X.Y}/index.json`
> **Data source (application):** `docs/{app}/business-analyse/v{X.Y}/index.json`
> **Related:** `react/application-viewer.md` for application-level view
> **Context7:** /facebook/react, /lucide-icons/lucide-react

---

## BusinessAnalyseViewer Component

```tsx
// web/smartstack-web/src/pages/docs/[app]/[module]/BusinessAnalyseViewer.tsx

import { useState, useEffect } from 'react';
import { Link, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import {
  FileText,
  Users,
  Shield,
  CheckCircle,
  AlertTriangle,
  ArrowRight,
  Layout,
  Database,
  GitBranch,
  Zap,
  BookOpen,
  Target
} from 'lucide-react';
import type { FeatureJson, FeatureStatus } from '@/types/docs';
import { loadFeature, listVersions } from '@/services/docs';

// Status Badge Component (supports both module and application statuses)
function StatusBadge({ status }: { status: FeatureStatus }) {
  const config: Record<FeatureStatus, { color: string; label: string }> = {
    'draft': { color: 'bg-gray-500/10 text-gray-600', label: 'Draft' },
    'framed': { color: 'bg-sky-500/10 text-sky-600', label: 'Framed' },
    'analysed': { color: 'bg-blue-500/10 text-blue-600', label: 'Analysed' },
    'decomposed': { color: 'bg-indigo-500/10 text-indigo-600', label: 'Decomposed' },
    'specified': { color: 'bg-yellow-500/10 text-yellow-600', label: 'Specified' },
    'consolidated': { color: 'bg-orange-500/10 text-orange-600', label: 'Consolidated' },
    'approved': { color: 'bg-green-500/10 text-green-600', label: 'Approved' },
    'handed-off': { color: 'bg-purple-500/10 text-purple-600', label: 'Handed Off' }
  };
  const { color, label } = config[status] || { color: 'bg-gray-500/10 text-gray-600', label: status };
  return (
    <span className={`px-2 py-0.5 rounded text-xs font-medium ${color}`}>
      {label}
    </span>
  );
}

// Priority Badge Component
function PriorityBadge({ priority }: { priority: string }) {
  const colors: Record<string, string> = {
    Must: 'bg-red-500/10 text-red-600',
    Should: 'bg-yellow-500/10 text-yellow-600',
    Could: 'bg-blue-500/10 text-blue-600'
  };
  return (
    <span className={`px-2 py-0.5 rounded text-xs font-medium ${colors[priority] || 'bg-gray-500/10 text-gray-600'}`}>
      {priority}
    </span>
  );
}

// Numbered Section Component
function NumberedSection({
  number,
  title,
  icon: Icon,
  children
}: {
  number: number;
  title: string;
  icon: React.ComponentType<{ className?: string }>;
  children: React.ReactNode;
}) {
  return (
    <section id={`section-${number}`} className="scroll-mt-4">
      <h2 className="text-xl font-semibold mb-4 flex items-center gap-3">
        <span className="w-8 h-8 rounded-full bg-[var(--color-primary-600)] text-white flex items-center justify-center text-sm font-bold">
          {number}
        </span>
        <Icon className="w-5 h-5 text-[var(--color-primary-600)]" />
        {title}
      </h2>
      <div className="card p-6">
        {children}
      </div>
    </section>
  );
}

// Version Selector Component
function VersionSelector({
  versions,
  current,
  onChange
}: {
  versions: string[];
  current: string;
  onChange: (v: string) => void;
}) {
  return (
    <div className="flex items-center gap-2">
      <GitBranch className="w-4 h-4 text-[var(--text-secondary)]" />
      <select
        value={current}
        onChange={(e) => onChange(e.target.value)}
        className="text-sm bg-[var(--bg-secondary)] border border-[var(--border-color)] rounded px-2 py-1"
      >
        {versions.map((v) => (
          <option key={v} value={v}>v{v}</option>
        ))}
      </select>
    </div>
  );
}

// Main Viewer Component
export function BusinessAnalyseViewer() {
  const { app, module } = useParams<{ app: string; module: string }>();
  const { t } = useTranslation('docs-business');
  const [feature, setFeature] = useState<FeatureJson | null>(null);
  const [versions, setVersions] = useState<string[]>([]);
  const [currentVersion, setCurrentVersion] = useState<string>('');
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    if (!app || !module) return;
    listVersions(app, module).then((vs) => {
      setVersions(vs);
      setCurrentVersion(vs[vs.length - 1]); // latest
    });
  }, [app, module]);

  useEffect(() => {
    if (!app || !module || !currentVersion) return;
    setLoading(true);
    loadFeature(app, module, currentVersion)
      .then(setFeature)
      .finally(() => setLoading(false));
  }, [app, module, currentVersion]);

  if (loading || !feature) {
    return <div className="animate-pulse p-8">Loading...</div>;
  }

  const sections = [
    { key: 'discovery', label: t('sections.discovery', 'Discovery'), icon: Target },
    { key: 'businessRules', label: t('sections.businessRules', 'Business Rules'), icon: CheckCircle },
    { key: 'entities', label: t('sections.entities', 'Entities'), icon: Database },
    { key: 'useCases', label: t('sections.useCases', 'Use Cases'), icon: Users },
    { key: 'permissions', label: t('sections.permissions', 'Permissions'), icon: Shield },
    { key: 'api', label: t('sections.api', 'API Endpoints'), icon: Zap },
    { key: 'wireframes', label: t('sections.wireframes', 'Wireframes'), icon: Layout },
    { key: 'suggestions', label: t('sections.suggestions', 'Suggestions'), icon: BookOpen },
    { key: 'changelog', label: t('sections.changelog', 'Changelog'), icon: GitBranch }
  ];

  return (
    <div className="space-y-8">
      {/* Breadcrumb */}
      <nav className="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
        <Link to="/docs" className="hover:text-[var(--color-primary-600)]">
          {t('breadcrumb.docs', 'Documentation')}
        </Link>
        <span>/</span>
        <Link to={`/docs/${app}`} className="hover:text-[var(--color-primary-600)]">
          {feature.metadata.application}
        </Link>
        <span>/</span>
        <span>{feature.metadata.module}</span>
      </nav>

      {/* Header */}
      <header>
        <div className="flex items-center gap-2 mb-2">
          <span className="px-2 py-0.5 rounded bg-[var(--color-primary-600)]/10 text-[var(--color-primary-600)] text-xs font-medium">
            {feature.id}
          </span>
          <StatusBadge status={feature.status} />
          <VersionSelector
            versions={versions}
            current={currentVersion}
            onChange={setCurrentVersion}
          />
        </div>
        <h1 className="text-3xl font-bold mb-4 flex items-center gap-3">
          <FileText className="w-8 h-8 text-[var(--color-primary-600)]" />
          {feature.metadata.module} - Business Analysis
        </h1>
        <p className="text-lg text-[var(--text-secondary)]">
          {feature.discovery.problem}
        </p>
      </header>


      {/* Stats Overview */}
      <div className="grid grid-cols-2 md:grid-cols-5 gap-4">
        {[
          { label: 'Business Rules', value: feature.analysis.businessRules.length },
          { label: 'Entities', value: feature.analysis.entities.length },
          { label: 'Use Cases', value: feature.specification.useCases.length },
          { label: 'Requirements', value: feature.specification.functionalRequirements.length },
          { label: 'Permissions', value: feature.specification.permissionMatrix.permissions.length }
        ].map(({ label, value }) => (
          <div key={label} className="p-4 rounded-lg bg-[var(--bg-secondary)] text-center">
            <div className="text-2xl font-bold text-[var(--color-primary-600)]">{value}</div>
            <div className="text-sm text-[var(--text-secondary)]">{label}</div>
          </div>
        ))}
      </div>

      {/* Section 1: Discovery */}
      <NumberedSection number={1} title={sections[0].label} icon={Target}>
        <div className="space-y-4">
          <div>
            <h3 className="font-medium mb-1">Problem</h3>
            <p className="text-[var(--text-secondary)]">{feature.discovery.problem}</p>
          </div>
          <div className="grid grid-cols-2 gap-4">
            <div>
              <h3 className="font-medium mb-1">AS-IS</h3>
              <p className="text-[var(--text-secondary)] text-sm">{feature.discovery.asIs}</p>
            </div>
            <div>
              <h3 className="font-medium mb-1">TO-BE</h3>
              <p className="text-[var(--text-secondary)] text-sm">{feature.discovery.toBe}</p>
            </div>
          </div>
          {feature.discovery.risks.length > 0 && (
            <div>
              <h3 className="font-medium mb-2">Risks</h3>
              {feature.discovery.risks.map((risk) => (
                <div key={risk.id} className="flex items-start gap-2 p-2 rounded bg-[var(--bg-secondary)] mb-1">
                  <AlertTriangle className={`w-4 h-4 flex-shrink-0 mt-0.5 ${
                    risk.severity === 'high' ? 'text-red-500' :
                    risk.severity === 'medium' ? 'text-yellow-500' : 'text-blue-500'
                  }`} />
                  <div>
                    <span className="text-sm font-medium">{risk.description}</span>
                    <span className="text-xs text-[var(--text-secondary)] ml-2">({risk.mitigation})</span>
                  </div>
                </div>
              ))}
            </div>
          )}
        </div>
      </NumberedSection>

      {/* Section 2: Business Rules */}
      <NumberedSection number={2} title={sections[1].label} icon={CheckCircle}>
        <div className="overflow-x-auto">
          <table className="w-full text-sm">
            <thead>
              <tr className="bg-[var(--bg-secondary)]">
                <th className="text-left py-2 px-3 rounded-tl-lg">ID</th>
                <th className="text-left py-2 px-3">Rule</th>
                <th className="text-left py-2 px-3">Category</th>
                <th className="text-left py-2 px-3 rounded-tr-lg">Priority</th>
              </tr>
            </thead>
            <tbody>
              {feature.analysis.businessRules.map((br, idx) => (
                <tr key={br.id} className={idx % 2 === 1 ? 'bg-[var(--bg-secondary)]/50' : ''}>
                  <td className="py-2 px-3 font-mono text-[var(--color-primary-600)]">{br.id}</td>
                  <td className="py-2 px-3">{br.rule}</td>
                  <td className="py-2 px-3 text-[var(--text-secondary)]">{br.category}</td>
                  <td className="py-2 px-3"><PriorityBadge priority={br.priority} /></td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </NumberedSection>

      {/* Section 3: Entities */}
      <NumberedSection number={3} title={sections[2].label} icon={Database}>
        <div className="space-y-4">
          {feature.analysis.entities.map((entity) => (
            <div key={entity.name} className="border border-[var(--border-color)] rounded-lg p-4">
              <h3 className="font-semibold mb-2">{entity.name}</h3>
              <p className="text-sm text-[var(--text-secondary)] mb-3">{entity.description}</p>
              <table className="w-full text-sm">
                <thead>
                  <tr className="bg-[var(--bg-secondary)]">
                    <th className="text-left py-1 px-2">Attribute</th>
                    <th className="text-left py-1 px-2">Type</th>
                    <th className="text-left py-1 px-2">Required</th>
                    <th className="text-left py-1 px-2">Rules</th>
                  </tr>
                </thead>
                <tbody>
                  {entity.attributes.map((attr) => (
                    <tr key={attr.name}>
                      <td className="py-1 px-2 font-mono text-xs">{attr.name}</td>
                      <td className="py-1 px-2 text-xs">{attr.type}</td>
                      <td className="py-1 px-2 text-xs">{attr.required ? 'Yes' : 'No'}</td>
                      <td className="py-1 px-2 text-xs text-[var(--text-secondary)]">{attr.rules}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
              {entity.relationships.length > 0 && (
                <div className="mt-2 text-xs text-[var(--text-secondary)]">
                  Relationships: {entity.relationships.map(r => `${r.type} -> ${r.target}`).join(', ')}
                </div>
              )}
            </div>
          ))}
        </div>
      </NumberedSection>

      {/* Section 4: Use Cases */}
      <NumberedSection number={4} title={sections[3].label} icon={Users}>
        <div className="space-y-4">
          {feature.specification.useCases.map((uc) => (
            <div key={uc.id} className="border border-[var(--border-color)] rounded-lg p-4">
              <div className="flex items-center justify-between mb-2">
                <h3 className="font-semibold flex items-center gap-2">
                  <span className="px-2 py-0.5 rounded bg-[var(--color-primary-600)]/10 text-[var(--color-primary-600)] text-xs font-mono">
                    {uc.id}
                  </span>
                  {uc.name}
                </h3>
                <span className="text-sm text-[var(--text-secondary)]">
                  Actor: {uc.actor}
                </span>
              </div>
              <div className="text-sm mb-2">
                <ol className="list-decimal list-inside space-y-1 text-[var(--text-secondary)]">
                  {uc.mainScenario.map((step, i) => (
                    <li key={i}>{step}</li>
                  ))}
                </ol>
              </div>
              <div className="flex items-center gap-2 text-xs">
                <Shield className="w-3 h-3 text-amber-500" />
                <code className="bg-[var(--bg-secondary)] px-2 py-0.5 rounded">{uc.permission}</code>
                {uc.linkedRules.length > 0 && (
                  <span className="text-[var(--text-secondary)]">
                    Rules: {uc.linkedRules.join(', ')}
                  </span>
                )}
              </div>
            </div>
          ))}
        </div>
      </NumberedSection>

      {/* Section 5: Permissions */}
      <NumberedSection number={5} title={sections[4].label} icon={Shield}>
        <div className="space-y-3">
          {feature.specification.permissionMatrix.permissions.map((perm) => {
            const assignedRoles = feature.specification.permissionMatrix.roleAssignments
              .filter(ra => ra.permissions.includes(perm.path))
              .map(ra => ra.role);
            return (
              <div key={perm.path} className="flex items-center justify-between p-3 rounded-lg bg-[var(--bg-secondary)]">
                <div>
                  <code className="text-sm font-mono text-[var(--color-primary-600)]">{perm.path}</code>
                  <p className="text-sm text-[var(--text-secondary)] mt-1">{perm.description}</p>
                </div>
                <div className="flex gap-1">
                  {assignedRoles.map((role) => (
                    <span key={role} className="px-2 py-0.5 rounded bg-[var(--bg-tertiary)] text-xs">
                      {role}
                    </span>
                  ))}
                </div>
              </div>
            );
          })}
        </div>
      </NumberedSection>

      {/* Section 6: API Endpoints */}
      <NumberedSection number={6} title={sections[5].label} icon={Zap}>
        <div className="overflow-x-auto">
          <table className="w-full text-sm font-mono">
            <thead>
              <tr className="bg-[var(--bg-secondary)]">
                <th className="text-left py-2 px-3 rounded-tl-lg">Method</th>
                <th className="text-left py-2 px-3">Endpoint</th>
                <th className="text-left py-2 px-3 rounded-tr-lg">Permission</th>
              </tr>
            </thead>
            <tbody>
              {feature.specification.apiEndpoints.map((ep, idx) => {
                const methodColors: Record<string, string> = {
                  GET: 'bg-green-500/10 text-green-600',
                  POST: 'bg-blue-500/10 text-blue-600',
                  PUT: 'bg-yellow-500/10 text-yellow-600',
                  DELETE: 'bg-red-500/10 text-red-600'
                };
                return (
                  <tr key={`${ep.method}-${ep.path}`} className={idx % 2 === 1 ? 'bg-[var(--bg-secondary)]/50' : ''}>
                    <td className="py-2 px-3">
                      <span className={`px-2 py-0.5 rounded ${methodColors[ep.method]}`}>{ep.method}</span>
                    </td>
                    <td className="py-2 px-3">{ep.path}</td>
                    <td className="py-2 px-3 text-[var(--text-secondary)]">{ep.permission}</td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </NumberedSection>

      {/* Section 7: Wireframes & Mockups */}
      {feature.specification.wireframes?.length > 0 && (
        <NumberedSection number={7} title="Wireframes & Mockups" icon={Layout}>
          <div className="space-y-6">
            {feature.specification.wireframes.map((wf) => (
              <div key={wf.screen} className="border border-[var(--border-color)] rounded-lg p-4">
                <div className="flex items-center justify-between mb-3">
                  <h3 className="font-semibold flex items-center gap-2">
                    {wf.screen}
                    {wf.section && (
                      <span className="px-2 py-0.5 rounded bg-[var(--bg-secondary)] text-xs text-[var(--text-secondary)]">
                        Section: {wf.section}
                      </span>
                    )}
                  </h3>
                  <div className="flex gap-1">
                    {wf.permissionsRequired?.map((perm) => (
                      <code key={perm} className="px-2 py-0.5 rounded bg-amber-500/10 text-amber-600 text-xs">
                        {perm}
                      </code>
                    ))}
                  </div>
                </div>
                <p className="text-sm text-[var(--text-secondary)] mb-3">{wf.description}</p>
                {/* Render mockup based on format */}
                {wf.mockup && (
                  <div className="bg-[var(--bg-secondary)] rounded-lg p-4 overflow-x-auto">
                    {wf.mockupFormat === 'svg' ? (
                      <div dangerouslySetInnerHTML={{ __html: wf.mockup }} />
                    ) : (
                      <pre className="text-xs font-mono whitespace-pre leading-relaxed">
                        {wf.mockup}
                      </pre>
                    )}
                  </div>
                )}
                <div className="flex gap-4 mt-3 text-xs text-[var(--text-secondary)]">
                  <span>Elements: {wf.elements.join(', ')}</span>
                  <span>Actions: {wf.actions.join(', ')}</span>
                </div>
              </div>
            ))}
          </div>
        </NumberedSection>
      )}

      {/* Section 8: Suggestions */}
      {feature.suggestions.length > 0 && (
        <NumberedSection number={8} title={sections[7].label} icon={BookOpen}>
          <div className="space-y-2">
            {feature.suggestions.map((s) => (
              <div key={s.code} className={`flex items-center justify-between p-3 rounded-lg border ${
                s.accepted === true ? 'border-green-500/30 bg-green-500/5' :
                s.accepted === false ? 'border-red-500/30 bg-red-500/5 opacity-50' :
                'border-[var(--border-color)]'
              }`}>
                <div>
                  <span className="font-medium text-sm">{s.title}</span>
                  <span className="text-xs text-[var(--text-secondary)] ml-2">({s.type}: {s.code})</span>
                  <p className="text-xs text-[var(--text-secondary)]">{s.reason}</p>
                </div>
                <span className={`px-2 py-0.5 rounded text-xs ${
                  s.accepted === true ? 'bg-green-500/10 text-green-600' :
                  s.accepted === false ? 'bg-red-500/10 text-red-600' :
                  'bg-gray-500/10 text-gray-600'
                }`}>
                  {s.accepted === true ? 'Accepted' : s.accepted === false ? 'Declined' : 'Pending'}
                </span>
              </div>
            ))}
          </div>
        </NumberedSection>
      )}

      {/* Section 9: Changelog */}
      <NumberedSection number={9} title={sections[8].label} icon={GitBranch}>
        <div className="space-y-3">
          {feature.changelog.map((entry) => (
            <div key={entry.version} className="border-l-2 border-[var(--color-primary-600)] pl-4">
              <div className="flex items-center gap-2 mb-1">
                <span className="font-mono text-sm font-medium">v{entry.version}</span>
                <span className="text-xs text-[var(--text-secondary)]">{entry.timestamp}</span>
              </div>
              <ul className="text-sm text-[var(--text-secondary)] space-y-0.5">
                {entry.changes.map((change, i) => (
                  <li key={i}>{change}</li>
                ))}
              </ul>
            </div>
          ))}
        </div>
      </NumberedSection>

      {/* Navigation Footer */}
      <div className="flex justify-between pt-6 border-t border-[var(--border-color)]">
        <Link
          to={`/docs/${app}`}
          className="flex items-center gap-2 text-[var(--text-secondary)] hover:text-[var(--color-primary-600)]"
        >
          <ArrowRight className="w-4 h-4 rotate-180" />
          {t('nav.backToApp', 'Back to Application')}
        </Link>
        <Link
          to="/docs"
          className="flex items-center gap-2 text-[var(--color-primary-600)]"
        >
          {t('nav.allModules', 'All Modules')}
          <ArrowRight className="w-4 h-4" />
        </Link>
      </div>
    </div>
  );
}
```
