@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.
- package/.documentation/business-analyse.html +1503 -1058
- package/dist/index.js +92 -55
- package/dist/index.js.map +1 -1
- package/package.json +10 -7
- package/templates/agents/ba-reader.md +250 -0
- package/templates/agents/ba-writer.md +210 -0
- package/templates/agents/docs-context-reader.md +51 -33
- package/templates/skills/_shared.md +2 -0
- package/templates/skills/business-analyse/SKILL.md +120 -108
- package/templates/skills/business-analyse/_shared.md +136 -146
- package/templates/skills/business-analyse/patterns/suggestion-catalog.md +478 -0
- package/templates/skills/business-analyse/questionnaire/01-context.md +3 -15
- package/templates/skills/business-analyse/questionnaire/03-scope.md +7 -7
- package/templates/skills/business-analyse/questionnaire/08-performance.md +7 -21
- package/templates/skills/business-analyse/questionnaire/09-constraints.md +0 -13
- package/templates/skills/business-analyse/questionnaire/10-documentation.md +0 -13
- package/templates/skills/business-analyse/questionnaire/12-migration.md +1 -1
- package/templates/skills/business-analyse/questionnaire.md +72 -76
- package/templates/skills/business-analyse/react/components.md +317 -154
- package/templates/skills/business-analyse/react/i18n-template.md +167 -106
- package/templates/skills/business-analyse/react/schema.md +474 -107
- package/templates/skills/business-analyse/schemas/feature-schema.json +860 -0
- package/templates/skills/business-analyse/steps/step-00-init.md +395 -285
- package/templates/skills/business-analyse/steps/step-01-analyse.md +523 -0
- package/templates/skills/business-analyse/steps/step-02-specify.md +899 -0
- package/templates/skills/business-analyse/steps/step-03-validate.md +1009 -0
- package/templates/skills/business-analyse/steps/step-04-handoff.md +1802 -0
- package/templates/skills/business-analyse/templates/tpl-handoff.md +49 -64
- package/templates/skills/business-analyse/steps/step-01-discover.md +0 -737
- package/templates/skills/business-analyse/steps/step-02-analyse.md +0 -299
- package/templates/skills/business-analyse/steps/step-03-specify.md +0 -472
- package/templates/skills/business-analyse/steps/step-04-validate.md +0 -335
- package/templates/skills/business-analyse/steps/step-05-handoff.md +0 -741
- package/templates/skills/business-analyse/steps/step-06-doc-html.md +0 -320
- package/templates/skills/business-analyse/templates/00-context.md +0 -105
- package/templates/skills/business-analyse/templates/tpl-brd.md +0 -97
- package/templates/skills/business-analyse/templates/tpl-discovery.md +0 -78
- package/templates/skills/business-analyse/tracking/change-template.md +0 -30
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
# React Components -
|
|
1
|
+
# React Components - Business Analyse Viewer
|
|
2
2
|
|
|
3
|
-
> **Usage:** React component
|
|
4
|
-
> **Loaded in:** step-
|
|
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
|
-
##
|
|
10
|
+
## BusinessAnalyseViewer Component
|
|
10
11
|
|
|
11
12
|
```tsx
|
|
12
|
-
// web/smartstack-web/src/pages/docs/business/
|
|
13
|
+
// web/smartstack-web/src/pages/docs/business/[app]/[module]/BusinessAnalyseViewer.tsx
|
|
13
14
|
|
|
14
|
-
import {
|
|
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 {
|
|
34
|
+
import type { FeatureJson, FeatureStatus } from '@/types/business-analyse';
|
|
35
|
+
import { loadFeature, listVersions } from '@/services/businessAnalyse';
|
|
29
36
|
|
|
30
|
-
//
|
|
31
|
-
|
|
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
|
|
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
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
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="
|
|
81
|
-
|
|
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
|
-
|
|
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/${
|
|
102
|
-
{
|
|
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>{
|
|
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
|
-
{
|
|
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
|
-
{
|
|
196
|
+
{feature.metadata.module} - Business Analysis
|
|
121
197
|
</h1>
|
|
122
198
|
<p className="text-lg text-[var(--text-secondary)]">
|
|
123
|
-
{
|
|
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-
|
|
134
|
-
{
|
|
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
|
-
{
|
|
217
|
+
{section.label}
|
|
142
218
|
</a>
|
|
143
219
|
))}
|
|
144
220
|
</nav>
|
|
145
221
|
</div>
|
|
146
222
|
|
|
147
|
-
{/*
|
|
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:
|
|
150
|
-
<NumberedSection number={1} title={
|
|
151
|
-
<
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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="
|
|
162
|
-
<div
|
|
163
|
-
|
|
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
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
{
|
|
185
|
-
</
|
|
186
|
-
|
|
187
|
-
|
|
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
|
|
203
|
-
<NumberedSection number={
|
|
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">
|
|
210
|
-
<th className="text-left py-2 px-3">
|
|
211
|
-
<th className="text-left py-2 px-3 rounded-tr-lg">
|
|
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
|
-
{
|
|
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
|
|
229
|
-
<NumberedSection number={
|
|
230
|
-
<div className="space-y-
|
|
231
|
-
{
|
|
232
|
-
<div key={
|
|
233
|
-
<
|
|
234
|
-
|
|
235
|
-
|
|
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="
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
{
|
|
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:
|
|
261
|
-
<NumberedSection number={5} title={
|
|
262
|
-
<div className="
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
<
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
<
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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
|
-
|
|
279
|
-
|
|
397
|
+
);
|
|
398
|
+
})}
|
|
280
399
|
</div>
|
|
281
400
|
</NumberedSection>
|
|
282
401
|
|
|
283
402
|
{/* Section 6: API Endpoints */}
|
|
284
|
-
<NumberedSection number={6} title={
|
|
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
|
-
|
|
296
|
-
<
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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/${
|
|
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>
|