@atlashub/smartstack-cli 2.1.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/.documentation/business-analyse.html +1503 -1058
  2. package/dist/index.js +92 -55
  3. package/dist/index.js.map +1 -1
  4. package/package.json +10 -7
  5. package/templates/agents/ba-reader.md +250 -0
  6. package/templates/agents/ba-writer.md +210 -0
  7. package/templates/agents/docs-context-reader.md +51 -33
  8. package/templates/skills/_shared.md +2 -0
  9. package/templates/skills/business-analyse/SKILL.md +120 -108
  10. package/templates/skills/business-analyse/_shared.md +136 -146
  11. package/templates/skills/business-analyse/patterns/suggestion-catalog.md +478 -0
  12. package/templates/skills/business-analyse/questionnaire/01-context.md +3 -15
  13. package/templates/skills/business-analyse/questionnaire/03-scope.md +7 -7
  14. package/templates/skills/business-analyse/questionnaire/08-performance.md +7 -21
  15. package/templates/skills/business-analyse/questionnaire/09-constraints.md +0 -13
  16. package/templates/skills/business-analyse/questionnaire/10-documentation.md +0 -13
  17. package/templates/skills/business-analyse/questionnaire/12-migration.md +1 -1
  18. package/templates/skills/business-analyse/questionnaire.md +72 -76
  19. package/templates/skills/business-analyse/react/components.md +317 -154
  20. package/templates/skills/business-analyse/react/i18n-template.md +167 -106
  21. package/templates/skills/business-analyse/react/schema.md +474 -107
  22. package/templates/skills/business-analyse/schemas/feature-schema.json +860 -0
  23. package/templates/skills/business-analyse/steps/step-00-init.md +395 -285
  24. package/templates/skills/business-analyse/steps/step-01-analyse.md +523 -0
  25. package/templates/skills/business-analyse/steps/step-02-specify.md +899 -0
  26. package/templates/skills/business-analyse/steps/step-03-validate.md +1009 -0
  27. package/templates/skills/business-analyse/steps/step-04-handoff.md +1802 -0
  28. package/templates/skills/business-analyse/templates/tpl-handoff.md +49 -64
  29. package/templates/skills/business-analyse/steps/step-01-discover.md +0 -737
  30. package/templates/skills/business-analyse/steps/step-02-analyse.md +0 -299
  31. package/templates/skills/business-analyse/steps/step-03-specify.md +0 -472
  32. package/templates/skills/business-analyse/steps/step-04-validate.md +0 -335
  33. package/templates/skills/business-analyse/steps/step-05-handoff.md +0 -741
  34. package/templates/skills/business-analyse/steps/step-06-doc-html.md +0 -320
  35. package/templates/skills/business-analyse/templates/00-context.md +0 -105
  36. package/templates/skills/business-analyse/templates/tpl-brd.md +0 -97
  37. package/templates/skills/business-analyse/templates/tpl-discovery.md +0 -78
  38. package/templates/skills/business-analyse/tracking/change-template.md +0 -30
@@ -1,17 +1,19 @@
1
- # React Components - FRD Documentation
1
+ # React Components - Business Analyse Viewer
2
2
 
3
- > **Usage:** React component templates for documentation pages
4
- > **Loaded in:** step-06-doc-html.md
3
+ > **Usage:** React component that renders feature.json directly
4
+ > **Loaded in:** step-04-handoff.md
5
+ > **Data source:** `docs/business/{app}/{module}/business-analyse/v{X.Y}/feature.json`
5
6
  > **Context7:** /facebook/react, /lucide-icons/lucide-react
6
7
 
7
8
  ---
8
9
 
9
- ## FrdDocPage Component
10
+ ## BusinessAnalyseViewer Component
10
11
 
11
12
  ```tsx
12
- // web/smartstack-web/src/pages/docs/business/{app}/{module}/FrdDocPage.tsx
13
+ // web/smartstack-web/src/pages/docs/business/[app]/[module]/BusinessAnalyseViewer.tsx
13
14
 
14
- import { Link } from 'react-router-dom';
15
+ import { useState, useEffect } from 'react';
16
+ import { Link, useParams } from 'react-router-dom';
15
17
  import { useTranslation } from 'react-i18next';
16
18
  import {
17
19
  FileText,
@@ -23,22 +25,41 @@ import {
23
25
  ArrowRight,
24
26
  List,
25
27
  Layout,
26
- Database
28
+ Database,
29
+ GitBranch,
30
+ Zap,
31
+ BookOpen,
32
+ Target
27
33
  } from 'lucide-react';
28
- import type { FrdData } from './types';
34
+ import type { FeatureJson, FeatureStatus } from '@/types/business-analyse';
35
+ import { loadFeature, listVersions } from '@/services/businessAnalyse';
29
36
 
30
- // Import data (generated from FRD)
31
- import { frdData } from './frdData';
37
+ // Status Badge Component
38
+ function StatusBadge({ status }: { status: FeatureStatus }) {
39
+ const config: Record<FeatureStatus, { color: string; label: string }> = {
40
+ 'draft': { color: 'bg-gray-500/10 text-gray-600', label: 'Draft' },
41
+ 'analysed': { color: 'bg-blue-500/10 text-blue-600', label: 'Analysed' },
42
+ 'specified': { color: 'bg-yellow-500/10 text-yellow-600', label: 'Specified' },
43
+ 'approved': { color: 'bg-green-500/10 text-green-600', label: 'Approved' },
44
+ 'handed-off': { color: 'bg-purple-500/10 text-purple-600', label: 'Handed Off' }
45
+ };
46
+ const { color, label } = config[status];
47
+ return (
48
+ <span className={`px-2 py-0.5 rounded text-xs font-medium ${color}`}>
49
+ {label}
50
+ </span>
51
+ );
52
+ }
32
53
 
33
54
  // Priority Badge Component
34
55
  function PriorityBadge({ priority }: { priority: string }) {
35
- const colors = {
56
+ const colors: Record<string, string> = {
36
57
  Must: 'bg-red-500/10 text-red-600',
37
58
  Should: 'bg-yellow-500/10 text-yellow-600',
38
59
  Could: 'bg-blue-500/10 text-blue-600'
39
60
  };
40
61
  return (
41
- <span className={`px-2 py-0.5 rounded text-xs font-medium ${colors[priority as keyof typeof colors] || 'bg-gray-500/10 text-gray-600'}`}>
62
+ <span className={`px-2 py-0.5 rounded text-xs font-medium ${colors[priority] || 'bg-gray-500/10 text-gray-600'}`}>
42
63
  {priority}
43
64
  </span>
44
65
  );
@@ -72,55 +93,110 @@ function NumberedSection({
72
93
  );
73
94
  }
74
95
 
75
- // Export page component
76
- export function {ModuleName}FrdDocPage() {
77
- const { t } = useTranslation('docs-business');
78
-
96
+ // Version Selector Component
97
+ function VersionSelector({
98
+ versions,
99
+ current,
100
+ onChange
101
+ }: {
102
+ versions: string[];
103
+ current: string;
104
+ onChange: (v: string) => void;
105
+ }) {
79
106
  return (
80
- <div className="space-y-8">
81
- {/* Component implementation follows standard patterns */}
107
+ <div className="flex items-center gap-2">
108
+ <GitBranch className="w-4 h-4 text-[var(--text-secondary)]" />
109
+ <select
110
+ value={current}
111
+ onChange={(e) => onChange(e.target.value)}
112
+ className="text-sm bg-[var(--bg-secondary)] border border-[var(--border-color)] rounded px-2 py-1"
113
+ >
114
+ {versions.map((v) => (
115
+ <option key={v} value={v}>v{v}</option>
116
+ ))}
117
+ </select>
82
118
  </div>
83
119
  );
84
120
  }
85
121
 
86
- export function {ModuleName}FrdDocPage() {
122
+ // Main Viewer Component
123
+ export function BusinessAnalyseViewer() {
124
+ const { app, module } = useParams<{ app: string; module: string }>();
87
125
  const { t } = useTranslation('docs-business');
126
+ const [feature, setFeature] = useState<FeatureJson | null>(null);
127
+ const [versions, setVersions] = useState<string[]>([]);
128
+ const [currentVersion, setCurrentVersion] = useState<string>('');
129
+ const [loading, setLoading] = useState(true);
130
+
131
+ useEffect(() => {
132
+ if (!app || !module) return;
133
+ listVersions(app, module).then((vs) => {
134
+ setVersions(vs);
135
+ setCurrentVersion(vs[vs.length - 1]); // latest
136
+ });
137
+ }, [app, module]);
138
+
139
+ useEffect(() => {
140
+ if (!app || !module || !currentVersion) return;
141
+ setLoading(true);
142
+ loadFeature(app, module, currentVersion)
143
+ .then(setFeature)
144
+ .finally(() => setLoading(false));
145
+ }, [app, module, currentVersion]);
146
+
147
+ if (loading || !feature) {
148
+ return <div className="animate-pulse p-8">Loading...</div>;
149
+ }
150
+
151
+ const sections = [
152
+ { key: 'discovery', label: t('sections.discovery', 'Discovery'), icon: Target },
153
+ { key: 'businessRules', label: t('sections.businessRules', 'Business Rules'), icon: CheckCircle },
154
+ { key: 'entities', label: t('sections.entities', 'Entities'), icon: Database },
155
+ { key: 'useCases', label: t('sections.useCases', 'Use Cases'), icon: Users },
156
+ { key: 'permissions', label: t('sections.permissions', 'Permissions'), icon: Shield },
157
+ { key: 'api', label: t('sections.api', 'API Endpoints'), icon: Zap },
158
+ { key: 'suggestions', label: t('sections.suggestions', 'Suggestions'), icon: BookOpen },
159
+ { key: 'changelog', label: t('sections.changelog', 'Changelog'), icon: GitBranch }
160
+ ];
88
161
 
89
162
  return (
90
163
  <div className="space-y-8">
91
164
  {/* Breadcrumb */}
92
165
  <nav className="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
93
166
  <Link to="/docs" className="hover:text-[var(--color-primary-600)]">
94
- {t('breadcrumb.docs')}
167
+ {t('breadcrumb.docs', 'Documentation')}
95
168
  </Link>
96
169
  <span>/</span>
97
170
  <Link to="/docs/business" className="hover:text-[var(--color-primary-600)]">
98
- {t('breadcrumb.business')}
171
+ {t('breadcrumb.business', 'Business')}
99
172
  </Link>
100
173
  <span>/</span>
101
- <Link to={`/docs/business/${frdData.applicationName.toLowerCase()}`} className="hover:text-[var(--color-primary-600)]">
102
- {t(`apps.${frdData.applicationName.toLowerCase()}.name`)}
174
+ <Link to={`/docs/business/${app}`} className="hover:text-[var(--color-primary-600)]">
175
+ {feature.metadata.application}
103
176
  </Link>
104
177
  <span>/</span>
105
- <span>{t(`modules.${frdData.moduleName.toLowerCase()}.name`)}</span>
178
+ <span>{feature.metadata.module}</span>
106
179
  </nav>
107
180
 
108
181
  {/* Header */}
109
182
  <header>
110
183
  <div className="flex items-center gap-2 mb-2">
111
184
  <span className="px-2 py-0.5 rounded bg-[var(--color-primary-600)]/10 text-[var(--color-primary-600)] text-xs font-medium">
112
- {frdData.featureId}
113
- </span>
114
- <span className="px-2 py-0.5 rounded bg-green-500/10 text-green-600 text-xs font-medium">
115
- v{frdData.version}
185
+ {feature.id}
116
186
  </span>
187
+ <StatusBadge status={feature.status} />
188
+ <VersionSelector
189
+ versions={versions}
190
+ current={currentVersion}
191
+ onChange={setCurrentVersion}
192
+ />
117
193
  </div>
118
194
  <h1 className="text-3xl font-bold mb-4 flex items-center gap-3">
119
195
  <FileText className="w-8 h-8 text-[var(--color-primary-600)]" />
120
- {t(`modules.${frdData.moduleName.toLowerCase()}.title`)}
196
+ {feature.metadata.module} - Business Analysis
121
197
  </h1>
122
198
  <p className="text-lg text-[var(--text-secondary)]">
123
- {t(`modules.${frdData.moduleName.toLowerCase()}.subtitle`)}
199
+ {feature.discovery.problem}
124
200
  </p>
125
201
  </header>
126
202
 
@@ -128,91 +204,89 @@ export function {ModuleName}FrdDocPage() {
128
204
  <div className="card p-4 bg-[var(--bg-secondary)]">
129
205
  <h2 className="font-semibold mb-3 flex items-center gap-2">
130
206
  <List className="w-4 h-4" />
131
- {t('common.tableOfContents')}
207
+ {t('common.tableOfContents', 'Table of Contents')}
132
208
  </h2>
133
- <nav className="grid grid-cols-2 md:grid-cols-3 gap-2">
134
- {['overview', 'useCases', 'businessRules', 'permissions', 'wireframes', 'api'].map((section, idx) => (
209
+ <nav className="grid grid-cols-2 md:grid-cols-4 gap-2">
210
+ {sections.map((section, idx) => (
135
211
  <a
136
- key={section}
212
+ key={section.key}
137
213
  href={`#section-${idx + 1}`}
138
214
  className="flex items-center gap-2 p-2 rounded hover:bg-[var(--bg-tertiary)] text-sm"
139
215
  >
140
216
  <ChevronRight className="w-4 h-4 text-[var(--color-primary-600)]" />
141
- {t(`sections.${section}`)}
217
+ {section.label}
142
218
  </a>
143
219
  ))}
144
220
  </nav>
145
221
  </div>
146
222
 
147
- {/* Sections follow standard pattern - see templates-react.md */}
223
+ {/* Stats Overview */}
224
+ <div className="grid grid-cols-2 md:grid-cols-5 gap-4">
225
+ {[
226
+ { label: 'Business Rules', value: feature.analysis.businessRules.length },
227
+ { label: 'Entities', value: feature.analysis.entities.length },
228
+ { label: 'Use Cases', value: feature.specification.useCases.length },
229
+ { label: 'Requirements', value: feature.specification.functionalRequirements.length },
230
+ { label: 'Permissions', value: feature.specification.permissionMatrix.permissions.length }
231
+ ].map(({ label, value }) => (
232
+ <div key={label} className="p-4 rounded-lg bg-[var(--bg-secondary)] text-center">
233
+ <div className="text-2xl font-bold text-[var(--color-primary-600)]">{value}</div>
234
+ <div className="text-sm text-[var(--text-secondary)]">{label}</div>
235
+ </div>
236
+ ))}
237
+ </div>
148
238
 
149
- {/* Section 1: Overview */}
150
- <NumberedSection number={1} title={t('sections.overview')} icon={Layout}>
151
- <p className="text-[var(--text-secondary)] mb-4">
152
- {t(`modules.${frdData.moduleName.toLowerCase()}.overview`)}
153
- </p>
154
- <div className="grid grid-cols-2 md:grid-cols-4 gap-4 mt-4">
155
- <div className="p-4 rounded-lg bg-[var(--bg-secondary)] text-center">
156
- <div className="text-2xl font-bold text-[var(--color-primary-600)]">
157
- {frdData.useCases.length}
158
- </div>
159
- <div className="text-sm text-[var(--text-secondary)]">{t('stats.useCases')}</div>
239
+ {/* Section 1: Discovery */}
240
+ <NumberedSection number={1} title={sections[0].label} icon={Target}>
241
+ <div className="space-y-4">
242
+ <div>
243
+ <h3 className="font-medium mb-1">Problem</h3>
244
+ <p className="text-[var(--text-secondary)]">{feature.discovery.problem}</p>
160
245
  </div>
161
- <div className="p-4 rounded-lg bg-[var(--bg-secondary)] text-center">
162
- <div className="text-2xl font-bold text-[var(--color-primary-600)]">
163
- {frdData.businessRules.length}
246
+ <div className="grid grid-cols-2 gap-4">
247
+ <div>
248
+ <h3 className="font-medium mb-1">AS-IS</h3>
249
+ <p className="text-[var(--text-secondary)] text-sm">{feature.discovery.asIs}</p>
164
250
  </div>
165
- <div className="text-sm text-[var(--text-secondary)]">{t('stats.businessRules')}</div>
166
- </div>
167
- <div className="p-4 rounded-lg bg-[var(--bg-secondary)] text-center">
168
- <div className="text-2xl font-bold text-[var(--color-primary-600)]">
169
- {frdData.permissions.length}
251
+ <div>
252
+ <h3 className="font-medium mb-1">TO-BE</h3>
253
+ <p className="text-[var(--text-secondary)] text-sm">{feature.discovery.toBe}</p>
170
254
  </div>
171
- <div className="text-sm text-[var(--text-secondary)]">{t('stats.permissions')}</div>
172
255
  </div>
173
- </div>
174
- </NumberedSection>
175
-
176
- {/* Section 2: Use Cases */}
177
- <NumberedSection number={2} title={t('sections.useCases')} icon={Users}>
178
- <div className="space-y-4">
179
- {frdData.useCases.map((uc) => (
180
- <div key={uc.id} className="border border-[var(--border-color)] rounded-lg p-4">
181
- <div className="flex items-center justify-between mb-2">
182
- <h3 className="font-semibold flex items-center gap-2">
183
- <span className="px-2 py-0.5 rounded bg-[var(--color-primary-600)]/10 text-[var(--color-primary-600)] text-xs font-mono">
184
- {uc.id}
185
- </span>
186
- {uc.name}
187
- </h3>
188
- <span className="text-sm text-[var(--text-secondary)]">
189
- {t('labels.actor')}: {uc.actor}
190
- </span>
191
- </div>
192
- <p className="text-sm text-[var(--text-secondary)] mb-2">{uc.description}</p>
193
- <div className="flex items-center gap-2 text-xs">
194
- <Shield className="w-3 h-3 text-amber-500" />
195
- <code className="bg-[var(--bg-secondary)] px-2 py-0.5 rounded">{uc.permission}</code>
196
- </div>
256
+ {feature.discovery.risks.length > 0 && (
257
+ <div>
258
+ <h3 className="font-medium mb-2">Risks</h3>
259
+ {feature.discovery.risks.map((risk) => (
260
+ <div key={risk.id} className="flex items-start gap-2 p-2 rounded bg-[var(--bg-secondary)] mb-1">
261
+ <AlertTriangle className={`w-4 h-4 flex-shrink-0 mt-0.5 ${
262
+ risk.severity === 'high' ? 'text-red-500' :
263
+ risk.severity === 'medium' ? 'text-yellow-500' : 'text-blue-500'
264
+ }`} />
265
+ <div>
266
+ <span className="text-sm font-medium">{risk.description}</span>
267
+ <span className="text-xs text-[var(--text-secondary)] ml-2">({risk.mitigation})</span>
268
+ </div>
269
+ </div>
270
+ ))}
197
271
  </div>
198
- ))}
272
+ )}
199
273
  </div>
200
274
  </NumberedSection>
201
275
 
202
- {/* Section 3: Business Rules */}
203
- <NumberedSection number={3} title={t('sections.businessRules')} icon={CheckCircle}>
276
+ {/* Section 2: Business Rules */}
277
+ <NumberedSection number={2} title={sections[1].label} icon={CheckCircle}>
204
278
  <div className="overflow-x-auto">
205
279
  <table className="w-full text-sm">
206
280
  <thead>
207
281
  <tr className="bg-[var(--bg-secondary)]">
208
282
  <th className="text-left py-2 px-3 rounded-tl-lg">ID</th>
209
- <th className="text-left py-2 px-3">{t('labels.rule')}</th>
210
- <th className="text-left py-2 px-3">{t('labels.category')}</th>
211
- <th className="text-left py-2 px-3 rounded-tr-lg">{t('labels.priority')}</th>
283
+ <th className="text-left py-2 px-3">Rule</th>
284
+ <th className="text-left py-2 px-3">Category</th>
285
+ <th className="text-left py-2 px-3 rounded-tr-lg">Priority</th>
212
286
  </tr>
213
287
  </thead>
214
288
  <tbody>
215
- {frdData.businessRules.map((br, idx) => (
289
+ {feature.analysis.businessRules.map((br, idx) => (
216
290
  <tr key={br.id} className={idx % 2 === 1 ? 'bg-[var(--bg-secondary)]/50' : ''}>
217
291
  <td className="py-2 px-3 font-mono text-[var(--color-primary-600)]">{br.id}</td>
218
292
  <td className="py-2 px-3">{br.rule}</td>
@@ -225,63 +299,108 @@ export function {ModuleName}FrdDocPage() {
225
299
  </div>
226
300
  </NumberedSection>
227
301
 
228
- {/* Section 4: Permissions */}
229
- <NumberedSection number={4} title={t('sections.permissions')} icon={Shield}>
230
- <div className="space-y-3">
231
- {frdData.permissions.map((perm) => (
232
- <div key={perm.path} className="flex items-center justify-between p-3 rounded-lg bg-[var(--bg-secondary)]">
233
- <div>
234
- <code className="text-sm font-mono text-[var(--color-primary-600)]">{perm.path}</code>
235
- <p className="text-sm text-[var(--text-secondary)] mt-1">{perm.description}</p>
302
+ {/* Section 3: Entities */}
303
+ <NumberedSection number={3} title={sections[2].label} icon={Database}>
304
+ <div className="space-y-4">
305
+ {feature.analysis.entities.map((entity) => (
306
+ <div key={entity.name} className="border border-[var(--border-color)] rounded-lg p-4">
307
+ <h3 className="font-semibold mb-2">{entity.name}</h3>
308
+ <p className="text-sm text-[var(--text-secondary)] mb-3">{entity.description}</p>
309
+ <table className="w-full text-sm">
310
+ <thead>
311
+ <tr className="bg-[var(--bg-secondary)]">
312
+ <th className="text-left py-1 px-2">Attribute</th>
313
+ <th className="text-left py-1 px-2">Type</th>
314
+ <th className="text-left py-1 px-2">Required</th>
315
+ <th className="text-left py-1 px-2">Rules</th>
316
+ </tr>
317
+ </thead>
318
+ <tbody>
319
+ {entity.attributes.map((attr) => (
320
+ <tr key={attr.name}>
321
+ <td className="py-1 px-2 font-mono text-xs">{attr.name}</td>
322
+ <td className="py-1 px-2 text-xs">{attr.type}</td>
323
+ <td className="py-1 px-2 text-xs">{attr.required ? 'Yes' : 'No'}</td>
324
+ <td className="py-1 px-2 text-xs text-[var(--text-secondary)]">{attr.rules}</td>
325
+ </tr>
326
+ ))}
327
+ </tbody>
328
+ </table>
329
+ {entity.relationships.length > 0 && (
330
+ <div className="mt-2 text-xs text-[var(--text-secondary)]">
331
+ Relationships: {entity.relationships.map(r => `${r.type} -> ${r.target}`).join(', ')}
332
+ </div>
333
+ )}
334
+ </div>
335
+ ))}
336
+ </div>
337
+ </NumberedSection>
338
+
339
+ {/* Section 4: Use Cases */}
340
+ <NumberedSection number={4} title={sections[3].label} icon={Users}>
341
+ <div className="space-y-4">
342
+ {feature.specification.useCases.map((uc) => (
343
+ <div key={uc.id} className="border border-[var(--border-color)] rounded-lg p-4">
344
+ <div className="flex items-center justify-between mb-2">
345
+ <h3 className="font-semibold flex items-center gap-2">
346
+ <span className="px-2 py-0.5 rounded bg-[var(--color-primary-600)]/10 text-[var(--color-primary-600)] text-xs font-mono">
347
+ {uc.id}
348
+ </span>
349
+ {uc.name}
350
+ </h3>
351
+ <span className="text-sm text-[var(--text-secondary)]">
352
+ Actor: {uc.actor}
353
+ </span>
236
354
  </div>
237
- <div className="flex gap-1">
238
- {perm.roles.map((role) => (
239
- <span key={role} className="px-2 py-0.5 rounded bg-[var(--bg-tertiary)] text-xs">
240
- {role}
355
+ <div className="text-sm mb-2">
356
+ <ol className="list-decimal list-inside space-y-1 text-[var(--text-secondary)]">
357
+ {uc.mainScenario.map((step, i) => (
358
+ <li key={i}>{step}</li>
359
+ ))}
360
+ </ol>
361
+ </div>
362
+ <div className="flex items-center gap-2 text-xs">
363
+ <Shield className="w-3 h-3 text-amber-500" />
364
+ <code className="bg-[var(--bg-secondary)] px-2 py-0.5 rounded">{uc.permission}</code>
365
+ {uc.linkedRules.length > 0 && (
366
+ <span className="text-[var(--text-secondary)]">
367
+ Rules: {uc.linkedRules.join(', ')}
241
368
  </span>
242
- ))}
369
+ )}
243
370
  </div>
244
371
  </div>
245
372
  ))}
246
373
  </div>
247
-
248
- {/* Permission Warning */}
249
- <div className="mt-4 p-4 rounded-lg bg-amber-500/10 border border-amber-500/20 flex items-start gap-3">
250
- <AlertTriangle className="w-5 h-5 text-amber-600 flex-shrink-0 mt-0.5" />
251
- <div>
252
- <div className="font-medium text-amber-600">{t('warnings.permissionCheck')}</div>
253
- <p className="text-sm text-[var(--text-secondary)]">
254
- {t('warnings.permissionCheckDescription')}
255
- </p>
256
- </div>
257
- </div>
258
374
  </NumberedSection>
259
375
 
260
- {/* Section 5: Wireframes */}
261
- <NumberedSection number={5} title={t('sections.wireframes')} icon={Layout}>
262
- <div className="border-2 border-dashed border-[var(--border-color)] rounded-lg p-6">
263
- <div className="text-center text-sm text-[var(--text-tertiary)] mb-4">
264
- {t('common.wireframePreview')}
265
- </div>
266
- <div className="space-y-4">
267
- <div className="h-12 bg-[var(--bg-secondary)] rounded flex items-center px-4 justify-between">
268
- <span className="text-sm">[Header + Navigation]</span>
269
- <span className="text-sm">[User Menu]</span>
270
- </div>
271
- <div className="grid grid-cols-4 gap-4">
272
- <div className="col-span-1 h-64 bg-[var(--bg-secondary)] rounded p-4">
273
- <span className="text-sm">[Filters]</span>
274
- </div>
275
- <div className="col-span-3 h-64 bg-[var(--bg-secondary)] rounded p-4">
276
- <span className="text-sm">[Data Table]</span>
376
+ {/* Section 5: Permissions */}
377
+ <NumberedSection number={5} title={sections[4].label} icon={Shield}>
378
+ <div className="space-y-3">
379
+ {feature.specification.permissionMatrix.permissions.map((perm) => {
380
+ const assignedRoles = feature.specification.permissionMatrix.roleAssignments
381
+ .filter(ra => ra.permissions.includes(perm.path))
382
+ .map(ra => ra.role);
383
+ return (
384
+ <div key={perm.path} className="flex items-center justify-between p-3 rounded-lg bg-[var(--bg-secondary)]">
385
+ <div>
386
+ <code className="text-sm font-mono text-[var(--color-primary-600)]">{perm.path}</code>
387
+ <p className="text-sm text-[var(--text-secondary)] mt-1">{perm.description}</p>
388
+ </div>
389
+ <div className="flex gap-1">
390
+ {assignedRoles.map((role) => (
391
+ <span key={role} className="px-2 py-0.5 rounded bg-[var(--bg-tertiary)] text-xs">
392
+ {role}
393
+ </span>
394
+ ))}
395
+ </div>
277
396
  </div>
278
- </div>
279
- </div>
397
+ );
398
+ })}
280
399
  </div>
281
400
  </NumberedSection>
282
401
 
283
402
  {/* Section 6: API Endpoints */}
284
- <NumberedSection number={6} title={t('sections.api')} icon={Database}>
403
+ <NumberedSection number={6} title={sections[5].label} icon={Zap}>
285
404
  <div className="overflow-x-auto">
286
405
  <table className="w-full text-sm font-mono">
287
406
  <thead>
@@ -292,45 +411,89 @@ export function {ModuleName}FrdDocPage() {
292
411
  </tr>
293
412
  </thead>
294
413
  <tbody>
295
- <tr>
296
- <td className="py-2 px-3"><span className="px-2 py-0.5 rounded bg-green-500/10 text-green-600">GET</span></td>
297
- <td className="py-2 px-3">/api/business/{'{module}'}</td>
298
- <td className="py-2 px-3 text-[var(--text-secondary)]">.read</td>
299
- </tr>
300
- <tr className="bg-[var(--bg-secondary)]/50">
301
- <td className="py-2 px-3"><span className="px-2 py-0.5 rounded bg-blue-500/10 text-blue-600">POST</span></td>
302
- <td className="py-2 px-3">/api/business/{'{module}'}</td>
303
- <td className="py-2 px-3 text-[var(--text-secondary)]">.create</td>
304
- </tr>
305
- <tr>
306
- <td className="py-2 px-3"><span className="px-2 py-0.5 rounded bg-yellow-500/10 text-yellow-600">PUT</span></td>
307
- <td className="py-2 px-3">/api/business/{'{module}'}/{'{id}'}</td>
308
- <td className="py-2 px-3 text-[var(--text-secondary)]">.update</td>
309
- </tr>
310
- <tr className="bg-[var(--bg-secondary)]/50">
311
- <td className="py-2 px-3"><span className="px-2 py-0.5 rounded bg-red-500/10 text-red-600">DELETE</span></td>
312
- <td className="py-2 px-3">/api/business/{'{module}'}/{'{id}'}</td>
313
- <td className="py-2 px-3 text-[var(--text-secondary)]">.delete</td>
314
- </tr>
414
+ {feature.specification.apiEndpoints.map((ep, idx) => {
415
+ const methodColors: Record<string, string> = {
416
+ GET: 'bg-green-500/10 text-green-600',
417
+ POST: 'bg-blue-500/10 text-blue-600',
418
+ PUT: 'bg-yellow-500/10 text-yellow-600',
419
+ DELETE: 'bg-red-500/10 text-red-600'
420
+ };
421
+ return (
422
+ <tr key={`${ep.method}-${ep.path}`} className={idx % 2 === 1 ? 'bg-[var(--bg-secondary)]/50' : ''}>
423
+ <td className="py-2 px-3">
424
+ <span className={`px-2 py-0.5 rounded ${methodColors[ep.method]}`}>{ep.method}</span>
425
+ </td>
426
+ <td className="py-2 px-3">{ep.path}</td>
427
+ <td className="py-2 px-3 text-[var(--text-secondary)]">{ep.permission}</td>
428
+ </tr>
429
+ );
430
+ })}
315
431
  </tbody>
316
432
  </table>
317
433
  </div>
318
434
  </NumberedSection>
319
435
 
436
+ {/* Section 7: Suggestions */}
437
+ {feature.suggestions.length > 0 && (
438
+ <NumberedSection number={7} title={sections[6].label} icon={BookOpen}>
439
+ <div className="space-y-2">
440
+ {feature.suggestions.map((s) => (
441
+ <div key={s.code} className={`flex items-center justify-between p-3 rounded-lg border ${
442
+ s.accepted === true ? 'border-green-500/30 bg-green-500/5' :
443
+ s.accepted === false ? 'border-red-500/30 bg-red-500/5 opacity-50' :
444
+ 'border-[var(--border-color)]'
445
+ }`}>
446
+ <div>
447
+ <span className="font-medium text-sm">{s.title}</span>
448
+ <span className="text-xs text-[var(--text-secondary)] ml-2">({s.type}: {s.code})</span>
449
+ <p className="text-xs text-[var(--text-secondary)]">{s.reason}</p>
450
+ </div>
451
+ <span className={`px-2 py-0.5 rounded text-xs ${
452
+ s.accepted === true ? 'bg-green-500/10 text-green-600' :
453
+ s.accepted === false ? 'bg-red-500/10 text-red-600' :
454
+ 'bg-gray-500/10 text-gray-600'
455
+ }`}>
456
+ {s.accepted === true ? 'Accepted' : s.accepted === false ? 'Declined' : 'Pending'}
457
+ </span>
458
+ </div>
459
+ ))}
460
+ </div>
461
+ </NumberedSection>
462
+ )}
463
+
464
+ {/* Section 8: Changelog */}
465
+ <NumberedSection number={8} title={sections[7].label} icon={GitBranch}>
466
+ <div className="space-y-3">
467
+ {feature.changelog.map((entry) => (
468
+ <div key={entry.version} className="border-l-2 border-[var(--color-primary-600)] pl-4">
469
+ <div className="flex items-center gap-2 mb-1">
470
+ <span className="font-mono text-sm font-medium">v{entry.version}</span>
471
+ <span className="text-xs text-[var(--text-secondary)]">{entry.timestamp}</span>
472
+ </div>
473
+ <ul className="text-sm text-[var(--text-secondary)] space-y-0.5">
474
+ {entry.changes.map((change, i) => (
475
+ <li key={i}>{change}</li>
476
+ ))}
477
+ </ul>
478
+ </div>
479
+ ))}
480
+ </div>
481
+ </NumberedSection>
482
+
320
483
  {/* Navigation Footer */}
321
484
  <div className="flex justify-between pt-6 border-t border-[var(--border-color)]">
322
485
  <Link
323
- to={`/docs/business/${frdData.applicationName.toLowerCase()}`}
486
+ to={`/docs/business/${app}`}
324
487
  className="flex items-center gap-2 text-[var(--text-secondary)] hover:text-[var(--color-primary-600)]"
325
488
  >
326
489
  <ArrowRight className="w-4 h-4 rotate-180" />
327
- {t('nav.backToApp')}
490
+ {t('nav.backToApp', 'Back to Application')}
328
491
  </Link>
329
492
  <Link
330
493
  to="/docs/business"
331
494
  className="flex items-center gap-2 text-[var(--color-primary-600)]"
332
495
  >
333
- {t('nav.allModules')}
496
+ {t('nav.allModules', 'All Modules')}
334
497
  <ArrowRight className="w-4 h-4" />
335
498
  </Link>
336
499
  </div>