@orderful/droid 0.53.0 → 0.55.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/.claude-plugin/plugin.json +4 -0
- package/CHANGELOG.md +16 -0
- package/README.md +21 -1
- package/dist/tools/propose-plan/skills/propose-plan/SKILL.md +6 -1
- package/dist/tools/test-blueprints/.claude-plugin/plugin.json +22 -0
- package/dist/tools/test-blueprints/TOOL.yaml +22 -0
- package/dist/tools/test-blueprints/commands/test-blueprints.md +27 -0
- package/dist/tools/test-blueprints/skills/test-blueprints/SKILL.md +160 -0
- package/dist/tools/test-blueprints/skills/test-blueprints/references/codebase-paths.md +79 -0
- package/dist/tools/test-blueprints/skills/test-blueprints/scripts/validate.d.ts +28 -0
- package/dist/tools/test-blueprints/skills/test-blueprints/scripts/validate.d.ts.map +1 -0
- package/dist/tools/test-blueprints/skills/test-blueprints/scripts/validate.ts +250 -0
- package/dist/tools/webform/.claude-plugin/plugin.json +22 -0
- package/dist/tools/webform/TOOL.yaml +26 -0
- package/dist/tools/webform/commands/webform.md +17 -0
- package/dist/tools/webform/skills/webform/SKILL.md +354 -0
- package/package.json +1 -1
- package/src/tools/propose-plan/skills/propose-plan/SKILL.md +6 -1
- package/src/tools/test-blueprints/.claude-plugin/plugin.json +22 -0
- package/src/tools/test-blueprints/TOOL.yaml +22 -0
- package/src/tools/test-blueprints/commands/test-blueprints.md +27 -0
- package/src/tools/test-blueprints/skills/test-blueprints/SKILL.md +160 -0
- package/src/tools/test-blueprints/skills/test-blueprints/references/codebase-paths.md +79 -0
- package/src/tools/test-blueprints/skills/test-blueprints/scripts/validate.ts +250 -0
- package/src/tools/webform/.claude-plugin/plugin.json +22 -0
- package/src/tools/webform/TOOL.yaml +26 -0
- package/src/tools/webform/commands/webform.md +17 -0
- package/src/tools/webform/skills/webform/SKILL.md +354 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* test-blueprints validate
|
|
4
|
+
*
|
|
5
|
+
* Validate blueprint .json.hbs templates against their Zod schema.
|
|
6
|
+
* Generates a temporary validation script in the workspace and runs it
|
|
7
|
+
* via ts-node so that tsconfig paths resolve correctly.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* droid exec test-blueprints validate \
|
|
11
|
+
* --workspace /path/to/orderful-workspace \
|
|
12
|
+
* --schema-import "simplifiedSchemas/schemaFile" \
|
|
13
|
+
* --schema-name "Simplified943Schema" \
|
|
14
|
+
* --blueprints-dir "apps/platform-api/src/modules/testTransformation/data/testBlueprints/943s"
|
|
15
|
+
*
|
|
16
|
+
* Output (JSON):
|
|
17
|
+
* {
|
|
18
|
+
* "success": true,
|
|
19
|
+
* "results": [
|
|
20
|
+
* { "file": "943_standard.json.hbs", "status": "pass" },
|
|
21
|
+
* { "file": "943_templated.json.hbs", "status": "skip", "reason": "contains Handlebars expressions" },
|
|
22
|
+
* { "file": "943_bad.json.hbs", "status": "fail", "errors": [{ "path": "header.type", "message": "Invalid enum value" }] }
|
|
23
|
+
* ],
|
|
24
|
+
* "summary": { "pass": 8, "fail": 1, "skip": 2, "total": 11 }
|
|
25
|
+
* }
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { execSync } from 'child_process';
|
|
29
|
+
import { existsSync, writeFileSync, unlinkSync, readdirSync, readFileSync } from 'fs';
|
|
30
|
+
import { join } from 'path';
|
|
31
|
+
import { tmpdir } from 'os';
|
|
32
|
+
|
|
33
|
+
interface ValidationError {
|
|
34
|
+
path: string;
|
|
35
|
+
message: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface FileResult {
|
|
39
|
+
file: string;
|
|
40
|
+
status: 'pass' | 'fail' | 'skip';
|
|
41
|
+
reason?: string;
|
|
42
|
+
errors?: ValidationError[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface ValidateResult {
|
|
46
|
+
success: boolean;
|
|
47
|
+
results?: FileResult[];
|
|
48
|
+
summary?: { pass: number; fail: number; skip: number; total: number };
|
|
49
|
+
error?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface ParsedArgs {
|
|
53
|
+
workspace?: string;
|
|
54
|
+
schemaImport?: string;
|
|
55
|
+
schemaName?: string;
|
|
56
|
+
blueprintsDir?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function parseArgs(args: string[]): ParsedArgs {
|
|
60
|
+
const result: ParsedArgs = {};
|
|
61
|
+
|
|
62
|
+
for (let i = 0; i < args.length; i++) {
|
|
63
|
+
const arg = args[i];
|
|
64
|
+
if (arg === '--workspace' && args[i + 1]) {
|
|
65
|
+
result.workspace = args[++i];
|
|
66
|
+
} else if (arg === '--schema-import' && args[i + 1]) {
|
|
67
|
+
result.schemaImport = args[++i];
|
|
68
|
+
} else if (arg === '--schema-name' && args[i + 1]) {
|
|
69
|
+
result.schemaName = args[++i];
|
|
70
|
+
} else if (arg === '--blueprints-dir' && args[i + 1]) {
|
|
71
|
+
result.blueprintsDir = args[++i];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function expandPath(p: string): string {
|
|
79
|
+
if (p.startsWith('~/')) {
|
|
80
|
+
return p.replace('~', process.env.HOME || '');
|
|
81
|
+
}
|
|
82
|
+
if (p.startsWith('$HOME/')) {
|
|
83
|
+
return p.replace('$HOME', process.env.HOME || '');
|
|
84
|
+
}
|
|
85
|
+
return p;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function validate(parsed: ParsedArgs): ValidateResult {
|
|
89
|
+
if (!parsed.workspace) {
|
|
90
|
+
return { success: false, error: 'Missing --workspace' };
|
|
91
|
+
}
|
|
92
|
+
if (!parsed.schemaImport) {
|
|
93
|
+
return { success: false, error: 'Missing --schema-import' };
|
|
94
|
+
}
|
|
95
|
+
if (!parsed.schemaName) {
|
|
96
|
+
return { success: false, error: 'Missing --schema-name' };
|
|
97
|
+
}
|
|
98
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(parsed.schemaName)) {
|
|
99
|
+
return { success: false, error: 'Invalid --schema-name: must be a valid identifier' };
|
|
100
|
+
}
|
|
101
|
+
if (/['"\n\r]/.test(parsed.schemaImport)) {
|
|
102
|
+
return { success: false, error: 'Invalid --schema-import: must not contain quotes or newlines' };
|
|
103
|
+
}
|
|
104
|
+
if (!parsed.blueprintsDir) {
|
|
105
|
+
return { success: false, error: 'Missing --blueprints-dir' };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const workspace = expandPath(parsed.workspace);
|
|
109
|
+
if (!existsSync(workspace)) {
|
|
110
|
+
return { success: false, error: `Workspace not found: ${workspace}` };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const blueprintsDir = parsed.blueprintsDir.startsWith('/')
|
|
114
|
+
? parsed.blueprintsDir
|
|
115
|
+
: join(workspace, parsed.blueprintsDir);
|
|
116
|
+
|
|
117
|
+
if (!existsSync(blueprintsDir)) {
|
|
118
|
+
return { success: false, error: `Blueprints directory not found: ${blueprintsDir}` };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Find .json.hbs files
|
|
122
|
+
const files = readdirSync(blueprintsDir).filter(f => f.endsWith('.json.hbs'));
|
|
123
|
+
if (files.length === 0) {
|
|
124
|
+
return { success: false, error: `No .json.hbs files found in ${blueprintsDir}` };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Pre-check for Handlebars templates (skip these from Zod validation)
|
|
128
|
+
const results: FileResult[] = [];
|
|
129
|
+
const filesToValidate: string[] = [];
|
|
130
|
+
|
|
131
|
+
for (const file of files) {
|
|
132
|
+
const content = readFileSync(join(blueprintsDir, file), 'utf-8');
|
|
133
|
+
if (content.includes('{{')) {
|
|
134
|
+
results.push({ file, status: 'skip', reason: 'contains Handlebars expressions' });
|
|
135
|
+
} else {
|
|
136
|
+
// Check JSON parsability before sending to ts-node
|
|
137
|
+
try {
|
|
138
|
+
JSON.parse(content);
|
|
139
|
+
filesToValidate.push(file);
|
|
140
|
+
} catch (e) {
|
|
141
|
+
const err = e as Error;
|
|
142
|
+
results.push({ file, status: 'fail', errors: [{ path: '(root)', message: `Invalid JSON: ${err.message}` }] });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// If all files are skipped or failed JSON parse, return early
|
|
148
|
+
if (filesToValidate.length === 0) {
|
|
149
|
+
const summary = {
|
|
150
|
+
pass: 0,
|
|
151
|
+
fail: results.filter(r => r.status === 'fail').length,
|
|
152
|
+
skip: results.filter(r => r.status === 'skip').length,
|
|
153
|
+
total: results.length,
|
|
154
|
+
};
|
|
155
|
+
return { success: summary.fail === 0, results, summary };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Generate validation script
|
|
159
|
+
const validationScript = `
|
|
160
|
+
import { ${parsed.schemaName} } from '${parsed.schemaImport}';
|
|
161
|
+
import * as fs from 'fs';
|
|
162
|
+
import * as path from 'path';
|
|
163
|
+
|
|
164
|
+
const dir = ${JSON.stringify(blueprintsDir)};
|
|
165
|
+
const filesToValidate: string[] = ${JSON.stringify(filesToValidate)};
|
|
166
|
+
const results: Array<{ file: string; status: string; errors?: Array<{ path: string; message: string }> }> = [];
|
|
167
|
+
|
|
168
|
+
for (const file of filesToValidate) {
|
|
169
|
+
const content = fs.readFileSync(path.join(dir, file), 'utf-8');
|
|
170
|
+
const parsed = JSON.parse(content);
|
|
171
|
+
const result = ${parsed.schemaName}.safeParse(parsed);
|
|
172
|
+
|
|
173
|
+
if (result.success) {
|
|
174
|
+
results.push({ file, status: 'pass' });
|
|
175
|
+
} else {
|
|
176
|
+
results.push({
|
|
177
|
+
file,
|
|
178
|
+
status: 'fail',
|
|
179
|
+
errors: result.error.issues.map((issue: { path: (string | number)[]; message: string }) => ({
|
|
180
|
+
path: issue.path.join('.'),
|
|
181
|
+
message: issue.message,
|
|
182
|
+
})),
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
console.log(JSON.stringify(results));
|
|
188
|
+
`.trim();
|
|
189
|
+
|
|
190
|
+
const scriptPath = join(tmpdir(), `_validate-${Date.now()}.ts`);
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
writeFileSync(scriptPath, validationScript, 'utf-8');
|
|
194
|
+
|
|
195
|
+
const output = execSync(
|
|
196
|
+
`npx ts-node -r tsconfig-paths/register --project apps/platform-api/tsconfig.json ${JSON.stringify(scriptPath)}`,
|
|
197
|
+
{
|
|
198
|
+
cwd: workspace,
|
|
199
|
+
encoding: 'utf-8',
|
|
200
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
201
|
+
timeout: 60_000,
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
let zodResults: Array<{ file: string; status: string; errors?: ValidationError[] }>;
|
|
206
|
+
try {
|
|
207
|
+
zodResults = JSON.parse(output.trim());
|
|
208
|
+
} catch {
|
|
209
|
+
return { success: false, error: `Failed to parse validation output: ${output}` };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
for (const r of zodResults) {
|
|
213
|
+
results.push({
|
|
214
|
+
file: r.file,
|
|
215
|
+
status: r.status as 'pass' | 'fail',
|
|
216
|
+
errors: r.errors,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
} catch (err: unknown) {
|
|
220
|
+
const error = err as { stderr?: string; message?: string };
|
|
221
|
+
return {
|
|
222
|
+
success: false,
|
|
223
|
+
error: `Validation script failed: ${error.stderr || error.message}`,
|
|
224
|
+
};
|
|
225
|
+
} finally {
|
|
226
|
+
try { unlinkSync(scriptPath); } catch { /* ignore cleanup errors */ }
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Sort results to match original file order
|
|
230
|
+
results.sort((a, b) => files.indexOf(a.file) - files.indexOf(b.file));
|
|
231
|
+
|
|
232
|
+
const summary = {
|
|
233
|
+
pass: results.filter(r => r.status === 'pass').length,
|
|
234
|
+
fail: results.filter(r => r.status === 'fail').length,
|
|
235
|
+
skip: results.filter(r => r.status === 'skip').length,
|
|
236
|
+
total: results.length,
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
return { success: summary.fail === 0, results, summary };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Main
|
|
243
|
+
const args = process.argv.slice(2);
|
|
244
|
+
const parsed = parseArgs(args);
|
|
245
|
+
const result = validate(parsed);
|
|
246
|
+
console.log(JSON.stringify(result, null, 2));
|
|
247
|
+
|
|
248
|
+
if (!result.success) {
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "droid-webform",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Generate PrintView webform templates for EDI transaction types. Reads Orderful JSON schemas and sample transactions, proposes a visual layout, then generates template code with reusable subsection items.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Orderful",
|
|
7
|
+
"url": "https://github.com/orderful"
|
|
8
|
+
},
|
|
9
|
+
"repository": "https://github.com/orderful/droid",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"droid",
|
|
13
|
+
"ai",
|
|
14
|
+
"webform"
|
|
15
|
+
],
|
|
16
|
+
"skills": [
|
|
17
|
+
"./skills/webform/SKILL.md"
|
|
18
|
+
],
|
|
19
|
+
"commands": [
|
|
20
|
+
"./commands/webform.md"
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: webform
|
|
2
|
+
description: "Generate PrintView webform templates for EDI transaction types. Reads Orderful JSON schemas and sample transactions, proposes a visual layout, then generates template code with reusable subsection items."
|
|
3
|
+
version: 0.1.0
|
|
4
|
+
status: beta
|
|
5
|
+
audience:
|
|
6
|
+
- engineering
|
|
7
|
+
- product
|
|
8
|
+
|
|
9
|
+
includes:
|
|
10
|
+
skills:
|
|
11
|
+
- name: webform
|
|
12
|
+
required: true
|
|
13
|
+
commands:
|
|
14
|
+
- name: webform
|
|
15
|
+
is_alias: false
|
|
16
|
+
agents: []
|
|
17
|
+
|
|
18
|
+
dependencies: []
|
|
19
|
+
|
|
20
|
+
config_schema:
|
|
21
|
+
schema_repo:
|
|
22
|
+
type: string
|
|
23
|
+
description: "Path to orderful-workspace repo (contains Orderful JSON schemas)"
|
|
24
|
+
ui_repo:
|
|
25
|
+
type: string
|
|
26
|
+
description: "Path to o2-app-ui repo (contains PrintView templates and subsection items)"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: webform
|
|
3
|
+
description: "Generate PrintView webform templates for EDI transaction types"
|
|
4
|
+
argument-hint: "{transaction-type} [sample-transaction-path-or-inline-json]"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /webform
|
|
8
|
+
|
|
9
|
+
**User invoked:** `/webform $ARGUMENTS`
|
|
10
|
+
|
|
11
|
+
**Your task:** Invoke the **webform skill** with these arguments.
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
- `/webform 850 ~/Downloads/sample-850.json` → Generate 850 template from sample
|
|
16
|
+
- `/webform 812 ~/Downloads/812.edi` → Generate 812 template from EDI file
|
|
17
|
+
- `/webform 997` → Start 997 template (will ask for sample transaction)
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: webform
|
|
3
|
+
description: "Generate PrintView webform templates for EDI transaction types. Reads Orderful JSON schemas and sample transactions, proposes a visual layout, then generates template code with reusable subsection items. User prompts like '/webform 850 sample.json', 'create a webform for 810'."
|
|
4
|
+
argument-hint: "{transaction-type} [sample-transaction-path-or-inline-json]"
|
|
5
|
+
globs: []
|
|
6
|
+
alwaysApply: false
|
|
7
|
+
allowed-tools: [Read, Write, Edit, Glob, Grep, Bash, Agent]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Webform Skill
|
|
11
|
+
|
|
12
|
+
Generate PrintView webform templates for Orderful EDI transaction types.
|
|
13
|
+
|
|
14
|
+
## When to Use
|
|
15
|
+
|
|
16
|
+
- Creating a new webform/PrintView template for a transaction type
|
|
17
|
+
- User says "webform", "print view", "create a template for {type}"
|
|
18
|
+
|
|
19
|
+
## When NOT to Use
|
|
20
|
+
|
|
21
|
+
- Modifying existing templates (just edit directly)
|
|
22
|
+
- Working on forms-mapper shapes/node mappers (different system)
|
|
23
|
+
|
|
24
|
+
## Configuration
|
|
25
|
+
|
|
26
|
+
**IMPORTANT:** Run `droid config --get tools.webform` first and parse the JSON output. If paths are not configured, **ask the user** for them.
|
|
27
|
+
|
|
28
|
+
| Setting | Default | Description |
|
|
29
|
+
|---------|---------|-------------|
|
|
30
|
+
| `schema_repo` | (none) | Path to orderful-workspace repo (contains Orderful JSON schemas) |
|
|
31
|
+
| `ui_repo` | (none) | Path to o2-app-ui repo (contains PrintView templates) |
|
|
32
|
+
| `override` | (none) | User-defined behaviour overrides |
|
|
33
|
+
|
|
34
|
+
**If not configured:** Ask the user:
|
|
35
|
+
> "I need two repo paths to generate webform templates:
|
|
36
|
+
> 1. **orderful-workspace** — where are the Orderful JSON schemas? (e.g., `~/Projects/orderful-workspace`)
|
|
37
|
+
> 2. **o2-app-ui** — where are the PrintView templates? (e.g., `~/Projects/o2-app-ui`)"
|
|
38
|
+
|
|
39
|
+
Then set their choices:
|
|
40
|
+
```bash
|
|
41
|
+
droid config --set tools.webform.schema_repo="{user's choice}"
|
|
42
|
+
droid config --set tools.webform.ui_repo="{user's choice}"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Derived Paths
|
|
46
|
+
|
|
47
|
+
Once configured, derive all paths from the config:
|
|
48
|
+
|
|
49
|
+
- **Schema location:** `{schema_repo}/libs/schemas/src/lib/data/transactionSchemas/orderful/{type}.schema.json`
|
|
50
|
+
- **Template location:** `{ui_repo}/src/pages/Transaction/PrintView/templates/`
|
|
51
|
+
- **Subsection items:** `{ui_repo}/src/pages/Transaction/PrintView/templates/subsectionItems/`
|
|
52
|
+
- **Helpers:** `{ui_repo}/src/pages/Transaction/PrintView/templates/helpers.ts`
|
|
53
|
+
- **Types:** `{ui_repo}/src/pages/Transaction/PrintView/types/index.ts`
|
|
54
|
+
- **Tests:** `{ui_repo}/src/pages/Transaction/PrintView/templates/tests/templates.spec.ts`
|
|
55
|
+
- **Allowed types:** `{ui_repo}/src/pages/Transaction/CreateTransaction/index.tsx`
|
|
56
|
+
- **Print view routing:** `{ui_repo}/src/shared/helpers/printViewHelpers/getPrintViewComponentAndCsv.ts`
|
|
57
|
+
|
|
58
|
+
**Overrides:** This skill supports user-defined overrides. See `/droid` skill § Skill Overrides for how to create, register, and use overrides.
|
|
59
|
+
|
|
60
|
+
## Invocation
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
/webform {transaction-type} {sample-transaction}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
- `transaction-type`: e.g., `850`, `810`, `997`
|
|
67
|
+
- `sample-transaction`: file path to a JSON file, or inline JSON pasted by the user. If not provided, ask for it.
|
|
68
|
+
|
|
69
|
+
## Workflow
|
|
70
|
+
|
|
71
|
+
Follow these steps in order. Do NOT skip the visual layout step.
|
|
72
|
+
|
|
73
|
+
### Step 1: Load Inputs
|
|
74
|
+
|
|
75
|
+
1. **Read config** — run `droid config --get tools.webform` and parse the JSON. If not configured, ask the user.
|
|
76
|
+
|
|
77
|
+
2. **Read the Orderful JSON schema** for the transaction type:
|
|
78
|
+
```
|
|
79
|
+
{schema_repo}/libs/schemas/src/lib/data/transactionSchemas/orderful/{type}.schema.json
|
|
80
|
+
```
|
|
81
|
+
This gives you field names (camelCase), titles, descriptions, and nesting structure. Pay attention to:
|
|
82
|
+
- `schemaType`: `element`, `segment`, `segmentArray`, `loop`, `loopArray`
|
|
83
|
+
- `title`: human-readable name like "BEG03 - Purchase Order Number"
|
|
84
|
+
- `segmentIdentifier`: X12 segment ID like "BEG", "N1", "PO1"
|
|
85
|
+
|
|
86
|
+
3. **Parse the sample transaction** (file or inline JSON). This determines which segments/loops are actually used and should appear in the template. **Only model what's in the sample — not every possible field in the schema.**
|
|
87
|
+
|
|
88
|
+
### Step 2: Inventory Existing Subsection Items
|
|
89
|
+
|
|
90
|
+
Scan the existing subsection items directory:
|
|
91
|
+
```
|
|
92
|
+
{ui_repo}/src/pages/Transaction/PrintView/templates/subsectionItems/
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Build a map of what already exists. Read the `index.ts` barrel file and glob for all `.ts` files. For each item, note:
|
|
96
|
+
- The segment/loop it handles (e.g., `BEG`, `N1_loop`, `DTM`)
|
|
97
|
+
- Whether it exports a block, table, or both variants
|
|
98
|
+
- The `parentPath` it expects
|
|
99
|
+
|
|
100
|
+
**CRITICAL: X12 is composed of shared building blocks. The same segment (N1, DTM, REF, etc.) appears across many transaction types. ALWAYS reuse existing subsection items. NEVER duplicate.**
|
|
101
|
+
|
|
102
|
+
**NAMING: Subsection items must NOT include transaction type suffixes (e.g., `CDD_loop`, not `CDD_loop_812`).** Subsection items represent reusable X12 building blocks, not transaction-specific components. Some existing items like `PO1_loop_850` have this antipattern — do not follow that convention. Name items after the segment/loop they represent (e.g., `BCD`, `SHD`, `CDD_loop`).
|
|
103
|
+
|
|
104
|
+
### Step 3: Map Sample to Subsection Items
|
|
105
|
+
|
|
106
|
+
Walk the sample transaction's `transactionSets` structure. For each segment or loop present:
|
|
107
|
+
|
|
108
|
+
1. **Check if a subsection item already exists** → mark as EXISTING
|
|
109
|
+
2. **Check if it exists but needs additional fields** for this transaction type → mark as EXTEND
|
|
110
|
+
3. **No subsection item exists** → mark as NEW
|
|
111
|
+
|
|
112
|
+
Use the schema to determine:
|
|
113
|
+
- Field labels (from `title`, strip the element ID prefix like "BEG03 - ")
|
|
114
|
+
- Field types (`ElementType`: Alphanumeric, Numeric, Date, Time, etc. — infer from `X12DataElementType` or schema `type`)
|
|
115
|
+
- Whether a field value comes from a qualifier code (use `QualifiedValueFieldMapItem` or `QualifiedLabelFieldMapItem`)
|
|
116
|
+
- Whether data is a single record (→ block) or repeating array (→ table)
|
|
117
|
+
|
|
118
|
+
### Step 4: Propose Visual Layout
|
|
119
|
+
|
|
120
|
+
Present an ASCII/markdown layout to the user showing the proposed template structure. This is the most important step — **wait for user approval before generating any code.**
|
|
121
|
+
|
|
122
|
+
Format:
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
# Proposed Webform: {type} - {title}
|
|
126
|
+
|
|
127
|
+
## Section 1: {Section Title}
|
|
128
|
+
|
|
129
|
+
[{SEGMENT}] {Description} (block) ← EXISTING
|
|
130
|
+
• {Field Label} • {Field Label} • {Field Label}
|
|
131
|
+
• {Field Label} • {Field Label}
|
|
132
|
+
|
|
133
|
+
[{SEGMENT}] {Description} (table) ← NEW
|
|
134
|
+
| {Column 1} | {Column 2} | {Column 3} |
|
|
135
|
+
|
|
136
|
+
## Section 2: {Section Title}
|
|
137
|
+
|
|
138
|
+
[{LOOP}] {Description} (block, 2-col) ← EXISTING
|
|
139
|
+
• {Field Label} • {Field Label}
|
|
140
|
+
...
|
|
141
|
+
|
|
142
|
+
## Section N: Totals
|
|
143
|
+
|
|
144
|
+
[footer] ← EXISTING (footer_CTT_loop)
|
|
145
|
+
Left: {fields} Right: {fields}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
For each subsection item, clearly indicate:
|
|
149
|
+
- `EXISTING` — will import and reuse as-is
|
|
150
|
+
- `EXTEND` — exists but needs fields added (list which fields)
|
|
151
|
+
- `NEW` — will create a new reusable subsection item
|
|
152
|
+
|
|
153
|
+
**Grouping guidance** (based on existing templates):
|
|
154
|
+
- Header/general info segments go in the first section (BEG/BAK/BIG + CUR, PER, CSH, REF, N9)
|
|
155
|
+
- Allowances/charges (SAC_loop) get their own section if present
|
|
156
|
+
- Additional info segments together (ITD, FOB, DTM, PID, TD5, etc.)
|
|
157
|
+
- Party identification (N1_loop) gets its own section, typically as `block--two-col`
|
|
158
|
+
- Line items loop gets its own section with `countPath`
|
|
159
|
+
- Totals/footer in the last section
|
|
160
|
+
|
|
161
|
+
### Step 5: Iterate on Feedback
|
|
162
|
+
|
|
163
|
+
The user may request changes:
|
|
164
|
+
- Regroup sections
|
|
165
|
+
- Add/remove fields
|
|
166
|
+
- Change block ↔ table rendering
|
|
167
|
+
- Adjust column layout for tables
|
|
168
|
+
|
|
169
|
+
Iterate until the user confirms the layout.
|
|
170
|
+
|
|
171
|
+
### Step 6: Generate Code
|
|
172
|
+
|
|
173
|
+
Once approved, generate the template and any new subsection items.
|
|
174
|
+
|
|
175
|
+
#### Template File
|
|
176
|
+
|
|
177
|
+
Create `{ui_repo}/src/pages/Transaction/PrintView/templates/{type}.template.ts`
|
|
178
|
+
|
|
179
|
+
Follow the exact patterns from existing templates. Example structure:
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
import { PrintViewTemplate } from './types';
|
|
183
|
+
import {
|
|
184
|
+
BEG,
|
|
185
|
+
CUR,
|
|
186
|
+
// ... other imports from subsectionItems
|
|
187
|
+
} from './subsectionItems';
|
|
188
|
+
|
|
189
|
+
export const template: PrintViewTemplate = [
|
|
190
|
+
{
|
|
191
|
+
sectionTitle: 'Section Title',
|
|
192
|
+
subsections: [
|
|
193
|
+
[BEG],
|
|
194
|
+
[CUR],
|
|
195
|
+
],
|
|
196
|
+
},
|
|
197
|
+
// ... more sections
|
|
198
|
+
];
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Key patterns:
|
|
202
|
+
- Each section is a `PrintViewPredefinedSection` with `sectionTitle` and `subsections`
|
|
203
|
+
- `subsections` is an array of arrays — each inner array is a group of subsection items rendered together
|
|
204
|
+
- Line item sections use `countPath` pointing to the loop array path (e.g., `'PO1_loop'`)
|
|
205
|
+
- Party sections typically use `block--two-col` layout via the subsection item's `subsectionType`
|
|
206
|
+
|
|
207
|
+
#### New Subsection Items
|
|
208
|
+
|
|
209
|
+
For each NEW item, create a file in `subsectionItems/`:
|
|
210
|
+
```
|
|
211
|
+
{ui_repo}/src/pages/Transaction/PrintView/templates/subsectionItems/{Name}.ts
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Follow existing patterns exactly. Key rules:
|
|
215
|
+
- Import types from `'../types'`
|
|
216
|
+
- Use `ElementType` enum for field value types
|
|
217
|
+
- For blocks: set `subsectionType: 'block'` or `'block--two-col'`
|
|
218
|
+
- For tables: set `subsectionType: 'table'`
|
|
219
|
+
- For footers: set `subsectionType: 'footer'` with `leftFieldMap` and `rightFieldMap`
|
|
220
|
+
- `parentPath` should use the Orderful JSON path (e.g., `'beginningSegmentForPurchaseOrder'`)
|
|
221
|
+
- Field `valuePath` is relative to the parentPath
|
|
222
|
+
- For qualifier fields, use `valueQualifierPath` or `labelQualifierPath`
|
|
223
|
+
- Export the item as a named const
|
|
224
|
+
|
|
225
|
+
#### Extended Subsection Items
|
|
226
|
+
|
|
227
|
+
For EXTEND items, propose the specific `Edit` changes to add new fields to the existing file. Show the user what will change before applying.
|
|
228
|
+
|
|
229
|
+
#### Register in helpers.ts
|
|
230
|
+
|
|
231
|
+
Add the new template to the `loadPrintViewTemplate` switch/map in:
|
|
232
|
+
```
|
|
233
|
+
{ui_repo}/src/pages/Transaction/PrintView/templates/helpers.ts
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
#### Update barrel exports
|
|
237
|
+
|
|
238
|
+
Add any new subsection items to:
|
|
239
|
+
```
|
|
240
|
+
{ui_repo}/src/pages/Transaction/PrintView/templates/subsectionItems/index.ts
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
#### Add TransactionType enum value
|
|
244
|
+
|
|
245
|
+
If this transaction type isn't already in the `TransactionType` enum, add it to:
|
|
246
|
+
```
|
|
247
|
+
{ui_repo}/src/pages/Transaction/PrintView/types/index.ts
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Follow the naming convention: `SCREAMING_SNAKE_CASE = '{type}_{SCREAMING_SNAKE_NAME}'` (e.g., `CREDIT_DEBIT_ADJUSTMENT = '812_CREDIT_DEBIT_ADJUSTMENT'`).
|
|
251
|
+
|
|
252
|
+
#### Add to ALLOWED_TRANSACTION_TYPES
|
|
253
|
+
|
|
254
|
+
If this is a new transaction type, add it to the `ALLOWED_TRANSACTION_TYPES` array in:
|
|
255
|
+
```
|
|
256
|
+
{ui_repo}/src/pages/Transaction/CreateTransaction/index.tsx
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
#### Wire up PrintView component
|
|
260
|
+
|
|
261
|
+
Update the print view routing in:
|
|
262
|
+
```
|
|
263
|
+
{ui_repo}/src/shared/helpers/printViewHelpers/getPrintViewComponentAndCsv.ts
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
If there's an existing legacy component for this transaction type, replace it with `PrintView`. If there's no existing case, add one:
|
|
267
|
+
```typescript
|
|
268
|
+
case '{type_enum_value}':
|
|
269
|
+
printViewComponent = PrintView;
|
|
270
|
+
break;
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
#### Add to test coverage
|
|
274
|
+
|
|
275
|
+
Add the transaction type to `testedTransactionSets` in:
|
|
276
|
+
```
|
|
277
|
+
{ui_repo}/src/pages/Transaction/PrintView/templates/tests/templates.spec.ts
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
This test validates that all paths in the template exist in the schema. The template **must** pass this test — if paths don't resolve, the field names in subsection items are wrong.
|
|
281
|
+
|
|
282
|
+
### Step 7: Summary
|
|
283
|
+
|
|
284
|
+
After generating, provide a summary:
|
|
285
|
+
- Files created (with paths)
|
|
286
|
+
- Files modified (with description of changes)
|
|
287
|
+
- Existing subsection items reused
|
|
288
|
+
- Any items that were extended
|
|
289
|
+
|
|
290
|
+
## Type Reference
|
|
291
|
+
|
|
292
|
+
Quick reference for the template type system (defined in `templates/types.ts`):
|
|
293
|
+
|
|
294
|
+
### Subsection Item Types
|
|
295
|
+
|
|
296
|
+
| Type | Use When | Key Fields |
|
|
297
|
+
|------|----------|------------|
|
|
298
|
+
| Block (static header) | Single record, fixed title | `subsectionType`, `header`, `parentPath`, `fieldMap` |
|
|
299
|
+
| Block (dynamic header) | Single record, title from data | `subsectionType`, `headerQualifierPath`, `parentPath`, `fieldMap` |
|
|
300
|
+
| Table (static header) | Repeating records, fixed title | `subsectionType`, `header`, `parentPath`, `columns` |
|
|
301
|
+
| Table (dynamic header) | Repeating records, title from data | `subsectionType`, `headerQualifierPath`, `parentPath`, `columns` |
|
|
302
|
+
| Footer | Totals/summary | `subsectionType: 'footer'`, `parentPath`, `leftFieldMap`, `rightFieldMap` |
|
|
303
|
+
| Hierarchy | HL loops (856 only) | `subsectionType: 'hierarchy'`, complex nesting |
|
|
304
|
+
|
|
305
|
+
### FieldMapItem Types
|
|
306
|
+
|
|
307
|
+
| Type | Use When | Key Fields |
|
|
308
|
+
|------|----------|------------|
|
|
309
|
+
| DirectFieldMapItem | Simple label + value | `label`, `valuePath`, `valueType` |
|
|
310
|
+
| QualifiedLabelFieldMapItem | Label comes from a code qualifier | `labelQualifierPath`, `valuePath`, `valueType` |
|
|
311
|
+
| QualifiedValueFieldMapItem | The qualifier code IS the display value | `label`, `valueQualifierPath` |
|
|
312
|
+
|
|
313
|
+
### Table Column Types
|
|
314
|
+
|
|
315
|
+
| Type | Use When | Key Fields |
|
|
316
|
+
|------|----------|------------|
|
|
317
|
+
| TableStaticValueColumn | Simple column | `header`, `valuePath`, `valueType` |
|
|
318
|
+
| TableQualifiedValueColumn | Column value from qualifier | `header`, `valueQualifierPath` |
|
|
319
|
+
| TableFieldMapColumn | Multiple fields in one column | `header`, `fieldMap` |
|
|
320
|
+
| NestedTableColumn | Blocks nested inside column | `header`, `subsections` |
|
|
321
|
+
|
|
322
|
+
### ValueFormat
|
|
323
|
+
|
|
324
|
+
Use `valueFormat` on DirectFieldMapItem when needed:
|
|
325
|
+
- `'currency'` — e.g., $123.45
|
|
326
|
+
- `'signedCurrency'` — with +/- sign
|
|
327
|
+
- `'percent'` — e.g., 15%
|
|
328
|
+
- `'decimalPercent'` — e.g., 0.15
|
|
329
|
+
|
|
330
|
+
### ElementType
|
|
331
|
+
|
|
332
|
+
Common values: `Alphanumeric`, `Numeric`, `Date`, `Time`, `ID`
|
|
333
|
+
|
|
334
|
+
## Existing Subsection Items
|
|
335
|
+
|
|
336
|
+
**Do NOT hardcode this list — always scan the subsectionItems directory at runtime.** This list is for reference only and may be outdated.
|
|
337
|
+
|
|
338
|
+
### High Reuse (4+ templates)
|
|
339
|
+
N1_loop, DTM (table + block), REF (table + block), SAC_loop, SAC_loop_block, FOB (table + block), TD5 (table + block), PID (table + block)
|
|
340
|
+
|
|
341
|
+
### Medium Reuse (2-3 templates)
|
|
342
|
+
CUR, PER, CSH, ITD, MEA (table + block), TD1, NTE, MAN, LIN, AMT, CAD, PKG, QTY, TXI
|
|
343
|
+
|
|
344
|
+
### Transaction-Specific Headers
|
|
345
|
+
BEG (850), BAK (855), BIG (810), BIA (846), BSN (856), G82 (875/880), BCD (812)
|
|
346
|
+
|
|
347
|
+
### Line Item Loops
|
|
348
|
+
PO1_loop_850, PO1_loop_855, IT1_loop_810, CDD_loop, G68_loop, G83_loop, HL_loop + HL_loop_items
|
|
349
|
+
|
|
350
|
+
### Footers
|
|
351
|
+
footer_CTT, footer_CTT_loop, footer_TDS_CTT, footer_G76, footer_G84
|
|
352
|
+
|
|
353
|
+
### Other
|
|
354
|
+
ISS_loop, LIN_loop, R4, V1, SDQ, SCH, LDT, PRF, PO4, SN1, IT3, TD3, TD4_block, MTX, YNQ, N9 (block + loop + table), SHD, G01-G86 (grocery segments), constants
|